Git

Git is the version control system (VCS) designed and developed by Linus Torvalds, the creator of the Linux kernel. Git is now used to maintain sources for the Linux kernel as well as thousands of other projects, including a number of Arch Linux projects.

Installation

Install git from the official repositories.

If you want to use Git's built-in GUI tools (e.g. gitk or git gui), make sure you have installed the tk package, or they will refuse to start with a cryptic error:

/usr/bin/gitk: line 3: exec: wish: not found.

Also, the GUI tools require gsfonts, or they will crash with a segmentation fault.

If you want to use the Git SVN bridge (git svn) you will need perl-term-readkey, or you will receive the following error:

Can't locate Term/ReadKey.pm in @INC (you may need to install the Term::ReadKey module)

Basic configuration

In order to use Git you need to set at least a name and email:

$ git config --global user.name  "John Doe"
$ git config --global user.email "johndoe@foobar.com"

See #Advanced configuration for more settings.

Basic usage

This tutorial teaches how to use Git for basic distributed revision control of a project. A typical Git workflow is generally:

  1. Create a new project or clone a remote one.
  2. Create a branch to make changes; then commit those changes.
  3. Consolidate commits for better organization/understanding.
  4. Merge commits back into the main branch.
  5. (Optional) Push changes to a remote server.

Local repository

Create a repository

Initialize a new repository:

$ git init

Add files to the repository index:

$ git add file1 file2

Or, to add all files:

$ git add .
Tip: To use git add and have some files ignored, create a .gitignore file:
.gitignore
# File I'll likely delete
test-script

Remove a file:

$ git rm file

Rename a file:

$ git mv file1 file2

List files:

$ git ls-files

Commit changes

In order to record the changes to the repository, they must first be added to the index, or staging area, with an operation often also referred to as staging. This is done with the git add command:

$ git add file

Once the content to be recorded is staged, it can be committed to the repository:

$ git commit --message "First commit."

The --message option is for a short message: if omitted, the preset editor will be spawned to allow entering a longer message.

Tip:
  • Always commit small changes frequently and with meaningful messages.
  • If you want to add all the modified files to the index, and commit them in a single command, do:
$ git commit --all --message "First commit."

To go back and edit the commit message:

$ git commit --amend

Many of the commands in this article take commits as arguments. A commit can be identified by any of the following:

  • Its 40-digit SHA-1 hash (the first 7 digits are usually sufficient to identify it uniquely)
  • Any commit label such as a branch or tag name
  • The label HEAD always refers to the currently checked-out commit (usually the head of the branch, unless you used git checkout to jump back in history to an old commit)
  • Any of the above plus ~ to refer to previous commits. For example, HEAD~ refers to one commit before HEAD and HEAD~5 refers to five commits before HEAD.

View changes

Show differences between commits:

$ git diff

To get a general overview of the changes:

$ git status

View history of changes with:

$ git log

Branch a repository

Fixes and new features are usually tested in branches. When changes are satisfactory they can merged back into the default (master) branch. Branch names should reflect the alteration accurately:

$ git branch help-section-addition

To see the created branches:

$ git branch

To switch to a branch:

$ git checkout branch

To create and switch to a branch in one step:

$ git checkout -b branch

To merge a branch back to the master branch:

$ git checkout master
$ git merge branch

The changes will be merged if they do not conflict. If they do, the conflicts will be recorded. What is causing the conflicts can been seen with git diff, then conflict resolution will need to be done manually.

When done with a branch, it can be deleted by:

$ git branch -d branch

Collaboration

Adopting a good etiquette

  • When considering contributing to an existing project, read and understand its license, as it may excessively limit your ability to change the code. Some licenses can generate disputes over the ownership of the code.
  • Think about the project's community and how well you can fit into it. To get a feeling of the direction of the project, read any documentation and even the log of the repository.
  • When requesting to pull a commit, or submit a patch, keep it small and well documented; this will help the maintainers understand your changes and decide whether to merge them or ask you to make some amendments.
  • If a contribution is rejected, do not get discouraged, it is their project after all. If it is important, discuss the reasoning for the contribution as clearly and as patiently as possible: with such an approach a resolution may eventually be possible.

Clone a repository

To begin contributing to a project start by cloning its repository:

$ git clone location folder

location can be either a path or network address. Also, when cloning is done, the location is recorded so just a git pull will be needed later.

Pull requests

After making and committing some changes, the contributor can ask the original author to the merge them; this is called a "pull request".

The original author can merge the changes by doing:

$ git pull location master

