您可以捐助,支持我们的公益事业。

1元 10元 50元





认证码:  验证码,看不清楚?请点击刷新验证码 必填



  求知 文章 文库 Lib 视频 iPerson 课程 认证 咨询 工具 讲座 Modeler   Code  
会员   
 
   
 
 
     
   
 订阅
  捐助
Git与Repo入门(二)
 
作者 AngelDevil的BLOG,火龙果软件    发布于 2014-08-08
  2174  次浏览      15
 

五、GIT分支

分支被称之为GIT最强大的特性,因为它非常地轻量级,如果用Perforce等工具应该知道,创建分支就是克隆原目录的一个完整副本,对于大型工程来说,太费时费力了,而对于GIT来说,可以在瞬间生成一个新的分支,无论工程的规模有多大,因为GIT的分支其实就是一指针而已。在了解GIT分支之前,应该先了解GIT是如何存储数据的。

前面说过,GIT存储的不是文件各个版本的差异,而是文件的每一个版本存储一个快照对象,然后通过SHA-1索引,不只是文件,包换每个提交都是一个对象并通过SHA-1索引。无论是文本文件,二进制文件还是提交,都是GIT对象。

GIT对象

每个对象(object) 包括三个部分:类型,大小和内容。大小就是指内容的大小,内容取决于对象的类型,有四种类型的对象:"blob"、"tree"、 "commit" 和"tag"。

1.“blob”用来存储文件数据,通常是一个文件。

2.“tree”有点像一个目录,它管理一些“tree”或是 “blob”(就像文件和子目录)

3.一个“commit”指向一个"tree",它用来标记项目某一个特定时间点的状态。它包括一些关于时间点的元数据,如提交时间、提交说明、作者、提交者、指向上次提交(commits)的指针等等。

4.一个“tag”是来标记某一个提交(commit) 的方法。

比如说我们执行了以下代码进行了一次提交:

$ git add README test.rb LICENSE2
$ git commit -m 'initial commit of my project'

现在,Git 仓库中有五个对象:三个表示文件快照内容的 blob 对象;一个记录着目录树内容及其中各个文件对应 blob 对象索引的 tree 对象;以及一个包含指向 tree 对象(根目录)的索引和其他提交信息元数据的 commit 对象。概念上来说,仓库中的各个对象保存的数据和相互关系看起来如下图:

如果进行多次提交,仓库的历史会像这样:

分支引用

所谓的GIT分支,其实就是一个指向某一个Commit对象的指针,像下面这样,有两个分支,master与testing:

而我们怎么知道当前在哪一个分支呢?其实就是很简单地使用了一个名叫HEAD的指针,如上图所示。HEAD指针的值可以为一个SHA-1值或是一个引用,看以下例子:

git的所有版本信息都保存了Working Directory下的.git目录,而HEAD指针就保存在.git目录下,如上图所有,目前为止已经有3个提交,通过查看HEAD的值可以看到我们当前在master分支:refs/heads/master,当我们通过git checkout取出某一特定提交后,HEAD的值就是成了我们checkout的提交的SHA-1值。

记录我们当前的位置很简单,就是能过HEAD指针,HEAD指向某一提交的SHA-1值或是某一分支的引用。

新建分支

git branch <branch-name>

有时需要在新建分支后直接切换到新建的分支,可以直接用checkout的-b选项

git checkout -b <branch-name>

删除分支

git branch -d <branch-name>

如果在指定的分支有一些unmerged的提交,删除分支会失败,这里可以使用-D参数强制删除分支。

git branch -D <branch-name>

检出分支或提交

检出某一分支或某一提交是同一个命令

git checkout <branch-name> | <commit>

分支合并(merge)

当我们新建一个分支进行开发,并提交了几次更新后,感觉是时候将这个分支的内容合回主线了,这是就可以取出主线分支,然后把分支的更新merge回来:

git checkout master

git merge testing

