git rebase after previous git merge
I have the following situation:
- I created a
clone
(Y)开发者_如何学Go from a main repository(X), because there were many people working on Y we didn't do anyrebase
but onlymerge
s. When we want to deliver(push
) Y to X we would like to do arebase
in order to have things nice and clean
The problem is that when doing rebase
we are asked to do all the merges that we already did in the previous merge
steps. Is there a solution to this, beside the one that means actually re-doing the merges?
I expected it to be pretty straightforward since we already solved the conflicting merges.
git merge --squash
is now my preferred way of rebasing after a large amount of work and many merges (see this answer). If the branch you're working on is called my-branch
and you want to rebase from main
then just do the following:
git checkout my-branch
git branch -m my-branch-old
git checkout main
git checkout -b my-branch
git merge --squash my-branch-old
git commit
Rebasing to get a "clean" history is overrated. The best way if you want to preserve history is just to do the merge instead of a rebase. That way if you ever need to go back to a revision, it is exactly the same as the one you tested during development. That also solves your issue about the previously solved merge conflicts.
If you don't care about preserving history, you can create a new branch off of master, check it out, then do a git read-tree -u -m dev
to update your working tree to match the dev
branch. Then you can commit everything into one big commit and merge it into master as normal.
Two remarks:
- you can rebase your own (non yet pushed) work as many time as you want on top of newly fetched commits.
- You could avoid the merge conflicts (during rebase) if you had activated
git rerere
, which is done for this kind of situation.
See more atgit rerere
.
You can take all of the changes in your branch and put them into a new commit in master
with the following:
git diff master > my_branch.patch
git checkout master
patch -p1 < my_branch.patch
Then stage your files and commit.
Regarding the replay of merge conflicts, you can use git rerere to maintain a database of how merge conflicts have already been solved, so that performing a rebase that results in the same conflicts will have the laborious parts done for you automatically.
https://hackernoon.com/fix-conflicts-only-once-with-git-rerere-7d116b2cec67
git config --global rerere.enabled true
The one thing to look out for is that if you resolved something incorrectly it will be automatically borked for you next time too, and you may not really realize it.
More formal documentation here: https://git-scm.com/docs/git-rerere
I found @sky's answer helpful. And I made it into a squash-rebase function.
Assuming you are on your-branch
, what this does is create a branch your-branch-tmp
that is the result of doing merge --squash
on main. It also preserves all the commit messages and lets you edit. Importantly, it also leaves your current branch alone.
After running squash-rebase, if you're satisfied with what it did, you must do a hard reset to the tmp branch. Then in effect you've completed a squash rebase.
function squash-rebase() {
MESSAGES="$(git log $(git merge-base main HEAD)..HEAD --reverse --format=%B)"
SRC_BRANCH="$(git_current_branch)"
TMP_BRANCH="$(git_current_branch)-tmp"
echo "creating temp branch $TMP_BRANCH from $(git_current_branch)"
git checkout -b $TMP_BRANCH main # create tmp branch from main and checkout
git merge $SRC_BRANCH --squash
git commit -m "$MESSAGES" -n
git commit --amend -n # just so you can edit the messages
git checkout $SRC_BRANCH
echo "IMPORTANT: run git reset --hard $TMP_BRANCH to complete squash rebase."
echo "First, though, you may run git diff $TMP_BRANCH to make sure no changes."
echo "Finally, delete the tmp branch with git branch -D $TMP_BRANCH"
}
Note: assumes you are using oh-my-zsh, otherwise you may have to find another way to get current branch name.
精彩评论