temporary commit on complex merge

2019-04-13 13:19发布

问题:

I have two branches master which is the main branch and feature_branch_1 which is very old and many commits behind the master. I need to merge feature_branch_1 into master, so I did:

git pull --rebase origin master

As expected there are tons of conflicts, soon after there was a need to develop another feature on a new feature_branch_2 and this is against the HEAD of master and definitely needs to be worked on before feature_branch_1. Being more or less in the middle of the complex merge, how do I do a temporary commit on feature_branch_1 so I can come back to it later. I know I can just do a:

git add <things-that-are-done>
git commit -m 'intermediary commit between merges'
... come back to it later
git checkout 'that-temp-commit-hash'
... after finishing
git add .
git commit -m 'done with feature_branch_1'
git rebase --interactive HEAD~1

making a temporary commit then rebasing to squash it afterward.

is this the best way to handle this?

回答1:

Git will not let you make a commit with an index that contains unmerged entries.

Using git add will resolve away the unmerged entries, so that you can then commit, but this approach may be unsatisfactory for several reasons. Without going into too much detail:

  • Unresolved paths are indicated by the use of index slots 1-3 instead of slot 0. These hold the merge-base version of the file, and both "sides" (--ours and --theirs, although during a rebase the roles are kind of swapped) of the merge.
  • git add copies from the work-tree to slot 0, clobbering slots 1-3, so that the file is now resolved (even if the work-tree file is still full of conflict markers).
  • An "undo" (REUC) entry is recorded in the index at this point as well, so that you can git checkout -m the path to re-create the conflict (wiping away the slot 0 entry and restoring the 1-3 entries).
  • Actually committing the merge result loses the undo state permanently.

This means that if you do commit a partial merge, and then want to go back to it and want to finish it, some information—specifically the unmerged state, even if it was moved to an undo entry—is missing. The closest you can get to figuring out, later, which files you still need to merge properly, is to search for conflict markers. (If that's OK with you, this method may be satisfactory after all.)

Besides this, a rebase copies multiple commits, and you have not mentioned whether this particular merge conflict has occurred on the very last such commit. You need to either finish or abort the rebase before going on to other operations. (It's technically possible to do some things in the middle of a rebase, but this is tricky.)

As for this part:

... come back to it later
git checkout 'that-temp-commit-hash'
... after finishing
git add .
git commit -m 'done with feature_branch_1'
git rebase --interactive HEAD~1

Because this particular unmerged-state (which you quickly resolved "wrong" on purpose so as to do something else and come back later) was the result of a rebase rather than a merge, that sequence could work. It would be simpler, and more generally applicable, to replace the last two steps with git commit --amend, though. The --amend option tells git commit to make its new commit using the current commit's parent(s), instead of the current commit itself. This works for both normal (single-parent, non-merge) commits and actual merges (two or more parents).

Alternatives

Your best bet is probably to leave this work-tree alone and make another one where you handle the other issues.

The simplest way to do that, which always works with every version of Git, is to make another clone.

You can make the extra clone by cloning the in-progress rebase. On sensible systems that have hard links, if you clone your repository to another repository on the same file system, using local paths (git clone work/repo work/new-clone-for-fast-fix for instance, from one level up from where you have been working), Git will use hard links to the underlying packs and objects, so that you need only work-tree space.

If your Git is relatively new (I recommend at least Git 2.6 although the feature was there in 2.5), you can use git worktree add to create a new "linked work-tree". A linked work-tree remembers its origin repository (and vice versa), but also has a private index / staging-area. You can put it on whatever branch you want and do anything you like there, including creating other branches or doing other rebases, all without disturbing your "main" work-tree and "main" index.



标签: git workflow