Git快速参考
===============
常用命令一览
------------------------
 * git clone
 * git status
 * git add
 * git commit
 * git pull
 * git push
 * git mv
 * git rm
 * git log
 * git diff
Git和Svn的几个重要区别
------------------------
  和svn比起来,Git的提交代码多了2个重要步骤:远程提交和暂存区。
  远程提交。Git是一个分布式的源码管理系统,不像SVN有中心服务器的概念,每个人的版本库都可以被另一个人获取和更新当中心服务器使用.在我们目前的项目中,文件服务器上的Git库被指定为"中心服务器".
  如果你想提交新代码到文件服务器,需要2步,流程如下:提交到本地 --- 提交到文件服务器.(具体命令下文会讲到.)
  svn则只需要一步:直接提交到远程服务器.
  Git多出的一步,可供开发者保存不想公开的代码,如,暂时还不成熟的代码.
  暂存区。Git另一个强大之处是它有一块区域专门存放"修改了但还未提交到本地版本库"的代码。如:修改了3个文件,一个未完成,只能提交2个,这时可以先提交这2个文件到暂存区,再从暂存区正式到本地。
  暂存区是Git中很重要的概念,弄清了它,就入门了一半。理解远程提交则是另一半。
  这里有篇文章详细的解释了Git的文件状态,包括暂存区: http://progit.org/book/zh/ch2-2.html
Git基本操作
---------------
git clone
^^^^^^^^^^^^^^^
获取远程服务器上的代码::
   
    $ git clone git@192.168.1.223:wenku
    Cloning into doc...
    remote: Counting objects: 54, done.
    remote: Compressing objects: 100% (51/51), done.
    remote: Total 54 (delta 23), reused 0 (delta 0)
    Receiving objects: 100% (54/54), 14.04 KiB, done.
    Resolving deltas: 100% (23/23), done.
git status
^^^^^^^^^^^^^^^
查看当前工作区的状态::
    $ git status
    # On branch master
    # Changed but not updated: #文件被修改,但还未提交到版本库
    #   (use "git add <file>..." to update what will be committed)
    #   (use "git checkout -- <file>..." to discard changes in working directory)
    #
    #       modified:   index.rst
    #
    # Untracked files: #新文件,没有提交过
    #   (use "git add <file>..." to include in what will be committed)
    #
    #       source/git.rst
    no changes added to commit (use "git add" and/or "git commit -a")
Untracked files 没有跟踪过的文件,即从未提交过的文件。
Changed but not updated 修改但还没有提交
这2个文件都还未进暂存区
git add
^^^^^^^^^^^^^^^
将文件添加进暂存区::
    $ git add source/git.rst
    $ git status
    # On branch master
    # Changes to be committed:
    #   (use "git reset HEAD <file>..." to unstage)
    #
    #       new file:   source/git.rst
    #
    # Changed but not updated:
    #   (use "git add <file>..." to update what will be committed)
    #   (use "git checkout -- <file>..." to discard changes in working directory)
    #
    #       modified:   index.rst
    #
source/git.rst 已添加进暂存区,只差提交(git commit)这一步就进入本地的版本库了。
index.rst 虽然被改动,但还未进暂存区,这个文件如需同批提交到本地的库,提交前需要如法炮制添加进暂存区。如果下次提交进本地库,则这里不用加到暂存区。
这也是git强大处之一 --- 利用暂存区,灵活控制需要提交的文件。
git commit
^^^^^^^^^^^^^^^
将暂存区的文件提交进本地版本库::
    $ git commit -m '新增git快速参考'
    [master d1eed2a] 新增git快速参考
    2 files changed, 76 insertions(+), 0 deletions(-)
    create mode 100644 source/git.rst
    zhangyuan@loenv doc $ git status
    # On branch master
    nothing to commit (working directory clean)
提交以后暂存区的文件都进了本地版本库,此时暂存区是空的。
git pull
^^^^^^^^^^^^^^^
更新远程服务器上的最新代码到本地.
提交本地代码到远程服务器前,必须更新至最新版本(如果其它人更新了远程版本库,git会警告并禁止提交)::
    $ git pull git@192.168.1.223:wenku
    From 192.168.1.223:wenku
    * branch            HEAD       -> FETCH_HEAD
    Already up-to-date.
这代表本地库已是最新的版本
git push
^^^^^^^^^^^^^^^
本地代码提交到远程::
    $ git push
    Counting objects: 8, done.
    Delta compression using up to 8 threads.
    Compressing objects: 100% (5/5), done.
    Writing objects: 100% (5/5), 1.86 KiB, done.
    Total 5 (delta 2), reused 0 (delta 0)
    To git@192.168.1.223:wenku
       d4d7249..d1eed2a  master -> master
