0

Let's say I have a repository with branch master. I checkout a branch feature-branch and started working on it. I raise a PR and push 3 commits in the PR over time. Let's say the master branch moves forward with 10 commits during this time and I have to rebase my PR to the tip of master. I follow these commands for that:

git checkout feature-branch
git rebase master
git pull
git push

These steps do put my PR forward but now my PR contains 3 + 10 = 13 commits, which makes getting it reviewed difficult. Any solution for this?

abhishek
  • 53
  • 7
  • Why is the reviewing difficult? Most git servers are capable of processing the differences and showing them. In the PR interface you should only see the changes introduced by your new 3 commits, since the other 10 are the same both in master and your branch. – HiroCereal Oct 04 '22 at 16:50
  • What is the upstream branch? a branch with the same name of the feature branch in the remote? – eftshift0 Oct 04 '22 at 16:52
  • And now I am also wondering what is the _target_ branch of your PR. Is it master? – eftshift0 Oct 04 '22 at 16:53
  • @eftshift0, yes for both the questions. – abhishek Oct 04 '22 at 17:02

2 Answers2

3

Ok.... as LightBender is saying, you have a problem in your recipe because you are merging the old branch that you are trying to rebase so you are keeping the old commits (not to mention that I am not seeing that you are pulling changes into your local master).

The way you should go about it normally is:

git checkout master
git pull # bring changes from upstream master into your local master
git checkout feature-branch
git rebase master # this is how you move your branch on top of master, keeping it straight
git push -f # assuming that the upstream branch as been set before
# if the upstream branch is not set, then write git push -f origin @ -u
# after that, you can just run git push -f (on subsequent pushes)

Now.... how do you get out of the mess that you are in? Well.... I am not completely sure this will work, but it will probably. Assuming that you just came of of the last pull that you were doing there:

git checkout feature-branch~ # go to the first parent of that merge.... you should be on top of 3 commits that are straight from main, no merges
# If that is the case, just place your branch over here:
git branch -f feature-branch
git checkout feature-branch
git push -f

If actually, that is not the case, then you will need to get yours hands dirty and cherry-pick the 3 revisions that you want to have in your branch.... or rebase interactive, which is nice, but can be a little bit scary... which cherry-pick it should be simple:

git checkout --detach master # let's get ourselves on top of master, but let's not move it around
git cherry-pick first-commit-of-the-branch-you-want #you probably have more than one _first_ commit in the messy branch, any of those _firsts_ would do, use its commit ID
git cherry-pick second-commit-of-the-branch-you-want # same thing with the second
git cherry-pick third-commit-of-the-branch-you-want
# if this is your branch and it looks good (straight line from master, 3 commits, no merges):
git branch -f feature-branch
git push -f

And you got out of hell. Now you can start using the recipe as I described in the beginning.

eftshift0
  • 26,375
  • 3
  • 36
  • 60
  • An excellent answer, but I'll add the note that when you force push, anyone else who has the original version and does a pull will merge this new history with their existing copy and create the exact same mess all over again. So, force push requires some level of coordination and should be used sparingly and with great caution. – LightBender Oct 04 '22 at 17:26
  • I did not mention that I have already pulled the changes of the upstream master branch into local just like the way you mentioned. So my full sequence of commands has been this: `git checkout master` -> `git pull` -> `git checkout feature-branch` -> `git rebase master` -> `git pull` -> `git push`. So the only difference I see with yours is that I have an extra 'git pull' and 'git push' without -f. So if I'd have made these two changes, it should work fine? – abhishek Oct 04 '22 at 17:28
  • @abhishek, no it should not... think of vanilla `git pull` as being an alias for `git fetch; git merge origin/mybranch` to see why. It's essentially saying, get whatever version of this branch is on the server and merge it into my local copy of that branch. – LightBender Oct 04 '22 at 17:32
  • Good point about being careful with git push -f. Given that it is a feature branch, I am assuming that the OP is the only one playing with it and so ot should be no problem force-puahing. – eftshift0 Oct 04 '22 at 17:38
  • I will kindly disagree with @LightBender and say that _yes, you got it_. They are right that rebase does require some finesse. I would actually advice you to sit down and _study_ what is going on instead of just developing muscle memory to reproduce the recipe. Using our neurons makes it a little slower, but you will be able to _understand_ what (and why) you are doing which will go a long way in sticky situations. (And the recipe could be shortened a little bit if you worked with origin/master directly but that's not what _most_ people do, so... I for one avoid local master, just like LB). – eftshift0 Oct 04 '22 at 17:46
  • @eftshift0 Nothing to disagree about :) my comment was in response to the original unedited comment which read, roughly, "so `git pull` shouldn't be there?". I agree that git recipes are often dangerous as they get people into trouble. I also agree that if you're working on a feature branch *in theory* it should be safe to git push, sadly in practice all it takes is the maintainer having previously pulled the branch to test the patch to accidentally pollute the history. – LightBender Oct 04 '22 at 18:36
  • @eftshift0 , thanks for the answer. I will try to understand git more, but for now, your recipe worked. The reason I used `git pull` in the first place is after I did `git rebase master`, I got this message displayed `Your branch and 'origin/feature-branch' have diverged, and have 10 and 3 different commits each, respectively. (use "git pull" to merge the remote branch into yours)`. Not knowing what it actually means I got compelled to execute that command. – abhishek Oct 05 '22 at 07:57
  • yep... the message is to be expected because when you _rebase_, you are setting yourself on a completely different history from the original commits that are already in the remote.... so if your branch is X commits and you rebased, it should tell you that the upstream is X commits behind (the ones that are in the remote) and X ahead (the ones in your new branch).... it probably told you something closer the first time you rebased (3/3) but as you kept on _merging_, you increased those numbers.... and if you add commits while working, those numbers can differ, but it should always _make sense_. – eftshift0 Oct 05 '22 at 08:21
1

As a general rule, rebasing branches that have already been pushed is a bad idea. In this case, after you rebase—creating new commits on the tip of master—you're pulling (and merging) the original feature branch with all its commits into your rebased branch, then pushing the whole thing up.

The merge-base remains the same as a result, with all of the commits since that point now being "on your branch" including two copies of your commits.

They should have no issues in reviewing because generally they'll be looking at the diff, not the individual commits. You have, however completely defeated the purpose of a single-line history (the usual reason for requiring rebase over merge) because you've introduced a merge.

In these situations, it's usually best practice to create a new branch if you've already pushed the code and point the PR at the new branch to avoid cross-contamination between the two versions of the history.

LightBender
  • 4,046
  • 1
  • 15
  • 31