Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge https://github.com/nanjj/gitmagic

  • Loading branch information...
commit ab47155a33e17d5f214043450d8c26d28e6f2e78 2 parents 3177fe9 + 8ab61aa
@blynn blynn authored
View
3  en/preface.txt
@@ -12,7 +12,8 @@ Rather than go into details, we provide rough instructions for particular effect
.Translations
- - http://docs.google.com/View?id=dfwthj68_675gz3bw8kj[Chinese (Simplified)]: by JunJie, Meng and JiangWei.
+ - link:/~/blynn/gitmagic/intl/zh_cn/[Simplified Chinese]: by JunJie, Meng and JiangWei.
+ - link:/~/blynn/gitmagic/intl/zh_tw/[Traditional Chinese]: converted with command *cconv -f UTF8-CN -t UTF8-TW*.
- link:/~blynn/gitmagic/intl/fr/[French]: by Alexandre Garel, Paul Gaborit, and Nicolas Deram. Also hosted at http://tutoriels.itaapy.com/[itaapy].
- link:/~blynn/gitmagic/intl/de/[German]: by Benjamin Bellee and Armin Stebich; also http://gitmagic.lordofbikes.de/[hosted on Armin's website].
- http://www.slideshare.net/slide_user/magia-git[Portuguese]: by Leonardo Siqueira Rodrigues [http://www.slideshare.net/slide_user/magia-git-verso-odt[ODT version]].
View
224 zh_cn/basic.txt
@@ -0,0 +1,224 @@
+== 基本技巧 ==
+
+与其一头扎进Git命令的海洋中,不如来点基本的例子试试手。它们简单而且实用。实际
+上,在开始使用Git的头几个月,我所用的从来没超出本章介绍的内容。
+
+=== 保存状态 ===
+
+要不来点猛的?在做之前,先为当前目录所有文件做个快照,使用:
+
+ $ git init
+ $ git add .
+ $ git commit -m "My first backup"
+
+现在如果你的编辑乱了套,恢复之前的版本:
+
+ $ git reset --hard
+
+再次保存状态:
+
+ $ git commit -a -m "Another backup"
+
+=== 添加、删除、重命名 ===
+
+以上命令将只跟踪你第一次运行 *git add* 命令时就已经存在的文件。如果要添加新文
+件或子目录,你需要告诉Git:
+
+ $ git add readme.txt Documentation
+
+类似,如果你想让Git忘记某些文件:
+
+ $ git rm kludge.h obsolete.c
+ $ git rm -r incriminating/evidence/
+
+这些文件如果还没删除,Git删除它们。
+
+重命名文件和先删除旧文件,再添加新文件的一样。也有一个快捷方式 *git mv* ,和
+*mv* 命令的用法一样。例如:
+
+ $ git mv bug.c feature.c
+
+=== 进阶撤销/重做 ===
+
+有时候你只想把某个时间点之后的所有改动都回滚掉,因为这些的改动是不正确的。那
+么:
+
+ $ git log
+
+来显示最近提交列表,以及他们的SHA1哈希值:
+
+----------------------------------
+commit 766f9881690d240ba334153047649b8b8f11c664
+Author: Bob <bob@example.com>
+Date: Tue Mar 14 01:59:26 2000 -0800
+
+ Replace printf() with write().
+
+commit 82f5ea346a2e651544956a8653c0f58dc151275c
+Author: Alice <alice@example.com>
+Date: Thu Jan 1 00:00:00 1970 +0000
+
+ Initial commit.
+----------------------------------
+
+哈希值的前几个字符足够确定一个提交;也可以拷贝粘贴完整的哈希值,键入:
+
+ $ git reset --hard 766f
+
+来恢复到一个指定的提交状态,并从记录里永久抹掉所有比该记录新一些的提交。
+
+另一些时候你想简单地跳到一个旧状态。这种情况,键入:
+
+ $ git checkout 82f5
+
+这个操作将把你带回过去,同时也保留较新提交。然而,像科幻电影里时光旅行一样,
+如果你这时编辑并提交的话,你将身处另一个现实里,因为你的动作与开始时相比是不
+同的。
+
+这另一个现实叫作“分支”(branch),之后 <<branch,我们会对这点多讨论一些>>。
+至于现在,只要记住:
+
+ $ git checkout master
+
+会把你带到当下来就可以了。另外,为避免Git的抱怨,应该在每次运行checkout之前提
+交(commit)或重置(reset)你的改动。
+
+还以电脑游戏作为类比:
+
+- *`git reset --hard`*: 加载一个旧记录并删除所有比之新的记录。
+
+- *`git checkout`*: 加载一个旧记录,但如果你在这个记录上玩,游戏状态将偏离第
+ 一轮的较新状态。你现在打的所有游戏记录会在你刚进入的、代表另一个真实的分支
+ 里。<<branch,我们稍后论述>>。
+
+你可以选择只恢复特定文件和目录,通过将其加在命令之后:
+
+ $ git checkout 82f5 some.file another.file
+
+小心,这种形式的 *checkout* 会不声不响地覆盖文件。为阻止意外发生,在运行任何
+checkout命令之前做提交,尤其在初学Git的时候。通常,任何时候你觉得对运行某个命
+令不放心,无论Git命令还是不是Git命令,就先运行一下 *git commit -a* 。
+
+不喜欢拷贝站题哈希值?那就用:
+
+ $ git checkout :/"My first b"
+
+来跳到以特定字符串开头的提交。你也可以回到倒数第五个保存状态:
+
+ $ git checkout master~5
+
+=== 撤销 ===
+
+在法庭上,事件可以从法庭记录里敲出来。同样,你可以检出特定提交以撤销。
+
+ $ git commit -a
+ $ git revert 1b6d
+
+讲撤销给定哈希值的提交。本撤销被记录为一个新的提交,你可以通过运行 *git log*
+来确认这一点。
+
+=== 变更日志生成 ===
+
+一些项目要求生成变更日志http://en.wikipedia.org/wiki/Changelog[changelog]. 生
+成一个,通过键入:
+
+ $ git log > ChangeLog
+
+=== 下载文件 ===
+
+得到一个由Git管理的项目的拷贝,通过键入:
+
+ $ git clone git://server/path/to/files
+
+例如,得到我用来创建该站的所有文件:
+
+ $ git clone git://git.or.cz/gitmagic.git
+
+我们很快会对 *clone* 命令谈的很多。
+
+=== 到最新 ===
+
+如果你已经使用 *git clone* 命令得到了一个项目的一份拷贝,你可以更新到最新版,
+通过:
+
+ $ git pull
+
+
+=== 快速发布 ===
+
+假设你写了一个脚本,想和他人分享。你可以只告诉他们从你的计算机下载,但如果此
+时你正在改进你的脚本,或加入试验性质的改动,他们下载了你的脚本,他们可能由此
+陷入困境。当然,这就是发布周期存在的原因。开发人员可能频繁进行项目修改,但他
+们只在他们觉得代码可以见人的时候才择时发布。
+
+用Git来完成这项,需要进入你的脚本所在目录:
+
+ $ git init
+ $ git add .
+ $ git commit -m "First release"
+
+然后告诉你的用户去运行:
+
+ $ git clone your.computer:/path/to/script
+
+来下载你的脚本。这要假定他们有ssh访问权限。如果没有,需要运行 *git daemon* 并
+告诉你的用户去运行:
+
+ $ git clone git://your.computer/path/to/script
+
+从现在开始,每次你的脚本准备好发布时,就运行:
+
+ $ git commit -a -m "Next release"
+
+并且你的用户可以通过进入包含你脚本的目录,并键入下列命令,来更新他们的版本:
+
+ $ git pull
+
+你的用户永远也不会取到你不想让他们看到的脚本版本。显然这个技巧对所有的东西都
+是可以,不仅是对脚本。
+
+
+=== 我们已经做了什么? ===
+
+找出自从上次提交之后你已经做了什么改变:
+
+ $ git diff
+
+或者自昨天的改变:
+
+ $ git diff "@{yesterday}"
+
+或者一个特定版本与倒数第二个变更之间:
+
+ $ git diff 1b6d "master~2"
+
+输出结果都是补丁格式,可以用 *git apply* 来把补丁打上。也可以试一下:
+
+ $ git whatchanged --since="2 weeks ago"
+
+我也经常用http://sourceforge.net/projects/qgit[qgit] 浏览历史, 因为他的图形界
+面很养眼,或者 http://jonas.nitro.dk/tig/[tig] ,一个文本界面的东西,很慢的网
+络状况下也工作的很好。也可以安装web 服务器,运行 *git instaweb* ,就可以用任
+何浏览器浏览了。
+
+=== 练习 ===
+
+比方A,B,C,D是四个连续的提交,其中B与A一样,除了一些文件删除了。我们想把这
+些删除的文件加回D。我们如何做到这个呢?
+
+至少有三个解决方案。假设我们在D:
+
+ 1. A与B的差别是那些删除的文件。我们可以创建一个补丁代表这些差别,然后吧补丁
+ 打上:
+
+ $ git diff B A | git apply
+
+ 2. 既然这些文件存在A,我们可以把它们拿出来:
+
+ $ git checkout A foo.c bar.h
+
+ 3. 我们可以把从A到B的变化视为可撤销的变更:
+
+ $ git revert B
+
+哪个选择最好?这取决于你的喜好。利用Git满足自己需求是容易,经常还有多个方法。
View
236 zh_cn/branch.txt
@@ -0,0 +1,236 @@
+== 分支巫术 ==
+
+即时分支合并是Git最给力的杀手锏。
+
+*问题* :外部因素要求必须切换场景。在发布版本中突然蹦出个严重缺陷。某个特性完
+成的截至日期就要来临。在项目关键部分可以提供帮助的一个开发正打算离职。所有情
+况逼迫你停下所有手头工作,全力扑到到这个完全不同的任务上。
+
+打断思维的连续性会使你的生产力大大降低,并且切换上下文也更麻烦,更大的损失。
+使用中心版本控制我们必须从中心服务器下载一个新的工作拷贝。分布式系统的情况就
+好多了,因为我们能够在本地克隆所需要的版本。
+
+但是克隆仍然需要拷贝整个工作目录,还有直到给定点的整个历史记录。尽管Git使用文
+件共享和硬链接减少了花费,项目文件自身还是必须在新的工作目录里重建。
+
+*方案* :Git有一个更好的工具对付这种情况,比克隆快多了而且节省空间: *git
+ branch* 。
+
+使用这个魔咒,目录里的文件突然从一个版本变到另一个。除了只是在历史记录里上跳
+下窜外,这个转换还可以做更多。你的文件可以从上一个发布版变到实验版本到当前开
+发版本到你朋友的版本等等。
+
+=== 老板键 ===
+
+曾经玩过那样的游戏吗?按一个键(“老板键”),屏幕立即显示一个电子表格或别的?
+那么如果老板走进办公室,而你正在玩游戏,就可以快速将游戏藏起来。
+
+在某个目录:
+
+ $ echo "I'm smarter than my boss" > myfile.txt
+ $ git init
+ $ git add .
+ $ git commit -m "Initial commit"
+
+我们已经创建了一个Git仓库,该仓库记录一个包含特定信息的文件。现在我们键入:
+
+ $ git checkout -b boss # 之后似乎没啥变化
+ $ echo "My boss is smarter than me" > myfile.txt
+ $ git commit -a -m "Another commit"
+
+看起来我们刚刚只是覆盖了原来的文件并提交了它。但这是个错觉。键入:
+
+ $ git checkout master # 切到文件的原先版本
+
+嘿真快!这个文件就恢复了。并且如果老板决定窥视这个目录,键入:
+
+ $ git checkout boss # 切到适合老板看的版本
+
+你可以在两个版本之间相切多少次就切多少次,而且每个版本都可以独立提交。
+
+=== 肮脏的工作 ===
+
+[[branch]]
+
+比如你正在开发某个特性,并且由于某种原因,你需要回退三个版本,临时加进几行打
+印语句来,来看看一些东西是如何工作的。那么:
+
+ $ git commit -a
+ $ git checkout HEAD~3
+
+现在你可以到处加丑陋的临时代码。你甚至可以提交这些改动。当你做完的时候,
+
+ $ git checkout master
+
+来返回到你原来的工作。看,所有未提交变更都结转了。
+
+如果你后来想保存临时变更怎么办?简单:
+
+ $ git checkout -b dirty
+
+只要在切换到主分支之前提交就可以了。无论你什么时候想回到脏的变更,只需键入:
+
+ $ git checkout dirty
+
+我们在前面章节讨论加载旧状态的时候,曾经接触过这个命令。最终我们把故事说全:
+文件改变成请求的状态,但我们必须离开主分支。从现在开始的任何提交都会将你的文
+件提交到另一条不同的路,这个路可以之后命名。
+
+换一个说法,在checkout一个旧状态之后,Git自动把你放到一个新的,未命名的分支,
+这个分支可以使用 *git checkout -b* 来命名和保存。
+
+=== 快速修订 ===
+
+你正在做某件事的当间,被告知先停所有的事情,去修理一个新近发现的臭虫,这个臭
+虫在提交 `1b6d...`:
+
+ $ git commit -a
+ $ git checkout -b fixes 1b6d
+
+那么一旦你修正了这个臭虫:
+
+ $ git commit -a -m "Bug fixed"
+ $ git checkout master
+
+并可以继续你原来的任务。你甚至可以“合并”到最新修订:
+
+ $ git merge fixes
+
+=== 合并 ===
+
+一些版本控制系统,创建分支很容易,但把分支合并回来很难。使用Git,合并简直是家
+常便饭,以至于甚至你可能对其发生没有察觉。
+
+我们很久之前就遇到合并了。 *pull* 命令取出提交并合并它们到你的当前分支。如果
+你没有本地变更,那这个合并就是一个“快进”,相当于中心式版本控制系统里的一个
+弱化的获取最新版本操作。但如有本地变更,Git将自动合并,并报告任何冲突。
+
+通常,一个提交只有一个“父提交”,也叫前一个提交。合并分支到一起产生一个至少
+有两个父的提交。这就引出了问题: `HEAD~10` 真正指哪个提交?一个提交可能有多个
+父,那我们跟哪个呢?
+
+原来这个表示每次选择第一个父。这是可取的,因为在合并时候当前分支成了第一个父;
+多数情况下我们只关注我们在当前分支都改了什么,而不是从其他分支合并来的变更。
+
+你可以用插入符号来特别指定父。比如,显示来自第二个父的日志:
+
+ $ git log HEAD^2
+
+你可以忽略数字以指代第一个父。比如,显示与第一个父的差别:
+
+ $ git diff HEAD^
+
+你可以结合其他类型使用这个记号。比如:
+
+ $ git checkout 1b6d^^2~10 -b ancient
+
+开始一个新分支 ``ancient'' ,表示第一个父的第二个父的倒数第十次提交的状态。
+
+=== 不间断工作流 ===
+
+经常在硬件项目里,计划的第二步必须等第一步完成才能开始。待修的汽车傻等在车库
+里,直到特定的零件从工厂运来。一个原型在其可以构建之前,可能苦等芯片成型。
+
+软件项目可能也类似。新功能的第二部分不得不等待,直到第一部分发布并通过测试。
+一些项目要求你的代码需要审批才能接受,因此你可能需要等待第一部分得到批准,才
+能开始第二部分。
+
+多亏了无痛分支合并,我们可以不必遵循这些规则,在第一部分正式准备好前开始第二
+部分的工作。假设你已经将第一部分提交并发去审批,比如说你现在在主分支。那么分
+岔:
+
+ $ git checkout -b part2
+
+接下来,做第二部分,随时可以提交变更。只要是人就可能犯错误,经常你将回到第一
+部分在修修补补。如果你非常幸运,或者超级棒,你可能不必做这几行:
+
+ $ git checkout master # 回到第一部分
+ $ 修复问题
+ $ git commit -a # 提交变更
+ $ git checkout part2 # 回到第二部分
+ $ git merge master # 合并这些改动
+
+最终,第一部分获得批准:
+
+ $ git checkout master # 回到第一部分
+ $ submit files # 对世界发布
+ $ git merge part2 # 合并第二部分
+ $ git branch -d part2 # 删除分支“part2”
+
+现在你再次处在主分支,第二部分的代码也在工作目录。
+
+很容易扩展这个技巧,应用到任意数目的部分。它也很容易追溯分支:假如你很晚才意
+识到你本应在7次提交前就创建分支。那么键入:
+
+ $ git branch -m master part2 # 重命名“master”分支为“part2”。
+ $ git branch master HEAD~7 # 以七次前提交建一个新的“master”。
+
+分支 `master` 只有第一部分内容,其他内容在分支 `part2` 。 我们现在后一个分支;
+我们创建了 `master` 分支还没有切换过去,因为我们想继续工作在 `part2` 。这是不
+寻常的。直到现在,我们已经在创建之后切换到分支,如:
+
+ $ git checkout HEAD~7 -b master # 创建分支,并切换过去。
+
+=== 重组杂乱 ===
+
+或许你喜欢在同一个分支下完成工作的方方面面。你想为自己保留工作进度并希望其他
+人只能看到你仔细整理过后的提交。开启一对分支:
+
+ $ git branch sanitized # 为干净提交创建分支
+ $ git checkout -b medley # 创建并切换分支以进去工作
+
+接下来,做任何事情:修臭虫,加特性,加临时代码,诸如此类,经常按这种方式提交。
+然后:
+
+ $ git checkout sanitized
+ $ git cherry-pick medley^^
+
+应用分支 ``medley'' 的祖父提交到分支 ``sanitized'' 。通过合适的挑选(像选樱桃
+那样)你可以构建一个只包含成熟代码的分支,而且相关的提交也组织在一起。
+
+=== 管理分支 ===
+
+列出所有分支:
+
+ $ git branch
+
+默认你从叫 ``master'' 的分支开始。一些人主张别碰“master”分支,而是创建你自
+己版本的新分支。
+
+选项 *-d* 和 *-m* 允许你来删除和移动(重命名)分支。参见 *git help branch* 。
+
+分支``master'' 是一个有用的惯例。其他人可能假定你的仓库有一个叫这个名字的分
+支,并且该分支包含你项目的官方版本。尽管你可以重命名或抹杀 ``master'' 分支,
+你最好还是尊重这个约定。
+
+=== 临时分支 ===
+
+很快你会发现你经常会因为一些相似的原因创建短期的分支:每个其它分支只是为了保
+存当前状态,那样你就可以直接跳到较老状态以修复高优先级的臭虫之类。
+
+可以和电视的换台做类比,临时切到别的频道,来看看其它台那正放什么。但并不是简
+单地按几个按钮,你不得不创建,检出,合并,以及删除临时分支。幸运的是,Git已经
+有了和电视机遥控器一样方便的快捷方式:
+
+ $ git stash
+
+这个命令保存当前状态到一个临时的地方(一个隐藏的地方)并且恢复之前状态。你的
+工作目录看起来和你开始编辑之前一样,并且你可以修复臭虫,引入之前变更等。当你
+想回到隐藏状态的时候,键入:
+
+ $ git stash apply # 你可能需要解决一些冲突
+
+你可以有多个隐藏,并用不同的方式来操作他们。参见 *git help slash* 。也许你已
+经猜到,Git维护在这个场景之后的分支以执行魔法技巧.
+
+=== 按你希望的方式工作 ===
+
+你可能犹疑于分支是否值得一试。毕竟,克隆也几乎一样快,并且你可以用 *cd* 来在
+彼此之间切换,而不是用Git深奥的命令。
+
+考虑一下浏览器。为什么同时支持多标签和多窗口?因为允许两者同时接纳纳了多种风
+格的用户。一些用户喜欢只保持一个打开的窗口,然后用标签浏览多个网页。一些可能
+坚持另一个极端:任何地方都没有标签的多窗口。一些喜好处在两者之间。
+
+分支类似你工作目录的标签,克隆类似打开的浏览器新窗口。这些是本地操作很快,那
+为什么不试着找出最适合你的组合呢?Git让你按你确实所希望的那样工作。
View
228 zh_cn/clone.txt
@@ -0,0 +1,228 @@
+== 克隆周边 ==
+
+在较老一代的版本控制系统里,checkout是获取文件的标准操作。你将获得一组特定保
+存状态的文件。
+
+在Git和其他分布式版本控制系统里,克隆是标准的操作。通过创建整个仓库的克隆来
+获得文件。或者说,你实际上把整个中心服务器做了个镜像。凡是主仓库上能做的事,
+你都能做。
+
+=== 计算机间同步 ===
+
+我可以忍受制作tar包或利用rsync来作备份和基本同步。但我有时在我笔记本上编辑,
+其他时间在台式机上,而且这俩之间也许并不交互。
+
+在一个机器上初始化一个Git仓库并提交你的文件。然后转到另一台机器上:
+
+ $ git clone other.computer:/path/to/files
+
+以创建这些文件和Git仓库的第二个拷贝。从现在开始,
+
+ $ git commit -a
+ $ git pull other.computer:/path/to/files HEAD
+
+将把另一台机器上特定状态的文件“拉”到你正工作的机器上。如果你最近对同一个文
+件做了有冲突的修改,Git将通知你,而你也应该在解决冲突之后再次提交。
+
+=== 典型源码控制 ===
+
+为你的文件初始化Git仓库:
+
+ $ git init
+ $ git add .
+ $ git commit -m "Initial commit"
+
+在中心服务器,在某个目录初始化一个“裸仓库”:
+
+ $ mkdir proj.git
+ $ cd proj.git
+ $ git init --bare
+ $ # 只用一行命令: GIT_DIR=proj.git git init
+
+如果需要的话,启动Git守护进程:
+
+ $ git daemon --detach # 它也许已经在运行了
+
+对一些Git伺服服务,按照其指导来初始化空Git仓库。一般是在网页上填一个表单。
+
+把你的项目“推”到中心服务器:
+ $ git push git://central.server/path/to/proj.git HEAD
+
+捡出源码,开发键入:
+
+ $ git clone git://central.server/path/to/proj.git
+
+做了改动之后,开发保存变更到本地:
+
+ $ git commit -a
+
+更新到最近版本:
+
+ $ git pull
+
+所有冲突应被处理,然后提交:
+
+ $ git commit -a
+
+把本地改动捡入到中心仓库:
+
+ $ git push
+
+如果主服务器由于其他开发的活动,有了新的变更,这个捡入会失败,该开发应该把最
+新版本拿下来,解决合并冲突,然后重试。
+
+=== 裸仓库 ===
+
+之所以叫裸仓库是因为其没有工作目录;它只包含正常情况下隐藏在`.git`子目录下
+的文件。换句话说,它维护项目历史,而且从不保存任何给定版本的快照。
+
+裸仓库扮演的角色和中心版本控制系统中中心服务器的角色类似:你项目的中心。开
+发从其中克隆项目,捡入新近改动。典型地裸仓库存在一个服务器上,该服务器除了
+分散数据外并不做啥。开发活动发生在克隆上,因此中心仓库没有工作目录也行。
+
+
+很多Git命令在裸仓库上失败,除非指定仓库路径到环境变量`GIT_DIR`,或者指定
+`--bare`选项。
+
+=== 推还是拽 ===
+
+为什么我们介绍了push命令,而不是依赖熟悉的pull命令?首先,在裸仓库上pull会
+失败:除非你必须“fetch”,一个之后我们要讨论的命令。但即使我们在中心服务器上
+保持一个正常的仓库,拽些东西进去仍然很繁琐。我们不得不登陆服务器先,给pull
+命令我们要拽自机器的网络地址。防火墙会阻碍,并且首先如果我们没有到服务器的
+shell访问怎么办呢?
+
+然而,除了这个案例,我们反对推进仓库,因为当目标有工作目录时,困惑随之而来。
+
+简短截说,学习Git的时候,只在目标是裸仓库的时候push,否则用pull的方式。
+
+=== 项目分叉 ===
+
+项目走歪了吗?或者认为你可以做得更好?那么在服务器上:
+
+ $ git clone git://main.server/path/to/files
+
+之后告诉每个相关的人你服务器上项目的分支。
+
+在之后的时间,你可以合并来自原先项目的改变,使用命令:
+
+ $ git pull
+
+=== 终极备份 ===
+
+会有很多散布在各处,禁止篡改的冗余存档吗? 如果你的项目有很多开发,那干脆啥也
+别做了。你的每份代码克隆是一个有效备份。不仅当前状态,还包括你项目整个历史。
+感谢哈希加密算法,如果任何人的克隆被损坏,只要他们与其他的交互,这个克隆就会
+被修好。
+
+如果你的项目并不是那么流行,那就找尽可能多的伺服来放克隆吧。
+
+真正的偏执狂应该总是把HEAD最近20字节的SHA1哈希值写到安全的地方。应该保证安全,
+而不是把它藏起来。比如,把它发布到报纸上就不错,因为对攻击者而言,更改每份报
+纸是很难的。
+
+=== 轻快多任务 ===
+
+比如你想并行开发多个功能。那么提交你的项目并运行:
+
+ $ git clone . /some/new/directory
+
+Git使用硬链接和文件共享来尽可能安全地创建克隆,因此它一眨眼就完成了,因此你现
+在可以并行操作两个没有相互依赖的功能。例如,你可以编辑一个克隆,同时编译另一
+个。感谢 http://en.wikipedia.org/wiki/Hard_link[hardlinking], 本地克隆比简单
+备份省时省地。
+
+现在你可以同时工作在两个彼此独立的特性上。比如,你可以在编译一个克隆的时候编
+辑另一个克隆。任何时候,你都可以从其它克隆提交并拖拽变更。
+
+ $ git pull /the/other/clone HEAD
+
+=== 游击版本控制 ===
+
+你正做一个使用其他版本控制系统的项目, 而你非常思念Git? 那么在你的工作目录初
+始化一个Git仓库:
+
+ $ git init
+ $ git add .
+ $ git commit -m "Initial commit"
+
+然后克隆它:
+
+ $ git clone . /some/new/directory
+
+并在这个目录工作,按你所想在使用Git。过一会,一旦你想和其他每个人同步,在这种
+情况下,转到原来的目录,用其他的版本控制工具同步,并键入:
+
+ $ git add .
+ $ git commit -m "Sync with everyone else"
+
+现在转到新目录运行:
+
+ $ git commit -a -m "Description of my changes"
+ $ git pull
+
+把你的变更提交给他人的过程依赖于其他版本控制系统。这个新目录包含你的改动的文
+件。需要运行其他版本控制系统的命令来上载这些变更到中心仓库。
+
+Subversion, 或许是最好的中心式版本控制系统,为无数项目所用。 *git svn* 命令为
+Subversion仓库自动化了上面的操作,并且也可以用作
+http://google-opensource.blogspot.com/2008/05/export-git-project-to-google-code.html[
+导出Git项目到Subversion仓库] 的替代。
+
+=== Mercurial ===
+
+Mercurial是一个类似的的版本控制系统,几乎可以和Git一起无缝工作。使用
+`hg-git`插件,一个Mercurial用户可以无损地往Git仓库推送,从Git仓库拖拽。
+
+使用Git获得`hg-git`插件:
+
+ $ git clone git://github.com/schacon/hg-git.git
+
+或使用Mercurial:
+
+ $ hg clone http://bitbucket.org/durin42/hg-git/
+
+不好意思,我没注意Git有类似的插件。因此, 我主张使用Git而不是Mercurial作为主资
+源库,即使你偏爱Mercurial。使用Mercurial项目,通常一个自愿者维护一个平行的
+Git项目以适应Git用户,然而感谢`hg-git`插件,一个Git项目自动地适应Mercurial用
+户。
+
+尽管该插件可以把一个Mercurial仓库转成一个Git仓库,通过推到一个空的仓库,
+这个差事交给`hg-fast-export.sh`脚本还是更容易些。来自:
+
+ $ git clone git://repo.or.cz/fast-export.git
+
+要转化,只需在一个空目录运行:
+
+ $ git init
+ $ hg-fast-export.sh -r /hg/repo
+
+注意该脚本应加入你的`$PATH`。
+
+=== Bazaar ===
+
+我们简略提一下Bazaar,它毕竟是紧跟Git和Mercurial之后最流行的自由分布式版本控
+制系统。
+
+Bazaar有后来者的优势,它相对年轻些;它的设计者可以从前人的错误中学习,并且躲
+过去翻历史上犯过的错误。另外,它的开发人员对可移植性以及和与其它版本控制系统
+的互操作性也考虑周全。
+
+一个`bzr-git`插件让Bazaar用户在一定程度下可以工作在Git仓库。`tailor`程序转
+换Bazaar仓库到Git仓库,并且可以递增的方式做,要知道`bzr-fast-export`只是
+在一次性转换性情况下工作良好。
+
+=== 我偏爱Git的原因 ===
+
+我起先选择Git是因为我听说它能管理不可想象地不可管理的Linux内核源码。我从来没
+觉得有离开的必要。Git已经服侍的很好了,并且我也没有被其瑕疵所困扰。因为我主要
+使用Linux,其他平台上的问题与我无关。
+
+还有,我偏爱C程序和bash脚本,以及诸如Python的可执行可脚本:较少依赖,并且我也
+沉迷于快速的执行时间。
+
+我考虑过Git才能如何提高,甚至自己写类似的工具,但只作为研究练练手。即使完成这
+个项目,我也无论如何会继续使用Git,因为使用一个古里古怪的系统所获甚微。
+
+自然地,你的需求和期望可能不同,并且你可能使用另一个系统会好些。尽管如此,使
+用Git你都错不太远。
View
133 zh_cn/drawbacks.txt
@@ -0,0 +1,133 @@
+== 附录 A: Git的缺点 ==
+
+有一些Git的问题,我已经藏在毯子下面了。有些可以通过脚本或回调方法轻易地解决,
+有些需要重组或重定义项目,少数剩下的烦恼,还只能等待。或者更好地,投入进来帮
+忙。
+
+=== SHA1 的弱点 ===
+
+随着时间的推移,密码学家发现越来越多的SHA1的弱点。已经发现对对资源雄厚的组织
+哈希冲撞是可能的。在几年内,或许甚至一个一般的PC也将有足够计算能力悄悄摧毁一
+个Git仓库。
+
+希望在进一步研究摧毁SHA1之前,Git能迁移到一个更好的哈希算法。
+
+=== 微软 Windows ===
+
+Git在微软Windows上可能有些繁琐:
+
+- http://cygwin.com/[Cygwin] ,, 一个Windows下的类Linux的环境,包含一个 http://cygwin.com/packages/git/[ 一个Git在Windows下的移植].
+
+- http://code.google.com/p/msysgit/[基于MSys的Git] 是另一个,要求最小运行时支持,不过一些命令不能马上工作。
+
+=== 不相关的文件 ===
+
+如果你的项目非常大,包含很多不相关的文件,而且正在不断改变,Git可能比其他系统
+更不管用,因为独立的文件是不被跟踪的。Git跟踪整个项目的变更,这通常才是有益的。
+
+一个方案是将你的项目拆成小块,每个都由相关文件组成。如果你仍然希望在同一个资
+源库里保存所有内容的话,可以使用 *git submodule* 。
+
+=== 谁在编辑什么? ===
+
+一些版本控制系统在编辑前强迫你显示地用某个方法标记一个文件。尽管这种要求很烦
+人,尤其是需要和中心服务器通讯时,不过它还是有以下两个好处的:
+
+ 1. 比较速度快,因为只有被标记的文件需要检查。
+
+ 2. 可以知道谁在这个文件上工作,通过查询在中心服务器谁把这个文件标记为编辑状
+ 态。
+
+使用适当的脚本,你也可以使Git达到同样的效果。这要求程序员协同工作,当他编辑一
+个文件的时候还要运行特定的脚本。
+
+=== 文件历史 ===
+
+因为Git记录的是项目范围的变更,重造单一文件的变更历史比其他跟踪单一文件的版本
+控制系统要稍微麻烦些。
+
+好在麻烦还不大,也是值得的,因为Git其他的操作难以置信地高效。例如,`git
+checkout`比`cp -a`都快,而且项目范围的delta压缩也比基于文件的delta集合的做法
+好多了。
+
+=== 初始克隆 ===
+
+The initial cost is worth paying in the long run, as most future operations will then be fast and offline. However, in some situations, it may be preferable to create a shallow clone with the `--depth` option. This is much faster, but the resulting clone has reduced functionality.
+
+当一个项目历史很长后,与在其他版本系统里的检出代码相比,创建一个克隆的开销会
+大的多。
+
+长远来看,开始付出的代价还是值得付出的,因为大多将来的操作将由此变得很快,并
+可以离线完成。然而,在一些情况下,使用`--depth`创建一个浅克隆比较划算些。这种
+克隆初始化的更快,但得到克隆的功能有所削减。
+
+=== 不稳定的项目 ===
+
+变更的大小决定写入的速度快慢是Git的设计。一般人做了小的改动就会提交新版本。这
+里一行臭虫修改,那里一个新功能,修改掉的注释等等。但如果你的文件在相邻版本之
+间存在极大的差异,那每次提交时,你的历史记录会以整个项目的大小增长。
+
+
+
+
+
+
+
+
+
+任何版本控制系统对此都束手无策,但标准的Git用户将遭受更多,因为一般来说,历史
+记录也会被克隆。
+
+应该检查一下变更巨大的原因。或许文件格式需要改变一下。小修改应该仅仅导致几个
+文件的细小改动。
+
+或许,数据库或备份/打包方案才是正选,而不是版本控制系统。例如,版本控制就不适
+宜用来管理网络摄像头周期性拍下的照片。
+
+如果这些文件实在需要不断更改,他们实在需要版本控制,一个可能的办法是以中心的
+方式使用Git。可以创建浅克隆,这样检出的较少,也没有项目的历史记录。当然,很多
+Git工具就不能用了,并且修复必须以补丁的形式提交。这也许还不错,因为似乎没人需
+要大幅度变化的不稳定文件历史。
+
+另一个例子是基于固件的项目,使用巨大的二进制文件形式。用户对固件文件的变化历
+史没有兴趣,更新的压缩比很低,因此固件修订将使仓库无谓的变大。
+
+这种情况,源码应该保存在一个Git仓库里,二进制文件应该单独保存。为了简化问题,
+应该发布一个脚本,使用Git克隆源码,对固件只做同步或Git浅克隆。
+
+=== 全局计数器 ===
+
+一些中心版本控制系统维护一个正整数,当一个新提交被接受的时候这个整数就增长。Git则是通过哈希值来记录所有变更,这在大多数情况下都工作的不错。
+
+但一些人喜欢使用整数的方法。幸运的是,很容易就可以写个脚本,这样每次更新,中心Git仓库就增大这个整数,或使用tag的方式,把最新提交的哈希值与这个整数关联起来。
+
+每个克隆都可以维护这么个计数器,但这或许没什么用,因为只有中心仓库以及它的计数器对每个人才有意义。
+
+=== 空子目录 ===
+
+空子目录不可加入管理。可以通过创建一个空文件以绕过这个问题。
+
+Git的当前实现,而不是它的设计,是造成这个缺陷的原因。如果运气好,一旦Git得到
+更多关注,更多用户要求这个功能,这个功能就会被实现。
+
+=== 初始提交 ===
+
+传统的计算机系统从0计数,而不是1。不幸的是,关于提交,Git并不遵从这一约定。很
+多命令在初始提交之前都不友好。另外,一些极少数的情况必须作特别地处理。例如重
+订一个使用不同初始提交的分支。
+
+Git将从定义零提交中受益:一旦一个仓库被创建起来,HEAD将被设为包含20个零字节
+的字符串。这个特别的提交代表一棵空的树,没有父节点,早于所有Git仓库。
+
+然后运行git log,比如,通知用户至今还没有提交过变更,而不是报告致命错误并退出。
+这与其他工具类似。
+
+每个初始提交都隐式地成为这个零提交的后代。
+
+不幸的是还有更糟糕的情况。如果把几个具有不同初始提交的分支合并到一起,之后的
+重新修订不可避免的需要人员的介入。
+
+=== 接口怪癖 ===
+
+对提交A和提交B,表达式“A..B”和“A...B”的含义,取决于命令期望两个终点还是一
+个范围。参见 *git help diff* 和 *git help rev-parse* 。
View
226 zh_cn/grandmaster.txt
@@ -0,0 +1,226 @@
+== Git大师技 ==
+
+到现在,你应该有能力查阅 *git help* 页,并理解几乎所有东西。然而,查明解决特
+定问题需要的确切命令可能是乏味的。或许我可以省你点功夫:以下是我过去曾经需要
+的一些食谱。
+
+=== 源码发布 ===
+
+就我的项目而言,Git完全跟踪了我想打包并发布给用户的文件。创建一个源码包,我运
+行:
+
+ $ git archive --format=tar --prefix=proj-1.2.3/ HEAD
+
+=== 提交变更 ===
+
+对特定项目而言,告诉Git你增加,删除和重命名了一些文件很麻烦。而键入如下命令会容易的多:
+
+ $ git add .
+ $ git add -u
+
+Git将查找当前目录的文件并自己算出具体的情况。除了用第二个add命令,如果你也打
+算这时提交,可以运行`git commit -a`。关于如何指定应被忽略的文件,参见 *git
+help ignore* 。
+
+你也可以用一行命令完成以上任务:
+
+ $ git ls-files -d -m -o -z | xargs -0 git update-index --add --remove
+
+这里 *-z* 和 *-0* 选项可以消除包含特殊字符的文件名引起的不良副作用。注意这个
+命令也添加应被忽略的文件,这时你可能需要加上 `-x` 或 `-X` 选项。
+
+=== 我的提交太大了! ===
+
+是不是忽视提交太久了?痴迷地编码,直到现在才想起有源码控制工具这回事?提交一
+系列不相关的变更,因为那是你的风格?
+
+别担心,运行:
+
+ $ git add -p
+
+为你做的每次修改,Git将展示给你变动的代码,并询问该变动是否应是下一次提交的一
+部分。回答“y”或者“n”。也有其他选项,比如延迟决定;键入“?”来学习更多。
+
+一旦你满意,键入
+
+ $ git commit
+
+来精确地提交你所选择的变更(阶段变更)。确信你没加上 *-a* 选项,否则Git将提交
+所有修改。
+
+如果你修改了许多地方的许多文件怎么办?一个一个地查看变更令人沮丧,心态麻木。
+这种情况下,使用 *git add -i* , 它的界面不是很直观,但更灵活。敲几个键,你可
+以一次决定阶段或非阶段性提交几个文件,或查看并只选择特定文件的变更。作为另一
+种选择,你还可以运行 *git commit --interactive* ,这个命令会在你操作完后自动
+进行提交。
+
+=== 索引:Git的中转区域 ===
+
+当目前为止,我们已经忽略Git著名的'索引‘概念,但现在我们必须面对它,以解释上
+面发生的。索引是一个临时中转区。Git很少在你的项目和它的历史之间直接倒腾数据。
+通常,Git先写数据到索引,然后拷贝索引中的数据到最终目的地。
+
+例如, *commit -a* 实际上是一个两步过程。第一步把每个追踪文件当前状态的快照放
+到索引中。第二步永久记录索引中的快照。 没有 *-a* 的提交只执行第二步,并且只在
+运行不知何故改变索引的命令才有意义,比如 *git add* 。
+
+通常我们可以忽略索引并假装从历史中直接读并直接写。在这个情况下,我们希望更好
+地控制,因此我们操作索引。我们放我们变更的一些的快照到索引中,而不是所有的,
+然后永久地记录这个小心操纵的快照。
+
+=== 别丢了你的HEAD ===
+
+HEAD好似一个游标,通常指向最新提交,随最新提交向前移动。一些Git命令让你来移动
+它。 例如:
+
+ $ git reset HEAD~3
+
+将立即向回移动HEAD三个提交。这样所有Git命令都表现得好似你没有做那最后三个提交,
+然而你的文件保持在现在的状态。具体应用参见帮助页。
+
+但如何回到将来呢?过去的提交对将来一无所知。
+
+如果你有原先Head的SHA1值,那么:
+
+ $ git reset 1b6d
+
+但假设你从来没有记下呢?别担心,像这些命令,Git保存原先的Head为一个叫
+ORGI_HEAD的标记,你可以安全体面的返回:
+
+ $ git reset ORIG_HEAD
+
+=== HEAD捕猎 ===
+
+或许ORG_HEAD不够。或许你刚认识到你犯了个历史性的错误,你需要回到一个早已忘记
+分支上一个远古的提交。
+
+默认,Git保存一个提交至少两星期,即使你命令Git摧毁该提交所在的分支。难点是找
+到相应的哈希值。你可以查看在.git/objects里所有的哈希值并尝试找到你期望的。但
+有一个更容易的办法。
+
+Git把算出的提交哈希值记录在“.git/logs”。这个子目录引用包括所有分支上所有活
+动的历史,同时文件HEAD显示它曾经有过的所有哈希值。后者可用来发现分支上一些不
+小心丢掉提交的哈希值。
+
+The reflog command provides a friendly interface to these log files. Try
+
+命令reflog为访问这些日志文件提供友好的接口,试试
+
+ $ git reflog
+
+而不是从reflog拷贝粘贴哈希值,试一下:
+
+ $ git checkout "@{10 minutes ago}"
+
+或者捡出后五次访问过的提交,通过:
+
+ $ git checkout "@{5}"
+
+更多内容参见 *git help rev-parse* 的``Specifying Revisions''部分。
+
+你或许期望去为已删除的提交设置一个更长的保存周期。例如:
+
+ $ git config gc.pruneexpire "30 days"
+
+意思是一个被删除的提交会在删除30天后,且运行 *git gc* 以后,被永久丢弃。
+
+你或许还想关掉 *git gc* 的自动运行:
+
+ $ git config gc.auto 0
+
+在这种情况下提交将只在你手工运行 *git gc* 的情况下才永久删除。
+
+=== 基于Git构建 ===
+
+依照真正的UNIX风格设计,Git允许其易于用作其他程序的底层组件,比如图形界面,
+Web界面,可选择的命令行界面,补丁管理工具,导入和转换工具等等。实际上,一些
+Git命令它们自己就是站在巨人肩膀上的脚本。通过一点修补,你可以定制Git适应你的
+偏好。
+
+一个简单的技巧是,用Git内建alias命令来缩短你最常使用命令:
+
+ $ git config --global alias.co checkout
+ $ git config --global --get-regexp alias # 显示当前别名
+ alias.co checkout
+ $ git co foo # 和“git checkout foo”一样
+
+另一个技巧,在提示符或窗口标题上打印当前分支。调用:
+
+ $ git symbolic-ref HEAD
+
+显示当前分支名。在实际应用中,你可能最想去掉“refs/heads/”并忽略错误:
+
+ $ git symbolic-ref HEAD 2> /dev/null | cut -b 12-
+
+子目录 +contrib+ 是一个基于Git工具的宝库。它们中的一些时时会被提升为官方命令。
+在Debian和Ubuntu,这个目录位于 +/usr/share/doc/git-core/contrib+ 。
+
+一个受欢迎的居民是 +workdir/git-new-workdir+ 。通过聪明的符号链接,这个脚本创
+建一个新的工作目录,其历史与原来的仓库共享:
+
+ $ git-new-workdir an/existing/repo new/directory
+
+这个新的目录和其中的文件可被视为一个克隆,除了既然历史是共享的,两者的树自动
+保持同步。不必合并,推入或拉出。
+
+=== 大胆的特技 ===
+
+这些天,Git使得用户意外摧毁数据变得更困难。但如若你知道你在做什么,你可以突破
+为通用命令所设的防卫保障。
+
+*Checkout*:未提交的变更会导致捡出失败。销毁你的变更,并无论如何都checkout一
+ 个指定的提交,使用强制标记:
+
+ $ git checkout -f HEAD^
+
+另外,如果你为捡出指定特别路径,那就没有安全检查了。提供的路径将被不加提示地
+覆盖。如你使用这种方式的检出,要小心。
+
+*Reset*: 如有未提交变更重置也会失败。强制其通过,运行:
+
+ $ git reset --hard 1b6d
+
+*Branch*: 引起变更丢失的分支删除会失败。强制删除,键入:
+
+ $ git branch -D dead_branch # instead of -d
+
+类似,通过移动试图覆盖分支,如果随之而来有数据丢失,也会失败。强制移动分支,键入:
+
+ $ git branch -M source target # 而不是 -m
+
+不像checkout和重置,这两个命令延迟数据销毁。这个变更仍然存储在.git的子目录里,
+并且可以通过恢复.git/logs里的相应哈希值获取(参见上面 上面“HEAD猎捕”)。默
+认情况下,这些数据会保存至少两星期。
+
+*Clean*: 一些Git命令拒绝执行,因为它们担心会重装未纳入管理的文件。如果你确信
+ 所有未纳入管理的文件都是消耗,那就无情地删除它们,使用:
+
+ $ git clean -f -d
+
+下次,那个讨厌的命令就会工作!
+
+=== 阻止坏提交 ===
+
+愚蠢的错误污染我的仓库。最可怕的是由于忘记 *git add* 而引起的文件丢失。较小
+的罪过是行末追加空格并引起合并冲突:尽管危害少,我希望浙西永远不要出现在公开
+记录里。
+
+不过我购买了傻瓜保险,通过使用一个_钩子_来提醒我这些问题:
+
+ $ cd .git/hooks
+ $ cp pre-commit.sample pre-commit # 对旧版本Git,先运行chmod +x
+
+现在Git放弃提交,如果检测到无用的空格或未解决的合并冲突。
+
+对本文档,我最终添加以下到 *pre-commit* 钩子的前面,来防止缺魂儿的事:
+
+ if git ls-files -o | grep '\.txt$'; then
+ echo FAIL! Untracked .txt files.
+ exit 1
+ fi
+
+几个git操作支持钩子;参见 *git help hooks* 。我们早先激活了作为例子的
+*post-update* 钩子,当讨论基于HTTP的Git的时候。无论head何时移动,这个钩子都会
+运行。例子脚本post-update更新Git在基于Git并不知晓的传输协议,诸如HTTP,通讯时
+所需的文件。
+
View
224 zh_cn/history.txt
@@ -0,0 +1,224 @@
+== 关于历史 ==
+
+Git分布式本性使得历史可以轻易编辑。但你若篡改过去,需要小心:只重写你独自拥有
+的那部分。正如民族间会无休止的争论谁犯下了什么暴行一样,如果在另一个人的克隆
+里,历史版本与你的不同,当你们的树互操作时,你会遇到一致性方面的问题。
+
+一些开发人员强烈地感觉历史应该永远不变,不好的部分也不变所有都不变。另一些觉
+得代码树在向外发布之前,应该整得漂漂亮亮的。Git同时支持两者的观点。像克隆,分
+支和合并一样,重写历史只是Git给你的另一强大功能,至于如何明智地使用它,那是你
+的事了。
+
+=== 我认错 ===
+
+Did you just commit, but wish you had typed a different message? Then run:
+刚提交,但你期望你输入的是一条不同的信息?那么键入:
+
+ $ 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命令。
+
+最后,用你修订过的版本替换你的项目克隆,如果你想之后和它们交互的话。
+
+=== 制造历史 ===
+
+[[makinghistory]]
+想把一个项目迁移到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带来大麻
+烦,但是当它需要像本地硬盘一样稳定的时候,它使开发困难重重。另外,我发现我自
+己有选择地避免特定的命令,以避免踏雷,这极大地影响了我,使我不能按照我喜欢的
+方式工作。
+
+当我不得不运行一个慢的命令的时候,这种等待极大地破坏了我思绪连续性。在等待服
+务器通讯完成的时候,我选择做其他的事情以度过这段时光,比如查看邮件或写其他的
+文档。当我返回我原先的工作场景的时候,这个命令早已结束,并且我还需要浪费时间
+试图记起我之前正在做什么。人类不擅长场景间的切换。
+
+还有一个有意思的大众悲剧效应:预料到网络拥挤,为了减少将来的等待时间,每个人
+将比以往消费更多的带宽在各种操作上。共同的努力加剧了拥挤,这等于是鼓励个人下
+次消费更多带宽以避免更长时间的等待。
+
+
View
96 zh_cn/intro.txt
@@ -0,0 +1,96 @@
+== 入门 ==
+
+我将用类比方式来介绍版本控制的概念。更严谨的解释参见
+http://en.wikipedia.org/wiki/Revision_control[维基百科版本修订控制条目]。
+
+=== 工作是玩 ===
+
+我几乎玩了一辈子电脑游戏。相反,我成人之后才开始使用版本控制系统。也许不止我
+一个人这样,对比两者工作方式可使概念更易解释,也易于理解。
+
+把编写代码或编辑文档与玩游戏类比。一旦有很多进展,你会喜欢存盘。去做这你会点
+击你的编辑器的存盘按钮。
+
+但这将覆盖老版本。就像那些学校里玩的老游戏,只有一个存档:你确实可以保存,但
+你不能回到之前的状态了。这真让人扫兴,可能之前状态恰好保存了这个游戏特别有意
+思一关,也许某天你想再玩一下。或者更糟糕的,你当前的存档是个必败的局,这样你
+就不得不从头开始玩了。
+
+=== 版本控制 ===
+
+在编辑的时候,如果想保留旧版本,你可以将文件“另存为”一个不同的文件,或在保
+存之前将文件拷贝到别处。你会把压缩这些文件节省空间。这是一个原始的劳动密集型
+的版本控制方式。游戏软件早就提高了这块,很多都提供多个基于时间戳的自动存档。
+
+让我们看看稍稍复杂的情况。比如你有很多放在一起的文件,比如项目的源代码,或网
+站的文件。现在如果你想保留旧版本你不得存档整个目录。手工保存多个版本很不方便,
+很快会耗费巨大。
+
+一些电脑游戏的存档真的包含在一个充满文件的目录里。这些游戏为玩家屏蔽了具体细
+节,展现一个方便易用的界面来管理该目录的不同版本。
+
+版本控制系统也没有两样。两者都有友好的界面来管理目录里的东西。你可以频繁保存,
+也可以之后加载任一保存。不像大多计算机游戏,版本控制系统通常精于节省存储空间。
+一般情况如果两个版本间只有少数文件的变更,每个文件的变更也不大,那就只存储差
+异的部分以节省存储空间,而不是把全部拷贝的都保存下来。
+
+=== 分布控制 ===
+
+现在设想有一个很难的游戏。太难打了,以至于世界各地很多骨灰级玩家决定组队,分
+享他们游戏存档以攻克它。Speedrun们就是实际中的例子:在同一个游戏里,玩家们分别
+攻克不同的等级,协同工作以创造惊人战绩。
+
+你如何搭建一个系统,使得他们易于得到彼此的所保存的?并易于上载新的?
+
+在过去,每个项目都使用中心式版本控制。某个服务器上放所有保存的游戏记录。其他
+人都不用了。每个玩家在他们机器上最多保留几个游戏记录。当一个玩家想更新进度时
+候,他们需要把最新进度从主服务器下载下来,玩一会儿,保存并上载到主服务器以供
+其他人使用。
+
+假如一个玩家由于某种原因,想得到一个较旧版本的游戏进度怎么样?或许当前保存的
+游戏是一个注定的败局,因为某人在第三级忘记捡某个物品;他们希望能找到最近一个
+可以完成的游戏记录。或者他们想比较两个旧版本间的差异,来估算某个特定玩家干了
+多少活。
+
+查看旧版本的理由有很多,但检查的办法都是一样的。他们必须去问中心服务器要那个
+旧版本的记录。需要的旧版本越多,和服务器的交互就越多。
+
+新一代的版本控制系统,Git就是其中之一,是分布式的,可以被认作广义上的中心式系
+统。从主服务器下载时玩家会得到所有保存的记录,而不仅是最新版。这看起来他们好
+像把中心服务器做了个镜像。
+
+最初的克隆操作可能比较费时,特别当有很长历史的时,但从长远看这是值得的。一个
+显而易见的好处是,当查看一个旧版本时,不再需要和中心服务器通讯了。
+
+=== 一个误区 ===
+
+一个很常见的错误观念是,分布式系统不适合需要官方中心仓库的项目。这与事实并
+不相符。给谁照相也不会偷走他们的灵魂。类似地,克隆主仓库并不降低它的重要性。
+
+一般来说,一个中心版本控制系统能做的任何事,一个良好设计的分布式系统都能做得
+更好。网络资源总要比本地资源耗费更费。不过我们应该在稍后分析分布式方案的缺点,
+这样人们才不会按照习惯做出错误的比较。
+
+一个小项目或许只需要分布式系统提供的一小部分功能,但是,在你的项目很小的时候,
+说你应该用规划不好的系统,好比说,在计算较小数目的时候应该使用罗马数字吗?
+
+而且,你的项目的增长可能会超出你最初的预期。从一开始就使用Git好似带着一把瑞士
+军刀,尽管你很多时候只是用它来开开瓶盖。到你迫切需要一把改锥的那一天,你就会
+庆幸你有的不单单是一个启瓶器。
+
+=== 合并冲突 ===
+
+对于这个话题,电脑游戏的类比显得太单薄了。那么让我们再来看看文档编辑的情况吧。
+
+假设Alice在文档开头插入一行,并且Bob在文档末尾添加一行。他们都上传了他们的改
+动。大多数系统将自动给出一个合理的处理方式:接受且合并他们的改动,这样Alice和
+Bob两人的改动都会生效。
+
+现在假设Alice和Bob对文件的同一行做了不同的改动。如果没有人工参与的话,这个冲
+突是无法解决的。第二个人在上载文件时,会收到 _合并冲突_ 的通知,要么用一个人的
+改动覆盖另一个的,要么完全修订这一行。
+
+更复杂的情况也可能出现。版本控制系统自己处理相对简单的情况,把困难的情况留给
+人来处理。它们的行为通常是可配置的。
+
+
View
208 zh_cn/multiplayer.txt
@@ -0,0 +1,208 @@
+== 多人Git ==
+
+我最初在一个私人项目上使用Git,那里我是唯一的开发。在与Git分布式本性有关的命
+令中,我只用到了 *pull* 和 *clone*,用以在不同地方保持同一个项目。
+
+后来我想用Git发布我的代码,并且包括其他贡献者的变更。我不得不学习如何管理有来
+自世界各地的多个开发的项目,幸运的是,这是Git的长处,也可以说是其存在的理由。
+
+=== 我是谁? ===
+
+每个提交都有一个作者姓名和电子信箱,这显示在 *git log* 里。默认, Git使用系统
+设定来填充这些域。要显示地设定,键入:
+
+ $ git config --global user.name "John Doe"
+ $ git config --global user.email johndoe@example.com
+
+去掉global选项设定只对当前仓库生效。
+
+=== Git在SSH, HTTP上 ===
+
+假设你有ssh访问权限,以访问一个网页服务器,但上面并没有安装Git。尽管比着它的
+原生协议效率低,Git也是可以通过HTTP来进行通信的。
+
+那么在你的帐户下,下载,编译并安装Git。在你的网页目录里创建一个Git仓库:
+
+ $ GIT_DIR=proj.git git init
+ $ cd proj.git
+ $ git --bare update-server-info
+ $ cp hooks/post-update.sample hooks/post-update
+
+对较老版本的Git,只拷贝还不够,你应运行:
+
+ $ chmod a+x hooks/post-update
+
+现在你可以通过SSH从随便哪个克隆发布你的最新版本:
+
+ $ git push web.server:/path/to/proj.git master
+
+那随便谁都可以通过如下命令得到你的项目:
+
+ $ git clone http://web.server/proj.git
+
+=== Git在随便什么上 ===
+
+想无需服务器,甚至无需网络连接的时候同步仓库?需要在紧急时期凑合一下?我们
+已经看过<<makinghistory, *git fast-export* 和 *git fast-import* 可以转换资源
+库到一个单一文件以及转回来>>。 我们可以来来会会传送这些文件以传输git仓库,
+通过任何媒介,但一个更有效率的工具是 *git bundle* 。
+
+发送者创建一个“文件包”:
+
+ $ git bundle create somefile HEAD
+
+然后传输这个文件包, +somefile+ ,给某个其他参与者:电子邮件,优盘,一个
+*xxd* 打印品和一个OCR扫描仪,通过电话读字节,狼烟,等等。接收者通过键入如下命
+令从文件包获取提交:
+
+ $ git pull somefile
+
+接收者甚至可以在一个空仓库做这个。不考虑大小, +somefile+ 可以包含整个原先
+git仓库。
+
+在较大的项目里,可以通过只打包其他仓库缺少的变更消除浪费。例如,假设提交
+``1b6d...''是两个参与者共享的最近提交:
+
+ $ git bundle create somefile HEAD ^1b6d
+
+如果做的频繁,人可能容易忘记刚发了哪个提交。帮助页面建议使用标签解决这个问题。
+即,在你发了一个文件包后,键入:
+
+ $ git tag -f lastbundle HEAD
+
+并创建较新文件包,使用:
+
+ $ git bundle create newbundle HEAD ^lastbundle
+
+=== 补丁:全球货币 ===
+
+补丁是变更的文本形式,易于计算机理解,人也类似。补丁可以通吃。你可以给开发电
+邮一个补丁,不用管他们用的什么版本控制系统。只要你的观众可以读电子邮件,他们
+就能看到你的修改。类似,在你这边,你只需要一个电子邮件帐号:不必搭建一个在线
+的Git仓库。
+
+回想一下第一章:
+
+ $ git diff 1b6d > my.patch
+
+输出是一个补丁,可以粘贴到电子邮件里用以讨论。在一个Git仓库,键入:
+
+ $ git apply < my.patch
+
+来打这个补丁。
+
+在更正式些的设置里,当作者名字以及或许签名应该记录下的时候,为过去某一刻生成
+补丁,键入:
+
+ $ git format-patch 1b6d
+
+结果文件可以给 *git-send-email* 发送,或者手工发送。你也可以指定一个提交范围:
+
+ $ git format-patch 1b6d..HEAD^^
+
+在接收一端,保存邮件到一个文件,然后键入:
+
+ $ git am < email.txt
+
+这就打了补丁并创建了一个提交,包含诸如作者之类的信息。
+
+使用浏览器邮件客户端,在保存补丁为文件之前,你可能需要建一个按钮,看看邮件内
+容原来的原始形式。
+
+对基于mbox的邮件客户端有些微不同,但如果你在使用的话,你可能是那种能轻易找出
+答案的那种人,不用读教程。
+
+=== 对不起,移走了 ===
+
+克隆一个仓库后,运行 *git push* 或 *git pull* 讲自动推到或从原先URL拉。Git
+如何做这个呢?秘密在和克隆一起创建的配置选项。让我们看一下:
+
+ $ git config --list
+
+选项 +remote.origin.url+ 控制URL源;``origin'' 是给源仓库的昵称。和
+``master'' 分支的惯例一样,我们可以改变或删除这个昵称,但通常没有理由这么做。
+
+如果原先仓库移走,我们可以更新URL,通过:
+
+ $ git config remote.origin.url git://new.url/proj.git
+
+选项 +branch.master.merge+ 指定 *git pull* 里的默认远端分支。在初始克隆的时候,
+它被设为原仓库的当前分支,因此即使原仓库之后挪到一个不同的分支,后来的
+pull也将忠实地跟着原来的分支。
+
+这个选项只使用我们初次克隆的仓库,它的值记录在选项 +branch.master.remote+
+。如果我们从其他仓库拉入,我们必须显示指定我们想要哪个分支:
+
+ $ git pull git://example.com/other.git master
+
+以上也解释了为什么我们较早一些push和pull的例子没有参数。
+
+=== 远端分支 ===
+
+当你克隆一个仓库,你也克隆了它的所有分支。你或许没有注意到因为Git将它们隐藏
+起来了:你必须明确地要求。这使得远端仓库里的分支不至于干扰你的分支,也使
+Git对初学者稍稍容易些。
+
+列出远端分支,使用:
+
+ $ git branch -r
+
+你应该看到类似:
+
+ origin/HEAD
+ origin/master
+ origin/experimental
+
+这显示了远端仓库的分支和HEAD,可以用在常用的Git命令里。例如,假设你已经做了
+很多提交,并希望和最后取到的版本比较一下。你可以搜索适当的SHA1哈希值,但使用
+下面命令更容易些:
+
+ $ git diff origin/HEAD
+
+或你可以看看``experimental''分支都有啥:
+
+ $ git log origin/experimental
+
+=== 多远端 ===
+
+假设另两个开发在同一个项目上工作,我们希望保持两个标签。我们可以同事跟多个仓库:
+
+ $ git remote add other git://example.com/some_repo.git
+ $ git pull other some_branch
+
+现在我们已经从第二个仓库合并到一个分支,并且我们已容易访问所有仓库的所有
+分支。
+
+ $ git diff origin/experimental^ other/some_branch~5
+
+但如果为了不影响自己的工作,我们只想比较他们的变更怎么办呢?换句话说,我们想
+检查一下他们的分支,又不使他们的变更入侵我们的工作目录。那不是运行pull命令,
+而是运行:
+
+ $ git fetch # Fetch from origin, the default.
+ $ git fetch other # Fetch from the second programmer.
+
+这只是获取历史。尽管工作目录维持不变,我们可以参考任何仓库的任何分支,使用
+一个Git命令,因为我们现在有一个本地拷贝。
+
+回想一下,在幕后,一个pull是简单地一个 *fetch* 然后 *merge* 。通常,我们
+*pull* 因为我们想在获取后合并最近提交;这个情况是一个值得注意的例外。
+
+关于如何去除远端仓库,如何忽略特定分支等更多,参见 *git help remote* 。
+
+=== 我的喜好 ===
+
+对我手头的项目,我喜欢贡献者去准备仓库,这样我可以从其中拉。一些Git伺服让你
+点一个按钮,拥有自己的分叉项目。
+
+在我获取一个树之后,我运行Git命令去浏览并检查这些变更,理想情况下这些变更组织
+良好,描述良好。我合并这些变更,也或许做些编辑。直到满意,我才把变更推入主资
+源库。
+
+尽管我不经常收到贡献,我相信这个方法扩展性良好。参见
+http://torvalds-family.blogspot.com/2009/06/happiness-is-warm-scm.html[ 这篇
+来自Linus Torvalds的博客 ]
+
+呆在Git的世界里比补丁文件稍更方便,因为不用我将补丁转换到Git提交。更进一步,
+Git处理诸如作者姓名和信箱地址的细节,还有时间和日期,以及要求作者描述他们的提
+交。
View
97 zh_cn/preface.txt
@@ -0,0 +1,97 @@
+= Git 魔法 =
+Ben Lynn
+2007年8月
+
+== 前言 ==
+
+http://git.or.cz/[Git] 是一个版本控制瑞士军刀,一个可靠的、多功能、多用途的校
+订工具,其异常灵活,不易掌握。
+
+正如Arthur C. Clarke所洞察,足够先进的技术和魔法无二。一个接近Git的绝妙办法:
+新手们可以忽略Git的内部机理,只当个小把戏玩,炫耀其功能,让朋友羡慕,让敌人暴
+怒。
+
+为了不陷入细节,我们对特定功能提供大略的指导。在重复使用之后,慢慢地你会理解
+每个小技巧如何工作,以及如何组合这些技巧以满足你的需求。
+
+.翻译
+
+ - link:/~blynn/gitmagic/intl/zh_cn/[简体中文]: 俊杰,萌和江薇。
+ - link:/~blynn/gitmagic/intl/zh_tw/[正体中文]: 由命令 *cconv -f UTF8-CN -t
+ UTF8-TW* 转换。
+ - link:/~blynn/gitmagic/intl/fr/[法文]: Alexandre Garel。也在
+ http://tutoriels.itaapy.com/[itaapy]。
+ - link:/~blynn/gitmagic/intl/de/[德文]: Benjamin Bellee和Armin Stebich;也在
+ http://gitmagic.lordofbikes.de/[Armin的网站]。
+ - http://www.slideshare.net/slide_user/magia-git[葡萄牙文]: Leonardo
+ Siqueira Rodrigues
+ [http://www.slideshare.net/slide_user/magia-git-verso-odt[ODT版]]。
+ - link:/~blynn/gitmagic/intl/ru/[俄文]: Tikhon Tarnavsky, Mikhail Dymskov,
+ 和其他人。
+ - link:/~blynn/gitmagic/intl/es/[西班牙]: Rodrigo Toledo和Ariset Llerena
+ Tapia。
+ - link:/~blynn/gitmagic/intl/vi/[越南文]: Trần Ngọc Quân; 也在
+ http://vnwildman.users.sourceforge.net/gitmagic.html[他的网站].
+
+.其它版本
+
+ - link:/~blynn/gitmagic/intl/zh/book.html[单一文件]: 纯HTML,无CSS。
+ - link:/~blynn/gitmagic/intl/zh/book.pdf[PDF文件]: 打印效果好.
+ - http://packages.debian.org/gitmagic[Debian包],
+ http:://packages.ubuntu.com/gitmagic[Ubuntu包]: 本站快速本地拷贝。如果
+ http://csdcf.stanford.edu/status/[下线了]会方便些。
+ - http://www.amazon.com/Git-Magic-Ben-Lynn/dp/1451523343/[纸质书
+ [Amazon.com]]: 64 页, 15.24cm x 22.86cm, 黑白。 没有电子设备的时候会方便些。
+
+=== 致谢! ===
+
+感谢Dustin Sallings, Alberto Bertogli, James Cameron, Douglas Livingstone,
+Michael Budde, Richard Albury, Tarmigan,Derek Mahar,Frode Aannevik,和
+Keith Rarick提出的建议和改进。
+
+Daniel Baumann维护Debian包。
+
+俊杰, 萌, 江薇, Rodrigo Toledo,和Leonardo Rodrigues致力于本文档翻译。
+
+感谢其他拥护者和赞美者。如果这是本纸质书,我会在封面上引用你们慷慨的赞美以提
+高销量。
+
+如果我不小心忘记了感谢谁,请告诉我或者发一个补丁(Patch)给我。
+
+那么多人对本文档的翻译让我受宠若惊。他们的付出拓宽了读者群,我非常感激。
+
+Dustin Sallings、Alberto Bertogli、James Cameron、Douglas Livingstone、
+Michael Budde、Richard Albury、Tarmigan、 Derek Mahar、Frode Aannevik、
+Keith Rarick、 Andy Somerville、 Ralf Recker、 Øyvind A. Holm、 Miklos Vajna、
+Sébastien Hinderer、 Thomas Miedema、 Joe Malin、 和Tyler Breisacher对本文档
+正确性和优化做出了贡献。
+
+François Marier维护Debian包,该Debian包起初由Daniel
+Baumann创建。
+
+感谢其他很多提供帮助和赞扬的人。名单太长了我无法一一写下。
+
+如果我不小心把你的名字落下,请告诉我或者发一个补丁。
+
+.免费Git伺服
+
+ - http://repo.or.cz/[http://repo.or.cz/] 为自由项目提供服务。第一个Git服务器。
+ 由最早的Git开发人员创建和维护。
+ - http://gitorious.org/[http://gitorious.org/] 是另一个Git服务站点,为开源项
+ 目提供服务。
+ - http://github.com/[http://github.com/] 开源项目免费,私有项目收钱。
+
+感谢以上站点为本文档提供伺服服务。
+
+=== 许可 ===
+
+本指南在http://www.gnu.org/licenses/gpl-3.0.html[ GNU通用公共许可协议版本3 ]
+之下发布。很自然,源码保存在一个Git仓库里,可以通过以下命令获得源码:
+
+ $ git clone git://repo.or.cz/gitmagic.git # 创建“gitmagic”目录.
+
+或从以下镜像得到:
+
+ $ git clone git://github.com/blynn/gitmagic.git
+ $ git clone git://gitorious.org/gitmagic/mainline.git
+
View
215 zh_cn/secrets.txt
@@ -0,0 +1,215 @@
+== 揭开面纱 ==
+
+我们揭开Git神秘面纱,往里瞧瞧它是如何创造奇迹的。我会跳过细节。更深入的描述参
+见 http://www.kernel.org/pub/software/scm/git/docs/user-manual.html[ 用户手
+册]。
+
+=== 大象无形 ===
+
+Git怎么这么谦逊寡言呢?除了偶尔提交和合并外,你可以如常工作,就像不知道版本控
+制系统存在一样。那就是,直到你需要它的时候,而且那是你欢欣的时候,Git一直默默
+注视着你。
+
+其他版本控制系统强迫你与繁文缛节和官僚主义不断斗争。文件的权限可能是只读的,
+除非你显式地告诉中心服务器哪些文件你打算编辑。即使最基本的命令,随着用户数目
+的增多,也会慢的像爬一样。中心服务器可能正跟踪什么人,什么时候check out了什么
+代码。当网络连接断了的时候,你就遭殃了。开发人员不断地与这些版本控制系统的种
+种限制作斗争。一旦网络或中心服务器瘫痪,工作就嘎然而止。
+
+与之相反,Git简单地在你工作目录下的`.git`目录保存你项目的历史。这是你自己的历
+史拷贝,因此你可以保持离线,直到你想和他人沟通为止。你拥有你的文件命运完全的
+控制权,因为Git可以轻易在任何时候从`.git`重建一个保存状态。
+
+=== 数据完整性 ===
+
+很多人把加密和保持信息机密关联起来,但一个同等重要的目标是保证信息安全。合理
+使用哈希加密功能可以防止无意或有意的数据损坏行为。
+
+一个SHA1哈希值可被认为是一个唯一的160位ID数,用它可以唯一标识你一生中遇到的每
+个字节串。 实际上不止如此:每个字节串可供任何人用好多辈子。
+
+对一个文件而言,其整体内容的哈希值可以被看作这个文件的唯一标识ID数。
+
+因为一个SHA1哈希值本身也是一个字节串,我们可以哈希包括其他哈希值的字节串。这
+个简单的观察出奇地有用:查看“哈希链”。我们之后会看Git如何利用这一点来高效地
+保证数据完整性。
+
+简言之,Git把你数据保存在`.git/objects`子目录,那里看不到正常文件名,相反你只
+看到ID。通过用ID作为文件名,加上一些文件锁和时间戳技巧,Git把任意一个原始的文
+件系统转化为一个高效而稳定的数据库。
+
+=== 智能 ===
+
+Git是如何知道你重命名了一个文件,即使你从来没有明确提及这个事实?当然,你或许
+是运行了 *git mv* ,但这个命令和 *git add* 紧接 *git rm* 是完全一样的。
+
+Git启发式地找出相连版本之间的重命名和拷贝。实际上,它能检测文件之间代码块的移
+动或拷贝!尽管它不能覆盖所有的情况,但它已经做的很好了,并且这个功能也总在改
+进中。如果它在你那儿不工作的话,可以尝试打开开销更高的拷贝检测选项,并考虑升
+级。
+
+=== 索引 ===
+
+为每个加入管理的文件,Git在一个名为“index”的文件里记录统计信息,诸如大小,
+创建时间和最后修改时间。为了确定文件是否更改,Git比较其当前统计信息与那些在索
+引里的统计信息。如果一致,那Git就跳过重新读文件。
+
+因为统计信息的调用比读文件内容快的很多,如果你仅仅编辑了少数几个文件,Git几乎
+不需要什么时间就能更新他们的统计信息。
+
+我们前面讲过索引是一个中转区。为什么一堆文件的统计数据是一个中转区?因为添加
+命令将文件放到Git的数据库并更新它们的统计信息,而无参数的提交命令创建一个提交,
+只基于这些统计信息和已经在数据库里的文件。
+
+=== Git的源起 ===
+
+这个 http://lkml.org/lkml/2005/4/6/121[ Linux内核邮件列表帖子] 描述了导致Git
+的一系列事件。整个讨论线索是一个令人着迷的历史探究过程,对Git史学家而言。
+
+=== 对象数据库 ===
+
+你数据的每个版本都保存在“对象数据库”里,其位于子目录`.git/objects`;其他位
+于`.git/`的较少数据:索引,分支名,标签,配置选项,日志,头提交的当前位置等。
+对象数据库朴素而优雅,是Git的力量之源。
+
+`.git/objects`里的每个文件是一个对象。有3中对象跟我们有关:“blob”对象,
+“tree”对象,和“commit”对象。
+
+=== Blob对象 ===
+
+首先来一个小把戏。去一个文件名,任意文件名。在一个空目录:
+
+ $ echo sweet > YOUR_FILENAME
+ $ git init
+ $ git add .
+ $ find .git/objects -type f
+
+你将看到 +.git/objects/aa/823728ea7d592acc69b36875a482cdf3fd5c8d+ 。
+
+我如何在不知道文件名的情况下知道这个?这是因为以下内容的SHA1哈希值:
+
+ "blob" SP "6" NUL "sweet" LF
+
+是 aa823728ea7d592acc69b36875a482cdf3fd5c8d,这里SP是一个空格,NUL是一个0字节,
+LF是一个换行符。你可以验证这一点,键入:
+
+ $ printf "blob 6\000sweet\n" | sha1sum
+
+Git基于“内容寻址”:文件并不按它们的文件名存储,而是按它们包含内容的哈希值,
+在一个叫“blob对象”的文件里。我们可以把文件内容的哈希值看作一个唯一ID,这样
+在某种意义上我们通过他们内容放置文件。开始的“blob 6”只是一个包含对象类型与
+其长度的头;它简化了内部存储。
+
+这样我可以轻易语言你所看到的。文件名是无关的:只有里面的内容被用作构建blob对象。
+
+你可能想知道对相同的文件什么会发生。试图加一个你文件的拷贝,什么文件名都行。
+在 +.git/objects+ 的内容保持不变,不管你加了多少。Git只存储一次数据。
+
+顺便说一句,在 +.git/objects+ 里的文件用zlib压缩,因此你不应该直接查看他们。
+可以通过http://www.zlib.net/zpipe.c[zpipe -d] 管道, 或者键入:
+
+ $ git cat-file -p aa823728ea7d592acc69b36875a482cdf3fd5c8d
+
+这漂亮地打印出给定的对象。
+
+=== Tree对象 ===
+
+但文件名在哪?它们必定在某个阶段保存在某个地方。Git在提交时得到文件名:
+
+ $ git commit # 输入一些信息。
+ $ find .git/objects -type f
+
+你应看到3个对象。这次我不能告诉你这两个新文件是什么,因为它部分依赖你选择的文
+件名。我继续进行,假设你选了``rose''。如果你没有,你可以重写历史以让它看起来
+像似你做了:
+
+ $ git filter-branch --tree-filter 'mv YOUR_FILENAME rose'
+ $ find .git/objects -type f
+
+现在你硬看到文件 +.git/objects/05/b217bb859794d08bb9e4f7f04cbda4b207fbe9+ ,因为这是以下内容的SHA1哈希值:
+
+ "tree" SP "32" NUL "100644 rose" NUL 0xaa823728ea7d592acc69b36875a482cdf3fd5c8d
+
+检查这个文件真的包含上面内容通过键入:
+
+ $ echo 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | git cat-file --batch
+
+使用zpipe,验证哈希值是容易的:
+
+ $ zpipe -d < .git/objects/05/b217bb859794d08bb9e4f7f04cbda4b207fbe9 | sha1sum
+
+与查看文件相比,哈希值验证更技巧一些,因为其输出不止包含原始未压缩文件。
+
+这个文件是一个“tree”对象:一组数据包含文件类型,文件名和哈希值。在我们的例
+子里,文件类型是100644,这意味着“rose”是一个一般文件,并且哈希值指blob对象,
+包含“rose”的内容。其他可能文件类型有可执行,链接或者目录。在最后一个例子里,
+哈希值指向一个tree对象。
+
+在一些过渡性的分支,你会有一些你不在需要的老的对象,尽管有宽限过期之后,它们
+会被自动清除,现在我们还是将其删除,以使我们比较容易跟上这个玩具例子。
+
+ $ rm -r .git/refs/original
+ $ git reflog expire --expire=now --all
+ $ git prune
+
+在真实项目里你通常应该避免像这样的命令,因为你在破换备份。如果你期望一个干净
+的仓库,通常最好做一个新的克隆。还有,直接操作 +.git+ 时一定要小心:如果
+Git命令同时也在运行会怎样,或者突然停电?一般,引用应由 *git update-ref -d*
+删除,尽管通常手工删除 +refs/original+ 也是安全的。
+
+=== Commit对象 ===
+
+我们已经解释了三个对象中的两个。第三个是“commit”对象。其内容依赖于提交信息
+以及其创建的日期和时间。为满足这里我们所有的,我们不得不调整一下:
+
+ $ git commit --amend -m Shakespeare # 改提交信息
+ $ git filter-branch --env-filter 'export
+ GIT_AUTHOR_DATE="Fri 13 Feb 2009 15:31:30 -0800"
+ GIT_AUTHOR_NAME="Alice"
+ GIT_AUTHOR_EMAIL="alice@example.com"
+ GIT_COMMITTER_DATE="Fri, 13 Feb 2009 15:31:30 -0800"
+ GIT_COMMITTER_NAME="Bob"
+ GIT_COMMITTER_EMAIL="bob@example.com"' # Rig timestamps and authors.
+ $ find .git/objects -type f
+
+你现在应看到 +.git/objects/49/993fe130c4b3bf24857a15d7969c396b7bc187+ 是下列
+内容的SHA1哈希值:
+
+ "commit 158" NUL
+ "tree 05b217bb859794d08bb9e4f7f04cbda4b207fbe9" LF
+ "author Alice <alice@example.com> 1234567890 -0800" LF
+ "committer Bob <bob@example.com> 1234567890 -0800" LF
+ LF
+ "Shakespeare" LF
+
+和前面一样,你可以运行zpipe或者cat-file来自己看。
+
+这是第一个提交,因此没有父提交,但之后的提交将总有至少一行,指定一个父提交。
+
+=== 没那么神 ===
+
+Git的秘密似乎太简单。看起来似乎你可以整合几个shell脚本,加几行C代码来弄起来,
+也就几个小时的事:一个基本文件操作和SHA1哈希化的混杂,用锁文件装饰一下,文件
+同步保证健壮性。实际上,这准确描述了Git的最早期版本。尽管如此,除了巧妙地打包
+以节省空间,巧妙地索引以省时间,我们现在知道Git如何灵巧地改造文件系统成为一个
+对版本控制完美的数据库。
+
+例如,如果对象数据库里的任何一个文件由于硬盘错误损毁,那么其哈希值将不再匹配,
+这个错误会报告给我们。通过哈希化其他对象的哈希值,我们在所有层面维护数据完整
+性。Commit对象是原子的,也就是说,一个提交永远不会部分地记录变更:在我们已经
+存储所有相关tree对象,blob对象和父commit对象之后,我们才可以计算提交的的哈希
+值并将其存储在数据库,对象数据库不受诸如停电之类的意外中断影响。
+
+我们打败即使是最狡猾的对手。假设有谁试图悄悄修改一个项目里一个远古版本文件的
+内容。为使对象据库看起来健康,他们也必须修改相应blob对象的哈希值,既然它现在
+是一个不同的字节串。这意味着他们讲不得不引用这个文件的tree对象的哈希值,并反
+过来改变所有与这个tree相关的commit对象的哈希值,还要加上这些提交所有后裔的哈
+希值。这暗示官方head的哈希值与这个坏仓库不同。通过跟踪不匹配哈希值线索,我
+们可以查明残缺文件,以及第一个被破坏的提交。
+
+总之,只要20个字节代表最后一次提交的是安全的,不可能篡改一个Git仓库。
+
+那么Git的著名功能怎样呢?分支?合并?标签?单纯的细节。当前head保存在文件
++.git /HEAD+ ,其中包含了一个commit对象的哈希值。该哈希值在运行提交以及其他命
+令是更新。分支几乎一样:它们是保存在 +.git/refs/heads+ 的文件。标签也是:它们
+住在住在 +.git/refs/tags+ ,但它们由一套不同的命令更新。
View
31 zh_cn/translate.txt
@@ -0,0 +1,31 @@
+== 附录 B: 本指南的翻译 ==
+
+克隆源码,然后针对不同目标
+http://www.iana.org/assignments/language-subtag-registry[语言的IETF tag]创建
+一个目录。参见
+http://www.w3.org/International/articles/language-tags/Overview.en.php[ W3C在
+国际化方面的文章 ]。例如,英语是“en”,日语是“ja”,正体中文是“zh-Hant”。
+然后在新建目录,翻译这些来自“en”目录的 +txt+ 文件。
+
+例如,将本指南译为 http://en.wikipedia.org/wiki/Klingon_language[ 克林贡语 ],
+你也许键入:
+
+ $ git clone git://repo.or.cz/gitmagic.git
+ $ cd gitmagic
+ $ mkdir tlh # "tlh" 是克林贡语的IETF语言码。
+ $ cd tlh
+ $ cp ../en/intro.txt .
+ $ edit intro.txt # 翻译这个文件
+
+对每个文件都一样。你可以增量地查看你的工作:
+
+ $ make LANG=tlh
+ $ firefox book.html
+
+经常提交你的变更,然后然我知道他们什么时候完成。GitHub.com提供一个便于fork
+“gitmatic”项目的界面,提交你的变更,然后告诉我去合并。
+
+我更喜欢遵循以上方式的翻译,这样我的脚本就可以生成HTML和PDF版本。另外,它也便
+于使所有翻译都在官方仓库。但请按照最适合你的方式做:例如,中文译者就使用
+Google Docs。只要你的工作使更多人看到我的工作,我就高兴。
+
View
224 zh_tw/basic.txt
@@ -0,0 +1,224 @@
+== 基本技巧 ==
+
+與其一頭紮進Git命令的海洋中,不如來點基本的例子試試手。它們簡單而且實用。實際
+上,在開始使用Git的頭幾個月,我所用的從來沒超出本章介紹的內容。
+
+=== 保存狀態 ===
+
+要不來點猛的?在做之前,先為當前目錄所有檔案做個快照,使用:
+
+ $ git init
+ $ git add .
+ $ git commit -m "My first backup"
+
+現在如果你的編輯亂了套,恢復之前的版本:
+
+ $ git reset --hard
+
+再次保存狀態:
+
+ $ git commit -a -m "Another backup"
+
+=== 添加、刪除、重命名 ===
+
+以上命令將只跟蹤你第一次運行 *git add* 命令時就已經存在的檔案。如果要添加新文
+件或子目錄,你需要告訴Git:
+
+ $ git add readme.txt Documentation
+
+類似,如果你想讓Git忘記某些檔案:
+
+ $ git rm kludge.h obsolete.c
+ $ git rm -r incriminating/evidence/
+
+這些檔案如果還沒刪除,Git刪除它們。
+
+重命名檔案和先刪除舊檔案,再添加新檔案的一樣。也有一個快捷方式 *git mv* ,和
+*mv* 命令的用法一樣。例如:
+
+ $ git mv bug.c feature.c
+
+=== 進階撤銷/重做 ===
+
+有時候你只想把某個時間點之後的所有改動都回滾掉,因為這些的改動是不正確的。那
+麼:
+
+ $ git log
+
+來顯示最近提交列表,以及他們的SHA1哈希值:
+
+----------------------------------
+commit 766f9881690d240ba334153047649b8b8f11c664
+Author: Bob <bob@example.com>
+Date: Tue Mar 14 01:59:26 2000 -0800
+
+ Replace printf() with write().
+
+commit 82f5ea346a2e651544956a8653c0f58dc151275c
+Author: Alice <alice@example.com>
+Date: Thu Jan 1 00:00:00 1970 +0000
+
+ Initial commit.
+----------------------------------
+
+哈希值的前幾個字元足夠確定一個提交;也可以拷貝粘貼完整的哈希值,鍵入:
+
+ $ git reset --hard 766f
+
+來恢復到一個指定的提交狀態,並從記錄裡永久抹掉所有比該記錄新一些的提交。
+
+另一些時候你想簡單地跳到一個舊狀態。這種情況,鍵入:
+
+ $ git checkout 82f5
+
+這個操作將把你帶回過去,同時也保留較新提交。然而,像科幻電影裡時光旅行一樣,
+如果你這時編輯並提交的話,你將身處另一個現實裡,因為你的動作與開始時相比是不
+同的。
+
+這另一個現實叫作“分支”(branch),之後 <<branch,我們會對這點多討論一些>>。
+至于現在,只要記住:
+
+ $ git checkout master
+
+會把你帶到當下來就可以了。另外,為避免Git的抱怨,應該在每次運行checkout之前提
+交(commit)或重置(reset)你的改動。
+
+還以電腦遊戲作為類比:
+
+- *`git reset --hard`*: 加載一個舊記錄並刪除所有比之新的記錄。
+
+- *`git checkout`*: 加載一個舊記錄,但如果你在這個記錄上玩,遊戲狀態將偏離第
+ 一輪的較新狀態。你現在打的所有遊戲記錄會在你剛進入的、代表另一個真實的分支
+ 裡。<<branch,我們稍後論述>>。
+
+你可以選擇只恢復特定檔案和目錄,通過將其加在命令之後:
+
+ $ git checkout 82f5 some.file another.file
+
+小心,這種形式的 *checkout* 會不聲不響地覆蓋檔案。為阻止意外發生,在運行任何
+checkout命令之前做提交,尤其在初學Git的時候。通常,任何時候你覺得對運行某個命
+令不放心,無論Git命令還是不是Git命令,就先運行一下 *git commit -a* 。
+
+不喜歡拷貝站題哈希值?那就用:
+
+ $ git checkout :/"My first b"
+
+來跳到以特定字元串開頭的提交。你也可以回到倒數第五個保存狀態:
+
+ $ git checkout master~5
+
+=== 撤銷 ===
+
+在法庭上,事件可以從法庭記錄裡敲出來。同樣,你可以檢出特定提交以撤銷。
+
+ $ git commit -a
+ $ git revert 1b6d
+
+講撤銷給定哈希值的提交。本撤銷被記錄為一個新的提交,你可以通過運行 *git log*
+來確認這一點。
+
+=== 變更日誌生成 ===
+
+一些項目要求生成變更日誌http://en.wikipedia.org/wiki/Changelog[changelog]. 生
+成一個,通過鍵入:
+
+ $ git log > ChangeLog
+
+=== 下載檔案 ===
+
+得到一個由Git管理的項目的拷貝,通過鍵入:
+
+ $ git clone git://server/path/to/files
+
+例如,得到我用來創建該站的所有檔案:
+
+ $ git clone git://git.or.cz/gitmagic.git
+
+我們很快會對 *clone* 命令談的很多。
+
+=== 到最新 ===
+
+如果你已經使用 *git clone* 命令得到了一個項目的一份拷貝,你可以更新到最新版,
+通過:
+
+ $ git pull
+
+
+=== 快速發佈 ===
+
+假設你寫了一個腳本,想和他人分享。你可以只告訴他們從你的計算機下載,但如果此
+時你正在改進你的腳本,或加入試驗性質的改動,他們下載了你的腳本,他們可能由此
+陷入困境。當然,這就是發佈周期存在的原因。開發人員可能頻繁進行項目修改,但他
+們只在他們覺得代碼可以見人的時候才擇時發佈。
+
+用Git來完成這項,需要進入你的腳本所在目錄:
+
+ $ git init
+ $ git add .
+ $ git commit -m "First release"
+
+然後告訴你的用戶去運行:
+
+ $ git clone your.computer:/path/to/script
+
+來下載你的腳本。這要假定他們有ssh訪問權限。如果沒有,需要運行 *git daemon* 並
+告訴你的用戶去運行:
+
+ $ git clone git://your.computer/path/to/script
+
+從現在開始,每次你的腳本準備好發佈時,就運行:
+
+ $ git commit -a -m "Next release"
+
+並且你的用戶可以通過進入包含你腳本的目錄,並鍵入下列命令,來更新他們的版本:
+
+ $ git pull
+
+你的用戶永遠也不會取到你不想讓他們看到的腳本版本。顯然這個技巧對所有的東西都
+是可以,不僅是對腳本。
+
+
+=== 我們已經做了什麼? ===
+
+找出自從上次提交之後你已經做了什麼改變:
+
+ $ git diff
+
+或者自昨天的改變:
+
+ $ git diff "@{yesterday}"
+
+或者一個特定版本與倒數第二個變更之間:
+
+ $ git diff 1b6d "master~2"
+
+輸出結果都是補丁格式,可以用 *git apply* 來把補丁打上。也可以試一下:
+
+ $ git whatchanged --since="2 weeks ago"
+
+我也經常用http://sourceforge.net/projects/qgit[qgit] 瀏覽歷史, 因為他的圖形界
+面很養眼,或者 http://jonas.nitro.dk/tig/[tig] ,一個文本界面的東西,很慢的網
+絡狀況下也工作的很好。也可以安裝web 伺服器,運行 *git instaweb* ,就可以用任
+何瀏覽器瀏覽了。
+
+=== 練習 ===
+
+比方A,B,C,D是四個連續的提交,其中B與A一樣,除了一些檔案刪除了。我們想把這
+些刪除的檔案加回D。我們如何做到這個呢?
+
+至少有三個解決方案。假設我們在D:
+
+ 1. A與B的差別是那些刪除的檔案。我們可以創建一個補丁代表這些差別,然後吧補丁
+ 打上:
+
+ $ git diff B A | git apply
+
+ 2. 既然這些檔案存在A,我們可以把它們拿出來:
+
+ $ git checkout A foo.c bar.h
+
+ 3. 我們可以把從A到B的變化視為可撤銷的變更:
+
+ $ git revert B
+
+哪個選擇最好?這取決於你的喜好。利用Git滿足自己需求是容易,經常還有多個方法。
View
236 zh_tw/branch.txt
@@ -0,0 +1,236 @@
+== 分支巫術 ==
+
+即時分支合併是Git最給力的殺手鐧。
+
+*問題* :外部因素要求必須切換場景。在發佈版本中突然蹦出個嚴重缺陷。某個特性完
+成的截至日期就要來臨。在項目關鍵部分可以提供幫助的一個開發正打算離職。所有情
+況逼迫你停下所有手頭工作,全力撲到到這個完全不同的任務上。
+
+打斷思維的連續性會使你的生產力大大降低,並且切換上下文也更麻煩,更大的損失。
+使用中心版本控制我們必須從中心伺服器下載一個新的工作拷貝。分散式系統的情況就
+好多了,因為我們能夠在本地克隆所需要的版本。
+
+但是克隆仍然需要拷貝整個工作目錄,還有直到給定點的整個歷史記錄。儘管Git使用文
+件共享和硬連結減少了花費,項目檔案自身還是必須在新的工作目錄裡重建。
+
+*方案* :Git有一個更好的工具對付這種情況,比克隆快多了而且節省空間: *git
+ branch* 。
+
+使用這個魔咒,目錄裡的檔案突然從一個版本變到另一個。除了只是在歷史記錄裡上跳
+下竄外,這個轉換還可以做更多。你的檔案可以從上一個發佈版變到實驗版本到當前開
+發版本到你朋友的版本等等。
+
+=== 老闆鍵 ===
+
+曾經玩過那樣的遊戲嗎?按一個鍵(“老闆鍵”),屏幕立即顯示一個電子表格或別的?
+那麼如果老闆走進辦公室,而你正在玩遊戲,就可以快速將遊戲藏起來。
+
+在某個目錄:
+
+ $ echo "I'm smarter than my boss" > myfile.txt
+ $ git init
+ $ git add .
+ $ git commit -m "Initial commit"
+
+我們已經創建了一個Git倉庫,該倉庫記錄一個包含特定信息的檔案。現在我們鍵入:
+
+ $ git checkout -b boss # 之後似乎沒啥變化
+ $ echo "My boss is smarter than me" > myfile.txt
+ $ git commit -a -m "Another commit"
+
+看起來我們剛剛只是覆蓋了原來的檔案並提交了它。但這是個錯覺。鍵入:
+
+ $ git checkout master # 切到檔案的原先版本
+
+嘿真快!這個檔案就恢復了。並且如果老闆決定窺視這個目錄,鍵入:
+
+ $ git checkout boss # 切到適合老闆看的版本
+
+你可以在兩個版本之間相切多少次就切多少次,而且每個版本都可以獨立提交。
+
+=== 骯髒的工作 ===
+
+[[branch]]
+
+比如你正在開發某個特性,並且由於某種原因,你需要回退三個版本,臨時加進幾行打
+印語句來,來看看一些東西是如何工作的。那麼:
+
+ $ git commit -a
+ $ git checkout HEAD~3
+
+現在你可以到處加醜陋的臨時代碼。你甚至可以提交這些改動。當你做完的時候,
+
+ $ git checkout master
+
+來返回到你原來的工作。看,所有未提交變更都結轉了。
+
+如果你後來想保存臨時變更怎麼辦?簡單:
+
+ $ git checkout -b dirty
+
+只要在切換到主分支之前提交就可以了。無論你什麼時候想回到髒的變更,只需鍵入:
+
+ $ git checkout dirty
+
+我們在前面章節討論加載舊狀態的時候,曾經接觸過這個命令。最終我們把故事說全:
+檔案改變成請求的狀態,但我們必須離開主分支。從現在開始的任何提交都會將你的文
+件提交到另一條不同的路,這個路可以之後命名。
+
+換一個說法,在checkout一個舊狀態之後,Git自動把你放到一個新的,未命名的分支,
+這個分支可以使用 *git checkout -b* 來命名和保存。
+
+=== 快速修訂 ===
+
+你正在做某件事的當間,被告知先停所有的事情,去修理一個新近發現的臭蟲,這個臭
+蟲在提交 `1b6d...`:
+
+ $ git commit -a
+ $ git checkout -b fixes 1b6d
+
+那麼一旦你修正了這個臭蟲:
+
+ $ git commit -a -m "Bug fixed"
+ $ git checkout master
+
+並可以繼續你原來的任務。你甚至可以“合併”到最新修訂:
+
+ $ git merge fixes
+
+=== 合併 ===
+
+一些版本控制系統,創建分支很容易,但把分支合併回來很難。使用Git,合併簡直是家
+常便飯,以至于甚至你可能對其發生沒有察覺。
+
+我們很久之前就遇到合併了。 *pull* 命令取出提交併合並它們到你的當前分支。如果
+你沒有本地變更,那這個合併就是一個“快進”,相當於中心式版本控制系統裡的一個
+弱化的獲取最新版本操作。但如有本地變更,Git將自動合併,並報告任何衝突。
+
+通常,一個提交只有一個“父提交”,也叫前一個提交。合併分支到一起產生一個至少
+有兩個父的提交。這就引出了問題: `HEAD~10` 真正指哪個提交?一個提交可能有多個
+父,那我們跟哪個呢?
+
+原來這個表示每次選擇第一個父。這是可取的,因為在合併時候當前分支成了第一個父;
+多數情況下我們只關注我們在當前分支都改了什麼,而不是從其他分支合併來的變更。
+
+你可以用插入符號來特別指定父。比如,顯示來自第二個父的日誌:
+
+ $ git log HEAD^2
+
+你可以忽略數字以指代第一個父。比如,顯示與第一個父的差別:
+
+ $ git diff HEAD^
+
+你可以結合其他類型使用這個記號。比如:
+
+ $ git checkout 1b6d^^2~10 -b ancient
+
+開始一個新分支 ``ancient'' ,表示第一個父的第二個父的倒數第十次提交的狀態。
+
+=== 不間斷工作流 ===
+
+經常在硬件項目裡,計劃的第二步必須等第一步完成才能開始。待修的汽車傻等在車庫
+裡,直到特定的零件從工廠運來。一個原型在其可以構建之前,可能苦等晶片成型。
+
+軟件項目可能也類似。新功能的第二部分不得不等待,直到第一部分發佈並通過測試。
+一些項目要求你的代碼需要審批才能接受,因此你可能需要等待第一部分得到批准,才
+能開始第二部分。
+
+多虧了無痛分支合併,我們可以不必遵循這些規則,在第一部分正式準備好前開始第二
+部分的工作。假設你已經將第一部分提交並發去審批,比如說你現在在主分支。那麼分
+岔:
+
+ $ git checkout -b part2
+
+接下來,做第二部分,隨時可以提交變更。只要是人就可能犯錯誤,經常你將回到第一
+部分在修修補補。如果你非常幸運,或者超級棒,你可能不必做這幾行:
+
+ $ git checkout master # 回到第一部分
+ $ 修復問題
+ $ git commit -a # 提交變更
+ $ git checkout part2 # 回到第二部分
+ $ git merge master # 合併這些改動
+
+最終,第一部分獲得批准:
+
+ $ git checkout master # 回到第一部分
+ $ submit files # 對世界發佈
+ $ git merge part2 # 合併第二部分
+ $ git branch -d part2 # 刪除分支“part2”
+
+現在你再次處在主分支,第二部分的代碼也在工作目錄。
+
+很容易擴展這個技巧,應用到任意數目的部分。它也很容易追溯分支:假如你很晚才意
+識到你本應在7次提交前就創建分支。那麼鍵入:
+
+ $ git branch -m master part2 # 重命名“master”分支為“part2”。
+ $ git branch master HEAD~7 # 以七次前提交建一個新的“master”。
+
+分支 `master` 只有第一部分內容,其他內容在分支 `part2` 。 我們現在後一個分支;
+我們創建了 `master` 分支還沒有切換過去,因為我們想繼續工作在 `part2` 。這是不
+尋常的。直到現在,我們已經在創建之後切換到分支,如:
+
+ $ git checkout HEAD~7 -b master # 創建分支,並切換過去。
+
+=== 重組雜亂 ===
+
+或許你喜歡在同一個分支下完成工作的方方面面。你想為自己保留工作進度並希望其他
+人只能看到你仔細整理過後的提交。開啟一對分支:
+
+ $ git branch sanitized # 為乾淨提交創建分支
+ $ git checkout -b medley # 創建並切換分支以進去工作
+
+接下來,做任何事情:修臭蟲,加特性,加臨時代碼,諸如此類,經常按這種方式提交。
+然後:
+
+ $ git checkout sanitized
+ $ git cherry-pick medley^^
+
+應用分支 ``medley'' 的祖父提交到分支 ``sanitized'' 。通過合適的挑選(像選櫻桃
+那樣)你可以構建一個只包含成熟代碼的分支,而且相關的提交也組織在一起。
+
+=== 管理分支 ===
+
+列出所有分支:
+
+ $ git branch
+
+預設你從叫 ``master'' 的分支開始。一些人主張別碰“master”分支,而是創建你自
+己版本的新分支。
+
+選項 *-d* 和 *-m* 允許你來刪除和移動(重命名)分支。參見 *git help branch* 。
+
+分支``master'' 是一個有用的慣例。其他人可能假定你的倉庫有一個叫這個名字的分
+支,並且該分支包含你項目的官方版本。儘管你可以重命名或抹殺 ``master'' 分支,
+你最好還是尊重這個約定。
+
+=== 臨時分支 ===
+
+很快你會發現你經常會因為一些相似的原因創建短期的分支:每個其它分支只是為了保
+存當前狀態,那樣你就可以直接跳到較老狀態以修復高優先順序的臭蟲之類。
+
+可以和電視的換台做類比,臨時切到別的頻道,來看看其它台那正放什麼。但並不是簡
+單地按幾個按鈕,你不得不創建,檢出,合併,以及刪除臨時分支。幸運的是,Git已經
+有了和電視機遙控器一樣方便的快捷方式:
+
+ $ git stash
+
+這個命令保存當前狀態到一個臨時的地方(一個隱藏的地方)並且恢復之前狀態。你的
+工作目錄看起來和你開始編輯之前一樣,並且你可以修復臭蟲,引入之前變更等。當你
+想回到隱藏狀態的時候,鍵入:
+
+ $ git stash apply # 你可能需要解決一些衝突
+
+你可以有多個隱藏,並用不同的方式來操作他們。參見 *git help slash* 。也許你已
+經猜到,Git維護在這個場景之後的分支以執行魔法技巧.
+
+=== 按你希望的方式工作 ===
+
+你可能猶疑于分支是否值得一試。畢竟,克隆也几乎一樣快,並且你可以用 *cd* 來在
+彼此之間切換,而不是用Git深奧的命令。
+
+考慮一下瀏覽器。為什麼同時支持多標籤和多窗口?因為允許兩者同時接納納了多種風
+格的用戶。一些用戶喜歡只保持一個打開的窗口,然後用標籤瀏覽多個網頁。一些可能
+堅持另一個極端:任何地方都沒有標籤的多窗口。一些喜好處在兩者之間。
+
+分支類似你工作目錄的標籤,克隆類似打開的瀏覽器新窗口。這些是本地操作很快,那
+為什麼不試着找出最適合你的組合呢?Git讓你按你確實所希望的那樣工作。
View
228 zh_tw/clone.txt
@@ -0,0 +1,228 @@
+== 克隆周邊 ==
+
+在較老一代的版本控制系統裡,checkout是獲取檔案的標準操作。你將獲得一組特定保
+存狀態的檔案。
+
+在Git和其他分散式版本控制系統裡,克隆是標準的操作。通過創建整個倉庫的克隆來
+獲得檔案。或者說,你實際上把整個中心伺服器做了個鏡像。凡是主倉庫上能做的事,
+你都能做。
+
+=== 計算機間同步 ===
+
+我可以忍受製作tar包或利用rsync來作備份和基本同步。但我有時在我筆記本上編輯,
+其他時間在台式機上,而且這倆之間也許並不交互。
+
+在一個機器上初始化一個Git倉庫並提交你的檔案。然後轉到另一台機器上:
+
+ $ git clone other.computer:/path/to/files
+
+以創建這些檔案和Git倉庫的第二個拷貝。從現在開始,
+
+ $ git commit -a
+ $ git pull other.computer:/path/to/files HEAD
+
+將把另一台機器上特定狀態的檔案“拉”到你正工作的機器上。如果你最近對同一個文
+件做了有衝突的修改,Git將通知你,而你也應該在解決衝突之後再次提交。
+
+=== 典型源碼控制 ===
+
+為你的檔案初始化Git倉庫:
+
+ $ git init
+ $ git add .
+ $ git commit -m "Initial commit"
+
+在中心伺服器,在某個目錄初始化一個“裸倉庫”:
+
+ $ mkdir proj.git
+ $ cd proj.git
+ $ git init --bare
+ $ # 只用一行命令: GIT_DIR=proj.git git init
+
+如果需要的話,啟動Git守護進程:
+
+ $ git daemon --detach # 它也許已經在運行了
+
+對一些Git伺服服務,按照其指導來初始化空Git倉庫。一般是在網頁上填一個表單。
+
+把你的項目“推”到中心伺服器:
+ $ git push git://central.server/path/to/proj.git HEAD
+
+撿出源碼,開發鍵入:
+
+ $ git clone git://central.server/path/to/proj.git
+
+做了改動之後,開發保存變更到本地:
+
+ $ git commit -a
+
+更新到最近版本:
+
+ $ git pull
+
+所有衝突應被處理,然後提交:
+
+ $ git commit -a
+
+把本地改動撿入到中心倉庫:
+
+ $ git push
+
+如果主伺服器由於其他開發的活動,有了新的變更,這個撿入會失敗,該開發應該把最
+新版本拿下來,解決合併衝突,然後重試。
+
+=== 裸倉庫 ===
+
+之所以叫裸倉庫是因為其沒有工作目錄;它只包含正常情況下隱藏在`.git`子目錄下
+的檔案。換句話說,它維護項目歷史,而且從不保存任何給定版本的快照。
+
+裸倉庫扮演的角色和中心版本控制系統中中心伺服器的角色類似:你項目的中心。開
+發從其中克隆項目,撿入新近改動。典型地裸倉庫存在一個伺服器上,該伺服器除了
+分散數據外並不做啥。開發活動發生在克隆上,因此中心倉庫沒有工作目錄也行。
+
+
+很多Git命令在裸倉庫上失敗,除非指定倉庫路徑到環境變數`GIT_DIR`,或者指定
+`--bare`選項。
+
+=== 推還是拽 ===
+
+為什麼我們介紹了push命令,而不是依賴熟悉的pull命令?首先,在裸倉庫上pull會
+失敗:除非你必須“fetch”,一個之後我們要討論的命令。但即使我們在中心伺服器上
+保持一個正常的倉庫,拽些東西進去仍然很繁瑣。我們不得不登陸伺服器先,給pull
+命令我們要拽自機器的網絡地址。防火牆會阻礙,並且首先如果我們沒有到伺服器的
+shell訪問怎麼辦呢?
+
+然而,除了這個案例,我們反對推進倉庫,因為當目標有工作目錄時,困惑隨之而來。
+
+簡短截說,學習Git的時候,只在目標是裸倉庫的時候push,否則用pull的方式。
+
+=== 項目分叉 ===
+
+項目走歪了嗎?或者認為你可以做得更好?那麼在伺服器上:
+
+ $ git clone git://main.server/path/to/files
+
+之後告訴每個相關的人你伺服器上項目的分支。
+
+在之後的時間,你可以合併來自原先項目的改變,使用命令:
+
+ $ git pull
+
+=== 終極備份 ===
+
+會有很多散佈在各處,禁止篡改的冗餘存檔嗎? 如果你的項目有很多開發,那乾脆啥也
+別做了。你的每份代碼克隆是一個有效備份。不僅當前狀態,還包括你項目整個歷史。
+感謝哈希加密算法,如果任何人的克隆被損壞,只要他們與其他的交互,這個克隆就會
+被修好。
+
+如果你的項目並不是那麼流行,那就找儘可能多的伺服來放克隆吧。
+
+真正的偏執狂應該總是把HEAD最近20位元組的SHA1哈希值寫到安全的地方。應該保證安全,
+而不是把它藏起來。比如,把它發佈到報紙上就不錯,因為對攻擊者而言,更改每份報
+紙是很難的。
+
+=== 輕快多任務 ===
+
+比如你想並行開發多個功能。那麼提交你的項目並運行:
+
+ $ git clone . /some/new/directory
+
+Git使用硬連結和檔案共享來儘可能安全地創建克隆,因此它一眨眼就完成了,因此你現
+在可以並行操作兩個沒有相互依賴的功能。例如,你可以編輯一個克隆,同時編譯另一
+個。感謝 http://en.wikipedia.org/wiki/Hard_link[hardlinking], 本地克隆比簡單
+備份省時省地。
+
+現在你可以同時工作在兩個彼此獨立的特性上。比如,你可以在編譯一個克隆的時候編
+輯另一個克隆。任何時候,你都可以從其它克隆提交並拖拽變更。
+
+ $ git pull /the/other/clone HEAD
+
+=== 游擊版本控制 ===
+
+你正做一個使用其他版本控制系統的項目, 而你非常思念Git? 那麼在你的工作目錄初
+始化一個Git倉庫:
+
+ $ git init
+ $ git add .
+ $ git commit -m "Initial commit"
+
+然後克隆它:
+
+ $ git clone . /some/new/directory
+
+並在這個目錄工作,按你所想在使用Git。過一會,一旦你想和其他每個人同步,在這種
+情況下,轉到原來的目錄,用其他的版本控制工具同步,並鍵入:
+
+ $ git add .
+ $ git commit -m "Sync with everyone else"
+
+現在轉到新目錄運行:
+
+ $ git commit -a -m "Description of my changes"
+ $ git pull
+
+把你的變更提交給他人的過程依賴于其他版本控制系統。這個新目錄包含你的改動的文
+件。需要運行其他版本控制系統的命令來上載這些變更到中心倉庫。
+
+Subversion, 或許是最好的中心式版本控制系統,為無數項目所用。 *git svn* 命令為
+Subversion倉庫自動化了上面的操作,並且也可以用作
+http://google-opensource.blogspot.com/2008/05/export-git-project-to-google-code.html[
+導出Git項目到Subversion倉庫] 的替代。
+
+=== Mercurial ===
+
+Mercurial是一個類似的的版本控制系統,几乎可以和Git一起無縫工作。使用
+`hg-git`插件,一個Mercurial用戶可以無損地往Git倉庫推送,從Git倉庫拖拽。
+
+使用Git獲得`hg-git`插件:
+
+ $ git clone git://github.com/schacon/hg-git.git
+
+或使用Mercurial:
+
+ $ hg clone http://bitbucket.org/durin42/hg-git/
+
+不好意思,我沒注意Git有類似的插件。因此, 我主張使用Git而不是Mercurial作為主資
+源庫,即使你偏愛Mercurial。使用Mercurial項目,通常一個自願者維護一個平行的
+Git項目以適應Git用戶,然而感謝`hg-git`插件,一個Git項目自動地適應Mercurial用
+戶。
+
+儘管該插件可以把一個Mercurial倉庫轉成一個Git倉庫,通過推到一個空的倉庫,
+這個差事交給`hg-fast-export.sh`腳本還是更容易些。來自:
+
+ $ git clone git://repo.or.cz/fast-export.git
+
+要轉化,只需在一個空目錄運行:
+
+ $ git init
+ $ hg-fast-export.sh -r /hg/repo
+
+注意該腳本應加入你的`$PATH`。
+
+=== Bazaar ===
+
+我們簡略提一下Bazaar,它畢竟是緊跟Git和Mercurial之後最流行的自由分散式版本控
+制系統。
+
+Bazaar有後來者的優勢,它相對年輕些;它的設計者可以從前人的錯誤中學習,並且躲
+過去翻歷史上犯過的錯誤。另外,它的開發人員對可移植性以及和與其它版本控制系統
+的互操作性也考慮周全。
+
+一個`bzr-git`插件讓Bazaar用戶在一定程度下可以工作在Git倉庫。`tailor`程序轉
+換Bazaar倉庫到Git倉庫,並且可以遞增的方式做,要知道`bzr-fast-export`只是
+在一次性轉換性情況下工作良好。
+
+=== 我偏愛Git的原因 ===
+
+我起先選擇Git是因為我聽說它能管理不可想象地不可管理的Linux內核源碼。我從來沒
+覺得有離開的必要。Git已經服侍的很好了,並且我也沒有被其瑕疵所困擾。因為我主要
+使用Linux,其他平台上的問題與我無關。
+
+還有,我偏愛C程序和bash腳本,以及諸如Python的可執行可腳本:較少依賴,並且我也
+沉迷于快速的執行時間。
+
+我考慮過Git才能如何提高,甚至自己寫類似的工具,但只作為研究練練手。即使完成這
+個項目,我也無論如何會繼續使用Git,因為使用一個古裡古怪的系統所獲甚微。
+
+自然地,你的需求和期望可能不同,並且你可能使用另一個系統會好些。儘管如此,使
+用Git你都錯不太遠。
View
133 zh_tw/drawbacks.txt
@@ -0,0 +1,133 @@
+== 附錄 A: Git的缺點 ==
+
+有一些Git的問題,我已經藏在毯子下面了。有些可以通過腳本或回調方法輕易地解決,
+有些需要重組或重定義項目,少數剩下的煩惱,還只能等待。或者更好地,投入進來幫
+忙。
+
+=== SHA1 的弱點 ===
+
+隨着時間的推移,密碼學家發現越來越多的SHA1的弱點。已經發現對對資源雄厚的組織
+哈希衝撞是可能的。在幾年內,或許甚至一個一般的PC也將有足夠計算能力悄悄摧毀一
+個Git倉庫。
+
+希望在進一步研究摧毀SHA1之前,Git能遷移到一個更好的哈希算法。
+
+=== 微軟 Windows ===
+
+Git在微軟Windows上可能有些繁瑣:
+
+- http://cygwin.com/[Cygwin] ,, 一個Windows下的類Linux的環境,包含一個 http://cygwin.com/packages/git/[ 一個Git在Windows下的移植].
+
+- http://code.google.com/p/msysgit/[基于MSys的Git] 是另一個,要求最小運行時支持,不過一些命令不能馬上工作。
+
+=== 不相關的檔案 ===
+
+如果你的項目非常大,包含很多不相關的檔案,而且正在不斷改變,Git可能比其他系統
+更不管用,因為獨立的檔案是不被跟蹤的。Git跟蹤整個項目的變更,這通常才是有益的。
+
+一個方案是將你的項目拆成小塊,每個都由相關檔案組成。如果你仍然希望在同一個資
+源庫裡保存所有內容的話,可以使用 *git submodule* 。
+
+=== 誰在編輯什麼? ===
+
+一些版本控制系統在編輯前強迫你顯示地用某個方法標記一個檔案。儘管這種要求很煩
+人,尤其是需要和中心伺服器通訊時,不過它還是有以下兩個好處的:
+
+ 1. 比較速度快,因為只有被標記的檔案需要檢查。
+