Version 5.3
Copyright © 2009, 2010, 2011, 2012, 2013 Lars Vogel
02.04.2013
| Revision History | |||
|---|---|---|---|
| Revision 0.1 | 13.09.2009 | Lars Vogel |
Created |
| Revision 0.2 - 5.3 | 21.11.2009 - 02.04.2013 | Lars Vogel |
bug fixes and improvements |
Table of Contents
A version control system allows you to track the history of a collection of files and includes the functionality to revert the collection of files to another version. Each version captures a snapshot of the file system at a certain point in time. The collection of files is usually source code for a programming language but a typical version control system can put any type of file under version control.
The collection of files and their complete history are stored in a repository.
The process of creating different versions (snapshots) in the repository is depicted in the following graphic. Please note that this picture fits primarily to Git, another version control systems like CVS don't create snapshots but store file deltas.

These snapshots can be used to change your collection of files. You may, for example, revert the collection of files to a state from 2 days ago. Or you may switch between versions for experimental features.
A distributed version control system has not necessary a central server which stores the data.
The user can copy an existing repository. This copying process is typically called cloning in a distributed version control system and the resulting repository can be referred to as clone.
Typically there is a central server for keeping a repository but each cloned repository is a full copy of this repository. The decision which of the copies is considered to be the central server repository is a pure convention and not tied to the capabilities of the distributed version control system itself.
Every clone contains the full history of the collection of files and a cloned repository has the same functionality as the original repository.
Every repository can exchange versions of the files with other repositories by transporting these changes. This is typically done via a repository running on a server which is, other than the local machine of a developer, always online.
Git is a distributed version control system.
Git originates from the Linux kernel development and is used by many popular Open Source projects, e.g. the Android or the Eclipse Open Source projects, as well as by many commercial organizations.
The core of Git was originally written in the programming language C but Git has also been re-implemented in other languages, e.g. Java and Python.
After cloning or creating a repository the user has a complete copy of the repository. The user performs version control operations against this local repository, e.g. create new versions, revert changes, etc.
There are two types of Git repositories:
bare repositories used on servers to share changes coming from different developers
working repositories which allow you to create new changes through modification of files and to create new versions in the repository
If you want to delete a Git repository, you can simply delete the folder which contains the repository.
Git allows the user to synchronize the local repository with other (remote) repositories.
Users with sufficient authorization can push changes from their local repository to remote repositories. They can also fetch or pull changes from other repositories to their local Git repository.
Git supports branching which means that you can work on different versions of your collection of files. A branch separate these different versions and allows the user to switch between these version to work on them.
For example if you want to develop a new feature, you can create a branch and make the changes in this branch without affecting the state of your files in another branch.
Branches in Git are local to the respository. A branch created in a local repository, which was cloned from another repository, does not need to have a counterpart in the remote repository. Local branches can be compared with other local branches and with remote tracking branches. A remote tracking branches proxy the state of branches in another remote repository.
Git supports that changes from different branches can be combined. This allows the developer for example to work independently on a branch called production for bugfixes and another branch called feature_123 for implementing a new feature. The developer can use Git commands to combine the changes at a later point in time.
For example the Linux kernel community used to share code corrections (patches) via mailing lists to combine changes coming from different developers. Git is a system which allows developers to automate such a process.
The user works on a collection of files which may originate from a certain point in time of the repository. The user may also create new files or change and delete existing ones. The current collection of files is called the working tree.
A standard Git repository contains the working tree (single checkout of one version of the project) and the full history of the repository. You can work in this working tree by modifying content and committing the changes to the Git repository.
If you modify your working tree, e.g. by creating a new file or by changing an existing file, you need to perform two steps in Git to persist the changes in the Git repository. You first add selected files to the staging area and afterwards you commit the changes of the staging area to the Git repository.
The staging area term is currently preferred by the Git community over the old index term. Both terms mean the same thing.
You need to mark changes in the working tree to be relevant for Git. This process is called staging or to add changes to the staging area.
You add changing in the working tree to the
staging area
with the
git add
command. This command stores a snapshot of the specified files in
the
staging area.
The
git add
command allows you to incrementally modify files, stage them,
modify
and
stage
them again until you are satisfied with your changes.
After adding the selected files to the staging area, you store this change in the Git repository. Storing the changes in the Git repository is called committing. Committing creates a new persistent snapshot (called commit or commit object) of the complete working tree in the Git repository. A commit object is immutable.
The staging area keeps track of the snapshots of the files until the staged changes are committed.
For committing the staged changes you use the
git commit
command.
This process is depicted in the following graphic.

If you commit changes to your Git repository, you create a new commit object in the Git repository. This commit object is addressable via a SHA-1 checksum. This checksum is 40 byte long and is a secure hash of the content of the files, the content of the directories, the complete history of up to the new commit, the committer and several other factors.
This means that Git is safe, you cannot manipulate a file in the Git repository without Git noticing that SHA-1 checksum does not fit anymore to the content.
The commit object points via a tree object to the individual files in this commit. The files are stored in the Git repository as blob objects and might be packed by Git for better performance and more compact storage. Blobs are addressed via their SHA1 hash.
Packing involves storing changes as deltas, compression and storage of many objects in a single pack file. Pack files are accompanied by one or multiple index files which speedup access to individual objects stored in these packs.
A commit object is depicted in the following picture.

