3

I got a classic situation - master and multiple branches. I need to merge changes (many changes through multiple commits) into master, but I want to ignore any changes that were made in master itself. I want master to be a clone of my branch.

I know that I could just checkout every file from branch into master and commit changes, but that won't make graph pretty :)

How can I achieve that? Solving merge-conflits is not enough, because there are still some changes that were auto-merged. Do I have to remove them manually or is there some git merge --magic option that could help me?

ADD: I'd like files to be exact copy between branch and master (not commit history), but I'd like to have all that squished to single commit.

mrozo
  • 53
  • 6

2 Answers2

9

The answer depends on whether you care about preserving the history of the master branch. If you use a git reset --hard, you'll lose the history of master that diverged from the feature branches.

If you want to maintain history, a better trick would be to do it as two merges, with the first merge using the -s ours merge strategy.

git checkout feature
git merge -s ours master
git checkout master
git merge --ff-only feature

The first merge, git merge -s ours master will create an empty merge commit on feature. The code will be an exact copy of what is on feature.

The second merge will fast-forward master to the empty merge commit. The end result will be two branches with the exact same code and preserved histories. The code will be an exact copy of what was on feature prior to the two merges.

As far as I know, there isn't a single option/command to perform this operation. -Xtheirs is not truly a merge strategy, it's just an option for the default merge strategy that tells it how to resolve conflicts.

git merge -s ours exists specifically for the operation of merging the history of obsolete branches and throwing away the changes. There is no -s theirs strategy.

The trick here is that you want to throw away the changes on master, but still keep the branch afterward. Normally you would just delete the obsolete branch after doing an -s ours merge.

TonyArra
  • 10,607
  • 1
  • 30
  • 46
  • 1
    You saved my butt royally with this! Thank you so much. Your description was clear, and solution perfect! I just wanted to drop you a big thank you @TonyArra – BoomShadow Nov 04 '22 at 04:49
2

The key here is to realize that Git stores snapshots (which Git calls the tree of a commit). There are no changes in any commit, just a snapshot and some metadata. If you see changes, that's an illusion brought about by taking two commits and comparing them. Think about an old style movie film: each frame of the film holds a snapshot image, frozen in time, and we only see movement because we look at 24 carefully-sequenced frames every second.

What this means is that you can construct a commit "by hand" at any time if you like. If you want the snapshot of commit a123456 to be the next snapshot on the current branch, you simply make a new commit whose parent is the current commit, but whose tree is from commit a123456:

git commit-tree -m "insert your message here" -p HEAD -p a123456 a123456^{tree}

or similar. The two -p arguments here make this a merge commit, so that this is the result of the missing -s theirs merge strategy. This new commit—Git prints its hash ID to stdout; you must grab it from there—is not yet on any branch, so you now need to update the current branch name, using git merge --ff-only or git update-ref.

See jthill's answer to Is there a "theirs" version of "git merge -s ours"? (note that the question itself is now a bit confused due to December 2008 edit to the original October 2008 question, and some answers are about -X theirs, which is different).

torek
  • 448,244
  • 59
  • 642
  • 775
  • nice - I hadn't seen this before. It has the bonus of not actually affecting the topic branch. – TonyArra Apr 14 '22 at 01:35
  • ~~@torek that's a great solution, but after I did `git commit-tree` as you presented, `git merge --ff-only` returns "Already up to date." And calling `git update-ref` return help-page. I also don't see any changes in `git log` after all doing all of that.~~ **EDIT** I didn't notice that `git commit-tree` returned new commit number, my mistake, sorry for ping :) Everything is great now :) – mrozo Apr 14 '22 at 12:46
  • 2
    @mrozo: added a note about that. You should click through to jthill's answer though. – torek Apr 14 '22 at 13:30