Categories
How To

Changing Git History

It’s back to the future when you make too many commits in git!

Working on a group project in Git, I did the smart thing with my code. I made a branch and proceeded to edit my files. I also did a dumb thing. I made four commits.

The first was for the first, ugly, functional version of the code. The second was a less ugly, kind of broken version. The third was the rewrite and the fourth was the working version. When I wanted to submit my changes for a review, it was going to be ugly. I did not need or want people looking at four commits They only wanted the one.

Now I’m a weird person for how I do commits. I add a new feature like a new function to parse things, and I commit that. Then I change my CSS and commit that. And so on and so on. This means I can look through my commit history and see exactly when I made a change. When I’m ready to do my release, I document all the changes based on that commit log and have it as my message.

But when you’re working with a team, and all they want is one clean commit? Well I’m their worst nightmare. There is a cure for this, though! You can squash your commits, merging them all into one.

Squash

Actually it’s rebase. It can be squash too, though. I ran the following command which says to rebase my last 4 commits:

git rebase -i HEAD~4

That opens up another editor

pick b17617p Crap I need to do this thing!
pick 122hdla Added feature HUMAN to autogenerate a humans.txt file
pick nw9v88a Changed comment avatar size to 96px
pick 8jsdy1m Updated CSS for comment avatars to make them a circle

# Rebase b17617p..8jsdy1m onto b17617p
#
# 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.
#

Now here’s where it’s weird. The first one, b17617p is the one I have to merge everything into. And it has the worst commit message, doesn’t it? Oh and I was totally not using the right formatting for how the company wants me to format my commits. They want the comment to be “Feature: Change” so I would have “Humans: Added new feature to autogenerate humans.txt”

Since I knew I wanted to merge it all and totally rewrite the commit, I just did this:

pick b17617p Crap I need to do this thing!
squash 122hdla Added feature HUMAN to autogenerate a humans.txt file
squash nw9v88a Changed comment avatar size to 96px
squash 8jsdy1m Updated CSS for comment avatars to make them a circle

Which, once saved and exited, gave me this:

# This is a combination of 4 commits.
# The first commit's message is:

Crap I need to do this thing!

# This is the 2nd commit message:

Added feature HUMAN to autogenerate a humans.txt file

# This is the 3rd commit message:

Changed comment avatar size to 96px

# This is the 4th commit message:

Updated CSS for comment avatars to make them a circle

# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# Explicit paths specified without -i nor -o; assuming --only paths...
# Not currently on any branch.
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#	new file:   LICENSE
#	modified:   README.textile
#	modified:   Rakefile
#	modified:   bin/jekyll
#

Since everything with a # is ignored, I deleted it and made it this:

Humans: New Feature -- Humans.txt is now autogenerated
Comments: Changed avatar size to 96px and edited CSS to make it a circle

Yeah, that’s it. Admittedly, these should be two separate changes, but they’re all a part of the same project in this case so it’s okay.

Of course, at the end of this, I looked at my code on our web tool and swore, because I’d left a debug line in. My hero Mike said “Don’t worry! ammend!”

I made my change, instead of a normal git commit -a -m "These are my changes" I ran a git add FILENAME and git commit --ammend to fix up your most recent commit.

It lets you combine staged changes with the previous commit instead of committing it as an entirely new snapshot. It can also be used to simply edit the previous commit message without changing its snapshot.

And yes, it’s pretty awesome. Use it wisely.

4 replies on “Changing Git History”

For those reading Otto‘s comment and going WTF?

git svn dcommit

Commit each diff from a specified head directly to the SVN repository, and then rebase or reset (depending on whether or not there is a diff between SVN and head). This will create a revision in SVN for each commit in git. It is recommended that you run git-svn fetch and rebase (not pull or merge) your commits against the latest changes in the SVN repository. An optional command-line argument may be specified as an alternative to HEAD. This is advantageous over set-tree (below) because it produces cleaner, more linear history

Yes, you can commit a diff from git to svn. This will create a revision in SVN for each commit in git.. So if, like me, you’re a doofus with a hundred commits, you sent 100 svn pushes. And Otto will growl at you.

Comments are closed.