首页 > 代码库 > git中级技能

git中级技能

中级技能(上)

 
 
 
 
 
 
 

一、实验说明

从本节开始,我们会介绍一些中级和高级的用法,这些用法很少用到,前面三节的内容已经满足了日常工作需要,从本节开始的内容可以简单了解,需要的时候再详细查看。

1.1 下载测试项目环境

通过下列命令获得gitproject项目环境,该项目默认只有一个文件README.md,可以用来进行后续git实验

$ git clone http://git.shiyanlou.com/shiyanlou/gitproject

二、忽略某些文件

1.忽略某些文件

项目中经常会生成一些Git系统不需要追踪(track)的文件。典型的是在编译生成过程中产生的文件或是编程器生成的临时备份文件。当然,你不追踪(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]

三、rebase

1.rebase

假设你现在基于远程分支"origin",创建一个叫"mywork"的分支。

$ git checkout -b mywork origin

现在我们在这个分支做一些修改,然后生成两个提交(commit)。

$ vi file.txt
$ git commit
$ vi otherfile.txt
$ git commit

但是与此同时,有些人也在"origin"分支上做了一些修改并且做了提交了。这就意味着"origin"和"mywork"这两个分支各自"前进"了,它们之间"分叉"了。 在这里,你可以用"pull"命令把"origin"分支上的修改拉下来并且和你的修改合并; 结果看起来就像一个新的"合并的提交"(merge commit): 但是,如果你想让"mywork"分支历史看起来像没有经过任何合并一样,你也许可以用git rebase:

$ git checkout mywork
$ git rebase origin

这些命令会把你的"mywork"分支里的每个提交(commit)取消掉,并且把它们临时保存为补丁(patch)(这些补丁放到".git/rebase"目录中),然后把"mywork"分支更新 到最新的"origin"分支,最后把保存的这些补丁应用到"mywork"分支上。 当‘mywork‘分支更新之后,它会指向这些新创建的提交(commit),而那些老的提交会被丢弃。 如果运行垃圾收集命令(pruning garbage collection), 这些被丢弃的提交就会删除。 在rebase的过程中,也许会出现冲突(conflict). 在这种情况,Git会停止rebase并会让你去解决冲突;在解决完冲突后,用"git-add"命令去更新这些内容的索引(index), 然后,你无需执行 git-commit,只要执行:

$ git rebase --continue

这样git会继续应用(apply)余下的补丁。 在任何时候,你可以用--abort参数来终止rebase的行动,并且"mywork" 分支会回到rebase开始前的状态。

$ git rebase --abort

四、交互式rebase

1.交互式rebase

你亦可以选择进行交互式的rebase。这种方法通常用于在向别处推送提交之前对它们进行重写。交互式rebase提供了一个简单易用的途径让你在和别人分享提交之前对你的提交进行分割、合并或者重排序。在把从其他开发者处拉取的提交应用到本地时,你也可以使用交互式rebase对它们进行清理。 如果你想在rebase的过程中对一部分提交进行修改,你可以在‘git rebase‘命令中加入‘-i‘或‘--interactive‘参数去调用交互模式。

$ git rebase -i origin/master

这个命令会执行交互式rebase操作,操作对象是那些自最后一次从origin仓库拉取或者向origin推送之后的所有提交。 若想查看一下将被rebase的提交,可以用如下的log命令:

$ git log github/master..

一旦你完成对提交信息的编辑并且退出编辑器,这个新的提交及提交信息会被保存起来。 如果指定进行‘edit‘操作,git会完成同样的工作,但是在对下一提交进行操作之前,它会返回到命令行让你对提交进行修正,或者对提交内容进行修改。 例如你想要分割一个提交,你需要对那个提交指定‘edit‘操作: 你会进入到命令行,撤销(revert)该提交,然后创建两个(或者更多个)新提交。假设提交21d80a5修改了两个文件,file1和file2,你想把这两个修改放到不同的提交里。你可以在进入命令行之后进行如下的操作:

$ git reset HEAD
$ git add file1
$ git commit -m ‘first part of split commit‘
$ git add file2
$ git commit -m ‘second part of split commit‘
$ git rebase --continue

交互式rebase的最后一个作用是丢弃提交。如果把一行删除而不是指定‘pick‘、‘squash‘和‘edit‘中的任何一个,git会从历史中移除该提交

五、交互式添加

1.交互式添加

交互式添加提供友好的界面去操作Git索引(index),同时亦提供了可视化索引的能力。只需简单键入‘git add -i‘,即可使用此功能。Git会列出所有修改过的文件及它们的状态。

$ git add -i

在这个例子中,我们可以看到有5个修改过的文件还没有被加入到索引中(unstaged),甚至可以看到每个文件增加和减少的行数。紧接着是一个交互式的菜单,列出了我们可以在此模式中使用的命令。 如果我们想要暂存(stage)这些文件,我们可以键入‘2‘或者‘u‘进入更新(update)模式。然后我们可以通过键入文件的范围(本例中是1-4)来决定把哪些文件加入到索引之中。

What now> 2
           staged     unstaged path
  1:    unchanged        +4/-0 assets/stylesheets/style.css
  2:    unchanged      +23/-11 layout/book_index_template.html
  3:    unchanged        +7/-7 layout/chapter_template.html
  4:    unchanged        +3/-3 script/pdf.rb
  5:    unchanged      +121/-0 text/14_Interactive_Rebasing/0_ Interactive_Rebasing.markdown
Update>> 1-4
           staged     unstaged path
* 1:    unchanged        +4/-0 assets/stylesheets/style.css
* 2:    unchanged      +23/-11 layout/book_index_template.html
* 3:    unchanged        +7/-7 layout/chapter_template.html
* 4:    unchanged        +3/-3 script/pdf.rb
  5:    unchanged      +121/-0 text/14_Interactive_Rebasing/0_ Interactive_Rebasing.markdown
