11. How do git references work?#
Important
This picks up directly from where we left off in: How does git really work?
We start in the test
repo:
cd test
ls -a
. .. .git test.txt
We have one file, test.txt
, but we were working more inside the .git
directory.
Remember: git objects
There are 3 types:
blob objects: the content of your files (data)
tree objects: stores file names and groups files together (organization)
Commit Objects: stores information about the sha values of the snapshots
Recall the .git
directory has many other files in it.
ls .git
HEAD description index objects
config hooks info refs
11.1. What does git status do?#
compares the working directory to the current state of the active branch
we cansee the working directory with:
ls
we can see the active branch in the
HEAD
filewhat is its status?
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 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.
In our case because we made the commit manually, we did not update the branch, so it says we have no commits…
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.
We can verify by looking at the HEAD
file
cat .git/HEAD
ref: refs/heads/main
and then viewing that file
cat .git/refs/heads/main
cat: .git/refs/heads/main: No such file or directory
which does not even exist!
ls .git/refs/heads
nothing exists there yet!
ls .git/refs
heads tags
we can see the objects though:
find .git/objects/ -type f
.git/objects//0c/1e7391ca4e59584f8b773ecdbbb9467eba1547
.git/objects//d6/70460b4b4aece5915caf5c68d12f560a9fe3e4
.git/objects//d8/329fc1cc938780ffdd9f94e0d364e0ea74f579
.git/objects//18/8a75ef66b6a85be0ab68d8575ec27808881dfc
.git/objects//4b/825dc642cb6eb9a060e54bf8d69288fbee4904
.git/objects//83/baae61804e65cc73a7201a7252750c76066a30
git cat-file -t 188a
commit
This is our repo currently, but the branches are not there!
11.2. Git References#
echo 188a75ef66b6a85be0ab68d8575ec27808881dfc > .git/refs/heads/main
git status
On branch main
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
no changes added to commit (use "git add" and/or "git commit -a")
We can see that indeed we have one object that is a commit
git cat-file -p 188a
tree d8329fc1cc938780ffdd9f94e0d364e0ea74f579
author Sarah M Brown <brownsarahm@uri.edu> 1677177139 -0500
committer Sarah M Brown <brownsarahm@uri.edu> 1677177139 -0500
first commit
git cat-file -p d8329
100644 blob 83baae61804e65cc73a7201a7252750c76066a30 test.txt
git cat-file -p 83baa
version 1
So we now have HEAD-> main and main -> our commit -> tree –> blob.
11.3. Making another commit#
First lets find the hash for the version 2 of test.txt
find .git/objects/ -type f
.git/objects//0c/1e7391ca4e59584f8b773ecdbbb9467eba1547
.git/objects//d6/70460b4b4aece5915caf5c68d12f560a9fe3e4
.git/objects//d8/329fc1cc938780ffdd9f94e0d364e0ea74f579
.git/objects//18/8a75ef66b6a85be0ab68d8575ec27808881dfc
.git/objects//4b/825dc642cb6eb9a060e54bf8d69288fbee4904
.git/objects//83/baae61804e65cc73a7201a7252750c76066a30
We can display objects to see that it is what we want.
git cat-file -p 0c1e
version 1
version 2
Then we add it to the index
git update-index --add --cacheinfo 100644 \
0c1e7391ca4e59584f8b773ecdbbb9467eba1547 test.txt
Let’s also add a second file
echo "new file" > new.txt
and hash that and write it to the repo
git hash-object -w new.txt
fa49b077972391ad58037050f2a75f74e3671e92
and stage it as well
git update-index --add --cacheinfo 100644 \
> fa49b077972391ad58037050f2a75f74e3671e92 new.txt
Now we have two files stages:
git status
On branch main
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
new file: new.txt
modified: test.txt
and in our repo
ls
new.txt test.txt
Now we will write a tree object from all of the staged things:
git write-tree
163b45f0a0925b0655da232ea8a4188ccec615f5
and make a second comit.
echo "second commit" | git commit-tree 163b -p 188a
This command:
echo
sends “second commit” to stdoutthe pipe
|
connects that std out to stdin of the next commandgit commit-tree
needs a tree (163b
) as its inputthe
-p
option specifies the parent which is in this case our previous commit (mines is188a
)
90f8d145f3b264e99832b47a662ed5d50b687e7a
and it returns the commit hash, which as with the first comit is unique to each of us.
git status
On branch main
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
new file: new.txt
modified: test.txt
THen we can set our main branch to point to the new commit so that it will see what we expect
git update-ref refs/heads/main 90f8
and we can also use git log
now
git log
commit 90f8d145f3b264e99832b47a662ed5d50b687e7a (HEAD -> main)
Author: Sarah M Brown <brownsarahm@uri.edu>
Date: Thu Mar 2 13:15:39 2023 -0500
second commit
commit 188a75ef66b6a85be0ab68d8575ec27808881dfc
Author: Sarah M Brown <brownsarahm@uri.edu>
Date: Thu Feb 23 13:32:19 2023 -0500
first commit
11.4. What does all this get me?#
We can create a new branch at a previous point:
git update-ref refs/heads/test 188a
git log
commit 90f8d145f3b264e99832b47a662ed5d50b687e7a (HEAD -> main)
Author: Sarah M Brown <brownsarahm@uri.edu>
Date: Thu Mar 2 13:15:39 2023 -0500
second commit
commit 188a75ef66b6a85be0ab68d8575ec27808881dfc (test)
Author: Sarah M Brown <brownsarahm@uri.edu>
Date: Thu Feb 23 13:32:19 2023 -0500
first commit
We see the file for each branch points to the commit
cat .git/refs/heads/test
188a75ef66b6a85be0ab68d8575ec27808881dfc
We can change them around as we like:
git update-ref refs/heads/main 188a
git log
commit 188a75ef66b6a85be0ab68d8575ec27808881dfc (HEAD -> main, test)
Author: Sarah M Brown <brownsarahm@uri.edu>
Date: Thu Feb 23 13:32:19 2023 -0500
first commit
This does not lose any of our data, it just makes it harder to access, we have to know the hashes of thigns to find them if we did not have the branch structure to traverse the structure.
We can put this back
git update-ref refs/heads/main 90f8
git log
commit 90f8d145f3b264e99832b47a662ed5d50b687e7a (HEAD -> main)
Author: Sarah M Brown <brownsarahm@uri.edu>
Date: Thu Mar 2 13:15:39 2023 -0500
second commit
commit 188a75ef66b6a85be0ab68d8575ec27808881dfc (test)
Author: Sarah M Brown <brownsarahm@uri.edu>
Date: Thu Feb 23 13:32:19 2023 -0500
first commit
and see that the files are as expected:
ls
new.txt test.txt
git status
On branch main
nothing to commit, working tree clean
11.4.1. We can checkout any commit, not jsut branches#
git checkout 188a
Note: switching to '188a'.
You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by switching back to a branch.
If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -c with the switch command. Example:
git switch -c <new-branch-name>
Or undo this operation with:
git switch -
Turn off this advice by setting config variable advice.detachedHead to false
HEAD is now at 188a75e first commit
cat .git/HEAD
188a75ef66b6a85be0ab68d8575ec27808881dfc
This changes the head pointer directly to the commit.
ls
test.txt
and checkout also updates the working directory
cat test.txt
version 1
git checkout test
Switched to branch 'test'
ls
test.txt
cat test.txt
version 1
11.5. What does this mean?#
We tend to think of comits like this:
In reality
flowchart RL
subgraph A[first commit]
commitA
%% file1v1
treeA
blob1
commitA-->treeA
treeA-->blob1
end
subgraph B[second commit]
%% file1v2
%% file2v1
%% file3v1
commitB
treeB
blob1v2
blob2
blob3
commitB-->treeB
treeB-->blob1v2
treeB--->blob2
treeB-->blob3
end
%% B--> A
commitB -->commitA
11.6. We can move pointers around freely#
flowchart BT
blob1
blob2
blob3
subgraph A[first commit]
direction TB
commitA
%% file1v1
treeA
commitA-->treeA
end
subgraph B[second commit]
direction TB
%% file1v2
%% file2v1
%% file3v1
commitB
treeB
commitB-->treeB
end
%% subgraph C[third commit]
%% direction TB
%% %% file1v2
%% %% file2v1
%% %% file3v1
%% commitC
%% commitC-->treeC
%% end
B--> A
%% treeC-->blob1
treeC--->blob2
treeC-->blob3
treeB-->blob1
treeB--->blob2
treeA-->blob1
commitB -->commitA
commitC-->commitB
treeA ~~~ treeB
treeB ~~~treeC
main --> commitA
branchB --> commitB
branchC --> commitC2
subgraph C2[new commit]
commitC2 -->treeC
commitC2 -->commitA
treeC
end
%% A ~~~blob1
%% B ~~~blob2
%% C ~~~blob3
11.7. Review today’s class#
Read the notes and repeat the activity if needed
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. eg0c913 commit
11.8. Prepare for Next Class#
Read about the Learn more about the SHA-1 collision attach
Think about different ways you know to represent numbers.
11.9. More Practice#
Read the notes and repeat the activity if needed
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. eg0c913 commit
Add “version 3” to the test.txt file and hash that object
Add that to the staging area
Add the tree from the first commit to the staging area as a subdirectory with
git read-tree --prefix=back <hash>
Write the new tree
Make a commit with message “Commit 3” point to that tree and have your second commit as its parent.
Update your diagram in test_repo_map.md after the following.
11.10. Experience Report Evidence#
write your git status and object list to a file.
11.11. Questions After Today’s Class#
Important
I will get to these later