The pull command fetches the changes and attempts to merge them. If there are conflicts (e.g. the original author made changes in the same time span) then it will be necessary to manually fix them.

Alternatively, the original author can pick the changes wanting to be incorporated. Using the fetch option (and log option with a special FETCH_HEAD symbol) the contents of the pull request can be viewed before deciding what to do:

$ git fetch location master
$ git log -p HEAD..FETCH_HEAD
$ git merge location master

Using remotes

Remotes are tracked repositories, a label defining a location. They are commonly used for frequently accessed repositories.

$ git remote add label location
$ git fetch label
$ git log -p master..label/master

Remotes for the current repository can be viewed with:

$ git remote -v

When defining a remote that is a parent of the fork (the project lead), it is defined as upstream.

Push to a repository

If the contributor is given the rights from the original authors, the changes can be pushed:

$ git push location branch

When git clone is performed it records the original location and gives it a remote name of origin. So what is typically done is this:

$ git push origin master

If the --set-upstream option is used, the location is recorded so the next time just a git push is necessary.

Dealing with merges

See Basic Merge Conflicts in the Git Book for a detailed explanation on how to resolve merge conflicts. Merges are generally reversible. If wanting to back out of a merge one can usually use the --abort command (e.g. git merge --abort or git pull --abort).

History and versioning

Searching the history

git log will give the history with a commit checksum, author, date, and the short message. To see the long message (the checksum can be truncated as long as it is unique):

$ git show checksum

To show details of the last commit:

$ git show HEAD

To search through files of a previous commit it can be done with:

$ git grep checksum

Ranges can also be specified:

$ git grep tag2..tag4

Versioning for release

For versioning, commits can be tagged:

$ git tag 2.14 checksum

Tagging is generally done for releasing/versioning but it can be any string. Generally annotated tags are used because they get added to the Git database. To tag the current commit:

$ git tag -a 2.14 -m "Version 2.14"

Tags can be listed and deleted with:

$ git tag -l
$ git tag -d 2.08

Tags that have to be updated remotely, have to be done so separately:

$ git push --tags

Organizing commits

Before submitting a pull request it may be desirable to consolidate/organize the commits. This is done with the git rebase interactive option:

$ git rebase -i checksum

This will open the editor with a summary of all the commits in the range specified; in this case including the newest (HEAD) to, but excluding, checksum. Or to use a number notation, use for example HEAD~3, which will rebase the last three commits.

pick d146cc7 Mountpoint test.
pick 4f47712 Explain -o option in readme.
pick 8a4d479 Rename documentation.

Editing the action in the first column will dictate how the rebase will be done. The options are:

  • pick — Apply this commit as is (the default).
  • edit — Edit files and/or commit message.
  • reword — Edit commit message.
  • squash — Merge/fold into previous commit.
  • fixup — Merge/fold into previous commit discarding its message.

The commits can be re-ordered or erased from the history (but be very careful with these). After editing the file, Git will perform the specified actions; if prompted to resolve merge problems, fix them and continue with git rebase --continue or back out with the git rebase --abort command.

Note: Squashing commits is only used for local commits, it will cause troubles on a repository that is shared by other people.

Advanced configuration

Git reads its configuration from a few INI-type configuration files:

  • Each repository contains a .git/config file for specific configuration.
  • Each user has a $HOME/.gitconfig file for fallback values.
  • /etc/gitconfig is used for system-wide defaults.

These files can be edited directly, but the usual method is to use the git-config utility as shown in the examples below.

To list the currently set variables:

$ git config {--local,--global,--system} --list

The default editor is vim but to set it to nano:

$ git config --global core.editor "nano -w"

To set a default push action:

$ git config --global push.default simple

To set a tool for git difftool (by default it is meld):

$ git config --global diff.tool vimdiff

See git-config(1) and Git Configuration for more information.

Bash completion

In order to enable Bash completion, source /usr/share/git/completion/git-completion.bash in a Bash startup file. Alternatively, install bash-completion.

Git prompt

The Git package comes with a prompt script. To enable it, source the /usr/share/git/completion/git-prompt.sh script in a shell startup file, then set a custom prompt with the %s parameter:

  • For Bash: PS1='[\u@\h \W$(__git_ps1 " (%s)")]\$ '
  • For zsh: PS1='[%n@%m %c$(__git_ps1 " (%s)")]\$ '

When changing to a directory of a Git repository, the prompt will change to show the branch name. Extra details can be set to be shown by the prompt:

