原文地址:http://blog.csdn.net/ppn029012/article/details/8878416
1. 好好的,为什么要用版本控制
1.1 单身吊丝一个人(就你一个人)
幻想一下以下场景,你在用你家Windows中的传奇软件"记事本”写一篇小说<<武松三打白骨精>>。经过了1个多小时的单干,你的小说中,武松好不容易把白骨精1打死了。(血腥情节1)
又过了一个小时,你的武松又把白骨精2打死了。(血腥情节2)
突然间, 你觉得白1号死得太迅速了,应该和武松增加些感情戏, 于是需要在情节1中增加一段床戏,而相应地,在情节2中也需要做些修改,因为白2和白1是亲姐妹。 (血腥情节1 + 武松床戏 + 新血腥情节2)
虽然白3还没打完,但是你的快乐写作一切都显得很顺利。
突然有一天(想找老版本)
你回头审视,发现糟了,白1和白2是两个单独的女吊丝,根本不应该有亲情关系,所以“新血腥情节2”应该采用原来的血腥情节2更合适些。忘记备份了, 重写么?
忽然又有一天(想要去冒险)
虽然3只白骨精还没完全打完,但是有一天你想冒险一下,想尝试一下白1和白2是同性恋的关系文章会怎样,或者想尝试一下,武松发现白骨精2是自己的妹妹会有什么样的一个反应。但是又不想动摇当前文章的主线,该怎么办?
人民的智慧是无穷的 --- 新建文件夹(123)
很明显,这个问题,只要创建一个新建文件夹,然后把旧的文件拖进去就完了,最多在后面标上序号, 于是你有了新建文件夹(1),..(2)...(n)...
出现问题了?
啊?--- 100个文件夹! 好吧,有一天有人向你约稿<<一千零一夜>>, 你会发现你的桌面上的新建文件夹已经多得快放不下了。
啊? --- 没有层级关系! 所有文件夹全放在桌面上,到底哪一个是我的支线情节?
啊? --- 天知道这几个版本之间有什么区别啊? 新建文件夹234和236之间有什么区别?
啊? --- 电脑被炸了? 谁在我家投了个炸弹?别搞笑好么?
于是悟空还没疯,武松已经替你行了疯的道儿。
1.2 版本控制能为我做什么
-
我不想新建文件夹。 版本控制会帮你新建一个类似“文件夹”的东西,并且会很明显地给你这个文件夹带上几个信息"时间,描述”。
-
我需要层级关系。 你需要把这些版本与版本之间的关系解释清楚,谁包含谁,谁是谁的前一版本,这时候版本控制软件更是在行。
-
我想知道版本之间的区别。 很简单,让它帮你去比较就好了。
1.3 Git 和 Github能帮我做什么
Git 是你亲爱的版本控制软件里的一种,但是它有别于他的亲戚(subversion, cvs)的原因在于,他是分布式(暂时没想明白有什么意义)的,而且速度更快。
Github 就像是一个远端的仓库,你的所有版本都可以推到(Push)仓库里放着一份。这样就能有效地防止炸弹袭击。而你的小Git就是一辆小推车,可以帮助你把版本库推进去,又拉出来。
2. 用Git写《武松三打白骨精》的故事
2.1 让我们有个Git(安装Git)
这一步真的只能靠你自己了哦. 当然现在讲的是命令行版的(command line)。(你当然可以根据个人喜欢,装个有图形界面的...)
http://git-scm.com/book/en/Getting-Started-First-Time-Git-Setup
2.2 建立一个根据地
现在你要.找一块Git 能开始为你服务的地方,比如一个叫"武松三打白骨精”的文件夹.然后输入git init,git开始能为你服务了
../武松三打白骨精> git init
Initialised empty Git repository in ../武松三打白骨精/.git/
2.3 做爱 做的事
现在可以干正事了,什么是正事?写东西啊. 于是我在这文件夹里使用记事本新建了一个文件“好人一生平安.txt”, 然后在里面郑重其事地写下了第一段话:”一个男人和三个女人的事故从这开始!”
------ 好人一生平安.txt ------
一个男人和三个女人的事故从这里面开始。
当然你也可以尝试着多写点什么,直到你觉得你的第一版本已经很满意了,今天就到这,那么你就可以选择结束了。
2.4 向你的Git诉说你干了什么
这时你就可以告诉Git,我想要把这个简陋的版本保存一下,作为一个新的版本。用git add 语句就可以你想要保存的文件添加到这个版本的档案里。当然,如果你又修改了一下这个文件,那么还可以用git add 把新文件添加进去(覆盖).相反,你想把已添加的文件取消的话,用git rm --cached即可.
>git add 好人一生平安.txt
>git status
# On branch master
# Initial commit
#
# Changes to be committed:
# (use "git rm --cached <file>..." to unstage)
#
#new file: "好人一生平安.txt"
用git status 可以查看已经添加的文件的列表。
最后,你把你想要的东西都添加完了,决定这可以当做一个版本了。于是,提交吧
>git commit -m “就这么决定了,第一个版本! happy?”
[master (root-commit) 34d53ca] 就这么决定了,1st version
1 file changed, 1 insertion(+)
create mode 100644 "好人一生平安.txt"
commit 命令会很自觉地把你刚才提交的东西集合成一个新的版本(这个版本号为34d53ca)。而且会友好地提示你,新版本里有一个文件做了改变(添加了一个文件).
到此,你的第一个版本已经成功建立了。
2.5 新剧本的来临(查看历史)
新的一天来了,我们又为我们的大作写了一段。
------ 好人一生平安.txt ------
一个男人和三个女人的事故从这里面开始。
武松对着白骨精1拳打脚踢好几下,白1痛不欲生,故自尽。(血腥1)
觉得这版本不错,于是又使用git add 和 git commit把这个版本提交了。
>git add 好人一生平安.txt
>git commit -m “加了血腥1,受不了”
[master 24b2a77] 加了血腥1,受不了
1 file changed, 1 insertion(+)
我们有了两个版本现在。想不想回顾一下这两个版本的主要内容?用git log 就可以.
>git log
commit 24b2a77740de5b45820fe284270add2b9bd8216c
Author: pangyunong <pyn@xxx.com>
Date: Wed May 1 21:02:08 2013 +0100
加了血腥1,受不了
commit 34d53caf65284a8b4b1e9a1dd4debfe9f18bf63c
Author: pangyunong <pyn@xxx.com>
Date: Wed May 1 20:39:43 2013 +0100
就这么决定了,1st version
当然你要是嫌这个版本不好看, 可以来个图画版的,也很酷哦(现在版本太少不好看...)
>git log --pretty=format:'%h : %s' --topo-order --graph
* 24b2a77 : 加了血腥1,受不了
* 34d53ca : 就这么决定了,1st version
(如果你突然觉得还缺点什么才能提交,那么可以通过git commit --amend 补交缺漏文件 )
如果想对比一下这两个版本,那么可以用git diff <变动前的commit_id > <变动后的commit_id>来比较。
>git diff 34d53 24b2a
diff --git "a/好人一生平安.txt" "b/好人一生平安.txt"
index 65fb5fd..e618579 100644
--- "a/好人一生平安.txt"
+++ "b/好人一生平安.txt"
@@ -1 +1,2 @@
一个男人和三个女人的事故从这里面开始。
+武松对着白骨精1拳打脚踢好几下,白1痛不欲生,故自尽。(血腥1)
(详细的diff 输出格式解读可见http://www.ruanyifeng.com/blog/2012/08/how_to_read_diff.html)
这里简单介绍一下, @@-1 +1,2@@中的内容指的是下面内容的起始和结束行数(1,2).
“-”指的是改变前的文件(34d53), “+”指的是改变以后的文件(24b2a)
“+武松...”指的是 改变以后的文件增加了一行 “武松...”
2.6 看看老版本
为了然游戏更有趣,我再加两个版本.
* a92c316 : 打第二只排骨精
* 52b672d : 增加了血腥1的描写
* 24b2a77 : 加了血腥1,受不了
* 34d53ca : 就这么决定了,1st version
现在我们处于最新的版本上,也就是a92c316. 我们可以看看现在的文件都被写成啥样了.
------ 好人一生平安.txt ------
一个男人和三个女人的事故从这里面开始。
武松对着白骨精1拳打脚踢好几下,白1痛不欲生,故自尽。樱花飘落(血腥1)
白骨精二号出场,爱上武松,却被武松无视之,痛不欲生,(哀乐奏),悬崖摔下(血腥2)
现在我们想看看第一次血腥1是怎么写的, 那么,现在可以用checkout将版本跳回到24b2a.
(如果想要checkout 的版本并不在某根线的顶端,checkout会创造一根临时的支线,而你原来的主线(master)仍然保持不变)
>git checkout24b2a
Note: checking out '24b2a'.
You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by performing another checkout.
If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -b with the checkout command again. Example:
git checkout -b new_branch_name
HEAD is now at 24b2a77... 加了血腥1,受不了
版本示意图
如果看完了,又想回到最新版本继续写东西该怎么办? 使用checkout再跳回主线去就可以了,这时候,临时的支线就会被取消掉。只剩下孤单的主线了。
>git checkout master
2.7 无痛冒险开始
现在你要尝试着写写别的支线情节,比如说,白骨精1要和武松有一段新的恋情,但又不想毁坏当前的主线。所以你需要一条支线,如下图。
具体该怎么做呢?
先回到我们的"提交2”(增加血腥1,受不了)
>git checkout 24b2a
然后在"提交2”上建立一个我们的新分支.
>git checkout -b 新爱情分支
现在我们就可以用git branch 来查看我们现在有哪些分支和我们现在处于哪个分支上。
>git branch
master
* 新爱情分支
然后我们就可以在这个分支上尽情地发挥了,此处省去(git add. commit.. 云云 n字).
最后在版本(ws 与白1床戏)里的内容为,
------ 好人一生平安.txt ------
一个男人和三个女人的事故从这里面开始。
武松对着白骨精1拳打脚踢好几下,白1痛不欲生,正要自尽。(血腥1)
说时迟,那时快,武松一把抓住了白骨精,这是为何? (新恋情传奇)
情感有时就是那么奇妙,悄悄地就占住了你的心菲,让武松动弹不得,只得躺在床上(导演语:赶紧抬床),一阵抽搐。
2.8 删除冒险分支(冒险失败)
有的时候男人就是不负责任,在玩了一会爱情分支之后觉得不太靠谱,想要把爱情分支删掉,继续自己人生的主线。这时候
>git branch -d 新爱情分支
就可以帮助你删除掉那冒险的爱情分支了。
2.9合并分支和主线(爱情的冒险成功了!)
但是感情有时候就是那么奇妙,人鬼情未了,情来了又去,去了还还.
好了,现在突然你又发现这个爱情这段写得还是挺好的,想要把它融合到主线故事来。
有一个专业的术语叫着,合并分支,意味着,你需要把这两条线的东西融合成一条。想象中的结果应该如下图.
用 git merge <某分支线路>命令就能将某分支融合到当前所在分支来.
>git merge 新爱情分支
Auto-merging 好人一生平安.txt
CONFLICT (content): Merge conflict in 好人一生平安.txt
Automatic merge failed; fix conflicts and then commit the result.
这个提示说的是,现在Git已经为您合并好了一个新的commit, 但是这两个版本中的"好人一生平安.txt”是不同的,所以你需要把两个版本中有冲突的这个文件修改(揉合)一下。
很贴心地是,Git 在你执行完merge之后,已经帮你把“好人一生平安.txt”这个文件改成了包含两个冲突版本之间的内容了,你就不用在两个版本之间跳跃就可以很好地修改(揉合)这个文件。
------ 好人一生平安.txt ------
一个男人和三个女人的事故从这里面开始。
<<<<<<< HEAD
武松对着白骨精1拳打脚踢好几下,白1痛不欲生,故自尽。樱花飘落(血腥1)
白骨精二号出场,爱上武松,却被武松无视之,痛不欲生,(哀乐奏),悬崖摔下(血腥2)
=======
武松对着白骨精1拳打脚踢好几下,白1痛不欲生,正要自尽。(血腥1)
说时迟,那时快,武松一把抓住了白骨精,这是为何? (新恋情传奇)
情感有时就是那么奇妙,悄悄地就占住了你的心菲,让武松动弹不得,只得躺在床上(导演语:赶紧抬床),一阵抽搐。
>>>>>>> 新爱情分支
===== 分开的上下两段分别是master分支和新爱情分支对这段的描述。
揉合以后的文件是:
------ 好人一生平安.txt ------
一个男人和三个女人的事故从这里面开始。
武松对着白骨精1拳打脚踢好几下,白1痛不欲生,正要自尽。(血腥1)
说时迟,那时快,武松一把抓住了白骨精,这是为何? (新恋情传奇)
情感有时就是那么奇妙,悄悄地就占住了你的心菲,让武松动弹不得,只得躺在床上(导演语:赶紧抬床),一阵抽搐。
白骨精二号出场,爱上武松,却被武松无视之,痛不欲生,(哀乐奏),悬崖摔下(血腥2)
最后再使用commit 确认一下,版本的提交。
>git commit -m “新武松排骨传奇”
这样就完成了版本的融合,你的主线又得到了爱情的升华,从此武松与排骨1号就幸福地生活在了一起。看一下现在这几个版本的拓扑图,可以看到一个版本的分支,与合并的过程。
>git log --pretty=format:'%h : %s' --topo-order --graph
* da569ed : 新武松排骨传奇
|\
| * 10e88e1 : ws与白1床戏
| * eb63560 : 武松与白1恋爱了
* | 45a508a : 打第二排骨精
* | 52b672d : 增加了血腥1的描写
|/
* 24b2a77 : 加了血腥1,受不了
* 34d53ca : 就这么决定了,1st version
3. 访止炸弹袭击(把本地仓库备份到远端去)
3.1 申请一个Github账号
进入Github申请到账号,并且建立一个repository以后就能得到一个地址,这个以.git结尾的地址就能用来推送你本地的仓库。
例如, 我建立的仓库地址就是
https://github.com/wusong/story.git
3.2把现有的仓库推到远端去
>git remote add origin https://github.com/wusong/story.git
这句话将origin(远程服务器地址) 设置为https://github.com/wusong/story.git
然后就可以把当前的主线推到Github服务器上了
>git push -u origin master
当然你也可以把你用来冒险的支线也一块推到服务器上,
>git push -u origin 新爱情故事
3.3 我想在别的地方继续写小说
如果想在别的电脑(比如原来我在家写的,现在我在台湾)上继续写你的东西,发展你的项目,那么好,用clone指令把你放在Github上的东西取下来就可以了, 完事以后再推上去就好了。
>git clone https://github.com/wusong/story.git
(但是,要注意,当你在两个地方同时对一个仓库进行操作的话,会造成两个地方的仓库发生不同步的情况。当你遇到这种情况的时候,可以通过每次写东西前都先用git clone(或git pull)在你本地建立一个与服务器同步的最新的仓库,这样就可以保证文件的同步了。)
总结一下:
这篇文章以写小说(一个文本文件)来做为例子说明如何使用版本控制来进行对单机项目的管理。有时候看似很没有必要(同时也比较麻烦),但是当项目不断增大的时候,比如说一个程序项目里有成千上万个文件,用一般的粘贴复制的方法是没有办法进行版本控制的。
所以Git将会在你做大型项目的时候给你提供很好的管理方法,同时也能使你养成良好的项目管理的习惯。