I'm told that the marketing department is always looking for blog posts on something, so I figured I'd try to start posting about some ways that I use GIT. These certainly aren't brain buster techniques, but they may be helpful to the GIT novices of the world. I'll try to keep the posts to stuff that you could never do with SVN, using GIT though, that leaves the possibilities endless. Tonight I'm only going to mention a simple trick, discarding only some changes to a single file in your working directory. If you're like me, then you are a deficient GIT committer. If committing were a class then I'd get a C for sheer laziness. I'll go hours, sometimes even days :O, without commiting. This is a horrible thing to do with GIT - I know - but what can I say, maybe I have a fear of commitment*. Unsurprisingly this bad habit gets me into a mess, I'll end up with a file that has a whole set of changes that I want, and a bunch that I don't want. The changes I do want though - I'm still not yet ready to pull the trigger and make a commit. In any case I'll end up with working directory that looks like this:
<span class="ps1">23:52:22 [erick@erocksbox:~/tmp/awesome.git] (git:master [m])</span><span class="ps1">$</span><span class="ps1-input"> git st</span># On branch master# Changed but not updated:# (use "git add ..." to update what will be committed)# (use "git checkout -- ..." to discard changes in working directory)## <span class="git-status-mod">modified: index.html</span>#no changes added to commit (use "git add" and/or "git commit -a")<span class="ps1">23:52:37 [erick@erocksbox:~/tmp/awesome.git] (git:master [m])</span><span class="ps1">$</span><span class="ps1-input"> git diff</span><span class="git-diff-hdr">diff --git a/index.html b/index.html</span><span class="git-diff-hdr">index 317cdaa..f115444 100644</span><span class="git-diff-hdr">--- a/index.html</span><span class="git-diff-hdr">+++ b/index.html</span><span class="git-diff-chng">@@ -5,16 +5,27 @@</span> </head> <body> <div id="page"><span class="git-diff-add">+ <!-- a mess I've made</span> <div id="header"> A Header </div><span class="git-diff-add">+ all over the place --></span><span class="git-diff-add">+</span><span class="git-diff-add">+ <ul></span><span class="git-diff-add">+ <li>with absolute</li></span><span class="git-diff-add">+ <li>disregard for some</li></span><span class="git-diff-add">+ <li>of my own work</li></span><span class="git-diff-add">+ </ul></span> <div id="content"> Page Inner </div> <span class="git-diff-add">+ <span>the mess is every where</span></span><span class="git-diff-add">+</span> <div id="footer"><span class="git-diff-rmv">- Here is a bunch of work I would really like to save, but I've been lazy and haven't been commiting all that much.</span><span class="git-diff-add">+ Footer</span><span class="git-diff-add">+ <span>Here is a bunch of work I would really like to save, but I've been lazy and haven't been commiting all that much.</span></span> </div> </div> </body>
OK, so what's a commitment procrastinator to do? This one is real easy, I'll do a git add --patch index.html
and just stage the chunk at the end that I want. Then my working directory will end up looking like this:
<span class="ps1">00:14:45 [erick@erocksbox:~/tmp/awesome.git] (git:master [mi])</span><span class="ps1">$</span><span class="ps1-input"> git st</span># On branch master# Changes to be committed:# (use "git reset HEAD ..." to unstage)## <span class="git-status-staged">modified: index.html</span>## Changed but not updated:# (use "git add ..." to update what will be committed)# (use "git checkout -- ..." to discard changes in working directory)## <span class="git-status-mod">modified: index.html</span>#
This status may look odd to you if you aren't familiar with what GIT actually does. This status says that index.html is both staged and unstaged. But that's the wrong way to think about this, remember, GIT manages patches not files. What the status actually says is that there are patches in index.html that are staged, and there are some patches that are not staged. As long as you remember that GIT is tracking patches (deltas, diffs, whatever you want to call them) then this status message shouldn't confuse you. If I do a git diff --cached
you'll see I've only stage the chunk that I want.
<span class="ps1">00:20:57 [erick@erocksbox:~/tmp/awesome.git] (git:master [mi])</span><span class="ps1">$</span><span class="ps1-input"> git diff --cached</span><span class="git-diff-hdr">diff --git a/index.html b/index.html</span><span class="git-diff-hdr">index 317cdaa..d6aa567 100644</span><span class="git-diff-hdr">--- a/index.html</span><span class="git-diff-hdr">+++ b/index.html</span><span class="git-diff-chng">@@ -14,7 +14,8 @@</span> </div> <div id="footer"><span class="git-diff-rmv">- Here is a bunch of work I would really like to save, but I've been lazy and haven't been commiting all that much.</span><span class="git-diff-add">+ Footer</span><span class="git-diff-add">+ <span>Here is a bunch of work I would really like to save, but I've been lazy and haven't been commiting all that much.</span></span> </div> </div> </body>
Now to get rid of that ugly mess just do the normal git checkout -- index.html
. This will trash all of the changes that are unstaged in index.html. Then git reset HEAD
to unstage my changes to index.html back into the working directory. Voila, I'm back to a dirty working directory with only the changes I want to my file.
<span class="ps1">00:31:37 [erick@erocksbox:~/tmp/awesome.git] (git:master [mi])</span><span class="ps1">$</span><span class="ps1-input"> git co -- index.html</span><span class="ps1">00:31:43 [erick@erocksbox:~/tmp/awesome.git] (git:master [i])</span><span class="ps1">$</span><span class="ps1-input"> git reset HEAD</span>Unstaged changes after reset:M index.html<span class="ps1">00:31:47 [erick@erocksbox:~/tmp/awesome.git] (git:master [m])</span><span class="ps1">$</span><span class="ps1-input"> git st</span># On branch master# Changed but not updated:# (use "git add ..." to update what will be committed)# (use "git checkout -- ..." to discard changes in working directory)## <span class="git-status-mod">modified: index.html</span>#no changes added to commit (use "git add" and/or "git commit -a")<span class="ps1">00:31:51 [erick@erocksbox:~/tmp/awesome.git] (git:master [m])</span><span class="ps1">$</span><span class="ps1-input"> git diff</span><span class="git-diff-hdr">diff --git a/index.html b/index.html</span><span class="git-diff-hdr">index 317cdaa..d6aa567 100644</span><span class="git-diff-hdr">--- a/index.html</span><span class="git-diff-hdr">+++ b/index.html</span><span class="git-diff-chng">@@ -14,7 +14,8 @@</span> </div> <div id="footer"><span class="git-diff-rmv">- Here is a bunch of work I would really like to save, but I've been lazy and haven't been commiting all that much.</span><span class="git-diff-add">+ Footer</span><span class="git-diff-add">+ <spangt;Here is a bunch of work I would really like to save, but I've been lazy and haven't been commiting all that much.</span></span> </div> </div> </body>
Let me wrap up... if you learn anything from this post I hope it's to not be like me, commit early and often. Rebase your changes to cleanup your commits. Second, just remember that GIT is all about managing patches, not files. Finally, if you don't commit often and you're just stuck in your ways, then I hope this technique helps to save you some time from manually going through your work to remove changes that you don't want anymore. * I'm so ashamed of myself for that pun. I apologize to everyone.