git 搭建多人开发环境

 若是你是一名谨慎的项目开发者,就一定会用到版本管理软件,现有市场上成熟的版本管理软件有cvs,svn,clearcase和更加灵活敏捷的git。相比传统的重量级中央版本管理软件svn,clearcase等,git更像是一个轻盈的分布式版本管理系统,它不须要架设中央服务器来时时同步版本信息,我的只须要在本身的电脑上建立一个git版本管理库,就拥有一个完整项目开发历史的分支集合,最大程度支持自由软件开发者的离线工做,同时git又提供了方便的同步接口协同其余人的并行开发。
    在实际的项目开发过程当中,咱们采用了双系统来进行版本管理,svn管理项目正式版本和预发布版本,而git管理子功能模块开发和我的在服务器和客户端之间的协同开发,在保证功能稳定以后再将git上的内容merge到svn主干上。
    这篇文章将主要结合真实的使用环境介绍git的各类基本操做,多人如何协同开发客户端和服务端大功能模块,避免频繁提交svn来进行版本管理。html

  • git基本操做

git安装 
    debian系统能够用apt-get install git直接安装 
    windows系统中能够到http://code.google.com/p/msysgit/下载git for windows的版本,经过git的命令行进行操做,不建议使用tortoisegit等图形化工具。 


git 配置 
    使用git的第一件事就是设置你的名字和email,这些就是你在提交commit时的签名。 
$ git config --global user.name "usrname" 
$ git config --global user.email " usrname@gmail.com" 


     执行了上面的命令后,会在你的主目录(home directory)创建一个叫 ~/.gitconfig 的文件。 内容如如下格式: 
[user] 
name = usrname 
email = usrname @gmail.com 

    这些属性都是全局设置,会影响该用户创建的每一个项目。若是你想使项目里的属性与前面的全局设置有区别(例如把私人邮箱地址改成工做邮箱);你能够在项目中使用git config 命令不带 --global 选项来设置. 这会在你项目目录下的 .git/config 文件增长一节[user]内容(如上所示)。 

    设置中文字符集为gbk 
$ git config --global i18n.commitencoding gbk linux

$ git config --global i18n.logoutputencoding gbk git

初始化一个新的仓库
    首先建立一个目录,在这个目录下进行版本管理
$ mkdir hellogit

    在这个目录下建立空git版本库
$git init

    执行完这个命令以后,会在这个目录下面产生一个.git目录,全部的版本信息会保存在这个目录之下。.git目录下的文件显示以下:github


其中branches目录下面保存着全部已经创建的branch信息;
config描述了这个版本库的全部属性配置;
HEAD描述了当前的版本库指向了哪一个branch;
info中包含exclude文件描述了整个版本库中有哪些文件不须要版本控制;
refs中保存了开发过程当中所打的tag和各个tag相关联的版本信息;
objects中保存了各个版本信息,而且以各个版本的hash值为文件名。web

配置例外(ignore)
    项目中常常会生成一些Git系统不须要追踪(track)的文件。典型的是在编译生成过程当中产生的文件或是编程器生成的临时备份文件。固然,你不追踪这些文件,能够平时不用"git add"去把它们加到索引中。可是这样会很快变成一件烦人的事,你发现项目中处处有未追踪(untracked)的文件; 这样也使"git add ." 和"git commit -a" 变得实际上没有用处,同时"git status"命令的输出也会有它们的信息。
    你能够在你的顶层工做目录中添加一个叫".gitignore"的文件,来告诉Git系统要忽略掉哪些文件,下面是文件内容的示例:
# 以'#' 开始的行,被视为注释.
# 忽略掉全部文件名是 foo.txt 的文件.
foo.txt
# 忽略全部生成的 html 文件,
*.html
# foo.html是手工维护的,因此例外.
!foo.html
#  忽略全部.o 和 .a文件.
*.[oa]
# 忽略svn目录
.svn/编程

    你能够把".gitignore" 这个文件放到工做树(working tree)里的其它目录中,这就会在它和它的子目录起忽略(ignore) 指定文件的做用。. gitignore文件一样能够像其它文件同样加到项目仓库里( 直接用git add .gitignore和git commit等命令), 这样项目里的其它开发者也能共享同一套忽略文件规则。
    若是你想忽略规则只对特定的仓库起做用,你能够把这些忽略规则写到你的仓库下 .git/info/exclude文件中,或是写在Git配置变量core.excludesfile中指定的文件里。有些Git命令也可在命令行参数中指定忽略规则,你能够在这里:gitignore查看详细的用法。windows

