monotone-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Monotone-devel] Updated Issue 209 - support drop/modified conflict


From: Stephen Leake
Subject: Re: [Monotone-devel] Updated Issue 209 - support drop/modified conflict
Date: Sat, 09 Jun 2012 10:47:22 -0400
User-agent: Gnus/5.13 (Gnus v5.13) Emacs/23.2 (windows-nt)

Markus Wanner <address@hidden> writes:

> Hi,
>
> On 06/08/2012 12:04 AM, Stephen Leake wrote:
>> I don't see this as a valid use case; can you elaborate?
>> 
>> Hmm. I can imaging having win32 and unix support in the parent, and
>> dropping win32 in the child.
>
> Yeah, sounds like an example. Did you really never see the warning and
> just ignore it, because you know you want the file deleted?

Every time I see the warning, I'm annoyed that it isn't a conflict :).

>> My prefered solution for that case would be to split the project into
>> five (or more) projects; win32 support, unix support, common
>> project; release project 1 contains common, unix, and win32; release
>> project two contains common and unix.
>
> Now assume the parent project is not under your control. 

Right; the 'upstream branch' vs 'local branch' does make for a good use
case. 

> ...  Every modification from the parent project
> will currently invoke the warning about the modification not being
> visible in your sub-project, because of the files you deleted.

So we need some way to tell monotone what our permanent decision is.

>> But it's easier to use directories and build directives to achieve that
>> result, so I'm back to thinking this is not a valid use case.
>
> Here, you need to elaborate. How can directories and build directives be
> a replacement for a thing as simple as a file deletion.

See the current monotone system for win32 and unix; much simpler than
dealing with multiple projects.

> Also keep in mind that - as currently often adviced - you could move
> your files to an attic directory instead of deleting. The move doesn't
> emit the same warning a delete does. And it remains persistent, in the
> sense that the file won't ever move back. However, that's clearly a hack
> around die-die-die already. I think it's properly reasonable for the
> user to wish these files to vanish from their checkouts.

Right.

>> Assuming for the moment it is a valid use case, one way to handle it is
>> to put an attribute on the file (in the parent branch) that indicates it
>> is permanently deleted in the child branch, and have the conflict
>> detection code honor that attribute.
>> 
>> That is a mechanism that allows the user to inform mtn about their
>> considered resolution to the on-going conflict.
>
> Well, it's a bit hard to put an attribute on a deleted file...  

Which is why I said it has to be on the upstream branch.

> And no, requiring to put it on the parent branch is not a option
> (because you might not be in control of it, see above example).

I was assuming upstream was not in mtn, so you would be in control of
the 'upstream' branch. But we need to include the case where that is not
true. 

So you need a 'pure' upstream branch, an 'annotated' upstream branch
(with the delete attributes), and a 'local' branch. Then the update
process would be:

Retreive new upstream version, in pure upstream branch.

Propagate pure to annotated upstream branch. Check for new annotations needed.

Propagate annotated upstream to local branch. modified delete conflicts
that are on annotated files are handled automatically, according to the
attribute.

An attribute would eliminate the mtn 1.0 warning, so it would be a
better solution to your use case.

>> Another way would be to put some option (like --no-dropped-modified, or
>> perhaps "--resolve-dropped-modified <file> drop") in the workspace
>> options file for the child branch. Not as good as an attribute, because
>> it's not in the database.
>
> Full ACK. I'd like to add: it should not only be something in the
> database, but something that can be synchronized between databases. So
> multiple users don't have to repeat that decision.

right.

>> Note that file suture is no help in this case.
>
> Agreed.
>
> Proper file resurrection could be, though. 

I don't see that.

> At least when thinking of deleted files as "moved to some unreachable
> place, contents intact". There once was such a proposal for tracking
> (and properly merging) a files liveliness.

That makes sense. CVS has an internal Attic directory for deleted files.

