Show More
@@ -63,23 +63,31 b' merge is simply checking whether each no' | |||||
63 | table of graph B. If not, we pull them in, adding them sequentially to |
|
63 | table of graph B. If not, we pull them in, adding them sequentially to | |
64 | the revlog. |
|
64 | the revlog. | |
65 |
|
65 | |||
66 | Graph resolving: |
|
66 | Branching and merging: | |
67 |
|
67 | |||
68 | Mercurial does branching by copying (or COWing) a repository and thus |
|
68 | Everything in Mercurial is potentially a branch and every user | |
69 | keeps everything nice and linear within a repository. However, when a |
|
69 | effectively works in their own branch. When you do a checkout, | |
70 | merge of repositories (a "pull") is done, we may often have two head |
|
70 | Mercurial remembers what the parent changeset was and uses it for the | |
71 | revisions in a given graph. To keep things simple, Mercurial forces |
|
71 | next check in. | |
72 | the head revisions to be merged. |
|
72 | ||
|
73 | To do a merge of branches in Mercurial, you check out the heads of the | |||
|
74 | two branches into the same working directory which causes a merge to | |||
|
75 | be performed, and then check in the result once you're happy with it. | |||
|
76 | The resulting checkin will have two parents. | |||
73 |
|
77 | |||
74 | It first finds the closest common ancestor of the two heads. If one is |
|
78 | It decides when a merge is necessary by first determining if there are | |
75 | a child of the other, it becomes the new head. Otherwise, we call out |
|
79 | any uncommitted changes in the working directory. This effectively | |
76 | to a user-specified 3-way merge tool. |
|
80 | makes the working directory a branch off the checked in version it's | |
|
81 | based on. Then it also determines if the working directory is a direct | |||
|
82 | ancestor or descendent of the second version we're attempting to | |||
|
83 | checkout. If neither is true, we simply replace the working directory | |||
|
84 | version with the new version. Otherwise we perform a merge between the | |||
|
85 | two versions. | |||
77 |
|
86 | |||
78 |
Merging files |
|
87 | Merging files and manifests: | |
79 |
|
88 | |||
80 | We begin by comparing changeset DAGs, pulling all nodes we don't have |
|
89 | We begin by comparing two versions manifests and deciding which files | |
81 | in our DAG from the other repository. As we do so, we collect a list |
|
90 | need to be added, deleted, and merged. | |
82 | of changed files to merge. |
|
|||
83 |
|
91 | |||
84 | Then for each file, we perform a graph merge and resolve as above. |
|
92 | Then for each file, we perform a graph merge and resolve as above. | |
85 | It's important to merge files using per-file DAGs rather than just |
|
93 | It's important to merge files using per-file DAGs rather than just | |
@@ -103,18 +111,8 b' aB AB file A is change in main' | |||||
103 | but if we look at the files independently, everything |
|
111 | but if we look at the files independently, everything | |
104 | is fine |
|
112 | is fine | |
105 |
|
113 | |||
106 | After we've merged files, we merge the manifest log DAG and resolve |
|
114 | The result is a merged version in the working directory, waiting for | |
107 | additions and deletions. Then we are ready to resolve the changeset |
|
115 | check-in. | |
108 | DAG - if our merge required any changes (the new head is not a |
|
|||
109 | decendent of our tip), we must create a new changeset describing all |
|
|||
110 | of the changes needed to merge it into the tip. |
|
|||
111 |
|
||||
112 | Merge performance: |
|
|||
113 |
|
||||
114 | The I/O operations for performing a merge are O(changed files), not |
|
|||
115 | O(total changes) and in many cases, we needn't even unpack the deltas |
|
|||
116 | to add them to our repository (though this optimization isn't |
|
|||
117 | necessary). |
|
|||
118 |
|
116 | |||
119 | Rollback: |
|
117 | Rollback: | |
120 |
|
118 | |||
@@ -123,36 +121,26 b' changeset entry gets added last. We keep' | |||||
123 | of each file touched and its length prior to the transaction. On |
|
121 | of each file touched and its length prior to the transaction. On | |
124 | abort, we simply truncate each file to its prior length. This is one |
|
122 | abort, we simply truncate each file to its prior length. This is one | |
125 | of the nice properties of the append-only structure of the revlogs. |
|
123 | of the nice properties of the append-only structure of the revlogs. | |
|
124 | We can also reuse this journal for "undo". | |||
126 |
|
125 | |||
127 | Remote access: |
|
126 | Merging between repositories: | |
128 |
|
127 | |||
129 | Mercurial currently supports pulling from "serverless" repositories. |
|
128 | One of the key features of Mercurial is the ability to merge between | |
130 | Simply making the repo directory accessibly via the web and pointing |
|
129 | independent repositories in a decentralized fashion. Each repository | |
131 | hg at it can accomplish a pull. This is relatively bandwidth efficient |
|
130 | can act as a read-only server or a client. Clients operating by | |
132 | but no effort has been spent on pipelining, so it won't work |
|
131 | pulling all branches that it hasn't seen from the server and adding | |
133 | especially well over LAN yet. |
|
132 | them into its graph. This is done in two steps: searching for new | |
134 |
|
133 | "roots" and pulling a "changegroup" | ||
135 | It's also quite amenable to rsync, if you don't mind keeping an intact |
|
|||
136 | copy of the master around locally. |
|
|||
137 |
|
134 | |||
138 | Also note the append-only and ordering properties of the commit |
|
135 | Searching for new "roots" begins by finding all new heads and | |
139 | guarantee that readers will always see a repository in a consistent |
|
136 | searching backwards from those heads to the first unknown nodes in | |
140 | state and no special locking is necessary. As there is generally only |
|
137 | their respective branches. These nodes are the 'roots' that are used | |
141 | one writer to an hg repository, there is in fact no exclusion |
|
138 | to calculate the 'changegroup': the set of all changesets starting at | |
142 | implemented yet. |
|
139 | those roots. Mercurial takes pains to make this search efficient in | |
143 |
|
140 | both bandwidth and round-trips. | ||
144 |
|
||||
145 | Some comparisons to git: |
|
|||
146 |
|
141 | |||
147 | Most notably, Mercurial uses delta compression and repositories |
|
142 | Once the roots are found, the changegroup can be transferred as a | |
148 | created with it will grow much more slowly over time. This also allows |
|
143 | single streaming transfer. This is organized as an ordered set of | |
149 | it to be much more bandwidth efficient. I expect repos sizes and sync |
|
144 | deltas for changesets, manifests, and files. Large chunks of deltas | |
150 | speeds to be similar to or better than BK, given the use of binary diffs. |
|
145 | can be directly added to the repository without unpacking so it's | |
151 |
|
146 | fairly fast. | ||
152 | Mercurial is roughly the same performance as git in some areas and is |
|
|||
153 | faster in others as it keeps around more metadata. One example is |
|
|||
154 | listing and retrieving past versions of a file, which it can do |
|
|||
155 | without reading all the changesets. This metadata will also allow it |
|
|||
156 | to perform better merges as described above. |
|
|||
157 |
|
||||
158 |
|
General Comments 0
You need to be logged in to leave comments.
Login now