Clone一个仓库
    咱们也能够在已知一个项目版本库的地址(Git URL)状况下,拷贝这个版本库到本地开发环境。Git可以支持不少连接协议的拷贝, Git URL能够是ssh://, http(s)://, git://,或是包含有.git目录的本地路径。在一个新的目录下须要拷贝刚才版本库,可使用:
$git clone ~/hellogit

    对于远程的版本库,能够经过不仅一种协议来访问,例如,Git自己的源代码你既能够用 git:// 协议来访问:
$ git clone git://git.kernel.org/pub/scm/git/git.git

也能够经过http 协议来访问:
$ git clone http://www.kernel.org/pub/scm/git/git.git

git://协议较为快速和有效,可是有时必须使用http协议或者ssh协议,以ssh协议为例,你首先须要在当前用户目录下的.ssh文件夹中放有连接对方ssh服务器的公钥和私钥,而后经过命令
$ git clone ssh://usrname@IP:PORT/home/szh/hellogit

在实际应用中,为了方即可以添加如下命令
$ git remote add alias_hellogit_path ssh://name@ip:port/<gitpath>bash

上面的命令就会增长ssh地址别名为alias_hellogit_path的远程服务器,之后提交代码的时候只须要使用 alias_hellogit_path别名便可,此时在.git/config会有以下内容出现:
[remote " alias_hellogit_path"]
    ssh://name@ip:port/<gitpath>
    fetch = +refs/heads/*:refs/remotes/ alias_hellogit_path /*

通过alias设置,能够直接经过一下命令进行clone
$ git clone alias_hellogit_path服务器

追踪更新
    创建了版本仓库以后,就能够跟踪这个仓库下面的全部改动。首先建立三个文件file1, file2, file3,对其中文件进行修改。markdown

若是须要将更新的内容添进行跟踪,使用以下指令将它们更新的内容添加到索引中.
$ git add file1 file2 file3

你如今为commit作好了准备,你可使用git diff命令再加上 --cached 参数 ,看看哪些文件将被提交(commit)。
$ git diff --cached

(若是没有--cached参数,git diff 会显示当前你全部已作的但没有加入到索引里的修改.) 你也能够用git status命令来得到当前项目的一个情况:
$ git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#   modified:   file1
#   modified:   file2
#   modified:   file3

若是你要作进一步的修改, 那就继续作, 作完后就把新修改的文件经过add加入到索引中. 最后把他们提交:
$ git commit

这会提示你输入本次修改的注释,完成后就会记录一个新的项目版本.
除了用git add命令,我还能够用
$ git commit -a

    这会自动把全部内容被修改的文件(不包括新建立的文件)都添加到索引中,而且同时把它们提交。
hint: commit注释最好以#issue_id为开头,再附上这个issue的简要描述;而后空一行再把详细的注释写清楚。这样就能够很方便的用工具来检索工程中每一个开发单的进度.

Git跟踪的是内容不是文件
    不少版本控制系统都提供了一个 "add" 命令:告诉系统开始去跟踪某一个文件的改动。可是Git里的 ”add” 命令从某种程度上讲更为简单和强大. git add不可是用来添加不在版本控制中的新文件,也用于添加已在版本控制中可是刚修改过的文件; 在这两种状况下, Git都会得到当前文件的快照而且把内容暂存(stage)到索引中,为下一次commit作好准备。

撒销操做

    在项目的开发过程当中,误操做或者需求临时变动都会要求撤销以前的提交,Git提供了多种方法来实现这些操做。git对add和commit的撤销也是不相同的。

1)对未跟踪内容的撤销 
    若是你修改了test1这个文件,但未将这些改动跟踪到了版本库(未进行add操做),这时只须要输入如下命令,就能够回退: 
$git checkout test1 

若是须要回退全部文件,能够直接输入: 
$git checkout . 

2)add 撤销 
    若是你修改了test1这个文件,而且将这些改动跟踪到了版本库(只进行了add操做,没有进行commit操做),最简单的操做就是先 删除这个文件,而后输入如下命令: 
$ git checkout test1 

若是跟踪的文件比较多,删除文件比较繁琐,则能够选择如下命令 
$ git reset --hard HEAD 
这里的HEAD指该分支中最新的一次commit内容,该命令将工程里的全部文件恢复到版本库里最近一次提交的内容。 

 

3) commit的撤销 
    若是想撤销已经commit的内容,可使用的git的revert或者reset命令。假设版本库里面有3次版本提交,分别为c1, c2, c3;有了一次版本跟踪,为a1;还有一些本地未跟踪的修改,为m1,版本库信息以下图所示。

下面将讲述如何使用reset和revert来撤销c2的提交.

revert撤销
    revert命令不容许用户的本地版本库中有任何未提交的修改,因此若是强行输入revert命令,git将提示以下信息:
error: Your local changes would be overwritten by revert.
hint: Commit your changes or stash them to proceed.
fatal: revert failed

因此在revert以前,你必须将a1和m1进行提交或者撤销。假使咱们撤销了a1和m1以后使用命令:
$ git revert HEAD~2

其中命令最后的数字表示在版本仓库中向后回退版本的个数。你也能够指定具体的版本id进行回退,如
$git revert $COMMIT _CODE

此时git会将撤销的c2内容与最新c3内容进行merge,并产生版本号c4', 此时版本库信息以下图所示:

 

reset撤销
$ git reset --soft HEAD~2
这时在版本库中已经没有了c2,c3两次提交的内容,可是原文件test1的内容与撤销以前的内容一致,a1的跟踪信息和m1的修改内容还都存在,reset以后的版本信息如图所示:



$git reset --mixed HEAD~2
这也是reset命令在没有参数状况下的默认操做,这个命令以后版本库中已经没有了c2,c3两次提交的内容,a1的跟踪信息也被撤销,可是原文件test1的内容与撤销以前的内容一致,a1和m1的修改内容还都存在,reset以后的版本信息如图所示:


$git reset --hard HEAD~2
这个命令以后版本库中已经没有了c2,c3两次提交的内容,原文件test1的内容也变回了与c1版本一致,a1的跟踪信息和m1的修改内容都丢失,reset以后的版本信息如图所示:

 

比较(diff)
你能够用git diff来比较项目中任意两个版本的差别。
$ git diff master..test
上面这条命令只显示两个分支间的差别,若是你想找出‘master’,‘test’的共有父分支和'test'分支之间的差别,你用3个‘.'来取代前面的两个'.' 。
$ git diff master...test

git diff是一个难以置信的有用的工具,能够找出你项目上任意两点间的改动,或是用来查看别人提交进来的新分支。

哪些内容会被提交(commit)
你一般用git diff来找你当前工做目录中上次提交与本地索引间的差别。
$ git diff
上面的命令会显示在当前的工做目录里没有被追踪,且在下次提交时不会被提交的修改。

$ git diff HEAD~$NUM HEAD~$NUM
上面的命令会显示在当前的工做目录中两个版本号之间的差异。

若是你要看在下次提交时要提交的内容),你能够运行:
$ git diff --cached
上面的命令会显示你当前的索引和上次提交间的差别;这些内容在不带"-a"参数运行 "git commit"命令时就会被提交。

$ git diff HEAD
上面这条命令会显示你工做目录与上次提交时之间的全部差异,这条命令所显示的内容都会在执行"git commit -a"命令时被提交。

$ git diff HEAD -- ./lib 
上面这条命令会显示你当前工做目录下的lib目录与上次提交之间的差异(或者更准确的说是在当前分支)。

若是不是查看每一个文件的详细差异,而是统计一下有哪些文件被改动,有多少行被改动,就可使用‘--stat' 参数。
$>git diff --stat
 layout/book_index_template.html                    |    8 ++-
 text/05_Installing_Git/0_Source.markdown           |   14 ++++++
 text/05_Installing_Git/1_Linux.markdown            |   17 +++++++
 text/05_Installing_Git/2_Mac_104.markdown          |   11 +++++
 text/05_Installing_Git/3_Mac_105.markdown          |    8 ++++
 text/05_Installing_Git/4_Windows.markdown          |    7 +++
 .../1_Getting_a_Git_Repo.markdown                  |    7 +++-
 .../0_ Comparing_Commits_Git_Diff.markdown         |   45 +++++++++++++++++++-
 .../0_ Hosting_Git_gitweb_repoorcz_github.markdown |    4 +-
 9 files changed, 115 insertions(+), 6 deletions(-)
有时这样全局性的查看哪些文件被修改,能让你更轻轻一点。

 

  • 分支和合并

建立分支 
一个Git仓库能够维护不少开发分支。如今咱们来建立一个新的叫”experimental”的分支: 
$ git branch experimental 

此时你运行下面这条命令: 
$ git branch 
你会获得当前仓库中存在的全部分支列表: 
  experimental 

* master

“experimental” 分支是你刚才建立的,“master”分支是Git系统默认建立的主分支。星号(“*”)标识了你当工做在哪一个分支下,


切换分支
$ git checkout experimental
切换到”experimental”分支,先编辑里面的一个文件,再提交(commit)改动,最后切换回 “master”分支。

$ git commit -a
$ git checkout master
你如今能够看一下你原来在“experimental”分支下所做的修改还在不在;由于你如今切换回了“master”分支,因此原来那些修改就不存在了。

你如今能够在“master”分支下再做一些不一样的修改:
$ git commit -a
这时,两个分支就有了各自不一样的修改(diverged);咱们能够经过下面的命令来合并“experimental”和“master”两个分支:
$ git merge -no-ff experimental

默认状况下,Git执行”快进式合并”(fast-farward merge),会直接将Master分支指向Develop分支。
使用–no–ff参数后,会执行正常合并,在Master分支上生成一个新节点。为了保证版本log的清晰,推荐采用这种作法。

 

    若是这个两个分支间的修改没有冲突(conflict), 那么合并就完成了。若有有冲突,会有以下相似的信息显示

100% (4/4) done
Auto-merged file.txt
CONFLICT (content): Merge conflict in file.txt
Automatic merge failed; fix conflicts and then commit the result.

输入下面的命令就能够查看当前有哪些文件产生了冲突:
$ git diff

    若是执行自动合并无成功的话,git会在索引和工做树里设置一个特殊的状态,提示你如何解决合并中出现的冲突。
    有冲突(conflicts)的文件会保存在索引中,除非你解决了问题了而且更新了索引,不然执行git commit都会失败:
$ git commit
file.txt: needs merge

若是执行git status会显示这些文件没有合并(unmerged),这些有冲突的文件里面会添加像下面的冲突标识符:
<<<<<<< HEAD:file.txt
Hello world
=======
Goodbye
>>>>>>> 77976da35a11db4580b80ae27e8d65caf5208086:file.txt

你所须要的作是就是编辑解决冲突,(接着把冲突标识符删掉),再执行下面的命令:
$ git add file.txt
$ git commit
注意:提交注释里已经有一些关于合并的信息了,一般是用这些默认信息,可是你能够添加一些你想要的注释。

这时你就能够删除掉你的 “experimental” 分支了(若是愿意):
$ git branch -d experimental

git branch -d只能删除那些已经被当前分支合并的分支. 若是你要强制删除某个分支的话就用git branch –D;下面假设你要强制删除一个叫”crazy-idea”的分支:
$ git branch -D crazy-idea

 

 

  • 分布式协同开发

    假设Alice如今开始了一个新项目,在/home/alice/project建了一个新的git 仓库(repository);另外一个叫Bob的工做目录也在同一台机器,他要提交代码。 
Bob 执行了这样的命令: 
$ git clone /home/alice/project myrepo

    这就建了一个新的叫"myrepo"的目录,这个目录里包含了一份Alice的仓库的克隆(clone). 这份克隆和原始的项目如出一辙,而且拥有原始项目的历史记录。
Bob 作了一些修改而且提交(commit)它们:
$ git commit -a
(repeat as necessary)

当他准备好了,他告诉Alice从仓库/home/bob/myrepo中把他的修改给拉 (pull)下来。她执行了下面几条命令:
$ cd /home/alice/project
$ git pull /home/bob/myrepo master

    这就把Bob的主(master)分支合并到了Alice的当前分支里了。若是Alice在 Bob修改文件内容的同时也作了修改的话,她可能须要手工去修复冲突. (注意:"master"参数在上面的命令中并不必定是必须的,由于这是一个默认参数)
    git pull命令执行两个操做: 它从远程分支(remote branch)抓取修改的内容,而后把它合并进当前的分支。
若是你要常常操做远程分支(remote branch),你能够定义它们的缩写:
$ git remote add bob /home/bob/myrepo

    这样,Alic能够用"git fetch"" 来执行"git pull"前半部分的工做,可是这条命令并不会把抓下来的修改合并到当前分支里。
$ git fetch bob

咱们用git remote命令创建了Bob的运程仓库的缩写,用这个(缩写) 名字我从Bob那获得全部远程分支的历史记录。在这里远程分支的名字就叫bob/master.
$ git log -p master..bob/master

上面的命令把Bob从Alice的主分支(master)中签出后所作的修改所有显示出来。
当检查完修改后,Alice就能够把修改合并到她的主分支中。
$ git merge bob/master

这种合并(merge)也能够用pull来完成,就像下面的命令同样:
$ git pull . remotes/bob/master

注意:git pull 会把远程分支合并进当前的分支里,而无论你在命令行里指定什么。
其后,Bob能够更新它的本地仓库--把Alice作的修改拉过来(pull):
$ git pull

若是Bob从Alice的仓库克隆(clone),那么他就不须要指定Alice仓库的地址;由于Git把Alice仓库的地址存储到Bob的仓库配库文件,这个地址就是在git pull时使用:
$ git config --get remote.origin.url
/home/alice/project

(若是要查看git clone建立的全部配置参数,可使用"git config -l", git config的帮助文件里解释了每一个参数的含义.)

Git同时也保存了一份最初(pristine)的Alice主分支(master),在 "origin/master"下面。
$ git branch -r
  origin/master

若是Bob打算在另一台主机上工做,他能够经过ssh协议来执行"clone" 和"pull"操做:
$ git clone alice.org:/home/alice/project myrepo

 

  • 集中式Git仓库

 

    一旦涉及多人在不一样的工做目录下协同开发时,须要创建一个中央版本仓库,由于分布式的版本仓库一旦checkout以后,就不能被其余开发者主动执行push操做。下图就是一个多系统下多人合做协同开发的工做目录示意图,user1和user2须要一块儿合做开发一个功能,为了不在开发完成以前就提交svn主版本库,能够运用git来进行小规模的代码同步,同时user1和user2又须要在linux和windows下面进行开发,也须要时时同步代码。为了完成这个需求,能够在windows和linux下各创建一个git工做目录,而后在任一台机子创建一个git中央版本仓库,将全部工做目录指向这个中央版本仓库。具体步骤以下: 

 
    你仍是天天在你的本地私人仓库里工做,可是会按期的把本地的修改push到你的公开仓库中;其它开发者就能够从这个公开仓库pull最新的代码。 
    在linux系统中建立中央版本仓库(repository),在一个空的文件夹下,输入命令: 
$git --bare init 
--bare 在这里是产生一个干净的中央版本仓库,仅包含版本历史记录不包含工做代码和工做目录,相似于svn服务器,这样的版本仓库能够被开发者主动push修改。 

建立私有版本仓库,在我的文件夹下,输入命令: 
$ git init 
这样就为咱们须要版本管理的代码建立了一个私有的分布式版本仓库,这个版本仓库跟咱们平时在用版本仓库没有两样,你能够经过命令add,commit和reset进行版本管理。 

在本地仓库添加远程仓库路径,输入命令: 
$git remote add origin $CENTER_REPOSITORY_PATH 
其中origin是版本库路径的别名,$CENTER_REPOSITORY_PATH 
是中央版本仓库的路径,在之后的操做中能够用origin来代替。 

$git push origin $BRANCH_NAME 
这个命令将私有版本中的工做代码和版本历史记录添加到中央版本仓库中,其中包括这个私有版本仓库中全部的修改提交记录。 
这个命令以后本地目录下的 .git/config会发生改变。 

    windows端checkout出代码,在msysGit的bash中,输入命令: 
$ git clone origin 
由于咱们的服务器上的ssh协议只容许公钥/私钥远程登入,因此在执行这个命令以前须要将公私钥放到~/.ssh的目录下面。 

    在完成这个步骤以后,在windows和linux下已经各有一个独立的私有版本仓库,它们的地位是相同的,二者经过中央版本仓库进行代码同步。 
    此刻对各自私有版本仓库的commit,add和revert等操做只是对本私有版本仓库可见,修改历史对其余版本库不可见。 

多人协同  user1作了些修改,而后提交修改到中央版本库  $git push origin $BRANCH_NAME  其中$BRANCH_NAME指代功能开发的分支。  user2要同步user1的修改  $ git fetch origin $BRANCH_NAME  这个是抓取一个远程的版本库中的信息到一个私有版本库中,但不更新代码。通过这个操做能够经过命令git log看到远程版本库中的全部操做。  $ git pull origin $BRANCH_NAME  这个命令真正将将user1的修改merge到当前的版本库。  通过这个操,user2已经有了user1的全部修改和提交记录。

相关文章
相关标签/搜索