Git Reference

Kip Landergren

(Updated: )

Contents

Commands

git(1)

git(1) documentation

High-Level Commands (Porcelain)

Main Porcelain Commands

git-bisect(1)

git-bisect(1) documentation

Use binary search to find the commit that introduced a regression / bug.

Tips:

git-branch(1)

git-branch(1) documentation

List, create, or delete branches.

git-clean(1)

git-clean(1) documentation

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)

git-clone(1) documentation

Clone a repository into a new directory.

Notes:

git-commit(1)

git-commit(1) documentation

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)

git-diff(1) documentation

Shows changes between:

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)

git-log(1) documentation

Tips:

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)

git-merge(1) documentation

Tips:

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)

git-rebase(1) documentation

Reapply commits on top of another base tip.

Notes:

git-reset(1)

git-reset(1) documentation

Reset current HEAD to the specified state.

Cancel last commit:

git reset --hard HEAD^
git-restore(1)

git-restore(1) documentation

Restore working tree files.

Restore all files in the current directory:

git restore .
git-show(1)

git-show(1) documentation

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)

git-cat-file(1) documentation

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-list(1) documentation

git-rev-parse(1)

git-rev-parse(1) documentation

Ancillary Commands

Manipulators
git-config(1)

git-config(1) documentation

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-blame(1) documentation

git-help(1)

git-help(1) documentation

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)

gitignore(5) documentation

Keep in mind:

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:

Consider alternatives if:

GitHub Flow

Useful for:

Consider alternatives if:

Others

Resources and Documentation

Official Documentation:

Others:

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:

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