Korean CJKmainfont changed
[gitmagic.git] / zh_cn / history.txt
blobe3423293505daac4bdf11dbbd51b5069f6c4ffcf
1 == 关于历史 ==
3 Git分布式本性使得历史可以轻易编辑。但你若篡改过去,需要小心:只重写你独自拥有
4 的那部分。正如民族间会无休止的争论谁犯下了什么暴行一样,如果在另一个人的克隆
5 里,历史版本与你的不同,当你们的树互操作时,你们会遇到一致性方面的问题。
7 一些开发人员强烈地感到历史应该永远不变,不好的部分也不变,所有都不变。另一些觉
8 得代码树在向外发布之前,应该整得漂漂亮亮的。Git同时支持两者的观点。像克隆,分
9 支和合并一样,重写历史只是Git给你的另一强大功能,至于如何明智地使用它,那是你
10 的事了。
12 === 我认错 ===
14 刚提交,但你期望你输入的是一条不同的信息?那么键入:
16  $ git commit --amend
18 来改变上一条信息。意识到你还忘记了加一个文件?运行git add来加,然后运行上面的
19 命令。
21 希望在上次提交里包括多一点的改动?那么就做这些改动并运行:
23  $ git commit --amend -a
25 === 更复杂情况 ===
27 假设上面的问题再糟糕十倍,比如在漫长的时间里我们提交了一堆,但你不太喜欢他们的
28 组织方式,而且一些提交信息需要重写。那么键入:
30  $ git rebase -i HEAD~10
32 则最后的10个提交会出现在你喜爱的$EDITOR(即通过设置EDITOR环境变量决定使用哪个
33 文本编辑器)。一个例子:
35     pick 5c6eb73 Added repo.or.cz link
36     pick a311a64 Reordered analogies in "Work How You Want"
37     pick 100834f Added push target to Makefile
39 不像git log,列表中的提交是旧的在前,新的在后。在上面的列表中,5c6eb73是最老的
40 提交,100834是最新的提交。接下来可以在$EDITOR中:
42 - 通过删除行来移去提交。
43 - 通过对几行记录重新排序,来重组提交顺序。
44 - 替换 `pick` 命令:
45    * `edit` 标记一个提交需要修订。
46    * `reword` 改变日志信息。
47    * `squash` 将一个提交与其前一个合并。
48    * `fixup` 将一个提交与其前一个合并,并丢弃日志信息。
50 比如,将第二个提交的`pick`修改为`squash`:
51     pick 5c6eb73 Added repo.or.cz link
52     squash a311a64 Reordered analogies in "Work How You Want"
53     pick 100834f Added push target to Makefile
55 保存退出,则Git会把a311a64合并进5c6eb73。想象一下“挤压”动作,squash的作用就是
56 把一个提交“挤压”进前一个提交。
58 “挤压”的同时,Git合并了两个提交的日志信息供编辑。比较一下*fixup*命令,其直接
59 扔掉“挤压“后合并的日志信息。
61 如果你把一个提交标记为*edit*,Git会带你回到这个提交点,你可以修改当时的提交信息
62 ,甚至可以增加新的提交。修改完毕后执行:
64  $ git rebase --continue
66 直到遇到下一个*edit*标记点,Git会回放所有遇到的提交。
68 也可以放弃修改:
70  $ git rebase --abort
72 这样尽早提交,经常提交:你之后还可以用rebase来规整。
74 === 本地变更之后 ===
76 你正在一个活跃的项目上工作。随着时间推移,你做了几个本地提交,然后你使用合并
77 与官方版本同步。在你准备好提交到中心分支之前,这个循环会重复几次。
79 但现在你本地Git克隆掺杂了你的改动和官方改动。你更期望在变更列表里,你所有的变
80 更能够连续列出。
82 这就是上面提到的 *git rebase* 所做的工作。在很多情况下你可以使用 *--onto* 标
83 记以避免交互。
85 另外参见 *git help rebase* 以获取这个让人惊奇的命令更详细的例子。你可以拆分提
86 交。你甚至可以重新组织一棵树的分支。
88 要小心的是,rebase指令非常强悍,对于复杂的rebase,最好首先使用*git clone*备份
89 一下。
91 === 重写历史 ===
93 偶尔,你需要做一些代码控制——好比从正式的照片中去除一些人一样——你需要从历史记录
94 里面彻底的抹掉他们。例如,假设我们要发布一个项目,但由于一些原因,项目中的某
95 个文件不能公开。或许我把我的信用卡号记录在了一个文本文件里,而我又意外的把它
96 加入到了这个项目中。仅仅删除这个文件是不够的,因为从别的提交记录中还是可以访
97 问到这个文件。因此我们必须从所有的提交记录中彻底删除这个文件。
99  $ git filter-branch --tree-filter 'rm top/secret/file' HEAD
101 参见 *git help filter-branch* ,那里讨论了这个例子并给出一个更快的方法。一般
102 地, *filter-branch* 允许你使用一个单一命令来大范围地更改历史。
104 此后,+.git/refs/original+目录描述操作之前的状态。检查命令 *filter-branch* 的
105 确做了你想要做的,然后删除此目录,如果你想运行多次filter-branch命令。
107 最后,如果你想之后和修订过的版本交互的话,记得用你修订过的版本替换你的项目克隆。
109 === 制造历史 ===
111 [[makinghistory]]
112 想把一个项目迁移到Git吗?如果这个项目是在用比较有名气的系统,那可以使用一些其
113 他人已经写好的脚本,把整个项目历史记录导出来放到Git里。
115 否则,查一下 *git fast-import* ,这个命令会从一个特定格式的文本读入,从头来创
116 建Git历史记录。通常可以用这个命令很快写一个脚本运行一次,一次迁移整个项目。
118 作为一个例子,粘贴以下所列到临时文件,比如/tmp/history:
120 ----------------------------------
121 commit refs/heads/master
122 committer Alice <alice@example.com> Thu, 01 Jan 1970 00:00:00 +0000
123 data <<EOT
124 Initial commit.
127 M 100644 inline hello.c
128 data <<EOT
129 #include <stdio.h>
131 int main() {
132   printf("Hello, world!\n");
133   return 0;
138 commit refs/heads/master
139 committer Bob <bob@example.com> Tue, 14 Mar 2000 01:59:26 -0800
140 data <<EOT
141 Replace printf() with write().
144 M 100644 inline hello.c
145 data <<EOT
146 #include <unistd.h>
148 int main() {
149   write(1, "Hello, world!\n", 14);
150   return 0;
154 ----------------------------------
156 之后从这个临时文件创建一个Git仓库,键入:
158  $ mkdir project; cd project; git init
159  $ git fast-import --date-format=rfc2822 < /tmp/history
161 你可以从这个项目checkout出最新的版本,使用:
163  $ git checkout master .
165 命令*git fast-export* 可将任意仓库转换成 *git fast-import* 格式,你可以研究其输
166 出来写新的导出程序, 或以人类可以理解的格式转移仓库。的确,这些命令可以通过只接受文
167 本的渠道来发送仓库文件。
170 === 哪儿错了? ===
172 你刚刚发现程序里有一个功能出错了,而你十分确定几个月以前它运行的很正常。天啊!
173 这个臭虫是从哪里冒出来的?要是那时候能按照开发的内容进行过测试该多好啊。
175 现在说这个已经太晚了。然而,即使你过去经常提交变更,Git还是可以精确的找出问题所在:
177  $ git bisect start
178  $ git bisect bad HEAD
179  $ git bisect good 1b6d
181 Git从历史记录中检出一个中间的状态。在这个状态上测试功能,如果还是有问题:
183  $ git bisect bad
185 如果可以工作了,则把"bad"替换成"good"。Git会再次帮你找到一个以确定的好版本和
186 坏版本之间的状态,通过这种方式缩小范围。经过一系列的迭代,这种二分搜索会帮你
187 找到导致这个错误的那次提交。一旦完成了问题定位的调查,你可以返回到原始状态,
188 键入:
190  $ git bisect reset
192 不需要手工测试每一次改动,执行如下命令可以自动的完成上面的搜索:
194  $ git bisect run my_script
196 Git使用指定命令(通常是一个一次性的脚本)的返回值来决定一次改动是否是正确的:
197 命令退出时的代码0代表改动是正确的,125代表要跳过对这次改动的检查,1到127之间
198 的其他数值代表改动是错误的。返回负数将会中断整个bisect的检查。
200 你还能做更多的事情: 帮助文档解释了如何展示bisects, 检查或重放bisect的日志,并
201 可以通过排除对已知正确改动的检查,得到更好的搜索速度。
203 === 谁让事情变糟了? ===
205 和其他许多版本控制系统一样,Git也有一个"blame"命令:
207  $ git blame bug.c
209 这个命令可以标注出一个指定的文件里每一行内容的最后修改者,和最后修改时间。但
210 不像其他版本控制系统,Git的这个操作是在线下完成的,它只需要从本地磁盘读取信息。
212 === 个人经验 ===
214 在一个中心版本控制系统里,历史的更改是一个困难的操作,并且只有管理员才有权这
215 么做。没有网络,克隆,分支和合并都没法做。像一些基本的操作如浏览历史,或提交
216 变更也是如此。在一些系统里,用户使用网络连接仅仅是为了查看他们自己的变更,或
217 打开文件进行编辑。
219 中心系统排斥离线工作,也需要更昂贵的网络设施,特别是当开发人员增多的时候。最
220 重要的是,所有操作都一定程度变慢,一般在用户避免使用那些能不用则不用的高级命
221 令时。在极端的情况下,即使是最基本的命令也会变慢。当用户必须运行缓慢的命令的
222 时候,由于工作流被打断,生产力就会降低。
224 我有这方面的一手经验。Git是我使用的第一个版本控制系统。我很快学会适应了它,并
225 使用了它提供的许多功能。我简单地假设其他系统也是相似的:选择一个版本控制系统应
226 该和选择一个编辑器或浏览器没啥两样。
228 在我之后被迫使用中心系统的时候,我被震惊到了。我那有些脆弱的网络没给Git带来
229 大麻烦,但是当它需要像本地硬盘一样稳定工作的时候,它使开发变得困难重重。另
230 外,我发现我自己有选择地避免使用特定的命令,以避免踏雷,这极大地影响了我,使
231 我不能按照我喜欢的方式工作。
233 当我不得不运行一个速度缓慢的命令时,这种等待极大地破坏了我思绪的连续性。在等
234 待服务器通讯完成的时候,我选择做其他的事情以度过这段时光,比如查看邮件或写其
235 他的文档。当我返回我原先的工作场景的时候,这个命令早已结束,并且我还需要浪费
236 时间试图记起我之前正在做什么。人类并不擅长场景间的切换。
238 还有一个有意思的大众悲剧效应:预料到网络拥挤,为了减少将来的等待时间,每个人
239 将比以往消费更多的带宽在各种操作上。大家共同的努力结果加剧了拥挤,这等于是鼓
240 励个人下次消费更多带宽以避免更长的等待时间。