如果master分支是testing分支的直接上游,即从master延着testing分支的提交历史往前走可以直接走到testing分支的最新提交,那么系统什么也不需要做,只需要改变master分支的指针即可,这被称之为"Fast Forward"。

但是,一般情况是这样的,你取出了最新的master分支,比如说master分支最新的提交是C2(假设共3次提交C0<-C1<-C2),在此基础上你新建了分支,当你在分支上提交了C3、C5后想将br1时merge回master时,你发现已经有其他人提交了C4,这时候就不能直接修改master的指针了,不然会丢失别人的提交,这个时候就需要将你新建分支时master所在的提交(C2)后的修改(C4),与你新建分支后在分支上的修改(C3、C5)做合并,将合并后的结果作为一个新的提交提交到master,GIT可以自动推导出应该基于哪个提交进行合并(C2),如果没有冲突,系统会自动提交新的提交,如果有冲突,系统会提示你解决冲突,当冲突解决后,你就可以将修改加入暂存区并提交。提交历史类似下面这样(图来自Pro-Git):

merge后的提交是按时间排序的,比如下图,我们在rename提交处新建分支test,在test上提交Commit from branch test,然后回到master提交commit in master after committing in branch,再将test分支merge进master,这时看提交提交历史,Commit from branch test是在commit in master...之前的,尽管在master上我们是在rename的基础上提交的commit in master...而GIT会在最后添加一个新的提交(Merge branch 'test')表示我们在此处将一个分支merge进来了。这种情况会有一个问题,比如说在rename提交处某人A从你这里Copy了一个GIT仓库,然后你release了一个patch(通过git format-patch)给A,这时候test分支还没有merge进来,所以patch中只包含提交:commit in master...然后你把test分支merge了进来又给了A一个patch,这个patch会包含提交:Commit from branch test,而这个patch是以rename为base的,如果commit in master...和Commit from branch test修改了相同的文件,则第二次的patch可能会打不上去,因为以rename为base的patch可能在新的Code上找不到在哪个位置应用修改。

分支衍合(rebase)

有两种方法将一个分支的改动合并进另一个分支,一个就是前面所说的分支合并,另一个就是分支衍合,这两种方式有什么区别呢?

分支合并(merge)是将两个分支的改动合并到一起,并生成一个新的提交,提交历史是按时间排序的,即我们实际提交的顺序,通过git log --graph或一些图形化工具,可能很明显地看到分支的合并历史,如果分支比较多就很混乱,而且如果以功能点新建分支,等功能点完成后合回主线,由于merge后提交是按提交时间排序的,提交历史就比较乱,各个功能点的提交混杂在一起,还可能遇到上面提到的patch问题。

而分支衍合(rebase)是找到两个分支的共同祖先提交,将要被rebase进来的分支的提交依次在要被rebase到的分支上重演一遍,即回到两个分支的共同祖先,将branch(假如叫experiment)的每次提交的差异保存到临时文件里,然后切换到要衍合入的分支(假如是master),依次应用补丁文件。experiment上有几次提交,在master就生成几次新的提交,而且是连在一起的,这样合进主线后每个功能点的提交就都在一起,而且提交历史是线性的

对比merge与rebase的提交历史会是下图这样的(图来自Pro-GIt):

(merge)

(rebase)

rebase后C3提交就不存在了,取而代之的是C3',而master也成为了experiment的直接上游,只需一次Fast Forward(git merge)后master就指向了最新的提交,就可以删除experiment分支了。

衍合--onto

git rebase --onto master server client

这条命令的意思是:检出server分支与client分支共同祖先之后client上的变化,然后在master上重演一遍。

父提交

HEAD表示当前所在的提交,如果要查看当前提交父提交呢?git log查看提交历史,显然太麻烦了,而且输入一长串的Commit-ID也不是一个令人愉悦的事。这时可借助两个特殊的符号:~与^。

^ 表示指定提交的父提交,这个提交可能由多个交提交,^之后跟上数字表示第几个父提交,不跟数字等同于^1。

