#pragma section-numbers 2 = 理解 Mercurial = Mercurial 的分布式协同模式,对于新手而言是混乱的, 本文试图澄清一些基本概念,至于 hg 的使用,请参考:[[ChineseTutorial|教程]]. ''(Translations: [[ChineseUnderstandingMercurial|中文(简体)]], [[FrenchUnderstandingMercurial|French]], [[GermanUnderstandingMercurial|German]], [[ItalianUnderstandingMercurial|Italian]], [[JapaneseUnderstandingMercurial|Japanese]], [[KoreanUnderstandingMercurial|Korean]], [[RussianUnderstandingMercurial|Russian]], [[SpanishUnderstandingMercurial|Spanish]], [[ThaiUnderstandingMercurial|Thai]] )'' <> == 仓库(Repository)中有什么? == Mercurial [[Repository|仓库(Repository)]] 包含 [[WorkingDirectory|工作目录(Working Directory)]] 和版本仓库: {{{#!dot digraph G { rankdir = LR; compound=true; background="#999999"; subgraph cluster_0 { label="working directory"; style=filled; color=lightgrey; node [style=filled,color=white]; edge [style=invis]; "main.c" -> "main.h" -> ".hgignore" -> ".hgtags"; } subgraph cluster_1 { label = "store"; labelloc = b; style=filled; color="#eeeeee"; node [shape=box, style=filled, color=lightgray]; "rev 0" -> "rev 1" -> "rev 2" -> "rev 3" [dir=back, label="parent"]; } "main.c" -> "rev 2" [ltail=cluster_0, label="parent", labeldistance=5, minlen=2]; } }}} 版本仓库^存在于.hg隐藏目录中^包含了'''完整'''的项目历史. 不同与其它配置管理系统,那些集中式版本管理系统,只有一个包含所有版本历史的中央仓库,而每人本地的工作目录则仅包含当前最新版本. 这将有利于开发者进行并行协作. 工作目录包含的是项目文件当供给编辑的在某个时间点的状态(比如上图中的rev 2). Mercurial中的[[Tag|标签]] 和 [[.hgignore|忽略文件声明]] 也被版本控制,所以他们也包括在上图中. Mercurial中的每个版本都有其`父辈版本`,比如上图中rev 2的父辈是rev 1,而工作目录的父辈是rev 2. == 提交变更(Commit Changes) == [[Commit|提交(Commit)]]操作后,工作目录的 [[Parent|父辈版本]] 就成了刚刚提交的新 [[ChangeSet|变更集(Changeset)]] (也称为新 "[[Revision|版本(Revision)]]"): {{{#!dot digraph G { compound=true; rankdir = LR background="#999999"; subgraph cluster_0 { label="working directory"; style=filled; color=lightgrey; node [style=filled,color=white]; edge [style=invis]; "main.c"-> "main.h" -> ".hgignore" -> ".hgtags" } subgraph cluster_1 { label = "store"; labelloc = b; style=filled; color="#eeeeee"; node [shape=box,style=filled,color=lightgray]; "rev 0" -> "rev 1" -> "rev 2" -> "rev 3" [dir=back]; "rev 2" -> "rev 4" [dir=back]; } "rev 2" -> ".hgtags" [dir=back, style=dotted, lhead=cluster_0, label="parent before commit"] "rev 4" -> ".hgtags" [dir=back, color=red, lhead=cluster_0, headlabel="commit", labelfontcolor=red ] } }}} 上图中的`rev 4`是`rev 2`的一个'''[[Branch|分支]]''', 现在`rev 4`是工作目录的'''父辈'''. == 版本,变更集,头部,顶部 == Mercurial中多个文件的相关修改称为[[ChangeSet|变更集(ChangeSet)]], 每个`版本(Revision)`对应一个变更集. 每个变更集会分配一个递增的整数 [[RevisionNumber|版本号]]. 在分布式开发过程中, 各个用户的版本号会产生冲突. 因此每个变更及也会被分配一个全局唯一的[[ChangeSetID|变更集ID]]. 变更集ID是四十位的16进制数字, 也可以略写成足够明确的"e38487"形式. {{{#!dot digraph { rankdir = LR node [shape=record] rev0 [label="{{ p1 | p2} | rev 0:838e}"]; rev1 [label="{{ p1 | p2} | rev 1:34ef}"]; rev2 [label="{{ p1 | p2} | rev 2:4563}"]; rev3 [label="{{ p1 | p2} | rev 3:fe56}"]; rev4 [label="{{ p1 | p2} | rev 4:ac98}"]; rev5 [label="{{ p1 | p2} | rev 5:0345}"]; rev6 [label="{{ p1 | p2} | rev 6:19e3 (tip)}"]; workingdir [label="{{ p1 | p2} | working directory}"]; rev0 -> rev1:p1 [dir=back] rev1 -> rev2:p1 [dir=back] rev1 -> rev3:p1 [dir=back] rev2 -> rev4:p1 [dir=back] rev3 -> rev4:p2 [dir=back] rev4 -> rev5:p1 [dir=back] rev4 -> rev6:p1 [dir=back] rev6 -> workingdir:p1 [dir=back] label="example repository" } }}} 在版本历史的任何一点,都可以进行分支与[[Merge|合并(Merge)]]. 而每一个未合并的分支,实际都创建了版本历史的一个新[[Head|头部(Head)]]. Here, revisions 5 and 6 are heads. Mercurial considers revision 6 to be the [[Tip|tip]] of the repository, the head with the highest revision number. 上图中的`rev 5`和`rev 6`都是头部. 版本号最大的头部被称为[[Tip|顶部]], 如上图中的`rev 6`. `rev 4`有'''两个'''父辈(`rev 2` 和`rev 3`),它是个[[MergeChangeset|合并变更集]]. == 克隆,变更,合并,拉和更新 == 假设用户Alice有如下所示的仓库: {{{#!dot digraph { label="Alice's repo" rankdir = LR node [shape=box] a -> b -> c -> d -> "working dir" [dir=back] } }}} Bob 在本地[[Clone|克隆(Clone)]]了这个仓库, 得到了Alice的版本仓库的一个完整、独立的本地副本. 并通过检出操作,获得了最新`顶部`版本. {{{#!dot digraph { label="Bob's repo" rankdir = LR node [shape=box] a -> b -> c -> d -> "working dir" [dir=back] } }}} Bob[[Commit|提交(Commit)]]两个修改`e`和`f` ^在他本地仓库^: {{{#!dot digraph { label="Bob's repo" rankdir = LR node [shape=box] a -> b -> c -> d -> e -> f -> "working dir" [dir=back] e [color=blue] f [color=blue] } }}} 同时,Alice也修改她的版本`g`, 因此她的仓库与Bob的不同了, 也就是说,她创建了一个 [[Branch|分支(Branch)]]: {{{#!dot digraph { label="Alice's repo" rankdir = LR node [shape=box] a -> b -> c -> d -> g -> "working dir" [dir=back] g [color=red] } }}} Bob使用[[Pull|pulls]]^拉^操作将Alice的仓库变更到本地. 这个操作将Alice的所有修改集更新到Bob的`版本仓库`中 (这个例子中只有一个修改集g). 需要注意的是,这个操作并 '''没有''' 更改Bob的工作目录: {{{#!dot digraph { label="Bob's repo" rankdir = LR node [shape=box] a -> b -> c -> d -> e -> f -> "working dir" [dir=back] e [color=blue] f [color=blue] d -> g [dir=back] g [color=red, label="g (tip)"] } }}} 因为Alice's '''g''' 版本是最新的`头部`, 因此此版本也是 '''tip'''^顶部^. Bob随后进行了 [[Merge|合并]] 操作, 将其本地修改(f)与仓库中的'''tip'''进行合并. 这时, 他的工作目录具有两个父辈(f和g): {{{#!dot digraph { label="Bob's repo" rankdir = LR node [shape=box] a -> b -> c -> d -> e -> f [dir=back] e [color=blue] f [color=blue] d -> g [dir=back] g [color=red] f -> "working dir" [dir=back, weight=3.0] g -> "working dir" [dir=back, weight=3.0] } }}} 查看并确认操作合并成功后, Bob 提交合并结果,得到了一个新的 [[MergeChangeset|合并变更集]] `h`在他的本地仓库中: {{{#!dot digraph { label="Bob's repo" rankdir = LR node [shape=box] a -> b -> c -> d -> e -> f [dir=back] e [color=blue] f [color=blue] d -> g [dir=back] g [color=red] f -> h [dir=back, weight=3.0] g -> h [dir=back, weight=3.0] h -> "working dir" [dir=back] h [color=green, label="h (tip)"] } }}} 现在,如果Alice '''pulls''' 从 Bob的仓库, 她会得到Bob的变更 `e`, `f`和`h`: {{{#!dot digraph { label="Alice's repo" rankdir = LR node [shape=box] a -> b -> c -> d -> e -> f [dir=back] e [color=blue] f [color=blue] d -> g [dir=back] g [color=red] f -> h [dir=back, weight=3.0] g -> h [dir=back, weight=3.0] g -> "working dir" [dir=back] h [color=green;label="h (tip)"] } }}} 注意! 当前 Alice 的工作目录并没有因 pull^拉^操作而改变. 她必须使用 [[Update|更新(Update)]] 操作来同步版本仓库到合并变更集 `h`. 这将改变她版本仓库的父辈到`h`,并将工作目录中的文件更新成`h`版本的. {{{#!dot digraph { label="Alice's repo" rankdir = LR node [shape=box] a -> b -> c -> d -> e -> f [dir=back] e [color=blue] f [color=blue] d -> g [dir=back] g [color=red] f -> h [dir=back, weight=3.0] g -> h [dir=back, weight=3.0] h -> "working dir" [dir=back] h [color=green, label="h (tip)"] } }}} Alice和Bob完全同步了. == 分布式系统 == Mercurial是一个完全的分布式系统, 因此没有所谓的集中式仓库概念. 这也意味着用户可以自由的定义协同工作的组织结构 (参阅 [[CommunicatingChanges|变更传播]] ): {{{#!dot digraph { Alice -> Central Central -> Alice Bob -> Central Alice -> Bob Alice -> Carl Carl -> Central Bob -> Carl Carl -> Bob "Carl's Laptop" -> Carl Carl -> "Carl's Laptop" "Carl's Laptop" -> Central Central [style=fill;color=blue;label="Main Public Repo"] label="A Mercurial Network" } }}} 在一个集中式版本管理系统中提交实验性的修改可能会造成较大的负面影响, 但对于Mercurial之类的DVCS来说, 可以肆意的进行试验性操作, 大不了删除本地工作目录,因为在别的地方还有若干完整的。 == 什么是Mercurial不能作的 == SVN/CVS用户会将多个相关的项目放在同一个仓库里. 但是这真的不应该在Hg 中这么做,因为在Hg 中你只能检出整个仓库,而不是其中的某个目录. 如果确实想要将多个项目放在同一个仓库中, 可以使用1.3版本以后的[[ChineseSubrepository|子仓库]] 功能或者更老版本的 ForestExtension 将不同项目的仓库,嵌套成一个大仓库. 关于Mercurial的入门操作, 请参阅 [[ChineseTutorial|教程]]. ---- CategoryChinese CategoryChinese