Install

_$: apt-get install git

Configure

_$: git config --global user.name "John Doe"
_$: git config --global user.email john@example.com

Set your editor to vim

_$: git config --global core.editor "vim"

Create a new repository

_$: cd /srv/git
_$: mkdir repo
_$: git init --shared=true --bare ./repo
_$: chown --recursive root:gitgroup ./repo/
_$: chmod --recursive 2775 ./repo/		# Alternatively: g+ws

Clone a repository

Regular repository as root

#                 <user>@<server>       <path to repository>
_$: git clone ssh://root@git.example.com/srv/git/repo

If we run this command in the /a/b directory it will create the repository in /a/b/repo.

Regular repository as non-root

(root@_)_$: mkdir /srv/git/repo
(root@_)_$: chown user:user ./repo
(root@_)_$: su - user
(user@_)_$: cd /srv/git/repo
(user@_)_$: git clone ssh://user@git.example.com/srv/git/repo ./

If we run this command, it will also create the repository as /a/b/repo, but the permissions in that directory will be for the user user, which is the one with whom we have cloned the repository.

Mirror repository

Create a mirror repository:

_$: git clone --mirror ssh://root@git.example.com/srv/git/repo
_$: chown --recursive root:gitgroup /srv/git/repo
_$: chmod 2775 /srv/git/repo      # Set SGID

Update a mirror repository:

_$: git remote update

Users and groups

_$: adduser user
_$: addgroup gitgroup
_$: usermod user -aG gitgroup

Permissions

/srv/git:       root:gitgroup    0770
/srv/git/repo:  root:gitgroup    0770

SSH access

Generate key:

(user@workstation)_$: ssh-keygen    # no passphrase only if you now what you are doing

Copy the key to the git server:

(user@workstation)_$: ssh-copy-id .ssh/id_rsa.pub user@gitserver

Remove access with password to the user:

(root@gitserver)_$: passwd -l user

Check it is possible to connect with the key:

(user@workstation)_$: ssh user@gitserver

Remove ssh access but allow the user to work with git:

(root@gitserver)_$: mkdir /home/user/git-shell-commands
(root@gitserver)_$: cp /usr/share/doc/git/contrib/git-shell-commands/list /home/user/git-shell-commands/
(root@gitserver)_$: cp /usr/share/doc/git/contrib/git-shell-commands/help /home/user/git-shell-commands/
(root@gitserver)_$: chown -R user /home/user/git-shell-commands
(root@gitserver)_$: chmod -R u+x  /home/user/git-shell-commands
(root@gitserver)_$: which git-shell
/usr/bin/git-shell
(root@gitserver)_$: vipw
user:x:...:/usr/bin/git-shell

Migrate a repository

We will migrate a repository from the src host to the dst host:

(user@src)_$: git clone --mirror repo repo.bak
(user@src)_$: tar -cvf repo.tar repo.bak
(user@src)_$: scp repo.tar root@dst/srv/git

(user@dst)_$: tar -xvf repo.tar
(user@dst)_$: mv repo.bak repo ; rm repo.tar
(user@dst)_$: chown root:gitgroup /srv/git/repo
(user@dst)_$: chmod 0770 /srv/git
(user@dst)_$: chmod 2775 /srv/git/repo
(user@dst)_$: chmod --recursive g+w /srv/git/repo

Alternatively, we can do:

(user@dst)_$: git init --shared=true --bare repo
(user@dst)_$: ...
# See 'Create a new repository' for all steps
(user@src)_$: git remote add new_origin ssh://user@dst/srv/git/repo
(user@src)_$: git push --all new_origin
(user@src)_$: git push --tags new_origin
(user@src)_$: git remote rm origin
(user@src)_$: git remote rename new_origin origin

Change the origin path in the development host

/.../repo/.git/config:
----------------------
[core]
	repositoryformatversion = 0
	filemode = true
	bare = false
	logallrefupdates = true
