简体   繁体   中英

What is the best way for distributed conflict resolution during merge in git? (deliberate partial commit)

Assume the following:

  1. A team of several designers (DesignerA,DesignerB,DesignerC...).

  2. Main branch master

  3. Two feature branches featureA , featureB .

  4. In featureA:
    4.1. DesignerA Modified ./fileA .
    4.2. DesignerB Modified ./fileB .
    4.3. DesignerC Modified ./fileC .

  5. In featureB:
    5.1. DesignerA Modified ./fileA .
    5.2. DesignerB Modified ./fileB .
    5.3. DesignerC Modified ./fileC .

  6. DesignerC merges featureA + featureB into master :
    6.1. merge featureA to master :
    fileA , fileB and fileC 's changes are automatically merged.

    6.2. merge featureB to master : fileC 's conflict resolved by DesignerC fileA + fileB are conflicted but DesignerA owns fileA and DesignerB owns fileB.

My question:

How can DesignerC pass the partially merged commit and let each designer to solve his own conflicts?

say:

  • Send to designerA:
    • resolved fileC
    • unresolved fileA - to handle
    • unresolved fileB - to leave unresolved
  • Send to designerB:
    • resolved fileC
    • unresolved fileA - to leave unresolved
    • unresolved fileB - to handle
  • Merge resolutions and push to master.

NOTE:

  • The team tries to use git as simply as possible - please avoid fancy gitting in answers:)
  • The solution of temporary branches is not the one I am looking for.

There is no good way to do this

That doesn't mean you can't do it at all, but all ways that you can do it are currently rather bad. The fundamental problem is simple, and has three or more parts depending on how you count:

  • You can only push commits .
  • Conflict resolution takes place in Git's index.
  • New commits are made from Git's index.
  • You can't make a new commit until you resolve all the conflicts.

Users may use their working tree files, and/or additional files inserted into their working tree or anywhere else, to help out with the resolving, but fundamentally, a conflict exists because Git has modified its index such that some entry is marked "conflicted". To un-mark the entry—so that you can make a new commit, provided that this was the last conflict—you must resolve the conflict.

Hence, to make a commit that you can send to someone else so that they can resolve their part of the conflict, you must first resolve all parts of the conflict. Now you can commit and send the commit and they can... uhhhmmmm... undo your resolution of their parts of the conflict? Except, there's no conflict left.

Git could do this, if only you could commit a conflict

Unfortunately, you can't commit a conflict. But all the tools exist within the Git ecosystem. Conflicts exist because Git's index has non-zero-stage index entries. The user's working tree also has tracked and/or untracked files that may represent partial resolutions. If we could just take each of these—the non-zero-stage entries, and the tracked and untracked files—and make commits out of them and then use those commits to re-assemble the conflicted index and work-tree contents later, we'd have the makings of exactly what you want.

The git ls-files --stage output shows everything that is actually in Git's index. We just need to write a program that takes this output and creates multiple separate temporary indices (that, each, aren't conflicted but allow another program to build a new conflicted index later) and commits those, along with another commit or two to save the work-tree state. This mass of commits—however many are needed to save the stage 0, 1, 2, and 3 entries 1 plus the work-tree state—would probably be best represented as a faked-up merge, a la git stash , and then indexed via some reference for temporarily resolved states that can be restored to resume merging later, a la refs/stash for git stash .

The similarities to git stash are clear enough, and the implementation would likely borrow from the old git stash shell script to some extent. The exact format for these commits, and the ref or refs to use, will require some thought and experimentation.

This command does not exist today.


1 In theory, when there are conflicted entries at stage 1 in the index for instance, there could be more than one file with some specific name, such as path/to/file.ext . This would represent multiple merge bases. In practice Git doesn't seem to make use of this, and if it did, it is not clear how one would know which version of path/to/file.ext went with which merge base: would every file that has a stage-1 entry have to have N such entries, where N is the number of merge bases? What hash value would go in the index slot if, say, merge base #1 has no file named path/to/file.ext , but merge base #2 does?

This all affects the design of the in-progress-merge "merge commit" we would make for handling this.

Using git alone: you can't share a conflict, the solutions I have in mind involve creating temporary branches to let each designer fix its part independently, then combine these changes together.


Other solutions:

  • have the three designers work on one same clone of the repo (eg: physically walk over to one's office, remotely connect to a workstation, work on a common ssh machine...)

  • if the amount of files is low (one file per designer as in your example?), send a copy of the "base/ours/theirs" versions of said files, and ask to send back the resolution

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM