object model
Git Book - The Git Object Model
everything is named as the SHA1 hash of their contents
blob

- generally a file
- nothing but a chunk of binary data
- used to store file data
- doesn’t have any attributes (not even filename)
- renaming doesn’t change the object that file is associated with
- doesn’t refer to anything else
- independent of its location in the directory tree
- use git show to examine contents of any blob
$ git show 6ff87c4664
Note that the only valid version of the GPL as far as this project
is concerned is _this_ particular version of the license (ie v2, not
v2.2 or v3.x or whatever), unless explicitly otherwise stated.
...tree

- like a directory
- generally represents contents of a directory or subdirectory
- references a bunch of other trees and/or blobs (i.e. files and subdirectories)
- use git ls-tree to examine a tree
$ git ls-tree 02a5067af6526
100644 blob ac1ecdc73efd8eedb9c6549ec021b3f6f72ef338 .env
100644 blob 4808c13dee7e0999e366ad6872effe1d8a726920 .gcloudignore
100644 blob 112a2eaed3ee7705008178df0360c03266ef00fa .nvmrc
100644 blob f22eab4e337f3dde0c98eb4259abe6f17da9f301 index.js
100755 blob 1dd098fcfaee27cea05c6d66b6ce66575d91e9b4 pack.sh
100644 blob 9d0d6ae88ed82c887319bb6dcb0934bb26e8a29c package-lock.json
100644 blob 02f46a575cea50818de38cb69b8e76c6fb9a6844 package.json- two trees have the same SHA1 name iff their contents (including recursive contents of all subdirectories are identical
- this is how git is able to determine diffs so quickly — can ignore any entries with identical object names
- in the presence of submodules, trees may also have commits as entries
- files all have mode
644or755— git only pays attention to the executable bit
commit

- points to a single tree
- links a physical state of a tree with a description of how we got there and why
- marks what a project looked like at a certain point in time
- contains metadata about that point in time
- tree — SHA1 name of a tree object representing the contents of a directory at a certain point in time
- parent(s) — pointer to previous commit
- author — name of the person responsible for the change + timestamp
- committer — name of person who created the commit + timestamp
- may be different from author, e.g.:
- if the author wrote a patch and emailed it to another person who used the patch to create the commit
- may be different from author, e.g.:
- comment
- use
git show -s —pretty=rawto inspect commit metadata
$ git show -s -pretty=raw
commit d2772f81953c5336f2eee1a0bf53f2fd92b39635
tree c3a075f68c282d9f76f3fb5d6a3adad942535d44
author Charlie Tysse <[email protected]m> 1666387137 -0400
committer Charlie Tysse <[email protected]m> 1666387137 -0400
gpgsig -----BEGIN SSH SIGNATURE-----
U1NIU0lHAAAAA...
-----END SSH SIGNATURE-----
initial commit- doesn’t contain info about what actually changed
- all changes are calculated by comparing contents of tree referred to by this commit with the trees associated with its parents
- use
git commit -m "message"to create a commit with a message
tag

- marks a specific commit as special in some way
- normally used to tag certain commits as releases
- contains
- object name (
object) - object type
- tag name
- tagger (name of the person who created the tag)
- message
- may contain a (GPG) signature
- object name (
- use git tag -a v1.0.0 -m “version 1” to create an annotated tag with message
- use
git cat-fileto inspect a tag
$ git cat-file tag v1.5.0
object 437b1b20df4b356c9342dac8d38849f24ef44f27
type commit
tag v1.5.0
tagger Junio C Hamano <[email protected]t> 1171411200 +0000
GIT 1.5.0
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.6 (GNU/Linux)iD8DBQBF0lGqwMbZpPMRm5oRAuRiAJ9ohBLd7s2kqjkKlq1qqC57SbnmzQCdG4uinLE/L9aUXdWeTFPron96DLA==2E+0-----END PGP SIGNATURE------ “lightweight tags” aren’t tags, but simple references whose names begin with
refs/tags/
example
$ tree
.
├── README
└── lib
├── inc
| └── tricks.rb
└── mylib.rb
2 directories, 3 files
working directory & git
directory
git directory
- directory that stores all git’s history and meta information about a project
- includes all objects, pointers, etc.
- only one git directory per project
- by default is
.gitin the root of a project
.git
├── HEAD # pointer to your current branch
├── config # your configuration preferences
├── description # description of your project
├── hooks/ # pre/post action hooks
├── index # index file (see next section)
├── logs/ # a history of where your branches have been
├── objects/ # your objects (commits, trees, blobs, tags)
└── refs/ # pointers to your branchesworking directory
- holds the current checkout of the files you’re working on
- files in this directory are often moved/replaced by git as you switch branches
- all history is stored in the git directory
- working directory is simply a temporary checkout place where you can modify the files until your next commit
the index
- used as a staging area between working directory and repository
- use the index to build up a set of changes that you want to commit together
- what gets committed is what is currently in the index, not what’s in the working directory
- use
git statusto see the index
$ git status
# On branch master
# Your branch is behind 'origin/master' by 11 commits, and can be fast-forwarded.
## Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
## modified: daemon.c
## Changed but not updated:
# (use "git add <file>..." to update what will be committed)
## modified: grep.c
# modified: grep.h
## Untracked files:
# (use "git add <file>..." to include in what will be committed)
## blametree
# blametree-init
# git-gui/git-citoolrefs and heads
ref
- a human-readable name that references a commit ID
- essentially a pointer to a commit
- examples:
- git branch names like
main - git tags like
v0.1
- git branch names like
- stored in
.git/refs/
head
- a git object that points to the tip (most recent commit ID) on a branch
- stored in
.git/refs/heads/
merge strategies
explicit
- the default merge type
- creates a new merge commit
- alters the commit history and explicitly shows where a merge was executed
- merge commit content shows which commits were the parents of the merge commit
- adds “noise” to the history of the project
ort
- default merge type when pulling or merging one branch
- can only resolve two heads using a 3-way merge algo
- stands for Ostensibly Recursive’s Twin (written as a replacement for
recursive)
recursive
- used to be the default algo when pulling or merging one branch
- operates on two heads
- default merge strategy when pulling or merging one branch
- can detect and handle merges involving renames
- cannot make use of detected copies
resolve
- can only resolve two heads using a 3-way merge algorithm
- use a dedicated commit to tie together two histories
- two branch tips and common ancestor commit
- tries to carefully detect cris-cross merge ambiguities
- considered generally safe and fast
octopus
- default merge strategy for more than two heads
- if a merge has conflicts that need manual resolution, octopus refuses the merge attempt
- primarily used for bundling similar feature branch heads together
ours
- operates on multiple N number of branches
- output result is always that of the current branch
HEAD - “ours” term implies the preference, effectively ignoring changes from all other branches
- intended to combine history of similar feature branches
subtree
- an extension of the recursive strategy
- when merging A and B:
- if B is a child subtree of A
- B is first updated to reflect the tree structure of A
- this update is also done to the common ancestor tree shared between A and B
- B is first updated to reflect the tree structure of A
- if B is a child subtree of A
implicit via rebase or fast
forward
fast forward
- doesn’t actually merge branches
- moves (or “fast forwards”) the current branch tip up to the target branch tip
- effectively combines the histories
- not possible if branches have diverged
rebase



- use to keep your divergent work a simple series of commits without a merge
- removes commits from you branch
- temporarily saves them as patches (in
.git/rebase) - updates branch to point to the latest version of
origin - applies each of the saved patches to the new branch
squash
tagging
annotated tags
Annotated tags are stored as full objects in the Git database. To reiterate, They store extra meta data such as: the tagger name, email, and date. Similar to commits and commit messages Annotated tags have a tagging message. Additionally, for security, annotated tags can be signed and verified with GNU Privacy Guard (GPG). Suggested best practices for git tagging is to prefer annotated tags over lightweight so you can have all the associated meta-data.
git tag -a v1.4 -m "my version 1.4"To push the tag:
git push origin v1.4lightweight tags
Lightweight tags are created with the absence of the -a, -s, or -m options. Lightweight tags create a new tag checksum and store it in the .git/ directory of the project’s repo.
git tag v1.4-lw
git-workflow.jpeg