《Git权威指南》笔记2

###Git克隆 Git使用git clone命令实现版本库克隆,主要有如下3种用法:
1)git clone <repository> <direcctory>
将repository指向的版本库创建一个克隆岛directory目录。目录directory相当于克隆版本库的工作区,文件都会从版本库中检出,版本库位于工作区下的.git目录中。

2个版本库之间可以通过PULL和PUSH操作实现同步。但是对于这种git clone出来对等工作区模式,版本库的同步只有一种可行的操作模式,就是备份库执行git pull命令从源版本库中拉回新的提交实现版本库同步。git pull命令无需指定上游版本库的地址,是因为在执行git clone后,克隆出来的版本库中对源版本库进行了注册。备份库中上游版本库的注册保存在git配置文件中。

2)git clone --bare <repository> <direcctory.git>
创建的版本库不包含工作区,直接就是版本库的目录,这样的版本库称为裸版本库。一般约定成俗裸版本库以.git为后缀。

这种模式的git clone可以支持原版本库到备份版本库的git push操作,因为备份版本库没有工作区。git配置文件中decore.bare的配置为true

裸版本库也可以通过git init生成:

git init --bare 备份库路径pathB
git push 源版本库路径A master:master

3)git clone --mirror <repository> <direcctory.git>
和2一样,另外克隆出来的裸版本对上游版本库进行了注册,这样可以在裸版本库中使用git fetch命令和上游版本库进行持续同步。

###Git库管理 ####打包 从远程克隆一个库到本地,发现refs目录下没有引用文件,原有的引用文件都被打爆到一个文本文件.git/packed-refs中。objects目录下没有对象文件,原有的对象文件都被打包到.pack结尾的打包文件和.idx结尾的索引文件并保存在.git/objects/pack目录下。

克隆远程版本库时,使用了“智能”的通信协议,远程Git服务器将对象打包后传输给本地,形成本地版本库的对象库中的一个包含所有对象的包及索引文件。

Git对于以SHA1哈希值作为目录名和文件名保存的对象有一个术语:松散对象。松散对象打包后悔提高访问效率,而且不同的对象可以通过增量存储节省磁盘空间。

####临时对象 暂存区操作有可能在对象库中产生临时对象,例如,文件反复修改、反复地向暂存区添加,或者添加到暂存区后不提交甚至直接撤销,就会产生垃圾数据占用磁盘空间。

git fsck:查看版本库中包含的没有被任何引用关联的松散对象
git fsck --no-reflogs:查看包括reflog记录中的临时对象
git prune:清理临时对象

如果是使用重置命令引入的临时对象用git prune就不能被清除,因为这些临时对象还是可以通过reflog追踪到的,还在使用着,所以不能被git prune删除。要丢弃这些临时对象,需要把reflog做过期操作,相当于把.git/logs下的文件清空:git reflog expire -all

####Git管家 git-gc 相比git prune更常用的命令是git gc,它对版本库进行一系列的优化动作:
1)对分散在.git/refs下的文件进行打包,打包到文件.git/packed-refs中:git pack-refs --all --prune;
2)丢弃90天前的reflog记录:git reflog expire --all;
3)对松散对象进行打包:git repack:凡是有引用关联的对象都被打包,未被关联的对象仍旧以送松散对象的形式保存;
4)清除未被关联的对象。默认只清楚2周以前的未被关联的对象。
5)其他清理:对合并冲突的历史记录进行过期操作。

git gc -- prune=<date> 中时间参数是传递给git prune --expire <date>实现对指定日期之前的未被关联的松散对象进行清理。

git gc会在部分git命令下会自动执行,如git merge, git receice-pack, git rebase -i, git am 等。 共享式写操作的git版本库因为会push触发自动gc,非独立工作的本地库要pull merge触发自动gc。

git gc触发的条件:松散对象只有草果一定数量才会只写。在统计松散对象数量时,为降低在.git/objects目录下搜索松散对象对系统造成的负担,实际采取取样搜索,即只对对象库下的一个子目录.git/objects/17进行文件搜索。在默认配置下,只有该目录中对象数目草果27个才会触发版本库的整理。至于为什么只在对象库中选择一个子目录进行松散对象搜索,这是因为SHA1哈希值是完全随机的,文件有前2位哈希值组成的文件中差不多是平均分布的。gc.auto是触发版本库整理的全部松散对象树的阈值。

