|11:05 pm - Fun with telling stories|
Somebody wrote an end-user documentation that describes this command:
$ git reset HEAD^ -- file2
This brings us into a funny state. The command does not reset HEAD, and the contents for the file2 recorded in that commit is also in the working tree, but the index records contents from an older commit. Because the copy in the working tree and the contents in the HEAD are the same, "git commit -a" will not commit any change. Worse, "git diff" will report changes to the file. In order to commit from this state, you have to "git add" the file you just reset and then run "git commit".
There is no lie in what is said in the above, but it makes me sad that new users will be taught by people who would say things like this. They may know "Doing A causes effect B", but they do not teach "For X, you want to see B" in the first place. Without knowing why you want B, a new user would not understand why being able to do A to cause B is a good thing to begin with.
By setting the stage in a more appropriate way, you can turn the "funny state" misconception into a more positive experience. For example, here is how I would tell the same story:
You have been working on two files, file1 and file2, and made a commit to record changes to them. But you realized that the change is better done as two commits, first commit that updates file1 with a new infrastructure, and then the second commit that records the change to use the new infrastructure in file2 on top of that. Also, you are not entirely satisfied with what you wrote in file2 yet.
For the purpose of fixing the commit you just made, i.e. step 1, you would want to amend it in such a way that it does not have any change to file2. After amending, you would want to perfect the changes you started making to file2 in your working tree, and then make another commit on top of the amended commit to record that change, which would be your step 2.
How would you perform the first step of amending the current commit, without disrupting the unfinished work you have in file2 in your working tree, so that you don't have to re-type anything to file2 while working in your step 2?
"git reset HEAD^ -- file2" is the answer. It will prepare your index so that it has the same contents as the commit you just made, except for file2, whose contents you just took from the commit before that with this "reset" command. From that state, all you have to do is to "git commit --amend" without -a option. Before amending the commit, you will see nothing in "git diff HEAD^ file2" (because you do not want the HEAD to be rewritten to have any change to that file). By running "git diff file2", you can check the progress of your unfinished change to file2 that you are not going to include in your amended, first, commit.
After amending the commit, you will further work on file2, and then "git commit -a" or "git commit file2" to conclude the second step.
With such an understanding of what the command is for (i.e. "why you would want to see B? Because you want to do X"), you would never write things like "git diff still shows the changes" or "committing from this state requires you to 'git add' the file again", as if they are problems to be worked around. These remarks are true but entirely miss the point, as you would actively want "diff" to show the changes and you would never "commit -a" that state, if you know what you are doing.
Perhaps the person who wrote the above passage does not understand what "reset HEAD^ -- path" is about. But then why is he teaching new people?
And that is the saddest part. The author of the above passage is supposed to be an expert who teaches other people, and I am sure there are plenty of other similar "experts" in the field.
When it comes to teaching people, WHY and WHAT FOR is much more important part of understanding than merely knowing HOW.
At least that is what I think, and that is why my Git book
(the publisher said it is now at the fourth reprint) is full of descriptions on WHY and WHAT FOR.
Current Mood: annoyed