Git
Git is a source code management tool designed for managing the development of software (Private) projects, originally built to replace BitKeeper for Linux kernel development. Its key innovation is the use of a DAG to represent relationships between commits.
Git is distributed: all repositories are a complete copy of the state of the repository at the time they fetched changes. There is no requirement for a canonical, centralised repository: the Linux kernel uses a series of forks for development and merging, with changes gradually flowing between maintainer forks until they reach Linus's tree.
Concepts
- Repositories are the root.
- Commits make up the history, describing the patches included in each file in the change set.
- References represent positions or entries in the history:
- Tree-ish can be dereferenced to a tree.
- Commit-ish can be dereferenced to a commit.
- Branches represent places where the history diverges.
- Tags represent release versions of software.
- The Staging area contains files staged for the following commit.
- The Working tree represents our working copy of the files.
- Remotes are other repositories.
HEAD
represents the location of the checkout.
Configuration levels
Git configuration can be stored at the following levels, with the most specific winning:
- System configuration is typically stored in
/etc/gitconfig
and covers all users. - Global refers to user configuration, usually at
~/.gitconfig
. - Repository configuration is stored in
.git/config
. - Worktrees each have their own
.git/config
file. - Specified file with the
--file
or--blob
switches.
Merging
Git has multiple merge strategies:
- Fast forwards can be applied when the mergee shares a common base with the destination branch. This can be achieved via a rebase if necessary.
- Merge commits are required when there's no common base, or the contents of the objects would be in conflict.
Merge commits have two parents, one for each of the conflicting histories.
Merging a branch that already contains a merge commit resolving a conflict back into one of the branches that initiated the conflict is a fast-forward operation: since the content is the same, there's no need to create another object.
Detaching your HEAD
Git allows you to directly checkout a specific commit, leading to a detached HEAD state. When doing this, the HEAD
file's ref
value will point directly at a commit. This even allows committing: the HEAD moves with each commit.
If we don't checkout a new branch based on these commits, the dangling commits will eventually be pruned by git gc
.
Distribution
All clones are a complete copy of the repository's history: they're equal peers from a technical perspective.
Remotes are stored in .git/config
:
[remote "origin"]
url = git@github.com:git/git.git
fetch = +refs/heads/*
Usage
Git's CLI is split into "porcelain" and "plumbing" commands. The former at intended for user interaction, the latter expose Git's internals.
git init
git remote add name url
git clone
git switch my-branch
git switch -c new-branch
git rebase origin/master
git rebase -i origin/master
git log
git branch -v
git branch new-branch
git merge
git fetch
git push
git pull
git merge-base feature-branch base-branch
git grep
across branches and tags
git grep
searches all tracked objects for the given pattern, and can be useful in software archeology. This command searches all local revisions rather than just those in the current branch's history:
git rev-list --all | (while read rev; do git grep Helm $rev; done)
You can narrow down the search space by only checking branches, tags, etc.:
git rev-list --branches | (while read rev; do git grep Helm $rev; done)
The following alias in ~/.gitconfig
reduces this to git grep-all branches Helm
:
[alias]
grep-all = "!f() { local type="--${1}"; shift; git rev-list $type | (while read rev; do git grep "$@" "$rev"; done); }; f"
Configuration
Line endings on Windows
There are two different characters, CR and LF, that can be used as EOL characters to break a line. Naturally there's little consistency to their use:
- Linux, macOS and other POSIX systems use LF.
- Windows uses CRLF.
- Older versions of macOS used CR.
To compensate for this Git provides a tool for conversion of line endings on platforms, exposed via the core.autocrlf
option, which has three possible values:
input
means "use the files as they appear in the repository".true
enables converting to CRLF on checkout and LF on commit.false
means leaving them as they appear in the checked out files.
Using Meld as merge tool
First of all, install Meld; on Windows using their binaries, on Linux using your package manager.
Configure Git to use it, on Linux:
git config --global diff.tool meld
git config --global merge.tool meld
On Windows:
git config --global difftool.meld.path 'C:\Program Files (x86)\Meld\meld\meld.exe'
git config --global diff.tool meld
git config --global mergetool.meld.path 'C:\Program Files (x86)\Meld\meld\meld.exe'
git config --global merge.tool meld
Have pull
only apply fast-forwards
You can force git pull
to only allow a merge with the fast forward merge strategy:
git config --global pull.ff only
Submodules
Make pull
and fetch
automatically update submodules:
git config --global submodule.recurse true
Interactive ergonomics
Setting interactive.singleKey
to true
removes the need to press Enter> in a bunch of scenarios, particularly during staging changes in --patch
mode. Note that it requires an otherwise optional dependency: Perl's Term::ReadKey
.
Children
- Attributes
- Bisect
- Flow
- Hooks
- Ignore
- LFS
- Object database
- Rebasing
- Staging from diffs
- Submodules
- Subtrees
- Worktrees
- git-crypt
Backlinks