[remote "origin"]
	fetch = +refs/heads/*:refs/remotes/origin/*
	url = ssh://<user>@<destination>/srv/git/<repository>
[branch "master"]
	remote = origin
	merge = refs/heads/master

If the repository is not bare, you can also do the following:

_$: git remote add <new_repo_name> <new_repo_url>
_$: git push <new_repo_name> master
_$: git remote rm origin
_$: git push <new_repo_name> master

Repository in the development host

_$: cd /home/user/code
_$: mkdir project
_$: cd project
_$: git clone ssh://git.example.com/srv/gitdata/project/
_$: ln -s /home/user/code/project /home/user/pyvenv/project

Deployment automation

a) Force to origin/master

_$: git fetch --all
_$: git reset --hard origin/master

b) Do not force to origin/master

_$: git fetch --all
_$: git pull origin/master

Branches

Create a branch:

_$: git branch <branch>

Create a local branch that tracks a branch in origin:

_$: git checkout --track origin/<branch>

Delete a local branch:

_$: git branch -d <local_branch>
_$: git branch -D <local_branch>    # Force removal even when the branch has not been updated

Delete a remote branch:

a) With the option --delete:

_$: git push origin --delete <remote_branch>

b) Without any option:

_$: git push origin :<remote_branch>

Check in which branch we are:

_$: git branch

Change to another branch:

_$: git checkout <branch>

Set a branch which tracks a remote branch:

_$: git branch --set-upstream <branch> origin/<branch>    # git 1.7

Upload a local branch to origin:

_$: git branch --set-upstream-to=origin/<branch> <branch>

Push

Push from one branch to another:

_$: git push origin <source_branch>:<destination_branch>

Merges

Up to a certain commit:

_$: git fetch
_$: git merge <sha1>

All the way:

_$: git fetch
_$: git merge origin/master

Differences

Differences between commits:

_$: git diff HEAD^    --numstat  # Last commit vs HEAD
_$: git diff HEAD     --numstat  # Working tree (staged + unstaged) vs HEAD
_$: git diff          --numstat  # Working tree (staged) vs HEAD
_$: git diff --cached --numstat  # Working tree (unstaged) vs HEAD

Clean a repository

_$: cd /srv/git/repo.git
_$: git gc

Update the date of the last commit

We can update the date of the last commit to the current date and time:

_$: git commit --amend --no-edit --date="$(date -R)"

If you find yourself doing this a lot, you can add it to your .bashrc file as an alias:

.bashrc:
--------
...
alias gupdate='git commit --amend --no-edit --date="$(date -R)"'

Delete a file from the commit history

Sources:

  • https://help.github.com/articles/remove-sensitive-data
  • http://stackoverflow.com/questions/5563564/completely-remove-files-from-git-repo-and-remote-on-github
  • http://stackoverflow.com/questions/2164581/remove-file-from-git-repository-history
  • http://stubbisms.wordpress.com/2009/07/10/git-script-to-show-largest-pack-objects-and-trim-your-waist-line/
_$: git filter-branch         --index-filter 'git rm --cached --ignore-unmatch <file>' HEAD
_$: git filter-branch --force --index-filter 'git rm --cached --ignore-unmatch <file>' --prune-empty --tag-name-filter cat -- --all
_$: git push origin master --force

In order for the push to work, the repository must have the fast forward enabled:

repo/.git/config:
-----------------
...
[receive]
	denyNonFastforwards = false

Force the rewriting of the repo to one branch

_$: git fetch origin master     # or: git fetch origin <branch>
_$: git reset --hard FETCH_HEAD

Move to a previous commit

_$: git log
...
_$: git reset ba59b4eee45e9a357e8ed26510250c40bcc5edf5  # Commit ID

Changes made

Yours (working tree) with respect to HEAD:

_$: git log  @{upstream}..HEAD
_$: git diff @{upstream}..HEAD              # Usual
_$: git diff --stat @{upstream}..HEAD       # With statistics
_$: git diff --name-only @{upstream}..HEAD  # Only filenames

HEAD with respect to yours (working tree):

_$: git log  HEAD..@{upstream}
_$: git diff HEAD..@{upstream}              # Unusual
_$: git diff --stat HEAD..@{upstream}       # With statistics
_$: git diff --name-only HEAD..@{upstream}  # Only filenames

Remember: HEAD –> index –> working tree

index with respect to HEAD:

_$: git diff --cached

working tree with respect to index:

_$: git diff

working tree with respect to HEAD:

_$: git diff HEAD

Some commit to another:

_$: git diff <oldest SHA> <newest SHA>

Some commit to another, just a file:

_$: git diff <oldest SHA> <newest SHA> -- <file>

Some commit:

_$: git show <commit SHA>

Save changes

Add a file:

_$: git add <file>

Commit changes

_$: git commit -m "Commit message explaining changes."

Push changes:

_$: git push origin master

Discard changes

Of all files in unstage state:

_$: git clean -df && git checkout -- .      # Run from the main directory

Of a file:

_$: git checkout -- </path/to/file>

Of a commit in master already pushed:

_$: git log                     # Find the previous commit's SHA -> fabc72b2
_$: git reset fabc72b2 --hard   # Reset to that commit
_$: git push origin +master     # Force push to master

History of a deleted file

_$: git log --all -- /path/to/file