##// END OF EJS Templates
salvaged: persist the salvaged set on disk...
marmoute -
r46261:e53778ad default
parent child Browse files
Show More
@@ -1,914 +1,918 b''
1 # coding: utf8
1 # coding: utf8
2 # metadata.py -- code related to various metadata computation and access.
2 # metadata.py -- code related to various metadata computation and access.
3 #
3 #
4 # Copyright 2019 Google, Inc <martinvonz@google.com>
4 # Copyright 2019 Google, Inc <martinvonz@google.com>
5 # Copyright 2020 Pierre-Yves David <pierre-yves.david@octobus.net>
5 # Copyright 2020 Pierre-Yves David <pierre-yves.david@octobus.net>
6 #
6 #
7 # This software may be used and distributed according to the terms of the
7 # This software may be used and distributed according to the terms of the
8 # GNU General Public License version 2 or any later version.
8 # GNU General Public License version 2 or any later version.
9 from __future__ import absolute_import, print_function
9 from __future__ import absolute_import, print_function
10
10
11 import multiprocessing
11 import multiprocessing
12 import struct
12 import struct
13
13
14 from . import (
14 from . import (
15 error,
15 error,
16 node,
16 node,
17 pycompat,
17 pycompat,
18 util,
18 util,
19 )
19 )
20
20
21 from .revlogutils import (
21 from .revlogutils import (
22 flagutil as sidedataflag,
22 flagutil as sidedataflag,
23 sidedata as sidedatamod,
23 sidedata as sidedatamod,
24 )
24 )
25
25
26
26
27 class ChangingFiles(object):
27 class ChangingFiles(object):
28 """A class recording the changes made to files by a changeset
28 """A class recording the changes made to files by a changeset
29
29
30 Actions performed on files are gathered into 3 sets:
30 Actions performed on files are gathered into 3 sets:
31
31
32 - added: files actively added in the changeset.
32 - added: files actively added in the changeset.
33 - merged: files whose history got merged
33 - merged: files whose history got merged
34 - removed: files removed in the revision
34 - removed: files removed in the revision
35 - salvaged: files that might have been deleted by a merge but were not
35 - salvaged: files that might have been deleted by a merge but were not
36 - touched: files affected by the merge
36 - touched: files affected by the merge
37
37
38 and copies information is held by 2 mappings
38 and copies information is held by 2 mappings
39
39
40 - copied_from_p1: {"<new-name>": "<source-name-in-p1>"} mapping for copies
40 - copied_from_p1: {"<new-name>": "<source-name-in-p1>"} mapping for copies
41 - copied_from_p2: {"<new-name>": "<source-name-in-p2>"} mapping for copies
41 - copied_from_p2: {"<new-name>": "<source-name-in-p2>"} mapping for copies
42
42
43 See their inline help for details.
43 See their inline help for details.
44 """
44 """
45
45
46 def __init__(
46 def __init__(
47 self,
47 self,
48 touched=None,
48 touched=None,
49 added=None,
49 added=None,
50 removed=None,
50 removed=None,
51 merged=None,
51 merged=None,
52 salvaged=None,
52 salvaged=None,
53 p1_copies=None,
53 p1_copies=None,
54 p2_copies=None,
54 p2_copies=None,
55 ):
55 ):
56 self._added = set(() if added is None else added)
56 self._added = set(() if added is None else added)
57 self._merged = set(() if merged is None else merged)
57 self._merged = set(() if merged is None else merged)
58 self._removed = set(() if removed is None else removed)
58 self._removed = set(() if removed is None else removed)
59 self._touched = set(() if touched is None else touched)
59 self._touched = set(() if touched is None else touched)
60 self._salvaged = set(() if salvaged is None else salvaged)
60 self._salvaged = set(() if salvaged is None else salvaged)
61 self._touched.update(self._added)
61 self._touched.update(self._added)
62 self._touched.update(self._merged)
62 self._touched.update(self._merged)
63 self._touched.update(self._removed)
63 self._touched.update(self._removed)
64 self._p1_copies = dict(() if p1_copies is None else p1_copies)
64 self._p1_copies = dict(() if p1_copies is None else p1_copies)
65 self._p2_copies = dict(() if p2_copies is None else p2_copies)
65 self._p2_copies = dict(() if p2_copies is None else p2_copies)
66
66
67 def __eq__(self, other):
67 def __eq__(self, other):
68 return (
68 return (
69 self.added == other.added
69 self.added == other.added
70 and self.merged == other.merged
70 and self.merged == other.merged
71 and self.removed == other.removed
71 and self.removed == other.removed
72 and self.salvaged == other.salvaged
72 and self.salvaged == other.salvaged
73 and self.touched == other.touched
73 and self.touched == other.touched
74 and self.copied_from_p1 == other.copied_from_p1
74 and self.copied_from_p1 == other.copied_from_p1
75 and self.copied_from_p2 == other.copied_from_p2
75 and self.copied_from_p2 == other.copied_from_p2
76 )
76 )
77
77
78 @util.propertycache
78 @util.propertycache
79 def added(self):
79 def added(self):
80 """files actively added in the changeset
80 """files actively added in the changeset
81
81
82 Any file present in that revision that was absent in all the changeset's
82 Any file present in that revision that was absent in all the changeset's
83 parents.
83 parents.
84
84
85 In case of merge, this means a file absent in one of the parents but
85 In case of merge, this means a file absent in one of the parents but
86 existing in the other will *not* be contained in this set. (They were
86 existing in the other will *not* be contained in this set. (They were
87 added by an ancestor)
87 added by an ancestor)
88 """
88 """
89 return frozenset(self._added)
89 return frozenset(self._added)
90
90
91 def mark_added(self, filename):
91 def mark_added(self, filename):
92 if 'added' in vars(self):
92 if 'added' in vars(self):
93 del self.added
93 del self.added
94 self._added.add(filename)
94 self._added.add(filename)
95 self.mark_touched(filename)
95 self.mark_touched(filename)
96
96
97 def update_added(self, filenames):
97 def update_added(self, filenames):
98 for f in filenames:
98 for f in filenames:
99 self.mark_added(f)
99 self.mark_added(f)
100
100
101 @util.propertycache
101 @util.propertycache
102 def merged(self):
102 def merged(self):
103 """files actively merged during a merge
103 """files actively merged during a merge
104
104
105 Any modified files which had modification on both size that needed merging.
105 Any modified files which had modification on both size that needed merging.
106
106
107 In this case a new filenode was created and it has two parents.
107 In this case a new filenode was created and it has two parents.
108 """
108 """
109 return frozenset(self._merged)
109 return frozenset(self._merged)
110
110
111 def mark_merged(self, filename):
111 def mark_merged(self, filename):
112 if 'merged' in vars(self):
112 if 'merged' in vars(self):
113 del self.merged
113 del self.merged
114 self._merged.add(filename)
114 self._merged.add(filename)
115 self.mark_touched(filename)
115 self.mark_touched(filename)
116
116
117 def update_merged(self, filenames):
117 def update_merged(self, filenames):
118 for f in filenames:
118 for f in filenames:
119 self.mark_merged(f)
119 self.mark_merged(f)
120
120
121 @util.propertycache
121 @util.propertycache
122 def removed(self):
122 def removed(self):
123 """files actively removed by the changeset
123 """files actively removed by the changeset
124
124
125 In case of merge this will only contain the set of files removing "new"
125 In case of merge this will only contain the set of files removing "new"
126 content. For any file absent in the current changeset:
126 content. For any file absent in the current changeset:
127
127
128 a) If the file exists in both parents, it is clearly "actively" removed
128 a) If the file exists in both parents, it is clearly "actively" removed
129 by this changeset.
129 by this changeset.
130
130
131 b) If a file exists in only one parent and in none of the common
131 b) If a file exists in only one parent and in none of the common
132 ancestors, then the file was newly added in one of the merged branches
132 ancestors, then the file was newly added in one of the merged branches
133 and then got "actively" removed.
133 and then got "actively" removed.
134
134
135 c) If a file exists in only one parent and at least one of the common
135 c) If a file exists in only one parent and at least one of the common
136 ancestors using the same filenode, then the file was unchanged on one
136 ancestors using the same filenode, then the file was unchanged on one
137 side and deleted on the other side. The merge "passively" propagated
137 side and deleted on the other side. The merge "passively" propagated
138 that deletion, but didn't "actively" remove the file. In this case the
138 that deletion, but didn't "actively" remove the file. In this case the
139 file is *not* included in the `removed` set.
139 file is *not* included in the `removed` set.
140
140
141 d) If a file exists in only one parent and at least one of the common
141 d) If a file exists in only one parent and at least one of the common
142 ancestors using a different filenode, then the file was changed on one
142 ancestors using a different filenode, then the file was changed on one
143 side and removed on the other side. The merge process "actively"
143 side and removed on the other side. The merge process "actively"
144 decided to drop the new change and delete the file. Unlike in the
144 decided to drop the new change and delete the file. Unlike in the
145 previous case, (c), the file included in the `removed` set.
145 previous case, (c), the file included in the `removed` set.
146
146
147 Summary table for merge:
147 Summary table for merge:
148
148
149 case | exists in parents | exists in gca || removed
149 case | exists in parents | exists in gca || removed
150 (a) | both | * || yes
150 (a) | both | * || yes
151 (b) | one | none || yes
151 (b) | one | none || yes
152 (c) | one | same filenode || no
152 (c) | one | same filenode || no
153 (d) | one | new filenode || yes
153 (d) | one | new filenode || yes
154 """
154 """
155 return frozenset(self._removed)
155 return frozenset(self._removed)
156
156
157 def mark_removed(self, filename):
157 def mark_removed(self, filename):
158 if 'removed' in vars(self):
158 if 'removed' in vars(self):
159 del self.removed
159 del self.removed
160 self._removed.add(filename)
160 self._removed.add(filename)
161 self.mark_touched(filename)
161 self.mark_touched(filename)
162
162
163 def update_removed(self, filenames):
163 def update_removed(self, filenames):
164 for f in filenames:
164 for f in filenames:
165 self.mark_removed(f)
165 self.mark_removed(f)
166
166
167 @util.propertycache
167 @util.propertycache
168 def salvaged(self):
168 def salvaged(self):
169 """files that might have been deleted by a merge, but still exists.
169 """files that might have been deleted by a merge, but still exists.
170
170
171 During a merge, the manifest merging might select some files for
171 During a merge, the manifest merging might select some files for
172 removal, or for a removed/changed conflict. If at commit time the file
172 removal, or for a removed/changed conflict. If at commit time the file
173 still exists, its removal was "reverted" and the file is "salvaged"
173 still exists, its removal was "reverted" and the file is "salvaged"
174 """
174 """
175 return frozenset(self._salvaged)
175 return frozenset(self._salvaged)
176
176
177 def mark_salvaged(self, filename):
177 def mark_salvaged(self, filename):
178 if "salvaged" in vars(self):
178 if "salvaged" in vars(self):
179 del self.salvaged
179 del self.salvaged
180 self._salvaged.add(filename)
180 self._salvaged.add(filename)
181 self.mark_touched(filename)
181 self.mark_touched(filename)
182
182
183 def update_salvaged(self, filenames):
183 def update_salvaged(self, filenames):
184 for f in filenames:
184 for f in filenames:
185 self.mark_salvaged(f)
185 self.mark_salvaged(f)
186
186
187 @util.propertycache
187 @util.propertycache
188 def touched(self):
188 def touched(self):
189 """files either actively modified, added or removed"""
189 """files either actively modified, added or removed"""
190 return frozenset(self._touched)
190 return frozenset(self._touched)
191
191
192 def mark_touched(self, filename):
192 def mark_touched(self, filename):
193 if 'touched' in vars(self):
193 if 'touched' in vars(self):
194 del self.touched
194 del self.touched
195 self._touched.add(filename)
195 self._touched.add(filename)
196
196
197 def update_touched(self, filenames):
197 def update_touched(self, filenames):
198 for f in filenames:
198 for f in filenames:
199 self.mark_touched(f)
199 self.mark_touched(f)
200
200
201 @util.propertycache
201 @util.propertycache
202 def copied_from_p1(self):
202 def copied_from_p1(self):
203 return self._p1_copies.copy()
203 return self._p1_copies.copy()
204
204
205 def mark_copied_from_p1(self, source, dest):
205 def mark_copied_from_p1(self, source, dest):
206 if 'copied_from_p1' in vars(self):
206 if 'copied_from_p1' in vars(self):
207 del self.copied_from_p1
207 del self.copied_from_p1
208 self._p1_copies[dest] = source
208 self._p1_copies[dest] = source
209
209
210 def update_copies_from_p1(self, copies):
210 def update_copies_from_p1(self, copies):
211 for dest, source in copies.items():
211 for dest, source in copies.items():
212 self.mark_copied_from_p1(source, dest)
212 self.mark_copied_from_p1(source, dest)
213
213
214 @util.propertycache
214 @util.propertycache
215 def copied_from_p2(self):
215 def copied_from_p2(self):
216 return self._p2_copies.copy()
216 return self._p2_copies.copy()
217
217
218 def mark_copied_from_p2(self, source, dest):
218 def mark_copied_from_p2(self, source, dest):
219 if 'copied_from_p2' in vars(self):
219 if 'copied_from_p2' in vars(self):
220 del self.copied_from_p2
220 del self.copied_from_p2
221 self._p2_copies[dest] = source
221 self._p2_copies[dest] = source
222
222
223 def update_copies_from_p2(self, copies):
223 def update_copies_from_p2(self, copies):
224 for dest, source in copies.items():
224 for dest, source in copies.items():
225 self.mark_copied_from_p2(source, dest)
225 self.mark_copied_from_p2(source, dest)
226
226
227
227
228 def compute_all_files_changes(ctx):
228 def compute_all_files_changes(ctx):
229 """compute the files changed by a revision"""
229 """compute the files changed by a revision"""
230 p1 = ctx.p1()
230 p1 = ctx.p1()
231 p2 = ctx.p2()
231 p2 = ctx.p2()
232 if p1.rev() == node.nullrev and p2.rev() == node.nullrev:
232 if p1.rev() == node.nullrev and p2.rev() == node.nullrev:
233 return _process_root(ctx)
233 return _process_root(ctx)
234 elif p1.rev() != node.nullrev and p2.rev() == node.nullrev:
234 elif p1.rev() != node.nullrev and p2.rev() == node.nullrev:
235 return _process_linear(p1, ctx)
235 return _process_linear(p1, ctx)
236 elif p1.rev() == node.nullrev and p2.rev() != node.nullrev:
236 elif p1.rev() == node.nullrev and p2.rev() != node.nullrev:
237 # In the wild, one can encounter changeset where p1 is null but p2 is not
237 # In the wild, one can encounter changeset where p1 is null but p2 is not
238 return _process_linear(p1, ctx, parent=2)
238 return _process_linear(p1, ctx, parent=2)
239 elif p1.rev() == p2.rev():
239 elif p1.rev() == p2.rev():
240 # In the wild, one can encounter such "non-merge"
240 # In the wild, one can encounter such "non-merge"
241 return _process_linear(p1, ctx)
241 return _process_linear(p1, ctx)
242 else:
242 else:
243 return _process_merge(p1, p2, ctx)
243 return _process_merge(p1, p2, ctx)
244
244
245
245
246 def _process_root(ctx):
246 def _process_root(ctx):
247 """compute the appropriate changed files for a changeset with no parents
247 """compute the appropriate changed files for a changeset with no parents
248 """
248 """
249 # Simple, there was nothing before it, so everything is added.
249 # Simple, there was nothing before it, so everything is added.
250 md = ChangingFiles()
250 md = ChangingFiles()
251 manifest = ctx.manifest()
251 manifest = ctx.manifest()
252 for filename in manifest:
252 for filename in manifest:
253 md.mark_added(filename)
253 md.mark_added(filename)
254 return md
254 return md
255
255
256
256
257 def _process_linear(parent_ctx, children_ctx, parent=1):
257 def _process_linear(parent_ctx, children_ctx, parent=1):
258 """compute the appropriate changed files for a changeset with a single parent
258 """compute the appropriate changed files for a changeset with a single parent
259 """
259 """
260 md = ChangingFiles()
260 md = ChangingFiles()
261 parent_manifest = parent_ctx.manifest()
261 parent_manifest = parent_ctx.manifest()
262 children_manifest = children_ctx.manifest()
262 children_manifest = children_ctx.manifest()
263
263
264 copies_candidate = []
264 copies_candidate = []
265
265
266 for filename, d in parent_manifest.diff(children_manifest).items():
266 for filename, d in parent_manifest.diff(children_manifest).items():
267 if d[1][0] is None:
267 if d[1][0] is None:
268 # no filenode for the "new" value, file is absent
268 # no filenode for the "new" value, file is absent
269 md.mark_removed(filename)
269 md.mark_removed(filename)
270 else:
270 else:
271 copies_candidate.append(filename)
271 copies_candidate.append(filename)
272 if d[0][0] is None:
272 if d[0][0] is None:
273 # not filenode for the "old" value file was absent
273 # not filenode for the "old" value file was absent
274 md.mark_added(filename)
274 md.mark_added(filename)
275 else:
275 else:
276 # filenode for both "old" and "new"
276 # filenode for both "old" and "new"
277 md.mark_touched(filename)
277 md.mark_touched(filename)
278
278
279 if parent == 1:
279 if parent == 1:
280 copied = md.mark_copied_from_p1
280 copied = md.mark_copied_from_p1
281 elif parent == 2:
281 elif parent == 2:
282 copied = md.mark_copied_from_p2
282 copied = md.mark_copied_from_p2
283 else:
283 else:
284 assert False, "bad parent value %d" % parent
284 assert False, "bad parent value %d" % parent
285
285
286 for filename in copies_candidate:
286 for filename in copies_candidate:
287 copy_info = children_ctx[filename].renamed()
287 copy_info = children_ctx[filename].renamed()
288 if copy_info:
288 if copy_info:
289 source, srcnode = copy_info
289 source, srcnode = copy_info
290 copied(source, filename)
290 copied(source, filename)
291
291
292 return md
292 return md
293
293
294
294
295 def _process_merge(p1_ctx, p2_ctx, ctx):
295 def _process_merge(p1_ctx, p2_ctx, ctx):
296 """compute the appropriate changed files for a changeset with two parents
296 """compute the appropriate changed files for a changeset with two parents
297
297
298 This is a more advance case. The information we need to record is summarise
298 This is a more advance case. The information we need to record is summarise
299 in the following table:
299 in the following table:
300
300
301 β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
301 β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
302 β”‚ diff β•² diff β”‚ ΓΈ β”‚ (Some, None) β”‚ (None, Some) β”‚ (Some, Some) β”‚
302 β”‚ diff β•² diff β”‚ ΓΈ β”‚ (Some, None) β”‚ (None, Some) β”‚ (Some, Some) β”‚
303 β”‚ p2 β•² p1 β”‚ β”‚ β”‚ β”‚ β”‚
303 β”‚ p2 β•² p1 β”‚ β”‚ β”‚ β”‚ β”‚
304 β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
304 β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
305 β”‚ β”‚ β”‚πŸ„± No Changes β”‚πŸ„³ No Changes β”‚ β”‚
305 β”‚ β”‚ β”‚πŸ„± No Changes β”‚πŸ„³ No Changes β”‚ β”‚
306 β”‚ ΓΈ β”‚πŸ„° No Changes β”‚ OR β”‚ OR β”‚πŸ„΅ No Changes β”‚
306 β”‚ ΓΈ β”‚πŸ„° No Changes β”‚ OR β”‚ OR β”‚πŸ„΅ No Changes β”‚
307 β”‚ β”‚ β”‚πŸ„² Deleted[1] β”‚πŸ„΄ Salvaged[2]β”‚ [3] β”‚
307 β”‚ β”‚ β”‚πŸ„² Deleted[1] β”‚πŸ„΄ Salvaged[2]β”‚ [3] β”‚
308 β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
308 β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
309 β”‚ β”‚πŸ„Ά No Changes β”‚ β”‚ β”‚ β”‚
309 β”‚ β”‚πŸ„Ά No Changes β”‚ β”‚ β”‚ β”‚
310 β”‚ (Some, None) β”‚ OR β”‚πŸ„» Deleted β”‚ ΓΈ β”‚ ΓΈ β”‚
310 β”‚ (Some, None) β”‚ OR β”‚πŸ„» Deleted β”‚ ΓΈ β”‚ ΓΈ β”‚
311 β”‚ β”‚πŸ„· Deleted[1] β”‚ β”‚ β”‚ β”‚
311 β”‚ β”‚πŸ„· Deleted[1] β”‚ β”‚ β”‚ β”‚
312 β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
312 β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
313 β”‚ β”‚πŸ„Έ No Changes β”‚ β”‚ β”‚ β”‚
313 β”‚ β”‚πŸ„Έ No Changes β”‚ β”‚ β”‚ β”‚
314 β”‚ (None, Some) β”‚ OR β”‚ ΓΈ β”‚πŸ„Ό Added β”‚πŸ„½ Merged β”‚
314 β”‚ (None, Some) β”‚ OR β”‚ ΓΈ β”‚πŸ„Ό Added β”‚πŸ„½ Merged β”‚
315 β”‚ β”‚πŸ„Ή Salvaged[2]β”‚ β”‚ (copied?) β”‚ (copied?) β”‚
315 β”‚ β”‚πŸ„Ή Salvaged[2]β”‚ β”‚ (copied?) β”‚ (copied?) β”‚
316 β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
316 β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
317 β”‚ β”‚ β”‚ β”‚ β”‚ β”‚
317 β”‚ β”‚ β”‚ β”‚ β”‚ β”‚
318 β”‚ (Some, Some) β”‚πŸ„Ί No Changes β”‚ ΓΈ β”‚πŸ„Ύ Merged β”‚πŸ„Ώ Merged β”‚
318 β”‚ (Some, Some) β”‚πŸ„Ί No Changes β”‚ ΓΈ β”‚πŸ„Ύ Merged β”‚πŸ„Ώ Merged β”‚
319 β”‚ β”‚ [3] β”‚ β”‚ (copied?) β”‚ (copied?) β”‚
319 β”‚ β”‚ [3] β”‚ β”‚ (copied?) β”‚ (copied?) β”‚
320 β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
320 β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
321
321
322 Special case [1]:
322 Special case [1]:
323
323
324 The situation is:
324 The situation is:
325 - parent-A: file exists,
325 - parent-A: file exists,
326 - parent-B: no file,
326 - parent-B: no file,
327 - working-copy: no file.
327 - working-copy: no file.
328
328
329 Detecting a "deletion" will depend on the presence of actual change on
329 Detecting a "deletion" will depend on the presence of actual change on
330 the "parent-A" branch:
330 the "parent-A" branch:
331
331
332 Subcase πŸ„± or πŸ„Ά : if the state of the file in "parent-A" is unchanged
332 Subcase πŸ„± or πŸ„Ά : if the state of the file in "parent-A" is unchanged
333 compared to the merge ancestors, then parent-A branch left the file
333 compared to the merge ancestors, then parent-A branch left the file
334 untouched while parent-B deleted it. We simply apply the change from
334 untouched while parent-B deleted it. We simply apply the change from
335 "parent-B" branch the file was automatically dropped.
335 "parent-B" branch the file was automatically dropped.
336 The result is:
336 The result is:
337 - file is not recorded as touched by the merge.
337 - file is not recorded as touched by the merge.
338
338
339 Subcase πŸ„² or πŸ„· : otherwise, the change from parent-A branch were explicitly dropped and
339 Subcase πŸ„² or πŸ„· : otherwise, the change from parent-A branch were explicitly dropped and
340 the file was "deleted again". From a user perspective, the message
340 the file was "deleted again". From a user perspective, the message
341 about "locally changed" while "remotely deleted" (or the other way
341 about "locally changed" while "remotely deleted" (or the other way
342 around) was issued and the user chose to deleted the file.
342 around) was issued and the user chose to deleted the file.
343 The result:
343 The result:
344 - file is recorded as touched by the merge.
344 - file is recorded as touched by the merge.
345
345
346
346
347 Special case [2]:
347 Special case [2]:
348
348
349 The situation is:
349 The situation is:
350 - parent-A: no file,
350 - parent-A: no file,
351 - parent-B: file,
351 - parent-B: file,
352 - working-copy: file (same content as parent-B).
352 - working-copy: file (same content as parent-B).
353
353
354 There are three subcases depending on the ancestors contents:
354 There are three subcases depending on the ancestors contents:
355
355
356 - A) the file is missing in all ancestors,
356 - A) the file is missing in all ancestors,
357 - B) at least one ancestor has the file with filenode β‰  from parent-B,
357 - B) at least one ancestor has the file with filenode β‰  from parent-B,
358 - C) all ancestors use the same filenode as parent-B,
358 - C) all ancestors use the same filenode as parent-B,
359
359
360 Subcase (A) is the simpler, nothing happend on parent-A side while
360 Subcase (A) is the simpler, nothing happend on parent-A side while
361 parent-B added it.
361 parent-B added it.
362
362
363 The result:
363 The result:
364 - the file is not marked as touched by the merge.
364 - the file is not marked as touched by the merge.
365
365
366 Subcase (B) is the counter part of "Special case [1]", the file was
366 Subcase (B) is the counter part of "Special case [1]", the file was
367 modified on parent-B side, while parent-A side deleted it. However this
367 modified on parent-B side, while parent-A side deleted it. However this
368 time, the conflict was solved by keeping the file (and its
368 time, the conflict was solved by keeping the file (and its
369 modification). We consider the file as "salvaged".
369 modification). We consider the file as "salvaged".
370
370
371 The result:
371 The result:
372 - the file is marked as "salvaged" by the merge.
372 - the file is marked as "salvaged" by the merge.
373
373
374 Subcase (C) is subtle variation of the case above. In this case, the
374 Subcase (C) is subtle variation of the case above. In this case, the
375 file in unchanged on the parent-B side and actively removed on the
375 file in unchanged on the parent-B side and actively removed on the
376 parent-A side. So the merge machinery correctly decide it should be
376 parent-A side. So the merge machinery correctly decide it should be
377 removed. However, the file was explicitly restored to its parent-B
377 removed. However, the file was explicitly restored to its parent-B
378 content before the merge was commited. The file is be marked
378 content before the merge was commited. The file is be marked
379 as salvaged too. From the merge result perspective, this is similar to
379 as salvaged too. From the merge result perspective, this is similar to
380 Subcase (B), however from the merge resolution perspective they differ
380 Subcase (B), however from the merge resolution perspective they differ
381 since in (C), there was some conflict not obvious solution to the
381 since in (C), there was some conflict not obvious solution to the
382 merge (That got reversed)
382 merge (That got reversed)
383
383
384 Special case [3]:
384 Special case [3]:
385
385
386 The situation is:
386 The situation is:
387 - parent-A: file,
387 - parent-A: file,
388 - parent-B: file (different filenode as parent-A),
388 - parent-B: file (different filenode as parent-A),
389 - working-copy: file (same filenode as parent-B).
389 - working-copy: file (same filenode as parent-B).
390
390
391 This case is in theory much simple, for this to happens, this mean the
391 This case is in theory much simple, for this to happens, this mean the
392 filenode in parent-A is purely replacing the one in parent-B (either a
392 filenode in parent-A is purely replacing the one in parent-B (either a
393 descendant, or a full new file history, see changeset). So the merge
393 descendant, or a full new file history, see changeset). So the merge
394 introduce no changes, and the file is not affected by the merge...
394 introduce no changes, and the file is not affected by the merge...
395
395
396 However, in the wild it is possible to find commit with the above is not
396 However, in the wild it is possible to find commit with the above is not
397 True. For example repository have some commit where the *new* node is an
397 True. For example repository have some commit where the *new* node is an
398 ancestor of the node in parent-A, or where parent-A and parent-B are two
398 ancestor of the node in parent-A, or where parent-A and parent-B are two
399 branches of the same file history, yet not merge-filenode were created
399 branches of the same file history, yet not merge-filenode were created
400 (while the "merge" should have led to a "modification").
400 (while the "merge" should have led to a "modification").
401
401
402 Detecting such cases (and not recording the file as modified) would be a
402 Detecting such cases (and not recording the file as modified) would be a
403 nice bonus. However do not any of this yet.
403 nice bonus. However do not any of this yet.
404 """
404 """
405
405
406 md = ChangingFiles()
406 md = ChangingFiles()
407
407
408 m = ctx.manifest()
408 m = ctx.manifest()
409 p1m = p1_ctx.manifest()
409 p1m = p1_ctx.manifest()
410 p2m = p2_ctx.manifest()
410 p2m = p2_ctx.manifest()
411 diff_p1 = p1m.diff(m)
411 diff_p1 = p1m.diff(m)
412 diff_p2 = p2m.diff(m)
412 diff_p2 = p2m.diff(m)
413
413
414 cahs = ctx.repo().changelog.commonancestorsheads(
414 cahs = ctx.repo().changelog.commonancestorsheads(
415 p1_ctx.node(), p2_ctx.node()
415 p1_ctx.node(), p2_ctx.node()
416 )
416 )
417 if not cahs:
417 if not cahs:
418 cahs = [node.nullrev]
418 cahs = [node.nullrev]
419 mas = [ctx.repo()[r].manifest() for r in cahs]
419 mas = [ctx.repo()[r].manifest() for r in cahs]
420
420
421 copy_candidates = []
421 copy_candidates = []
422
422
423 # Dealing with case πŸ„° happens automatically. Since there are no entry in
423 # Dealing with case πŸ„° happens automatically. Since there are no entry in
424 # d1 nor d2, we won't iterate on it ever.
424 # d1 nor d2, we won't iterate on it ever.
425
425
426 # Iteration over d1 content will deal with all cases, but the one in the
426 # Iteration over d1 content will deal with all cases, but the one in the
427 # first column of the table.
427 # first column of the table.
428 for filename, d1 in diff_p1.items():
428 for filename, d1 in diff_p1.items():
429
429
430 d2 = diff_p2.pop(filename, None)
430 d2 = diff_p2.pop(filename, None)
431
431
432 if d2 is None:
432 if d2 is None:
433 # this deal with the first line of the table.
433 # this deal with the first line of the table.
434 _process_other_unchanged(md, mas, filename, d1)
434 _process_other_unchanged(md, mas, filename, d1)
435 else:
435 else:
436
436
437 if d1[0][0] is None and d2[0][0] is None:
437 if d1[0][0] is None and d2[0][0] is None:
438 # case πŸ„Ό β€” both deleted the file.
438 # case πŸ„Ό β€” both deleted the file.
439 md.mark_added(filename)
439 md.mark_added(filename)
440 copy_candidates.append(filename)
440 copy_candidates.append(filename)
441 elif d1[1][0] is None and d2[1][0] is None:
441 elif d1[1][0] is None and d2[1][0] is None:
442 # case πŸ„» β€” both deleted the file.
442 # case πŸ„» β€” both deleted the file.
443 md.mark_removed(filename)
443 md.mark_removed(filename)
444 elif d1[1][0] is not None and d2[1][0] is not None:
444 elif d1[1][0] is not None and d2[1][0] is not None:
445 # case πŸ„½ πŸ„Ύ πŸ„Ώ
445 # case πŸ„½ πŸ„Ύ πŸ„Ώ
446 md.mark_merged(filename)
446 md.mark_merged(filename)
447 copy_candidates.append(filename)
447 copy_candidates.append(filename)
448 else:
448 else:
449 # Impossible case, the post-merge file status cannot be None on
449 # Impossible case, the post-merge file status cannot be None on
450 # one side and Something on the other side.
450 # one side and Something on the other side.
451 assert False, "unreachable"
451 assert False, "unreachable"
452
452
453 # Iteration over remaining d2 content deal with the first column of the
453 # Iteration over remaining d2 content deal with the first column of the
454 # table.
454 # table.
455 for filename, d2 in diff_p2.items():
455 for filename, d2 in diff_p2.items():
456 _process_other_unchanged(md, mas, filename, d2)
456 _process_other_unchanged(md, mas, filename, d2)
457
457
458 for filename in copy_candidates:
458 for filename in copy_candidates:
459 copy_info = ctx[filename].renamed()
459 copy_info = ctx[filename].renamed()
460 if copy_info:
460 if copy_info:
461 source, srcnode = copy_info
461 source, srcnode = copy_info
462 if source in p1_ctx and p1_ctx[source].filenode() == srcnode:
462 if source in p1_ctx and p1_ctx[source].filenode() == srcnode:
463 md.mark_copied_from_p1(source, filename)
463 md.mark_copied_from_p1(source, filename)
464 elif source in p2_ctx and p2_ctx[source].filenode() == srcnode:
464 elif source in p2_ctx and p2_ctx[source].filenode() == srcnode:
465 md.mark_copied_from_p2(source, filename)
465 md.mark_copied_from_p2(source, filename)
466 return md
466 return md
467
467
468
468
469 def _find(manifest, filename):
469 def _find(manifest, filename):
470 """return the associate filenode or None"""
470 """return the associate filenode or None"""
471 if filename not in manifest:
471 if filename not in manifest:
472 return None
472 return None
473 return manifest.find(filename)[0]
473 return manifest.find(filename)[0]
474
474
475
475
476 def _process_other_unchanged(md, mas, filename, diff):
476 def _process_other_unchanged(md, mas, filename, diff):
477 source_node = diff[0][0]
477 source_node = diff[0][0]
478 target_node = diff[1][0]
478 target_node = diff[1][0]
479
479
480 if source_node is not None and target_node is None:
480 if source_node is not None and target_node is None:
481 if any(not _find(ma, filename) == source_node for ma in mas):
481 if any(not _find(ma, filename) == source_node for ma in mas):
482 # case πŸ„² of πŸ„·
482 # case πŸ„² of πŸ„·
483 md.mark_removed(filename)
483 md.mark_removed(filename)
484 # else, we have case πŸ„± or πŸ„Ά : no change need to be recorded
484 # else, we have case πŸ„± or πŸ„Ά : no change need to be recorded
485 elif source_node is None and target_node is not None:
485 elif source_node is None and target_node is not None:
486 if any(_find(ma, filename) is not None for ma in mas):
486 if any(_find(ma, filename) is not None for ma in mas):
487 # case πŸ„΄ or πŸ„Ή
487 # case πŸ„΄ or πŸ„Ή
488 md.mark_salvaged(filename)
488 md.mark_salvaged(filename)
489 # else, we have case πŸ„³ or πŸ„Έ : simple merge without intervention
489 # else, we have case πŸ„³ or πŸ„Έ : simple merge without intervention
490 elif source_node is not None and target_node is not None:
490 elif source_node is not None and target_node is not None:
491 # case πŸ„΅ or πŸ„Ί : simple merge without intervention
491 # case πŸ„΅ or πŸ„Ί : simple merge without intervention
492 #
492 #
493 # In buggy case where source_node is not an ancestors of target_node.
493 # In buggy case where source_node is not an ancestors of target_node.
494 # There should have a been a new filenode created, recording this as
494 # There should have a been a new filenode created, recording this as
495 # "modified". We do not deal with them yet.
495 # "modified". We do not deal with them yet.
496 pass
496 pass
497 else:
497 else:
498 # An impossible case, the diff algorithm should not return entry if the
498 # An impossible case, the diff algorithm should not return entry if the
499 # file is missing on both side.
499 # file is missing on both side.
500 assert False, "unreachable"
500 assert False, "unreachable"
501
501
502
502
503 def _missing_from_all_ancestors(mas, filename):
503 def _missing_from_all_ancestors(mas, filename):
504 return all(_find(ma, filename) is None for ma in mas)
504 return all(_find(ma, filename) is None for ma in mas)
505
505
506
506
507 def computechangesetfilesadded(ctx):
507 def computechangesetfilesadded(ctx):
508 """return the list of files added in a changeset
508 """return the list of files added in a changeset
509 """
509 """
510 added = []
510 added = []
511 for f in ctx.files():
511 for f in ctx.files():
512 if not any(f in p for p in ctx.parents()):
512 if not any(f in p for p in ctx.parents()):
513 added.append(f)
513 added.append(f)
514 return added
514 return added
515
515
516
516
517 def get_removal_filter(ctx, x=None):
517 def get_removal_filter(ctx, x=None):
518 """return a function to detect files "wrongly" detected as `removed`
518 """return a function to detect files "wrongly" detected as `removed`
519
519
520 When a file is removed relative to p1 in a merge, this
520 When a file is removed relative to p1 in a merge, this
521 function determines whether the absence is due to a
521 function determines whether the absence is due to a
522 deletion from a parent, or whether the merge commit
522 deletion from a parent, or whether the merge commit
523 itself deletes the file. We decide this by doing a
523 itself deletes the file. We decide this by doing a
524 simplified three way merge of the manifest entry for
524 simplified three way merge of the manifest entry for
525 the file. There are two ways we decide the merge
525 the file. There are two ways we decide the merge
526 itself didn't delete a file:
526 itself didn't delete a file:
527 - neither parent (nor the merge) contain the file
527 - neither parent (nor the merge) contain the file
528 - exactly one parent contains the file, and that
528 - exactly one parent contains the file, and that
529 parent has the same filelog entry as the merge
529 parent has the same filelog entry as the merge
530 ancestor (or all of them if there two). In other
530 ancestor (or all of them if there two). In other
531 words, that parent left the file unchanged while the
531 words, that parent left the file unchanged while the
532 other one deleted it.
532 other one deleted it.
533 One way to think about this is that deleting a file is
533 One way to think about this is that deleting a file is
534 similar to emptying it, so the list of changed files
534 similar to emptying it, so the list of changed files
535 should be similar either way. The computation
535 should be similar either way. The computation
536 described above is not done directly in _filecommit
536 described above is not done directly in _filecommit
537 when creating the list of changed files, however
537 when creating the list of changed files, however
538 it does something very similar by comparing filelog
538 it does something very similar by comparing filelog
539 nodes.
539 nodes.
540 """
540 """
541
541
542 if x is not None:
542 if x is not None:
543 p1, p2, m1, m2 = x
543 p1, p2, m1, m2 = x
544 else:
544 else:
545 p1 = ctx.p1()
545 p1 = ctx.p1()
546 p2 = ctx.p2()
546 p2 = ctx.p2()
547 m1 = p1.manifest()
547 m1 = p1.manifest()
548 m2 = p2.manifest()
548 m2 = p2.manifest()
549
549
550 @util.cachefunc
550 @util.cachefunc
551 def mas():
551 def mas():
552 p1n = p1.node()
552 p1n = p1.node()
553 p2n = p2.node()
553 p2n = p2.node()
554 cahs = ctx.repo().changelog.commonancestorsheads(p1n, p2n)
554 cahs = ctx.repo().changelog.commonancestorsheads(p1n, p2n)
555 if not cahs:
555 if not cahs:
556 cahs = [node.nullrev]
556 cahs = [node.nullrev]
557 return [ctx.repo()[r].manifest() for r in cahs]
557 return [ctx.repo()[r].manifest() for r in cahs]
558
558
559 def deletionfromparent(f):
559 def deletionfromparent(f):
560 if f in m1:
560 if f in m1:
561 return f not in m2 and all(
561 return f not in m2 and all(
562 f in ma and ma.find(f) == m1.find(f) for ma in mas()
562 f in ma and ma.find(f) == m1.find(f) for ma in mas()
563 )
563 )
564 elif f in m2:
564 elif f in m2:
565 return all(f in ma and ma.find(f) == m2.find(f) for ma in mas())
565 return all(f in ma and ma.find(f) == m2.find(f) for ma in mas())
566 else:
566 else:
567 return True
567 return True
568
568
569 return deletionfromparent
569 return deletionfromparent
570
570
571
571
572 def computechangesetfilesremoved(ctx):
572 def computechangesetfilesremoved(ctx):
573 """return the list of files removed in a changeset
573 """return the list of files removed in a changeset
574 """
574 """
575 removed = []
575 removed = []
576 for f in ctx.files():
576 for f in ctx.files():
577 if f not in ctx:
577 if f not in ctx:
578 removed.append(f)
578 removed.append(f)
579 if removed:
579 if removed:
580 rf = get_removal_filter(ctx)
580 rf = get_removal_filter(ctx)
581 removed = [r for r in removed if not rf(r)]
581 removed = [r for r in removed if not rf(r)]
582 return removed
582 return removed
583
583
584
584
585 def computechangesetfilesmerged(ctx):
585 def computechangesetfilesmerged(ctx):
586 """return the list of files merged in a changeset
586 """return the list of files merged in a changeset
587 """
587 """
588 merged = []
588 merged = []
589 if len(ctx.parents()) < 2:
589 if len(ctx.parents()) < 2:
590 return merged
590 return merged
591 for f in ctx.files():
591 for f in ctx.files():
592 if f in ctx:
592 if f in ctx:
593 fctx = ctx[f]
593 fctx = ctx[f]
594 parents = fctx._filelog.parents(fctx._filenode)
594 parents = fctx._filelog.parents(fctx._filenode)
595 if parents[1] != node.nullid:
595 if parents[1] != node.nullid:
596 merged.append(f)
596 merged.append(f)
597 return merged
597 return merged
598
598
599
599
600 def computechangesetcopies(ctx):
600 def computechangesetcopies(ctx):
601 """return the copies data for a changeset
601 """return the copies data for a changeset
602
602
603 The copies data are returned as a pair of dictionnary (p1copies, p2copies).
603 The copies data are returned as a pair of dictionnary (p1copies, p2copies).
604
604
605 Each dictionnary are in the form: `{newname: oldname}`
605 Each dictionnary are in the form: `{newname: oldname}`
606 """
606 """
607 p1copies = {}
607 p1copies = {}
608 p2copies = {}
608 p2copies = {}
609 p1 = ctx.p1()
609 p1 = ctx.p1()
610 p2 = ctx.p2()
610 p2 = ctx.p2()
611 narrowmatch = ctx._repo.narrowmatch()
611 narrowmatch = ctx._repo.narrowmatch()
612 for dst in ctx.files():
612 for dst in ctx.files():
613 if not narrowmatch(dst) or dst not in ctx:
613 if not narrowmatch(dst) or dst not in ctx:
614 continue
614 continue
615 copied = ctx[dst].renamed()
615 copied = ctx[dst].renamed()
616 if not copied:
616 if not copied:
617 continue
617 continue
618 src, srcnode = copied
618 src, srcnode = copied
619 if src in p1 and p1[src].filenode() == srcnode:
619 if src in p1 and p1[src].filenode() == srcnode:
620 p1copies[dst] = src
620 p1copies[dst] = src
621 elif src in p2 and p2[src].filenode() == srcnode:
621 elif src in p2 and p2[src].filenode() == srcnode:
622 p2copies[dst] = src
622 p2copies[dst] = src
623 return p1copies, p2copies
623 return p1copies, p2copies
624
624
625
625
626 def encodecopies(files, copies):
626 def encodecopies(files, copies):
627 items = []
627 items = []
628 for i, dst in enumerate(files):
628 for i, dst in enumerate(files):
629 if dst in copies:
629 if dst in copies:
630 items.append(b'%d\0%s' % (i, copies[dst]))
630 items.append(b'%d\0%s' % (i, copies[dst]))
631 if len(items) != len(copies):
631 if len(items) != len(copies):
632 raise error.ProgrammingError(
632 raise error.ProgrammingError(
633 b'some copy targets missing from file list'
633 b'some copy targets missing from file list'
634 )
634 )
635 return b"\n".join(items)
635 return b"\n".join(items)
636
636
637
637
638 def decodecopies(files, data):
638 def decodecopies(files, data):
639 try:
639 try:
640 copies = {}
640 copies = {}
641 if not data:
641 if not data:
642 return copies
642 return copies
643 for l in data.split(b'\n'):
643 for l in data.split(b'\n'):
644 strindex, src = l.split(b'\0')
644 strindex, src = l.split(b'\0')
645 i = int(strindex)
645 i = int(strindex)
646 dst = files[i]
646 dst = files[i]
647 copies[dst] = src
647 copies[dst] = src
648 return copies
648 return copies
649 except (ValueError, IndexError):
649 except (ValueError, IndexError):
650 # Perhaps someone had chosen the same key name (e.g. "p1copies") and
650 # Perhaps someone had chosen the same key name (e.g. "p1copies") and
651 # used different syntax for the value.
651 # used different syntax for the value.
652 return None
652 return None
653
653
654
654
655 def encodefileindices(files, subset):
655 def encodefileindices(files, subset):
656 subset = set(subset)
656 subset = set(subset)
657 indices = []
657 indices = []
658 for i, f in enumerate(files):
658 for i, f in enumerate(files):
659 if f in subset:
659 if f in subset:
660 indices.append(b'%d' % i)
660 indices.append(b'%d' % i)
661 return b'\n'.join(indices)
661 return b'\n'.join(indices)
662
662
663
663
664 def decodefileindices(files, data):
664 def decodefileindices(files, data):
665 try:
665 try:
666 subset = []
666 subset = []
667 if not data:
667 if not data:
668 return subset
668 return subset
669 for strindex in data.split(b'\n'):
669 for strindex in data.split(b'\n'):
670 i = int(strindex)
670 i = int(strindex)
671 if i < 0 or i >= len(files):
671 if i < 0 or i >= len(files):
672 return None
672 return None
673 subset.append(files[i])
673 subset.append(files[i])
674 return subset
674 return subset
675 except (ValueError, IndexError):
675 except (ValueError, IndexError):
676 # Perhaps someone had chosen the same key name (e.g. "added") and
676 # Perhaps someone had chosen the same key name (e.g. "added") and
677 # used different syntax for the value.
677 # used different syntax for the value.
678 return None
678 return None
679
679
680
680
681 # see mercurial/helptext/internals/revlogs.txt for details about the format
681 # see mercurial/helptext/internals/revlogs.txt for details about the format
682
682
683 ACTION_MASK = int("111" "00", 2)
683 ACTION_MASK = int("111" "00", 2)
684 # note: untouched file used as copy source will as `000` for this mask.
684 # note: untouched file used as copy source will as `000` for this mask.
685 ADDED_FLAG = int("001" "00", 2)
685 ADDED_FLAG = int("001" "00", 2)
686 MERGED_FLAG = int("010" "00", 2)
686 MERGED_FLAG = int("010" "00", 2)
687 REMOVED_FLAG = int("011" "00", 2)
687 REMOVED_FLAG = int("011" "00", 2)
688 # `100` is reserved for future use
688 SALVAGED_FLAG = int("100" "00", 2)
689 TOUCHED_FLAG = int("101" "00", 2)
689 TOUCHED_FLAG = int("101" "00", 2)
690
690
691 COPIED_MASK = int("11", 2)
691 COPIED_MASK = int("11", 2)
692 COPIED_FROM_P1_FLAG = int("10", 2)
692 COPIED_FROM_P1_FLAG = int("10", 2)
693 COPIED_FROM_P2_FLAG = int("11", 2)
693 COPIED_FROM_P2_FLAG = int("11", 2)
694
694
695 # structure is <flag><filename-end><copy-source>
695 # structure is <flag><filename-end><copy-source>
696 INDEX_HEADER = struct.Struct(">L")
696 INDEX_HEADER = struct.Struct(">L")
697 INDEX_ENTRY = struct.Struct(">bLL")
697 INDEX_ENTRY = struct.Struct(">bLL")
698
698
699
699
700 def encode_files_sidedata(files):
700 def encode_files_sidedata(files):
701 all_files = set(files.touched - files.salvaged)
701 all_files = set(files.touched)
702 all_files.update(files.copied_from_p1.values())
702 all_files.update(files.copied_from_p1.values())
703 all_files.update(files.copied_from_p2.values())
703 all_files.update(files.copied_from_p2.values())
704 all_files = sorted(all_files)
704 all_files = sorted(all_files)
705 file_idx = {f: i for (i, f) in enumerate(all_files)}
705 file_idx = {f: i for (i, f) in enumerate(all_files)}
706 file_idx[None] = 0
706 file_idx[None] = 0
707
707
708 chunks = [INDEX_HEADER.pack(len(all_files))]
708 chunks = [INDEX_HEADER.pack(len(all_files))]
709
709
710 filename_length = 0
710 filename_length = 0
711 for f in all_files:
711 for f in all_files:
712 filename_size = len(f)
712 filename_size = len(f)
713 filename_length += filename_size
713 filename_length += filename_size
714 flag = 0
714 flag = 0
715 if f in files.added:
715 if f in files.added:
716 flag |= ADDED_FLAG
716 flag |= ADDED_FLAG
717 elif f in files.merged:
717 elif f in files.merged:
718 flag |= MERGED_FLAG
718 flag |= MERGED_FLAG
719 elif f in files.removed:
719 elif f in files.removed:
720 flag |= REMOVED_FLAG
720 flag |= REMOVED_FLAG
721 elif f in files.salvaged:
722 flag |= SALVAGED_FLAG
721 elif f in files.touched:
723 elif f in files.touched:
722 flag |= TOUCHED_FLAG
724 flag |= TOUCHED_FLAG
723
725
724 copy = None
726 copy = None
725 if f in files.copied_from_p1:
727 if f in files.copied_from_p1:
726 flag |= COPIED_FROM_P1_FLAG
728 flag |= COPIED_FROM_P1_FLAG
727 copy = files.copied_from_p1.get(f)
729 copy = files.copied_from_p1.get(f)
728 elif f in files.copied_from_p2:
730 elif f in files.copied_from_p2:
729 copy = files.copied_from_p2.get(f)
731 copy = files.copied_from_p2.get(f)
730 flag |= COPIED_FROM_P2_FLAG
732 flag |= COPIED_FROM_P2_FLAG
731 copy_idx = file_idx[copy]
733 copy_idx = file_idx[copy]
732 chunks.append(INDEX_ENTRY.pack(flag, filename_length, copy_idx))
734 chunks.append(INDEX_ENTRY.pack(flag, filename_length, copy_idx))
733 chunks.extend(all_files)
735 chunks.extend(all_files)
734 return {sidedatamod.SD_FILES: b''.join(chunks)}
736 return {sidedatamod.SD_FILES: b''.join(chunks)}
735
737
736
738
737 def decode_files_sidedata(sidedata):
739 def decode_files_sidedata(sidedata):
738 md = ChangingFiles()
740 md = ChangingFiles()
739 raw = sidedata.get(sidedatamod.SD_FILES)
741 raw = sidedata.get(sidedatamod.SD_FILES)
740
742
741 if raw is None:
743 if raw is None:
742 return md
744 return md
743
745
744 copies = []
746 copies = []
745 all_files = []
747 all_files = []
746
748
747 assert len(raw) >= INDEX_HEADER.size
749 assert len(raw) >= INDEX_HEADER.size
748 total_files = INDEX_HEADER.unpack_from(raw, 0)[0]
750 total_files = INDEX_HEADER.unpack_from(raw, 0)[0]
749
751
750 offset = INDEX_HEADER.size
752 offset = INDEX_HEADER.size
751 file_offset_base = offset + (INDEX_ENTRY.size * total_files)
753 file_offset_base = offset + (INDEX_ENTRY.size * total_files)
752 file_offset_last = file_offset_base
754 file_offset_last = file_offset_base
753
755
754 assert len(raw) >= file_offset_base
756 assert len(raw) >= file_offset_base
755
757
756 for idx in range(total_files):
758 for idx in range(total_files):
757 flag, file_end, copy_idx = INDEX_ENTRY.unpack_from(raw, offset)
759 flag, file_end, copy_idx = INDEX_ENTRY.unpack_from(raw, offset)
758 file_end += file_offset_base
760 file_end += file_offset_base
759 filename = raw[file_offset_last:file_end]
761 filename = raw[file_offset_last:file_end]
760 filesize = file_end - file_offset_last
762 filesize = file_end - file_offset_last
761 assert len(filename) == filesize
763 assert len(filename) == filesize
762 offset += INDEX_ENTRY.size
764 offset += INDEX_ENTRY.size
763 file_offset_last = file_end
765 file_offset_last = file_end
764 all_files.append(filename)
766 all_files.append(filename)
765 if flag & ACTION_MASK == ADDED_FLAG:
767 if flag & ACTION_MASK == ADDED_FLAG:
766 md.mark_added(filename)
768 md.mark_added(filename)
767 elif flag & ACTION_MASK == MERGED_FLAG:
769 elif flag & ACTION_MASK == MERGED_FLAG:
768 md.mark_merged(filename)
770 md.mark_merged(filename)
769 elif flag & ACTION_MASK == REMOVED_FLAG:
771 elif flag & ACTION_MASK == REMOVED_FLAG:
770 md.mark_removed(filename)
772 md.mark_removed(filename)
773 elif flag & ACTION_MASK == SALVAGED_FLAG:
774 md.mark_salvaged(filename)
771 elif flag & ACTION_MASK == TOUCHED_FLAG:
775 elif flag & ACTION_MASK == TOUCHED_FLAG:
772 md.mark_touched(filename)
776 md.mark_touched(filename)
773
777
774 copied = None
778 copied = None
775 if flag & COPIED_MASK == COPIED_FROM_P1_FLAG:
779 if flag & COPIED_MASK == COPIED_FROM_P1_FLAG:
776 copied = md.mark_copied_from_p1
780 copied = md.mark_copied_from_p1
777 elif flag & COPIED_MASK == COPIED_FROM_P2_FLAG:
781 elif flag & COPIED_MASK == COPIED_FROM_P2_FLAG:
778 copied = md.mark_copied_from_p2
782 copied = md.mark_copied_from_p2
779
783
780 if copied is not None:
784 if copied is not None:
781 copies.append((copied, filename, copy_idx))
785 copies.append((copied, filename, copy_idx))
782
786
783 for copied, filename, copy_idx in copies:
787 for copied, filename, copy_idx in copies:
784 copied(all_files[copy_idx], filename)
788 copied(all_files[copy_idx], filename)
785
789
786 return md
790 return md
787
791
788
792
789 def _getsidedata(srcrepo, rev):
793 def _getsidedata(srcrepo, rev):
790 ctx = srcrepo[rev]
794 ctx = srcrepo[rev]
791 files = compute_all_files_changes(ctx)
795 files = compute_all_files_changes(ctx)
792 return encode_files_sidedata(files)
796 return encode_files_sidedata(files)
793
797
794
798
795 def getsidedataadder(srcrepo, destrepo):
799 def getsidedataadder(srcrepo, destrepo):
796 use_w = srcrepo.ui.configbool(b'experimental', b'worker.repository-upgrade')
800 use_w = srcrepo.ui.configbool(b'experimental', b'worker.repository-upgrade')
797 if pycompat.iswindows or not use_w:
801 if pycompat.iswindows or not use_w:
798 return _get_simple_sidedata_adder(srcrepo, destrepo)
802 return _get_simple_sidedata_adder(srcrepo, destrepo)
799 else:
803 else:
800 return _get_worker_sidedata_adder(srcrepo, destrepo)
804 return _get_worker_sidedata_adder(srcrepo, destrepo)
801
805
802
806
803 def _sidedata_worker(srcrepo, revs_queue, sidedata_queue, tokens):
807 def _sidedata_worker(srcrepo, revs_queue, sidedata_queue, tokens):
804 """The function used by worker precomputing sidedata
808 """The function used by worker precomputing sidedata
805
809
806 It read an input queue containing revision numbers
810 It read an input queue containing revision numbers
807 It write in an output queue containing (rev, <sidedata-map>)
811 It write in an output queue containing (rev, <sidedata-map>)
808
812
809 The `None` input value is used as a stop signal.
813 The `None` input value is used as a stop signal.
810
814
811 The `tokens` semaphore is user to avoid having too many unprocessed
815 The `tokens` semaphore is user to avoid having too many unprocessed
812 entries. The workers needs to acquire one token before fetching a task.
816 entries. The workers needs to acquire one token before fetching a task.
813 They will be released by the consumer of the produced data.
817 They will be released by the consumer of the produced data.
814 """
818 """
815 tokens.acquire()
819 tokens.acquire()
816 rev = revs_queue.get()
820 rev = revs_queue.get()
817 while rev is not None:
821 while rev is not None:
818 data = _getsidedata(srcrepo, rev)
822 data = _getsidedata(srcrepo, rev)
819 sidedata_queue.put((rev, data))
823 sidedata_queue.put((rev, data))
820 tokens.acquire()
824 tokens.acquire()
821 rev = revs_queue.get()
825 rev = revs_queue.get()
822 # processing of `None` is completed, release the token.
826 # processing of `None` is completed, release the token.
823 tokens.release()
827 tokens.release()
824
828
825
829
826 BUFF_PER_WORKER = 50
830 BUFF_PER_WORKER = 50
827
831
828
832
829 def _get_worker_sidedata_adder(srcrepo, destrepo):
833 def _get_worker_sidedata_adder(srcrepo, destrepo):
830 """The parallel version of the sidedata computation
834 """The parallel version of the sidedata computation
831
835
832 This code spawn a pool of worker that precompute a buffer of sidedata
836 This code spawn a pool of worker that precompute a buffer of sidedata
833 before we actually need them"""
837 before we actually need them"""
834 # avoid circular import copies -> scmutil -> worker -> copies
838 # avoid circular import copies -> scmutil -> worker -> copies
835 from . import worker
839 from . import worker
836
840
837 nbworkers = worker._numworkers(srcrepo.ui)
841 nbworkers = worker._numworkers(srcrepo.ui)
838
842
839 tokens = multiprocessing.BoundedSemaphore(nbworkers * BUFF_PER_WORKER)
843 tokens = multiprocessing.BoundedSemaphore(nbworkers * BUFF_PER_WORKER)
840 revsq = multiprocessing.Queue()
844 revsq = multiprocessing.Queue()
841 sidedataq = multiprocessing.Queue()
845 sidedataq = multiprocessing.Queue()
842
846
843 assert srcrepo.filtername is None
847 assert srcrepo.filtername is None
844 # queue all tasks beforehand, revision numbers are small and it make
848 # queue all tasks beforehand, revision numbers are small and it make
845 # synchronisation simpler
849 # synchronisation simpler
846 #
850 #
847 # Since the computation for each node can be quite expensive, the overhead
851 # Since the computation for each node can be quite expensive, the overhead
848 # of using a single queue is not revelant. In practice, most computation
852 # of using a single queue is not revelant. In practice, most computation
849 # are fast but some are very expensive and dominate all the other smaller
853 # are fast but some are very expensive and dominate all the other smaller
850 # cost.
854 # cost.
851 for r in srcrepo.changelog.revs():
855 for r in srcrepo.changelog.revs():
852 revsq.put(r)
856 revsq.put(r)
853 # queue the "no more tasks" markers
857 # queue the "no more tasks" markers
854 for i in range(nbworkers):
858 for i in range(nbworkers):
855 revsq.put(None)
859 revsq.put(None)
856
860
857 allworkers = []
861 allworkers = []
858 for i in range(nbworkers):
862 for i in range(nbworkers):
859 args = (srcrepo, revsq, sidedataq, tokens)
863 args = (srcrepo, revsq, sidedataq, tokens)
860 w = multiprocessing.Process(target=_sidedata_worker, args=args)
864 w = multiprocessing.Process(target=_sidedata_worker, args=args)
861 allworkers.append(w)
865 allworkers.append(w)
862 w.start()
866 w.start()
863
867
864 # dictionnary to store results for revision higher than we one we are
868 # dictionnary to store results for revision higher than we one we are
865 # looking for. For example, if we need the sidedatamap for 42, and 43 is
869 # looking for. For example, if we need the sidedatamap for 42, and 43 is
866 # received, when shelve 43 for later use.
870 # received, when shelve 43 for later use.
867 staging = {}
871 staging = {}
868
872
869 def sidedata_companion(revlog, rev):
873 def sidedata_companion(revlog, rev):
870 sidedata = {}
874 sidedata = {}
871 if util.safehasattr(revlog, b'filteredrevs'): # this is a changelog
875 if util.safehasattr(revlog, b'filteredrevs'): # this is a changelog
872 # Is the data previously shelved ?
876 # Is the data previously shelved ?
873 sidedata = staging.pop(rev, None)
877 sidedata = staging.pop(rev, None)
874 if sidedata is None:
878 if sidedata is None:
875 # look at the queued result until we find the one we are lookig
879 # look at the queued result until we find the one we are lookig
876 # for (shelve the other ones)
880 # for (shelve the other ones)
877 r, sidedata = sidedataq.get()
881 r, sidedata = sidedataq.get()
878 while r != rev:
882 while r != rev:
879 staging[r] = sidedata
883 staging[r] = sidedata
880 r, sidedata = sidedataq.get()
884 r, sidedata = sidedataq.get()
881 tokens.release()
885 tokens.release()
882 return False, (), sidedata
886 return False, (), sidedata
883
887
884 return sidedata_companion
888 return sidedata_companion
885
889
886
890
887 def _get_simple_sidedata_adder(srcrepo, destrepo):
891 def _get_simple_sidedata_adder(srcrepo, destrepo):
888 """The simple version of the sidedata computation
892 """The simple version of the sidedata computation
889
893
890 It just compute it in the same thread on request"""
894 It just compute it in the same thread on request"""
891
895
892 def sidedatacompanion(revlog, rev):
896 def sidedatacompanion(revlog, rev):
893 sidedata = {}
897 sidedata = {}
894 if util.safehasattr(revlog, 'filteredrevs'): # this is a changelog
898 if util.safehasattr(revlog, 'filteredrevs'): # this is a changelog
895 sidedata = _getsidedata(srcrepo, rev)
899 sidedata = _getsidedata(srcrepo, rev)
896 return False, (), sidedata
900 return False, (), sidedata
897
901
898 return sidedatacompanion
902 return sidedatacompanion
899
903
900
904
901 def getsidedataremover(srcrepo, destrepo):
905 def getsidedataremover(srcrepo, destrepo):
902 def sidedatacompanion(revlog, rev):
906 def sidedatacompanion(revlog, rev):
903 f = ()
907 f = ()
904 if util.safehasattr(revlog, 'filteredrevs'): # this is a changelog
908 if util.safehasattr(revlog, 'filteredrevs'): # this is a changelog
905 if revlog.flags(rev) & sidedataflag.REVIDX_SIDEDATA:
909 if revlog.flags(rev) & sidedataflag.REVIDX_SIDEDATA:
906 f = (
910 f = (
907 sidedatamod.SD_P1COPIES,
911 sidedatamod.SD_P1COPIES,
908 sidedatamod.SD_P2COPIES,
912 sidedatamod.SD_P2COPIES,
909 sidedatamod.SD_FILESADDED,
913 sidedatamod.SD_FILESADDED,
910 sidedatamod.SD_FILESREMOVED,
914 sidedatamod.SD_FILESREMOVED,
911 )
915 )
912 return False, f, {}
916 return False, f, {}
913
917
914 return sidedatacompanion
918 return sidedatacompanion
@@ -1,1192 +1,1196 b''
1 #testcases filelog compatibility sidedata
1 #testcases filelog compatibility sidedata
2
2
3 =====================================================
3 =====================================================
4 Test Copy tracing for chain of copies involving merge
4 Test Copy tracing for chain of copies involving merge
5 =====================================================
5 =====================================================
6
6
7 This test files covers copies/rename case for a chains of commit where merges
7 This test files covers copies/rename case for a chains of commit where merges
8 are involved. It cheks we do not have unwanted update of behavior and that the
8 are involved. It cheks we do not have unwanted update of behavior and that the
9 different options to retrieve copies behave correctly.
9 different options to retrieve copies behave correctly.
10
10
11
11
12 Setup
12 Setup
13 =====
13 =====
14
14
15 use git diff to see rename
15 use git diff to see rename
16
16
17 $ cat << EOF >> $HGRCPATH
17 $ cat << EOF >> $HGRCPATH
18 > [diff]
18 > [diff]
19 > git=yes
19 > git=yes
20 > [ui]
20 > [ui]
21 > logtemplate={rev} {desc}\n
21 > logtemplate={rev} {desc}\n
22 > EOF
22 > EOF
23
23
24 #if compatibility
24 #if compatibility
25 $ cat >> $HGRCPATH << EOF
25 $ cat >> $HGRCPATH << EOF
26 > [experimental]
26 > [experimental]
27 > copies.read-from = compatibility
27 > copies.read-from = compatibility
28 > EOF
28 > EOF
29 #endif
29 #endif
30
30
31 #if sidedata
31 #if sidedata
32 $ cat >> $HGRCPATH << EOF
32 $ cat >> $HGRCPATH << EOF
33 > [format]
33 > [format]
34 > exp-use-side-data = yes
34 > exp-use-side-data = yes
35 > exp-use-copies-side-data-changeset = yes
35 > exp-use-copies-side-data-changeset = yes
36 > EOF
36 > EOF
37 #endif
37 #endif
38
38
39
39
40 $ hg init repo-chain
40 $ hg init repo-chain
41 $ cd repo-chain
41 $ cd repo-chain
42
42
43 Add some linear rename initialy
43 Add some linear rename initialy
44
44
45 $ touch a b h
45 $ touch a b h
46 $ hg ci -Am 'i-0 initial commit: a b h'
46 $ hg ci -Am 'i-0 initial commit: a b h'
47 adding a
47 adding a
48 adding b
48 adding b
49 adding h
49 adding h
50 $ hg mv a c
50 $ hg mv a c
51 $ hg ci -Am 'i-1: a -move-> c'
51 $ hg ci -Am 'i-1: a -move-> c'
52 $ hg mv c d
52 $ hg mv c d
53 $ hg ci -Am 'i-2: c -move-> d'
53 $ hg ci -Am 'i-2: c -move-> d'
54 $ hg log -G
54 $ hg log -G
55 @ 2 i-2: c -move-> d
55 @ 2 i-2: c -move-> d
56 |
56 |
57 o 1 i-1: a -move-> c
57 o 1 i-1: a -move-> c
58 |
58 |
59 o 0 i-0 initial commit: a b h
59 o 0 i-0 initial commit: a b h
60
60
61
61
62 And having another branch with renames on the other side
62 And having another branch with renames on the other side
63
63
64 $ hg mv d e
64 $ hg mv d e
65 $ hg ci -Am 'a-1: d -move-> e'
65 $ hg ci -Am 'a-1: d -move-> e'
66 $ hg mv e f
66 $ hg mv e f
67 $ hg ci -Am 'a-2: e -move-> f'
67 $ hg ci -Am 'a-2: e -move-> f'
68 $ hg log -G --rev '::.'
68 $ hg log -G --rev '::.'
69 @ 4 a-2: e -move-> f
69 @ 4 a-2: e -move-> f
70 |
70 |
71 o 3 a-1: d -move-> e
71 o 3 a-1: d -move-> e
72 |
72 |
73 o 2 i-2: c -move-> d
73 o 2 i-2: c -move-> d
74 |
74 |
75 o 1 i-1: a -move-> c
75 o 1 i-1: a -move-> c
76 |
76 |
77 o 0 i-0 initial commit: a b h
77 o 0 i-0 initial commit: a b h
78
78
79
79
80 Have a branching with nothing on one side
80 Have a branching with nothing on one side
81
81
82 $ hg up 'desc("i-2")'
82 $ hg up 'desc("i-2")'
83 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
83 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
84 $ echo foo > b
84 $ echo foo > b
85 $ hg ci -m 'b-1: b update'
85 $ hg ci -m 'b-1: b update'
86 created new head
86 created new head
87 $ hg log -G --rev '::.'
87 $ hg log -G --rev '::.'
88 @ 5 b-1: b update
88 @ 5 b-1: b update
89 |
89 |
90 o 2 i-2: c -move-> d
90 o 2 i-2: c -move-> d
91 |
91 |
92 o 1 i-1: a -move-> c
92 o 1 i-1: a -move-> c
93 |
93 |
94 o 0 i-0 initial commit: a b h
94 o 0 i-0 initial commit: a b h
95
95
96
96
97 Create a branch that delete a file previous renamed
97 Create a branch that delete a file previous renamed
98
98
99 $ hg up 'desc("i-2")'
99 $ hg up 'desc("i-2")'
100 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
100 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
101 $ hg rm d
101 $ hg rm d
102 $ hg ci -m 'c-1 delete d'
102 $ hg ci -m 'c-1 delete d'
103 created new head
103 created new head
104 $ hg log -G --rev '::.'
104 $ hg log -G --rev '::.'
105 @ 6 c-1 delete d
105 @ 6 c-1 delete d
106 |
106 |
107 o 2 i-2: c -move-> d
107 o 2 i-2: c -move-> d
108 |
108 |
109 o 1 i-1: a -move-> c
109 o 1 i-1: a -move-> c
110 |
110 |
111 o 0 i-0 initial commit: a b h
111 o 0 i-0 initial commit: a b h
112
112
113
113
114 Create a branch that delete a file previous renamed and recreate it
114 Create a branch that delete a file previous renamed and recreate it
115
115
116 $ hg up 'desc("i-2")'
116 $ hg up 'desc("i-2")'
117 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
117 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
118 $ hg rm d
118 $ hg rm d
119 $ hg ci -m 'd-1 delete d'
119 $ hg ci -m 'd-1 delete d'
120 created new head
120 created new head
121 $ echo bar > d
121 $ echo bar > d
122 $ hg add d
122 $ hg add d
123 $ hg ci -m 'd-2 re-add d'
123 $ hg ci -m 'd-2 re-add d'
124 $ hg log -G --rev '::.'
124 $ hg log -G --rev '::.'
125 @ 8 d-2 re-add d
125 @ 8 d-2 re-add d
126 |
126 |
127 o 7 d-1 delete d
127 o 7 d-1 delete d
128 |
128 |
129 o 2 i-2: c -move-> d
129 o 2 i-2: c -move-> d
130 |
130 |
131 o 1 i-1: a -move-> c
131 o 1 i-1: a -move-> c
132 |
132 |
133 o 0 i-0 initial commit: a b h
133 o 0 i-0 initial commit: a b h
134
134
135
135
136 Having another branch renaming a different file to the same filename as another
136 Having another branch renaming a different file to the same filename as another
137
137
138 $ hg up 'desc("i-2")'
138 $ hg up 'desc("i-2")'
139 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
139 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
140 $ hg mv b g
140 $ hg mv b g
141 $ hg ci -m 'e-1 b -move-> g'
141 $ hg ci -m 'e-1 b -move-> g'
142 created new head
142 created new head
143 $ hg mv g f
143 $ hg mv g f
144 $ hg ci -m 'e-2 g -move-> f'
144 $ hg ci -m 'e-2 g -move-> f'
145 $ hg log -G --rev '::.'
145 $ hg log -G --rev '::.'
146 @ 10 e-2 g -move-> f
146 @ 10 e-2 g -move-> f
147 |
147 |
148 o 9 e-1 b -move-> g
148 o 9 e-1 b -move-> g
149 |
149 |
150 o 2 i-2: c -move-> d
150 o 2 i-2: c -move-> d
151 |
151 |
152 o 1 i-1: a -move-> c
152 o 1 i-1: a -move-> c
153 |
153 |
154 o 0 i-0 initial commit: a b h
154 o 0 i-0 initial commit: a b h
155
155
156
156
157 merging with unrelated change does not interfere with the renames
157 merging with unrelated change does not interfere with the renames
158 ---------------------------------------------------------------
158 ---------------------------------------------------------------
159
159
160 - rename on one side
160 - rename on one side
161 - unrelated change on the other side
161 - unrelated change on the other side
162
162
163 $ hg up 'desc("b-1")'
163 $ hg up 'desc("b-1")'
164 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
164 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
165 $ hg merge 'desc("a-2")'
165 $ hg merge 'desc("a-2")'
166 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
166 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
167 (branch merge, don't forget to commit)
167 (branch merge, don't forget to commit)
168 $ hg ci -m 'mBAm-0 simple merge - one way'
168 $ hg ci -m 'mBAm-0 simple merge - one way'
169 $ hg up 'desc("a-2")'
169 $ hg up 'desc("a-2")'
170 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
170 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
171 $ hg merge 'desc("b-1")'
171 $ hg merge 'desc("b-1")'
172 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
172 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
173 (branch merge, don't forget to commit)
173 (branch merge, don't forget to commit)
174 $ hg ci -m 'mABm-0 simple merge - the other way'
174 $ hg ci -m 'mABm-0 simple merge - the other way'
175 created new head
175 created new head
176 $ hg log -G --rev '::(desc("mABm")+desc("mBAm"))'
176 $ hg log -G --rev '::(desc("mABm")+desc("mBAm"))'
177 @ 12 mABm-0 simple merge - the other way
177 @ 12 mABm-0 simple merge - the other way
178 |\
178 |\
179 +---o 11 mBAm-0 simple merge - one way
179 +---o 11 mBAm-0 simple merge - one way
180 | |/
180 | |/
181 | o 5 b-1: b update
181 | o 5 b-1: b update
182 | |
182 | |
183 o | 4 a-2: e -move-> f
183 o | 4 a-2: e -move-> f
184 | |
184 | |
185 o | 3 a-1: d -move-> e
185 o | 3 a-1: d -move-> e
186 |/
186 |/
187 o 2 i-2: c -move-> d
187 o 2 i-2: c -move-> d
188 |
188 |
189 o 1 i-1: a -move-> c
189 o 1 i-1: a -move-> c
190 |
190 |
191 o 0 i-0 initial commit: a b h
191 o 0 i-0 initial commit: a b h
192
192
193
193
194 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mABm")'
194 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mABm")'
195 A f
195 A f
196 d
196 d
197 R d
197 R d
198 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mBAm")'
198 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mBAm")'
199 A f
199 A f
200 d
200 d
201 R d
201 R d
202 $ hg status --copies --rev 'desc("a-2")' --rev 'desc("mABm")'
202 $ hg status --copies --rev 'desc("a-2")' --rev 'desc("mABm")'
203 M b
203 M b
204 $ hg status --copies --rev 'desc("a-2")' --rev 'desc("mBAm")'
204 $ hg status --copies --rev 'desc("a-2")' --rev 'desc("mBAm")'
205 M b
205 M b
206 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mABm")'
206 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mABm")'
207 M b
207 M b
208 A f
208 A f
209 d
209 d
210 R d
210 R d
211 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mBAm")'
211 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mBAm")'
212 M b
212 M b
213 A f
213 A f
214 d
214 d
215 R d
215 R d
216 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mABm")'
216 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mABm")'
217 M b
217 M b
218 A f
218 A f
219 a
219 a
220 R a
220 R a
221 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mBAm")'
221 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mBAm")'
222 M b
222 M b
223 A f
223 A f
224 a
224 a
225 R a
225 R a
226
226
227 merging with the side having a delete
227 merging with the side having a delete
228 -------------------------------------
228 -------------------------------------
229
229
230 case summary:
230 case summary:
231 - one with change to an unrelated file
231 - one with change to an unrelated file
232 - one deleting the change
232 - one deleting the change
233 and recreate an unrelated file after the merge
233 and recreate an unrelated file after the merge
234
234
235 $ hg up 'desc("b-1")'
235 $ hg up 'desc("b-1")'
236 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
236 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
237 $ hg merge 'desc("c-1")'
237 $ hg merge 'desc("c-1")'
238 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
238 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
239 (branch merge, don't forget to commit)
239 (branch merge, don't forget to commit)
240 $ hg ci -m 'mBCm-0 simple merge - one way'
240 $ hg ci -m 'mBCm-0 simple merge - one way'
241 $ echo bar > d
241 $ echo bar > d
242 $ hg add d
242 $ hg add d
243 $ hg ci -m 'mBCm-1 re-add d'
243 $ hg ci -m 'mBCm-1 re-add d'
244 $ hg up 'desc("c-1")'
244 $ hg up 'desc("c-1")'
245 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
245 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
246 $ hg merge 'desc("b-1")'
246 $ hg merge 'desc("b-1")'
247 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
247 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
248 (branch merge, don't forget to commit)
248 (branch merge, don't forget to commit)
249 $ hg ci -m 'mCBm-0 simple merge - the other way'
249 $ hg ci -m 'mCBm-0 simple merge - the other way'
250 created new head
250 created new head
251 $ echo bar > d
251 $ echo bar > d
252 $ hg add d
252 $ hg add d
253 $ hg ci -m 'mCBm-1 re-add d'
253 $ hg ci -m 'mCBm-1 re-add d'
254 $ hg log -G --rev '::(desc("mCBm")+desc("mBCm"))'
254 $ hg log -G --rev '::(desc("mCBm")+desc("mBCm"))'
255 @ 16 mCBm-1 re-add d
255 @ 16 mCBm-1 re-add d
256 |
256 |
257 o 15 mCBm-0 simple merge - the other way
257 o 15 mCBm-0 simple merge - the other way
258 |\
258 |\
259 | | o 14 mBCm-1 re-add d
259 | | o 14 mBCm-1 re-add d
260 | | |
260 | | |
261 +---o 13 mBCm-0 simple merge - one way
261 +---o 13 mBCm-0 simple merge - one way
262 | |/
262 | |/
263 | o 6 c-1 delete d
263 | o 6 c-1 delete d
264 | |
264 | |
265 o | 5 b-1: b update
265 o | 5 b-1: b update
266 |/
266 |/
267 o 2 i-2: c -move-> d
267 o 2 i-2: c -move-> d
268 |
268 |
269 o 1 i-1: a -move-> c
269 o 1 i-1: a -move-> c
270 |
270 |
271 o 0 i-0 initial commit: a b h
271 o 0 i-0 initial commit: a b h
272
272
273 - comparing from the merge
273 - comparing from the merge
274
274
275 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mBCm-0")'
275 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mBCm-0")'
276 R d
276 R d
277 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mCBm-0")'
277 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mCBm-0")'
278 R d
278 R d
279 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mBCm-0")'
279 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mBCm-0")'
280 M b
280 M b
281 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mCBm-0")'
281 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mCBm-0")'
282 M b
282 M b
283 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mBCm-0")'
283 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mBCm-0")'
284 M b
284 M b
285 R d
285 R d
286 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mCBm-0")'
286 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mCBm-0")'
287 M b
287 M b
288 R d
288 R d
289 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mBCm-0")'
289 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mBCm-0")'
290 M b
290 M b
291 R a
291 R a
292 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mCBm-0")'
292 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mCBm-0")'
293 M b
293 M b
294 R a
294 R a
295
295
296 - comparing with the merge children re-adding the file
296 - comparing with the merge children re-adding the file
297
297
298 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mBCm-1")'
298 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mBCm-1")'
299 M d
299 M d
300 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mCBm-1")'
300 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mCBm-1")'
301 M d
301 M d
302 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mBCm-1")'
302 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mBCm-1")'
303 M b
303 M b
304 A d
304 A d
305 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mCBm-1")'
305 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mCBm-1")'
306 M b
306 M b
307 A d
307 A d
308 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mBCm-1")'
308 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mBCm-1")'
309 M b
309 M b
310 M d
310 M d
311 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mCBm-1")'
311 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mCBm-1")'
312 M b
312 M b
313 M d
313 M d
314 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mBCm-1")'
314 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mBCm-1")'
315 M b
315 M b
316 A d
316 A d
317 R a
317 R a
318 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mCBm-1")'
318 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mCBm-1")'
319 M b
319 M b
320 A d
320 A d
321 R a
321 R a
322
322
323 Comparing with a merge re-adding the file afterward
323 Comparing with a merge re-adding the file afterward
324 ---------------------------------------------------
324 ---------------------------------------------------
325
325
326 Merge:
326 Merge:
327 - one with change to an unrelated file
327 - one with change to an unrelated file
328 - one deleting and recreating the change
328 - one deleting and recreating the change
329
329
330 $ hg up 'desc("b-1")'
330 $ hg up 'desc("b-1")'
331 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
331 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
332 $ hg merge 'desc("d-2")'
332 $ hg merge 'desc("d-2")'
333 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
333 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
334 (branch merge, don't forget to commit)
334 (branch merge, don't forget to commit)
335 $ hg ci -m 'mBDm-0 simple merge - one way'
335 $ hg ci -m 'mBDm-0 simple merge - one way'
336 $ hg up 'desc("d-2")'
336 $ hg up 'desc("d-2")'
337 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
337 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
338 $ hg merge 'desc("b-1")'
338 $ hg merge 'desc("b-1")'
339 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
339 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
340 (branch merge, don't forget to commit)
340 (branch merge, don't forget to commit)
341 $ hg ci -m 'mDBm-0 simple merge - the other way'
341 $ hg ci -m 'mDBm-0 simple merge - the other way'
342 created new head
342 created new head
343 $ hg log -G --rev '::(desc("mDBm")+desc("mBDm"))'
343 $ hg log -G --rev '::(desc("mDBm")+desc("mBDm"))'
344 @ 18 mDBm-0 simple merge - the other way
344 @ 18 mDBm-0 simple merge - the other way
345 |\
345 |\
346 +---o 17 mBDm-0 simple merge - one way
346 +---o 17 mBDm-0 simple merge - one way
347 | |/
347 | |/
348 | o 8 d-2 re-add d
348 | o 8 d-2 re-add d
349 | |
349 | |
350 | o 7 d-1 delete d
350 | o 7 d-1 delete d
351 | |
351 | |
352 o | 5 b-1: b update
352 o | 5 b-1: b update
353 |/
353 |/
354 o 2 i-2: c -move-> d
354 o 2 i-2: c -move-> d
355 |
355 |
356 o 1 i-1: a -move-> c
356 o 1 i-1: a -move-> c
357 |
357 |
358 o 0 i-0 initial commit: a b h
358 o 0 i-0 initial commit: a b h
359
359
360 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mBDm-0")'
360 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mBDm-0")'
361 M d
361 M d
362 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mDBm-0")'
362 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mDBm-0")'
363 M d
363 M d
364 $ hg status --copies --rev 'desc("d-2")' --rev 'desc("mBDm-0")'
364 $ hg status --copies --rev 'desc("d-2")' --rev 'desc("mBDm-0")'
365 M b
365 M b
366 $ hg status --copies --rev 'desc("d-2")' --rev 'desc("mDBm-0")'
366 $ hg status --copies --rev 'desc("d-2")' --rev 'desc("mDBm-0")'
367 M b
367 M b
368 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mBDm-0")'
368 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mBDm-0")'
369 M b
369 M b
370 M d
370 M d
371 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mDBm-0")'
371 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mDBm-0")'
372 M b
372 M b
373 M d
373 M d
374
374
375 The bugs makes recorded copy is different depending of where we started the merge from since
375 The bugs makes recorded copy is different depending of where we started the merge from since
376
376
377 $ hg manifest --debug --rev 'desc("mBDm-0")' | grep '644 d'
377 $ hg manifest --debug --rev 'desc("mBDm-0")' | grep '644 d'
378 b004912a8510032a0350a74daa2803dadfb00e12 644 d
378 b004912a8510032a0350a74daa2803dadfb00e12 644 d
379 $ hg manifest --debug --rev 'desc("mDBm-0")' | grep '644 d'
379 $ hg manifest --debug --rev 'desc("mDBm-0")' | grep '644 d'
380 b004912a8510032a0350a74daa2803dadfb00e12 644 d
380 b004912a8510032a0350a74daa2803dadfb00e12 644 d
381
381
382 $ hg manifest --debug --rev 'desc("d-2")' | grep '644 d'
382 $ hg manifest --debug --rev 'desc("d-2")' | grep '644 d'
383 b004912a8510032a0350a74daa2803dadfb00e12 644 d
383 b004912a8510032a0350a74daa2803dadfb00e12 644 d
384 $ hg manifest --debug --rev 'desc("b-1")' | grep '644 d'
384 $ hg manifest --debug --rev 'desc("b-1")' | grep '644 d'
385 01c2f5eabdc4ce2bdee42b5f86311955e6c8f573 644 d
385 01c2f5eabdc4ce2bdee42b5f86311955e6c8f573 644 d
386 $ hg debugindex d
386 $ hg debugindex d
387 rev linkrev nodeid p1 p2
387 rev linkrev nodeid p1 p2
388 0 2 01c2f5eabdc4 000000000000 000000000000
388 0 2 01c2f5eabdc4 000000000000 000000000000
389 1 8 b004912a8510 000000000000 000000000000
389 1 8 b004912a8510 000000000000 000000000000
390
390
391 Log output should not include a merge commit as it did not happen
391 Log output should not include a merge commit as it did not happen
392
392
393 $ hg log -Gfr 'desc("mBDm-0")' d
393 $ hg log -Gfr 'desc("mBDm-0")' d
394 o 8 d-2 re-add d
394 o 8 d-2 re-add d
395 |
395 |
396 ~
396 ~
397
397
398 $ hg log -Gfr 'desc("mDBm-0")' d
398 $ hg log -Gfr 'desc("mDBm-0")' d
399 o 8 d-2 re-add d
399 o 8 d-2 re-add d
400 |
400 |
401 ~
401 ~
402
402
403 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mBDm-0")'
403 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mBDm-0")'
404 M b
404 M b
405 A d
405 A d
406 R a
406 R a
407 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mDBm-0")'
407 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mDBm-0")'
408 M b
408 M b
409 A d
409 A d
410 R a
410 R a
411
411
412
412
413 Comparing with a merge with colliding rename
413 Comparing with a merge with colliding rename
414 --------------------------------------------
414 --------------------------------------------
415
415
416 - the "e-" branch renaming b to f (through 'g')
416 - the "e-" branch renaming b to f (through 'g')
417 - the "a-" branch renaming d to f (through e)
417 - the "a-" branch renaming d to f (through e)
418
418
419 $ hg up 'desc("a-2")'
419 $ hg up 'desc("a-2")'
420 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
420 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
421 $ hg merge 'desc("e-2")'
421 $ hg merge 'desc("e-2")'
422 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
422 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
423 (branch merge, don't forget to commit)
423 (branch merge, don't forget to commit)
424 $ hg ci -m 'mAEm-0 simple merge - one way'
424 $ hg ci -m 'mAEm-0 simple merge - one way'
425 $ hg up 'desc("e-2")'
425 $ hg up 'desc("e-2")'
426 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
426 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
427 $ hg merge 'desc("a-2")'
427 $ hg merge 'desc("a-2")'
428 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
428 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
429 (branch merge, don't forget to commit)
429 (branch merge, don't forget to commit)
430 $ hg ci -m 'mEAm-0 simple merge - the other way'
430 $ hg ci -m 'mEAm-0 simple merge - the other way'
431 created new head
431 created new head
432 $ hg log -G --rev '::(desc("mAEm")+desc("mEAm"))'
432 $ hg log -G --rev '::(desc("mAEm")+desc("mEAm"))'
433 @ 20 mEAm-0 simple merge - the other way
433 @ 20 mEAm-0 simple merge - the other way
434 |\
434 |\
435 +---o 19 mAEm-0 simple merge - one way
435 +---o 19 mAEm-0 simple merge - one way
436 | |/
436 | |/
437 | o 10 e-2 g -move-> f
437 | o 10 e-2 g -move-> f
438 | |
438 | |
439 | o 9 e-1 b -move-> g
439 | o 9 e-1 b -move-> g
440 | |
440 | |
441 o | 4 a-2: e -move-> f
441 o | 4 a-2: e -move-> f
442 | |
442 | |
443 o | 3 a-1: d -move-> e
443 o | 3 a-1: d -move-> e
444 |/
444 |/
445 o 2 i-2: c -move-> d
445 o 2 i-2: c -move-> d
446 |
446 |
447 o 1 i-1: a -move-> c
447 o 1 i-1: a -move-> c
448 |
448 |
449 o 0 i-0 initial commit: a b h
449 o 0 i-0 initial commit: a b h
450
450
451 $ hg manifest --debug --rev 'desc("mAEm-0")' | grep '644 f'
451 $ hg manifest --debug --rev 'desc("mAEm-0")' | grep '644 f'
452 eb806e34ef6be4c264effd5933d31004ad15a793 644 f
452 eb806e34ef6be4c264effd5933d31004ad15a793 644 f
453 $ hg manifest --debug --rev 'desc("mEAm-0")' | grep '644 f'
453 $ hg manifest --debug --rev 'desc("mEAm-0")' | grep '644 f'
454 eb806e34ef6be4c264effd5933d31004ad15a793 644 f
454 eb806e34ef6be4c264effd5933d31004ad15a793 644 f
455 $ hg manifest --debug --rev 'desc("a-2")' | grep '644 f'
455 $ hg manifest --debug --rev 'desc("a-2")' | grep '644 f'
456 0dd616bc7ab1a111921d95d76f69cda5c2ac539c 644 f
456 0dd616bc7ab1a111921d95d76f69cda5c2ac539c 644 f
457 $ hg manifest --debug --rev 'desc("e-2")' | grep '644 f'
457 $ hg manifest --debug --rev 'desc("e-2")' | grep '644 f'
458 6da5a2eecb9c833f830b67a4972366d49a9a142c 644 f
458 6da5a2eecb9c833f830b67a4972366d49a9a142c 644 f
459 $ hg debugindex f
459 $ hg debugindex f
460 rev linkrev nodeid p1 p2
460 rev linkrev nodeid p1 p2
461 0 4 0dd616bc7ab1 000000000000 000000000000
461 0 4 0dd616bc7ab1 000000000000 000000000000
462 1 10 6da5a2eecb9c 000000000000 000000000000
462 1 10 6da5a2eecb9c 000000000000 000000000000
463 2 19 eb806e34ef6b 0dd616bc7ab1 6da5a2eecb9c
463 2 19 eb806e34ef6b 0dd616bc7ab1 6da5a2eecb9c
464
464
465 # Here the filelog based implementation is not looking at the rename
465 # Here the filelog based implementation is not looking at the rename
466 # information (because the file exist on both side). However the changelog
466 # information (because the file exist on both side). However the changelog
467 # based on works fine. We have different output.
467 # based on works fine. We have different output.
468
468
469 $ hg status --copies --rev 'desc("a-2")' --rev 'desc("mAEm-0")'
469 $ hg status --copies --rev 'desc("a-2")' --rev 'desc("mAEm-0")'
470 M f
470 M f
471 b (no-filelog !)
471 b (no-filelog !)
472 R b
472 R b
473 $ hg status --copies --rev 'desc("a-2")' --rev 'desc("mEAm-0")'
473 $ hg status --copies --rev 'desc("a-2")' --rev 'desc("mEAm-0")'
474 M f
474 M f
475 b (no-filelog !)
475 b (no-filelog !)
476 R b
476 R b
477 $ hg status --copies --rev 'desc("e-2")' --rev 'desc("mAEm-0")'
477 $ hg status --copies --rev 'desc("e-2")' --rev 'desc("mAEm-0")'
478 M f
478 M f
479 d (no-filelog !)
479 d (no-filelog !)
480 R d
480 R d
481 $ hg status --copies --rev 'desc("e-2")' --rev 'desc("mEAm-0")'
481 $ hg status --copies --rev 'desc("e-2")' --rev 'desc("mEAm-0")'
482 M f
482 M f
483 d (no-filelog !)
483 d (no-filelog !)
484 R d
484 R d
485 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("a-2")'
485 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("a-2")'
486 A f
486 A f
487 d
487 d
488 R d
488 R d
489 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("e-2")'
489 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("e-2")'
490 A f
490 A f
491 b
491 b
492 R b
492 R b
493
493
494 # From here, we run status against revision where both source file exists.
494 # From here, we run status against revision where both source file exists.
495 #
495 #
496 # The filelog based implementation picks an arbitrary side based on revision
496 # The filelog based implementation picks an arbitrary side based on revision
497 # numbers. So the same side "wins" whatever the parents order is. This is
497 # numbers. So the same side "wins" whatever the parents order is. This is
498 # sub-optimal because depending on revision numbers means the result can be
498 # sub-optimal because depending on revision numbers means the result can be
499 # different from one repository to the next.
499 # different from one repository to the next.
500 #
500 #
501 # The changeset based algorithm use the parent order to break tie on conflicting
501 # The changeset based algorithm use the parent order to break tie on conflicting
502 # information and will have a different order depending on who is p1 and p2.
502 # information and will have a different order depending on who is p1 and p2.
503 # That order is stable accross repositories. (data from p1 prevails)
503 # That order is stable accross repositories. (data from p1 prevails)
504
504
505 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mAEm-0")'
505 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mAEm-0")'
506 A f
506 A f
507 d
507 d
508 R b
508 R b
509 R d
509 R d
510 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mEAm-0")'
510 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mEAm-0")'
511 A f
511 A f
512 d (filelog !)
512 d (filelog !)
513 b (no-filelog !)
513 b (no-filelog !)
514 R b
514 R b
515 R d
515 R d
516 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mAEm-0")'
516 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mAEm-0")'
517 A f
517 A f
518 a
518 a
519 R a
519 R a
520 R b
520 R b
521 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mEAm-0")'
521 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mEAm-0")'
522 A f
522 A f
523 a (filelog !)
523 a (filelog !)
524 b (no-filelog !)
524 b (no-filelog !)
525 R a
525 R a
526 R b
526 R b
527
527
528
528
529 Note:
529 Note:
530 | In this case, one of the merge wrongly record a merge while there is none.
530 | In this case, one of the merge wrongly record a merge while there is none.
531 | This lead to bad copy tracing information to be dug up.
531 | This lead to bad copy tracing information to be dug up.
532
532
533
533
534 Merge:
534 Merge:
535 - one with change to an unrelated file (b)
535 - one with change to an unrelated file (b)
536 - one overwriting a file (d) with a rename (from h to i to d)
536 - one overwriting a file (d) with a rename (from h to i to d)
537
537
538 $ hg up 'desc("i-2")'
538 $ hg up 'desc("i-2")'
539 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
539 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
540 $ hg mv h i
540 $ hg mv h i
541 $ hg commit -m "f-1: rename h -> i"
541 $ hg commit -m "f-1: rename h -> i"
542 created new head
542 created new head
543 $ hg mv --force i d
543 $ hg mv --force i d
544 $ hg commit -m "f-2: rename i -> d"
544 $ hg commit -m "f-2: rename i -> d"
545 $ hg debugindex d
545 $ hg debugindex d
546 rev linkrev nodeid p1 p2
546 rev linkrev nodeid p1 p2
547 0 2 01c2f5eabdc4 000000000000 000000000000
547 0 2 01c2f5eabdc4 000000000000 000000000000
548 1 8 b004912a8510 000000000000 000000000000
548 1 8 b004912a8510 000000000000 000000000000
549 2 22 c72365ee036f 000000000000 000000000000
549 2 22 c72365ee036f 000000000000 000000000000
550 $ hg up 'desc("b-1")'
550 $ hg up 'desc("b-1")'
551 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
551 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
552 $ hg merge 'desc("f-2")'
552 $ hg merge 'desc("f-2")'
553 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
553 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
554 (branch merge, don't forget to commit)
554 (branch merge, don't forget to commit)
555 $ hg ci -m 'mBFm-0 simple merge - one way'
555 $ hg ci -m 'mBFm-0 simple merge - one way'
556 $ hg up 'desc("f-2")'
556 $ hg up 'desc("f-2")'
557 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
557 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
558 $ hg merge 'desc("b-1")'
558 $ hg merge 'desc("b-1")'
559 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
559 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
560 (branch merge, don't forget to commit)
560 (branch merge, don't forget to commit)
561 $ hg ci -m 'mFBm-0 simple merge - the other way'
561 $ hg ci -m 'mFBm-0 simple merge - the other way'
562 created new head
562 created new head
563 $ hg log -G --rev '::(desc("mBFm")+desc("mFBm"))'
563 $ hg log -G --rev '::(desc("mBFm")+desc("mFBm"))'
564 @ 24 mFBm-0 simple merge - the other way
564 @ 24 mFBm-0 simple merge - the other way
565 |\
565 |\
566 +---o 23 mBFm-0 simple merge - one way
566 +---o 23 mBFm-0 simple merge - one way
567 | |/
567 | |/
568 | o 22 f-2: rename i -> d
568 | o 22 f-2: rename i -> d
569 | |
569 | |
570 | o 21 f-1: rename h -> i
570 | o 21 f-1: rename h -> i
571 | |
571 | |
572 o | 5 b-1: b update
572 o | 5 b-1: b update
573 |/
573 |/
574 o 2 i-2: c -move-> d
574 o 2 i-2: c -move-> d
575 |
575 |
576 o 1 i-1: a -move-> c
576 o 1 i-1: a -move-> c
577 |
577 |
578 o 0 i-0 initial commit: a b h
578 o 0 i-0 initial commit: a b h
579
579
580 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mBFm-0")'
580 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mBFm-0")'
581 M b
581 M b
582 A d
582 A d
583 h
583 h
584 R a
584 R a
585 R h
585 R h
586 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mFBm-0")'
586 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mFBm-0")'
587 M b
587 M b
588 A d
588 A d
589 h
589 h
590 R a
590 R a
591 R h
591 R h
592 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mBFm-0")'
592 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mBFm-0")'
593 M d
593 M d
594 h (no-filelog !)
594 h (no-filelog !)
595 R h
595 R h
596 $ hg status --copies --rev 'desc("f-2")' --rev 'desc("mBFm-0")'
596 $ hg status --copies --rev 'desc("f-2")' --rev 'desc("mBFm-0")'
597 M b
597 M b
598 $ hg status --copies --rev 'desc("f-1")' --rev 'desc("mBFm-0")'
598 $ hg status --copies --rev 'desc("f-1")' --rev 'desc("mBFm-0")'
599 M b
599 M b
600 M d
600 M d
601 i (no-filelog !)
601 i (no-filelog !)
602 R i
602 R i
603 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mFBm-0")'
603 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mFBm-0")'
604 M d
604 M d
605 h (no-filelog !)
605 h (no-filelog !)
606 R h
606 R h
607 $ hg status --copies --rev 'desc("f-2")' --rev 'desc("mFBm-0")'
607 $ hg status --copies --rev 'desc("f-2")' --rev 'desc("mFBm-0")'
608 M b
608 M b
609 $ hg status --copies --rev 'desc("f-1")' --rev 'desc("mFBm-0")'
609 $ hg status --copies --rev 'desc("f-1")' --rev 'desc("mFBm-0")'
610 M b
610 M b
611 M d
611 M d
612 i (no-filelog !)
612 i (no-filelog !)
613 R i
613 R i
614
614
615 $ hg log -Gfr 'desc("mBFm-0")' d
615 $ hg log -Gfr 'desc("mBFm-0")' d
616 o 22 f-2: rename i -> d
616 o 22 f-2: rename i -> d
617 |
617 |
618 o 21 f-1: rename h -> i
618 o 21 f-1: rename h -> i
619 :
619 :
620 o 0 i-0 initial commit: a b h
620 o 0 i-0 initial commit: a b h
621
621
622
622
623 $ hg log -Gfr 'desc("mFBm-0")' d
623 $ hg log -Gfr 'desc("mFBm-0")' d
624 o 22 f-2: rename i -> d
624 o 22 f-2: rename i -> d
625 |
625 |
626 o 21 f-1: rename h -> i
626 o 21 f-1: rename h -> i
627 :
627 :
628 o 0 i-0 initial commit: a b h
628 o 0 i-0 initial commit: a b h
629
629
630
630
631
631
632 Merge:
632 Merge:
633 - one with change to a file
633 - one with change to a file
634 - one deleting and recreating the file
634 - one deleting and recreating the file
635
635
636 Unlike in the 'BD/DB' cases, an actual merge happened here. So we should
636 Unlike in the 'BD/DB' cases, an actual merge happened here. So we should
637 consider history and rename on both branch of the merge.
637 consider history and rename on both branch of the merge.
638
638
639 $ hg up 'desc("i-2")'
639 $ hg up 'desc("i-2")'
640 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
640 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
641 $ echo "some update" >> d
641 $ echo "some update" >> d
642 $ hg commit -m "g-1: update d"
642 $ hg commit -m "g-1: update d"
643 created new head
643 created new head
644 $ hg up 'desc("d-2")'
644 $ hg up 'desc("d-2")'
645 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
645 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
646 $ hg merge 'desc("g-1")' --tool :union
646 $ hg merge 'desc("g-1")' --tool :union
647 merging d
647 merging d
648 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
648 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
649 (branch merge, don't forget to commit)
649 (branch merge, don't forget to commit)
650 $ hg ci -m 'mDGm-0 simple merge - one way'
650 $ hg ci -m 'mDGm-0 simple merge - one way'
651 $ hg up 'desc("g-1")'
651 $ hg up 'desc("g-1")'
652 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
652 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
653 $ hg merge 'desc("d-2")' --tool :union
653 $ hg merge 'desc("d-2")' --tool :union
654 merging d
654 merging d
655 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
655 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
656 (branch merge, don't forget to commit)
656 (branch merge, don't forget to commit)
657 $ hg ci -m 'mGDm-0 simple merge - the other way'
657 $ hg ci -m 'mGDm-0 simple merge - the other way'
658 created new head
658 created new head
659 $ hg log -G --rev '::(desc("mDGm")+desc("mGDm"))'
659 $ hg log -G --rev '::(desc("mDGm")+desc("mGDm"))'
660 @ 27 mGDm-0 simple merge - the other way
660 @ 27 mGDm-0 simple merge - the other way
661 |\
661 |\
662 +---o 26 mDGm-0 simple merge - one way
662 +---o 26 mDGm-0 simple merge - one way
663 | |/
663 | |/
664 | o 25 g-1: update d
664 | o 25 g-1: update d
665 | |
665 | |
666 o | 8 d-2 re-add d
666 o | 8 d-2 re-add d
667 | |
667 | |
668 o | 7 d-1 delete d
668 o | 7 d-1 delete d
669 |/
669 |/
670 o 2 i-2: c -move-> d
670 o 2 i-2: c -move-> d
671 |
671 |
672 o 1 i-1: a -move-> c
672 o 1 i-1: a -move-> c
673 |
673 |
674 o 0 i-0 initial commit: a b h
674 o 0 i-0 initial commit: a b h
675
675
676 One side of the merge have a long history with rename. The other side of the
676 One side of the merge have a long history with rename. The other side of the
677 merge point to a new file with a smaller history. Each side is "valid".
677 merge point to a new file with a smaller history. Each side is "valid".
678
678
679 (and again the filelog based algorithm only explore one, with a pick based on
679 (and again the filelog based algorithm only explore one, with a pick based on
680 revision numbers)
680 revision numbers)
681
681
682 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mDGm-0")'
682 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mDGm-0")'
683 A d
683 A d
684 a (filelog !)
684 a (filelog !)
685 R a
685 R a
686 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mGDm-0")'
686 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mGDm-0")'
687 A d
687 A d
688 a
688 a
689 R a
689 R a
690 $ hg status --copies --rev 'desc("d-2")' --rev 'desc("mDGm-0")'
690 $ hg status --copies --rev 'desc("d-2")' --rev 'desc("mDGm-0")'
691 M d
691 M d
692 $ hg status --copies --rev 'desc("d-2")' --rev 'desc("mGDm-0")'
692 $ hg status --copies --rev 'desc("d-2")' --rev 'desc("mGDm-0")'
693 M d
693 M d
694 $ hg status --copies --rev 'desc("g-1")' --rev 'desc("mDGm-0")'
694 $ hg status --copies --rev 'desc("g-1")' --rev 'desc("mDGm-0")'
695 M d
695 M d
696 $ hg status --copies --rev 'desc("g-1")' --rev 'desc("mGDm-0")'
696 $ hg status --copies --rev 'desc("g-1")' --rev 'desc("mGDm-0")'
697 M d
697 M d
698
698
699 $ hg log -Gfr 'desc("mDGm-0")' d
699 $ hg log -Gfr 'desc("mDGm-0")' d
700 o 26 mDGm-0 simple merge - one way
700 o 26 mDGm-0 simple merge - one way
701 |\
701 |\
702 | o 25 g-1: update d
702 | o 25 g-1: update d
703 | |
703 | |
704 o | 8 d-2 re-add d
704 o | 8 d-2 re-add d
705 |/
705 |/
706 o 2 i-2: c -move-> d
706 o 2 i-2: c -move-> d
707 |
707 |
708 o 1 i-1: a -move-> c
708 o 1 i-1: a -move-> c
709 |
709 |
710 o 0 i-0 initial commit: a b h
710 o 0 i-0 initial commit: a b h
711
711
712
712
713
713
714 $ hg log -Gfr 'desc("mDGm-0")' d
714 $ hg log -Gfr 'desc("mDGm-0")' d
715 o 26 mDGm-0 simple merge - one way
715 o 26 mDGm-0 simple merge - one way
716 |\
716 |\
717 | o 25 g-1: update d
717 | o 25 g-1: update d
718 | |
718 | |
719 o | 8 d-2 re-add d
719 o | 8 d-2 re-add d
720 |/
720 |/
721 o 2 i-2: c -move-> d
721 o 2 i-2: c -move-> d
722 |
722 |
723 o 1 i-1: a -move-> c
723 o 1 i-1: a -move-> c
724 |
724 |
725 o 0 i-0 initial commit: a b h
725 o 0 i-0 initial commit: a b h
726
726
727
727
728
728
729 Merge:
729 Merge:
730 - one with change to a file (d)
730 - one with change to a file (d)
731 - one overwriting that file with a rename (from h to i, to d)
731 - one overwriting that file with a rename (from h to i, to d)
732
732
733 This case is similar to BF/FB, but an actual merge happens, so both side of the
733 This case is similar to BF/FB, but an actual merge happens, so both side of the
734 history are relevant.
734 history are relevant.
735
735
736 Note:
736 Note:
737 | In this case, the merge get conflicting information since on one side we have
737 | In this case, the merge get conflicting information since on one side we have
738 | "a -> c -> d". and one the other one we have "h -> i -> d".
738 | "a -> c -> d". and one the other one we have "h -> i -> d".
739 |
739 |
740 | The current code arbitrarily pick one side
740 | The current code arbitrarily pick one side
741
741
742 $ hg up 'desc("f-2")'
742 $ hg up 'desc("f-2")'
743 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
743 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
744 $ hg merge 'desc("g-1")' --tool :union
744 $ hg merge 'desc("g-1")' --tool :union
745 merging d
745 merging d
746 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
746 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
747 (branch merge, don't forget to commit)
747 (branch merge, don't forget to commit)
748 $ hg ci -m 'mFGm-0 simple merge - one way'
748 $ hg ci -m 'mFGm-0 simple merge - one way'
749 created new head
749 created new head
750 $ hg up 'desc("g-1")'
750 $ hg up 'desc("g-1")'
751 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
751 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
752 $ hg merge 'desc("f-2")' --tool :union
752 $ hg merge 'desc("f-2")' --tool :union
753 merging d
753 merging d
754 0 files updated, 1 files merged, 1 files removed, 0 files unresolved
754 0 files updated, 1 files merged, 1 files removed, 0 files unresolved
755 (branch merge, don't forget to commit)
755 (branch merge, don't forget to commit)
756 $ hg ci -m 'mGFm-0 simple merge - the other way'
756 $ hg ci -m 'mGFm-0 simple merge - the other way'
757 created new head
757 created new head
758 $ hg log -G --rev '::(desc("mGFm")+desc("mFGm"))'
758 $ hg log -G --rev '::(desc("mGFm")+desc("mFGm"))'
759 @ 29 mGFm-0 simple merge - the other way
759 @ 29 mGFm-0 simple merge - the other way
760 |\
760 |\
761 +---o 28 mFGm-0 simple merge - one way
761 +---o 28 mFGm-0 simple merge - one way
762 | |/
762 | |/
763 | o 25 g-1: update d
763 | o 25 g-1: update d
764 | |
764 | |
765 o | 22 f-2: rename i -> d
765 o | 22 f-2: rename i -> d
766 | |
766 | |
767 o | 21 f-1: rename h -> i
767 o | 21 f-1: rename h -> i
768 |/
768 |/
769 o 2 i-2: c -move-> d
769 o 2 i-2: c -move-> d
770 |
770 |
771 o 1 i-1: a -move-> c
771 o 1 i-1: a -move-> c
772 |
772 |
773 o 0 i-0 initial commit: a b h
773 o 0 i-0 initial commit: a b h
774
774
775 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mFGm-0")'
775 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mFGm-0")'
776 A d
776 A d
777 h (no-filelog !)
777 h (no-filelog !)
778 a (filelog !)
778 a (filelog !)
779 R a
779 R a
780 R h
780 R h
781 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mGFm-0")'
781 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mGFm-0")'
782 A d
782 A d
783 a
783 a
784 R a
784 R a
785 R h
785 R h
786 $ hg status --copies --rev 'desc("f-2")' --rev 'desc("mFGm-0")'
786 $ hg status --copies --rev 'desc("f-2")' --rev 'desc("mFGm-0")'
787 M d
787 M d
788 $ hg status --copies --rev 'desc("f-2")' --rev 'desc("mGFm-0")'
788 $ hg status --copies --rev 'desc("f-2")' --rev 'desc("mGFm-0")'
789 M d
789 M d
790 $ hg status --copies --rev 'desc("f-1")' --rev 'desc("mFGm-0")'
790 $ hg status --copies --rev 'desc("f-1")' --rev 'desc("mFGm-0")'
791 M d
791 M d
792 i (no-filelog !)
792 i (no-filelog !)
793 R i
793 R i
794 $ hg status --copies --rev 'desc("f-1")' --rev 'desc("mGFm-0")'
794 $ hg status --copies --rev 'desc("f-1")' --rev 'desc("mGFm-0")'
795 M d
795 M d
796 i (no-filelog !)
796 i (no-filelog !)
797 R i
797 R i
798 $ hg status --copies --rev 'desc("g-1")' --rev 'desc("mFGm-0")'
798 $ hg status --copies --rev 'desc("g-1")' --rev 'desc("mFGm-0")'
799 M d
799 M d
800 h (no-filelog !)
800 h (no-filelog !)
801 R h
801 R h
802 $ hg status --copies --rev 'desc("g-1")' --rev 'desc("mGFm-0")'
802 $ hg status --copies --rev 'desc("g-1")' --rev 'desc("mGFm-0")'
803 M d
803 M d
804 h (no-filelog !)
804 h (no-filelog !)
805 R h
805 R h
806
806
807 $ hg log -Gfr 'desc("mFGm-0")' d
807 $ hg log -Gfr 'desc("mFGm-0")' d
808 o 28 mFGm-0 simple merge - one way
808 o 28 mFGm-0 simple merge - one way
809 |\
809 |\
810 | o 25 g-1: update d
810 | o 25 g-1: update d
811 | |
811 | |
812 o | 22 f-2: rename i -> d
812 o | 22 f-2: rename i -> d
813 | |
813 | |
814 o | 21 f-1: rename h -> i
814 o | 21 f-1: rename h -> i
815 |/
815 |/
816 o 2 i-2: c -move-> d
816 o 2 i-2: c -move-> d
817 |
817 |
818 o 1 i-1: a -move-> c
818 o 1 i-1: a -move-> c
819 |
819 |
820 o 0 i-0 initial commit: a b h
820 o 0 i-0 initial commit: a b h
821
821
822
822
823 $ hg log -Gfr 'desc("mGFm-0")' d
823 $ hg log -Gfr 'desc("mGFm-0")' d
824 @ 29 mGFm-0 simple merge - the other way
824 @ 29 mGFm-0 simple merge - the other way
825 |\
825 |\
826 | o 25 g-1: update d
826 | o 25 g-1: update d
827 | |
827 | |
828 o | 22 f-2: rename i -> d
828 o | 22 f-2: rename i -> d
829 | |
829 | |
830 o | 21 f-1: rename h -> i
830 o | 21 f-1: rename h -> i
831 |/
831 |/
832 o 2 i-2: c -move-> d
832 o 2 i-2: c -move-> d
833 |
833 |
834 o 1 i-1: a -move-> c
834 o 1 i-1: a -move-> c
835 |
835 |
836 o 0 i-0 initial commit: a b h
836 o 0 i-0 initial commit: a b h
837
837
838
838
839
839
840 Comparing with merging with a deletion (and keeping the file)
840 Comparing with merging with a deletion (and keeping the file)
841 -------------------------------------------------------------
841 -------------------------------------------------------------
842
842
843 Merge:
843 Merge:
844 - one removing a file (d)
844 - one removing a file (d)
845 - one updating that file
845 - one updating that file
846 - the merge keep the modified version of the file (canceling the delete)
846 - the merge keep the modified version of the file (canceling the delete)
847
847
848 In this case, the file keep on living after the merge. So we should not drop its
848 In this case, the file keep on living after the merge. So we should not drop its
849 copy tracing chain.
849 copy tracing chain.
850
850
851 $ hg up 'desc("c-1")'
851 $ hg up 'desc("c-1")'
852 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
852 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
853 $ hg merge 'desc("g-1")'
853 $ hg merge 'desc("g-1")'
854 file 'd' was deleted in local [working copy] but was modified in other [merge rev].
854 file 'd' was deleted in local [working copy] but was modified in other [merge rev].
855 You can use (c)hanged version, leave (d)eleted, or leave (u)nresolved.
855 You can use (c)hanged version, leave (d)eleted, or leave (u)nresolved.
856 What do you want to do? u
856 What do you want to do? u
857 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
857 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
858 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
858 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
859 [1]
859 [1]
860 $ hg resolve -t :other d
860 $ hg resolve -t :other d
861 (no more unresolved files)
861 (no more unresolved files)
862 $ hg ci -m "mCGm-0"
862 $ hg ci -m "mCGm-0"
863 created new head
863 created new head
864
864
865 $ hg up 'desc("g-1")'
865 $ hg up 'desc("g-1")'
866 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
866 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
867 $ hg merge 'desc("c-1")'
867 $ hg merge 'desc("c-1")'
868 file 'd' was deleted in other [merge rev] but was modified in local [working copy].
868 file 'd' was deleted in other [merge rev] but was modified in local [working copy].
869 You can use (c)hanged version, (d)elete, or leave (u)nresolved.
869 You can use (c)hanged version, (d)elete, or leave (u)nresolved.
870 What do you want to do? u
870 What do you want to do? u
871 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
871 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
872 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
872 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
873 [1]
873 [1]
874 $ hg resolve -t :local d
874 $ hg resolve -t :local d
875 (no more unresolved files)
875 (no more unresolved files)
876 $ hg ci -m "mGCm-0"
876 $ hg ci -m "mGCm-0"
877 created new head
877 created new head
878
878
879 $ hg log -G --rev '::(desc("mCGm")+desc("mGCm"))'
879 $ hg log -G --rev '::(desc("mCGm")+desc("mGCm"))'
880 @ 31 mGCm-0
880 @ 31 mGCm-0
881 |\
881 |\
882 +---o 30 mCGm-0
882 +---o 30 mCGm-0
883 | |/
883 | |/
884 | o 25 g-1: update d
884 | o 25 g-1: update d
885 | |
885 | |
886 o | 6 c-1 delete d
886 o | 6 c-1 delete d
887 |/
887 |/
888 o 2 i-2: c -move-> d
888 o 2 i-2: c -move-> d
889 |
889 |
890 o 1 i-1: a -move-> c
890 o 1 i-1: a -move-> c
891 |
891 |
892 o 0 i-0 initial commit: a b h
892 o 0 i-0 initial commit: a b h
893
893
894
894
895 BROKEN: 'a' should be the the source of 'd' in the changeset centric algorithm too
895 BROKEN: 'a' should be the the source of 'd' in the changeset centric algorithm too
896
896
897 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mCGm-0")'
897 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mCGm-0")'
898 A d
898 A d
899 a (filelog !)
899 a (filelog !)
900 R a
900 R a
901 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mGCm-0")'
901 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mGCm-0")'
902 A d
902 A d
903 a (filelog !)
903 a (filelog !)
904 R a
904 R a
905 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mCGm-0")'
905 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mCGm-0")'
906 A d
906 A d
907 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mGCm-0")'
907 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mGCm-0")'
908 A d
908 A d
909 $ hg status --copies --rev 'desc("g-1")' --rev 'desc("mCGm-0")'
909 $ hg status --copies --rev 'desc("g-1")' --rev 'desc("mCGm-0")'
910 $ hg status --copies --rev 'desc("g-1")' --rev 'desc("mGCm-0")'
910 $ hg status --copies --rev 'desc("g-1")' --rev 'desc("mGCm-0")'
911
911
912
912
913 Comparing with merge restoring an untouched deleted file
913 Comparing with merge restoring an untouched deleted file
914 --------------------------------------------------------
914 --------------------------------------------------------
915
915
916 Merge:
916 Merge:
917 - one removing a file (d)
917 - one removing a file (d)
918 - one leaving the file untouched
918 - one leaving the file untouched
919 - the merge actively restore the file to the same content.
919 - the merge actively restore the file to the same content.
920
920
921 In this case, the file keep on living after the merge. So we should not drop its
921 In this case, the file keep on living after the merge. So we should not drop its
922 copy tracing chain.
922 copy tracing chain.
923
923
924 $ hg up 'desc("c-1")'
924 $ hg up 'desc("c-1")'
925 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
925 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
926 $ hg merge 'desc("b-1")'
926 $ hg merge 'desc("b-1")'
927 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
927 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
928 (branch merge, don't forget to commit)
928 (branch merge, don't forget to commit)
929 $ hg revert --rev 'desc("b-1")' d
929 $ hg revert --rev 'desc("b-1")' d
930 $ hg ci -m "mCB-revert-m-0"
930 $ hg ci -m "mCB-revert-m-0"
931 created new head
931 created new head
932
932
933 $ hg up 'desc("b-1")'
933 $ hg up 'desc("b-1")'
934 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
934 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
935 $ hg merge 'desc("c-1")'
935 $ hg merge 'desc("c-1")'
936 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
936 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
937 (branch merge, don't forget to commit)
937 (branch merge, don't forget to commit)
938 $ hg revert --rev 'desc("b-1")' d
938 $ hg revert --rev 'desc("b-1")' d
939 $ hg ci -m "mBC-revert-m-0"
939 $ hg ci -m "mBC-revert-m-0"
940 created new head
940 created new head
941
941
942 $ hg log -G --rev '::(desc("mCB-revert-m")+desc("mBC-revert-m"))'
942 $ hg log -G --rev '::(desc("mCB-revert-m")+desc("mBC-revert-m"))'
943 @ 33 mBC-revert-m-0
943 @ 33 mBC-revert-m-0
944 |\
944 |\
945 +---o 32 mCB-revert-m-0
945 +---o 32 mCB-revert-m-0
946 | |/
946 | |/
947 | o 6 c-1 delete d
947 | o 6 c-1 delete d
948 | |
948 | |
949 o | 5 b-1: b update
949 o | 5 b-1: b update
950 |/
950 |/
951 o 2 i-2: c -move-> d
951 o 2 i-2: c -move-> d
952 |
952 |
953 o 1 i-1: a -move-> c
953 o 1 i-1: a -move-> c
954 |
954 |
955 o 0 i-0 initial commit: a b h
955 o 0 i-0 initial commit: a b h
956
956
957
957
958 BROKEN: 'a' should be the the source of 'd' in the changeset centric algorithm too
958 BROKEN: 'a' should be the the source of 'd' in the changeset centric algorithm too
959
959
960 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mCB-revert-m-0")'
960 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mCB-revert-m-0")'
961 M b
961 M b
962 A d
962 A d
963 a (filelog !)
963 a (filelog !)
964 R a
964 R a
965 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mBC-revert-m-0")'
965 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mBC-revert-m-0")'
966 M b
966 M b
967 A d
967 A d
968 a (filelog !)
968 a (filelog !)
969 R a
969 R a
970 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mCB-revert-m-0")'
970 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mCB-revert-m-0")'
971 M b
971 M b
972 A d
972 A d
973 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mBC-revert-m-0")'
973 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mBC-revert-m-0")'
974 M b
974 M b
975 A d
975 A d
976 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mCB-revert-m-0")'
976 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mCB-revert-m-0")'
977 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mBC-revert-m-0")'
977 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mBC-revert-m-0")'
978
978
979
979
980 Test that sidedata computations during upgrades ares correct
980 Test that sidedata computations during upgrades ares correct
981 ============================================================
981 ============================================================
982
982
983 We upgrade a repository that is not using sidedata (the filelog case) and
983 We upgrade a repository that is not using sidedata (the filelog case) and
984 check that the same side data have been generated as if they were computed at
984 check that the same side data have been generated as if they were computed at
985 commit time.
985 commit time.
986
986
987
987
988 #if filelog
988 #if filelog
989 $ cat >> $HGRCPATH << EOF
989 $ cat >> $HGRCPATH << EOF
990 > [format]
990 > [format]
991 > exp-use-side-data = yes
991 > exp-use-side-data = yes
992 > exp-use-copies-side-data-changeset = yes
992 > exp-use-copies-side-data-changeset = yes
993 > EOF
993 > EOF
994 $ hg debugformat -v
994 $ hg debugformat -v
995 format-variant repo config default
995 format-variant repo config default
996 fncache: yes yes yes
996 fncache: yes yes yes
997 dotencode: yes yes yes
997 dotencode: yes yes yes
998 generaldelta: yes yes yes
998 generaldelta: yes yes yes
999 sparserevlog: yes yes yes
999 sparserevlog: yes yes yes
1000 sidedata: no yes no
1000 sidedata: no yes no
1001 persistent-nodemap: no no no
1001 persistent-nodemap: no no no
1002 copies-sdc: no yes no
1002 copies-sdc: no yes no
1003 plain-cl-delta: yes yes yes
1003 plain-cl-delta: yes yes yes
1004 compression: * (glob)
1004 compression: * (glob)
1005 compression-level: default default default
1005 compression-level: default default default
1006 $ hg debugupgraderepo --run --quiet
1006 $ hg debugupgraderepo --run --quiet
1007 upgrade will perform the following actions:
1007 upgrade will perform the following actions:
1008
1008
1009 requirements
1009 requirements
1010 preserved: * (glob)
1010 preserved: * (glob)
1011 added: exp-copies-sidedata-changeset, exp-sidedata-flag
1011 added: exp-copies-sidedata-changeset, exp-sidedata-flag
1012
1012
1013 #endif
1013 #endif
1014
1014
1015
1015
1016 #if no-compatibility
1016 #if no-compatibility
1017
1017
1018 $ for rev in `hg log --rev 'all()' -T '{rev}\n'`; do
1018 $ for rev in `hg log --rev 'all()' -T '{rev}\n'`; do
1019 > echo "##### revision $rev #####"
1019 > echo "##### revision $rev #####"
1020 > hg debugsidedata -c -v -- $rev
1020 > hg debugsidedata -c -v -- $rev
1021 > hg debugchangedfiles $rev
1021 > hg debugchangedfiles $rev
1022 > done
1022 > done
1023 ##### revision 0 #####
1023 ##### revision 0 #####
1024 1 sidedata entries
1024 1 sidedata entries
1025 entry-0014 size 34
1025 entry-0014 size 34
1026 '\x00\x00\x00\x03\x04\x00\x00\x00\x01\x00\x00\x00\x00\x04\x00\x00\x00\x02\x00\x00\x00\x00\x04\x00\x00\x00\x03\x00\x00\x00\x00abh'
1026 '\x00\x00\x00\x03\x04\x00\x00\x00\x01\x00\x00\x00\x00\x04\x00\x00\x00\x02\x00\x00\x00\x00\x04\x00\x00\x00\x03\x00\x00\x00\x00abh'
1027 added : a, ;
1027 added : a, ;
1028 added : b, ;
1028 added : b, ;
1029 added : h, ;
1029 added : h, ;
1030 ##### revision 1 #####
1030 ##### revision 1 #####
1031 1 sidedata entries
1031 1 sidedata entries
1032 entry-0014 size 24
1032 entry-0014 size 24
1033 '\x00\x00\x00\x02\x0c\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00ac'
1033 '\x00\x00\x00\x02\x0c\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00ac'
1034 removed : a, ;
1034 removed : a, ;
1035 added p1: c, a;
1035 added p1: c, a;
1036 ##### revision 2 #####
1036 ##### revision 2 #####
1037 1 sidedata entries
1037 1 sidedata entries
1038 entry-0014 size 24
1038 entry-0014 size 24
1039 '\x00\x00\x00\x02\x0c\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00cd'
1039 '\x00\x00\x00\x02\x0c\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00cd'
1040 removed : c, ;
1040 removed : c, ;
1041 added p1: d, c;
1041 added p1: d, c;
1042 ##### revision 3 #####
1042 ##### revision 3 #####
1043 1 sidedata entries
1043 1 sidedata entries
1044 entry-0014 size 24
1044 entry-0014 size 24
1045 '\x00\x00\x00\x02\x0c\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00de'
1045 '\x00\x00\x00\x02\x0c\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00de'
1046 removed : d, ;
1046 removed : d, ;
1047 added p1: e, d;
1047 added p1: e, d;
1048 ##### revision 4 #####
1048 ##### revision 4 #####
1049 1 sidedata entries
1049 1 sidedata entries
1050 entry-0014 size 24
1050 entry-0014 size 24
1051 '\x00\x00\x00\x02\x0c\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00ef'
1051 '\x00\x00\x00\x02\x0c\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00ef'
1052 removed : e, ;
1052 removed : e, ;
1053 added p1: f, e;
1053 added p1: f, e;
1054 ##### revision 5 #####
1054 ##### revision 5 #####
1055 1 sidedata entries
1055 1 sidedata entries
1056 entry-0014 size 14
1056 entry-0014 size 14
1057 '\x00\x00\x00\x01\x14\x00\x00\x00\x01\x00\x00\x00\x00b'
1057 '\x00\x00\x00\x01\x14\x00\x00\x00\x01\x00\x00\x00\x00b'
1058 touched : b, ;
1058 touched : b, ;
1059 ##### revision 6 #####
1059 ##### revision 6 #####
1060 1 sidedata entries
1060 1 sidedata entries
1061 entry-0014 size 14
1061 entry-0014 size 14
1062 '\x00\x00\x00\x01\x0c\x00\x00\x00\x01\x00\x00\x00\x00d'
1062 '\x00\x00\x00\x01\x0c\x00\x00\x00\x01\x00\x00\x00\x00d'
1063 removed : d, ;
1063 removed : d, ;
1064 ##### revision 7 #####
1064 ##### revision 7 #####
1065 1 sidedata entries
1065 1 sidedata entries
1066 entry-0014 size 14
1066 entry-0014 size 14
1067 '\x00\x00\x00\x01\x0c\x00\x00\x00\x01\x00\x00\x00\x00d'
1067 '\x00\x00\x00\x01\x0c\x00\x00\x00\x01\x00\x00\x00\x00d'
1068 removed : d, ;
1068 removed : d, ;
1069 ##### revision 8 #####
1069 ##### revision 8 #####
1070 1 sidedata entries
1070 1 sidedata entries
1071 entry-0014 size 14
1071 entry-0014 size 14
1072 '\x00\x00\x00\x01\x04\x00\x00\x00\x01\x00\x00\x00\x00d'
1072 '\x00\x00\x00\x01\x04\x00\x00\x00\x01\x00\x00\x00\x00d'
1073 added : d, ;
1073 added : d, ;
1074 ##### revision 9 #####
1074 ##### revision 9 #####
1075 1 sidedata entries
1075 1 sidedata entries
1076 entry-0014 size 24
1076 entry-0014 size 24
1077 '\x00\x00\x00\x02\x0c\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00bg'
1077 '\x00\x00\x00\x02\x0c\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00bg'
1078 removed : b, ;
1078 removed : b, ;
1079 added p1: g, b;
1079 added p1: g, b;
1080 ##### revision 10 #####
1080 ##### revision 10 #####
1081 1 sidedata entries
1081 1 sidedata entries
1082 entry-0014 size 24
1082 entry-0014 size 24
1083 '\x00\x00\x00\x02\x06\x00\x00\x00\x01\x00\x00\x00\x01\x0c\x00\x00\x00\x02\x00\x00\x00\x00fg'
1083 '\x00\x00\x00\x02\x06\x00\x00\x00\x01\x00\x00\x00\x01\x0c\x00\x00\x00\x02\x00\x00\x00\x00fg'
1084 added p1: f, g;
1084 added p1: f, g;
1085 removed : g, ;
1085 removed : g, ;
1086 ##### revision 11 #####
1086 ##### revision 11 #####
1087 1 sidedata entries
1087 1 sidedata entries
1088 entry-0014 size 4
1088 entry-0014 size 4
1089 '\x00\x00\x00\x00'
1089 '\x00\x00\x00\x00'
1090 ##### revision 12 #####
1090 ##### revision 12 #####
1091 1 sidedata entries
1091 1 sidedata entries
1092 entry-0014 size 4
1092 entry-0014 size 4
1093 '\x00\x00\x00\x00'
1093 '\x00\x00\x00\x00'
1094 ##### revision 13 #####
1094 ##### revision 13 #####
1095 1 sidedata entries
1095 1 sidedata entries
1096 entry-0014 size 4
1096 entry-0014 size 4
1097 '\x00\x00\x00\x00'
1097 '\x00\x00\x00\x00'
1098 ##### revision 14 #####
1098 ##### revision 14 #####
1099 1 sidedata entries
1099 1 sidedata entries
1100 entry-0014 size 14
1100 entry-0014 size 14
1101 '\x00\x00\x00\x01\x04\x00\x00\x00\x01\x00\x00\x00\x00d'
1101 '\x00\x00\x00\x01\x04\x00\x00\x00\x01\x00\x00\x00\x00d'
1102 added : d, ;
1102 added : d, ;
1103 ##### revision 15 #####
1103 ##### revision 15 #####
1104 1 sidedata entries
1104 1 sidedata entries
1105 entry-0014 size 4
1105 entry-0014 size 4
1106 '\x00\x00\x00\x00'
1106 '\x00\x00\x00\x00'
1107 ##### revision 16 #####
1107 ##### revision 16 #####
1108 1 sidedata entries
1108 1 sidedata entries
1109 entry-0014 size 14
1109 entry-0014 size 14
1110 '\x00\x00\x00\x01\x04\x00\x00\x00\x01\x00\x00\x00\x00d'
1110 '\x00\x00\x00\x01\x04\x00\x00\x00\x01\x00\x00\x00\x00d'
1111 added : d, ;
1111 added : d, ;
1112 ##### revision 17 #####
1112 ##### revision 17 #####
1113 1 sidedata entries
1113 1 sidedata entries
1114 entry-0014 size 4
1114 entry-0014 size 4
1115 '\x00\x00\x00\x00'
1115 '\x00\x00\x00\x00'
1116 ##### revision 18 #####
1116 ##### revision 18 #####
1117 1 sidedata entries
1117 1 sidedata entries
1118 entry-0014 size 4
1118 entry-0014 size 4
1119 '\x00\x00\x00\x00'
1119 '\x00\x00\x00\x00'
1120 ##### revision 19 #####
1120 ##### revision 19 #####
1121 1 sidedata entries
1121 1 sidedata entries
1122 entry-0014 size 14
1122 entry-0014 size 14
1123 '\x00\x00\x00\x01\x08\x00\x00\x00\x01\x00\x00\x00\x00f'
1123 '\x00\x00\x00\x01\x08\x00\x00\x00\x01\x00\x00\x00\x00f'
1124 merged : f, ;
1124 merged : f, ;
1125 ##### revision 20 #####
1125 ##### revision 20 #####
1126 1 sidedata entries
1126 1 sidedata entries
1127 entry-0014 size 14
1127 entry-0014 size 14
1128 '\x00\x00\x00\x01\x08\x00\x00\x00\x01\x00\x00\x00\x00f'
1128 '\x00\x00\x00\x01\x08\x00\x00\x00\x01\x00\x00\x00\x00f'
1129 merged : f, ;
1129 merged : f, ;
1130 ##### revision 21 #####
1130 ##### revision 21 #####
1131 1 sidedata entries
1131 1 sidedata entries
1132 entry-0014 size 24
1132 entry-0014 size 24
1133 '\x00\x00\x00\x02\x0c\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00hi'
1133 '\x00\x00\x00\x02\x0c\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00hi'
1134 removed : h, ;
1134 removed : h, ;
1135 added p1: i, h;
1135 added p1: i, h;
1136 ##### revision 22 #####
1136 ##### revision 22 #####
1137 1 sidedata entries
1137 1 sidedata entries
1138 entry-0014 size 24
1138 entry-0014 size 24
1139 '\x00\x00\x00\x02\x16\x00\x00\x00\x01\x00\x00\x00\x01\x0c\x00\x00\x00\x02\x00\x00\x00\x00di'
1139 '\x00\x00\x00\x02\x16\x00\x00\x00\x01\x00\x00\x00\x01\x0c\x00\x00\x00\x02\x00\x00\x00\x00di'
1140 touched p1: d, i;
1140 touched p1: d, i;
1141 removed : i, ;
1141 removed : i, ;
1142 ##### revision 23 #####
1142 ##### revision 23 #####
1143 1 sidedata entries
1143 1 sidedata entries
1144 entry-0014 size 4
1144 entry-0014 size 4
1145 '\x00\x00\x00\x00'
1145 '\x00\x00\x00\x00'
1146 ##### revision 24 #####
1146 ##### revision 24 #####
1147 1 sidedata entries
1147 1 sidedata entries
1148 entry-0014 size 4
1148 entry-0014 size 4
1149 '\x00\x00\x00\x00'
1149 '\x00\x00\x00\x00'
1150 ##### revision 25 #####
1150 ##### revision 25 #####
1151 1 sidedata entries
1151 1 sidedata entries
1152 entry-0014 size 14
1152 entry-0014 size 14
1153 '\x00\x00\x00\x01\x14\x00\x00\x00\x01\x00\x00\x00\x00d'
1153 '\x00\x00\x00\x01\x14\x00\x00\x00\x01\x00\x00\x00\x00d'
1154 touched : d, ;
1154 touched : d, ;
1155 ##### revision 26 #####
1155 ##### revision 26 #####
1156 1 sidedata entries
1156 1 sidedata entries
1157 entry-0014 size 14
1157 entry-0014 size 14
1158 '\x00\x00\x00\x01\x08\x00\x00\x00\x01\x00\x00\x00\x00d'
1158 '\x00\x00\x00\x01\x08\x00\x00\x00\x01\x00\x00\x00\x00d'
1159 merged : d, ;
1159 merged : d, ;
1160 ##### revision 27 #####
1160 ##### revision 27 #####
1161 1 sidedata entries
1161 1 sidedata entries
1162 entry-0014 size 14
1162 entry-0014 size 14
1163 '\x00\x00\x00\x01\x08\x00\x00\x00\x01\x00\x00\x00\x00d'
1163 '\x00\x00\x00\x01\x08\x00\x00\x00\x01\x00\x00\x00\x00d'
1164 merged : d, ;
1164 merged : d, ;
1165 ##### revision 28 #####
1165 ##### revision 28 #####
1166 1 sidedata entries
1166 1 sidedata entries
1167 entry-0014 size 14
1167 entry-0014 size 14
1168 '\x00\x00\x00\x01\x08\x00\x00\x00\x01\x00\x00\x00\x00d'
1168 '\x00\x00\x00\x01\x08\x00\x00\x00\x01\x00\x00\x00\x00d'
1169 merged : d, ;
1169 merged : d, ;
1170 ##### revision 29 #####
1170 ##### revision 29 #####
1171 1 sidedata entries
1171 1 sidedata entries
1172 entry-0014 size 14
1172 entry-0014 size 14
1173 '\x00\x00\x00\x01\x08\x00\x00\x00\x01\x00\x00\x00\x00d'
1173 '\x00\x00\x00\x01\x08\x00\x00\x00\x01\x00\x00\x00\x00d'
1174 merged : d, ;
1174 merged : d, ;
1175 ##### revision 30 #####
1175 ##### revision 30 #####
1176 1 sidedata entries
1176 1 sidedata entries
1177 entry-0014 size 4
1177 entry-0014 size 14
1178 '\x00\x00\x00\x00'
1178 '\x00\x00\x00\x01\x10\x00\x00\x00\x01\x00\x00\x00\x00d'
1179 salvaged : d, ;
1179 ##### revision 31 #####
1180 ##### revision 31 #####
1180 1 sidedata entries
1181 1 sidedata entries
1181 entry-0014 size 4
1182 entry-0014 size 14
1182 '\x00\x00\x00\x00'
1183 '\x00\x00\x00\x01\x10\x00\x00\x00\x01\x00\x00\x00\x00d'
1184 salvaged : d, ;
1183 ##### revision 32 #####
1185 ##### revision 32 #####
1184 1 sidedata entries
1186 1 sidedata entries
1185 entry-0014 size 4
1187 entry-0014 size 14
1186 '\x00\x00\x00\x00'
1188 '\x00\x00\x00\x01\x10\x00\x00\x00\x01\x00\x00\x00\x00d'
1189 salvaged : d, ;
1187 ##### revision 33 #####
1190 ##### revision 33 #####
1188 1 sidedata entries
1191 1 sidedata entries
1189 entry-0014 size 4
1192 entry-0014 size 14
1190 '\x00\x00\x00\x00'
1193 '\x00\x00\x00\x01\x10\x00\x00\x00\x01\x00\x00\x00\x00d'
1194 salvaged : d, ;
1191
1195
1192 #endif
1196 #endif
General Comments 0
You need to be logged in to leave comments. Login now