~n相当于n个^,比如~3=^^^,表示第一个父提交的第一个父提交的第一个父提交。

远程分支

远程分支以(远程仓库名)/(分支名)命令,远程分支在本地无法移动修改,当我们clone一个远程仓库时会自动在本地生成一个名叫original的远程仓库,下载远程仓库的所有数据,并新建一个指向它的分支original/master,但这个分支我们是无法修改的,所以需要在本地重新一个分支,比如叫master,并跟踪远程分支。

Clone了远程仓库后,我们还会在本地新建其他分支,并且可能也想跟踪远程分支,这时可以用以下命令:

git checkout -b [branch_name] --track|-t <remote>/<remote-banch>

和新建分支的方法一样,只是加了一个参数--track或其缩写形式-t,可以指定本地分支的名字,如果不指定就会被命名为remote-branch。

要拉取某个远程仓库的数据,可以用git fetch:

git fetch <remote>

当拉取到了远程仓库的数据后只是把数据保存到了一个远程分支中,如original/master,而这个分支的数据是无法修改的,此时我们可以把这个远程分支的数据合并到我们当前分支

git merge <remote>/<remote-branch>

如果当前分支已经跟踪了远程分支,那么上述两个部分就可以合并为一个

git pull

当在本地修改提交后,我们可能需要把这些本地的提交推送到远程仓库,这里就可以用git push命令,由于本地可以由多个远程仓库,所以需要指定远程仓库的名字,并同时指定需要推的本地分支及需要推送到远程仓库的哪一个分支

git push <remote> <local-branch>:<remote-branch>

如果本地分支与远程分支同名,命令可以更简单

git push <remote> <branch-name> 等价于 git 
                          push <remote> refs/heads/<branch-name>:refs/for/<branch-name>

如果本地分支的名字为空,可以删除远程分支。

前面说过可以有不止一个远程分支f,添加远程分支的方法为

git remote add <short-name> <url>

六、标签-tag

作为一个版本控制工具,针对某一时间点的某一版本打tag的功能是必不可少的,要查看tag也非常简单,查看tag使用如下命令

git tag

参数"-l"可以对tag进行过滤

git tag -l "v1.1.*"

Git 使用的标签有两种类型:轻量级的(lightweight)和含附注的(annotated)。轻量级标签就像是个不会变化的分支,实际上它就是个指向特定提交对象的引用。而含附注标签,实际上是存储在仓库中的一个独立对象,它有自身的校验和信息,包含着标签的名字,电子邮件地址和日期,以及标签说明,标签本身也允许使用 GNU Privacy Guard (GPG) 来签署或验证。

轻量级标签只需在git tag后加上tag的名字,如果tag名字

git tag <tag_name>

含附注的标签需要加上参数-a(annotated),同时加上-m跟上标签的说明

git tag -a <tag_name> -m "<tag_description>"

如果你有自己的私钥,还可以用 GPG 来签署标签,只需要把之前的 -a 改为 -s(signed)

查看标签的内容用

git show <tag_name>

验证已签署的标签用-v(verify)

git tag -v <tag_name>

有时在某一个版本忘记打tag了,可以在后期再补上,只需在打tag时加上commit-id

要将tag推送到远程服务器上,可以用

git push <remote> <tag_name>

或者可以用下面的命令推送所有的tag

git push <remote> --tags

七、Git配置

使用"git config"可以配置Git的环境变量,这些变量可以存放在以下三个不同的地方:

1./etc/gitconfig 文件:系统中对所有用户都普遍适用的配置。若使用 git config 时用 --system选项,读写的就是这个文件。

2.~/.gitconfig 文件:用户目录下的配置文件只适用于该用户。若使用 git config 时用 --global选项,读写的就是这个文件。

3.当前项目的 git 目录中的配置文件(也就是工作目录中的 .git/config 文件):这里的配置仅仅针对当前项目有效。每一个级别的配置都会覆盖上层的相同配置,所以 .git/config 里的配置会覆盖/etc/gitconfig 中的同名变量。

