Thread (3 messages) flat view 3 messages, 2 authors, 2016-06-15

RE: Rename conflicts in the index

From: Edward Thomson <hidden>
Date: 2016-06-15 22:56:31

Junio C Hamano [mailto:gitster@pobox.com] wrote:
Edward Thomson [off-list ref] writes:
quoted
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
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help