###Git协议与工作协同 ####Git支持的协议 版本库间数据交换需要使用协议,协议分2种:
1)智能协议:在使用智能协议时,会在会话的2个版本库的各自一段打开相应的程序进行数据交换,在数据传输过程中会有清晰的进度显示,而且因为是按需传输所以传输量更小速度更快。SSH、GIT、本地协议、特殊配置的HTTP是智能协议。
2)哑协议:使用哑协议时,远程版本库不会允许辅助衬线,完全靠客户端去主动发现,需要访问refs获取引用列表,根据引用再去访问objects下的对象文件,如果对象文件被打包了还要访问packs以获取打包文件列表,在读取完整的打包文件获取对象。整个过程效率非常低,没有进度条。FTP、RSYNC、没有特殊配置的HTTP是哑协议。

####强制非快进式推送 快进式推送faset-forwards:要推送的本地版本的提交时建立在远程版本库相应分支的现有提交基础上的,即远程版本库相应分支的最新提交时本地版本库最新提交的祖先提交。

git push -f:强制推送,推送的提交会覆盖刷新版本库中的版本。

####合并后推送 理性的工作协同要避免非快进式推送。一旦向服务器推送后,如果发现错误,不要使用会更改历史的操作(变基、修补提交),而是采用不会改变历史提交的反转提交等操作。

如果在想服务器推送过程中,由于他人率先推送了新的提交导致遭遇到非快进式推送的警告,应该先执行git pull获取服务器最新的提交并和本地提交进行合并,合并成功后再向服务器提交。

####禁止非快进式推送 1)配置receive.denyNonFastForwards设置为true可以禁止任何用户进行非快进式推送操作。
2)通过钩子脚本进行设置,可以仅对某些情况下的非快进式推送进行限制,而不是全部拒绝。

###冲突解决 实际上拉回操作git pull 由2步骤组成:获取操作git fetch,合并操作git merge

git merge [选项...] <commit>...

合并操作的大多数情况,只须提供一个commit(提交ID或对应的引用:分支、tag等)作为参数。合并操作将commit对应的目录树和当前工作分支的目录树的内容进行合并,合并后的提交以当前分支的提交作为第一个父提交,以commit为第二个父提交。合并操作还支持多个commit代表的分支和当前分支进行合并,过程类似。 默认情况下合并后的结果会自动提交,但是如果提供--no-commit选项,则合并后的结果会放入暂存区,用户可以对合并结果进行检查、更改,然后手动提交。

冲突类型:成功的自动合并、逻辑冲突、真正的冲突、树冲突。

####自动合并 1)修改不同的文件;
2)修改相同文件的不同区域; 3)一个修改文件名,一个修改文件内容;

####逻辑冲突 例子:一个用户修改了一个文件的文件名,而另外的用户在其他文件中引用旧的文件名,这样的合并虽然能成功,但是包含着逻辑冲突。

####真正的冲突 执行git pull时所做的合并操作由于遇到冲突导致中断。合并过程是通过.git目录下的MERGE_HEAD(记录合并的提交ID),MERGE_MSG(记录合并失败的信息),MERGE_MODE(标记合并状态)。 并且版本库库暂存区会记录冲突文件的多个不同版本。使用git ls-files查看。当合并冲突发生后,会用到0以上的暂存区编号,编号1暂存区用于保存冲突文件修改之前的副本,即冲突双方共同的祖先版本,可以使用:1:xxx访问;编号2暂存区用于保存当前冲突文件在当前分支中修改的副本,可以使用:2:xxx访问;编号3暂存区用于保存当前冲突文件在合并版本(分支)中修改的副本,可以使用:3:xxx访问。这3个副本实际上是提供冲突解决工具,用于实现三向文件合并的。

工作区的版本则可能同时包含了成功的合并和冲突的合并,其中冲突的合并会用特殊的标记7个小于号,7个等号,7个大于号进行标识。7个小于号和7个等号之间的内容是当前分支所更改的内容。7个等号和7个大于号之间的内容是所合并的版本更改的内容。

冲突解决的实质就是通过编辑操作,将冲突标识的冲突内容替换为合适的内容,并去掉冲突标识。编辑完毕后执行git add命令将文件添加到暂存区(标号0),然后载提交完成冲突解决。

当工作去处于合并冲突状态,无法再执行提交操作。此时又2个选择:放弃合并操作,或者对合并冲突进行冲突解决操作。放弃合并操作就是执行git reset将暂存区重置即可。而冲突解决有2种办法:1)手工编辑操作;2)图形化解决冲突工具git mergetool。