> Note for example, that the
> path of a file is just one of the properties of a "file" (node) in
> monotone, which gets mark-merged separately from its contents, AFAIUI.
> (Please, anybody, correct me if I'm wrong here).

Yes, that's right.

> I read through your suture documentation and noticed you trapped into
> the same fallacy of trying to identify files by their file names. 

That was not the intent. I'm sure it uses file names when trying to
resolve conflicts.

> IMO sutures must not depend on file names. Instead, it should well be
> possible to suture two files with different paths.

That was one use case covered in the sutures doc.

But I'm not talking about actually implementing sutures; let's focus on
the smallest change that gives us both what we want.

>>> We could allow a single revision to "patch" a file to zero length before
>>> deleting it. Therefore store the users wish for that delete to conflict
>>> with modifications of the file. In such a case, monotone should clearly
>>> raise a merge conflict against modifications of that file.
>> 
>> I don't understand this.
>
> Well, at the moment, monotone doesn't care if you modify the file's
> contents to zero length before the delete. It simply drops that
> modification and records a file deletion in the revision.

right.

> Instead of adding an attribute, we could allow monotone to store a patch
> (trucation) prior to deletion (in the same revision). However, that
> requires the user to decide on a delete type (conflict resolution) at
> the time of the commit, which I dislike.

I don't see how that patch would be useful. Let's put this in a use
case:

     A
    / \
   M1  D    -- D change list includes patch to zero length, and delete
   | \ |
   M2  P    -- how is that information used here?
    \ /
     Q      -- or here?


If the node is just gone in D and P, how do we notice the patch?

We could examine the change list A->D, but that's a whole new mechanism
in the merge code. And even that's not present in D->P.

> In principle, I like the attribute approach better. 

Ok.

> However, there's the technical difficulty of being unable to store
> attributes on deleted files.

Yes. 

I could imagine storing a node with no content but with attributes.
That's a bigger change than the dropped/modified conflict, but smaller
than full sutures.

I think having an annotated branch for the purpose of storing those
attributes is a reasonable workaround.

It really depends on how common this use case is.

>>> However, I'm concerned about how to expose these two kinds of deletes to
>>> the user. I don't expect the average user to be able to understand the
>>> difference. Certainly not at the point in time of the delete. Maybe at
>>> the point in time of a merge (which currently emits the warning).
>> 
>> Perhaps my attribute proposal above would work?
>> 
>>> Maybe, the user even wants different revisions to merge differently for
>>> the same deleted file. So it's not a property of the delete, but... of
>>> what else?
>> 
>> I don't know :).
>
> I think we agree that it's a decision the user shouldn't have to make at
> commit (of the delete) time. But monotone should rather ask the user
> what to do the first time there's a choice (i.e. a conflict).

Right.

> It just occurs to me that this resembles the case of copying files. For
> a user, it's just copying a file. However, afterward, there are two
> options on how to merge modifications to "copied" files, which I've seen
> referred to as "symmetrical" and "asymmetrical" copy: either the merge
> only adjusts the original file (asym), leaving the copy as is. Or it
> could duplicate that modification as well and apply the modification to
> both files as part of the merge. There exist valid use cases for both
> variants. My point for this discussion is: I think that's another case
> where the user has a better chance of understanding the effects of his
> decision when asked at the time of the merge (instead of at commit time).

Right. So a general 'conflict resolution' attribute might be appropriate.

> Complicating things, I don't think it's a file specific decision. Maybe
> there exist use cases where you want to ignore modifications from the
> parent project, but at least want to see a warning, or even better a
> real conflict, if somebody else from your project tried to modify the
> file "before" seeing your deletion.

I don't follow this. It's still per file. Although, for the win32/unix
example, it could be per directory. 

> My gut feeling is that such decisions should be stored per branch. I.e.
> assuming branches "parent" and "child", a file "foo" existing in
> "parent" and propagated to "child", but then deleted there. Once a
> conflict with a modification of "foo" in "parent" arises, the user can
> choose to:
>
>  - keep ignoring modifications from branch "parent" to file "foo"
>  - continue to get conflicts for mods from branch "parent" to file
>  "foo"

But what about file "bar"? There could easily be some files that want
one resolution, others that want another.

> Given that such a preference only needs to be stored in case of a real
> conflict, I think storing an attribute might not hurt too much. After
> all, it would even be possible to remove such "merge preference
> attributes" once you no longer need them.

Right.

> I'd simply store these as attributes of the root node and let them
> reference a file name. (Which would get more complicated to track in the
> face of renames on the parent branch, but well...)

Why a file name, instead of a node id? Ah, because the node doesn't exist.
But actually, that's ok; this information is only used when merging with
revs where that node _does_ exist. And in that case, a node id is much
better; it allows for renames.

Storing on the root node does eliminate the requirement for the
annotated branch. 

On the other hand, it complicates renaming the root node.

We need a place to store information in revision A that describes what
to do when merging with revision B that has nodes that don't exist in A.
There's no reason to attach that information to the root node; we can
just append a 'rev annotations' structure to a revision.

Implementing that change does not require a flag day; I had the same
problem in the sutures branch, and someone suggested a nifty way to
solve it. You have a predicate that decides if any revision needs the
'rev annotations' structure (in this case, that's simple; "did the user
specify any?"). If the predicate returns false, the structure is not
stored with the rev; that means all revs prior to 1.0 don't change
structure (or, more importantly, rev id), and most revs post 1.0 also
wont have the rev annotations structure.

-- 
-- Stephe



reply via email to

[Prev in Thread] Current Thread [Next in Thread]