0

Here's a picture of the last part of my simple straightforward history (from SourceTree, time marches from bottom to top):

enter image description here

So, in this situation, I asked SourceTree to do an interactive rebase on the children of the bottommost commit ("Xcode 10.2, Swift 5"), because I wanted to collapse the next two commits ("darn, property list encoding" and "v1.1.1build24") into a single commit. There is no remote so I'm free to rewrite history and keep it clean and informative.

But when I did that, a whole bunch of new commits were created branching off from "Xcode 10.2, Swift 5" - copies of the existing chain of commits. I couldn't understand why, and it took me a lot of scurrying around to clean it up.

It wasn't exactly a new "branch" (as my question title has it); but the existing commits up thru "v1.1.2build28" ended up on their own dead-end branch line, and master now had these new commits in it, duplicating those commits but without the tags and with today's date.

My question is: why did that happen, and what should I have done instead?

matt
  • 515,959
  • 87
  • 875
  • 1,141

2 Answers2

1

It's done as expected.

"darn, property list encoding" and "v1.1.1build24" are squashed as one commit. Git implements this squash by creating a new commit, whose parent is "Xcode 10.2, Swift 5" and whose changes are equivalent to those of "darn, property list encoding" and "v1.1.1build24".

The parent or the base of "v1.1.1build25" was "v1.1.1build24". Now a new commit has replaced "v1.1.1build24" and "darn, property list encoding". As a result, the parent of "v1.1.1build25" needs to be the new commit. Otherwise, the commits above "v1.1.1build24" will no longer exist on master. In order to move "v1.1.1build25" from the old base to the new base, Git also generates a new commit for "v1.1.1build25", whose parent now is the squashed commit and whose changes are equivalent to those of "v1.1.1build25". Accordingly, all the rest descendants are recreated because their parents/bases are changed.

Git creates new commits as if the old ones are replaced, but the old ones still exist in the repository except that they are no longer reachable from master. However the tags like "v1.1.1build25" and the branches like "temp" still point to the old commits. Since these old commits are not on the current master now, these tags and branches are not seen on master either.

ElpieKay
  • 27,194
  • 6
  • 32
  • 53
  • Brilliant description. So it was the tags further up the chain that did me in! So what should I have done instead? – matt May 06 '19 at 03:52
  • @matt It seems these commits and tags have been published. If so, it's not a good idea to rewrite the history like this. If you insist, you need to move the tags and branches to the new commits, and inform all the contributors of the changes. And they need to update their local branches and tags. Big trouble. If anything goes wrong, the old history will be mixed with the new history, which makes rewriting the history meaningless. – ElpieKay May 06 '19 at 03:56
  • “There is no remote so I'm free to rewrite history and keep it clean and informative.” There are no contributors. – matt May 06 '19 at 04:29
  • @matt Sorry, I didn't notice it. Use `git tag -f $tagname $newcommit` to move tags, and `git branch -f $branchname $newcommit` to move temp and testmigration3 if necessary. – ElpieKay May 06 '19 at 06:23
  • But that doesn’t solve the problem of being left with a dozen commits appearing twice. – matt May 06 '19 at 06:28
  • @matt If you move the tags and branches from the old commits to the corresponding new commits, the old commits should be hidden unless you view reflogs. – ElpieKay May 06 '19 at 06:32
  • But the dates on the new commits are wrong. That’s why in the end I deleted them. My hope next time is to prevent this duplication in the first place. – matt May 06 '19 at 06:59
1

Nothing wrong. This is exactly how git works. Git rebase is nothing else than a small robot which makes a new version of a bunch of commits.

It is simply bad wording that some people, even the docs, speak of "rewriting history". Rewriting history isn't possible at all in git - what once has been committed, can never be changed again. git rebase only helps to copy an old history to a new version of the history. However - if the old history, as it is in your case, has other tags or branches, they will continue to point to the old history even after rebase.

So it is totally normal that a git repository consists of lots of dead branches. Sometimes you can see them (because other tags/branches still refer to them), sometimes you can't - but you can always refer to them if you happened to memorize the SHA ID. (Once in a time, git's garbage collector will come along and collect the really dead parts of the history where nothing points at).

This is great because you cannot make any mistakes (1). You can always simply git reset --hard to some point in history, because history can never be changed in git. If you tried a rebase, a merge or a whatever and it didn't work like expected - if you memorized the SHA you were before (or if you tagged it), you can go back there. (If you didn't, you can find it by git reflog). And you always have a couple of days to do so before garbage is collected.

Clearly this is bad for you, because you have a lot of tags you want also to point to the new history. I googled a bit, there may be tools to take them over to the new branch, but I never used such a tool. Try it out or take it as a fact, that git has an unchangeable history. Perhaps at some day, you will start to like that you can experiment around without any risk :-)

(1) The only way to really lose work is to make changes in your working directory and to not commit them. If you have uncommitted changes and make a reset --hard, they will be permanently lost and nobody will warn you.

Marcus
  • 617
  • 3
  • 8