The above picture is simplified. Tree objects point to other tree objects and file blobs. Objects which didn't change between commits are reused by multiple commits.
The tooling for Git is originally based on the command line. These days there is a huge variety of available Git tools.
You can use graphical tools, for example EGit for the Eclipse IDE.
The following table provides a summary of important Git terminology.
Table 1. Git Terminology
| Term | Definition |
|---|---|
| Repository |
A repository contains the history, the different versions over time and all different branches and tags. In Git each copy of the repository is a complete repository. If the repository is not a bare repository, it allows you to checkout revisions into your working tree and to capture changes by creating new commits. Bare repositories are only changed by transporting changes from other repositories. This tutorial uses the term repository to talk about a non bare repository. If it talks about a bare repository this is explicitly mentioned. |
| Working tree | The working tree contains the content of a commit which you can checkout from the Git repository. You can modify the content and commit the changes as new commits to the repository. |
| Branches |
A branch is a named pointer to a commit. Selecting a branch in Git terminology is called to checkout a branch. If you are working in a certain branch, the creation of a new commit advances this pointer to the newly created commit. Each commit knows commits only know their parents (predecessors). Successors are retrieved by traversing the commit graph starting from branches, refs, symbolic links (e.g. HEAD) or explicit commit objects. This way a branch defines its own line of descents in the overall version graph formed by all commits in the repository. You can create a new branch from an existing one and change the code independently from other branches. One of the branches is the default (typically named master). The default branch is the one for which a local branch is automatically created when cloning the repository. |
| Tags |
A tag points to a commit which uniquely identifies a version of the Git repository. With a tag, you can have a named point to which you can always revert to. Branches and tags are named pointers, the difference is that branches move when a new commit is created while tags always point to the same commit. |
| Commit |
You commit your changes into a repository. This creates a new commit object in the Git repository which uniquely identifies a new revision of the content of the repository. This revision can be retrieved later, for example if you want to see the source code of an older version. Each commit object contains the author and the committer, thus making it possible to identify who did the change. The author and committer might be different people. The author did the change and the committer applied the change to the Git repository. |
| URL | A URL in Git determines the location of the repository. |
| Revision | Represents a version of the source code. Git implements revisions as commit objects (or short commits). These are identified by a SHA-1 secure hash. SHA-1 ids are 160 bits long and are represented in hexadecimal. |
| HEAD |
HEAD is a symbolic link most often pointing to the currently checked out branch. Sometimes the HEAD pointer points directly to a commit object, this is called detached HEAD mode. In that state creation of a commit will not move any branch. The first successor of the HEAD pointer can be addressed via HEAD~1, HEAD~2 and so on. If you switch branches the HEAD pointer moves to the last commit in the branch. If you checkout a specific commit the HEAD points to this commit. |
| Staging area | The staging area is the place to store changes in the working tree before the commit. The staging area contains the set of the snapshots of changes in the working tree (change or new files) relevant to create the next commit and stores their mode (file type, executable bit). |
| Index | Index is an alternative term for the staging area. |
A file in the working tree of a Git repository can have different states. These states are the following:
untracked: not tracked by the Git repository, this means it was neither staged, i.e. added to the staging area nor committed
tracked: committed and not staged
staged: staged to be included in the next commit
dirty / modified: changed but the change is not staged
You can use ^ (caret) and ~ (tilde) to reference predecessor commits objects from other references. Predecessor commits are sometimes also called parent commits. You can combine the ^ and ~ operators.
[reference]~1 describes the first predecessor of the commit object accessed via [reference]. [reference]~2 is the first predecessor of the first predecessor of the [reference] commit. [reference]~3 is the first predecessor of the first predecessor of the first predecessor of the [reference] commit, etc.
[reference]~ is an abbreviation for [reference]~1.
For example you can use the HEAD~1 or HEAD~ reference to access the first [reference] of the commit to which the HEAD pointer currently points.
[reference]^1 also describes the first predecessor of the commit object accessed via [reference].
The difference is that [reference]^2 describes the second predecessor of a commit. A merge commit has two predecessors.
[reference]^ is an abbreviation for [reference]^1.
You can also specify ranges of commits. This is useful for certain Git commands for example for seeing the changes between a series of commits.
The double dot operator allows you to select all commits which are reachable from a commit c2 but not from commit c1. The syntax for this is "c1..c2". A commit A is reachable from another commit B, if A is a direct or indirect predecessor of B.
Think of c1..c2 as all commits as of c1 (not including c1) until commit c2.
For example you can ask Git to show all commits which happened between HEAD and HEAD~4.
git log HEAD~4..HEAD
This also works for branches. To list all commits which are in the "master" branch but not in the "testing" branch use the following command.
git log testing..master
You can also lists all commits which are in the "testing" but not in the "master" branch.
git log master..testing
The triple dot operator allows you to select all commits which are reachable either from commit c1 or commit c2 but not both of them.
This is useful to show all commits in two branches which have not yet been combined.
# show all commits which # can be reached by master or testing # but not both git log master...testing
On Ubuntu and similar systems you can install the Git command line tool via the following command:
sudo apt-get install git
On Ubuntu and similar systems you can install the Git command line tool via the following command:
yum install git
To install Git on other Linux distributions please check the documentation of your distribution. The following listing contains the commands for the most popular ones.
# Arch Linux pacman -S git # Gentoo emerge -av git # SUSE zypper install git
A windows version of Git can be found on the msysgit Project site. The URL to this webpage is listed below. This website also describes the installation process.
http://code.google.com/p/msysgit/
The easiest way to install Git on a Mac is via a graphical installer. This installer can be found under the following URL.
http://code.google.com/p/git-osx-installer
As this procedure it not an official Apple one, it may change from time to time. The easiest way to find the current procedure is to Google for the "How to install Git on a Mac" search term.
Git allows you to store global settings in the
.gitconfig
file located in the user home directory. Git stores
the
committer and
author of a change in each commit. This and
additional
information
can
be stored in the global settings.
In each Git repository you can also configure the settings for this
repository. Global configuration is done if you include the
--global
flag, otherwise your configuration is specific for the current Git
repository.
You can also setup system wide configuration.
Git stores theses values
is in the
/etc/gitconfig
file, which contains the configuration for every user and repository
on the system.
To set this up, ensure you have sufficient rights,
i.e.
root rights, in your OS and use the
--system
option.
The following configures Git so that a certain user and email address is used, enable color coding and tell Git to ignore certain files.
Configure your user and email for Git via the following command.
# configure the user which will be used by git # Of course you should use your name git config --global user.name "Example Surname" # Same for the email address git config --global user.email "your.email@gmail.com"
The following command configure Git so that the
git push
command pushes only the active branch (in case it is connected to a
remote
branch, i.e.configured as remote tracking
branches) to your Git
remote
repository.
As of Git version 2.0 this is the default and
therefore it is
good practice to
configure this behavior.
# set default so that only the current branch is pushed
git config --global push.default simple
# alternatively configure Git to push all matching branches
# git config --global push.default matching
If you pull in changes from a remote repository, Git by default creates merge commits. This is typically undesired and you can avoid this via the following setting.
# set default so that you avoid unnecessary commits
git config --global branch.autosetuprebase always
This setting depends on the individual workflow. Some teams prefer to create merge commits, but the author of this tutorial likes to avoid them.
The following commands enables color highlighting for Git in the console.
git config --global color.ui true git config --global color.status auto git config --global color.branch auto
By default Git uses the system default editor. You can configure a different one via the following setting.
# setup vim as default editor for Git (Linux)
git config --global core.editor vim
Git does not provide a default merge tool for integrating conflicting changes into your working tree. You have to use third party visual merge tools like tortoisemerge, p4merge, kdiff3 etc. A Google search for these tools help you to install them on your platform.
Once you have installed them you can set your selected tool as default merge tool with the following command.
# setup kdiff3 as default merge tool (Linux)
git config --global merge.tool kdiff3
# to install it under Ubuntu use
# sudo apt-get install kdiff3
All possible Git settings are described under the following link: git-config manual page
Git can be configured to ignore certain files and directories. This
is
configured in a
.gitignore
file.
This file can be in any directory and can contain patterns for
files.
You can use certain wildcards in this file.
*
matches several characters.
The
?
parameter
matches one character. More patterns are possible and
described under the following URL:
gitignore manpage
For example, the following
.gitignore
file tells Git to ignore the
bin
and
target
directory and all files ending with a ~.
# ignore all bin directories # matches "bin" in any subfolder bin/ # ignore all target directories target/ # ignore all files ending with ~ *~
You can create the
.gitignore
file
in the root directory of the working tree to make it specific for
the Git repository.
Files that are committed to the Git repository are
not
automatically
removed if you add them to a
.gitignore
file. You can use the
git rm -r --cached [filename]
command
to remove existing files from a Git repository.
The
.gitignore
file tells Git to ignore the specified files in Git
commands.
You can
still add ignored files to the
staging area
of the Git repository
by using the
--force
parameter, i.g. with the
git add --force [filename]
command.
This is useful if you want to add for example auto-generated binaries but you need to have a fine control about the version which is added and want to exclude them from the normal workflow.
You can also setup a global
.gitignore
file valid for all Git repositories via the
core.excludesfile
setting. The setup of this setting is demonstrated in the following
code snippet.
# Create a ~/.gitignore in your user directory cd ~/ touch .gitignore # Exclude bin and .metadata directories echo "bin" >> .gitignore echo ".metadata" >> .gitignore echo "*~" >> .gitignore echo "target/" >> .gitignore # Configure Git to use this file # as global .gitignore git config --global core.excludesfile ~/.gitignore
The local
.gitignore
file can be committed into the Git repository and
therefore is visible
to everyone who clones the repository. The
global
.gitignore
file is only
locally visible.
Git ignores empty directories, i.e. it does not put them under version control.
If you want to track such a directory, it is a common practice to put a file called .gitkeep in the directory. The file could be called anything; Git assigns no special significance to this name. As the directory now contains a file, Git includes it into its version control mechanism.
In this chapter you create a few files, create a local Git repository and commit your files into this repository. The comments (marked with #) before the commands explain the specific actions.
Open a command shell for the operations.
The following commands create an empty directory which you will use as Git repository.
# switch to home cd ~/ # create a directory and switch into it mkdir ~/repo01 cd repo01 # create a new directory mkdir datafiles
The following explanation is based on a non bare repository (short: repository). See Section 3, “Terminology ” for the difference between a bare repository and a repository with a working tree.
Every Git repository is stored in the
.git
folder of the
directory in which the Git repository has been created.
This
directory
contains the complete history of the
repository.
The
.git/config
file
contains the
configuration for the
repository.
The following command creates a Git repository in the current directory.
# Initialize the Git repository
# for the current directory
git init
All files inside the repository folder excluding the
.git
folder are the
working tree
for a Git repository.
The following commands create some files with some content that will be placed under version control.
# switch to your new repository cd ~/repo01 # create another directory touch datafiles/data.txt # create a few files with content ls > test01 echo "bar" > test02 echo "foo" > test03
The
git status
command shows the working tree status, i.e. which files have changed,
which are staged and which
are not part of the staging area. It also
shows
which files have merge conflicts and gives an indication what
the
user can
do with these changes, e.g. add them to the staging area
or remove
them,
etc.
Run it via the following command.
git status
Before committing change to a Git repository you need to mark the changes that should be committed. This is done by adding the new and changed files to the staging area. This creates a snapshot of the affected files.
In case you change one of the files again before committing, you need to add it again to the staging area to commit the new changes.
# add all files to the index of the
# Git repository
git add .
Afterwards run the
git status
command again to see the current status.
After adding the files to the Git staging area, you can commit them
to
the
Git repository. This creates a new commit object with the staged
changes in the Git
repository and moves the HEAD pointer to the new
commit. The
-m
parameter
allows you to specify the commit message.
If you leave this
parameter
out, your default editor is started and you
can enter the
message in
the editor.
# commit your file to the local repository git commit -m "Initial commit"
The Git operations you performed have created a local Git
repository
in the
.git
folder and added all files to this repository via one commit. Run the
git log
command
# show the Git log for the change
git log
You see an output similar to the following.
commit e744d6b22afe12ce75cbd1b671b58d6703ab83f5 Author: Lars Vogel <Lars.Vogel@gmail.com> Date: Mon Feb 25 11:48:50 2013 +0100 Initial commit
If you delete a file which is under version control
git add .
does not record this file deletion.
You can use the
git rm
command to delete the file from your working tree and record the
deletion of the file in the staging area.
# Create a file and commit it touch nonsense2.txt git add . && git commit -m "more nonsense" # remove the file and record the deletion in Git git rm nonsense2.txt # commit the removal git commit -m "Removes nonsense2.txt file"
Alternatively to the
git rm
command you can use the
git commit
command
with the
-a
flag or the
-A
flag in the
git add
command. This flag adds changes of files known by the Git repository
to the commit
in case of the
git commit
command. In case of the
git add
command it adds all file changes including deletions to the staging
area.
For this test, commit a new file and remove it afterwards.
# Create a file and put it under version control touch nonsense.txt git add . && git commit -m "a new file has been created" # Remove the file rm nonsense.txt # show status, output listed below the command git status # On branch master # Changes not staged for commit: # (use "git add/rm <file>..." to update what will be committed) # (use "git checkout -- <file>..." to discard changes in working directory) # # deleted: nonsense.txt # # no changes added to commit (use "git add" and/or "git commit -a") # try standard way of committing -> will NOT work # output of the command listed below git add . && git commit -m "file has NOT been removed" # On branch master # Changes not staged for commit: # (use "git add/rm <file>..." to update what will be committed) # (use "git checkout -- <file>..." to discard changes in working directory) # # deleted: nonsense.txt # # no changes added to commit (use "git add" and/or "git commit -a")
After validating that this command does not remove the file from the
Git repository you can use the
-a
parameter. Be aware that the
-a
adds also other changes.
# commit the remove with the -a flag git commit -a -m "File nonsense.txt is now removed" # alternatively you could add deleted files to the staging index via # git add -A . # git commit -m "File nonsense.txt is now removed"
You can use the
git reset [filename]
command to remove a file from the staging area, which you added with
git add [filename]. Removing a file from the staging area, avoids that it included in
the next commit.
# create a file and add to index touch unwantedstaged.txt git add unwantedstaged.txt # remove it from the index git reset unwantedstaged.txt # to cleanup, delete it rm unwantedstaged.txt
The
git --amend
command makes it possible to replace the last
commit. This allows you
to change the last commit including the
commit message.
The old commit is still available until a clean-up job remove it. See Section 34.2, “git reflog ” for details.
Assume the last commit message was incorrect as it
contained
a typo.
The
following command corrects this via the
--amend
parameter.
# assume you have something to commit git commit -m "message with a tpyo here"
git commit --amend -m "More changes - now correct"
You should use the
git --amend
command only for commits which have not been shared with others. The
git --amend
command creates a new commit ID and people may have based their work
already on the existing commit. In this case they would need to
migrate their work based on the new commit.
Sometimes you change your
.gitignore
file. Git will
stop
tracking the new entries from this moment. The last
version
is still in the Git repository.
If you want to remove the related files from your Git repository you need to do this explicitly via the following command.
# Remove directory .metadata from git repo git rm -r --cached .metadata # Remove file test.txt from repo git rm --cached test.txt
This does not remove the file from the repository history. If the
file
should also be removed from the history, have a look at
git
filter-branch
which allows you to rewrite the commit history. See
Section 38.1, “Using git filter-branch”
for details.
Remotes are pointers (URLs) in a Git repository to other remote repositories that are hosted on the Internet, locally or in the network.
Such remotes can be used to synchronize the changes of several Git repositories. A local Git repository can be connected to multiple remote repositories and you can synchronize your local repository with them via Git operations.
Think of remotes as shorter bookmarks for repositories. You can always connect to a remote repository if you know its URL and if you have access to it. Without remotes the user would have to type the URL for each and every command which communicates with another repository.
It is possible that users connect their individual repositories directly, but a typically Git workflow involve one or more remote repositories which are used to synchronize the individual repository. Typically the remote repository which is used to synchronize against is located on a server which is always available.

Keep in mind that a remote repository can also be hosted in the local file system. Hence a remote is more like a pointer in a repository to another repository.
A remote repository on a server typically does not require a
working tree. A Git repository without a
working tree
is called a
bare repository. You can create such a repository with the
--bare
option. The command to create a new empty bare remote repository is
displayed below.
# create a bare repository git init --bare
By convention the name of a bare repository should end with the
.git
extension.
In this section you create a bare Git repository. In order to simplify the following examples, the Git repository is hosted locally in the filesystem and not on a server in the Internet.
To create a bare Git repository in the Internet you would for example connect to your server via ssh or you would use some Git hosting platform, e.g. Github.com.
Execute the following commands to create a bare repository based on your existing Git repository.
# switch to the first repository cd ~/repo01 # create a new bare repository by cloning the first one git clone --bare . ../remote-repository.git # check the content, it is identical to the .git directory in repo01 ls ~/remote-repository.git
You can convert a normal Git repository into a bare repository by
moving the content of the
.git
folder into the root of the repository and removing all others files
from the working tree. Afterwards you need to update the
Git
repository configuration with the
git config core.bare true
command. The problem with this process is that it does not take
into
account potential future internal changes of Git, hence cloning a
repository with the --bare option should be preferred.
If you clone a repository, Git implicitly creates a remote named origin by default. The origin remote links back to the cloned repository.
If you
create a Git repository from
scratch with the
git init
command, the
origin
remote is not created automatically.
You add
more
remotes
to your repository with the
git
remote add
command.
You created earlier a new Git repository from scratch. Use the following command to add a pointer to your new bare repository using the origin name.
# Add ../remote-repository.git with the name origin
git remote add origin ../remote-repository.git
You can synchronize your local Git repository with remote repositories. These commands are covered in detail in later sections but the following command demonstrates how you can send changes to your remote repository.
# do some changes echo "I added a remote repo" > test02 # commit git commit -a -m "This is a test for the new remote origin" # to push use the command: # git push [target] # default for [target] is origin git push origin
To see the existing definitions of the remote repositories, use the following command.
# show the details of the remote repo called origin
git remote show origin
To see the details of the remotes, e.g. the URL use the following command.
# show the existing defined remotes git remote # show details about the remotes git remote -v
Clone a repository and checkout a working tree in a new directory via the following commands.
# Switch to home cd ~ # Make new directory mkdir repo02 # Switch to new directory cd ~/repo02 # Clone git clone ../remote-repository.git .
The
git push
command allows you to send data to other repositories. By default it
sends data from your current branch to the same branch of the
remote
repository. This can be configured. See
Section 16.6, “Push changes of a branch to a remote repository”
for details on pushing branches or
Git push manpage
for general information.
Make some changes in your local repository and push them from your first repository to the remote repository via the following commands.
# Make some changes in the first repository cd ~/repo01 # Make some changes in the file echo "Hello, hello. Turn your radio on" > test01 echo "Bye, bye. Turn your radio off" > test02 # Commit the changes, -a will commit changes for modified files # but will not add automatically new files git commit -a -m "Some changes" # Push the changes git push ../remote-repository.git
By default you can only push to bare repositories (repositories without working tree). Also you can only push a change to a remote repository which results in a fast-forward merge. See Section 22, “Merging” to learn about fast-forward merges.
The
git pull
command allows you to get the latest changes from another
repository
for the current branch.
To test this in your example Git repositories, switch to your second repository, pull in the recent changes in the remote repository, make some changes, push them to your remote repository via the following commands.
# switch to second directory cd ~/repo02 # pull in the latest changes of your remote repository git pull # make changes echo "A change" > test01 # commit the changes git commit -a -m "A change" # push changes to remote repository # origin is automatically created as we cloned original from this repository git push origin
You can pull in the changes in your first example repository with the following commands.
# switch to the first repository and pull in the changes cd ~/repo01 git pull ../remote-repository.git/ # check the changes git status
The
git pull
command is actually a shortcut for
git fetch
followed by the
git merge
or
git rebase
command depending on your configuration. In
Section 6.4, “Avoid merge commits for pulling”
you configured your Git repository so that
git pull
is a fetch followed by a rebase. See
Section 21.1, “Fetch”
for more information about the fetch command.
Git supports several transport
protocols to connect to other Git
repositories; the native protocol for Git is also called
git.
The following command clones an existing repository using the Git protocol.
# switch to a new directory mkdir ~/online cd ~/online # clone online repository git clone git://github.com/vogella/gitbook.git
If you have ssh access to a Git repository you can also use the ssh protocol. The name preceding @ is the user name used for the ssh connection.
# clone online repository
git clone git@github.com:vogella/gitbook.git
Alternatively you could clone the same repository via the
http
protocol.
# The following will clone via HTTP
git clone http://vogella@github.com/vogella/gitbook.git
As discussed earlier cloning repository creates a
remote
called
origin
pointing to the remote repository which you cloned from.
You can push changes to this
origin repository via
git push
as Git uses
origin
as default.
Of course, pushing to a remote repository requires write
access to
this
repository.
You can add more
remotes
via
the
git
remote
add name [URL_to_Git_repo]
command. For example if you cloned the
repository from
above
via the Git
protocol, you could add a new remote with the name
github_http
for the http
protocol via the following command.
// Add the https protocol
git remote add github_http https://vogella@github.com/vogella/gitbook.git
It is possible to use the HTTP protocol to clone Git repositories. This is especially helpful, if your firewall blocks everything except http or https.
Git also provides support for http access via a proxy server. The following Git command could, for example, clone a repository via http and a proxy. You can either set the proxy variable in general for all applications or set it only for Git.
This example uses environment variables.
# Linux export http_proxy=http://proxy:8080 # On Windows # Set http_proxy=http://proxy:8080 git clone http://dev.eclipse.org/git/org.eclipse.jface/org.eclipse.jface.snippets.git # Push back to the origin using http git push origin
For secured SSL encrypted communication you should use the ssh or https protocol in order to guarantee security.
This example uses the following Git config settings.
// Set proxy for git globally git config --global http.proxy http://proxy:8080 // To check the proxy settings git config --get http.proxy // Just in case you need to you can also revoke the proxy settings git config --global --unset http.proxy
Git is able to store different proxy configurations for different domains, see "core.gitProxy" in http://git-scm.com/docs/git-config
Git allows you to create branches, i.e. named pointers to commits. You can work on different branches independently from each other. The default branch is most often called master.
Git allows you to create branches very fast and cheap in terms of resource consumption. Git encourages the usage of branches on a regular basis.
If you decide to work on a branch, you checkout this branch. This means that Git populates the working tree with the content of the commit to which the branch points and moves the HEAD pointer to the new branch. As explained in Section 3, “Terminology ” HEAD is a symbolic link most often pointing to the currently checked out branch.
The
git branch
command lists all
locally available branches. The
currently
active
branch is marked with
*.
# lists available branches
git branch
If you want to see all branches (including remote tracking
branches),
use
the
-a
for the
git branch
command. See
Section 18.1, “Remote tracking branches”
for information about remote tracking branches.
# lists all branches including the remote branches
git branch -a
The
-v
option lists more information about the branches.
in order to list branches or tags in a remote repository use the
git ls-remote
command as demonstrated in the following example.
# lists branches and tags in the
# remote repository called origin
git ls-remote origin
You can create a new branch via the
git branch [newname]
command. This command allows to specify the starting
point
(commit id,
tag, remote or local branch). If
not specified the
commit
of the HEAD
pointer is used to create
the branch.
# Syntax: git branch <name> <hash>
# <hash> in the above is optional
git branch testing
To start working in a branch you have to checkout the branch. If you checkout a branch the HEAD pointer moves to the last commit in this branch and the files in the working tree are set to the state of this commit.
The following commands demonstrates how you switch to the branch called testing, perform some changes in this branch and switch back to the branch called master.
# switch to your new branch git checkout testing # do some changes echo "Cool new feature in this branch" > test01 git commit -a -m "new feature" # switch to the master branch git checkout master # check that the content of # the test01 file is the old one cat test01
To create a branch and to switch to it at the same time you can use
the
git checkout
command with the
-b
parameter.
# Create branch and switch to it git checkout -b bugreport12 # Creates a new branch based on the master branch # without the last commit git checkout -b mybranch master~1
Renaming a branch can be done with the following command.
# rename branch
git branch -m [old_name] [new_name]
To delete a branch which is not needed anymore, you can use the following command.
# delete branch testing git branch -d testing # check if branch has been deleted git branch
You can push the changes in the current active branch to a remote repository by specifying the target branch. This creates the target branch in the remote repository if it does not yet exists.
# push current branch to a branch called "testing" to remote repository git push origin testing # switch to the testing branch git checkout testing # some changes echo "News for you" > test01 git commit -a -m "new feature in branch" # push all including branch git push
This way you can decide which branches you want to push to other repositories and which should be local branches. You learn more about branches and remote repositories in Section 18.1, “Remote tracking branches”.
To see the difference between two branches you can use the following command.
# shows the differences between
# current head of master and your_branch
git diff master your_branch
You can also use commmit ranges as described in Section 4.2, “Commit ranges with the double dot operator” and Section 4.3, “Commit ranges with the triple dot operator”. For example if you compare a branch called your_branch with the master branch the following command shows the changes in your_branch and master since these branches diverted.
# shows the differences in your
# branch based on the common
# ancestor for both branches
git diff master...your_branch
See
Section 45, “Viewing changes with git diff and git show ”
for more examples of the
git diff
command.
Your local Git repository contains references to the state of the branches on the remote repositories to which it is connected. These local references are called remote tracking branches.
You can see your remote tracking branches with the following command.
# list all remote branches
git branch -r
It is also safe to delete a remote branch in your local Git repository. You can use the following command for that.
# delete remote branch from origin
git branch -d -r origin/<remote branch>
The next time you run the
git fetch
command the remote branch is recreated.
To delete the branch in a remote repository use the following command.
# delete branch in a remote repository
git push [remote] :branch]
Alternatively you can also use the following command.
# delete branch in a remote repository
git push [remote] --delete [branch]
For example if you want to delete the branch called testbranch in the remote repository called origin you can use the following command.
git push origin :testbranch
Local tracking branches
are local branches which are directly connected to a
remote tracking branch. These
local tracking branches
allow you to use the
git pull
and
git push
command directly without specifying the branch and repository.
# list all branches (local and remote tracking branches)
git branch -a
If you clone a Git repository, your local master branch is created as a tracking branches for origin/master by Git.
You can create new tracking branches by specifying the remote branch during the creation of a branch. The following example demonstrates that.
# setup a tracking branch called newbrach
# which tracks origin/newbranch
git checkout -b newbranch origin/newbranch
To update remote branches without changing local branches you
use the
git fetch
command which is covered in
Section 21, “Updating your remote branches with git fetch ”.
To see the tracking branches for a remote repository (short: remote) you can use the following command.
# show all remote and tracking branches for origin
git remote show origin
An example output of this might look like the following.
* remote origin Fetch URL: ssh://test@git.eclipse.org/gitroot/e4/org.eclipse.e4.tools.git Push URL: ssh://test@git.eclipse.org/gitroot/e4/org.eclipse.e4.tools.git HEAD branch: master Remote branches: integration tracked interm_rc2 tracked master tracked smcela/HandlerAddonUpdates tracked Local branches configured for 'git pull': integration rebases onto remote integration master rebases onto remote master testing rebases onto remote master Local refs configured for 'git push': integration pushes to integration (up to date) master pushes to master (up to date)
Git has the option to tag a commit in the repository history so that you find it easier at a later point in time. Most commonly, this is used to tag a certain version which has been released.
If you tag a commit you create a tag object.
Git supports two different types of tags, lightweight and annotated tags.
A lightweight tag is a pointer to a commit, without any additional information about the tag. An annotated tag contains additional information about the tag, e.g. the name and email of the person who created the tag, a tagging message and the date of the tagging. Annotated tags can also be signed and verified with GNU Privacy Guard (GPG).
You can create a new annotated tag via the
git tag -a
command. An annotated tag can also be created using the
-m
parameter, which is used to specify the description of the tag.
The
following
command tags the current active HEAD.
# create tag git tag 1.6.1 -m 'Release 1.6.1' # show the tag git show 1.6.1
You can also create tags for a certain commit id.
git tag 1.5.1 -m 'version 1.5' [commit id]
You can use the option
-s
to create a signed tag. These tags are signed with
GNU Privacy Guard (GPG)
and can also be verified with GPG. For details on this please see the
man page of git for tags which can be accessed via the
man git-tag
command on a Linux system. You find this manpage also online under
the following URL:
Git tag man page
To create a lightweight tag don't use the
-m,
-a
or
-s
option. Lightweight tags are often used for build tags which do not
need additional information other than the build number or the
timestamp.
# create lightweight tag git tag 1.7.1 # See the tag git show 1.7.1
If you want to use the code associated with the tag, use:
git checkout <tag_name>
If you checkout a tag, you are in the detached head mode and commits created in this mode are harder to find after you checkout a branch again. . See Section 34.1, “Detached HEAD ” for details.
By default the
git push
command does not transfer tags to remote
repositories. You explicitly
have to push the tag with the following command.
# push a tag or branch called tagname git push origin [tagname] # to explicitly push a tag and not a branch git push origin tag <tagname>
You can delete tags with the
-d
parameter. This deletes the tag from your local repository. By
default Git does not push tag deletions to a remote repository, you
have to trigger that explicitly.
The following commands demonstrate how to push a tag deletion.
# delete tag locally git tag -d 1.7.0 # delete tag in remote repository # called origin git push origin :refs/tags/1.7.0
Tags are frequently used to tag the state of a release of the Git repository. In this case they are typically called release tags.
Convention is that release tags are labeled based on the [major].[minor].[patch] naming scheme, for example "1.0.0". Several projects also use the "v" prefix.
The idea is that the patch version is incremented if (only) backwards compatible bug fixes are introduced, the minor version is incremented if new, backwards compatible functionality is introduced to the public API and the major version is incremented if any backwards incompatible changes are introduced to the public API.
For the detailed discussion on naming conventions please see the following URL: Semantic versioning .
You can update your remote branches with the
git fetch
command.
The
git fetch
command updates your
remote branches, i.e. it updates the local copy of branches stored in a remote
repository. The following command updates the
remote branches
from the repository
called
origin.
git fetch origin
The fetch
command
only
updates the
remote branches
and none of the local
branches
and it does not change the working tree
of the Git repository.
Therefore you can run the
git fetch
command
at any
point in
time.
After reviewing
the changes in
the
remote
tracking
branch you can
merge or
rebase these
changes onto
your
local
branches.
Alternatively you
can also
use the
git
cherry-pick "sha"
command
to take
over
only
selected commits.
A detailed description of merge, rebase and cherry-pick can be found in Section 24, “Merging branches”.
The
git fetch
command updates only the
remote branches
for one remote repository. In case you want to update the
remote branches
of all your remote repositories you can use the following command.
# simplification of the fetch command # this runs git fetch for every remote repository git remote update # the same but remove all stale branches which # are not in the remote anymore git remote update --prune
The following code shows a few options how you can compare your branches.
# show the long entries between the last local commit and the # remote branch git log HEAD..origin # show the diff for each patch git log -p HEAD..origin # show a single diff git diff HEAD...origin
The above commands shows the changes introduced in HEAD compared to origin. If you want to see the changes in origin compared to HEAD you can switch the arguments.
You can apply the changes of the remote branches on your current local branch for example with the following command.
# assume you want to rebase master based on the latest fetch # therefore check it out git checkout master # update your remote tracking branch git fetch # rebase your master onto origin/master git rebase origin/master
More information on the rebase command can be found in Section 26.1, “Rebasing branches”.
The
git merge
command
performs a merge.
You can merge changes
from
one
branch to the
current
active one
via
the
following command.
# syntax: git merge <branch-name>
# merges into your current selected branch
git merge testing
If the commits which are merged are direct successors of the HEAD pointer of the current branch, Git simplifies things by performing a so-called fast forward merge. This fast forward merge simply moves the HEAD pointer of the current branch to the last commit which is being merged.
This process is depicted in the following graphics. The first picture assumes that master is checked out and that you want to merge the changes of the branch labeled "branch" into your "master" branch. Each commit points to its successor.

