Skip to content

Files

326 lines (212 loc) · 15.3 KB

版本控制之道——使用Git.md

File metadata and controls

326 lines (212 loc) · 15.3 KB

版本控制之道——使用 Git

第三章 创建第一个项目

3.5 处理发布

给指定分支添加 tag

$ git tag <tagName> <branchName>

这样会在分支 branchName 的末梢打上 tagName 的 tag

变基命令

See Also 9.3 分支变基

给 tag 打补丁

See Also 8.1 使用标签标记里程碑

为代码发布创建归档文件

See Also 9.2 导出版本库

添加与提交:Git 基础

添加文件到暂存区

命令 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)

$ git merge <branchName>

压合合并 (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 blame -C -C cp.txt

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 使用标签标记里程碑

$ git checkout <tagName>

这时会检出直到 的历史到 (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>]

使用上次的提交注释

$ git commit -c

如果不想修改,直接使用的话,就将 -c 参数变为 -C 参数。

3 分支

你可以从版本库的任何一个版本开始创建新分支。起始点可以用 分支名称、提交名称或者标签名称。

$ git branch <new branch> <start branch>

加入 -f 参数可以创建同名新分支,覆盖已有分支。

加入 -m 用于移动或重命名分支, -M 用于重命名时覆盖已有分支。

附注(也就是书中没有的)

hook

有关 hook 的内容,可以看 《Pro Git》 的 7.3 自定义 Git - Git挂钩

diff 二进制文件

See Also 7.2 自定义 Git - Git属性#二进制文件

在文件签出/提交前根据关键字自动插入文本

See Also 7.2 自定义 Git - Git属性#关键字扩展