As we discussed before,
in our GitLab Flow workflow we do not advise worrying about
re-syncing (merge
or rebase
) with the upstream code for
small developments that you can do
in isolation (because they are fast or because you are the only
developer working on that package). When the time comes
to merge your changes, git is smart enough to only take
your changes into upstream and it matters not at all if
other files you did not work on changed in the meantime.
(Actually, even if files you were working on did change
merges are possible if only different parts of the file were
changed.)
For larger, longer-lived developments, rebasing periodically (for example, at the start of the day but latest when you are ready to push your branch) is a very good idea, ensuring that you do not get too out-of-sync with the HEAD of the branch you want to merge to. In case there were changes upstream that conflict with your own changes, you will have to resolve these.
This is the recommended procedure to resolve conflicts:
git checkout [mybranch]
git fetch upstream
git rebase upstream/main
# Fix any conflicts
git push --force origin
What is effectively happening here is git is making your local branch point to the HEAD of main
,
and then replaying your commits on top.
If there are conflicts, the affected files are marked up with the usual conflict
indicators (>>>>>>>
) indicating which pieces of the file came from which versions.
As the developer you now need to decide what the correct solution is
(StackOverflow
has lot of useful guides to understanding the markers,
and github has
a nice step-by-step). For each resolved conflict, git add
the file(s) and then continue the rebase
with git rebase --continue
.
If your branch had already been pushed to GitLab before the rebase, you will need to --force
push
as the last step (because the rebase rewrote its commit history).
If some merge conflict resolution is going badly wrong then you can abort a rebase in progress with
git rebase --abort
This would also allow you to restart with a different conflict resolution strategy.
If you created your local branch off of the wrong upstream commit, for example you started your work from the HEAD of main
but actually wanted to start from the HEAD of 24.0
. You can use rebase
just as described above to replay your commits onto the
correct branch. The only difference is in the third line where you specify the branch you want to rebase onto in addition to the branch you originally branched off from.
git checkout [mybranch]
git fetch upstream
git rebase --onto upstream/24.0 upstream/main
# Fix any conflicts
git push --force origin
If you already have an open MR that needs to be moved between branches, proceed as follows:
rebase --onto
procedure described above and force pushIf you repeatedly merged your branch with the target (e.g. main
) and resolved conflicts, the automatic Gitlab commit squashing may fail, requiring you to manually squash your commits into one.
When possible, this should be done with interactive rebase.
However, in situations where you have many commits to squash, rebasing will require you to solve the conflicts again for every commit, which may be more error prone.
In this case, you can reset your branch with respect to the target, which will retain the diffs locally but remove them from the commit history, allowing you to make a fresh commit.
To ensure that you don’t lose work, you can do the following (commands below):
[mybranch]
as appropriate)[target-branch]
below with e.g.upstream/main
)git checkout [mybranch]
git branch -c [mybranch]-backup
git reset $(git merge-base [target branch] $(git rev-parse --abbrev-ref HEAD))
git diff [mybranch]-backup
git commit -a
git push --force origin [mybranch]
If at step 3 you find that the diffs do not match, do not proceed! You may have to check the target branch, update your development branch further, etc. Once you have successfully reset and squashed the commits, you can delete the backup branch.
See this StackOverflow thread for more details.