11. How do git references work?#

Important

This picks up directly from the class 2 sessions ago

We will go back to the test repo and review waht we have there.

cd ../test/
ls
test.txt
cat test.txt
version 1
version 2
git status
On branch main

No commits yet

Changes to be committed:
  (use "git rm --cached <file>..." to unstage)
	new file:   test.txt

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
	modified:   test.txt

We have a few files and we have content staged and unstaged as changes to those files.

git log
fatal: your current branch 'main' does not have any commits yet

Notice, we have no commits yet even though we had written a commit. This is because the main branch does not point to any commit.

fin .git/objects/ -type f
-bash: fin: command not found

To get a better look, we can list the objects.

find .git/objects/ -type f
.git/objects//0c/1e7391ca4e59584f8b773ecdbbb9467eba1547
.git/objects//d6/70460b4b4aece5915caf5c68d12f560a9fe3e4
.git/objects//d8/329fc1cc938780ffdd9f94e0d364e0ea74f579
.git/objects//e0/9139a38f4fd6d82715c32aab9adfed67a87ba5
.git/objects//83/baae61804e65cc73a7201a7252750c76066a30

We can see that indeed we have one object that is a commit

git cat-file -t e0913
commit

And we can look at its contents

git cat-file -p e0913
tree d8329fc1cc938780ffdd9f94e0d364e0ea74f579
author Sarah M Brown <brownsarahm@uri.edu> 1665005715 -0400
committer Sarah M Brown <brownsarahm@uri.edu> 1665005715 -0400

first commit

Let’s add one more new file to review

echo "new file" > new.txt

first we hash the object. The -w option writes to the directory.

git hash-object -w new.txt

then we get the hash back

fa49b077972391ad58037050f2a75f74e3671e92

Then we stage it.

git update-index --add --cacheinfo 100644 \
> fa49b077972391ad58037050f2a75f74e3671e92 new.txt

and again we can look on the overall repo

git status
On branch main

No commits yet

Changes to be committed:
  (use "git rm --cached <file>..." to unstage)
	new file:   new.txt
	new file:   test.txt

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
	modified:   test.txt

and list out the .git objects

find .git/objects/ -type f
.git/objects//0c/1e7391ca4e59584f8b773ecdbbb9467eba1547
.git/objects//d6/70460b4b4aece5915caf5c68d12f560a9fe3e4
.git/objects//d8/329fc1cc938780ffdd9f94e0d364e0ea74f579
.git/objects//fa/49b077972391ad58037050f2a75f74e3671e92
.git/objects//e0/9139a38f4fd6d82715c32aab9adfed67a87ba5
.git/objects//83/baae61804e65cc73a7201a7252750c76066a30
git cat-file -p 0c1e
version 1
version 2

then we can stage the second version of our first file

git update-index --add --cacheinfo 100644 \
> 0c1e7391ca4e59584f8b773ecdbbb9467eba1547 test.txt
git status
On branch main

No commits yet

Changes to be committed:
  (use "git rm --cached <file>..." to unstage)
	new file:   new.txt
	new file:   test.txt

and now we see that everything is staged.

Then we confirm what hash is our previous commit

git cat-file -t e0913
commit
git cat-file -p e0913
tree d8329fc1cc938780ffdd9f94e0d364e0ea74f579
author Sarah M Brown <brownsarahm@uri.edu> 1665005715 -0400
committer Sarah M Brown <brownsarahm@uri.edu> 1665005715 -0400

first commit

Next we write a tree object from the staging area

git write-tree
163b45f0a0925b0655da232ea8a4188ccec615f5
git cat-file -p 163b4
100644 blob fa49b077972391ad58037050f2a75f74e3671e92	new.txt
100644 blob 0c1e7391ca4e59584f8b773ecdbbb9467eba1547	test.txt

This tree has the current version of each file.

Now we can make a new commit. We will echo the message into the commit-tree plumbing function with a pipr and then we will tell this commit to point to the tree we just created and that its parent is the first commit.

