添加文件到暂存区
命令
git add -p
能够在不进入交互模式的情况下直接进入补丁模式。
补丁模式是 Git 中非常有用的模式,在补丁模式中 Git 会显示这些文件的当前内容与版本库的差异,然后可以据此决定是否添加这些修改到暂存区。
将文件从暂存区移除
命令
git reset HEAD
可以将所有文件从暂存区移除(也可以是
git reset
,因为
HEAD
是默认参数),
git reset -- /path/to/file
将单个文件从暂存区移除。
命令
git commit -a
会把已经纳入 Git 版本控制的文件提交,而不会添加尚未被跟踪的文件。
查看修改内容
git diff
默认会比较工作目录树和暂存区的区别,因此如果已经把文件加入了暂存区,那么
git diff
命令是无法看到任何 diff 信息的。这时可以使用
git diff --cached
命令来比较暂存区和版本库之间的区别。
文件的重命名与移动
命令
git mv <old name> <new name>
打包了
mv <old name> <new name>
、
git add <new name>
和
git rm <old name>
三个操作。
第五章 理解和使用分支
5.3 合并分支间的修改
直接合并 (straight merge)
压合合并 (squashed commits)
压合指的是 Git 将一条分支上的所有历史提交压合成一个提交,提交到另一条分支上。
要小心使用压合提交
,因为大多数情况下,每个提交都应该作为一个单独的条目存在于历史记录中。
如果某条分支上的所有提交都密切相关,应随后作为一个正题记录(在父分支上)时,适合作压合提交。
$ git merge --squash <branchName>
这条命令会将 branchName 分支上的全部提交压合成当前分支上的一个提交,但这个时候,这些改变还没有提交,需要再次输入
git commit
命令进行提交。
拣选合并 (cherry-picking)
$ git cherry-pick <commit SHA-1>
中间跳过了几个 commit ,可能会要求处理冲突。可以带上 -n 参数,用于拣选多个提交,进行合并操作,等全部拣选完毕后输入
git commit
命令提交。
5.4 冲突处理
一旦发生冲突后,就会在发生冲突的文件里发现
<<<<<<< <branchName1>:<fileName>
,
=======
和
>>>>>>> <branchName2>:<fileName>
这样的标识。
<<<<<<<
后面跟随的是当前分支 branchName1 中的代码。而
>>>>>>>
之前则是另一条分支 branchName2 上的代码。
可以使用
git mergetool
工具来解决复杂的冲突,不同的系统不同的环境会有不同的工具,Git 会尽可能的找到每一个合并工具。
也可以在设置里配置 merge.tool 的值
所有合并工具得到的结果应该都差不多,都是显示各冲突区域,供用户选取定夺。
解决了所有冲突后,Git 自动暂存修改,等待提交。
5.6 分支重命名
命令
git branch -m <old name> <new name>
可以用来重命名分支。
第六章 查询 Git 历史记录
6.2 指定查找范围
在
git log
后面可以跟上以下参数用于指定查找范围:
-<数字>
显示最近若干条提交
--since
最近的时间内的提交
--before
多少时间前的最后一个提交
<commit1 SHA-1>..<commit2 SHA-1>
检索从 commit1 到 commit2 之间的提交(不包括 commit1)
-since
和
--before
参数接受大多数英文格式的日期。Git 工具本身能够识别诸如 "24 hours"、"1 minute"、"2008-10.01" 这样格式的日期,哪怕日期中间既有连字符又有句点。
第三种检索方式可以省略
<commit2 SHA-1>
,这时 commit2 就会被 HEAD 替代,也可以用 tag 名替代。
另一种指定版本的常见方法是指出它与另一版本的关系,此时有两种操作符可以使用:
^ 相当于回溯一个版本。
2222222^
相当于 2222222 的前一个版本;
2222222^^
代表 2222222 的前第二个版本(也可以写作
2222222^2
,
^
其实是
^1
的别名)
~N 回溯 N 个版本。
2222222^
相当于
222222~1
可以混合使用两种操作符。
6.3 查看版本之间的差异
在
git log
命令中加入
-p
参数,能够显示版本间的内容差异。
在
git diff
中指定版本范围和
git log
一样,唯一差别是
git diff
输出的是最老版本和最新版本之间的差异。
在
git diff
中使用标签作为参数,是一种获取发布版本之间代码量统计的好方法,通过加入一个
--stat
参数,可以计算出修改和删除的代码行数。如果只传入一个参数,Git 会默认把工作目录树作为参数。
6.4 查明该向谁问责
命令
git blame
用于查看特定代码块的历史信息。通过传入
-L
参数可以缩小显示的范围,范围用
<start>,<end>
表示。范围的值可以是数字,可以是 +N,-N 的形式,也可以是正则表达式。同时,
blame
命令也可以使用
指定查找范围
中的操作符:
$ git blame -L "/<\/body>/",-2 2222222^ -- hello.html
命令中的
--
符号是在通知 Git 查询指定文件。
6.5 跟踪内容
命令
git blame
加入参数
-M
可以检测同一个文件内移动或复制的代码行,用同一个提交名称标识。加入参数
-C -C
可以检测文件之间的复制:
Git 会输出初始的提交名称和初始的文件名。给
git log
传入参数
-C -C
也能显示复制信息,如果传入
-p
参数,
git log
还会显示代码的具体变动。
6.6 撤销修改
在
git commit
命令中传入
-C <commit SHA-1>
参数,Git 会复用那个提交的 commit,如果传入的是小写的 c ,就会打开预设的编辑器,以便在已有的提交留言基础上编辑修改。
增补提交只能针对最后一个提交,如果想更正几个提交之前的某个错误,就需要用到
git revert
命令。
命令
git revert <commit SHA-1>
通过在版本库中创建一个“反向的”新提交来抵消原来提交的改动。在命令中加入
-n
参数可以制止 Git 立即提交反转结果,来进行多次反转。
通常,命令会启动默认编辑器并把默认信息加入其中,可以添加参数
--no-edit
直接使用默认的 commit 信息。
命令
git reset <commit SHA-1>
的默认参数是 HEAD ,可以使用
指定查找范围
中的操作符。如果传入
--soft
参数,Git 会暂存所有因果复位带来的差异,但不提交。用户可以修改这些内容再提交。
如果传入
--hard
参数,Git 会从版本库和工作目录同时删除提交,不可恢复。
6.7 重新改写历史记录
命令
git rebase -i
能够以人机交互的方式改写历史记录。具体内容可以看 《Pro Git》 的 第六章第四节
《重写历史》
和 《Git 魔法》的第五章
《关于历史》
。
第七章 与远程版本库合作
7.5 添加新的远程版本库
在本地版本库中,远程版本库的别名默认是 origin 。它是克隆远程版本库时自动生成的。
命令
git remote add <别名> <版本库全称>
用于为远程版本库添加别名。
命令
git remote rm
用于删除别名。
第八章 管理本地版本库
8.1 使用标签标记里程碑
这时会检出直到 的历史到
(no branch)
上。
$ git [branch|checkout -b] <branchName> <tagName>
这时就会检出到 tagName 为止的代码到 branchName 分支
第九章 高阶功能
9.1 压缩版本库
Git 版本库里存储了
所有的东西
,由此带来的问题是偶尔会留下一些没有用的数据。比如
--amend
时 Git 也会记住原来的版本;或者用
git branch -D
删除一个试验分支时,Git 依旧会保留该分支上的内容。
命令
git gc
整理版本库以优化 Git 内部存储历史记录,一个月清理一次,或者大约100次提交清理一次即可。它不改变历史记录,只改变历史记录的存储方式。
在命令中带上
--aggressive
参数可使版本库得到进一步的优化。这会增加时间,但值得尝试。
Git 在增量存储单元 (delta) 中存储修改。一般情况下,
git gc
命令运行时会压缩这些增量存储单元,但是不会重新计算它们。如果使用
--aggressive
参数,则 Git 会重新计算它们。
9.2 导出版本库
$ git archive --format tar \
--prefix=mysite-1.0/ 1.0 \
| gzip > mysite-10.tar.gz
--format
指明要产生 tar 格式的输出;
--prefix
指明包中所有东西都放到 mysite-1.0/ 目录下;
1.0
指明需要归档的标签名称。
$ git archive --format zip \
--prefix=mysite-1.0/ 1.0 \
> mysite-10.zip
来产生一个 zip 文件。
9.3 分支变基
同步分支间的提交记录
要同步分支之间的历史,除了使用 Git 提供的合并分支功能,还可以使用变基操作。
$ git rebase <branchName>
变基的意思是“改变分支的基底”,把一条分支上的修改在另一条分支的末梢重现。你可以把这种方式看作是基于一个新的基点,重演分支上发生过的改动。
除了同步历史以外,
git rebase
还拥有修改历史的能力,具体可以参考
6.7 重新改写历史记录
。
命令
git rebase
命令还提供了一个
--onto
参数,用于将分支3与分支2的提交记录差值变基到分支1上。
例如,有三条分支,主分支 master ,从 master 拉出来的 contacts 和从 contacts 拉出来的 search 分支。
当完成 search 分支上的代码时,发现不需要任何在 contects 分支上完成的改动,搜索功能即可运行。
这时就可以输入以下命令,将 search 分支变基到 master 。
$ git rebase --onto master contacts search
这个命令是将 search 分支从 contacts 分支剥离,移动到主分支上,如果要合并 search 分支上的内容到 master 上,不需要 contacts 上的任何东西,可以使用此方法。当然,search 分支要完全独立于 contacts 分支,尽量避免变基到 master 时出现合并冲突。
还可以使用
6.2 指定查找范围
中提到的
提交范围参数
,来做一些其他有趣的事情,比如抹消倒数第二个提交。
$ git rebase --onto HEAD^^ HEAD^ HEAD
9.4 重现隐藏的历史
重现是指记录分支末梢变化的情况,并予以复现。第五章
理解和使用分支
中介绍过,分支本质上只是指向最新提交的指针。重现功能可以记录和跟踪所有这些指针的变化。通过使用它,你能找到想要的提交,并据此恢复分支。
命令
git reflog
的输出结果是按时间倒序排列的,类似
git log
。它的职能只是列出重现记录,而想要恢复这个记录的步骤可以参考
8.1 使用标签标记里程碑
。
注意,在
9.1 压缩版本库
中提到的
git gc
会删除比较旧的重现记录,阀值通常是30天,可以通过修改配置 gc.reflogExpireUnreachable 的值来改变有效期。
同时,除非修改 gc.reflogExpire 的值,重现记录通常会在 90 天后过期。
9.5 二分查找
命令
git bisect
基于一个已知的坏提交和一个已知的好提交,逐步排查版本库中的历史记录。它带领你在版本库中的历史上标出哪些是好提交哪些是坏提交,直到最后找出那个引进 Bug 的提交。
首先运行
git bisect start <某个提交或标签>
来开始二分查找,然后调用
git bisect bad|good <某个提交或标签>
进行标记。如果没有输入提交或标签,则默认为 HEAD 。
当找到 Bug 后,需要回到 HEAD 进行修补,这时候使用命令
git bisect reset
即可。
如果希望直观的理解代码的演进历史,可以使用
git bisect visualize
命令来可视化记录。
如果喜欢文本输出,可以使用
git bisect log
存储成为一个文件;删除该文件中刚才错误的标识操作及其后的所有记录;然后将该文件作为命令
git bisect replay <某一文件>
的参数,让 Git 重新执行,直到刚才错误标识之前一步。
命令
git bisect
还可以适时自动运行自动测试套件。为了使用这一功能,需要构造一个可以在命令行运行的脚本,且该脚本在测试通过是返回 0 ,测试失败时返回一个正数(这个正数通常是 1),如果要跳过一条提交并让 Git 二分查找自动移向下一条提交,则应返回 125 。
命令
git bisect run
可以用来自动运行这个脚本。例如:
$ git bisect start HEAD 1.0
$ git bisect run /work/run-test
推荐将测试文件放在仓库之外,以确保 Git 不会改变
git bisect
正在运行的脚本文件。
附录A Git 命令快速参考
1 安装和初始化
全局的配置
$ git config --global <configName> <configValue>
局部的配置
只作用于特定版本库的配置
$ git config <configName> <configValue>
2 日常操作
暂存已纳入 Git 版本控制之下的文件的修改
$ git add -u [<some path> [<some path>]]
清除工作目录树的修改
$ git checkout HEAD <some file> [<some file>]
取消已暂存但尚未提交的修改的暂存标识
$ git reset HEAD <some file> [<some file>]
使用上次的提交注释
如果不想修改,直接使用的话,就将
-c
参数变为
-C
参数。
你可以从版本库的任何一个版本开始创建新分支。起始点可以用 分支名称、提交名称或者标签名称。
$ git branch <new branch> <start branch>
加入
-f
参数可以创建同名新分支,覆盖已有分支。
加入
-m
用于移动或重命名分支,
-M
用于重命名时覆盖已有分支。
附注(也就是书中没有的)
有关 hook 的内容,可以看 《Pro Git》 的
7.3 自定义 Git - Git挂钩
。
diff 二进制文件
See Also
7.2 自定义 Git - Git属性#二进制文件
在文件签出/提交前根据关键字自动插入文本
See Also
7.2 自定义 Git - Git属性#关键字扩展