git rm
^^^^^^^^^^^^^^^
删除文件或目录::
    git rm file
    git rm dir/
git mv
^^^^^^^^^^^^^^^
重命名或者移动文件(或者目录)::
    git mv file1 file2
    git mv dir1/ dir2/
撤销修改
---------------
修改上一次提交
^^^^^^^^^^^^^^^^
::
    git commit --amend

取消已经暂存的文件
^^^^^^^^^^^^^^^^^^^
::
    git reset HEAD FILE

取消对文件的修改(未进入暂存区)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
::
    git checkout --FILE
分支操作
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
查看分支::
    git branch
创建分支::
    git branch 分支名
删除分支::
    git branch -d 分支名
切换分支(切换分支前需将当前分支下的修改commit)::
    git checkout 分支名
    git checkout -b 分支名   #新建分支并切换
 合并分支::
    git merge 分支名
有 人把 Git 的分支模型称为“必杀技特性”,而正是因为它,将 Git 从版本控制系统家族里区分出来。Git 鼓励在工作流程中频繁使用分支与合并,哪怕一天之内进行许多次都没有关系。理解分支的概念并熟练运用后,你才会意识到为什么 Git 是一个如此强大而独特的工具,并从此真正改变你的开发方式。

其它使用操作
---------------
git log
^^^^^^^^^^^^^^^
查看提交日志::
    git log
git diff
^^^^^^^^^^^^^^^
显示本次改动::
    git diff
Git高级操作
---------------
git config
^^^^^^^^^^^^^^^
Git 命令别名::
$ git config --global alias.co checkout
$ git config --global alias.br branch
$ git config --global alias.ci commit
$ git config --global alias.st status
Git中的着色::
$ git config --global color.ui true
test库git操作示范
------------------------
上面我们将了git的一系列常规指令。下面我们以test库为例,讲过整个开发过程中使用的git操作。同时希望读者能跟着实际操作一遍,以便加深印象。
首先将test库从文件服务器上clone一份到开发环境下。
$ cd ~/www                                     #一般情况下我们的web代码都放在www目录下
$ git clone git:192.168.1.223:test      #文件服务器ip为192.168.1.223,库名test
$ cd test                                         #clone完毕后,会出现test代码目录。进入该目录,后面的操作都在此目录下
$ git checkout --track origin/develop  #我们的开发都在develop分支下,因此要跟踪文件服务器上的develop分支,并切换到develop分支。可以通过git branch指令查看当前分支情况。
此时,我们的代码以全部从远程的文件服务器上拷贝下来。接下去进入日常的开发或维护工作。
假设现在你接到一个任务。任务编号为157,任务内容为完善README文件。那么我们进行如下操作:
$ cd ~/www/test                              #进入test库工作目录
$ git checkout -b iss157                   #为任务157创建一个独立的分支,并在iss157分支下开发。
$ vim README                               #根据任务内容描述,去完善README。此处我们用vim编辑器编辑README文件,当然你也可以在windows下,通过samba共享,来修改README文件。
假设一天的工作已结束,我们要将还为完成的README文件先入库一次。那么我们进行如下操作:
$ git status                                     #查阅当前状态,此时git会列出修改过的,未跟踪的,或已暂存的文件列表
# On branch iss157
# Changes not staged for commit:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#       modified:   README
#
no changes added to commit (use "git add" and/or "git commit -a")
在状态中,我们可以了解到,有一个README文件修改过,如果想要提交文件,首先使用git add <file>指令(use "git add <file>..." to update what will be committed)。
$ git add README                          #添加README文件。小技巧:如果在putty中操作,可以用鼠标双击README,然后鼠标右击,粘帖到指令中。如果有多个文件,可以用空格分隔。
$ git status                                     #再次查看状态
# On branch iss157
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#       modified:   README
#
此 时,状态显示README已进入将要提交的文件列表中(Changes to be committed)。如果想取消add,可以使用git reset HEAD <file> 指令 (use "git reset HEAD <file>..." to unstage)

$ git commit -m '测试'               #提交代码到本地库中,此处的测试为注释信息
$ git status
# On branch iss157
nothing to commit (working directory clean)
提交完成后的状态
$ git log   查看提交记录
commit 484e49709d8c2b45d3050c92560c4b3c04cf5b04
Author: zhangyuan <zhangyuan@socialvoice.cn>
Date:   Thu Feb 23 11:16:35 2012 +0800
    测试
commit c287961e015d07a149e82a7b8e44811649b5125f
Author: zhangyuan <zhangyuan@socialvoice.cn>
Date:   Wed Feb 22 18:15:41 2012 +0800
    t
