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!
Nice one.
But how can you test then that the added changes can be built fine (and maybe run some tests)? I mean, it’s easy to miss some change like adding a header declaration that would result in a non-working patch.
That’s up to you. If you want each commit to be buildable (that’s certainly a great goal), you must ensure that each your patch is functional standalone. You can check out individual commits and run your test suite on them (or build the project) when in doubt. If something is broken, you can refactor it until you’re satisfied. Unfortunately it’s a lot of work usually.
Thanks, that wasn’t what I meant, but I figured out how to do what I wanted.
There’s
git stash -k
which allows one to stash non-changed parts, and keep ones staged for commit. So the workflow is:git add -p
, and pick the changes.git stash -k
Check that it builds/passes tests
git commit -sv
(or whatever you use.-v
allows for autocompletion from the changes, if supported by text editor)git stash pop
to get the changes you didn’t pick up yet back. Note: it might result in conflict — in this case resolve it (e.g. by removing conflict markers), thengit add …
the problem file, then usegit stash pop
again.Hmm, there was a numbered list. Dat broken wordpress’es markdown 😦