Git

From KevinWiki

Jump to: navigation, search

Contents

Git

Client-side Installation

In the client,

$ apt-get install git-core 

Or to install the latest version of Git on Ubuntu

add-apt-repository ppa:git-core/ppa 
apt-get update 
apt-get install git-core 

Configuration

User Info

$ git config --global user.name "GivenName Surname" 
$ git config --global user.email user@email.address 

Default Text Editor

$ git config --global core.editor "sublime-text -w"

External Diff Tool

Meld As External Diff Tool

  • To use Meld as a diff tool, install Meld
$ sudo apt-get install meld 
  • Set it as the default diff tool
$ git config --global diff.external meld 
  • It doesn't work and gives the following error messages.
$ git diff 
Usage: 
  meld                        Start with an empty window
  meld <file|dir>             Start a version control comparison
  meld <file> <file> [<file>] Start a 2- or 3-way file comparison
  meld <dir> <dir> [<dir>]    Start a 2- or 3-way directory comparison
  meld <file> <dir>           Start a comparison between file and dir/file

meld: error: too many arguments (wanted 0-4, got 7)
  • Create ~/diff.py
#!/usr/bin/python

import sys
import os

os.system('meld "%s" "%s"' % (sys.argv[2], sys.argv[5]))
  • Make it executable
$ chmod a+x ~/diff.py 
  • Set the script as the diff tool
$ git config --global diff.external ~/diff.py 
  • Now it works
$ git diff 
$ git diff HEAD 


Meld as Diff Tool

  • This way is much easier.
$ git config --global diff.tool meld 
$ git difftool

Meld as Diff Tool

$ git config --global diff.tool vimdiff 


Unset External Diff Tool

$ git config --global --unset diff.external 


Commit Template

Create a template file
e.g.) $HOME/git/.your-config/git-commit-comment-template.txt

CLOSED - #TICKET_NUMBER: Summary
TICKET_URI
Details
$ git config --global commit.template $HOME/git/.your-config/git-commit-comment-template.txt 


Etc.

$ git config --global color.ui true

Server-side Installation

In the server,

$ apt-get install git 

Gitolite

Add User to Manage Gitolite

  1. Add user to manage gitolite
$ adduser \
  --system \
  --shell /bin/bash \
  --gecos 'git version control' \
  --group \
  --disabled-password \
  --home /home/git \
  git