echo "second commit" | git commit-tree 163b4 -p e0913
cf857a865c6088ab9bf90e2732df967f8e2582ab

But still when we check status

git status
On branch main

No commits yet

Changes to be committed:
  (use "git rm --cached <file>..." to unstage)
	new file:   new.txt
	new file:   test.txt

The files look staged. This is beacuse of what git status does. It compares the current working directory (what we see when we do ls) to the staging area and the content that the current branch points to.

find .git/objects -type f
.git/objects/0c/1e7391ca4e59584f8b773ecdbbb9467eba1547
.git/objects/d6/70460b4b4aece5915caf5c68d12f560a9fe3e4
.git/objects/d8/329fc1cc938780ffdd9f94e0d364e0ea74f579
.git/objects/cf/857a865c6088ab9bf90e2732df967f8e2582ab
.git/objects/16/3b45f0a0925b0655da232ea8a4188ccec615f5
.git/objects/fa/49b077972391ad58037050f2a75f74e3671e92
.git/objects/e0/9139a38f4fd6d82715c32aab9adfed67a87ba5
.git/objects/83/baae61804e65cc73a7201a7252750c76066a30

11.1. Git References#

Recall the .git directory has many other files in it.

ls .git
HEAD		description	index		objects
config		hooks		info		refs

Let’s look at the refs dir.

ls .git/refs/
heads	tags
ls .git/refs/heads

there’s nothing in the heads dir

Though when we use git status

git status
On branch main

No commits yet

Changes to be committed:
  (use "git rm --cached <file>..." to unstage)
	new file:   new.txt
	new file:   test.txt

we see it is “on main” this is because we set the branch to main , but since we have not written there, we have to do it directly. Notice that when we use the porcelain command for commit, it does this automatically; the porcelain commands do many things.

We can write tothat file directly

echo cf857a865c6088ab9bf90e2732df967f8e2582ab > .git/refs/heads/main

And now if we check git status

git status
On branch main
nothing to commit, working tree clean

we see what we expec.

Alternatively, we can use only a short reference to the hash if we use git update-ref

git update-ref refs/heads/main cf857

Now git log also works.

git log
commit cf857a865c6088ab9bf90e2732df967f8e2582ab (HEAD -> main)
Author: Sarah M Brown <brownsarahm@uri.edu>
Date:   Mon Oct 17 16:55:10 2022 -0400

    second commit

commit e09139a38f4fd6d82715c32aab9adfed67a87ba5
Author: Sarah M Brown <brownsarahm@uri.edu>
Date:   Wed Oct 5 17:35:15 2022 -0400

    first commit

Lets create a branch at the point of our first commit.

git update-ref refs/heads/test e0913
git log
commit cf857a865c6088ab9bf90e2732df967f8e2582ab (HEAD -> main)
Author: Sarah M Brown <brownsarahm@uri.edu>
Date:   Mon Oct 17 16:55:10 2022 -0400

    second commit

commit e09139a38f4fd6d82715c32aab9adfed67a87ba5 (test)
Author: Sarah M Brown <brownsarahm@uri.edu>
Date:   Wed Oct 5 17:35:15 2022 -0400

    first commit

Notice that the commit history in git log also lists the branches.

We can also look at the HEAD pointer

git symbolic-ref HEAD
refs/heads/main

update the HEAD directly

git symbolic-ref HEAD refs/heads/test

and see what this does.

git status
On branch test
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
	new file:   new.txt
	modified:   test.txt

This is only part of what git checkout does, because it switches the branch, but does not update the working directory.

11.2. Git Tags#

Just like branches, but used differently.

ls .git/refs/
heads	tags
git update-ref refs/tags/v1.0
usage: git update-ref [<options>] -d <refname> [<old-val>]
   or: git update-ref [<options>]    <refname> <new-val> [<old-val>]
   or: git update-ref [<options>] --stdin [-z]

    -m <reason>           reason of the update
    -d                    delete the reference
    --no-deref            update <refname> not the one it points to
    -z                    stdin has NUL-terminated arguments
    --stdin               read updates from stdin
    --create-reflog       create a reflog

