Sunday, January 20, 2008

Using git for newbie

I am doing some experiment with Rubinius code base, which uses distributed SCM git. Comparing with Subversion, Git is a little bit more difficult, so It takes me a while to understand its commands and find the proper way to work with it. Without commit right, here are typical commands I use in my work flow.

Clone a remote repository into the local disk - repository is create as a clone of the remote repository identified by a given url
git clone git://git.rubini.us/code 
cd code # goto directory containing local repository 
git config user.name "le huy"  #configure your name 
git config user.email "lehuy20@gmail.com" #and email that will be used when committing
Sample .git/config - git create a .git directory to store it's data, file .git/config contains configuration data including remote repository, which remote branch is link with which local one
cat .git/config 
[core]
        repositoryformatversion = 0
        filemode = true
        bare = false
        logallrefupdates = true
[remote "origin"]
        url = git://github.com/evanphx/rubinius.git
        fetch = +refs/heads/*:refs/remotes/origin/*
[gui]
        geometry = 885x450+5+65 399 192
[branch "master"]
        remote = origin
        merge = refs/heads/master
[branch "cpp"]
        remote = origin
        merge = refs/heads/cpp
Submodule - git submodule is used to link to external repository (equivalent to subversion extern)
cat .gitmodules
[submodule "mspec"]
        path = mspec
        url = git://github.com/brixen/mspec.git
[submodule "spec/frozen"]
        path = spec/frozen
        url = git://github.com/brixen/rubyspec.git
init and update submodule from external repository
git submodule init # init submodule repository based on .gitmodules
git submodule update
Show current branch - show which branch we are working on, switch between them
git branch # will show us local branches 
* cpp
  master

git branch -a # will show us all branches including local and remote 
* cpp
  master
  origin/cmakebuild
  origin/cpp
  origin/fusion-experiment
  origin/instance_eval
  origin/master
  origin/wilson64

git checkout master # switch to branch named 'master'
git checkout dev # switch to branch named 'dev'
We can sometime create new branches in addition to 'master' to work concurrently on many features
Create new branch
git branch working  # create branch 'working' 
git checkout working # switch to branch named 'working'
git branch dev --track origin/dev # create local branch 'dev' that track the remote branch origin/dev
The current working branch is now 'working'

Delete branch - we can remove unused branch if it is no longer needed
git branch -d working # delete 'working' branch
Update from remote repository - e.g. wait until someone commit the patch to the main trunk then update changes from git://git.rubini.us/code
git pull will perform merge change on remote branch to local branch
git pull
git rebase will save local changes away, update from remote branch, and then reapply them
git rebase master 
Push change to remote repository
git push # push will update a --track branch in remote repository,
Edit source code - do some source code editing
gedit kernel/core/kernel.rb
Show modification - show what I have modify without committing change
git diff HEAD # HEAD is symbol for latest/head version of current branch
Revert back -when recognizing that there is something wrong, we can undo the modification, all changes are lost
git reset --hard HEAD
Edit source code - Do some source code editing again
gedit kernel/core/module.rb
Commit changes - commit change in current branch
git commit -a -s # vi editor will be popped up for entering committing message
Create patch - create patch containing changes between 2 branches local 'master' and remote 'origin/master'
git checkout master # switch to branch 'master'
git format-patch origin/master --stdout # to standard output
git format-patch origin/master -o ../patch # create formated patch as a file and store in directory ../patch
Create a ticket - goto in http://rubinius.lighthouseapp.com/m create a ticket and attach a patch file to it

Revert change - switch to branch named 'master' and revert back directory tree in original state, all changes are lost
git checkout master
git reset --hard HEAD 
Resolve conflict - if there is a conflict on file, git stop doing and ask for manually resolve, to inform that the conflict has been resolved on e.g. kernel/core/module.rb, use
gedit kernel/core/module.rb # after manually resolve conflict
git add kernel/core/module.rb # inform git that conflict on file kernel/core/module.rb is resolved
Apply patch
git-apply ../patch/0001-Fixes-for-Class-include-and-Class-extend-to-handle-c.patch
Apply patch partly - if a patch contains modification of multi files and there are conflict with some of them during patching, then use --reject option to modify only non-conflicted files and create filename.rej for those conflicted
git-apply --reject ../patch/0001-Fixes-for-Class-include-and-Class-extend-to-handle-c.patch 
Remove file from stage Making change into git repository actually happens in two steps, 1) add the new/modified file into stage area 2) commit stage area. If for some reason, we want to revert the step 1) we can do as follow
$git status
# On branch master
#
# Initial commit
#
# Changes to be committed:
#   (use "git rm --cached ..." to unstage)
#
# new file: dict_parser/tests/dict_tests.py
#
$ git rm --cached dict_parser/tests/dict_tests.py
rm 'dict_parser/tests/dict_tests.py'
$ git status
# On branch master
#
# Initial commit
#
# Untracked files:
#   (use "git add ..." to include in what will be committed)
#
# dict_parser/
Git getting popular now when many open source projects moved from subversion to git, Zack Rusin has done very nice git cheat sheet summarizing git usage scenarios below