在 Windows 系统上,Git 会找寻用户主目录下的 .gitconfig 文件。主目录即 $HOME 变量指定的目录,一般都是 C:\Documents and Settings\$USER。此外,Git 还会尝试找寻 /etc/gitconfig 文件,只不过看当初 Git 装在什么目录,就以此作为根目录来定位。

最基础的配置是配置git的用户,用来标识作者的身份

git config --global user.name 
git config --global user.email 

文本编辑器也可以配置,比如在git commit的时候就会调用我们设置的文本编辑器

git config --global core.editor vim

另一个常用的是diff工具,比如我们想用可视化的对比工具

git config --global merge.tool meld

要查看所有的配置,可以用

git config --list

或者可以在git config后加上配置项的名字查看具体项的配置

git config user.name

作为一个懒人,虽然checkout、status等命令只是一个单词,但是还是嫌太长了,我们还可以给命令设置别名如

git config --global alias.co checkout

这样git co就等于git checkout

前面说地,git配置项都保存在那3个文件里,可以直接打开相应的配置文件查看配置,也可以直接修改这些配置文件来配置git,想删除某一个配置,直接删除相应的行就行了

八、其他

关于GIT各命令的说明可以查看相关帮助文档,通过以下方法:

git config --global alias.co checkout

REPO

repo start <topic_name>

开启一个新的主题,其实就是每个Project都新建一个分支。

repo init -u <url> [OPTIONS]

在当前目录下初始化repo,会在当前目录生生成一个.repo目录,像Git Project下的.git一样,-u指定url,可以加参数-m指定manifest文件,默认是default.xml,.repo/manifests保存manifest文件。.repo/projects下有所有的project的数据信息,repo是一系列git project的集合,每个git project下的.git目录中的refs等目录都是链接到.repo/manifests下的。

repo manifest

可以根据当前各Project的版本信息生成一个manifest文件

repo sync [PROJECT1...PROJECTN]

同步Code。

repo status

查看本地所有Project的修改,在每个修改的文件前有两个字符,第一个字符表示暂存区的状态。

每二个字符表示工作区的状态

repo prune <topic> 

删除已经merge的分支

repo abandon <topic>

删除分支,无论是否merged

repo branch或repo branches

查看所有分支

repo diff

查看修改

repo upload

上传本地提交至服务器

repo forall [PROJECT_LIST]-c COMMAND

对指定的Project列表或所有Project执行命令COMMAND,加上-p参数可打印出Project的路径。

repo forall -c 'git reset --hard HEAD;git clean -df;git rebase --abort'

这个命令可以撤销整个工程的本地修改。

   
2174 次浏览       15
相关文章

每日构建解决方案
如何制定有效的配置管理流程
配置管理主要活动及实现方法
构建管理入门
相关文档

配置管理流程
配置管理白皮书
CM09_C配置管理标准
使用SVN进行版本控制
相关课程

配置管理实践
配置管理方法、工具与应用
多层次集成配置管理
产品发布管理
最新课程计划
信息架构建模(基于UML+EA)3-21[北京]
软件架构设计师 3-21[北京]
图数据库与知识图谱 3-25[北京]
业务架构设计 4-11[北京]
SysML和EA系统设计与建模 4-22[北京]
DoDAF规范、模型与实例 5-23[北京]

软件配置管理的问题、目的
软件配置管理规范
CQWeb 7.1性能测试与调优指南
为什么需要使用ClearCase
ClearCase与RTC的集成
利用ClearQuest 进行测试管理
更多...   


产品发布管理
配置管理方法、实践、工具
多层次集成配置管理
使用CC与CQ进行项目实践
CVS与配置管理
Subversion管理员


配置管理实践(从组织级到项目级)
通号院 配置管理规范与应用
配置管理日构建及持续集成
丹佛斯 ClearCase与配置管理
中国移动 软件配置管理
中国银行 软件配置管理
天津华翼蓝天科技 配置管理与Pvcs