I have been using subversion for a very long time to manage my project and for the past year my interest in Git has grown more and more. A while back we made the decision to move MefContrib and not to long ago we finally got the opportunity to make the move.
Most version control systems have two placers where your code can exist; the working copy or in the repository. However, with Git there is a things option known as the staging area (also sometimes referred to as the Index) that changes the game a bit. You can think of it as a loading area where you can collect things from your working copy that should be sent of to the repository. If you are like me then you are probably thinking “Well, pretty much all version control systems lets me choose what to commit and what not to commit – so what is the point?”.
The point is that the staging area adds even more awesomeness into equation. One of thing (but not only limited to) Git enables you to do with the help of the staging area is to pick individual hunks, from a file, and put them into the staging area while others are left out. If you did not quite get that, it means you can pick and choose exactly what modifications of a file you want to commit to the repository and which you want to hold on for a while longer – partial commits!
To use this feature you simply use the -p a switch on the git add command. However, while I am going to show you how to use this I want to show you how to do it by using the Interactive Adding feature of the git add command. Let us start of by taking a look at a simple sample repository
Staging [master +0 ~1 -0]> git status# On branch master# Changed but not updated:# (use "git add <file>..." to update what will be committed)# (use "git checkout -- <file>..." to discard changes in working directory)## modified: LICENSE.txt#no changes added to commit (use "git add" and/or "git commit -a")
We can see that there is one file that has had modifications made to it, the LICENSE.txt file. Like the help message says, we can now use the git add command to stage this file for a commit, or use the git commit –a shortcut to stage and commit in one command. But I said I wanted to show how to use the interactive adding feature of Git, so let us instead use the –i switch on the git add command to get started.
Staging [master +0 ~1 -0]> git add -istaged unstaged path1: unchanged +2/-1 LICENSE.txt*** Commands ***1: status 2: update 3: revert 4: add untracked5: patch 6: diff 7: quit 8: helpWhat now>
This brings up the interactive adding menu. Here we can see the status of each file (in this case we only have one file) a long with a list of commands at the bottom. If you look closely you see what it says that there are two (+2) additions to the file, one remove (-1) and that the file is unstaged. The command we want to use is the patch command, this is the equivalent to the git add –p command. To do this we can either enter 5 or p, either works.
What now> pstaged unstaged path1: unchanged +2/-1 LICENSE.txtPatch update>>
Next up is telling the patch command which files you want to apply the command this. Here you get quite a bit of flexibility and also the ability to change your mind before proceeding with the actual patch operation. Below are the things you can do at this point
- Enter a single file number, for example 1
- Enter a list of files, for example 1,2,3
- Enter a range of files, for example 1-4
- Enter a mix of the above 1,2,4-6
In our case we just want to run the command on the LICENSE.txt file to we enter a 1
Patch update>> 1staged unstaged path* 1: unchanged +2/-1 LICENSE.txtPatch update>>
We can now see that there is a star (*) next to the LICENSE.txt file, indicating that the command will be applied to this file. At this point we can continue to modify the list of affected file with the entries listed above, but we also have the added ability to remove files from the list by using the same entry options as above but also adding a minus sign in front.
Once we are doing selecting the files we want to run the patch command on, we simply hit enter to get started
diff --git a/LICENSE.txt b/LICENSE.txtindex ecaa6e9..14dac81 100644--- a/LICENSE.txt+++ b/LICENSE.txt@@ -1,3 +1,4 @@+Added some text beforeThis is some license mumbo jumbo-This is some more stuff\ No newline at end of file+This is some more\ No newline at end of fileStage this hunk [y,n,q,a,d,/,s,e,?]?
What we then get to see if a diff view of the first (and in our case, the only) file in the list, we we can see what there are two modifications to the file. We are now requested to make a decision about staging this hunk, which in this case means the entire file with all of the changed (which is the same as just running git add on it). I promised partial commit and partial commits you shall get!
The last line shows a list of actions we can take at this point. The list y,n,q,a,d,/,s,e,? pretty much looks like a bunch of jibberish the first time you see it so to shine some light on the options we have, simply enter a ? to bring up the description
y - stage this hunkn - do not stage this hunkq - quit, do not stage this hunk nor any of the remaining onesa - stage this and all the remaining hunks in the filed - do not stage this hunk nor any of the remaining hunks in the fileg - select a hunk to go to/ - search for a hunk matching the given regexj - leave this hunk undecided, see next undecided hunkJ - leave this hunk undecided, see next hunkk - leave this hunk undecided, see previous undecided hunkK - leave this hunk undecided, see previous hunks - split the current hunk into smaller hunkse - manually edit the current hunk? - print help
What we want to do is to select which hunks we want to commit and which we want to leave for now. To do this we use the s option, which tells the patch command to split the current hunk into smaller hunks. So let us do that
Stage this hunk [y,n,q,a,d,/,s,e,?]? sSplit into 2 hunks.@@ -1,2 +1,3 @@+Added some text beforeThis is some license mumbo jumboStage this hunk [y,n,q,a,d,/,j,J,g,e,?]
Now one get the option to decide on the first hunk of the file. By entering y we tell Git to stage this hunk for us
Stage this hunk [y,n,q,a,d,/,j,J,g,e,?]? y@@ -1,3 +2,5 @@This is some license mumbo jumbo-This is some more stuff\ No newline at end of file+This is some more\ No newline at end of fileStage this hunk [y,n,q,a,d,/,j,J,g,e,?]
It now displays the second hunk along with the same options as before. This time we are going to enter n to tell Git not to stage this hunk. Since this was the last hunk in the file, and we only had one file, we are now brought back to the interactive adding menu.
Enter 1 or s to run the status command
What now> sstaged unstaged path1: +1/-0 +1/-1 LICENSE.txt
We can now see that the license file is both staged and unstaged at the same time! How how cool is this?!
Enter 7 or q to quit the interactive adding command, then run a normal git status
Staging [master +0 ~1 -0 | +0 ~1 -0]> git status# On branch master# Changes to be committed:# (use "git reset HEAD <file>..." to unstage)## modified: LICENSE.txt## Changed but not updated:# (use "git add <file>..." to update what will be committed)# (use "git checkout -- <file>..." to discard changes in working directory)## modified: LICENSE.txt#
As you can see, the file is also displayed as both staged and unstaged when using the normal status command. Now all that is left to do is to commit the change like you normally would do
Staging [master +0 ~1 -0 | +0 ~1 -0]> git commit -m "Added additional text to the license file"[master 9a89da5] Added additional text to the license file1 files changed, 1 insertions(+), 0 deletions(-)
The change was commit as expected. Be careful not to use the –am option with the commit command, since it will ignore the staged hunks and commit the entire file, this is because the –a option tells Git to add and commit all tracked files.
Now run a git status to verify that we have uncommited modifications to the LICENSE.txt file
Staging [master +0 ~1 -0]> git status# On branch master# Changed but not updated:# (use "git add <file>..." to update what will be committed)# (use "git checkout -- <file>..." to discard changes in working directory)## modified: LICENSE.txt#no changes added to commit (use "git add" and/or "git commit -a")
Yep, they are still there and if we want to be really sure we can run the git diff command to see that the modification that we have is the same as the hunk we decided not to stage before
Staging [master +0 ~1 -0]> git diffdiff --git a/LICENSE.txt b/LICENSE.txtindex 0b6cc6c..14dac81 100644--- a/LICENSE.txt+++ b/LICENSE.txt@@ -1,4 +1,4 @@Added some text beforeThis is some license mumbo jumbo-This is some more stuff\ No newline at end of file+This is some more\ No newline at end of file
And that is all there is to it. The Git staging area is more than just a fancy waiting area for files, it brings some powerful features onto the table. There are more things you can do with the staging area but those are way beyond the scope of this