You can get C
and D
back [edit: if you had "lost" them in your original repo, but I see you haven't]. Use git reflog
to find them by ID:
$ git reflog
or:
$ git reflog master
(if you were on master
and just want to see the changes that happened there), etc.
Once you find commit D
, stick a branch label on it. Let's say that the output from the above makes it clear that HEAD@{3}
is the one you want. Then:
$ git branch recover_my_stuff 'HEAD@{3}'
or:
$ git branch get_d_back 0123456
to do it by number (and with a different name and so on). You can run gitk HEAD@{3}
and gitk 0123456
before this, or git log HEAD@{3}
, etc., to double check whether that's the one you want. Once you have a branch label on it, git branch
and gitk --all
will show it to you again.
Edit: since you still have them in the original repo, let's work in there instead. First, your master
currently has the a - b - C - D
sequence. Let's rename this branch to temp
, then use git fetch
to get up to date with the remote repo, and create a new master
that tracks origin/master
:
$ git branch -m master temp
$ git fetch origin
$ git checkout -b master --track origin/master
(Here, temp
—since it's just a rename of the original master
—will "track" origin/master
too, so git will tell you that it's different from origin/master
, being both ahead and behind by several commits. But you don't need to care, you can ignore these notices from git. If you like, you can make it stop "tracking" by editing the git config, but it will all be harmless either way.)
OK, so, let's say you've got the above done, and gitk
now shows (in a more pretty graphical form) this. I'll use the branch label temp
for the recovered stuff, and assume the rest is all on master
(well, you said it was :-) ):
a - b - E - f <-- HEAD=master, origin/master
\
C - D <-- temp
In general it's not nice to "rewind" branch labels on other people (it's OK to do it to yourself :-) ) so let's leave E
in there. You can at any point git revert
it to add a commit that simply un-does whatever E
did. All you need to do now is graft copies of C
and D
on top of f
. This is what git rebase
does: it copies the changes in the original commits, to new commits. Then it moves the label.
$ git checkout temp
$ git rebase master
Now you'll have this:
a - b - E - f <-- master, origin/master
\ \
\ C'-D' <-- HEAD=temp
\
C - D [no branch label]
(since C and D have no label, they will disappear from gitk --all
. You can put another label back on them again, if you want to see them, but you don't need them anymore now that you have the copies.)
Now all you have to do is bring master
forward, which you can do with git merge
. First you have to git checkout master
(there's a way to do this without the intermediate step but let's keep it simple):
$ git checkout master
a - b - E - f <-- HEAD=master, origin/master
\
C'-D' <-- temp
This is the same commit diagram, we just changed where HEAD points (to master) and dropped the invisible commits.
$ git merge temp
The merge
op will "fast-forward" master, as the only thing needed to achieve the merge is to "slide the label down" from commit f
to commit D'
:
a - b - E - f <-- origin/master
\
C'-D' <-- HEAD=master, temp
Now you can git branch -d temp
to delete the temporary branch, and git push origin master
to push.
Side note: congrats on drawing clear examples of what the commit graph looks like now, and what you want it to look like. That's about 80% of all the work needed to figure out which git commands to use. :-)