VCS 3 Git 1 note 2

How to use Git

http://nozer0.github.io/en/technology/program/how-to-use-git/
2014-03-04 by nozer0

Here are some notes I’d like to share about using Git, let’s start.

Take the index as cheat sheet:


Install

The most simple way to install Git is to download the installer file directly from official site.

Or we can also choose to use some package manage tools that help us to do this job, such as ‘apt-get’ on Debian, or ‘port’ and ‘brew’ on OSX. Ensure that we already install the depending packages (curl, zlib, openssl, expat, and libiconv) before running.

	# for ubuntu
	apt-get install git
	# for mac
	brew install git

For the users who already installed Git before, try this to get latest one.

	git clone git://git.kernel.org/pub/scm/git/git.git

Setup

Set some configurations before we start to use Git.

	# replace 'nozer0' and 'c.nozer0@gmail.com' to what you use
	git config --global user.name nozer0
	git config --global user.email c.nozer0@gmail.com

Or change all configurations using editor at one time.

	git config --global -e

s## Launch

Usually we need to get codes from existing remote repository first. This operation is mostly like the checkout operation of other VCS such as CVS or Subversion.

	# copying files from repository
	git clone https://github.com/nozer0/one-piece.git
	git clone /usr/local/codes/one-piece ./op

	# use `-b` option if want to use branch instead of HEAD
	git clone -b branch1 https://github.com/nozer0/one-piece.git

Or, we can also choose to initialize a local directory as working area first, then set remote URL to be connected.

	# create '.git' directory contains all repository info needed.
	git init
	git remote add origin https://github.com/nozer0/one-piece.git

Working Cycle

Since Git is DVCS(Distributed control version system), it has a local repository and one more place to store info of files which are tracked but uncommitted, called ‘index’ or ‘staged’ area, correspondingly, the new created or modified files out of version control are named as ‘unstaged’ files.

	+---------+   add   +-------+  commit  +------------+  push   +------------+
	| Working | ------> | Index | -------> |   Local    | ------> |   Remote   |
	|  Area   | <------ | Area  |          | Repository | <------ | Repository |
	+---------+  reset  +-------+          +------------+  fetch  +------------+
	     ^                  ^      checkout       |
	     +------------------+---------------------+

Update

First, we need ‘update’ latest data from remote repository to working area.

Use git fetch command to synchronize the data from remote repository to local repository.

	git fetch
	# fetch 'origin/master~2' commit from remote to local 'foo' branch
	git fetch origin master~2:foo

After that, we can choose to merge or rebase the remote changes or checkout directly. For more details, please check the next Branch section.

	# do merge work if some local changes
	git merge origin/master
	# or apply the remote data to working area
	git checkout origin/master

Git also has a shorthand command git pull to update the data from remote repository to working tree in one go.

	# shorthand one for `git fetch` and `git merge FETCH_HEAD`
	git pull
	# or choose 'rebase' instead of 'merge'
	git pull --rebase
	# also support the complex format like `fetch`
	git pull origin foo:bar

Change

OK, now we start to do some changes in working area to index by using git add or other commands.

	# try `git status` before and after this command to see the difference
	# edit
	git status
	git add test.txt
	git status

	# affect index only
	git rm old.txt --cached
	# remove from index and working tree both
	git rm unused.txt

	# rename files
	git mv old.c new.c
	# equivalent one
	mv old.c new.c
	git rm old.c
	git add new.c

Revert

Sometimes, we may mistake to commit something, or we want to revert the recent changes, here are some commands satisfy the purpose.

	# revert change on index only, and keep in working directory
	git reset text.txt
	# reset to 2 commits before
	git reset HEAD~2
	# equivalent one if in 'master' branch
	git branch -f master HEAD~2

	# or revert change on both index and working area
	git reset --hard

Commit

Since there are more areas in Git than other VCS, we need to one more step to do this job instead of single ‘commit’ operation.

After all files are staged, we can commit the changes to our local repository, yes, use git commit like normal.

	git commit -m 'first commit'
	# if the files are staged before, we can merge `add` and `commit` steps into one go
	git commit -am 'commit again' test.txt

Especially, if we want to change last commit, this is always happened when finding some changes forgot to give or files forgot to add after commit.

	git commit --amend

One thing to be noticed here, if the file has tracked once, it will commit with the newest one in working area instead of staged area, for example.

	echo 1 > test
	git add test
	echo 2 >> test
	# the actual commit file content is '1 2'
	git commit test -m 'test file'