当冲突解决完毕后,会看到.git目录下与合并相关的文件MERGE_HEAD、MERGE_MSG、MERGE_MODE文件会自动删除,而且暂存区中的3个副本也会都清除了。

####树冲突 如果一个用户将某个文件改名,另外一个用户将同样的文件改为另外的名字,当这2个用户的提交进行合并操作时,就发生树冲突。

解决方法:git rm掉暂存区中当前分支的冲突文件和合并版本的冲突文件,然后git add工作区中修改好的文件,提交完成冲突解决。同样也可以用git mergetool交互式解决

####合并策略

git merge [-s <strategy>] [-X <strategy-option>] <commit>...

-s:用于设定合并策略,-X用于为所选的合并策略提供附加的参数
1)resolve:该合并策略只能用于合并2个头(当前分支和另外一个分支),使用三向合并策略
2)recursive:该合并策略只能用于合并2个头(当前分支和另外一个分支),使用三向合并策略,是2个头指针合并时的默认合并策略。当合并的头指针拥有一个以上的祖先时,会针对多个公共祖先先创建一个合并的树,并以此作为三向合并的参照。 附加选项:ours,thires,substree
3)octopus:主要用于多个主题分支合并,是>=3个以上的头指针合并的默认策略
4)ours:合并任意数量的头指针,合并总是使用自己的内容,丢弃其他分支的内容
5)substree:经过调整的recursive策略,当合并树A和树B时,如果B和A的一个子树相同,B首先会调整已匹配A树的结构,以避免2颗树在同一级被进行合并。

####合并相关的配置 1)merge.conflictstyle:定义冲突文件中冲突的标记风格:merge或diff3
2)merge.tool:执行git mergetool进行解决时条用的图形化工具:
3)merge.<tool>.path:
4)merge.<tool>.cmd:
5)merge.log:是否在合并提交的提交说明中包含合并提交的概要信息,默认false

###Git里程碑Tag 里程碑即Tag,是人为对提交进行的命名。
git tag:显示当前版本库的tag列表
git tag -n<num>:显示tag时同时显示num行的说明

git describe:将提交显示成一个易记的名称,这个易记的名称来自于建立在该提交上的里程碑,若该提交没有里程碑则使用该提交历史版本上的里程碑并加上可理解的寻址信息。规则:
1)如果该提交恰好被打上一个tag,则显示该tag;
2)若提交没有对应的tag,则用<tag>-<num>-g<commit>的格式显示。tag是最接近祖先提交的tag,num是该tag和提交之间的距离,commit是提交的精简提交id;
3)若工作区对文件有修改,还可以通过后缀-dirty表示出来;
4)若提交本身没有包含tag,可以通过传递--always参数显示精简提交ID

git name-rev:和git describe类似,会显示提交id及其对应的一个引用。默认优先使用分支名,除非使用--tags参数。如果提交上没有相对应的引用,则会使用最新提交上的引用名称并加上向后回溯的符号~<num>

####轻量级tag git tag <tagename> <commit>:创建轻量级tag,无需输入描述信息。忽略commit则默认在HEAD上创建tag。

创建tag后,会在版本库的.git/refs/tags目录下创建一个新文件。
1)这个文件的内容是一个40位的SHA1哈希值;
2)这个tag指向的对象类型是commit;
3)查看tag指向的提交,其内容是普通的提交信息。
4)轻量级tag的创建过程没有记录,因此无法知道是谁创建的tag,何时创建tag。
5)git describe命令默认不使用轻量级tag生成版本描述字符串。但使用--tags参数,可以将轻量级tag作为版本描述符。

####带说明tag git tag -a <tagename> <commit> git tag -m <msg> <tagename> <commit> 在创建tag时提供一个关于该tag的说明。

创建带说明的tag后,会在版本库的.git/refs/tags目录下创建一个新文件。 1)这个文件的内容是一个40位的SHA1哈希值;
2)这个tag指向的对象类型是tag;
3)tag对象的内容是:创建tag时的说明+对应的提交ID+创建tag的用户+创建tag时间

####带签名的tag 带签名的tag和带说明的tag本质上是一样的,相比多了一个工作:为tag对象添加GnuPG签名。创建tag时候使用参数-s或-u <key=id>即可创建一个带签名的tag

