Start with git reflog
. The result will look something like this (but with a lot of rebase -i
entries):
aa4e140 HEAD@ {0}: rebase -i (finish): returning to refs/heads/branch aa4e140 HEAD@ {1}: rebase -i (squash): c1-c3, squashed 3a422a7 HEAD@ {2}: rebase -i (squash):
This last rebase
line has a SHA1 commit, which was HEAD before you did rebase -i
. You might want to stick a temporary branch or label on it, although this is not technically required. Here I put on it a lightweight tag called temp
:
git tag temp 283263c
Now you can simply run git log temp
or (to limit it to only those that you recreated):
git log temp --not HEAD
or
git log temp ^HEAD
(these are two ways to write the same thing) 1 or:
git log -n 20 temp
(this uses the fact that there are 20 commits in rebased HEAD~20..HEAD
, which is only true if the original story was linear and, of course, depends on this part ~20
).
When you are done with the timestamp, delete it:
git tag -d temp
The way this works is that rebase -i
doesn't actually remove any , it only adds a new one . (This is actually true for almost every git command. An exception is the garbage collection process, which deletes objects without references.)
The full set of commit-and-branch arguments for rebase (that is, ignoring important flags such as -i
and -p
): start-point, destination, and branch-name. The documentation will slyly mask :-) the first two of them are “upstream” and “on” (seriously, “on” is not a bad name, and I will stick to it below, but the “upstream” is misleading in some cases ) All commits after the starting point, right up to the tip of the branch, are copied (or omitted or crushed or something else), essentially picking them up in cherry, one at a time, as a new commit is added to the branch grown for the purpose. If the original commit tree looks (partially) as follows:
old -- start-point -- c2 -- c3 -- c4 <-- branch \ onto -- c6 -- c7 <-- another-branch
then rebase starts by copying (or “playing back changes from”) c2
(the first commit after the start point) to an identical change (but with different commit information), c2'
, placing it so that onto
is its parent:
old -- start-point -- c2 -- c3 -- c4 <-- branch \ onto -- c6 -- c7 <-- another-branch \ c2'
Then it copies c3
to the new ( c3'
) version with c3'
parent c2'
, etc. When everything is done, it will undo the branch
label from c4
and insert it, indicating the last new commit ( c4'
) instead:
old -- start-point -- c2 -- c3 -- c4 <-- [no label] \ onto -- c6 -- c7 <-- another-branch \ c2' -- c3' -- c4' <-- branch
Note that the old commits ( start-point
, c2
, c3
and c4
) are still there, they no longer have a branch mark. Also note that in this particular case (using the --onto
argument, as shown with this particular commit tree), a commit named start-point
itself becomes "invisible" (in the same sense as c2
through c4
) like this as it no longer has a branch label or a label pointing to it - unless, of course, you set it before or after you rebase. (Usually there are no such “missed” commits. Of course, in the interactive permutation you can skip it, but then it's clear enough for you to do this.)
Unmarked ("invisible" or "hidden") are blocked until they remain in the reflog (90 days, unless you change the default setting). To make them stick even longer, set a label - for example, a branch or tag name - to point to them. This is what I am doing with the temp
tag above. Now they are visible again, and they can be easily seen in any commit-tree handler file, such as gitk
or git log
.
(If you ask the interactive rebase to “squash” several commits, it simply puts the changes together into one fixed one. Since each new commit is a copy - “replay”, it’s as if simple: just save putting more changes together, fixing them as one compressed-commit .If you omit the commit, it just skips it, copying only the remaining ones. If you re-order commits, it just copies them in the new order.)
1 There are many ways to write this down. In fact, git log HEAD..temp
does the trick, even if it doesn't look right, somehow. :-) In these versions, everyone assumes that HEAD
is still the name of the branch on which rebase was enabled. If you were in the squiggle
branch, you can use git log temp ^squiggle
as soon as HEAD
moved elsewhere, for example.