git
git is a very powerful distributed version control system allowing people to track changes to and work together on all kinds of (text)files. Alternative free and open-source version control systems are Fossil, Darcs and Mercurial.
This wiki will NOT cover the very basics of git. Please read the first two chapters of the freely available ProGit book to for an introduction to git.
Writing clean commits
When creating commits, try to follow this advice1:
- Write good commit messages. Apart from following the correct formatting (header + body + commit trailers), make sure that your commit message is informative. Keep the description of what your changes do short (make sure the code is as self-explanatory as possible) and rather focus on why you chose to implement things this way rather than another one.
- A commit should be a self-contained change (i.e. atomic) and represent a unit of work (feature, bug fix, etc.). Avoid clumping separate things together or drawing out the implementation of the same feature of multiple commits. Ideally, every single commit in the history of your project would compile and pass the tests.
- Follow the standards of the project you are working on: While all of us might have personal preferences, it aids collaboration if everyone plays by some agreed upon rules. Use pre-commit hooks extensively, if that aids you.
Specifying revisions
Pointing to specific commits can be done in a myriad of ways in git.
Some examples for the most common use cases:
git checkout <branchname>
git checkout <commit hash>
git checkout <tag>
git checkout HEAD^{n} # n-th parent of HEAD (remember: commits can have multiple parents)
git checkout HEAD~{n} # n-th generation ancestor following first parent
Specifying ranges
Just like for individual revisions (i.e. commits), git also has multiple
ways for specifying commit ranges since there are git commands which
operate on commit ranges (e.g. git log).
When specifying ranges, the notion of what is reachable from a given revision is central:
A commit’s reachable set is the commit itself and the commits in its ancestry chain.
Some common syntax cases for specifying ranges include:
git log <rev> # include commits that are reachable from <rev>
git log ^<rev> # exclude commits that are reachable from <rev>
git log <rev1>..<rev2> # include commits that are reachable from <rev2> but exclude those that are reachable from <rev1>
git log <rev1>...<rev2> # include commits that are reachable from either <rev1> or <rev2> but exclude those that are reachable from both.
Storing executable bits
Git has the ability to track executable bits (not read/write permissions though).
On POSIX systems, git picks up changes to the executable bit from the filesystem and those "changes" to the file can be commited as usual. On Windows, you need to use the
git add --chmod +/-x <executable>
crutch.
For more details, see here.
Inspecting diffs
git diff and git diff --cached give you the changes in the working
directory or index, respectively.
Since git by default diffs line-by-line, this can be a problem if you're
working with very long lines. To view the changes on a character level,
adapt the git diff call thus:
git diff -U0 --word-diff-regex=.
To follow the diffs from commit_a to commit_b in chronological order, use:
git log --reverse -p commit_a_hash...commit_b_hash
git bisect
git bisect is a powerful tool to find commits which introduced
regressions. Basically, you tell git two things: a good commit where a
feature worked as expected and a subsequent bad commit where the feature
is broken; somewhat like this:
git bisect start
git bisect bad # assuming the currently checked out commit has the bug
git bisect good <pointer_to_good_commit> # a commit where you know that the bug is not present
git will then take a commit in the middle of that range and ask you to
tell it whether the bug exists at this point in the git history. If you
indicate that the bug exists with
git bisect bad
git will choose a commit between the initial good commit and the current
commit. Should you indicate that the bug does not exist with
git bisect good
git will checkout a commit between the current commit and the initial bad
commit. This process of checking a particular commit and passing the
feedback to git bisect repeats until git bisect can single out the one
commit which introduces the breakage.
For more information on git bisect, have a look at the
documentation.
Branch management
Delete remote branch
Run
git push <remote name> --delete <branchname>
to delete the branch <branchname> in the remote <remote name>
repository.
Searching the commit history
This wiki section will focus on very specific use cases of working with the git commit history. For more general information on viewing the git commit history, refer to chapter 2.3 of the ProGit book.
List all authors in repository
To list all authors in a repository with commit count and email, run
git shortlog -e -s -n
Add --no-merges to the command options to exclude merge commits from
the statistic.
Finding deleted files
Sometimes you want to find information on or restore files that have been deleted from the repository in the past. Obviously, those are not around anymore in the working directory with the checked out files, but the history of the deleted files is still preserved in the git repository.
Now, the difficulties start with finding the deleted file, e.g. maybe you don't remember the full path to the file. Run
git log --diff-filter=D --summary
to show all commits which have deleted files.
If you know the filename, simply use
git log -- path/to/file
to show all commits for that file.
To then restore the file from a specific revision, run:
git checkout REVISION -- path/to/file
Working with patches
Before software forges like Microsoft Github or Gitlab have made the Pull Request based collaboration workflow popular, developers used to collaborate with git by sending around patches via email.
Creating a patch
To create one/or multiple patches, run
git format-patch <single commit identifier>
to create patches for all commits since the specified commit. Alternatively, you can run
git format-patch <revision range>
to create patches for all commits in the specified revision range. For a single commit use
git format-patch -1 <single commit identifier>
See the documentation page for more information.
Applying a patch
If someone has sent you a .patch file, you can apply it to the
HEAD of the current repository in different ways:
If you want to apply the patch to your index without commiting yet, run:
git apply <path/to/patchfile>
More often, however, you will want to create a commit from the patchfile which preserves the original author information. To do so, run:
git am <path/to/patchfile>
If the patch does not apply, you can either ask the submitter to fix the patch or try to fix it up yourself. If you decide to do it yourself, you can use
git apply -3 <path/to/patchfile> # requires you to have the commit the diff in the patch is applied to locally
or
git apply --reject --whitespace=fix <path/to/patchfile>
While the former command will create a merge with conflict markers, the
latter will break the commit into hunks, apply those that apply cleanly, and
store the rejected hunks in .rej files (which you can then fix manually or
using wiggle).
Mailing a patch
In short,
git send-email --to="mailing-list@project.org" COMMIT_OR_REVISION_RANGE
Please make sure to use git send-email to mail around patches as
other tools tend to break the patch format.
For more details on the installation and configuration of git send-email
please consult this excellent guide.
Submodules
Sometimes it happens that you need the files from one (git-versioned) project within another. To ease administering local and upstream changes of the included project, git has the concept of submodules.
adding submodules
To add a submodule to your project, run:
git submodule add <SUBMODULE_REPO>
and commit the changes to the .gitmodules file and the newly checked
out folder to the parent project repository.
cloning projects with submodules
The normal git clone command will not fetch and checkout the
submodules' contents. To include those, run
git clone --recurse-submodules <REPO_URL>
If you have forgotten to specify the --recurse-submodules option,
you can load the submodules retroactively by
git submodule update --init --recursive
working with submodules
How you use the submodule will drive the way you interact with it in git. Please refer to the "Working on a Project with Submodules" section in the submodules documentation which outlines different use cases.
git and the web
While one can use git locally on a single machine to track changes to files over time, the tool itself was built in a way to allow collaborating with others over the internet.
web frontends
- cgit (see also the ArchWiki entry for cgit)
- stagit
forge software with git support
See also
- Pro Git book
- How to use
gitwith email - Using git with discipline
git rebasein depth- How git core devs configure git
- cheat sheets:
- Oh Shit, Git!?!
-
Slightly modified from this blog post of Drew Devault. ↩