简体   繁体   中英

How to force git into “merge conflict mode”?

I have a file foo in branch master . It needs some work so I create branch bar where I do some work. Things look satisfactory so I am ready to merge back into master . Just one problem: I want to keep all of the changes I made in branch bar for future reference, but not all of the changes belong in the master , not right now anyway. But if I do the usual merge, git will not see any conflicts -- since file foo in master hasn't been edited -- and will just do the merge. Is there a way to force git to do this merge as if there were conflicts that I need to resolve? Or is there some other way to place only selected changes in bar into master ?

There are several options. The one that directly answers your question:

git merge --no-commit

will perform the auto-merge to the index and then stop, just as if manual conflict resolution were needed. Of course there will be no conflict markers and no changes "unstaged" / waiting for resolution, but you can modify the commit to look how you want before you commit.

The biggest problem with that, aside from "it's probably a mess to do", is that as far as git is concerned all changes in bar are now accounted for in master . Since you implied that you might want the remaining changes later, that's not good.

What you really want is something like

x --- O --- M <--(master)
       \   /
         A --- B <--(bar)

where O is the original branch point, A has the changes that you want now, and B has the changes you'll want later.

(Or, if you want to avoid the merge commit, you would want

x --- O --- A <--(master)
             \  
              B <--(bar)

instead.)

How best to get to this depends on what you have now. If bar has just a single commit (or, in any event, if you're happy to end up with just one "commit to merge now" and one "commit to save for later"), and if there are no commits on master after O , you could do this:

Starting with

x --- O  <--(master)
       \   
         AB <--(bar)

you do

git checkout bar
git reset --mixed HEAD^

(assuming there's really just a single commit on bar ; otherwise, replace HEAD^ with something like the SHA1 value for commit O or a tag that you've put in place on commit O ). Now you have

x --- O  <--(master)(bar)
                      ^HEAD

with all of the original changes from bar untracked in your working tree. Because all of the changes are in a single file, we need to use patch mode to selectively add changes

git add -p
# select the changes to merge
git commit
git stash

giving you

x --- O  <--(master)
       \
        A <--(bar)
         \     ^HEAD
          B <--{stash}

So next

git checkout master
git merge bar

If you want a merge commit (preserving the topology in which your changes were made on bar ) then pass --no-ff to the merge command. Otherwise, since we're assuming master hasn't diverged from bar , you'll just get a fast-forward leaving

x --- O --- A <--(master)(bar)
             \             ^HEAD
              B <--{stash}

(Conversely, if master had diverged yet you decide to linearlize history, you would rebase A onto master instead of merging...)

Then you can do something like

git branch --delete bar
git stash branch bar

to end at

x --- O --- A <--(master)
             \
              B <--(bar)

If I'm guessing it right, you want only selective changes to be merged from bar into master . In that case, you can use cherry-pick or rebase --onto to accomplish that, as long as the changes you want to bring in master branch are in atomic commits. (If they aren't you might need to rewrite history to do that)

>>> git checkout bar
>>> git log --oneline
b00ac1e third
24097f8 second
ade3073 first

# Let's say you want to bring `third` and `second` commit into master but not first

# Use cherry-pick if you have few commits, that you want to transfer
>>> git checkout master
>>> git cherry-pick 24097f8 # sha id of `second` commit
>>> git cherry-pick b00ac1e # sha id of `third` commit

You need to keep the changes that are supposed to be merged into master and the changes in separate commits than the ones which are not to be merged into master , but to be kept in bar :

o [master]
 \
  A - B - C - D - E - F [bar]
  ^^^^^^^^^
  to be merged

Note that the commits which are to be merged need to come first. If this is not the case you need to reorder them using git rebase -i master bar .

In order to create a real merge commit (as opposed to fast-forwarding master to C ) you need to do

$ git checkout master
$ git merge --no-ff <hash of commit C>

This should result in

o --------- G [master]
 \         /
  A - B - C - D - E - F [bar]

By omitting --no-ff you would get

o - A - B - C [master]
             \
              D - E - F [bar]

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