RE: Rename conflicts in the index
- From
Edward Thomson <ethomson@microsoft.com>
- Date
- Mar 26, 2013, 18:30 UTC
- Message-ID
- <A54CE3E330039942B33B670D971F857403A0AA7A@TK5EX14MBXC253.redmond.corp.microsoft.com>
- In-Reply-To
- <7va9q72n1u.fsf@alter.siamese.dyndns.org>
Junio C Hamano [mailto:gitster@pobox.com] wrote:
Show 23 quoted lines
> Edward Thomson <ethomson@microsoft.com> writes: > > > I would propose that we store the data about the file in conflict as > > it occurred through the renames. For example, in a rename 1->2 > > conflict where A was renamed to both B and C, you would have a single > > conflict entry containing the data for A, B and C. This would allow > > us to provide more detailed information to the user - and allow them > > to (say) choose a single name to proceed with. > > > > Is this something that has value to core git as well? Alternately, is > > there something particularly stupid about this proposal? > > I do not offhand see anything particularly stupid; a new optional index extension > section CACHE_EXT_RENAME_CONFLICT might be a good addition. > > Is "one side moves A to B while the other side moves it to C" the only case, or is > it just an example? Off the top of my head, "one side moves A to x while the > other side moves B to x/y" would also be something we would want to know. I > am sure there are other cases that need to be considered. > > I do not think we can discuss the design at the concrete level until the proposal > spells out to cover all interesting cases in order for implementations to agree on > the common semantics.
Sorry about the delay here: besides getting busy with some other things, I wanted both a complete writeup and to have taken a pass at a test implementation this in libgit2 to make sure seemed like a reasonably sensible approach.
I would propose a new extension, 'CONF', to handle conflict data, differing from the stage >0 entries in the index in that this extension tracks the conflicting file across names if the underlying merge engine has support for renames.
I made an attempt to keep the entry data similar to other entries in the index. I would propose that entries in the conflict are as follows:
Flags Four octets that describe the conflict. Data includes:
0x01 HAS_ANCESTOR
There is a file in the common ancestor branch that contributes
to this conflict. Its data will follow.
0x02 HAS_OURS
There is a file in "our" branch that contributes to this conflict.
Its data will follow.
0x04 HAS_THEIRS
There is a file in "their" branch that contributes to this conflict.
Its data will follow. 0x08 NAME_CONFLICT_OURS
This item has a path in "our" branch that overlaps a different
item in "their" branch. (Eg, this conflict represents the "our"
side of a rename/add conflict.)
0x10 NAME_CONFLICT_THEIRS
This item has a path in "their" branch that overlaps a different
item in "our" branch. (Eg, this conflict represents the "theirs"
side of a rename/add conflict.) 0x20 DF_CONFLICT_FILE
This is the file involved in a directory/file conflict.
0x40 DF_CONFLICT_CHILD
This is a child of a directory involved in a directory/file conflict.Other bits are reserved.
Conflict Sides The data about one side of a conflict will contain: mode (ASCII string representation of octal, null-terminated) path (null terminated) sha1 (raw bytes)
The conflict sides will be written in this order: Ancestor (if HAS_ANCESTOR is set) Ours (if HAS_OURS is set) Theirs (if HAS_THEIRS is set)
I would propose that this not simply track rename conflicts, but all conflicts. Having a single canonical location is preferable - if the index contains a CONF section (and the client supports it), it would use that. Otherwise, the client would look at stage >0 entries.
I would propose that another extension, 'RSVD', track these conflicts once they are resolved. The format would be the same - when a conflict is resolved from the CONF the entry will be placed as-is in the RSVD.
Examples are not an exhaustive list, but should help elucidate the name and d/f conflicts:
Normal edit / edit conflict, where A is edited in ours and theirs:
Conflict one:
Flags = HAS_ANCESTOR|HAS_OURS|HAS_THEIRS
Entry 1 = A [Ancestor]
Entry 2 = B [Ancestor]
Entry 3 = C [Ancestor]Rename / add conflict, where A is renamed to B in ours and B is added in theirs:
Conflict one:
Flags = HAS_ANCESTOR|HAS_OURS|NAME_CONFLICT_OURS
Entry 1 = A [Ancestor]
Entry 2 = B [Ours]
Entry 3 = A [Theirs]
Conflict two:
Flags = HAS_THEIRS|NAME_CONFLICT_THEIRS
Entry 1 = File B [Theirs]D/F conflict, where some file A is deleted in theirs, and a directory A is created with file child:
Conflict one:
Flags = HAS_ANCESTOR|HAS_OURS|HAS_THEIRS|DF_CONFLICT_FILE
Entry 1 = A [Ancestor]
Entry 2 = A [Ours]
Conflict two:
Flags = HAS_THEIRS|DF_CONFLICT_CHILD
Entry 1 = A/child [Theirs]Thanks for your input on this.
-ed