##// END OF EJS Templates
copies: properly match result during changeset centric copy tracing...
marmoute -
r46774:92905484 default
parent child Browse files
Show More
@@ -1,1221 +1,1217 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 2
44 # in the following table (not including trivial cases). For example, case 2
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 # remove copies from files that didn't exist
62 # remove copies from files that didn't exist
63 if v not in src:
63 if v not in src:
64 del t[k]
64 del t[k]
65 # remove criss-crossed copies
65 # remove criss-crossed copies
66 elif k in src and v in dst:
66 elif k in src and v in dst:
67 del t[k]
67 del t[k]
68 # remove copies to files that were then removed
68 # remove copies to files that were then removed
69 elif k not in dst:
69 elif k not in dst:
70 del t[k]
70 del t[k]
71
71
72
72
73 def _chain(prefix, suffix):
73 def _chain(prefix, suffix):
74 """chain two sets of copies 'prefix' and 'suffix'"""
74 """chain two sets of copies 'prefix' and 'suffix'"""
75 result = prefix.copy()
75 result = prefix.copy()
76 for key, value in pycompat.iteritems(suffix):
76 for key, value in pycompat.iteritems(suffix):
77 result[key] = prefix.get(value, value)
77 result[key] = prefix.get(value, value)
78 return result
78 return result
79
79
80
80
81 def _tracefile(fctx, am, basemf):
81 def _tracefile(fctx, am, basemf):
82 """return file context that is the ancestor of fctx present in ancestor
82 """return file context that is the ancestor of fctx present in ancestor
83 manifest am
83 manifest am
84
84
85 Note: we used to try and stop after a given limit, however checking if that
85 Note: we used to try and stop after a given limit, however checking if that
86 limit is reached turned out to be very expensive. we are better off
86 limit is reached turned out to be very expensive. we are better off
87 disabling that feature."""
87 disabling that feature."""
88
88
89 for f in fctx.ancestors():
89 for f in fctx.ancestors():
90 path = f.path()
90 path = f.path()
91 if am.get(path, None) == f.filenode():
91 if am.get(path, None) == f.filenode():
92 return path
92 return path
93 if basemf and basemf.get(path, None) == f.filenode():
93 if basemf and basemf.get(path, None) == f.filenode():
94 return path
94 return path
95
95
96
96
97 def _dirstatecopies(repo, match=None):
97 def _dirstatecopies(repo, match=None):
98 ds = repo.dirstate
98 ds = repo.dirstate
99 c = ds.copies().copy()
99 c = ds.copies().copy()
100 for k in list(c):
100 for k in list(c):
101 if ds[k] not in b'anm' or (match and not match(k)):
101 if ds[k] not in b'anm' or (match and not match(k)):
102 del c[k]
102 del c[k]
103 return c
103 return c
104
104
105
105
106 def _computeforwardmissing(a, b, match=None):
106 def _computeforwardmissing(a, b, match=None):
107 """Computes which files are in b but not a.
107 """Computes which files are in b but not a.
108 This is its own function so extensions can easily wrap this call to see what
108 This is its own function so extensions can easily wrap this call to see what
109 files _forwardcopies is about to process.
109 files _forwardcopies is about to process.
110 """
110 """
111 ma = a.manifest()
111 ma = a.manifest()
112 mb = b.manifest()
112 mb = b.manifest()
113 return mb.filesnotin(ma, match=match)
113 return mb.filesnotin(ma, match=match)
114
114
115
115
116 def usechangesetcentricalgo(repo):
116 def usechangesetcentricalgo(repo):
117 """Checks if we should use changeset-centric copy algorithms"""
117 """Checks if we should use changeset-centric copy algorithms"""
118 if repo.filecopiesmode == b'changeset-sidedata':
118 if repo.filecopiesmode == b'changeset-sidedata':
119 return True
119 return True
120 readfrom = repo.ui.config(b'experimental', b'copies.read-from')
120 readfrom = repo.ui.config(b'experimental', b'copies.read-from')
121 changesetsource = (b'changeset-only', b'compatibility')
121 changesetsource = (b'changeset-only', b'compatibility')
122 return readfrom in changesetsource
122 return readfrom in changesetsource
123
123
124
124
125 def _committedforwardcopies(a, b, base, match):
125 def _committedforwardcopies(a, b, base, match):
126 """Like _forwardcopies(), but b.rev() cannot be None (working copy)"""
126 """Like _forwardcopies(), but b.rev() cannot be None (working copy)"""
127 # files might have to be traced back to the fctx parent of the last
127 # files might have to be traced back to the fctx parent of the last
128 # one-side-only changeset, but not further back than that
128 # one-side-only changeset, but not further back than that
129 repo = a._repo
129 repo = a._repo
130
130
131 if usechangesetcentricalgo(repo):
131 if usechangesetcentricalgo(repo):
132 return _changesetforwardcopies(a, b, match)
132 return _changesetforwardcopies(a, b, match)
133
133
134 debug = repo.ui.debugflag and repo.ui.configbool(b'devel', b'debug.copies')
134 debug = repo.ui.debugflag and repo.ui.configbool(b'devel', b'debug.copies')
135 dbg = repo.ui.debug
135 dbg = repo.ui.debug
136 if debug:
136 if debug:
137 dbg(b'debug.copies: looking into rename from %s to %s\n' % (a, b))
137 dbg(b'debug.copies: looking into rename from %s to %s\n' % (a, b))
138 am = a.manifest()
138 am = a.manifest()
139 basemf = None if base is None else base.manifest()
139 basemf = None if base is None else base.manifest()
140
140
141 # find where new files came from
141 # find where new files came from
142 # we currently don't try to find where old files went, too expensive
142 # we currently don't try to find where old files went, too expensive
143 # this means we can miss a case like 'hg rm b; hg cp a b'
143 # this means we can miss a case like 'hg rm b; hg cp a b'
144 cm = {}
144 cm = {}
145
145
146 # Computing the forward missing is quite expensive on large manifests, since
146 # Computing the forward missing is quite expensive on large manifests, since
147 # it compares the entire manifests. We can optimize it in the common use
147 # it compares the entire manifests. We can optimize it in the common use
148 # case of computing what copies are in a commit versus its parent (like
148 # case of computing what copies are in a commit versus its parent (like
149 # during a rebase or histedit). Note, we exclude merge commits from this
149 # during a rebase or histedit). Note, we exclude merge commits from this
150 # optimization, since the ctx.files() for a merge commit is not correct for
150 # optimization, since the ctx.files() for a merge commit is not correct for
151 # this comparison.
151 # this comparison.
152 forwardmissingmatch = match
152 forwardmissingmatch = match
153 if b.p1() == a and b.p2().node() == nullid:
153 if b.p1() == a and b.p2().node() == nullid:
154 filesmatcher = matchmod.exact(b.files())
154 filesmatcher = matchmod.exact(b.files())
155 forwardmissingmatch = matchmod.intersectmatchers(match, filesmatcher)
155 forwardmissingmatch = matchmod.intersectmatchers(match, filesmatcher)
156 missing = _computeforwardmissing(a, b, match=forwardmissingmatch)
156 missing = _computeforwardmissing(a, b, match=forwardmissingmatch)
157
157
158 ancestrycontext = a._repo.changelog.ancestors([b.rev()], inclusive=True)
158 ancestrycontext = a._repo.changelog.ancestors([b.rev()], inclusive=True)
159
159
160 if debug:
160 if debug:
161 dbg(b'debug.copies: missing files to search: %d\n' % len(missing))
161 dbg(b'debug.copies: missing files to search: %d\n' % len(missing))
162
162
163 for f in sorted(missing):
163 for f in sorted(missing):
164 if debug:
164 if debug:
165 dbg(b'debug.copies: tracing file: %s\n' % f)
165 dbg(b'debug.copies: tracing file: %s\n' % f)
166 fctx = b[f]
166 fctx = b[f]
167 fctx._ancestrycontext = ancestrycontext
167 fctx._ancestrycontext = ancestrycontext
168
168
169 if debug:
169 if debug:
170 start = util.timer()
170 start = util.timer()
171 opath = _tracefile(fctx, am, basemf)
171 opath = _tracefile(fctx, am, basemf)
172 if opath:
172 if opath:
173 if debug:
173 if debug:
174 dbg(b'debug.copies: rename of: %s\n' % opath)
174 dbg(b'debug.copies: rename of: %s\n' % opath)
175 cm[f] = opath
175 cm[f] = opath
176 if debug:
176 if debug:
177 dbg(
177 dbg(
178 b'debug.copies: time: %f seconds\n'
178 b'debug.copies: time: %f seconds\n'
179 % (util.timer() - start)
179 % (util.timer() - start)
180 )
180 )
181 return cm
181 return cm
182
182
183
183
184 def _revinfo_getter(repo, match):
184 def _revinfo_getter(repo, match):
185 """returns a function that returns the following data given a <rev>"
185 """returns a function that returns the following data given a <rev>"
186
186
187 * p1: revision number of first parent
187 * p1: revision number of first parent
188 * p2: revision number of first parent
188 * p2: revision number of first parent
189 * changes: a ChangingFiles object
189 * changes: a ChangingFiles object
190 """
190 """
191 cl = repo.changelog
191 cl = repo.changelog
192 parents = cl.parentrevs
192 parents = cl.parentrevs
193 flags = cl.flags
193 flags = cl.flags
194
194
195 HASCOPIESINFO = flagutil.REVIDX_HASCOPIESINFO
195 HASCOPIESINFO = flagutil.REVIDX_HASCOPIESINFO
196
196
197 changelogrevision = cl.changelogrevision
197 changelogrevision = cl.changelogrevision
198
198
199 alwaysmatch = match.always()
199 if rustmod is not None:
200
201 if rustmod is not None and alwaysmatch:
202
200
203 def revinfo(rev):
201 def revinfo(rev):
204 p1, p2 = parents(rev)
202 p1, p2 = parents(rev)
205 if flags(rev) & HASCOPIESINFO:
203 if flags(rev) & HASCOPIESINFO:
206 raw = changelogrevision(rev)._sidedata.get(sidedatamod.SD_FILES)
204 raw = changelogrevision(rev)._sidedata.get(sidedatamod.SD_FILES)
207 else:
205 else:
208 raw = None
206 raw = None
209 return (p1, p2, raw)
207 return (p1, p2, raw)
210
208
211 else:
209 else:
212
210
213 def revinfo(rev):
211 def revinfo(rev):
214 p1, p2 = parents(rev)
212 p1, p2 = parents(rev)
215 if flags(rev) & HASCOPIESINFO:
213 if flags(rev) & HASCOPIESINFO:
216 changes = changelogrevision(rev).changes
214 changes = changelogrevision(rev).changes
217 else:
215 else:
218 changes = None
216 changes = None
219 return (p1, p2, changes)
217 return (p1, p2, changes)
220
218
221 return revinfo
219 return revinfo
222
220
223
221
224 def cached_is_ancestor(is_ancestor):
222 def cached_is_ancestor(is_ancestor):
225 """return a cached version of is_ancestor"""
223 """return a cached version of is_ancestor"""
226 cache = {}
224 cache = {}
227
225
228 def _is_ancestor(anc, desc):
226 def _is_ancestor(anc, desc):
229 if anc > desc:
227 if anc > desc:
230 return False
228 return False
231 elif anc == desc:
229 elif anc == desc:
232 return True
230 return True
233 key = (anc, desc)
231 key = (anc, desc)
234 ret = cache.get(key)
232 ret = cache.get(key)
235 if ret is None:
233 if ret is None:
236 ret = cache[key] = is_ancestor(anc, desc)
234 ret = cache[key] = is_ancestor(anc, desc)
237 return ret
235 return ret
238
236
239 return _is_ancestor
237 return _is_ancestor
240
238
241
239
242 def _changesetforwardcopies(a, b, match):
240 def _changesetforwardcopies(a, b, match):
243 if a.rev() in (nullrev, b.rev()):
241 if a.rev() in (nullrev, b.rev()):
244 return {}
242 return {}
245
243
246 repo = a.repo().unfiltered()
244 repo = a.repo().unfiltered()
247 children = {}
245 children = {}
248
246
249 cl = repo.changelog
247 cl = repo.changelog
250 isancestor = cl.isancestorrev
248 isancestor = cl.isancestorrev
251
249
252 # To track rename from "A" to B, we need to gather all parent β†’ children
250 # To track rename from "A" to B, we need to gather all parent β†’ children
253 # edges that are contains in `::B` but not in `::A`.
251 # edges that are contains in `::B` but not in `::A`.
254 #
252 #
255 #
253 #
256 # To do so, we need to gather all revisions exclusiveΒΉ to "B" (ieΒΉ: `::b -
254 # To do so, we need to gather all revisions exclusiveΒΉ to "B" (ieΒΉ: `::b -
257 # ::a`) and also all the "roots point", ie the parents of the exclusive set
255 # ::a`) and also all the "roots point", ie the parents of the exclusive set
258 # that belong to ::a. These are exactly all the revisions needed to express
256 # that belong to ::a. These are exactly all the revisions needed to express
259 # the parent β†’ children we need to combine.
257 # the parent β†’ children we need to combine.
260 #
258 #
261 # [1] actually, we need to gather all the edges within `(::a)::b`, ie:
259 # [1] actually, we need to gather all the edges within `(::a)::b`, ie:
262 # excluding paths that leads to roots that are not ancestors of `a`. We
260 # excluding paths that leads to roots that are not ancestors of `a`. We
263 # keep this out of the explanation because it is hard enough without this special case..
261 # keep this out of the explanation because it is hard enough without this special case..
264
262
265 parents = cl._uncheckedparentrevs
263 parents = cl._uncheckedparentrevs
266 graph_roots = (nullrev, nullrev)
264 graph_roots = (nullrev, nullrev)
267
265
268 ancestors = cl.ancestors([a.rev()], inclusive=True)
266 ancestors = cl.ancestors([a.rev()], inclusive=True)
269 revs = cl.findmissingrevs(common=[a.rev()], heads=[b.rev()])
267 revs = cl.findmissingrevs(common=[a.rev()], heads=[b.rev()])
270 roots = set()
268 roots = set()
271 has_graph_roots = False
269 has_graph_roots = False
272
270
273 # iterate over `only(B, A)`
271 # iterate over `only(B, A)`
274 for r in revs:
272 for r in revs:
275 ps = parents(r)
273 ps = parents(r)
276 if ps == graph_roots:
274 if ps == graph_roots:
277 has_graph_roots = True
275 has_graph_roots = True
278 else:
276 else:
279 p1, p2 = ps
277 p1, p2 = ps
280
278
281 # find all the "root points" (see larger comment above)
279 # find all the "root points" (see larger comment above)
282 if p1 != nullrev and p1 in ancestors:
280 if p1 != nullrev and p1 in ancestors:
283 roots.add(p1)
281 roots.add(p1)
284 if p2 != nullrev and p2 in ancestors:
282 if p2 != nullrev and p2 in ancestors:
285 roots.add(p2)
283 roots.add(p2)
286 if not roots:
284 if not roots:
287 # no common revision to track copies from
285 # no common revision to track copies from
288 return {}
286 return {}
289 if has_graph_roots:
287 if has_graph_roots:
290 # this deal with the special case mentionned in the [1] footnotes. We
288 # this deal with the special case mentionned in the [1] footnotes. We
291 # must filter out revisions that leads to non-common graphroots.
289 # must filter out revisions that leads to non-common graphroots.
292 roots = list(roots)
290 roots = list(roots)
293 m = min(roots)
291 m = min(roots)
294 h = [b.rev()]
292 h = [b.rev()]
295 roots_to_head = cl.reachableroots(m, h, roots, includepath=True)
293 roots_to_head = cl.reachableroots(m, h, roots, includepath=True)
296 roots_to_head = set(roots_to_head)
294 roots_to_head = set(roots_to_head)
297 revs = [r for r in revs if r in roots_to_head]
295 revs = [r for r in revs if r in roots_to_head]
298
296
299 if repo.filecopiesmode == b'changeset-sidedata':
297 if repo.filecopiesmode == b'changeset-sidedata':
300 # When using side-data, we will process the edges "from" the children.
298 # When using side-data, we will process the edges "from" the children.
301 # We iterate over the childre, gathering previous collected data for
299 # We iterate over the childre, gathering previous collected data for
302 # the parents. Do know when the parents data is no longer necessary, we
300 # the parents. Do know when the parents data is no longer necessary, we
303 # keep a counter of how many children each revision has.
301 # keep a counter of how many children each revision has.
304 #
302 #
305 # An interresting property of `children_count` is that it only contains
303 # An interresting property of `children_count` is that it only contains
306 # revision that will be relevant for a edge of the graph. So if a
304 # revision that will be relevant for a edge of the graph. So if a
307 # children has parent not in `children_count`, that edges should not be
305 # children has parent not in `children_count`, that edges should not be
308 # processed.
306 # processed.
309 children_count = dict((r, 0) for r in roots)
307 children_count = dict((r, 0) for r in roots)
310 for r in revs:
308 for r in revs:
311 for p in cl.parentrevs(r):
309 for p in cl.parentrevs(r):
312 if p == nullrev:
310 if p == nullrev:
313 continue
311 continue
314 children_count[r] = 0
312 children_count[r] = 0
315 if p in children_count:
313 if p in children_count:
316 children_count[p] += 1
314 children_count[p] += 1
317 revinfo = _revinfo_getter(repo, match)
315 revinfo = _revinfo_getter(repo, match)
318 return _combine_changeset_copies(
316 return _combine_changeset_copies(
319 revs, children_count, b.rev(), revinfo, match, isancestor
317 revs, children_count, b.rev(), revinfo, match, isancestor
320 )
318 )
321 else:
319 else:
322 # When not using side-data, we will process the edges "from" the parent.
320 # When not using side-data, we will process the edges "from" the parent.
323 # so we need a full mapping of the parent -> children relation.
321 # so we need a full mapping of the parent -> children relation.
324 children = dict((r, []) for r in roots)
322 children = dict((r, []) for r in roots)
325 for r in revs:
323 for r in revs:
326 for p in cl.parentrevs(r):
324 for p in cl.parentrevs(r):
327 if p == nullrev:
325 if p == nullrev:
328 continue
326 continue
329 children[r] = []
327 children[r] = []
330 if p in children:
328 if p in children:
331 children[p].append(r)
329 children[p].append(r)
332 x = revs.pop()
330 x = revs.pop()
333 assert x == b.rev()
331 assert x == b.rev()
334 revs.extend(roots)
332 revs.extend(roots)
335 revs.sort()
333 revs.sort()
336
334
337 revinfo = _revinfo_getter_extra(repo)
335 revinfo = _revinfo_getter_extra(repo)
338 return _combine_changeset_copies_extra(
336 return _combine_changeset_copies_extra(
339 revs, children, b.rev(), revinfo, match, isancestor
337 revs, children, b.rev(), revinfo, match, isancestor
340 )
338 )
341
339
342
340
343 def _combine_changeset_copies(
341 def _combine_changeset_copies(
344 revs, children_count, targetrev, revinfo, match, isancestor
342 revs, children_count, targetrev, revinfo, match, isancestor
345 ):
343 ):
346 """combine the copies information for each item of iterrevs
344 """combine the copies information for each item of iterrevs
347
345
348 revs: sorted iterable of revision to visit
346 revs: sorted iterable of revision to visit
349 children_count: a {parent: <number-of-relevant-children>} mapping.
347 children_count: a {parent: <number-of-relevant-children>} mapping.
350 targetrev: the final copies destination revision (not in iterrevs)
348 targetrev: the final copies destination revision (not in iterrevs)
351 revinfo(rev): a function that return (p1, p2, p1copies, p2copies, removed)
349 revinfo(rev): a function that return (p1, p2, p1copies, p2copies, removed)
352 match: a matcher
350 match: a matcher
353
351
354 It returns the aggregated copies information for `targetrev`.
352 It returns the aggregated copies information for `targetrev`.
355 """
353 """
356
354
357 alwaysmatch = match.always()
355 alwaysmatch = match.always()
358
356
359 if rustmod is not None and alwaysmatch:
357 if rustmod is not None:
360 final_copies = rustmod.combine_changeset_copies(
358 final_copies = rustmod.combine_changeset_copies(
361 list(revs), children_count, targetrev, revinfo, isancestor
359 list(revs), children_count, targetrev, revinfo, isancestor
362 )
360 )
363 else:
361 else:
364 isancestor = cached_is_ancestor(isancestor)
362 isancestor = cached_is_ancestor(isancestor)
365
363
366 all_copies = {}
364 all_copies = {}
367 # iterate over all the "children" side of copy tracing "edge"
365 # iterate over all the "children" side of copy tracing "edge"
368 for current_rev in revs:
366 for current_rev in revs:
369 p1, p2, changes = revinfo(current_rev)
367 p1, p2, changes = revinfo(current_rev)
370 current_copies = None
368 current_copies = None
371 # iterate over all parents to chain the existing data with the
369 # iterate over all parents to chain the existing data with the
372 # data from the parent β†’ child edge.
370 # data from the parent β†’ child edge.
373 for parent, parent_rev in ((1, p1), (2, p2)):
371 for parent, parent_rev in ((1, p1), (2, p2)):
374 if parent_rev == nullrev:
372 if parent_rev == nullrev:
375 continue
373 continue
376 remaining_children = children_count.get(parent_rev)
374 remaining_children = children_count.get(parent_rev)
377 if remaining_children is None:
375 if remaining_children is None:
378 continue
376 continue
379 remaining_children -= 1
377 remaining_children -= 1
380 children_count[parent_rev] = remaining_children
378 children_count[parent_rev] = remaining_children
381 if remaining_children:
379 if remaining_children:
382 copies = all_copies.get(parent_rev, None)
380 copies = all_copies.get(parent_rev, None)
383 else:
381 else:
384 copies = all_copies.pop(parent_rev, None)
382 copies = all_copies.pop(parent_rev, None)
385
383
386 if copies is None:
384 if copies is None:
387 # this is a root
385 # this is a root
388 copies = {}
386 copies = {}
389
387
390 newcopies = copies
388 newcopies = copies
391 # chain the data in the edge with the existing data
389 # chain the data in the edge with the existing data
392 if changes is not None:
390 if changes is not None:
393 childcopies = {}
391 childcopies = {}
394 if parent == 1:
392 if parent == 1:
395 childcopies = changes.copied_from_p1
393 childcopies = changes.copied_from_p1
396 elif parent == 2:
394 elif parent == 2:
397 childcopies = changes.copied_from_p2
395 childcopies = changes.copied_from_p2
398
396
399 if not alwaysmatch:
400 childcopies = {
401 dst: src
402 for dst, src in childcopies.items()
403 if match(dst)
404 }
405 if childcopies:
397 if childcopies:
406 newcopies = copies.copy()
398 newcopies = copies.copy()
407 for dest, source in pycompat.iteritems(childcopies):
399 for dest, source in pycompat.iteritems(childcopies):
408 prev = copies.get(source)
400 prev = copies.get(source)
409 if prev is not None and prev[1] is not None:
401 if prev is not None and prev[1] is not None:
410 source = prev[1]
402 source = prev[1]
411 newcopies[dest] = (current_rev, source)
403 newcopies[dest] = (current_rev, source)
412 assert newcopies is not copies
404 assert newcopies is not copies
413 if changes.removed:
405 if changes.removed:
414 if newcopies is copies:
406 if newcopies is copies:
415 newcopies = copies.copy()
407 newcopies = copies.copy()
416 for f in changes.removed:
408 for f in changes.removed:
417 if f in newcopies:
409 if f in newcopies:
418 if newcopies is copies:
410 if newcopies is copies:
419 # copy on write to avoid affecting potential other
411 # copy on write to avoid affecting potential other
420 # branches. when there are no other branches, this
412 # branches. when there are no other branches, this
421 # could be avoided.
413 # could be avoided.
422 newcopies = copies.copy()
414 newcopies = copies.copy()
423 newcopies[f] = (current_rev, None)
415 newcopies[f] = (current_rev, None)
424 # check potential need to combine the data from another parent (for
416 # check potential need to combine the data from another parent (for
425 # that child). See comment below for details.
417 # that child). See comment below for details.
426 if current_copies is None:
418 if current_copies is None:
427 current_copies = newcopies
419 current_copies = newcopies
428 elif current_copies is newcopies:
420 elif current_copies is newcopies:
429 # nothing to merge:
421 # nothing to merge:
430 pass
422 pass
431 else:
423 else:
432 # we are the second parent to work on c, we need to merge our
424 # we are the second parent to work on c, we need to merge our
433 # work with the other.
425 # work with the other.
434 #
426 #
435 # In case of conflict, parent 1 take precedence over parent 2.
427 # In case of conflict, parent 1 take precedence over parent 2.
436 # This is an arbitrary choice made anew when implementing
428 # This is an arbitrary choice made anew when implementing
437 # changeset based copies. It was made without regards with
429 # changeset based copies. It was made without regards with
438 # potential filelog related behavior.
430 # potential filelog related behavior.
439 assert parent == 2
431 assert parent == 2
440 current_copies = _merge_copies_dict(
432 current_copies = _merge_copies_dict(
441 newcopies, current_copies, isancestor, changes
433 newcopies, current_copies, isancestor, changes
442 )
434 )
443 all_copies[current_rev] = current_copies
435 all_copies[current_rev] = current_copies
444
436
445 # filter out internal details and return a {dest: source mapping}
437 # filter out internal details and return a {dest: source mapping}
446 final_copies = {}
438 final_copies = {}
447 for dest, (tt, source) in all_copies[targetrev].items():
439 for dest, (tt, source) in all_copies[targetrev].items():
448 if source is not None:
440 if source is not None:
449 final_copies[dest] = source
441 final_copies[dest] = source
442 if not alwaysmatch:
443 for filename in list(final_copies.keys()):
444 if not match(filename):
445 del final_copies[filename]
450 return final_copies
446 return final_copies
451
447
452
448
453 def _merge_copies_dict(minor, major, isancestor, changes):
449 def _merge_copies_dict(minor, major, isancestor, changes):
454 """merge two copies-mapping together, minor and major
450 """merge two copies-mapping together, minor and major
455
451
456 In case of conflict, value from "major" will be picked.
452 In case of conflict, value from "major" will be picked.
457
453
458 - `isancestors(low_rev, high_rev)`: callable return True if `low_rev` is an
454 - `isancestors(low_rev, high_rev)`: callable return True if `low_rev` is an
459 ancestors of `high_rev`,
455 ancestors of `high_rev`,
460
456
461 - `ismerged(path)`: callable return True if `path` have been merged in the
457 - `ismerged(path)`: callable return True if `path` have been merged in the
462 current revision,
458 current revision,
463
459
464 return the resulting dict (in practice, the "minor" object, updated)
460 return the resulting dict (in practice, the "minor" object, updated)
465 """
461 """
466 for dest, value in major.items():
462 for dest, value in major.items():
467 other = minor.get(dest)
463 other = minor.get(dest)
468 if other is None:
464 if other is None:
469 minor[dest] = value
465 minor[dest] = value
470 else:
466 else:
471 new_tt = value[0]
467 new_tt = value[0]
472 other_tt = other[0]
468 other_tt = other[0]
473 if value[1] == other[1]:
469 if value[1] == other[1]:
474 continue
470 continue
475 # content from "major" wins, unless it is older
471 # content from "major" wins, unless it is older
476 # than the branch point or there is a merge
472 # than the branch point or there is a merge
477 if new_tt == other_tt:
473 if new_tt == other_tt:
478 minor[dest] = value
474 minor[dest] = value
479 elif (
475 elif (
480 changes is not None
476 changes is not None
481 and value[1] is None
477 and value[1] is None
482 and dest in changes.salvaged
478 and dest in changes.salvaged
483 ):
479 ):
484 pass
480 pass
485 elif (
481 elif (
486 changes is not None
482 changes is not None
487 and other[1] is None
483 and other[1] is None
488 and dest in changes.salvaged
484 and dest in changes.salvaged
489 ):
485 ):
490 minor[dest] = value
486 minor[dest] = value
491 elif changes is not None and dest in changes.merged:
487 elif changes is not None and dest in changes.merged:
492 minor[dest] = value
488 minor[dest] = value
493 elif not isancestor(new_tt, other_tt):
489 elif not isancestor(new_tt, other_tt):
494 if value[1] is not None:
490 if value[1] is not None:
495 minor[dest] = value
491 minor[dest] = value
496 elif isancestor(other_tt, new_tt):
492 elif isancestor(other_tt, new_tt):
497 minor[dest] = value
493 minor[dest] = value
498 return minor
494 return minor
499
495
500
496
501 def _revinfo_getter_extra(repo):
497 def _revinfo_getter_extra(repo):
502 """return a function that return multiple data given a <rev>"i
498 """return a function that return multiple data given a <rev>"i
503
499
504 * p1: revision number of first parent
500 * p1: revision number of first parent
505 * p2: revision number of first parent
501 * p2: revision number of first parent
506 * p1copies: mapping of copies from p1
502 * p1copies: mapping of copies from p1
507 * p2copies: mapping of copies from p2
503 * p2copies: mapping of copies from p2
508 * removed: a list of removed files
504 * removed: a list of removed files
509 * ismerged: a callback to know if file was merged in that revision
505 * ismerged: a callback to know if file was merged in that revision
510 """
506 """
511 cl = repo.changelog
507 cl = repo.changelog
512 parents = cl.parentrevs
508 parents = cl.parentrevs
513
509
514 def get_ismerged(rev):
510 def get_ismerged(rev):
515 ctx = repo[rev]
511 ctx = repo[rev]
516
512
517 def ismerged(path):
513 def ismerged(path):
518 if path not in ctx.files():
514 if path not in ctx.files():
519 return False
515 return False
520 fctx = ctx[path]
516 fctx = ctx[path]
521 parents = fctx._filelog.parents(fctx._filenode)
517 parents = fctx._filelog.parents(fctx._filenode)
522 nb_parents = 0
518 nb_parents = 0
523 for n in parents:
519 for n in parents:
524 if n != nullid:
520 if n != nullid:
525 nb_parents += 1
521 nb_parents += 1
526 return nb_parents >= 2
522 return nb_parents >= 2
527
523
528 return ismerged
524 return ismerged
529
525
530 def revinfo(rev):
526 def revinfo(rev):
531 p1, p2 = parents(rev)
527 p1, p2 = parents(rev)
532 ctx = repo[rev]
528 ctx = repo[rev]
533 p1copies, p2copies = ctx._copies
529 p1copies, p2copies = ctx._copies
534 removed = ctx.filesremoved()
530 removed = ctx.filesremoved()
535 return p1, p2, p1copies, p2copies, removed, get_ismerged(rev)
531 return p1, p2, p1copies, p2copies, removed, get_ismerged(rev)
536
532
537 return revinfo
533 return revinfo
538
534
539
535
540 def _combine_changeset_copies_extra(
536 def _combine_changeset_copies_extra(
541 revs, children, targetrev, revinfo, match, isancestor
537 revs, children, targetrev, revinfo, match, isancestor
542 ):
538 ):
543 """version of `_combine_changeset_copies` that works with the Google
539 """version of `_combine_changeset_copies` that works with the Google
544 specific "extra" based storage for copy information"""
540 specific "extra" based storage for copy information"""
545 all_copies = {}
541 all_copies = {}
546 alwaysmatch = match.always()
542 alwaysmatch = match.always()
547 for r in revs:
543 for r in revs:
548 copies = all_copies.pop(r, None)
544 copies = all_copies.pop(r, None)
549 if copies is None:
545 if copies is None:
550 # this is a root
546 # this is a root
551 copies = {}
547 copies = {}
552 for i, c in enumerate(children[r]):
548 for i, c in enumerate(children[r]):
553 p1, p2, p1copies, p2copies, removed, ismerged = revinfo(c)
549 p1, p2, p1copies, p2copies, removed, ismerged = revinfo(c)
554 if r == p1:
550 if r == p1:
555 parent = 1
551 parent = 1
556 childcopies = p1copies
552 childcopies = p1copies
557 else:
553 else:
558 assert r == p2
554 assert r == p2
559 parent = 2
555 parent = 2
560 childcopies = p2copies
556 childcopies = p2copies
561 if not alwaysmatch:
557 if not alwaysmatch:
562 childcopies = {
558 childcopies = {
563 dst: src for dst, src in childcopies.items() if match(dst)
559 dst: src for dst, src in childcopies.items() if match(dst)
564 }
560 }
565 newcopies = copies
561 newcopies = copies
566 if childcopies:
562 if childcopies:
567 newcopies = copies.copy()
563 newcopies = copies.copy()
568 for dest, source in pycompat.iteritems(childcopies):
564 for dest, source in pycompat.iteritems(childcopies):
569 prev = copies.get(source)
565 prev = copies.get(source)
570 if prev is not None and prev[1] is not None:
566 if prev is not None and prev[1] is not None:
571 source = prev[1]
567 source = prev[1]
572 newcopies[dest] = (c, source)
568 newcopies[dest] = (c, source)
573 assert newcopies is not copies
569 assert newcopies is not copies
574 for f in removed:
570 for f in removed:
575 if f in newcopies:
571 if f in newcopies:
576 if newcopies is copies:
572 if newcopies is copies:
577 # copy on write to avoid affecting potential other
573 # copy on write to avoid affecting potential other
578 # branches. when there are no other branches, this
574 # branches. when there are no other branches, this
579 # could be avoided.
575 # could be avoided.
580 newcopies = copies.copy()
576 newcopies = copies.copy()
581 newcopies[f] = (c, None)
577 newcopies[f] = (c, None)
582 othercopies = all_copies.get(c)
578 othercopies = all_copies.get(c)
583 if othercopies is None:
579 if othercopies is None:
584 all_copies[c] = newcopies
580 all_copies[c] = newcopies
585 else:
581 else:
586 # we are the second parent to work on c, we need to merge our
582 # we are the second parent to work on c, we need to merge our
587 # work with the other.
583 # work with the other.
588 #
584 #
589 # In case of conflict, parent 1 take precedence over parent 2.
585 # In case of conflict, parent 1 take precedence over parent 2.
590 # This is an arbitrary choice made anew when implementing
586 # This is an arbitrary choice made anew when implementing
591 # changeset based copies. It was made without regards with
587 # changeset based copies. It was made without regards with
592 # potential filelog related behavior.
588 # potential filelog related behavior.
593 if parent == 1:
589 if parent == 1:
594 _merge_copies_dict_extra(
590 _merge_copies_dict_extra(
595 othercopies, newcopies, isancestor, ismerged
591 othercopies, newcopies, isancestor, ismerged
596 )
592 )
597 else:
593 else:
598 _merge_copies_dict_extra(
594 _merge_copies_dict_extra(
599 newcopies, othercopies, isancestor, ismerged
595 newcopies, othercopies, isancestor, ismerged
600 )
596 )
601 all_copies[c] = newcopies
597 all_copies[c] = newcopies
602
598
603 final_copies = {}
599 final_copies = {}
604 for dest, (tt, source) in all_copies[targetrev].items():
600 for dest, (tt, source) in all_copies[targetrev].items():
605 if source is not None:
601 if source is not None:
606 final_copies[dest] = source
602 final_copies[dest] = source
607 return final_copies
603 return final_copies
608
604
609
605
610 def _merge_copies_dict_extra(minor, major, isancestor, ismerged):
606 def _merge_copies_dict_extra(minor, major, isancestor, ismerged):
611 """version of `_merge_copies_dict` that works with the Google
607 """version of `_merge_copies_dict` that works with the Google
612 specific "extra" based storage for copy information"""
608 specific "extra" based storage for copy information"""
613 for dest, value in major.items():
609 for dest, value in major.items():
614 other = minor.get(dest)
610 other = minor.get(dest)
615 if other is None:
611 if other is None:
616 minor[dest] = value
612 minor[dest] = value
617 else:
613 else:
618 new_tt = value[0]
614 new_tt = value[0]
619 other_tt = other[0]
615 other_tt = other[0]
620 if value[1] == other[1]:
616 if value[1] == other[1]:
621 continue
617 continue
622 # content from "major" wins, unless it is older
618 # content from "major" wins, unless it is older
623 # than the branch point or there is a merge
619 # than the branch point or there is a merge
624 if (
620 if (
625 new_tt == other_tt
621 new_tt == other_tt
626 or not isancestor(new_tt, other_tt)
622 or not isancestor(new_tt, other_tt)
627 or ismerged(dest)
623 or ismerged(dest)
628 ):
624 ):
629 minor[dest] = value
625 minor[dest] = value
630
626
631
627
632 def _forwardcopies(a, b, base=None, match=None):
628 def _forwardcopies(a, b, base=None, match=None):
633 """find {dst@b: src@a} copy mapping where a is an ancestor of b"""
629 """find {dst@b: src@a} copy mapping where a is an ancestor of b"""
634
630
635 if base is None:
631 if base is None:
636 base = a
632 base = a
637 match = a.repo().narrowmatch(match)
633 match = a.repo().narrowmatch(match)
638 # check for working copy
634 # check for working copy
639 if b.rev() is None:
635 if b.rev() is None:
640 cm = _committedforwardcopies(a, b.p1(), base, match)
636 cm = _committedforwardcopies(a, b.p1(), base, match)
641 # combine copies from dirstate if necessary
637 # combine copies from dirstate if necessary
642 copies = _chain(cm, _dirstatecopies(b._repo, match))
638 copies = _chain(cm, _dirstatecopies(b._repo, match))
643 else:
639 else:
644 copies = _committedforwardcopies(a, b, base, match)
640 copies = _committedforwardcopies(a, b, base, match)
645 return copies
641 return copies
646
642
647
643
648 def _backwardrenames(a, b, match):
644 def _backwardrenames(a, b, match):
649 if a._repo.ui.config(b'experimental', b'copytrace') == b'off':
645 if a._repo.ui.config(b'experimental', b'copytrace') == b'off':
650 return {}
646 return {}
651
647
652 # Even though we're not taking copies into account, 1:n rename situations
648 # Even though we're not taking copies into account, 1:n rename situations
653 # can still exist (e.g. hg cp a b; hg mv a c). In those cases we
649 # can still exist (e.g. hg cp a b; hg mv a c). In those cases we
654 # arbitrarily pick one of the renames.
650 # arbitrarily pick one of the renames.
655 # We don't want to pass in "match" here, since that would filter
651 # We don't want to pass in "match" here, since that would filter
656 # the destination by it. Since we're reversing the copies, we want
652 # the destination by it. Since we're reversing the copies, we want
657 # to filter the source instead.
653 # to filter the source instead.
658 f = _forwardcopies(b, a)
654 f = _forwardcopies(b, a)
659 r = {}
655 r = {}
660 for k, v in sorted(pycompat.iteritems(f)):
656 for k, v in sorted(pycompat.iteritems(f)):
661 if match and not match(v):
657 if match and not match(v):
662 continue
658 continue
663 # remove copies
659 # remove copies
664 if v in a:
660 if v in a:
665 continue
661 continue
666 r[v] = k
662 r[v] = k
667 return r
663 return r
668
664
669
665
670 def pathcopies(x, y, match=None):
666 def pathcopies(x, y, match=None):
671 """find {dst@y: src@x} copy mapping for directed compare"""
667 """find {dst@y: src@x} copy mapping for directed compare"""
672 repo = x._repo
668 repo = x._repo
673 debug = repo.ui.debugflag and repo.ui.configbool(b'devel', b'debug.copies')
669 debug = repo.ui.debugflag and repo.ui.configbool(b'devel', b'debug.copies')
674 if debug:
670 if debug:
675 repo.ui.debug(
671 repo.ui.debug(
676 b'debug.copies: searching copies from %s to %s\n' % (x, y)
672 b'debug.copies: searching copies from %s to %s\n' % (x, y)
677 )
673 )
678 if x == y or not x or not y:
674 if x == y or not x or not y:
679 return {}
675 return {}
680 if y.rev() is None and x == y.p1():
676 if y.rev() is None and x == y.p1():
681 if debug:
677 if debug:
682 repo.ui.debug(b'debug.copies: search mode: dirstate\n')
678 repo.ui.debug(b'debug.copies: search mode: dirstate\n')
683 # short-circuit to avoid issues with merge states
679 # short-circuit to avoid issues with merge states
684 return _dirstatecopies(repo, match)
680 return _dirstatecopies(repo, match)
685 a = y.ancestor(x)
681 a = y.ancestor(x)
686 if a == x:
682 if a == x:
687 if debug:
683 if debug:
688 repo.ui.debug(b'debug.copies: search mode: forward\n')
684 repo.ui.debug(b'debug.copies: search mode: forward\n')
689 copies = _forwardcopies(x, y, match=match)
685 copies = _forwardcopies(x, y, match=match)
690 elif a == y:
686 elif a == y:
691 if debug:
687 if debug:
692 repo.ui.debug(b'debug.copies: search mode: backward\n')
688 repo.ui.debug(b'debug.copies: search mode: backward\n')
693 copies = _backwardrenames(x, y, match=match)
689 copies = _backwardrenames(x, y, match=match)
694 else:
690 else:
695 if debug:
691 if debug:
696 repo.ui.debug(b'debug.copies: search mode: combined\n')
692 repo.ui.debug(b'debug.copies: search mode: combined\n')
697 base = None
693 base = None
698 if a.rev() != nullrev:
694 if a.rev() != nullrev:
699 base = x
695 base = x
700 copies = _chain(
696 copies = _chain(
701 _backwardrenames(x, a, match=match),
697 _backwardrenames(x, a, match=match),
702 _forwardcopies(a, y, base, match=match),
698 _forwardcopies(a, y, base, match=match),
703 )
699 )
704 _filter(x, y, copies)
700 _filter(x, y, copies)
705 return copies
701 return copies
706
702
707
703
708 def mergecopies(repo, c1, c2, base):
704 def mergecopies(repo, c1, c2, base):
709 """
705 """
710 Finds moves and copies between context c1 and c2 that are relevant for
706 Finds moves and copies between context c1 and c2 that are relevant for
711 merging. 'base' will be used as the merge base.
707 merging. 'base' will be used as the merge base.
712
708
713 Copytracing is used in commands like rebase, merge, unshelve, etc to merge
709 Copytracing is used in commands like rebase, merge, unshelve, etc to merge
714 files that were moved/ copied in one merge parent and modified in another.
710 files that were moved/ copied in one merge parent and modified in another.
715 For example:
711 For example:
716
712
717 o ---> 4 another commit
713 o ---> 4 another commit
718 |
714 |
719 | o ---> 3 commit that modifies a.txt
715 | o ---> 3 commit that modifies a.txt
720 | /
716 | /
721 o / ---> 2 commit that moves a.txt to b.txt
717 o / ---> 2 commit that moves a.txt to b.txt
722 |/
718 |/
723 o ---> 1 merge base
719 o ---> 1 merge base
724
720
725 If we try to rebase revision 3 on revision 4, since there is no a.txt in
721 If we try to rebase revision 3 on revision 4, since there is no a.txt in
726 revision 4, and if user have copytrace disabled, we prints the following
722 revision 4, and if user have copytrace disabled, we prints the following
727 message:
723 message:
728
724
729 ```other changed <file> which local deleted```
725 ```other changed <file> which local deleted```
730
726
731 Returns a tuple where:
727 Returns a tuple where:
732
728
733 "branch_copies" an instance of branch_copies.
729 "branch_copies" an instance of branch_copies.
734
730
735 "diverge" is a mapping of source name -> list of destination names
731 "diverge" is a mapping of source name -> list of destination names
736 for divergent renames.
732 for divergent renames.
737
733
738 This function calls different copytracing algorithms based on config.
734 This function calls different copytracing algorithms based on config.
739 """
735 """
740 # avoid silly behavior for update from empty dir
736 # avoid silly behavior for update from empty dir
741 if not c1 or not c2 or c1 == c2:
737 if not c1 or not c2 or c1 == c2:
742 return branch_copies(), branch_copies(), {}
738 return branch_copies(), branch_copies(), {}
743
739
744 narrowmatch = c1.repo().narrowmatch()
740 narrowmatch = c1.repo().narrowmatch()
745
741
746 # avoid silly behavior for parent -> working dir
742 # avoid silly behavior for parent -> working dir
747 if c2.node() is None and c1.node() == repo.dirstate.p1():
743 if c2.node() is None and c1.node() == repo.dirstate.p1():
748 return (
744 return (
749 branch_copies(_dirstatecopies(repo, narrowmatch)),
745 branch_copies(_dirstatecopies(repo, narrowmatch)),
750 branch_copies(),
746 branch_copies(),
751 {},
747 {},
752 )
748 )
753
749
754 copytracing = repo.ui.config(b'experimental', b'copytrace')
750 copytracing = repo.ui.config(b'experimental', b'copytrace')
755 if stringutil.parsebool(copytracing) is False:
751 if stringutil.parsebool(copytracing) is False:
756 # stringutil.parsebool() returns None when it is unable to parse the
752 # stringutil.parsebool() returns None when it is unable to parse the
757 # value, so we should rely on making sure copytracing is on such cases
753 # value, so we should rely on making sure copytracing is on such cases
758 return branch_copies(), branch_copies(), {}
754 return branch_copies(), branch_copies(), {}
759
755
760 if usechangesetcentricalgo(repo):
756 if usechangesetcentricalgo(repo):
761 # The heuristics don't make sense when we need changeset-centric algos
757 # The heuristics don't make sense when we need changeset-centric algos
762 return _fullcopytracing(repo, c1, c2, base)
758 return _fullcopytracing(repo, c1, c2, base)
763
759
764 # Copy trace disabling is explicitly below the node == p1 logic above
760 # Copy trace disabling is explicitly below the node == p1 logic above
765 # because the logic above is required for a simple copy to be kept across a
761 # because the logic above is required for a simple copy to be kept across a
766 # rebase.
762 # rebase.
767 if copytracing == b'heuristics':
763 if copytracing == b'heuristics':
768 # Do full copytracing if only non-public revisions are involved as
764 # Do full copytracing if only non-public revisions are involved as
769 # that will be fast enough and will also cover the copies which could
765 # that will be fast enough and will also cover the copies which could
770 # be missed by heuristics
766 # be missed by heuristics
771 if _isfullcopytraceable(repo, c1, base):
767 if _isfullcopytraceable(repo, c1, base):
772 return _fullcopytracing(repo, c1, c2, base)
768 return _fullcopytracing(repo, c1, c2, base)
773 return _heuristicscopytracing(repo, c1, c2, base)
769 return _heuristicscopytracing(repo, c1, c2, base)
774 else:
770 else:
775 return _fullcopytracing(repo, c1, c2, base)
771 return _fullcopytracing(repo, c1, c2, base)
776
772
777
773
778 def _isfullcopytraceable(repo, c1, base):
774 def _isfullcopytraceable(repo, c1, base):
779 """Checks that if base, source and destination are all no-public branches,
775 """Checks that if base, source and destination are all no-public branches,
780 if yes let's use the full copytrace algorithm for increased capabilities
776 if yes let's use the full copytrace algorithm for increased capabilities
781 since it will be fast enough.
777 since it will be fast enough.
782
778
783 `experimental.copytrace.sourcecommitlimit` can be used to set a limit for
779 `experimental.copytrace.sourcecommitlimit` can be used to set a limit for
784 number of changesets from c1 to base such that if number of changesets are
780 number of changesets from c1 to base such that if number of changesets are
785 more than the limit, full copytracing algorithm won't be used.
781 more than the limit, full copytracing algorithm won't be used.
786 """
782 """
787 if c1.rev() is None:
783 if c1.rev() is None:
788 c1 = c1.p1()
784 c1 = c1.p1()
789 if c1.mutable() and base.mutable():
785 if c1.mutable() and base.mutable():
790 sourcecommitlimit = repo.ui.configint(
786 sourcecommitlimit = repo.ui.configint(
791 b'experimental', b'copytrace.sourcecommitlimit'
787 b'experimental', b'copytrace.sourcecommitlimit'
792 )
788 )
793 commits = len(repo.revs(b'%d::%d', base.rev(), c1.rev()))
789 commits = len(repo.revs(b'%d::%d', base.rev(), c1.rev()))
794 return commits < sourcecommitlimit
790 return commits < sourcecommitlimit
795 return False
791 return False
796
792
797
793
798 def _checksinglesidecopies(
794 def _checksinglesidecopies(
799 src, dsts1, m1, m2, mb, c2, base, copy, renamedelete
795 src, dsts1, m1, m2, mb, c2, base, copy, renamedelete
800 ):
796 ):
801 if src not in m2:
797 if src not in m2:
802 # deleted on side 2
798 # deleted on side 2
803 if src not in m1:
799 if src not in m1:
804 # renamed on side 1, deleted on side 2
800 # renamed on side 1, deleted on side 2
805 renamedelete[src] = dsts1
801 renamedelete[src] = dsts1
806 elif src not in mb:
802 elif src not in mb:
807 # Work around the "short-circuit to avoid issues with merge states"
803 # Work around the "short-circuit to avoid issues with merge states"
808 # thing in pathcopies(): pathcopies(x, y) can return a copy where the
804 # thing in pathcopies(): pathcopies(x, y) can return a copy where the
809 # destination doesn't exist in y.
805 # destination doesn't exist in y.
810 pass
806 pass
811 elif mb[src] != m2[src] and not _related(c2[src], base[src]):
807 elif mb[src] != m2[src] and not _related(c2[src], base[src]):
812 return
808 return
813 elif mb[src] != m2[src] or mb.flags(src) != m2.flags(src):
809 elif mb[src] != m2[src] or mb.flags(src) != m2.flags(src):
814 # modified on side 2
810 # modified on side 2
815 for dst in dsts1:
811 for dst in dsts1:
816 copy[dst] = src
812 copy[dst] = src
817
813
818
814
819 class branch_copies(object):
815 class branch_copies(object):
820 """Information about copies made on one side of a merge/graft.
816 """Information about copies made on one side of a merge/graft.
821
817
822 "copy" is a mapping from destination name -> source name,
818 "copy" is a mapping from destination name -> source name,
823 where source is in c1 and destination is in c2 or vice-versa.
819 where source is in c1 and destination is in c2 or vice-versa.
824
820
825 "movewithdir" is a mapping from source name -> destination name,
821 "movewithdir" is a mapping from source name -> destination name,
826 where the file at source present in one context but not the other
822 where the file at source present in one context but not the other
827 needs to be moved to destination by the merge process, because the
823 needs to be moved to destination by the merge process, because the
828 other context moved the directory it is in.
824 other context moved the directory it is in.
829
825
830 "renamedelete" is a mapping of source name -> list of destination
826 "renamedelete" is a mapping of source name -> list of destination
831 names for files deleted in c1 that were renamed in c2 or vice-versa.
827 names for files deleted in c1 that were renamed in c2 or vice-versa.
832
828
833 "dirmove" is a mapping of detected source dir -> destination dir renames.
829 "dirmove" is a mapping of detected source dir -> destination dir renames.
834 This is needed for handling changes to new files previously grafted into
830 This is needed for handling changes to new files previously grafted into
835 renamed directories.
831 renamed directories.
836 """
832 """
837
833
838 def __init__(
834 def __init__(
839 self, copy=None, renamedelete=None, dirmove=None, movewithdir=None
835 self, copy=None, renamedelete=None, dirmove=None, movewithdir=None
840 ):
836 ):
841 self.copy = {} if copy is None else copy
837 self.copy = {} if copy is None else copy
842 self.renamedelete = {} if renamedelete is None else renamedelete
838 self.renamedelete = {} if renamedelete is None else renamedelete
843 self.dirmove = {} if dirmove is None else dirmove
839 self.dirmove = {} if dirmove is None else dirmove
844 self.movewithdir = {} if movewithdir is None else movewithdir
840 self.movewithdir = {} if movewithdir is None else movewithdir
845
841
846 def __repr__(self):
842 def __repr__(self):
847 return '<branch_copies\n copy=%r\n renamedelete=%r\n dirmove=%r\n movewithdir=%r\n>' % (
843 return '<branch_copies\n copy=%r\n renamedelete=%r\n dirmove=%r\n movewithdir=%r\n>' % (
848 self.copy,
844 self.copy,
849 self.renamedelete,
845 self.renamedelete,
850 self.dirmove,
846 self.dirmove,
851 self.movewithdir,
847 self.movewithdir,
852 )
848 )
853
849
854
850
855 def _fullcopytracing(repo, c1, c2, base):
851 def _fullcopytracing(repo, c1, c2, base):
856 """The full copytracing algorithm which finds all the new files that were
852 """The full copytracing algorithm which finds all the new files that were
857 added from merge base up to the top commit and for each file it checks if
853 added from merge base up to the top commit and for each file it checks if
858 this file was copied from another file.
854 this file was copied from another file.
859
855
860 This is pretty slow when a lot of changesets are involved but will track all
856 This is pretty slow when a lot of changesets are involved but will track all
861 the copies.
857 the copies.
862 """
858 """
863 m1 = c1.manifest()
859 m1 = c1.manifest()
864 m2 = c2.manifest()
860 m2 = c2.manifest()
865 mb = base.manifest()
861 mb = base.manifest()
866
862
867 copies1 = pathcopies(base, c1)
863 copies1 = pathcopies(base, c1)
868 copies2 = pathcopies(base, c2)
864 copies2 = pathcopies(base, c2)
869
865
870 if not (copies1 or copies2):
866 if not (copies1 or copies2):
871 return branch_copies(), branch_copies(), {}
867 return branch_copies(), branch_copies(), {}
872
868
873 inversecopies1 = {}
869 inversecopies1 = {}
874 inversecopies2 = {}
870 inversecopies2 = {}
875 for dst, src in copies1.items():
871 for dst, src in copies1.items():
876 inversecopies1.setdefault(src, []).append(dst)
872 inversecopies1.setdefault(src, []).append(dst)
877 for dst, src in copies2.items():
873 for dst, src in copies2.items():
878 inversecopies2.setdefault(src, []).append(dst)
874 inversecopies2.setdefault(src, []).append(dst)
879
875
880 copy1 = {}
876 copy1 = {}
881 copy2 = {}
877 copy2 = {}
882 diverge = {}
878 diverge = {}
883 renamedelete1 = {}
879 renamedelete1 = {}
884 renamedelete2 = {}
880 renamedelete2 = {}
885 allsources = set(inversecopies1) | set(inversecopies2)
881 allsources = set(inversecopies1) | set(inversecopies2)
886 for src in allsources:
882 for src in allsources:
887 dsts1 = inversecopies1.get(src)
883 dsts1 = inversecopies1.get(src)
888 dsts2 = inversecopies2.get(src)
884 dsts2 = inversecopies2.get(src)
889 if dsts1 and dsts2:
885 if dsts1 and dsts2:
890 # copied/renamed on both sides
886 # copied/renamed on both sides
891 if src not in m1 and src not in m2:
887 if src not in m1 and src not in m2:
892 # renamed on both sides
888 # renamed on both sides
893 dsts1 = set(dsts1)
889 dsts1 = set(dsts1)
894 dsts2 = set(dsts2)
890 dsts2 = set(dsts2)
895 # If there's some overlap in the rename destinations, we
891 # If there's some overlap in the rename destinations, we
896 # consider it not divergent. For example, if side 1 copies 'a'
892 # consider it not divergent. For example, if side 1 copies 'a'
897 # to 'b' and 'c' and deletes 'a', and side 2 copies 'a' to 'c'
893 # to 'b' and 'c' and deletes 'a', and side 2 copies 'a' to 'c'
898 # and 'd' and deletes 'a'.
894 # and 'd' and deletes 'a'.
899 if dsts1 & dsts2:
895 if dsts1 & dsts2:
900 for dst in dsts1 & dsts2:
896 for dst in dsts1 & dsts2:
901 copy1[dst] = src
897 copy1[dst] = src
902 copy2[dst] = src
898 copy2[dst] = src
903 else:
899 else:
904 diverge[src] = sorted(dsts1 | dsts2)
900 diverge[src] = sorted(dsts1 | dsts2)
905 elif src in m1 and src in m2:
901 elif src in m1 and src in m2:
906 # copied on both sides
902 # copied on both sides
907 dsts1 = set(dsts1)
903 dsts1 = set(dsts1)
908 dsts2 = set(dsts2)
904 dsts2 = set(dsts2)
909 for dst in dsts1 & dsts2:
905 for dst in dsts1 & dsts2:
910 copy1[dst] = src
906 copy1[dst] = src
911 copy2[dst] = src
907 copy2[dst] = src
912 # TODO: Handle cases where it was renamed on one side and copied
908 # TODO: Handle cases where it was renamed on one side and copied
913 # on the other side
909 # on the other side
914 elif dsts1:
910 elif dsts1:
915 # copied/renamed only on side 1
911 # copied/renamed only on side 1
916 _checksinglesidecopies(
912 _checksinglesidecopies(
917 src, dsts1, m1, m2, mb, c2, base, copy1, renamedelete1
913 src, dsts1, m1, m2, mb, c2, base, copy1, renamedelete1
918 )
914 )
919 elif dsts2:
915 elif dsts2:
920 # copied/renamed only on side 2
916 # copied/renamed only on side 2
921 _checksinglesidecopies(
917 _checksinglesidecopies(
922 src, dsts2, m2, m1, mb, c1, base, copy2, renamedelete2
918 src, dsts2, m2, m1, mb, c1, base, copy2, renamedelete2
923 )
919 )
924
920
925 # find interesting file sets from manifests
921 # find interesting file sets from manifests
926 cache = []
922 cache = []
927
923
928 def _get_addedfiles(idx):
924 def _get_addedfiles(idx):
929 if not cache:
925 if not cache:
930 addedinm1 = m1.filesnotin(mb, repo.narrowmatch())
926 addedinm1 = m1.filesnotin(mb, repo.narrowmatch())
931 addedinm2 = m2.filesnotin(mb, repo.narrowmatch())
927 addedinm2 = m2.filesnotin(mb, repo.narrowmatch())
932 u1 = sorted(addedinm1 - addedinm2)
928 u1 = sorted(addedinm1 - addedinm2)
933 u2 = sorted(addedinm2 - addedinm1)
929 u2 = sorted(addedinm2 - addedinm1)
934 cache.extend((u1, u2))
930 cache.extend((u1, u2))
935 return cache[idx]
931 return cache[idx]
936
932
937 u1fn = lambda: _get_addedfiles(0)
933 u1fn = lambda: _get_addedfiles(0)
938 u2fn = lambda: _get_addedfiles(1)
934 u2fn = lambda: _get_addedfiles(1)
939 if repo.ui.debugflag:
935 if repo.ui.debugflag:
940 u1 = u1fn()
936 u1 = u1fn()
941 u2 = u2fn()
937 u2 = u2fn()
942
938
943 header = b" unmatched files in %s"
939 header = b" unmatched files in %s"
944 if u1:
940 if u1:
945 repo.ui.debug(
941 repo.ui.debug(
946 b"%s:\n %s\n" % (header % b'local', b"\n ".join(u1))
942 b"%s:\n %s\n" % (header % b'local', b"\n ".join(u1))
947 )
943 )
948 if u2:
944 if u2:
949 repo.ui.debug(
945 repo.ui.debug(
950 b"%s:\n %s\n" % (header % b'other', b"\n ".join(u2))
946 b"%s:\n %s\n" % (header % b'other', b"\n ".join(u2))
951 )
947 )
952
948
953 renamedeleteset = set()
949 renamedeleteset = set()
954 divergeset = set()
950 divergeset = set()
955 for dsts in diverge.values():
951 for dsts in diverge.values():
956 divergeset.update(dsts)
952 divergeset.update(dsts)
957 for dsts in renamedelete1.values():
953 for dsts in renamedelete1.values():
958 renamedeleteset.update(dsts)
954 renamedeleteset.update(dsts)
959 for dsts in renamedelete2.values():
955 for dsts in renamedelete2.values():
960 renamedeleteset.update(dsts)
956 renamedeleteset.update(dsts)
961
957
962 repo.ui.debug(
958 repo.ui.debug(
963 b" all copies found (* = to merge, ! = divergent, "
959 b" all copies found (* = to merge, ! = divergent, "
964 b"% = renamed and deleted):\n"
960 b"% = renamed and deleted):\n"
965 )
961 )
966 for side, copies in ((b"local", copies1), (b"remote", copies2)):
962 for side, copies in ((b"local", copies1), (b"remote", copies2)):
967 if not copies:
963 if not copies:
968 continue
964 continue
969 repo.ui.debug(b" on %s side:\n" % side)
965 repo.ui.debug(b" on %s side:\n" % side)
970 for f in sorted(copies):
966 for f in sorted(copies):
971 note = b""
967 note = b""
972 if f in copy1 or f in copy2:
968 if f in copy1 or f in copy2:
973 note += b"*"
969 note += b"*"
974 if f in divergeset:
970 if f in divergeset:
975 note += b"!"
971 note += b"!"
976 if f in renamedeleteset:
972 if f in renamedeleteset:
977 note += b"%"
973 note += b"%"
978 repo.ui.debug(
974 repo.ui.debug(
979 b" src: '%s' -> dst: '%s' %s\n" % (copies[f], f, note)
975 b" src: '%s' -> dst: '%s' %s\n" % (copies[f], f, note)
980 )
976 )
981 del renamedeleteset
977 del renamedeleteset
982 del divergeset
978 del divergeset
983
979
984 repo.ui.debug(b" checking for directory renames\n")
980 repo.ui.debug(b" checking for directory renames\n")
985
981
986 dirmove1, movewithdir2 = _dir_renames(repo, c1, copy1, copies1, u2fn)
982 dirmove1, movewithdir2 = _dir_renames(repo, c1, copy1, copies1, u2fn)
987 dirmove2, movewithdir1 = _dir_renames(repo, c2, copy2, copies2, u1fn)
983 dirmove2, movewithdir1 = _dir_renames(repo, c2, copy2, copies2, u1fn)
988
984
989 branch_copies1 = branch_copies(copy1, renamedelete1, dirmove1, movewithdir1)
985 branch_copies1 = branch_copies(copy1, renamedelete1, dirmove1, movewithdir1)
990 branch_copies2 = branch_copies(copy2, renamedelete2, dirmove2, movewithdir2)
986 branch_copies2 = branch_copies(copy2, renamedelete2, dirmove2, movewithdir2)
991
987
992 return branch_copies1, branch_copies2, diverge
988 return branch_copies1, branch_copies2, diverge
993
989
994
990
995 def _dir_renames(repo, ctx, copy, fullcopy, addedfilesfn):
991 def _dir_renames(repo, ctx, copy, fullcopy, addedfilesfn):
996 """Finds moved directories and files that should move with them.
992 """Finds moved directories and files that should move with them.
997
993
998 ctx: the context for one of the sides
994 ctx: the context for one of the sides
999 copy: files copied on the same side (as ctx)
995 copy: files copied on the same side (as ctx)
1000 fullcopy: files copied on the same side (as ctx), including those that
996 fullcopy: files copied on the same side (as ctx), including those that
1001 merge.manifestmerge() won't care about
997 merge.manifestmerge() won't care about
1002 addedfilesfn: function returning added files on the other side (compared to
998 addedfilesfn: function returning added files on the other side (compared to
1003 ctx)
999 ctx)
1004 """
1000 """
1005 # generate a directory move map
1001 # generate a directory move map
1006 invalid = set()
1002 invalid = set()
1007 dirmove = {}
1003 dirmove = {}
1008
1004
1009 # examine each file copy for a potential directory move, which is
1005 # examine each file copy for a potential directory move, which is
1010 # when all the files in a directory are moved to a new directory
1006 # when all the files in a directory are moved to a new directory
1011 for dst, src in pycompat.iteritems(fullcopy):
1007 for dst, src in pycompat.iteritems(fullcopy):
1012 dsrc, ddst = pathutil.dirname(src), pathutil.dirname(dst)
1008 dsrc, ddst = pathutil.dirname(src), pathutil.dirname(dst)
1013 if dsrc in invalid:
1009 if dsrc in invalid:
1014 # already seen to be uninteresting
1010 # already seen to be uninteresting
1015 continue
1011 continue
1016 elif ctx.hasdir(dsrc) and ctx.hasdir(ddst):
1012 elif ctx.hasdir(dsrc) and ctx.hasdir(ddst):
1017 # directory wasn't entirely moved locally
1013 # directory wasn't entirely moved locally
1018 invalid.add(dsrc)
1014 invalid.add(dsrc)
1019 elif dsrc in dirmove and dirmove[dsrc] != ddst:
1015 elif dsrc in dirmove and dirmove[dsrc] != ddst:
1020 # files from the same directory moved to two different places
1016 # files from the same directory moved to two different places
1021 invalid.add(dsrc)
1017 invalid.add(dsrc)
1022 else:
1018 else:
1023 # looks good so far
1019 # looks good so far
1024 dirmove[dsrc] = ddst
1020 dirmove[dsrc] = ddst
1025
1021
1026 for i in invalid:
1022 for i in invalid:
1027 if i in dirmove:
1023 if i in dirmove:
1028 del dirmove[i]
1024 del dirmove[i]
1029 del invalid
1025 del invalid
1030
1026
1031 if not dirmove:
1027 if not dirmove:
1032 return {}, {}
1028 return {}, {}
1033
1029
1034 dirmove = {k + b"/": v + b"/" for k, v in pycompat.iteritems(dirmove)}
1030 dirmove = {k + b"/": v + b"/" for k, v in pycompat.iteritems(dirmove)}
1035
1031
1036 for d in dirmove:
1032 for d in dirmove:
1037 repo.ui.debug(
1033 repo.ui.debug(
1038 b" discovered dir src: '%s' -> dst: '%s'\n" % (d, dirmove[d])
1034 b" discovered dir src: '%s' -> dst: '%s'\n" % (d, dirmove[d])
1039 )
1035 )
1040
1036
1041 movewithdir = {}
1037 movewithdir = {}
1042 # check unaccounted nonoverlapping files against directory moves
1038 # check unaccounted nonoverlapping files against directory moves
1043 for f in addedfilesfn():
1039 for f in addedfilesfn():
1044 if f not in fullcopy:
1040 if f not in fullcopy:
1045 for d in dirmove:
1041 for d in dirmove:
1046 if f.startswith(d):
1042 if f.startswith(d):
1047 # new file added in a directory that was moved, move it
1043 # new file added in a directory that was moved, move it
1048 df = dirmove[d] + f[len(d) :]
1044 df = dirmove[d] + f[len(d) :]
1049 if df not in copy:
1045 if df not in copy:
1050 movewithdir[f] = df
1046 movewithdir[f] = df
1051 repo.ui.debug(
1047 repo.ui.debug(
1052 b" pending file src: '%s' -> dst: '%s'\n"
1048 b" pending file src: '%s' -> dst: '%s'\n"
1053 % (f, df)
1049 % (f, df)
1054 )
1050 )
1055 break
1051 break
1056
1052
1057 return dirmove, movewithdir
1053 return dirmove, movewithdir
1058
1054
1059
1055
1060 def _heuristicscopytracing(repo, c1, c2, base):
1056 def _heuristicscopytracing(repo, c1, c2, base):
1061 """Fast copytracing using filename heuristics
1057 """Fast copytracing using filename heuristics
1062
1058
1063 Assumes that moves or renames are of following two types:
1059 Assumes that moves or renames are of following two types:
1064
1060
1065 1) Inside a directory only (same directory name but different filenames)
1061 1) Inside a directory only (same directory name but different filenames)
1066 2) Move from one directory to another
1062 2) Move from one directory to another
1067 (same filenames but different directory names)
1063 (same filenames but different directory names)
1068
1064
1069 Works only when there are no merge commits in the "source branch".
1065 Works only when there are no merge commits in the "source branch".
1070 Source branch is commits from base up to c2 not including base.
1066 Source branch is commits from base up to c2 not including base.
1071
1067
1072 If merge is involved it fallbacks to _fullcopytracing().
1068 If merge is involved it fallbacks to _fullcopytracing().
1073
1069
1074 Can be used by setting the following config:
1070 Can be used by setting the following config:
1075
1071
1076 [experimental]
1072 [experimental]
1077 copytrace = heuristics
1073 copytrace = heuristics
1078
1074
1079 In some cases the copy/move candidates found by heuristics can be very large
1075 In some cases the copy/move candidates found by heuristics can be very large
1080 in number and that will make the algorithm slow. The number of possible
1076 in number and that will make the algorithm slow. The number of possible
1081 candidates to check can be limited by using the config
1077 candidates to check can be limited by using the config
1082 `experimental.copytrace.movecandidateslimit` which defaults to 100.
1078 `experimental.copytrace.movecandidateslimit` which defaults to 100.
1083 """
1079 """
1084
1080
1085 if c1.rev() is None:
1081 if c1.rev() is None:
1086 c1 = c1.p1()
1082 c1 = c1.p1()
1087 if c2.rev() is None:
1083 if c2.rev() is None:
1088 c2 = c2.p1()
1084 c2 = c2.p1()
1089
1085
1090 changedfiles = set()
1086 changedfiles = set()
1091 m1 = c1.manifest()
1087 m1 = c1.manifest()
1092 if not repo.revs(b'%d::%d', base.rev(), c2.rev()):
1088 if not repo.revs(b'%d::%d', base.rev(), c2.rev()):
1093 # If base is not in c2 branch, we switch to fullcopytracing
1089 # If base is not in c2 branch, we switch to fullcopytracing
1094 repo.ui.debug(
1090 repo.ui.debug(
1095 b"switching to full copytracing as base is not "
1091 b"switching to full copytracing as base is not "
1096 b"an ancestor of c2\n"
1092 b"an ancestor of c2\n"
1097 )
1093 )
1098 return _fullcopytracing(repo, c1, c2, base)
1094 return _fullcopytracing(repo, c1, c2, base)
1099
1095
1100 ctx = c2
1096 ctx = c2
1101 while ctx != base:
1097 while ctx != base:
1102 if len(ctx.parents()) == 2:
1098 if len(ctx.parents()) == 2:
1103 # To keep things simple let's not handle merges
1099 # To keep things simple let's not handle merges
1104 repo.ui.debug(b"switching to full copytracing because of merges\n")
1100 repo.ui.debug(b"switching to full copytracing because of merges\n")
1105 return _fullcopytracing(repo, c1, c2, base)
1101 return _fullcopytracing(repo, c1, c2, base)
1106 changedfiles.update(ctx.files())
1102 changedfiles.update(ctx.files())
1107 ctx = ctx.p1()
1103 ctx = ctx.p1()
1108
1104
1109 copies2 = {}
1105 copies2 = {}
1110 cp = _forwardcopies(base, c2)
1106 cp = _forwardcopies(base, c2)
1111 for dst, src in pycompat.iteritems(cp):
1107 for dst, src in pycompat.iteritems(cp):
1112 if src in m1:
1108 if src in m1:
1113 copies2[dst] = src
1109 copies2[dst] = src
1114
1110
1115 # file is missing if it isn't present in the destination, but is present in
1111 # file is missing if it isn't present in the destination, but is present in
1116 # the base and present in the source.
1112 # the base and present in the source.
1117 # Presence in the base is important to exclude added files, presence in the
1113 # Presence in the base is important to exclude added files, presence in the
1118 # source is important to exclude removed files.
1114 # source is important to exclude removed files.
1119 filt = lambda f: f not in m1 and f in base and f in c2
1115 filt = lambda f: f not in m1 and f in base and f in c2
1120 missingfiles = [f for f in changedfiles if filt(f)]
1116 missingfiles = [f for f in changedfiles if filt(f)]
1121
1117
1122 copies1 = {}
1118 copies1 = {}
1123 if missingfiles:
1119 if missingfiles:
1124 basenametofilename = collections.defaultdict(list)
1120 basenametofilename = collections.defaultdict(list)
1125 dirnametofilename = collections.defaultdict(list)
1121 dirnametofilename = collections.defaultdict(list)
1126
1122
1127 for f in m1.filesnotin(base.manifest()):
1123 for f in m1.filesnotin(base.manifest()):
1128 basename = os.path.basename(f)
1124 basename = os.path.basename(f)
1129 dirname = os.path.dirname(f)
1125 dirname = os.path.dirname(f)
1130 basenametofilename[basename].append(f)
1126 basenametofilename[basename].append(f)
1131 dirnametofilename[dirname].append(f)
1127 dirnametofilename[dirname].append(f)
1132
1128
1133 for f in missingfiles:
1129 for f in missingfiles:
1134 basename = os.path.basename(f)
1130 basename = os.path.basename(f)
1135 dirname = os.path.dirname(f)
1131 dirname = os.path.dirname(f)
1136 samebasename = basenametofilename[basename]
1132 samebasename = basenametofilename[basename]
1137 samedirname = dirnametofilename[dirname]
1133 samedirname = dirnametofilename[dirname]
1138 movecandidates = samebasename + samedirname
1134 movecandidates = samebasename + samedirname
1139 # f is guaranteed to be present in c2, that's why
1135 # f is guaranteed to be present in c2, that's why
1140 # c2.filectx(f) won't fail
1136 # c2.filectx(f) won't fail
1141 f2 = c2.filectx(f)
1137 f2 = c2.filectx(f)
1142 # we can have a lot of candidates which can slow down the heuristics
1138 # we can have a lot of candidates which can slow down the heuristics
1143 # config value to limit the number of candidates moves to check
1139 # config value to limit the number of candidates moves to check
1144 maxcandidates = repo.ui.configint(
1140 maxcandidates = repo.ui.configint(
1145 b'experimental', b'copytrace.movecandidateslimit'
1141 b'experimental', b'copytrace.movecandidateslimit'
1146 )
1142 )
1147
1143
1148 if len(movecandidates) > maxcandidates:
1144 if len(movecandidates) > maxcandidates:
1149 repo.ui.status(
1145 repo.ui.status(
1150 _(
1146 _(
1151 b"skipping copytracing for '%s', more "
1147 b"skipping copytracing for '%s', more "
1152 b"candidates than the limit: %d\n"
1148 b"candidates than the limit: %d\n"
1153 )
1149 )
1154 % (f, len(movecandidates))
1150 % (f, len(movecandidates))
1155 )
1151 )
1156 continue
1152 continue
1157
1153
1158 for candidate in movecandidates:
1154 for candidate in movecandidates:
1159 f1 = c1.filectx(candidate)
1155 f1 = c1.filectx(candidate)
1160 if _related(f1, f2):
1156 if _related(f1, f2):
1161 # if there are a few related copies then we'll merge
1157 # if there are a few related copies then we'll merge
1162 # changes into all of them. This matches the behaviour
1158 # changes into all of them. This matches the behaviour
1163 # of upstream copytracing
1159 # of upstream copytracing
1164 copies1[candidate] = f
1160 copies1[candidate] = f
1165
1161
1166 return branch_copies(copies1), branch_copies(copies2), {}
1162 return branch_copies(copies1), branch_copies(copies2), {}
1167
1163
1168
1164
1169 def _related(f1, f2):
1165 def _related(f1, f2):
1170 """return True if f1 and f2 filectx have a common ancestor
1166 """return True if f1 and f2 filectx have a common ancestor
1171
1167
1172 Walk back to common ancestor to see if the two files originate
1168 Walk back to common ancestor to see if the two files originate
1173 from the same file. Since workingfilectx's rev() is None it messes
1169 from the same file. Since workingfilectx's rev() is None it messes
1174 up the integer comparison logic, hence the pre-step check for
1170 up the integer comparison logic, hence the pre-step check for
1175 None (f1 and f2 can only be workingfilectx's initially).
1171 None (f1 and f2 can only be workingfilectx's initially).
1176 """
1172 """
1177
1173
1178 if f1 == f2:
1174 if f1 == f2:
1179 return True # a match
1175 return True # a match
1180
1176
1181 g1, g2 = f1.ancestors(), f2.ancestors()
1177 g1, g2 = f1.ancestors(), f2.ancestors()
1182 try:
1178 try:
1183 f1r, f2r = f1.linkrev(), f2.linkrev()
1179 f1r, f2r = f1.linkrev(), f2.linkrev()
1184
1180
1185 if f1r is None:
1181 if f1r is None:
1186 f1 = next(g1)
1182 f1 = next(g1)
1187 if f2r is None:
1183 if f2r is None:
1188 f2 = next(g2)
1184 f2 = next(g2)
1189
1185
1190 while True:
1186 while True:
1191 f1r, f2r = f1.linkrev(), f2.linkrev()
1187 f1r, f2r = f1.linkrev(), f2.linkrev()
1192 if f1r > f2r:
1188 if f1r > f2r:
1193 f1 = next(g1)
1189 f1 = next(g1)
1194 elif f2r > f1r:
1190 elif f2r > f1r:
1195 f2 = next(g2)
1191 f2 = next(g2)
1196 else: # f1 and f2 point to files in the same linkrev
1192 else: # f1 and f2 point to files in the same linkrev
1197 return f1 == f2 # true if they point to the same file
1193 return f1 == f2 # true if they point to the same file
1198 except StopIteration:
1194 except StopIteration:
1199 return False
1195 return False
1200
1196
1201
1197
1202 def graftcopies(wctx, ctx, base):
1198 def graftcopies(wctx, ctx, base):
1203 """reproduce copies between base and ctx in the wctx
1199 """reproduce copies between base and ctx in the wctx
1204
1200
1205 Unlike mergecopies(), this function will only consider copies between base
1201 Unlike mergecopies(), this function will only consider copies between base
1206 and ctx; it will ignore copies between base and wctx. Also unlike
1202 and ctx; it will ignore copies between base and wctx. Also unlike
1207 mergecopies(), this function will apply copies to the working copy (instead
1203 mergecopies(), this function will apply copies to the working copy (instead
1208 of just returning information about the copies). That makes it cheaper
1204 of just returning information about the copies). That makes it cheaper
1209 (especially in the common case of base==ctx.p1()) and useful also when
1205 (especially in the common case of base==ctx.p1()) and useful also when
1210 experimental.copytrace=off.
1206 experimental.copytrace=off.
1211
1207
1212 merge.update() will have already marked most copies, but it will only
1208 merge.update() will have already marked most copies, but it will only
1213 mark copies if it thinks the source files are related (see
1209 mark copies if it thinks the source files are related (see
1214 merge._related()). It will also not mark copies if the file wasn't modified
1210 merge._related()). It will also not mark copies if the file wasn't modified
1215 on the local side. This function adds the copies that were "missed"
1211 on the local side. This function adds the copies that were "missed"
1216 by merge.update().
1212 by merge.update().
1217 """
1213 """
1218 new_copies = pathcopies(base, ctx)
1214 new_copies = pathcopies(base, ctx)
1219 _filter(wctx.p1(), wctx, new_copies)
1215 _filter(wctx.p1(), wctx, new_copies)
1220 for dst, src in pycompat.iteritems(new_copies):
1216 for dst, src in pycompat.iteritems(new_copies):
1221 wctx[dst].markcopied(src)
1217 wctx[dst].markcopied(src)
@@ -1,1672 +1,1685 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 >> $HGRCPATH
17 $ cat << EOF >> $HGRCPATH
18 > [diff]
18 > [diff]
19 > git=yes
19 > git=yes
20 > [command-templates]
20 > [command-templates]
21 > log={rev} {desc}\n
21 > log={rev} {desc}\n
22 > EOF
22 > EOF
23
23
24 #if compatibility
24 #if compatibility
25 $ cat >> $HGRCPATH << EOF
25 $ cat >> $HGRCPATH << EOF
26 > [experimental]
26 > [experimental]
27 > copies.read-from = compatibility
27 > copies.read-from = compatibility
28 > EOF
28 > EOF
29 #endif
29 #endif
30
30
31 #if changeset
31 #if changeset
32 $ cat >> $HGRCPATH << EOF
32 $ cat >> $HGRCPATH << EOF
33 > [experimental]
33 > [experimental]
34 > copies.read-from = changeset-only
34 > copies.read-from = changeset-only
35 > copies.write-to = changeset-only
35 > copies.write-to = changeset-only
36 > EOF
36 > EOF
37 #endif
37 #endif
38
38
39 #if sidedata
39 #if sidedata
40 $ cat >> $HGRCPATH << EOF
40 $ cat >> $HGRCPATH << EOF
41 > [format]
41 > [format]
42 > exp-use-side-data = yes
42 > exp-use-side-data = yes
43 > exp-use-copies-side-data-changeset = yes
43 > exp-use-copies-side-data-changeset = yes
44 > EOF
44 > EOF
45 #endif
45 #endif
46
46
47
47
48 $ hg init repo-chain
48 $ hg init repo-chain
49 $ cd repo-chain
49 $ cd repo-chain
50
50
51 Add some linear rename initialy
51 Add some linear rename initialy
52
52
53 $ echo a > a
53 $ echo a > a
54 $ echo b > b
54 $ echo b > b
55 $ echo h > h
55 $ echo h > h
56 $ hg ci -Am 'i-0 initial commit: a b h'
56 $ hg ci -Am 'i-0 initial commit: a b h'
57 adding a
57 adding a
58 adding b
58 adding b
59 adding h
59 adding h
60 $ hg mv a c
60 $ hg mv a c
61 $ hg ci -Am 'i-1: a -move-> c'
61 $ hg ci -Am 'i-1: a -move-> c'
62 $ hg mv c d
62 $ hg mv c d
63 $ hg ci -Am 'i-2: c -move-> d'
63 $ hg ci -Am 'i-2: c -move-> d'
64 $ hg log -G
64 $ hg log -G
65 @ 2 i-2: c -move-> d
65 @ 2 i-2: c -move-> d
66 |
66 |
67 o 1 i-1: a -move-> c
67 o 1 i-1: a -move-> c
68 |
68 |
69 o 0 i-0 initial commit: a b h
69 o 0 i-0 initial commit: a b h
70
70
71
71
72 And having another branch with renames on the other side
72 And having another branch with renames on the other side
73
73
74 $ hg mv d e
74 $ hg mv d e
75 $ hg ci -Am 'a-1: d -move-> e'
75 $ hg ci -Am 'a-1: d -move-> e'
76 $ hg mv e f
76 $ hg mv e f
77 $ hg ci -Am 'a-2: e -move-> f'
77 $ hg ci -Am 'a-2: e -move-> f'
78 $ hg log -G --rev '::.'
78 $ hg log -G --rev '::.'
79 @ 4 a-2: e -move-> f
79 @ 4 a-2: e -move-> f
80 |
80 |
81 o 3 a-1: d -move-> e
81 o 3 a-1: d -move-> e
82 |
82 |
83 o 2 i-2: c -move-> d
83 o 2 i-2: c -move-> d
84 |
84 |
85 o 1 i-1: a -move-> c
85 o 1 i-1: a -move-> c
86 |
86 |
87 o 0 i-0 initial commit: a b h
87 o 0 i-0 initial commit: a b h
88
88
89
89
90 Have a branching with nothing on one side
90 Have a branching with nothing on one side
91
91
92 $ hg up 'desc("i-2")'
92 $ hg up 'desc("i-2")'
93 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
93 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
94 $ echo foo > b
94 $ echo foo > b
95 $ hg ci -m 'b-1: b update'
95 $ hg ci -m 'b-1: b update'
96 created new head
96 created new head
97 $ hg log -G --rev '::.'
97 $ hg log -G --rev '::.'
98 @ 5 b-1: b update
98 @ 5 b-1: b update
99 |
99 |
100 o 2 i-2: c -move-> d
100 o 2 i-2: c -move-> d
101 |
101 |
102 o 1 i-1: a -move-> c
102 o 1 i-1: a -move-> c
103 |
103 |
104 o 0 i-0 initial commit: a b h
104 o 0 i-0 initial commit: a b h
105
105
106
106
107 Create a branch that delete a file previous renamed
107 Create a branch that delete a file previous renamed
108
108
109 $ hg up 'desc("i-2")'
109 $ hg up 'desc("i-2")'
110 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
110 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
111 $ hg rm d
111 $ hg rm d
112 $ hg ci -m 'c-1 delete d'
112 $ hg ci -m 'c-1 delete d'
113 created new head
113 created new head
114 $ hg log -G --rev '::.'
114 $ hg log -G --rev '::.'
115 @ 6 c-1 delete d
115 @ 6 c-1 delete d
116 |
116 |
117 o 2 i-2: c -move-> d
117 o 2 i-2: c -move-> d
118 |
118 |
119 o 1 i-1: a -move-> c
119 o 1 i-1: a -move-> c
120 |
120 |
121 o 0 i-0 initial commit: a b h
121 o 0 i-0 initial commit: a b h
122
122
123
123
124 Create a branch that delete a file previous renamed and recreate it
124 Create a branch that delete a file previous renamed and recreate it
125
125
126 $ hg up 'desc("i-2")'
126 $ hg up 'desc("i-2")'
127 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
127 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
128 $ hg rm d
128 $ hg rm d
129 $ hg ci -m 'd-1 delete d'
129 $ hg ci -m 'd-1 delete d'
130 created new head
130 created new head
131 $ echo bar > d
131 $ echo bar > d
132 $ hg add d
132 $ hg add d
133 $ hg ci -m 'd-2 re-add d'
133 $ hg ci -m 'd-2 re-add d'
134 $ hg log -G --rev '::.'
134 $ hg log -G --rev '::.'
135 @ 8 d-2 re-add d
135 @ 8 d-2 re-add d
136 |
136 |
137 o 7 d-1 delete d
137 o 7 d-1 delete d
138 |
138 |
139 o 2 i-2: c -move-> d
139 o 2 i-2: c -move-> d
140 |
140 |
141 o 1 i-1: a -move-> c
141 o 1 i-1: a -move-> c
142 |
142 |
143 o 0 i-0 initial commit: a b h
143 o 0 i-0 initial commit: a b h
144
144
145
145
146 Having another branch renaming a different file to the same filename as another
146 Having another branch renaming a different file to the same filename as another
147
147
148 $ hg up 'desc("i-2")'
148 $ hg up 'desc("i-2")'
149 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
149 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
150 $ hg mv b g
150 $ hg mv b g
151 $ hg ci -m 'e-1 b -move-> g'
151 $ hg ci -m 'e-1 b -move-> g'
152 created new head
152 created new head
153 $ hg mv g f
153 $ hg mv g f
154 $ hg ci -m 'e-2 g -move-> f'
154 $ hg ci -m 'e-2 g -move-> f'
155 $ hg log -G --rev '::.'
155 $ hg log -G --rev '::.'
156 @ 10 e-2 g -move-> f
156 @ 10 e-2 g -move-> f
157 |
157 |
158 o 9 e-1 b -move-> g
158 o 9 e-1 b -move-> g
159 |
159 |
160 o 2 i-2: c -move-> d
160 o 2 i-2: c -move-> d
161 |
161 |
162 o 1 i-1: a -move-> c
162 o 1 i-1: a -move-> c
163 |
163 |
164 o 0 i-0 initial commit: a b h
164 o 0 i-0 initial commit: a b h
165
165
166
166
167 Setup all merge
167 Setup all merge
168 ===============
168 ===============
169
169
170 This is done beforehand to validate that the upgrade process creates valid copy
170 This is done beforehand to validate that the upgrade process creates valid copy
171 information.
171 information.
172
172
173 merging with unrelated change does not interfere with the renames
173 merging with unrelated change does not interfere with the renames
174 ---------------------------------------------------------------
174 ---------------------------------------------------------------
175
175
176 - rename on one side
176 - rename on one side
177 - unrelated change on the other side
177 - unrelated change on the other side
178
178
179 $ hg up 'desc("b-1")'
179 $ hg up 'desc("b-1")'
180 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
180 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
181 $ hg merge 'desc("a-2")'
181 $ hg merge 'desc("a-2")'
182 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
182 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
183 (branch merge, don't forget to commit)
183 (branch merge, don't forget to commit)
184 $ hg ci -m 'mBAm-0 simple merge - one way'
184 $ hg ci -m 'mBAm-0 simple merge - one way'
185 $ hg up 'desc("a-2")'
185 $ hg up 'desc("a-2")'
186 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
186 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
187 $ hg merge 'desc("b-1")'
187 $ hg merge 'desc("b-1")'
188 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
188 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
189 (branch merge, don't forget to commit)
189 (branch merge, don't forget to commit)
190 $ hg ci -m 'mABm-0 simple merge - the other way'
190 $ hg ci -m 'mABm-0 simple merge - the other way'
191 created new head
191 created new head
192 $ hg log -G --rev '::(desc("mABm")+desc("mBAm"))'
192 $ hg log -G --rev '::(desc("mABm")+desc("mBAm"))'
193 @ 12 mABm-0 simple merge - the other way
193 @ 12 mABm-0 simple merge - the other way
194 |\
194 |\
195 +---o 11 mBAm-0 simple merge - one way
195 +---o 11 mBAm-0 simple merge - one way
196 | |/
196 | |/
197 | o 5 b-1: b update
197 | o 5 b-1: b update
198 | |
198 | |
199 o | 4 a-2: e -move-> f
199 o | 4 a-2: e -move-> f
200 | |
200 | |
201 o | 3 a-1: d -move-> e
201 o | 3 a-1: d -move-> e
202 |/
202 |/
203 o 2 i-2: c -move-> d
203 o 2 i-2: c -move-> d
204 |
204 |
205 o 1 i-1: a -move-> c
205 o 1 i-1: a -move-> c
206 |
206 |
207 o 0 i-0 initial commit: a b h
207 o 0 i-0 initial commit: a b h
208
208
209
209
210
210
211 merging with the side having a delete
211 merging with the side having a delete
212 -------------------------------------
212 -------------------------------------
213
213
214 case summary:
214 case summary:
215 - one with change to an unrelated file
215 - one with change to an unrelated file
216 - one deleting the change
216 - one deleting the change
217 and recreate an unrelated file after the merge
217 and recreate an unrelated file after the merge
218
218
219 $ hg up 'desc("b-1")'
219 $ hg up 'desc("b-1")'
220 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
220 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
221 $ hg merge 'desc("c-1")'
221 $ hg merge 'desc("c-1")'
222 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
222 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
223 (branch merge, don't forget to commit)
223 (branch merge, don't forget to commit)
224 $ hg ci -m 'mBCm-0 simple merge - one way'
224 $ hg ci -m 'mBCm-0 simple merge - one way'
225 $ echo bar > d
225 $ echo bar > d
226 $ hg add d
226 $ hg add d
227 $ hg ci -m 'mBCm-1 re-add d'
227 $ hg ci -m 'mBCm-1 re-add d'
228 $ hg up 'desc("c-1")'
228 $ hg up 'desc("c-1")'
229 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
229 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
230 $ hg merge 'desc("b-1")'
230 $ hg merge 'desc("b-1")'
231 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
231 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
232 (branch merge, don't forget to commit)
232 (branch merge, don't forget to commit)
233 $ hg ci -m 'mCBm-0 simple merge - the other way'
233 $ hg ci -m 'mCBm-0 simple merge - the other way'
234 created new head
234 created new head
235 $ echo bar > d
235 $ echo bar > d
236 $ hg add d
236 $ hg add d
237 $ hg ci -m 'mCBm-1 re-add d'
237 $ hg ci -m 'mCBm-1 re-add d'
238 $ hg log -G --rev '::(desc("mCBm")+desc("mBCm"))'
238 $ hg log -G --rev '::(desc("mCBm")+desc("mBCm"))'
239 @ 16 mCBm-1 re-add d
239 @ 16 mCBm-1 re-add d
240 |
240 |
241 o 15 mCBm-0 simple merge - the other way
241 o 15 mCBm-0 simple merge - the other way
242 |\
242 |\
243 | | o 14 mBCm-1 re-add d
243 | | o 14 mBCm-1 re-add d
244 | | |
244 | | |
245 +---o 13 mBCm-0 simple merge - one way
245 +---o 13 mBCm-0 simple merge - one way
246 | |/
246 | |/
247 | o 6 c-1 delete d
247 | o 6 c-1 delete d
248 | |
248 | |
249 o | 5 b-1: b update
249 o | 5 b-1: b update
250 |/
250 |/
251 o 2 i-2: c -move-> d
251 o 2 i-2: c -move-> d
252 |
252 |
253 o 1 i-1: a -move-> c
253 o 1 i-1: a -move-> c
254 |
254 |
255 o 0 i-0 initial commit: a b h
255 o 0 i-0 initial commit: a b h
256
256
257
257
258 Comparing with a merge re-adding the file afterward
258 Comparing with a merge re-adding the file afterward
259 ---------------------------------------------------
259 ---------------------------------------------------
260
260
261 Merge:
261 Merge:
262 - one with change to an unrelated file
262 - one with change to an unrelated file
263 - one deleting and recreating the change
263 - one deleting and recreating the change
264
264
265 $ hg up 'desc("b-1")'
265 $ hg up 'desc("b-1")'
266 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
266 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
267 $ hg merge 'desc("d-2")'
267 $ hg merge 'desc("d-2")'
268 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
268 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
269 (branch merge, don't forget to commit)
269 (branch merge, don't forget to commit)
270 $ hg ci -m 'mBDm-0 simple merge - one way'
270 $ hg ci -m 'mBDm-0 simple merge - one way'
271 $ hg up 'desc("d-2")'
271 $ hg up 'desc("d-2")'
272 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
272 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
273 $ hg merge 'desc("b-1")'
273 $ hg merge 'desc("b-1")'
274 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
274 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
275 (branch merge, don't forget to commit)
275 (branch merge, don't forget to commit)
276 $ hg ci -m 'mDBm-0 simple merge - the other way'
276 $ hg ci -m 'mDBm-0 simple merge - the other way'
277 created new head
277 created new head
278 $ hg log -G --rev '::(desc("mDBm")+desc("mBDm"))'
278 $ hg log -G --rev '::(desc("mDBm")+desc("mBDm"))'
279 @ 18 mDBm-0 simple merge - the other way
279 @ 18 mDBm-0 simple merge - the other way
280 |\
280 |\
281 +---o 17 mBDm-0 simple merge - one way
281 +---o 17 mBDm-0 simple merge - one way
282 | |/
282 | |/
283 | o 8 d-2 re-add d
283 | o 8 d-2 re-add d
284 | |
284 | |
285 | o 7 d-1 delete d
285 | o 7 d-1 delete d
286 | |
286 | |
287 o | 5 b-1: b update
287 o | 5 b-1: b update
288 |/
288 |/
289 o 2 i-2: c -move-> d
289 o 2 i-2: c -move-> d
290 |
290 |
291 o 1 i-1: a -move-> c
291 o 1 i-1: a -move-> c
292 |
292 |
293 o 0 i-0 initial commit: a b h
293 o 0 i-0 initial commit: a b h
294
294
295
295
296
296
297 Comparing with a merge with colliding rename
297 Comparing with a merge with colliding rename
298 --------------------------------------------
298 --------------------------------------------
299
299
300 - the "e-" branch renaming b to f (through 'g')
300 - the "e-" branch renaming b to f (through 'g')
301 - the "a-" branch renaming d to f (through e)
301 - the "a-" branch renaming d to f (through e)
302
302
303 $ hg up 'desc("a-2")'
303 $ hg up 'desc("a-2")'
304 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
304 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
305 $ hg merge 'desc("e-2")' --tool :union
305 $ hg merge 'desc("e-2")' --tool :union
306 merging f
306 merging f
307 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
307 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
308 (branch merge, don't forget to commit)
308 (branch merge, don't forget to commit)
309 $ hg ci -m 'mAEm-0 simple merge - one way'
309 $ hg ci -m 'mAEm-0 simple merge - one way'
310 $ hg up 'desc("e-2")'
310 $ hg up 'desc("e-2")'
311 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
311 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
312 $ hg merge 'desc("a-2")' --tool :union
312 $ hg merge 'desc("a-2")' --tool :union
313 merging f
313 merging f
314 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
314 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
315 (branch merge, don't forget to commit)
315 (branch merge, don't forget to commit)
316 $ hg ci -m 'mEAm-0 simple merge - the other way'
316 $ hg ci -m 'mEAm-0 simple merge - the other way'
317 created new head
317 created new head
318 $ hg log -G --rev '::(desc("mAEm")+desc("mEAm"))'
318 $ hg log -G --rev '::(desc("mAEm")+desc("mEAm"))'
319 @ 20 mEAm-0 simple merge - the other way
319 @ 20 mEAm-0 simple merge - the other way
320 |\
320 |\
321 +---o 19 mAEm-0 simple merge - one way
321 +---o 19 mAEm-0 simple merge - one way
322 | |/
322 | |/
323 | o 10 e-2 g -move-> f
323 | o 10 e-2 g -move-> f
324 | |
324 | |
325 | o 9 e-1 b -move-> g
325 | o 9 e-1 b -move-> g
326 | |
326 | |
327 o | 4 a-2: e -move-> f
327 o | 4 a-2: e -move-> f
328 | |
328 | |
329 o | 3 a-1: d -move-> e
329 o | 3 a-1: d -move-> e
330 |/
330 |/
331 o 2 i-2: c -move-> d
331 o 2 i-2: c -move-> d
332 |
332 |
333 o 1 i-1: a -move-> c
333 o 1 i-1: a -move-> c
334 |
334 |
335 o 0 i-0 initial commit: a b h
335 o 0 i-0 initial commit: a b h
336
336
337
337
338
338
339 Merge:
339 Merge:
340 - one with change to an unrelated file (b)
340 - one with change to an unrelated file (b)
341 - one overwriting a file (d) with a rename (from h to i to d)
341 - one overwriting a file (d) with a rename (from h to i to d)
342
342
343 $ hg up 'desc("i-2")'
343 $ hg up 'desc("i-2")'
344 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
344 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
345 $ hg mv h i
345 $ hg mv h i
346 $ hg commit -m "f-1: rename h -> i"
346 $ hg commit -m "f-1: rename h -> i"
347 created new head
347 created new head
348 $ hg mv --force i d
348 $ hg mv --force i d
349 $ hg commit -m "f-2: rename i -> d"
349 $ hg commit -m "f-2: rename i -> d"
350 $ hg debugindex d
350 $ hg debugindex d
351 rev linkrev nodeid p1 p2
351 rev linkrev nodeid p1 p2
352 0 2 169be882533b 000000000000 000000000000 (no-changeset !)
352 0 2 169be882533b 000000000000 000000000000 (no-changeset !)
353 0 2 b789fdd96dc2 000000000000 000000000000 (changeset !)
353 0 2 b789fdd96dc2 000000000000 000000000000 (changeset !)
354 1 8 b004912a8510 000000000000 000000000000
354 1 8 b004912a8510 000000000000 000000000000
355 2 22 4a067cf8965d 000000000000 000000000000 (no-changeset !)
355 2 22 4a067cf8965d 000000000000 000000000000 (no-changeset !)
356 2 22 fe6f8b4f507f 000000000000 000000000000 (changeset !)
356 2 22 fe6f8b4f507f 000000000000 000000000000 (changeset !)
357 $ hg up 'desc("b-1")'
357 $ hg up 'desc("b-1")'
358 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
358 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
359 $ hg merge 'desc("f-2")'
359 $ hg merge 'desc("f-2")'
360 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
360 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
361 (branch merge, don't forget to commit)
361 (branch merge, don't forget to commit)
362 $ hg ci -m 'mBFm-0 simple merge - one way'
362 $ hg ci -m 'mBFm-0 simple merge - one way'
363 $ hg up 'desc("f-2")'
363 $ hg up 'desc("f-2")'
364 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
364 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
365 $ hg merge 'desc("b-1")'
365 $ hg merge 'desc("b-1")'
366 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
366 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
367 (branch merge, don't forget to commit)
367 (branch merge, don't forget to commit)
368 $ hg ci -m 'mFBm-0 simple merge - the other way'
368 $ hg ci -m 'mFBm-0 simple merge - the other way'
369 created new head
369 created new head
370 $ hg log -G --rev '::(desc("mBFm")+desc("mFBm"))'
370 $ hg log -G --rev '::(desc("mBFm")+desc("mFBm"))'
371 @ 24 mFBm-0 simple merge - the other way
371 @ 24 mFBm-0 simple merge - the other way
372 |\
372 |\
373 +---o 23 mBFm-0 simple merge - one way
373 +---o 23 mBFm-0 simple merge - one way
374 | |/
374 | |/
375 | o 22 f-2: rename i -> d
375 | o 22 f-2: rename i -> d
376 | |
376 | |
377 | o 21 f-1: rename h -> i
377 | o 21 f-1: rename h -> i
378 | |
378 | |
379 o | 5 b-1: b update
379 o | 5 b-1: b update
380 |/
380 |/
381 o 2 i-2: c -move-> d
381 o 2 i-2: c -move-> d
382 |
382 |
383 o 1 i-1: a -move-> c
383 o 1 i-1: a -move-> c
384 |
384 |
385 o 0 i-0 initial commit: a b h
385 o 0 i-0 initial commit: a b h
386
386
387
387
388
388
389 Merge:
389 Merge:
390 - one with change to a file
390 - one with change to a file
391 - one deleting and recreating the file
391 - one deleting and recreating the file
392
392
393 Unlike in the 'BD/DB' cases, an actual merge happened here. So we should
393 Unlike in the 'BD/DB' cases, an actual merge happened here. So we should
394 consider history and rename on both branch of the merge.
394 consider history and rename on both branch of the merge.
395
395
396 $ hg up 'desc("i-2")'
396 $ hg up 'desc("i-2")'
397 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
397 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
398 $ echo "some update" >> d
398 $ echo "some update" >> d
399 $ hg commit -m "g-1: update d"
399 $ hg commit -m "g-1: update d"
400 created new head
400 created new head
401 $ hg up 'desc("d-2")'
401 $ hg up 'desc("d-2")'
402 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
402 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
403 $ hg merge 'desc("g-1")' --tool :union
403 $ hg merge 'desc("g-1")' --tool :union
404 merging d
404 merging d
405 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
405 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
406 (branch merge, don't forget to commit)
406 (branch merge, don't forget to commit)
407 $ hg ci -m 'mDGm-0 simple merge - one way'
407 $ hg ci -m 'mDGm-0 simple merge - one way'
408 $ hg up 'desc("g-1")'
408 $ hg up 'desc("g-1")'
409 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
409 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
410 $ hg merge 'desc("d-2")' --tool :union
410 $ hg merge 'desc("d-2")' --tool :union
411 merging d
411 merging d
412 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
412 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
413 (branch merge, don't forget to commit)
413 (branch merge, don't forget to commit)
414 $ hg ci -m 'mGDm-0 simple merge - the other way'
414 $ hg ci -m 'mGDm-0 simple merge - the other way'
415 created new head
415 created new head
416 $ hg log -G --rev '::(desc("mDGm")+desc("mGDm"))'
416 $ hg log -G --rev '::(desc("mDGm")+desc("mGDm"))'
417 @ 27 mGDm-0 simple merge - the other way
417 @ 27 mGDm-0 simple merge - the other way
418 |\
418 |\
419 +---o 26 mDGm-0 simple merge - one way
419 +---o 26 mDGm-0 simple merge - one way
420 | |/
420 | |/
421 | o 25 g-1: update d
421 | o 25 g-1: update d
422 | |
422 | |
423 o | 8 d-2 re-add d
423 o | 8 d-2 re-add d
424 | |
424 | |
425 o | 7 d-1 delete d
425 o | 7 d-1 delete d
426 |/
426 |/
427 o 2 i-2: c -move-> d
427 o 2 i-2: c -move-> d
428 |
428 |
429 o 1 i-1: a -move-> c
429 o 1 i-1: a -move-> c
430 |
430 |
431 o 0 i-0 initial commit: a b h
431 o 0 i-0 initial commit: a b h
432
432
433
433
434
434
435 Merge:
435 Merge:
436 - one with change to a file (d)
436 - one with change to a file (d)
437 - one overwriting that file with a rename (from h to i, to d)
437 - one overwriting that file with a rename (from h to i, to d)
438
438
439 This case is similar to BF/FB, but an actual merge happens, so both side of the
439 This case is similar to BF/FB, but an actual merge happens, so both side of the
440 history are relevant.
440 history are relevant.
441
441
442 Note:
442 Note:
443 | In this case, the merge get conflicting information since on one side we have
443 | In this case, the merge get conflicting information since on one side we have
444 | "a -> c -> d". and one the other one we have "h -> i -> d".
444 | "a -> c -> d". and one the other one we have "h -> i -> d".
445 |
445 |
446 | The current code arbitrarily pick one side
446 | The current code arbitrarily pick one side
447
447
448 $ hg up 'desc("f-2")'
448 $ hg up 'desc("f-2")'
449 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
449 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
450 $ hg merge 'desc("g-1")' --tool :union
450 $ hg merge 'desc("g-1")' --tool :union
451 merging d
451 merging d
452 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
452 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
453 (branch merge, don't forget to commit)
453 (branch merge, don't forget to commit)
454 $ hg ci -m 'mFGm-0 simple merge - one way'
454 $ hg ci -m 'mFGm-0 simple merge - one way'
455 created new head
455 created new head
456 $ hg up 'desc("g-1")'
456 $ hg up 'desc("g-1")'
457 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
457 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
458 $ hg merge 'desc("f-2")' --tool :union
458 $ hg merge 'desc("f-2")' --tool :union
459 merging d
459 merging d
460 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
460 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
461 (branch merge, don't forget to commit)
461 (branch merge, don't forget to commit)
462 $ hg ci -m 'mGFm-0 simple merge - the other way'
462 $ hg ci -m 'mGFm-0 simple merge - the other way'
463 created new head
463 created new head
464 $ hg log -G --rev '::(desc("mGFm")+desc("mFGm"))'
464 $ hg log -G --rev '::(desc("mGFm")+desc("mFGm"))'
465 @ 29 mGFm-0 simple merge - the other way
465 @ 29 mGFm-0 simple merge - the other way
466 |\
466 |\
467 +---o 28 mFGm-0 simple merge - one way
467 +---o 28 mFGm-0 simple merge - one way
468 | |/
468 | |/
469 | o 25 g-1: update d
469 | o 25 g-1: update d
470 | |
470 | |
471 o | 22 f-2: rename i -> d
471 o | 22 f-2: rename i -> d
472 | |
472 | |
473 o | 21 f-1: rename h -> i
473 o | 21 f-1: rename h -> i
474 |/
474 |/
475 o 2 i-2: c -move-> d
475 o 2 i-2: c -move-> d
476 |
476 |
477 o 1 i-1: a -move-> c
477 o 1 i-1: a -move-> c
478 |
478 |
479 o 0 i-0 initial commit: a b h
479 o 0 i-0 initial commit: a b h
480
480
481
481
482
482
483 Comparing with merging with a deletion (and keeping the file)
483 Comparing with merging with a deletion (and keeping the file)
484 -------------------------------------------------------------
484 -------------------------------------------------------------
485
485
486 Merge:
486 Merge:
487 - one removing a file (d)
487 - one removing a file (d)
488 - one updating that file
488 - one updating that file
489 - the merge keep the modified version of the file (canceling the delete)
489 - the merge keep the modified version of the file (canceling the delete)
490
490
491 In this case, the file keep on living after the merge. So we should not drop its
491 In this case, the file keep on living after the merge. So we should not drop its
492 copy tracing chain.
492 copy tracing chain.
493
493
494 $ hg up 'desc("c-1")'
494 $ hg up 'desc("c-1")'
495 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
495 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
496 $ hg merge 'desc("g-1")'
496 $ hg merge 'desc("g-1")'
497 file 'd' was deleted in local [working copy] but was modified in other [merge rev].
497 file 'd' was deleted in local [working copy] but was modified in other [merge rev].
498 You can use (c)hanged version, leave (d)eleted, or leave (u)nresolved.
498 You can use (c)hanged version, leave (d)eleted, or leave (u)nresolved.
499 What do you want to do? u
499 What do you want to do? u
500 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
500 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
501 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
501 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
502 [1]
502 [1]
503 $ hg resolve -t :other d
503 $ hg resolve -t :other d
504 (no more unresolved files)
504 (no more unresolved files)
505 $ hg ci -m "mCGm-0"
505 $ hg ci -m "mCGm-0"
506 created new head
506 created new head
507
507
508 $ hg up 'desc("g-1")'
508 $ hg up 'desc("g-1")'
509 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
509 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
510 $ hg merge 'desc("c-1")'
510 $ hg merge 'desc("c-1")'
511 file 'd' was deleted in other [merge rev] but was modified in local [working copy].
511 file 'd' was deleted in other [merge rev] but was modified in local [working copy].
512 You can use (c)hanged version, (d)elete, or leave (u)nresolved.
512 You can use (c)hanged version, (d)elete, or leave (u)nresolved.
513 What do you want to do? u
513 What do you want to do? u
514 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
514 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
515 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
515 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
516 [1]
516 [1]
517 $ hg resolve -t :local d
517 $ hg resolve -t :local d
518 (no more unresolved files)
518 (no more unresolved files)
519 $ hg ci -m "mGCm-0"
519 $ hg ci -m "mGCm-0"
520 created new head
520 created new head
521
521
522 $ hg log -G --rev '::(desc("mCGm")+desc("mGCm"))'
522 $ hg log -G --rev '::(desc("mCGm")+desc("mGCm"))'
523 @ 31 mGCm-0
523 @ 31 mGCm-0
524 |\
524 |\
525 +---o 30 mCGm-0
525 +---o 30 mCGm-0
526 | |/
526 | |/
527 | o 25 g-1: update d
527 | o 25 g-1: update d
528 | |
528 | |
529 o | 6 c-1 delete d
529 o | 6 c-1 delete d
530 |/
530 |/
531 o 2 i-2: c -move-> d
531 o 2 i-2: c -move-> d
532 |
532 |
533 o 1 i-1: a -move-> c
533 o 1 i-1: a -move-> c
534 |
534 |
535 o 0 i-0 initial commit: a b h
535 o 0 i-0 initial commit: a b h
536
536
537
537
538
538
539
539
540 Comparing with merge restoring an untouched deleted file
540 Comparing with merge restoring an untouched deleted file
541 --------------------------------------------------------
541 --------------------------------------------------------
542
542
543 Merge:
543 Merge:
544 - one removing a file (d)
544 - one removing a file (d)
545 - one leaving the file untouched
545 - one leaving the file untouched
546 - the merge actively restore the file to the same content.
546 - the merge actively restore the file to the same content.
547
547
548 In this case, the file keep on living after the merge. So we should not drop its
548 In this case, the file keep on living after the merge. So we should not drop its
549 copy tracing chain.
549 copy tracing chain.
550
550
551 $ hg up 'desc("c-1")'
551 $ hg up 'desc("c-1")'
552 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
552 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
553 $ hg merge 'desc("b-1")'
553 $ hg merge 'desc("b-1")'
554 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
554 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
555 (branch merge, don't forget to commit)
555 (branch merge, don't forget to commit)
556 $ hg revert --rev 'desc("b-1")' d
556 $ hg revert --rev 'desc("b-1")' d
557 $ hg ci -m "mCB-revert-m-0"
557 $ hg ci -m "mCB-revert-m-0"
558 created new head
558 created new head
559
559
560 $ hg up 'desc("b-1")'
560 $ hg up 'desc("b-1")'
561 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
561 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
562 $ hg merge 'desc("c-1")'
562 $ hg merge 'desc("c-1")'
563 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
563 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
564 (branch merge, don't forget to commit)
564 (branch merge, don't forget to commit)
565 $ hg revert --rev 'desc("b-1")' d
565 $ hg revert --rev 'desc("b-1")' d
566 $ hg ci -m "mBC-revert-m-0"
566 $ hg ci -m "mBC-revert-m-0"
567 created new head
567 created new head
568
568
569 $ hg log -G --rev '::(desc("mCB-revert-m")+desc("mBC-revert-m"))'
569 $ hg log -G --rev '::(desc("mCB-revert-m")+desc("mBC-revert-m"))'
570 @ 33 mBC-revert-m-0
570 @ 33 mBC-revert-m-0
571 |\
571 |\
572 +---o 32 mCB-revert-m-0
572 +---o 32 mCB-revert-m-0
573 | |/
573 | |/
574 | o 6 c-1 delete d
574 | o 6 c-1 delete d
575 | |
575 | |
576 o | 5 b-1: b update
576 o | 5 b-1: b update
577 |/
577 |/
578 o 2 i-2: c -move-> d
578 o 2 i-2: c -move-> d
579 |
579 |
580 o 1 i-1: a -move-> c
580 o 1 i-1: a -move-> c
581 |
581 |
582 o 0 i-0 initial commit: a b h
582 o 0 i-0 initial commit: a b h
583
583
584
584
585
585
586 $ hg up null --quiet
586 $ hg up null --quiet
587
587
588 Merging a branch where a rename was deleted with a branch where the same file was renamed
588 Merging a branch where a rename was deleted with a branch where the same file was renamed
589 ------------------------------------------------------------------------------------------
589 ------------------------------------------------------------------------------------------
590
590
591 Create a "conflicting" merge where `d` get removed on one branch before its
591 Create a "conflicting" merge where `d` get removed on one branch before its
592 rename information actually conflict with the other branch.
592 rename information actually conflict with the other branch.
593
593
594 (the copy information from the branch that was not deleted should win).
594 (the copy information from the branch that was not deleted should win).
595
595
596 $ hg up 'desc("i-0")'
596 $ hg up 'desc("i-0")'
597 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
597 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
598 $ hg mv b d
598 $ hg mv b d
599 $ hg ci -m "h-1: b -(move)-> d"
599 $ hg ci -m "h-1: b -(move)-> d"
600 created new head
600 created new head
601
601
602 $ hg up 'desc("c-1")'
602 $ hg up 'desc("c-1")'
603 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
603 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
604 $ hg merge 'desc("h-1")'
604 $ hg merge 'desc("h-1")'
605 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
605 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
606 (branch merge, don't forget to commit)
606 (branch merge, don't forget to commit)
607 $ hg ci -m "mCH-delete-before-conflict-m-0"
607 $ hg ci -m "mCH-delete-before-conflict-m-0"
608
608
609 $ hg up 'desc("h-1")'
609 $ hg up 'desc("h-1")'
610 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
610 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
611 $ hg merge 'desc("c-1")'
611 $ hg merge 'desc("c-1")'
612 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
612 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
613 (branch merge, don't forget to commit)
613 (branch merge, don't forget to commit)
614 $ hg ci -m "mHC-delete-before-conflict-m-0"
614 $ hg ci -m "mHC-delete-before-conflict-m-0"
615 created new head
615 created new head
616 $ hg log -G --rev '::(desc("mCH-delete-before-conflict-m")+desc("mHC-delete-before-conflict-m"))'
616 $ hg log -G --rev '::(desc("mCH-delete-before-conflict-m")+desc("mHC-delete-before-conflict-m"))'
617 @ 36 mHC-delete-before-conflict-m-0
617 @ 36 mHC-delete-before-conflict-m-0
618 |\
618 |\
619 +---o 35 mCH-delete-before-conflict-m-0
619 +---o 35 mCH-delete-before-conflict-m-0
620 | |/
620 | |/
621 | o 34 h-1: b -(move)-> d
621 | o 34 h-1: b -(move)-> d
622 | |
622 | |
623 o | 6 c-1 delete d
623 o | 6 c-1 delete d
624 | |
624 | |
625 o | 2 i-2: c -move-> d
625 o | 2 i-2: c -move-> d
626 | |
626 | |
627 o | 1 i-1: a -move-> c
627 o | 1 i-1: a -move-> c
628 |/
628 |/
629 o 0 i-0 initial commit: a b h
629 o 0 i-0 initial commit: a b h
630
630
631
631
632
632
633 Test that sidedata computations during upgrades are correct
633 Test that sidedata computations during upgrades are correct
634 ===========================================================
634 ===========================================================
635
635
636 We upgrade a repository that is not using sidedata (the filelog case) and
636 We upgrade a repository that is not using sidedata (the filelog case) and
637 check that the same side data have been generated as if they were computed at
637 check that the same side data have been generated as if they were computed at
638 commit time.
638 commit time.
639
639
640
640
641 #if upgraded
641 #if upgraded
642 $ cat >> $HGRCPATH << EOF
642 $ cat >> $HGRCPATH << EOF
643 > [format]
643 > [format]
644 > exp-use-side-data = yes
644 > exp-use-side-data = yes
645 > exp-use-copies-side-data-changeset = yes
645 > exp-use-copies-side-data-changeset = yes
646 > EOF
646 > EOF
647 $ hg debugformat -v
647 $ hg debugformat -v
648 format-variant repo config default
648 format-variant repo config default
649 fncache: yes yes yes
649 fncache: yes yes yes
650 dotencode: yes yes yes
650 dotencode: yes yes yes
651 generaldelta: yes yes yes
651 generaldelta: yes yes yes
652 exp-sharesafe: no no no
652 exp-sharesafe: no no no
653 sparserevlog: yes yes yes
653 sparserevlog: yes yes yes
654 sidedata: no yes no
654 sidedata: no yes no
655 persistent-nodemap: no no no
655 persistent-nodemap: no no no
656 copies-sdc: no yes no
656 copies-sdc: no yes no
657 plain-cl-delta: yes yes yes
657 plain-cl-delta: yes yes yes
658 compression: * (glob)
658 compression: * (glob)
659 compression-level: default default default
659 compression-level: default default default
660 $ hg debugupgraderepo --run --quiet
660 $ hg debugupgraderepo --run --quiet
661 upgrade will perform the following actions:
661 upgrade will perform the following actions:
662
662
663 requirements
663 requirements
664 preserved: * (glob)
664 preserved: * (glob)
665 added: exp-copies-sidedata-changeset, exp-sidedata-flag
665 added: exp-copies-sidedata-changeset, exp-sidedata-flag
666
666
667 processed revlogs:
667 processed revlogs:
668 - all-filelogs
668 - all-filelogs
669 - changelog
669 - changelog
670 - manifest
670 - manifest
671
671
672 #endif
672 #endif
673
673
674
674
675 #if no-compatibility no-filelog no-changeset
675 #if no-compatibility no-filelog no-changeset
676
676
677 $ for rev in `hg log --rev 'all()' -T '{rev}\n'`; do
677 $ for rev in `hg log --rev 'all()' -T '{rev}\n'`; do
678 > echo "##### revision $rev #####"
678 > echo "##### revision $rev #####"
679 > hg debugsidedata -c -v -- $rev
679 > hg debugsidedata -c -v -- $rev
680 > hg debugchangedfiles $rev
680 > hg debugchangedfiles $rev
681 > done
681 > done
682 ##### revision 0 #####
682 ##### revision 0 #####
683 1 sidedata entries
683 1 sidedata entries
684 entry-0014 size 34
684 entry-0014 size 34
685 '\x00\x00\x00\x03\x04\x00\x00\x00\x01\x00\x00\x00\x00\x04\x00\x00\x00\x02\x00\x00\x00\x00\x04\x00\x00\x00\x03\x00\x00\x00\x00abh'
685 '\x00\x00\x00\x03\x04\x00\x00\x00\x01\x00\x00\x00\x00\x04\x00\x00\x00\x02\x00\x00\x00\x00\x04\x00\x00\x00\x03\x00\x00\x00\x00abh'
686 added : a, ;
686 added : a, ;
687 added : b, ;
687 added : b, ;
688 added : h, ;
688 added : h, ;
689 ##### revision 1 #####
689 ##### revision 1 #####
690 1 sidedata entries
690 1 sidedata entries
691 entry-0014 size 24
691 entry-0014 size 24
692 '\x00\x00\x00\x02\x0c\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00ac'
692 '\x00\x00\x00\x02\x0c\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00ac'
693 removed : a, ;
693 removed : a, ;
694 added p1: c, a;
694 added p1: c, a;
695 ##### revision 2 #####
695 ##### revision 2 #####
696 1 sidedata entries
696 1 sidedata entries
697 entry-0014 size 24
697 entry-0014 size 24
698 '\x00\x00\x00\x02\x0c\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00cd'
698 '\x00\x00\x00\x02\x0c\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00cd'
699 removed : c, ;
699 removed : c, ;
700 added p1: d, c;
700 added p1: d, c;
701 ##### revision 3 #####
701 ##### revision 3 #####
702 1 sidedata entries
702 1 sidedata entries
703 entry-0014 size 24
703 entry-0014 size 24
704 '\x00\x00\x00\x02\x0c\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00de'
704 '\x00\x00\x00\x02\x0c\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00de'
705 removed : d, ;
705 removed : d, ;
706 added p1: e, d;
706 added p1: e, d;
707 ##### revision 4 #####
707 ##### revision 4 #####
708 1 sidedata entries
708 1 sidedata entries
709 entry-0014 size 24
709 entry-0014 size 24
710 '\x00\x00\x00\x02\x0c\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00ef'
710 '\x00\x00\x00\x02\x0c\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00ef'
711 removed : e, ;
711 removed : e, ;
712 added p1: f, e;
712 added p1: f, e;
713 ##### revision 5 #####
713 ##### revision 5 #####
714 1 sidedata entries
714 1 sidedata entries
715 entry-0014 size 14
715 entry-0014 size 14
716 '\x00\x00\x00\x01\x14\x00\x00\x00\x01\x00\x00\x00\x00b'
716 '\x00\x00\x00\x01\x14\x00\x00\x00\x01\x00\x00\x00\x00b'
717 touched : b, ;
717 touched : b, ;
718 ##### revision 6 #####
718 ##### revision 6 #####
719 1 sidedata entries
719 1 sidedata entries
720 entry-0014 size 14
720 entry-0014 size 14
721 '\x00\x00\x00\x01\x0c\x00\x00\x00\x01\x00\x00\x00\x00d'
721 '\x00\x00\x00\x01\x0c\x00\x00\x00\x01\x00\x00\x00\x00d'
722 removed : d, ;
722 removed : d, ;
723 ##### revision 7 #####
723 ##### revision 7 #####
724 1 sidedata entries
724 1 sidedata entries
725 entry-0014 size 14
725 entry-0014 size 14
726 '\x00\x00\x00\x01\x0c\x00\x00\x00\x01\x00\x00\x00\x00d'
726 '\x00\x00\x00\x01\x0c\x00\x00\x00\x01\x00\x00\x00\x00d'
727 removed : d, ;
727 removed : d, ;
728 ##### revision 8 #####
728 ##### revision 8 #####
729 1 sidedata entries
729 1 sidedata entries
730 entry-0014 size 14
730 entry-0014 size 14
731 '\x00\x00\x00\x01\x04\x00\x00\x00\x01\x00\x00\x00\x00d'
731 '\x00\x00\x00\x01\x04\x00\x00\x00\x01\x00\x00\x00\x00d'
732 added : d, ;
732 added : d, ;
733 ##### revision 9 #####
733 ##### revision 9 #####
734 1 sidedata entries
734 1 sidedata entries
735 entry-0014 size 24
735 entry-0014 size 24
736 '\x00\x00\x00\x02\x0c\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00bg'
736 '\x00\x00\x00\x02\x0c\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00bg'
737 removed : b, ;
737 removed : b, ;
738 added p1: g, b;
738 added p1: g, b;
739 ##### revision 10 #####
739 ##### revision 10 #####
740 1 sidedata entries
740 1 sidedata entries
741 entry-0014 size 24
741 entry-0014 size 24
742 '\x00\x00\x00\x02\x06\x00\x00\x00\x01\x00\x00\x00\x01\x0c\x00\x00\x00\x02\x00\x00\x00\x00fg'
742 '\x00\x00\x00\x02\x06\x00\x00\x00\x01\x00\x00\x00\x01\x0c\x00\x00\x00\x02\x00\x00\x00\x00fg'
743 added p1: f, g;
743 added p1: f, g;
744 removed : g, ;
744 removed : g, ;
745 ##### revision 11 #####
745 ##### revision 11 #####
746 1 sidedata entries
746 1 sidedata entries
747 entry-0014 size 4
747 entry-0014 size 4
748 '\x00\x00\x00\x00'
748 '\x00\x00\x00\x00'
749 ##### revision 12 #####
749 ##### revision 12 #####
750 1 sidedata entries
750 1 sidedata entries
751 entry-0014 size 4
751 entry-0014 size 4
752 '\x00\x00\x00\x00'
752 '\x00\x00\x00\x00'
753 ##### revision 13 #####
753 ##### revision 13 #####
754 1 sidedata entries
754 1 sidedata entries
755 entry-0014 size 4
755 entry-0014 size 4
756 '\x00\x00\x00\x00'
756 '\x00\x00\x00\x00'
757 ##### revision 14 #####
757 ##### revision 14 #####
758 1 sidedata entries
758 1 sidedata entries
759 entry-0014 size 14
759 entry-0014 size 14
760 '\x00\x00\x00\x01\x04\x00\x00\x00\x01\x00\x00\x00\x00d'
760 '\x00\x00\x00\x01\x04\x00\x00\x00\x01\x00\x00\x00\x00d'
761 added : d, ;
761 added : d, ;
762 ##### revision 15 #####
762 ##### revision 15 #####
763 1 sidedata entries
763 1 sidedata entries
764 entry-0014 size 4
764 entry-0014 size 4
765 '\x00\x00\x00\x00'
765 '\x00\x00\x00\x00'
766 ##### revision 16 #####
766 ##### revision 16 #####
767 1 sidedata entries
767 1 sidedata entries
768 entry-0014 size 14
768 entry-0014 size 14
769 '\x00\x00\x00\x01\x04\x00\x00\x00\x01\x00\x00\x00\x00d'
769 '\x00\x00\x00\x01\x04\x00\x00\x00\x01\x00\x00\x00\x00d'
770 added : d, ;
770 added : d, ;
771 ##### revision 17 #####
771 ##### revision 17 #####
772 1 sidedata entries
772 1 sidedata entries
773 entry-0014 size 4
773 entry-0014 size 4
774 '\x00\x00\x00\x00'
774 '\x00\x00\x00\x00'
775 ##### revision 18 #####
775 ##### revision 18 #####
776 1 sidedata entries
776 1 sidedata entries
777 entry-0014 size 4
777 entry-0014 size 4
778 '\x00\x00\x00\x00'
778 '\x00\x00\x00\x00'
779 ##### revision 19 #####
779 ##### revision 19 #####
780 1 sidedata entries
780 1 sidedata entries
781 entry-0014 size 14
781 entry-0014 size 14
782 '\x00\x00\x00\x01\x08\x00\x00\x00\x01\x00\x00\x00\x00f'
782 '\x00\x00\x00\x01\x08\x00\x00\x00\x01\x00\x00\x00\x00f'
783 merged : f, ;
783 merged : f, ;
784 ##### revision 20 #####
784 ##### revision 20 #####
785 1 sidedata entries
785 1 sidedata entries
786 entry-0014 size 14
786 entry-0014 size 14
787 '\x00\x00\x00\x01\x08\x00\x00\x00\x01\x00\x00\x00\x00f'
787 '\x00\x00\x00\x01\x08\x00\x00\x00\x01\x00\x00\x00\x00f'
788 merged : f, ;
788 merged : f, ;
789 ##### revision 21 #####
789 ##### revision 21 #####
790 1 sidedata entries
790 1 sidedata entries
791 entry-0014 size 24
791 entry-0014 size 24
792 '\x00\x00\x00\x02\x0c\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00hi'
792 '\x00\x00\x00\x02\x0c\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00hi'
793 removed : h, ;
793 removed : h, ;
794 added p1: i, h;
794 added p1: i, h;
795 ##### revision 22 #####
795 ##### revision 22 #####
796 1 sidedata entries
796 1 sidedata entries
797 entry-0014 size 24
797 entry-0014 size 24
798 '\x00\x00\x00\x02\x16\x00\x00\x00\x01\x00\x00\x00\x01\x0c\x00\x00\x00\x02\x00\x00\x00\x00di'
798 '\x00\x00\x00\x02\x16\x00\x00\x00\x01\x00\x00\x00\x01\x0c\x00\x00\x00\x02\x00\x00\x00\x00di'
799 touched p1: d, i;
799 touched p1: d, i;
800 removed : i, ;
800 removed : i, ;
801 ##### revision 23 #####
801 ##### revision 23 #####
802 1 sidedata entries
802 1 sidedata entries
803 entry-0014 size 4
803 entry-0014 size 4
804 '\x00\x00\x00\x00'
804 '\x00\x00\x00\x00'
805 ##### revision 24 #####
805 ##### revision 24 #####
806 1 sidedata entries
806 1 sidedata entries
807 entry-0014 size 4
807 entry-0014 size 4
808 '\x00\x00\x00\x00'
808 '\x00\x00\x00\x00'
809 ##### revision 25 #####
809 ##### revision 25 #####
810 1 sidedata entries
810 1 sidedata entries
811 entry-0014 size 14
811 entry-0014 size 14
812 '\x00\x00\x00\x01\x14\x00\x00\x00\x01\x00\x00\x00\x00d'
812 '\x00\x00\x00\x01\x14\x00\x00\x00\x01\x00\x00\x00\x00d'
813 touched : d, ;
813 touched : d, ;
814 ##### revision 26 #####
814 ##### revision 26 #####
815 1 sidedata entries
815 1 sidedata entries
816 entry-0014 size 14
816 entry-0014 size 14
817 '\x00\x00\x00\x01\x08\x00\x00\x00\x01\x00\x00\x00\x00d'
817 '\x00\x00\x00\x01\x08\x00\x00\x00\x01\x00\x00\x00\x00d'
818 merged : d, ;
818 merged : d, ;
819 ##### revision 27 #####
819 ##### revision 27 #####
820 1 sidedata entries
820 1 sidedata entries
821 entry-0014 size 14
821 entry-0014 size 14
822 '\x00\x00\x00\x01\x08\x00\x00\x00\x01\x00\x00\x00\x00d'
822 '\x00\x00\x00\x01\x08\x00\x00\x00\x01\x00\x00\x00\x00d'
823 merged : d, ;
823 merged : d, ;
824 ##### revision 28 #####
824 ##### revision 28 #####
825 1 sidedata entries
825 1 sidedata entries
826 entry-0014 size 14
826 entry-0014 size 14
827 '\x00\x00\x00\x01\x08\x00\x00\x00\x01\x00\x00\x00\x00d'
827 '\x00\x00\x00\x01\x08\x00\x00\x00\x01\x00\x00\x00\x00d'
828 merged : d, ;
828 merged : d, ;
829 ##### revision 29 #####
829 ##### revision 29 #####
830 1 sidedata entries
830 1 sidedata entries
831 entry-0014 size 14
831 entry-0014 size 14
832 '\x00\x00\x00\x01\x08\x00\x00\x00\x01\x00\x00\x00\x00d'
832 '\x00\x00\x00\x01\x08\x00\x00\x00\x01\x00\x00\x00\x00d'
833 merged : d, ;
833 merged : d, ;
834 ##### revision 30 #####
834 ##### revision 30 #####
835 1 sidedata entries
835 1 sidedata entries
836 entry-0014 size 14
836 entry-0014 size 14
837 '\x00\x00\x00\x01\x10\x00\x00\x00\x01\x00\x00\x00\x00d'
837 '\x00\x00\x00\x01\x10\x00\x00\x00\x01\x00\x00\x00\x00d'
838 salvaged : d, ;
838 salvaged : d, ;
839 ##### revision 31 #####
839 ##### revision 31 #####
840 1 sidedata entries
840 1 sidedata entries
841 entry-0014 size 14
841 entry-0014 size 14
842 '\x00\x00\x00\x01\x10\x00\x00\x00\x01\x00\x00\x00\x00d'
842 '\x00\x00\x00\x01\x10\x00\x00\x00\x01\x00\x00\x00\x00d'
843 salvaged : d, ;
843 salvaged : d, ;
844 ##### revision 32 #####
844 ##### revision 32 #####
845 1 sidedata entries
845 1 sidedata entries
846 entry-0014 size 14
846 entry-0014 size 14
847 '\x00\x00\x00\x01\x10\x00\x00\x00\x01\x00\x00\x00\x00d'
847 '\x00\x00\x00\x01\x10\x00\x00\x00\x01\x00\x00\x00\x00d'
848 salvaged : d, ;
848 salvaged : d, ;
849 ##### revision 33 #####
849 ##### revision 33 #####
850 1 sidedata entries
850 1 sidedata entries
851 entry-0014 size 14
851 entry-0014 size 14
852 '\x00\x00\x00\x01\x10\x00\x00\x00\x01\x00\x00\x00\x00d'
852 '\x00\x00\x00\x01\x10\x00\x00\x00\x01\x00\x00\x00\x00d'
853 salvaged : d, ;
853 salvaged : d, ;
854 ##### revision 34 #####
854 ##### revision 34 #####
855 1 sidedata entries
855 1 sidedata entries
856 entry-0014 size 24
856 entry-0014 size 24
857 '\x00\x00\x00\x02\x0c\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00bd'
857 '\x00\x00\x00\x02\x0c\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00bd'
858 removed : b, ;
858 removed : b, ;
859 added p1: d, b;
859 added p1: d, b;
860 ##### revision 35 #####
860 ##### revision 35 #####
861 1 sidedata entries
861 1 sidedata entries
862 entry-0014 size 4
862 entry-0014 size 4
863 '\x00\x00\x00\x00'
863 '\x00\x00\x00\x00'
864 ##### revision 36 #####
864 ##### revision 36 #####
865 1 sidedata entries
865 1 sidedata entries
866 entry-0014 size 4
866 entry-0014 size 4
867 '\x00\x00\x00\x00'
867 '\x00\x00\x00\x00'
868
868
869 #endif
869 #endif
870
870
871
871
872 Test copy information chaining
872 Test copy information chaining
873 ==============================
873 ==============================
874
874
875 Check that matching only affect the destination and not intermediate path
876 -------------------------------------------------------------------------
877
878 The two status call should give the same value for f
879
880 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("a-2")'
881 A f
882 a
883 R a
884 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("a-2")' f
885 A f
886 a (no-changeset no-compatibility !)
887
875 merging with unrelated change does not interfere with the renames
888 merging with unrelated change does not interfere with the renames
876 ---------------------------------------------------------------
889 ---------------------------------------------------------------
877
890
878 - rename on one side
891 - rename on one side
879 - unrelated change on the other side
892 - unrelated change on the other side
880
893
881 $ hg log -G --rev '::(desc("mABm")+desc("mBAm"))'
894 $ hg log -G --rev '::(desc("mABm")+desc("mBAm"))'
882 o 12 mABm-0 simple merge - the other way
895 o 12 mABm-0 simple merge - the other way
883 |\
896 |\
884 +---o 11 mBAm-0 simple merge - one way
897 +---o 11 mBAm-0 simple merge - one way
885 | |/
898 | |/
886 | o 5 b-1: b update
899 | o 5 b-1: b update
887 | |
900 | |
888 o | 4 a-2: e -move-> f
901 o | 4 a-2: e -move-> f
889 | |
902 | |
890 o | 3 a-1: d -move-> e
903 o | 3 a-1: d -move-> e
891 |/
904 |/
892 o 2 i-2: c -move-> d
905 o 2 i-2: c -move-> d
893 |
906 |
894 o 1 i-1: a -move-> c
907 o 1 i-1: a -move-> c
895 |
908 |
896 o 0 i-0 initial commit: a b h
909 o 0 i-0 initial commit: a b h
897
910
898
911
899 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mABm")'
912 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mABm")'
900 A f
913 A f
901 d
914 d
902 R d
915 R d
903 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mBAm")'
916 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mBAm")'
904 A f
917 A f
905 d
918 d
906 R d
919 R d
907 $ hg status --copies --rev 'desc("a-2")' --rev 'desc("mABm")'
920 $ hg status --copies --rev 'desc("a-2")' --rev 'desc("mABm")'
908 M b
921 M b
909 $ hg status --copies --rev 'desc("a-2")' --rev 'desc("mBAm")'
922 $ hg status --copies --rev 'desc("a-2")' --rev 'desc("mBAm")'
910 M b
923 M b
911 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mABm")'
924 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mABm")'
912 M b
925 M b
913 A f
926 A f
914 d
927 d
915 R d
928 R d
916 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mBAm")'
929 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mBAm")'
917 M b
930 M b
918 A f
931 A f
919 d
932 d
920 R d
933 R d
921 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mABm")'
934 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mABm")'
922 M b
935 M b
923 A f
936 A f
924 a
937 a
925 R a
938 R a
926 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mBAm")'
939 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mBAm")'
927 M b
940 M b
928 A f
941 A f
929 a
942 a
930 R a
943 R a
931
944
932 merging with the side having a delete
945 merging with the side having a delete
933 -------------------------------------
946 -------------------------------------
934
947
935 case summary:
948 case summary:
936 - one with change to an unrelated file
949 - one with change to an unrelated file
937 - one deleting the change
950 - one deleting the change
938 and recreate an unrelated file after the merge
951 and recreate an unrelated file after the merge
939
952
940 $ hg log -G --rev '::(desc("mCBm")+desc("mBCm"))'
953 $ hg log -G --rev '::(desc("mCBm")+desc("mBCm"))'
941 o 16 mCBm-1 re-add d
954 o 16 mCBm-1 re-add d
942 |
955 |
943 o 15 mCBm-0 simple merge - the other way
956 o 15 mCBm-0 simple merge - the other way
944 |\
957 |\
945 | | o 14 mBCm-1 re-add d
958 | | o 14 mBCm-1 re-add d
946 | | |
959 | | |
947 +---o 13 mBCm-0 simple merge - one way
960 +---o 13 mBCm-0 simple merge - one way
948 | |/
961 | |/
949 | o 6 c-1 delete d
962 | o 6 c-1 delete d
950 | |
963 | |
951 o | 5 b-1: b update
964 o | 5 b-1: b update
952 |/
965 |/
953 o 2 i-2: c -move-> d
966 o 2 i-2: c -move-> d
954 |
967 |
955 o 1 i-1: a -move-> c
968 o 1 i-1: a -move-> c
956 |
969 |
957 o 0 i-0 initial commit: a b h
970 o 0 i-0 initial commit: a b h
958
971
959 - comparing from the merge
972 - comparing from the merge
960
973
961 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mBCm-0")'
974 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mBCm-0")'
962 R d
975 R d
963 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mCBm-0")'
976 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mCBm-0")'
964 R d
977 R d
965 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mBCm-0")'
978 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mBCm-0")'
966 M b
979 M b
967 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mCBm-0")'
980 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mCBm-0")'
968 M b
981 M b
969 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mBCm-0")'
982 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mBCm-0")'
970 M b
983 M b
971 R d
984 R d
972 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mCBm-0")'
985 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mCBm-0")'
973 M b
986 M b
974 R d
987 R d
975 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mBCm-0")'
988 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mBCm-0")'
976 M b
989 M b
977 R a
990 R a
978 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mCBm-0")'
991 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mCBm-0")'
979 M b
992 M b
980 R a
993 R a
981
994
982 - comparing with the merge children re-adding the file
995 - comparing with the merge children re-adding the file
983
996
984 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mBCm-1")'
997 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mBCm-1")'
985 M d
998 M d
986 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mCBm-1")'
999 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mCBm-1")'
987 M d
1000 M d
988 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mBCm-1")'
1001 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mBCm-1")'
989 M b
1002 M b
990 A d
1003 A d
991 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mCBm-1")'
1004 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mCBm-1")'
992 M b
1005 M b
993 A d
1006 A d
994 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mBCm-1")'
1007 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mBCm-1")'
995 M b
1008 M b
996 M d
1009 M d
997 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mCBm-1")'
1010 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mCBm-1")'
998 M b
1011 M b
999 M d
1012 M d
1000 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mBCm-1")'
1013 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mBCm-1")'
1001 M b
1014 M b
1002 A d
1015 A d
1003 R a
1016 R a
1004 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mCBm-1")'
1017 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mCBm-1")'
1005 M b
1018 M b
1006 A d
1019 A d
1007 R a
1020 R a
1008
1021
1009 Comparing with a merge re-adding the file afterward
1022 Comparing with a merge re-adding the file afterward
1010 ---------------------------------------------------
1023 ---------------------------------------------------
1011
1024
1012 Merge:
1025 Merge:
1013 - one with change to an unrelated file
1026 - one with change to an unrelated file
1014 - one deleting and recreating the change
1027 - one deleting and recreating the change
1015
1028
1016 $ hg log -G --rev '::(desc("mDBm")+desc("mBDm"))'
1029 $ hg log -G --rev '::(desc("mDBm")+desc("mBDm"))'
1017 o 18 mDBm-0 simple merge - the other way
1030 o 18 mDBm-0 simple merge - the other way
1018 |\
1031 |\
1019 +---o 17 mBDm-0 simple merge - one way
1032 +---o 17 mBDm-0 simple merge - one way
1020 | |/
1033 | |/
1021 | o 8 d-2 re-add d
1034 | o 8 d-2 re-add d
1022 | |
1035 | |
1023 | o 7 d-1 delete d
1036 | o 7 d-1 delete d
1024 | |
1037 | |
1025 o | 5 b-1: b update
1038 o | 5 b-1: b update
1026 |/
1039 |/
1027 o 2 i-2: c -move-> d
1040 o 2 i-2: c -move-> d
1028 |
1041 |
1029 o 1 i-1: a -move-> c
1042 o 1 i-1: a -move-> c
1030 |
1043 |
1031 o 0 i-0 initial commit: a b h
1044 o 0 i-0 initial commit: a b h
1032
1045
1033 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mBDm-0")'
1046 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mBDm-0")'
1034 M d
1047 M d
1035 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mDBm-0")'
1048 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mDBm-0")'
1036 M d
1049 M d
1037 $ hg status --copies --rev 'desc("d-2")' --rev 'desc("mBDm-0")'
1050 $ hg status --copies --rev 'desc("d-2")' --rev 'desc("mBDm-0")'
1038 M b
1051 M b
1039 $ hg status --copies --rev 'desc("d-2")' --rev 'desc("mDBm-0")'
1052 $ hg status --copies --rev 'desc("d-2")' --rev 'desc("mDBm-0")'
1040 M b
1053 M b
1041 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mBDm-0")'
1054 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mBDm-0")'
1042 M b
1055 M b
1043 M d
1056 M d
1044 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mDBm-0")'
1057 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mDBm-0")'
1045 M b
1058 M b
1046 M d
1059 M d
1047
1060
1048 The bugs makes recorded copy is different depending of where we started the merge from since
1061 The bugs makes recorded copy is different depending of where we started the merge from since
1049
1062
1050 $ hg manifest --debug --rev 'desc("mBDm-0")' | grep '644 d'
1063 $ hg manifest --debug --rev 'desc("mBDm-0")' | grep '644 d'
1051 b004912a8510032a0350a74daa2803dadfb00e12 644 d
1064 b004912a8510032a0350a74daa2803dadfb00e12 644 d
1052 $ hg manifest --debug --rev 'desc("mDBm-0")' | grep '644 d'
1065 $ hg manifest --debug --rev 'desc("mDBm-0")' | grep '644 d'
1053 b004912a8510032a0350a74daa2803dadfb00e12 644 d
1066 b004912a8510032a0350a74daa2803dadfb00e12 644 d
1054
1067
1055 $ hg manifest --debug --rev 'desc("d-2")' | grep '644 d'
1068 $ hg manifest --debug --rev 'desc("d-2")' | grep '644 d'
1056 b004912a8510032a0350a74daa2803dadfb00e12 644 d
1069 b004912a8510032a0350a74daa2803dadfb00e12 644 d
1057 $ hg manifest --debug --rev 'desc("b-1")' | grep '644 d'
1070 $ hg manifest --debug --rev 'desc("b-1")' | grep '644 d'
1058 169be882533bc917905d46c0c951aa9a1e288dcf 644 d (no-changeset !)
1071 169be882533bc917905d46c0c951aa9a1e288dcf 644 d (no-changeset !)
1059 b789fdd96dc2f3bd229c1dd8eedf0fc60e2b68e3 644 d (changeset !)
1072 b789fdd96dc2f3bd229c1dd8eedf0fc60e2b68e3 644 d (changeset !)
1060 $ hg debugindex d | head -n 4
1073 $ hg debugindex d | head -n 4
1061 rev linkrev nodeid p1 p2
1074 rev linkrev nodeid p1 p2
1062 0 2 169be882533b 000000000000 000000000000 (no-changeset !)
1075 0 2 169be882533b 000000000000 000000000000 (no-changeset !)
1063 0 2 b789fdd96dc2 000000000000 000000000000 (changeset !)
1076 0 2 b789fdd96dc2 000000000000 000000000000 (changeset !)
1064 1 8 b004912a8510 000000000000 000000000000
1077 1 8 b004912a8510 000000000000 000000000000
1065 2 22 4a067cf8965d 000000000000 000000000000 (no-changeset !)
1078 2 22 4a067cf8965d 000000000000 000000000000 (no-changeset !)
1066 2 22 fe6f8b4f507f 000000000000 000000000000 (changeset !)
1079 2 22 fe6f8b4f507f 000000000000 000000000000 (changeset !)
1067
1080
1068 Log output should not include a merge commit as it did not happen
1081 Log output should not include a merge commit as it did not happen
1069
1082
1070 $ hg log -Gfr 'desc("mBDm-0")' d
1083 $ hg log -Gfr 'desc("mBDm-0")' d
1071 o 8 d-2 re-add d
1084 o 8 d-2 re-add d
1072 |
1085 |
1073 ~
1086 ~
1074
1087
1075 $ hg log -Gfr 'desc("mDBm-0")' d
1088 $ hg log -Gfr 'desc("mDBm-0")' d
1076 o 8 d-2 re-add d
1089 o 8 d-2 re-add d
1077 |
1090 |
1078 ~
1091 ~
1079
1092
1080 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mBDm-0")'
1093 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mBDm-0")'
1081 M b
1094 M b
1082 A d
1095 A d
1083 R a
1096 R a
1084 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mDBm-0")'
1097 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mDBm-0")'
1085 M b
1098 M b
1086 A d
1099 A d
1087 R a
1100 R a
1088
1101
1089
1102
1090 Comparing with a merge with colliding rename
1103 Comparing with a merge with colliding rename
1091 --------------------------------------------
1104 --------------------------------------------
1092
1105
1093 - the "e-" branch renaming b to f (through 'g')
1106 - the "e-" branch renaming b to f (through 'g')
1094 - the "a-" branch renaming d to f (through e)
1107 - the "a-" branch renaming d to f (through e)
1095
1108
1096 $ hg log -G --rev '::(desc("mAEm")+desc("mEAm"))'
1109 $ hg log -G --rev '::(desc("mAEm")+desc("mEAm"))'
1097 o 20 mEAm-0 simple merge - the other way
1110 o 20 mEAm-0 simple merge - the other way
1098 |\
1111 |\
1099 +---o 19 mAEm-0 simple merge - one way
1112 +---o 19 mAEm-0 simple merge - one way
1100 | |/
1113 | |/
1101 | o 10 e-2 g -move-> f
1114 | o 10 e-2 g -move-> f
1102 | |
1115 | |
1103 | o 9 e-1 b -move-> g
1116 | o 9 e-1 b -move-> g
1104 | |
1117 | |
1105 o | 4 a-2: e -move-> f
1118 o | 4 a-2: e -move-> f
1106 | |
1119 | |
1107 o | 3 a-1: d -move-> e
1120 o | 3 a-1: d -move-> e
1108 |/
1121 |/
1109 o 2 i-2: c -move-> d
1122 o 2 i-2: c -move-> d
1110 |
1123 |
1111 o 1 i-1: a -move-> c
1124 o 1 i-1: a -move-> c
1112 |
1125 |
1113 o 0 i-0 initial commit: a b h
1126 o 0 i-0 initial commit: a b h
1114
1127
1115 #if no-changeset
1128 #if no-changeset
1116 $ hg manifest --debug --rev 'desc("mAEm-0")' | grep '644 f'
1129 $ hg manifest --debug --rev 'desc("mAEm-0")' | grep '644 f'
1117 c39c6083dad048d5138618a46f123e2f397f4f18 644 f
1130 c39c6083dad048d5138618a46f123e2f397f4f18 644 f
1118 $ hg manifest --debug --rev 'desc("mEAm-0")' | grep '644 f'
1131 $ hg manifest --debug --rev 'desc("mEAm-0")' | grep '644 f'
1119 a9a8bc3860c9d8fa5f2f7e6ea8d40498322737fd 644 f
1132 a9a8bc3860c9d8fa5f2f7e6ea8d40498322737fd 644 f
1120 $ hg manifest --debug --rev 'desc("a-2")' | grep '644 f'
1133 $ hg manifest --debug --rev 'desc("a-2")' | grep '644 f'
1121 263ea25e220aaeb7b9bac551c702037849aa75e8 644 f
1134 263ea25e220aaeb7b9bac551c702037849aa75e8 644 f
1122 $ hg manifest --debug --rev 'desc("e-2")' | grep '644 f'
1135 $ hg manifest --debug --rev 'desc("e-2")' | grep '644 f'
1123 71b9b7e73d973572ade6dd765477fcee6890e8b1 644 f
1136 71b9b7e73d973572ade6dd765477fcee6890e8b1 644 f
1124 $ hg debugindex f
1137 $ hg debugindex f
1125 rev linkrev nodeid p1 p2
1138 rev linkrev nodeid p1 p2
1126 0 4 263ea25e220a 000000000000 000000000000
1139 0 4 263ea25e220a 000000000000 000000000000
1127 1 10 71b9b7e73d97 000000000000 000000000000
1140 1 10 71b9b7e73d97 000000000000 000000000000
1128 2 19 c39c6083dad0 263ea25e220a 71b9b7e73d97
1141 2 19 c39c6083dad0 263ea25e220a 71b9b7e73d97
1129 3 20 a9a8bc3860c9 71b9b7e73d97 263ea25e220a
1142 3 20 a9a8bc3860c9 71b9b7e73d97 263ea25e220a
1130 #else
1143 #else
1131 $ hg manifest --debug --rev 'desc("mAEm-0")' | grep '644 f'
1144 $ hg manifest --debug --rev 'desc("mAEm-0")' | grep '644 f'
1132 498e8799f49f9da1ca06bb2d6d4accf165c5b572 644 f
1145 498e8799f49f9da1ca06bb2d6d4accf165c5b572 644 f
1133 $ hg manifest --debug --rev 'desc("mEAm-0")' | grep '644 f'
1146 $ hg manifest --debug --rev 'desc("mEAm-0")' | grep '644 f'
1134 c5b506a7118667a38a9c9348a1f63b679e382f57 644 f
1147 c5b506a7118667a38a9c9348a1f63b679e382f57 644 f
1135 $ hg manifest --debug --rev 'desc("a-2")' | grep '644 f'
1148 $ hg manifest --debug --rev 'desc("a-2")' | grep '644 f'
1136 b789fdd96dc2f3bd229c1dd8eedf0fc60e2b68e3 644 f
1149 b789fdd96dc2f3bd229c1dd8eedf0fc60e2b68e3 644 f
1137 $ hg manifest --debug --rev 'desc("e-2")' | grep '644 f'
1150 $ hg manifest --debug --rev 'desc("e-2")' | grep '644 f'
1138 1e88685f5ddec574a34c70af492f95b6debc8741 644 f
1151 1e88685f5ddec574a34c70af492f95b6debc8741 644 f
1139 $ hg debugindex f
1152 $ hg debugindex f
1140 rev linkrev nodeid p1 p2
1153 rev linkrev nodeid p1 p2
1141 0 4 b789fdd96dc2 000000000000 000000000000
1154 0 4 b789fdd96dc2 000000000000 000000000000
1142 1 10 1e88685f5dde 000000000000 000000000000
1155 1 10 1e88685f5dde 000000000000 000000000000
1143 2 19 498e8799f49f b789fdd96dc2 1e88685f5dde
1156 2 19 498e8799f49f b789fdd96dc2 1e88685f5dde
1144 3 20 c5b506a71186 1e88685f5dde b789fdd96dc2
1157 3 20 c5b506a71186 1e88685f5dde b789fdd96dc2
1145 #endif
1158 #endif
1146
1159
1147 # Here the filelog based implementation is not looking at the rename
1160 # Here the filelog based implementation is not looking at the rename
1148 # information (because the file exist on both side). However the changelog
1161 # information (because the file exist on both side). However the changelog
1149 # based on works fine. We have different output.
1162 # based on works fine. We have different output.
1150
1163
1151 $ hg status --copies --rev 'desc("a-2")' --rev 'desc("mAEm-0")'
1164 $ hg status --copies --rev 'desc("a-2")' --rev 'desc("mAEm-0")'
1152 M f
1165 M f
1153 b (no-filelog !)
1166 b (no-filelog !)
1154 R b
1167 R b
1155 $ hg status --copies --rev 'desc("a-2")' --rev 'desc("mEAm-0")'
1168 $ hg status --copies --rev 'desc("a-2")' --rev 'desc("mEAm-0")'
1156 M f
1169 M f
1157 b (no-filelog !)
1170 b (no-filelog !)
1158 R b
1171 R b
1159 $ hg status --copies --rev 'desc("e-2")' --rev 'desc("mAEm-0")'
1172 $ hg status --copies --rev 'desc("e-2")' --rev 'desc("mAEm-0")'
1160 M f
1173 M f
1161 d (no-filelog !)
1174 d (no-filelog !)
1162 R d
1175 R d
1163 $ hg status --copies --rev 'desc("e-2")' --rev 'desc("mEAm-0")'
1176 $ hg status --copies --rev 'desc("e-2")' --rev 'desc("mEAm-0")'
1164 M f
1177 M f
1165 d (no-filelog !)
1178 d (no-filelog !)
1166 R d
1179 R d
1167 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("a-2")'
1180 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("a-2")'
1168 A f
1181 A f
1169 d
1182 d
1170 R d
1183 R d
1171 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("e-2")'
1184 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("e-2")'
1172 A f
1185 A f
1173 b
1186 b
1174 R b
1187 R b
1175
1188
1176 # From here, we run status against revision where both source file exists.
1189 # From here, we run status against revision where both source file exists.
1177 #
1190 #
1178 # The filelog based implementation picks an arbitrary side based on revision
1191 # The filelog based implementation picks an arbitrary side based on revision
1179 # numbers. So the same side "wins" whatever the parents order is. This is
1192 # numbers. So the same side "wins" whatever the parents order is. This is
1180 # sub-optimal because depending on revision numbers means the result can be
1193 # sub-optimal because depending on revision numbers means the result can be
1181 # different from one repository to the next.
1194 # different from one repository to the next.
1182 #
1195 #
1183 # The changeset based algorithm use the parent order to break tie on conflicting
1196 # The changeset based algorithm use the parent order to break tie on conflicting
1184 # information and will have a different order depending on who is p1 and p2.
1197 # information and will have a different order depending on who is p1 and p2.
1185 # That order is stable accross repositories. (data from p1 prevails)
1198 # That order is stable accross repositories. (data from p1 prevails)
1186
1199
1187 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mAEm-0")'
1200 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mAEm-0")'
1188 A f
1201 A f
1189 d
1202 d
1190 R b
1203 R b
1191 R d
1204 R d
1192 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mEAm-0")'
1205 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mEAm-0")'
1193 A f
1206 A f
1194 d (filelog !)
1207 d (filelog !)
1195 b (no-filelog !)
1208 b (no-filelog !)
1196 R b
1209 R b
1197 R d
1210 R d
1198 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mAEm-0")'
1211 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mAEm-0")'
1199 A f
1212 A f
1200 a
1213 a
1201 R a
1214 R a
1202 R b
1215 R b
1203 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mEAm-0")'
1216 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mEAm-0")'
1204 A f
1217 A f
1205 a (filelog !)
1218 a (filelog !)
1206 b (no-filelog !)
1219 b (no-filelog !)
1207 R a
1220 R a
1208 R b
1221 R b
1209
1222
1210
1223
1211 Note:
1224 Note:
1212 | In this case, one of the merge wrongly record a merge while there is none.
1225 | In this case, one of the merge wrongly record a merge while there is none.
1213 | This lead to bad copy tracing information to be dug up.
1226 | This lead to bad copy tracing information to be dug up.
1214
1227
1215
1228
1216 Merge:
1229 Merge:
1217 - one with change to an unrelated file (b)
1230 - one with change to an unrelated file (b)
1218 - one overwriting a file (d) with a rename (from h to i to d)
1231 - one overwriting a file (d) with a rename (from h to i to d)
1219
1232
1220 $ hg log -G --rev '::(desc("mBFm")+desc("mFBm"))'
1233 $ hg log -G --rev '::(desc("mBFm")+desc("mFBm"))'
1221 o 24 mFBm-0 simple merge - the other way
1234 o 24 mFBm-0 simple merge - the other way
1222 |\
1235 |\
1223 +---o 23 mBFm-0 simple merge - one way
1236 +---o 23 mBFm-0 simple merge - one way
1224 | |/
1237 | |/
1225 | o 22 f-2: rename i -> d
1238 | o 22 f-2: rename i -> d
1226 | |
1239 | |
1227 | o 21 f-1: rename h -> i
1240 | o 21 f-1: rename h -> i
1228 | |
1241 | |
1229 o | 5 b-1: b update
1242 o | 5 b-1: b update
1230 |/
1243 |/
1231 o 2 i-2: c -move-> d
1244 o 2 i-2: c -move-> d
1232 |
1245 |
1233 o 1 i-1: a -move-> c
1246 o 1 i-1: a -move-> c
1234 |
1247 |
1235 o 0 i-0 initial commit: a b h
1248 o 0 i-0 initial commit: a b h
1236
1249
1237 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mBFm-0")'
1250 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mBFm-0")'
1238 M b
1251 M b
1239 A d
1252 A d
1240 h
1253 h
1241 R a
1254 R a
1242 R h
1255 R h
1243 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mFBm-0")'
1256 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mFBm-0")'
1244 M b
1257 M b
1245 A d
1258 A d
1246 h
1259 h
1247 R a
1260 R a
1248 R h
1261 R h
1249 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mBFm-0")'
1262 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mBFm-0")'
1250 M d
1263 M d
1251 h (no-filelog !)
1264 h (no-filelog !)
1252 R h
1265 R h
1253 $ hg status --copies --rev 'desc("f-2")' --rev 'desc("mBFm-0")'
1266 $ hg status --copies --rev 'desc("f-2")' --rev 'desc("mBFm-0")'
1254 M b
1267 M b
1255 $ hg status --copies --rev 'desc("f-1")' --rev 'desc("mBFm-0")'
1268 $ hg status --copies --rev 'desc("f-1")' --rev 'desc("mBFm-0")'
1256 M b
1269 M b
1257 M d
1270 M d
1258 i (no-filelog !)
1271 i (no-filelog !)
1259 R i
1272 R i
1260 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mFBm-0")'
1273 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mFBm-0")'
1261 M d
1274 M d
1262 h (no-filelog !)
1275 h (no-filelog !)
1263 R h
1276 R h
1264 $ hg status --copies --rev 'desc("f-2")' --rev 'desc("mFBm-0")'
1277 $ hg status --copies --rev 'desc("f-2")' --rev 'desc("mFBm-0")'
1265 M b
1278 M b
1266 $ hg status --copies --rev 'desc("f-1")' --rev 'desc("mFBm-0")'
1279 $ hg status --copies --rev 'desc("f-1")' --rev 'desc("mFBm-0")'
1267 M b
1280 M b
1268 M d
1281 M d
1269 i (no-filelog !)
1282 i (no-filelog !)
1270 R i
1283 R i
1271
1284
1272 #if no-changeset
1285 #if no-changeset
1273 $ hg log -Gfr 'desc("mBFm-0")' d
1286 $ hg log -Gfr 'desc("mBFm-0")' d
1274 o 22 f-2: rename i -> d
1287 o 22 f-2: rename i -> d
1275 |
1288 |
1276 o 21 f-1: rename h -> i
1289 o 21 f-1: rename h -> i
1277 :
1290 :
1278 o 0 i-0 initial commit: a b h
1291 o 0 i-0 initial commit: a b h
1279
1292
1280 #else
1293 #else
1281 BROKEN: `hg log --follow <file>` relies on filelog metadata to work
1294 BROKEN: `hg log --follow <file>` relies on filelog metadata to work
1282 $ hg log -Gfr 'desc("mBFm-0")' d
1295 $ hg log -Gfr 'desc("mBFm-0")' d
1283 o 22 f-2: rename i -> d
1296 o 22 f-2: rename i -> d
1284 |
1297 |
1285 ~
1298 ~
1286 #endif
1299 #endif
1287
1300
1288 #if no-changeset
1301 #if no-changeset
1289 $ hg log -Gfr 'desc("mFBm-0")' d
1302 $ hg log -Gfr 'desc("mFBm-0")' d
1290 o 22 f-2: rename i -> d
1303 o 22 f-2: rename i -> d
1291 |
1304 |
1292 o 21 f-1: rename h -> i
1305 o 21 f-1: rename h -> i
1293 :
1306 :
1294 o 0 i-0 initial commit: a b h
1307 o 0 i-0 initial commit: a b h
1295
1308
1296 #else
1309 #else
1297 BROKEN: `hg log --follow <file>` relies on filelog metadata to work
1310 BROKEN: `hg log --follow <file>` relies on filelog metadata to work
1298 $ hg log -Gfr 'desc("mFBm-0")' d
1311 $ hg log -Gfr 'desc("mFBm-0")' d
1299 o 22 f-2: rename i -> d
1312 o 22 f-2: rename i -> d
1300 |
1313 |
1301 ~
1314 ~
1302 #endif
1315 #endif
1303
1316
1304
1317
1305 Merge:
1318 Merge:
1306 - one with change to a file
1319 - one with change to a file
1307 - one deleting and recreating the file
1320 - one deleting and recreating the file
1308
1321
1309 Unlike in the 'BD/DB' cases, an actual merge happened here. So we should
1322 Unlike in the 'BD/DB' cases, an actual merge happened here. So we should
1310 consider history and rename on both branch of the merge.
1323 consider history and rename on both branch of the merge.
1311
1324
1312 $ hg log -G --rev '::(desc("mDGm")+desc("mGDm"))'
1325 $ hg log -G --rev '::(desc("mDGm")+desc("mGDm"))'
1313 o 27 mGDm-0 simple merge - the other way
1326 o 27 mGDm-0 simple merge - the other way
1314 |\
1327 |\
1315 +---o 26 mDGm-0 simple merge - one way
1328 +---o 26 mDGm-0 simple merge - one way
1316 | |/
1329 | |/
1317 | o 25 g-1: update d
1330 | o 25 g-1: update d
1318 | |
1331 | |
1319 o | 8 d-2 re-add d
1332 o | 8 d-2 re-add d
1320 | |
1333 | |
1321 o | 7 d-1 delete d
1334 o | 7 d-1 delete d
1322 |/
1335 |/
1323 o 2 i-2: c -move-> d
1336 o 2 i-2: c -move-> d
1324 |
1337 |
1325 o 1 i-1: a -move-> c
1338 o 1 i-1: a -move-> c
1326 |
1339 |
1327 o 0 i-0 initial commit: a b h
1340 o 0 i-0 initial commit: a b h
1328
1341
1329 One side of the merge have a long history with rename. The other side of the
1342 One side of the merge have a long history with rename. The other side of the
1330 merge point to a new file with a smaller history. Each side is "valid".
1343 merge point to a new file with a smaller history. Each side is "valid".
1331
1344
1332 (and again the filelog based algorithm only explore one, with a pick based on
1345 (and again the filelog based algorithm only explore one, with a pick based on
1333 revision numbers)
1346 revision numbers)
1334
1347
1335 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mDGm-0")'
1348 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mDGm-0")'
1336 A d
1349 A d
1337 a (filelog !)
1350 a (filelog !)
1338 R a
1351 R a
1339 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mGDm-0")'
1352 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mGDm-0")'
1340 A d
1353 A d
1341 a
1354 a
1342 R a
1355 R a
1343 $ hg status --copies --rev 'desc("d-2")' --rev 'desc("mDGm-0")'
1356 $ hg status --copies --rev 'desc("d-2")' --rev 'desc("mDGm-0")'
1344 M d
1357 M d
1345 $ hg status --copies --rev 'desc("d-2")' --rev 'desc("mGDm-0")'
1358 $ hg status --copies --rev 'desc("d-2")' --rev 'desc("mGDm-0")'
1346 M d
1359 M d
1347 $ hg status --copies --rev 'desc("g-1")' --rev 'desc("mDGm-0")'
1360 $ hg status --copies --rev 'desc("g-1")' --rev 'desc("mDGm-0")'
1348 M d
1361 M d
1349 $ hg status --copies --rev 'desc("g-1")' --rev 'desc("mGDm-0")'
1362 $ hg status --copies --rev 'desc("g-1")' --rev 'desc("mGDm-0")'
1350 M d
1363 M d
1351
1364
1352 #if no-changeset
1365 #if no-changeset
1353 $ hg log -Gfr 'desc("mDGm-0")' d
1366 $ hg log -Gfr 'desc("mDGm-0")' d
1354 o 26 mDGm-0 simple merge - one way
1367 o 26 mDGm-0 simple merge - one way
1355 |\
1368 |\
1356 | o 25 g-1: update d
1369 | o 25 g-1: update d
1357 | |
1370 | |
1358 o | 8 d-2 re-add d
1371 o | 8 d-2 re-add d
1359 |/
1372 |/
1360 o 2 i-2: c -move-> d
1373 o 2 i-2: c -move-> d
1361 |
1374 |
1362 o 1 i-1: a -move-> c
1375 o 1 i-1: a -move-> c
1363 |
1376 |
1364 o 0 i-0 initial commit: a b h
1377 o 0 i-0 initial commit: a b h
1365
1378
1366 #else
1379 #else
1367 BROKEN: `hg log --follow <file>` relies on filelog metadata to work
1380 BROKEN: `hg log --follow <file>` relies on filelog metadata to work
1368 $ hg log -Gfr 'desc("mDGm-0")' d
1381 $ hg log -Gfr 'desc("mDGm-0")' d
1369 o 26 mDGm-0 simple merge - one way
1382 o 26 mDGm-0 simple merge - one way
1370 |\
1383 |\
1371 | o 25 g-1: update d
1384 | o 25 g-1: update d
1372 | |
1385 | |
1373 o | 8 d-2 re-add d
1386 o | 8 d-2 re-add d
1374 |/
1387 |/
1375 o 2 i-2: c -move-> d
1388 o 2 i-2: c -move-> d
1376 |
1389 |
1377 ~
1390 ~
1378 #endif
1391 #endif
1379
1392
1380
1393
1381 #if no-changeset
1394 #if no-changeset
1382 $ hg log -Gfr 'desc("mDGm-0")' d
1395 $ hg log -Gfr 'desc("mDGm-0")' d
1383 o 26 mDGm-0 simple merge - one way
1396 o 26 mDGm-0 simple merge - one way
1384 |\
1397 |\
1385 | o 25 g-1: update d
1398 | o 25 g-1: update d
1386 | |
1399 | |
1387 o | 8 d-2 re-add d
1400 o | 8 d-2 re-add d
1388 |/
1401 |/
1389 o 2 i-2: c -move-> d
1402 o 2 i-2: c -move-> d
1390 |
1403 |
1391 o 1 i-1: a -move-> c
1404 o 1 i-1: a -move-> c
1392 |
1405 |
1393 o 0 i-0 initial commit: a b h
1406 o 0 i-0 initial commit: a b h
1394
1407
1395 #else
1408 #else
1396 BROKEN: `hg log --follow <file>` relies on filelog metadata to work
1409 BROKEN: `hg log --follow <file>` relies on filelog metadata to work
1397 $ hg log -Gfr 'desc("mDGm-0")' d
1410 $ hg log -Gfr 'desc("mDGm-0")' d
1398 o 26 mDGm-0 simple merge - one way
1411 o 26 mDGm-0 simple merge - one way
1399 |\
1412 |\
1400 | o 25 g-1: update d
1413 | o 25 g-1: update d
1401 | |
1414 | |
1402 o | 8 d-2 re-add d
1415 o | 8 d-2 re-add d
1403 |/
1416 |/
1404 o 2 i-2: c -move-> d
1417 o 2 i-2: c -move-> d
1405 |
1418 |
1406 ~
1419 ~
1407 #endif
1420 #endif
1408
1421
1409
1422
1410 Merge:
1423 Merge:
1411 - one with change to a file (d)
1424 - one with change to a file (d)
1412 - one overwriting that file with a rename (from h to i, to d)
1425 - one overwriting that file with a rename (from h to i, to d)
1413
1426
1414 This case is similar to BF/FB, but an actual merge happens, so both side of the
1427 This case is similar to BF/FB, but an actual merge happens, so both side of the
1415 history are relevant.
1428 history are relevant.
1416
1429
1417 Note:
1430 Note:
1418 | In this case, the merge get conflicting information since on one side we have
1431 | In this case, the merge get conflicting information since on one side we have
1419 | "a -> c -> d". and one the other one we have "h -> i -> d".
1432 | "a -> c -> d". and one the other one we have "h -> i -> d".
1420 |
1433 |
1421 | The current code arbitrarily pick one side
1434 | The current code arbitrarily pick one side
1422
1435
1423 $ hg log -G --rev '::(desc("mGFm")+desc("mFGm"))'
1436 $ hg log -G --rev '::(desc("mGFm")+desc("mFGm"))'
1424 o 29 mGFm-0 simple merge - the other way
1437 o 29 mGFm-0 simple merge - the other way
1425 |\
1438 |\
1426 +---o 28 mFGm-0 simple merge - one way
1439 +---o 28 mFGm-0 simple merge - one way
1427 | |/
1440 | |/
1428 | o 25 g-1: update d
1441 | o 25 g-1: update d
1429 | |
1442 | |
1430 o | 22 f-2: rename i -> d
1443 o | 22 f-2: rename i -> d
1431 | |
1444 | |
1432 o | 21 f-1: rename h -> i
1445 o | 21 f-1: rename h -> i
1433 |/
1446 |/
1434 o 2 i-2: c -move-> d
1447 o 2 i-2: c -move-> d
1435 |
1448 |
1436 o 1 i-1: a -move-> c
1449 o 1 i-1: a -move-> c
1437 |
1450 |
1438 o 0 i-0 initial commit: a b h
1451 o 0 i-0 initial commit: a b h
1439
1452
1440 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mFGm-0")'
1453 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mFGm-0")'
1441 A d
1454 A d
1442 h
1455 h
1443 R a
1456 R a
1444 R h
1457 R h
1445 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mGFm-0")'
1458 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mGFm-0")'
1446 A d
1459 A d
1447 a (no-filelog !)
1460 a (no-filelog !)
1448 h (filelog !)
1461 h (filelog !)
1449 R a
1462 R a
1450 R h
1463 R h
1451 $ hg status --copies --rev 'desc("f-2")' --rev 'desc("mFGm-0")'
1464 $ hg status --copies --rev 'desc("f-2")' --rev 'desc("mFGm-0")'
1452 M d
1465 M d
1453 $ hg status --copies --rev 'desc("f-2")' --rev 'desc("mGFm-0")'
1466 $ hg status --copies --rev 'desc("f-2")' --rev 'desc("mGFm-0")'
1454 M d
1467 M d
1455 $ hg status --copies --rev 'desc("f-1")' --rev 'desc("mFGm-0")'
1468 $ hg status --copies --rev 'desc("f-1")' --rev 'desc("mFGm-0")'
1456 M d
1469 M d
1457 i (no-filelog !)
1470 i (no-filelog !)
1458 R i
1471 R i
1459 $ hg status --copies --rev 'desc("f-1")' --rev 'desc("mGFm-0")'
1472 $ hg status --copies --rev 'desc("f-1")' --rev 'desc("mGFm-0")'
1460 M d
1473 M d
1461 i (no-filelog !)
1474 i (no-filelog !)
1462 R i
1475 R i
1463 $ hg status --copies --rev 'desc("g-1")' --rev 'desc("mFGm-0")'
1476 $ hg status --copies --rev 'desc("g-1")' --rev 'desc("mFGm-0")'
1464 M d
1477 M d
1465 h (no-filelog !)
1478 h (no-filelog !)
1466 R h
1479 R h
1467 $ hg status --copies --rev 'desc("g-1")' --rev 'desc("mGFm-0")'
1480 $ hg status --copies --rev 'desc("g-1")' --rev 'desc("mGFm-0")'
1468 M d
1481 M d
1469 h (no-filelog !)
1482 h (no-filelog !)
1470 R h
1483 R h
1471
1484
1472 #if no-changeset
1485 #if no-changeset
1473 $ hg log -Gfr 'desc("mFGm-0")' d
1486 $ hg log -Gfr 'desc("mFGm-0")' d
1474 o 28 mFGm-0 simple merge - one way
1487 o 28 mFGm-0 simple merge - one way
1475 |\
1488 |\
1476 | o 25 g-1: update d
1489 | o 25 g-1: update d
1477 | |
1490 | |
1478 o | 22 f-2: rename i -> d
1491 o | 22 f-2: rename i -> d
1479 | |
1492 | |
1480 o | 21 f-1: rename h -> i
1493 o | 21 f-1: rename h -> i
1481 |/
1494 |/
1482 o 2 i-2: c -move-> d
1495 o 2 i-2: c -move-> d
1483 |
1496 |
1484 o 1 i-1: a -move-> c
1497 o 1 i-1: a -move-> c
1485 |
1498 |
1486 o 0 i-0 initial commit: a b h
1499 o 0 i-0 initial commit: a b h
1487
1500
1488 #else
1501 #else
1489 BROKEN: `hg log --follow <file>` relies on filelog metadata to work
1502 BROKEN: `hg log --follow <file>` relies on filelog metadata to work
1490 $ hg log -Gfr 'desc("mFGm-0")' d
1503 $ hg log -Gfr 'desc("mFGm-0")' d
1491 o 28 mFGm-0 simple merge - one way
1504 o 28 mFGm-0 simple merge - one way
1492 |\
1505 |\
1493 | o 25 g-1: update d
1506 | o 25 g-1: update d
1494 | |
1507 | |
1495 o | 22 f-2: rename i -> d
1508 o | 22 f-2: rename i -> d
1496 |/
1509 |/
1497 o 2 i-2: c -move-> d
1510 o 2 i-2: c -move-> d
1498 |
1511 |
1499 ~
1512 ~
1500 #endif
1513 #endif
1501
1514
1502 #if no-changeset
1515 #if no-changeset
1503 $ hg log -Gfr 'desc("mGFm-0")' d
1516 $ hg log -Gfr 'desc("mGFm-0")' d
1504 o 29 mGFm-0 simple merge - the other way
1517 o 29 mGFm-0 simple merge - the other way
1505 |\
1518 |\
1506 | o 25 g-1: update d
1519 | o 25 g-1: update d
1507 | |
1520 | |
1508 o | 22 f-2: rename i -> d
1521 o | 22 f-2: rename i -> d
1509 | |
1522 | |
1510 o | 21 f-1: rename h -> i
1523 o | 21 f-1: rename h -> i
1511 |/
1524 |/
1512 o 2 i-2: c -move-> d
1525 o 2 i-2: c -move-> d
1513 |
1526 |
1514 o 1 i-1: a -move-> c
1527 o 1 i-1: a -move-> c
1515 |
1528 |
1516 o 0 i-0 initial commit: a b h
1529 o 0 i-0 initial commit: a b h
1517
1530
1518 #else
1531 #else
1519 BROKEN: `hg log --follow <file>` relies on filelog metadata to work
1532 BROKEN: `hg log --follow <file>` relies on filelog metadata to work
1520 $ hg log -Gfr 'desc("mGFm-0")' d
1533 $ hg log -Gfr 'desc("mGFm-0")' d
1521 o 29 mGFm-0 simple merge - the other way
1534 o 29 mGFm-0 simple merge - the other way
1522 |\
1535 |\
1523 | o 25 g-1: update d
1536 | o 25 g-1: update d
1524 | |
1537 | |
1525 o | 22 f-2: rename i -> d
1538 o | 22 f-2: rename i -> d
1526 |/
1539 |/
1527 o 2 i-2: c -move-> d
1540 o 2 i-2: c -move-> d
1528 |
1541 |
1529 ~
1542 ~
1530 #endif
1543 #endif
1531
1544
1532
1545
1533 Comparing with merging with a deletion (and keeping the file)
1546 Comparing with merging with a deletion (and keeping the file)
1534 -------------------------------------------------------------
1547 -------------------------------------------------------------
1535
1548
1536 Merge:
1549 Merge:
1537 - one removing a file (d)
1550 - one removing a file (d)
1538 - one updating that file
1551 - one updating that file
1539 - the merge keep the modified version of the file (canceling the delete)
1552 - the merge keep the modified version of the file (canceling the delete)
1540
1553
1541 In this case, the file keep on living after the merge. So we should not drop its
1554 In this case, the file keep on living after the merge. So we should not drop its
1542 copy tracing chain.
1555 copy tracing chain.
1543
1556
1544 $ hg log -G --rev '::(desc("mCGm")+desc("mGCm"))'
1557 $ hg log -G --rev '::(desc("mCGm")+desc("mGCm"))'
1545 o 31 mGCm-0
1558 o 31 mGCm-0
1546 |\
1559 |\
1547 +---o 30 mCGm-0
1560 +---o 30 mCGm-0
1548 | |/
1561 | |/
1549 | o 25 g-1: update d
1562 | o 25 g-1: update d
1550 | |
1563 | |
1551 o | 6 c-1 delete d
1564 o | 6 c-1 delete d
1552 |/
1565 |/
1553 o 2 i-2: c -move-> d
1566 o 2 i-2: c -move-> d
1554 |
1567 |
1555 o 1 i-1: a -move-> c
1568 o 1 i-1: a -move-> c
1556 |
1569 |
1557 o 0 i-0 initial commit: a b h
1570 o 0 i-0 initial commit: a b h
1558
1571
1559
1572
1560 'a' is the copy source of 'd'
1573 'a' is the copy source of 'd'
1561
1574
1562 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mCGm-0")'
1575 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mCGm-0")'
1563 A d
1576 A d
1564 a (no-compatibility no-changeset !)
1577 a (no-compatibility no-changeset !)
1565 R a
1578 R a
1566 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mGCm-0")'
1579 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mGCm-0")'
1567 A d
1580 A d
1568 a (no-compatibility no-changeset !)
1581 a (no-compatibility no-changeset !)
1569 R a
1582 R a
1570 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mCGm-0")'
1583 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mCGm-0")'
1571 A d
1584 A d
1572 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mGCm-0")'
1585 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mGCm-0")'
1573 A d
1586 A d
1574 $ hg status --copies --rev 'desc("g-1")' --rev 'desc("mCGm-0")'
1587 $ hg status --copies --rev 'desc("g-1")' --rev 'desc("mCGm-0")'
1575 $ hg status --copies --rev 'desc("g-1")' --rev 'desc("mGCm-0")'
1588 $ hg status --copies --rev 'desc("g-1")' --rev 'desc("mGCm-0")'
1576
1589
1577
1590
1578 Comparing with merge restoring an untouched deleted file
1591 Comparing with merge restoring an untouched deleted file
1579 --------------------------------------------------------
1592 --------------------------------------------------------
1580
1593
1581 Merge:
1594 Merge:
1582 - one removing a file (d)
1595 - one removing a file (d)
1583 - one leaving the file untouched
1596 - one leaving the file untouched
1584 - the merge actively restore the file to the same content.
1597 - the merge actively restore the file to the same content.
1585
1598
1586 In this case, the file keep on living after the merge. So we should not drop its
1599 In this case, the file keep on living after the merge. So we should not drop its
1587 copy tracing chain.
1600 copy tracing chain.
1588
1601
1589 $ hg log -G --rev '::(desc("mCB-revert-m")+desc("mBC-revert-m"))'
1602 $ hg log -G --rev '::(desc("mCB-revert-m")+desc("mBC-revert-m"))'
1590 o 33 mBC-revert-m-0
1603 o 33 mBC-revert-m-0
1591 |\
1604 |\
1592 +---o 32 mCB-revert-m-0
1605 +---o 32 mCB-revert-m-0
1593 | |/
1606 | |/
1594 | o 6 c-1 delete d
1607 | o 6 c-1 delete d
1595 | |
1608 | |
1596 o | 5 b-1: b update
1609 o | 5 b-1: b update
1597 |/
1610 |/
1598 o 2 i-2: c -move-> d
1611 o 2 i-2: c -move-> d
1599 |
1612 |
1600 o 1 i-1: a -move-> c
1613 o 1 i-1: a -move-> c
1601 |
1614 |
1602 o 0 i-0 initial commit: a b h
1615 o 0 i-0 initial commit: a b h
1603
1616
1604
1617
1605 'a' is the the copy source of 'd'
1618 'a' is the the copy source of 'd'
1606
1619
1607 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mCB-revert-m-0")'
1620 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mCB-revert-m-0")'
1608 M b
1621 M b
1609 A d
1622 A d
1610 a (no-compatibility no-changeset !)
1623 a (no-compatibility no-changeset !)
1611 R a
1624 R a
1612 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mBC-revert-m-0")'
1625 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mBC-revert-m-0")'
1613 M b
1626 M b
1614 A d
1627 A d
1615 a (no-compatibility no-changeset !)
1628 a (no-compatibility no-changeset !)
1616 R a
1629 R a
1617 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mCB-revert-m-0")'
1630 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mCB-revert-m-0")'
1618 M b
1631 M b
1619 A d
1632 A d
1620 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mBC-revert-m-0")'
1633 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mBC-revert-m-0")'
1621 M b
1634 M b
1622 A d
1635 A d
1623 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mCB-revert-m-0")'
1636 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mCB-revert-m-0")'
1624 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mBC-revert-m-0")'
1637 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mBC-revert-m-0")'
1625
1638
1626
1639
1627 Merging a branch where a rename was deleted with a branch where the same file was renamed
1640 Merging a branch where a rename was deleted with a branch where the same file was renamed
1628 ------------------------------------------------------------------------------------------
1641 ------------------------------------------------------------------------------------------
1629
1642
1630 Create a "conflicting" merge where `d` get removed on one branch before its
1643 Create a "conflicting" merge where `d` get removed on one branch before its
1631 rename information actually conflict with the other branch.
1644 rename information actually conflict with the other branch.
1632
1645
1633 (the copy information from the branch that was not deleted should win).
1646 (the copy information from the branch that was not deleted should win).
1634
1647
1635 $ hg log -G --rev '::(desc("mCH-delete-before-conflict-m")+desc("mHC-delete-before-conflict-m"))'
1648 $ hg log -G --rev '::(desc("mCH-delete-before-conflict-m")+desc("mHC-delete-before-conflict-m"))'
1636 @ 36 mHC-delete-before-conflict-m-0
1649 @ 36 mHC-delete-before-conflict-m-0
1637 |\
1650 |\
1638 +---o 35 mCH-delete-before-conflict-m-0
1651 +---o 35 mCH-delete-before-conflict-m-0
1639 | |/
1652 | |/
1640 | o 34 h-1: b -(move)-> d
1653 | o 34 h-1: b -(move)-> d
1641 | |
1654 | |
1642 o | 6 c-1 delete d
1655 o | 6 c-1 delete d
1643 | |
1656 | |
1644 o | 2 i-2: c -move-> d
1657 o | 2 i-2: c -move-> d
1645 | |
1658 | |
1646 o | 1 i-1: a -move-> c
1659 o | 1 i-1: a -move-> c
1647 |/
1660 |/
1648 o 0 i-0 initial commit: a b h
1661 o 0 i-0 initial commit: a b h
1649
1662
1650
1663
1651 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mCH-delete-before-conflict-m")'
1664 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mCH-delete-before-conflict-m")'
1652 A d
1665 A d
1653 b (no-compatibility no-changeset !)
1666 b (no-compatibility no-changeset !)
1654 R a
1667 R a
1655 R b
1668 R b
1656 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mHC-delete-before-conflict-m")'
1669 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mHC-delete-before-conflict-m")'
1657 A d
1670 A d
1658 b
1671 b
1659 R a
1672 R a
1660 R b
1673 R b
1661 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mCH-delete-before-conflict-m")'
1674 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mCH-delete-before-conflict-m")'
1662 A d
1675 A d
1663 b
1676 b
1664 R b
1677 R b
1665 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mHC-delete-before-conflict-m")'
1678 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mHC-delete-before-conflict-m")'
1666 A d
1679 A d
1667 b
1680 b
1668 R b
1681 R b
1669 $ hg status --copies --rev 'desc("h-1")' --rev 'desc("mCH-delete-before-conflict-m")'
1682 $ hg status --copies --rev 'desc("h-1")' --rev 'desc("mCH-delete-before-conflict-m")'
1670 R a
1683 R a
1671 $ hg status --copies --rev 'desc("h-1")' --rev 'desc("mHC-delete-before-conflict-m")'
1684 $ hg status --copies --rev 'desc("h-1")' --rev 'desc("mHC-delete-before-conflict-m")'
1672 R a
1685 R a
General Comments 0
You need to be logged in to leave comments. Login now