Adding system user `git' (UID 108) ...
Adding new group `git' (GID 110) ...
Adding new user `git' (UID 108) with group `git' ...
Creating home directory `/home/git' ...

Create SSH key

$ ssh-keygen -t rsa 

/home/username/.ssh/some_git_rsa [ENTER] [ENTER]

Transfer SSH Keys to Server

$ ssh-copy-id '-p PORT_NUMBER -i /home/user/.ssh/some_git_rsa.pub git_user@host' 

If it doesn't work because the user is the only user who can access the server with his/her ssh key so accessing with git_user doesn't work.

$ scp -P PORT ~/.ssh/some_git_rsa.pub user@host: 

Log in with the user account

$ ssh -p PORT user@host
$ mv some_git_rsa.pub git.pub 

Setup

$ su - git 
$ cd ~ 
$ echo "PATH=$HOME/bin:$PATH" >> ~/.bashrc 
  • don't need these 2 lines
$ mkdir .ssh 
$ cat /home/user/git.pub >> /home/git/.ssh/authorized_keys 
$ exit

Gitolite Installation

$ sudo -i 
$ apt-get install gitolite
[ENTER] -> :q to exit
$ exit

Setup git User

$ su - git
$ gl-setup /home/user/git.pub 
$ vim /etc/ssh/sshd_config 

Add git to the allowed user

AllowUsers user git

Restart ssh

$ service ssh restart 

Test

  • Testing (from user local machine)
$ ssh -p PORT_NUMBER git@host 

Having many keys?

$ ssh -p PORT_NUMBER -i ~/.ssh/user@host1.pub git@host 

For debugging

$ ssh -p PORT_NUMBER -vT git@host 
$ mkdir some-dir 
$ cd some-dir 
$ git clone git@host:gitolite-admin 

Or if you use a specific port,

$ git clone ssh://git@host:PORT_NUMBER/gitolite-admin

Add Git User (through Gitolite)

Adding User

$ cd gitolite-admin/keydir 
$ mkdir user_name 
$ cp -p ~/.ssh/user@host1.pub .
$ cp -p ~/.ssh/user@host2.pub .
$ git add user@host1.pub 
$ git commit -a -m "users added: user@host1 and user@host2" 
$ git push

If directly copied to /home/git/.gitolite/keydir, run

$ gl-setup

to apply the changes

Usage

Initializing a Repository in an Existing Directory

  • To make a directory a git repository, go to the project directory and run the following command
 $ git init 

Then it will create a new directory named .git inside the project directory. The .git folder contains all necessary files for the git repository.

Host Info Config using SSH_Config

Create ~/.ssh/config file and edit.

Host host.name
  Hostname host.name
  Port 1111

Host another.host.name
  Hostname another.host.name
  Port 2222

Host host1
  Hostname host.name
  Port 1111
  User git
  IdentityFile ~/.ssh/git_rsa

Host user-host2
  Hostname host2.name
  Port 1234
  User user
  IdentityFile ~/.ssh/user@localhost1

Host user-host3
  Hostname host3.com
  Port 1122
  User user
  IdentityFile ~/.ssh/user@localhost1

IdentityFile is a private key

To use this info,

$ git clone git@host1:repo-name 
# it will use 'git' as a user and access the server 'host.name' using the port nubmer 1111.
$ git clone git@host1:repo-name 
# it will use 'user' as a user and access the server 'host2.name' using the port nubmer 1234.

Push to Specific Host

$ git push git@host1:repo-name master

Set up Project Specific User Info

  • Move to the project repository
$ git config user.email user@email.address 

To see and edit all the config details

$ git config -e

.gitignore

To exclude files when committing, create .gitignore file and put in the project root (git project repo root). For maven project, the content should be

target/

To set up the global ignore file

$ git config --global core.excludesfile $HOME/git/.your-config/.global-gitignore 

Other gitignore files,

https://github.com/github/gitignore


.gitignore for Eclipse and Maven (for Kevin)

target/

*.pydevproject
.project
.classpath
.settings/
.metadata
bin/
tmp/
*.tmp
*.bak
*.swp
*~.nib
local.properties
.loadpath

# External tool builders
.externalToolBuilders/

# Locally stored "Eclipse launch configurations"
*.launch

# CDT-specific
.cproject

# PDT-specific
.buildpath


Commit

  • Add a file
$ git add fileName
  • Commit changes
$ git commit -m "comment"
  • Add all the files and folders
$ git add -A 
# It is equivalent to git add .; git add -u
# git add . -> add all changed and new ones but not removed ones
# git add -u -> changed and removed but not new ones.

Amend Last Comment

  • Amend the last comment
$ git commit --amend -m "New comment"

Push

Add Remote

  • To view all remote
$ git remote -v 
  • Add a remote
$ git remote add origin git@git-repo:project 

Then

$ git push -u origin master 

Or

$ git config branch.master.remote origin 
$ git config branch.master.merge refs/heads/master 

Push to Remote

  • Push to the remote git repository
$ git push
  • If it gives error messages like:
No refs in common and none specified; doing nothing.
Perhaps you should specify a branch such as 'master'.

(It doesn't happen if you have already done git push -u origin master ).

  • Do
$ git push origin master

Revert

Reset

  • Revert to last commit.
$ git reset --hard HEAD 

Or to specific commit/tag/branch

$ git reset --hard <tag/branch/commit id> 
  • Check the id using git log
$ git log 
  • To push it
$ git push <reponame> -f 

Check out

  • Or change the name of master branch then check out from the commit to be used and make it master
  1. Rename the current master branch
    $ git branch -m another_name_for_master 
  2. Check out from another commit:
     $ git checkout a1b2c3a3blah 
  3. Make it the new master branch:
     $ git checkout -b master 
  • If the old master is no longer needed and has to be removed, remove it by
$ git branch -D another_name_for_master


Restore Removed File

This has to be tested first then should be rewritten.
Find the last commit that affected the given path. As the file isn't in the HEAD commit, this commit must have deleted it.

$ git rev-list -n 1 HEAD -- <file_path> 

Then checkout the version at the commit before.

$ git checkout <deleting_commit>^ -- <file_path> 

Or in one command, if $file is the file.

$ git checkout $(git rev-list -n 1 HEAD -- "$file")^ -- "$file"
  • Reference

http://stackoverflow.com/questions/953481/restore-a-deleted-file-in-a-git-repo?answertab=votes#tab-top


Branch

Create Branch

  • Create branch
$ git branch branch_name 
  • Change to the branch just created
$ git checkout branch_name 

OR

  • Create and change
git checkout -b branch_name 
  • Back to master
$ git checkout master 
  • To push,
$ git push origin branch_name 

Change Branch

  • Change branch
$ git checkout branch_name 
  • It does not work when the local git repo has multiple remote repositories. If it's the case,
$ git checkout -b branch remote/branch 

e.g.)

$ git checkout -b my-branch origin/my-branch 


Pull Without Checkout

  • To pull a specific branch without checking out
$ git fetch <remote> <remoteBranch>:<localBranch> 

e.g.)

git fetch origin master:master 
git fetch origin adding-new-feature:adding-new-feature 

Tag

List Tags

$ git tag 

View Tag

  • To view tag data with commit details.
$ git show tag_name 

Create Tag

  • Create Tag
$ git tag -a tag_name -m 'tag message: new version blah blah' 
  • Tag later
$ git tag -a tag_name -m 'tag message' commit_checksum 

e.g.)

$ git tag -a v1.5 -m 'new version with blah blah' b015e9d 
  • To see the list of checksum use the following command.
$ git log --pretty=oneline 

Remove Tag

$ git tag -d tag_name 


Merge Multiple Git Repositories into One

If the directory structures look like this

parent-dir (not git repo)
 │
 ├─git-repo1 ## each is connected to its own remote git repository
 │  ├─src
 │  └.git
 │
 ├─git-repo2 ## each is connected to its own remote git repository
 │  ├─src
 │  └.git
 │
 └─ git-repo3 ## each is connected to its own remote git repository
    ├─src
    └.git

To make it like

parent-dir (now it is git repo)
 ├─src ## contains all the files and sub-dirs from those three repositories (git-repo1, git-repo2 and git-repo3).
 └.git

and it still keeps all the histories from all those repositories, do following instructions.

  • Initialize the new repository directory.
$ cd parent-dir 
$ git init 
  • Pull the old ones and remove these one by one.
$ git pull git-repo1 

## If there is any conflict, solve it first then
## commit the changes
$ git add file 
$ git commit -a 
## Now pull the one had conflict issue again.
$ git pull git-repo1 

$ rm -rf git-repo1 

$ git pull git-repo2 
$ rm -rf git-repo2 

$ git pull git-repo3 
$ rm -rf git-repo3 
  • Done! Add the remote repo info if necessary.


Multiple Remote Repositories

Push

Add more remote repository (origin) for push

 git remote set-url origin --add git@github.com:YOUR_USERNAME/your_project.git 

Pull

Add remote repository info for pull

 git remote add github git@github.com:YOUR_USERNAME/YOUR_PROJECT.git 
 git remote add gitlab git@gitlab.com:YOUR_USERNAME/YOUR_PROJECT.git 
 git remote add bitbucket git@bitbucket.org:YOUR_USERNAME/your_project.git 

Pull from all remote repos

  • For bash
gitpullall()
{
  repos_to_ignore="origin"
  should_ignore=-1

  echo "running: git pull"
  git pull
  echo ""

  for remote in `git remote`
    do
    echo "=================================="
    echo "Remote: $remote"
    echo "----------------------------------"
    for ignore in $repos_to_ignore
      do
      [ "$remote" == "$ignore" ] && { should_ignore=1; break; } || :
    done

    if [ "$should_ignore" == "-1" ]
      then
      THIS_BRANCH_NAME=`git rev-parse --abbrev-ref HEAD`
      git branch -r | grep -s "^[[:space:]]\+$remote/$THIS_BRANCH_NAME$" 2>&1 > /dev/null || git branch -r | grep -s "^[[:space:]]\+$remote/$THIS_BRANCH_NAME""[[:space:]]\+" 2>&1 > /dev/null || {
        echo "No info of the remote repo named '$remote' is found."
        echo "So $remote will be fetched."
        echo "running: git fetch $remote"
        git fetch "$remote"
        echo ""
      }
      if git branch -r | grep -sw "$remote/$THIS_BRANCH_NAME" 2>&1 > /dev/null ; then
        echo "running: git pull $remote $THIS_BRANCH_NAME"
        git pull "$remote" "$THIS_BRANCH_NAME"
      else
        echo "$THIS_BRANCH_NAME branch does not exist on the remote called '$remote'."
        echo "So $remote will not be pulled from $remote."
      fi
    else
      echo "$remote is on the ignored list so ignore it."
      echo "Ignored: $remote"
    fi
    should_ignore=-1
    echo "=================================="
    echo ""
  done
}
  • For ZSH
gitpullall()
{
  repos_to_ignore="origin"
  should_ignore=-1

  echo "running: git pull"
  git pull
  echo ""

  for remote in `git remote`
    do
    echo "=================================="
    echo "Remote: $remote"
    echo "----------------------------------"
    for ignore in $repos_to_ignore
      do
      [ "$remote" = "$ignore" ] && { should_ignore=1; break; } || :
    done

    if [ "$should_ignore" = "-1" ]
      then
      THIS_BRANCH_NAME=`git rev-parse --abbrev-ref HEAD`
      { git branch -r | grep -s "^[[:space:]]\+$remote/$THIS_BRANCH_NAME$" 2>&1 > /dev/null } || { git branch -r | grep -s "^[[:space:]]\+$remote/$THIS_BRANCH_NAME""[[:space:]]\+" 2>&1 > /dev/null } || {
        echo "No info of the remote repo named '$remote' is found."
        echo "So $remote will be fetched."
        echo "running: git fetch $remote"
        git fetch "$remote"
        echo ""
      }
      if git branch -r | grep -sw "$remote/$THIS_BRANCH_NAME" 2>&1 > /dev/null ; then
        echo "running: git pull $remote $THIS_BRANCH_NAME"
        git pull "$remote" "$THIS_BRANCH_NAME"
      else
        echo "$THIS_BRANCH_NAME branch does not exist on the remote called '$remote'."
        echo "So $remote will not be pulled from $remote."
      fi
    else
      echo "$remote is on the ignored list so ignore it."
      echo "Ignored: $remote"
    fi
    should_ignore=-1
    echo "=================================="
    echo ""
  done
}

Checkout

Checkout to a branch for git repo having multiple remote repos.

 git checkout -b existing-branch origin/existing-branch 

Migration from SVN to Git

Install git-svn

$ apt-get install git-svn 

Prepare for Migration

In some dir, create users.txt having all the user info e.g.)

USERNAME = FirstName LastName <user@host>

Migrate

Without Tags

$ git svn clone --stdlayout --no-metadata -A users.txt --username=USERNAME https://svn.repo/some-parent-folder/project-folder project-tmp 

(https://svn.repo/some-parent-folder/project-folder <- Not trunk)

$ cd project-tmp 
$ git svn fetch 
  • To view the other SVN branches
$ git branch -r
$ cd ..
$ git clone project-tmp project 
$ rm -Rf project-tmp 
$ cd project 
$ git remote rm origin 
  • Before this, add git repo for the project first.
$ git remote add origin git@git-repo:project 
  • To view all remote
$ git remote -v 
$ git config branch.master.remote origin 
$ git config branch.master.merge refs/heads/master 


  • add user info to project/.git/config
[user]
  email = user@host
$ git push -u origin master 
  • Check status
$ git status 
# On branch master
nothing to commit (working directory clean)


With Tags

If there are nested tag folders like tags/released/1.x/1.1.0, tags/released/1.x/1.2.0, tags/released/1.x/1.3.0, tags/released/2.x/2.0.1 and tags/released/2.x/2.2.0, add --tags (for branches, it's --branches)

$ git svn clone --stdlayout --tags=tags/*/*/* --no-metadata -A users.txt --username=USERNAME https://svn.repo/some-parent-folder/project-folder project 
$ cd project
$ git svn fetch 
#### not necessary as it will be handled differently (by create tag and remove the svn tag) 
$ cp -Rf .git/refs/remotes/tags/* .git/refs/tags/ 
$ rm -Rf .git/refs/remotes/tags 

$ cp -Rf .git/refs/remotes/* .git/refs/heads/ 
$ rm -Rf .git/refs/remotes 
###################################################################################
$ git branch -r

Then for each tag listed do:

$ git tag tagname tags/tagname
$ git branch -r -d tags/tagname
$ git tag tag-name-for-git tags/tag-name-from-svn
$ git branch -r -d tags/tag-name-from-svn

e.g.)

$ git tag 0.1.15 tags/released/0.x/some-app-0.1.15
$ git branch -r -d tags/released/0.x/some-app-0.1.15
  • list tags
$ git tag -l
  • remove old svn tags
$ rm -Rf .git/refs/remotes/tags 
$ git remote add origin git@git-repo:project 
* add user info to project/.git/config 
$ git config -e
[user]
  email = user@host

and remove [svn-remote "svn"] and [svn]

$ git push origin --all 

It doesn't seem like the tags are pushed?

$ git push origin --tags
Retrieved from "http://kevinlee.io/wiki/Git"
Personal tools