关于历史
Git 分散式本性使得历史可以轻易编辑。但你若篡改过去,需要小心:只重写你独自拥有 的那部分。正如民族间会无休止的争论谁犯下了什麽暴行一样,如果在另一个人的克隆 裡,历史版本与你的不同,当你们的树互操作时,你会遇到一致性方面的问题。
一些开发人员强烈地感觉历史应该永远不变,不好的部分也不变所有都不变。另一些觉 得代码树在向外发佈之前,应该整得漂漂亮亮的。Git 同时支持两者的观点。像克隆,分 支和合併一样,重写历史只是 Git 给你的另一强大功能,至于如何明智地使用它,那是你 的事了。
我认错
刚提交,但你期望你输入的是一条不同的信息?那麽键入:
$ git commit --amend
来改变上一条信息。意识到你还忘记了加一个档案?运行 git add 来加,然后运行上面的 命令。
希望在上次提交裡包括多一点的改动?那麽就做这些改动并运行:
$ git commit --amend -a
更複杂情况
假设前面的问题还要糟糕十倍。在漫长的时间裡我们提交了一堆。但你不太喜欢他们的 组织方式,而且一些提交信息需要重写。那麽键入:
$ git rebase -i HEAD~10
并且后 10 个提交会出现在你喜爱的$EDITOR。一个例子:
pick 5c6eb73 Added repo.or.cz link
pick a311a64 Reordered analogies in "Work How You Want"
pick 100834f Added push target to Makefile
之后:
- 通过删除行来移去提交。
- 通过为行重新排序行来重新排序提交。
- 替换
pick
使用:edit
标记一个提交需要修订。reword
改变日志信息。squash
将一个提交与其和前一个合併。fixup
将一个提交与其和前一个合併,并丢弃日志信息。
保存退出。如果你把一个提交标记为可编辑,那麽运行
$ git commit --amend
否则,运行:
$ git rebase --continue
这样儘早提交,经常提交:你之后还可以用 rebase 来规整。
本地变更之后
你正在一个活跃的项目上工作。随着时间推移,你做了几个本地提交,然后你使用合併 与官方版本同步。在你准备好提交到中心分支之前,这个循环会重複几次。
但现在你本地 Git 克隆掺杂了你的改动和官方改动。你更期望在变更列表裡,你所有的变 更能够连续。
这就是上面提到的 git rebase 所做的工作。在很多情况下你可以使用 --onto 标 记以避免交互。
另外参见 git help rebase 以获取这个让人惊奇的命令更详细的例子。你可以拆分提 交。你甚至可以重新组织一棵树的分支。
重写历史
偶尔,你需要做一些代码控制,好比从正式的照片中去除一些人一样,需要从历史记录 裡面彻底的抹掉他们。例如,假设我们要发佈一个项目,但由于一些原因,项目中的某 个档案不能公开。或许我把我的信用卡号记录在了一个文本档案裡,而我又意外的把它 加入到了这个项目中。仅仅删除这个档案是不够的,因为从别的提交记录中还是可以访 问到这个档案。因此我们必须从所有的提交记录中彻底删除这个档案。
$ git filter-branch --tree-filter 'rm top/secret/file' HEAD
参见 git help filter-branch ,那裡讨论了这个例子并给出一个更快的方法。一般 地, filter-branch 允许你使用一个单一命令来大范围地更改历史。
此后,+.git/refs/original+目录描述操作之前的状态。检查命令 filter-branch 的确做 了你想要做的,然后删除此目录,如果你想运行多次 filter-branch 命令。
最后,用你修订过的版本替换你的项目克隆,如果你想之后和它们交互的话。
制造历史
想把一个项目迁移到 Git 吗?如果这个项目是在用比较有名气的系统,那可以使用一些其 他人已经写好的脚本,把整个项目历史记录导出来放到 Git 裡。
否则,查一下 git fast-import ,这个命令会从一个特定格式的文本读入,从头来创 建 Git 历史记录。通常可以用这个命令很快写一个脚本运行一次,一次迁移整个项目。
作为一个例子,粘贴以下所列到临时档案,比如/tmp/history:
commit refs/heads/master
committer Alice <alice@example.com> Thu, 01 Jan 1970 00:00:00 +0000
data <<EOT
Initial commit.
EOT
M 100644 inline hello.c
data <<EOT
#include <stdio.h>
int main() {
printf("Hello, world!\n");
return 0;
}
EOT
commit refs/heads/master
committer Bob <bob@example.com> Tue, 14 Mar 2000 01:59:26 -0800
data <<EOT
Replace printf() with write().
EOT
M 100644 inline hello.c
data <<EOT
#include <unistd.h>
int main() {
write(1, "Hello, world!\n", 14);
return 0;
}
EOT
之后从这个临时档案创建一个 Git 仓库,键入:
$ mkdir project; cd project; git init
$ git fast-import --date-format=rfc2822 < /tmp/history
你可以从这个项目 checkout 出最新的版本,使用:
$ git checkout master .
命令 git fast-export 转换任意仓库到 git fast-import 格式,你可以研究其输 出来写导出程序, 也以可读格式传送仓库。的确,这些命令可以发送仓库文本档案 通过只接受文本的渠道。
哪儿错了?
你刚刚发现程序裡有一个功能出错了,而你十分确定几个月以前它运行的很正常。天啊! 这个臭虫是从哪裡冒出来的?要是那时候能按照开发的内容进行过测试该多好啊。
现在说这个已经太晚了。然而,即使你过去经常提交变更,Git 还是可以精确的找出问题所在:
$ git bisect start
$ git bisect bad HEAD
$ git bisect good 1b6d
Git 从历史记录中检出一个中间的状态。在这个状态上测试功能,如果还是有问题:
$ git bisect bad
如果可以工作了,则把"bad"替换成"good"。Git 会再次帮你找到一个以确定的好版本和 坏版本之间的状态,通过这种方式缩小范围。经过一系列的迭代,这种二分搜索会帮你 找到导致这个错误的那次提交。一旦完成了问题定位的调查,你可以返回到原始状态, 键入:
$ git bisect reset
不需要手工测试每一次改动,执行如下命令可以自动的完成上面的搜索:
$ git bisect run my_script
Git 使用指定命令(通常是一个一次性的脚本)的返回值来决定一次改动是否是正确的: 命令退出时的代码 0 代表改动是正确的,125 代表要跳过对这次改动的检查,1 到 127 之间 的其他数值代表改动是错误的。返回负数将会中断整个 bisect 的检查。
你还能做更多的事情: 帮助文档解释了如何展示 bisects, 检查或重放 bisect 的日志,并 可以通过排除对已知正确改动的检查,得到更好的搜索速度。
谁让事情变糟了?
和其他许多版本控制系统一样,Git 也有一个"blame"命令:
$ git blame bug.c
这个命令可以标注出一个指定的档案裡每一行内容的最后修改者,和最后修改时间。但 不像其他版本控制系统,Git 的这个操作是在綫下完成的,它只需要从本地磁碟读取信息。
个人经验
在一个中心版本控制系统裡,历史的更改是一个困难的操作,并且只有管理员才有权这 麽做。没有网络,克隆,分支和合併都没法做。像一些基本的操作如浏览历史,或提交 变更也是如此。在一些系统裡,用户使用网络连接仅仅是为了查看他们自己的变更,或 打开档案进行编辑。
中心繫统排斥离线工作,也需要更昂贵的网络设施,特别是当开发人员增多的时候。最 重要的是,所有操作都一定程度变慢,一般在用户避免使用那些能不用则不用的高级命 令时。在极端的情况下,即使是最基本的命令也会变慢。当用户必须运行缓慢的命令的 时候,由于工作流被打断,生产力降低。
我有这些的一手经验。Git 是我使用的第一个版本控制系统。我很快学会适应了它,用了 它提供的许多功能。我简单地假设其他系统也是相似的:选择一个版本控制系统应该和 选择一个编辑器或浏览器没啥两样。
在我之后被迫使用中心繫统的时候,我被震惊了。我那有些脆弱的网络没给 Git 带来大麻 烦,但是当它需要像本地硬碟一样稳定的时候,它使开发困难重重。另外,我发现我自 己有选择地避免特定的命令,以避免踏雷,这极大地影响了我,使我不能按照我喜欢的 方式工作。
当我不得不运行一个慢的命令的时候,这种等待极大地破坏了我思绪连续性。在等待服 务器通讯完成的时候,我选择做其他的事情以度过这段时光,比如查看邮件或写其他的 文档。当我返回我原先的工作场景的时候,这个命令早已结束,并且我还需要浪费时间 试图记起我之前正在做什麽。人类不擅长场景间的切换。
还有一个有意思的大众悲剧效应:预料到网络拥挤,为了减少将来的等待时间,每个人 将比以往消费更多的频宽在各种操作上。共同的努力加剧了拥挤,这等于是鼓励个人下 次消费更多频宽以避免更长时间的等待。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论