Reputation: 5254
I have a project with a dev branch and a production branch. Recently I've been working on a big new set of features in dev, so I haven't merged into production for about two weeks. In the meantime, though, there were some bugs that needed to be fixed in production.
For the most part I was able to make the fixes in dev and cherry-pick them into production. Sometimes, however, I would need to fix production by hand, as the fixes were substantially different in the two branches. Point is, the two branches have diverged a fair bit since they split.
Now I want to just merge all of dev into production. I don't care about keeping any of the commits in production since the split, I just want production to look exactly like dev since the split, but don't want to rewrite history before the split.
However, when I try to merge I get dozens of conflicts that I'd prefer not to fix by hand.
What's the best way to force a merge in git? Can I revert my production changes back to the split and then just fast-forward to the dev branch?
Upvotes: 35
Views: 81296
Reputation: 3766
The OP's desire is: "I want production to look exactly like dev since the split, but don't want to rewrite history before the split."
Indeed, you don't need to rewrite history or force push at all! If you are looking to force the code from dev
into main
without rewriting history, just follow these steps: (main
is the production branch here)
git checkout dev
git merge -s ours main
Then create a merge/pull request from dev
into main
or do an ordinary merge of dev back into main:
git checkout main
git merge
This replaces everything in main
with the current contents of dev
, as the OP requests. It does this by recording a merge of main
into dev
without actually changing anything in dev
. But because the main
is there in the merge history of dev
, the pull request from dev
into main
is considered a fast-forward merge.
The whole process looks something like this:
$ git adog
* 890f8d9 (dev) dev continues further with better code
* 3ce948b dev branches from main with great code
| * d5dbf64 (HEAD -> main) main continues further
|/
* 6e24a88 main continues
* f389591 Start of main branch
$ git checkout dev
Switched to branch 'dev'
$ git merge -s ours main -m "Merge branch 'main' into dev, keeping only contents of dev."
Merge made by the 'ours' strategy.
$ git checkout main
Switched to branch 'main'
$ git merge dev
Updating d5dbf64..f28fddb
Fast-forward
tmp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
$ git adog
* f28fddb (HEAD -> main, dev) Merge branch 'main' into dev, keeping only contents of dev.
|\
| * d5dbf64 main continues further
* | 890f8d9 dev continues further with better code
* | 3ce948b dev branches from main with great code
|/
* 6e24a88 main continues
* f389591 Start of main branch
Notice the clean history that is easy for later developers to follow.
This solution is much simpler and cleaner than the current community consensus on this question. There is no need for a dangerous force push
and the git history clearly shows where the intentional overwrite occurred. Be sure to mention in the commit comment that it is a merge -s ours commit, e.g., by saying "Keeping contents of dev only" in the commit message.
There is one condition under which I know git merge -s ours main
fails: That is when the dev
is directly downstream main
, such as if you try to do the -s ours merge twice:
$ git adog
* f28fddb (HEAD -> dev) Merge branch 'main' into dev, keeping only contents of dev.
|\
| * d5dbf64 (main) main continues further
* | 890f8d9 dev continues further with better code
* | 3ce948b dev branches from main with great code
|/
* 6e24a88 main continues
* f389591 Start of main branch
yoder@MSOE-PF3TQZ3S MINGW64 ~/Box/Josiah/msoe/class/s/swe2410/24s2/Labs/tmp (dev)
$ git merge -s ours main
Already up to date.
In this case, the solution is clear -- just continue to the other steps, and they will work as expected. dev
is already considered a continuation of the main branch, so an ordinary merge will do exactly what you want.
-s recursive -X ours
?Some readers will already be familiar with git merge -s recursive -X ours
or have heard elsewhere about it. In cases like this where you want to keep the contents of the current branch only, the command presented in this post, git merge -s ours
is the one on you want. Kelvin has explained in a comment on a different question when you would want -s recursive -X ours
(emphasis added).
Note about git's merge "strategies":
--strategy=ours
[-s ours
] is different from--strategy=recursive -Xours
[-s recursive -X ours
]. I.e.ours
can be a strategy in itself (result will be the current branch no matter what), or passed as an option to the "recursive" strategy (bring in other branch's changes, and automatically prefer current branch's changes when there's a conflict).
In other words, you would use -s recursive -X ours
when you want an automated merge that takes changes from both branches, but brings in changes from the current branch to resolve conflicts. (But personally, in such a case, I would generally recommend just performing the manual merge. If there is conflict, that means both branches have changed code at the same spot in a file. Rarely do you want to keep just one branch's version in such cases.
On the other hand, I use git merge -s ours
a few times a year now. Wanting to make the side branch the main branch without losing the main branch's history is a very common simple operation that you may want to do.
The solution in this answer was previously posted to a different question here. This answer goes into a little more detail which may help users who are performing it for the first time.
Upvotes: 0
Reputation: 15366
This will merge your newBranch in existing baseBranch
git checkout <baseBranch> // checkout baseBranch
git merge -s ours <newBranch> // this will simply merge newBranch in baseBranch
git rm -rf .
git checkout newBranch -- .
Upvotes: -5
Reputation: 14073
You can just push your dev branch onto the master repo production branch:
git push --force upstream-remote dev:production
upstream-remote
may just be origin
if you're on a default clone.
Updated for mod'ed question:
You probably don't want to revert
in the git sense but, yes, that's more or less what you want to do. Something like
git checkout -b merge <split hash>
git merge dev
git push --force origin merge:production
<split hash> was the last commit on production that you want to keep.
Upvotes: 28