Git Tip of the Day – changing last commit

Very often you realize you want to change something right after you’ve committed it. At least I do. How to change the last commit?

It is extremely easy to do. Let’s say you want to change just the commit message. All you need is to run:

$ git commit --amend

That’s all. An editor will open up for you with the last commit message and you can edit it. Piece of cake.

Now something more advanced! You realized you forgot to add some file/remove some file/change some file. You need to change the file contents of the last commit. How to do that?

It’s easy. First, do all the changes you need:

$ vim file_to_be_changed
$ touch file_to_be_added

Now put the desired changes into the staging area:

$ git add file_to_be_changed
$ git add file_to_be_added
$ git rm file_to_be_removed

And now run the same magic command as before:

$ git commit --amend

That’s all. Not only git will allow you to change the commit message, but it will also add the changes in the staging area to the last commit. Work done. Ask for pay raise.

Important warning at the end: Changing last commit (even just the commit message) changes SHA1 checksum of the commit. Never change commits that you already pushed to some remote location if there is a chance somebody else already based his/her work on it. You would cause such person a lot of difficulties. Change commits for your local development only.

Enjoy!

Flattr this

Git Tip of the Day – splitting changes into several patches

Sometimes you change the code and then you realize you would like to commit it as several separate patches. For example you fixed two bugs at once but then you decided you want to have the commits separated into two logical units – first bug fix and second bug fix.

As long as the changes don’t involve the same file, it’s easy to do. You just git add first file, commit, git add second file, commit. But what will you do if the two changes involved the same file?

There is a simple solution: git add --patch

Let’s see an example. I have made two changes in my README file – deleted one line and added one line. I want to stage it as two separate patches.

$ git add --patch README
diff --git a/README b/README
index 05ad773..2768fc1 100644
--- a/README
+++ b/README
@@ -1,4 +1,3 @@
-Welcome to autoqa!

 Everything here is under heavy development so don't expect anything to
 stay stable - modules, APIs, whatever.  If it's expected to stay stable we
Stage this hunk [y,n,q,a,d,/,j,J,g,e,?]? y
@@ -8,6 +7,7 @@ but if you have questions please feel free to contact us.
 == Architecture ==

 There's four main things in this tree: 'autoqa', 'events', 'watchers', and 'tests'.
+foo!

 'autoqa' is the main binary that kicks off tests (using the autotest framework -
 see http://autotest.kernel.org/ for details). It's typically supposed to be 
Stage this hunk [y,n,q,a,d,/,K,g,e,?]? n

Git detects “hunks” (changed sections) in the file and asks for every hunk whether you want to stage it or not (see the prompt “Stage this hunk [y,n,q,a,d,/,j,J,g,e,?]?“) . You can see that I staged the first hunk, and I didn’t staged the second one. (For explanation of the all the letters in the prompts type in the question mark.).

Now I see something like this:

$ git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#	modified:   README
#
# Changes not staged for commit:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#	modified:   README
#

Some parts of README were staged, some parts were not. I can display the difference between my HEAD and the staging area:

$ git diff --staged 
diff --git a/README b/README
index 05ad773..bbc5249 100644
--- a/README
+++ b/README
@@ -1,4 +1,3 @@
-Welcome to autoqa!

 Everything here is under heavy development so don't expect anything to
 stay stable - modules, APIs, whatever.  If it's expected to stay stable we

You can see that only one changed (the deleted line) is part of my staging area. Now I can commit and only this change will be part of it. Then I’ll stage the remaining changes in README and commit again. I’ve just easily split two changes in one file into two different commits. Voila!

Interactive mode

You can do the same way through interactive mode if you prefer:

$ git add --interactive 
           staged     unstaged path
  1:    unchanged        +1/-1 README

*** Commands ***
  1: status	  2: update	  3: revert	  4: add untracked
  5: patch	  6: diff	  7: quit	  8: help
What now>

You are presented with the list of changed files and you can do different operations on them. Don’t forget to press h or 8 in this step. Action patch or 5 is exactly the same we saw with git add --patch. After you select which file to work on (and press Enter once more), you select all the desired hunks:

What now> p   
           staged     unstaged path
  1:    unchanged        +1/-1 README
Patch update>> 1
           staged     unstaged path
* 1:    unchanged        +1/-1 README
Patch update>> 
diff --git a/README b/README
index 05ad773..2768fc1 100644
--- a/README
+++ b/README
@@ -1,4 +1,3 @@
-Welcome to autoqa!
 
 Everything here is under heavy development so don't expect anything to
 stay stable - modules, APIs, whatever.  If it's expected to stay stable we
Stage this hunk [y,n,q,a,d,/,j,J,g,e,?]? y
@@ -8,6 +7,7 @@ but if you have questions please feel free to contact us.
 == Architecture ==
 
 There's four main things in this tree: 'autoqa', 'events', 'watchers', and 'tests'.
+foo!
 
 'autoqa' is the main binary that kicks off tests (using the autotest framework -
 see http://autotest.kernel.org/ for details). It's typically supposed to be 
Stage this hunk [y,n,q,a,d,/,K,g,e,?]? n

*** Commands ***
  1: status	  2: update	  3: revert	  4: add untracked
  5: patch	  6: diff	  7: quit	  8: help
What now>

Now I can display the status:

What now> s
           staged     unstaged path
  1:        +0/-1        +1/-0 README

*** Commands ***
  1: status	  2: update	  3: revert	  4: add untracked
  5: patch	  6: diff	  7: quit	  8: help
What now>

In the README file I have 0 lines added and 1 line removed in the staging area, and one line added and zero lines removed in the unstaged area.

I can easily display the diff between HEAD and the staging area:

What now> d
           staged     unstaged path
  1:        +0/-1        +1/-0 README
Review diff>> 1
diff --git a/README b/README
index 05ad773..bbc5249 100644
--- a/README
+++ b/README
@@ -1,4 +1,3 @@
-Welcome to autoqa!
 
 Everything here is under heavy development so don't expect anything to
 stay stable - modules, APIs, whatever.  If it's expected to stay stable we
*** Commands ***
  1: status	  2: update	  3: revert	  4: add untracked
  5: patch	  6: diff	  7: quit	  8: help
What now>

That’s exactly what I want. Now I can just quit the interactive mode and commit.

The first approach (non-interactive) seems faster to me, but interactive mode can be also appealing for some people.

Enjoy!

Flattr this