简体   繁体   中英

Git workflow for partial merges?

I have a two branches dev and a . I need to merge feature a into dev , but my teammate has made certain features in one file that is not ready for merge. If I just merge and resolve using mine, git marks the changes in this file as invalid, and rather than permitting these changes to merge at a later time, just fast forwards if I try to re-merge the branch. If I don't resolve conflicts, git refuses to perform the merge.

My strategy for working around this is to create a branch aside and suppress the broken feature, then merge in this branch. The problem is that the git commands for this get somewhat complicated, so I need the help of an expert.

I would like if possible to generalize this type of action, into a git extension I would call something to the effect of git-cherrymerge .

The steps would be as follows:

  1. Replay commits specified by user (maybe this can be automated with some strategy) onto a temporary branch
  2. Git filter-branch to remove the broken files
  3. Squash temporary branch, adding automatic commit message
  4. Merge temporary branch into target branch (here it's dev)

I'm not quite an expert in filter-branch or rebase and it looks like I can quite seriously damage the history by misusing them .

I suppose my question is

  1. Will this work or is there a better canonical way to do this?
  2. What git commands should I execute in what sequence to avoid accidentally damaging my repositories history.

You've basically got the right idea, you want to turn one branch into two: a branch with the stuff that's ready to go, and a branch on top of that which is the incomplete changes. For example, a branch might contain a bunch of refactoring and bug fixes interleaved with incomplete features.

A - B - C - D [master]
         \
          R1 - B1 - F1 - R2 - B2 - F2 [feature]

R1 and R2 are refactoring changes. B1 and B2 are bugfix changes. F1 and F2 are incomplete features. What you want is this:

A - B - C - D [master]
         \
          R1 - B1 - R2 - B2 [fixes]
                           \
                            F1 - F2 [feature]

There's two steps there, reordering the commits and declaring the new branch. Reorder the commits with git rebase -i . This will present something like:

pick f37beee Refactor 1
pick 7f238ea Bugfix 1
pick d100dd2 Feature 1
pick aa1124b Refactor 2
pick beadbee Bugfix 2
pick 0123abc Feature 2

Then you literally reorder them in the editor.

pick f37beee Refactor 1
pick 7f238ea Bugfix 1
pick aa1124b Refactor 2
pick beadbee Bugfix 2
pick d100dd2 Feature 1
pick 0123abc Feature 2

Git will rebuild the branch by applying those patches in the new order. You might have to resolve conflicts. For more info, see Rewriting History in Pro Git .

Then you need to declare a new branch. That's just git branch fixes beadbee to declare the fixes branch starting at the last commit you want.

Merge fixes into master normally, and rebase feature on top of master.


But often the commits are not so neatly split out. If you had a commit which contains multiple changes and you only want some of them, you can turn it into multiple commits.

Use git rebase -i as before, but set the commit you want to split up as edit instead of pick .

pick f37beee Refactor 1
pick 7f238ea Bugfix 1
pick aa1124b Refactor 2
pick beadbee Bugfix 2
edit beacd4a Messy commit
pick d100dd2 Feature 1
pick 0123abc Feature 2

Then Git will stop on that commit and allow you to edit it how you like. Use git add -p to add only pieces of the change to the staging area (where you build a commit) and git commit only the partial changes. Do this until each change has its own commit. For example, maybe it changes the name of a method, fixes a bug, but also changes an unrelated method. You'd split those into three commits: one to change the name, one to fix the bug, and one to change the unrelated method.

You can read more about that in Interactive Staging .


I'm not quite an expert in filter-branch or rebase and it looks like I can quite seriously damage the history be misusing them.

Yes, but you can reverse those mistakes, and nothing will affect anyone else unless you git push it. Git doesn't rewrite history, it writes new history and pretends it was that way all along. The old history is still there, for a while, and you can go back to it using things like ORIG_HEAD and git reflog .

Worst case scenario, delete your local repository and clone a new one .

I've marked Schwern's answer as the correct one, since it's the most general case - but it's worth noting that for my case there was a very simple strategy as follows. Since I intended to squash the changes anyway, that means that the procedure can be done in a single commit. It is still necessary to create a new branch, but it can be done quickly using a soft reset. If there are only a small easily manageable number of changes that you want to defer I would recommend this strategy

  1. Checkout the HEAD of A
  2. Branch to a temporary branch
  3. Git soft reset to the "root" of A
  4. Commit changes excluding the ones you don't want
  5. Merge Into develop

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