Basic commands

These are basically the commands you will use to interact with git the majority of the time. In general git is easy, but it is the more advanced commands that can land you in trouble if not careful. Again, read the man for each command when in doubt.


  • git add <path-to-file | pattern> adds zero or more files to the staging area.
  • git commit -m "<message>" commits what changes are in the staging area.
  • git status shows the status of the working directory and staging area, which includes overview of tracked, staged and untracked files.









Exercise: Tracing state of changes

Trace the state of changes in git from untracked to tracked.


Command overview

git add <path-to-file | pattern>
git commit -m "<message>"
git status

  • In the git repo my-git-repo, create a new file README.md.
  • Check the status of git to see that it is untracked.
  • Add README.md to the staging area.
  • Check the status of git to see that it is now tracked in the staging area.
  • Commit with a message.
  • Check the status of git to see there are no changes waiting or untracked.









Changes in a repository

When working with a repo, we will eventually need to look into the history of changes. Anything committed and shared with others is in the history, so beware of committing sensitive information.

As mentioned earlier, the act of "blaming" is to find out who made a change. This is done with the git blame command.

Another command to traverse the history is git log. This command shows the history of changes in the repo. It can be styled in several ways, but the default is a simple list of commits.


  • git log shows the history of changes.
  • git blame <file> shows who made the changes to a file.









Try look up the man page for git log and search for the --graph and --decorate options.


Hint: man git-log, and searching is done with / with n and N to go to the next and previous search result.










Exercise: Display the history

Display the history of the git repo with the --graph and --decorate option.












Solution: Display the history

git log --graph --decorate









Internal representations

SHAs

Short for "Secure Hash Algorithm", SHA is a cryptographic hash function that produces a fixed-size hash value. In git, the SHA is used to identify objects, such as commits, trees, and blobs.


Git commits are all identified by a SHA, which is a 40-character hexadecimal string comprised of 0-9a-f characters. The SHA is unique to the commit and is used to reference the commit in the history.


You can get away with specifying only the first 7 characters of a SHA when referencing a commit. This is because the first 7 characters are usually unique enough to identify the commit.


Main way to get SHAs is with git log.










Exercise: Identify a commit

Find the commit SHA of the first commit and copy it.












Solution: Identify a commit

git log

Output:

commit 486f1898821ee74a59ca154e0785f0f589ccb6e2 (HEAD -> main)
Author: Lasse Lund Sten Jensen <lajl@itu.dk>
Date:   Tue Sep 17 22:11:03 2024 +0200

    batman
commit 486f1898821ee74a59ca154e0785f0f589ccb6e2
---------^ this is the SHA











Question

Why is your commit SHA different than mine?












Here is another question

This is a bit harder.

Can you find your commit's SHA within the .git directory?


Hint: You will need that SHA you copied earlier.












Observation

All commits exist in the .git/objects directory with the first 2 characters of the SHA as the directory and the rest (38) as the file name.












Remember

Everything in git is stored as a file in the .git directory.

What is in the file for our SHA?


cat .git/objects/48/6f1898821ee74a59ca154e0785f0f589ccb6e2

xK ]s71|
Z&+:!RxW2q:AdȠBe]
lVO*{.su48we9c_9 %











Exercise: Reading the commit object

Can you get git cat-file -p <SHA> to output the content of README.md?












Solution: Reading the commit object

git cat-file -p 486f1898821ee74a59ca154e0785f0f589ccb6e2

tree f93e3a1a1525fb5b91020da86e44810c87a2d7bc
author Lasse Lund Sten Jensen <lajl@itu.dk> 1726603863 +0200
committer Lasse Lund Sten Jensen <lajl@itu.dk> 1726603863 +0200

batman

Not quite what we wanted, but we can look at the other SHA for tree.

git cat-file -p f93e3a1a1525fb5b91020da86e44810c87a2d7bc

100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391    README.md

Still not the content of README.md, but we can see the SHA of the blob. Let us look at that.

git cat-file -p e69de29bb2d1d6434b8b29ae775ad8c2e48c5391

We are learning git!

So there is our content of README.md—assuming we set it to "We are learning git!".












Observation

Tree is a directory.

Blob is a file.

Important: Git does not store diffs, but the entirety of the change.












Exercise: A second commit

Make a second commit with a new file and content.

  • Create a new file, we call it document.md.
  • Add some content to the file. We will use "This is a document".
  • Add the file to the staging area.
  • Commit the file with a message.











Solution: A second commit

Create the file document.md and add the content This is a document.

git add document.md
git commit -m "document"

[main ef831b9] document
 1 file changed, 1 insertion(+)
 create mode 100644 document.md











Exercise: Reading the document commit object

Can you get git cat-file -p <SHA> to output the content of document.md?












Solution: Reading the document commit object

git cat-file -p ef831b9

tree 7de536eec1170876ccb1238b78a0ee4d7dbab94a
parent 486f1898821ee74a59ca154e0785f0f589ccb6e2
author Lasse Lund Sten Jensen <lajl@itu.dk> 1726606575 +0200
committer Lasse Lund Sten Jensen <lajl@itu.dk> 1726606575 +0200

document

Notice we have a parent SHA.












Observation

If we do the same for the tree, we can see that git stores the entirety of the repository and not just the one change.

git cat-file -p 7de536eec1170876ccb1238b78a0ee4d7dbab94a

100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391    README.md
100644 blob 9de2f7e0f0b41ff960078f4879c6e9a67ee562f4    document.md

In other words, git stores the entire state of the repository at each commit, and we only need a single commit SHA to recover the entire state of the repository at that commit.












Git commits are organized as an acyclic graph that happens to be linear. Time is the order commits follow.