Update>>

如果键入回车,我会回到主菜单中,同时可以看到那些指定文件的状态已经发生了改变:

What now> status
           staged     unstaged path
  1:        +4/-0      nothing assets/stylesheets/style.css
  2:      +23/-11      nothing layout/book_index_template.html
  3:        +7/-7      nothing layout/chapter_template.html
  4:        +3/-3      nothing script/pdf.rb
  5:    unchanged      +121/-0 text/14_Interactive_Rebasing/0_ Interactive_Rebasing.markdown

现在我们可以看到前4个文件已经被暂存,但是最后一个没有。基本上,这是一个更加紧凑的查看状态的方式,实质上的信息与我们在命令行中运行‘git status‘是一致的:

$ git status

六、储藏

1.储藏

当你正在做一项复杂的工作时, 发现了一个和当前工作不相关但是又很讨厌的bug. 你这时想先修复bug再做手头的工作, 那么就可以用 git stash 来保存当前的工作状态, 等你修复完bug后,执行‘反储藏‘(unstash)操作就可以回到之前的工作里。

$ git stash save "work in progress for foo feature"

上面这条命令会保存你的本地修改到储藏(stash)中, 然后将你的工作目录和索引里的内容全部重置, 回到你当前所在分支的上次提交时的状态。 好了, 你现在就可以开始你的修复工作了。

$ git commit -a -m "blorpl: typofix"

当你修复完bug后, 你可以用git stash apply来回复到以前的工作状态。

$ git stash apply

2.储藏队列

你也可多次使用‘git stash‘命令, 每执行一次就会把针对当前修改的‘储藏’(stash)添加到储藏队列中. 用‘git stash list‘命令可以查看你保存的‘储藏‘(stashes):

$ git stash list

可以用类似‘git stash apply stash@{1}‘的命令来使用在队列中的任意一个‘储藏‘(stashes). ‘git stash clear‘则是用来清空这个队列。

七、Git树名

1.Git树名

不用40个字节长的SHA串来表示一个提交(commit)或是其它git对象,有很多种名字表示方法。在Git里,这些名字就叫‘树名‘(treeish)

2.Sha短名

如果你的一个提交(commit)的sha名字是 ‘980e3ccdaac54a0d4de358f3fe5d718027d96aae‘, git会把下面的串视为等价的:

980e3ccdaac54a0d4de358f3fe5d718027d96aae
980e3ccdaac54a0d4
980e3cc

只要你的‘sha短名’(Partial Sha)是不重复的(unique),它就不会和其它名字冲突(如果你使用了5个字节以上那是很难重复的),git也会把‘sha短名’(Partial Sha)自动补全。

3.分支, Remote 或 标签

你可以使用分支,remote或标签名来代替SHA串名, 它们只是指向某个对象的指针。假设你的master分支目前在提交(commit):‘980e3‘上, 现在把它推送(push)到origin上并把它命名为标签‘v1.0‘, 那么下面的串都会被git视为等价的:

980e3ccdaac54a0d4de358f3fe5d718027d96aae
origin/master
refs/remotes/origin/master
master
refs/heads/master
v1.0
refs/tags/v1.0

这意味着你执行下面的两条命令会有同样的输出:

$ git log master
$ git log refs/tags/v1.0

4.日期标识符

Git的引用日志(Ref Log)可以让你做一些‘相对‘查询操作

master@{yesterday}
master@{1 month ago}:

上面的第一条命令是:‘master分支的昨天状态(head)的缩写‘。注意: 即使在两个有相同master分支指向的仓库上执行这条命令, 但是如果这个两个仓库在不同机器上, 那么执行结果也很可能会不一样。

5.顺序标识符

这种格式用来表达某点前面的第N个提交(ref)。

master@{5}

上面的表达式代表着master前面的第5个提交(ref)。

6.多个父对象

这能告诉你某个提交的第N个直接父提交(parent)。这种格式在合并提交(merge commits)时特别有用, 这样就可以使提交对象(commit object)有多于一个直接父对象(direct parent)。

master^2

7.波浪号

波浪号用来标识一个提交对象(commit object)的第N级嫡(祖)父对象(Nth grandparent)。 例如:

master~2

就代表master所指向的提交对象的第一个父对象的第一个父对象(译者:你可以理解成是嫡系爷爷:))。 它和下面的这个表达式是等价的:

master^^

你也可以把这些‘标识符‘(spec)叠加起来, 下面这个3个表达式都是指向同一个提交(commit):

master^^^^^^
master~3^~2
master~6

8.树对象指针

如果大家对第一章Git对象模型还有印象的话, 就记得提交对象(commit object)是指向一个树对象(tree object)的. 假如你要得到一个提交对象(commit object)指向的树对象(tree object)的sha串名, 你就可以在‘树名‘的后面加上‘{tree}‘来得到它:

master^{tree}

9.二进制标识符

如果你要某个二进制对象(blob)的sha串名,你可以在‘树名‘(treeish)后添加二进制对象(blob)对应的文件路径来得到它。

master:/path/to/file

10.区间

最后, 你可以用".."来指两个提交(commit)之间的区间. 下面的命令会给出你在"7b593b5" 和"51bea1"之间除了"7b593b5外"的所有提交(commit)(注意:51bea1是最近的提交)。

7b593b5..51bea1

这会包括所有 从 7b593b开始的提交(commit). 译者注: 相当于 7b593b..HEAD

7b593b..

八、小结

本节讲解了git的中级知识,在添加索引时可以通过配置.gitignore文件来忽略文件,又讲解了git rebase、git stash和git树名。

九、练习

git中级技能