Git recipes


A collection of small useful recipes for using with Git.

Sharing repositories

Use the command:

git init --bare --shared /srv/git/myrepo.git

Creates a repo with the following config:

  1. core.bare = true: make it a bare repo
  2. core.sharedrepository = 1 (same as core.sharedrepository = group): the repo directory and all directories later created in it will be managed by git to allow group read, write, and execute permissions (with the sgid bit set as well -- so as to work with users different primary group)
  3. receive.denyNonFastforwards = 1: deny non fast-forward pushes to the repo

Make sure that the user and group ownership of the files is correct. You may need to run chown -R or chgrp -R to correct this.

If you want to fine-tune the user, group, or other users' permissions, use --shared=0NNN, where NNN are the standard user, group, and other bits for files (the execute and sgid bits on directories will be managed appropriately by git). For example, this allows read and write access to the user, and read-only access to the group (and no access to other):

git init --bare --shared=0640 /srv/git/myrepo.git

This allows read and write access to the user and group (and no access to other):

git init --bare --shared=0660 /srv/git/myrepo.git

This allows read and write access to the user and group, and read-only access to other:

git init --bare --shared=0664 /srv/git/myrepo.git

This is the default.

Rewriting history

Rolling back the last commit

if nobody has pulled your remote repo yet, you can change your branch HEAD and force push it to said remote repo:

git reset --hard HEAD^
git push -f

Restoring changes

So in the event that you want to go back to a previous version of a file. First you must identify the version using:

git log $file

Once you know which commit to go to, do:

git checkout $hash $file

Then

git commit $file

User friendly version ids

Creating version ids Use:

 git describe

Gives:

 $tag-$commit_count-$hash

However for this to work, you need to have a good tag set and a good tag naming convention.

Branches

Main branch names:

  • master - The main branch. Source code of HEAD always reflects production-ready status.
  • develop or dev - Main dev branch. HEAD always reflects state with the latest development changes for the next release. This can sometimes be called the "integration branch" and used to generate automatic nightly builds.

Also a variety of supporting branches to aid parallel development between team members, ease tracking of features, prepare for production releases and to assist in quickly fixing live production problems. Unlike the main branches, these branches always have a limited life time, since they will be removed eventually. Creating a new branch:

 git checkout -b new_branch develop
 # Creates a branch called "new_branch" from "develop" and switches to it
  git push -u origin new_branch
  # Pushes "new_branch" to the remote repo

Listing branches

 git branch      # List all local branches
 git branch -a  # List local and remote branches

Merging branches

 git checkout dev
 # Switches to branch that will receive the commits...
 git merge --no-ff "feature_branch"
 # makes the a single commit (instead of replaying all the commits from the feature branch)

Deleting branches

git branch -d branch_name    # Only local branches
git push origin --delete branch_name # Remote branch
git push origin :branch_name # Old format for deleting... prefix with ":"

Clean-up delete branches in remote repo from local repo...

git branch --delete branch
git remote prune origin

Tagging

Creating tags

Tag releases with

git tag -a $tagname -m "$descr"

This creates an annotated tag that has full meta data content and it is favored by Git describe.

Temporary snapshots

git tag $tagname

These are lightweight tag that are associated to a specific commit.

Sharing tags

By default are not pushed. They need to be exported with:

git push origin $tagname

or

git push origin --tags

To pull tags (if there aren't any)

git fetch --tags

Deleting tags

git tag -d $tagname    # Local tags
git push --delete origin $tagname # Remote tags
git push origin :refs/tags/$tagname   # Remote tags (OLD VERSION)

Rename a tag:

git tag new old
git tag -d old
git push origin :refs/tags/old

Setting up GIT

git config --global user.name "user"
git config --global user.email "email"

Other settings:

[http]
  sslVerify = false
  proxy = http://10.47.142.30:8080/
[user]
  email = [email protected]
  name = alex

Using ~/.netrc for persistent authentication

Create a file called .netrc in your home directory. Make sure you sets permissions 600 so that it is only readable by user. With Windows, create a file _netrc in your home directory. You may need to define a %HOME% environment variable. In Windows 7 you can use:

setx HOME %USERPROFILE%

or

set HOME=%HOMEDRIVE%%HOMEPATH%

The contents of .netrc (or _netrc) are as follows:

|machine $system
|   login $user
|   password $pwd
|machine $system
|   login $user
|   password $pwd

Creating new repositories

mkdir ~/hello-world
cd ~/hello-world
git init
# Creates an empty repository in ~/hello-world
touch file
git add file
git commit -m 'first commit'
# Creates a new file and commits locally
git remote add origin 'https://$user:[email protected]/$user/hello-world.git
# Creates a remote name for push/pull
git push origin master
# Send commits to remote

Creating a bare repo:

mkdir templ
cd templ
echo "Initial commit" > README.md
git add README.md
git commit -m"Initial commit"
git clone --bare .

Vendor Branches

Set-up

unzip wordpress-2.3.zip
cd wordpress
# Note, unzip creates this directory...
git init
git add .
git commit -m 'Import wordpress 2.3'
git tag v2.3
git branch upstream
# Create the upstream branch used to track new vendor releases

When a new release comes out:

cd wordpress
git checkout upstream
rm -r \*
# Delete all files in the main directory but doesn't touch dot files (like .git)
(cd .. && unzip wordpress-2.3.1.zip)
git add .
git commit -a -m 'Import wordpress 2.3.1'
git tag v2.3.1
git checkout master
git merge upstream

A variation of vendor branches is to sync with an upstream fork in github. Read this guide on how to do that: Syncing a fork on github

GIT through patches

Creating a patch:

 ... prepare a new branch to keep work separate ...
 git checkout -b mybranch
 ... do work ...
 git commit -a
 .. create the patch from branch "master"...
 git format-patch master --stdout > file.patch

To apply patch..

 ... show what the patch file will do ...
 git apply --stat file.patch
 .. displays issues the patch might cause...
 git apply --check file.patch
 .. apply with am (so you can sign-off)
 git am --signoff < file.patch

Maintenance

git fsck
git gc --prune=now     # Clean-up
git remote prune origin # Clean-up stale references to deleted remote objects

Submodules

Add submodules to a project:

git submodule add $repo_url $dir

Clone a project with submodules:

git clone $repo_url
cd $repo
git submodule init
git submodule update

Or in a single command (Git >1.6.5):

git clone --recursive $repo_url

For already cloned (Git >1.6.5):

git clone $repo_url
cd $repo
git submodule update --init --recursive

To keep a submodule up-to-date:

git pull
git submodule update

Remove sub-modules:

git submodule deinit $submodule
git rm $submodule # No trailing slash!

setting git email per repository

Navigate to the work repository, then at the root folder run the following command to change the email.

git config --local user.email [email protected]

Note: this command only affects the current repository. Any other repositories will still use the default email specified in ~/.gitconfig.

Alternatively, you can have different configurations based on a directory path by using:

Contents of $HOME/.gitconfig

[includeIf "gitdir:~/work/"]
    path = .gitconfig-work
[includeIf "gitdir:~/personal/"]
    path = .gitconfig-personal