Git Reference
Kip Landergren
(Updated: )
Contents
- Commands
- Symbolic Identifiers
- Scripting with git
- gitignore(5)
- Git Workflows
- Resources and Documentation
-
Frequently Asked Questions (FAQ)
- How to access Git command man pages?
- Why do git command man pages use a dash?
- What is meant by “Git tracks content, not files”?
- What does the double dash (“--”) mean in command examples?
- Why doesn’t sha1sum of my file equal its object name?
- What does filemode 100644 refer to?
- What is meant by “porcelain” and “plumbing”?
- Does Git track changes? Does Git store deltas?
- What is a “three-way merge”?
- What is meant by “in Git, branches are cheap”? Why are branches free? Why does Git have cheap branching?
- Git Terminology
Commands
git(1)
High-Level Commands (Porcelain)
Main Porcelain Commands
git-bisect(1)
Use binary search to find the commit that introduced a regression / bug.
Tips:
- refer to Why bisecting merge commits can be harder than bisecting\ linear history and other resources
git-branch(1)
List, create, or delete branches.
git-clean(1)
Removes untracked files from the working tree.
dry run, descending into untracked directories:
git clean --dry-run -d
preferred invocation (--force necessary unless clean.requireForce set to false):
git clean -d --force
git-clone(1)
Clone a repository into a new directory.
Notes:
- origin is just the default name for a remote and can be overridden via --origin
- using
git clone
does not record information about the downstream repository in the remote repository
git-commit(1)
Stores the contents of the index as a snapshot in the repository.
Anatomy of a commit:
$ git cat-file -p 3b5c9f6
tree a936d5526f972cfbaaf7eb18c891cda540b5876f
parent 5084f842cf25dacf86893576adc76f5a2c34375b
author Kip Landergren <klandergren@users.noreply.github.com> 1635542920 -0700
committer Kip Landergren <klandergren@users.noreply.github.com> 1635542920 -0700
update readme
Anatomy of a merge commit:
$ git cat-file -p a90dd43
tree 64f2c6dd748699e74c598752f862a51a58c148be
parent d32271182ffbcd41e0d7e203f201a2d44d9ed221
parent 3ed416b987a9578d0e66f81c21443d52299ac4e8
author Kip Landergren <klandergren@users.noreply.github.com> 1635543254 -0700
committer Kip Landergren <klandergren@users.noreply.github.com> 1635543254 -0700
Merge branch 'topic-a-feature-4' into topic-a
git-diff(1)
Shows changes between:
- commits
- a commit, like HEAD, and the working tree
- a commit, like HEAD, and the index
- others
Difference between index and working tree:
git diff
Difference between HEAD and index:
git diff --staged HEAD
git diff --staged
git diff --cached HEAD
git diff --cached
Difference between HEAD and working tree:
git diff HEAD
Example:
$ git diff
$ echo 'foo' >> README
$ git diff
diff --git a/README b/README
index 038d718..02005ac 100644
--- a/README
+++ b/README
@@ -1 +1,2 @@
testing
+foo
$ git diff --staged
$ git diff HEAD
diff --git a/README b/README
index 038d718..02005ac 100644
--- a/README
+++ b/README
@@ -1 +1,2 @@
testing
+foo
$ git add README
$ git diff
$ git diff --staged
diff --git a/README b/README
index 038d718..02005ac 100644
--- a/README
+++ b/README
@@ -1 +1,2 @@
testing
+foo
$ git diff HEAD
diff --git a/README b/README
index 038d718..02005ac 100644
--- a/README
+++ b/README
@@ -1 +1,2 @@
testing
+foo
$ echo 'bar' >> README
$ git diff
diff --git a/README b/README
index 02005ac..73dabd8 100644
--- a/README
+++ b/README
@@ -1,2 +1,3 @@
testing
foo
+bar
$ git diff --staged
diff --git a/README b/README
index 038d718..02005ac 100644
--- a/README
+++ b/README
@@ -1 +1,2 @@
testing
+foo
$ git diff HEAD
diff --git a/README b/README
index 038d718..73dabd8 100644
--- a/README
+++ b/README
@@ -1 +1,3 @@
testing
+foo
+bar
git-log(1)
Tips:
git log
operates on a set of commits- be aware of the
-c
and--cc
options for combined diff format and how they can help reveal changes that were made during merges that had no ancestor (i.e. an “evil merge”); read more about diff format
preferred display:
git log --oneline --graph
always choose first parent across merges:
git log --first-parent
skip merge commits, both are equivalent:
$ git log --no-merges
$ git log --max-parents=1
only log merge commits, both are equivalent:
$ git log --merges
$ git log --min-parents=2
git-merge(1)
Tips:
- do not start a merge with uncommitted changes because of the difficulty in reconstructing state if the merge is aborted
- remember that a conflict-free merge does not imply working code! (e.g. one branch removing a calling pattern and a separate branch introducing a new call site)
Fast-Forward Merge
Initial state allowing a fast-forward merge:
main
↓
o---o
\
a---b---c
↑
topic
Commands:
$ git checkout main
$ git merge --ff topic
Final state:
o---o main
\ ↓
a---b---c
↑
topic
Important: a fast-forward merge is not possible if the merged-in branch is not a descendant of the receiving branch’s tip:
main
↓
o---o---o
\
a---b---c
↑
topic
Non-Fast-Forward Merge
Initial state:
main
↓
o---o
\
a---b---c
↑
topic
Commands:
$ git checkout main
$ git merge --no-ff topic
Final state:
main
↓
o---o-----------m
\ /
a---b---c
↑
topic
Merge Direction
Merges are not symmetric operations. The parents of a merge commit are numbered (1..n) in the order that they are passed as args.
Assume this starting scenario:
main
↓
a---b---f
\
c---d---e
↑
topic
The result of:
$ git checkout main
$ git merge topic
is the creation of merge commit m:
main
↓
a---b---f-------m
\ /
c---d---e
↑
topic
The result of:
$ git checkout topic
$ git merge main
is the creation of merge commit m':
main
↓
a---b-----------f
\ \
c---d---e---m'
↑
topic
git-rebase(1)
Reapply commits on top of another base tip.
Notes:
- refer to git-rebase(1) section “REBASING MERGES”
git-reset(1)
Reset current HEAD to the specified state.
Cancel last commit:
git reset --hard HEAD^
git-restore(1)
Restore working tree files.
Restore all files in the current directory:
git restore .
git-show(1)
Display a commit exactly as stored, without diff output:
git show -s --pretty=raw <commit-ish object>
Low-Level Commands (Plumbing)
Internal helper commands
git-check-ignore(1)
git-check-ignore(1) documentation
git check-ignore --verbose some/path
Interrogation commands
git-cat-file(1)
About the name containing “cat-file”: think of this as cat
’ing the git
object file specified by the revision.
Interrogate the type of an object:
$ git cat-file -t 94cf6e7dfcec7870be1da2f603dbb47c37eedecf
commit
Pretty print an object (that we know to be a tree ):
$ git cat-file -p 59b6b
100644 blob 2617c87dce8b25f1c67acd220677749e0e3b3f81 README.md
git-rev-list(1)
git-rev-parse(1)
git-rev-parse(1) documentation
Ancillary Commands
Manipulators
git-config(1)
Show all config scope, origin, and values:
git config --show-scope --show-origin --list
Or apply a specific scope:
git config --local --list
git config --global --list
git config --system --list
Set a global config value:
git config --global init.defaultBranch main
All possible config values: consult the man page.
Interrogators
git-blame(1)
git-help(1)
git help
git --help
git -h
Symbolic Identifiers
@ | same as HEAD |
HEAD | names the commit on which you are basing the changes in the working tree |
FETCH_HEAD | the head of the branch from which the last git fetch fetched |
ORIG_HEAD | the head previous to running a command that moves HEAD |
MERGE_HEAD | the commit(s) which one merges during git merge |
CHERRY_PICK_HEAD | the commit being cherry-picked during git cherry-pick |
Scripting with git
Use plumbing commands, not porcelain, as they are more stable.
gitignore(5)
Keep in mind:
- rules impact bulk operations; prefer simplicity
- ignoring a directory also ignores its containing files; negating will not work
- Git will not follow symbolic links (symlinks)—they are treated like a regular file (which is consistent with how pathspec behavior works in general in Git)
- a leading /, or a middle /, or both, indicates pattern matching relative to the .gitignore file location
- use the appropriate location:
- .gitignore for repository-specific rules that apply to all developers
- $GIT_DIR/info/exclude for repository-specific rules that apply to just the local user (e.g. due to their specific workflow for that repository)
- the file pointed to by variable core.excludesFile for global rules that apply to just the local user (e.g. due to their editor’s backup files)
Examples
glob pattern
tree:
.gitignore
baz.txt
foo/
foo/bar.txt
.gitignore:
*.txt
result:
.gitignore # tracked
baz.txt # ignored (pattern)
foo/ # directories not tracked by git
foo/bar.txt # ignored (pattern)
directories
tree:
.gitignore
d1/
d1/.gitkeep
d2/
d2/.gitkeep
d3/
d3/.gitkeep
.gitignore:
# ignore anything (file, directory, link) at top level `d1` directory
/d1
# ignore entire top level `d2` directory
/d2/
# ignore anything (files, directories, links) _within_ top level `d3` directory
/d3/*
# negating pattern for any previously matched pattern
!.gitkeep
result:
.gitignore # tracked
d1/ # directories not tracked by git
d1/.gitkeep # never pattern matched because `/d1` ignores directory
d2/ # directories not tracked by git
d2/.gitkeep # never pattern matched because `/d2/` ignores entire directory
d3/ # directories not tracked by git
d3/.gitkeep # tracked. initally pattern matched to be ignored, but then negated
More info in this answer to “Difference between .gitignore rules with and without trailing slash like /dir and /dir/”.
comments
# I am a .gitignore comment!
/wont-work # I am NOT a comment!
Debugging .gitignore
Print all ignored files and the rules excluding them:
find . -path ./.git -prune -o -print | git check-ignore --stdin --verbose
Frequently Asked Questions (FAQ)
Can I have multiple .gitignore files in a single repository?
Yes. Refer to the gitignore documentation for rules of precedence.
What does .gitkeep mean? What is the purpose of the .gitkeep file?
Using an empty .gitkeep file is an external convention (i.e. not used by Git) used to ensure that a directory is created upon clone of a repository.
Can git track empty directories? Does git track directories?
No. More information in this answer to “How can I add a blank directory to a Git repository?”. Add any file (e.g. an empty .gitignore file) to track. Some have adopted the convention of naming these files .gitkeep to connote its purpose.
Git Workflows
Fork and Pull / Integration Manager / Integrator
Contributors do not (necessarily) have commit access to a repository and instead fork it to create their own copy, work within that forked version, and communicate to the original repo’s maintainer (possibly through pull requests) that their forked repository’s branch has contributions ready for pulling and merging into the original repository.
Centralized / Shared Repository
Multiple contributors have commit access to a repository and work on clones of it.
git-flow
Useful for:
- aggregating multiple features into a single, testable, isolated release branch
- maintaining specific release versions
- unambiguous process steps
Consider alternatives if:
-
git rebase
is preferred -
a lightweight workflow is needed
-
Authoritative:
- A Successful git Branching Model by Vincent Driessen (nvie)
- nvie/gitflow GitHub repo with git extensions implementing the branching model
-
Perspectives:
- Why aren't you using git-flow? | Hacker News (2010 August)
- GitFlow considered harmful | Hacker News (2015 June)
- That "git-flow” (2016 February)
- What is wrong with “A successful Git branching model”? | Hacker News (2016 February)
- Please stop recommending Git Flow! | Hacker News (2020 March)
GitHub Flow
Useful for:
- continuous deployment to production, like with a web app
- code review through GitHub’s Pull Requests
Consider alternatives if:
-
specific releases must be maintained
-
Authoritative:
-
Perspectives:
- Git email flow vs Github flow | Hacker News (2021 March)
Others
- Git workflows - The Linux Kernel Archives
- GitLab Flow
- Comparing Workflows
- Git Feature Branch Workflow
- Trunk Based Development
- a simple git branching model (written in 2013)
- Patterns for Managing Source Code Branches
- Fossil vs Git | Hacker News
Resources and Documentation
Official Documentation:
- git-scm homepage
- Git - Documentation
- Git - Reference
- GUIDES
- The Git User’s Manual
- gittutorial(7) - a tutorial introduction to git: part one
- gittutorial-2(7) - a tutorial introduction to git: part two
- gitcore-tutorial(7) - a git core tutorial for developers
- gitrepository-layout(5) - git repository layout
- gitrevisions(7) - specifying revisions and ranges for git
- gitignore(5) - specifies intentionally untracked files to ignore
- gitglossary - a Git glossary
- merge-strategies
- GUIDES
- Pro Git book, written by Scott Chacon and Ben Straub
- Git - Reference
- Git - Documentation
- git(1) - online git-htmldocs
Others:
- Selected blog posts by Junio C Hamano (Git maintainer):
- Fun with first parent history
- How to inject a malicious commit to a Git repository (or not)
- Fun with --first-parent
- Where do evil merges come from?
- Checking the current branch programatically
- Fun with various workflows (1)
- Fun with Non-Fast-Forward
- Fun with recreating an evil merge
- Fun with a new feature in recent Git
- Tech Talk: Linus Torvalds on git
- Re: [git pull] drm-next, Linus’ response and thoughts regarding repository history
- Rebasing and merging: some git best practices, largely a breakdown of Re: [git pull] drm-next
- The Thing About Git
- Stay away from rebase | Hacker News
- Squash your commits
- Git rebase: apply your changes onto another branch
- The Case for Git Rebase
- Why you should use git merge --no-ff when rebasing
- Why do git branching diagrams not track branches "correctly"?
- Understanding When To Use Git Rebase
- How is git commit sha1 formed
- What is the internal format of a Git tree object?
- Why Do the Same Git Commits Not Have the Same Hash?
- GitMinutes podcast episodes
- Git rebase --preserve-merges fails
- Rebase from the ground up
Frequently Asked Questions (FAQ)
How to access Git command man pages?
Generally, via:
man git-[COMMAND]
e.g. for git add
, one would use:
man git-add
Why do git command man pages use a dash?
Short answer: history.
Longer answer: Git used to install every command as a separate script. A change was made to create git
as a wrapper that new the location of each subcommand so that git-init
became git init
.
What is meant by “Git tracks content, not files”?
I think the difference is more clear if the quote is restated as:
“Git tracks file content, not just file paths”
Let’s imagine Git did track files just by their paths. If we ran:
git add some/path/foo
then we would expect any further change made to foo to be reflected in the difference between our last commit and the current state of the working tree.
But Git does something different. When we run:
git add some/path/foo
the actual contents of some/path/foo are added to the index, allowing continued modification of foo. We can even delete foo from the working tree and Git continues to work fine: the file and its contents we added to the index will still be there.
Remember: the index is a snapshot of the repository contents ready to be committed.
I find it helpful to think of git-add as saying: “add the contents of the specified file(s), as they exist during invocation time, to the snapshot”.
By metaphor:
The index is a photo collage of all the contents within the repository. git-add says “hey, for these specific contents, use this photo in the collage instead” and gives the index a photo of whatever those contents looked like at invocation time.
What does the double dash (“--”) mean in command examples?
Do not interpret any further arguments as options.
Why doesn’t sha1sum of my file equal its object name?
There is additional meta data included.
What does filemode 100644 refer to?
A normal file. Git restricts the number of valid file modes. More information from this stackoverflow answer and its related links.
What is meant by “porcelain” and “plumbing”?
The analogy is to indoor plumbing: the pipes (plumbing) are typically not changed often but the porcelain (toilets and vanities) can be swapped as needed to provide a better user interface. In Git’s case, the plumbing commands are relatively stable and scriptable. The porcelain commands are just the user interface that Git ships with; other porcelains, like magit, are available.
The plumbing commands are data transfer
Does Git track changes? Does Git store deltas?
Git stores a snapshot of the working tree, as a tree object, on every commit. Any changes are calculated as a diff between commits. No actual diff is stored.
Confusion gets introduced because Git does use deltas under the hood: under certain conditions—like pushing to a remote repository—Git will compress its object database into pack files that store someoriginal object and then a series of deltas necessary to recreate the object at various commits.
What is a “three-way merge”?
A three-way merge of some file foo between branches branch-a and branch-b uses:
- branch-a and branch-b’s common ancestor’s version of foo
- branch-a’s version of foo
- branch-b’s version of foo
See merge-strategies and git-merge(1) for more information.
What is meant by “in Git, branches are cheap”? Why are branches free? Why does Git have cheap branching?
Creating a branch foo is just the creation of a reference refs/heads/foo that points to a commit (i.e. contains a commit hash). No Git objects or files are copied or duplicated. It helps to remember that a project’s history is comprised solely of interconnected commits, and branches are just pointers to commits. The linear sequences that form “backward” from these pointers are referred to as “branches”.
Git Terminology
- FETCH_HEAD
- a symbolic ref that contains the name of the most recently fetched ref, like a remote branch
- HEAD
- a symbolic ref that contains the name of the currently checked out branch
- author
- the creator of the patch, which may be different than the committer in certain workflows
- blob
- an object type representing untyped data, like file contents
- branch
- noun: a pointer from .git/refs to a commit within the object database; noun: a line of commits chronicling development
- cache
- obsolete term. see index
- commit
- noun: an object type representing the state of the repository at a specific point in time; AKA rev, revision
- commit-ish
- an object that can be recursively dereferenced to a commit object
- committer
- the creator of the commit, which may be different than the author in certain workflows
- downstream / downstream repository
- the repository that pulls from a remote repository to keep up to date
- hash
- the object name / identifier; the SHA-1 of the object’s contents
- head
- the most recent commit of a branch; also, specifically: a ref under refs/heads/branchName that points to the most recent commit of branchName
- history
- the directed, acyclic, graph (DAG) of a repository’s commits; AKA revision graph
- hunk
- an area where file contents differ
- identifier
- the hash (SHA-1), ref, or other means of identifying a git object
- index
- the stored version of your working tree; acts as staging area for commits and other Git operations, like merges; AKA staging area, cache (obsolete)
- object / Git object
- the immutable Git unit of storage, named and identified by the SHA-1 of its contents
- object store
- the .git/objects/ directory containing the objects themselves, sequestered into 256 possible two-character sub-directories based on their object name (hash), and information and data related to the store’s operation
- reference / ref
- a named pointer to an object or another ref; typically starts with refs/, such as refs/heads/main, but may be special refs like HEAD or MERGE_HEAD, and may be used in Git commands through unambiguous abbreviations
- remote / remote repository
- another repository used to track development
- repository
- the working tree and local Git information (the refs and object database that comprise the history of a project, as tracked by Git, and—by default—co-located with the working tree via the top-level .git/ subdirectory)
- rerere
- “reuse recorded resolution”; must be turned on to use
- revision / rev
- the name (SHA-1 id) of a Git object; typically, but not necessarily, a commit (example:
git-show
) - revision graph
- see history
- revision-ish / rev-ish
- an object that can be recursively dereferenced to a revision
- staging area
- see index
- symbolic ref
- a ref that points to another ref, like HEAD
- tip
- the head of the branch in question
- topic branch
- AKA “feature branch”, “project branch”
- tree
- an object type representing a directory; may contain references to other trees or blobs
- trunk
- the authoritative, canonical history of a repository, following first-parents from the main tip
- upstream
- the remote repository that is pulled from to keep up to date; in some workflows it is also pushed to by downstream repositories
- working directory
- see working tree
- working tree
- the actual directory containing checked out files and local changes; AKA working directory
- “evil” merge
- a merge where edits are made that did not exist in any of the merge parents