Inspecting changes
While git status
gives you information on the files that were changed since the last commit, it doesn’t provide any information on what those changes are.
In this section, we will see how we can get information on the changes to the files contents.
The three trees of Git
Before we can jump into this section, we need to understand a bit more how Git works.
One useful mental representation is to imagine three file trees:
The working tree
Let’s imagine that you are starting to work on a project.
First, you create a directory.
In it, you create several sub-directories.
In those, you create a number of files.
You can open these files, read them, edit them, etc. This is something you are very familiar with.
In the Git world, this is the working directory or working tree of the project.
That is: an uncompressed version of your files that you can access and edit.
You can think of it as a sandbox because this is where you can experiment with the project. This is where the project gets developed.
Now, Git has two other important pieces in its architecture.
The index
If you want the project history to be useful to future you, it has to be nice and tidy. You don’t want to record snapshots haphazardly or you will never be able to find anything back.
Before you record a snapshot, you carefully select the elements of the project as it is now that would be useful to write to the project history together. The index or staging area is what allows to do that: it contains the suggested future commit.
HEAD
Finally, the last tree in Git architecture is one snapshot in the project history that serves as a reference version of the project: if you want to see what you have been experimenting on in your “sandbox”, you need to compare the state of the working directory with some snapshot.
Remember that HEAD is a pointer pointing at a branch, that a branch is itself a pointer pointing at a commit, and finally that a commit is a Git object pointing at compressed blobs containing data about your project at a certain commit. When the HEAD pointer moves around, whatever commit it points to populates the HEAD tree with the corresponding data.
As we saw earlier, when you create a commit, HEAD automatically points to the new commit. So the HEAD tree is often filled with the last snapshot you created. But—as we will see later—we can move the HEAD pointer around through other ways. So the HEAD tree can be populated by any snapshot in your project history.
git diff
The command git diff
prints the differences between any two Git objects. In particular, it allows to compare any two of the three trees with each other.
Diff between working directory & index
git diff
displays the differences between the working directory (the uncompressed files) and the index (the staging area):
git diff
Right now, git diff
does not return anything because our working tree and staging area are at the same point.
Let’s make some changes in our proposal:
nano ms/proposal.md
Now, if we run git diff
again, we get:
diff --git a/ms/proposal.md b/ms/proposal.md
index 0fb5cc8..4fdc1b0 100644
--- a/ms/proposal.md
+++ b/ms/proposal.md
@@ -16,4 +16,4 @@ We hope to achieve a lot.
# Conclusion
-This is truly a great proposal.
+This is truly a great proposal, but it needs a little more work.
Diff between index & last commit
To see what would be committed if you ran git commit
(that is the differences between the index and the last commit), you need to run instead:
git diff --cached
We aren’t getting any output because we haven’t staged anything that isn’t in our last commit. Let’s stage our changes and try again:
git add .
git diff --cached
diff --git a/ms/proposal.md b/ms/proposal.md
index 0fb5cc8..4fdc1b0 100644
--- a/ms/proposal.md
+++ b/ms/proposal.md
@@ -16,4 +16,4 @@ We hope to achieve a lot.
# Conclusion
-This is truly a great proposal.
+This is truly a great proposal, but it needs a little more work.
The changes are now between the staging area and HEAD.
If we run git diff
now, we aren’t getting any output because the staging area has caught up with the working tree: they are now the same, so no differences.
git diff --cached
is very convenient to check what will enter in the next commit.
Diff between working directory & last commit
Finally, to see the differences between your working directory and HEAD, you run:
git diff HEAD
This will be the sum of the previous two.
Right now, git diff --cached
and git diff HEAD
print the same result, but if we make new changes, they will become different since git diff HEAD
will reflect all the changes between the working tree and the last commit, while git diff --cached
will only contain the differences between the staging area and the last commit.
Your turn:
Modify one of the tracked files to visualize this.