Git Cheat Sheet, Overview & Tutorial

Introduction

This is my personal cheat sheet for Git, which I built as I learned the tool more in depth in the past few days, cleanly formatted for your enjoyment (God knows it didn’t look like this before I started bringing all of it into WordPress!).

If you’re already fluent using a version control system such as SVN you should be able to get a good grasp of Git by reading this article. Though if you’re looking for a complete, extensive multi-page tutorial, I suggest this one which is very well-written and easy to follow: http://book.git-scm.com/index.html.

I’ve tried to include as much relevant information in as little space as possible. If you have some favorite commands and tips of your own, please share them in the comments. Enjoy!

Important Git Facts to Keep In Mind

  • Each Git repository clone contains the whole commit history and all branches (including master, which you could consider the equivalent of a SVN project’s trunk). You use git clone instead of svn checkout to clone a whole repository onto your own machine. Example:
    git clone git://git.videolan.org/vlc.git
    
  • The concept of branches, tags and trunk (the master branch, in Git’s case) is part of Git itself. This structure is not visible on disk, it is stored in myproject/.git in compressed form; you can switch between branches using git checkout [branch name] which replaces the contents of the working copy with the branch in question.
  • Because of its distributed nature, Git doesn’t have monotonic revision numbers, it rather uses SHA-1 hashes as commit identifiers. Still, it is possible to obtain monotonically-increasing commit names using git describe and other commands. While this may seem awkward, I didn’t find this to be an issue because of how tags work in Git.
  • You can refer to commits using only part of their commit name (the SHA-1 hash). As long as no other commit name contains the same string the commit will be found.
  • Git workflows can vary a lot. They can be very inventive or they can pretty much match the kind of workflow you may be used to with SVN or other version control systems. See the “Any Workflow” section in this page for some very nice and clear examples of various Git workflows.

Configuration

Before committing code to a repository, you should setup your Git user name and email so that your commits contain the correct contact information. There are also some interesting global variables that you can tweak before you begin working with Git.

# User configuration, shown in commit log.
git config --global user.name = "John Doe"
git config --global user.email = "john.doe@email.com"
# Activate colored output. IMHO makes "git diff" and "git log --graph"
# much clearer and easier to follow.
git config --global color.diff = auto
git config --global color.status = auto
git config --global color.branch = auto

The --global flag stores the settings in the ~/.gitconfig file which applies automatically to all projects configured for the current user on the machine. You can set variables on a per-project basis by using the same command without the --global flag within the project’s directory.

Creating Repositories

Normal repositories contain both the repository (history, branches, tags, etc.) and a working copy, while bare repositories contain only the repository and no working copy. This makes bare repositories ideal for server-side or centralized repositories where no work will be done directly.

# Bare repository
git --bare init
# Normal repository
git init

Staging & Committing

Git has what’s called the staging area or index. Here’s how it works:

The Git add / commit / push & pull process

The git add command effectively stages new, removed and even modified files for the next commit. If you don’t stage changes, they won’t be committed. This means that by using git add filename and git status (which prints the staging area’s contents as well as information about what’s not staged) you can add part of your changes to the staging area and commit only that. You can then commit the rest afterwards. If you would rather commit all changes (added, modified and removed files), you can use git add -A and git commit -a. More below:

Adding files

# Adds and stages a single file
git add myfile.txt

# Stages all files, including new files that haven't been added yet.
git add -A

Staging and committing

# Stage all files that have previously been added at least once (-a) and commit.
# New files aren't staged automatically.
# Use git add -A or git add filename for this.
git commit -a

# Add to the last commit (very useful!).
git commit -a --amend

# Stage, commit and specify an inline commit message.
git commit -a -m "This is a description."

Seeing what’s staged and what’s not

# Detailed output
git status

# Compact output
git status -sb

The staging area can be useful, so make sure you read a bit on this subject before compulsively appending the “-a” option to all commit commands. For more information about the staging area or “index”:
Git Community Book – The Git Index
git ready – the staging area

The Commit Message

Regarding the commit message, it is recommended (but not necessary) to include a small summary as the first line (about 50 characters or less), then leave the second line empty and enter the rest of the commit details below. The syntax highlighting in the commit message editor (vim) will indicate this by making the first 50 characters or so a different color and marking the second line as an error if it’s not empty.

A non-optimal Git commit message
A better Git commit message

Pushing & Pulling

Because you have a complete clone of the repository on your machine, what you’re actually doing when using git commit is committing to your own local repository. If you want to send the committed changes to a remote repository (whether on same machine, somewhere on your local network or on a web server), you must use the git push command. To obtain changes from a remote repository, use git pull.

A relevant quote regarding tags and git push (more in the Tags section of this article): “By default, tag objects (i.e. signed or annotated tags) attached to commits new to the remote being pushed to are pushed. Lightweight tags and tags attached to commits already on the remote aren’t pushed.” — http://stackoverflow.com/questions/2988088/do-git-tags-get-pushed-as-well

Setting up remote repositories

# Setting up the "origin", a default remote repository (usually once per project).
git remote add origin ssh://username@mydomain.com/~/repos/myproject

# Setting up another remote repository.
git remote add otherrepo ssh://username@myotherdomain.com/~/git/myproject

Remote repositories can also be accessed using HTTP and Git’s own protocol (git://). Obviously SSH has the security advantage. The Git protocol though has the speed advantage. HTTP can be useful when you want to be able to bypass firewalls and proxies.

Pushing

# Push all branches that are common to the remote and local repositories.
git push

# Push the branch "master" to the "origin" repository.
git push origin master

# Push tags to the remote repository (tags are NOT pushed automatically!).
git push --tags

Pulling

# Pull changes from origin remote repository.
# Fetches from repository then merges into current branch.
git pull

Status, Logs & Graph

Often you will want to know in what state your repository is. Using these commands you can obtain details about previous commits, branching and merging in the repository, changes between the working copy and the staging area, etc.

# Shows the commit log.
git log

# Shows the commit log for the repository, including tag / branch membership.
git log --decorate

# Obtain a graphical view of commits, branches and merges.
git log --graph --all

# Same as the previous command but with compact output.
git log --graph --all --oneline

# Shows what changed in the current branch since the last commit.
git status

Diffs

You can use git diff to see the differences between your working copy and the last commit, between different commits, in your local repository, or between various other things in your repositories.

Seeing what you will commit

# Working copy VS the staging area.
git diff

# Staging area VS the latest commit.
git diff --cached

# Working copy VS the latest commit.
git diff HEAD

Other comparisons

# Compare the master branch with the tag named "1.1".
git diff master..1.1

# Compare the master branch with a branch named "mybranch".
git diff master..mybranch

# Compare only the files and folders containing "Unit" in branch1 and branch2.
# Note the use of the "*" wildcard character. The "--" is a path delimiter.
git diff branch1..branch2 -- *Unit*

Tags

There are two types of tags in Git: lightweight tags and annotated tags. Lightweight tags are essentially a simple reference to a commit’s name, while annotated tags are actual objects in the Git repository database and contain additional information such as the tag author, date and other useful metadata.

It’s generally recommended to create annotated tags (using the -a option) because those are supported by git describe and other useful commands. You can at any point in time replace a lightweight tag with an annotated one by noting the commit name, deleting the lightweight tag and re-tagging the commit with an annotated tag.

Obtaining information about tags

# List all tags.
git tag

# Show the commit log including tags and branches
git log --decorate

# List details of the tag named "1.1b1".
git show 1.1b1

Working with tags

# Tags the latest commit with the name "1.1b1" (annotated tag).
git tag -a 1.1b1

# Tags the commit with the name that begins with "1b2e1d63ff"
# with the tag name "1.1b1".
git tag -a 1.1b1 1b2e1d63ff

# Tags the latest commit with the name "1.1b1" and a message.
git tag -a 1.1b1 -m 'Version 1.1 beta 1 (pre-release)'

# Deletes the tag named "1.1b1".
git tag -d 1.1b1

Branches

You can easily create, remove and merge branches with Git. As explained in the Important Git Facts to Keep In Mind section, branches are not stored as-is in the filesystem but are rather stored in the hidden .git folder in your project’s directory. When you use git checkout mybranch, the whole working copy is replaced with the “mybranch” branch. You can switch back to the master branch by using git checkout master.

# Lists all branches in the local repository.
git branch

# Creates a new branch named "mybranch" based on the current branch.
git branch mybranch

# Switches the working copy to "mybranch".
git checkout mybranch

# Creates a new branch named "mybranch" and switches to it right away.
git checkout -b mybranch

# Deletes the branch named "mybranch" but only if the branch
# doesn't contain commits that haven't been merged into the current branch.
git branch -d mybranch

# Deletes the branch named "mybranch" regardless
# of differences with the current branch.
git branch -D mybranch

Checking out a branch with local changes

When using git checkout to switch to another branch, if your current branch has local changes, Git can either try to carry your local changes across to the new branch or discard the changes altogether. See this Stack Overflow question for more details.

Merge

Of course if you branch some work you will want to merge it back into your master branch after your work is done. The git merge command does this. Note that when you use git pull, Git actually fetches the changes from the remote repository then merges them into the current repository.

# Merges the branch "mybranch" into the current branch.
git merge mybranch

Fixing merge conflicts

If there are conflicts after a merge, Git will have inserted conflict markers in the conflicted files. The git status command will show those files are “unmerged”. All you need to do is to edit those files to fix the conflicts, then stage and commit them. Note that when committing after fixing merge conflicts, you don’t need to specify a commit message (but you can); the commit message will have already been filled in with information about the merge.

If the conflict is in a binary file, you can choose either your current branch’s file or the merge source’s file as the correct one using these commands:

# Replace the current branch's file with the version in the merge source branch.
git checkout --theirs -- path/to/conflicted-file.txt

# Consider the file in the current branch to be the correct one.
git checkout --ours -- path/to/conflicted-file.txt

Related useful commands

# Returns the working copy to pre-merge state.
# Use this if your merge wasn't successful and you want to start over.
git reset --hard HEAD

# Replace a file with the version in the latest commit.
git checkout filename

For more information on git merge see this page:
Git Community Book – Basic Branching and Merging

Conclusion

That’s it! For now that’s what I’ve found to be the most important knowledge to get going using Git (at least in a one-man-team context). I hope you learned some things reading this. I’ll make sure to post some new interesting Git tips as I discover them, so I encourage you to subscribe to the RSS feed if you liked what you read. You are also invited to post some feedback in the comments if you find inaccuracies, if you have something interesting to share or simply if you have something to say about the article. Until next time!

This entry was posted in Version Control. Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>