##// END OF EJS Templates
copies: detect case when a merge decision overwrite previous data...
marmoute -
r47310:c19c6620 default
parent child Browse files
Show More
@@ -1,1269 +1,1281 b''
1 # coding: utf8
1 # coding: utf8
2 # copies.py - copy detection for Mercurial
2 # copies.py - copy detection for Mercurial
3 #
3 #
4 # Copyright 2008 Matt Mackall <mpm@selenic.com>
4 # Copyright 2008 Matt Mackall <mpm@selenic.com>
5 #
5 #
6 # This software may be used and distributed according to the terms of the
6 # This software may be used and distributed according to the terms of the
7 # GNU General Public License version 2 or any later version.
7 # GNU General Public License version 2 or any later version.
8
8
9 from __future__ import absolute_import
9 from __future__ import absolute_import
10
10
11 import collections
11 import collections
12 import os
12 import os
13
13
14 from .i18n import _
14 from .i18n import _
15 from .node import (
15 from .node import (
16 nullid,
16 nullid,
17 nullrev,
17 nullrev,
18 )
18 )
19
19
20 from . import (
20 from . import (
21 match as matchmod,
21 match as matchmod,
22 pathutil,
22 pathutil,
23 policy,
23 policy,
24 pycompat,
24 pycompat,
25 util,
25 util,
26 )
26 )
27
27
28
28
29 from .utils import stringutil
29 from .utils import stringutil
30
30
31 from .revlogutils import (
31 from .revlogutils import (
32 flagutil,
32 flagutil,
33 sidedata as sidedatamod,
33 sidedata as sidedatamod,
34 )
34 )
35
35
36 rustmod = policy.importrust("copy_tracing")
36 rustmod = policy.importrust("copy_tracing")
37
37
38
38
39 def _filter(src, dst, t):
39 def _filter(src, dst, t):
40 """filters out invalid copies after chaining"""
40 """filters out invalid copies after chaining"""
41
41
42 # When _chain()'ing copies in 'a' (from 'src' via some other commit 'mid')
42 # When _chain()'ing copies in 'a' (from 'src' via some other commit 'mid')
43 # with copies in 'b' (from 'mid' to 'dst'), we can get the different cases
43 # with copies in 'b' (from 'mid' to 'dst'), we can get the different cases
44 # in the following table (not including trivial cases). For example, case 6
44 # in the following table (not including trivial cases). For example, case 6
45 # is where a file existed in 'src' and remained under that name in 'mid' and
45 # is where a file existed in 'src' and remained under that name in 'mid' and
46 # then was renamed between 'mid' and 'dst'.
46 # then was renamed between 'mid' and 'dst'.
47 #
47 #
48 # case src mid dst result
48 # case src mid dst result
49 # 1 x y - -
49 # 1 x y - -
50 # 2 x y y x->y
50 # 2 x y y x->y
51 # 3 x y x -
51 # 3 x y x -
52 # 4 x y z x->z
52 # 4 x y z x->z
53 # 5 - x y -
53 # 5 - x y -
54 # 6 x x y x->y
54 # 6 x x y x->y
55 #
55 #
56 # _chain() takes care of chaining the copies in 'a' and 'b', but it
56 # _chain() takes care of chaining the copies in 'a' and 'b', but it
57 # cannot tell the difference between cases 1 and 2, between 3 and 4, or
57 # cannot tell the difference between cases 1 and 2, between 3 and 4, or
58 # between 5 and 6, so it includes all cases in its result.
58 # between 5 and 6, so it includes all cases in its result.
59 # Cases 1, 3, and 5 are then removed by _filter().
59 # Cases 1, 3, and 5 are then removed by _filter().
60
60
61 for k, v in list(t.items()):
61 for k, v in list(t.items()):
62 if k == v: # case 3
62 if k == v: # case 3
63 del t[k]
63 del t[k]
64 elif v not in src: # case 5
64 elif v not in src: # case 5
65 # remove copies from files that didn't exist
65 # remove copies from files that didn't exist
66 del t[k]
66 del t[k]
67 elif k not in dst: # case 1
67 elif k not in dst: # case 1
68 # remove copies to files that were then removed
68 # remove copies to files that were then removed
69 del t[k]
69 del t[k]
70
70
71
71
72 def _chain(prefix, suffix):
72 def _chain(prefix, suffix):
73 """chain two sets of copies 'prefix' and 'suffix'"""
73 """chain two sets of copies 'prefix' and 'suffix'"""
74 result = prefix.copy()
74 result = prefix.copy()
75 for key, value in pycompat.iteritems(suffix):
75 for key, value in pycompat.iteritems(suffix):
76 result[key] = prefix.get(value, value)
76 result[key] = prefix.get(value, value)
77 return result
77 return result
78
78
79
79
80 def _tracefile(fctx, am, basemf):
80 def _tracefile(fctx, am, basemf):
81 """return file context that is the ancestor of fctx present in ancestor
81 """return file context that is the ancestor of fctx present in ancestor
82 manifest am
82 manifest am
83
83
84 Note: we used to try and stop after a given limit, however checking if that
84 Note: we used to try and stop after a given limit, however checking if that
85 limit is reached turned out to be very expensive. we are better off
85 limit is reached turned out to be very expensive. we are better off
86 disabling that feature."""
86 disabling that feature."""
87
87
88 for f in fctx.ancestors():
88 for f in fctx.ancestors():
89 path = f.path()
89 path = f.path()
90 if am.get(path, None) == f.filenode():
90 if am.get(path, None) == f.filenode():
91 return path
91 return path
92 if basemf and basemf.get(path, None) == f.filenode():
92 if basemf and basemf.get(path, None) == f.filenode():
93 return path
93 return path
94
94
95
95
96 def _dirstatecopies(repo, match=None):
96 def _dirstatecopies(repo, match=None):
97 ds = repo.dirstate
97 ds = repo.dirstate
98 c = ds.copies().copy()
98 c = ds.copies().copy()
99 for k in list(c):
99 for k in list(c):
100 if ds[k] not in b'anm' or (match and not match(k)):
100 if ds[k] not in b'anm' or (match and not match(k)):
101 del c[k]
101 del c[k]
102 return c
102 return c
103
103
104
104
105 def _computeforwardmissing(a, b, match=None):
105 def _computeforwardmissing(a, b, match=None):
106 """Computes which files are in b but not a.
106 """Computes which files are in b but not a.
107 This is its own function so extensions can easily wrap this call to see what
107 This is its own function so extensions can easily wrap this call to see what
108 files _forwardcopies is about to process.
108 files _forwardcopies is about to process.
109 """
109 """
110 ma = a.manifest()
110 ma = a.manifest()
111 mb = b.manifest()
111 mb = b.manifest()
112 return mb.filesnotin(ma, match=match)
112 return mb.filesnotin(ma, match=match)
113
113
114
114
115 def usechangesetcentricalgo(repo):
115 def usechangesetcentricalgo(repo):
116 """Checks if we should use changeset-centric copy algorithms"""
116 """Checks if we should use changeset-centric copy algorithms"""
117 if repo.filecopiesmode == b'changeset-sidedata':
117 if repo.filecopiesmode == b'changeset-sidedata':
118 return True
118 return True
119 readfrom = repo.ui.config(b'experimental', b'copies.read-from')
119 readfrom = repo.ui.config(b'experimental', b'copies.read-from')
120 changesetsource = (b'changeset-only', b'compatibility')
120 changesetsource = (b'changeset-only', b'compatibility')
121 return readfrom in changesetsource
121 return readfrom in changesetsource
122
122
123
123
124 def _committedforwardcopies(a, b, base, match):
124 def _committedforwardcopies(a, b, base, match):
125 """Like _forwardcopies(), but b.rev() cannot be None (working copy)"""
125 """Like _forwardcopies(), but b.rev() cannot be None (working copy)"""
126 # files might have to be traced back to the fctx parent of the last
126 # files might have to be traced back to the fctx parent of the last
127 # one-side-only changeset, but not further back than that
127 # one-side-only changeset, but not further back than that
128 repo = a._repo
128 repo = a._repo
129
129
130 if usechangesetcentricalgo(repo):
130 if usechangesetcentricalgo(repo):
131 return _changesetforwardcopies(a, b, match)
131 return _changesetforwardcopies(a, b, match)
132
132
133 debug = repo.ui.debugflag and repo.ui.configbool(b'devel', b'debug.copies')
133 debug = repo.ui.debugflag and repo.ui.configbool(b'devel', b'debug.copies')
134 dbg = repo.ui.debug
134 dbg = repo.ui.debug
135 if debug:
135 if debug:
136 dbg(b'debug.copies: looking into rename from %s to %s\n' % (a, b))
136 dbg(b'debug.copies: looking into rename from %s to %s\n' % (a, b))
137 am = a.manifest()
137 am = a.manifest()
138 basemf = None if base is None else base.manifest()
138 basemf = None if base is None else base.manifest()
139
139
140 # find where new files came from
140 # find where new files came from
141 # we currently don't try to find where old files went, too expensive
141 # we currently don't try to find where old files went, too expensive
142 # this means we can miss a case like 'hg rm b; hg cp a b'
142 # this means we can miss a case like 'hg rm b; hg cp a b'
143 cm = {}
143 cm = {}
144
144
145 # Computing the forward missing is quite expensive on large manifests, since
145 # Computing the forward missing is quite expensive on large manifests, since
146 # it compares the entire manifests. We can optimize it in the common use
146 # it compares the entire manifests. We can optimize it in the common use
147 # case of computing what copies are in a commit versus its parent (like
147 # case of computing what copies are in a commit versus its parent (like
148 # during a rebase or histedit). Note, we exclude merge commits from this
148 # during a rebase or histedit). Note, we exclude merge commits from this
149 # optimization, since the ctx.files() for a merge commit is not correct for
149 # optimization, since the ctx.files() for a merge commit is not correct for
150 # this comparison.
150 # this comparison.
151 forwardmissingmatch = match
151 forwardmissingmatch = match
152 if b.p1() == a and b.p2().node() == nullid:
152 if b.p1() == a and b.p2().node() == nullid:
153 filesmatcher = matchmod.exact(b.files())
153 filesmatcher = matchmod.exact(b.files())
154 forwardmissingmatch = matchmod.intersectmatchers(match, filesmatcher)
154 forwardmissingmatch = matchmod.intersectmatchers(match, filesmatcher)
155 if repo.ui.configbool(b'devel', b'copy-tracing.trace-all-files'):
155 if repo.ui.configbool(b'devel', b'copy-tracing.trace-all-files'):
156 missing = list(b.walk(match))
156 missing = list(b.walk(match))
157 # _computeforwardmissing(a, b, match=forwardmissingmatch)
157 # _computeforwardmissing(a, b, match=forwardmissingmatch)
158 if debug:
158 if debug:
159 dbg(b'debug.copies: searching all files: %d\n' % len(missing))
159 dbg(b'debug.copies: searching all files: %d\n' % len(missing))
160 else:
160 else:
161 missing = _computeforwardmissing(a, b, match=forwardmissingmatch)
161 missing = _computeforwardmissing(a, b, match=forwardmissingmatch)
162 if debug:
162 if debug:
163 dbg(
163 dbg(
164 b'debug.copies: missing files to search: %d\n'
164 b'debug.copies: missing files to search: %d\n'
165 % len(missing)
165 % len(missing)
166 )
166 )
167
167
168 ancestrycontext = a._repo.changelog.ancestors([b.rev()], inclusive=True)
168 ancestrycontext = a._repo.changelog.ancestors([b.rev()], inclusive=True)
169
169
170 for f in sorted(missing):
170 for f in sorted(missing):
171 if debug:
171 if debug:
172 dbg(b'debug.copies: tracing file: %s\n' % f)
172 dbg(b'debug.copies: tracing file: %s\n' % f)
173 fctx = b[f]
173 fctx = b[f]
174 fctx._ancestrycontext = ancestrycontext
174 fctx._ancestrycontext = ancestrycontext
175
175
176 if debug:
176 if debug:
177 start = util.timer()
177 start = util.timer()
178 opath = _tracefile(fctx, am, basemf)
178 opath = _tracefile(fctx, am, basemf)
179 if opath:
179 if opath:
180 if debug:
180 if debug:
181 dbg(b'debug.copies: rename of: %s\n' % opath)
181 dbg(b'debug.copies: rename of: %s\n' % opath)
182 cm[f] = opath
182 cm[f] = opath
183 if debug:
183 if debug:
184 dbg(
184 dbg(
185 b'debug.copies: time: %f seconds\n'
185 b'debug.copies: time: %f seconds\n'
186 % (util.timer() - start)
186 % (util.timer() - start)
187 )
187 )
188 return cm
188 return cm
189
189
190
190
191 def _revinfo_getter(repo, match):
191 def _revinfo_getter(repo, match):
192 """returns a function that returns the following data given a <rev>"
192 """returns a function that returns the following data given a <rev>"
193
193
194 * p1: revision number of first parent
194 * p1: revision number of first parent
195 * p2: revision number of first parent
195 * p2: revision number of first parent
196 * changes: a ChangingFiles object
196 * changes: a ChangingFiles object
197 """
197 """
198 cl = repo.changelog
198 cl = repo.changelog
199 parents = cl.parentrevs
199 parents = cl.parentrevs
200 flags = cl.flags
200 flags = cl.flags
201
201
202 HASCOPIESINFO = flagutil.REVIDX_HASCOPIESINFO
202 HASCOPIESINFO = flagutil.REVIDX_HASCOPIESINFO
203
203
204 changelogrevision = cl.changelogrevision
204 changelogrevision = cl.changelogrevision
205
205
206 if rustmod is not None:
206 if rustmod is not None:
207
207
208 def revinfo(rev):
208 def revinfo(rev):
209 p1, p2 = parents(rev)
209 p1, p2 = parents(rev)
210 if flags(rev) & HASCOPIESINFO:
210 if flags(rev) & HASCOPIESINFO:
211 raw = changelogrevision(rev)._sidedata.get(sidedatamod.SD_FILES)
211 raw = changelogrevision(rev)._sidedata.get(sidedatamod.SD_FILES)
212 else:
212 else:
213 raw = None
213 raw = None
214 return (p1, p2, raw)
214 return (p1, p2, raw)
215
215
216 else:
216 else:
217
217
218 def revinfo(rev):
218 def revinfo(rev):
219 p1, p2 = parents(rev)
219 p1, p2 = parents(rev)
220 if flags(rev) & HASCOPIESINFO:
220 if flags(rev) & HASCOPIESINFO:
221 changes = changelogrevision(rev).changes
221 changes = changelogrevision(rev).changes
222 else:
222 else:
223 changes = None
223 changes = None
224 return (p1, p2, changes)
224 return (p1, p2, changes)
225
225
226 return revinfo
226 return revinfo
227
227
228
228
229 def cached_is_ancestor(is_ancestor):
229 def cached_is_ancestor(is_ancestor):
230 """return a cached version of is_ancestor"""
230 """return a cached version of is_ancestor"""
231 cache = {}
231 cache = {}
232
232
233 def _is_ancestor(anc, desc):
233 def _is_ancestor(anc, desc):
234 if anc > desc:
234 if anc > desc:
235 return False
235 return False
236 elif anc == desc:
236 elif anc == desc:
237 return True
237 return True
238 key = (anc, desc)
238 key = (anc, desc)
239 ret = cache.get(key)
239 ret = cache.get(key)
240 if ret is None:
240 if ret is None:
241 ret = cache[key] = is_ancestor(anc, desc)
241 ret = cache[key] = is_ancestor(anc, desc)
242 return ret
242 return ret
243
243
244 return _is_ancestor
244 return _is_ancestor
245
245
246
246
247 def _changesetforwardcopies(a, b, match):
247 def _changesetforwardcopies(a, b, match):
248 if a.rev() in (nullrev, b.rev()):
248 if a.rev() in (nullrev, b.rev()):
249 return {}
249 return {}
250
250
251 repo = a.repo().unfiltered()
251 repo = a.repo().unfiltered()
252 children = {}
252 children = {}
253
253
254 cl = repo.changelog
254 cl = repo.changelog
255 isancestor = cl.isancestorrev
255 isancestor = cl.isancestorrev
256
256
257 # To track rename from "A" to B, we need to gather all parent β†’ children
257 # To track rename from "A" to B, we need to gather all parent β†’ children
258 # edges that are contains in `::B` but not in `::A`.
258 # edges that are contains in `::B` but not in `::A`.
259 #
259 #
260 #
260 #
261 # To do so, we need to gather all revisions exclusiveΒΉ to "B" (ieΒΉ: `::b -
261 # To do so, we need to gather all revisions exclusiveΒΉ to "B" (ieΒΉ: `::b -
262 # ::a`) and also all the "roots point", ie the parents of the exclusive set
262 # ::a`) and also all the "roots point", ie the parents of the exclusive set
263 # that belong to ::a. These are exactly all the revisions needed to express
263 # that belong to ::a. These are exactly all the revisions needed to express
264 # the parent β†’ children we need to combine.
264 # the parent β†’ children we need to combine.
265 #
265 #
266 # [1] actually, we need to gather all the edges within `(::a)::b`, ie:
266 # [1] actually, we need to gather all the edges within `(::a)::b`, ie:
267 # excluding paths that leads to roots that are not ancestors of `a`. We
267 # excluding paths that leads to roots that are not ancestors of `a`. We
268 # keep this out of the explanation because it is hard enough without this special case..
268 # keep this out of the explanation because it is hard enough without this special case..
269
269
270 parents = cl._uncheckedparentrevs
270 parents = cl._uncheckedparentrevs
271 graph_roots = (nullrev, nullrev)
271 graph_roots = (nullrev, nullrev)
272
272
273 ancestors = cl.ancestors([a.rev()], inclusive=True)
273 ancestors = cl.ancestors([a.rev()], inclusive=True)
274 revs = cl.findmissingrevs(common=[a.rev()], heads=[b.rev()])
274 revs = cl.findmissingrevs(common=[a.rev()], heads=[b.rev()])
275 roots = set()
275 roots = set()
276 has_graph_roots = False
276 has_graph_roots = False
277
277
278 # iterate over `only(B, A)`
278 # iterate over `only(B, A)`
279 for r in revs:
279 for r in revs:
280 ps = parents(r)
280 ps = parents(r)
281 if ps == graph_roots:
281 if ps == graph_roots:
282 has_graph_roots = True
282 has_graph_roots = True
283 else:
283 else:
284 p1, p2 = ps
284 p1, p2 = ps
285
285
286 # find all the "root points" (see larger comment above)
286 # find all the "root points" (see larger comment above)
287 if p1 != nullrev and p1 in ancestors:
287 if p1 != nullrev and p1 in ancestors:
288 roots.add(p1)
288 roots.add(p1)
289 if p2 != nullrev and p2 in ancestors:
289 if p2 != nullrev and p2 in ancestors:
290 roots.add(p2)
290 roots.add(p2)
291 if not roots:
291 if not roots:
292 # no common revision to track copies from
292 # no common revision to track copies from
293 return {}
293 return {}
294 if has_graph_roots:
294 if has_graph_roots:
295 # this deal with the special case mentionned in the [1] footnotes. We
295 # this deal with the special case mentionned in the [1] footnotes. We
296 # must filter out revisions that leads to non-common graphroots.
296 # must filter out revisions that leads to non-common graphroots.
297 roots = list(roots)
297 roots = list(roots)
298 m = min(roots)
298 m = min(roots)
299 h = [b.rev()]
299 h = [b.rev()]
300 roots_to_head = cl.reachableroots(m, h, roots, includepath=True)
300 roots_to_head = cl.reachableroots(m, h, roots, includepath=True)
301 roots_to_head = set(roots_to_head)
301 roots_to_head = set(roots_to_head)
302 revs = [r for r in revs if r in roots_to_head]
302 revs = [r for r in revs if r in roots_to_head]
303
303
304 if repo.filecopiesmode == b'changeset-sidedata':
304 if repo.filecopiesmode == b'changeset-sidedata':
305 # When using side-data, we will process the edges "from" the children.
305 # When using side-data, we will process the edges "from" the children.
306 # We iterate over the childre, gathering previous collected data for
306 # We iterate over the childre, gathering previous collected data for
307 # the parents. Do know when the parents data is no longer necessary, we
307 # the parents. Do know when the parents data is no longer necessary, we
308 # keep a counter of how many children each revision has.
308 # keep a counter of how many children each revision has.
309 #
309 #
310 # An interresting property of `children_count` is that it only contains
310 # An interresting property of `children_count` is that it only contains
311 # revision that will be relevant for a edge of the graph. So if a
311 # revision that will be relevant for a edge of the graph. So if a
312 # children has parent not in `children_count`, that edges should not be
312 # children has parent not in `children_count`, that edges should not be
313 # processed.
313 # processed.
314 children_count = dict((r, 0) for r in roots)
314 children_count = dict((r, 0) for r in roots)
315 for r in revs:
315 for r in revs:
316 for p in cl.parentrevs(r):
316 for p in cl.parentrevs(r):
317 if p == nullrev:
317 if p == nullrev:
318 continue
318 continue
319 children_count[r] = 0
319 children_count[r] = 0
320 if p in children_count:
320 if p in children_count:
321 children_count[p] += 1
321 children_count[p] += 1
322 revinfo = _revinfo_getter(repo, match)
322 revinfo = _revinfo_getter(repo, match)
323 return _combine_changeset_copies(
323 return _combine_changeset_copies(
324 revs, children_count, b.rev(), revinfo, match, isancestor
324 revs, children_count, b.rev(), revinfo, match, isancestor
325 )
325 )
326 else:
326 else:
327 # When not using side-data, we will process the edges "from" the parent.
327 # When not using side-data, we will process the edges "from" the parent.
328 # so we need a full mapping of the parent -> children relation.
328 # so we need a full mapping of the parent -> children relation.
329 children = dict((r, []) for r in roots)
329 children = dict((r, []) for r in roots)
330 for r in revs:
330 for r in revs:
331 for p in cl.parentrevs(r):
331 for p in cl.parentrevs(r):
332 if p == nullrev:
332 if p == nullrev:
333 continue
333 continue
334 children[r] = []
334 children[r] = []
335 if p in children:
335 if p in children:
336 children[p].append(r)
336 children[p].append(r)
337 x = revs.pop()
337 x = revs.pop()
338 assert x == b.rev()
338 assert x == b.rev()
339 revs.extend(roots)
339 revs.extend(roots)
340 revs.sort()
340 revs.sort()
341
341
342 revinfo = _revinfo_getter_extra(repo)
342 revinfo = _revinfo_getter_extra(repo)
343 return _combine_changeset_copies_extra(
343 return _combine_changeset_copies_extra(
344 revs, children, b.rev(), revinfo, match, isancestor
344 revs, children, b.rev(), revinfo, match, isancestor
345 )
345 )
346
346
347
347
348 def _combine_changeset_copies(
348 def _combine_changeset_copies(
349 revs, children_count, targetrev, revinfo, match, isancestor
349 revs, children_count, targetrev, revinfo, match, isancestor
350 ):
350 ):
351 """combine the copies information for each item of iterrevs
351 """combine the copies information for each item of iterrevs
352
352
353 revs: sorted iterable of revision to visit
353 revs: sorted iterable of revision to visit
354 children_count: a {parent: <number-of-relevant-children>} mapping.
354 children_count: a {parent: <number-of-relevant-children>} mapping.
355 targetrev: the final copies destination revision (not in iterrevs)
355 targetrev: the final copies destination revision (not in iterrevs)
356 revinfo(rev): a function that return (p1, p2, p1copies, p2copies, removed)
356 revinfo(rev): a function that return (p1, p2, p1copies, p2copies, removed)
357 match: a matcher
357 match: a matcher
358
358
359 It returns the aggregated copies information for `targetrev`.
359 It returns the aggregated copies information for `targetrev`.
360 """
360 """
361
361
362 alwaysmatch = match.always()
362 alwaysmatch = match.always()
363
363
364 if rustmod is not None:
364 if rustmod is not None:
365 final_copies = rustmod.combine_changeset_copies(
365 final_copies = rustmod.combine_changeset_copies(
366 list(revs), children_count, targetrev, revinfo, isancestor
366 list(revs), children_count, targetrev, revinfo, isancestor
367 )
367 )
368 else:
368 else:
369 isancestor = cached_is_ancestor(isancestor)
369 isancestor = cached_is_ancestor(isancestor)
370
370
371 all_copies = {}
371 all_copies = {}
372 # iterate over all the "children" side of copy tracing "edge"
372 # iterate over all the "children" side of copy tracing "edge"
373 for current_rev in revs:
373 for current_rev in revs:
374 p1, p2, changes = revinfo(current_rev)
374 p1, p2, changes = revinfo(current_rev)
375 current_copies = None
375 current_copies = None
376 # iterate over all parents to chain the existing data with the
376 # iterate over all parents to chain the existing data with the
377 # data from the parent β†’ child edge.
377 # data from the parent β†’ child edge.
378 for parent, parent_rev in ((1, p1), (2, p2)):
378 for parent, parent_rev in ((1, p1), (2, p2)):
379 if parent_rev == nullrev:
379 if parent_rev == nullrev:
380 continue
380 continue
381 remaining_children = children_count.get(parent_rev)
381 remaining_children = children_count.get(parent_rev)
382 if remaining_children is None:
382 if remaining_children is None:
383 continue
383 continue
384 remaining_children -= 1
384 remaining_children -= 1
385 children_count[parent_rev] = remaining_children
385 children_count[parent_rev] = remaining_children
386 if remaining_children:
386 if remaining_children:
387 copies = all_copies.get(parent_rev, None)
387 copies = all_copies.get(parent_rev, None)
388 else:
388 else:
389 copies = all_copies.pop(parent_rev, None)
389 copies = all_copies.pop(parent_rev, None)
390
390
391 if copies is None:
391 if copies is None:
392 # this is a root
392 # this is a root
393 newcopies = copies = {}
393 newcopies = copies = {}
394 elif remaining_children:
394 elif remaining_children:
395 newcopies = copies.copy()
395 newcopies = copies.copy()
396 else:
396 else:
397 newcopies = copies
397 newcopies = copies
398 # chain the data in the edge with the existing data
398 # chain the data in the edge with the existing data
399 if changes is not None:
399 if changes is not None:
400 childcopies = {}
400 childcopies = {}
401 if parent == 1:
401 if parent == 1:
402 childcopies = changes.copied_from_p1
402 childcopies = changes.copied_from_p1
403 elif parent == 2:
403 elif parent == 2:
404 childcopies = changes.copied_from_p2
404 childcopies = changes.copied_from_p2
405
405
406 if childcopies:
406 if childcopies:
407 newcopies = copies.copy()
407 newcopies = copies.copy()
408 for dest, source in pycompat.iteritems(childcopies):
408 for dest, source in pycompat.iteritems(childcopies):
409 prev = copies.get(source)
409 prev = copies.get(source)
410 if prev is not None and prev[1] is not None:
410 if prev is not None and prev[1] is not None:
411 source = prev[1]
411 source = prev[1]
412 newcopies[dest] = (current_rev, source)
412 newcopies[dest] = (current_rev, source)
413 assert newcopies is not copies
413 assert newcopies is not copies
414 if changes.removed:
414 if changes.removed:
415 for f in changes.removed:
415 for f in changes.removed:
416 if f in newcopies:
416 if f in newcopies:
417 if newcopies is copies:
417 if newcopies is copies:
418 # copy on write to avoid affecting potential other
418 # copy on write to avoid affecting potential other
419 # branches. when there are no other branches, this
419 # branches. when there are no other branches, this
420 # could be avoided.
420 # could be avoided.
421 newcopies = copies.copy()
421 newcopies = copies.copy()
422 newcopies[f] = (current_rev, None)
422 newcopies[f] = (current_rev, None)
423 # check potential need to combine the data from another parent (for
423 # check potential need to combine the data from another parent (for
424 # that child). See comment below for details.
424 # that child). See comment below for details.
425 if current_copies is None:
425 if current_copies is None:
426 current_copies = newcopies
426 current_copies = newcopies
427 else:
427 else:
428 # we are the second parent to work on c, we need to merge our
428 # we are the second parent to work on c, we need to merge our
429 # work with the other.
429 # work with the other.
430 #
430 #
431 # In case of conflict, parent 1 take precedence over parent 2.
431 # In case of conflict, parent 1 take precedence over parent 2.
432 # This is an arbitrary choice made anew when implementing
432 # This is an arbitrary choice made anew when implementing
433 # changeset based copies. It was made without regards with
433 # changeset based copies. It was made without regards with
434 # potential filelog related behavior.
434 # potential filelog related behavior.
435 assert parent == 2
435 assert parent == 2
436 current_copies = _merge_copies_dict(
436 current_copies = _merge_copies_dict(
437 newcopies, current_copies, isancestor, changes
437 newcopies,
438 current_copies,
439 isancestor,
440 changes,
441 current_rev,
438 )
442 )
439 all_copies[current_rev] = current_copies
443 all_copies[current_rev] = current_copies
440
444
441 # filter out internal details and return a {dest: source mapping}
445 # filter out internal details and return a {dest: source mapping}
442 final_copies = {}
446 final_copies = {}
443 for dest, (tt, source) in all_copies[targetrev].items():
447 for dest, (tt, source) in all_copies[targetrev].items():
444 if source is not None:
448 if source is not None:
445 final_copies[dest] = source
449 final_copies[dest] = source
446 if not alwaysmatch:
450 if not alwaysmatch:
447 for filename in list(final_copies.keys()):
451 for filename in list(final_copies.keys()):
448 if not match(filename):
452 if not match(filename):
449 del final_copies[filename]
453 del final_copies[filename]
450 return final_copies
454 return final_copies
451
455
452
456
453 # constant to decide which side to pick with _merge_copies_dict
457 # constant to decide which side to pick with _merge_copies_dict
454 PICK_MINOR = 0
458 PICK_MINOR = 0
455 PICK_MAJOR = 1
459 PICK_MAJOR = 1
456 PICK_EITHER = 2
460 PICK_EITHER = 2
457
461
458
462
459 def _merge_copies_dict(minor, major, isancestor, changes):
463 def _merge_copies_dict(minor, major, isancestor, changes, current_merge):
460 """merge two copies-mapping together, minor and major
464 """merge two copies-mapping together, minor and major
461
465
462 In case of conflict, value from "major" will be picked.
466 In case of conflict, value from "major" will be picked.
463
467
464 - `isancestors(low_rev, high_rev)`: callable return True if `low_rev` is an
468 - `isancestors(low_rev, high_rev)`: callable return True if `low_rev` is an
465 ancestors of `high_rev`,
469 ancestors of `high_rev`,
466
470
467 - `ismerged(path)`: callable return True if `path` have been merged in the
471 - `ismerged(path)`: callable return True if `path` have been merged in the
468 current revision,
472 current revision,
469
473
470 return the resulting dict (in practice, the "minor" object, updated)
474 return the resulting dict (in practice, the "minor" object, updated)
471 """
475 """
472 for dest, value in major.items():
476 for dest, value in major.items():
473 other = minor.get(dest)
477 other = minor.get(dest)
474 if other is None:
478 if other is None:
475 minor[dest] = value
479 minor[dest] = value
476 else:
480 else:
477 pick = _compare_values(changes, isancestor, dest, other, value)
481 pick, overwrite = _compare_values(
478 if pick == PICK_MAJOR:
482 changes, isancestor, dest, other, value
483 )
484 if overwrite:
485 if pick == PICK_MAJOR:
486 minor[dest] = (current_merge, value[1])
487 else:
488 minor[dest] = (current_merge, other[1])
489 elif pick == PICK_MAJOR:
479 minor[dest] = value
490 minor[dest] = value
480 return minor
491 return minor
481
492
482
493
483 def _compare_values(changes, isancestor, dest, minor, major):
494 def _compare_values(changes, isancestor, dest, minor, major):
484 """compare two value within a _merge_copies_dict loop iteration
495 """compare two value within a _merge_copies_dict loop iteration
485
496
486 return pick
497 return (pick, overwrite).
487
498
488 - pick is one of PICK_MINOR, PICK_MAJOR or PICK_EITHER
499 - pick is one of PICK_MINOR, PICK_MAJOR or PICK_EITHER
500 - overwrite is True if pick is a return of an ambiguity that needs resolution.
489 """
501 """
490 major_tt, major_value = major
502 major_tt, major_value = major
491 minor_tt, minor_value = minor
503 minor_tt, minor_value = minor
492
504
493 if major_tt == minor_tt:
505 if major_tt == minor_tt:
494 # if it comes from the same revision it must be the same value
506 # if it comes from the same revision it must be the same value
495 assert major_value == minor_value
507 assert major_value == minor_value
496 return PICK_EITHER
508 return PICK_EITHER, False
497 elif (
509 elif (
498 changes is not None
510 changes is not None
499 and minor_value is not None
511 and minor_value is not None
500 and major_value is None
512 and major_value is None
501 and dest in changes.salvaged
513 and dest in changes.salvaged
502 ):
514 ):
503 # In this case, a deletion was reverted, the "alive" value overwrite
515 # In this case, a deletion was reverted, the "alive" value overwrite
504 # the deleted one.
516 # the deleted one.
505 return PICK_MINOR
517 return PICK_MINOR, True
506 elif (
518 elif (
507 changes is not None
519 changes is not None
508 and major_value is not None
520 and major_value is not None
509 and minor_value is None
521 and minor_value is None
510 and dest in changes.salvaged
522 and dest in changes.salvaged
511 ):
523 ):
512 # In this case, a deletion was reverted, the "alive" value overwrite
524 # In this case, a deletion was reverted, the "alive" value overwrite
513 # the deleted one.
525 # the deleted one.
514 return PICK_MAJOR
526 return PICK_MAJOR, True
515 elif isancestor(minor_tt, major_tt):
527 elif isancestor(minor_tt, major_tt):
516 if changes is not None and dest in changes.merged:
528 if changes is not None and dest in changes.merged:
517 # change to dest happened on the branch without copy-source change,
529 # change to dest happened on the branch without copy-source change,
518 # so both source are valid and "major" wins.
530 # so both source are valid and "major" wins.
519 return PICK_MAJOR
531 return PICK_MAJOR, True
520 else:
532 else:
521 return PICK_MAJOR
533 return PICK_MAJOR, False
522 elif isancestor(major_tt, minor_tt):
534 elif isancestor(major_tt, minor_tt):
523 if changes is not None and dest in changes.merged:
535 if changes is not None and dest in changes.merged:
524 # change to dest happened on the branch without copy-source change,
536 # change to dest happened on the branch without copy-source change,
525 # so both source are valid and "major" wins.
537 # so both source are valid and "major" wins.
526 return PICK_MAJOR
538 return PICK_MAJOR, True
527 else:
539 else:
528 return PICK_MINOR
540 return PICK_MINOR, False
529 elif minor_value is None:
541 elif minor_value is None:
530 # in case of conflict, the "alive" side wins.
542 # in case of conflict, the "alive" side wins.
531 return PICK_MAJOR
543 return PICK_MAJOR, True
532 elif major_value is None:
544 elif major_value is None:
533 # in case of conflict, the "alive" side wins.
545 # in case of conflict, the "alive" side wins.
534 return PICK_MINOR
546 return PICK_MINOR, True
535 else:
547 else:
536 # in case of conflict where both side are alive, major wins.
548 # in case of conflict where both side are alive, major wins.
537 return PICK_MAJOR
549 return PICK_MAJOR, True
538
550
539
551
540 def _revinfo_getter_extra(repo):
552 def _revinfo_getter_extra(repo):
541 """return a function that return multiple data given a <rev>"i
553 """return a function that return multiple data given a <rev>"i
542
554
543 * p1: revision number of first parent
555 * p1: revision number of first parent
544 * p2: revision number of first parent
556 * p2: revision number of first parent
545 * p1copies: mapping of copies from p1
557 * p1copies: mapping of copies from p1
546 * p2copies: mapping of copies from p2
558 * p2copies: mapping of copies from p2
547 * removed: a list of removed files
559 * removed: a list of removed files
548 * ismerged: a callback to know if file was merged in that revision
560 * ismerged: a callback to know if file was merged in that revision
549 """
561 """
550 cl = repo.changelog
562 cl = repo.changelog
551 parents = cl.parentrevs
563 parents = cl.parentrevs
552
564
553 def get_ismerged(rev):
565 def get_ismerged(rev):
554 ctx = repo[rev]
566 ctx = repo[rev]
555
567
556 def ismerged(path):
568 def ismerged(path):
557 if path not in ctx.files():
569 if path not in ctx.files():
558 return False
570 return False
559 fctx = ctx[path]
571 fctx = ctx[path]
560 parents = fctx._filelog.parents(fctx._filenode)
572 parents = fctx._filelog.parents(fctx._filenode)
561 nb_parents = 0
573 nb_parents = 0
562 for n in parents:
574 for n in parents:
563 if n != nullid:
575 if n != nullid:
564 nb_parents += 1
576 nb_parents += 1
565 return nb_parents >= 2
577 return nb_parents >= 2
566
578
567 return ismerged
579 return ismerged
568
580
569 def revinfo(rev):
581 def revinfo(rev):
570 p1, p2 = parents(rev)
582 p1, p2 = parents(rev)
571 ctx = repo[rev]
583 ctx = repo[rev]
572 p1copies, p2copies = ctx._copies
584 p1copies, p2copies = ctx._copies
573 removed = ctx.filesremoved()
585 removed = ctx.filesremoved()
574 return p1, p2, p1copies, p2copies, removed, get_ismerged(rev)
586 return p1, p2, p1copies, p2copies, removed, get_ismerged(rev)
575
587
576 return revinfo
588 return revinfo
577
589
578
590
579 def _combine_changeset_copies_extra(
591 def _combine_changeset_copies_extra(
580 revs, children, targetrev, revinfo, match, isancestor
592 revs, children, targetrev, revinfo, match, isancestor
581 ):
593 ):
582 """version of `_combine_changeset_copies` that works with the Google
594 """version of `_combine_changeset_copies` that works with the Google
583 specific "extra" based storage for copy information"""
595 specific "extra" based storage for copy information"""
584 all_copies = {}
596 all_copies = {}
585 alwaysmatch = match.always()
597 alwaysmatch = match.always()
586 for r in revs:
598 for r in revs:
587 copies = all_copies.pop(r, None)
599 copies = all_copies.pop(r, None)
588 if copies is None:
600 if copies is None:
589 # this is a root
601 # this is a root
590 copies = {}
602 copies = {}
591 for i, c in enumerate(children[r]):
603 for i, c in enumerate(children[r]):
592 p1, p2, p1copies, p2copies, removed, ismerged = revinfo(c)
604 p1, p2, p1copies, p2copies, removed, ismerged = revinfo(c)
593 if r == p1:
605 if r == p1:
594 parent = 1
606 parent = 1
595 childcopies = p1copies
607 childcopies = p1copies
596 else:
608 else:
597 assert r == p2
609 assert r == p2
598 parent = 2
610 parent = 2
599 childcopies = p2copies
611 childcopies = p2copies
600 if not alwaysmatch:
612 if not alwaysmatch:
601 childcopies = {
613 childcopies = {
602 dst: src for dst, src in childcopies.items() if match(dst)
614 dst: src for dst, src in childcopies.items() if match(dst)
603 }
615 }
604 newcopies = copies
616 newcopies = copies
605 if childcopies:
617 if childcopies:
606 newcopies = copies.copy()
618 newcopies = copies.copy()
607 for dest, source in pycompat.iteritems(childcopies):
619 for dest, source in pycompat.iteritems(childcopies):
608 prev = copies.get(source)
620 prev = copies.get(source)
609 if prev is not None and prev[1] is not None:
621 if prev is not None and prev[1] is not None:
610 source = prev[1]
622 source = prev[1]
611 newcopies[dest] = (c, source)
623 newcopies[dest] = (c, source)
612 assert newcopies is not copies
624 assert newcopies is not copies
613 for f in removed:
625 for f in removed:
614 if f in newcopies:
626 if f in newcopies:
615 if newcopies is copies:
627 if newcopies is copies:
616 # copy on write to avoid affecting potential other
628 # copy on write to avoid affecting potential other
617 # branches. when there are no other branches, this
629 # branches. when there are no other branches, this
618 # could be avoided.
630 # could be avoided.
619 newcopies = copies.copy()
631 newcopies = copies.copy()
620 newcopies[f] = (c, None)
632 newcopies[f] = (c, None)
621 othercopies = all_copies.get(c)
633 othercopies = all_copies.get(c)
622 if othercopies is None:
634 if othercopies is None:
623 all_copies[c] = newcopies
635 all_copies[c] = newcopies
624 else:
636 else:
625 # we are the second parent to work on c, we need to merge our
637 # we are the second parent to work on c, we need to merge our
626 # work with the other.
638 # work with the other.
627 #
639 #
628 # In case of conflict, parent 1 take precedence over parent 2.
640 # In case of conflict, parent 1 take precedence over parent 2.
629 # This is an arbitrary choice made anew when implementing
641 # This is an arbitrary choice made anew when implementing
630 # changeset based copies. It was made without regards with
642 # changeset based copies. It was made without regards with
631 # potential filelog related behavior.
643 # potential filelog related behavior.
632 if parent == 1:
644 if parent == 1:
633 _merge_copies_dict_extra(
645 _merge_copies_dict_extra(
634 othercopies, newcopies, isancestor, ismerged
646 othercopies, newcopies, isancestor, ismerged
635 )
647 )
636 else:
648 else:
637 _merge_copies_dict_extra(
649 _merge_copies_dict_extra(
638 newcopies, othercopies, isancestor, ismerged
650 newcopies, othercopies, isancestor, ismerged
639 )
651 )
640 all_copies[c] = newcopies
652 all_copies[c] = newcopies
641
653
642 final_copies = {}
654 final_copies = {}
643 for dest, (tt, source) in all_copies[targetrev].items():
655 for dest, (tt, source) in all_copies[targetrev].items():
644 if source is not None:
656 if source is not None:
645 final_copies[dest] = source
657 final_copies[dest] = source
646 return final_copies
658 return final_copies
647
659
648
660
649 def _merge_copies_dict_extra(minor, major, isancestor, ismerged):
661 def _merge_copies_dict_extra(minor, major, isancestor, ismerged):
650 """version of `_merge_copies_dict` that works with the Google
662 """version of `_merge_copies_dict` that works with the Google
651 specific "extra" based storage for copy information"""
663 specific "extra" based storage for copy information"""
652 for dest, value in major.items():
664 for dest, value in major.items():
653 other = minor.get(dest)
665 other = minor.get(dest)
654 if other is None:
666 if other is None:
655 minor[dest] = value
667 minor[dest] = value
656 else:
668 else:
657 new_tt = value[0]
669 new_tt = value[0]
658 other_tt = other[0]
670 other_tt = other[0]
659 if value[1] == other[1]:
671 if value[1] == other[1]:
660 continue
672 continue
661 # content from "major" wins, unless it is older
673 # content from "major" wins, unless it is older
662 # than the branch point or there is a merge
674 # than the branch point or there is a merge
663 if (
675 if (
664 new_tt == other_tt
676 new_tt == other_tt
665 or not isancestor(new_tt, other_tt)
677 or not isancestor(new_tt, other_tt)
666 or ismerged(dest)
678 or ismerged(dest)
667 ):
679 ):
668 minor[dest] = value
680 minor[dest] = value
669
681
670
682
671 def _forwardcopies(a, b, base=None, match=None):
683 def _forwardcopies(a, b, base=None, match=None):
672 """find {dst@b: src@a} copy mapping where a is an ancestor of b"""
684 """find {dst@b: src@a} copy mapping where a is an ancestor of b"""
673
685
674 if base is None:
686 if base is None:
675 base = a
687 base = a
676 match = a.repo().narrowmatch(match)
688 match = a.repo().narrowmatch(match)
677 # check for working copy
689 # check for working copy
678 if b.rev() is None:
690 if b.rev() is None:
679 cm = _committedforwardcopies(a, b.p1(), base, match)
691 cm = _committedforwardcopies(a, b.p1(), base, match)
680 # combine copies from dirstate if necessary
692 # combine copies from dirstate if necessary
681 copies = _chain(cm, _dirstatecopies(b._repo, match))
693 copies = _chain(cm, _dirstatecopies(b._repo, match))
682 else:
694 else:
683 copies = _committedforwardcopies(a, b, base, match)
695 copies = _committedforwardcopies(a, b, base, match)
684 return copies
696 return copies
685
697
686
698
687 def _backwardrenames(a, b, match):
699 def _backwardrenames(a, b, match):
688 if a._repo.ui.config(b'experimental', b'copytrace') == b'off':
700 if a._repo.ui.config(b'experimental', b'copytrace') == b'off':
689 return {}
701 return {}
690
702
691 # Even though we're not taking copies into account, 1:n rename situations
703 # Even though we're not taking copies into account, 1:n rename situations
692 # can still exist (e.g. hg cp a b; hg mv a c). In those cases we
704 # can still exist (e.g. hg cp a b; hg mv a c). In those cases we
693 # arbitrarily pick one of the renames.
705 # arbitrarily pick one of the renames.
694 # We don't want to pass in "match" here, since that would filter
706 # We don't want to pass in "match" here, since that would filter
695 # the destination by it. Since we're reversing the copies, we want
707 # the destination by it. Since we're reversing the copies, we want
696 # to filter the source instead.
708 # to filter the source instead.
697 f = _forwardcopies(b, a)
709 f = _forwardcopies(b, a)
698 r = {}
710 r = {}
699 for k, v in sorted(pycompat.iteritems(f)):
711 for k, v in sorted(pycompat.iteritems(f)):
700 if match and not match(v):
712 if match and not match(v):
701 continue
713 continue
702 # remove copies
714 # remove copies
703 if v in a:
715 if v in a:
704 continue
716 continue
705 r[v] = k
717 r[v] = k
706 return r
718 return r
707
719
708
720
709 def pathcopies(x, y, match=None):
721 def pathcopies(x, y, match=None):
710 """find {dst@y: src@x} copy mapping for directed compare"""
722 """find {dst@y: src@x} copy mapping for directed compare"""
711 repo = x._repo
723 repo = x._repo
712 debug = repo.ui.debugflag and repo.ui.configbool(b'devel', b'debug.copies')
724 debug = repo.ui.debugflag and repo.ui.configbool(b'devel', b'debug.copies')
713 if debug:
725 if debug:
714 repo.ui.debug(
726 repo.ui.debug(
715 b'debug.copies: searching copies from %s to %s\n' % (x, y)
727 b'debug.copies: searching copies from %s to %s\n' % (x, y)
716 )
728 )
717 if x == y or not x or not y:
729 if x == y or not x or not y:
718 return {}
730 return {}
719 if y.rev() is None and x == y.p1():
731 if y.rev() is None and x == y.p1():
720 if debug:
732 if debug:
721 repo.ui.debug(b'debug.copies: search mode: dirstate\n')
733 repo.ui.debug(b'debug.copies: search mode: dirstate\n')
722 # short-circuit to avoid issues with merge states
734 # short-circuit to avoid issues with merge states
723 return _dirstatecopies(repo, match)
735 return _dirstatecopies(repo, match)
724 a = y.ancestor(x)
736 a = y.ancestor(x)
725 if a == x:
737 if a == x:
726 if debug:
738 if debug:
727 repo.ui.debug(b'debug.copies: search mode: forward\n')
739 repo.ui.debug(b'debug.copies: search mode: forward\n')
728 copies = _forwardcopies(x, y, match=match)
740 copies = _forwardcopies(x, y, match=match)
729 elif a == y:
741 elif a == y:
730 if debug:
742 if debug:
731 repo.ui.debug(b'debug.copies: search mode: backward\n')
743 repo.ui.debug(b'debug.copies: search mode: backward\n')
732 copies = _backwardrenames(x, y, match=match)
744 copies = _backwardrenames(x, y, match=match)
733 else:
745 else:
734 if debug:
746 if debug:
735 repo.ui.debug(b'debug.copies: search mode: combined\n')
747 repo.ui.debug(b'debug.copies: search mode: combined\n')
736 base = None
748 base = None
737 if a.rev() != nullrev:
749 if a.rev() != nullrev:
738 base = x
750 base = x
739 copies = _chain(
751 copies = _chain(
740 _backwardrenames(x, a, match=match),
752 _backwardrenames(x, a, match=match),
741 _forwardcopies(a, y, base, match=match),
753 _forwardcopies(a, y, base, match=match),
742 )
754 )
743 _filter(x, y, copies)
755 _filter(x, y, copies)
744 return copies
756 return copies
745
757
746
758
747 def mergecopies(repo, c1, c2, base):
759 def mergecopies(repo, c1, c2, base):
748 """
760 """
749 Finds moves and copies between context c1 and c2 that are relevant for
761 Finds moves and copies between context c1 and c2 that are relevant for
750 merging. 'base' will be used as the merge base.
762 merging. 'base' will be used as the merge base.
751
763
752 Copytracing is used in commands like rebase, merge, unshelve, etc to merge
764 Copytracing is used in commands like rebase, merge, unshelve, etc to merge
753 files that were moved/ copied in one merge parent and modified in another.
765 files that were moved/ copied in one merge parent and modified in another.
754 For example:
766 For example:
755
767
756 o ---> 4 another commit
768 o ---> 4 another commit
757 |
769 |
758 | o ---> 3 commit that modifies a.txt
770 | o ---> 3 commit that modifies a.txt
759 | /
771 | /
760 o / ---> 2 commit that moves a.txt to b.txt
772 o / ---> 2 commit that moves a.txt to b.txt
761 |/
773 |/
762 o ---> 1 merge base
774 o ---> 1 merge base
763
775
764 If we try to rebase revision 3 on revision 4, since there is no a.txt in
776 If we try to rebase revision 3 on revision 4, since there is no a.txt in
765 revision 4, and if user have copytrace disabled, we prints the following
777 revision 4, and if user have copytrace disabled, we prints the following
766 message:
778 message:
767
779
768 ```other changed <file> which local deleted```
780 ```other changed <file> which local deleted```
769
781
770 Returns a tuple where:
782 Returns a tuple where:
771
783
772 "branch_copies" an instance of branch_copies.
784 "branch_copies" an instance of branch_copies.
773
785
774 "diverge" is a mapping of source name -> list of destination names
786 "diverge" is a mapping of source name -> list of destination names
775 for divergent renames.
787 for divergent renames.
776
788
777 This function calls different copytracing algorithms based on config.
789 This function calls different copytracing algorithms based on config.
778 """
790 """
779 # avoid silly behavior for update from empty dir
791 # avoid silly behavior for update from empty dir
780 if not c1 or not c2 or c1 == c2:
792 if not c1 or not c2 or c1 == c2:
781 return branch_copies(), branch_copies(), {}
793 return branch_copies(), branch_copies(), {}
782
794
783 narrowmatch = c1.repo().narrowmatch()
795 narrowmatch = c1.repo().narrowmatch()
784
796
785 # avoid silly behavior for parent -> working dir
797 # avoid silly behavior for parent -> working dir
786 if c2.node() is None and c1.node() == repo.dirstate.p1():
798 if c2.node() is None and c1.node() == repo.dirstate.p1():
787 return (
799 return (
788 branch_copies(_dirstatecopies(repo, narrowmatch)),
800 branch_copies(_dirstatecopies(repo, narrowmatch)),
789 branch_copies(),
801 branch_copies(),
790 {},
802 {},
791 )
803 )
792
804
793 copytracing = repo.ui.config(b'experimental', b'copytrace')
805 copytracing = repo.ui.config(b'experimental', b'copytrace')
794 if stringutil.parsebool(copytracing) is False:
806 if stringutil.parsebool(copytracing) is False:
795 # stringutil.parsebool() returns None when it is unable to parse the
807 # stringutil.parsebool() returns None when it is unable to parse the
796 # value, so we should rely on making sure copytracing is on such cases
808 # value, so we should rely on making sure copytracing is on such cases
797 return branch_copies(), branch_copies(), {}
809 return branch_copies(), branch_copies(), {}
798
810
799 if usechangesetcentricalgo(repo):
811 if usechangesetcentricalgo(repo):
800 # The heuristics don't make sense when we need changeset-centric algos
812 # The heuristics don't make sense when we need changeset-centric algos
801 return _fullcopytracing(repo, c1, c2, base)
813 return _fullcopytracing(repo, c1, c2, base)
802
814
803 # Copy trace disabling is explicitly below the node == p1 logic above
815 # Copy trace disabling is explicitly below the node == p1 logic above
804 # because the logic above is required for a simple copy to be kept across a
816 # because the logic above is required for a simple copy to be kept across a
805 # rebase.
817 # rebase.
806 if copytracing == b'heuristics':
818 if copytracing == b'heuristics':
807 # Do full copytracing if only non-public revisions are involved as
819 # Do full copytracing if only non-public revisions are involved as
808 # that will be fast enough and will also cover the copies which could
820 # that will be fast enough and will also cover the copies which could
809 # be missed by heuristics
821 # be missed by heuristics
810 if _isfullcopytraceable(repo, c1, base):
822 if _isfullcopytraceable(repo, c1, base):
811 return _fullcopytracing(repo, c1, c2, base)
823 return _fullcopytracing(repo, c1, c2, base)
812 return _heuristicscopytracing(repo, c1, c2, base)
824 return _heuristicscopytracing(repo, c1, c2, base)
813 else:
825 else:
814 return _fullcopytracing(repo, c1, c2, base)
826 return _fullcopytracing(repo, c1, c2, base)
815
827
816
828
817 def _isfullcopytraceable(repo, c1, base):
829 def _isfullcopytraceable(repo, c1, base):
818 """Checks that if base, source and destination are all no-public branches,
830 """Checks that if base, source and destination are all no-public branches,
819 if yes let's use the full copytrace algorithm for increased capabilities
831 if yes let's use the full copytrace algorithm for increased capabilities
820 since it will be fast enough.
832 since it will be fast enough.
821
833
822 `experimental.copytrace.sourcecommitlimit` can be used to set a limit for
834 `experimental.copytrace.sourcecommitlimit` can be used to set a limit for
823 number of changesets from c1 to base such that if number of changesets are
835 number of changesets from c1 to base such that if number of changesets are
824 more than the limit, full copytracing algorithm won't be used.
836 more than the limit, full copytracing algorithm won't be used.
825 """
837 """
826 if c1.rev() is None:
838 if c1.rev() is None:
827 c1 = c1.p1()
839 c1 = c1.p1()
828 if c1.mutable() and base.mutable():
840 if c1.mutable() and base.mutable():
829 sourcecommitlimit = repo.ui.configint(
841 sourcecommitlimit = repo.ui.configint(
830 b'experimental', b'copytrace.sourcecommitlimit'
842 b'experimental', b'copytrace.sourcecommitlimit'
831 )
843 )
832 commits = len(repo.revs(b'%d::%d', base.rev(), c1.rev()))
844 commits = len(repo.revs(b'%d::%d', base.rev(), c1.rev()))
833 return commits < sourcecommitlimit
845 return commits < sourcecommitlimit
834 return False
846 return False
835
847
836
848
837 def _checksinglesidecopies(
849 def _checksinglesidecopies(
838 src, dsts1, m1, m2, mb, c2, base, copy, renamedelete
850 src, dsts1, m1, m2, mb, c2, base, copy, renamedelete
839 ):
851 ):
840 if src not in m2:
852 if src not in m2:
841 # deleted on side 2
853 # deleted on side 2
842 if src not in m1:
854 if src not in m1:
843 # renamed on side 1, deleted on side 2
855 # renamed on side 1, deleted on side 2
844 renamedelete[src] = dsts1
856 renamedelete[src] = dsts1
845 elif src not in mb:
857 elif src not in mb:
846 # Work around the "short-circuit to avoid issues with merge states"
858 # Work around the "short-circuit to avoid issues with merge states"
847 # thing in pathcopies(): pathcopies(x, y) can return a copy where the
859 # thing in pathcopies(): pathcopies(x, y) can return a copy where the
848 # destination doesn't exist in y.
860 # destination doesn't exist in y.
849 pass
861 pass
850 elif mb[src] != m2[src] and not _related(c2[src], base[src]):
862 elif mb[src] != m2[src] and not _related(c2[src], base[src]):
851 return
863 return
852 elif mb[src] != m2[src] or mb.flags(src) != m2.flags(src):
864 elif mb[src] != m2[src] or mb.flags(src) != m2.flags(src):
853 # modified on side 2
865 # modified on side 2
854 for dst in dsts1:
866 for dst in dsts1:
855 copy[dst] = src
867 copy[dst] = src
856
868
857
869
858 class branch_copies(object):
870 class branch_copies(object):
859 """Information about copies made on one side of a merge/graft.
871 """Information about copies made on one side of a merge/graft.
860
872
861 "copy" is a mapping from destination name -> source name,
873 "copy" is a mapping from destination name -> source name,
862 where source is in c1 and destination is in c2 or vice-versa.
874 where source is in c1 and destination is in c2 or vice-versa.
863
875
864 "movewithdir" is a mapping from source name -> destination name,
876 "movewithdir" is a mapping from source name -> destination name,
865 where the file at source present in one context but not the other
877 where the file at source present in one context but not the other
866 needs to be moved to destination by the merge process, because the
878 needs to be moved to destination by the merge process, because the
867 other context moved the directory it is in.
879 other context moved the directory it is in.
868
880
869 "renamedelete" is a mapping of source name -> list of destination
881 "renamedelete" is a mapping of source name -> list of destination
870 names for files deleted in c1 that were renamed in c2 or vice-versa.
882 names for files deleted in c1 that were renamed in c2 or vice-versa.
871
883
872 "dirmove" is a mapping of detected source dir -> destination dir renames.
884 "dirmove" is a mapping of detected source dir -> destination dir renames.
873 This is needed for handling changes to new files previously grafted into
885 This is needed for handling changes to new files previously grafted into
874 renamed directories.
886 renamed directories.
875 """
887 """
876
888
877 def __init__(
889 def __init__(
878 self, copy=None, renamedelete=None, dirmove=None, movewithdir=None
890 self, copy=None, renamedelete=None, dirmove=None, movewithdir=None
879 ):
891 ):
880 self.copy = {} if copy is None else copy
892 self.copy = {} if copy is None else copy
881 self.renamedelete = {} if renamedelete is None else renamedelete
893 self.renamedelete = {} if renamedelete is None else renamedelete
882 self.dirmove = {} if dirmove is None else dirmove
894 self.dirmove = {} if dirmove is None else dirmove
883 self.movewithdir = {} if movewithdir is None else movewithdir
895 self.movewithdir = {} if movewithdir is None else movewithdir
884
896
885 def __repr__(self):
897 def __repr__(self):
886 return '<branch_copies\n copy=%r\n renamedelete=%r\n dirmove=%r\n movewithdir=%r\n>' % (
898 return '<branch_copies\n copy=%r\n renamedelete=%r\n dirmove=%r\n movewithdir=%r\n>' % (
887 self.copy,
899 self.copy,
888 self.renamedelete,
900 self.renamedelete,
889 self.dirmove,
901 self.dirmove,
890 self.movewithdir,
902 self.movewithdir,
891 )
903 )
892
904
893
905
894 def _fullcopytracing(repo, c1, c2, base):
906 def _fullcopytracing(repo, c1, c2, base):
895 """The full copytracing algorithm which finds all the new files that were
907 """The full copytracing algorithm which finds all the new files that were
896 added from merge base up to the top commit and for each file it checks if
908 added from merge base up to the top commit and for each file it checks if
897 this file was copied from another file.
909 this file was copied from another file.
898
910
899 This is pretty slow when a lot of changesets are involved but will track all
911 This is pretty slow when a lot of changesets are involved but will track all
900 the copies.
912 the copies.
901 """
913 """
902 m1 = c1.manifest()
914 m1 = c1.manifest()
903 m2 = c2.manifest()
915 m2 = c2.manifest()
904 mb = base.manifest()
916 mb = base.manifest()
905
917
906 copies1 = pathcopies(base, c1)
918 copies1 = pathcopies(base, c1)
907 copies2 = pathcopies(base, c2)
919 copies2 = pathcopies(base, c2)
908
920
909 if not (copies1 or copies2):
921 if not (copies1 or copies2):
910 return branch_copies(), branch_copies(), {}
922 return branch_copies(), branch_copies(), {}
911
923
912 inversecopies1 = {}
924 inversecopies1 = {}
913 inversecopies2 = {}
925 inversecopies2 = {}
914 for dst, src in copies1.items():
926 for dst, src in copies1.items():
915 inversecopies1.setdefault(src, []).append(dst)
927 inversecopies1.setdefault(src, []).append(dst)
916 for dst, src in copies2.items():
928 for dst, src in copies2.items():
917 inversecopies2.setdefault(src, []).append(dst)
929 inversecopies2.setdefault(src, []).append(dst)
918
930
919 copy1 = {}
931 copy1 = {}
920 copy2 = {}
932 copy2 = {}
921 diverge = {}
933 diverge = {}
922 renamedelete1 = {}
934 renamedelete1 = {}
923 renamedelete2 = {}
935 renamedelete2 = {}
924 allsources = set(inversecopies1) | set(inversecopies2)
936 allsources = set(inversecopies1) | set(inversecopies2)
925 for src in allsources:
937 for src in allsources:
926 dsts1 = inversecopies1.get(src)
938 dsts1 = inversecopies1.get(src)
927 dsts2 = inversecopies2.get(src)
939 dsts2 = inversecopies2.get(src)
928 if dsts1 and dsts2:
940 if dsts1 and dsts2:
929 # copied/renamed on both sides
941 # copied/renamed on both sides
930 if src not in m1 and src not in m2:
942 if src not in m1 and src not in m2:
931 # renamed on both sides
943 # renamed on both sides
932 dsts1 = set(dsts1)
944 dsts1 = set(dsts1)
933 dsts2 = set(dsts2)
945 dsts2 = set(dsts2)
934 # If there's some overlap in the rename destinations, we
946 # If there's some overlap in the rename destinations, we
935 # consider it not divergent. For example, if side 1 copies 'a'
947 # consider it not divergent. For example, if side 1 copies 'a'
936 # to 'b' and 'c' and deletes 'a', and side 2 copies 'a' to 'c'
948 # to 'b' and 'c' and deletes 'a', and side 2 copies 'a' to 'c'
937 # and 'd' and deletes 'a'.
949 # and 'd' and deletes 'a'.
938 if dsts1 & dsts2:
950 if dsts1 & dsts2:
939 for dst in dsts1 & dsts2:
951 for dst in dsts1 & dsts2:
940 copy1[dst] = src
952 copy1[dst] = src
941 copy2[dst] = src
953 copy2[dst] = src
942 else:
954 else:
943 diverge[src] = sorted(dsts1 | dsts2)
955 diverge[src] = sorted(dsts1 | dsts2)
944 elif src in m1 and src in m2:
956 elif src in m1 and src in m2:
945 # copied on both sides
957 # copied on both sides
946 dsts1 = set(dsts1)
958 dsts1 = set(dsts1)
947 dsts2 = set(dsts2)
959 dsts2 = set(dsts2)
948 for dst in dsts1 & dsts2:
960 for dst in dsts1 & dsts2:
949 copy1[dst] = src
961 copy1[dst] = src
950 copy2[dst] = src
962 copy2[dst] = src
951 # TODO: Handle cases where it was renamed on one side and copied
963 # TODO: Handle cases where it was renamed on one side and copied
952 # on the other side
964 # on the other side
953 elif dsts1:
965 elif dsts1:
954 # copied/renamed only on side 1
966 # copied/renamed only on side 1
955 _checksinglesidecopies(
967 _checksinglesidecopies(
956 src, dsts1, m1, m2, mb, c2, base, copy1, renamedelete1
968 src, dsts1, m1, m2, mb, c2, base, copy1, renamedelete1
957 )
969 )
958 elif dsts2:
970 elif dsts2:
959 # copied/renamed only on side 2
971 # copied/renamed only on side 2
960 _checksinglesidecopies(
972 _checksinglesidecopies(
961 src, dsts2, m2, m1, mb, c1, base, copy2, renamedelete2
973 src, dsts2, m2, m1, mb, c1, base, copy2, renamedelete2
962 )
974 )
963
975
964 # find interesting file sets from manifests
976 # find interesting file sets from manifests
965 cache = []
977 cache = []
966
978
967 def _get_addedfiles(idx):
979 def _get_addedfiles(idx):
968 if not cache:
980 if not cache:
969 addedinm1 = m1.filesnotin(mb, repo.narrowmatch())
981 addedinm1 = m1.filesnotin(mb, repo.narrowmatch())
970 addedinm2 = m2.filesnotin(mb, repo.narrowmatch())
982 addedinm2 = m2.filesnotin(mb, repo.narrowmatch())
971 u1 = sorted(addedinm1 - addedinm2)
983 u1 = sorted(addedinm1 - addedinm2)
972 u2 = sorted(addedinm2 - addedinm1)
984 u2 = sorted(addedinm2 - addedinm1)
973 cache.extend((u1, u2))
985 cache.extend((u1, u2))
974 return cache[idx]
986 return cache[idx]
975
987
976 u1fn = lambda: _get_addedfiles(0)
988 u1fn = lambda: _get_addedfiles(0)
977 u2fn = lambda: _get_addedfiles(1)
989 u2fn = lambda: _get_addedfiles(1)
978 if repo.ui.debugflag:
990 if repo.ui.debugflag:
979 u1 = u1fn()
991 u1 = u1fn()
980 u2 = u2fn()
992 u2 = u2fn()
981
993
982 header = b" unmatched files in %s"
994 header = b" unmatched files in %s"
983 if u1:
995 if u1:
984 repo.ui.debug(
996 repo.ui.debug(
985 b"%s:\n %s\n" % (header % b'local', b"\n ".join(u1))
997 b"%s:\n %s\n" % (header % b'local', b"\n ".join(u1))
986 )
998 )
987 if u2:
999 if u2:
988 repo.ui.debug(
1000 repo.ui.debug(
989 b"%s:\n %s\n" % (header % b'other', b"\n ".join(u2))
1001 b"%s:\n %s\n" % (header % b'other', b"\n ".join(u2))
990 )
1002 )
991
1003
992 renamedeleteset = set()
1004 renamedeleteset = set()
993 divergeset = set()
1005 divergeset = set()
994 for dsts in diverge.values():
1006 for dsts in diverge.values():
995 divergeset.update(dsts)
1007 divergeset.update(dsts)
996 for dsts in renamedelete1.values():
1008 for dsts in renamedelete1.values():
997 renamedeleteset.update(dsts)
1009 renamedeleteset.update(dsts)
998 for dsts in renamedelete2.values():
1010 for dsts in renamedelete2.values():
999 renamedeleteset.update(dsts)
1011 renamedeleteset.update(dsts)
1000
1012
1001 repo.ui.debug(
1013 repo.ui.debug(
1002 b" all copies found (* = to merge, ! = divergent, "
1014 b" all copies found (* = to merge, ! = divergent, "
1003 b"% = renamed and deleted):\n"
1015 b"% = renamed and deleted):\n"
1004 )
1016 )
1005 for side, copies in ((b"local", copies1), (b"remote", copies2)):
1017 for side, copies in ((b"local", copies1), (b"remote", copies2)):
1006 if not copies:
1018 if not copies:
1007 continue
1019 continue
1008 repo.ui.debug(b" on %s side:\n" % side)
1020 repo.ui.debug(b" on %s side:\n" % side)
1009 for f in sorted(copies):
1021 for f in sorted(copies):
1010 note = b""
1022 note = b""
1011 if f in copy1 or f in copy2:
1023 if f in copy1 or f in copy2:
1012 note += b"*"
1024 note += b"*"
1013 if f in divergeset:
1025 if f in divergeset:
1014 note += b"!"
1026 note += b"!"
1015 if f in renamedeleteset:
1027 if f in renamedeleteset:
1016 note += b"%"
1028 note += b"%"
1017 repo.ui.debug(
1029 repo.ui.debug(
1018 b" src: '%s' -> dst: '%s' %s\n" % (copies[f], f, note)
1030 b" src: '%s' -> dst: '%s' %s\n" % (copies[f], f, note)
1019 )
1031 )
1020 del renamedeleteset
1032 del renamedeleteset
1021 del divergeset
1033 del divergeset
1022
1034
1023 repo.ui.debug(b" checking for directory renames\n")
1035 repo.ui.debug(b" checking for directory renames\n")
1024
1036
1025 dirmove1, movewithdir2 = _dir_renames(repo, c1, copy1, copies1, u2fn)
1037 dirmove1, movewithdir2 = _dir_renames(repo, c1, copy1, copies1, u2fn)
1026 dirmove2, movewithdir1 = _dir_renames(repo, c2, copy2, copies2, u1fn)
1038 dirmove2, movewithdir1 = _dir_renames(repo, c2, copy2, copies2, u1fn)
1027
1039
1028 branch_copies1 = branch_copies(copy1, renamedelete1, dirmove1, movewithdir1)
1040 branch_copies1 = branch_copies(copy1, renamedelete1, dirmove1, movewithdir1)
1029 branch_copies2 = branch_copies(copy2, renamedelete2, dirmove2, movewithdir2)
1041 branch_copies2 = branch_copies(copy2, renamedelete2, dirmove2, movewithdir2)
1030
1042
1031 return branch_copies1, branch_copies2, diverge
1043 return branch_copies1, branch_copies2, diverge
1032
1044
1033
1045
1034 def _dir_renames(repo, ctx, copy, fullcopy, addedfilesfn):
1046 def _dir_renames(repo, ctx, copy, fullcopy, addedfilesfn):
1035 """Finds moved directories and files that should move with them.
1047 """Finds moved directories and files that should move with them.
1036
1048
1037 ctx: the context for one of the sides
1049 ctx: the context for one of the sides
1038 copy: files copied on the same side (as ctx)
1050 copy: files copied on the same side (as ctx)
1039 fullcopy: files copied on the same side (as ctx), including those that
1051 fullcopy: files copied on the same side (as ctx), including those that
1040 merge.manifestmerge() won't care about
1052 merge.manifestmerge() won't care about
1041 addedfilesfn: function returning added files on the other side (compared to
1053 addedfilesfn: function returning added files on the other side (compared to
1042 ctx)
1054 ctx)
1043 """
1055 """
1044 # generate a directory move map
1056 # generate a directory move map
1045 invalid = set()
1057 invalid = set()
1046 dirmove = {}
1058 dirmove = {}
1047
1059
1048 # examine each file copy for a potential directory move, which is
1060 # examine each file copy for a potential directory move, which is
1049 # when all the files in a directory are moved to a new directory
1061 # when all the files in a directory are moved to a new directory
1050 for dst, src in pycompat.iteritems(fullcopy):
1062 for dst, src in pycompat.iteritems(fullcopy):
1051 dsrc, ddst = pathutil.dirname(src), pathutil.dirname(dst)
1063 dsrc, ddst = pathutil.dirname(src), pathutil.dirname(dst)
1052 if dsrc in invalid:
1064 if dsrc in invalid:
1053 # already seen to be uninteresting
1065 # already seen to be uninteresting
1054 continue
1066 continue
1055 elif ctx.hasdir(dsrc) and ctx.hasdir(ddst):
1067 elif ctx.hasdir(dsrc) and ctx.hasdir(ddst):
1056 # directory wasn't entirely moved locally
1068 # directory wasn't entirely moved locally
1057 invalid.add(dsrc)
1069 invalid.add(dsrc)
1058 elif dsrc in dirmove and dirmove[dsrc] != ddst:
1070 elif dsrc in dirmove and dirmove[dsrc] != ddst:
1059 # files from the same directory moved to two different places
1071 # files from the same directory moved to two different places
1060 invalid.add(dsrc)
1072 invalid.add(dsrc)
1061 else:
1073 else:
1062 # looks good so far
1074 # looks good so far
1063 dirmove[dsrc] = ddst
1075 dirmove[dsrc] = ddst
1064
1076
1065 for i in invalid:
1077 for i in invalid:
1066 if i in dirmove:
1078 if i in dirmove:
1067 del dirmove[i]
1079 del dirmove[i]
1068 del invalid
1080 del invalid
1069
1081
1070 if not dirmove:
1082 if not dirmove:
1071 return {}, {}
1083 return {}, {}
1072
1084
1073 dirmove = {k + b"/": v + b"/" for k, v in pycompat.iteritems(dirmove)}
1085 dirmove = {k + b"/": v + b"/" for k, v in pycompat.iteritems(dirmove)}
1074
1086
1075 for d in dirmove:
1087 for d in dirmove:
1076 repo.ui.debug(
1088 repo.ui.debug(
1077 b" discovered dir src: '%s' -> dst: '%s'\n" % (d, dirmove[d])
1089 b" discovered dir src: '%s' -> dst: '%s'\n" % (d, dirmove[d])
1078 )
1090 )
1079
1091
1080 movewithdir = {}
1092 movewithdir = {}
1081 # check unaccounted nonoverlapping files against directory moves
1093 # check unaccounted nonoverlapping files against directory moves
1082 for f in addedfilesfn():
1094 for f in addedfilesfn():
1083 if f not in fullcopy:
1095 if f not in fullcopy:
1084 for d in dirmove:
1096 for d in dirmove:
1085 if f.startswith(d):
1097 if f.startswith(d):
1086 # new file added in a directory that was moved, move it
1098 # new file added in a directory that was moved, move it
1087 df = dirmove[d] + f[len(d) :]
1099 df = dirmove[d] + f[len(d) :]
1088 if df not in copy:
1100 if df not in copy:
1089 movewithdir[f] = df
1101 movewithdir[f] = df
1090 repo.ui.debug(
1102 repo.ui.debug(
1091 b" pending file src: '%s' -> dst: '%s'\n"
1103 b" pending file src: '%s' -> dst: '%s'\n"
1092 % (f, df)
1104 % (f, df)
1093 )
1105 )
1094 break
1106 break
1095
1107
1096 return dirmove, movewithdir
1108 return dirmove, movewithdir
1097
1109
1098
1110
1099 def _heuristicscopytracing(repo, c1, c2, base):
1111 def _heuristicscopytracing(repo, c1, c2, base):
1100 """Fast copytracing using filename heuristics
1112 """Fast copytracing using filename heuristics
1101
1113
1102 Assumes that moves or renames are of following two types:
1114 Assumes that moves or renames are of following two types:
1103
1115
1104 1) Inside a directory only (same directory name but different filenames)
1116 1) Inside a directory only (same directory name but different filenames)
1105 2) Move from one directory to another
1117 2) Move from one directory to another
1106 (same filenames but different directory names)
1118 (same filenames but different directory names)
1107
1119
1108 Works only when there are no merge commits in the "source branch".
1120 Works only when there are no merge commits in the "source branch".
1109 Source branch is commits from base up to c2 not including base.
1121 Source branch is commits from base up to c2 not including base.
1110
1122
1111 If merge is involved it fallbacks to _fullcopytracing().
1123 If merge is involved it fallbacks to _fullcopytracing().
1112
1124
1113 Can be used by setting the following config:
1125 Can be used by setting the following config:
1114
1126
1115 [experimental]
1127 [experimental]
1116 copytrace = heuristics
1128 copytrace = heuristics
1117
1129
1118 In some cases the copy/move candidates found by heuristics can be very large
1130 In some cases the copy/move candidates found by heuristics can be very large
1119 in number and that will make the algorithm slow. The number of possible
1131 in number and that will make the algorithm slow. The number of possible
1120 candidates to check can be limited by using the config
1132 candidates to check can be limited by using the config
1121 `experimental.copytrace.movecandidateslimit` which defaults to 100.
1133 `experimental.copytrace.movecandidateslimit` which defaults to 100.
1122 """
1134 """
1123
1135
1124 if c1.rev() is None:
1136 if c1.rev() is None:
1125 c1 = c1.p1()
1137 c1 = c1.p1()
1126 if c2.rev() is None:
1138 if c2.rev() is None:
1127 c2 = c2.p1()
1139 c2 = c2.p1()
1128
1140
1129 changedfiles = set()
1141 changedfiles = set()
1130 m1 = c1.manifest()
1142 m1 = c1.manifest()
1131 if not repo.revs(b'%d::%d', base.rev(), c2.rev()):
1143 if not repo.revs(b'%d::%d', base.rev(), c2.rev()):
1132 # If base is not in c2 branch, we switch to fullcopytracing
1144 # If base is not in c2 branch, we switch to fullcopytracing
1133 repo.ui.debug(
1145 repo.ui.debug(
1134 b"switching to full copytracing as base is not "
1146 b"switching to full copytracing as base is not "
1135 b"an ancestor of c2\n"
1147 b"an ancestor of c2\n"
1136 )
1148 )
1137 return _fullcopytracing(repo, c1, c2, base)
1149 return _fullcopytracing(repo, c1, c2, base)
1138
1150
1139 ctx = c2
1151 ctx = c2
1140 while ctx != base:
1152 while ctx != base:
1141 if len(ctx.parents()) == 2:
1153 if len(ctx.parents()) == 2:
1142 # To keep things simple let's not handle merges
1154 # To keep things simple let's not handle merges
1143 repo.ui.debug(b"switching to full copytracing because of merges\n")
1155 repo.ui.debug(b"switching to full copytracing because of merges\n")
1144 return _fullcopytracing(repo, c1, c2, base)
1156 return _fullcopytracing(repo, c1, c2, base)
1145 changedfiles.update(ctx.files())
1157 changedfiles.update(ctx.files())
1146 ctx = ctx.p1()
1158 ctx = ctx.p1()
1147
1159
1148 copies2 = {}
1160 copies2 = {}
1149 cp = _forwardcopies(base, c2)
1161 cp = _forwardcopies(base, c2)
1150 for dst, src in pycompat.iteritems(cp):
1162 for dst, src in pycompat.iteritems(cp):
1151 if src in m1:
1163 if src in m1:
1152 copies2[dst] = src
1164 copies2[dst] = src
1153
1165
1154 # file is missing if it isn't present in the destination, but is present in
1166 # file is missing if it isn't present in the destination, but is present in
1155 # the base and present in the source.
1167 # the base and present in the source.
1156 # Presence in the base is important to exclude added files, presence in the
1168 # Presence in the base is important to exclude added files, presence in the
1157 # source is important to exclude removed files.
1169 # source is important to exclude removed files.
1158 filt = lambda f: f not in m1 and f in base and f in c2
1170 filt = lambda f: f not in m1 and f in base and f in c2
1159 missingfiles = [f for f in changedfiles if filt(f)]
1171 missingfiles = [f for f in changedfiles if filt(f)]
1160
1172
1161 copies1 = {}
1173 copies1 = {}
1162 if missingfiles:
1174 if missingfiles:
1163 basenametofilename = collections.defaultdict(list)
1175 basenametofilename = collections.defaultdict(list)
1164 dirnametofilename = collections.defaultdict(list)
1176 dirnametofilename = collections.defaultdict(list)
1165
1177
1166 for f in m1.filesnotin(base.manifest()):
1178 for f in m1.filesnotin(base.manifest()):
1167 basename = os.path.basename(f)
1179 basename = os.path.basename(f)
1168 dirname = os.path.dirname(f)
1180 dirname = os.path.dirname(f)
1169 basenametofilename[basename].append(f)
1181 basenametofilename[basename].append(f)
1170 dirnametofilename[dirname].append(f)
1182 dirnametofilename[dirname].append(f)
1171
1183
1172 for f in missingfiles:
1184 for f in missingfiles:
1173 basename = os.path.basename(f)
1185 basename = os.path.basename(f)
1174 dirname = os.path.dirname(f)
1186 dirname = os.path.dirname(f)
1175 samebasename = basenametofilename[basename]
1187 samebasename = basenametofilename[basename]
1176 samedirname = dirnametofilename[dirname]
1188 samedirname = dirnametofilename[dirname]
1177 movecandidates = samebasename + samedirname
1189 movecandidates = samebasename + samedirname
1178 # f is guaranteed to be present in c2, that's why
1190 # f is guaranteed to be present in c2, that's why
1179 # c2.filectx(f) won't fail
1191 # c2.filectx(f) won't fail
1180 f2 = c2.filectx(f)
1192 f2 = c2.filectx(f)
1181 # we can have a lot of candidates which can slow down the heuristics
1193 # we can have a lot of candidates which can slow down the heuristics
1182 # config value to limit the number of candidates moves to check
1194 # config value to limit the number of candidates moves to check
1183 maxcandidates = repo.ui.configint(
1195 maxcandidates = repo.ui.configint(
1184 b'experimental', b'copytrace.movecandidateslimit'
1196 b'experimental', b'copytrace.movecandidateslimit'
1185 )
1197 )
1186
1198
1187 if len(movecandidates) > maxcandidates:
1199 if len(movecandidates) > maxcandidates:
1188 repo.ui.status(
1200 repo.ui.status(
1189 _(
1201 _(
1190 b"skipping copytracing for '%s', more "
1202 b"skipping copytracing for '%s', more "
1191 b"candidates than the limit: %d\n"
1203 b"candidates than the limit: %d\n"
1192 )
1204 )
1193 % (f, len(movecandidates))
1205 % (f, len(movecandidates))
1194 )
1206 )
1195 continue
1207 continue
1196
1208
1197 for candidate in movecandidates:
1209 for candidate in movecandidates:
1198 f1 = c1.filectx(candidate)
1210 f1 = c1.filectx(candidate)
1199 if _related(f1, f2):
1211 if _related(f1, f2):
1200 # if there are a few related copies then we'll merge
1212 # if there are a few related copies then we'll merge
1201 # changes into all of them. This matches the behaviour
1213 # changes into all of them. This matches the behaviour
1202 # of upstream copytracing
1214 # of upstream copytracing
1203 copies1[candidate] = f
1215 copies1[candidate] = f
1204
1216
1205 return branch_copies(copies1), branch_copies(copies2), {}
1217 return branch_copies(copies1), branch_copies(copies2), {}
1206
1218
1207
1219
1208 def _related(f1, f2):
1220 def _related(f1, f2):
1209 """return True if f1 and f2 filectx have a common ancestor
1221 """return True if f1 and f2 filectx have a common ancestor
1210
1222
1211 Walk back to common ancestor to see if the two files originate
1223 Walk back to common ancestor to see if the two files originate
1212 from the same file. Since workingfilectx's rev() is None it messes
1224 from the same file. Since workingfilectx's rev() is None it messes
1213 up the integer comparison logic, hence the pre-step check for
1225 up the integer comparison logic, hence the pre-step check for
1214 None (f1 and f2 can only be workingfilectx's initially).
1226 None (f1 and f2 can only be workingfilectx's initially).
1215 """
1227 """
1216
1228
1217 if f1 == f2:
1229 if f1 == f2:
1218 return True # a match
1230 return True # a match
1219
1231
1220 g1, g2 = f1.ancestors(), f2.ancestors()
1232 g1, g2 = f1.ancestors(), f2.ancestors()
1221 try:
1233 try:
1222 f1r, f2r = f1.linkrev(), f2.linkrev()
1234 f1r, f2r = f1.linkrev(), f2.linkrev()
1223
1235
1224 if f1r is None:
1236 if f1r is None:
1225 f1 = next(g1)
1237 f1 = next(g1)
1226 if f2r is None:
1238 if f2r is None:
1227 f2 = next(g2)
1239 f2 = next(g2)
1228
1240
1229 while True:
1241 while True:
1230 f1r, f2r = f1.linkrev(), f2.linkrev()
1242 f1r, f2r = f1.linkrev(), f2.linkrev()
1231 if f1r > f2r:
1243 if f1r > f2r:
1232 f1 = next(g1)
1244 f1 = next(g1)
1233 elif f2r > f1r:
1245 elif f2r > f1r:
1234 f2 = next(g2)
1246 f2 = next(g2)
1235 else: # f1 and f2 point to files in the same linkrev
1247 else: # f1 and f2 point to files in the same linkrev
1236 return f1 == f2 # true if they point to the same file
1248 return f1 == f2 # true if they point to the same file
1237 except StopIteration:
1249 except StopIteration:
1238 return False
1250 return False
1239
1251
1240
1252
1241 def graftcopies(wctx, ctx, base):
1253 def graftcopies(wctx, ctx, base):
1242 """reproduce copies between base and ctx in the wctx
1254 """reproduce copies between base and ctx in the wctx
1243
1255
1244 Unlike mergecopies(), this function will only consider copies between base
1256 Unlike mergecopies(), this function will only consider copies between base
1245 and ctx; it will ignore copies between base and wctx. Also unlike
1257 and ctx; it will ignore copies between base and wctx. Also unlike
1246 mergecopies(), this function will apply copies to the working copy (instead
1258 mergecopies(), this function will apply copies to the working copy (instead
1247 of just returning information about the copies). That makes it cheaper
1259 of just returning information about the copies). That makes it cheaper
1248 (especially in the common case of base==ctx.p1()) and useful also when
1260 (especially in the common case of base==ctx.p1()) and useful also when
1249 experimental.copytrace=off.
1261 experimental.copytrace=off.
1250
1262
1251 merge.update() will have already marked most copies, but it will only
1263 merge.update() will have already marked most copies, but it will only
1252 mark copies if it thinks the source files are related (see
1264 mark copies if it thinks the source files are related (see
1253 merge._related()). It will also not mark copies if the file wasn't modified
1265 merge._related()). It will also not mark copies if the file wasn't modified
1254 on the local side. This function adds the copies that were "missed"
1266 on the local side. This function adds the copies that were "missed"
1255 by merge.update().
1267 by merge.update().
1256 """
1268 """
1257 new_copies = pathcopies(base, ctx)
1269 new_copies = pathcopies(base, ctx)
1258 parent = wctx.p1()
1270 parent = wctx.p1()
1259 _filter(parent, wctx, new_copies)
1271 _filter(parent, wctx, new_copies)
1260 # Extra filtering to drop copy information for files that existed before
1272 # Extra filtering to drop copy information for files that existed before
1261 # the graft. This is to handle the case of grafting a rename onto a commit
1273 # the graft. This is to handle the case of grafting a rename onto a commit
1262 # that already has the rename. Otherwise the presence of copy information
1274 # that already has the rename. Otherwise the presence of copy information
1263 # would result in the creation of an empty commit where we would prefer to
1275 # would result in the creation of an empty commit where we would prefer to
1264 # not create one.
1276 # not create one.
1265 for dest, __ in list(new_copies.items()):
1277 for dest, __ in list(new_copies.items()):
1266 if dest in parent:
1278 if dest in parent:
1267 del new_copies[dest]
1279 del new_copies[dest]
1268 for dst, src in pycompat.iteritems(new_copies):
1280 for dst, src in pycompat.iteritems(new_copies):
1269 wctx[dst].markcopied(src)
1281 wctx[dst].markcopied(src)
@@ -1,815 +1,871 b''
1 use crate::utils::hg_path::HgPath;
1 use crate::utils::hg_path::HgPath;
2 use crate::utils::hg_path::HgPathBuf;
2 use crate::utils::hg_path::HgPathBuf;
3 use crate::Revision;
3 use crate::Revision;
4 use crate::NULL_REVISION;
4 use crate::NULL_REVISION;
5
5
6 use im_rc::ordmap::DiffItem;
6 use im_rc::ordmap::DiffItem;
7 use im_rc::ordmap::Entry;
7 use im_rc::ordmap::Entry;
8 use im_rc::ordmap::OrdMap;
8 use im_rc::ordmap::OrdMap;
9
9
10 use std::cmp::Ordering;
10 use std::cmp::Ordering;
11 use std::collections::HashMap;
11 use std::collections::HashMap;
12 use std::convert::TryInto;
12 use std::convert::TryInto;
13
13
14 pub type PathCopies = HashMap<HgPathBuf, HgPathBuf>;
14 pub type PathCopies = HashMap<HgPathBuf, HgPathBuf>;
15
15
16 type PathToken = usize;
16 type PathToken = usize;
17
17
18 #[derive(Clone, Debug, PartialEq, Copy)]
18 #[derive(Clone, Debug, PartialEq, Copy)]
19 struct TimeStampedPathCopy {
19 struct TimeStampedPathCopy {
20 /// revision at which the copy information was added
20 /// revision at which the copy information was added
21 rev: Revision,
21 rev: Revision,
22 /// the copy source, (Set to None in case of deletion of the associated
22 /// the copy source, (Set to None in case of deletion of the associated
23 /// key)
23 /// key)
24 path: Option<PathToken>,
24 path: Option<PathToken>,
25 }
25 }
26
26
27 /// maps CopyDestination to Copy Source (+ a "timestamp" for the operation)
27 /// maps CopyDestination to Copy Source (+ a "timestamp" for the operation)
28 type TimeStampedPathCopies = OrdMap<PathToken, TimeStampedPathCopy>;
28 type TimeStampedPathCopies = OrdMap<PathToken, TimeStampedPathCopy>;
29
29
30 /// hold parent 1, parent 2 and relevant files actions.
30 /// hold parent 1, parent 2 and relevant files actions.
31 pub type RevInfo<'a> = (Revision, Revision, ChangedFiles<'a>);
31 pub type RevInfo<'a> = (Revision, Revision, ChangedFiles<'a>);
32
32
33 /// represent the files affected by a changesets
33 /// represent the files affected by a changesets
34 ///
34 ///
35 /// This hold a subset of mercurial.metadata.ChangingFiles as we do not need
35 /// This hold a subset of mercurial.metadata.ChangingFiles as we do not need
36 /// all the data categories tracked by it.
36 /// all the data categories tracked by it.
37 /// This hold a subset of mercurial.metadata.ChangingFiles as we do not need
37 /// This hold a subset of mercurial.metadata.ChangingFiles as we do not need
38 /// all the data categories tracked by it.
38 /// all the data categories tracked by it.
39 pub struct ChangedFiles<'a> {
39 pub struct ChangedFiles<'a> {
40 nb_items: u32,
40 nb_items: u32,
41 index: &'a [u8],
41 index: &'a [u8],
42 data: &'a [u8],
42 data: &'a [u8],
43 }
43 }
44
44
45 /// Represent active changes that affect the copy tracing.
45 /// Represent active changes that affect the copy tracing.
46 enum Action<'a> {
46 enum Action<'a> {
47 /// The parent ? children edge is removing a file
47 /// The parent ? children edge is removing a file
48 ///
48 ///
49 /// (actually, this could be the edge from the other parent, but it does
49 /// (actually, this could be the edge from the other parent, but it does
50 /// not matters)
50 /// not matters)
51 Removed(&'a HgPath),
51 Removed(&'a HgPath),
52 /// The parent ? children edge introduce copy information between (dest,
52 /// The parent ? children edge introduce copy information between (dest,
53 /// source)
53 /// source)
54 Copied(&'a HgPath, &'a HgPath),
54 Copied(&'a HgPath, &'a HgPath),
55 }
55 }
56
56
57 /// This express the possible "special" case we can get in a merge
57 /// This express the possible "special" case we can get in a merge
58 ///
58 ///
59 /// See mercurial/metadata.py for details on these values.
59 /// See mercurial/metadata.py for details on these values.
60 #[derive(PartialEq)]
60 #[derive(PartialEq)]
61 enum MergeCase {
61 enum MergeCase {
62 /// Merged: file had history on both side that needed to be merged
62 /// Merged: file had history on both side that needed to be merged
63 Merged,
63 Merged,
64 /// Salvaged: file was candidate for deletion, but survived the merge
64 /// Salvaged: file was candidate for deletion, but survived the merge
65 Salvaged,
65 Salvaged,
66 /// Normal: Not one of the two cases above
66 /// Normal: Not one of the two cases above
67 Normal,
67 Normal,
68 }
68 }
69
69
70 type FileChange<'a> = (u8, &'a HgPath, &'a HgPath);
70 type FileChange<'a> = (u8, &'a HgPath, &'a HgPath);
71
71
72 const EMPTY: &[u8] = b"";
72 const EMPTY: &[u8] = b"";
73 const COPY_MASK: u8 = 3;
73 const COPY_MASK: u8 = 3;
74 const P1_COPY: u8 = 2;
74 const P1_COPY: u8 = 2;
75 const P2_COPY: u8 = 3;
75 const P2_COPY: u8 = 3;
76 const ACTION_MASK: u8 = 28;
76 const ACTION_MASK: u8 = 28;
77 const REMOVED: u8 = 12;
77 const REMOVED: u8 = 12;
78 const MERGED: u8 = 8;
78 const MERGED: u8 = 8;
79 const SALVAGED: u8 = 16;
79 const SALVAGED: u8 = 16;
80
80
81 impl<'a> ChangedFiles<'a> {
81 impl<'a> ChangedFiles<'a> {
82 const INDEX_START: usize = 4;
82 const INDEX_START: usize = 4;
83 const ENTRY_SIZE: u32 = 9;
83 const ENTRY_SIZE: u32 = 9;
84 const FILENAME_START: u32 = 1;
84 const FILENAME_START: u32 = 1;
85 const COPY_SOURCE_START: u32 = 5;
85 const COPY_SOURCE_START: u32 = 5;
86
86
87 pub fn new(data: &'a [u8]) -> Self {
87 pub fn new(data: &'a [u8]) -> Self {
88 assert!(
88 assert!(
89 data.len() >= 4,
89 data.len() >= 4,
90 "data size ({}) is too small to contain the header (4)",
90 "data size ({}) is too small to contain the header (4)",
91 data.len()
91 data.len()
92 );
92 );
93 let nb_items_raw: [u8; 4] = (&data[0..=3])
93 let nb_items_raw: [u8; 4] = (&data[0..=3])
94 .try_into()
94 .try_into()
95 .expect("failed to turn 4 bytes into 4 bytes");
95 .expect("failed to turn 4 bytes into 4 bytes");
96 let nb_items = u32::from_be_bytes(nb_items_raw);
96 let nb_items = u32::from_be_bytes(nb_items_raw);
97
97
98 let index_size = (nb_items * Self::ENTRY_SIZE) as usize;
98 let index_size = (nb_items * Self::ENTRY_SIZE) as usize;
99 let index_end = Self::INDEX_START + index_size;
99 let index_end = Self::INDEX_START + index_size;
100
100
101 assert!(
101 assert!(
102 data.len() >= index_end,
102 data.len() >= index_end,
103 "data size ({}) is too small to fit the index_data ({})",
103 "data size ({}) is too small to fit the index_data ({})",
104 data.len(),
104 data.len(),
105 index_end
105 index_end
106 );
106 );
107
107
108 let ret = ChangedFiles {
108 let ret = ChangedFiles {
109 nb_items,
109 nb_items,
110 index: &data[Self::INDEX_START..index_end],
110 index: &data[Self::INDEX_START..index_end],
111 data: &data[index_end..],
111 data: &data[index_end..],
112 };
112 };
113 let max_data = ret.filename_end(nb_items - 1) as usize;
113 let max_data = ret.filename_end(nb_items - 1) as usize;
114 assert!(
114 assert!(
115 ret.data.len() >= max_data,
115 ret.data.len() >= max_data,
116 "data size ({}) is too small to fit all data ({})",
116 "data size ({}) is too small to fit all data ({})",
117 data.len(),
117 data.len(),
118 index_end + max_data
118 index_end + max_data
119 );
119 );
120 ret
120 ret
121 }
121 }
122
122
123 pub fn new_empty() -> Self {
123 pub fn new_empty() -> Self {
124 ChangedFiles {
124 ChangedFiles {
125 nb_items: 0,
125 nb_items: 0,
126 index: EMPTY,
126 index: EMPTY,
127 data: EMPTY,
127 data: EMPTY,
128 }
128 }
129 }
129 }
130
130
131 /// internal function to return an individual entry at a given index
131 /// internal function to return an individual entry at a given index
132 fn entry(&'a self, idx: u32) -> FileChange<'a> {
132 fn entry(&'a self, idx: u32) -> FileChange<'a> {
133 if idx >= self.nb_items {
133 if idx >= self.nb_items {
134 panic!(
134 panic!(
135 "index for entry is higher that the number of file {} >= {}",
135 "index for entry is higher that the number of file {} >= {}",
136 idx, self.nb_items
136 idx, self.nb_items
137 )
137 )
138 }
138 }
139 let flags = self.flags(idx);
139 let flags = self.flags(idx);
140 let filename = self.filename(idx);
140 let filename = self.filename(idx);
141 let copy_idx = self.copy_idx(idx);
141 let copy_idx = self.copy_idx(idx);
142 let copy_source = self.filename(copy_idx);
142 let copy_source = self.filename(copy_idx);
143 (flags, filename, copy_source)
143 (flags, filename, copy_source)
144 }
144 }
145
145
146 /// internal function to return the filename of the entry at a given index
146 /// internal function to return the filename of the entry at a given index
147 fn filename(&self, idx: u32) -> &HgPath {
147 fn filename(&self, idx: u32) -> &HgPath {
148 let filename_start;
148 let filename_start;
149 if idx == 0 {
149 if idx == 0 {
150 filename_start = 0;
150 filename_start = 0;
151 } else {
151 } else {
152 filename_start = self.filename_end(idx - 1)
152 filename_start = self.filename_end(idx - 1)
153 }
153 }
154 let filename_end = self.filename_end(idx);
154 let filename_end = self.filename_end(idx);
155 let filename_start = filename_start as usize;
155 let filename_start = filename_start as usize;
156 let filename_end = filename_end as usize;
156 let filename_end = filename_end as usize;
157 HgPath::new(&self.data[filename_start..filename_end])
157 HgPath::new(&self.data[filename_start..filename_end])
158 }
158 }
159
159
160 /// internal function to return the flag field of the entry at a given
160 /// internal function to return the flag field of the entry at a given
161 /// index
161 /// index
162 fn flags(&self, idx: u32) -> u8 {
162 fn flags(&self, idx: u32) -> u8 {
163 let idx = idx as usize;
163 let idx = idx as usize;
164 self.index[idx * (Self::ENTRY_SIZE as usize)]
164 self.index[idx * (Self::ENTRY_SIZE as usize)]
165 }
165 }
166
166
167 /// internal function to return the end of a filename part at a given index
167 /// internal function to return the end of a filename part at a given index
168 fn filename_end(&self, idx: u32) -> u32 {
168 fn filename_end(&self, idx: u32) -> u32 {
169 let start = (idx * Self::ENTRY_SIZE) + Self::FILENAME_START;
169 let start = (idx * Self::ENTRY_SIZE) + Self::FILENAME_START;
170 let end = (idx * Self::ENTRY_SIZE) + Self::COPY_SOURCE_START;
170 let end = (idx * Self::ENTRY_SIZE) + Self::COPY_SOURCE_START;
171 let start = start as usize;
171 let start = start as usize;
172 let end = end as usize;
172 let end = end as usize;
173 let raw = (&self.index[start..end])
173 let raw = (&self.index[start..end])
174 .try_into()
174 .try_into()
175 .expect("failed to turn 4 bytes into 4 bytes");
175 .expect("failed to turn 4 bytes into 4 bytes");
176 u32::from_be_bytes(raw)
176 u32::from_be_bytes(raw)
177 }
177 }
178
178
179 /// internal function to return index of the copy source of the entry at a
179 /// internal function to return index of the copy source of the entry at a
180 /// given index
180 /// given index
181 fn copy_idx(&self, idx: u32) -> u32 {
181 fn copy_idx(&self, idx: u32) -> u32 {
182 let start = (idx * Self::ENTRY_SIZE) + Self::COPY_SOURCE_START;
182 let start = (idx * Self::ENTRY_SIZE) + Self::COPY_SOURCE_START;
183 let end = (idx + 1) * Self::ENTRY_SIZE;
183 let end = (idx + 1) * Self::ENTRY_SIZE;
184 let start = start as usize;
184 let start = start as usize;
185 let end = end as usize;
185 let end = end as usize;
186 let raw = (&self.index[start..end])
186 let raw = (&self.index[start..end])
187 .try_into()
187 .try_into()
188 .expect("failed to turn 4 bytes into 4 bytes");
188 .expect("failed to turn 4 bytes into 4 bytes");
189 u32::from_be_bytes(raw)
189 u32::from_be_bytes(raw)
190 }
190 }
191
191
192 /// Return an iterator over all the `Action` in this instance.
192 /// Return an iterator over all the `Action` in this instance.
193 fn iter_actions(&self, parent: Parent) -> ActionsIterator {
193 fn iter_actions(&self, parent: Parent) -> ActionsIterator {
194 ActionsIterator {
194 ActionsIterator {
195 changes: &self,
195 changes: &self,
196 parent: parent,
196 parent: parent,
197 current: 0,
197 current: 0,
198 }
198 }
199 }
199 }
200
200
201 /// return the MergeCase value associated with a filename
201 /// return the MergeCase value associated with a filename
202 fn get_merge_case(&self, path: &HgPath) -> MergeCase {
202 fn get_merge_case(&self, path: &HgPath) -> MergeCase {
203 if self.nb_items == 0 {
203 if self.nb_items == 0 {
204 return MergeCase::Normal;
204 return MergeCase::Normal;
205 }
205 }
206 let mut low_part = 0;
206 let mut low_part = 0;
207 let mut high_part = self.nb_items;
207 let mut high_part = self.nb_items;
208
208
209 while low_part < high_part {
209 while low_part < high_part {
210 let cursor = (low_part + high_part - 1) / 2;
210 let cursor = (low_part + high_part - 1) / 2;
211 let (flags, filename, _source) = self.entry(cursor);
211 let (flags, filename, _source) = self.entry(cursor);
212 match path.cmp(filename) {
212 match path.cmp(filename) {
213 Ordering::Less => low_part = cursor + 1,
213 Ordering::Less => low_part = cursor + 1,
214 Ordering::Greater => high_part = cursor,
214 Ordering::Greater => high_part = cursor,
215 Ordering::Equal => {
215 Ordering::Equal => {
216 return match flags & ACTION_MASK {
216 return match flags & ACTION_MASK {
217 MERGED => MergeCase::Merged,
217 MERGED => MergeCase::Merged,
218 SALVAGED => MergeCase::Salvaged,
218 SALVAGED => MergeCase::Salvaged,
219 _ => MergeCase::Normal,
219 _ => MergeCase::Normal,
220 };
220 };
221 }
221 }
222 }
222 }
223 }
223 }
224 MergeCase::Normal
224 MergeCase::Normal
225 }
225 }
226 }
226 }
227
227
228 /// A struct responsible for answering "is X ancestors of Y" quickly
228 /// A struct responsible for answering "is X ancestors of Y" quickly
229 ///
229 ///
230 /// The structure will delegate ancestors call to a callback, and cache the
230 /// The structure will delegate ancestors call to a callback, and cache the
231 /// result.
231 /// result.
232 #[derive(Debug)]
232 #[derive(Debug)]
233 struct AncestorOracle<'a, A: Fn(Revision, Revision) -> bool> {
233 struct AncestorOracle<'a, A: Fn(Revision, Revision) -> bool> {
234 inner: &'a A,
234 inner: &'a A,
235 pairs: HashMap<(Revision, Revision), bool>,
235 pairs: HashMap<(Revision, Revision), bool>,
236 }
236 }
237
237
238 impl<'a, A: Fn(Revision, Revision) -> bool> AncestorOracle<'a, A> {
238 impl<'a, A: Fn(Revision, Revision) -> bool> AncestorOracle<'a, A> {
239 fn new(func: &'a A) -> Self {
239 fn new(func: &'a A) -> Self {
240 Self {
240 Self {
241 inner: func,
241 inner: func,
242 pairs: HashMap::default(),
242 pairs: HashMap::default(),
243 }
243 }
244 }
244 }
245
245
246 fn record_overwrite(&mut self, anc: Revision, desc: Revision) {
246 fn record_overwrite(&mut self, anc: Revision, desc: Revision) {
247 self.pairs.insert((anc, desc), true);
247 self.pairs.insert((anc, desc), true);
248 }
248 }
249
249
250 /// returns `true` if `anc` is an ancestors of `desc`, `false` otherwise
250 /// returns `true` if `anc` is an ancestors of `desc`, `false` otherwise
251 fn is_overwrite(&mut self, anc: Revision, desc: Revision) -> bool {
251 fn is_overwrite(&mut self, anc: Revision, desc: Revision) -> bool {
252 if anc > desc {
252 if anc > desc {
253 false
253 false
254 } else if anc == desc {
254 } else if anc == desc {
255 true
255 true
256 } else {
256 } else {
257 if let Some(b) = self.pairs.get(&(anc, desc)) {
257 if let Some(b) = self.pairs.get(&(anc, desc)) {
258 *b
258 *b
259 } else {
259 } else {
260 let b = (self.inner)(anc, desc);
260 let b = (self.inner)(anc, desc);
261 self.pairs.insert((anc, desc), b);
261 self.pairs.insert((anc, desc), b);
262 b
262 b
263 }
263 }
264 }
264 }
265 }
265 }
266 }
266 }
267
267
268 struct ActionsIterator<'a> {
268 struct ActionsIterator<'a> {
269 changes: &'a ChangedFiles<'a>,
269 changes: &'a ChangedFiles<'a>,
270 parent: Parent,
270 parent: Parent,
271 current: u32,
271 current: u32,
272 }
272 }
273
273
274 impl<'a> Iterator for ActionsIterator<'a> {
274 impl<'a> Iterator for ActionsIterator<'a> {
275 type Item = Action<'a>;
275 type Item = Action<'a>;
276
276
277 fn next(&mut self) -> Option<Action<'a>> {
277 fn next(&mut self) -> Option<Action<'a>> {
278 let copy_flag = match self.parent {
278 let copy_flag = match self.parent {
279 Parent::FirstParent => P1_COPY,
279 Parent::FirstParent => P1_COPY,
280 Parent::SecondParent => P2_COPY,
280 Parent::SecondParent => P2_COPY,
281 };
281 };
282 while self.current < self.changes.nb_items {
282 while self.current < self.changes.nb_items {
283 let (flags, file, source) = self.changes.entry(self.current);
283 let (flags, file, source) = self.changes.entry(self.current);
284 self.current += 1;
284 self.current += 1;
285 if (flags & ACTION_MASK) == REMOVED {
285 if (flags & ACTION_MASK) == REMOVED {
286 return Some(Action::Removed(file));
286 return Some(Action::Removed(file));
287 }
287 }
288 let copy = flags & COPY_MASK;
288 let copy = flags & COPY_MASK;
289 if copy == copy_flag {
289 if copy == copy_flag {
290 return Some(Action::Copied(file, source));
290 return Some(Action::Copied(file, source));
291 }
291 }
292 }
292 }
293 return None;
293 return None;
294 }
294 }
295 }
295 }
296
296
297 /// A small struct whose purpose is to ensure lifetime of bytes referenced in
297 /// A small struct whose purpose is to ensure lifetime of bytes referenced in
298 /// ChangedFiles
298 /// ChangedFiles
299 ///
299 ///
300 /// It is passed to the RevInfoMaker callback who can assign any necessary
300 /// It is passed to the RevInfoMaker callback who can assign any necessary
301 /// content to the `data` attribute. The copy tracing code is responsible for
301 /// content to the `data` attribute. The copy tracing code is responsible for
302 /// keeping the DataHolder alive at least as long as the ChangedFiles object.
302 /// keeping the DataHolder alive at least as long as the ChangedFiles object.
303 pub struct DataHolder<D> {
303 pub struct DataHolder<D> {
304 /// RevInfoMaker callback should assign data referenced by the
304 /// RevInfoMaker callback should assign data referenced by the
305 /// ChangedFiles struct it return to this attribute. The DataHolder
305 /// ChangedFiles struct it return to this attribute. The DataHolder
306 /// lifetime will be at least as long as the ChangedFiles one.
306 /// lifetime will be at least as long as the ChangedFiles one.
307 pub data: Option<D>,
307 pub data: Option<D>,
308 }
308 }
309
309
310 pub type RevInfoMaker<'a, D> =
310 pub type RevInfoMaker<'a, D> =
311 Box<dyn for<'r> Fn(Revision, &'r mut DataHolder<D>) -> RevInfo<'r> + 'a>;
311 Box<dyn for<'r> Fn(Revision, &'r mut DataHolder<D>) -> RevInfo<'r> + 'a>;
312
312
313 /// enum used to carry information about the parent β†’ child currently processed
313 /// enum used to carry information about the parent β†’ child currently processed
314 #[derive(Copy, Clone, Debug)]
314 #[derive(Copy, Clone, Debug)]
315 enum Parent {
315 enum Parent {
316 /// The `p1(x) β†’ x` edge
316 /// The `p1(x) β†’ x` edge
317 FirstParent,
317 FirstParent,
318 /// The `p2(x) β†’ x` edge
318 /// The `p2(x) β†’ x` edge
319 SecondParent,
319 SecondParent,
320 }
320 }
321
321
322 /// A small "tokenizer" responsible of turning full HgPath into lighter
322 /// A small "tokenizer" responsible of turning full HgPath into lighter
323 /// PathToken
323 /// PathToken
324 ///
324 ///
325 /// Dealing with small object, like integer is much faster, so HgPath input are
325 /// Dealing with small object, like integer is much faster, so HgPath input are
326 /// turned into integer "PathToken" and converted back in the end.
326 /// turned into integer "PathToken" and converted back in the end.
327 #[derive(Clone, Debug, Default)]
327 #[derive(Clone, Debug, Default)]
328 struct TwoWayPathMap {
328 struct TwoWayPathMap {
329 token: HashMap<HgPathBuf, PathToken>,
329 token: HashMap<HgPathBuf, PathToken>,
330 path: Vec<HgPathBuf>,
330 path: Vec<HgPathBuf>,
331 }
331 }
332
332
333 impl TwoWayPathMap {
333 impl TwoWayPathMap {
334 fn tokenize(&mut self, path: &HgPath) -> PathToken {
334 fn tokenize(&mut self, path: &HgPath) -> PathToken {
335 match self.token.get(path) {
335 match self.token.get(path) {
336 Some(a) => *a,
336 Some(a) => *a,
337 None => {
337 None => {
338 let a = self.token.len();
338 let a = self.token.len();
339 let buf = path.to_owned();
339 let buf = path.to_owned();
340 self.path.push(buf.clone());
340 self.path.push(buf.clone());
341 self.token.insert(buf, a);
341 self.token.insert(buf, a);
342 a
342 a
343 }
343 }
344 }
344 }
345 }
345 }
346
346
347 fn untokenize(&self, token: PathToken) -> &HgPathBuf {
347 fn untokenize(&self, token: PathToken) -> &HgPathBuf {
348 assert!(token < self.path.len(), format!("Unknown token: {}", token));
348 assert!(token < self.path.len(), format!("Unknown token: {}", token));
349 &self.path[token]
349 &self.path[token]
350 }
350 }
351 }
351 }
352
352
353 /// Same as mercurial.copies._combine_changeset_copies, but in Rust.
353 /// Same as mercurial.copies._combine_changeset_copies, but in Rust.
354 ///
354 ///
355 /// Arguments are:
355 /// Arguments are:
356 ///
356 ///
357 /// revs: all revisions to be considered
357 /// revs: all revisions to be considered
358 /// children: a {parent ? [childrens]} mapping
358 /// children: a {parent ? [childrens]} mapping
359 /// target_rev: the final revision we are combining copies to
359 /// target_rev: the final revision we are combining copies to
360 /// rev_info(rev): callback to get revision information:
360 /// rev_info(rev): callback to get revision information:
361 /// * first parent
361 /// * first parent
362 /// * second parent
362 /// * second parent
363 /// * ChangedFiles
363 /// * ChangedFiles
364 /// isancestors(low_rev, high_rev): callback to check if a revision is an
364 /// isancestors(low_rev, high_rev): callback to check if a revision is an
365 /// ancestor of another
365 /// ancestor of another
366 pub fn combine_changeset_copies<A: Fn(Revision, Revision) -> bool, D>(
366 pub fn combine_changeset_copies<A: Fn(Revision, Revision) -> bool, D>(
367 revs: Vec<Revision>,
367 revs: Vec<Revision>,
368 mut children_count: HashMap<Revision, usize>,
368 mut children_count: HashMap<Revision, usize>,
369 target_rev: Revision,
369 target_rev: Revision,
370 rev_info: RevInfoMaker<D>,
370 rev_info: RevInfoMaker<D>,
371 is_ancestor: &A,
371 is_ancestor: &A,
372 ) -> PathCopies {
372 ) -> PathCopies {
373 let mut all_copies = HashMap::new();
373 let mut all_copies = HashMap::new();
374 let mut oracle = AncestorOracle::new(is_ancestor);
374 let mut oracle = AncestorOracle::new(is_ancestor);
375
375
376 let mut path_map = TwoWayPathMap::default();
376 let mut path_map = TwoWayPathMap::default();
377
377
378 for rev in revs {
378 for rev in revs {
379 let mut d: DataHolder<D> = DataHolder { data: None };
379 let mut d: DataHolder<D> = DataHolder { data: None };
380 let (p1, p2, changes) = rev_info(rev, &mut d);
380 let (p1, p2, changes) = rev_info(rev, &mut d);
381
381
382 // We will chain the copies information accumulated for the parent with
382 // We will chain the copies information accumulated for the parent with
383 // the individual copies information the curent revision. Creating a
383 // the individual copies information the curent revision. Creating a
384 // new TimeStampedPath for each `rev` β†’ `children` vertex.
384 // new TimeStampedPath for each `rev` β†’ `children` vertex.
385 let mut copies: Option<TimeStampedPathCopies> = None;
385 let mut copies: Option<TimeStampedPathCopies> = None;
386 if p1 != NULL_REVISION {
386 if p1 != NULL_REVISION {
387 // Retrieve data computed in a previous iteration
387 // Retrieve data computed in a previous iteration
388 let parent_copies = get_and_clean_parent_copies(
388 let parent_copies = get_and_clean_parent_copies(
389 &mut all_copies,
389 &mut all_copies,
390 &mut children_count,
390 &mut children_count,
391 p1,
391 p1,
392 );
392 );
393 if let Some(parent_copies) = parent_copies {
393 if let Some(parent_copies) = parent_copies {
394 // combine it with data for that revision
394 // combine it with data for that revision
395 let vertex_copies = add_from_changes(
395 let vertex_copies = add_from_changes(
396 &mut path_map,
396 &mut path_map,
397 &mut oracle,
397 &mut oracle,
398 &parent_copies,
398 &parent_copies,
399 &changes,
399 &changes,
400 Parent::FirstParent,
400 Parent::FirstParent,
401 rev,
401 rev,
402 );
402 );
403 // keep that data around for potential later combination
403 // keep that data around for potential later combination
404 copies = Some(vertex_copies);
404 copies = Some(vertex_copies);
405 }
405 }
406 }
406 }
407 if p2 != NULL_REVISION {
407 if p2 != NULL_REVISION {
408 // Retrieve data computed in a previous iteration
408 // Retrieve data computed in a previous iteration
409 let parent_copies = get_and_clean_parent_copies(
409 let parent_copies = get_and_clean_parent_copies(
410 &mut all_copies,
410 &mut all_copies,
411 &mut children_count,
411 &mut children_count,
412 p2,
412 p2,
413 );
413 );
414 if let Some(parent_copies) = parent_copies {
414 if let Some(parent_copies) = parent_copies {
415 // combine it with data for that revision
415 // combine it with data for that revision
416 let vertex_copies = add_from_changes(
416 let vertex_copies = add_from_changes(
417 &mut path_map,
417 &mut path_map,
418 &mut oracle,
418 &mut oracle,
419 &parent_copies,
419 &parent_copies,
420 &changes,
420 &changes,
421 Parent::SecondParent,
421 Parent::SecondParent,
422 rev,
422 rev,
423 );
423 );
424
424
425 copies = match copies {
425 copies = match copies {
426 None => Some(vertex_copies),
426 None => Some(vertex_copies),
427 // Merge has two parents needs to combines their copy
427 // Merge has two parents needs to combines their copy
428 // information.
428 // information.
429 //
429 //
430 // If we got data from both parents, We need to combine
430 // If we got data from both parents, We need to combine
431 // them.
431 // them.
432 Some(copies) => Some(merge_copies_dict(
432 Some(copies) => Some(merge_copies_dict(
433 &path_map,
433 &path_map,
434 rev,
434 rev,
435 vertex_copies,
435 vertex_copies,
436 copies,
436 copies,
437 &changes,
437 &changes,
438 &mut oracle,
438 &mut oracle,
439 )),
439 )),
440 };
440 };
441 }
441 }
442 }
442 }
443 match copies {
443 match copies {
444 Some(copies) => {
444 Some(copies) => {
445 all_copies.insert(rev, copies);
445 all_copies.insert(rev, copies);
446 }
446 }
447 _ => {}
447 _ => {}
448 }
448 }
449 }
449 }
450
450
451 // Drop internal information (like the timestamp) and return the final
451 // Drop internal information (like the timestamp) and return the final
452 // mapping.
452 // mapping.
453 let tt_result = all_copies
453 let tt_result = all_copies
454 .remove(&target_rev)
454 .remove(&target_rev)
455 .expect("target revision was not processed");
455 .expect("target revision was not processed");
456 let mut result = PathCopies::default();
456 let mut result = PathCopies::default();
457 for (dest, tt_source) in tt_result {
457 for (dest, tt_source) in tt_result {
458 if let Some(path) = tt_source.path {
458 if let Some(path) = tt_source.path {
459 let path_dest = path_map.untokenize(dest).to_owned();
459 let path_dest = path_map.untokenize(dest).to_owned();
460 let path_path = path_map.untokenize(path).to_owned();
460 let path_path = path_map.untokenize(path).to_owned();
461 result.insert(path_dest, path_path);
461 result.insert(path_dest, path_path);
462 }
462 }
463 }
463 }
464 result
464 result
465 }
465 }
466
466
467 /// fetch previous computed information
467 /// fetch previous computed information
468 ///
468 ///
469 /// If no other children are expected to need this information, we drop it from
469 /// If no other children are expected to need this information, we drop it from
470 /// the cache.
470 /// the cache.
471 ///
471 ///
472 /// If parent is not part of the set we are expected to walk, return None.
472 /// If parent is not part of the set we are expected to walk, return None.
473 fn get_and_clean_parent_copies(
473 fn get_and_clean_parent_copies(
474 all_copies: &mut HashMap<Revision, TimeStampedPathCopies>,
474 all_copies: &mut HashMap<Revision, TimeStampedPathCopies>,
475 children_count: &mut HashMap<Revision, usize>,
475 children_count: &mut HashMap<Revision, usize>,
476 parent_rev: Revision,
476 parent_rev: Revision,
477 ) -> Option<TimeStampedPathCopies> {
477 ) -> Option<TimeStampedPathCopies> {
478 let count = children_count.get_mut(&parent_rev)?;
478 let count = children_count.get_mut(&parent_rev)?;
479 *count -= 1;
479 *count -= 1;
480 if *count == 0 {
480 if *count == 0 {
481 match all_copies.remove(&parent_rev) {
481 match all_copies.remove(&parent_rev) {
482 Some(c) => Some(c),
482 Some(c) => Some(c),
483 None => Some(TimeStampedPathCopies::default()),
483 None => Some(TimeStampedPathCopies::default()),
484 }
484 }
485 } else {
485 } else {
486 match all_copies.get(&parent_rev) {
486 match all_copies.get(&parent_rev) {
487 Some(c) => Some(c.clone()),
487 Some(c) => Some(c.clone()),
488 None => Some(TimeStampedPathCopies::default()),
488 None => Some(TimeStampedPathCopies::default()),
489 }
489 }
490 }
490 }
491 }
491 }
492
492
493 /// Combine ChangedFiles with some existing PathCopies information and return
493 /// Combine ChangedFiles with some existing PathCopies information and return
494 /// the result
494 /// the result
495 fn add_from_changes<A: Fn(Revision, Revision) -> bool>(
495 fn add_from_changes<A: Fn(Revision, Revision) -> bool>(
496 path_map: &mut TwoWayPathMap,
496 path_map: &mut TwoWayPathMap,
497 oracle: &mut AncestorOracle<A>,
497 oracle: &mut AncestorOracle<A>,
498 base_copies: &TimeStampedPathCopies,
498 base_copies: &TimeStampedPathCopies,
499 changes: &ChangedFiles,
499 changes: &ChangedFiles,
500 parent: Parent,
500 parent: Parent,
501 current_rev: Revision,
501 current_rev: Revision,
502 ) -> TimeStampedPathCopies {
502 ) -> TimeStampedPathCopies {
503 let mut copies = base_copies.clone();
503 let mut copies = base_copies.clone();
504 for action in changes.iter_actions(parent) {
504 for action in changes.iter_actions(parent) {
505 match action {
505 match action {
506 Action::Copied(path_dest, path_source) => {
506 Action::Copied(path_dest, path_source) => {
507 let dest = path_map.tokenize(path_dest);
507 let dest = path_map.tokenize(path_dest);
508 let source = path_map.tokenize(path_source);
508 let source = path_map.tokenize(path_source);
509 let entry;
509 let entry;
510 if let Some(v) = base_copies.get(&source) {
510 if let Some(v) = base_copies.get(&source) {
511 entry = match &v.path {
511 entry = match &v.path {
512 Some(path) => Some((*(path)).to_owned()),
512 Some(path) => Some((*(path)).to_owned()),
513 None => Some(source.to_owned()),
513 None => Some(source.to_owned()),
514 }
514 }
515 } else {
515 } else {
516 entry = Some(source.to_owned());
516 entry = Some(source.to_owned());
517 }
517 }
518 // Each new entry is introduced by the children, we
518 // Each new entry is introduced by the children, we
519 // record this information as we will need it to take
519 // record this information as we will need it to take
520 // the right decision when merging conflicting copy
520 // the right decision when merging conflicting copy
521 // information. See merge_copies_dict for details.
521 // information. See merge_copies_dict for details.
522 match copies.entry(dest) {
522 match copies.entry(dest) {
523 Entry::Vacant(slot) => {
523 Entry::Vacant(slot) => {
524 let ttpc = TimeStampedPathCopy {
524 let ttpc = TimeStampedPathCopy {
525 rev: current_rev,
525 rev: current_rev,
526 path: entry,
526 path: entry,
527 };
527 };
528 slot.insert(ttpc);
528 slot.insert(ttpc);
529 }
529 }
530 Entry::Occupied(mut slot) => {
530 Entry::Occupied(mut slot) => {
531 let mut ttpc = slot.get_mut();
531 let mut ttpc = slot.get_mut();
532 oracle.record_overwrite(ttpc.rev, current_rev);
532 oracle.record_overwrite(ttpc.rev, current_rev);
533 ttpc.rev = current_rev;
533 ttpc.rev = current_rev;
534 ttpc.path = entry;
534 ttpc.path = entry;
535 }
535 }
536 }
536 }
537 }
537 }
538 Action::Removed(deleted_path) => {
538 Action::Removed(deleted_path) => {
539 // We must drop copy information for removed file.
539 // We must drop copy information for removed file.
540 //
540 //
541 // We need to explicitly record them as dropped to
541 // We need to explicitly record them as dropped to
542 // propagate this information when merging two
542 // propagate this information when merging two
543 // TimeStampedPathCopies object.
543 // TimeStampedPathCopies object.
544 let deleted = path_map.tokenize(deleted_path);
544 let deleted = path_map.tokenize(deleted_path);
545 copies.entry(deleted).and_modify(|old| {
545 copies.entry(deleted).and_modify(|old| {
546 oracle.record_overwrite(old.rev, current_rev);
546 oracle.record_overwrite(old.rev, current_rev);
547 old.rev = current_rev;
547 old.rev = current_rev;
548 old.path = None;
548 old.path = None;
549 });
549 });
550 }
550 }
551 }
551 }
552 }
552 }
553 copies
553 copies
554 }
554 }
555
555
556 /// merge two copies-mapping together, minor and major
556 /// merge two copies-mapping together, minor and major
557 ///
557 ///
558 /// In case of conflict, value from "major" will be picked, unless in some
558 /// In case of conflict, value from "major" will be picked, unless in some
559 /// cases. See inline documentation for details.
559 /// cases. See inline documentation for details.
560 fn merge_copies_dict<A: Fn(Revision, Revision) -> bool>(
560 fn merge_copies_dict<A: Fn(Revision, Revision) -> bool>(
561 path_map: &TwoWayPathMap,
561 path_map: &TwoWayPathMap,
562 current_merge: Revision,
562 current_merge: Revision,
563 mut minor: TimeStampedPathCopies,
563 mut minor: TimeStampedPathCopies,
564 mut major: TimeStampedPathCopies,
564 mut major: TimeStampedPathCopies,
565 changes: &ChangedFiles,
565 changes: &ChangedFiles,
566 oracle: &mut AncestorOracle<A>,
566 oracle: &mut AncestorOracle<A>,
567 ) -> TimeStampedPathCopies {
567 ) -> TimeStampedPathCopies {
568 // This closure exist as temporary help while multiple developper are
568 // This closure exist as temporary help while multiple developper are
569 // actively working on this code. Feel free to re-inline it once this
569 // actively working on this code. Feel free to re-inline it once this
570 // code is more settled.
570 // code is more settled.
571 let mut cmp_value =
571 let cmp_value = |oracle: &mut AncestorOracle<A>,
572 |dest: &PathToken,
572 dest: &PathToken,
573 src_minor: &TimeStampedPathCopy,
573 src_minor: &TimeStampedPathCopy,
574 src_major: &TimeStampedPathCopy| {
574 src_major: &TimeStampedPathCopy| {
575 compare_value(
575 compare_value(
576 path_map,
576 path_map,
577 current_merge,
577 current_merge,
578 changes,
578 changes,
579 oracle,
579 oracle,
580 dest,
580 dest,
581 src_minor,
581 src_minor,
582 src_major,
582 src_major,
583 )
583 )
584 };
584 };
585 if minor.is_empty() {
585 if minor.is_empty() {
586 major
586 major
587 } else if major.is_empty() {
587 } else if major.is_empty() {
588 minor
588 minor
589 } else if minor.len() * 2 < major.len() {
589 } else if minor.len() * 2 < major.len() {
590 // Lets says we are merging two TimeStampedPathCopies instance A and B.
590 // Lets says we are merging two TimeStampedPathCopies instance A and B.
591 //
591 //
592 // If A contains N items, the merge result will never contains more
592 // If A contains N items, the merge result will never contains more
593 // than N values differents than the one in A
593 // than N values differents than the one in A
594 //
594 //
595 // If B contains M items, with M > N, the merge result will always
595 // If B contains M items, with M > N, the merge result will always
596 // result in a minimum of M - N value differents than the on in
596 // result in a minimum of M - N value differents than the on in
597 // A
597 // A
598 //
598 //
599 // As a result, if N < (M-N), we know that simply iterating over A will
599 // As a result, if N < (M-N), we know that simply iterating over A will
600 // yield less difference than iterating over the difference
600 // yield less difference than iterating over the difference
601 // between A and B.
601 // between A and B.
602 //
602 //
603 // This help performance a lot in case were a tiny
603 // This help performance a lot in case were a tiny
604 // TimeStampedPathCopies is merged with a much larger one.
604 // TimeStampedPathCopies is merged with a much larger one.
605 for (dest, src_minor) in minor {
605 for (dest, src_minor) in minor {
606 let src_major = major.get(&dest);
606 let src_major = major.get(&dest);
607 match src_major {
607 match src_major {
608 None => major.insert(dest, src_minor),
608 None => {
609 major.insert(dest, src_minor);
610 }
609 Some(src_major) => {
611 Some(src_major) => {
610 match cmp_value(&dest, &src_minor, src_major) {
612 let (pick, overwrite) =
611 MergePick::Any | MergePick::Major => None,
613 cmp_value(oracle, &dest, &src_minor, src_major);
612 MergePick::Minor => major.insert(dest, src_minor),
614 if overwrite {
615 oracle.record_overwrite(src_minor.rev, current_merge);
616 oracle.record_overwrite(src_major.rev, current_merge);
617 let path = match pick {
618 MergePick::Major => src_major.path,
619 MergePick::Minor => src_minor.path,
620 MergePick::Any => src_major.path,
621 };
622 let src = TimeStampedPathCopy {
623 rev: current_merge,
624 path,
625 };
626 major.insert(dest, src);
627 } else {
628 match pick {
629 MergePick::Any | MergePick::Major => None,
630 MergePick::Minor => major.insert(dest, src_minor),
631 };
613 }
632 }
614 }
633 }
615 };
634 };
616 }
635 }
617 major
636 major
618 } else if major.len() * 2 < minor.len() {
637 } else if major.len() * 2 < minor.len() {
619 // This use the same rational than the previous block.
638 // This use the same rational than the previous block.
620 // (Check previous block documentation for details.)
639 // (Check previous block documentation for details.)
621 for (dest, src_major) in major {
640 for (dest, src_major) in major {
622 let src_minor = minor.get(&dest);
641 let src_minor = minor.get(&dest);
623 match src_minor {
642 match src_minor {
624 None => minor.insert(dest, src_major),
643 None => {
644 minor.insert(dest, src_major);
645 }
625 Some(src_minor) => {
646 Some(src_minor) => {
626 match cmp_value(&dest, src_minor, &src_major) {
647 let (pick, overwrite) =
627 MergePick::Any | MergePick::Minor => None,
648 cmp_value(oracle, &dest, &src_major, src_minor);
628 MergePick::Major => minor.insert(dest, src_major),
649 if overwrite {
650 oracle.record_overwrite(src_minor.rev, current_merge);
651 oracle.record_overwrite(src_major.rev, current_merge);
652 let path = match pick {
653 MergePick::Major => src_minor.path,
654 MergePick::Minor => src_major.path,
655 MergePick::Any => src_major.path,
656 };
657 let src = TimeStampedPathCopy {
658 rev: current_merge,
659 path,
660 };
661 minor.insert(dest, src);
662 } else {
663 match pick {
664 MergePick::Any | MergePick::Major => None,
665 MergePick::Minor => minor.insert(dest, src_major),
666 };
629 }
667 }
630 }
668 }
631 };
669 };
632 }
670 }
633 minor
671 minor
634 } else {
672 } else {
635 let mut override_minor = Vec::new();
673 let mut override_minor = Vec::new();
636 let mut override_major = Vec::new();
674 let mut override_major = Vec::new();
637
675
638 let mut to_major = |k: &PathToken, v: &TimeStampedPathCopy| {
676 let mut to_major = |k: &PathToken, v: &TimeStampedPathCopy| {
639 override_major.push((k.clone(), v.clone()))
677 override_major.push((k.clone(), v.clone()))
640 };
678 };
641 let mut to_minor = |k: &PathToken, v: &TimeStampedPathCopy| {
679 let mut to_minor = |k: &PathToken, v: &TimeStampedPathCopy| {
642 override_minor.push((k.clone(), v.clone()))
680 override_minor.push((k.clone(), v.clone()))
643 };
681 };
644
682
645 // The diff function leverage detection of the identical subpart if
683 // The diff function leverage detection of the identical subpart if
646 // minor and major has some common ancestors. This make it very
684 // minor and major has some common ancestors. This make it very
647 // fast is most case.
685 // fast is most case.
648 //
686 //
649 // In case where the two map are vastly different in size, the current
687 // In case where the two map are vastly different in size, the current
650 // approach is still slowish because the iteration will iterate over
688 // approach is still slowish because the iteration will iterate over
651 // all the "exclusive" content of the larger on. This situation can be
689 // all the "exclusive" content of the larger on. This situation can be
652 // frequent when the subgraph of revision we are processing has a lot
690 // frequent when the subgraph of revision we are processing has a lot
653 // of roots. Each roots adding they own fully new map to the mix (and
691 // of roots. Each roots adding they own fully new map to the mix (and
654 // likely a small map, if the path from the root to the "main path" is
692 // likely a small map, if the path from the root to the "main path" is
655 // small.
693 // small.
656 //
694 //
657 // We could do better by detecting such situation and processing them
695 // We could do better by detecting such situation and processing them
658 // differently.
696 // differently.
659 for d in minor.diff(&major) {
697 for d in minor.diff(&major) {
660 match d {
698 match d {
661 DiffItem::Add(k, v) => to_minor(k, v),
699 DiffItem::Add(k, v) => to_minor(k, v),
662 DiffItem::Remove(k, v) => to_major(k, v),
700 DiffItem::Remove(k, v) => to_major(k, v),
663 DiffItem::Update { old, new } => {
701 DiffItem::Update { old, new } => {
664 let (dest, src_major) = new;
702 let (dest, src_major) = new;
665 let (_, src_minor) = old;
703 let (_, src_minor) = old;
666 match cmp_value(dest, src_minor, src_major) {
704 let (pick, overwrite) =
667 MergePick::Major => to_minor(dest, src_major),
705 cmp_value(oracle, dest, src_minor, src_major);
668 MergePick::Minor => to_major(dest, src_minor),
706 if overwrite {
669 // If the two entry are identical, no need to do
707 oracle.record_overwrite(src_minor.rev, current_merge);
670 // anything (but diff should not have yield them)
708 oracle.record_overwrite(src_major.rev, current_merge);
671 MergePick::Any => unreachable!(),
709 let path = match pick {
710 MergePick::Major => src_major.path,
711 MergePick::Minor => src_minor.path,
712 // If the two entry are identical, no need to do
713 // anything (but diff should not have yield them)
714 MergePick::Any => src_major.path,
715 };
716 let src = TimeStampedPathCopy {
717 rev: current_merge,
718 path,
719 };
720 to_minor(dest, &src);
721 to_major(dest, &src);
722 } else {
723 match pick {
724 MergePick::Major => to_minor(dest, src_major),
725 MergePick::Minor => to_major(dest, src_minor),
726 // If the two entry are identical, no need to do
727 // anything (but diff should not have yield them)
728 MergePick::Any => unreachable!(),
729 }
672 }
730 }
673 }
731 }
674 };
732 };
675 }
733 }
676
734
677 let updates;
735 let updates;
678 let mut result;
736 let mut result;
679 if override_major.is_empty() {
737 if override_major.is_empty() {
680 result = major
738 result = major
681 } else if override_minor.is_empty() {
739 } else if override_minor.is_empty() {
682 result = minor
740 result = minor
683 } else {
741 } else {
684 if override_minor.len() < override_major.len() {
742 if override_minor.len() < override_major.len() {
685 updates = override_minor;
743 updates = override_minor;
686 result = minor;
744 result = minor;
687 } else {
745 } else {
688 updates = override_major;
746 updates = override_major;
689 result = major;
747 result = major;
690 }
748 }
691 for (k, v) in updates {
749 for (k, v) in updates {
692 result.insert(k, v);
750 result.insert(k, v);
693 }
751 }
694 }
752 }
695 result
753 result
696 }
754 }
697 }
755 }
698
756
699 /// represent the side that should prevail when merging two
757 /// represent the side that should prevail when merging two
700 /// TimeStampedPathCopies
758 /// TimeStampedPathCopies
701 enum MergePick {
759 enum MergePick {
702 /// The "major" (p1) side prevails
760 /// The "major" (p1) side prevails
703 Major,
761 Major,
704 /// The "minor" (p2) side prevails
762 /// The "minor" (p2) side prevails
705 Minor,
763 Minor,
706 /// Any side could be used (because they are the same)
764 /// Any side could be used (because they are the same)
707 Any,
765 Any,
708 }
766 }
709
767
710 /// decide which side prevails in case of conflicting values
768 /// decide which side prevails in case of conflicting values
711 #[allow(clippy::if_same_then_else)]
769 #[allow(clippy::if_same_then_else)]
712 fn compare_value<A: Fn(Revision, Revision) -> bool>(
770 fn compare_value<A: Fn(Revision, Revision) -> bool>(
713 path_map: &TwoWayPathMap,
771 path_map: &TwoWayPathMap,
714 current_merge: Revision,
772 current_merge: Revision,
715 changes: &ChangedFiles,
773 changes: &ChangedFiles,
716 oracle: &mut AncestorOracle<A>,
774 oracle: &mut AncestorOracle<A>,
717 dest: &PathToken,
775 dest: &PathToken,
718 src_minor: &TimeStampedPathCopy,
776 src_minor: &TimeStampedPathCopy,
719 src_major: &TimeStampedPathCopy,
777 src_major: &TimeStampedPathCopy,
720 ) -> MergePick {
778 ) -> (MergePick, bool) {
721 if src_major.rev == current_merge {
779 if src_major.rev == current_merge {
722 if src_minor.rev == current_merge {
780 if src_minor.rev == current_merge {
723 if src_major.path.is_none() {
781 if src_major.path.is_none() {
724 // We cannot get different copy information for both p1 and p2
782 // We cannot get different copy information for both p1 and p2
725 // from the same revision. Unless this was a
783 // from the same revision. Unless this was a
726 // deletion
784 // deletion
727 MergePick::Any
785 (MergePick::Any, false)
728 } else {
786 } else {
729 unreachable!();
787 unreachable!();
730 }
788 }
731 } else {
789 } else {
732 // The last value comes the current merge, this value -will- win
790 // The last value comes the current merge, this value -will- win
733 // eventually.
791 // eventually.
734 oracle.record_overwrite(src_minor.rev, src_major.rev);
792 (MergePick::Major, true)
735 MergePick::Major
736 }
793 }
737 } else if src_minor.rev == current_merge {
794 } else if src_minor.rev == current_merge {
738 // The last value comes the current merge, this value -will- win
795 // The last value comes the current merge, this value -will- win
739 // eventually.
796 // eventually.
740 oracle.record_overwrite(src_major.rev, src_minor.rev);
797 (MergePick::Minor, true)
741 MergePick::Minor
742 } else if src_major.path == src_minor.path {
798 } else if src_major.path == src_minor.path {
743 // we have the same value, but from other source;
799 // we have the same value, but from other source;
744 if src_major.rev == src_minor.rev {
800 if src_major.rev == src_minor.rev {
745 // If the two entry are identical, they are both valid
801 // If the two entry are identical, they are both valid
746 MergePick::Any
802 (MergePick::Any, false)
747 } else if oracle.is_overwrite(src_major.rev, src_minor.rev) {
803 } else if oracle.is_overwrite(src_major.rev, src_minor.rev) {
748 MergePick::Minor
804 (MergePick::Minor, false)
749 } else if oracle.is_overwrite(src_minor.rev, src_major.rev) {
805 } else if oracle.is_overwrite(src_minor.rev, src_major.rev) {
750 MergePick::Major
806 (MergePick::Major, false)
751 } else {
807 } else {
752 MergePick::Major
808 (MergePick::Any, true)
753 }
809 }
754 } else if src_major.rev == src_minor.rev {
810 } else if src_major.rev == src_minor.rev {
755 // We cannot get copy information for both p1 and p2 in the
811 // We cannot get copy information for both p1 and p2 in the
756 // same rev. So this is the same value.
812 // same rev. So this is the same value.
757 unreachable!(
813 unreachable!(
758 "conflicting information from p1 and p2 in the same revision"
814 "conflicting information from p1 and p2 in the same revision"
759 );
815 );
760 } else {
816 } else {
761 let dest_path = path_map.untokenize(*dest);
817 let dest_path = path_map.untokenize(*dest);
762 let action = changes.get_merge_case(dest_path);
818 let action = changes.get_merge_case(dest_path);
763 if src_minor.path.is_some()
819 if src_minor.path.is_some()
764 && src_major.path.is_none()
820 && src_major.path.is_none()
765 && action == MergeCase::Salvaged
821 && action == MergeCase::Salvaged
766 {
822 {
767 // If the file is "deleted" in the major side but was
823 // If the file is "deleted" in the major side but was
768 // salvaged by the merge, we keep the minor side alive
824 // salvaged by the merge, we keep the minor side alive
769 MergePick::Minor
825 (MergePick::Minor, true)
770 } else if src_major.path.is_some()
826 } else if src_major.path.is_some()
771 && src_minor.path.is_none()
827 && src_minor.path.is_none()
772 && action == MergeCase::Salvaged
828 && action == MergeCase::Salvaged
773 {
829 {
774 // If the file is "deleted" in the minor side but was
830 // If the file is "deleted" in the minor side but was
775 // salvaged by the merge, unconditionnaly preserve the
831 // salvaged by the merge, unconditionnaly preserve the
776 // major side.
832 // major side.
777 MergePick::Major
833 (MergePick::Major, true)
778 } else if oracle.is_overwrite(src_minor.rev, src_major.rev) {
834 } else if oracle.is_overwrite(src_minor.rev, src_major.rev) {
779 // The information from the minor version are strictly older than
835 // The information from the minor version are strictly older than
780 // the major version
836 // the major version
781 if action == MergeCase::Merged {
837 if action == MergeCase::Merged {
782 // If the file was actively merged, its means some non-copy
838 // If the file was actively merged, its means some non-copy
783 // activity happened on the other branch. It
839 // activity happened on the other branch. It
784 // mean the older copy information are still relevant.
840 // mean the older copy information are still relevant.
785 //
841 //
786 // The major side wins such conflict.
842 // The major side wins such conflict.
787 MergePick::Major
843 (MergePick::Major, true)
788 } else {
844 } else {
789 // No activity on the minor branch, pick the newer one.
845 // No activity on the minor branch, pick the newer one.
790 MergePick::Major
846 (MergePick::Major, false)
791 }
847 }
792 } else if oracle.is_overwrite(src_major.rev, src_minor.rev) {
848 } else if oracle.is_overwrite(src_major.rev, src_minor.rev) {
793 if action == MergeCase::Merged {
849 if action == MergeCase::Merged {
794 // If the file was actively merged, its means some non-copy
850 // If the file was actively merged, its means some non-copy
795 // activity happened on the other branch. It
851 // activity happened on the other branch. It
796 // mean the older copy information are still relevant.
852 // mean the older copy information are still relevant.
797 //
853 //
798 // The major side wins such conflict.
854 // The major side wins such conflict.
799 MergePick::Major
855 (MergePick::Major, true)
800 } else {
856 } else {
801 // No activity on the minor branch, pick the newer one.
857 // No activity on the minor branch, pick the newer one.
802 MergePick::Minor
858 (MergePick::Minor, false)
803 }
859 }
804 } else if src_minor.path.is_none() {
860 } else if src_minor.path.is_none() {
805 // the minor side has no relevant information, pick the alive one
861 // the minor side has no relevant information, pick the alive one
806 MergePick::Major
862 (MergePick::Major, true)
807 } else if src_major.path.is_none() {
863 } else if src_major.path.is_none() {
808 // the major side has no relevant information, pick the alive one
864 // the major side has no relevant information, pick the alive one
809 MergePick::Minor
865 (MergePick::Minor, true)
810 } else {
866 } else {
811 // by default the major side wins
867 // by default the major side wins
812 MergePick::Major
868 (MergePick::Major, true)
813 }
869 }
814 }
870 }
815 }
871 }
@@ -1,3441 +1,3406 b''
1 #testcases filelog compatibility changeset sidedata upgraded
1 #testcases filelog compatibility changeset sidedata upgraded
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 >> ./no-linkrev
17 $ cat << EOF >> ./no-linkrev
18 > #!$PYTHON
18 > #!$PYTHON
19 > # filter out linkrev part of the debugindex command
19 > # filter out linkrev part of the debugindex command
20 > import sys
20 > import sys
21 > for line in sys.stdin:
21 > for line in sys.stdin:
22 > if " linkrev " in line:
22 > if " linkrev " in line:
23 > print(line.rstrip())
23 > print(line.rstrip())
24 > else:
24 > else:
25 > l = "%s *%s" % (line[:6], line[14:].rstrip())
25 > l = "%s *%s" % (line[:6], line[14:].rstrip())
26 > print(l)
26 > print(l)
27 > EOF
27 > EOF
28 $ chmod +x no-linkrev
28 $ chmod +x no-linkrev
29
29
30 $ cat << EOF >> $HGRCPATH
30 $ cat << EOF >> $HGRCPATH
31 > [diff]
31 > [diff]
32 > git=yes
32 > git=yes
33 > [command-templates]
33 > [command-templates]
34 > log={desc}\n
34 > log={desc}\n
35 > EOF
35 > EOF
36
36
37 #if compatibility
37 #if compatibility
38 $ cat >> $HGRCPATH << EOF
38 $ cat >> $HGRCPATH << EOF
39 > [experimental]
39 > [experimental]
40 > copies.read-from = compatibility
40 > copies.read-from = compatibility
41 > EOF
41 > EOF
42 #endif
42 #endif
43
43
44 #if changeset
44 #if changeset
45 $ cat >> $HGRCPATH << EOF
45 $ cat >> $HGRCPATH << EOF
46 > [experimental]
46 > [experimental]
47 > copies.read-from = changeset-only
47 > copies.read-from = changeset-only
48 > copies.write-to = changeset-only
48 > copies.write-to = changeset-only
49 > EOF
49 > EOF
50 #endif
50 #endif
51
51
52 #if sidedata
52 #if sidedata
53 $ cat >> $HGRCPATH << EOF
53 $ cat >> $HGRCPATH << EOF
54 > [format]
54 > [format]
55 > exp-use-side-data = yes
55 > exp-use-side-data = yes
56 > exp-use-copies-side-data-changeset = yes
56 > exp-use-copies-side-data-changeset = yes
57 > EOF
57 > EOF
58 #endif
58 #endif
59
59
60
60
61 $ cat > same-content.txt << EOF
61 $ cat > same-content.txt << EOF
62 > Here is some content that will be the same accros multiple file.
62 > Here is some content that will be the same accros multiple file.
63 >
63 >
64 > This is done on purpose so that we end up in some merge situation, were the
64 > This is done on purpose so that we end up in some merge situation, were the
65 > resulting content is the same as in the parent(s), but a new filenodes still
65 > resulting content is the same as in the parent(s), but a new filenodes still
66 > need to be created to record some file history information (especially
66 > need to be created to record some file history information (especially
67 > about copies).
67 > about copies).
68 > EOF
68 > EOF
69
69
70 $ hg init repo-chain
70 $ hg init repo-chain
71 $ cd repo-chain
71 $ cd repo-chain
72
72
73 Add some linear rename initialy
73 Add some linear rename initialy
74
74
75 $ cp ../same-content.txt a
75 $ cp ../same-content.txt a
76 $ cp ../same-content.txt b
76 $ cp ../same-content.txt b
77 $ cp ../same-content.txt h
77 $ cp ../same-content.txt h
78 $ echo "original content for P" > p
78 $ echo "original content for P" > p
79 $ echo "original content for Q" > q
79 $ echo "original content for Q" > q
80 $ echo "original content for R" > r
80 $ echo "original content for R" > r
81 $ hg ci -Am 'i-0 initial commit: a b h'
81 $ hg ci -Am 'i-0 initial commit: a b h'
82 adding a
82 adding a
83 adding b
83 adding b
84 adding h
84 adding h
85 adding p
85 adding p
86 adding q
86 adding q
87 adding r
87 adding r
88 $ hg mv a c
88 $ hg mv a c
89 $ hg mv p s
89 $ hg mv p s
90 $ hg ci -Am 'i-1: a -move-> c, p -move-> s'
90 $ hg ci -Am 'i-1: a -move-> c, p -move-> s'
91 $ hg mv c d
91 $ hg mv c d
92 $ hg mv s t
92 $ hg mv s t
93 $ hg ci -Am 'i-2: c -move-> d, s -move-> t'
93 $ hg ci -Am 'i-2: c -move-> d, s -move-> t'
94 $ hg log -G
94 $ hg log -G
95 @ i-2: c -move-> d, s -move-> t
95 @ i-2: c -move-> d, s -move-> t
96 |
96 |
97 o i-1: a -move-> c, p -move-> s
97 o i-1: a -move-> c, p -move-> s
98 |
98 |
99 o i-0 initial commit: a b h
99 o i-0 initial commit: a b h
100
100
101
101
102 And having another branch with renames on the other side
102 And having another branch with renames on the other side
103
103
104 $ hg mv d e
104 $ hg mv d e
105 $ hg ci -Am 'a-1: d -move-> e'
105 $ hg ci -Am 'a-1: d -move-> e'
106 $ hg mv e f
106 $ hg mv e f
107 $ hg ci -Am 'a-2: e -move-> f'
107 $ hg ci -Am 'a-2: e -move-> f'
108 $ hg log -G --rev '::.'
108 $ hg log -G --rev '::.'
109 @ a-2: e -move-> f
109 @ a-2: e -move-> f
110 |
110 |
111 o a-1: d -move-> e
111 o a-1: d -move-> e
112 |
112 |
113 o i-2: c -move-> d, s -move-> t
113 o i-2: c -move-> d, s -move-> t
114 |
114 |
115 o i-1: a -move-> c, p -move-> s
115 o i-1: a -move-> c, p -move-> s
116 |
116 |
117 o i-0 initial commit: a b h
117 o i-0 initial commit: a b h
118
118
119
119
120 Have a branching with nothing on one side
120 Have a branching with nothing on one side
121
121
122 $ hg up 'desc("i-2")'
122 $ hg up 'desc("i-2")'
123 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
123 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
124 $ echo foo > b
124 $ echo foo > b
125 $ hg ci -m 'b-1: b update'
125 $ hg ci -m 'b-1: b update'
126 created new head
126 created new head
127 $ hg log -G --rev '::.'
127 $ hg log -G --rev '::.'
128 @ b-1: b update
128 @ b-1: b update
129 |
129 |
130 o i-2: c -move-> d, s -move-> t
130 o i-2: c -move-> d, s -move-> t
131 |
131 |
132 o i-1: a -move-> c, p -move-> s
132 o i-1: a -move-> c, p -move-> s
133 |
133 |
134 o i-0 initial commit: a b h
134 o i-0 initial commit: a b h
135
135
136
136
137 Create a branch that delete a file previous renamed
137 Create a branch that delete a file previous renamed
138
138
139 $ hg up 'desc("i-2")'
139 $ hg up 'desc("i-2")'
140 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
140 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
141 $ hg rm d
141 $ hg rm d
142 $ hg ci -m 'c-1 delete d'
142 $ hg ci -m 'c-1 delete d'
143 created new head
143 created new head
144 $ hg log -G --rev '::.'
144 $ hg log -G --rev '::.'
145 @ c-1 delete d
145 @ c-1 delete d
146 |
146 |
147 o i-2: c -move-> d, s -move-> t
147 o i-2: c -move-> d, s -move-> t
148 |
148 |
149 o i-1: a -move-> c, p -move-> s
149 o i-1: a -move-> c, p -move-> s
150 |
150 |
151 o i-0 initial commit: a b h
151 o i-0 initial commit: a b h
152
152
153
153
154 Create a branch that delete a file previous renamed and recreate it
154 Create a branch that delete a file previous renamed and recreate it
155
155
156 $ hg up 'desc("i-2")'
156 $ hg up 'desc("i-2")'
157 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
157 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
158 $ hg rm d
158 $ hg rm d
159 $ hg ci -m 'd-1 delete d'
159 $ hg ci -m 'd-1 delete d'
160 created new head
160 created new head
161 $ echo bar > d
161 $ echo bar > d
162 $ hg add d
162 $ hg add d
163 $ hg ci -m 'd-2 re-add d'
163 $ hg ci -m 'd-2 re-add d'
164 $ hg log -G --rev '::.'
164 $ hg log -G --rev '::.'
165 @ d-2 re-add d
165 @ d-2 re-add d
166 |
166 |
167 o d-1 delete d
167 o d-1 delete d
168 |
168 |
169 o i-2: c -move-> d, s -move-> t
169 o i-2: c -move-> d, s -move-> t
170 |
170 |
171 o i-1: a -move-> c, p -move-> s
171 o i-1: a -move-> c, p -move-> s
172 |
172 |
173 o i-0 initial commit: a b h
173 o i-0 initial commit: a b h
174
174
175
175
176 Having another branch renaming a different file to the same filename as another
176 Having another branch renaming a different file to the same filename as another
177
177
178 $ hg up 'desc("i-2")'
178 $ hg up 'desc("i-2")'
179 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
179 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
180 $ hg mv b g
180 $ hg mv b g
181 $ hg ci -m 'e-1 b -move-> g'
181 $ hg ci -m 'e-1 b -move-> g'
182 created new head
182 created new head
183 $ hg mv g f
183 $ hg mv g f
184 $ hg ci -m 'e-2 g -move-> f'
184 $ hg ci -m 'e-2 g -move-> f'
185 $ hg log -G --rev '::.'
185 $ hg log -G --rev '::.'
186 @ e-2 g -move-> f
186 @ e-2 g -move-> f
187 |
187 |
188 o e-1 b -move-> g
188 o e-1 b -move-> g
189 |
189 |
190 o i-2: c -move-> d, s -move-> t
190 o i-2: c -move-> d, s -move-> t
191 |
191 |
192 o i-1: a -move-> c, p -move-> s
192 o i-1: a -move-> c, p -move-> s
193 |
193 |
194 o i-0 initial commit: a b h
194 o i-0 initial commit: a b h
195
195
196 $ hg up -q null
196 $ hg up -q null
197
197
198 Having a branch similar to the 'a' one, but moving the 'p' file around.
198 Having a branch similar to the 'a' one, but moving the 'p' file around.
199
199
200 $ hg up 'desc("i-2")'
200 $ hg up 'desc("i-2")'
201 6 files updated, 0 files merged, 0 files removed, 0 files unresolved
201 6 files updated, 0 files merged, 0 files removed, 0 files unresolved
202 $ hg mv t u
202 $ hg mv t u
203 $ hg ci -Am 'p-1: t -move-> u'
203 $ hg ci -Am 'p-1: t -move-> u'
204 created new head
204 created new head
205 $ hg mv u v
205 $ hg mv u v
206 $ hg ci -Am 'p-2: u -move-> v'
206 $ hg ci -Am 'p-2: u -move-> v'
207 $ hg log -G --rev '::.'
207 $ hg log -G --rev '::.'
208 @ p-2: u -move-> v
208 @ p-2: u -move-> v
209 |
209 |
210 o p-1: t -move-> u
210 o p-1: t -move-> u
211 |
211 |
212 o i-2: c -move-> d, s -move-> t
212 o i-2: c -move-> d, s -move-> t
213 |
213 |
214 o i-1: a -move-> c, p -move-> s
214 o i-1: a -move-> c, p -move-> s
215 |
215 |
216 o i-0 initial commit: a b h
216 o i-0 initial commit: a b h
217
217
218 $ hg up -q null
218 $ hg up -q null
219
219
220 Having another branch renaming a different file to the same filename as another
220 Having another branch renaming a different file to the same filename as another
221
221
222 $ hg up 'desc("i-2")'
222 $ hg up 'desc("i-2")'
223 6 files updated, 0 files merged, 0 files removed, 0 files unresolved
223 6 files updated, 0 files merged, 0 files removed, 0 files unresolved
224 $ hg mv r w
224 $ hg mv r w
225 $ hg ci -m 'q-1 r -move-> w'
225 $ hg ci -m 'q-1 r -move-> w'
226 created new head
226 created new head
227 $ hg mv w v
227 $ hg mv w v
228 $ hg ci -m 'q-2 w -move-> v'
228 $ hg ci -m 'q-2 w -move-> v'
229 $ hg log -G --rev '::.'
229 $ hg log -G --rev '::.'
230 @ q-2 w -move-> v
230 @ q-2 w -move-> v
231 |
231 |
232 o q-1 r -move-> w
232 o q-1 r -move-> w
233 |
233 |
234 o i-2: c -move-> d, s -move-> t
234 o i-2: c -move-> d, s -move-> t
235 |
235 |
236 o i-1: a -move-> c, p -move-> s
236 o i-1: a -move-> c, p -move-> s
237 |
237 |
238 o i-0 initial commit: a b h
238 o i-0 initial commit: a b h
239
239
240 $ hg up -q null
240 $ hg up -q null
241
241
242 Setup all merge
242 Setup all merge
243 ===============
243 ===============
244
244
245 This is done beforehand to validate that the upgrade process creates valid copy
245 This is done beforehand to validate that the upgrade process creates valid copy
246 information.
246 information.
247
247
248 merging with unrelated change does not interfere with the renames
248 merging with unrelated change does not interfere with the renames
249 ---------------------------------------------------------------
249 ---------------------------------------------------------------
250
250
251 - rename on one side
251 - rename on one side
252 - unrelated change on the other side
252 - unrelated change on the other side
253
253
254 $ case_desc="simple merge - A side: multiple renames, B side: unrelated update"
254 $ case_desc="simple merge - A side: multiple renames, B side: unrelated update"
255
255
256 $ hg up 'desc("b-1")'
256 $ hg up 'desc("b-1")'
257 6 files updated, 0 files merged, 0 files removed, 0 files unresolved
257 6 files updated, 0 files merged, 0 files removed, 0 files unresolved
258 $ hg merge 'desc("a-2")'
258 $ hg merge 'desc("a-2")'
259 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
259 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
260 (branch merge, don't forget to commit)
260 (branch merge, don't forget to commit)
261 $ hg ci -m "mBAm-0 $case_desc - one way"
261 $ hg ci -m "mBAm-0 $case_desc - one way"
262 $ hg up 'desc("a-2")'
262 $ hg up 'desc("a-2")'
263 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
263 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
264 $ hg merge 'desc("b-1")'
264 $ hg merge 'desc("b-1")'
265 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
265 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
266 (branch merge, don't forget to commit)
266 (branch merge, don't forget to commit)
267 $ hg ci -m "mABm-0 $case_desc - the other way"
267 $ hg ci -m "mABm-0 $case_desc - the other way"
268 created new head
268 created new head
269 $ hg log -G --rev '::(desc("mABm")+desc("mBAm"))'
269 $ hg log -G --rev '::(desc("mABm")+desc("mBAm"))'
270 @ mABm-0 simple merge - A side: multiple renames, B side: unrelated update - the other way
270 @ mABm-0 simple merge - A side: multiple renames, B side: unrelated update - the other way
271 |\
271 |\
272 +---o mBAm-0 simple merge - A side: multiple renames, B side: unrelated update - one way
272 +---o mBAm-0 simple merge - A side: multiple renames, B side: unrelated update - one way
273 | |/
273 | |/
274 | o b-1: b update
274 | o b-1: b update
275 | |
275 | |
276 o | a-2: e -move-> f
276 o | a-2: e -move-> f
277 | |
277 | |
278 o | a-1: d -move-> e
278 o | a-1: d -move-> e
279 |/
279 |/
280 o i-2: c -move-> d, s -move-> t
280 o i-2: c -move-> d, s -move-> t
281 |
281 |
282 o i-1: a -move-> c, p -move-> s
282 o i-1: a -move-> c, p -move-> s
283 |
283 |
284 o i-0 initial commit: a b h
284 o i-0 initial commit: a b h
285
285
286
286
287
287
288 merging with the side having a delete
288 merging with the side having a delete
289 -------------------------------------
289 -------------------------------------
290
290
291 case summary:
291 case summary:
292 - one with change to an unrelated file
292 - one with change to an unrelated file
293 - one deleting the change
293 - one deleting the change
294 and recreate an unrelated file after the merge
294 and recreate an unrelated file after the merge
295
295
296 $ case_desc="simple merge - C side: delete a file with copies history , B side: unrelated update"
296 $ case_desc="simple merge - C side: delete a file with copies history , B side: unrelated update"
297
297
298 $ hg up 'desc("b-1")'
298 $ hg up 'desc("b-1")'
299 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
299 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
300 $ hg merge 'desc("c-1")'
300 $ hg merge 'desc("c-1")'
301 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
301 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
302 (branch merge, don't forget to commit)
302 (branch merge, don't forget to commit)
303 $ hg ci -m "mBCm-0 $case_desc - one way"
303 $ hg ci -m "mBCm-0 $case_desc - one way"
304 $ echo bar > d
304 $ echo bar > d
305 $ hg add d
305 $ hg add d
306 $ hg ci -m 'mBCm-1 re-add d'
306 $ hg ci -m 'mBCm-1 re-add d'
307 $ hg up 'desc("c-1")'
307 $ hg up 'desc("c-1")'
308 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
308 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
309 $ hg merge 'desc("b-1")'
309 $ hg merge 'desc("b-1")'
310 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
310 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
311 (branch merge, don't forget to commit)
311 (branch merge, don't forget to commit)
312 $ hg ci -m "mCBm-0 $case_desc - the other way"
312 $ hg ci -m "mCBm-0 $case_desc - the other way"
313 created new head
313 created new head
314 $ echo bar > d
314 $ echo bar > d
315 $ hg add d
315 $ hg add d
316 $ hg ci -m 'mCBm-1 re-add d'
316 $ hg ci -m 'mCBm-1 re-add d'
317 $ hg log -G --rev '::(desc("mCBm")+desc("mBCm"))'
317 $ hg log -G --rev '::(desc("mCBm")+desc("mBCm"))'
318 @ mCBm-1 re-add d
318 @ mCBm-1 re-add d
319 |
319 |
320 o mCBm-0 simple merge - C side: delete a file with copies history , B side: unrelated update - the other way
320 o mCBm-0 simple merge - C side: delete a file with copies history , B side: unrelated update - the other way
321 |\
321 |\
322 | | o mBCm-1 re-add d
322 | | o mBCm-1 re-add d
323 | | |
323 | | |
324 +---o mBCm-0 simple merge - C side: delete a file with copies history , B side: unrelated update - one way
324 +---o mBCm-0 simple merge - C side: delete a file with copies history , B side: unrelated update - one way
325 | |/
325 | |/
326 | o c-1 delete d
326 | o c-1 delete d
327 | |
327 | |
328 o | b-1: b update
328 o | b-1: b update
329 |/
329 |/
330 o i-2: c -move-> d, s -move-> t
330 o i-2: c -move-> d, s -move-> t
331 |
331 |
332 o i-1: a -move-> c, p -move-> s
332 o i-1: a -move-> c, p -move-> s
333 |
333 |
334 o i-0 initial commit: a b h
334 o i-0 initial commit: a b h
335
335
336
336
337 Comparing with a merge re-adding the file afterward
337 Comparing with a merge re-adding the file afterward
338 ---------------------------------------------------
338 ---------------------------------------------------
339
339
340 Merge:
340 Merge:
341 - one with change to an unrelated file
341 - one with change to an unrelated file
342 - one deleting and recreating the change
342 - one deleting and recreating the change
343
343
344 $ case_desc="simple merge - B side: unrelated update, D side: delete and recreate a file (with different content)"
344 $ case_desc="simple merge - B side: unrelated update, D side: delete and recreate a file (with different content)"
345
345
346 $ hg up 'desc("b-1")'
346 $ hg up 'desc("b-1")'
347 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
347 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
348 $ hg merge 'desc("d-2")'
348 $ hg merge 'desc("d-2")'
349 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
349 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
350 (branch merge, don't forget to commit)
350 (branch merge, don't forget to commit)
351 $ hg ci -m "mBDm-0 $case_desc - one way"
351 $ hg ci -m "mBDm-0 $case_desc - one way"
352 $ hg up 'desc("d-2")'
352 $ hg up 'desc("d-2")'
353 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
353 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
354 $ hg merge 'desc("b-1")'
354 $ hg merge 'desc("b-1")'
355 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
355 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
356 (branch merge, don't forget to commit)
356 (branch merge, don't forget to commit)
357 $ hg ci -m "mDBm-0 $case_desc - the other way"
357 $ hg ci -m "mDBm-0 $case_desc - the other way"
358 created new head
358 created new head
359 $ hg log -G --rev '::(desc("mDBm")+desc("mBDm"))'
359 $ hg log -G --rev '::(desc("mDBm")+desc("mBDm"))'
360 @ mDBm-0 simple merge - B side: unrelated update, D side: delete and recreate a file (with different content) - the other way
360 @ mDBm-0 simple merge - B side: unrelated update, D side: delete and recreate a file (with different content) - the other way
361 |\
361 |\
362 +---o mBDm-0 simple merge - B side: unrelated update, D side: delete and recreate a file (with different content) - one way
362 +---o mBDm-0 simple merge - B side: unrelated update, D side: delete and recreate a file (with different content) - one way
363 | |/
363 | |/
364 | o d-2 re-add d
364 | o d-2 re-add d
365 | |
365 | |
366 | o d-1 delete d
366 | o d-1 delete d
367 | |
367 | |
368 o | b-1: b update
368 o | b-1: b update
369 |/
369 |/
370 o i-2: c -move-> d, s -move-> t
370 o i-2: c -move-> d, s -move-> t
371 |
371 |
372 o i-1: a -move-> c, p -move-> s
372 o i-1: a -move-> c, p -move-> s
373 |
373 |
374 o i-0 initial commit: a b h
374 o i-0 initial commit: a b h
375
375
376
376
377
377
378 Comparing with a merge with colliding rename
378 Comparing with a merge with colliding rename
379 --------------------------------------------
379 --------------------------------------------
380
380
381 Subcase: new copy information on both side
381 Subcase: new copy information on both side
382 ``````````````````````````````````````````
382 ``````````````````````````````````````````
383
383
384 - the "e-" branch renaming b to f (through 'g')
384 - the "e-" branch renaming b to f (through 'g')
385 - the "a-" branch renaming d to f (through e)
385 - the "a-" branch renaming d to f (through e)
386
386
387 $ case_desc="merge with copies info on both side - A side: rename d to f, E side: b to f, (same content for f)"
387 $ case_desc="merge with copies info on both side - A side: rename d to f, E side: b to f, (same content for f)"
388
388
389 $ hg up 'desc("a-2")'
389 $ hg up 'desc("a-2")'
390 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
390 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
391 $ hg merge 'desc("e-2")'
391 $ hg merge 'desc("e-2")'
392 1 files updated, 0 files merged, 1 files removed, 0 files unresolved (no-changeset !)
392 1 files updated, 0 files merged, 1 files removed, 0 files unresolved (no-changeset !)
393 0 files updated, 0 files merged, 1 files removed, 0 files unresolved (changeset !)
393 0 files updated, 0 files merged, 1 files removed, 0 files unresolved (changeset !)
394 (branch merge, don't forget to commit)
394 (branch merge, don't forget to commit)
395 $ hg ci -m "mAEm-0 $case_desc - one way"
395 $ hg ci -m "mAEm-0 $case_desc - one way"
396 $ hg up 'desc("e-2")'
396 $ hg up 'desc("e-2")'
397 2 files updated, 0 files merged, 0 files removed, 0 files unresolved (no-changeset !)
397 2 files updated, 0 files merged, 0 files removed, 0 files unresolved (no-changeset !)
398 1 files updated, 0 files merged, 0 files removed, 0 files unresolved (changeset !)
398 1 files updated, 0 files merged, 0 files removed, 0 files unresolved (changeset !)
399 $ hg merge 'desc("a-2")'
399 $ hg merge 'desc("a-2")'
400 1 files updated, 0 files merged, 1 files removed, 0 files unresolved (no-changeset !)
400 1 files updated, 0 files merged, 1 files removed, 0 files unresolved (no-changeset !)
401 0 files updated, 0 files merged, 1 files removed, 0 files unresolved (changeset !)
401 0 files updated, 0 files merged, 1 files removed, 0 files unresolved (changeset !)
402 (branch merge, don't forget to commit)
402 (branch merge, don't forget to commit)
403 $ hg ci -m "mEAm-0 $case_desc - the other way"
403 $ hg ci -m "mEAm-0 $case_desc - the other way"
404 created new head
404 created new head
405 $ hg log -G --rev '::(desc("mAEm")+desc("mEAm"))'
405 $ hg log -G --rev '::(desc("mAEm")+desc("mEAm"))'
406 @ mEAm-0 merge with copies info on both side - A side: rename d to f, E side: b to f, (same content for f) - the other way
406 @ mEAm-0 merge with copies info on both side - A side: rename d to f, E side: b to f, (same content for f) - the other way
407 |\
407 |\
408 +---o mAEm-0 merge with copies info on both side - A side: rename d to f, E side: b to f, (same content for f) - one way
408 +---o mAEm-0 merge with copies info on both side - A side: rename d to f, E side: b to f, (same content for f) - one way
409 | |/
409 | |/
410 | o e-2 g -move-> f
410 | o e-2 g -move-> f
411 | |
411 | |
412 | o e-1 b -move-> g
412 | o e-1 b -move-> g
413 | |
413 | |
414 o | a-2: e -move-> f
414 o | a-2: e -move-> f
415 | |
415 | |
416 o | a-1: d -move-> e
416 o | a-1: d -move-> e
417 |/
417 |/
418 o i-2: c -move-> d, s -move-> t
418 o i-2: c -move-> d, s -move-> t
419 |
419 |
420 o i-1: a -move-> c, p -move-> s
420 o i-1: a -move-> c, p -move-> s
421 |
421 |
422 o i-0 initial commit: a b h
422 o i-0 initial commit: a b h
423
423
424
424
425 Subcase: new copy information on both side with an actual merge happening
425 Subcase: new copy information on both side with an actual merge happening
426 `````````````````````````````````````````````````````````````````````````
426 `````````````````````````````````````````````````````````````````````````
427
427
428 - the "p-" branch renaming 't' to 'v' (through 'u')
428 - the "p-" branch renaming 't' to 'v' (through 'u')
429 - the "q-" branch renaming 'r' to 'v' (through 'w')
429 - the "q-" branch renaming 'r' to 'v' (through 'w')
430
430
431 $ case_desc="merge with copies info on both side - P side: rename t to v, Q side: r to v, (different content)"
431 $ case_desc="merge with copies info on both side - P side: rename t to v, Q side: r to v, (different content)"
432
432
433 $ hg up 'desc("p-2")'
433 $ hg up 'desc("p-2")'
434 3 files updated, 0 files merged, 2 files removed, 0 files unresolved
434 3 files updated, 0 files merged, 2 files removed, 0 files unresolved
435 $ hg merge 'desc("q-2")' --tool ':union'
435 $ hg merge 'desc("q-2")' --tool ':union'
436 merging v
436 merging v
437 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
437 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
438 (branch merge, don't forget to commit)
438 (branch merge, don't forget to commit)
439 $ hg ci -m "mPQm-0 $case_desc - one way"
439 $ hg ci -m "mPQm-0 $case_desc - one way"
440 $ hg up 'desc("q-2")'
440 $ hg up 'desc("q-2")'
441 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
441 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
442 $ hg merge 'desc("p-2")' --tool ':union'
442 $ hg merge 'desc("p-2")' --tool ':union'
443 merging v
443 merging v
444 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
444 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
445 (branch merge, don't forget to commit)
445 (branch merge, don't forget to commit)
446 $ hg ci -m "mQPm-0 $case_desc - the other way"
446 $ hg ci -m "mQPm-0 $case_desc - the other way"
447 created new head
447 created new head
448 $ hg log -G --rev '::(desc("mAEm")+desc("mEAm"))'
448 $ hg log -G --rev '::(desc("mAEm")+desc("mEAm"))'
449 o mEAm-0 merge with copies info on both side - A side: rename d to f, E side: b to f, (same content for f) - the other way
449 o mEAm-0 merge with copies info on both side - A side: rename d to f, E side: b to f, (same content for f) - the other way
450 |\
450 |\
451 +---o mAEm-0 merge with copies info on both side - A side: rename d to f, E side: b to f, (same content for f) - one way
451 +---o mAEm-0 merge with copies info on both side - A side: rename d to f, E side: b to f, (same content for f) - one way
452 | |/
452 | |/
453 | o e-2 g -move-> f
453 | o e-2 g -move-> f
454 | |
454 | |
455 | o e-1 b -move-> g
455 | o e-1 b -move-> g
456 | |
456 | |
457 o | a-2: e -move-> f
457 o | a-2: e -move-> f
458 | |
458 | |
459 o | a-1: d -move-> e
459 o | a-1: d -move-> e
460 |/
460 |/
461 o i-2: c -move-> d, s -move-> t
461 o i-2: c -move-> d, s -move-> t
462 |
462 |
463 o i-1: a -move-> c, p -move-> s
463 o i-1: a -move-> c, p -move-> s
464 |
464 |
465 o i-0 initial commit: a b h
465 o i-0 initial commit: a b h
466
466
467
467
468 Subcase: existing copy information overwritten on one branch
468 Subcase: existing copy information overwritten on one branch
469 ````````````````````````````````````````````````````````````
469 ````````````````````````````````````````````````````````````
470
470
471 Merge:
471 Merge:
472 - one with change to an unrelated file (b)
472 - one with change to an unrelated file (b)
473 - one overwriting a file (d) with a rename (from h to i to d)
473 - one overwriting a file (d) with a rename (from h to i to d)
474
474
475 $ case_desc="simple merge - B side: unrelated change, F side: overwrite d with a copy (from h->i->d)"
475 $ case_desc="simple merge - B side: unrelated change, F side: overwrite d with a copy (from h->i->d)"
476
476
477 $ hg up 'desc("i-2")'
477 $ hg up 'desc("i-2")'
478 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
478 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
479 $ hg mv h i
479 $ hg mv h i
480 $ hg commit -m "f-1: rename h -> i"
480 $ hg commit -m "f-1: rename h -> i"
481 created new head
481 created new head
482 $ hg mv --force i d
482 $ hg mv --force i d
483 $ hg commit -m "f-2: rename i -> d"
483 $ hg commit -m "f-2: rename i -> d"
484 $ hg debugindex d | ../no-linkrev
484 $ hg debugindex d | ../no-linkrev
485 rev linkrev nodeid p1 p2
485 rev linkrev nodeid p1 p2
486 0 * d8252ab2e760 000000000000 000000000000 (no-changeset !)
486 0 * d8252ab2e760 000000000000 000000000000 (no-changeset !)
487 0 * ae258f702dfe 000000000000 000000000000 (changeset !)
487 0 * ae258f702dfe 000000000000 000000000000 (changeset !)
488 1 * b004912a8510 000000000000 000000000000
488 1 * b004912a8510 000000000000 000000000000
489 2 * 7b79e2fe0c89 000000000000 000000000000 (no-changeset !)
489 2 * 7b79e2fe0c89 000000000000 000000000000 (no-changeset !)
490 $ hg up 'desc("b-1")'
490 $ hg up 'desc("b-1")'
491 3 files updated, 0 files merged, 0 files removed, 0 files unresolved (no-changeset !)
491 3 files updated, 0 files merged, 0 files removed, 0 files unresolved (no-changeset !)
492 2 files updated, 0 files merged, 0 files removed, 0 files unresolved (changeset !)
492 2 files updated, 0 files merged, 0 files removed, 0 files unresolved (changeset !)
493 $ hg merge 'desc("f-2")'
493 $ hg merge 'desc("f-2")'
494 1 files updated, 0 files merged, 1 files removed, 0 files unresolved (no-changeset !)
494 1 files updated, 0 files merged, 1 files removed, 0 files unresolved (no-changeset !)
495 0 files updated, 0 files merged, 1 files removed, 0 files unresolved (changeset !)
495 0 files updated, 0 files merged, 1 files removed, 0 files unresolved (changeset !)
496 (branch merge, don't forget to commit)
496 (branch merge, don't forget to commit)
497 $ hg ci -m "mBFm-0 $case_desc - one way"
497 $ hg ci -m "mBFm-0 $case_desc - one way"
498 $ hg up 'desc("f-2")'
498 $ hg up 'desc("f-2")'
499 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
499 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
500 $ hg merge 'desc("b-1")'
500 $ hg merge 'desc("b-1")'
501 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
501 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
502 (branch merge, don't forget to commit)
502 (branch merge, don't forget to commit)
503 $ hg ci -m "mFBm-0 $case_desc - the other way"
503 $ hg ci -m "mFBm-0 $case_desc - the other way"
504 created new head
504 created new head
505 $ hg up null --quiet
505 $ hg up null --quiet
506 $ hg log -G --rev '::(desc("mBFm")+desc("mFBm"))'
506 $ hg log -G --rev '::(desc("mBFm")+desc("mFBm"))'
507 o mFBm-0 simple merge - B side: unrelated change, F side: overwrite d with a copy (from h->i->d) - the other way
507 o mFBm-0 simple merge - B side: unrelated change, F side: overwrite d with a copy (from h->i->d) - the other way
508 |\
508 |\
509 +---o mBFm-0 simple merge - B side: unrelated change, F side: overwrite d with a copy (from h->i->d) - one way
509 +---o mBFm-0 simple merge - B side: unrelated change, F side: overwrite d with a copy (from h->i->d) - one way
510 | |/
510 | |/
511 | o f-2: rename i -> d
511 | o f-2: rename i -> d
512 | |
512 | |
513 | o f-1: rename h -> i
513 | o f-1: rename h -> i
514 | |
514 | |
515 o | b-1: b update
515 o | b-1: b update
516 |/
516 |/
517 o i-2: c -move-> d, s -move-> t
517 o i-2: c -move-> d, s -move-> t
518 |
518 |
519 o i-1: a -move-> c, p -move-> s
519 o i-1: a -move-> c, p -move-> s
520 |
520 |
521 o i-0 initial commit: a b h
521 o i-0 initial commit: a b h
522
522
523
523
524 Subcase: existing copy information overwritten on one branch, with different content)
524 Subcase: existing copy information overwritten on one branch, with different content)
525 `````````````````````````````````````````````````````````````````````````````````````
525 `````````````````````````````````````````````````````````````````````````````````````
526
526
527 Merge:
527 Merge:
528 - one with change to an unrelated file (b)
528 - one with change to an unrelated file (b)
529 - one overwriting a file (t) with a rename (from r to x to t), v content is not the same as on the other branch
529 - one overwriting a file (t) with a rename (from r to x to t), v content is not the same as on the other branch
530
530
531 $ case_desc="simple merge - B side: unrelated change, R side: overwrite d with a copy (from r->x->t) different content"
531 $ case_desc="simple merge - B side: unrelated change, R side: overwrite d with a copy (from r->x->t) different content"
532
532
533 $ hg up 'desc("i-2")'
533 $ hg up 'desc("i-2")'
534 6 files updated, 0 files merged, 0 files removed, 0 files unresolved
534 6 files updated, 0 files merged, 0 files removed, 0 files unresolved
535 $ hg mv r x
535 $ hg mv r x
536 $ hg commit -m "r-1: rename r -> x"
536 $ hg commit -m "r-1: rename r -> x"
537 created new head
537 created new head
538 $ hg mv --force x t
538 $ hg mv --force x t
539 $ hg commit -m "r-2: rename t -> x"
539 $ hg commit -m "r-2: rename t -> x"
540 $ hg debugindex t | ../no-linkrev
540 $ hg debugindex t | ../no-linkrev
541 rev linkrev nodeid p1 p2
541 rev linkrev nodeid p1 p2
542 0 * d74efbf65309 000000000000 000000000000 (no-changeset !)
542 0 * d74efbf65309 000000000000 000000000000 (no-changeset !)
543 1 * 02a930b9d7ad 000000000000 000000000000 (no-changeset !)
543 1 * 02a930b9d7ad 000000000000 000000000000 (no-changeset !)
544 0 * 5aed6a8dbff0 000000000000 000000000000 (changeset !)
544 0 * 5aed6a8dbff0 000000000000 000000000000 (changeset !)
545 1 * a38b2fa17021 000000000000 000000000000 (changeset !)
545 1 * a38b2fa17021 000000000000 000000000000 (changeset !)
546 $ hg up 'desc("b-1")'
546 $ hg up 'desc("b-1")'
547 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
547 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
548 $ hg merge 'desc("r-2")'
548 $ hg merge 'desc("r-2")'
549 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
549 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
550 (branch merge, don't forget to commit)
550 (branch merge, don't forget to commit)
551 $ hg ci -m "mBRm-0 $case_desc - one way"
551 $ hg ci -m "mBRm-0 $case_desc - one way"
552 $ hg up 'desc("r-2")'
552 $ hg up 'desc("r-2")'
553 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
553 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
554 $ hg merge 'desc("b-1")'
554 $ hg merge 'desc("b-1")'
555 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
555 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
556 (branch merge, don't forget to commit)
556 (branch merge, don't forget to commit)
557 $ hg ci -m "mRBm-0 $case_desc - the other way"
557 $ hg ci -m "mRBm-0 $case_desc - the other way"
558 created new head
558 created new head
559 $ hg up null --quiet
559 $ hg up null --quiet
560 $ hg log -G --rev '::(desc("mBRm")+desc("mRBm"))'
560 $ hg log -G --rev '::(desc("mBRm")+desc("mRBm"))'
561 o mRBm-0 simple merge - B side: unrelated change, R side: overwrite d with a copy (from r->x->t) different content - the other way
561 o mRBm-0 simple merge - B side: unrelated change, R side: overwrite d with a copy (from r->x->t) different content - the other way
562 |\
562 |\
563 +---o mBRm-0 simple merge - B side: unrelated change, R side: overwrite d with a copy (from r->x->t) different content - one way
563 +---o mBRm-0 simple merge - B side: unrelated change, R side: overwrite d with a copy (from r->x->t) different content - one way
564 | |/
564 | |/
565 | o r-2: rename t -> x
565 | o r-2: rename t -> x
566 | |
566 | |
567 | o r-1: rename r -> x
567 | o r-1: rename r -> x
568 | |
568 | |
569 o | b-1: b update
569 o | b-1: b update
570 |/
570 |/
571 o i-2: c -move-> d, s -move-> t
571 o i-2: c -move-> d, s -move-> t
572 |
572 |
573 o i-1: a -move-> c, p -move-> s
573 o i-1: a -move-> c, p -move-> s
574 |
574 |
575 o i-0 initial commit: a b h
575 o i-0 initial commit: a b h
576
576
577
577
578
578
579 Subcase: reset of the copy history on one side
579 Subcase: reset of the copy history on one side
580 ``````````````````````````````````````````````
580 ``````````````````````````````````````````````
581
581
582 Merge:
582 Merge:
583 - one with change to a file
583 - one with change to a file
584 - one deleting and recreating the file
584 - one deleting and recreating the file
585
585
586 Unlike in the 'BD/DB' cases, an actual merge happened here. So we should
586 Unlike in the 'BD/DB' cases, an actual merge happened here. So we should
587 consider history and rename on both branch of the merge.
587 consider history and rename on both branch of the merge.
588
588
589 $ case_desc="actual content merge, copies on one side - D side: delete and re-add (different content), G side: update content"
589 $ case_desc="actual content merge, copies on one side - D side: delete and re-add (different content), G side: update content"
590
590
591 $ hg up 'desc("i-2")'
591 $ hg up 'desc("i-2")'
592 6 files updated, 0 files merged, 0 files removed, 0 files unresolved
592 6 files updated, 0 files merged, 0 files removed, 0 files unresolved
593 $ echo "some update" >> d
593 $ echo "some update" >> d
594 $ hg commit -m "g-1: update d"
594 $ hg commit -m "g-1: update d"
595 created new head
595 created new head
596 $ hg up 'desc("d-2")'
596 $ hg up 'desc("d-2")'
597 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
597 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
598 $ hg merge 'desc("g-1")' --tool :union
598 $ hg merge 'desc("g-1")' --tool :union
599 merging d
599 merging d
600 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
600 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
601 (branch merge, don't forget to commit)
601 (branch merge, don't forget to commit)
602 $ hg ci -m "mDGm-0 $case_desc - one way"
602 $ hg ci -m "mDGm-0 $case_desc - one way"
603 $ hg up 'desc("g-1")'
603 $ hg up 'desc("g-1")'
604 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
604 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
605 $ hg merge 'desc("d-2")' --tool :union
605 $ hg merge 'desc("d-2")' --tool :union
606 merging d
606 merging d
607 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
607 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
608 (branch merge, don't forget to commit)
608 (branch merge, don't forget to commit)
609 $ hg ci -m "mGDm-0 $case_desc - the other way"
609 $ hg ci -m "mGDm-0 $case_desc - the other way"
610 created new head
610 created new head
611 $ hg log -G --rev '::(desc("mDGm")+desc("mGDm"))'
611 $ hg log -G --rev '::(desc("mDGm")+desc("mGDm"))'
612 @ mGDm-0 actual content merge, copies on one side - D side: delete and re-add (different content), G side: update content - the other way
612 @ mGDm-0 actual content merge, copies on one side - D side: delete and re-add (different content), G side: update content - the other way
613 |\
613 |\
614 +---o mDGm-0 actual content merge, copies on one side - D side: delete and re-add (different content), G side: update content - one way
614 +---o mDGm-0 actual content merge, copies on one side - D side: delete and re-add (different content), G side: update content - one way
615 | |/
615 | |/
616 | o g-1: update d
616 | o g-1: update d
617 | |
617 | |
618 o | d-2 re-add d
618 o | d-2 re-add d
619 | |
619 | |
620 o | d-1 delete d
620 o | d-1 delete d
621 |/
621 |/
622 o i-2: c -move-> d, s -move-> t
622 o i-2: c -move-> d, s -move-> t
623 |
623 |
624 o i-1: a -move-> c, p -move-> s
624 o i-1: a -move-> c, p -move-> s
625 |
625 |
626 o i-0 initial commit: a b h
626 o i-0 initial commit: a b h
627
627
628
628
629 Subcase: merging a change to a file with a "copy overwrite" to that file from another branch
629 Subcase: merging a change to a file with a "copy overwrite" to that file from another branch
630 ````````````````````````````````````````````````````````````````````````````````````````````
630 ````````````````````````````````````````````````````````````````````````````````````````````
631
631
632 Merge:
632 Merge:
633 - one with change to a file (d)
633 - one with change to a file (d)
634 - one overwriting that file with a rename (from h to i, to d)
634 - one overwriting that file with a rename (from h to i, to d)
635
635
636 This case is similar to BF/FB, but an actual merge happens, so both side of the
636 This case is similar to BF/FB, but an actual merge happens, so both side of the
637 history are relevant.
637 history are relevant.
638
638
639 Note:
639 Note:
640 | In this case, the merge get conflicting information since on one side we have
640 | In this case, the merge get conflicting information since on one side we have
641 | "a -> c -> d". and one the other one we have "h -> i -> d".
641 | "a -> c -> d". and one the other one we have "h -> i -> d".
642 |
642 |
643 | The current code arbitrarily pick one side
643 | The current code arbitrarily pick one side
644
644
645 $ case_desc="merge - G side: content change, F side: copy overwrite, no content change"
645 $ case_desc="merge - G side: content change, F side: copy overwrite, no content change"
646
646
647 $ hg up 'desc("f-2")'
647 $ hg up 'desc("f-2")'
648 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
648 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
649 $ hg merge 'desc("g-1")' --tool :union
649 $ hg merge 'desc("g-1")' --tool :union
650 merging d (no-changeset !)
650 merging d (no-changeset !)
651 0 files updated, 1 files merged, 0 files removed, 0 files unresolved (no-changeset !)
651 0 files updated, 1 files merged, 0 files removed, 0 files unresolved (no-changeset !)
652 1 files updated, 0 files merged, 0 files removed, 0 files unresolved (changeset !)
652 1 files updated, 0 files merged, 0 files removed, 0 files unresolved (changeset !)
653 (branch merge, don't forget to commit)
653 (branch merge, don't forget to commit)
654 $ hg ci -m "mFGm-0 $case_desc - one way"
654 $ hg ci -m "mFGm-0 $case_desc - one way"
655 created new head
655 created new head
656 $ hg up 'desc("g-1")'
656 $ hg up 'desc("g-1")'
657 2 files updated, 0 files merged, 0 files removed, 0 files unresolved (no-changeset !)
657 2 files updated, 0 files merged, 0 files removed, 0 files unresolved (no-changeset !)
658 1 files updated, 0 files merged, 0 files removed, 0 files unresolved (changeset !)
658 1 files updated, 0 files merged, 0 files removed, 0 files unresolved (changeset !)
659 $ hg merge 'desc("f-2")' --tool :union
659 $ hg merge 'desc("f-2")' --tool :union
660 merging d (no-changeset !)
660 merging d (no-changeset !)
661 0 files updated, 1 files merged, 1 files removed, 0 files unresolved (no-changeset !)
661 0 files updated, 1 files merged, 1 files removed, 0 files unresolved (no-changeset !)
662 0 files updated, 0 files merged, 1 files removed, 0 files unresolved (changeset !)
662 0 files updated, 0 files merged, 1 files removed, 0 files unresolved (changeset !)
663 (branch merge, don't forget to commit)
663 (branch merge, don't forget to commit)
664 $ hg ci -m "mGFm-0 $case_desc - the other way"
664 $ hg ci -m "mGFm-0 $case_desc - the other way"
665 created new head
665 created new head
666 $ hg log -G --rev '::(desc("mGFm")+desc("mFGm"))'
666 $ hg log -G --rev '::(desc("mGFm")+desc("mFGm"))'
667 @ mGFm-0 merge - G side: content change, F side: copy overwrite, no content change - the other way
667 @ mGFm-0 merge - G side: content change, F side: copy overwrite, no content change - the other way
668 |\
668 |\
669 +---o mFGm-0 merge - G side: content change, F side: copy overwrite, no content change - one way
669 +---o mFGm-0 merge - G side: content change, F side: copy overwrite, no content change - one way
670 | |/
670 | |/
671 | o g-1: update d
671 | o g-1: update d
672 | |
672 | |
673 o | f-2: rename i -> d
673 o | f-2: rename i -> d
674 | |
674 | |
675 o | f-1: rename h -> i
675 o | f-1: rename h -> i
676 |/
676 |/
677 o i-2: c -move-> d, s -move-> t
677 o i-2: c -move-> d, s -move-> t
678 |
678 |
679 o i-1: a -move-> c, p -move-> s
679 o i-1: a -move-> c, p -move-> s
680 |
680 |
681 o i-0 initial commit: a b h
681 o i-0 initial commit: a b h
682
682
683
683
684
684
685 Comparing with merging with a deletion (and keeping the file)
685 Comparing with merging with a deletion (and keeping the file)
686 -------------------------------------------------------------
686 -------------------------------------------------------------
687
687
688 Merge:
688 Merge:
689 - one removing a file (d)
689 - one removing a file (d)
690 - one updating that file
690 - one updating that file
691 - the merge keep the modified version of the file (canceling the delete)
691 - the merge keep the modified version of the file (canceling the delete)
692
692
693 In this case, the file keep on living after the merge. So we should not drop its
693 In this case, the file keep on living after the merge. So we should not drop its
694 copy tracing chain.
694 copy tracing chain.
695
695
696 $ case_desc="merge updated/deleted - revive the file (updated content)"
696 $ case_desc="merge updated/deleted - revive the file (updated content)"
697
697
698 $ hg up 'desc("c-1")'
698 $ hg up 'desc("c-1")'
699 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
699 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
700 $ hg merge 'desc("g-1")'
700 $ hg merge 'desc("g-1")'
701 file 'd' was deleted in local [working copy] but was modified in other [merge rev].
701 file 'd' was deleted in local [working copy] but was modified in other [merge rev].
702 You can use (c)hanged version, leave (d)eleted, or leave (u)nresolved.
702 You can use (c)hanged version, leave (d)eleted, or leave (u)nresolved.
703 What do you want to do? u
703 What do you want to do? u
704 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
704 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
705 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
705 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
706 [1]
706 [1]
707 $ hg resolve -t :other d
707 $ hg resolve -t :other d
708 (no more unresolved files)
708 (no more unresolved files)
709 $ hg ci -m "mCGm-0 $case_desc - one way"
709 $ hg ci -m "mCGm-0 $case_desc - one way"
710 created new head
710 created new head
711
711
712 $ hg up 'desc("g-1")'
712 $ hg up 'desc("g-1")'
713 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
713 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
714 $ hg merge 'desc("c-1")'
714 $ hg merge 'desc("c-1")'
715 file 'd' was deleted in other [merge rev] but was modified in local [working copy].
715 file 'd' was deleted in other [merge rev] but was modified in local [working copy].
716 You can use (c)hanged version, (d)elete, or leave (u)nresolved.
716 You can use (c)hanged version, (d)elete, or leave (u)nresolved.
717 What do you want to do? u
717 What do you want to do? u
718 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
718 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
719 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
719 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
720 [1]
720 [1]
721 $ hg resolve -t :local d
721 $ hg resolve -t :local d
722 (no more unresolved files)
722 (no more unresolved files)
723 $ hg ci -m "mGCm-0 $case_desc - the other way"
723 $ hg ci -m "mGCm-0 $case_desc - the other way"
724 created new head
724 created new head
725
725
726 $ hg log -G --rev '::(desc("mCGm")+desc("mGCm"))'
726 $ hg log -G --rev '::(desc("mCGm")+desc("mGCm"))'
727 @ mGCm-0 merge updated/deleted - revive the file (updated content) - the other way
727 @ mGCm-0 merge updated/deleted - revive the file (updated content) - the other way
728 |\
728 |\
729 +---o mCGm-0 merge updated/deleted - revive the file (updated content) - one way
729 +---o mCGm-0 merge updated/deleted - revive the file (updated content) - one way
730 | |/
730 | |/
731 | o g-1: update d
731 | o g-1: update d
732 | |
732 | |
733 o | c-1 delete d
733 o | c-1 delete d
734 |/
734 |/
735 o i-2: c -move-> d, s -move-> t
735 o i-2: c -move-> d, s -move-> t
736 |
736 |
737 o i-1: a -move-> c, p -move-> s
737 o i-1: a -move-> c, p -move-> s
738 |
738 |
739 o i-0 initial commit: a b h
739 o i-0 initial commit: a b h
740
740
741
741
742
742
743
743
744 Comparing with merge restoring an untouched deleted file
744 Comparing with merge restoring an untouched deleted file
745 --------------------------------------------------------
745 --------------------------------------------------------
746
746
747 Merge:
747 Merge:
748 - one removing a file (d)
748 - one removing a file (d)
749 - one leaving the file untouched
749 - one leaving the file untouched
750 - the merge actively restore the file to the same content.
750 - the merge actively restore the file to the same content.
751
751
752 In this case, the file keep on living after the merge. So we should not drop its
752 In this case, the file keep on living after the merge. So we should not drop its
753 copy tracing chain.
753 copy tracing chain.
754
754
755 $ case_desc="merge explicitely revive deleted file - B side: unrelated change, C side: delete d (restored by merge)"
755 $ case_desc="merge explicitely revive deleted file - B side: unrelated change, C side: delete d (restored by merge)"
756
756
757 $ hg up 'desc("c-1")'
757 $ hg up 'desc("c-1")'
758 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
758 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
759 $ hg merge 'desc("b-1")'
759 $ hg merge 'desc("b-1")'
760 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
760 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
761 (branch merge, don't forget to commit)
761 (branch merge, don't forget to commit)
762 $ hg revert --rev 'desc("b-1")' d
762 $ hg revert --rev 'desc("b-1")' d
763 $ hg ci -m "mCB-revert-m-0 $case_desc - one way"
763 $ hg ci -m "mCB-revert-m-0 $case_desc - one way"
764 created new head
764 created new head
765
765
766 $ hg up 'desc("b-1")'
766 $ hg up 'desc("b-1")'
767 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
767 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
768 $ hg merge 'desc("c-1")'
768 $ hg merge 'desc("c-1")'
769 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
769 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
770 (branch merge, don't forget to commit)
770 (branch merge, don't forget to commit)
771 $ hg revert --rev 'desc("b-1")' d
771 $ hg revert --rev 'desc("b-1")' d
772 $ hg ci -m "mBC-revert-m-0 $case_desc - the other way"
772 $ hg ci -m "mBC-revert-m-0 $case_desc - the other way"
773 created new head
773 created new head
774
774
775 $ hg log -G --rev '::(desc("mCB-revert-m")+desc("mBC-revert-m"))'
775 $ hg log -G --rev '::(desc("mCB-revert-m")+desc("mBC-revert-m"))'
776 @ mBC-revert-m-0 merge explicitely revive deleted file - B side: unrelated change, C side: delete d (restored by merge) - the other way
776 @ mBC-revert-m-0 merge explicitely revive deleted file - B side: unrelated change, C side: delete d (restored by merge) - the other way
777 |\
777 |\
778 +---o mCB-revert-m-0 merge explicitely revive deleted file - B side: unrelated change, C side: delete d (restored by merge) - one way
778 +---o mCB-revert-m-0 merge explicitely revive deleted file - B side: unrelated change, C side: delete d (restored by merge) - one way
779 | |/
779 | |/
780 | o c-1 delete d
780 | o c-1 delete d
781 | |
781 | |
782 o | b-1: b update
782 o | b-1: b update
783 |/
783 |/
784 o i-2: c -move-> d, s -move-> t
784 o i-2: c -move-> d, s -move-> t
785 |
785 |
786 o i-1: a -move-> c, p -move-> s
786 o i-1: a -move-> c, p -move-> s
787 |
787 |
788 o i-0 initial commit: a b h
788 o i-0 initial commit: a b h
789
789
790
790
791
791
792 $ hg up null --quiet
792 $ hg up null --quiet
793
793
794 Merging a branch where a rename was deleted with a branch where the same file was renamed
794 Merging a branch where a rename was deleted with a branch where the same file was renamed
795 ------------------------------------------------------------------------------------------
795 ------------------------------------------------------------------------------------------
796
796
797 Create a "conflicting" merge where `d` get removed on one branch before its
797 Create a "conflicting" merge where `d` get removed on one branch before its
798 rename information actually conflict with the other branch.
798 rename information actually conflict with the other branch.
799
799
800 (the copy information from the branch that was not deleted should win).
800 (the copy information from the branch that was not deleted should win).
801
801
802 $ case_desc="simple merge - C side: d is the results of renames then deleted, H side: d is result of another rename (same content as the other branch)"
802 $ case_desc="simple merge - C side: d is the results of renames then deleted, H side: d is result of another rename (same content as the other branch)"
803
803
804 $ hg up 'desc("i-0")'
804 $ hg up 'desc("i-0")'
805 6 files updated, 0 files merged, 0 files removed, 0 files unresolved
805 6 files updated, 0 files merged, 0 files removed, 0 files unresolved
806 $ hg mv b d
806 $ hg mv b d
807 $ hg ci -m "h-1: b -(move)-> d"
807 $ hg ci -m "h-1: b -(move)-> d"
808 created new head
808 created new head
809
809
810 $ hg up 'desc("c-1")'
810 $ hg up 'desc("c-1")'
811 2 files updated, 0 files merged, 3 files removed, 0 files unresolved
811 2 files updated, 0 files merged, 3 files removed, 0 files unresolved
812 $ hg merge 'desc("h-1")'
812 $ hg merge 'desc("h-1")'
813 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
813 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
814 (branch merge, don't forget to commit)
814 (branch merge, don't forget to commit)
815 $ hg ci -m "mCH-delete-before-conflict-m-0 $case_desc - one way"
815 $ hg ci -m "mCH-delete-before-conflict-m-0 $case_desc - one way"
816
816
817 $ hg up 'desc("h-1")'
817 $ hg up 'desc("h-1")'
818 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
818 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
819 $ hg merge 'desc("c-1")'
819 $ hg merge 'desc("c-1")'
820 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
820 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
821 (branch merge, don't forget to commit)
821 (branch merge, don't forget to commit)
822 $ hg ci -m "mHC-delete-before-conflict-m-0 $case_desc - the other way"
822 $ hg ci -m "mHC-delete-before-conflict-m-0 $case_desc - the other way"
823 created new head
823 created new head
824 $ hg log -G --rev '::(desc("mCH-delete-before-conflict-m")+desc("mHC-delete-before-conflict-m"))'
824 $ hg log -G --rev '::(desc("mCH-delete-before-conflict-m")+desc("mHC-delete-before-conflict-m"))'
825 @ mHC-delete-before-conflict-m-0 simple merge - C side: d is the results of renames then deleted, H side: d is result of another rename (same content as the other branch) - the other way
825 @ mHC-delete-before-conflict-m-0 simple merge - C side: d is the results of renames then deleted, H side: d is result of another rename (same content as the other branch) - the other way
826 |\
826 |\
827 +---o mCH-delete-before-conflict-m-0 simple merge - C side: d is the results of renames then deleted, H side: d is result of another rename (same content as the other branch) - one way
827 +---o mCH-delete-before-conflict-m-0 simple merge - C side: d is the results of renames then deleted, H side: d is result of another rename (same content as the other branch) - one way
828 | |/
828 | |/
829 | o h-1: b -(move)-> d
829 | o h-1: b -(move)-> d
830 | |
830 | |
831 o | c-1 delete d
831 o | c-1 delete d
832 | |
832 | |
833 o | i-2: c -move-> d, s -move-> t
833 o | i-2: c -move-> d, s -move-> t
834 | |
834 | |
835 o | i-1: a -move-> c, p -move-> s
835 o | i-1: a -move-> c, p -move-> s
836 |/
836 |/
837 o i-0 initial commit: a b h
837 o i-0 initial commit: a b h
838
838
839
839
840 Variant of previous with extra changes introduced by the merge
840 Variant of previous with extra changes introduced by the merge
841 --------------------------------------------------------------
841 --------------------------------------------------------------
842
842
843 Multiple cases above explicitely test cases where content are the same on both side during merge. In this section we will introduce variants for theses cases where new change are introduced to these file content during the merges.
843 Multiple cases above explicitely test cases where content are the same on both side during merge. In this section we will introduce variants for theses cases where new change are introduced to these file content during the merges.
844
844
845
845
846 Subcase: merge has same initial content on both side, but merge introduced a change
846 Subcase: merge has same initial content on both side, but merge introduced a change
847 ```````````````````````````````````````````````````````````````````````````````````
847 ```````````````````````````````````````````````````````````````````````````````````
848
848
849 Same as `mAEm` and `mEAm` but with extra change to the file before commiting
849 Same as `mAEm` and `mEAm` but with extra change to the file before commiting
850
850
851 - the "e-" branch renaming b to f (through 'g')
851 - the "e-" branch renaming b to f (through 'g')
852 - the "a-" branch renaming d to f (through e)
852 - the "a-" branch renaming d to f (through e)
853
853
854 $ case_desc="merge with file update and copies info on both side - A side: rename d to f, E side: b to f, (same content for f in parent)"
854 $ case_desc="merge with file update and copies info on both side - A side: rename d to f, E side: b to f, (same content for f in parent)"
855
855
856 $ hg up 'desc("a-2")'
856 $ hg up 'desc("a-2")'
857 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
857 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
858 $ hg merge 'desc("e-2")'
858 $ hg merge 'desc("e-2")'
859 1 files updated, 0 files merged, 1 files removed, 0 files unresolved (no-changeset !)
859 1 files updated, 0 files merged, 1 files removed, 0 files unresolved (no-changeset !)
860 0 files updated, 0 files merged, 1 files removed, 0 files unresolved (changeset !)
860 0 files updated, 0 files merged, 1 files removed, 0 files unresolved (changeset !)
861 (branch merge, don't forget to commit)
861 (branch merge, don't forget to commit)
862 $ echo "content change for mAE-change-m" > f
862 $ echo "content change for mAE-change-m" > f
863 $ hg ci -m "mAE-change-m-0 $case_desc - one way"
863 $ hg ci -m "mAE-change-m-0 $case_desc - one way"
864 created new head
864 created new head
865 $ hg up 'desc("e-2")'
865 $ hg up 'desc("e-2")'
866 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
866 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
867 $ hg merge 'desc("a-2")'
867 $ hg merge 'desc("a-2")'
868 1 files updated, 0 files merged, 1 files removed, 0 files unresolved (no-changeset !)
868 1 files updated, 0 files merged, 1 files removed, 0 files unresolved (no-changeset !)
869 0 files updated, 0 files merged, 1 files removed, 0 files unresolved (changeset !)
869 0 files updated, 0 files merged, 1 files removed, 0 files unresolved (changeset !)
870 (branch merge, don't forget to commit)
870 (branch merge, don't forget to commit)
871 $ echo "content change for mEA-change-m" > f
871 $ echo "content change for mEA-change-m" > f
872 $ hg ci -m "mEA-change-m-0 $case_desc - the other way"
872 $ hg ci -m "mEA-change-m-0 $case_desc - the other way"
873 created new head
873 created new head
874 $ hg log -G --rev '::(desc("mAE-change-m")+desc("mEA-change-m"))'
874 $ hg log -G --rev '::(desc("mAE-change-m")+desc("mEA-change-m"))'
875 @ mEA-change-m-0 merge with file update and copies info on both side - A side: rename d to f, E side: b to f, (same content for f in parent) - the other way
875 @ mEA-change-m-0 merge with file update and copies info on both side - A side: rename d to f, E side: b to f, (same content for f in parent) - the other way
876 |\
876 |\
877 +---o mAE-change-m-0 merge with file update and copies info on both side - A side: rename d to f, E side: b to f, (same content for f in parent) - one way
877 +---o mAE-change-m-0 merge with file update and copies info on both side - A side: rename d to f, E side: b to f, (same content for f in parent) - one way
878 | |/
878 | |/
879 | o e-2 g -move-> f
879 | o e-2 g -move-> f
880 | |
880 | |
881 | o e-1 b -move-> g
881 | o e-1 b -move-> g
882 | |
882 | |
883 o | a-2: e -move-> f
883 o | a-2: e -move-> f
884 | |
884 | |
885 o | a-1: d -move-> e
885 o | a-1: d -move-> e
886 |/
886 |/
887 o i-2: c -move-> d, s -move-> t
887 o i-2: c -move-> d, s -move-> t
888 |
888 |
889 o i-1: a -move-> c, p -move-> s
889 o i-1: a -move-> c, p -move-> s
890 |
890 |
891 o i-0 initial commit: a b h
891 o i-0 initial commit: a b h
892
892
893
893
894 Decision from previous merge are properly chained with later merge
894 Decision from previous merge are properly chained with later merge
895 ------------------------------------------------------------------
895 ------------------------------------------------------------------
896
896
897 Subcase: chaining conflicting rename resolution
897 Subcase: chaining conflicting rename resolution
898 ```````````````````````````````````````````````
898 ```````````````````````````````````````````````
899
899
900 The "mAEm" and "mEAm" case create a rename tracking conflict on file 'f'. We
900 The "mAEm" and "mEAm" case create a rename tracking conflict on file 'f'. We
901 add more change on the respective branch and merge again. These second merge
901 add more change on the respective branch and merge again. These second merge
902 does not involve the file 'f' and the arbitration done within "mAEm" and "mEA"
902 does not involve the file 'f' and the arbitration done within "mAEm" and "mEA"
903 about that file should stay unchanged.
903 about that file should stay unchanged.
904
904
905 $ case_desc="chained merges (conflict -> simple) - same content everywhere"
905 $ case_desc="chained merges (conflict -> simple) - same content everywhere"
906
906
907 (extra unrelated changes)
907 (extra unrelated changes)
908
908
909 $ hg up 'desc("a-2")'
909 $ hg up 'desc("a-2")'
910 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
910 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
911 $ echo j > unrelated-j
911 $ echo j > unrelated-j
912 $ hg add unrelated-j
912 $ hg add unrelated-j
913 $ hg ci -m 'j-1: unrelated changes (based on the "a" series of changes)'
913 $ hg ci -m 'j-1: unrelated changes (based on the "a" series of changes)'
914 created new head
914 created new head
915
915
916 $ hg up 'desc("e-2")'
916 $ hg up 'desc("e-2")'
917 2 files updated, 0 files merged, 2 files removed, 0 files unresolved (no-changeset !)
917 2 files updated, 0 files merged, 2 files removed, 0 files unresolved (no-changeset !)
918 1 files updated, 0 files merged, 2 files removed, 0 files unresolved (changeset !)
918 1 files updated, 0 files merged, 2 files removed, 0 files unresolved (changeset !)
919 $ echo k > unrelated-k
919 $ echo k > unrelated-k
920 $ hg add unrelated-k
920 $ hg add unrelated-k
921 $ hg ci -m 'k-1: unrelated changes (based on "e" changes)'
921 $ hg ci -m 'k-1: unrelated changes (based on "e" changes)'
922 created new head
922 created new head
923
923
924 (merge variant 1)
924 (merge variant 1)
925
925
926 $ hg up 'desc("mAEm")'
926 $ hg up 'desc("mAEm")'
927 1 files updated, 0 files merged, 2 files removed, 0 files unresolved (no-changeset !)
927 1 files updated, 0 files merged, 2 files removed, 0 files unresolved (no-changeset !)
928 0 files updated, 0 files merged, 2 files removed, 0 files unresolved (changeset !)
928 0 files updated, 0 files merged, 2 files removed, 0 files unresolved (changeset !)
929 $ hg merge 'desc("k-1")'
929 $ hg merge 'desc("k-1")'
930 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
930 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
931 (branch merge, don't forget to commit)
931 (branch merge, don't forget to commit)
932 $ hg ci -m "mAE,Km: $case_desc"
932 $ hg ci -m "mAE,Km: $case_desc"
933
933
934 (merge variant 2)
934 (merge variant 2)
935
935
936 $ hg up 'desc("k-1")'
936 $ hg up 'desc("k-1")'
937 2 files updated, 0 files merged, 0 files removed, 0 files unresolved (no-changeset !)
937 2 files updated, 0 files merged, 0 files removed, 0 files unresolved (no-changeset !)
938 1 files updated, 0 files merged, 0 files removed, 0 files unresolved (changeset !)
938 1 files updated, 0 files merged, 0 files removed, 0 files unresolved (changeset !)
939
939
940 $ hg merge 'desc("mAEm")'
940 $ hg merge 'desc("mAEm")'
941 1 files updated, 0 files merged, 1 files removed, 0 files unresolved (no-changeset !)
941 1 files updated, 0 files merged, 1 files removed, 0 files unresolved (no-changeset !)
942 0 files updated, 0 files merged, 1 files removed, 0 files unresolved (changeset !)
942 0 files updated, 0 files merged, 1 files removed, 0 files unresolved (changeset !)
943 (branch merge, don't forget to commit)
943 (branch merge, don't forget to commit)
944 $ hg ci -m "mK,AEm: $case_desc"
944 $ hg ci -m "mK,AEm: $case_desc"
945 created new head
945 created new head
946
946
947 (merge variant 3)
947 (merge variant 3)
948
948
949 $ hg up 'desc("mEAm")'
949 $ hg up 'desc("mEAm")'
950 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
950 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
951 $ hg merge 'desc("j-1")'
951 $ hg merge 'desc("j-1")'
952 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
952 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
953 (branch merge, don't forget to commit)
953 (branch merge, don't forget to commit)
954 $ hg ci -m "mEA,Jm: $case_desc"
954 $ hg ci -m "mEA,Jm: $case_desc"
955
955
956 (merge variant 4)
956 (merge variant 4)
957
957
958 $ hg up 'desc("j-1")'
958 $ hg up 'desc("j-1")'
959 2 files updated, 0 files merged, 0 files removed, 0 files unresolved (no-changeset !)
959 2 files updated, 0 files merged, 0 files removed, 0 files unresolved (no-changeset !)
960 1 files updated, 0 files merged, 0 files removed, 0 files unresolved (changeset !)
960 1 files updated, 0 files merged, 0 files removed, 0 files unresolved (changeset !)
961 $ hg merge 'desc("mEAm")'
961 $ hg merge 'desc("mEAm")'
962 1 files updated, 0 files merged, 1 files removed, 0 files unresolved (no-changeset !)
962 1 files updated, 0 files merged, 1 files removed, 0 files unresolved (no-changeset !)
963 0 files updated, 0 files merged, 1 files removed, 0 files unresolved (changeset !)
963 0 files updated, 0 files merged, 1 files removed, 0 files unresolved (changeset !)
964 (branch merge, don't forget to commit)
964 (branch merge, don't forget to commit)
965 $ hg ci -m "mJ,EAm: $case_desc"
965 $ hg ci -m "mJ,EAm: $case_desc"
966 created new head
966 created new head
967
967
968
968
969 $ hg log -G --rev '::(desc("mAE,Km") + desc("mK,AEm") + desc("mEA,Jm") + desc("mJ,EAm"))'
969 $ hg log -G --rev '::(desc("mAE,Km") + desc("mK,AEm") + desc("mEA,Jm") + desc("mJ,EAm"))'
970 @ mJ,EAm: chained merges (conflict -> simple) - same content everywhere
970 @ mJ,EAm: chained merges (conflict -> simple) - same content everywhere
971 |\
971 |\
972 +---o mEA,Jm: chained merges (conflict -> simple) - same content everywhere
972 +---o mEA,Jm: chained merges (conflict -> simple) - same content everywhere
973 | |/
973 | |/
974 | | o mK,AEm: chained merges (conflict -> simple) - same content everywhere
974 | | o mK,AEm: chained merges (conflict -> simple) - same content everywhere
975 | | |\
975 | | |\
976 | | +---o mAE,Km: chained merges (conflict -> simple) - same content everywhere
976 | | +---o mAE,Km: chained merges (conflict -> simple) - same content everywhere
977 | | | |/
977 | | | |/
978 | | | o k-1: unrelated changes (based on "e" changes)
978 | | | o k-1: unrelated changes (based on "e" changes)
979 | | | |
979 | | | |
980 | o | | j-1: unrelated changes (based on the "a" series of changes)
980 | o | | j-1: unrelated changes (based on the "a" series of changes)
981 | | | |
981 | | | |
982 o-----+ mEAm-0 merge with copies info on both side - A side: rename d to f, E side: b to f, (same content for f) - the other way
982 o-----+ mEAm-0 merge with copies info on both side - A side: rename d to f, E side: b to f, (same content for f) - the other way
983 |/ / /
983 |/ / /
984 | o / mAEm-0 merge with copies info on both side - A side: rename d to f, E side: b to f, (same content for f) - one way
984 | o / mAEm-0 merge with copies info on both side - A side: rename d to f, E side: b to f, (same content for f) - one way
985 |/|/
985 |/|/
986 | o e-2 g -move-> f
986 | o e-2 g -move-> f
987 | |
987 | |
988 | o e-1 b -move-> g
988 | o e-1 b -move-> g
989 | |
989 | |
990 o | a-2: e -move-> f
990 o | a-2: e -move-> f
991 | |
991 | |
992 o | a-1: d -move-> e
992 o | a-1: d -move-> e
993 |/
993 |/
994 o i-2: c -move-> d, s -move-> t
994 o i-2: c -move-> d, s -move-> t
995 |
995 |
996 o i-1: a -move-> c, p -move-> s
996 o i-1: a -move-> c, p -move-> s
997 |
997 |
998 o i-0 initial commit: a b h
998 o i-0 initial commit: a b h
999
999
1000
1000
1001 Subcase: chaining conflicting rename resolution, with actual merging happening
1001 Subcase: chaining conflicting rename resolution, with actual merging happening
1002 ``````````````````````````````````````````````````````````````````````````````
1002 ``````````````````````````````````````````````````````````````````````````````
1003
1003
1004 The "mPQm" and "mQPm" case create a rename tracking conflict on file 't'. We
1004 The "mPQm" and "mQPm" case create a rename tracking conflict on file 't'. We
1005 add more change on the respective branch and merge again. These second merge
1005 add more change on the respective branch and merge again. These second merge
1006 does not involve the file 't' and the arbitration done within "mPQm" and "mQP"
1006 does not involve the file 't' and the arbitration done within "mPQm" and "mQP"
1007 about that file should stay unchanged.
1007 about that file should stay unchanged.
1008
1008
1009 $ case_desc="chained merges (conflict -> simple) - different content"
1009 $ case_desc="chained merges (conflict -> simple) - different content"
1010
1010
1011 (extra unrelated changes)
1011 (extra unrelated changes)
1012
1012
1013 $ hg up 'desc("p-2")'
1013 $ hg up 'desc("p-2")'
1014 3 files updated, 0 files merged, 3 files removed, 0 files unresolved
1014 3 files updated, 0 files merged, 3 files removed, 0 files unresolved
1015 $ echo s > unrelated-s
1015 $ echo s > unrelated-s
1016 $ hg add unrelated-s
1016 $ hg add unrelated-s
1017 $ hg ci -m 's-1: unrelated changes (based on the "p" series of changes)'
1017 $ hg ci -m 's-1: unrelated changes (based on the "p" series of changes)'
1018 created new head
1018 created new head
1019
1019
1020 $ hg up 'desc("q-2")'
1020 $ hg up 'desc("q-2")'
1021 2 files updated, 0 files merged, 2 files removed, 0 files unresolved
1021 2 files updated, 0 files merged, 2 files removed, 0 files unresolved
1022 $ echo t > unrelated-t
1022 $ echo t > unrelated-t
1023 $ hg add unrelated-t
1023 $ hg add unrelated-t
1024 $ hg ci -m 't-1: unrelated changes (based on "q" changes)'
1024 $ hg ci -m 't-1: unrelated changes (based on "q" changes)'
1025 created new head
1025 created new head
1026
1026
1027 (merge variant 1)
1027 (merge variant 1)
1028
1028
1029 $ hg up 'desc("mPQm")'
1029 $ hg up 'desc("mPQm")'
1030 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
1030 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
1031 $ hg merge 'desc("t-1")'
1031 $ hg merge 'desc("t-1")'
1032 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1032 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1033 (branch merge, don't forget to commit)
1033 (branch merge, don't forget to commit)
1034 $ hg ci -m "mPQ,Tm: $case_desc"
1034 $ hg ci -m "mPQ,Tm: $case_desc"
1035
1035
1036 (merge variant 2)
1036 (merge variant 2)
1037
1037
1038 $ hg up 'desc("t-1")'
1038 $ hg up 'desc("t-1")'
1039 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
1039 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
1040
1040
1041 $ hg merge 'desc("mPQm")'
1041 $ hg merge 'desc("mPQm")'
1042 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
1042 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
1043 (branch merge, don't forget to commit)
1043 (branch merge, don't forget to commit)
1044 $ hg ci -m "mT,PQm: $case_desc"
1044 $ hg ci -m "mT,PQm: $case_desc"
1045 created new head
1045 created new head
1046
1046
1047 (merge variant 3)
1047 (merge variant 3)
1048
1048
1049 $ hg up 'desc("mQPm")'
1049 $ hg up 'desc("mQPm")'
1050 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
1050 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
1051 $ hg merge 'desc("s-1")'
1051 $ hg merge 'desc("s-1")'
1052 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1052 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1053 (branch merge, don't forget to commit)
1053 (branch merge, don't forget to commit)
1054 $ hg ci -m "mQP,Sm: $case_desc"
1054 $ hg ci -m "mQP,Sm: $case_desc"
1055
1055
1056 (merge variant 4)
1056 (merge variant 4)
1057
1057
1058 $ hg up 'desc("s-1")'
1058 $ hg up 'desc("s-1")'
1059 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
1059 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
1060 $ hg merge 'desc("mQPm")'
1060 $ hg merge 'desc("mQPm")'
1061 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
1061 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
1062 (branch merge, don't forget to commit)
1062 (branch merge, don't forget to commit)
1063 $ hg ci -m "mS,QPm: $case_desc"
1063 $ hg ci -m "mS,QPm: $case_desc"
1064 created new head
1064 created new head
1065 $ hg up null --quiet
1065 $ hg up null --quiet
1066
1066
1067
1067
1068 $ hg log -G --rev '::(desc("mPQ,Tm") + desc("mT,PQm") + desc("mQP,Sm") + desc("mS,QPm"))'
1068 $ hg log -G --rev '::(desc("mPQ,Tm") + desc("mT,PQm") + desc("mQP,Sm") + desc("mS,QPm"))'
1069 o mS,QPm: chained merges (conflict -> simple) - different content
1069 o mS,QPm: chained merges (conflict -> simple) - different content
1070 |\
1070 |\
1071 +---o mQP,Sm: chained merges (conflict -> simple) - different content
1071 +---o mQP,Sm: chained merges (conflict -> simple) - different content
1072 | |/
1072 | |/
1073 | | o mT,PQm: chained merges (conflict -> simple) - different content
1073 | | o mT,PQm: chained merges (conflict -> simple) - different content
1074 | | |\
1074 | | |\
1075 | | +---o mPQ,Tm: chained merges (conflict -> simple) - different content
1075 | | +---o mPQ,Tm: chained merges (conflict -> simple) - different content
1076 | | | |/
1076 | | | |/
1077 | | | o t-1: unrelated changes (based on "q" changes)
1077 | | | o t-1: unrelated changes (based on "q" changes)
1078 | | | |
1078 | | | |
1079 | o | | s-1: unrelated changes (based on the "p" series of changes)
1079 | o | | s-1: unrelated changes (based on the "p" series of changes)
1080 | | | |
1080 | | | |
1081 o-----+ mQPm-0 merge with copies info on both side - P side: rename t to v, Q side: r to v, (different content) - the other way
1081 o-----+ mQPm-0 merge with copies info on both side - P side: rename t to v, Q side: r to v, (different content) - the other way
1082 |/ / /
1082 |/ / /
1083 | o / mPQm-0 merge with copies info on both side - P side: rename t to v, Q side: r to v, (different content) - one way
1083 | o / mPQm-0 merge with copies info on both side - P side: rename t to v, Q side: r to v, (different content) - one way
1084 |/|/
1084 |/|/
1085 | o q-2 w -move-> v
1085 | o q-2 w -move-> v
1086 | |
1086 | |
1087 | o q-1 r -move-> w
1087 | o q-1 r -move-> w
1088 | |
1088 | |
1089 o | p-2: u -move-> v
1089 o | p-2: u -move-> v
1090 | |
1090 | |
1091 o | p-1: t -move-> u
1091 o | p-1: t -move-> u
1092 |/
1092 |/
1093 o i-2: c -move-> d, s -move-> t
1093 o i-2: c -move-> d, s -move-> t
1094 |
1094 |
1095 o i-1: a -move-> c, p -move-> s
1095 o i-1: a -move-> c, p -move-> s
1096 |
1096 |
1097 o i-0 initial commit: a b h
1097 o i-0 initial commit: a b h
1098
1098
1099
1099
1100 Subcase: chaining salvage information during a merge
1100 Subcase: chaining salvage information during a merge
1101 ````````````````````````````````````````````````````
1101 ````````````````````````````````````````````````````
1102
1102
1103 We add more change on the branch were the file was deleted. merging again
1103 We add more change on the branch were the file was deleted. merging again
1104 should preserve the fact eh file was salvaged.
1104 should preserve the fact eh file was salvaged.
1105
1105
1106 $ case_desc="chained merges (salvaged -> simple) - same content (when the file exists)"
1106 $ case_desc="chained merges (salvaged -> simple) - same content (when the file exists)"
1107
1107
1108 (creating the change)
1108 (creating the change)
1109
1109
1110 $ hg up 'desc("c-1")'
1110 $ hg up 'desc("c-1")'
1111 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
1111 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
1112 $ echo l > unrelated-l
1112 $ echo l > unrelated-l
1113 $ hg add unrelated-l
1113 $ hg add unrelated-l
1114 $ hg ci -m 'l-1: unrelated changes (based on "c" changes)'
1114 $ hg ci -m 'l-1: unrelated changes (based on "c" changes)'
1115 created new head
1115 created new head
1116
1116
1117 (Merge variant 1)
1117 (Merge variant 1)
1118
1118
1119 $ hg up 'desc("mBC-revert-m")'
1119 $ hg up 'desc("mBC-revert-m")'
1120 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
1120 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
1121 $ hg merge 'desc("l-1")'
1121 $ hg merge 'desc("l-1")'
1122 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1122 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1123 (branch merge, don't forget to commit)
1123 (branch merge, don't forget to commit)
1124 $ hg ci -m "mBC+revert,Lm: $case_desc"
1124 $ hg ci -m "mBC+revert,Lm: $case_desc"
1125
1125
1126 (Merge variant 2)
1126 (Merge variant 2)
1127
1127
1128 $ hg up 'desc("mCB-revert-m")'
1128 $ hg up 'desc("mCB-revert-m")'
1129 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1129 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1130 $ hg merge 'desc("l-1")'
1130 $ hg merge 'desc("l-1")'
1131 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1131 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1132 (branch merge, don't forget to commit)
1132 (branch merge, don't forget to commit)
1133 $ hg ci -m "mCB+revert,Lm: $case_desc"
1133 $ hg ci -m "mCB+revert,Lm: $case_desc"
1134
1134
1135 (Merge variant 3)
1135 (Merge variant 3)
1136
1136
1137 $ hg up 'desc("l-1")'
1137 $ hg up 'desc("l-1")'
1138 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
1138 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
1139
1139
1140 $ hg merge 'desc("mBC-revert-m")'
1140 $ hg merge 'desc("mBC-revert-m")'
1141 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
1141 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
1142 (branch merge, don't forget to commit)
1142 (branch merge, don't forget to commit)
1143 $ hg ci -m "mL,BC+revertm: $case_desc"
1143 $ hg ci -m "mL,BC+revertm: $case_desc"
1144 created new head
1144 created new head
1145
1145
1146 (Merge variant 4)
1146 (Merge variant 4)
1147
1147
1148 $ hg up 'desc("l-1")'
1148 $ hg up 'desc("l-1")'
1149 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
1149 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
1150
1150
1151 $ hg merge 'desc("mCB-revert-m")'
1151 $ hg merge 'desc("mCB-revert-m")'
1152 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
1152 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
1153 (branch merge, don't forget to commit)
1153 (branch merge, don't forget to commit)
1154 $ hg ci -m "mL,CB+revertm: $case_desc"
1154 $ hg ci -m "mL,CB+revertm: $case_desc"
1155 created new head
1155 created new head
1156
1156
1157 $ hg log -G --rev '::(desc("mBC+revert,Lm") + desc("mCB+revert,Lm") + desc("mL,BC+revertm") + desc("mL,CB+revertm"))'
1157 $ hg log -G --rev '::(desc("mBC+revert,Lm") + desc("mCB+revert,Lm") + desc("mL,BC+revertm") + desc("mL,CB+revertm"))'
1158 @ mL,CB+revertm: chained merges (salvaged -> simple) - same content (when the file exists)
1158 @ mL,CB+revertm: chained merges (salvaged -> simple) - same content (when the file exists)
1159 |\
1159 |\
1160 | | o mL,BC+revertm: chained merges (salvaged -> simple) - same content (when the file exists)
1160 | | o mL,BC+revertm: chained merges (salvaged -> simple) - same content (when the file exists)
1161 | |/|
1161 | |/|
1162 +-+---o mCB+revert,Lm: chained merges (salvaged -> simple) - same content (when the file exists)
1162 +-+---o mCB+revert,Lm: chained merges (salvaged -> simple) - same content (when the file exists)
1163 | | |
1163 | | |
1164 | +---o mBC+revert,Lm: chained merges (salvaged -> simple) - same content (when the file exists)
1164 | +---o mBC+revert,Lm: chained merges (salvaged -> simple) - same content (when the file exists)
1165 | | |/
1165 | | |/
1166 | o | l-1: unrelated changes (based on "c" changes)
1166 | o | l-1: unrelated changes (based on "c" changes)
1167 | | |
1167 | | |
1168 | | o mBC-revert-m-0 merge explicitely revive deleted file - B side: unrelated change, C side: delete d (restored by merge) - the other way
1168 | | o mBC-revert-m-0 merge explicitely revive deleted file - B side: unrelated change, C side: delete d (restored by merge) - the other way
1169 | |/|
1169 | |/|
1170 o---+ mCB-revert-m-0 merge explicitely revive deleted file - B side: unrelated change, C side: delete d (restored by merge) - one way
1170 o---+ mCB-revert-m-0 merge explicitely revive deleted file - B side: unrelated change, C side: delete d (restored by merge) - one way
1171 |/ /
1171 |/ /
1172 o | c-1 delete d
1172 o | c-1 delete d
1173 | |
1173 | |
1174 | o b-1: b update
1174 | o b-1: b update
1175 |/
1175 |/
1176 o i-2: c -move-> d, s -move-> t
1176 o i-2: c -move-> d, s -move-> t
1177 |
1177 |
1178 o i-1: a -move-> c, p -move-> s
1178 o i-1: a -move-> c, p -move-> s
1179 |
1179 |
1180 o i-0 initial commit: a b h
1180 o i-0 initial commit: a b h
1181
1181
1182
1182
1183
1183
1184 Subcase: chaining "merged" information during a merge
1184 Subcase: chaining "merged" information during a merge
1185 ``````````````````````````````````````````````````````
1185 ``````````````````````````````````````````````````````
1186
1186
1187 When a non-rename change are merged with a copy overwrite, the merge pick the copy source from (p1) as the reference. We should preserve this information in subsequent merges.
1187 When a non-rename change are merged with a copy overwrite, the merge pick the copy source from (p1) as the reference. We should preserve this information in subsequent merges.
1188
1188
1189 $ case_desc="chained merges (copy-overwrite -> simple) - same content"
1189 $ case_desc="chained merges (copy-overwrite -> simple) - same content"
1190
1190
1191 (extra unrelated changes)
1191 (extra unrelated changes)
1192
1192
1193 $ hg up 'desc("f-2")'
1193 $ hg up 'desc("f-2")'
1194 2 files updated, 0 files merged, 2 files removed, 0 files unresolved (no-changeset !)
1194 2 files updated, 0 files merged, 2 files removed, 0 files unresolved (no-changeset !)
1195 1 files updated, 0 files merged, 2 files removed, 0 files unresolved (changeset !)
1195 1 files updated, 0 files merged, 2 files removed, 0 files unresolved (changeset !)
1196 $ echo n > unrelated-n
1196 $ echo n > unrelated-n
1197 $ hg add unrelated-n
1197 $ hg add unrelated-n
1198 $ hg ci -m 'n-1: unrelated changes (based on the "f" series of changes)'
1198 $ hg ci -m 'n-1: unrelated changes (based on the "f" series of changes)'
1199 created new head
1199 created new head
1200
1200
1201 $ hg up 'desc("g-1")'
1201 $ hg up 'desc("g-1")'
1202 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
1202 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
1203 $ echo o > unrelated-o
1203 $ echo o > unrelated-o
1204 $ hg add unrelated-o
1204 $ hg add unrelated-o
1205 $ hg ci -m 'o-1: unrelated changes (based on "g" changes)'
1205 $ hg ci -m 'o-1: unrelated changes (based on "g" changes)'
1206 created new head
1206 created new head
1207
1207
1208 (merge variant 1)
1208 (merge variant 1)
1209
1209
1210 $ hg up 'desc("mFGm")'
1210 $ hg up 'desc("mFGm")'
1211 1 files updated, 0 files merged, 2 files removed, 0 files unresolved (no-changeset !)
1211 1 files updated, 0 files merged, 2 files removed, 0 files unresolved (no-changeset !)
1212 0 files updated, 0 files merged, 2 files removed, 0 files unresolved (changeset !)
1212 0 files updated, 0 files merged, 2 files removed, 0 files unresolved (changeset !)
1213 $ hg merge 'desc("o-1")'
1213 $ hg merge 'desc("o-1")'
1214 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1214 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1215 (branch merge, don't forget to commit)
1215 (branch merge, don't forget to commit)
1216 $ hg ci -m "mFG,Om: $case_desc"
1216 $ hg ci -m "mFG,Om: $case_desc"
1217
1217
1218 (merge variant 2)
1218 (merge variant 2)
1219
1219
1220 $ hg up 'desc("o-1")'
1220 $ hg up 'desc("o-1")'
1221 2 files updated, 0 files merged, 0 files removed, 0 files unresolved (no-changeset !)
1221 2 files updated, 0 files merged, 0 files removed, 0 files unresolved (no-changeset !)
1222 1 files updated, 0 files merged, 0 files removed, 0 files unresolved (changeset !)
1222 1 files updated, 0 files merged, 0 files removed, 0 files unresolved (changeset !)
1223 $ hg merge 'desc("FGm")'
1223 $ hg merge 'desc("FGm")'
1224 1 files updated, 0 files merged, 1 files removed, 0 files unresolved (no-changeset !)
1224 1 files updated, 0 files merged, 1 files removed, 0 files unresolved (no-changeset !)
1225 0 files updated, 0 files merged, 1 files removed, 0 files unresolved (changeset !)
1225 0 files updated, 0 files merged, 1 files removed, 0 files unresolved (changeset !)
1226 (branch merge, don't forget to commit)
1226 (branch merge, don't forget to commit)
1227 $ hg ci -m "mO,FGm: $case_desc"
1227 $ hg ci -m "mO,FGm: $case_desc"
1228 created new head
1228 created new head
1229
1229
1230 (merge variant 3)
1230 (merge variant 3)
1231
1231
1232 $ hg up 'desc("mGFm")'
1232 $ hg up 'desc("mGFm")'
1233 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1233 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1234 $ hg merge 'desc("n-1")'
1234 $ hg merge 'desc("n-1")'
1235 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1235 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1236 (branch merge, don't forget to commit)
1236 (branch merge, don't forget to commit)
1237 $ hg ci -m "mGF,Nm: $case_desc"
1237 $ hg ci -m "mGF,Nm: $case_desc"
1238
1238
1239 (merge variant 4)
1239 (merge variant 4)
1240
1240
1241 $ hg up 'desc("n-1")'
1241 $ hg up 'desc("n-1")'
1242 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1242 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1243 $ hg merge 'desc("mGFm")'
1243 $ hg merge 'desc("mGFm")'
1244 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1244 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1245 (branch merge, don't forget to commit)
1245 (branch merge, don't forget to commit)
1246 $ hg ci -m "mN,GFm: $case_desc"
1246 $ hg ci -m "mN,GFm: $case_desc"
1247 created new head
1247 created new head
1248
1248
1249 $ hg log -G --rev '::(desc("mFG,Om") + desc("mO,FGm") + desc("mGF,Nm") + desc("mN,GFm"))'
1249 $ hg log -G --rev '::(desc("mFG,Om") + desc("mO,FGm") + desc("mGF,Nm") + desc("mN,GFm"))'
1250 @ mN,GFm: chained merges (copy-overwrite -> simple) - same content
1250 @ mN,GFm: chained merges (copy-overwrite -> simple) - same content
1251 |\
1251 |\
1252 +---o mGF,Nm: chained merges (copy-overwrite -> simple) - same content
1252 +---o mGF,Nm: chained merges (copy-overwrite -> simple) - same content
1253 | |/
1253 | |/
1254 | | o mO,FGm: chained merges (copy-overwrite -> simple) - same content
1254 | | o mO,FGm: chained merges (copy-overwrite -> simple) - same content
1255 | | |\
1255 | | |\
1256 | | +---o mFG,Om: chained merges (copy-overwrite -> simple) - same content
1256 | | +---o mFG,Om: chained merges (copy-overwrite -> simple) - same content
1257 | | | |/
1257 | | | |/
1258 | | | o o-1: unrelated changes (based on "g" changes)
1258 | | | o o-1: unrelated changes (based on "g" changes)
1259 | | | |
1259 | | | |
1260 | o | | n-1: unrelated changes (based on the "f" series of changes)
1260 | o | | n-1: unrelated changes (based on the "f" series of changes)
1261 | | | |
1261 | | | |
1262 o-----+ mGFm-0 merge - G side: content change, F side: copy overwrite, no content change - the other way
1262 o-----+ mGFm-0 merge - G side: content change, F side: copy overwrite, no content change - the other way
1263 |/ / /
1263 |/ / /
1264 | o / mFGm-0 merge - G side: content change, F side: copy overwrite, no content change - one way
1264 | o / mFGm-0 merge - G side: content change, F side: copy overwrite, no content change - one way
1265 |/|/
1265 |/|/
1266 | o g-1: update d
1266 | o g-1: update d
1267 | |
1267 | |
1268 o | f-2: rename i -> d
1268 o | f-2: rename i -> d
1269 | |
1269 | |
1270 o | f-1: rename h -> i
1270 o | f-1: rename h -> i
1271 |/
1271 |/
1272 o i-2: c -move-> d, s -move-> t
1272 o i-2: c -move-> d, s -move-> t
1273 |
1273 |
1274 o i-1: a -move-> c, p -move-> s
1274 o i-1: a -move-> c, p -move-> s
1275 |
1275 |
1276 o i-0 initial commit: a b h
1276 o i-0 initial commit: a b h
1277
1277
1278
1278
1279 Subcase: chaining conflicting rename resolution, with extra change during the merge
1279 Subcase: chaining conflicting rename resolution, with extra change during the merge
1280 ```````````````````````````````````````````````````````````````````````````````````
1280 ```````````````````````````````````````````````````````````````````````````````````
1281
1281
1282 The "mEA-change-m-0" and "mAE-change-m-0" case create a rename tracking conflict on file 'f'. We
1282 The "mEA-change-m-0" and "mAE-change-m-0" case create a rename tracking conflict on file 'f'. We
1283 add more change on the respective branch and merge again. These second merge
1283 add more change on the respective branch and merge again. These second merge
1284 does not involve the file 'f' and the arbitration done within "mAEm" and "mEA"
1284 does not involve the file 'f' and the arbitration done within "mAEm" and "mEA"
1285 about that file should stay unchanged.
1285 about that file should stay unchanged.
1286
1286
1287 $ case_desc="chained merges (conflict+change -> simple) - same content on both branch in the initial merge"
1287 $ case_desc="chained merges (conflict+change -> simple) - same content on both branch in the initial merge"
1288
1288
1289
1289
1290 (merge variant 1)
1290 (merge variant 1)
1291
1291
1292 $ hg up 'desc("mAE-change-m")'
1292 $ hg up 'desc("mAE-change-m")'
1293 2 files updated, 0 files merged, 3 files removed, 0 files unresolved
1293 2 files updated, 0 files merged, 3 files removed, 0 files unresolved
1294 $ hg merge 'desc("k-1")'
1294 $ hg merge 'desc("k-1")'
1295 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1295 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1296 (branch merge, don't forget to commit)
1296 (branch merge, don't forget to commit)
1297 $ hg ci -m "mAE-change,Km: $case_desc"
1297 $ hg ci -m "mAE-change,Km: $case_desc"
1298
1298
1299 (merge variant 2)
1299 (merge variant 2)
1300
1300
1301 $ hg up 'desc("k-1")'
1301 $ hg up 'desc("k-1")'
1302 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
1302 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
1303
1303
1304 $ hg merge 'desc("mAE-change-m")'
1304 $ hg merge 'desc("mAE-change-m")'
1305 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
1305 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
1306 (branch merge, don't forget to commit)
1306 (branch merge, don't forget to commit)
1307 $ hg ci -m "mK,AE-change-m: $case_desc"
1307 $ hg ci -m "mK,AE-change-m: $case_desc"
1308 created new head
1308 created new head
1309
1309
1310 (merge variant 3)
1310 (merge variant 3)
1311
1311
1312 $ hg up 'desc("mEA-change-m")'
1312 $ hg up 'desc("mEA-change-m")'
1313 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
1313 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
1314 $ hg merge 'desc("j-1")'
1314 $ hg merge 'desc("j-1")'
1315 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1315 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1316 (branch merge, don't forget to commit)
1316 (branch merge, don't forget to commit)
1317 $ hg ci -m "mEA-change,Jm: $case_desc"
1317 $ hg ci -m "mEA-change,Jm: $case_desc"
1318
1318
1319 (merge variant 4)
1319 (merge variant 4)
1320
1320
1321 $ hg up 'desc("j-1")'
1321 $ hg up 'desc("j-1")'
1322 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
1322 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
1323 $ hg merge 'desc("mEA-change-m")'
1323 $ hg merge 'desc("mEA-change-m")'
1324 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
1324 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
1325 (branch merge, don't forget to commit)
1325 (branch merge, don't forget to commit)
1326 $ hg ci -m "mJ,EA-change-m: $case_desc"
1326 $ hg ci -m "mJ,EA-change-m: $case_desc"
1327 created new head
1327 created new head
1328
1328
1329
1329
1330 $ hg log -G --rev '::(desc("mAE-change,Km") + desc("mK,AE-change-m") + desc("mEA-change,Jm") + desc("mJ,EA-change-m"))'
1330 $ hg log -G --rev '::(desc("mAE-change,Km") + desc("mK,AE-change-m") + desc("mEA-change,Jm") + desc("mJ,EA-change-m"))'
1331 @ mJ,EA-change-m: chained merges (conflict+change -> simple) - same content on both branch in the initial merge
1331 @ mJ,EA-change-m: chained merges (conflict+change -> simple) - same content on both branch in the initial merge
1332 |\
1332 |\
1333 +---o mEA-change,Jm: chained merges (conflict+change -> simple) - same content on both branch in the initial merge
1333 +---o mEA-change,Jm: chained merges (conflict+change -> simple) - same content on both branch in the initial merge
1334 | |/
1334 | |/
1335 | | o mK,AE-change-m: chained merges (conflict+change -> simple) - same content on both branch in the initial merge
1335 | | o mK,AE-change-m: chained merges (conflict+change -> simple) - same content on both branch in the initial merge
1336 | | |\
1336 | | |\
1337 | | +---o mAE-change,Km: chained merges (conflict+change -> simple) - same content on both branch in the initial merge
1337 | | +---o mAE-change,Km: chained merges (conflict+change -> simple) - same content on both branch in the initial merge
1338 | | | |/
1338 | | | |/
1339 | | | o k-1: unrelated changes (based on "e" changes)
1339 | | | o k-1: unrelated changes (based on "e" changes)
1340 | | | |
1340 | | | |
1341 | o | | j-1: unrelated changes (based on the "a" series of changes)
1341 | o | | j-1: unrelated changes (based on the "a" series of changes)
1342 | | | |
1342 | | | |
1343 o-----+ mEA-change-m-0 merge with file update and copies info on both side - A side: rename d to f, E side: b to f, (same content for f in parent) - the other way
1343 o-----+ mEA-change-m-0 merge with file update and copies info on both side - A side: rename d to f, E side: b to f, (same content for f in parent) - the other way
1344 |/ / /
1344 |/ / /
1345 | o / mAE-change-m-0 merge with file update and copies info on both side - A side: rename d to f, E side: b to f, (same content for f in parent) - one way
1345 | o / mAE-change-m-0 merge with file update and copies info on both side - A side: rename d to f, E side: b to f, (same content for f in parent) - one way
1346 |/|/
1346 |/|/
1347 | o e-2 g -move-> f
1347 | o e-2 g -move-> f
1348 | |
1348 | |
1349 | o e-1 b -move-> g
1349 | o e-1 b -move-> g
1350 | |
1350 | |
1351 o | a-2: e -move-> f
1351 o | a-2: e -move-> f
1352 | |
1352 | |
1353 o | a-1: d -move-> e
1353 o | a-1: d -move-> e
1354 |/
1354 |/
1355 o i-2: c -move-> d, s -move-> t
1355 o i-2: c -move-> d, s -move-> t
1356 |
1356 |
1357 o i-1: a -move-> c, p -move-> s
1357 o i-1: a -move-> c, p -move-> s
1358 |
1358 |
1359 o i-0 initial commit: a b h
1359 o i-0 initial commit: a b h
1360
1360
1361
1361
1362 Summary of all created cases
1362 Summary of all created cases
1363 ----------------------------
1363 ----------------------------
1364
1364
1365 $ hg up --quiet null
1365 $ hg up --quiet null
1366
1366
1367 (This exists to help keeping a compact list of the various cases we have built)
1367 (This exists to help keeping a compact list of the various cases we have built)
1368
1368
1369 $ hg log -T '{desc|firstline}\n'| sort
1369 $ hg log -T '{desc|firstline}\n'| sort
1370 a-1: d -move-> e
1370 a-1: d -move-> e
1371 a-2: e -move-> f
1371 a-2: e -move-> f
1372 b-1: b update
1372 b-1: b update
1373 c-1 delete d
1373 c-1 delete d
1374 d-1 delete d
1374 d-1 delete d
1375 d-2 re-add d
1375 d-2 re-add d
1376 e-1 b -move-> g
1376 e-1 b -move-> g
1377 e-2 g -move-> f
1377 e-2 g -move-> f
1378 f-1: rename h -> i
1378 f-1: rename h -> i
1379 f-2: rename i -> d
1379 f-2: rename i -> d
1380 g-1: update d
1380 g-1: update d
1381 h-1: b -(move)-> d
1381 h-1: b -(move)-> d
1382 i-0 initial commit: a b h
1382 i-0 initial commit: a b h
1383 i-1: a -move-> c, p -move-> s
1383 i-1: a -move-> c, p -move-> s
1384 i-2: c -move-> d, s -move-> t
1384 i-2: c -move-> d, s -move-> t
1385 j-1: unrelated changes (based on the "a" series of changes)
1385 j-1: unrelated changes (based on the "a" series of changes)
1386 k-1: unrelated changes (based on "e" changes)
1386 k-1: unrelated changes (based on "e" changes)
1387 l-1: unrelated changes (based on "c" changes)
1387 l-1: unrelated changes (based on "c" changes)
1388 mABm-0 simple merge - A side: multiple renames, B side: unrelated update - the other way
1388 mABm-0 simple merge - A side: multiple renames, B side: unrelated update - the other way
1389 mAE,Km: chained merges (conflict -> simple) - same content everywhere
1389 mAE,Km: chained merges (conflict -> simple) - same content everywhere
1390 mAE-change,Km: chained merges (conflict+change -> simple) - same content on both branch in the initial merge
1390 mAE-change,Km: chained merges (conflict+change -> simple) - same content on both branch in the initial merge
1391 mAE-change-m-0 merge with file update and copies info on both side - A side: rename d to f, E side: b to f, (same content for f in parent) - one way
1391 mAE-change-m-0 merge with file update and copies info on both side - A side: rename d to f, E side: b to f, (same content for f in parent) - one way
1392 mAEm-0 merge with copies info on both side - A side: rename d to f, E side: b to f, (same content for f) - one way
1392 mAEm-0 merge with copies info on both side - A side: rename d to f, E side: b to f, (same content for f) - one way
1393 mBAm-0 simple merge - A side: multiple renames, B side: unrelated update - one way
1393 mBAm-0 simple merge - A side: multiple renames, B side: unrelated update - one way
1394 mBC+revert,Lm: chained merges (salvaged -> simple) - same content (when the file exists)
1394 mBC+revert,Lm: chained merges (salvaged -> simple) - same content (when the file exists)
1395 mBC-revert-m-0 merge explicitely revive deleted file - B side: unrelated change, C side: delete d (restored by merge) - the other way
1395 mBC-revert-m-0 merge explicitely revive deleted file - B side: unrelated change, C side: delete d (restored by merge) - the other way
1396 mBCm-0 simple merge - C side: delete a file with copies history , B side: unrelated update - one way
1396 mBCm-0 simple merge - C side: delete a file with copies history , B side: unrelated update - one way
1397 mBCm-1 re-add d
1397 mBCm-1 re-add d
1398 mBDm-0 simple merge - B side: unrelated update, D side: delete and recreate a file (with different content) - one way
1398 mBDm-0 simple merge - B side: unrelated update, D side: delete and recreate a file (with different content) - one way
1399 mBFm-0 simple merge - B side: unrelated change, F side: overwrite d with a copy (from h->i->d) - one way
1399 mBFm-0 simple merge - B side: unrelated change, F side: overwrite d with a copy (from h->i->d) - one way
1400 mBRm-0 simple merge - B side: unrelated change, R side: overwrite d with a copy (from r->x->t) different content - one way
1400 mBRm-0 simple merge - B side: unrelated change, R side: overwrite d with a copy (from r->x->t) different content - one way
1401 mCB+revert,Lm: chained merges (salvaged -> simple) - same content (when the file exists)
1401 mCB+revert,Lm: chained merges (salvaged -> simple) - same content (when the file exists)
1402 mCB-revert-m-0 merge explicitely revive deleted file - B side: unrelated change, C side: delete d (restored by merge) - one way
1402 mCB-revert-m-0 merge explicitely revive deleted file - B side: unrelated change, C side: delete d (restored by merge) - one way
1403 mCBm-0 simple merge - C side: delete a file with copies history , B side: unrelated update - the other way
1403 mCBm-0 simple merge - C side: delete a file with copies history , B side: unrelated update - the other way
1404 mCBm-1 re-add d
1404 mCBm-1 re-add d
1405 mCGm-0 merge updated/deleted - revive the file (updated content) - one way
1405 mCGm-0 merge updated/deleted - revive the file (updated content) - one way
1406 mCH-delete-before-conflict-m-0 simple merge - C side: d is the results of renames then deleted, H side: d is result of another rename (same content as the other branch) - one way
1406 mCH-delete-before-conflict-m-0 simple merge - C side: d is the results of renames then deleted, H side: d is result of another rename (same content as the other branch) - one way
1407 mDBm-0 simple merge - B side: unrelated update, D side: delete and recreate a file (with different content) - the other way
1407 mDBm-0 simple merge - B side: unrelated update, D side: delete and recreate a file (with different content) - the other way
1408 mDGm-0 actual content merge, copies on one side - D side: delete and re-add (different content), G side: update content - one way
1408 mDGm-0 actual content merge, copies on one side - D side: delete and re-add (different content), G side: update content - one way
1409 mEA,Jm: chained merges (conflict -> simple) - same content everywhere
1409 mEA,Jm: chained merges (conflict -> simple) - same content everywhere
1410 mEA-change,Jm: chained merges (conflict+change -> simple) - same content on both branch in the initial merge
1410 mEA-change,Jm: chained merges (conflict+change -> simple) - same content on both branch in the initial merge
1411 mEA-change-m-0 merge with file update and copies info on both side - A side: rename d to f, E side: b to f, (same content for f in parent) - the other way
1411 mEA-change-m-0 merge with file update and copies info on both side - A side: rename d to f, E side: b to f, (same content for f in parent) - the other way
1412 mEAm-0 merge with copies info on both side - A side: rename d to f, E side: b to f, (same content for f) - the other way
1412 mEAm-0 merge with copies info on both side - A side: rename d to f, E side: b to f, (same content for f) - the other way
1413 mFBm-0 simple merge - B side: unrelated change, F side: overwrite d with a copy (from h->i->d) - the other way
1413 mFBm-0 simple merge - B side: unrelated change, F side: overwrite d with a copy (from h->i->d) - the other way
1414 mFG,Om: chained merges (copy-overwrite -> simple) - same content
1414 mFG,Om: chained merges (copy-overwrite -> simple) - same content
1415 mFGm-0 merge - G side: content change, F side: copy overwrite, no content change - one way
1415 mFGm-0 merge - G side: content change, F side: copy overwrite, no content change - one way
1416 mGCm-0 merge updated/deleted - revive the file (updated content) - the other way
1416 mGCm-0 merge updated/deleted - revive the file (updated content) - the other way
1417 mGDm-0 actual content merge, copies on one side - D side: delete and re-add (different content), G side: update content - the other way
1417 mGDm-0 actual content merge, copies on one side - D side: delete and re-add (different content), G side: update content - the other way
1418 mGF,Nm: chained merges (copy-overwrite -> simple) - same content
1418 mGF,Nm: chained merges (copy-overwrite -> simple) - same content
1419 mGFm-0 merge - G side: content change, F side: copy overwrite, no content change - the other way
1419 mGFm-0 merge - G side: content change, F side: copy overwrite, no content change - the other way
1420 mHC-delete-before-conflict-m-0 simple merge - C side: d is the results of renames then deleted, H side: d is result of another rename (same content as the other branch) - the other way
1420 mHC-delete-before-conflict-m-0 simple merge - C side: d is the results of renames then deleted, H side: d is result of another rename (same content as the other branch) - the other way
1421 mJ,EA-change-m: chained merges (conflict+change -> simple) - same content on both branch in the initial merge
1421 mJ,EA-change-m: chained merges (conflict+change -> simple) - same content on both branch in the initial merge
1422 mJ,EAm: chained merges (conflict -> simple) - same content everywhere
1422 mJ,EAm: chained merges (conflict -> simple) - same content everywhere
1423 mK,AE-change-m: chained merges (conflict+change -> simple) - same content on both branch in the initial merge
1423 mK,AE-change-m: chained merges (conflict+change -> simple) - same content on both branch in the initial merge
1424 mK,AEm: chained merges (conflict -> simple) - same content everywhere
1424 mK,AEm: chained merges (conflict -> simple) - same content everywhere
1425 mL,BC+revertm: chained merges (salvaged -> simple) - same content (when the file exists)
1425 mL,BC+revertm: chained merges (salvaged -> simple) - same content (when the file exists)
1426 mL,CB+revertm: chained merges (salvaged -> simple) - same content (when the file exists)
1426 mL,CB+revertm: chained merges (salvaged -> simple) - same content (when the file exists)
1427 mN,GFm: chained merges (copy-overwrite -> simple) - same content
1427 mN,GFm: chained merges (copy-overwrite -> simple) - same content
1428 mO,FGm: chained merges (copy-overwrite -> simple) - same content
1428 mO,FGm: chained merges (copy-overwrite -> simple) - same content
1429 mPQ,Tm: chained merges (conflict -> simple) - different content
1429 mPQ,Tm: chained merges (conflict -> simple) - different content
1430 mPQm-0 merge with copies info on both side - P side: rename t to v, Q side: r to v, (different content) - one way
1430 mPQm-0 merge with copies info on both side - P side: rename t to v, Q side: r to v, (different content) - one way
1431 mQP,Sm: chained merges (conflict -> simple) - different content
1431 mQP,Sm: chained merges (conflict -> simple) - different content
1432 mQPm-0 merge with copies info on both side - P side: rename t to v, Q side: r to v, (different content) - the other way
1432 mQPm-0 merge with copies info on both side - P side: rename t to v, Q side: r to v, (different content) - the other way
1433 mRBm-0 simple merge - B side: unrelated change, R side: overwrite d with a copy (from r->x->t) different content - the other way
1433 mRBm-0 simple merge - B side: unrelated change, R side: overwrite d with a copy (from r->x->t) different content - the other way
1434 mS,QPm: chained merges (conflict -> simple) - different content
1434 mS,QPm: chained merges (conflict -> simple) - different content
1435 mT,PQm: chained merges (conflict -> simple) - different content
1435 mT,PQm: chained merges (conflict -> simple) - different content
1436 n-1: unrelated changes (based on the "f" series of changes)
1436 n-1: unrelated changes (based on the "f" series of changes)
1437 o-1: unrelated changes (based on "g" changes)
1437 o-1: unrelated changes (based on "g" changes)
1438 p-1: t -move-> u
1438 p-1: t -move-> u
1439 p-2: u -move-> v
1439 p-2: u -move-> v
1440 q-1 r -move-> w
1440 q-1 r -move-> w
1441 q-2 w -move-> v
1441 q-2 w -move-> v
1442 r-1: rename r -> x
1442 r-1: rename r -> x
1443 r-2: rename t -> x
1443 r-2: rename t -> x
1444 s-1: unrelated changes (based on the "p" series of changes)
1444 s-1: unrelated changes (based on the "p" series of changes)
1445 t-1: unrelated changes (based on "q" changes)
1445 t-1: unrelated changes (based on "q" changes)
1446
1446
1447
1447
1448 Test that sidedata computations during upgrades are correct
1448 Test that sidedata computations during upgrades are correct
1449 ===========================================================
1449 ===========================================================
1450
1450
1451 We upgrade a repository that is not using sidedata (the filelog case) and
1451 We upgrade a repository that is not using sidedata (the filelog case) and
1452 check that the same side data have been generated as if they were computed at
1452 check that the same side data have been generated as if they were computed at
1453 commit time.
1453 commit time.
1454
1454
1455
1455
1456 #if upgraded
1456 #if upgraded
1457 $ cat >> $HGRCPATH << EOF
1457 $ cat >> $HGRCPATH << EOF
1458 > [format]
1458 > [format]
1459 > exp-use-side-data = yes
1459 > exp-use-side-data = yes
1460 > exp-use-copies-side-data-changeset = yes
1460 > exp-use-copies-side-data-changeset = yes
1461 > EOF
1461 > EOF
1462 $ hg debugformat -v
1462 $ hg debugformat -v
1463 format-variant repo config default
1463 format-variant repo config default
1464 fncache: yes yes yes
1464 fncache: yes yes yes
1465 dotencode: yes yes yes
1465 dotencode: yes yes yes
1466 generaldelta: yes yes yes
1466 generaldelta: yes yes yes
1467 share-safe: no no no
1467 share-safe: no no no
1468 sparserevlog: yes yes yes
1468 sparserevlog: yes yes yes
1469 sidedata: no yes no
1469 sidedata: no yes no
1470 persistent-nodemap: no no no
1470 persistent-nodemap: no no no
1471 copies-sdc: no yes no
1471 copies-sdc: no yes no
1472 plain-cl-delta: yes yes yes
1472 plain-cl-delta: yes yes yes
1473 compression: * (glob)
1473 compression: * (glob)
1474 compression-level: default default default
1474 compression-level: default default default
1475 $ hg debugupgraderepo --run --quiet
1475 $ hg debugupgraderepo --run --quiet
1476 upgrade will perform the following actions:
1476 upgrade will perform the following actions:
1477
1477
1478 requirements
1478 requirements
1479 preserved: * (glob)
1479 preserved: * (glob)
1480 added: exp-copies-sidedata-changeset, exp-sidedata-flag
1480 added: exp-copies-sidedata-changeset, exp-sidedata-flag
1481
1481
1482 processed revlogs:
1482 processed revlogs:
1483 - all-filelogs
1483 - all-filelogs
1484 - changelog
1484 - changelog
1485 - manifest
1485 - manifest
1486
1486
1487 #endif
1487 #endif
1488
1488
1489
1489
1490 #if no-compatibility no-filelog no-changeset
1490 #if no-compatibility no-filelog no-changeset
1491
1491
1492 $ hg debugchangedfiles --compute 0
1492 $ hg debugchangedfiles --compute 0
1493 added : a, ;
1493 added : a, ;
1494 added : b, ;
1494 added : b, ;
1495 added : h, ;
1495 added : h, ;
1496 added : p, ;
1496 added : p, ;
1497 added : q, ;
1497 added : q, ;
1498 added : r, ;
1498 added : r, ;
1499
1499
1500 $ for rev in `hg log --rev 'all()' -T '{rev}\n'`; do
1500 $ for rev in `hg log --rev 'all()' -T '{rev}\n'`; do
1501 > case_id=`hg log -r $rev -T '{word(0, desc, ":")}\n'`
1501 > case_id=`hg log -r $rev -T '{word(0, desc, ":")}\n'`
1502 > echo "##### revision \"$case_id\" #####"
1502 > echo "##### revision \"$case_id\" #####"
1503 > hg debugsidedata -c -v -- $rev
1503 > hg debugsidedata -c -v -- $rev
1504 > hg debugchangedfiles $rev
1504 > hg debugchangedfiles $rev
1505 > done
1505 > done
1506 ##### revision "i-0 initial commit" #####
1506 ##### revision "i-0 initial commit" #####
1507 1 sidedata entries
1507 1 sidedata entries
1508 entry-0014 size 64
1508 entry-0014 size 64
1509 '\x00\x00\x00\x06\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\x00\x04\x00\x00\x00\x04\x00\x00\x00\x00\x04\x00\x00\x00\x05\x00\x00\x00\x00\x04\x00\x00\x00\x06\x00\x00\x00\x00abhpqr'
1509 '\x00\x00\x00\x06\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\x00\x04\x00\x00\x00\x04\x00\x00\x00\x00\x04\x00\x00\x00\x05\x00\x00\x00\x00\x04\x00\x00\x00\x06\x00\x00\x00\x00abhpqr'
1510 added : a, ;
1510 added : a, ;
1511 added : b, ;
1511 added : b, ;
1512 added : h, ;
1512 added : h, ;
1513 added : p, ;
1513 added : p, ;
1514 added : q, ;
1514 added : q, ;
1515 added : r, ;
1515 added : r, ;
1516 ##### revision "i-1" #####
1516 ##### revision "i-1" #####
1517 1 sidedata entries
1517 1 sidedata entries
1518 entry-0014 size 44
1518 entry-0014 size 44
1519 '\x00\x00\x00\x04\x0c\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00\x0c\x00\x00\x00\x03\x00\x00\x00\x00\x06\x00\x00\x00\x04\x00\x00\x00\x02acps'
1519 '\x00\x00\x00\x04\x0c\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00\x0c\x00\x00\x00\x03\x00\x00\x00\x00\x06\x00\x00\x00\x04\x00\x00\x00\x02acps'
1520 removed : a, ;
1520 removed : a, ;
1521 added p1: c, a;
1521 added p1: c, a;
1522 removed : p, ;
1522 removed : p, ;
1523 added p1: s, p;
1523 added p1: s, p;
1524 ##### revision "i-2" #####
1524 ##### revision "i-2" #####
1525 1 sidedata entries
1525 1 sidedata entries
1526 entry-0014 size 44
1526 entry-0014 size 44
1527 '\x00\x00\x00\x04\x0c\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00\x0c\x00\x00\x00\x03\x00\x00\x00\x00\x06\x00\x00\x00\x04\x00\x00\x00\x02cdst'
1527 '\x00\x00\x00\x04\x0c\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00\x0c\x00\x00\x00\x03\x00\x00\x00\x00\x06\x00\x00\x00\x04\x00\x00\x00\x02cdst'
1528 removed : c, ;
1528 removed : c, ;
1529 added p1: d, c;
1529 added p1: d, c;
1530 removed : s, ;
1530 removed : s, ;
1531 added p1: t, s;
1531 added p1: t, s;
1532 ##### revision "a-1" #####
1532 ##### revision "a-1" #####
1533 1 sidedata entries
1533 1 sidedata entries
1534 entry-0014 size 24
1534 entry-0014 size 24
1535 '\x00\x00\x00\x02\x0c\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00de'
1535 '\x00\x00\x00\x02\x0c\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00de'
1536 removed : d, ;
1536 removed : d, ;
1537 added p1: e, d;
1537 added p1: e, d;
1538 ##### revision "a-2" #####
1538 ##### revision "a-2" #####
1539 1 sidedata entries
1539 1 sidedata entries
1540 entry-0014 size 24
1540 entry-0014 size 24
1541 '\x00\x00\x00\x02\x0c\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00ef'
1541 '\x00\x00\x00\x02\x0c\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00ef'
1542 removed : e, ;
1542 removed : e, ;
1543 added p1: f, e;
1543 added p1: f, e;
1544 ##### revision "b-1" #####
1544 ##### revision "b-1" #####
1545 1 sidedata entries
1545 1 sidedata entries
1546 entry-0014 size 14
1546 entry-0014 size 14
1547 '\x00\x00\x00\x01\x14\x00\x00\x00\x01\x00\x00\x00\x00b'
1547 '\x00\x00\x00\x01\x14\x00\x00\x00\x01\x00\x00\x00\x00b'
1548 touched : b, ;
1548 touched : b, ;
1549 ##### revision "c-1 delete d" #####
1549 ##### revision "c-1 delete d" #####
1550 1 sidedata entries
1550 1 sidedata entries
1551 entry-0014 size 14
1551 entry-0014 size 14
1552 '\x00\x00\x00\x01\x0c\x00\x00\x00\x01\x00\x00\x00\x00d'
1552 '\x00\x00\x00\x01\x0c\x00\x00\x00\x01\x00\x00\x00\x00d'
1553 removed : d, ;
1553 removed : d, ;
1554 ##### revision "d-1 delete d" #####
1554 ##### revision "d-1 delete d" #####
1555 1 sidedata entries
1555 1 sidedata entries
1556 entry-0014 size 14
1556 entry-0014 size 14
1557 '\x00\x00\x00\x01\x0c\x00\x00\x00\x01\x00\x00\x00\x00d'
1557 '\x00\x00\x00\x01\x0c\x00\x00\x00\x01\x00\x00\x00\x00d'
1558 removed : d, ;
1558 removed : d, ;
1559 ##### revision "d-2 re-add d" #####
1559 ##### revision "d-2 re-add d" #####
1560 1 sidedata entries
1560 1 sidedata entries
1561 entry-0014 size 14
1561 entry-0014 size 14
1562 '\x00\x00\x00\x01\x04\x00\x00\x00\x01\x00\x00\x00\x00d'
1562 '\x00\x00\x00\x01\x04\x00\x00\x00\x01\x00\x00\x00\x00d'
1563 added : d, ;
1563 added : d, ;
1564 ##### revision "e-1 b -move-> g" #####
1564 ##### revision "e-1 b -move-> g" #####
1565 1 sidedata entries
1565 1 sidedata entries
1566 entry-0014 size 24
1566 entry-0014 size 24
1567 '\x00\x00\x00\x02\x0c\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00bg'
1567 '\x00\x00\x00\x02\x0c\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00bg'
1568 removed : b, ;
1568 removed : b, ;
1569 added p1: g, b;
1569 added p1: g, b;
1570 ##### revision "e-2 g -move-> f" #####
1570 ##### revision "e-2 g -move-> f" #####
1571 1 sidedata entries
1571 1 sidedata entries
1572 entry-0014 size 24
1572 entry-0014 size 24
1573 '\x00\x00\x00\x02\x06\x00\x00\x00\x01\x00\x00\x00\x01\x0c\x00\x00\x00\x02\x00\x00\x00\x00fg'
1573 '\x00\x00\x00\x02\x06\x00\x00\x00\x01\x00\x00\x00\x01\x0c\x00\x00\x00\x02\x00\x00\x00\x00fg'
1574 added p1: f, g;
1574 added p1: f, g;
1575 removed : g, ;
1575 removed : g, ;
1576 ##### revision "p-1" #####
1576 ##### revision "p-1" #####
1577 1 sidedata entries
1577 1 sidedata entries
1578 entry-0014 size 24
1578 entry-0014 size 24
1579 '\x00\x00\x00\x02\x0c\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00tu'
1579 '\x00\x00\x00\x02\x0c\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00tu'
1580 removed : t, ;
1580 removed : t, ;
1581 added p1: u, t;
1581 added p1: u, t;
1582 ##### revision "p-2" #####
1582 ##### revision "p-2" #####
1583 1 sidedata entries
1583 1 sidedata entries
1584 entry-0014 size 24
1584 entry-0014 size 24
1585 '\x00\x00\x00\x02\x0c\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00uv'
1585 '\x00\x00\x00\x02\x0c\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00uv'
1586 removed : u, ;
1586 removed : u, ;
1587 added p1: v, u;
1587 added p1: v, u;
1588 ##### revision "q-1 r -move-> w" #####
1588 ##### revision "q-1 r -move-> w" #####
1589 1 sidedata entries
1589 1 sidedata entries
1590 entry-0014 size 24
1590 entry-0014 size 24
1591 '\x00\x00\x00\x02\x0c\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00rw'
1591 '\x00\x00\x00\x02\x0c\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00rw'
1592 removed : r, ;
1592 removed : r, ;
1593 added p1: w, r;
1593 added p1: w, r;
1594 ##### revision "q-2 w -move-> v" #####
1594 ##### revision "q-2 w -move-> v" #####
1595 1 sidedata entries
1595 1 sidedata entries
1596 entry-0014 size 24
1596 entry-0014 size 24
1597 '\x00\x00\x00\x02\x06\x00\x00\x00\x01\x00\x00\x00\x01\x0c\x00\x00\x00\x02\x00\x00\x00\x00vw'
1597 '\x00\x00\x00\x02\x06\x00\x00\x00\x01\x00\x00\x00\x01\x0c\x00\x00\x00\x02\x00\x00\x00\x00vw'
1598 added p1: v, w;
1598 added p1: v, w;
1599 removed : w, ;
1599 removed : w, ;
1600 ##### revision "mBAm-0 simple merge - A side" #####
1600 ##### revision "mBAm-0 simple merge - A side" #####
1601 1 sidedata entries
1601 1 sidedata entries
1602 entry-0014 size 4
1602 entry-0014 size 4
1603 '\x00\x00\x00\x00'
1603 '\x00\x00\x00\x00'
1604 ##### revision "mABm-0 simple merge - A side" #####
1604 ##### revision "mABm-0 simple merge - A side" #####
1605 1 sidedata entries
1605 1 sidedata entries
1606 entry-0014 size 4
1606 entry-0014 size 4
1607 '\x00\x00\x00\x00'
1607 '\x00\x00\x00\x00'
1608 ##### revision "mBCm-0 simple merge - C side" #####
1608 ##### revision "mBCm-0 simple merge - C side" #####
1609 1 sidedata entries
1609 1 sidedata entries
1610 entry-0014 size 4
1610 entry-0014 size 4
1611 '\x00\x00\x00\x00'
1611 '\x00\x00\x00\x00'
1612 ##### revision "mBCm-1 re-add d" #####
1612 ##### revision "mBCm-1 re-add d" #####
1613 1 sidedata entries
1613 1 sidedata entries
1614 entry-0014 size 14
1614 entry-0014 size 14
1615 '\x00\x00\x00\x01\x04\x00\x00\x00\x01\x00\x00\x00\x00d'
1615 '\x00\x00\x00\x01\x04\x00\x00\x00\x01\x00\x00\x00\x00d'
1616 added : d, ;
1616 added : d, ;
1617 ##### revision "mCBm-0 simple merge - C side" #####
1617 ##### revision "mCBm-0 simple merge - C side" #####
1618 1 sidedata entries
1618 1 sidedata entries
1619 entry-0014 size 4
1619 entry-0014 size 4
1620 '\x00\x00\x00\x00'
1620 '\x00\x00\x00\x00'
1621 ##### revision "mCBm-1 re-add d" #####
1621 ##### revision "mCBm-1 re-add d" #####
1622 1 sidedata entries
1622 1 sidedata entries
1623 entry-0014 size 14
1623 entry-0014 size 14
1624 '\x00\x00\x00\x01\x04\x00\x00\x00\x01\x00\x00\x00\x00d'
1624 '\x00\x00\x00\x01\x04\x00\x00\x00\x01\x00\x00\x00\x00d'
1625 added : d, ;
1625 added : d, ;
1626 ##### revision "mBDm-0 simple merge - B side" #####
1626 ##### revision "mBDm-0 simple merge - B side" #####
1627 1 sidedata entries
1627 1 sidedata entries
1628 entry-0014 size 4
1628 entry-0014 size 4
1629 '\x00\x00\x00\x00'
1629 '\x00\x00\x00\x00'
1630 ##### revision "mDBm-0 simple merge - B side" #####
1630 ##### revision "mDBm-0 simple merge - B side" #####
1631 1 sidedata entries
1631 1 sidedata entries
1632 entry-0014 size 4
1632 entry-0014 size 4
1633 '\x00\x00\x00\x00'
1633 '\x00\x00\x00\x00'
1634 ##### revision "mAEm-0 merge with copies info on both side - A side" #####
1634 ##### revision "mAEm-0 merge with copies info on both side - A side" #####
1635 1 sidedata entries
1635 1 sidedata entries
1636 entry-0014 size 14
1636 entry-0014 size 14
1637 '\x00\x00\x00\x01\x08\x00\x00\x00\x01\x00\x00\x00\x00f'
1637 '\x00\x00\x00\x01\x08\x00\x00\x00\x01\x00\x00\x00\x00f'
1638 merged : f, ;
1638 merged : f, ;
1639 ##### revision "mEAm-0 merge with copies info on both side - A side" #####
1639 ##### revision "mEAm-0 merge with copies info on both side - A side" #####
1640 1 sidedata entries
1640 1 sidedata entries
1641 entry-0014 size 14
1641 entry-0014 size 14
1642 '\x00\x00\x00\x01\x08\x00\x00\x00\x01\x00\x00\x00\x00f'
1642 '\x00\x00\x00\x01\x08\x00\x00\x00\x01\x00\x00\x00\x00f'
1643 merged : f, ;
1643 merged : f, ;
1644 ##### revision "mPQm-0 merge with copies info on both side - P side" #####
1644 ##### revision "mPQm-0 merge with copies info on both side - P side" #####
1645 1 sidedata entries
1645 1 sidedata entries
1646 entry-0014 size 14
1646 entry-0014 size 14
1647 '\x00\x00\x00\x01\x08\x00\x00\x00\x01\x00\x00\x00\x00v'
1647 '\x00\x00\x00\x01\x08\x00\x00\x00\x01\x00\x00\x00\x00v'
1648 merged : v, ;
1648 merged : v, ;
1649 ##### revision "mQPm-0 merge with copies info on both side - P side" #####
1649 ##### revision "mQPm-0 merge with copies info on both side - P side" #####
1650 1 sidedata entries
1650 1 sidedata entries
1651 entry-0014 size 14
1651 entry-0014 size 14
1652 '\x00\x00\x00\x01\x08\x00\x00\x00\x01\x00\x00\x00\x00v'
1652 '\x00\x00\x00\x01\x08\x00\x00\x00\x01\x00\x00\x00\x00v'
1653 merged : v, ;
1653 merged : v, ;
1654 ##### revision "f-1" #####
1654 ##### revision "f-1" #####
1655 1 sidedata entries
1655 1 sidedata entries
1656 entry-0014 size 24
1656 entry-0014 size 24
1657 '\x00\x00\x00\x02\x0c\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00hi'
1657 '\x00\x00\x00\x02\x0c\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00hi'
1658 removed : h, ;
1658 removed : h, ;
1659 added p1: i, h;
1659 added p1: i, h;
1660 ##### revision "f-2" #####
1660 ##### revision "f-2" #####
1661 1 sidedata entries
1661 1 sidedata entries
1662 entry-0014 size 24
1662 entry-0014 size 24
1663 '\x00\x00\x00\x02\x16\x00\x00\x00\x01\x00\x00\x00\x01\x0c\x00\x00\x00\x02\x00\x00\x00\x00di'
1663 '\x00\x00\x00\x02\x16\x00\x00\x00\x01\x00\x00\x00\x01\x0c\x00\x00\x00\x02\x00\x00\x00\x00di'
1664 touched p1: d, i;
1664 touched p1: d, i;
1665 removed : i, ;
1665 removed : i, ;
1666 ##### revision "mBFm-0 simple merge - B side" #####
1666 ##### revision "mBFm-0 simple merge - B side" #####
1667 1 sidedata entries
1667 1 sidedata entries
1668 entry-0014 size 4
1668 entry-0014 size 4
1669 '\x00\x00\x00\x00'
1669 '\x00\x00\x00\x00'
1670 ##### revision "mFBm-0 simple merge - B side" #####
1670 ##### revision "mFBm-0 simple merge - B side" #####
1671 1 sidedata entries
1671 1 sidedata entries
1672 entry-0014 size 4
1672 entry-0014 size 4
1673 '\x00\x00\x00\x00'
1673 '\x00\x00\x00\x00'
1674 ##### revision "r-1" #####
1674 ##### revision "r-1" #####
1675 1 sidedata entries
1675 1 sidedata entries
1676 entry-0014 size 24
1676 entry-0014 size 24
1677 '\x00\x00\x00\x02\x0c\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00rx'
1677 '\x00\x00\x00\x02\x0c\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00rx'
1678 removed : r, ;
1678 removed : r, ;
1679 added p1: x, r;
1679 added p1: x, r;
1680 ##### revision "r-2" #####
1680 ##### revision "r-2" #####
1681 1 sidedata entries
1681 1 sidedata entries
1682 entry-0014 size 24
1682 entry-0014 size 24
1683 '\x00\x00\x00\x02\x16\x00\x00\x00\x01\x00\x00\x00\x01\x0c\x00\x00\x00\x02\x00\x00\x00\x00tx'
1683 '\x00\x00\x00\x02\x16\x00\x00\x00\x01\x00\x00\x00\x01\x0c\x00\x00\x00\x02\x00\x00\x00\x00tx'
1684 touched p1: t, x;
1684 touched p1: t, x;
1685 removed : x, ;
1685 removed : x, ;
1686 ##### revision "mBRm-0 simple merge - B side" #####
1686 ##### revision "mBRm-0 simple merge - B side" #####
1687 1 sidedata entries
1687 1 sidedata entries
1688 entry-0014 size 4
1688 entry-0014 size 4
1689 '\x00\x00\x00\x00'
1689 '\x00\x00\x00\x00'
1690 ##### revision "mRBm-0 simple merge - B side" #####
1690 ##### revision "mRBm-0 simple merge - B side" #####
1691 1 sidedata entries
1691 1 sidedata entries
1692 entry-0014 size 4
1692 entry-0014 size 4
1693 '\x00\x00\x00\x00'
1693 '\x00\x00\x00\x00'
1694 ##### revision "g-1" #####
1694 ##### revision "g-1" #####
1695 1 sidedata entries
1695 1 sidedata entries
1696 entry-0014 size 14
1696 entry-0014 size 14
1697 '\x00\x00\x00\x01\x14\x00\x00\x00\x01\x00\x00\x00\x00d'
1697 '\x00\x00\x00\x01\x14\x00\x00\x00\x01\x00\x00\x00\x00d'
1698 touched : d, ;
1698 touched : d, ;
1699 ##### revision "mDGm-0 actual content merge, copies on one side - D side" #####
1699 ##### revision "mDGm-0 actual content merge, copies on one side - D side" #####
1700 1 sidedata entries
1700 1 sidedata entries
1701 entry-0014 size 14
1701 entry-0014 size 14
1702 '\x00\x00\x00\x01\x08\x00\x00\x00\x01\x00\x00\x00\x00d'
1702 '\x00\x00\x00\x01\x08\x00\x00\x00\x01\x00\x00\x00\x00d'
1703 merged : d, ;
1703 merged : d, ;
1704 ##### revision "mGDm-0 actual content merge, copies on one side - D side" #####
1704 ##### revision "mGDm-0 actual content merge, copies on one side - D side" #####
1705 1 sidedata entries
1705 1 sidedata entries
1706 entry-0014 size 14
1706 entry-0014 size 14
1707 '\x00\x00\x00\x01\x08\x00\x00\x00\x01\x00\x00\x00\x00d'
1707 '\x00\x00\x00\x01\x08\x00\x00\x00\x01\x00\x00\x00\x00d'
1708 merged : d, ;
1708 merged : d, ;
1709 ##### revision "mFGm-0 merge - G side" #####
1709 ##### revision "mFGm-0 merge - G side" #####
1710 1 sidedata entries
1710 1 sidedata entries
1711 entry-0014 size 14
1711 entry-0014 size 14
1712 '\x00\x00\x00\x01\x08\x00\x00\x00\x01\x00\x00\x00\x00d'
1712 '\x00\x00\x00\x01\x08\x00\x00\x00\x01\x00\x00\x00\x00d'
1713 merged : d, ;
1713 merged : d, ;
1714 ##### revision "mGFm-0 merge - G side" #####
1714 ##### revision "mGFm-0 merge - G side" #####
1715 1 sidedata entries
1715 1 sidedata entries
1716 entry-0014 size 14
1716 entry-0014 size 14
1717 '\x00\x00\x00\x01\x08\x00\x00\x00\x01\x00\x00\x00\x00d'
1717 '\x00\x00\x00\x01\x08\x00\x00\x00\x01\x00\x00\x00\x00d'
1718 merged : d, ;
1718 merged : d, ;
1719 ##### revision "mCGm-0 merge updated/deleted - revive the file (updated content) - one way" #####
1719 ##### revision "mCGm-0 merge updated/deleted - revive the file (updated content) - one way" #####
1720 1 sidedata entries
1720 1 sidedata entries
1721 entry-0014 size 14
1721 entry-0014 size 14
1722 '\x00\x00\x00\x01\x10\x00\x00\x00\x01\x00\x00\x00\x00d'
1722 '\x00\x00\x00\x01\x10\x00\x00\x00\x01\x00\x00\x00\x00d'
1723 salvaged : d, ;
1723 salvaged : d, ;
1724 ##### revision "mGCm-0 merge updated/deleted - revive the file (updated content) - the other way" #####
1724 ##### revision "mGCm-0 merge updated/deleted - revive the file (updated content) - the other way" #####
1725 1 sidedata entries
1725 1 sidedata entries
1726 entry-0014 size 14
1726 entry-0014 size 14
1727 '\x00\x00\x00\x01\x10\x00\x00\x00\x01\x00\x00\x00\x00d'
1727 '\x00\x00\x00\x01\x10\x00\x00\x00\x01\x00\x00\x00\x00d'
1728 salvaged : d, ;
1728 salvaged : d, ;
1729 ##### revision "mCB-revert-m-0 merge explicitely revive deleted file - B side" #####
1729 ##### revision "mCB-revert-m-0 merge explicitely revive deleted file - B side" #####
1730 1 sidedata entries
1730 1 sidedata entries
1731 entry-0014 size 14
1731 entry-0014 size 14
1732 '\x00\x00\x00\x01\x10\x00\x00\x00\x01\x00\x00\x00\x00d'
1732 '\x00\x00\x00\x01\x10\x00\x00\x00\x01\x00\x00\x00\x00d'
1733 salvaged : d, ;
1733 salvaged : d, ;
1734 ##### revision "mBC-revert-m-0 merge explicitely revive deleted file - B side" #####
1734 ##### revision "mBC-revert-m-0 merge explicitely revive deleted file - B side" #####
1735 1 sidedata entries
1735 1 sidedata entries
1736 entry-0014 size 14
1736 entry-0014 size 14
1737 '\x00\x00\x00\x01\x10\x00\x00\x00\x01\x00\x00\x00\x00d'
1737 '\x00\x00\x00\x01\x10\x00\x00\x00\x01\x00\x00\x00\x00d'
1738 salvaged : d, ;
1738 salvaged : d, ;
1739 ##### revision "h-1" #####
1739 ##### revision "h-1" #####
1740 1 sidedata entries
1740 1 sidedata entries
1741 entry-0014 size 24
1741 entry-0014 size 24
1742 '\x00\x00\x00\x02\x0c\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00bd'
1742 '\x00\x00\x00\x02\x0c\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00bd'
1743 removed : b, ;
1743 removed : b, ;
1744 added p1: d, b;
1744 added p1: d, b;
1745 ##### revision "mCH-delete-before-conflict-m-0 simple merge - C side" #####
1745 ##### revision "mCH-delete-before-conflict-m-0 simple merge - C side" #####
1746 1 sidedata entries
1746 1 sidedata entries
1747 entry-0014 size 4
1747 entry-0014 size 4
1748 '\x00\x00\x00\x00'
1748 '\x00\x00\x00\x00'
1749 ##### revision "mHC-delete-before-conflict-m-0 simple merge - C side" #####
1749 ##### revision "mHC-delete-before-conflict-m-0 simple merge - C side" #####
1750 1 sidedata entries
1750 1 sidedata entries
1751 entry-0014 size 4
1751 entry-0014 size 4
1752 '\x00\x00\x00\x00'
1752 '\x00\x00\x00\x00'
1753 ##### revision "mAE-change-m-0 merge with file update and copies info on both side - A side" #####
1753 ##### revision "mAE-change-m-0 merge with file update and copies info on both side - A side" #####
1754 1 sidedata entries
1754 1 sidedata entries
1755 entry-0014 size 14
1755 entry-0014 size 14
1756 '\x00\x00\x00\x01\x08\x00\x00\x00\x01\x00\x00\x00\x00f'
1756 '\x00\x00\x00\x01\x08\x00\x00\x00\x01\x00\x00\x00\x00f'
1757 merged : f, ;
1757 merged : f, ;
1758 ##### revision "mEA-change-m-0 merge with file update and copies info on both side - A side" #####
1758 ##### revision "mEA-change-m-0 merge with file update and copies info on both side - A side" #####
1759 1 sidedata entries
1759 1 sidedata entries
1760 entry-0014 size 14
1760 entry-0014 size 14
1761 '\x00\x00\x00\x01\x08\x00\x00\x00\x01\x00\x00\x00\x00f'
1761 '\x00\x00\x00\x01\x08\x00\x00\x00\x01\x00\x00\x00\x00f'
1762 merged : f, ;
1762 merged : f, ;
1763 ##### revision "j-1" #####
1763 ##### revision "j-1" #####
1764 1 sidedata entries
1764 1 sidedata entries
1765 entry-0014 size 24
1765 entry-0014 size 24
1766 '\x00\x00\x00\x01\x04\x00\x00\x00\x0b\x00\x00\x00\x00unrelated-j'
1766 '\x00\x00\x00\x01\x04\x00\x00\x00\x0b\x00\x00\x00\x00unrelated-j'
1767 added : unrelated-j, ;
1767 added : unrelated-j, ;
1768 ##### revision "k-1" #####
1768 ##### revision "k-1" #####
1769 1 sidedata entries
1769 1 sidedata entries
1770 entry-0014 size 24
1770 entry-0014 size 24
1771 '\x00\x00\x00\x01\x04\x00\x00\x00\x0b\x00\x00\x00\x00unrelated-k'
1771 '\x00\x00\x00\x01\x04\x00\x00\x00\x0b\x00\x00\x00\x00unrelated-k'
1772 added : unrelated-k, ;
1772 added : unrelated-k, ;
1773 ##### revision "mAE,Km" #####
1773 ##### revision "mAE,Km" #####
1774 1 sidedata entries
1774 1 sidedata entries
1775 entry-0014 size 4
1775 entry-0014 size 4
1776 '\x00\x00\x00\x00'
1776 '\x00\x00\x00\x00'
1777 ##### revision "mK,AEm" #####
1777 ##### revision "mK,AEm" #####
1778 1 sidedata entries
1778 1 sidedata entries
1779 entry-0014 size 4
1779 entry-0014 size 4
1780 '\x00\x00\x00\x00'
1780 '\x00\x00\x00\x00'
1781 ##### revision "mEA,Jm" #####
1781 ##### revision "mEA,Jm" #####
1782 1 sidedata entries
1782 1 sidedata entries
1783 entry-0014 size 4
1783 entry-0014 size 4
1784 '\x00\x00\x00\x00'
1784 '\x00\x00\x00\x00'
1785 ##### revision "mJ,EAm" #####
1785 ##### revision "mJ,EAm" #####
1786 1 sidedata entries
1786 1 sidedata entries
1787 entry-0014 size 4
1787 entry-0014 size 4
1788 '\x00\x00\x00\x00'
1788 '\x00\x00\x00\x00'
1789 ##### revision "s-1" #####
1789 ##### revision "s-1" #####
1790 1 sidedata entries
1790 1 sidedata entries
1791 entry-0014 size 24
1791 entry-0014 size 24
1792 '\x00\x00\x00\x01\x04\x00\x00\x00\x0b\x00\x00\x00\x00unrelated-s'
1792 '\x00\x00\x00\x01\x04\x00\x00\x00\x0b\x00\x00\x00\x00unrelated-s'
1793 added : unrelated-s, ;
1793 added : unrelated-s, ;
1794 ##### revision "t-1" #####
1794 ##### revision "t-1" #####
1795 1 sidedata entries
1795 1 sidedata entries
1796 entry-0014 size 24
1796 entry-0014 size 24
1797 '\x00\x00\x00\x01\x04\x00\x00\x00\x0b\x00\x00\x00\x00unrelated-t'
1797 '\x00\x00\x00\x01\x04\x00\x00\x00\x0b\x00\x00\x00\x00unrelated-t'
1798 added : unrelated-t, ;
1798 added : unrelated-t, ;
1799 ##### revision "mPQ,Tm" #####
1799 ##### revision "mPQ,Tm" #####
1800 1 sidedata entries
1800 1 sidedata entries
1801 entry-0014 size 4
1801 entry-0014 size 4
1802 '\x00\x00\x00\x00'
1802 '\x00\x00\x00\x00'
1803 ##### revision "mT,PQm" #####
1803 ##### revision "mT,PQm" #####
1804 1 sidedata entries
1804 1 sidedata entries
1805 entry-0014 size 4
1805 entry-0014 size 4
1806 '\x00\x00\x00\x00'
1806 '\x00\x00\x00\x00'
1807 ##### revision "mQP,Sm" #####
1807 ##### revision "mQP,Sm" #####
1808 1 sidedata entries
1808 1 sidedata entries
1809 entry-0014 size 4
1809 entry-0014 size 4
1810 '\x00\x00\x00\x00'
1810 '\x00\x00\x00\x00'
1811 ##### revision "mS,QPm" #####
1811 ##### revision "mS,QPm" #####
1812 1 sidedata entries
1812 1 sidedata entries
1813 entry-0014 size 4
1813 entry-0014 size 4
1814 '\x00\x00\x00\x00'
1814 '\x00\x00\x00\x00'
1815 ##### revision "l-1" #####
1815 ##### revision "l-1" #####
1816 1 sidedata entries
1816 1 sidedata entries
1817 entry-0014 size 24
1817 entry-0014 size 24
1818 '\x00\x00\x00\x01\x04\x00\x00\x00\x0b\x00\x00\x00\x00unrelated-l'
1818 '\x00\x00\x00\x01\x04\x00\x00\x00\x0b\x00\x00\x00\x00unrelated-l'
1819 added : unrelated-l, ;
1819 added : unrelated-l, ;
1820 ##### revision "mBC+revert,Lm" #####
1820 ##### revision "mBC+revert,Lm" #####
1821 1 sidedata entries
1821 1 sidedata entries
1822 entry-0014 size 4
1822 entry-0014 size 4
1823 '\x00\x00\x00\x00'
1823 '\x00\x00\x00\x00'
1824 ##### revision "mCB+revert,Lm" #####
1824 ##### revision "mCB+revert,Lm" #####
1825 1 sidedata entries
1825 1 sidedata entries
1826 entry-0014 size 4
1826 entry-0014 size 4
1827 '\x00\x00\x00\x00'
1827 '\x00\x00\x00\x00'
1828 ##### revision "mL,BC+revertm" #####
1828 ##### revision "mL,BC+revertm" #####
1829 1 sidedata entries
1829 1 sidedata entries
1830 entry-0014 size 4
1830 entry-0014 size 4
1831 '\x00\x00\x00\x00'
1831 '\x00\x00\x00\x00'
1832 ##### revision "mL,CB+revertm" #####
1832 ##### revision "mL,CB+revertm" #####
1833 1 sidedata entries
1833 1 sidedata entries
1834 entry-0014 size 4
1834 entry-0014 size 4
1835 '\x00\x00\x00\x00'
1835 '\x00\x00\x00\x00'
1836 ##### revision "n-1" #####
1836 ##### revision "n-1" #####
1837 1 sidedata entries
1837 1 sidedata entries
1838 entry-0014 size 24
1838 entry-0014 size 24
1839 '\x00\x00\x00\x01\x04\x00\x00\x00\x0b\x00\x00\x00\x00unrelated-n'
1839 '\x00\x00\x00\x01\x04\x00\x00\x00\x0b\x00\x00\x00\x00unrelated-n'
1840 added : unrelated-n, ;
1840 added : unrelated-n, ;
1841 ##### revision "o-1" #####
1841 ##### revision "o-1" #####
1842 1 sidedata entries
1842 1 sidedata entries
1843 entry-0014 size 24
1843 entry-0014 size 24
1844 '\x00\x00\x00\x01\x04\x00\x00\x00\x0b\x00\x00\x00\x00unrelated-o'
1844 '\x00\x00\x00\x01\x04\x00\x00\x00\x0b\x00\x00\x00\x00unrelated-o'
1845 added : unrelated-o, ;
1845 added : unrelated-o, ;
1846 ##### revision "mFG,Om" #####
1846 ##### revision "mFG,Om" #####
1847 1 sidedata entries
1847 1 sidedata entries
1848 entry-0014 size 4
1848 entry-0014 size 4
1849 '\x00\x00\x00\x00'
1849 '\x00\x00\x00\x00'
1850 ##### revision "mO,FGm" #####
1850 ##### revision "mO,FGm" #####
1851 1 sidedata entries
1851 1 sidedata entries
1852 entry-0014 size 4
1852 entry-0014 size 4
1853 '\x00\x00\x00\x00'
1853 '\x00\x00\x00\x00'
1854 ##### revision "mGF,Nm" #####
1854 ##### revision "mGF,Nm" #####
1855 1 sidedata entries
1855 1 sidedata entries
1856 entry-0014 size 4
1856 entry-0014 size 4
1857 '\x00\x00\x00\x00'
1857 '\x00\x00\x00\x00'
1858 ##### revision "mN,GFm" #####
1858 ##### revision "mN,GFm" #####
1859 1 sidedata entries
1859 1 sidedata entries
1860 entry-0014 size 4
1860 entry-0014 size 4
1861 '\x00\x00\x00\x00'
1861 '\x00\x00\x00\x00'
1862 ##### revision "mAE-change,Km" #####
1862 ##### revision "mAE-change,Km" #####
1863 1 sidedata entries
1863 1 sidedata entries
1864 entry-0014 size 4
1864 entry-0014 size 4
1865 '\x00\x00\x00\x00'
1865 '\x00\x00\x00\x00'
1866 ##### revision "mK,AE-change-m" #####
1866 ##### revision "mK,AE-change-m" #####
1867 1 sidedata entries
1867 1 sidedata entries
1868 entry-0014 size 4
1868 entry-0014 size 4
1869 '\x00\x00\x00\x00'
1869 '\x00\x00\x00\x00'
1870 ##### revision "mEA-change,Jm" #####
1870 ##### revision "mEA-change,Jm" #####
1871 1 sidedata entries
1871 1 sidedata entries
1872 entry-0014 size 4
1872 entry-0014 size 4
1873 '\x00\x00\x00\x00'
1873 '\x00\x00\x00\x00'
1874 ##### revision "mJ,EA-change-m" #####
1874 ##### revision "mJ,EA-change-m" #####
1875 1 sidedata entries
1875 1 sidedata entries
1876 entry-0014 size 4
1876 entry-0014 size 4
1877 '\x00\x00\x00\x00'
1877 '\x00\x00\x00\x00'
1878
1878
1879 #endif
1879 #endif
1880
1880
1881
1881
1882 Test copy information chaining
1882 Test copy information chaining
1883 ==============================
1883 ==============================
1884
1884
1885 Check that matching only affect the destination and not intermediate path
1885 Check that matching only affect the destination and not intermediate path
1886 -------------------------------------------------------------------------
1886 -------------------------------------------------------------------------
1887
1887
1888 The two status call should give the same value for f
1888 The two status call should give the same value for f
1889
1889
1890 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("a-2")'
1890 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("a-2")'
1891 A f
1891 A f
1892 a
1892 a
1893 A t
1893 A t
1894 p
1894 p
1895 R a
1895 R a
1896 R p
1896 R p
1897 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("a-2")' f
1897 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("a-2")' f
1898 A f
1898 A f
1899 a (no-changeset no-compatibility !)
1899 a (no-changeset no-compatibility !)
1900
1900
1901 merging with unrelated change does not interfere with the renames
1901 merging with unrelated change does not interfere with the renames
1902 ---------------------------------------------------------------
1902 ---------------------------------------------------------------
1903
1903
1904 - rename on one side
1904 - rename on one side
1905 - unrelated change on the other side
1905 - unrelated change on the other side
1906
1906
1907 $ hg log -G --rev '::(desc("mABm")+desc("mBAm"))'
1907 $ hg log -G --rev '::(desc("mABm")+desc("mBAm"))'
1908 o mABm-0 simple merge - A side: multiple renames, B side: unrelated update - the other way
1908 o mABm-0 simple merge - A side: multiple renames, B side: unrelated update - the other way
1909 |\
1909 |\
1910 +---o mBAm-0 simple merge - A side: multiple renames, B side: unrelated update - one way
1910 +---o mBAm-0 simple merge - A side: multiple renames, B side: unrelated update - one way
1911 | |/
1911 | |/
1912 | o b-1: b update
1912 | o b-1: b update
1913 | |
1913 | |
1914 o | a-2: e -move-> f
1914 o | a-2: e -move-> f
1915 | |
1915 | |
1916 o | a-1: d -move-> e
1916 o | a-1: d -move-> e
1917 |/
1917 |/
1918 o i-2: c -move-> d, s -move-> t
1918 o i-2: c -move-> d, s -move-> t
1919 |
1919 |
1920 o i-1: a -move-> c, p -move-> s
1920 o i-1: a -move-> c, p -move-> s
1921 |
1921 |
1922 o i-0 initial commit: a b h
1922 o i-0 initial commit: a b h
1923
1923
1924
1924
1925 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mABm")'
1925 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mABm")'
1926 A f
1926 A f
1927 d
1927 d
1928 R d
1928 R d
1929 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mBAm")'
1929 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mBAm")'
1930 A f
1930 A f
1931 d
1931 d
1932 R d
1932 R d
1933 $ hg status --copies --rev 'desc("a-2")' --rev 'desc("mABm")'
1933 $ hg status --copies --rev 'desc("a-2")' --rev 'desc("mABm")'
1934 M b
1934 M b
1935 $ hg status --copies --rev 'desc("a-2")' --rev 'desc("mBAm")'
1935 $ hg status --copies --rev 'desc("a-2")' --rev 'desc("mBAm")'
1936 M b
1936 M b
1937 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mABm")'
1937 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mABm")'
1938 M b
1938 M b
1939 A f
1939 A f
1940 d
1940 d
1941 R d
1941 R d
1942 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mBAm")'
1942 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mBAm")'
1943 M b
1943 M b
1944 A f
1944 A f
1945 d
1945 d
1946 R d
1946 R d
1947 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mABm")'
1947 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mABm")'
1948 M b
1948 M b
1949 A f
1949 A f
1950 a
1950 a
1951 A t
1951 A t
1952 p
1952 p
1953 R a
1953 R a
1954 R p
1954 R p
1955 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mBAm")'
1955 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mBAm")'
1956 M b
1956 M b
1957 A f
1957 A f
1958 a
1958 a
1959 A t
1959 A t
1960 p
1960 p
1961 R a
1961 R a
1962 R p
1962 R p
1963
1963
1964 merging with the side having a delete
1964 merging with the side having a delete
1965 -------------------------------------
1965 -------------------------------------
1966
1966
1967 case summary:
1967 case summary:
1968 - one with change to an unrelated file
1968 - one with change to an unrelated file
1969 - one deleting the change
1969 - one deleting the change
1970 and recreate an unrelated file after the merge
1970 and recreate an unrelated file after the merge
1971
1971
1972 $ hg log -G --rev '::(desc("mCBm")+desc("mBCm"))'
1972 $ hg log -G --rev '::(desc("mCBm")+desc("mBCm"))'
1973 o mCBm-1 re-add d
1973 o mCBm-1 re-add d
1974 |
1974 |
1975 o mCBm-0 simple merge - C side: delete a file with copies history , B side: unrelated update - the other way
1975 o mCBm-0 simple merge - C side: delete a file with copies history , B side: unrelated update - the other way
1976 |\
1976 |\
1977 | | o mBCm-1 re-add d
1977 | | o mBCm-1 re-add d
1978 | | |
1978 | | |
1979 +---o mBCm-0 simple merge - C side: delete a file with copies history , B side: unrelated update - one way
1979 +---o mBCm-0 simple merge - C side: delete a file with copies history , B side: unrelated update - one way
1980 | |/
1980 | |/
1981 | o c-1 delete d
1981 | o c-1 delete d
1982 | |
1982 | |
1983 o | b-1: b update
1983 o | b-1: b update
1984 |/
1984 |/
1985 o i-2: c -move-> d, s -move-> t
1985 o i-2: c -move-> d, s -move-> t
1986 |
1986 |
1987 o i-1: a -move-> c, p -move-> s
1987 o i-1: a -move-> c, p -move-> s
1988 |
1988 |
1989 o i-0 initial commit: a b h
1989 o i-0 initial commit: a b h
1990
1990
1991 - comparing from the merge
1991 - comparing from the merge
1992
1992
1993 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mBCm-0")'
1993 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mBCm-0")'
1994 R d
1994 R d
1995 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mCBm-0")'
1995 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mCBm-0")'
1996 R d
1996 R d
1997 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mBCm-0")'
1997 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mBCm-0")'
1998 M b
1998 M b
1999 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mCBm-0")'
1999 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mCBm-0")'
2000 M b
2000 M b
2001 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mBCm-0")'
2001 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mBCm-0")'
2002 M b
2002 M b
2003 R d
2003 R d
2004 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mCBm-0")'
2004 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mCBm-0")'
2005 M b
2005 M b
2006 R d
2006 R d
2007 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mBCm-0")'
2007 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mBCm-0")'
2008 M b
2008 M b
2009 A t
2009 A t
2010 p
2010 p
2011 R a
2011 R a
2012 R p
2012 R p
2013 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mCBm-0")'
2013 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mCBm-0")'
2014 M b
2014 M b
2015 A t
2015 A t
2016 p
2016 p
2017 R a
2017 R a
2018 R p
2018 R p
2019
2019
2020 - comparing with the merge children re-adding the file
2020 - comparing with the merge children re-adding the file
2021
2021
2022 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mBCm-1")'
2022 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mBCm-1")'
2023 M d
2023 M d
2024 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mCBm-1")'
2024 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mCBm-1")'
2025 M d
2025 M d
2026 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mBCm-1")'
2026 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mBCm-1")'
2027 M b
2027 M b
2028 A d
2028 A d
2029 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mCBm-1")'
2029 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mCBm-1")'
2030 M b
2030 M b
2031 A d
2031 A d
2032 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mBCm-1")'
2032 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mBCm-1")'
2033 M b
2033 M b
2034 M d
2034 M d
2035 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mCBm-1")'
2035 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mCBm-1")'
2036 M b
2036 M b
2037 M d
2037 M d
2038 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mBCm-1")'
2038 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mBCm-1")'
2039 M b
2039 M b
2040 A d
2040 A d
2041 A t
2041 A t
2042 p
2042 p
2043 R a
2043 R a
2044 R p
2044 R p
2045 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mCBm-1")'
2045 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mCBm-1")'
2046 M b
2046 M b
2047 A d
2047 A d
2048 A t
2048 A t
2049 p
2049 p
2050 R a
2050 R a
2051 R p
2051 R p
2052
2052
2053 Comparing with a merge re-adding the file afterward
2053 Comparing with a merge re-adding the file afterward
2054 ---------------------------------------------------
2054 ---------------------------------------------------
2055
2055
2056 Merge:
2056 Merge:
2057 - one with change to an unrelated file
2057 - one with change to an unrelated file
2058 - one deleting and recreating the change
2058 - one deleting and recreating the change
2059
2059
2060 $ hg log -G --rev '::(desc("mDBm")+desc("mBDm"))'
2060 $ hg log -G --rev '::(desc("mDBm")+desc("mBDm"))'
2061 o mDBm-0 simple merge - B side: unrelated update, D side: delete and recreate a file (with different content) - the other way
2061 o mDBm-0 simple merge - B side: unrelated update, D side: delete and recreate a file (with different content) - the other way
2062 |\
2062 |\
2063 +---o mBDm-0 simple merge - B side: unrelated update, D side: delete and recreate a file (with different content) - one way
2063 +---o mBDm-0 simple merge - B side: unrelated update, D side: delete and recreate a file (with different content) - one way
2064 | |/
2064 | |/
2065 | o d-2 re-add d
2065 | o d-2 re-add d
2066 | |
2066 | |
2067 | o d-1 delete d
2067 | o d-1 delete d
2068 | |
2068 | |
2069 o | b-1: b update
2069 o | b-1: b update
2070 |/
2070 |/
2071 o i-2: c -move-> d, s -move-> t
2071 o i-2: c -move-> d, s -move-> t
2072 |
2072 |
2073 o i-1: a -move-> c, p -move-> s
2073 o i-1: a -move-> c, p -move-> s
2074 |
2074 |
2075 o i-0 initial commit: a b h
2075 o i-0 initial commit: a b h
2076
2076
2077 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mBDm-0")'
2077 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mBDm-0")'
2078 M d
2078 M d
2079 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mDBm-0")'
2079 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mDBm-0")'
2080 M d
2080 M d
2081 $ hg status --copies --rev 'desc("d-2")' --rev 'desc("mBDm-0")'
2081 $ hg status --copies --rev 'desc("d-2")' --rev 'desc("mBDm-0")'
2082 M b
2082 M b
2083 $ hg status --copies --rev 'desc("d-2")' --rev 'desc("mDBm-0")'
2083 $ hg status --copies --rev 'desc("d-2")' --rev 'desc("mDBm-0")'
2084 M b
2084 M b
2085 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mBDm-0")'
2085 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mBDm-0")'
2086 M b
2086 M b
2087 M d
2087 M d
2088 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mDBm-0")'
2088 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mDBm-0")'
2089 M b
2089 M b
2090 M d
2090 M d
2091
2091
2092 The bugs makes recorded copy is different depending of where we started the merge from since
2092 The bugs makes recorded copy is different depending of where we started the merge from since
2093
2093
2094 $ hg manifest --debug --rev 'desc("mBDm-0")' | grep '644 d'
2094 $ hg manifest --debug --rev 'desc("mBDm-0")' | grep '644 d'
2095 b004912a8510032a0350a74daa2803dadfb00e12 644 d
2095 b004912a8510032a0350a74daa2803dadfb00e12 644 d
2096 $ hg manifest --debug --rev 'desc("mDBm-0")' | grep '644 d'
2096 $ hg manifest --debug --rev 'desc("mDBm-0")' | grep '644 d'
2097 b004912a8510032a0350a74daa2803dadfb00e12 644 d
2097 b004912a8510032a0350a74daa2803dadfb00e12 644 d
2098
2098
2099 $ hg manifest --debug --rev 'desc("d-2")' | grep '644 d'
2099 $ hg manifest --debug --rev 'desc("d-2")' | grep '644 d'
2100 b004912a8510032a0350a74daa2803dadfb00e12 644 d
2100 b004912a8510032a0350a74daa2803dadfb00e12 644 d
2101 $ hg manifest --debug --rev 'desc("b-1")' | grep '644 d'
2101 $ hg manifest --debug --rev 'desc("b-1")' | grep '644 d'
2102 d8252ab2e760b0d4e5288fd44cbd15a0fa567e16 644 d (no-changeset !)
2102 d8252ab2e760b0d4e5288fd44cbd15a0fa567e16 644 d (no-changeset !)
2103 ae258f702dfeca05bf9b6a22a97a4b5645570f11 644 d (changeset !)
2103 ae258f702dfeca05bf9b6a22a97a4b5645570f11 644 d (changeset !)
2104 $ hg debugindex d | head -n 4 | ../no-linkrev
2104 $ hg debugindex d | head -n 4 | ../no-linkrev
2105 rev linkrev nodeid p1 p2
2105 rev linkrev nodeid p1 p2
2106 0 * d8252ab2e760 000000000000 000000000000 (no-changeset !)
2106 0 * d8252ab2e760 000000000000 000000000000 (no-changeset !)
2107 0 * ae258f702dfe 000000000000 000000000000 (changeset !)
2107 0 * ae258f702dfe 000000000000 000000000000 (changeset !)
2108 1 * b004912a8510 000000000000 000000000000
2108 1 * b004912a8510 000000000000 000000000000
2109 2 * 7b79e2fe0c89 000000000000 000000000000 (no-changeset !)
2109 2 * 7b79e2fe0c89 000000000000 000000000000 (no-changeset !)
2110 2 * 5cce88bf349f ae258f702dfe 000000000000 (changeset !)
2110 2 * 5cce88bf349f ae258f702dfe 000000000000 (changeset !)
2111
2111
2112 Log output should not include a merge commit as it did not happen
2112 Log output should not include a merge commit as it did not happen
2113
2113
2114 $ hg log -Gfr 'desc("mBDm-0")' d
2114 $ hg log -Gfr 'desc("mBDm-0")' d
2115 o d-2 re-add d
2115 o d-2 re-add d
2116 |
2116 |
2117 ~
2117 ~
2118
2118
2119 $ hg log -Gfr 'desc("mDBm-0")' d
2119 $ hg log -Gfr 'desc("mDBm-0")' d
2120 o d-2 re-add d
2120 o d-2 re-add d
2121 |
2121 |
2122 ~
2122 ~
2123
2123
2124 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mBDm-0")'
2124 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mBDm-0")'
2125 M b
2125 M b
2126 A d
2126 A d
2127 A t
2127 A t
2128 p
2128 p
2129 R a
2129 R a
2130 R p
2130 R p
2131 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mDBm-0")'
2131 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mDBm-0")'
2132 M b
2132 M b
2133 A d
2133 A d
2134 A t
2134 A t
2135 p
2135 p
2136 R a
2136 R a
2137 R p
2137 R p
2138
2138
2139
2139
2140 Comparing with a merge with colliding rename
2140 Comparing with a merge with colliding rename
2141 --------------------------------------------
2141 --------------------------------------------
2142
2142
2143 Subcase: new copy information on both side
2143 Subcase: new copy information on both side
2144 ``````````````````````````````````````````
2144 ``````````````````````````````````````````
2145
2145
2146 - the "e-" branch renaming b to f (through 'g')
2146 - the "e-" branch renaming b to f (through 'g')
2147 - the "a-" branch renaming d to f (through e)
2147 - the "a-" branch renaming d to f (through e)
2148
2148
2149 $ hg log -G --rev '::(desc("mAEm")+desc("mEAm"))'
2149 $ hg log -G --rev '::(desc("mAEm")+desc("mEAm"))'
2150 o mEAm-0 merge with copies info on both side - A side: rename d to f, E side: b to f, (same content for f) - the other way
2150 o mEAm-0 merge with copies info on both side - A side: rename d to f, E side: b to f, (same content for f) - the other way
2151 |\
2151 |\
2152 +---o mAEm-0 merge with copies info on both side - A side: rename d to f, E side: b to f, (same content for f) - one way
2152 +---o mAEm-0 merge with copies info on both side - A side: rename d to f, E side: b to f, (same content for f) - one way
2153 | |/
2153 | |/
2154 | o e-2 g -move-> f
2154 | o e-2 g -move-> f
2155 | |
2155 | |
2156 | o e-1 b -move-> g
2156 | o e-1 b -move-> g
2157 | |
2157 | |
2158 o | a-2: e -move-> f
2158 o | a-2: e -move-> f
2159 | |
2159 | |
2160 o | a-1: d -move-> e
2160 o | a-1: d -move-> e
2161 |/
2161 |/
2162 o i-2: c -move-> d, s -move-> t
2162 o i-2: c -move-> d, s -move-> t
2163 |
2163 |
2164 o i-1: a -move-> c, p -move-> s
2164 o i-1: a -move-> c, p -move-> s
2165 |
2165 |
2166 o i-0 initial commit: a b h
2166 o i-0 initial commit: a b h
2167
2167
2168 #if no-changeset
2168 #if no-changeset
2169 $ hg manifest --debug --rev 'desc("mAEm-0")' | grep '644 f'
2169 $ hg manifest --debug --rev 'desc("mAEm-0")' | grep '644 f'
2170 2ff93c643948464ee1f871867910ae43a45b0bea 644 f
2170 2ff93c643948464ee1f871867910ae43a45b0bea 644 f
2171 $ hg manifest --debug --rev 'desc("mEAm-0")' | grep '644 f'
2171 $ hg manifest --debug --rev 'desc("mEAm-0")' | grep '644 f'
2172 2ff93c643948464ee1f871867910ae43a45b0bea 644 f
2172 2ff93c643948464ee1f871867910ae43a45b0bea 644 f
2173 $ hg manifest --debug --rev 'desc("a-2")' | grep '644 f'
2173 $ hg manifest --debug --rev 'desc("a-2")' | grep '644 f'
2174 b76eb76580df486c3d51d63c5c210d4dd43a8ac7 644 f
2174 b76eb76580df486c3d51d63c5c210d4dd43a8ac7 644 f
2175 $ hg manifest --debug --rev 'desc("e-2")' | grep '644 f'
2175 $ hg manifest --debug --rev 'desc("e-2")' | grep '644 f'
2176 e8825b386367b29fec957283a80bb47b47483fe1 644 f
2176 e8825b386367b29fec957283a80bb47b47483fe1 644 f
2177 $ hg debugindex f | ../no-linkrev
2177 $ hg debugindex f | ../no-linkrev
2178 rev linkrev nodeid p1 p2
2178 rev linkrev nodeid p1 p2
2179 0 * b76eb76580df 000000000000 000000000000
2179 0 * b76eb76580df 000000000000 000000000000
2180 1 * e8825b386367 000000000000 000000000000
2180 1 * e8825b386367 000000000000 000000000000
2181 2 * 2ff93c643948 b76eb76580df e8825b386367
2181 2 * 2ff93c643948 b76eb76580df e8825b386367
2182 3 * 2f649fba7eb2 b76eb76580df e8825b386367
2182 3 * 2f649fba7eb2 b76eb76580df e8825b386367
2183 4 * 774e7c1637d5 e8825b386367 b76eb76580df
2183 4 * 774e7c1637d5 e8825b386367 b76eb76580df
2184 #else
2184 #else
2185 $ hg manifest --debug --rev 'desc("mAEm-0")' | grep '644 f'
2185 $ hg manifest --debug --rev 'desc("mAEm-0")' | grep '644 f'
2186 ae258f702dfeca05bf9b6a22a97a4b5645570f11 644 f
2186 ae258f702dfeca05bf9b6a22a97a4b5645570f11 644 f
2187 $ hg manifest --debug --rev 'desc("mEAm-0")' | grep '644 f'
2187 $ hg manifest --debug --rev 'desc("mEAm-0")' | grep '644 f'
2188 ae258f702dfeca05bf9b6a22a97a4b5645570f11 644 f
2188 ae258f702dfeca05bf9b6a22a97a4b5645570f11 644 f
2189 $ hg manifest --debug --rev 'desc("a-2")' | grep '644 f'
2189 $ hg manifest --debug --rev 'desc("a-2")' | grep '644 f'
2190 ae258f702dfeca05bf9b6a22a97a4b5645570f11 644 f
2190 ae258f702dfeca05bf9b6a22a97a4b5645570f11 644 f
2191 $ hg manifest --debug --rev 'desc("e-2")' | grep '644 f'
2191 $ hg manifest --debug --rev 'desc("e-2")' | grep '644 f'
2192 ae258f702dfeca05bf9b6a22a97a4b5645570f11 644 f
2192 ae258f702dfeca05bf9b6a22a97a4b5645570f11 644 f
2193 $ hg debugindex f | ../no-linkrev
2193 $ hg debugindex f | ../no-linkrev
2194 rev linkrev nodeid p1 p2
2194 rev linkrev nodeid p1 p2
2195 0 * ae258f702dfe 000000000000 000000000000
2195 0 * ae258f702dfe 000000000000 000000000000
2196 1 * d3613c1ec831 ae258f702dfe 000000000000
2196 1 * d3613c1ec831 ae258f702dfe 000000000000
2197 2 * 05e03c868bbc ae258f702dfe 000000000000
2197 2 * 05e03c868bbc ae258f702dfe 000000000000
2198 #endif
2198 #endif
2199
2199
2200 # Here the filelog based implementation is not looking at the rename
2200 # Here the filelog based implementation is not looking at the rename
2201 # information (because the file exist on both side). However the changelog
2201 # information (because the file exist on both side). However the changelog
2202 # based on works fine. We have different output.
2202 # based on works fine. We have different output.
2203
2203
2204 $ hg status --copies --rev 'desc("a-2")' --rev 'desc("mAEm-0")'
2204 $ hg status --copies --rev 'desc("a-2")' --rev 'desc("mAEm-0")'
2205 M f (no-changeset !)
2205 M f (no-changeset !)
2206 b (no-filelog no-changeset !)
2206 b (no-filelog no-changeset !)
2207 R b
2207 R b
2208 $ hg status --copies --rev 'desc("a-2")' --rev 'desc("mEAm-0")'
2208 $ hg status --copies --rev 'desc("a-2")' --rev 'desc("mEAm-0")'
2209 M f (no-changeset !)
2209 M f (no-changeset !)
2210 b (no-filelog no-changeset !)
2210 b (no-filelog no-changeset !)
2211 R b
2211 R b
2212 $ hg status --copies --rev 'desc("e-2")' --rev 'desc("mAEm-0")'
2212 $ hg status --copies --rev 'desc("e-2")' --rev 'desc("mAEm-0")'
2213 M f (no-changeset !)
2213 M f (no-changeset !)
2214 d (no-filelog no-changeset !)
2214 d (no-filelog no-changeset !)
2215 R d
2215 R d
2216 $ hg status --copies --rev 'desc("e-2")' --rev 'desc("mEAm-0")'
2216 $ hg status --copies --rev 'desc("e-2")' --rev 'desc("mEAm-0")'
2217 M f (no-changeset !)
2217 M f (no-changeset !)
2218 d (no-filelog no-changeset !)
2218 d (no-filelog no-changeset !)
2219 R d
2219 R d
2220 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("a-2")'
2220 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("a-2")'
2221 A f
2221 A f
2222 d
2222 d
2223 R d
2223 R d
2224 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("e-2")'
2224 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("e-2")'
2225 A f
2225 A f
2226 b
2226 b
2227 R b
2227 R b
2228
2228
2229 # From here, we run status against revision where both source file exists.
2229 # From here, we run status against revision where both source file exists.
2230 #
2230 #
2231 # The filelog based implementation picks an arbitrary side based on revision
2231 # The filelog based implementation picks an arbitrary side based on revision
2232 # numbers. So the same side "wins" whatever the parents order is. This is
2232 # numbers. So the same side "wins" whatever the parents order is. This is
2233 # sub-optimal because depending on revision numbers means the result can be
2233 # sub-optimal because depending on revision numbers means the result can be
2234 # different from one repository to the next.
2234 # different from one repository to the next.
2235 #
2235 #
2236 # The changeset based algorithm use the parent order to break tie on conflicting
2236 # The changeset based algorithm use the parent order to break tie on conflicting
2237 # information and will have a different order depending on who is p1 and p2.
2237 # information and will have a different order depending on who is p1 and p2.
2238 # That order is stable accross repositories. (data from p1 prevails)
2238 # That order is stable accross repositories. (data from p1 prevails)
2239
2239
2240 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mAEm-0")'
2240 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mAEm-0")'
2241 A f
2241 A f
2242 d
2242 d
2243 R b
2243 R b
2244 R d
2244 R d
2245 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mEAm-0")'
2245 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mEAm-0")'
2246 A f
2246 A f
2247 d (filelog !)
2247 d (filelog !)
2248 b (no-filelog !)
2248 b (no-filelog !)
2249 R b
2249 R b
2250 R d
2250 R d
2251 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mAEm-0")'
2251 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mAEm-0")'
2252 A f
2252 A f
2253 a
2253 a
2254 A t
2254 A t
2255 p
2255 p
2256 R a
2256 R a
2257 R b
2257 R b
2258 R p
2258 R p
2259 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mEAm-0")'
2259 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mEAm-0")'
2260 A f
2260 A f
2261 a (filelog !)
2261 a (filelog !)
2262 b (no-filelog !)
2262 b (no-filelog !)
2263 A t
2263 A t
2264 p
2264 p
2265 R a
2265 R a
2266 R b
2266 R b
2267 R p
2267 R p
2268
2268
2269
2269
2270 Subcase: existing copy information overwritten on one branch
2270 Subcase: existing copy information overwritten on one branch
2271 ````````````````````````````````````````````````````````````
2271 ````````````````````````````````````````````````````````````
2272
2272
2273 Note:
2273 Note:
2274 | In this case, one of the merge wrongly record a merge while there is none.
2274 | In this case, one of the merge wrongly record a merge while there is none.
2275 | This lead to bad copy tracing information to be dug up.
2275 | This lead to bad copy tracing information to be dug up.
2276
2276
2277
2277
2278 Merge:
2278 Merge:
2279 - one with change to an unrelated file (b)
2279 - one with change to an unrelated file (b)
2280 - one overwriting a file (d) with a rename (from h to i to d)
2280 - one overwriting a file (d) with a rename (from h to i to d)
2281
2281
2282 $ hg log -G --rev '::(desc("mBFm")+desc("mFBm"))'
2282 $ hg log -G --rev '::(desc("mBFm")+desc("mFBm"))'
2283 o mFBm-0 simple merge - B side: unrelated change, F side: overwrite d with a copy (from h->i->d) - the other way
2283 o mFBm-0 simple merge - B side: unrelated change, F side: overwrite d with a copy (from h->i->d) - the other way
2284 |\
2284 |\
2285 +---o mBFm-0 simple merge - B side: unrelated change, F side: overwrite d with a copy (from h->i->d) - one way
2285 +---o mBFm-0 simple merge - B side: unrelated change, F side: overwrite d with a copy (from h->i->d) - one way
2286 | |/
2286 | |/
2287 | o f-2: rename i -> d
2287 | o f-2: rename i -> d
2288 | |
2288 | |
2289 | o f-1: rename h -> i
2289 | o f-1: rename h -> i
2290 | |
2290 | |
2291 o | b-1: b update
2291 o | b-1: b update
2292 |/
2292 |/
2293 o i-2: c -move-> d, s -move-> t
2293 o i-2: c -move-> d, s -move-> t
2294 |
2294 |
2295 o i-1: a -move-> c, p -move-> s
2295 o i-1: a -move-> c, p -move-> s
2296 |
2296 |
2297 o i-0 initial commit: a b h
2297 o i-0 initial commit: a b h
2298
2298
2299 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mBFm-0")'
2299 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mBFm-0")'
2300 M b
2300 M b
2301 A d
2301 A d
2302 h
2302 h
2303 A t
2303 A t
2304 p
2304 p
2305 R a
2305 R a
2306 R h
2306 R h
2307 R p
2307 R p
2308 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mFBm-0")'
2308 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mFBm-0")'
2309 M b
2309 M b
2310 A d
2310 A d
2311 h
2311 h
2312 A t
2312 A t
2313 p
2313 p
2314 R a
2314 R a
2315 R h
2315 R h
2316 R p
2316 R p
2317 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mBFm-0")'
2317 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mBFm-0")'
2318 M d (no-changeset !)
2318 M d (no-changeset !)
2319 h (no-filelog no-changeset !)
2319 h (no-filelog no-changeset !)
2320 R h
2320 R h
2321 $ hg status --copies --rev 'desc("f-2")' --rev 'desc("mBFm-0")'
2321 $ hg status --copies --rev 'desc("f-2")' --rev 'desc("mBFm-0")'
2322 M b
2322 M b
2323 $ hg status --copies --rev 'desc("f-1")' --rev 'desc("mBFm-0")'
2323 $ hg status --copies --rev 'desc("f-1")' --rev 'desc("mBFm-0")'
2324 M b
2324 M b
2325 M d (no-changeset !)
2325 M d (no-changeset !)
2326 i (no-filelog no-changeset !)
2326 i (no-filelog no-changeset !)
2327 R i
2327 R i
2328 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mFBm-0")'
2328 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mFBm-0")'
2329 M d (no-changeset !)
2329 M d (no-changeset !)
2330 h (no-filelog no-changeset !)
2330 h (no-filelog no-changeset !)
2331 R h
2331 R h
2332 $ hg status --copies --rev 'desc("f-2")' --rev 'desc("mFBm-0")'
2332 $ hg status --copies --rev 'desc("f-2")' --rev 'desc("mFBm-0")'
2333 M b
2333 M b
2334 $ hg status --copies --rev 'desc("f-1")' --rev 'desc("mFBm-0")'
2334 $ hg status --copies --rev 'desc("f-1")' --rev 'desc("mFBm-0")'
2335 M b
2335 M b
2336 M d (no-changeset !)
2336 M d (no-changeset !)
2337 i (no-filelog no-changeset !)
2337 i (no-filelog no-changeset !)
2338 R i
2338 R i
2339
2339
2340 #if no-changeset
2340 #if no-changeset
2341 $ hg log -Gfr 'desc("mBFm-0")' d
2341 $ hg log -Gfr 'desc("mBFm-0")' d
2342 o f-2: rename i -> d
2342 o f-2: rename i -> d
2343 |
2343 |
2344 o f-1: rename h -> i
2344 o f-1: rename h -> i
2345 :
2345 :
2346 o i-0 initial commit: a b h
2346 o i-0 initial commit: a b h
2347
2347
2348 #else
2348 #else
2349 BROKEN: `hg log --follow <file>` relies on filelog metadata to work
2349 BROKEN: `hg log --follow <file>` relies on filelog metadata to work
2350 $ hg log -Gfr 'desc("mBFm-0")' d
2350 $ hg log -Gfr 'desc("mBFm-0")' d
2351 o i-2: c -move-> d, s -move-> t
2351 o i-2: c -move-> d, s -move-> t
2352 |
2352 |
2353 ~
2353 ~
2354 #endif
2354 #endif
2355
2355
2356 #if no-changeset
2356 #if no-changeset
2357 $ hg log -Gfr 'desc("mFBm-0")' d
2357 $ hg log -Gfr 'desc("mFBm-0")' d
2358 o f-2: rename i -> d
2358 o f-2: rename i -> d
2359 |
2359 |
2360 o f-1: rename h -> i
2360 o f-1: rename h -> i
2361 :
2361 :
2362 o i-0 initial commit: a b h
2362 o i-0 initial commit: a b h
2363
2363
2364 #else
2364 #else
2365 BROKEN: `hg log --follow <file>` relies on filelog metadata to work
2365 BROKEN: `hg log --follow <file>` relies on filelog metadata to work
2366 $ hg log -Gfr 'desc("mFBm-0")' d
2366 $ hg log -Gfr 'desc("mFBm-0")' d
2367 o i-2: c -move-> d, s -move-> t
2367 o i-2: c -move-> d, s -move-> t
2368 |
2368 |
2369 ~
2369 ~
2370 #endif
2370 #endif
2371
2371
2372
2372
2373 Subcase: existing copy information overwritten on one branch, with different content)
2373 Subcase: existing copy information overwritten on one branch, with different content)
2374 `````````````````````````````````````````````````````````````````````````````````````
2374 `````````````````````````````````````````````````````````````````````````````````````
2375
2375
2376 Merge:
2376 Merge:
2377 - one with change to an unrelated file (b)
2377 - one with change to an unrelated file (b)
2378 - one overwriting a file (t) with a rename (from r to x to t), v content is not the same as on the other branch
2378 - one overwriting a file (t) with a rename (from r to x to t), v content is not the same as on the other branch
2379
2379
2380 $ hg log -G --rev '::(desc("mBRm")+desc("mRBm"))'
2380 $ hg log -G --rev '::(desc("mBRm")+desc("mRBm"))'
2381 o mRBm-0 simple merge - B side: unrelated change, R side: overwrite d with a copy (from r->x->t) different content - the other way
2381 o mRBm-0 simple merge - B side: unrelated change, R side: overwrite d with a copy (from r->x->t) different content - the other way
2382 |\
2382 |\
2383 +---o mBRm-0 simple merge - B side: unrelated change, R side: overwrite d with a copy (from r->x->t) different content - one way
2383 +---o mBRm-0 simple merge - B side: unrelated change, R side: overwrite d with a copy (from r->x->t) different content - one way
2384 | |/
2384 | |/
2385 | o r-2: rename t -> x
2385 | o r-2: rename t -> x
2386 | |
2386 | |
2387 | o r-1: rename r -> x
2387 | o r-1: rename r -> x
2388 | |
2388 | |
2389 o | b-1: b update
2389 o | b-1: b update
2390 |/
2390 |/
2391 o i-2: c -move-> d, s -move-> t
2391 o i-2: c -move-> d, s -move-> t
2392 |
2392 |
2393 o i-1: a -move-> c, p -move-> s
2393 o i-1: a -move-> c, p -move-> s
2394 |
2394 |
2395 o i-0 initial commit: a b h
2395 o i-0 initial commit: a b h
2396
2396
2397 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mBRm-0")'
2397 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mBRm-0")'
2398 M b
2398 M b
2399 A d
2399 A d
2400 a
2400 a
2401 A t
2401 A t
2402 r
2402 r
2403 R a
2403 R a
2404 R p
2404 R p
2405 R r
2405 R r
2406 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mRBm-0")'
2406 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mRBm-0")'
2407 M b
2407 M b
2408 A d
2408 A d
2409 a
2409 a
2410 A t
2410 A t
2411 r
2411 r
2412 R a
2412 R a
2413 R p
2413 R p
2414 R r
2414 R r
2415 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mBRm-0")'
2415 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mBRm-0")'
2416 M t
2416 M t
2417 r (no-filelog !)
2417 r (no-filelog !)
2418 R r
2418 R r
2419 $ hg status --copies --rev 'desc("r-2")' --rev 'desc("mBRm-0")'
2419 $ hg status --copies --rev 'desc("r-2")' --rev 'desc("mBRm-0")'
2420 M b
2420 M b
2421 $ hg status --copies --rev 'desc("r-1")' --rev 'desc("mBRm-0")'
2421 $ hg status --copies --rev 'desc("r-1")' --rev 'desc("mBRm-0")'
2422 M b
2422 M b
2423 M t
2423 M t
2424 x (no-filelog !)
2424 x (no-filelog !)
2425 R x
2425 R x
2426 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mRBm-0")'
2426 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mRBm-0")'
2427 M t
2427 M t
2428 r (no-filelog !)
2428 r (no-filelog !)
2429 R r
2429 R r
2430 $ hg status --copies --rev 'desc("r-2")' --rev 'desc("mRBm-0")'
2430 $ hg status --copies --rev 'desc("r-2")' --rev 'desc("mRBm-0")'
2431 M b
2431 M b
2432 $ hg status --copies --rev 'desc("r-1")' --rev 'desc("mRBm-0")'
2432 $ hg status --copies --rev 'desc("r-1")' --rev 'desc("mRBm-0")'
2433 M b
2433 M b
2434 M t
2434 M t
2435 x (no-filelog !)
2435 x (no-filelog !)
2436 R x
2436 R x
2437
2437
2438 #if no-changeset
2438 #if no-changeset
2439 $ hg log -Gfr 'desc("mBRm-0")' d
2439 $ hg log -Gfr 'desc("mBRm-0")' d
2440 o i-2: c -move-> d, s -move-> t
2440 o i-2: c -move-> d, s -move-> t
2441 |
2441 |
2442 o i-1: a -move-> c, p -move-> s
2442 o i-1: a -move-> c, p -move-> s
2443 |
2443 |
2444 o i-0 initial commit: a b h
2444 o i-0 initial commit: a b h
2445
2445
2446 #else
2446 #else
2447 BROKEN: `hg log --follow <file>` relies on filelog metadata to work
2447 BROKEN: `hg log --follow <file>` relies on filelog metadata to work
2448 $ hg log -Gfr 'desc("mBRm-0")' d
2448 $ hg log -Gfr 'desc("mBRm-0")' d
2449 o i-2: c -move-> d, s -move-> t
2449 o i-2: c -move-> d, s -move-> t
2450 |
2450 |
2451 ~
2451 ~
2452 #endif
2452 #endif
2453
2453
2454 #if no-changeset
2454 #if no-changeset
2455 $ hg log -Gfr 'desc("mRBm-0")' d
2455 $ hg log -Gfr 'desc("mRBm-0")' d
2456 o i-2: c -move-> d, s -move-> t
2456 o i-2: c -move-> d, s -move-> t
2457 |
2457 |
2458 o i-1: a -move-> c, p -move-> s
2458 o i-1: a -move-> c, p -move-> s
2459 |
2459 |
2460 o i-0 initial commit: a b h
2460 o i-0 initial commit: a b h
2461
2461
2462 #else
2462 #else
2463 BROKEN: `hg log --follow <file>` relies on filelog metadata to work
2463 BROKEN: `hg log --follow <file>` relies on filelog metadata to work
2464 $ hg log -Gfr 'desc("mRBm-0")' d
2464 $ hg log -Gfr 'desc("mRBm-0")' d
2465 o i-2: c -move-> d, s -move-> t
2465 o i-2: c -move-> d, s -move-> t
2466 |
2466 |
2467 ~
2467 ~
2468 #endif
2468 #endif
2469
2469
2470 Subcase: reset of the copy history on one side
2470 Subcase: reset of the copy history on one side
2471 ``````````````````````````````````````````````
2471 ``````````````````````````````````````````````
2472
2472
2473 Merge:
2473 Merge:
2474 - one with change to a file
2474 - one with change to a file
2475 - one deleting and recreating the file
2475 - one deleting and recreating the file
2476
2476
2477 Unlike in the 'BD/DB' cases, an actual merge happened here. So we should
2477 Unlike in the 'BD/DB' cases, an actual merge happened here. So we should
2478 consider history and rename on both branch of the merge.
2478 consider history and rename on both branch of the merge.
2479
2479
2480 $ hg log -G --rev '::(desc("mDGm")+desc("mGDm"))'
2480 $ hg log -G --rev '::(desc("mDGm")+desc("mGDm"))'
2481 o mGDm-0 actual content merge, copies on one side - D side: delete and re-add (different content), G side: update content - the other way
2481 o mGDm-0 actual content merge, copies on one side - D side: delete and re-add (different content), G side: update content - the other way
2482 |\
2482 |\
2483 +---o mDGm-0 actual content merge, copies on one side - D side: delete and re-add (different content), G side: update content - one way
2483 +---o mDGm-0 actual content merge, copies on one side - D side: delete and re-add (different content), G side: update content - one way
2484 | |/
2484 | |/
2485 | o g-1: update d
2485 | o g-1: update d
2486 | |
2486 | |
2487 o | d-2 re-add d
2487 o | d-2 re-add d
2488 | |
2488 | |
2489 o | d-1 delete d
2489 o | d-1 delete d
2490 |/
2490 |/
2491 o i-2: c -move-> d, s -move-> t
2491 o i-2: c -move-> d, s -move-> t
2492 |
2492 |
2493 o i-1: a -move-> c, p -move-> s
2493 o i-1: a -move-> c, p -move-> s
2494 |
2494 |
2495 o i-0 initial commit: a b h
2495 o i-0 initial commit: a b h
2496
2496
2497 One side of the merge have a long history with rename. The other side of the
2497 One side of the merge have a long history with rename. The other side of the
2498 merge point to a new file with a smaller history. Each side is "valid".
2498 merge point to a new file with a smaller history. Each side is "valid".
2499
2499
2500 (and again the filelog based algorithm only explore one, with a pick based on
2500 (and again the filelog based algorithm only explore one, with a pick based on
2501 revision numbers)
2501 revision numbers)
2502
2502
2503 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mDGm-0")'
2503 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mDGm-0")'
2504 A d
2504 A d
2505 a (filelog !)
2505 a (filelog !)
2506 A t
2506 A t
2507 p
2507 p
2508 R a
2508 R a
2509 R p
2509 R p
2510 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mGDm-0")'
2510 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mGDm-0")'
2511 A d
2511 A d
2512 a
2512 a
2513 A t
2513 A t
2514 p
2514 p
2515 R a
2515 R a
2516 R p
2516 R p
2517 $ hg status --copies --rev 'desc("d-2")' --rev 'desc("mDGm-0")'
2517 $ hg status --copies --rev 'desc("d-2")' --rev 'desc("mDGm-0")'
2518 M d
2518 M d
2519 $ hg status --copies --rev 'desc("d-2")' --rev 'desc("mGDm-0")'
2519 $ hg status --copies --rev 'desc("d-2")' --rev 'desc("mGDm-0")'
2520 M d
2520 M d
2521 $ hg status --copies --rev 'desc("g-1")' --rev 'desc("mDGm-0")'
2521 $ hg status --copies --rev 'desc("g-1")' --rev 'desc("mDGm-0")'
2522 M d
2522 M d
2523 $ hg status --copies --rev 'desc("g-1")' --rev 'desc("mGDm-0")'
2523 $ hg status --copies --rev 'desc("g-1")' --rev 'desc("mGDm-0")'
2524 M d
2524 M d
2525
2525
2526 #if no-changeset
2526 #if no-changeset
2527 $ hg log -Gfr 'desc("mDGm-0")' d
2527 $ hg log -Gfr 'desc("mDGm-0")' d
2528 o mDGm-0 actual content merge, copies on one side - D side: delete and re-add (different content), G side: update content - one way
2528 o mDGm-0 actual content merge, copies on one side - D side: delete and re-add (different content), G side: update content - one way
2529 |\
2529 |\
2530 | o g-1: update d
2530 | o g-1: update d
2531 | |
2531 | |
2532 o | d-2 re-add d
2532 o | d-2 re-add d
2533 |/
2533 |/
2534 o i-2: c -move-> d, s -move-> t
2534 o i-2: c -move-> d, s -move-> t
2535 |
2535 |
2536 o i-1: a -move-> c, p -move-> s
2536 o i-1: a -move-> c, p -move-> s
2537 |
2537 |
2538 o i-0 initial commit: a b h
2538 o i-0 initial commit: a b h
2539
2539
2540 #else
2540 #else
2541 BROKEN: `hg log --follow <file>` relies on filelog metadata to work
2541 BROKEN: `hg log --follow <file>` relies on filelog metadata to work
2542 $ hg log -Gfr 'desc("mDGm-0")' d
2542 $ hg log -Gfr 'desc("mDGm-0")' d
2543 o mDGm-0 actual content merge, copies on one side - D side: delete and re-add (different content), G side: update content - one way
2543 o mDGm-0 actual content merge, copies on one side - D side: delete and re-add (different content), G side: update content - one way
2544 |\
2544 |\
2545 | o g-1: update d
2545 | o g-1: update d
2546 | |
2546 | |
2547 o | d-2 re-add d
2547 o | d-2 re-add d
2548 |/
2548 |/
2549 o i-2: c -move-> d, s -move-> t
2549 o i-2: c -move-> d, s -move-> t
2550 |
2550 |
2551 ~
2551 ~
2552 #endif
2552 #endif
2553
2553
2554
2554
2555 #if no-changeset
2555 #if no-changeset
2556 $ hg log -Gfr 'desc("mDGm-0")' d
2556 $ hg log -Gfr 'desc("mDGm-0")' d
2557 o mDGm-0 actual content merge, copies on one side - D side: delete and re-add (different content), G side: update content - one way
2557 o mDGm-0 actual content merge, copies on one side - D side: delete and re-add (different content), G side: update content - one way
2558 |\
2558 |\
2559 | o g-1: update d
2559 | o g-1: update d
2560 | |
2560 | |
2561 o | d-2 re-add d
2561 o | d-2 re-add d
2562 |/
2562 |/
2563 o i-2: c -move-> d, s -move-> t
2563 o i-2: c -move-> d, s -move-> t
2564 |
2564 |
2565 o i-1: a -move-> c, p -move-> s
2565 o i-1: a -move-> c, p -move-> s
2566 |
2566 |
2567 o i-0 initial commit: a b h
2567 o i-0 initial commit: a b h
2568
2568
2569 #else
2569 #else
2570 BROKEN: `hg log --follow <file>` relies on filelog metadata to work
2570 BROKEN: `hg log --follow <file>` relies on filelog metadata to work
2571 $ hg log -Gfr 'desc("mDGm-0")' d
2571 $ hg log -Gfr 'desc("mDGm-0")' d
2572 o mDGm-0 actual content merge, copies on one side - D side: delete and re-add (different content), G side: update content - one way
2572 o mDGm-0 actual content merge, copies on one side - D side: delete and re-add (different content), G side: update content - one way
2573 |\
2573 |\
2574 | o g-1: update d
2574 | o g-1: update d
2575 | |
2575 | |
2576 o | d-2 re-add d
2576 o | d-2 re-add d
2577 |/
2577 |/
2578 o i-2: c -move-> d, s -move-> t
2578 o i-2: c -move-> d, s -move-> t
2579 |
2579 |
2580 ~
2580 ~
2581 #endif
2581 #endif
2582
2582
2583 Subcase: merging a change to a file with a "copy overwrite" to that file from another branch
2583 Subcase: merging a change to a file with a "copy overwrite" to that file from another branch
2584 ````````````````````````````````````````````````````````````````````````````````````````````
2584 ````````````````````````````````````````````````````````````````````````````````````````````
2585
2585
2586 Merge:
2586 Merge:
2587 - one with change to a file (d)
2587 - one with change to a file (d)
2588 - one overwriting that file with a rename (from h to i, to d)
2588 - one overwriting that file with a rename (from h to i, to d)
2589
2589
2590 This case is similar to BF/FB, but an actual merge happens, so both side of the
2590 This case is similar to BF/FB, but an actual merge happens, so both side of the
2591 history are relevant.
2591 history are relevant.
2592
2592
2593
2593
2594 $ hg log -G --rev '::(desc("mGFm")+desc("mFGm"))'
2594 $ hg log -G --rev '::(desc("mGFm")+desc("mFGm"))'
2595 o mGFm-0 merge - G side: content change, F side: copy overwrite, no content change - the other way
2595 o mGFm-0 merge - G side: content change, F side: copy overwrite, no content change - the other way
2596 |\
2596 |\
2597 +---o mFGm-0 merge - G side: content change, F side: copy overwrite, no content change - one way
2597 +---o mFGm-0 merge - G side: content change, F side: copy overwrite, no content change - one way
2598 | |/
2598 | |/
2599 | o g-1: update d
2599 | o g-1: update d
2600 | |
2600 | |
2601 o | f-2: rename i -> d
2601 o | f-2: rename i -> d
2602 | |
2602 | |
2603 o | f-1: rename h -> i
2603 o | f-1: rename h -> i
2604 |/
2604 |/
2605 o i-2: c -move-> d, s -move-> t
2605 o i-2: c -move-> d, s -move-> t
2606 |
2606 |
2607 o i-1: a -move-> c, p -move-> s
2607 o i-1: a -move-> c, p -move-> s
2608 |
2608 |
2609 o i-0 initial commit: a b h
2609 o i-0 initial commit: a b h
2610
2610
2611
2611
2612 Note:
2612 Note:
2613 | In this case, the merge get conflicting information since on one side we have
2613 | In this case, the merge get conflicting information since on one side we have
2614 | "a -> c -> d". and one the other one we have "h -> i -> d".
2614 | "a -> c -> d". and one the other one we have "h -> i -> d".
2615 |
2615 |
2616 | The current code arbitrarily pick one side depending the ordering of the merged hash:
2616 | The current code arbitrarily pick one side depending the ordering of the merged hash:
2617
2617
2618 In this case, the file hash from "f-2" is lower, so it will be `p1` of the resulting filenode its copy tracing information will win (and trace back to "h"):
2618 In this case, the file hash from "f-2" is lower, so it will be `p1` of the resulting filenode its copy tracing information will win (and trace back to "h"):
2619
2619
2620 Details on this hash ordering pick:
2620 Details on this hash ordering pick:
2621
2621
2622 $ hg manifest --debug 'desc("g-1")' | egrep 'd$'
2622 $ hg manifest --debug 'desc("g-1")' | egrep 'd$'
2623 17ec97e605773eb44a117d1136b3849bcdc1924f 644 d (no-changeset !)
2623 17ec97e605773eb44a117d1136b3849bcdc1924f 644 d (no-changeset !)
2624 5cce88bf349f7c742bb440f2c53f81db9c294279 644 d (changeset !)
2624 5cce88bf349f7c742bb440f2c53f81db9c294279 644 d (changeset !)
2625 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("g-1")' d
2625 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("g-1")' d
2626 A d
2626 A d
2627 a (no-changeset no-compatibility !)
2627 a (no-changeset no-compatibility !)
2628
2628
2629 $ hg manifest --debug 'desc("f-2")' | egrep 'd$'
2629 $ hg manifest --debug 'desc("f-2")' | egrep 'd$'
2630 7b79e2fe0c8924e0e598a82f048a7b024afa4d96 644 d (no-changeset !)
2630 7b79e2fe0c8924e0e598a82f048a7b024afa4d96 644 d (no-changeset !)
2631 ae258f702dfeca05bf9b6a22a97a4b5645570f11 644 d (changeset !)
2631 ae258f702dfeca05bf9b6a22a97a4b5645570f11 644 d (changeset !)
2632 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("f-2")' d
2632 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("f-2")' d
2633 A d
2633 A d
2634 h (no-changeset no-compatibility !)
2634 h (no-changeset no-compatibility !)
2635
2635
2636 Copy tracing data on the resulting merge:
2636 Copy tracing data on the resulting merge:
2637
2637
2638 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mFGm-0")'
2638 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mFGm-0")'
2639 A d
2639 A d
2640 h (no-filelog !)
2640 h (no-filelog !)
2641 a (filelog !)
2641 a (filelog !)
2642 A t
2642 A t
2643 p
2643 p
2644 R a
2644 R a
2645 R h
2645 R h
2646 R p
2646 R p
2647 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mGFm-0")'
2647 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mGFm-0")'
2648 A d
2648 A d
2649 a (no-changeset !)
2649 a (no-changeset !)
2650 h (changeset !)
2650 h (changeset !)
2651 A t
2651 A t
2652 p
2652 p
2653 R a
2653 R a
2654 R h
2654 R h
2655 R p
2655 R p
2656 $ hg status --copies --rev 'desc("f-2")' --rev 'desc("mFGm-0")'
2656 $ hg status --copies --rev 'desc("f-2")' --rev 'desc("mFGm-0")'
2657 M d
2657 M d
2658 $ hg status --copies --rev 'desc("f-2")' --rev 'desc("mGFm-0")'
2658 $ hg status --copies --rev 'desc("f-2")' --rev 'desc("mGFm-0")'
2659 M d
2659 M d
2660 $ hg status --copies --rev 'desc("f-1")' --rev 'desc("mFGm-0")'
2660 $ hg status --copies --rev 'desc("f-1")' --rev 'desc("mFGm-0")'
2661 M d
2661 M d
2662 i (no-filelog !)
2662 i (no-filelog !)
2663 R i
2663 R i
2664 $ hg status --copies --rev 'desc("f-1")' --rev 'desc("mGFm-0")'
2664 $ hg status --copies --rev 'desc("f-1")' --rev 'desc("mGFm-0")'
2665 M d
2665 M d
2666 i (no-filelog !)
2666 i (no-filelog !)
2667 R i
2667 R i
2668 $ hg status --copies --rev 'desc("g-1")' --rev 'desc("mFGm-0")'
2668 $ hg status --copies --rev 'desc("g-1")' --rev 'desc("mFGm-0")'
2669 M d (no-changeset !)
2669 M d (no-changeset !)
2670 h (no-filelog no-changeset !)
2670 h (no-filelog no-changeset !)
2671 R h
2671 R h
2672 $ hg status --copies --rev 'desc("g-1")' --rev 'desc("mGFm-0")'
2672 $ hg status --copies --rev 'desc("g-1")' --rev 'desc("mGFm-0")'
2673 M d (no-changeset !)
2673 M d (no-changeset !)
2674 h (no-filelog no-changeset !)
2674 h (no-filelog no-changeset !)
2675 R h
2675 R h
2676
2676
2677 #if no-changeset
2677 #if no-changeset
2678 $ hg log -Gfr 'desc("mFGm-0")' d
2678 $ hg log -Gfr 'desc("mFGm-0")' d
2679 o mFGm-0 merge - G side: content change, F side: copy overwrite, no content change - one way
2679 o mFGm-0 merge - G side: content change, F side: copy overwrite, no content change - one way
2680 |\
2680 |\
2681 | o g-1: update d
2681 | o g-1: update d
2682 | |
2682 | |
2683 o | f-2: rename i -> d
2683 o | f-2: rename i -> d
2684 | |
2684 | |
2685 o | f-1: rename h -> i
2685 o | f-1: rename h -> i
2686 |/
2686 |/
2687 o i-2: c -move-> d, s -move-> t
2687 o i-2: c -move-> d, s -move-> t
2688 |
2688 |
2689 o i-1: a -move-> c, p -move-> s
2689 o i-1: a -move-> c, p -move-> s
2690 |
2690 |
2691 o i-0 initial commit: a b h
2691 o i-0 initial commit: a b h
2692
2692
2693 #else
2693 #else
2694 BROKEN: `hg log --follow <file>` relies on filelog metadata to work
2694 BROKEN: `hg log --follow <file>` relies on filelog metadata to work
2695 $ hg log -Gfr 'desc("mFGm-0")' d
2695 $ hg log -Gfr 'desc("mFGm-0")' d
2696 o g-1: update d
2696 o g-1: update d
2697 |
2697 |
2698 o i-2: c -move-> d, s -move-> t
2698 o i-2: c -move-> d, s -move-> t
2699 |
2699 |
2700 ~
2700 ~
2701 #endif
2701 #endif
2702
2702
2703 #if no-changeset
2703 #if no-changeset
2704 $ hg log -Gfr 'desc("mGFm-0")' d
2704 $ hg log -Gfr 'desc("mGFm-0")' d
2705 o mGFm-0 merge - G side: content change, F side: copy overwrite, no content change - the other way
2705 o mGFm-0 merge - G side: content change, F side: copy overwrite, no content change - the other way
2706 |\
2706 |\
2707 | o g-1: update d
2707 | o g-1: update d
2708 | |
2708 | |
2709 o | f-2: rename i -> d
2709 o | f-2: rename i -> d
2710 | |
2710 | |
2711 o | f-1: rename h -> i
2711 o | f-1: rename h -> i
2712 |/
2712 |/
2713 o i-2: c -move-> d, s -move-> t
2713 o i-2: c -move-> d, s -move-> t
2714 |
2714 |
2715 o i-1: a -move-> c, p -move-> s
2715 o i-1: a -move-> c, p -move-> s
2716 |
2716 |
2717 o i-0 initial commit: a b h
2717 o i-0 initial commit: a b h
2718
2718
2719 #else
2719 #else
2720 BROKEN: `hg log --follow <file>` relies on filelog metadata to work
2720 BROKEN: `hg log --follow <file>` relies on filelog metadata to work
2721 $ hg log -Gfr 'desc("mGFm-0")' d
2721 $ hg log -Gfr 'desc("mGFm-0")' d
2722 o g-1: update d
2722 o g-1: update d
2723 |
2723 |
2724 o i-2: c -move-> d, s -move-> t
2724 o i-2: c -move-> d, s -move-> t
2725 |
2725 |
2726 ~
2726 ~
2727 #endif
2727 #endif
2728
2728
2729 Subcase: new copy information on both side with an actual merge happening
2729 Subcase: new copy information on both side with an actual merge happening
2730 `````````````````````````````````````````````````````````````````````````
2730 `````````````````````````````````````````````````````````````````````````
2731
2731
2732 - the "p-" branch renaming 't' to 'v' (through 'u')
2732 - the "p-" branch renaming 't' to 'v' (through 'u')
2733 - the "q-" branch renaming 'r' to 'v' (through 'w')
2733 - the "q-" branch renaming 'r' to 'v' (through 'w')
2734
2734
2735
2735
2736 $ hg log -G --rev '::(desc("mPQm")+desc("mQPm"))'
2736 $ hg log -G --rev '::(desc("mPQm")+desc("mQPm"))'
2737 o mQPm-0 merge with copies info on both side - P side: rename t to v, Q side: r to v, (different content) - the other way
2737 o mQPm-0 merge with copies info on both side - P side: rename t to v, Q side: r to v, (different content) - the other way
2738 |\
2738 |\
2739 +---o mPQm-0 merge with copies info on both side - P side: rename t to v, Q side: r to v, (different content) - one way
2739 +---o mPQm-0 merge with copies info on both side - P side: rename t to v, Q side: r to v, (different content) - one way
2740 | |/
2740 | |/
2741 | o q-2 w -move-> v
2741 | o q-2 w -move-> v
2742 | |
2742 | |
2743 | o q-1 r -move-> w
2743 | o q-1 r -move-> w
2744 | |
2744 | |
2745 o | p-2: u -move-> v
2745 o | p-2: u -move-> v
2746 | |
2746 | |
2747 o | p-1: t -move-> u
2747 o | p-1: t -move-> u
2748 |/
2748 |/
2749 o i-2: c -move-> d, s -move-> t
2749 o i-2: c -move-> d, s -move-> t
2750 |
2750 |
2751 o i-1: a -move-> c, p -move-> s
2751 o i-1: a -move-> c, p -move-> s
2752 |
2752 |
2753 o i-0 initial commit: a b h
2753 o i-0 initial commit: a b h
2754
2754
2755
2755
2756 #if no-changeset
2756 #if no-changeset
2757 $ hg manifest --debug --rev 'desc("mPQm-0")' | grep '644 v'
2757 $ hg manifest --debug --rev 'desc("mPQm-0")' | grep '644 v'
2758 0946c662ef16e4e67397fd717389eb6693d41749 644 v
2758 0946c662ef16e4e67397fd717389eb6693d41749 644 v
2759 $ hg manifest --debug --rev 'desc("mQPm-0")' | grep '644 v'
2759 $ hg manifest --debug --rev 'desc("mQPm-0")' | grep '644 v'
2760 0db3aad7fcc1ec27fab57060e327b9e864ea0cc9 644 v
2760 0db3aad7fcc1ec27fab57060e327b9e864ea0cc9 644 v
2761 $ hg manifest --debug --rev 'desc("p-2")' | grep '644 v'
2761 $ hg manifest --debug --rev 'desc("p-2")' | grep '644 v'
2762 3f91841cd75cadc9a1f1b4e7c1aa6d411f76032e 644 v
2762 3f91841cd75cadc9a1f1b4e7c1aa6d411f76032e 644 v
2763 $ hg manifest --debug --rev 'desc("q-2")' | grep '644 v'
2763 $ hg manifest --debug --rev 'desc("q-2")' | grep '644 v'
2764 c43c088b811fd27983c0a9aadf44f3343cd4cd7e 644 v
2764 c43c088b811fd27983c0a9aadf44f3343cd4cd7e 644 v
2765 $ hg debugindex v | ../no-linkrev
2765 $ hg debugindex v | ../no-linkrev
2766 rev linkrev nodeid p1 p2
2766 rev linkrev nodeid p1 p2
2767 0 * 3f91841cd75c 000000000000 000000000000
2767 0 * 3f91841cd75c 000000000000 000000000000
2768 1 * c43c088b811f 000000000000 000000000000
2768 1 * c43c088b811f 000000000000 000000000000
2769 2 * 0946c662ef16 3f91841cd75c c43c088b811f
2769 2 * 0946c662ef16 3f91841cd75c c43c088b811f
2770 3 * 0db3aad7fcc1 c43c088b811f 3f91841cd75c
2770 3 * 0db3aad7fcc1 c43c088b811f 3f91841cd75c
2771 #else
2771 #else
2772 $ hg manifest --debug --rev 'desc("mPQm-0")' | grep '644 v'
2772 $ hg manifest --debug --rev 'desc("mPQm-0")' | grep '644 v'
2773 65fde9f6e4d4da23b3f610e07b53673ea9541d75 644 v
2773 65fde9f6e4d4da23b3f610e07b53673ea9541d75 644 v
2774 $ hg manifest --debug --rev 'desc("mQPm-0")' | grep '644 v'
2774 $ hg manifest --debug --rev 'desc("mQPm-0")' | grep '644 v'
2775 a098dda6413aecf154eefc976afc38b295acb7e5 644 v
2775 a098dda6413aecf154eefc976afc38b295acb7e5 644 v
2776 $ hg manifest --debug --rev 'desc("p-2")' | grep '644 v'
2776 $ hg manifest --debug --rev 'desc("p-2")' | grep '644 v'
2777 5aed6a8dbff0301328c08360d24354d3d064cf0d 644 v
2777 5aed6a8dbff0301328c08360d24354d3d064cf0d 644 v
2778 $ hg manifest --debug --rev 'desc("q-2")' | grep '644 v'
2778 $ hg manifest --debug --rev 'desc("q-2")' | grep '644 v'
2779 a38b2fa170219750dac9bc7d19df831f213ba708 644 v
2779 a38b2fa170219750dac9bc7d19df831f213ba708 644 v
2780 $ hg debugindex v | ../no-linkrev
2780 $ hg debugindex v | ../no-linkrev
2781 rev linkrev nodeid p1 p2
2781 rev linkrev nodeid p1 p2
2782 0 * 5aed6a8dbff0 000000000000 000000000000
2782 0 * 5aed6a8dbff0 000000000000 000000000000
2783 1 * a38b2fa17021 000000000000 000000000000
2783 1 * a38b2fa17021 000000000000 000000000000
2784 2 * 65fde9f6e4d4 5aed6a8dbff0 a38b2fa17021
2784 2 * 65fde9f6e4d4 5aed6a8dbff0 a38b2fa17021
2785 3 * a098dda6413a a38b2fa17021 5aed6a8dbff0
2785 3 * a098dda6413a a38b2fa17021 5aed6a8dbff0
2786 #endif
2786 #endif
2787
2787
2788 # Here the filelog based implementation is not looking at the rename
2788 # Here the filelog based implementation is not looking at the rename
2789 # information (because the file exist on both side). However the changelog
2789 # information (because the file exist on both side). However the changelog
2790 # based on works fine. We have different output.
2790 # based on works fine. We have different output.
2791
2791
2792 $ hg status --copies --rev 'desc("p-2")' --rev 'desc("mPQm-0")'
2792 $ hg status --copies --rev 'desc("p-2")' --rev 'desc("mPQm-0")'
2793 M v
2793 M v
2794 r (no-filelog !)
2794 r (no-filelog !)
2795 R r
2795 R r
2796 $ hg status --copies --rev 'desc("p-2")' --rev 'desc("mQPm-0")'
2796 $ hg status --copies --rev 'desc("p-2")' --rev 'desc("mQPm-0")'
2797 M v
2797 M v
2798 r (no-filelog !)
2798 r (no-filelog !)
2799 R r
2799 R r
2800 $ hg status --copies --rev 'desc("q-2")' --rev 'desc("mPQm-0")'
2800 $ hg status --copies --rev 'desc("q-2")' --rev 'desc("mPQm-0")'
2801 M v
2801 M v
2802 t (no-filelog !)
2802 t (no-filelog !)
2803 R t
2803 R t
2804 $ hg status --copies --rev 'desc("q-2")' --rev 'desc("mQPm-0")'
2804 $ hg status --copies --rev 'desc("q-2")' --rev 'desc("mQPm-0")'
2805 M v
2805 M v
2806 t (no-filelog !)
2806 t (no-filelog !)
2807 R t
2807 R t
2808 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("p-2")'
2808 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("p-2")'
2809 A v
2809 A v
2810 t
2810 t
2811 R t
2811 R t
2812 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("q-2")'
2812 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("q-2")'
2813 A v
2813 A v
2814 r
2814 r
2815 R r
2815 R r
2816
2816
2817 # From here, we run status against revision where both source file exists.
2817 # From here, we run status against revision where both source file exists.
2818 #
2818 #
2819 # The filelog based implementation picks an arbitrary side based on revision
2819 # The filelog based implementation picks an arbitrary side based on revision
2820 # numbers. So the same side "wins" whatever the parents order is. This is
2820 # numbers. So the same side "wins" whatever the parents order is. This is
2821 # sub-optimal because depending on revision numbers means the result can be
2821 # sub-optimal because depending on revision numbers means the result can be
2822 # different from one repository to the next.
2822 # different from one repository to the next.
2823 #
2823 #
2824 # The changeset based algorithm use the parent order to break tie on conflicting
2824 # The changeset based algorithm use the parent order to break tie on conflicting
2825 # information and will have a different order depending on who is p1 and p2.
2825 # information and will have a different order depending on who is p1 and p2.
2826 # That order is stable accross repositories. (data from p1 prevails)
2826 # That order is stable accross repositories. (data from p1 prevails)
2827
2827
2828 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mPQm-0")'
2828 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mPQm-0")'
2829 A v
2829 A v
2830 t
2830 t
2831 R r
2831 R r
2832 R t
2832 R t
2833 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mQPm-0")'
2833 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mQPm-0")'
2834 A v
2834 A v
2835 t (filelog !)
2835 t (filelog !)
2836 r (no-filelog !)
2836 r (no-filelog !)
2837 R r
2837 R r
2838 R t
2838 R t
2839 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mPQm-0")'
2839 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mPQm-0")'
2840 A d
2840 A d
2841 a
2841 a
2842 A v
2842 A v
2843 r (filelog !)
2843 r (filelog !)
2844 p (no-filelog !)
2844 p (no-filelog !)
2845 R a
2845 R a
2846 R p
2846 R p
2847 R r
2847 R r
2848 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mQPm-0")'
2848 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mQPm-0")'
2849 A d
2849 A d
2850 a
2850 a
2851 A v
2851 A v
2852 r
2852 r
2853 R a
2853 R a
2854 R p
2854 R p
2855 R r
2855 R r
2856
2856
2857
2857
2858 Comparing with merging with a deletion (and keeping the file)
2858 Comparing with merging with a deletion (and keeping the file)
2859 -------------------------------------------------------------
2859 -------------------------------------------------------------
2860
2860
2861 Merge:
2861 Merge:
2862 - one removing a file (d)
2862 - one removing a file (d)
2863 - one updating that file
2863 - one updating that file
2864 - the merge keep the modified version of the file (canceling the delete)
2864 - the merge keep the modified version of the file (canceling the delete)
2865
2865
2866 In this case, the file keep on living after the merge. So we should not drop its
2866 In this case, the file keep on living after the merge. So we should not drop its
2867 copy tracing chain.
2867 copy tracing chain.
2868
2868
2869 $ hg log -G --rev '::(desc("mCGm")+desc("mGCm"))'
2869 $ hg log -G --rev '::(desc("mCGm")+desc("mGCm"))'
2870 o mGCm-0 merge updated/deleted - revive the file (updated content) - the other way
2870 o mGCm-0 merge updated/deleted - revive the file (updated content) - the other way
2871 |\
2871 |\
2872 +---o mCGm-0 merge updated/deleted - revive the file (updated content) - one way
2872 +---o mCGm-0 merge updated/deleted - revive the file (updated content) - one way
2873 | |/
2873 | |/
2874 | o g-1: update d
2874 | o g-1: update d
2875 | |
2875 | |
2876 o | c-1 delete d
2876 o | c-1 delete d
2877 |/
2877 |/
2878 o i-2: c -move-> d, s -move-> t
2878 o i-2: c -move-> d, s -move-> t
2879 |
2879 |
2880 o i-1: a -move-> c, p -move-> s
2880 o i-1: a -move-> c, p -move-> s
2881 |
2881 |
2882 o i-0 initial commit: a b h
2882 o i-0 initial commit: a b h
2883
2883
2884
2884
2885 'a' is the copy source of 'd'
2885 'a' is the copy source of 'd'
2886
2886
2887 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mCGm-0")'
2887 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mCGm-0")'
2888 A d
2888 A d
2889 a (no-compatibility no-changeset !)
2889 a (no-compatibility no-changeset !)
2890 A t
2890 A t
2891 p
2891 p
2892 R a
2892 R a
2893 R p
2893 R p
2894 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mGCm-0")'
2894 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mGCm-0")'
2895 A d
2895 A d
2896 a (no-compatibility no-changeset !)
2896 a (no-compatibility no-changeset !)
2897 A t
2897 A t
2898 p
2898 p
2899 R a
2899 R a
2900 R p
2900 R p
2901 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mCGm-0")'
2901 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mCGm-0")'
2902 A d
2902 A d
2903 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mGCm-0")'
2903 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mGCm-0")'
2904 A d
2904 A d
2905 $ hg status --copies --rev 'desc("g-1")' --rev 'desc("mCGm-0")'
2905 $ hg status --copies --rev 'desc("g-1")' --rev 'desc("mCGm-0")'
2906 $ hg status --copies --rev 'desc("g-1")' --rev 'desc("mGCm-0")'
2906 $ hg status --copies --rev 'desc("g-1")' --rev 'desc("mGCm-0")'
2907
2907
2908
2908
2909 Comparing with merge restoring an untouched deleted file
2909 Comparing with merge restoring an untouched deleted file
2910 --------------------------------------------------------
2910 --------------------------------------------------------
2911
2911
2912 Merge:
2912 Merge:
2913 - one removing a file (d)
2913 - one removing a file (d)
2914 - one leaving the file untouched
2914 - one leaving the file untouched
2915 - the merge actively restore the file to the same content.
2915 - the merge actively restore the file to the same content.
2916
2916
2917 In this case, the file keep on living after the merge. So we should not drop its
2917 In this case, the file keep on living after the merge. So we should not drop its
2918 copy tracing chain.
2918 copy tracing chain.
2919
2919
2920 $ hg log -G --rev '::(desc("mCB-revert-m")+desc("mBC-revert-m"))'
2920 $ hg log -G --rev '::(desc("mCB-revert-m")+desc("mBC-revert-m"))'
2921 o mBC-revert-m-0 merge explicitely revive deleted file - B side: unrelated change, C side: delete d (restored by merge) - the other way
2921 o mBC-revert-m-0 merge explicitely revive deleted file - B side: unrelated change, C side: delete d (restored by merge) - the other way
2922 |\
2922 |\
2923 +---o mCB-revert-m-0 merge explicitely revive deleted file - B side: unrelated change, C side: delete d (restored by merge) - one way
2923 +---o mCB-revert-m-0 merge explicitely revive deleted file - B side: unrelated change, C side: delete d (restored by merge) - one way
2924 | |/
2924 | |/
2925 | o c-1 delete d
2925 | o c-1 delete d
2926 | |
2926 | |
2927 o | b-1: b update
2927 o | b-1: b update
2928 |/
2928 |/
2929 o i-2: c -move-> d, s -move-> t
2929 o i-2: c -move-> d, s -move-> t
2930 |
2930 |
2931 o i-1: a -move-> c, p -move-> s
2931 o i-1: a -move-> c, p -move-> s
2932 |
2932 |
2933 o i-0 initial commit: a b h
2933 o i-0 initial commit: a b h
2934
2934
2935
2935
2936 'a' is the the copy source of 'd'
2936 'a' is the the copy source of 'd'
2937
2937
2938 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mCB-revert-m-0")'
2938 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mCB-revert-m-0")'
2939 M b
2939 M b
2940 A d
2940 A d
2941 a (no-compatibility no-changeset !)
2941 a (no-compatibility no-changeset !)
2942 A t
2942 A t
2943 p
2943 p
2944 R a
2944 R a
2945 R p
2945 R p
2946 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mBC-revert-m-0")'
2946 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mBC-revert-m-0")'
2947 M b
2947 M b
2948 A d
2948 A d
2949 a (no-compatibility no-changeset !)
2949 a (no-compatibility no-changeset !)
2950 A t
2950 A t
2951 p
2951 p
2952 R a
2952 R a
2953 R p
2953 R p
2954 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mCB-revert-m-0")'
2954 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mCB-revert-m-0")'
2955 M b
2955 M b
2956 A d
2956 A d
2957 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mBC-revert-m-0")'
2957 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mBC-revert-m-0")'
2958 M b
2958 M b
2959 A d
2959 A d
2960 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mCB-revert-m-0")'
2960 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mCB-revert-m-0")'
2961 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mBC-revert-m-0")'
2961 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mBC-revert-m-0")'
2962
2962
2963
2963
2964 Merging a branch where a rename was deleted with a branch where the same file was renamed
2964 Merging a branch where a rename was deleted with a branch where the same file was renamed
2965 ------------------------------------------------------------------------------------------
2965 ------------------------------------------------------------------------------------------
2966
2966
2967 Create a "conflicting" merge where `d` get removed on one branch before its
2967 Create a "conflicting" merge where `d` get removed on one branch before its
2968 rename information actually conflict with the other branch.
2968 rename information actually conflict with the other branch.
2969
2969
2970 (the copy information from the branch that was not deleted should win).
2970 (the copy information from the branch that was not deleted should win).
2971
2971
2972 $ hg log -G --rev '::(desc("mCH-delete-before-conflict-m")+desc("mHC-delete-before-conflict-m"))'
2972 $ hg log -G --rev '::(desc("mCH-delete-before-conflict-m")+desc("mHC-delete-before-conflict-m"))'
2973 o mHC-delete-before-conflict-m-0 simple merge - C side: d is the results of renames then deleted, H side: d is result of another rename (same content as the other branch) - the other way
2973 o mHC-delete-before-conflict-m-0 simple merge - C side: d is the results of renames then deleted, H side: d is result of another rename (same content as the other branch) - the other way
2974 |\
2974 |\
2975 +---o mCH-delete-before-conflict-m-0 simple merge - C side: d is the results of renames then deleted, H side: d is result of another rename (same content as the other branch) - one way
2975 +---o mCH-delete-before-conflict-m-0 simple merge - C side: d is the results of renames then deleted, H side: d is result of another rename (same content as the other branch) - one way
2976 | |/
2976 | |/
2977 | o h-1: b -(move)-> d
2977 | o h-1: b -(move)-> d
2978 | |
2978 | |
2979 o | c-1 delete d
2979 o | c-1 delete d
2980 | |
2980 | |
2981 o | i-2: c -move-> d, s -move-> t
2981 o | i-2: c -move-> d, s -move-> t
2982 | |
2982 | |
2983 o | i-1: a -move-> c, p -move-> s
2983 o | i-1: a -move-> c, p -move-> s
2984 |/
2984 |/
2985 o i-0 initial commit: a b h
2985 o i-0 initial commit: a b h
2986
2986
2987
2987
2988 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mCH-delete-before-conflict-m")'
2988 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mCH-delete-before-conflict-m")'
2989 A d
2989 A d
2990 b (no-compatibility no-changeset !)
2990 b (no-compatibility no-changeset !)
2991 A t
2991 A t
2992 p
2992 p
2993 R a
2993 R a
2994 R b
2994 R b
2995 R p
2995 R p
2996 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mHC-delete-before-conflict-m")'
2996 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mHC-delete-before-conflict-m")'
2997 A d
2997 A d
2998 b
2998 b
2999 A t
2999 A t
3000 p
3000 p
3001 R a
3001 R a
3002 R b
3002 R b
3003 R p
3003 R p
3004 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mCH-delete-before-conflict-m")'
3004 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mCH-delete-before-conflict-m")'
3005 A d
3005 A d
3006 b
3006 b
3007 R b
3007 R b
3008 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mHC-delete-before-conflict-m")'
3008 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mHC-delete-before-conflict-m")'
3009 A d
3009 A d
3010 b
3010 b
3011 R b
3011 R b
3012 $ hg status --copies --rev 'desc("h-1")' --rev 'desc("mCH-delete-before-conflict-m")'
3012 $ hg status --copies --rev 'desc("h-1")' --rev 'desc("mCH-delete-before-conflict-m")'
3013 A t
3013 A t
3014 p
3014 p
3015 R a
3015 R a
3016 R p
3016 R p
3017 $ hg status --copies --rev 'desc("h-1")' --rev 'desc("mHC-delete-before-conflict-m")'
3017 $ hg status --copies --rev 'desc("h-1")' --rev 'desc("mHC-delete-before-conflict-m")'
3018 A t
3018 A t
3019 p
3019 p
3020 R a
3020 R a
3021 R p
3021 R p
3022
3022
3023 Variant of previous with extra changes introduced by the merge
3023 Variant of previous with extra changes introduced by the merge
3024 --------------------------------------------------------------
3024 --------------------------------------------------------------
3025
3025
3026 (see case declaration for details)
3026 (see case declaration for details)
3027
3027
3028 Subcase: merge has same initial content on both side, but merge introduced a change
3028 Subcase: merge has same initial content on both side, but merge introduced a change
3029 ```````````````````````````````````````````````````````````````````````````````````
3029 ```````````````````````````````````````````````````````````````````````````````````
3030
3030
3031 - the "e-" branch renaming b to f (through 'g')
3031 - the "e-" branch renaming b to f (through 'g')
3032 - the "a-" branch renaming d to f (through e)
3032 - the "a-" branch renaming d to f (through e)
3033 - the merge add new change to b
3033 - the merge add new change to b
3034
3034
3035 $ hg log -G --rev '::(desc("mAE-change-m")+desc("mEA-change-m"))'
3035 $ hg log -G --rev '::(desc("mAE-change-m")+desc("mEA-change-m"))'
3036 o mEA-change-m-0 merge with file update and copies info on both side - A side: rename d to f, E side: b to f, (same content for f in parent) - the other way
3036 o mEA-change-m-0 merge with file update and copies info on both side - A side: rename d to f, E side: b to f, (same content for f in parent) - the other way
3037 |\
3037 |\
3038 +---o mAE-change-m-0 merge with file update and copies info on both side - A side: rename d to f, E side: b to f, (same content for f in parent) - one way
3038 +---o mAE-change-m-0 merge with file update and copies info on both side - A side: rename d to f, E side: b to f, (same content for f in parent) - one way
3039 | |/
3039 | |/
3040 | o e-2 g -move-> f
3040 | o e-2 g -move-> f
3041 | |
3041 | |
3042 | o e-1 b -move-> g
3042 | o e-1 b -move-> g
3043 | |
3043 | |
3044 o | a-2: e -move-> f
3044 o | a-2: e -move-> f
3045 | |
3045 | |
3046 o | a-1: d -move-> e
3046 o | a-1: d -move-> e
3047 |/
3047 |/
3048 o i-2: c -move-> d, s -move-> t
3048 o i-2: c -move-> d, s -move-> t
3049 |
3049 |
3050 o i-1: a -move-> c, p -move-> s
3050 o i-1: a -move-> c, p -move-> s
3051 |
3051 |
3052 o i-0 initial commit: a b h
3052 o i-0 initial commit: a b h
3053
3053
3054 #if no-changeset
3054 #if no-changeset
3055 $ hg manifest --debug --rev 'desc("mAE-change-m-0")' | grep '644 f'
3055 $ hg manifest --debug --rev 'desc("mAE-change-m-0")' | grep '644 f'
3056 2f649fba7eb284e720d02b61f0546fcef694c045 644 f
3056 2f649fba7eb284e720d02b61f0546fcef694c045 644 f
3057 $ hg manifest --debug --rev 'desc("mEA-change-m-0")' | grep '644 f'
3057 $ hg manifest --debug --rev 'desc("mEA-change-m-0")' | grep '644 f'
3058 774e7c1637d536b99e2d8ef16fd731f87a82bd09 644 f
3058 774e7c1637d536b99e2d8ef16fd731f87a82bd09 644 f
3059 $ hg manifest --debug --rev 'desc("a-2")' | grep '644 f'
3059 $ hg manifest --debug --rev 'desc("a-2")' | grep '644 f'
3060 b76eb76580df486c3d51d63c5c210d4dd43a8ac7 644 f
3060 b76eb76580df486c3d51d63c5c210d4dd43a8ac7 644 f
3061 $ hg manifest --debug --rev 'desc("e-2")' | grep '644 f'
3061 $ hg manifest --debug --rev 'desc("e-2")' | grep '644 f'
3062 e8825b386367b29fec957283a80bb47b47483fe1 644 f
3062 e8825b386367b29fec957283a80bb47b47483fe1 644 f
3063 $ hg debugindex f | ../no-linkrev
3063 $ hg debugindex f | ../no-linkrev
3064 rev linkrev nodeid p1 p2
3064 rev linkrev nodeid p1 p2
3065 0 * b76eb76580df 000000000000 000000000000
3065 0 * b76eb76580df 000000000000 000000000000
3066 1 * e8825b386367 000000000000 000000000000
3066 1 * e8825b386367 000000000000 000000000000
3067 2 * 2ff93c643948 b76eb76580df e8825b386367
3067 2 * 2ff93c643948 b76eb76580df e8825b386367
3068 3 * 2f649fba7eb2 b76eb76580df e8825b386367
3068 3 * 2f649fba7eb2 b76eb76580df e8825b386367
3069 4 * 774e7c1637d5 e8825b386367 b76eb76580df
3069 4 * 774e7c1637d5 e8825b386367 b76eb76580df
3070 #else
3070 #else
3071 $ hg manifest --debug --rev 'desc("mAE-change-m-0")' | grep '644 f'
3071 $ hg manifest --debug --rev 'desc("mAE-change-m-0")' | grep '644 f'
3072 d3613c1ec8310a812ac4268fd853ac576b6caea5 644 f
3072 d3613c1ec8310a812ac4268fd853ac576b6caea5 644 f
3073 $ hg manifest --debug --rev 'desc("mEA-change-m-0")' | grep '644 f'
3073 $ hg manifest --debug --rev 'desc("mEA-change-m-0")' | grep '644 f'
3074 05e03c868bbcab4a649cb33a238d7aa07398a469 644 f
3074 05e03c868bbcab4a649cb33a238d7aa07398a469 644 f
3075 $ hg manifest --debug --rev 'desc("a-2")' | grep '644 f'
3075 $ hg manifest --debug --rev 'desc("a-2")' | grep '644 f'
3076 ae258f702dfeca05bf9b6a22a97a4b5645570f11 644 f
3076 ae258f702dfeca05bf9b6a22a97a4b5645570f11 644 f
3077 $ hg manifest --debug --rev 'desc("e-2")' | grep '644 f'
3077 $ hg manifest --debug --rev 'desc("e-2")' | grep '644 f'
3078 ae258f702dfeca05bf9b6a22a97a4b5645570f11 644 f
3078 ae258f702dfeca05bf9b6a22a97a4b5645570f11 644 f
3079 $ hg debugindex f | ../no-linkrev
3079 $ hg debugindex f | ../no-linkrev
3080 rev linkrev nodeid p1 p2
3080 rev linkrev nodeid p1 p2
3081 0 * ae258f702dfe 000000000000 000000000000
3081 0 * ae258f702dfe 000000000000 000000000000
3082 1 * d3613c1ec831 ae258f702dfe 000000000000
3082 1 * d3613c1ec831 ae258f702dfe 000000000000
3083 2 * 05e03c868bbc ae258f702dfe 000000000000
3083 2 * 05e03c868bbc ae258f702dfe 000000000000
3084 #endif
3084 #endif
3085
3085
3086 # Here the filelog based implementation is not looking at the rename
3086 # Here the filelog based implementation is not looking at the rename
3087 # information (because the file exist on both side). However the changelog
3087 # information (because the file exist on both side). However the changelog
3088 # based on works fine. We have different output.
3088 # based on works fine. We have different output.
3089
3089
3090 $ hg status --copies --rev 'desc("a-2")' --rev 'desc("mAE-change-m-0")'
3090 $ hg status --copies --rev 'desc("a-2")' --rev 'desc("mAE-change-m-0")'
3091 M f
3091 M f
3092 b (no-filelog !)
3092 b (no-filelog !)
3093 R b
3093 R b
3094 $ hg status --copies --rev 'desc("a-2")' --rev 'desc("mEA-change-m-0")'
3094 $ hg status --copies --rev 'desc("a-2")' --rev 'desc("mEA-change-m-0")'
3095 M f
3095 M f
3096 b (no-filelog !)
3096 b (no-filelog !)
3097 R b
3097 R b
3098 $ hg status --copies --rev 'desc("e-2")' --rev 'desc("mAE-change-m-0")'
3098 $ hg status --copies --rev 'desc("e-2")' --rev 'desc("mAE-change-m-0")'
3099 M f
3099 M f
3100 d (no-filelog !)
3100 d (no-filelog !)
3101 R d
3101 R d
3102 $ hg status --copies --rev 'desc("e-2")' --rev 'desc("mEA-change-m-0")'
3102 $ hg status --copies --rev 'desc("e-2")' --rev 'desc("mEA-change-m-0")'
3103 M f
3103 M f
3104 d (no-filelog !)
3104 d (no-filelog !)
3105 R d
3105 R d
3106 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("a-2")'
3106 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("a-2")'
3107 A f
3107 A f
3108 d
3108 d
3109 R d
3109 R d
3110 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("e-2")'
3110 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("e-2")'
3111 A f
3111 A f
3112 b
3112 b
3113 R b
3113 R b
3114
3114
3115 # From here, we run status against revision where both source file exists.
3115 # From here, we run status against revision where both source file exists.
3116 #
3116 #
3117 # The filelog based implementation picks an arbitrary side based on revision
3117 # The filelog based implementation picks an arbitrary side based on revision
3118 # numbers. So the same side "wins" whatever the parents order is. This is
3118 # numbers. So the same side "wins" whatever the parents order is. This is
3119 # sub-optimal because depending on revision numbers means the result can be
3119 # sub-optimal because depending on revision numbers means the result can be
3120 # different from one repository to the next.
3120 # different from one repository to the next.
3121 #
3121 #
3122 # The changeset based algorithm use the parent order to break tie on conflicting
3122 # The changeset based algorithm use the parent order to break tie on conflicting
3123 # information and will have a different order depending on who is p1 and p2.
3123 # information and will have a different order depending on who is p1 and p2.
3124 # That order is stable accross repositories. (data from p1 prevails)
3124 # That order is stable accross repositories. (data from p1 prevails)
3125
3125
3126 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mAE-change-m-0")'
3126 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mAE-change-m-0")'
3127 A f
3127 A f
3128 d
3128 d
3129 R b
3129 R b
3130 R d
3130 R d
3131 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mEA-change-m-0")'
3131 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mEA-change-m-0")'
3132 A f
3132 A f
3133 d (filelog !)
3133 d (filelog !)
3134 b (no-filelog !)
3134 b (no-filelog !)
3135 R b
3135 R b
3136 R d
3136 R d
3137 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mAE-change-m-0")'
3137 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mAE-change-m-0")'
3138 A f
3138 A f
3139 a
3139 a
3140 A t
3140 A t
3141 p
3141 p
3142 R a
3142 R a
3143 R b
3143 R b
3144 R p
3144 R p
3145 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mEA-change-m-0")'
3145 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mEA-change-m-0")'
3146 A f
3146 A f
3147 a (filelog !)
3147 a (filelog !)
3148 b (no-filelog !)
3148 b (no-filelog !)
3149 A t
3149 A t
3150 p
3150 p
3151 R a
3151 R a
3152 R b
3152 R b
3153 R p
3153 R p
3154
3154
3155
3155
3156 Decision from previous merge are properly chained with later merge
3156 Decision from previous merge are properly chained with later merge
3157 ------------------------------------------------------------------
3157 ------------------------------------------------------------------
3158
3158
3159
3159
3160 Subcase: chaining conflicting rename resolution
3160 Subcase: chaining conflicting rename resolution
3161 ```````````````````````````````````````````````
3161 ```````````````````````````````````````````````
3162
3162
3163 The "mAEm" and "mEAm" case create a rename tracking conflict on file 'f'. We
3163 The "mAEm" and "mEAm" case create a rename tracking conflict on file 'f'. We
3164 add more change on the respective branch and merge again. These second merge
3164 add more change on the respective branch and merge again. These second merge
3165 does not involve the file 'f' and the arbitration done within "mAEm" and "mEA"
3165 does not involve the file 'f' and the arbitration done within "mAEm" and "mEA"
3166 about that file should stay unchanged.
3166 about that file should stay unchanged.
3167
3167
3168 The result from mAEm is the same for the subsequent merge:
3168 The result from mAEm is the same for the subsequent merge:
3169
3169
3170 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mAEm")' f
3170 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mAEm")' f
3171 A f
3171 A f
3172 a (filelog !)
3172 a (filelog !)
3173 a (sidedata !)
3173 a (sidedata !)
3174 a (upgraded !)
3174 a (upgraded !)
3175
3175
3176 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mAE,Km")' f
3176 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mAE,Km")' f
3177 A f
3177 A f
3178 a (filelog !)
3178 a (filelog !)
3179 a (sidedata !)
3179 a (sidedata !)
3180 a (upgraded !)
3180 a (upgraded !)
3181
3181
3182 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mK,AEm")' f
3182 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mK,AEm")' f
3183 A f
3183 A f
3184 a (filelog !)
3184 a (filelog !)
3185 a (missing-correct-output sidedata !)
3185 a (sidedata !)
3186 a (missing-correct-output upgraded !)
3186 a (upgraded !)
3187 b (known-bad-output sidedata !)
3188 b (known-bad-output upgraded !)
3189
3187
3190
3188
3191 The result from mEAm is the same for the subsequent merge:
3189 The result from mEAm is the same for the subsequent merge:
3192
3190
3193 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mEAm")' f
3191 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mEAm")' f
3194 A f
3192 A f
3195 a (filelog !)
3193 a (filelog !)
3196 b (sidedata !)
3194 b (sidedata !)
3197 b (upgraded !)
3195 b (upgraded !)
3198
3196
3199 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mEA,Jm")' f
3197 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mEA,Jm")' f
3200 A f
3198 A f
3201 a (filelog !)
3199 a (filelog !)
3202 b (sidedata !)
3200 b (sidedata !)
3203 b (upgraded !)
3201 b (upgraded !)
3204
3202
3205 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mJ,EAm")' f
3203 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mJ,EAm")' f
3206 A f
3204 A f
3207 a (filelog !)
3205 a (filelog !)
3208 b (missing-correct-output sidedata !)
3206 b (sidedata !)
3209 b (missing-correct-output upgraded !)
3207 b (upgraded !)
3210 a (known-bad-output sidedata !)
3211 a (known-bad-output upgraded !)
3212
3208
3213 Subcase: chaining conflicting rename resolution
3209 Subcase: chaining conflicting rename resolution
3214 ```````````````````````````````````````````````
3210 ```````````````````````````````````````````````
3215
3211
3216 The "mPQm" and "mQPm" case create a rename tracking conflict on file 'v'. We
3212 The "mPQm" and "mQPm" case create a rename tracking conflict on file 'v'. We
3217 add more change on the respective branch and merge again. These second merge
3213 add more change on the respective branch and merge again. These second merge
3218 does not involve the file 'v' and the arbitration done within "mPQm" and "mQP"
3214 does not involve the file 'v' and the arbitration done within "mPQm" and "mQP"
3219 about that file should stay unchanged.
3215 about that file should stay unchanged.
3220
3216
3221 The result from mPQm is the same for the subsequent merge:
3217 The result from mPQm is the same for the subsequent merge:
3222
3218
3223 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mPQm")' v
3219 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mPQm")' v
3224 A v
3220 A v
3225 r (filelog !)
3221 r (filelog !)
3226 p (sidedata !)
3222 p (sidedata !)
3227 p (upgraded !)
3223 p (upgraded !)
3228
3224
3229 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mPQ,Tm")' v
3225 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mPQ,Tm")' v
3230 A v
3226 A v
3231 r (filelog !)
3227 r (filelog !)
3232 p (sidedata !)
3228 p (sidedata !)
3233 p (upgraded !)
3229 p (upgraded !)
3234
3230
3235 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mT,PQm")' v
3231 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mT,PQm")' v
3236 A v
3232 A v
3237 r (filelog !)
3233 r (filelog !)
3238 p (missing-correct-output sidedata !)
3234 p (sidedata !)
3239 p (missing-correct-output upgraded !)
3235 p (upgraded !)
3240 r (known-bad-output sidedata !)
3241 r (known-bad-output upgraded !)
3242
3236
3243
3237
3244 The result from mQPm is the same for the subsequent merge:
3238 The result from mQPm is the same for the subsequent merge:
3245
3239
3246 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mQPm")' v
3240 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mQPm")' v
3247 A v
3241 A v
3248 r (no-changeset no-compatibility !)
3242 r (no-changeset no-compatibility !)
3249
3243
3250 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mQP,Sm")' v
3244 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mQP,Sm")' v
3251 A v
3245 A v
3252 r (no-changeset no-compatibility !)
3246 r (no-changeset no-compatibility !)
3253
3247
3254 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mS,QPm")' v
3248 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mS,QPm")' v
3255 A v
3249 A v
3256 r (filelog !)
3250 r (filelog !)
3257 r (missing-correct-output sidedata !)
3251 r (sidedata !)
3258 r (missing-correct-output upgraded !)
3252 r (upgraded !)
3259 p (known-bad-output sidedata !)
3260 p (known-bad-output upgraded !)
3261
3253
3262
3254
3263 Subcase: chaining salvage information during a merge
3255 Subcase: chaining salvage information during a merge
3264 ````````````````````````````````````````````````````
3256 ````````````````````````````````````````````````````
3265
3257
3266 We add more change on the branch were the file was deleted. merging again
3258 We add more change on the branch were the file was deleted. merging again
3267 should preserve the fact eh file was salvaged.
3259 should preserve the fact eh file was salvaged.
3268
3260
3269 reference output:
3261 reference output:
3270
3262
3271 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mCB-revert-m-0")'
3263 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mCB-revert-m-0")'
3272 M b
3264 M b
3273 A d
3265 A d
3274 a (filelog !)
3266 a (no-changeset no-compatibility !)
3275 a (sidedata !)
3276 a (upgraded !)
3277 A t
3267 A t
3278 p
3268 p
3279 R a
3269 R a
3280 R p
3270 R p
3281 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mBC-revert-m-0")'
3271 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mBC-revert-m-0")'
3282 M b
3272 M b
3283 A d
3273 A d
3284 a (filelog !)
3274 a (no-changeset no-compatibility !)
3285 a (sidedata !)
3286 a (upgraded !)
3287 A t
3275 A t
3288 p
3276 p
3289 R a
3277 R a
3290 R p
3278 R p
3291
3279
3292 chained output
3280 chained output
3293
3294 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mBC+revert,Lm")'
3281 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mBC+revert,Lm")'
3295 M b
3282 M b
3296 A d
3283 A d
3297 a (filelog !)
3284 a (no-changeset no-compatibility !)
3298 a (missing-correct-output sidedata !)
3299 a (missing-correct-output upgraded !)
3300 A t
3285 A t
3301 p
3286 p
3302 A unrelated-l
3287 A unrelated-l
3303 R a
3288 R a
3304 R p
3289 R p
3305 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mCB+revert,Lm")'
3290 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mCB+revert,Lm")'
3306 M b
3291 M b
3307 A d
3292 A d
3308 a (filelog !)
3293 a (no-changeset no-compatibility !)
3309 a (missing-correct-output sidedata !)
3310 a (missing-correct-output upgraded !)
3311 A t
3294 A t
3312 p
3295 p
3313 A unrelated-l
3296 A unrelated-l
3314 R a
3297 R a
3315 R p
3298 R p
3316 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mL,BC+revertm")'
3299 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mL,BC+revertm")'
3317 M b
3300 M b
3318 A d
3301 A d
3319 a (filelog !)
3302 a (no-changeset no-compatibility !)
3320 a (missing-correct-output sidedata !)
3321 a (missing-correct-output upgraded !)
3322 A t
3303 A t
3323 p
3304 p
3324 A unrelated-l
3305 A unrelated-l
3325 R a
3306 R a
3326 R p
3307 R p
3327 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mL,CB+revertm")'
3308 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mL,CB+revertm")'
3328 M b
3309 M b
3329 A d
3310 A d
3330 a (filelog !)
3311 a (no-changeset no-compatibility !)
3331 a (missing-correct-output sidedata !)
3332 a (missing-correct-output upgraded !)
3333 A t
3312 A t
3334 p
3313 p
3335 A unrelated-l
3314 A unrelated-l
3336 R a
3315 R a
3337 R p
3316 R p
3338
3317
3339 Subcase: chaining "merged" information during a merge
3318 Subcase: chaining "merged" information during a merge
3340 ``````````````````````````````````````````````````````
3319 ``````````````````````````````````````````````````````
3341
3320
3342 When a non-rename change are merged with a copy overwrite, the merge pick the copy source from (p1) as the reference. We should preserve this information in subsequent merges.
3321 When a non-rename change are merged with a copy overwrite, the merge pick the copy source from (p1) as the reference. We should preserve this information in subsequent merges.
3343
3322
3344
3323
3345 reference output:
3324 reference output:
3346
3325
3347 (for details about the filelog pick, check the mFGm/mGFm case)
3326 (for details about the filelog pick, check the mFGm/mGFm case)
3348
3327
3349 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mFGm")' d
3328 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mFGm")' d
3350 A d
3329 A d
3351 a (filelog !)
3330 a (filelog !)
3352 h (sidedata !)
3331 h (sidedata !)
3353 h (upgraded !)
3332 h (upgraded !)
3354 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mGFm")' d
3333 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mGFm")' d
3355 A d
3334 A d
3356 a (filelog !)
3335 a (filelog !)
3357 a (sidedata !)
3336 a (sidedata !)
3358 a (upgraded !)
3337 a (upgraded !)
3359
3338
3360 Chained output
3339 Chained output
3361
3340
3362 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mO,FGm")' d
3341 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mO,FGm")' d
3363 A d
3342 A d
3364 a (filelog !)
3343 a (filelog !)
3365 h (sidedata !)
3344 h (sidedata !)
3366 h (upgraded !)
3345 h (upgraded !)
3367 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mFG,Om")' d
3346 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mFG,Om")' d
3368 A d
3347 A d
3369 a (filelog !)
3348 a (filelog !)
3370 h (sidedata !)
3349 h (sidedata !)
3371 h (upgraded !)
3350 h (upgraded !)
3372
3351
3373
3352
3374 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mGF,Nm")' d
3353 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mGF,Nm")' d
3375 A d
3354 A d
3376 a (filelog !)
3355 a (no-changeset no-compatibility !)
3377 a (missing-correct-output sidedata !)
3378 a (missing-correct-output upgraded !)
3379 h (known-bad-output sidedata !)
3380 h (known-bad-output upgraded !)
3381 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mN,GFm")' d
3356 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mN,GFm")' d
3382 A d
3357 A d
3383 a (filelog !)
3358 a (no-changeset no-compatibility !)
3384 a (missing-correct-output sidedata !)
3385 a (missing-correct-output upgraded !)
3386 h (known-bad-output sidedata !)
3387 h (known-bad-output upgraded !)
3388
3359
3389
3360
3390 Subcase: chaining conflicting rename resolution, with extra change during the merge
3361 Subcase: chaining conflicting rename resolution, with extra change during the merge
3391 ```````````````````````````````````````````````````````````````````````````````````
3362 ```````````````````````````````````````````````````````````````````````````````````
3392
3363
3393 The "mAEm" and "mEAm" case create a rename tracking conflict on file 'f'. We
3364 The "mAEm" and "mEAm" case create a rename tracking conflict on file 'f'. We
3394 add more change on the respective branch and merge again. These second merge
3365 add more change on the respective branch and merge again. These second merge
3395 does not involve the file 'f' and the arbitration done within "mAEm" and "mEA"
3366 does not involve the file 'f' and the arbitration done within "mAEm" and "mEA"
3396 about that file should stay unchanged.
3367 about that file should stay unchanged.
3397
3368
3398 The result from mAEm is the same for the subsequent merge:
3369 The result from mAEm is the same for the subsequent merge:
3399
3370
3400 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mAE-change-m")' f
3371 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mAE-change-m")' f
3401 A f
3372 A f
3402 a (filelog !)
3373 a (filelog !)
3403 a (sidedata !)
3374 a (sidedata !)
3404 a (upgraded !)
3375 a (upgraded !)
3405
3376
3406 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mAE-change,Km")' f
3377 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mAE-change,Km")' f
3407 A f
3378 A f
3408 a (filelog !)
3379 a (filelog !)
3409 a (sidedata !)
3380 a (sidedata !)
3410 a (upgraded !)
3381 a (upgraded !)
3411
3382
3412 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mK,AE-change-m")' f
3383 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mK,AE-change-m")' f
3413 A f
3384 A f
3414 a (filelog !)
3385 a (no-changeset no-compatibility !)
3415 a (missing-correct-output sidedata !)
3416 a (missing-correct-output upgraded !)
3417 b (known-bad-output sidedata !)
3418 b (known-bad-output upgraded !)
3419
3386
3420
3387
3421 The result from mEAm is the same for the subsequent merge:
3388 The result from mEAm is the same for the subsequent merge:
3422
3389
3423 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mEA-change-m")' f
3390 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mEA-change-m")' f
3424 A f
3391 A f
3425 a (filelog !)
3392 a (filelog !)
3426 b (sidedata !)
3393 b (sidedata !)
3427 b (upgraded !)
3394 b (upgraded !)
3428
3395
3429 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mEA-change,Jm")' f
3396 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mEA-change,Jm")' f
3430 A f
3397 A f
3431 a (filelog !)
3398 a (filelog !)
3432 b (sidedata !)
3399 b (sidedata !)
3433 b (upgraded !)
3400 b (upgraded !)
3434
3401
3435 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mJ,EA-change-m")' f
3402 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mJ,EA-change-m")' f
3436 A f
3403 A f
3437 a (filelog !)
3404 a (filelog !)
3438 b (missing-correct-output sidedata !)
3405 b (sidedata !)
3439 b (missing-correct-output upgraded !)
3406 b (upgraded !)
3440 a (known-bad-output sidedata !)
3441 a (known-bad-output upgraded !)
General Comments 0
You need to be logged in to leave comments. Login now