After the fast forward merge the HEAD pointer of "master" points to the existing commit.

If commits are merged which are not direct successors of the HEAD pointer of the current branch, Git performs a so-called three-way-merge between the latest snapshot of two branches, based on the most recent common ancestor of both.

As a result a so-called merge commit is created on the current branch which is combining the respective changes from the two branches being merged. This commit points to both its predecessors.

A merge conflicts occurs, if two people have modified the same content and Git cannot automatically determine how both changes should be applied.
If a merge conflict occurs Git will mark the conflict in the file and the programmer has to resolve the conflict manually. After resolving it, he can add the file to the staging area and commit the change.
In the following example you first create a merge conflict and afterwards you resolve the conflict and apply the change to the Git repository.
The following code creates a merge conflict.
# Switch to the first directory cd ~/repo01 # Make changes echo "Change in the first repository" > mergeconflict.txt # Stage and commit git add . && git commit -a -m "Will create merge conflict 1" # Switch to the second directory cd ~/repo02 # Make changes touch mergeconflict.txt echo "Change in the second repository" > mergeconflict.txt # Stage and commit git add . && git commit -a -m "Will create merge conflict 2" # Push to the master repository git push # Now try to push from the first directory # Switch to the first directory cd ~/repo01 # Try to push --> you will get an error message git push # Get the changes via a pull # this creates the merge conflict in your # local repository git pull origin master
Git marks the conflict in the affected file. This file looks like the following.
<<<<<<< HEAD Change in the first repository ======= Change in the second repository >>>>>>> b29196692f5ebfd10d8a9ca1911c8b08127c85f8
The above is the part from your repository and the below one from the remote repository. You can edit the file manually and afterwards commit the changes.
Alternatively, you could use the
git mergetool
command.
git mergetool
starts a
configurable merge tool that displays the changes in a
split
screen.
git mergetool
is not always available. It is also safe to edit the file with merge
conflicts in a normal editor.
# Either edit the file manually or use git mergetool # You will be prompted to select which merge tool you want to use # For example on Ubuntu you can use the tool "meld" # After merging the changes manually, commit them git commit -m "merged changes"
Instead of using the
-m
option in the above example you can also use the
git commit
command without this option. In this case the command opens your
default
editor with a default commit messages about the merged
conflicts. It is good practice to use this message.
You can use Git to rebase one branch on another one. As described
the
merge
command
combines the changes of two branches. If you rebase a branch
called A
onto another, the
git
command takes the
changes introduced by the commits of branch A and
applies them based on the
HEAD of the other branch. This way the
changes in the other branch are also available in branch A.
The processes is displayed in the following picture. We want to rebase the branch onto master.

