Skip to content

Instantly share code, notes, and snippets.

@justincbeck
Created February 9, 2012 22:14
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save justincbeck/1783723 to your computer and use it in GitHub Desktop.
Save justincbeck/1783723 to your computer and use it in GitHub Desktop.
Drop a commit
# git rebase -i head~n (where n is the number of commits from head you want)
# You will see something like this:
pick 907cdd8 Added new files to project
pick 8be0e96 Recent profile view pull to reload
# Rebase a1cd6ef..8be0e96 onto a1cd6ef
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
#
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.
#
# As you can see, if you delete a line that commit is gone.
# NOTE: You can seriously FUBAR your repo if you do this. Test everything locally before you push these changes. ...and push at your own risk.
@rduplain
Copy link

If you lose a commit, your best bet is git reflog which silently tracks the commit which is HEAD. Simply git reflog, find the sha, then git checkout c0ffee to recover -- history in tact.

http://gitready.com/intermediate/2009/02/09/reflog-your-safety-net.html

Of course, you don't want to rely on that.

@justincbeck
Copy link
Author

In Thibaud's case, he's not missing a commit, he has a commit (or 2) that's n commit's back in his history (where n > 10). In those 1 or 2 commits, he has some files (or changes to files) that he wishes he had not committed. The additional, unwanted files/changes do not make up the entirety of those erroneous commits. So he's in a position where he knows where the commits are and he knows that he wants to remove part of 1 or more commits.

Originally I thought an interactive rebase would do it. As above: he could interactively rebase, drop the commits he didn't want; by simply removing the lines from the interactive rebase session and then deal with the conflicts the removal of the commits would cause as the rebase continued. Although, as I suspected, the conflict resolution became too painful and he had to abort the rebase. I then suggested something along these lines:

http://bit.ly/zQAell (see the comment from Charles Bailey)

…although that appeared to work for me in a very simple case (I simply added a line to a README in a personal project about 5 commits deep in my history) I'm not sure how things would get in a more complicated case such as Thibauds.

@rduplain
Copy link

You have the right idea with interactive rebase. My comment on recovering commits is specific to:

NOTE: You can seriously FUBAR your repo if you do this.

My suggestion is that you can FU but not BAR, because git reflog has you covered and you can recover easily. When you are getting started with history revision in git, your best bet is to create a temporary branch, work from that, then clean up branches. Over time, you'll probably feel comfortable enough and stop creating temporary branches solely to prevent git rebase errors.

Any kind of revisionist history must regard your latter point:

... Test everything locally before you push these changes. ...and push at your own risk.

Push -f your own projects as you see fit, but don't push force a collaborative project unless you are 100% certain no one has cloned/fetch, else face breaking your collaborators' working trees. In simple cases, should it come up and you must revise history, you just ask your collaborator to git reset if they haven't made any local changes. This is completely out of the question if, say, your repository has 2k watchers. (Although as I understand it, this large repository rewrite has happened for legal reasons, which is why some of these tools exist.)

As for the commit clean up, this is where the name "rebase" is somewhat unfortunate. The basic operations rebase needs are also useful for interactive cleanup, even though semantically you are not re-basing your work, just repackaging your local changes since the fetch from the remote. In my opinion, rebase has only a few concepts to learn before you get it, but the implications are far reaching and take some practice. Rebase itself is much simpler than rebase interactive, described very well here:

http://progit.org/book/ch3-6.html

In cases where you'd like to remove a file from all of history (or recent history), http://progit.org/book/ch6-4.html:

git filter-branch --tree-filter 'rm -f passwords.txt' HEAD

... and filter-branch offers a full scripting environment on shell, but it's not intuitive beyond the line you see above.

Now for why rebase can make for good habits, and not just for fixing mistakes, which is either a controversy or a school of thought depending on your mood. For the daily grind, the git workflow is often just a matter of snapshotting, backing up, or automating builds -- you always want the latest state of the code. In public collaborations, watched projects, or in submitting pull requests, the tighter you can package your changes, the better, in my humble opinion.

A stream-of-consciousness set of commits is just as hard to parse and understand as a stream-of-consciousness email message. Diffs speak for themselves, but ordering of commits and their messages speak well to intent. I personally use a model of commit messages which read as "When you apply this patch, it will:". Matt and I discussed this pattern at various points over the years, concluding the active present voice is best. Examples:

  • Add parameters for upload paths, using defaults.
  • Memoize each property in class Foo.

To me, each commit is a complete thought, summarized in 50 characters or less on the top line. If I cannot write that thought, I split the commit. It leads to small commits, but it's trivial to sum commits and very difficult to split them.

Another benefit to well-crafted commits: there's a diff-driven-development mode where you are confident in your code because your diffs read so well. I love writing tests, but I'll keep momentum with a well-composed set of diffs on projects where testing methods are still maturing.

Somewhat long thread, but I overheard you say you wanted input.

PS: StackOverflow provides its own shortener, direct to answers. Look for the "link" tag next to each answer. http://stackoverflow.com/a/495526

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment