Reputation: 10375
I was trying to squash some old commits back together, that have already been pushed to master. I'm the only developer though so it's fine to force update the repository. I started with this:
% git log --oneline --decorate --graph --all
* 4b2fec5 (HEAD -> master, tag: v6.2.5, origin/master, origin/HEAD) Version 6.2.5 - Fixed MegaMillions payout bug
* b0ae8a9 (tag: v6.2.4) Version 6.2.4 - Handles large size text
* f4763bd (tag: v6.2.3) 6.2.3
* 3f02a27 (tag: v6.2.1) Version 6.2.1
* 7703d55 (tag: v6.2.0) Updated revision
* a2c6366 Removed legacy NSNotification stuff.
* 1e3b359 Changes UITextFieldTextDidChange to use an onPost handler.
* 35b910b Turns off the request for app store rating.
* 5c20d46 Put the detail segue back in.
* f90722d Got rid of stack view from MasterViewController
* eb34f07 Removed unnecessary null coalesce operator.
* c969126 Changed for Megamillions new payout structure.
* 2a32838 Version 6.2.0
* efb33b0 Initial commit
* 13a5b0e (tag: v6.0.1) 6.0.1 for app store
* 45ab2ba Downloads new numbers after creating a new ticket now.
* 2d60f30 Cancel button didn't have an exit segue on the master.
* 2185112 Changed to static size text for loser
* 872f408 Initial Commit
And now I want to squish everything between the v6.2.0 tag and the Version 6.2.0 commit into a single commit, so I did a
git rebase -i efb33b0
which said it was successful. But now I'm left with the following:
% git log --oneline --decorate --graph --all
* 0fb2623 (HEAD -> master) Version 6.2.5 - Fixed MegaMillions payout bug
* 81d3553 Version 6.2.4 - Handles large size text
* d7d7578 6.2.3
* eae8973 Version 6.2.1
* ded0fe9 Version 6.2.0
| * 4b2fec5 (tag: v6.2.5, origin/master, origin/HEAD) Version 6.2.5 - Fixed MegaMillions payout bug
| * b0ae8a9 (tag: v6.2.4) Version 6.2.4 - Handles large size text
| * f4763bd (tag: v6.2.3) 6.2.3
| * 3f02a27 (tag: v6.2.1) Version 6.2.1
| * 7703d55 (tag: v6.2.0) Updated revision
| * a2c6366 Removed legacy NSNotification stuff.
| * 1e3b359 Changes UITextFieldTextDidChange to use an onPost handler.
| * 35b910b Turns off the request for app store rating.
| * 5c20d46 Put the detail segue back in.
| * f90722d Got rid of stack view from MasterViewController
| * eb34f07 Removed unnecessary null coalesce operator.
| * c969126 Changed for Megamillions new payout structure.
| * 2a32838 Version 6.2.0
|/
* efb33b0 Initial commit
* 13a5b0e (tag: v6.0.1) 6.0.1 for app store
* 45ab2ba Downloads new numbers after creating a new ticket now.
* 2d60f30 Cancel button didn't have an exit segue on the master.
* 2185112 Changed to static size text for loser
* 872f408 Initial Commit
How do I get rid of that extra "branch" that's in there? Basically it looks like that entire branch to the right just needs to go away, so that it only follows the main path...and somehow change the tags over too.
Upvotes: 0
Views: 395
Reputation: 489848
The short answer is that you don't / can't. (But see exception below.)
What git rebase
does is copy some commits to make new, different commits, and then abandon the original set of commits in favor of the new-and-improved commits.
The problem that comes in at this point is that abandoning those commits works fine if and only if the branch you are "on" (as in git status
says on branch master
) is the only reference to those particular commits. If there are additional reference(s) to some or all of the original commits, those references don't change!
This is true even, or maybe especially, if those references are in some other repository. The latter can occur if you have git push
-ed the commits to some other repository and had that other repository set some of its references to remember those commits. This is why it's generally not great form to amend or rebase commits that you have already git push
ed.
(Small side note: a reference is Git's generalization of branch and tag names. There are more kinds, but these two are the most familiar ones. A branch name is just a reference whose full name starts with refs/heads/
: the rest of the name is the branch name. So refs/heads/master
is the full reference name of branch name master
.)
If:
then (and only then) it is perfectly fine to rebase shared commits like this. To do that, after copying the old, slightly-defective commits to shiny new commits, you must find all references (in all repository copies!) that use the old commits, and move them so that they refer instead to the shiny new commits.
In your case, I count at least six (6) such references:
refs/tags/v6.2.0
);master
on another Git over at origin
, which your Git is remembering for you as your origin/master
(that's their refs/heads/master
and your refs/remotes/origin/master
).If you convince them to move their master
(by force-push for instance), your own Git's memory in your refs/remotes/origin/master
reference will change automatically. If you convince them to move all five of the other tags, and you move all five of your tags, that will eliminate all six of these names-for-the-old commits. Git will then stop showing you the old commits, and eventually they will truly expire and be garbage-collected away.
To force-push your new master
you can just git push --force origin master
(but hold off on it for a moment). That still leaves you to re-adjust each of your tags, one by one, to point to the shiny new copied commits—each one has a different new, shiny hash ID—and then force-push those tags, with git push --force --tags
, and hope that they (whoever "they" are on origin
) allow all these force-pushes. You can push them all at once with git push --force --tags master
.
The problem with changing other people's references lies in getting all of them. Otherwise the old references, and hence the old commits, can come back to haunt you. But if your Git and the Git on origin
are the only copies of this repository, and you control both of those, you can do what you want.
Upvotes: 1