Running the rebase command creates a new commit with the changes of the branch on top of the master branch.

Performing a rebase from one branch to another branch does not create a merge commit.
The final result for the source code is the same as with merge but the commit history is cleaner; the history appears to be linear.
# create new branch git checkout -b rebasetest # To some changes touch rebase1.txt git add . && git commit -m "work in branch" # do changes in master git checkout master # make some changes and commit into testing echo "This will be rebased to rebasetest" > rebasefile.txt git add rebasefile.txt git commit -m "New file created" # rebase the rebasetest onto master git checkout rebasetest git rebase master # now you can fast forward your branch onto master git checkout master git merge rebasetest
Rebase is also useful to apply the changes of the master branch to feature branches to ensure that the feature branch still works with the latest developments from the master branch.
The
rebase
command allows you to combine several commits into
one commit. This is
useful as it allows the user to rewrite some of the
commit history
(cleaning it up) before pushing your changes to a
remote repository.
The following will create several commits which should be combined at a later point in time.
# Create a new file touch rebase.txt # Add it to git git add . && git commit -m "rebase.txt added to index" # Do some silly changes and commit echo "content" >> rebase.txt git add . && git commit -m "added content" echo " more content" >> rebase.txt git add . && git commit -m "this is just a test" echo " more content" >> rebase.txt git add . && git commit -m "ups" echo " more content" >> rebase.txt git add . && git commit -m "yes" echo " more content" >> rebase.txt git add . && git commit -m "added more content" echo " more content" >> rebase.txt git add . && git commit -m "creation of important configuration file" # Check the git log message git log
We will combine the last seven commits. You can do this interactively via the following command.
git rebase -i HEAD~7
This will open your editor of choice and let you configure the rebase operation by defining which commits to pick,a squash or fixup.
Pick includes the selected commit.
Squash
combines the commit
messages
while
fixup
will disregard
the commit
message. The following shows an example of
the selection, we pick the last commit, squash 5 commits and fix the
sixth commit.
p 7c6472e added more content f 4f73e68 added content f bc9ec3f this is just a test f 701cbb5 ups f 910f38b yes f 31d447d added more content s e08d5c3 creation of important configuration file # Rebase 06e7464..e08d5c3 onto 06e7464 # # Commands: # p, pick = use commit # r, reword = use commit, but edit the commit message # e, edit = use commit, but stop for amending # s, squash = use commit, but meld into previous commit # f, fixup = like "squash", but discard this commit's log message # x, exec = run command (the rest of the line) using shell # # These lines can be re-ordered; they are executed from top to bottom. # # If you remove a line here THAT COMMIT WILL BE LOST. # However, if you remove everything, the rebase will be aborted.
You should always check your local branch history before pushing changes to another Git repository or review system.
Git allows you to do local commits. This feature is frequently used to have commits to which you can go back, if something should go wrong during a feature development. Before pushing you should look at your local branch history and validate, whether or not these commits are relevant for others.
If they all belong to the implementation of the same feature you, most likely, want to summarize them in one single commit before pushing.
The interactive rebase is basically rewriting the history. It is safe to do this as long as the commits have not been pushed to another repository. This means commits should only be rewritten as long as they have not been pushed.
If you rewrite and push a commit that is already present in other Git repositories, it will look as if you implemented something that somebody had already implemented.
Avoid rebasing changes you already shared with others.
For example assume that a user has a local feature branch and wants to push it onto a branch on the remote repository. However, the branch has evolved and therefore pushing is not possible. Now it is good practice to fetch the latest state of the branch from the remote repository. Afterwards you rebase the local feature branch onto the remote tracking branch. This avoid an unnecessary merge commit. This rebasing of a local feature branch is also useful to incorporate the latest changes from remote into the local development, even if the user does not want to push right away.
Rebasing is safe as long as you did not push any of the changes involved in the rebase. For example if you cloned a repository and worked in this repository. Rebasing is a great way to keep the history clean when contributing back your modifications.
The
git cherry-pick
command allows you to select the patch which was introduced with an
individual commit and apply this patch on another branch. The patch
is captured as a new commit on the other branch.
This way you can select individual changes from one branch and transfer them to another branch.
In the following example you create a new branch and commit two changes.
# create new branch git checkout -b picktest # create some data and commit touch pickfile.txt git add pickfile.txt git commit -m "adds new file" # create second commit echo "changes to file" > pickfile.txt git commit -a -m "changes in file"
You can check the commit history for example with the
git log --oneline
command.
# see change commit history
git log --oneline
# results in the following output
2fc2e55 changes in file
ebb46b7 adds new file
[MORE COMMITS]
330b6a3 initial commit
The following command selects the first commit based on the commit id and applies its changes to the master branch. This creates a new commit on the master branch.
git checkout master git cherry-pick ebb46b7
The
cherry-pick
command can be used to change the order of commits.
git cherry-pick
also accepts commit ranges for example in the following command.
git checkout master
# pick both commits
git cherry-pick picktest~..picktest~1
See Section 4.2, “Commit ranges with the double dot operator” for more information about commit ranges.
Git provides the
git stash
command which allows you
to record the current state of the working
directory and the index and go back
to
the last
committed
revision.
This allows you to pull in the latest changes or to develop an urgent fix. Afterwards you can restore the stashed changes, which will reapply the changes to the current version of the source code.
In general using the stash command should be the exception in using Git. Typically you would create new branches for new features and switch between branches. You can also commit frequently in your local Git repository and use interactive rebase to combine these commits later before pushing them to another Git repository.
You can avoid using the
git stash
command. In this case
you commit the changes you want to put aside and
use
the
git commit --amend
command
to change the commit later. If you use the approach of
creating a commit, you typically put a marker
in the
commit
message to
mark it as a draft, e.g. "[DRAFT] implement
feature
x".
The following commands will save a stash and reapply them after some changes.
# Create a stash with uncommited changes git stash # TODO do changes to the source, e.g. by pulling # new changes from a remove repo # Afterwards reapply the stashed changes # and delete the stash from the list of stashes git stash pop
It is also possible to keep a list of stashes.
# create a stash with uncommited changes git stash save # see the list of available stashes git stash list # Result might be something like: stash@{0}: WIP on master: 273e4a0 Resize issue in Dialog stash@{1}: WIP on master: 273e4b0 Silly typo in Classname stash@{2}: WIP on master: 273e4c0 Silly typo in Javadoc # you can use the ID to apply a stash git stash apply stash@{0} # Or apply the latest stash and delete it afterwards git stash pop # also you can remove a stashed change # without applying it git stash drop stash@{0} # or delete all stashes git stash clear
You can also create a branch for your stash. You might want to do this because the branch from which you stashed has changed the same files and your want to have better control cover your changes or because you want to select individual changes from your stack.
# create a new branch from your stack and
# switch to it
git stash branch newbranchforstash
If you have untracked files in your working tree which you want to
remove you can use the
git clean
command.
The following commands demonstrates the usage of the
git clean
command.
# Create a new file with content echo "this is trash to be deleted" > test04 # Make a dry-run to see what would happen # -n is the same as --dry-run git clean -n # delete, -f is required if # variable clean.requireForce is not set to false # git clean -f # use -d flag to delete new directories # use -x to delete hidden files, e.g. ".example" git clean -fdx
Changes in the working tree which are unstaged can be undone with
git checkout
command. This command resets the file in the working tree to the last
staged or
committed version.
# Delete a file rm test01 # Revert the deletion git checkout test01 # Change a file echo "override" > test01 # Restore the file git checkout test01
For example you can restore a directory called
data
with the following command.
git checkout -- data
If you want to undo a staged but uncommitted change you
still can use
the
git checkout
command if you specify a commit pointer to use.
The following demonstrates the usage of this to restore a delete directory.
# assume you deleted a tracked directory
# and staged the changes
# restore the working tree and reset the staging area
git checkout HEAD -- your_dir_to_restore
The additional
commit pointer
parameter instructs the
git checkout
command to reset the working tree and to remove also the staged
changes.
If you added a new file to the staging area but do not want to commit
the
file,
you
can remove it from the index via the
git reset file
command.
# create a file and # accidently add it to the index touch incorrect.txt git add . # remove it from the index git reset incorrect.txt # to clean up, delete the file # not neccessary, maybe you want to add it later to the index rm incorrect.txt
To learn more about the
git reset
command see
Section 31.1, “Use cases for git reset”
If you have added the changes of a file to the staging area, you can also revert the changes in the index and checkout the file from the index.
# some nonsense change echo "change which should be removed later" > test01 # add the file to the staging index git add test01 # restore the file in the staging index git reset HEAD test01 # get the version from the staging index # into the working tree git checkout test01
The
git reset
command allows you to set the current HEAD to a specified state,
e.g.
commit. This way you can continue your work from another commit.
Depending on the specified parameters the
git reset
command performs the following:
If you specify the
--soft
parameter the
git reset
command moves the HEAD pointer.
If you specify the
--mixed
parameter (also the default) the
git reset
command moves the HEAD pointer and resets the staging area to the
new HEAD.
If you specify the
--hard
parameter the
git reset
command moves the HEAD pointer and resets the staging area and the
working tree to the
new HEAD.