And after some commits done in local repository, that we want to apply these changes to remote, we use git push.

	git push
	# push 'HEAD~2' commit to the remote 'origin/foo' branch
	git push origin HEAD~2:foo

Branch

Branch is just reference points to commit in Git, so it is used more frequently than others, as always said, ‘branch early, and branch often’. We create new branches for developing new feature, bug fixing, or some other things.

	# show all branches
	git branch -a
	# create a branch named 'new' on HEAD~2
	git branch new HEAD~2
	# rename branch
	git branch -m new branch1
	# move branch1 pointing to commit 'afe9...'
	git branch -f branch1 afe9
	# delete unused branch
	git branch -d branch1

Since we always have some branches in practice, we need to switch between them via git checkout command.

	# switch to the branch and set HEAD and index based on that branch
	git checkout master
	# create a new branch and switch to it in one go
	git checkout -b branch2 HEAD~2
	# or merge move and checkout branch operations into one
	git checkout -B master branch2

Here are several ways to rearrange the branches if necessary, merge, rebase or cherry-pick, let’s see some examples to compare the difference.

	# C0--C1 <- master*
	#  \
	#  C2--C3 <- develop

	git merge develop
	# C0--C1--C4 <- master*
	#  \     /
	#  C2--C3 <- develop
	# if conflicts happen between merging, we can choose to reset status before merge
	git merge --abort

	git rebase master develop
	#      C2'--C3' <- develop*
	#      /
	# C0--C1 <- master
	#  \
	#  C2--C3
	# we can also implement this on interactive mode with `-i` option

	git cherry-pick C2 C3
	# C0--C1--C2'--C3' <- master*
	#  \
	#  C2--C3 <- develop

Tag

Tag is another type of reference to commit, usually used for milestone mark or something else, which is very similar as ‘Branch’.

	# show all tags
	git tag -a
	# create a tag named 'v1.0' on HEAD~2
	git tag v1.0 HEAD~2
	# tag again if references to wrong commit
	git tag -f v1.0 HEAD
	# delete tag
	git tag -d v1.0-test

Auxiliary Commands

We already use the git status before, and git log can give us more detail history info.

	git log
	# show detail diff info
	git log -p
	# show statistics diff info
	git log --stat
	# show latest <n> log
	git log -2
	# show log based on conditions
	git log --since=<time>

And we can also get the difference between 2 states by running git diff.

	# compare between index and working area
	git diff
	# compare between commit and index
	git diff --cached
	# compare between HEAD and working area
	git diff HEAD

git show and git blame can let us check more detail info if need.

	git show 4c18
	# more interesting, this can show commit change info for each line
	git blame readme.txt
	# output:
	# 	^4c18e3c (nozer0 2014-03-05 14:17:44 +0800  1) hi
	# 	^4c18e3c (nozer0 2014-03-05 14:17:44 +0800  1) I'm nozer0

Additional info

‘Commit’, is the most important concept in Git, and there are many places we need to specify the detail commit id to be used in commands, Git provides several choices for us. Assume we have such commit tree.

	   v1 (tag)
	      |
	+-----------+     +-----------+
	| a9e82f... | --- | 39e768... | <- master* (branch)
	+-----------+     +-----------+
	               /
	+-----------+
	| 36bc54... |
	+-----------+

Git also have the same predefined keyword ‘HEAD’, different than other VCS like SVN, it indicates the current relevant commit working with. For the situation above, ‘HEAD’ is equivalent to ‘master’. So, If we want to assign with ‘a9e82f…’ commit, we can use one of ‘a9e8’, ‘v1’, ‘master~’ and ‘HEAD^’, and ‘36bc..’, ‘master^2’ for ‘36bc54…’ commit. And the difference between ‘~’ and ‘^’ is, ‘~’ shows the step goes backwards, and ‘^’ shows which path goes, we can also combine these two, like, ‘HEAD^2~3’.


Compare Table

As extra candy for the one who used SVN before, this is the compare table, which lists similar commands between SVN and Git.

SVN Git
svn checkout git clone
   
svn update git pull, git fetch + git checkout
svn add git add
svn move git move
svn revert git reset --hard
svn commit git commit + git push
   
svn status git status
svn diff git diff
svn log git log
   
svn copy <src> <branch path> git branch <branch name> <commit>
svn rm <branch path> git branch -d <branch name>
svn merge <path> git merge <name>
svn merge -r<rev> <path> git cherry-pick <commit>

Here is very interesting study site with interactive visualize graphic case.

VCS 3 Git 1 note 2