####删除tag git tag -d <tagname>:删除tag tag没有类似reflog的变更记录机制,一旦删除不易恢复慎用。Git没有提供对tag重命名的命令。如果需要修改,可以删除旧tag然后再用新名称创建tag。

####共享tag 创建的tag默认只能在本地版本库中可见,不会因为对分支执行推送而将tag也推送到远程版本库。这样的设计显然更为合理,否则的话,每个用户本地创建的tag都自动向上游推送,那么上游的tag将有那么杂乱,而且不同用户创建的相同名称的tag会互相覆盖。
git push origin mytag:把名称为mytag的tag共享到上游版本库。
git push origin refs/tags/*:将本地建立的所有tag全部推送到远程版本库,可以使用通配符。

在执行执行git pull操作,能够在获取远程共享版本库的提交的同时,获取新的tag。 只会获取远程分支所包含的新tag,不会将远程版本库的其他分支的tag获取到本地。

如果本地已有同名的tag,默认是不会从上游同步tag,即使二两tag的指向不同,需要显示的执行拉回操作:git pull origin refs/tags/mytag2:refs/tags/mytag2

####删除远程版本库的tag

git push <remote_url> :<tagname>

该命令的最后一个参数实际上是一个引用表达式,引用表达式格式为<ref>:<ref>,该推送命令使用的引用表达式冒号前的引用被省略,其含义是将一个空值推送到远程版本库对应的应用中,即删除远程版本库中相关的引用。这个命令不但可以用于删除tag,还有可以删除分支。
git push origin :mytag2

###Git分支 ####代码管理 1)发布分支Realease Branch:或Bugfix分支,在软件新版本发布后经常使用此技术进行软件维护,发布升级版本。
2)特性分支Feature Branch:或主题分支Topic Branch,采用分支将某个功能或模块的开发与开发主线独立出来。 以下几种情况应该建立特性分支:1)试验性、探索性的功能开发;2)功能复杂、开发周期长的模块;3)会更改软件体系架构,破坏软件集成,或容易导致冲突、影响他人开发进度的模块。
3)卖主分支Vendor Branch:在版本库中创建一个专门和上游代码进行同步的分支,一旦有上游代码发布就检入到卖主分支中。再在主线版本上合并卖主分支上的提交。

####分支命令 1)git branch:显示本地分支列表;
2)git branch <branchname>:基于HEAD所指的提交创建名为branchname的分支;
3)git branch <branchname> <start-point>:基于提交start-point创建名为branchname的新分支;
4)git branch -d <branchname>:删除分支,如果该分支还未合并到其他分支,则拒绝删除;
5)git branch -D <branchname>:强制删除分支;
6)git branch -m <oldbranch> <newbranch>:重命名分支,如果名称已存在则失败; 7)git branch -M <oldbranch> <newbranch>:强制重命名分支;

####开发常用命令 git checkout -b <new_branch> [<start_point>]:用于基于start_point创建分支,并切换到新分支上;

###远程版本库git remote ####远程分支 git branch -r :查看远程分支;

从远程版本库执行获取操作时,不是把远程版本库的分支原封不动地复制到本地版本库的分支中,而是复制到另外一个命名空间。如在克隆一个版本库时,会将远程分支都复制到目录.git/refs/remotes/origin/下。这样从不同的远程版本库执行获取操作,因为通过明明空间的相互隔离,所以就避免了在本地的相互覆盖。

那么克隆操作产生的远程分支为什么都有一个名为"origin/"的前缀? 答案就在配置文件.git/config中。

6 [remote "origin"]
7 	fetch= +refs/heads/*:refs/remotes/origin/*
8 	url= file:///path/to/repos/hello-world.git

这个小节可以称为[remote]小节,该小节以origin为名注册了一个远程版本库。该版本库的URL地址由第8行给出,会发现这个URL地址就是执行git clone命令时所使用的地址。第7行设置了执行git fetch origin操作时使用的默认引用表达式:+表示是牵制进行引用的替换,应用表达式使用了通配符,冒号前含有通配符的引用值得是远程版本库的所有分支,冒号后的引用含义是复制到本地的远程分支目录中。

远程分支类似tag,如果检出就会使得头指针HEAD处于分离头指针状态。实际上除了以refs/heads为前缀的引用之外,如果检出任何其他引用,都将使工作区处于分离头指针状态。如果对远程分支进行修改需要创建新的本地分支。

git checkout -b hello-1.x origin/hello-1.x 执行上面命令后,会自动建立了本地分支和远程分支的跟踪,建立跟踪的分支就具有以下特征: 1)检查工作去状态时,会显示本地分支和被跟踪远程分支提交之间的状态;
2)执行git pull时,会从远程分支拉取最新提交并合并;
3)执行git push时,会把最新的提交推送到远程版本库的同名分支中;
但是如果基于本地分支创建另外一个本地分支则新建的分支没有跟踪功能。如果需要能够使用跟踪功能,则创建分支时增加--track参数即可。

####远程版本库 名为origin的远程版本库是在版本库克隆时注册的,那么如何注册新的远程版本库? git remote add new-remote file:///path/to/repos/hello-user.git
以new-remote为名进行注册,打开配置文件可以看到remote "new-remote"小节。

git remote:显示已注册的远程版本库 git fetch:从当前默认远程版本库origin获取最新提交
git fetch new-remote:从new-remote远程版本库中获取最新提交 git remote set-url new-remote file:///path/to/repos/hello-user2.git更改远程版本库的地址
git remote set-url --push new-remote /path/to/repos/hello-user2.git 每个远程版本库都显示2个URL地址,分别用于执行git fetch和git push命令时用到的URL地址。

git remote rename new-remote user2:更改远程版本库的名称
git remote update:获取所有远程版本库的更新;
如果某个远程版本库不想再执行git remote update时获取更新,可以通过参数关闭自动更新:git config remote.user2.skipDefaultUpdate true

####push和pull操作与远程版本库 当不带任何参数执行git push命令时,实际的执行过程是:
1)如果为当前分支设置了<remote>,即由配置branch.<branchname>.remote给出了远程版本库代号,则不带参数执行git push相当于执行了git push <remote>;
2)如果没有为当前分支设置<remote>,则不带参数执行git push相当于执行了git push origin;
3)要推送的远程版本库的URL地址由remote.<remote>.pushurl给出。如果没有配置,则使用remote.<remote/>.url配置的URL地址;
4)如果为注册的远程版本库设置了push参数,即通过remote.<remote>.push配置了一个引用表达式,则使用该应用表达式执行推送。
5)否则使用“:”作为引用表达式。该表达式的含义是同步分支推送,即对所有在远程版本库中有同名分支的本地分支进行推送

git push <remote> <new_branch>:用本地分支推送到远程版本库的方式在远程版本库中创建分支。但是本地不能执行git pull(不带参数)获取远程版本库中其他人的推送。这是因为没有建立本地分支和远程分支的跟踪,没有设置branch.<branchname>.remote的值和branch.<branchname>.merge的值。

git push命令的执行过程:
1)如果为当前分支设置了<remote>,即由配置branch.<branchname>.remote给出了远程版本库代号,则不带参数执行git pull相当于执行了git pull <remote>;
2)如果没有为当前分支设置<remote>,则不带参数执行git pull相当于执行了git pull origin;
3)要获取的远程版本库的URL地址由remote.<remote>.url给出。
4)如果为注册的远程版本库设置了fetch参数,即通过remote.<remote>.fetch配置了一个引用表达式,则使用该应用表达式执行推获取。
5)接下来要确定合并的分支。如果设定了branch.<branchname>.merge,则对其设定的分支执行合并,否则报错推出。
在执行git pull操作的时候可以通过参数--rebase设置使用变基而非合并操作,将本地分支的改动变基到跟踪分支上。为避免因为忘记使用--rebase可以设置配置git config branch.<branchname>.rebase true。 为本地版本库设置参数branch.autosetuprebase为true时,基于远程分支建立的本地追踪分支会自动配置branch.<branchname>.rebase参数。

####tag和远程版本库 远程版本库中的tag同步到本地版本库,会使用相同的名称,而不像分支那样有另外的命名空间,所以tag会很混乱。在push的时候不会把tag带入其他版本库,但是fetch的时候会把tag一起获取,而且删除分支的时候,分支上的tag不会被删除。tag会越来越乱。 使用git fetch命令时加上--no-tags参数就不会把tag也获取到。 使用git remote add 注册版本库的时候带--no-tags不iba本地库的tag引到远程库上。

###补丁文件交互 git format-patch 版本范围列表:创建补丁,将提交批量转为补丁文件;
git am 补丁文件:应用补丁;

###其他命令 git help -g: 展示帮助信息

转载于:https://my.oschina.net/braveCS/blog/742286

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值