Via certain parameters you can define how the index and the working tree is updated. These parameters are listed in the following table.
The
git reset
command does not remove untracked files. Use the
git clean
command for this.
If you specify a path for the
git reset command
if does not move the HEAD pointer but only updates the index or also
the working tree depending in your specified option.
See Section 30.3, “Remove staged changes for new files ” and Section 30.4, “Remove staged changes for previously committed files ” for examples.
The commits which were above the commit to which you resetted,
can be
reached via the
git reflog
command. See
Section 34, “Recovering lost commits”.
The
git reset --hard
command does not delete new files in your working tree. If you want
to delete them too, you need to use the
git clean -f
command too.
# removes staged and working tree changes # of commited files git reset --hard # the above does not remove untracked files therefore # the next command is needed # removes new files which are still untracked git clean -f -d
As a soft reset does not remove your change to your files and index,
you can use the
git reset --soft
command to squash several commits into one commit.
As the index is not changed, you have it after the soft reset in the desired state for your new commit, i.e. it has all the changes from the commits that you removed with the reset.
# squashes the last two commits git reset --soft HEAD~1 && git commit -m "new commit message"
The interactive rebase adds more flexibility to squashing commits and allows to use the existing commit messages. See Section 26.2, “Interactive rebase to combine commits” for details.
The
git show
command allows to see and retrieve files from branches, commits and
tags.
It allows to see the status of these files in the selected
branch, commit or tag without
checking them out into your working
tree.
The following commands demonstrate that. You can also make a copy of the file.
# [reference] can be a branch, tag, HEAD or commit ID # [filename] is the filename including path git show [reference]:[filename] # To make a copy to copiedfile.txt git show [reference]:[filename] > copiedfile.txt
You can use the
--
option in
git log
to see the commit history for a file, even if you have deleted the
file.
# see the changes of a file, work even # if the file was deleted git log -- [file path] # limit the output of Git log to the # last commit, i.e. the commit which delete the file # -1 to see only the last commit # use 2 to see the last 2 commits etc git log -1 -- [file path] # include stat parameter to see # some statics, e.g. how many files were # deleted git log -1 --stat -- [file path]
You can check out older revisions of your file via the
commit
ID. The
commit ID is shown if you enter the
git log
command.
It is displayed behind the
commit
word.
# Switch to home cd ~/repo01 # Get the log git log # Checkout the older revision via git checkout commit_id
If you checkout a commit, you are in the detached head mode and commits in this mode are harder to find after you checkout another branch. Before committing it is good practice to create a new branch to leave the detached head mode. . See Section 34.1, “Detached HEAD ” for details.
If you checkout a commit or a tag you are in the so-called
detached HEAD
mode
. If you commit changes in this mode, you have no
branch
which
points to
this commit. After you checkout a branch you
cannot see
the commit you
did in detached head mode in the
git log
command.
To find such commits you can use the
git reflog
command.
Reflog is a mechanism to record the movements of the HEAD pointer. HEAD is a pointer to the currently selected commit object.
The Git reflog command gives a history of the complete changes of the HEAD pointer.
git reflog # Output # ... snip ... 1f1a73a HEAD@{2}: commit: More chaanges - typo in the commit message 45ca204 HEAD@{3}: commit: These are new changes cf616d4 HEAD@{4}: commit (initial): Initial commit
The
git reflog
command also list commits which you have removed.
The following example shows how you can use git reflog to revert to a commit which has been removed.
# Assume the ID for the second commit is # 45ca2045be3aeda054c5418ec3c4ce63b5f269f7 # Resets the head for your tree to the second commit git reset --hard 45ca2045be3aeda054c5418ec3c4ce63b5f269f7 # See the log git log # Output shows the history until the 45ca2045be commit # See all the history including the deletion git reflog # <Output> cf616d4 HEAD@{1}: reset: moving to 45ca2045be3aeda054c5418ec3c4ce63b5f269f7 # ...snip.... 1f1a73a HEAD@{2}: commit: More chaanges - typo in the commit message 45ca204 HEAD@{3}: commit: These are new changes cf616d4 HEAD@{4}: commit (initial): Initial commit git reset --hard 1f1a73a
An alias in Git allows you to setup your own Git command. For example, you can define an alias which is a short form of your own favorite commands or you can combine several commands with an alias.
The following defines an
alias
to see the staged changes with the new
git staged
command.
git config --global alias.staged 'diff --cached'
Or you can define an
alias
for a detailed
git log
command.
The following command defines the
git ll
alias.
git config --global alias.ll 'log --graph --oneline --decorate --all'
You can also run external commands. In this case you start the
alias
definition with a
!
character. For example, the following defines the
git ac
command which combines
git add . -A
and
git commit
commands.
# define alias git config --global alias.act '!git add . -A && git commit' # to use it git ac -m "message"
Unfortunately, defining an
alias
beginning with
!
is at the time of this writing
not
supported in
msysGit
for Windows.
The
git bisect
command allows you to run a binary search through the commit history
to identify the commit which introduced an issue. You specify a range
of commits and a script that the
bisect
command uses to identify whether a commit good or bad.
This script must return 0 if the condition is fulfilled and non-zero if the condition is not fulfilled.
Create a new Git repository, create the
file1.txt
and commit it
to the repository. Do a few more changes, remove the
file and again
do a few more changes.
We use a simple shell script which checks the existence of a file. Ensure that this file is executable.
#!/bin/bash FILE=$1 if [ -f $FILE ]; then exit 0; else exit 1; fi
Afterwards use the
git bisect
command to find the bad commit. First you
use the
git bisect start command
to define the known bad commit and the known good commit.
# define the check range between
# HEAD and HEAD minus 5 commits
git bisect start HEAD HEAD~5
Afterwards run the bisect command using the shell script.
# assumes that the check script # is a directory above the current git bisect run ../check.sh test1.txt
Git allows you to include other Git repositories into a Git repository. This is useful in case you want to include a certain library in another repository or in case you want to aggregate certain Git repositories.
Git calls these included Git repositories submodules. Git allows you to commit, pull and push to these repositories independently.
You add a submodule to a Git repository via the
git submodule add
command. The
git submodule init
command creates the local configuration file for
the submodules if it
does not yet exist.
# Add a submodule to your Git repo git submodule add [URL to Git repo] # Initialize submodule configuration git submodule init
To pull in changes into a Git repository including the changes in
submodules you
can use the
--recurse-submodules
parameter in the
git pull command.
# pull in the changes from main repo and submodules
git pull --recurse-submodules
Use the
git submodule update
command to set
the submodules to the commit specified
by the main
repository.
# setting the submodules to the commit defined by master
git submodule update
The fact that submodule track commits and not branches lead frequently to confusion. That is why Git 1.8.2 added the option to track also branches. Read the following sections to more learn about this.
By default submodules are tracked by commits, i.e. the main Git
repository remembers a certain commit of the submodule.
The
git submodule update
command sets the Git repository of the the submodule to that
particular commit.
For the main repository the submodule is just
content which is
tracked.
This means that if you pull in new changes in the submodules, you need to commit in your main repository to point to the new commit of the submodule.
If you update your submodule and want to use this update in your main
repository you need to commit this change in your main repository.
The
git submodule update
command
sets the submodule to the commit referred to in the main
repository.
The following example shows how to update a submodule to its latest commit in its master branch.
# update submodule to master # skip this is your use --recurse-submodules # and have the master branch checked out cd [submodule directory] git pull git checkout master # commit the change in main repo # to use the latest commit in master of the submodule cd .. git add [submodule directory] git commit -m "moved submodule to latest commit in master" # share your changes git push
Another developer can get the update by pulling in the changes and running the submodules update command.
# another developer wants to get the changes git pull # this updates the submodule to the latest # commit in master as set in the last example git submodule update
With this setup you are tracking commits, so if the master branch in the submodule moves on, you are still pointing to the existing commit. You need to repeat this procedure every time you want to use new changes of the submodules. See the next chapter for an alternative with a Git release of version 1.8.2 for higher.
Since tracking of branches is a very common requirement, Git
added to
option to track a certain branch in its 1.8.2 release. To track
branches you specify the branch with the
-b
parameter during the
submodule add
command.
This allows you use to use
--remote
parameter in the
git submodule update
command.
# add submodule to track master branch git submodule add -b master [URL to Git repo]; # update your submodule # --remote will also fetch and ensure that # the latest commit from the branch is used git submodule update --remote # to avoid fetching use git submodule update --remote --no-fetch
The
git filter-branch
command allows you to rewrite the Git commit history for selected
branches and to apply custom filters on each revision. This creates
different hashes for all modified commits.
The command allows you to filter for several values, e.g. the author, the message, etc. For details please see the following link.
http://www.kernel.org/pub/software/scm/git/docs/git-filter-branch.html
Using
filter-branch
is dangerous as it changes the Git repository. It changes the
commit
id and if someone else is working on a cloned version of the
repository his commit id references are gone.
The following listing shows an example on how to change the committer
name and his
email address via the
git filter-branch
command.
git filter-branch -f --env-filter \ "GIT_AUTHOR_NAME='Test Vogel'; GIT_AUTHOR_EMAIL='test@gmail.com'; \ GIT_COMMITTER_NAME='Lars Vogel'; \ GIT_COMMITTER_EMAIL='lars.vogel@gmail.com';" HEAD
A
patch
is a text file that contains changes to the source code. A patch
created with the
git format-patch
command includes meta-information
about the commit (committer, date,
commit message, etc) and
contains also the diff of binary data.
This file can be sent to someone else and this developer can use this file to apply the changes to his local repository. The metadata is preserved.
Alternatively you could create a diff file with the
git diff
command but this diff file does not contain the metadata information.
The following example creates a branch, changes the files and commits these changes into the branch.
# Create a new branch git branch mybranch # Use this new branch git checkout mybranch # Make some changes touch test05 # Change some content in an existing file echo "New content for test01" >test01 # Commit this to the branch git add . git commit -a -m "First commit in the branch"
The next example creates a patch for these changes.
# Create a patch --> git format-patch master git format-patch origin/master # This created the file: # patch 0001-First-commit-in-the-branch.patch
To apply this patch to your master branch, switch to it and use the
git apply
command.
# Switch to the master git checkout master # Apply the patch git apply 0001-First-commit-in-the-branch.patch
Afterwards you can commit the changes introduced by the patches and delete the patch file.
# Patch is applied to master # Change can be commited git add . git commit -a -m "Applied patch" # Delete the patch file rm 0001-First-commit-in-the-branch.patch
You can use specify the commit id and the number of patches which should be created. For example to create a patch for selected commits based on the HEAD pointer you can use the following commands.
# create patch for the last commit based on HEAD git format-patch -1 HEAD # create patches for the last three commits # based on head git format-patch -3 HEAD
Git provides commit hooks, e.g. programs which can be executed at a pre-defined point during the work with the repository. For example you can ensure that the commit message has a certain format or trigger an action after a push to the server.
These programs are scripts and can be written in any language, e.g. as shell scripts or in Perl, Python etc. Git calls these scripts based on a naming convention.
Git provides hooks for the client and for the server side. On
the
server side you can use the
pre-receive
and
post-receive
script to check the input or to trigger actions after the commit.
If you create a new Git repository, Git create example scripts into
the
.git/hooks. The example scripts end with
.sample.
To activate them make them executable and remove the
.sample
from the filename.
The commits hooks are documented in the Git man pages, use
man githooks
to see the documentation.
Linux and Mac uses different line endings than Windows. Windows uses a carriage-return and a linefeed character (CRLF), while Linux and Mac only uses a linefeed character (LF)).
To avoid commits because of line ending differences in your Git repository you should configure all clients to write the same line ending to the Git repository.
On Windows systems you can tell Git to convert during a checkout to CRLF and during a commit convert back the LF format. Use the following setting for this.
git config --global core.autocrlf true
On Linux and Mac you can tell Git to convert CRLF during a checkout to LF with the following setting.
git config --global core.autocrlf input
As described before, you do not need a server. You can just use a file system or a public Git provider, such as Github or Bitbucket. Sometimes, however, it is convenient to have your own server, and installing it under Ubuntu is relatively easy.
First make sure you have installed ssh.
sudo apt-get install ssh
If you have not yet installed Git on your server, you need to do this too.
sudo apt-get install git-core
Create a new user for git.
sudo adduser git
Now log on with your Git user and create a bare repository.
# Login to server # to test use localhost ssh git@IP_ADDRESS_OF_SERVER # Create repository git init --bare example.git
Now you can push to the remote repository.
mkdir gitexample cd gitexample git init touch README git add README git commit -m 'first commit' git remote add origin git@IP_ADDRESS_OF_SERVER:example.git git push origin master
The typical setup based on the created "git" user from above is
that
the
public SSH key of each user is added to the
~/.ssh/authorized_keys
file of the "git" user. Afterwards everyone can access the system
using the "git" user.
Alternatively you could use LDAP authentication or other special configurations.
The Git installation provides a specialized shell, which can be
assigned to the user. Typically this shell is located under in
/usr/bin/git-shell
and can be assigned to the user via the
/etc/passwd
configuration file to the Git user. If you assign this shell to the
Git user, this user can also perform Git command which add safety to
your Git setup.
The
git status
command shows the working tree status, i.e. which files have changed,
which are staged and which
are not part of the index. It also shows
which files have merge conflicts and gives an indication what the user can
do with these changes, e.g. add them to the index or remove them,
etc.
The
git log
commands shows the history of your repository in the current branch,
i.e. the list
of
commits.
# show the history of commits in the current branch
git log
The
oneline
parameter fits the output of the
git log
command in one line.
If you use the
abbrev-commit
the
git log
command uses shorter versions of the SHA-1 identifier for a commit
object but keep the SHA-1 unique.
The
graph
parameter draws a text-based graphical representation of the branches
and the merge history of the Git repository.
# uses shortend but unique SHA-1 values # for the commit objects git log --abbrev-commit # show the history of commits in one line # with a shortened version of the commit id # --online is a shorthand for "--pretty=oneline --abbrev-commit" git log --oneline # show the history as graph including branches git log --graph --oneline
For more options on the
git log
command see the
Git log manpage
.
To see changes in a file you can use the
-p
option in the
git log
command.
# git log filename shows the commits for this file git log [file path] # Use -p to see the diffs of each commit git log -p filename # --follow shows the entire history # including renames git log --follow -p file
To see which commit deleted a file you can use the following command.
# see the changes of a file, work even # if the file was deleted git log -- [file path] # limit the output of Git log to the # last commit, i.e. the commit which delete the file # -1 to see only the last commit # use 2 to see the last 2 commits etc git log -1 -- [file path] # include stat parameter to see # some statics, e.g. how many files were # deleted git log -1 --stat -- [file path]
The double hyphens (--) in Git separate flags from non-flags (usually filenames).
The
git diff
command allows the user to see the changes made. In order to test
this, make some changes to a file and check what the
git diff
command shows to you.
Afterwards
commit the changes to the repository.
# make some changes to the file echo "This is a change" > test01 echo "and this is another change" > test02 # check the changes via the diff command git diff # optional you can also specify a path to filter the displayed changes # path can be a file or directory # git diff [path]
To see the changes introduced by a commit use the following command.
git show <commit_id>
To see which changes you have staged, i.e. you are going to commit with the next commit, use the following command.
# make some changes to the file
git diff -cached
To see the differences introduced between two commits you use the
git diff
command specifying the commits. For example the
following command
shows the differences introduced in the last
commit.
git diff HEAD~1 HEAD
The
git blame
command allows you to see which commit and author
modified a file on
a per line base.
# git blame shows the author and commit per # line of a file git blame [filename] # the -L option allows to limit the selection # for example by line number # only show line 1 and 2 in git blame git blame -L 1-2 [filename]
The
git shortlog
command summarizes the
git log
output, it groups all commits by author and includes the first line of
the commit message.
The
-s
option suppresses the commit message and provides a commit count. The
-n
option sorts the output based on the number of commits by author.
# gives a summary of the changes by author git shortlog # compressed summary git shortlog -sn
Instead of setting up your own server, you can also use a hosting service. The most popular Git hosting sites are GitHub and Bitbucket. Both offer free hosting with certain limitations.
Most hosting provider allow to use the http protocol with manual user authentication or to use an ssh key for automatic authentication.
An ssh key has a public and private part. The public part is uploaded to the hosting provider. If you interact with the hosting provider via ssh, the public key will be validated based on the private key which is stored locally.
The ssh
key is usually generated in the
.ssh
directory. Ensure that you backup existing keys in this directory
before running the following commands.
To create an ssh key under Ubuntu switch to the command line and issue the following commands.
# Switch to your .ssh directory cd ~/.ssh # If the directory # does not exist, create it via: # mkdir .ssh # Manually backup all existing content of this dir!!! # Afterwards generate the ssh key ssh-keygen -t rsa -C "your_email@youremail.com" # Press enter to select the default directory # You will be prompted for an optional passphrase # A passphrase protects your private key # but you have to enter it manually during ssh operations
The result will be two files,
id_rsa
which is your private key and
id_rsa.pub
which is your public key.
You find more details for the generation of a SSH key on the following webpage.
# Link to ssh key creation on Github
https://help.github.com/articles/generating-ssh-keys
GitHub provide free hosting of Git repositories for public available repositories. If you want to have private repositories which are only visible for people you select, you have to pay a monthly fee to GitHub.
GitHub can be found under the following URL.
https://github.com/
If you create an account at GitHub you can create a repository. After creating a repository at GitHub you will get a description of all the commands you need to execute to upload your project to GitHub. Follow the instructions.
These instructions will be similar to the following commands.
Global setup: Set up git git config --global user.name "Your Name" git config --global user.email your.email@gmail.com Next steps: mkdir gitbook cd gitbook git init touch README git add README git commit -m 'first commit' git remote add origin git@github.com:vogella/gitbook.git git push -u origin master Existing Git Repo? cd existing_git_repo git remote add origin git@github.com:vogella/gitbook.git git push -u origin master
Bitbucket can be found under the following URL.
https://bitbucket.org/
Bitbucket allows unlimited public and private repositories. The number of participants for a free private repository is currently limited to 5 collaborators, i.e. if you have more than 5 developers which need access to a private repository you have to pay money to Bitbucket.
The following description highlights typical Git workflows.
Git emphasizes the creation of branches for feature development or to create bug fixes. The following description lists a typical Git workflow for fixing a bug in your source code (files) and providing a patch for it. This patch contains the changes and can be used by another person to apply the changes to his local Git repository.
This description assumes that the person which creates the changes cannot push changes directly to the remote repository. For example you may solve an issue in the source code of an Open Source project and want that the maintainer of the Open Source project integrates this into his project.
Clone the repository, in case you have not done that.
Create a new branch for the bug fix
Modify the files (source code)
Commit changes to your branch
Create patch
Send patch to another person or attach it to a bug report, so that is can be applied to the other Git repository
You may also want to commit several times during 3.) and 4.) and rebase your commits afterwards.
Even if you have commit rights, creating a local branch for every feature or bug fix is a good practice. Once your development is finished you merge your changes to your master and push the changes from master to your remote Git repository.
Sometimes you want to add another remote repository to your local Git repo and pull and push from and to both repositories. The following example describes how to add another remote repository and to pull and fetch from both repositories.
You can add another remote repository called remote_name via the following command.
# add remote git remote add <remote_name> <url_of_gitrepo> # see all repos git remote -v
For merging the changes in remote_name create a new branch called newbranch.
# create a new branch which will be used
# to merge changes in repository 1
git checkout -b <newbranch>
Afterwards you can pull from your new repository called remote_name and push to your original repository.
# reminder: your active branch is newbranch # pull remote_name and merge git pull <remote_name> # or fetch and merge in two steps git fetch <remote_name> git merge <remote_name>/<newbranch> # afterwards push to first repository git push -u origin master
Another very common Git workflow is the usage of pull requests. In this workflow a developer clones a repository and once he thinks he has something useful for another clone or the origin repository he sends the owner a pull request asking to merge his changes.
A pull request can be viewed as a notification which includes the information from which branch and URL the changes can be pulled and also the information to which URL and branch these changes should be pulled too.
This workflow is also actively promoted by the Github.com hosting platform but you can also provide the required information to someone via email.
Git emphasizes the creation of branches for feature development or to create bug fixes. The following description contains a typical Git workflow for developing a new feature or bug fix and pushing it to the remote repository which is shared with other developers. After cloning the repository the developer would:
creates a new branch for the development
changes content in the working tree and add and commit his changes
if required he switches to other branches to do other work
once the development in the branch is complete he rebases (or merges) the commit history onto the relevant remote tracking branch to allow a fast-forward merge for this development
he pushes his changes to the remote repository, this results in a fast-forward merge in the remote repository
During this development he may fetch and merge or rebase the changes
from
the remote repository at any point in time. The developer may use
the
pull
command instead of the
fetch
command.
Before posting questions, please see the vogella FAQ. If you have questions or find an error in this article please use the www.vogella.com Google Group. I have created a short list how to create good questions which might also help you.