I use git to keep track of changes made by our development team and committed into our central cvs-style repository. Since its cvs, it keeps track of files and not commits, making it sometimes difficult to tell exactly what files constitute the full patch for a bug fix. I just came across one and did the following:
1) trolling along, checking CVS logs and committing them to git as full patches
A--B--C--D
2) Found another file change that was actually for ticket (B), so I reset the current branch to B with
git reset --soft <sha1 ID for commit B>
3) I copy in the change, and append it to commit (B) with
git commit --amend
4) to my surprise, the tree now reads
A--B
with commits (C) and (D) only in the working tree. Their details are gone from the logs and I don’t think I can get them back. Where did I go wrong? Is my only option to make an additional commit on top of (D), and just know that its really part of (B)?
What happened
You mean amend, not append, right? I’m going to pretend this was on a branch called master, for convenience. This is what your repository looks like now:
Git commits explicitly depend on their parents – the same patch on top of a different parent is a different commit.
How to recover
You can recover the previous position of a few ways. There’s a nice shorthand for previous positions, which you can use to directly check it out or create a branch:
This is assuming it’s the first previous position. It might be the second (
master@{2})… or if you know when it was, you can usemaster@{7:35}ormaster@{23.hours.ago}. For a summary of these forms, see the “specifying revisions” section ofman git-rev-parse(online here).If you’re not sure exactly how to get to it, try
This will give you a list of previous positions of
master, and you should be able to tell from the descriptions which one you want (or maybe try a few). You can simply copy hashes from the list, and usegit checkoutorgit branchas above.What you should have done
Warning: editing history is a bad idea if it’s been published already – in that case, you should simply commit the fix. Yes, it’s kind of ugly having it split into two commits in the repository, but other users have to be able to trust what they’ve seen in the public repo not to change!
That said, to do this particular kind of history editing, you want interactive rebase:
master~4represents the commit four commits before the tip of master. You can use any form you want here – maybe it’s another branch, maybe a commit hash – whatever works.This will open up in an editor a list of the commits you’re playing with:
The commented-out help text there is pretty self-explanatory. In this case, you want to change ‘pick’ to ‘edit’ on commit B’s line, save, and quit. The rebase will start, and it’ll pause after applying
Bto let you make changes. You’ll do what you need to, add, usegit commit --amend, and thengit rebase --continue. It’ll applyCandD, and you’ll be done. If anything goes wrong in the middle, usegit rebase --abortto get back to where you started.Rebasing can be kind of scary – for example, don’t accidentally remove lines in that list! If you’re not comfortable with it yet, it’s a good idea to make heavy use of
gitkand backup branch names.