经过多次的修改和commit后,README进入稳定状态。此时我们可以将iss157分支合并到develop中。
$ git checkout develop                       #切换到develop分支。可以通过git branch来查看当前分支信息
$ git merge iss157                            #合并分支。此时README文件以合并到develop分支下。
$ git branch -d iss157                       #iss157分支以无用了,我们将其删除。
$ git branch                                     #查看分支情况,你可以发现iss157分支不在了。
$ git push                                       #将当前develop同步到远程分支。
整个README维护完毕。
 
通 过上文,我想你已对git在日常代码维护中的操作步骤有一个初步的了解。但有时操作并不会那么顺利。特别在多人协作开发过程中,经常会碰到多人同时修改同 一文件的情况,大部分情况,git能自动将多人修改的结果进行合并。但有时还是需要手动进行。我们在此将模拟此类环境,讲解git手动合并的过程。
 首先我们来创建模拟环境。为了演示简单,我们此处不使用分支来开发,直接在develop上进行修改,(注:日常开发中,我们还是需要使用分支,以使版本维护更清晰)。
$ cd ~/www/test
$ git checkout develop 
$ echo 'aaa' > demo1.html                       #创建文件demo1.html,并将aaa字符写入demo1.html
$ git add demo1.html                               
$ git commit -m 'demo1 aaa'                           
$ git push
此时我们创建并入库了一个demo1.html文件。demo1.html在远程库和本地库内容都为aaa
我们现模拟第二个用户来clone此文件
$ mkdir ~/www/programmer2                          
$ cd ~/www/programmer2                           #创建一个新目录,作为程序员2的工作目录
$ git clone git@192.168.1.223:test              #clone test库                       
$ cd ~/www/programmer2/test                            
$ git checkout --track origin/develop                            
$ echo 'aba' > demo1.html                          #将demo1.html文件的'aaa'字符改为'aba'                       
$ git add demo1.html    
$ git commit -m 'demo1 aba'                           
$ git push                                                  #将文件提交,并同步的文件服务器上。
此时demo1.html文件在远程库中的内容为  aba
我们在切换到~/www/test。
现在在~/www/test/demo1.html 的内容为aaa, 而远程库(文件服务器)上由于programmer2将其内容已改为aba。
$ cd ~/www/test                                   
$ echo 'aca' > demo1.html                            #我们将文件内容改成了aca         
$ git add demo1.html    
$ git commit -m 'demo1 aca'   
$ git push 
To git@192.168.1.223:test
 ! [rejected]        develop -> develop (non-fast-forward)
error: failed to push some refs to 'git@192.168.1.223:test'
To prevent you from losing history, non-fast-forward updates were rejected
Merge the remote changes (e.g. 'git pull') before pushing again.  See the
'Note about fast-forwards' section of 'git push --help' for details.    
push失败,原因在于,远程库的文件被programmer2修改了,版本较新,本地库的版本较旧。
从提示信息中我们得知,我们需要先git pull,将远程库上的文件同步下来,进行一个合并。
$ git pull
remote: Counting objects: 5, done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
From 192.168.1.223:test
   bf0c63a..c4295b9  develop    -> origin/develop
Auto-merging demo1.html
CONFLICT (content): Merge conflict in demo1.html
Automatic merge failed; fix conflicts and then commit the result.
从pull后的信息中,我们可以得知,git先试图合并demo1.html的两次修改,大部分情况下git能自动合并。在我们的示范中,由于改的是同一块内容,所以合并失败,此时我们得手动合并文件
$ vim demo1.html
demo1.html的内容如下:
 <<<<<<< HEAD
aca
=======
aba
>>>>>>> c4295b91e65092349f368ef83b6528d7a1d118e8
 在git无法自动完成合并时,和svn一样,在文件中会同时列出,两个版本的内容。如demo1.html中aca为最新版本,aba为老版本。
我们修改文件,留正确的结果。此处我们想修改为aca,因此修改内容为aca。操作如下:
删除  <<<<<<< HEAD
保留 aca
删除 =======
删除 aba
删除 >>>>>>> c4295b91e65092349f368ef83b6528d7a1d118e8
修改文件后,需要将正确的文件,再此提交。操作如下:
$ git status
# On branch develop
# Your branch and 'origin/develop' have diverged,
# and have 1 and 1 different commit(s) each, respectively.
#
# Unmerged paths:
#   (use "git add/rm <file>..." as appropriate to mark resolution)
#
#       both modified:      demo1.html
#
no changes added to commit (use "git add" and/or "git commit -a")
                           
$ git add demo1.html                           
$ git commit -m 'merge demo1'                           
$ git push                 
                 
完毕