Git Tutorial
http://www.vogella.com/articles/Git/article.html
Version 4.8
Copyright © 2009, 2010, 2011, 2012 Lars Vogel
29.06.2012
Revision History | ||
---|---|---|
Revision 0.1 | 13.09.2009 | Lars Vogel |
Created | ||
Revision 0.2 - 4.8 | 21.11.2009 - 29.06.2012 | Lars Vogel |
bug fixes and improvements |
Table of Contents
-
1. Git
- 2. Installation 3. Setup
- 4. Getting started with Git
- 5. Working with remote repositories
- 6. Revert Changes 7. Tagging in Git 8. Branches and Merging
- 9. Solving merge conflicts 10. Rebase
- 11. Create and apply patches 12. Define alias 13. Untrack a file / directory 14. Other useful commands 15. Installing a Git server 16. Online remote repositories
- 17. Git Hosting Provider
- 18. Graphical UI's for Git 19. Get the Kindle edition 20. Questions and Discussion 21. Links and Literature
Git is a distributed version control system (dvcs) written in the programming language C.
A version control system allows the creation of a history for a collection of files and includes the functionality to revert the collection of files to another state. Another state might be a different collection of files or different content in the files. The collection of files is usually called sourcecode.
You may, for example, change the collection of files to a state from 2 days ago or you may switch between states for experimental features and production issues.
Git keep track of all versions. Therefore you can revert to any point in your source code history.
If you modify a file and you want to persist this change in the repository you need to perform two steps. First you need to mark them to be relevant for Git. Afterwards you add this change to the Git repository.
Marking changes as relevant for the version control is called staging or to add them to the index. Adding the changes to the repository is called committing.
For example, if you make a change in a file and want that this change is relevant for the next commit, you have to add the file to the via the git add file
command. The git commit -m "your commit message"
will commit the marked changes into the Git repository.
In a distributed version control system everyone has a complete copy of the source code (including the complete history of the source code) and can perform version control operations against this local copy. The use of a dvcs does not require a central code repository.
Git performs commits to your local repository and you can synchronize your repository with other (remote) repositories. Git allows you to clone repositories, e.g. create an exact copy of a repository including the complete history of the source code. Owners of repositories can synchronize changes via push (transferring changes to a remote repository) or pull (getting changes from a remote repository).
Git supports branching, e.g. you can have different versions of your source code. If you want to develop a new feature, you may open a branch in your source code and make the changes in this branch without affecting the main line of your code.
Git allows to merges changes from difference branches. For example you may have a branch called master which contains the source code which you use to build your product which is delivered to your customers. You use another branch called feature_123 to finalize a certain feature and than use the Git merge command to bring the changes into your master branch.
Git can be used from the command line. You also find 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. The repository allows you to retrieve revisions into your working copy. |
Branches | A branch is a separate code line with its own history. 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 (normally named master). The user selects a branch and works in this selected branch, which is called the "working copy". Selecting a branch is called "checkout a branch". |
Tags | A tag points to a certain point in time in a specific branch. With a tag, you can have a named point to which you can always revert, e.g. the coding of 25.01.2009 in the branch "testing". |
Commit | You commit your changes into a repository. This creates a new revision which can be retrieved later, for example if you want to see the source code of an older version. Each commit contains the author and committer, thus making it possible to identify the source of the change. The author and committer might be different people. |
URL | A URL in Git determines the location of the repository. |
Revision | Represents a version of the source code. Git identifies revisions with SHA1 ids. SHA1 ids are 160 bits long and are represented in hexadecimal. The latest version can be addressed via "HEAD", the version before that via "HEAD~1" and so on. |
On Ubuntu you can install the Git command line tool via the following command:
sudo apt-get install git-core
For other Linux distributions please check your vendor documentation.
A windows version of Git can be found on the msysgit Project site. The URL to this webpage is http://code.google.com/p/msysgit/.
Git allows you to store global settings in a .gitconfig
file. This file is located in the user home directory. As mentioned before Git stores the committer and author in each commit. This and additional information can be stored in the global settings.
The following will configure 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" # Set default so that all changes are always pushed to the repository git config --global push.default "matching"
To query your Git settings, execute the following command:
git config --list
The following will enable some highlighting for the console.
git config --global color.status auto
git config --global color.branch auto
Git can be configured to ignore certain files and directories. This is configured via the .gitignore
file. This file can be in any directory and can contain pattern for files. For example, you can tell Git to ignore the bin
directory via the following .gitignore
file in the main directory.
bin
Git also offers the global setting core.excludesfile
to specify global excludes.
You can also setup a global .gitignore
file valid for all Git repositories.
# Create a ~/.gitignore in your user directory cd ~/ touch .gitignore # Exclude bin and .metadata directories echo "bin" > .gitignore echo ".metadata" >> .gitignore # Configure Git to use this file # as global .gitignore git config --global core.excludesfile ~/.gitignore
Git will ignore empty directories, e.g. do not put them under version control. If you want to track such directories, is it convention to put files called ".gitkeep" in these directories. The file could be called anything; Git assigns no special significance to this name. As the directory now contains a file, Git will include it into its version control mechanism.
The following will guide you through a typical Git workflow. You will create a few files, create a local Git repository and commit your file into this repository. Afterwards, you clone the repository and push and pull some changes between the repositories. The comments (marked with #) before the commands explain the specific actions.
Open a command line / shell for the operations.
The following creates some files with some content that will later be placed under version control.
#Switch to home
cd ~/
# Create a directory
mkdir ~/repo01
# Switch into it
cd repo01
# Create a new directory
mkdir datafiles
# Create a few files
touch test01
touch test02
touch test03
touch datafiles/data.txt
# Put a little text into the first file
ls >test01
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 local configuration for the repository.
The following will create a Git repository, add the files to the repository's index and commit the changes.
# Initialize the local Git repository git init # Add all (files and directories) to the Git repository git add . # Make a commit of your file to the local repository git commit -m "Initial commit" # Show the log file git log
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. Then, 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 # Commit the changes, -a will commit changes for modified files # but will not add automatically new files git commit -a -m "These are new changes"
The following helps you see the current status and the list of commits in your repository.
# Make some changes in the file echo "This is a new change" > test01 echo "and this is another new change" > test02 # See the current status of your repository # (which files are changed / new / deleted) git status # Show the differences between the uncommitted files # and the last commit in the current branch git diff # Add the changes to the index and commit git add . && git commit -m "More chaanges - typo in the commit message" # Show the history of commits in the current branch git log # This starts a nice graphical view of the changes gitk --all
The git amend command makes it possible to change the last commit message.
In the above example the commit message was incorrect as it contained a typo. The following will correct this via the --amend
parameter.
git commit --amend -m "More changes - now correct"
If you delete a file which is under version control git add .
will not pick this file up. You need to use the git commit command with the -a
flag or the -A
flag in the git add
command.
# 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 # Try standard way of committing -> will not work git add . && git commit -m "a new file has been created" # Now commit 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"
We will now create a remote Git repository. Git allows you to store this remote repository either on the network or locally.
A standard Git repository is different from a remote Git repository. A standard Git repository contains the source code and the Git repository. You can work directly in this directory as the repository contains a working copy of all files.
Remote repositories do not contain working copies of the files. They only contain repository files. To create such a repository, set the --bare
flag.
In order to simplify the following examples, the Git repository will be created locally in the filesystem.
# Switch to the first repository
cd ~/repo01
#
git clone --bare . ../remote-repository.git
# Check the content, it is identical to the .git directory in repo01
ls ~/remote-repository.git
Make some changes 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
You can always push to a Git repository via its full URL. But you can also add a "shortname" to a repository via the git remote add
command. origin
is a special name which is normally used automatically, if you clone a Git repository. Origin indicates the original repository from which you started. As we started from scratch, this name is still available.
# Add ../remote-repository.git with the name origin git remote add origin ../remote-repository.git # Again some changes echo "I added a remote repo" > test02 # Commit git commit -a -m "This is a test for the new remote origin" # If you do not label a repository it will push to origin git push origin
To see the existing definitions of the remote repositories, use the following command.
# Show the existing defined remote repositories
git remote
Create a new repository 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 .
Pull allows you to get the latest changes from another repository. In your second repository, make some changes, push them to your remote repository and pull these changes to your first repository.
# Switch to home cd ~ # Switch to second directory cd ~/repo02 # Make changes echo "A change" > test01 # Commit git commit -a -m "A change" # Push changes to remote repository # Origin is automatically maintained as we cloned from this repository git push origin # Switch to the first repository and pull in the changes cd ~/repo01 git pull ../remote-repository.git/ # Check the changes less test01
If you create files in your working copy which you do not want to commit, you can discard them.
# Create a new file with content touch test04 echo "this is trash" > test04 # Make a dry-run to see what would happen # -n is the same as --dry-run git clean -n # Now delete git clean -f
You can check out older revisions of your source code 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
# Copy one of the older commits and checkout the older revision via
git checkout commit_name
If you have not added the changes to the staging index, you can also revert the changes directly.
#Some nonsense change echo "nonsense change" > test01 # Not added to the staging index. Therefore we can # just checkout the old version git checkout test01 # Check the result cat test01 # Another nonsense change echo "another nonsense change" > test01 # We add the file to the staging index git add test01 # Restore the file in the staging index git reset HEAD test01 # Get the old version from the staging index git checkout test01
You can also revert commits via the following command:
# Revert a commit
git revert commit_name
If you deleted a file but you have not yet added it to the index or committed the change, you can check out the file again.
# Delete a file
rm test01
# Revert the deletion
git checkout test01
If you added a file to the index but do not want to commit the file, you can remove it from the index via the git reset file
command.
// Create a file
touch incorrect.txt
// Accidently add it to the index
git add .
// Remove it from the index
git reset incorrect.txt
// Delete the file
rm incorrect.txt
If you deleted a directory and you have not yet committed the changes, you can restore the directory via the following command:
git checkout HEAD -- your_dir_to_restore
Git has the option to tag certain versions in the history so that you find them more easily at a later point in time. Most commonly, this is used to tag a certain version which has been released.
You can list the available tags via the following command:
git tag
You can create a new tag via the following. Via the -m
parameter, you specify the description of this tag.
git tag version1.6 -m 'version 1.6'
If you want to use the code associated with the tag, use:
git checkout <tag_name>
Git allows you to create branches, e.g. independent copies of the source code which can be changed independently from each other. The default branch is called master.
Git allows you to create branches very fast and cheaply in terms of resource consumption. Developers are encouraged to use branches frequently.
The following command lists all locally available branches. The currently active branch is marked with *
.
git branch
If you want to see all branches (including remote branches), use the following command.
git branch -a
You can create a new branch via the following.
# Syntax: git branch <name> <hash> # <hash> in the above is optional # if not specified the last commit will be used # If specified the corresponding commit will be used git branch testing # Switch to your new branch git checkout testing # 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 test01 is the old one cat test01
Merge allows you to combine the changes of two branches. Merge 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, you have a new snapshot. You can merge changes from one branch to the current active one via the following command.
# Syntax: git merge <branch-name>
git merge testing
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 index and commit the change.
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
By default Git will only push matching branches to a remote repository. That means that you have to manually push a new branch once. Afterwards "git push" will also push the new branch.
# Push testing branch 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 decided which branches should be visible to other repositories and which should be local branches.
A merge conflicts occurs, if two people have modified the same content and Git cannot automatically determine how both changes should be applied.
Git requires that merge conflicts are solved manually. In this section; we will first create a merge conflict and then resolve it and apply the change to the Git repository.
The following will create a merge conflict.
# Switch to the first directory cd ~/repo01 # Make changes touch mergeconflict.txt 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 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 could now edit the file manually and then 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.
# 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"
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 "added more content" echo " more content" >> rebase.txt git add . && git commit -m "added more content" echo " more content" >> rebase.txt git add . && git commit -m "added more content" echo " more content" >> rebase.txt git add . && git commit -m "added more content" echo " more content" >> rebase.txt git add . && git commit -m "added more content" # 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 edit the commit message or squash
/ fixup
the commit with the last one.
Squash will combine the commit messages while fixup
will disregard the commit message.
You can also use Git to rebase two branches. As described; the merge
command combines the changes of two branches. Rebase takes the changes of a branch, creates a patch and applies it to another branch.
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 branch testing # Checkout the branch git checkout testing # Make some changes echo "This will be rebased to master" > test01 # Commit into testing branch git commit -a -m "New feature in branch" # Rebase the master git rebase master
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 points to which you can go back, if something should go wrong later during a feature development. If you do so you, before pushing, 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 already implemented in the past.
A patch is a text file that contains changes to the source code. This file can be sent to someone else and this person can use this file to apply the changes to his/her local repository.
The following creates a branch, makes some changes in this branch, creates a patch and applies the patch to the master.
# 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" # Create a patch --> git format-patch master git format-patch origin/master # This created patch 0001-First-commit-in-the-branch.patch # Switch to the master git checkout master # Apply the patch git apply 0001-First-commit-in-the-branch.patch # Do your normal commit in the master git add . git commit -a -m "Applied patch" # Delete the patch rm 0001-First-commit-in-the-branch.patch
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.
For example, the following defines the git add-commit
command which combines git add . -A
and git commit -m
. After defining this command, you can use it via the git add-commit -m "message"
command.
git config --global alias.add-commit '!git add . -A && git commit'
Unfortunately, defining an alias is at the time of writing this not completely supported in msysGit. You can do single aliases, e.g. ca
for ca
= commit -a) but you can not do ones beginning with !
.
Sometimes you want to have files or directories not being included in your Git repository. If you add it to your .gitignore
file, Git will stop tracking it from this moment. It will not remove it from the repository. Thus, the last version will still be in git. To untrack a file or directory in Git you can use.
# Remove directory .metadata from git repo
git rm -r --cached .metadata
# Remove file test.txt from repo
git rm --cached test.txt
This will not remove the file from the commit 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.
The following lists a few Git commands that are useful in the daily work with Git.
Table 2. Useful Git Commands
Command | Description |
---|---|
git blame filename | Who created / modified the file |
git checkout -b mybranch master~1 | Creates a new branch based on the master branch without the last commit |
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.
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 commit 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
Git also support remote operations. Git supports several transport types; the native protocol for Git is also called git
.
The following will clone an existing repository via the Git protocol.
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
If you clone a remote repository, the original repository will automatically be called origin
.
You can push changes to this origin repository via git push origin
. Of course, pushing to a remote repository requires write access to this repository.
You can add more remote repositories to your repository via the git remote add name gitrepo
command. For example if you cloned the repository from above via the Git protocol, you could add the http protocol via:
// Add the https protocol
git remote add githttp 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.
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
This example uses the 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
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.
A 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 hold 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 your have to enter in 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 generation a rsa key on the ssh key creation in Ubuntu webpage. For Windows please see msysgit ssh key generation .
GitHub can be found under the URL https://github.com/. GitHub is free for all public repositories, i.e. if you want to have private repositories which are only visible to people you select, you have to pay GitHub a monthly fee.
Create an account at GitHub and 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:
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 URL https://bitbucket.org/. Bitbucket allows unlimited public and private repositories, while the number of participants for one private repository is currently limited to 5 collaborators. I.e. if you have more then 5 developers which need access to a private repository you have to pay money to Bitbucket.
This tutorial focused on the usage of the command line for Git. After finishing this tutorial, you may want to look at graphical tools for working with Git.
Git provides two graphical tools. gitk
shows the history and git gui
shows an editor that allows you to perform Git operations.
The Eclipse EGit project provides Git integration into Eclipse, which is included in the latest Eclipse release.
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.
EGit - Teamprovider for Eclipse
Video with Linus Torwalds on Git
http://code.google.com/p/msysgit/ Git on Windows
http://github.com/guides/git-cheat-sheet Git Cheat Sheets