A colleague of mine created a feature branch feature/dotnet6
from our main branch developing
to work on migrating some of our .NET Framework projects to .NET 6.
His feature branch has been around for a while now and is 19 commits ahead
and 218 commits behind developing
.
A few days ago I started migrating one of our API .NET Framework projects to .NET 6.
In preparation for this I created my own feature branch feature/dotnet6-web-api
from my colleague's feature branch feature/dotnet6
as I require some of the work he has already done.
After doing some work on my feature branch feature/dotnet6-web-api
it is now 46 commits ahead
and 218 commits behind developing
.
Once the work on my branch is completed I will create a PR to merge my feature branch feature/dotnet6-web-api
into my colleagues feature branch feature/dotnet6
.
At which point we will have a single feature branch feature/dotnet6
that will contain ALL of the .NET 6 migration work which can then be PR'd and merged back into our main branch developing
.
I would like to update both my colleague's feature branch feature/dotnet6
and my feature branch feature/dotnet6-web-api
with the work that has been committed to our main branch developing
since the feature branches were created.
I thought that I would checkout my colleague's feature branch feature/dotnet6
and rebase on developing
.
e.g.
git checkout developing
git pull
git checkout feature/dotnet6
git rebase developing
git push --force
Once that was complete I would checkout my feature branch feature/dotnet6-web-api
and rebase on my colleague's feature branch feature/dotnet6
.
e.g.
git checkout feature/dotnet6
git pull
git checkout feature/dotnet6-web-api
git rebase feature/dotnet6
git push --force
My concerns are:
- By rebasing my colleague's feature branch
feature/dotnet6
will that effect him? - When it comes to merging the final version of my colleague's feature branch
feature/dotnet6
into our main branchdeveloping
will this cause problems as the commits from the rebase will have different SHA's to the "same" commits already in our main branchdeveloping
?
Would I be better off merging our main branch developing
into both of our feature branches instead or rebasing and the potential issues it could cause?
CodePudding user response:
TLDR: stick with merges
As a general rule, you should never rebase shared branches. To more directly answer your questions:
- Yes, rebasing will affect him. The way a rebase essentially works is it creates a temp branch off the branch you're rebasing, resets the head of your branch to the HEAD of origin/developing (or whatever commit you tell it to) and then cherry-picks your commits from the temp branch so they are on top. Cherry-picking changes the commit hashes because it creates a new commit. This means that when your colleague goes to pull, he'll have mismatching commit hashes which will cause quite a bit of confusion for git. If he has local changes of his own that he hasn't pushed, that can cause problems as well.
- One possible way around this is
git pull --rebase
which performs a rebase instead of a merge. It also attempts to look at differences in the code so it can identify commits that have the same changes but different hashes. I've used it before to share branches with my colleagues and it generally works. That said, for a long-lived branch like your colleague's, I wouldn't risk it.
- Potentially. If you did a rebase this would normally not affect the commit hashes on
origin/developing
unless you specified a base prior to it. If this were to happen, git would likely show them as conflicts since it won't know whether to go with the changes indeveloping
or your changes.
It's much safer for both you and your colleague to do the following:
git checkout feature/dotnet6
git pull origin developing # This will merge the latest developing into your branch without the need to checkout your local copy of developing and pull.
git push
git checkout feature/dotnet6-web-api
git pull origin developing
git push
Git is excellent at merging, and this will also allow both of you to merge into each other later.
If you really want a linear history though, the two of you could do a rebase after merging your code together but before raising the PR. That way the history is linear, the merges are flattened and you won't step on each other's toes during development. However, the one downside of this approach is git won't be aware that you merged your branch into your colleague's since git basically just checks to see if the HEAD of one branch is present in another to determine if they're merged. Changing the commit hashes throws this detection off.
But that can get a bit complicated. Honestly, just stick with merges unless your project maintainers/managers have a rule against merge commits.
CodePudding user response:
As a rule of thumb:
Generally, it is frowned upon to rebase shared branches.
Perhaps though the general case does not apply to you, since I would also suggest:
Although it is frowned upon to rebase shared branches, as long as everyone sharing the branch knows what to do if the branch is rebased, then it is probably fine to do it as long as everyone is informed.
It sounds like the only person that would be affected by branch feature/dotnet6
getting rebased, is you, and by the time you finish reading this answer you'll "know what to do", so I'd say go for it (and this is what I would probably do in your shoes as well).
You really only need to tweak the second half of your plan. The commands where you intended to rebase the second feature branch onto the first feature branch simply needs to be changed from:
git checkout feature/dotnet6
git pull
git checkout feature/dotnet6-web-api
git rebase feature/dotnet6
git push --force
to:
# Assuming you have a copy of feature/dotnet6 before it is rebased,
# skip the part where you update your local copy of feature/dotnet6
git fetch
git rebase feature/dotnet6 feature/dotnet6-web-api --onto origin/feature/dotnet6-web-api
git push --force-with-lease # using --force-with-lease is usually preferred
The problem with regular rebase is you will also be replaying all of the old commits from feature/dotnet6
that are no longer on the new rebased branch. It's possible they would all simply fall out because they are empty, but it's also possible this will cause pointless conflicts during the rebase. By using the --onto
option you can specify exactly where to start placing the commits to be replayed, and in this case we will place them onto the new version of that branch that is present in your remote tracking branch. This method takes advantage of the fact that you have previously checked out feature/dotnet6
and branched off of it, before that branch was rebased.
For completeness, here is another way you can accomplish your goal, using a slightly different tweak to the rebase command:
git checkout feature/dotnet6
git fetch
git reset --hard @{u} # Note @{u} is shorthand for origin/feature/dotnet6
git checkout feature/dotnet6-web-api
git rebase --fork-point feature/dotnet6
git push --force
The reason for the reset
instead of the pull
is so you don't merge the new copy of the commits with the old ones you already have on the branch. Note that this is identical to first deleting the branch, fetching, and then checking it out again. At this point your local copy of feature/dotnet6
will be identical to the remote copy.
Now for the rebase; you'll notice I added the option --fork-point
, which tells Git to look into your reflogs of feature/dotnet6
to see if your copy of that branch has been rewritten, and if so (which it was), try to determine a better starting point for the rebase. Note --fork-point
only works here if you previously had checked out a copy of feature/dotnet6
prior to it being rebased, and presumably you did.
Additional Notes:
- The fact that you are willing to rebase both of these feature branches makes me question the value of using a PR to merge your web-api branch into the dotnet6 branch. You both are essentially collaborating as is, so it might make more sense to either wait until your branch is finished and just PR it into
developing
, or once the dotnet6 branch is done it could be PR'd intodeveloping
and then you can repeat this rebase process one final time, but the last time use--onto origin/developing
after that PR is completed. All this being said, it really doesn't matter; if you want to PR your branch into the dotnet6 branch it certainly doesn't hurt anything. - I like rebasing. My personal preference is to always rebase unless there's a good reason not to. For example, I would probably almost never rebase
developing
because there's a good reason not to. And if multiple people were sharingfeature/dotnet6
I probably wouldn't rebase that either. It's pretty easy to just mergeorigin/developing
intofeature/dotnet6
and be done with it. Even if you don't rewritefeature/dotnet6
you can still rebase your feature branch onto it since it's not shared. Again, for the context of this question though I'd probably rebase both branches, since you now know how to handle the fancy rebase afterwards.