The third Git object is the commit, and this is what holds everything together. The top level object in the repository is the commit object. It references the top level tree and also keeps other information such as the committer, the date and time of the commit, references to the previous commit (or commits in the case of a merge) and the commit message. It can also have an author and can be cryptographically signed with GPG in projects where such verification is important.
There isn’t much to this because all the heavy lifting is being done by trees and blobs. The commit has the top level tree which in turn references all the other trees and objects. The beauty of them is that they can be strung in a line like a string of pearls to build up a timeline that maintains a history of a project.
Let’s take a look at the Todo project from yesterday’s post:
There is a commit object that points to a top level tree which in turn points to other trees and objects. Say we make a change to the shopping.txt file. Because the contents have changed there is a new blob for this file. Becuase there is a different blob in the home tree for shopping.txt the a new home tree is created. And because there is a different sha for the home tree in the top level tree, there is a new top level tree object. And a new commit object points to this. Since the diy.txt and the work tree are unchanged, those objects are reused. The old objects are not used in this commit, but they still exist in the object database becuase they are referenced by the previous commit. So this is what the new state looks like:
Note that there is a reference to the previous commit from the new commit, but not the other way around. Commit objects only reference their past, not their future.
A final example is renaming. Suppose I change the name of diy.txt to diny.txt (do it next year). Since the contents haven’t changed, the blob stays the same. However, as the name is kept in the home tree, a new tree object is created for that and because of that, there is a new top level tree object for a new commit to point to and all the unchanged objects are still connected. So we have:
It’s easy to see how a complex graph of changes can build up over time. But becuase the objects are made up of their contents, The same tree on my system will expand to the same tree on your system. But if I commit a tree and someone else commits the identical tree on their system, the commit objects will not be the same. Although the top level tree may be the same, because the user name, email and time are added to the commit, it is highly unlikely that they will match with anyone else making the same commit.
Viewing the contents of a commit is quite easy with
git cat-file -p and passing it the commit sha, or you can use any of the
git log methods to see the history of commits.