Variable Information
GIT_PS1_SHOWDIRTYSTATE + for staged, * if unstaged.
GIT_PS1_SHOWSTASHSTATE $ if something is stashed.
GIT_PS1_SHOWUNTRACKEDFILES % if there are untracked files.
GIT_PS1_SHOWUPSTREAM <,>,<> behind, ahead, or diverged from upstream.

GIT_PS1_SHOWUPSTREAM will need to be set to auto for changes to take effect.

Note: If you experience that $(__git_ps1) returns ((unknown)), then there's a .git folder in your current directory which does not contain any repository, and therefore Git does not recognize it. This can, for example, happen if you mistake Git's configuration file to be ~/.git/config instead of ~/.gitconfig.

Advanced usage

To view get an idea of the amount of work done:

$ git diff --stat

Git log with forking representation:

$ git log --graph --oneline --decorate

Git log graph alias (i.e. git graph will show the previous):

$ git config --global alias.graph 'log --graph --oneline --decorate'

Reset to previous commit (very dangerous, erases everything to specified commit):

$ git reset --hard HEAD^

If a repository address gets changed, its remote location will need to be updated:

$ git remote set-url origin git@address:user/repo.git

Signed-off-by line append (a name-email signature is added to the commit which is required by some projects):

 $ git commit -s

Signed-off-by automatically append to patches (when using git format-patch commit):

$ git config --local format.signoff true

Commit specific parts of files that have changed. This is useful if there are a large number of changes made that would be best split into several commits:

$ git add -p

Working with a non-master branch

Occasionally a maintainer will ask that work be done on a branch. These branches are often called devel or testing. Begin by cloning the repository.

To enter another branch beside master (git clone only shows master branch but others still exist, git branch -a to show):

$ git checkout -b branch origin/branch

Now edit normally; however to keep the repository tree in sync be sure to use both:

$ git pull --all
$ git push --all

Git server

How to set up connecting to repositories using varying protocols.

SSH

To use the SSH protocol, first set up a public SSH key; for that follow the guide at SSH keys. To set up a SSH server, follow the SSH guide.

With SSH working and a key generated, paste the contents of ~/.ssh/id_rsa.pub to ~/.ssh/authorized_keys (be sure it is all on one line). Now the Git repository can be accessed with SSH by doing:

$ git clone user@foobar.com:repository-name.git

You should now get an SSH yes/no question, if you have the SSH client setting StrictHostKeyChecking set to ask (the default). Type yes followed by Enter. Then you should have your repository checked out. Because this is with SSH, you also have commit rights now.

To modify an existing repository to use SSH, the remote location will need to be redefined:

$ git remote set-url origin git@localhost:my_repository.git

Connecting on a port other than 22 can be configured on a per-host basis in /etc/ssh/ssh_config or ~/.ssh/config. To set up ports for a repository, specify the path in .git/config. It will have a format like this if the repository is in the home directory and is using 443 for the port:

url = ssh://user@foobar.com:443/~repository-name/repo.git

Smart HTTP

Git is able to use the HTTP(S) protocol as efficiently as the SSH or Git protocols, by utilizing the git-http-backend. Furthermore it is not only possible to clone or pull from repositories, but also to push into repositories over HTTP(S).

The setup for this is rather simple as all you need to have installed is the Apache web server (apache, with mod_cgi, mod_alias, and mod_env enabled) and of course, git.

Once you have your basic setup running, add the following to your Apache configuration file, which is usually located at /etc/httpd/conf/httpd.conf:

<Directory "/usr/lib/git-core*">
    Require all granted
</Directory>

SetEnv GIT_PROJECT_ROOT /srv/git
SetEnv GIT_HTTP_EXPORT_ALL
ScriptAlias /git/ /usr/lib/git-core/git-http-backend/

The above example configuration assumes that your Git repositories are located at /srv/git and that you want to access them via something like http(s)://your_address.tld/git/your_repo.git. Feel free to customize this to your needs.

Note: Of course, you have to make sure that Apache can read and write (if you want to enable push access) to your Git repositories.

For more detailed documentation, visit the following links:

Git

Note: The Git protocol only allows read access.

Start and enable git-daemon.socket.

The daemon is started with the following options:

ExecStart=-/usr/lib/git-core/git-daemon --inetd --export-all --base-path=/srv/git

Repositories placed in /srv/git/ will be recognized by the daemon. Clients can connect with something similar to:

$ git clone git://location/repository.git

Setting access rights

To restrict read and/or write access, use standard Unix permissions. Refer to http://sitaramc.github.com/gitolite/doc/overkill.html[dead link 2013-11-06] (archive.org mirror) for more information.

For fine-grained access management, refer to gitolite and gitosis.

See also