git update-ref refs/tags/v1.0 cf857a

git checkout main
Switched to branch 'main'
git log
commit cf857a865c6088ab9bf90e2732df967f8e2582ab (HEAD -> main, tag: v1.0)
Author: Sarah M Brown <brownsarahm@uri.edu>
Date:   Mon Oct 17 16:55:10 2022 -0400

    second commit

commit e09139a38f4fd6d82715c32aab9adfed67a87ba5 (test)
Author: Sarah M Brown <brownsarahm@uri.edu>
Date:   Wed Oct 5 17:35:15 2022 -0400

    first commit

11.3. Adding a remote#

We created this repo locally, and we have not set a remote.

git remote

we can add one with

git remote add origin <url/to/remote>

in my case, I’ll use https://github.com/introcompsys/toy-repo-brownsarahm.git you should use your own.

git remote
origin
ls .git
HEAD		description	index		logs		refs
config		hooks		info		objects
ls .git/config
.git/config
cat .git/config
[core]
	repositoryformatversion = 0
	filemode = true
	bare = false
	logallrefupdates = true
	ignorecase = true
	precomposeunicode = true
[remote "origin"]
	url = https://github.com/introcompsys/toy-repo-brownsarahm.git
	fetch = +refs/heads/*:refs/remotes/origin/*
git push
fatal: The current branch main has no upstream branch.
To push the current branch and set the remote as upstream, use

    git push --set-upstream origin main

adding a remote directly doesn’t link the local branch to the remote branch, so we do that next.

git push -u origin main
Enumerating objects: 7, done.
Counting objects: 100% (7/7), done.
Delta compression using up to 8 threads
Compressing objects: 100% (3/3), done.
Writing objects: 100% (7/7), 497 bytes | 497.00 KiB/s, done.
Total 7 (delta 0), reused 0 (delta 0), pack-reused 0
To https://github.com/introcompsys/toy-repo-brownsarahm.git
 * [new branch]      main -> main
branch 'main' set up to track 'origin/main'.
find .git/objects/ -type f
.git/objects//0c/1e7391ca4e59584f8b773ecdbbb9467eba1547
.git/objects//d6/70460b4b4aece5915caf5c68d12f560a9fe3e4
.git/objects//d8/329fc1cc938780ffdd9f94e0d364e0ea74f579
.git/objects//cf/857a865c6088ab9bf90e2732df967f8e2582ab
.git/objects//16/3b45f0a0925b0655da232ea8a4188ccec615f5
.git/objects//fa/49b077972391ad58037050f2a75f74e3671e92
.git/objects//e0/9139a38f4fd6d82715c32aab9adfed67a87ba5
.git/objects//83/baae61804e65cc73a7201a7252750c76066a30

11.4. Review today’s class#

  1. Read the notes and repeat the activity if needed

  2. use git cat-file over the objects to draw a graph diagram of your current status in your test directory include your drawing in test_repo_map.md using mermaid syntax to diagram it. Name each node in your graph with 5-7 characters of the hash and the type. eg 0c913 commit

11.5. Prepare for Next Class#

  1. Bring ideas of what you want to write a bash script for and/or a small command line program

  2. Windows only, but important get python working in GitBash

  3. Make sure the gh command line tool works in a bash terminal (either MacOs, Linux, GitBash, or WSL)

11.6. More Practice#

  1. Add “version 3” to the test.txt file and hash that object

  2. Add that to the staging area

  3. Add the tree from the first commit to the staging area as a subdirectory with git read-tree --prefix=back <hash>

  4. Write the new tree

  5. Make a commit with message “Commit 3” point to that tree and have your second commit as its parent.

  6. Update your diagram in test_repo_map.md after the following.

  7. Update your gitplumbingdetail.md