##// END OF EJS Templates
copies: drop the findlimit logic...
marmoute -
r43470:069cbbb5 default
parent child Browse files
Show More
@@ -1,1011 +1,923 b''
1 # copies.py - copy detection for Mercurial
1 # copies.py - copy detection for Mercurial
2 #
2 #
3 # Copyright 2008 Matt Mackall <mpm@selenic.com>
3 # Copyright 2008 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import collections
10 import collections
11 import heapq
11 import heapq
12 import os
12 import os
13
13
14 from .i18n import _
14 from .i18n import _
15
15
16
16
17 from .revlogutils.flagutil import REVIDX_SIDEDATA
17 from .revlogutils.flagutil import REVIDX_SIDEDATA
18
18
19 from . import (
19 from . import (
20 error,
20 error,
21 match as matchmod,
21 match as matchmod,
22 node,
22 node,
23 pathutil,
23 pathutil,
24 pycompat,
24 pycompat,
25 util,
25 util,
26 )
26 )
27
27
28 from .revlogutils import sidedata as sidedatamod
28 from .revlogutils import sidedata as sidedatamod
29
29
30 from .utils import stringutil
30 from .utils import stringutil
31
31
32
32
33 def _findlimit(repo, ctxa, ctxb):
34 """
35 Find the last revision that needs to be checked to ensure that a full
36 transitive closure for file copies can be properly calculated.
37 Generally, this means finding the earliest revision number that's an
38 ancestor of a or b but not both, except when a or b is a direct descendent
39 of the other, in which case we can return the minimum revnum of a and b.
40 """
41
42 # basic idea:
43 # - mark a and b with different sides
44 # - if a parent's children are all on the same side, the parent is
45 # on that side, otherwise it is on no side
46 # - walk the graph in topological order with the help of a heap;
47 # - add unseen parents to side map
48 # - clear side of any parent that has children on different sides
49 # - track number of interesting revs that might still be on a side
50 # - track the lowest interesting rev seen
51 # - quit when interesting revs is zero
52
53 cl = repo.changelog
54 wdirparents = None
55 a = ctxa.rev()
56 b = ctxb.rev()
57 if a is None:
58 wdirparents = (ctxa.p1(), ctxa.p2())
59 a = node.wdirrev
60 if b is None:
61 assert not wdirparents
62 wdirparents = (ctxb.p1(), ctxb.p2())
63 b = node.wdirrev
64
65 side = {a: -1, b: 1}
66 visit = [-a, -b]
67 heapq.heapify(visit)
68 interesting = len(visit)
69 limit = node.wdirrev
70
71 while interesting:
72 r = -(heapq.heappop(visit))
73 if r == node.wdirrev:
74 parents = [pctx.rev() for pctx in wdirparents]
75 else:
76 parents = cl.parentrevs(r)
77 if parents[1] == node.nullrev:
78 parents = parents[:1]
79 for p in parents:
80 if p not in side:
81 # first time we see p; add it to visit
82 side[p] = side[r]
83 if side[p]:
84 interesting += 1
85 heapq.heappush(visit, -p)
86 elif side[p] and side[p] != side[r]:
87 # p was interesting but now we know better
88 side[p] = 0
89 interesting -= 1
90 if side[r]:
91 limit = r # lowest rev visited
92 interesting -= 1
93
94 # Consider the following flow (see test-commit-amend.t under issue4405):
95 # 1/ File 'a0' committed
96 # 2/ File renamed from 'a0' to 'a1' in a new commit (call it 'a1')
97 # 3/ Move back to first commit
98 # 4/ Create a new commit via revert to contents of 'a1' (call it 'a1-amend')
99 # 5/ Rename file from 'a1' to 'a2' and commit --amend 'a1-msg'
100 #
101 # During the amend in step five, we will be in this state:
102 #
103 # @ 3 temporary amend commit for a1-amend
104 # |
105 # o 2 a1-amend
106 # |
107 # | o 1 a1
108 # |/
109 # o 0 a0
110 #
111 # When _findlimit is called, a and b are revs 3 and 0, so limit will be 2,
112 # yet the filelog has the copy information in rev 1 and we will not look
113 # back far enough unless we also look at the a and b as candidates.
114 # This only occurs when a is a descendent of b or visa-versa.
115 return min(limit, a, b)
116
117
118 def _filter(src, dst, t):
33 def _filter(src, dst, t):
119 """filters out invalid copies after chaining"""
34 """filters out invalid copies after chaining"""
120
35
121 # When _chain()'ing copies in 'a' (from 'src' via some other commit 'mid')
36 # When _chain()'ing copies in 'a' (from 'src' via some other commit 'mid')
122 # with copies in 'b' (from 'mid' to 'dst'), we can get the different cases
37 # with copies in 'b' (from 'mid' to 'dst'), we can get the different cases
123 # in the following table (not including trivial cases). For example, case 2
38 # in the following table (not including trivial cases). For example, case 2
124 # is where a file existed in 'src' and remained under that name in 'mid' and
39 # is where a file existed in 'src' and remained under that name in 'mid' and
125 # then was renamed between 'mid' and 'dst'.
40 # then was renamed between 'mid' and 'dst'.
126 #
41 #
127 # case src mid dst result
42 # case src mid dst result
128 # 1 x y - -
43 # 1 x y - -
129 # 2 x y y x->y
44 # 2 x y y x->y
130 # 3 x y x -
45 # 3 x y x -
131 # 4 x y z x->z
46 # 4 x y z x->z
132 # 5 - x y -
47 # 5 - x y -
133 # 6 x x y x->y
48 # 6 x x y x->y
134 #
49 #
135 # _chain() takes care of chaining the copies in 'a' and 'b', but it
50 # _chain() takes care of chaining the copies in 'a' and 'b', but it
136 # cannot tell the difference between cases 1 and 2, between 3 and 4, or
51 # cannot tell the difference between cases 1 and 2, between 3 and 4, or
137 # between 5 and 6, so it includes all cases in its result.
52 # between 5 and 6, so it includes all cases in its result.
138 # Cases 1, 3, and 5 are then removed by _filter().
53 # Cases 1, 3, and 5 are then removed by _filter().
139
54
140 for k, v in list(t.items()):
55 for k, v in list(t.items()):
141 # remove copies from files that didn't exist
56 # remove copies from files that didn't exist
142 if v not in src:
57 if v not in src:
143 del t[k]
58 del t[k]
144 # remove criss-crossed copies
59 # remove criss-crossed copies
145 elif k in src and v in dst:
60 elif k in src and v in dst:
146 del t[k]
61 del t[k]
147 # remove copies to files that were then removed
62 # remove copies to files that were then removed
148 elif k not in dst:
63 elif k not in dst:
149 del t[k]
64 del t[k]
150
65
151
66
152 def _chain(a, b):
67 def _chain(a, b):
153 """chain two sets of copies 'a' and 'b'"""
68 """chain two sets of copies 'a' and 'b'"""
154 t = a.copy()
69 t = a.copy()
155 for k, v in pycompat.iteritems(b):
70 for k, v in pycompat.iteritems(b):
156 if v in t:
71 if v in t:
157 t[k] = t[v]
72 t[k] = t[v]
158 else:
73 else:
159 t[k] = v
74 t[k] = v
160 return t
75 return t
161
76
162
77
163 def _tracefile(fctx, am, basemf, limit):
78 def _tracefile(fctx, am, basemf):
164 """return file context that is the ancestor of fctx present in ancestor
79 """return file context that is the ancestor of fctx present in ancestor
165 manifest am
80 manifest am
166
81
167 Note: we used to try and stop after a given limit, however checking if that
82 Note: we used to try and stop after a given limit, however checking if that
168 limit is reached turned out to be very expensive. we are better off
83 limit is reached turned out to be very expensive. we are better off
169 disabling that feature."""
84 disabling that feature."""
170
85
171 for f in fctx.ancestors():
86 for f in fctx.ancestors():
172 path = f.path()
87 path = f.path()
173 if am.get(path, None) == f.filenode():
88 if am.get(path, None) == f.filenode():
174 return path
89 return path
175 if basemf and basemf.get(path, None) == f.filenode():
90 if basemf and basemf.get(path, None) == f.filenode():
176 return path
91 return path
177
92
178
93
179 def _dirstatecopies(repo, match=None):
94 def _dirstatecopies(repo, match=None):
180 ds = repo.dirstate
95 ds = repo.dirstate
181 c = ds.copies().copy()
96 c = ds.copies().copy()
182 for k in list(c):
97 for k in list(c):
183 if ds[k] not in b'anm' or (match and not match(k)):
98 if ds[k] not in b'anm' or (match and not match(k)):
184 del c[k]
99 del c[k]
185 return c
100 return c
186
101
187
102
188 def _computeforwardmissing(a, b, match=None):
103 def _computeforwardmissing(a, b, match=None):
189 """Computes which files are in b but not a.
104 """Computes which files are in b but not a.
190 This is its own function so extensions can easily wrap this call to see what
105 This is its own function so extensions can easily wrap this call to see what
191 files _forwardcopies is about to process.
106 files _forwardcopies is about to process.
192 """
107 """
193 ma = a.manifest()
108 ma = a.manifest()
194 mb = b.manifest()
109 mb = b.manifest()
195 return mb.filesnotin(ma, match=match)
110 return mb.filesnotin(ma, match=match)
196
111
197
112
198 def usechangesetcentricalgo(repo):
113 def usechangesetcentricalgo(repo):
199 """Checks if we should use changeset-centric copy algorithms"""
114 """Checks if we should use changeset-centric copy algorithms"""
200 if repo.filecopiesmode == b'changeset-sidedata':
115 if repo.filecopiesmode == b'changeset-sidedata':
201 return True
116 return True
202 readfrom = repo.ui.config(b'experimental', b'copies.read-from')
117 readfrom = repo.ui.config(b'experimental', b'copies.read-from')
203 changesetsource = (b'changeset-only', b'compatibility')
118 changesetsource = (b'changeset-only', b'compatibility')
204 return readfrom in changesetsource
119 return readfrom in changesetsource
205
120
206
121
207 def _committedforwardcopies(a, b, base, match):
122 def _committedforwardcopies(a, b, base, match):
208 """Like _forwardcopies(), but b.rev() cannot be None (working copy)"""
123 """Like _forwardcopies(), but b.rev() cannot be None (working copy)"""
209 # files might have to be traced back to the fctx parent of the last
124 # files might have to be traced back to the fctx parent of the last
210 # one-side-only changeset, but not further back than that
125 # one-side-only changeset, but not further back than that
211 repo = a._repo
126 repo = a._repo
212
127
213 if usechangesetcentricalgo(repo):
128 if usechangesetcentricalgo(repo):
214 return _changesetforwardcopies(a, b, match)
129 return _changesetforwardcopies(a, b, match)
215
130
216 debug = repo.ui.debugflag and repo.ui.configbool(b'devel', b'debug.copies')
131 debug = repo.ui.debugflag and repo.ui.configbool(b'devel', b'debug.copies')
217 dbg = repo.ui.debug
132 dbg = repo.ui.debug
218 if debug:
133 if debug:
219 dbg(b'debug.copies: looking into rename from %s to %s\n' % (a, b))
134 dbg(b'debug.copies: looking into rename from %s to %s\n' % (a, b))
220 limit = _findlimit(repo, a, b)
221 if debug:
222 dbg(b'debug.copies: search limit: %d\n' % limit)
223 am = a.manifest()
135 am = a.manifest()
224 basemf = None if base is None else base.manifest()
136 basemf = None if base is None else base.manifest()
225
137
226 # find where new files came from
138 # find where new files came from
227 # we currently don't try to find where old files went, too expensive
139 # we currently don't try to find where old files went, too expensive
228 # this means we can miss a case like 'hg rm b; hg cp a b'
140 # this means we can miss a case like 'hg rm b; hg cp a b'
229 cm = {}
141 cm = {}
230
142
231 # Computing the forward missing is quite expensive on large manifests, since
143 # Computing the forward missing is quite expensive on large manifests, since
232 # it compares the entire manifests. We can optimize it in the common use
144 # it compares the entire manifests. We can optimize it in the common use
233 # case of computing what copies are in a commit versus its parent (like
145 # case of computing what copies are in a commit versus its parent (like
234 # during a rebase or histedit). Note, we exclude merge commits from this
146 # during a rebase or histedit). Note, we exclude merge commits from this
235 # optimization, since the ctx.files() for a merge commit is not correct for
147 # optimization, since the ctx.files() for a merge commit is not correct for
236 # this comparison.
148 # this comparison.
237 forwardmissingmatch = match
149 forwardmissingmatch = match
238 if b.p1() == a and b.p2().node() == node.nullid:
150 if b.p1() == a and b.p2().node() == node.nullid:
239 filesmatcher = matchmod.exact(b.files())
151 filesmatcher = matchmod.exact(b.files())
240 forwardmissingmatch = matchmod.intersectmatchers(match, filesmatcher)
152 forwardmissingmatch = matchmod.intersectmatchers(match, filesmatcher)
241 missing = _computeforwardmissing(a, b, match=forwardmissingmatch)
153 missing = _computeforwardmissing(a, b, match=forwardmissingmatch)
242
154
243 ancestrycontext = a._repo.changelog.ancestors([b.rev()], inclusive=True)
155 ancestrycontext = a._repo.changelog.ancestors([b.rev()], inclusive=True)
244
156
245 if debug:
157 if debug:
246 dbg(b'debug.copies: missing files to search: %d\n' % len(missing))
158 dbg(b'debug.copies: missing files to search: %d\n' % len(missing))
247
159
248 for f in sorted(missing):
160 for f in sorted(missing):
249 if debug:
161 if debug:
250 dbg(b'debug.copies: tracing file: %s\n' % f)
162 dbg(b'debug.copies: tracing file: %s\n' % f)
251 fctx = b[f]
163 fctx = b[f]
252 fctx._ancestrycontext = ancestrycontext
164 fctx._ancestrycontext = ancestrycontext
253
165
254 if debug:
166 if debug:
255 start = util.timer()
167 start = util.timer()
256 opath = _tracefile(fctx, am, basemf, limit)
168 opath = _tracefile(fctx, am, basemf)
257 if opath:
169 if opath:
258 if debug:
170 if debug:
259 dbg(b'debug.copies: rename of: %s\n' % opath)
171 dbg(b'debug.copies: rename of: %s\n' % opath)
260 cm[f] = opath
172 cm[f] = opath
261 if debug:
173 if debug:
262 dbg(
174 dbg(
263 b'debug.copies: time: %f seconds\n'
175 b'debug.copies: time: %f seconds\n'
264 % (util.timer() - start)
176 % (util.timer() - start)
265 )
177 )
266 return cm
178 return cm
267
179
268
180
269 def _changesetforwardcopies(a, b, match):
181 def _changesetforwardcopies(a, b, match):
270 if a.rev() in (node.nullrev, b.rev()):
182 if a.rev() in (node.nullrev, b.rev()):
271 return {}
183 return {}
272
184
273 repo = a.repo()
185 repo = a.repo()
274 children = {}
186 children = {}
275 cl = repo.changelog
187 cl = repo.changelog
276 missingrevs = cl.findmissingrevs(common=[a.rev()], heads=[b.rev()])
188 missingrevs = cl.findmissingrevs(common=[a.rev()], heads=[b.rev()])
277 for r in missingrevs:
189 for r in missingrevs:
278 for p in cl.parentrevs(r):
190 for p in cl.parentrevs(r):
279 if p == node.nullrev:
191 if p == node.nullrev:
280 continue
192 continue
281 if p not in children:
193 if p not in children:
282 children[p] = [r]
194 children[p] = [r]
283 else:
195 else:
284 children[p].append(r)
196 children[p].append(r)
285
197
286 roots = set(children) - set(missingrevs)
198 roots = set(children) - set(missingrevs)
287 # 'work' contains 3-tuples of a (revision number, parent number, copies).
199 # 'work' contains 3-tuples of a (revision number, parent number, copies).
288 # The parent number is only used for knowing which parent the copies dict
200 # The parent number is only used for knowing which parent the copies dict
289 # came from.
201 # came from.
290 # NOTE: To reduce costly copying the 'copies' dicts, we reuse the same
202 # NOTE: To reduce costly copying the 'copies' dicts, we reuse the same
291 # instance for *one* of the child nodes (the last one). Once an instance
203 # instance for *one* of the child nodes (the last one). Once an instance
292 # has been put on the queue, it is thus no longer safe to modify it.
204 # has been put on the queue, it is thus no longer safe to modify it.
293 # Conversely, it *is* safe to modify an instance popped off the queue.
205 # Conversely, it *is* safe to modify an instance popped off the queue.
294 work = [(r, 1, {}) for r in roots]
206 work = [(r, 1, {}) for r in roots]
295 heapq.heapify(work)
207 heapq.heapify(work)
296 alwaysmatch = match.always()
208 alwaysmatch = match.always()
297 while work:
209 while work:
298 r, i1, copies = heapq.heappop(work)
210 r, i1, copies = heapq.heappop(work)
299 if work and work[0][0] == r:
211 if work and work[0][0] == r:
300 # We are tracing copies from both parents
212 # We are tracing copies from both parents
301 r, i2, copies2 = heapq.heappop(work)
213 r, i2, copies2 = heapq.heappop(work)
302 for dst, src in copies2.items():
214 for dst, src in copies2.items():
303 # Unlike when copies are stored in the filelog, we consider
215 # Unlike when copies are stored in the filelog, we consider
304 # it a copy even if the destination already existed on the
216 # it a copy even if the destination already existed on the
305 # other branch. It's simply too expensive to check if the
217 # other branch. It's simply too expensive to check if the
306 # file existed in the manifest.
218 # file existed in the manifest.
307 if dst not in copies:
219 if dst not in copies:
308 # If it was copied on the p1 side, leave it as copied from
220 # If it was copied on the p1 side, leave it as copied from
309 # that side, even if it was also copied on the p2 side.
221 # that side, even if it was also copied on the p2 side.
310 copies[dst] = copies2[dst]
222 copies[dst] = copies2[dst]
311 if r == b.rev():
223 if r == b.rev():
312 return copies
224 return copies
313 for i, c in enumerate(children[r]):
225 for i, c in enumerate(children[r]):
314 childctx = repo[c]
226 childctx = repo[c]
315 if r == childctx.p1().rev():
227 if r == childctx.p1().rev():
316 parent = 1
228 parent = 1
317 childcopies = childctx.p1copies()
229 childcopies = childctx.p1copies()
318 else:
230 else:
319 assert r == childctx.p2().rev()
231 assert r == childctx.p2().rev()
320 parent = 2
232 parent = 2
321 childcopies = childctx.p2copies()
233 childcopies = childctx.p2copies()
322 if not alwaysmatch:
234 if not alwaysmatch:
323 childcopies = {
235 childcopies = {
324 dst: src for dst, src in childcopies.items() if match(dst)
236 dst: src for dst, src in childcopies.items() if match(dst)
325 }
237 }
326 # Copy the dict only if later iterations will also need it
238 # Copy the dict only if later iterations will also need it
327 if i != len(children[r]) - 1:
239 if i != len(children[r]) - 1:
328 newcopies = copies.copy()
240 newcopies = copies.copy()
329 else:
241 else:
330 newcopies = copies
242 newcopies = copies
331 if childcopies:
243 if childcopies:
332 newcopies = _chain(newcopies, childcopies)
244 newcopies = _chain(newcopies, childcopies)
333 for f in childctx.filesremoved():
245 for f in childctx.filesremoved():
334 if f in newcopies:
246 if f in newcopies:
335 del newcopies[f]
247 del newcopies[f]
336 heapq.heappush(work, (c, parent, newcopies))
248 heapq.heappush(work, (c, parent, newcopies))
337 assert False
249 assert False
338
250
339
251
340 def _forwardcopies(a, b, base=None, match=None):
252 def _forwardcopies(a, b, base=None, match=None):
341 """find {dst@b: src@a} copy mapping where a is an ancestor of b"""
253 """find {dst@b: src@a} copy mapping where a is an ancestor of b"""
342
254
343 if base is None:
255 if base is None:
344 base = a
256 base = a
345 match = a.repo().narrowmatch(match)
257 match = a.repo().narrowmatch(match)
346 # check for working copy
258 # check for working copy
347 if b.rev() is None:
259 if b.rev() is None:
348 cm = _committedforwardcopies(a, b.p1(), base, match)
260 cm = _committedforwardcopies(a, b.p1(), base, match)
349 # combine copies from dirstate if necessary
261 # combine copies from dirstate if necessary
350 copies = _chain(cm, _dirstatecopies(b._repo, match))
262 copies = _chain(cm, _dirstatecopies(b._repo, match))
351 else:
263 else:
352 copies = _committedforwardcopies(a, b, base, match)
264 copies = _committedforwardcopies(a, b, base, match)
353 return copies
265 return copies
354
266
355
267
356 def _backwardrenames(a, b, match):
268 def _backwardrenames(a, b, match):
357 if a._repo.ui.config(b'experimental', b'copytrace') == b'off':
269 if a._repo.ui.config(b'experimental', b'copytrace') == b'off':
358 return {}
270 return {}
359
271
360 # Even though we're not taking copies into account, 1:n rename situations
272 # Even though we're not taking copies into account, 1:n rename situations
361 # can still exist (e.g. hg cp a b; hg mv a c). In those cases we
273 # can still exist (e.g. hg cp a b; hg mv a c). In those cases we
362 # arbitrarily pick one of the renames.
274 # arbitrarily pick one of the renames.
363 # We don't want to pass in "match" here, since that would filter
275 # We don't want to pass in "match" here, since that would filter
364 # the destination by it. Since we're reversing the copies, we want
276 # the destination by it. Since we're reversing the copies, we want
365 # to filter the source instead.
277 # to filter the source instead.
366 f = _forwardcopies(b, a)
278 f = _forwardcopies(b, a)
367 r = {}
279 r = {}
368 for k, v in sorted(pycompat.iteritems(f)):
280 for k, v in sorted(pycompat.iteritems(f)):
369 if match and not match(v):
281 if match and not match(v):
370 continue
282 continue
371 # remove copies
283 # remove copies
372 if v in a:
284 if v in a:
373 continue
285 continue
374 r[v] = k
286 r[v] = k
375 return r
287 return r
376
288
377
289
378 def pathcopies(x, y, match=None):
290 def pathcopies(x, y, match=None):
379 """find {dst@y: src@x} copy mapping for directed compare"""
291 """find {dst@y: src@x} copy mapping for directed compare"""
380 repo = x._repo
292 repo = x._repo
381 debug = repo.ui.debugflag and repo.ui.configbool(b'devel', b'debug.copies')
293 debug = repo.ui.debugflag and repo.ui.configbool(b'devel', b'debug.copies')
382 if debug:
294 if debug:
383 repo.ui.debug(
295 repo.ui.debug(
384 b'debug.copies: searching copies from %s to %s\n' % (x, y)
296 b'debug.copies: searching copies from %s to %s\n' % (x, y)
385 )
297 )
386 if x == y or not x or not y:
298 if x == y or not x or not y:
387 return {}
299 return {}
388 a = y.ancestor(x)
300 a = y.ancestor(x)
389 if a == x:
301 if a == x:
390 if debug:
302 if debug:
391 repo.ui.debug(b'debug.copies: search mode: forward\n')
303 repo.ui.debug(b'debug.copies: search mode: forward\n')
392 if y.rev() is None and x == y.p1():
304 if y.rev() is None and x == y.p1():
393 # short-circuit to avoid issues with merge states
305 # short-circuit to avoid issues with merge states
394 return _dirstatecopies(repo, match)
306 return _dirstatecopies(repo, match)
395 copies = _forwardcopies(x, y, match=match)
307 copies = _forwardcopies(x, y, match=match)
396 elif a == y:
308 elif a == y:
397 if debug:
309 if debug:
398 repo.ui.debug(b'debug.copies: search mode: backward\n')
310 repo.ui.debug(b'debug.copies: search mode: backward\n')
399 copies = _backwardrenames(x, y, match=match)
311 copies = _backwardrenames(x, y, match=match)
400 else:
312 else:
401 if debug:
313 if debug:
402 repo.ui.debug(b'debug.copies: search mode: combined\n')
314 repo.ui.debug(b'debug.copies: search mode: combined\n')
403 base = None
315 base = None
404 if a.rev() != node.nullrev:
316 if a.rev() != node.nullrev:
405 base = x
317 base = x
406 copies = _chain(
318 copies = _chain(
407 _backwardrenames(x, a, match=match),
319 _backwardrenames(x, a, match=match),
408 _forwardcopies(a, y, base, match=match),
320 _forwardcopies(a, y, base, match=match),
409 )
321 )
410 _filter(x, y, copies)
322 _filter(x, y, copies)
411 return copies
323 return copies
412
324
413
325
414 def mergecopies(repo, c1, c2, base):
326 def mergecopies(repo, c1, c2, base):
415 """
327 """
416 Finds moves and copies between context c1 and c2 that are relevant for
328 Finds moves and copies between context c1 and c2 that are relevant for
417 merging. 'base' will be used as the merge base.
329 merging. 'base' will be used as the merge base.
418
330
419 Copytracing is used in commands like rebase, merge, unshelve, etc to merge
331 Copytracing is used in commands like rebase, merge, unshelve, etc to merge
420 files that were moved/ copied in one merge parent and modified in another.
332 files that were moved/ copied in one merge parent and modified in another.
421 For example:
333 For example:
422
334
423 o ---> 4 another commit
335 o ---> 4 another commit
424 |
336 |
425 | o ---> 3 commit that modifies a.txt
337 | o ---> 3 commit that modifies a.txt
426 | /
338 | /
427 o / ---> 2 commit that moves a.txt to b.txt
339 o / ---> 2 commit that moves a.txt to b.txt
428 |/
340 |/
429 o ---> 1 merge base
341 o ---> 1 merge base
430
342
431 If we try to rebase revision 3 on revision 4, since there is no a.txt in
343 If we try to rebase revision 3 on revision 4, since there is no a.txt in
432 revision 4, and if user have copytrace disabled, we prints the following
344 revision 4, and if user have copytrace disabled, we prints the following
433 message:
345 message:
434
346
435 ```other changed <file> which local deleted```
347 ```other changed <file> which local deleted```
436
348
437 Returns five dicts: "copy", "movewithdir", "diverge", "renamedelete" and
349 Returns five dicts: "copy", "movewithdir", "diverge", "renamedelete" and
438 "dirmove".
350 "dirmove".
439
351
440 "copy" is a mapping from destination name -> source name,
352 "copy" is a mapping from destination name -> source name,
441 where source is in c1 and destination is in c2 or vice-versa.
353 where source is in c1 and destination is in c2 or vice-versa.
442
354
443 "movewithdir" is a mapping from source name -> destination name,
355 "movewithdir" is a mapping from source name -> destination name,
444 where the file at source present in one context but not the other
356 where the file at source present in one context but not the other
445 needs to be moved to destination by the merge process, because the
357 needs to be moved to destination by the merge process, because the
446 other context moved the directory it is in.
358 other context moved the directory it is in.
447
359
448 "diverge" is a mapping of source name -> list of destination names
360 "diverge" is a mapping of source name -> list of destination names
449 for divergent renames.
361 for divergent renames.
450
362
451 "renamedelete" is a mapping of source name -> list of destination
363 "renamedelete" is a mapping of source name -> list of destination
452 names for files deleted in c1 that were renamed in c2 or vice-versa.
364 names for files deleted in c1 that were renamed in c2 or vice-versa.
453
365
454 "dirmove" is a mapping of detected source dir -> destination dir renames.
366 "dirmove" is a mapping of detected source dir -> destination dir renames.
455 This is needed for handling changes to new files previously grafted into
367 This is needed for handling changes to new files previously grafted into
456 renamed directories.
368 renamed directories.
457
369
458 This function calls different copytracing algorithms based on config.
370 This function calls different copytracing algorithms based on config.
459 """
371 """
460 # avoid silly behavior for update from empty dir
372 # avoid silly behavior for update from empty dir
461 if not c1 or not c2 or c1 == c2:
373 if not c1 or not c2 or c1 == c2:
462 return {}, {}, {}, {}, {}
374 return {}, {}, {}, {}, {}
463
375
464 narrowmatch = c1.repo().narrowmatch()
376 narrowmatch = c1.repo().narrowmatch()
465
377
466 # avoid silly behavior for parent -> working dir
378 # avoid silly behavior for parent -> working dir
467 if c2.node() is None and c1.node() == repo.dirstate.p1():
379 if c2.node() is None and c1.node() == repo.dirstate.p1():
468 return _dirstatecopies(repo, narrowmatch), {}, {}, {}, {}
380 return _dirstatecopies(repo, narrowmatch), {}, {}, {}, {}
469
381
470 copytracing = repo.ui.config(b'experimental', b'copytrace')
382 copytracing = repo.ui.config(b'experimental', b'copytrace')
471 if stringutil.parsebool(copytracing) is False:
383 if stringutil.parsebool(copytracing) is False:
472 # stringutil.parsebool() returns None when it is unable to parse the
384 # stringutil.parsebool() returns None when it is unable to parse the
473 # value, so we should rely on making sure copytracing is on such cases
385 # value, so we should rely on making sure copytracing is on such cases
474 return {}, {}, {}, {}, {}
386 return {}, {}, {}, {}, {}
475
387
476 if usechangesetcentricalgo(repo):
388 if usechangesetcentricalgo(repo):
477 # The heuristics don't make sense when we need changeset-centric algos
389 # The heuristics don't make sense when we need changeset-centric algos
478 return _fullcopytracing(repo, c1, c2, base)
390 return _fullcopytracing(repo, c1, c2, base)
479
391
480 # Copy trace disabling is explicitly below the node == p1 logic above
392 # Copy trace disabling is explicitly below the node == p1 logic above
481 # because the logic above is required for a simple copy to be kept across a
393 # because the logic above is required for a simple copy to be kept across a
482 # rebase.
394 # rebase.
483 if copytracing == b'heuristics':
395 if copytracing == b'heuristics':
484 # Do full copytracing if only non-public revisions are involved as
396 # Do full copytracing if only non-public revisions are involved as
485 # that will be fast enough and will also cover the copies which could
397 # that will be fast enough and will also cover the copies which could
486 # be missed by heuristics
398 # be missed by heuristics
487 if _isfullcopytraceable(repo, c1, base):
399 if _isfullcopytraceable(repo, c1, base):
488 return _fullcopytracing(repo, c1, c2, base)
400 return _fullcopytracing(repo, c1, c2, base)
489 return _heuristicscopytracing(repo, c1, c2, base)
401 return _heuristicscopytracing(repo, c1, c2, base)
490 else:
402 else:
491 return _fullcopytracing(repo, c1, c2, base)
403 return _fullcopytracing(repo, c1, c2, base)
492
404
493
405
494 def _isfullcopytraceable(repo, c1, base):
406 def _isfullcopytraceable(repo, c1, base):
495 """ Checks that if base, source and destination are all no-public branches,
407 """ Checks that if base, source and destination are all no-public branches,
496 if yes let's use the full copytrace algorithm for increased capabilities
408 if yes let's use the full copytrace algorithm for increased capabilities
497 since it will be fast enough.
409 since it will be fast enough.
498
410
499 `experimental.copytrace.sourcecommitlimit` can be used to set a limit for
411 `experimental.copytrace.sourcecommitlimit` can be used to set a limit for
500 number of changesets from c1 to base such that if number of changesets are
412 number of changesets from c1 to base such that if number of changesets are
501 more than the limit, full copytracing algorithm won't be used.
413 more than the limit, full copytracing algorithm won't be used.
502 """
414 """
503 if c1.rev() is None:
415 if c1.rev() is None:
504 c1 = c1.p1()
416 c1 = c1.p1()
505 if c1.mutable() and base.mutable():
417 if c1.mutable() and base.mutable():
506 sourcecommitlimit = repo.ui.configint(
418 sourcecommitlimit = repo.ui.configint(
507 b'experimental', b'copytrace.sourcecommitlimit'
419 b'experimental', b'copytrace.sourcecommitlimit'
508 )
420 )
509 commits = len(repo.revs(b'%d::%d', base.rev(), c1.rev()))
421 commits = len(repo.revs(b'%d::%d', base.rev(), c1.rev()))
510 return commits < sourcecommitlimit
422 return commits < sourcecommitlimit
511 return False
423 return False
512
424
513
425
514 def _checksinglesidecopies(
426 def _checksinglesidecopies(
515 src, dsts1, m1, m2, mb, c2, base, copy, renamedelete
427 src, dsts1, m1, m2, mb, c2, base, copy, renamedelete
516 ):
428 ):
517 if src not in m2:
429 if src not in m2:
518 # deleted on side 2
430 # deleted on side 2
519 if src not in m1:
431 if src not in m1:
520 # renamed on side 1, deleted on side 2
432 # renamed on side 1, deleted on side 2
521 renamedelete[src] = dsts1
433 renamedelete[src] = dsts1
522 elif m2[src] != mb[src]:
434 elif m2[src] != mb[src]:
523 if not _related(c2[src], base[src]):
435 if not _related(c2[src], base[src]):
524 return
436 return
525 # modified on side 2
437 # modified on side 2
526 for dst in dsts1:
438 for dst in dsts1:
527 if dst not in m2:
439 if dst not in m2:
528 # dst not added on side 2 (handle as regular
440 # dst not added on side 2 (handle as regular
529 # "both created" case in manifestmerge otherwise)
441 # "both created" case in manifestmerge otherwise)
530 copy[dst] = src
442 copy[dst] = src
531
443
532
444
533 def _fullcopytracing(repo, c1, c2, base):
445 def _fullcopytracing(repo, c1, c2, base):
534 """ The full copytracing algorithm which finds all the new files that were
446 """ The full copytracing algorithm which finds all the new files that were
535 added from merge base up to the top commit and for each file it checks if
447 added from merge base up to the top commit and for each file it checks if
536 this file was copied from another file.
448 this file was copied from another file.
537
449
538 This is pretty slow when a lot of changesets are involved but will track all
450 This is pretty slow when a lot of changesets are involved but will track all
539 the copies.
451 the copies.
540 """
452 """
541 m1 = c1.manifest()
453 m1 = c1.manifest()
542 m2 = c2.manifest()
454 m2 = c2.manifest()
543 mb = base.manifest()
455 mb = base.manifest()
544
456
545 copies1 = pathcopies(base, c1)
457 copies1 = pathcopies(base, c1)
546 copies2 = pathcopies(base, c2)
458 copies2 = pathcopies(base, c2)
547
459
548 inversecopies1 = {}
460 inversecopies1 = {}
549 inversecopies2 = {}
461 inversecopies2 = {}
550 for dst, src in copies1.items():
462 for dst, src in copies1.items():
551 inversecopies1.setdefault(src, []).append(dst)
463 inversecopies1.setdefault(src, []).append(dst)
552 for dst, src in copies2.items():
464 for dst, src in copies2.items():
553 inversecopies2.setdefault(src, []).append(dst)
465 inversecopies2.setdefault(src, []).append(dst)
554
466
555 copy = {}
467 copy = {}
556 diverge = {}
468 diverge = {}
557 renamedelete = {}
469 renamedelete = {}
558 allsources = set(inversecopies1) | set(inversecopies2)
470 allsources = set(inversecopies1) | set(inversecopies2)
559 for src in allsources:
471 for src in allsources:
560 dsts1 = inversecopies1.get(src)
472 dsts1 = inversecopies1.get(src)
561 dsts2 = inversecopies2.get(src)
473 dsts2 = inversecopies2.get(src)
562 if dsts1 and dsts2:
474 if dsts1 and dsts2:
563 # copied/renamed on both sides
475 # copied/renamed on both sides
564 if src not in m1 and src not in m2:
476 if src not in m1 and src not in m2:
565 # renamed on both sides
477 # renamed on both sides
566 dsts1 = set(dsts1)
478 dsts1 = set(dsts1)
567 dsts2 = set(dsts2)
479 dsts2 = set(dsts2)
568 # If there's some overlap in the rename destinations, we
480 # If there's some overlap in the rename destinations, we
569 # consider it not divergent. For example, if side 1 copies 'a'
481 # consider it not divergent. For example, if side 1 copies 'a'
570 # to 'b' and 'c' and deletes 'a', and side 2 copies 'a' to 'c'
482 # to 'b' and 'c' and deletes 'a', and side 2 copies 'a' to 'c'
571 # and 'd' and deletes 'a'.
483 # and 'd' and deletes 'a'.
572 if dsts1 & dsts2:
484 if dsts1 & dsts2:
573 for dst in dsts1 & dsts2:
485 for dst in dsts1 & dsts2:
574 copy[dst] = src
486 copy[dst] = src
575 else:
487 else:
576 diverge[src] = sorted(dsts1 | dsts2)
488 diverge[src] = sorted(dsts1 | dsts2)
577 elif src in m1 and src in m2:
489 elif src in m1 and src in m2:
578 # copied on both sides
490 # copied on both sides
579 dsts1 = set(dsts1)
491 dsts1 = set(dsts1)
580 dsts2 = set(dsts2)
492 dsts2 = set(dsts2)
581 for dst in dsts1 & dsts2:
493 for dst in dsts1 & dsts2:
582 copy[dst] = src
494 copy[dst] = src
583 # TODO: Handle cases where it was renamed on one side and copied
495 # TODO: Handle cases where it was renamed on one side and copied
584 # on the other side
496 # on the other side
585 elif dsts1:
497 elif dsts1:
586 # copied/renamed only on side 1
498 # copied/renamed only on side 1
587 _checksinglesidecopies(
499 _checksinglesidecopies(
588 src, dsts1, m1, m2, mb, c2, base, copy, renamedelete
500 src, dsts1, m1, m2, mb, c2, base, copy, renamedelete
589 )
501 )
590 elif dsts2:
502 elif dsts2:
591 # copied/renamed only on side 2
503 # copied/renamed only on side 2
592 _checksinglesidecopies(
504 _checksinglesidecopies(
593 src, dsts2, m2, m1, mb, c1, base, copy, renamedelete
505 src, dsts2, m2, m1, mb, c1, base, copy, renamedelete
594 )
506 )
595
507
596 renamedeleteset = set()
508 renamedeleteset = set()
597 divergeset = set()
509 divergeset = set()
598 for dsts in diverge.values():
510 for dsts in diverge.values():
599 divergeset.update(dsts)
511 divergeset.update(dsts)
600 for dsts in renamedelete.values():
512 for dsts in renamedelete.values():
601 renamedeleteset.update(dsts)
513 renamedeleteset.update(dsts)
602
514
603 # find interesting file sets from manifests
515 # find interesting file sets from manifests
604 addedinm1 = m1.filesnotin(mb, repo.narrowmatch())
516 addedinm1 = m1.filesnotin(mb, repo.narrowmatch())
605 addedinm2 = m2.filesnotin(mb, repo.narrowmatch())
517 addedinm2 = m2.filesnotin(mb, repo.narrowmatch())
606 u1 = sorted(addedinm1 - addedinm2)
518 u1 = sorted(addedinm1 - addedinm2)
607 u2 = sorted(addedinm2 - addedinm1)
519 u2 = sorted(addedinm2 - addedinm1)
608
520
609 header = b" unmatched files in %s"
521 header = b" unmatched files in %s"
610 if u1:
522 if u1:
611 repo.ui.debug(b"%s:\n %s\n" % (header % b'local', b"\n ".join(u1)))
523 repo.ui.debug(b"%s:\n %s\n" % (header % b'local', b"\n ".join(u1)))
612 if u2:
524 if u2:
613 repo.ui.debug(b"%s:\n %s\n" % (header % b'other', b"\n ".join(u2)))
525 repo.ui.debug(b"%s:\n %s\n" % (header % b'other', b"\n ".join(u2)))
614
526
615 fullcopy = copies1.copy()
527 fullcopy = copies1.copy()
616 fullcopy.update(copies2)
528 fullcopy.update(copies2)
617 if not fullcopy:
529 if not fullcopy:
618 return copy, {}, diverge, renamedelete, {}
530 return copy, {}, diverge, renamedelete, {}
619
531
620 if repo.ui.debugflag:
532 if repo.ui.debugflag:
621 repo.ui.debug(
533 repo.ui.debug(
622 b" all copies found (* = to merge, ! = divergent, "
534 b" all copies found (* = to merge, ! = divergent, "
623 b"% = renamed and deleted):\n"
535 b"% = renamed and deleted):\n"
624 )
536 )
625 for f in sorted(fullcopy):
537 for f in sorted(fullcopy):
626 note = b""
538 note = b""
627 if f in copy:
539 if f in copy:
628 note += b"*"
540 note += b"*"
629 if f in divergeset:
541 if f in divergeset:
630 note += b"!"
542 note += b"!"
631 if f in renamedeleteset:
543 if f in renamedeleteset:
632 note += b"%"
544 note += b"%"
633 repo.ui.debug(
545 repo.ui.debug(
634 b" src: '%s' -> dst: '%s' %s\n" % (fullcopy[f], f, note)
546 b" src: '%s' -> dst: '%s' %s\n" % (fullcopy[f], f, note)
635 )
547 )
636 del divergeset
548 del divergeset
637
549
638 repo.ui.debug(b" checking for directory renames\n")
550 repo.ui.debug(b" checking for directory renames\n")
639
551
640 # generate a directory move map
552 # generate a directory move map
641 d1, d2 = c1.dirs(), c2.dirs()
553 d1, d2 = c1.dirs(), c2.dirs()
642 invalid = set()
554 invalid = set()
643 dirmove = {}
555 dirmove = {}
644
556
645 # examine each file copy for a potential directory move, which is
557 # examine each file copy for a potential directory move, which is
646 # when all the files in a directory are moved to a new directory
558 # when all the files in a directory are moved to a new directory
647 for dst, src in pycompat.iteritems(fullcopy):
559 for dst, src in pycompat.iteritems(fullcopy):
648 dsrc, ddst = pathutil.dirname(src), pathutil.dirname(dst)
560 dsrc, ddst = pathutil.dirname(src), pathutil.dirname(dst)
649 if dsrc in invalid:
561 if dsrc in invalid:
650 # already seen to be uninteresting
562 # already seen to be uninteresting
651 continue
563 continue
652 elif dsrc in d1 and ddst in d1:
564 elif dsrc in d1 and ddst in d1:
653 # directory wasn't entirely moved locally
565 # directory wasn't entirely moved locally
654 invalid.add(dsrc)
566 invalid.add(dsrc)
655 elif dsrc in d2 and ddst in d2:
567 elif dsrc in d2 and ddst in d2:
656 # directory wasn't entirely moved remotely
568 # directory wasn't entirely moved remotely
657 invalid.add(dsrc)
569 invalid.add(dsrc)
658 elif dsrc in dirmove and dirmove[dsrc] != ddst:
570 elif dsrc in dirmove and dirmove[dsrc] != ddst:
659 # files from the same directory moved to two different places
571 # files from the same directory moved to two different places
660 invalid.add(dsrc)
572 invalid.add(dsrc)
661 else:
573 else:
662 # looks good so far
574 # looks good so far
663 dirmove[dsrc] = ddst
575 dirmove[dsrc] = ddst
664
576
665 for i in invalid:
577 for i in invalid:
666 if i in dirmove:
578 if i in dirmove:
667 del dirmove[i]
579 del dirmove[i]
668 del d1, d2, invalid
580 del d1, d2, invalid
669
581
670 if not dirmove:
582 if not dirmove:
671 return copy, {}, diverge, renamedelete, {}
583 return copy, {}, diverge, renamedelete, {}
672
584
673 dirmove = {k + b"/": v + b"/" for k, v in pycompat.iteritems(dirmove)}
585 dirmove = {k + b"/": v + b"/" for k, v in pycompat.iteritems(dirmove)}
674
586
675 for d in dirmove:
587 for d in dirmove:
676 repo.ui.debug(
588 repo.ui.debug(
677 b" discovered dir src: '%s' -> dst: '%s'\n" % (d, dirmove[d])
589 b" discovered dir src: '%s' -> dst: '%s'\n" % (d, dirmove[d])
678 )
590 )
679
591
680 movewithdir = {}
592 movewithdir = {}
681 # check unaccounted nonoverlapping files against directory moves
593 # check unaccounted nonoverlapping files against directory moves
682 for f in u1 + u2:
594 for f in u1 + u2:
683 if f not in fullcopy:
595 if f not in fullcopy:
684 for d in dirmove:
596 for d in dirmove:
685 if f.startswith(d):
597 if f.startswith(d):
686 # new file added in a directory that was moved, move it
598 # new file added in a directory that was moved, move it
687 df = dirmove[d] + f[len(d) :]
599 df = dirmove[d] + f[len(d) :]
688 if df not in copy:
600 if df not in copy:
689 movewithdir[f] = df
601 movewithdir[f] = df
690 repo.ui.debug(
602 repo.ui.debug(
691 b" pending file src: '%s' -> dst: '%s'\n"
603 b" pending file src: '%s' -> dst: '%s'\n"
692 % (f, df)
604 % (f, df)
693 )
605 )
694 break
606 break
695
607
696 return copy, movewithdir, diverge, renamedelete, dirmove
608 return copy, movewithdir, diverge, renamedelete, dirmove
697
609
698
610
699 def _heuristicscopytracing(repo, c1, c2, base):
611 def _heuristicscopytracing(repo, c1, c2, base):
700 """ Fast copytracing using filename heuristics
612 """ Fast copytracing using filename heuristics
701
613
702 Assumes that moves or renames are of following two types:
614 Assumes that moves or renames are of following two types:
703
615
704 1) Inside a directory only (same directory name but different filenames)
616 1) Inside a directory only (same directory name but different filenames)
705 2) Move from one directory to another
617 2) Move from one directory to another
706 (same filenames but different directory names)
618 (same filenames but different directory names)
707
619
708 Works only when there are no merge commits in the "source branch".
620 Works only when there are no merge commits in the "source branch".
709 Source branch is commits from base up to c2 not including base.
621 Source branch is commits from base up to c2 not including base.
710
622
711 If merge is involved it fallbacks to _fullcopytracing().
623 If merge is involved it fallbacks to _fullcopytracing().
712
624
713 Can be used by setting the following config:
625 Can be used by setting the following config:
714
626
715 [experimental]
627 [experimental]
716 copytrace = heuristics
628 copytrace = heuristics
717
629
718 In some cases the copy/move candidates found by heuristics can be very large
630 In some cases the copy/move candidates found by heuristics can be very large
719 in number and that will make the algorithm slow. The number of possible
631 in number and that will make the algorithm slow. The number of possible
720 candidates to check can be limited by using the config
632 candidates to check can be limited by using the config
721 `experimental.copytrace.movecandidateslimit` which defaults to 100.
633 `experimental.copytrace.movecandidateslimit` which defaults to 100.
722 """
634 """
723
635
724 if c1.rev() is None:
636 if c1.rev() is None:
725 c1 = c1.p1()
637 c1 = c1.p1()
726 if c2.rev() is None:
638 if c2.rev() is None:
727 c2 = c2.p1()
639 c2 = c2.p1()
728
640
729 copies = {}
641 copies = {}
730
642
731 changedfiles = set()
643 changedfiles = set()
732 m1 = c1.manifest()
644 m1 = c1.manifest()
733 if not repo.revs(b'%d::%d', base.rev(), c2.rev()):
645 if not repo.revs(b'%d::%d', base.rev(), c2.rev()):
734 # If base is not in c2 branch, we switch to fullcopytracing
646 # If base is not in c2 branch, we switch to fullcopytracing
735 repo.ui.debug(
647 repo.ui.debug(
736 b"switching to full copytracing as base is not "
648 b"switching to full copytracing as base is not "
737 b"an ancestor of c2\n"
649 b"an ancestor of c2\n"
738 )
650 )
739 return _fullcopytracing(repo, c1, c2, base)
651 return _fullcopytracing(repo, c1, c2, base)
740
652
741 ctx = c2
653 ctx = c2
742 while ctx != base:
654 while ctx != base:
743 if len(ctx.parents()) == 2:
655 if len(ctx.parents()) == 2:
744 # To keep things simple let's not handle merges
656 # To keep things simple let's not handle merges
745 repo.ui.debug(b"switching to full copytracing because of merges\n")
657 repo.ui.debug(b"switching to full copytracing because of merges\n")
746 return _fullcopytracing(repo, c1, c2, base)
658 return _fullcopytracing(repo, c1, c2, base)
747 changedfiles.update(ctx.files())
659 changedfiles.update(ctx.files())
748 ctx = ctx.p1()
660 ctx = ctx.p1()
749
661
750 cp = _forwardcopies(base, c2)
662 cp = _forwardcopies(base, c2)
751 for dst, src in pycompat.iteritems(cp):
663 for dst, src in pycompat.iteritems(cp):
752 if src in m1:
664 if src in m1:
753 copies[dst] = src
665 copies[dst] = src
754
666
755 # file is missing if it isn't present in the destination, but is present in
667 # file is missing if it isn't present in the destination, but is present in
756 # the base and present in the source.
668 # the base and present in the source.
757 # Presence in the base is important to exclude added files, presence in the
669 # Presence in the base is important to exclude added files, presence in the
758 # source is important to exclude removed files.
670 # source is important to exclude removed files.
759 filt = lambda f: f not in m1 and f in base and f in c2
671 filt = lambda f: f not in m1 and f in base and f in c2
760 missingfiles = [f for f in changedfiles if filt(f)]
672 missingfiles = [f for f in changedfiles if filt(f)]
761
673
762 if missingfiles:
674 if missingfiles:
763 basenametofilename = collections.defaultdict(list)
675 basenametofilename = collections.defaultdict(list)
764 dirnametofilename = collections.defaultdict(list)
676 dirnametofilename = collections.defaultdict(list)
765
677
766 for f in m1.filesnotin(base.manifest()):
678 for f in m1.filesnotin(base.manifest()):
767 basename = os.path.basename(f)
679 basename = os.path.basename(f)
768 dirname = os.path.dirname(f)
680 dirname = os.path.dirname(f)
769 basenametofilename[basename].append(f)
681 basenametofilename[basename].append(f)
770 dirnametofilename[dirname].append(f)
682 dirnametofilename[dirname].append(f)
771
683
772 for f in missingfiles:
684 for f in missingfiles:
773 basename = os.path.basename(f)
685 basename = os.path.basename(f)
774 dirname = os.path.dirname(f)
686 dirname = os.path.dirname(f)
775 samebasename = basenametofilename[basename]
687 samebasename = basenametofilename[basename]
776 samedirname = dirnametofilename[dirname]
688 samedirname = dirnametofilename[dirname]
777 movecandidates = samebasename + samedirname
689 movecandidates = samebasename + samedirname
778 # f is guaranteed to be present in c2, that's why
690 # f is guaranteed to be present in c2, that's why
779 # c2.filectx(f) won't fail
691 # c2.filectx(f) won't fail
780 f2 = c2.filectx(f)
692 f2 = c2.filectx(f)
781 # we can have a lot of candidates which can slow down the heuristics
693 # we can have a lot of candidates which can slow down the heuristics
782 # config value to limit the number of candidates moves to check
694 # config value to limit the number of candidates moves to check
783 maxcandidates = repo.ui.configint(
695 maxcandidates = repo.ui.configint(
784 b'experimental', b'copytrace.movecandidateslimit'
696 b'experimental', b'copytrace.movecandidateslimit'
785 )
697 )
786
698
787 if len(movecandidates) > maxcandidates:
699 if len(movecandidates) > maxcandidates:
788 repo.ui.status(
700 repo.ui.status(
789 _(
701 _(
790 b"skipping copytracing for '%s', more "
702 b"skipping copytracing for '%s', more "
791 b"candidates than the limit: %d\n"
703 b"candidates than the limit: %d\n"
792 )
704 )
793 % (f, len(movecandidates))
705 % (f, len(movecandidates))
794 )
706 )
795 continue
707 continue
796
708
797 for candidate in movecandidates:
709 for candidate in movecandidates:
798 f1 = c1.filectx(candidate)
710 f1 = c1.filectx(candidate)
799 if _related(f1, f2):
711 if _related(f1, f2):
800 # if there are a few related copies then we'll merge
712 # if there are a few related copies then we'll merge
801 # changes into all of them. This matches the behaviour
713 # changes into all of them. This matches the behaviour
802 # of upstream copytracing
714 # of upstream copytracing
803 copies[candidate] = f
715 copies[candidate] = f
804
716
805 return copies, {}, {}, {}, {}
717 return copies, {}, {}, {}, {}
806
718
807
719
808 def _related(f1, f2):
720 def _related(f1, f2):
809 """return True if f1 and f2 filectx have a common ancestor
721 """return True if f1 and f2 filectx have a common ancestor
810
722
811 Walk back to common ancestor to see if the two files originate
723 Walk back to common ancestor to see if the two files originate
812 from the same file. Since workingfilectx's rev() is None it messes
724 from the same file. Since workingfilectx's rev() is None it messes
813 up the integer comparison logic, hence the pre-step check for
725 up the integer comparison logic, hence the pre-step check for
814 None (f1 and f2 can only be workingfilectx's initially).
726 None (f1 and f2 can only be workingfilectx's initially).
815 """
727 """
816
728
817 if f1 == f2:
729 if f1 == f2:
818 return True # a match
730 return True # a match
819
731
820 g1, g2 = f1.ancestors(), f2.ancestors()
732 g1, g2 = f1.ancestors(), f2.ancestors()
821 try:
733 try:
822 f1r, f2r = f1.linkrev(), f2.linkrev()
734 f1r, f2r = f1.linkrev(), f2.linkrev()
823
735
824 if f1r is None:
736 if f1r is None:
825 f1 = next(g1)
737 f1 = next(g1)
826 if f2r is None:
738 if f2r is None:
827 f2 = next(g2)
739 f2 = next(g2)
828
740
829 while True:
741 while True:
830 f1r, f2r = f1.linkrev(), f2.linkrev()
742 f1r, f2r = f1.linkrev(), f2.linkrev()
831 if f1r > f2r:
743 if f1r > f2r:
832 f1 = next(g1)
744 f1 = next(g1)
833 elif f2r > f1r:
745 elif f2r > f1r:
834 f2 = next(g2)
746 f2 = next(g2)
835 else: # f1 and f2 point to files in the same linkrev
747 else: # f1 and f2 point to files in the same linkrev
836 return f1 == f2 # true if they point to the same file
748 return f1 == f2 # true if they point to the same file
837 except StopIteration:
749 except StopIteration:
838 return False
750 return False
839
751
840
752
841 def duplicatecopies(repo, wctx, rev, fromrev, skiprev=None):
753 def duplicatecopies(repo, wctx, rev, fromrev, skiprev=None):
842 """reproduce copies from fromrev to rev in the dirstate
754 """reproduce copies from fromrev to rev in the dirstate
843
755
844 If skiprev is specified, it's a revision that should be used to
756 If skiprev is specified, it's a revision that should be used to
845 filter copy records. Any copies that occur between fromrev and
757 filter copy records. Any copies that occur between fromrev and
846 skiprev will not be duplicated, even if they appear in the set of
758 skiprev will not be duplicated, even if they appear in the set of
847 copies between fromrev and rev.
759 copies between fromrev and rev.
848 """
760 """
849 exclude = {}
761 exclude = {}
850 ctraceconfig = repo.ui.config(b'experimental', b'copytrace')
762 ctraceconfig = repo.ui.config(b'experimental', b'copytrace')
851 bctrace = stringutil.parsebool(ctraceconfig)
763 bctrace = stringutil.parsebool(ctraceconfig)
852 if skiprev is not None and (
764 if skiprev is not None and (
853 ctraceconfig == b'heuristics' or bctrace or bctrace is None
765 ctraceconfig == b'heuristics' or bctrace or bctrace is None
854 ):
766 ):
855 # copytrace='off' skips this line, but not the entire function because
767 # copytrace='off' skips this line, but not the entire function because
856 # the line below is O(size of the repo) during a rebase, while the rest
768 # the line below is O(size of the repo) during a rebase, while the rest
857 # of the function is much faster (and is required for carrying copy
769 # of the function is much faster (and is required for carrying copy
858 # metadata across the rebase anyway).
770 # metadata across the rebase anyway).
859 exclude = pathcopies(repo[fromrev], repo[skiprev])
771 exclude = pathcopies(repo[fromrev], repo[skiprev])
860 for dst, src in pycompat.iteritems(pathcopies(repo[fromrev], repo[rev])):
772 for dst, src in pycompat.iteritems(pathcopies(repo[fromrev], repo[rev])):
861 if dst in exclude:
773 if dst in exclude:
862 continue
774 continue
863 if dst in wctx:
775 if dst in wctx:
864 wctx[dst].markcopied(src)
776 wctx[dst].markcopied(src)
865
777
866
778
867 def computechangesetfilesadded(ctx):
779 def computechangesetfilesadded(ctx):
868 """return the list of files added in a changeset
780 """return the list of files added in a changeset
869 """
781 """
870 added = []
782 added = []
871 for f in ctx.files():
783 for f in ctx.files():
872 if not any(f in p for p in ctx.parents()):
784 if not any(f in p for p in ctx.parents()):
873 added.append(f)
785 added.append(f)
874 return added
786 return added
875
787
876
788
877 def computechangesetfilesremoved(ctx):
789 def computechangesetfilesremoved(ctx):
878 """return the list of files removed in a changeset
790 """return the list of files removed in a changeset
879 """
791 """
880 removed = []
792 removed = []
881 for f in ctx.files():
793 for f in ctx.files():
882 if f not in ctx:
794 if f not in ctx:
883 removed.append(f)
795 removed.append(f)
884 return removed
796 return removed
885
797
886
798
887 def computechangesetcopies(ctx):
799 def computechangesetcopies(ctx):
888 """return the copies data for a changeset
800 """return the copies data for a changeset
889
801
890 The copies data are returned as a pair of dictionnary (p1copies, p2copies).
802 The copies data are returned as a pair of dictionnary (p1copies, p2copies).
891
803
892 Each dictionnary are in the form: `{newname: oldname}`
804 Each dictionnary are in the form: `{newname: oldname}`
893 """
805 """
894 p1copies = {}
806 p1copies = {}
895 p2copies = {}
807 p2copies = {}
896 p1 = ctx.p1()
808 p1 = ctx.p1()
897 p2 = ctx.p2()
809 p2 = ctx.p2()
898 narrowmatch = ctx._repo.narrowmatch()
810 narrowmatch = ctx._repo.narrowmatch()
899 for dst in ctx.files():
811 for dst in ctx.files():
900 if not narrowmatch(dst) or dst not in ctx:
812 if not narrowmatch(dst) or dst not in ctx:
901 continue
813 continue
902 copied = ctx[dst].renamed()
814 copied = ctx[dst].renamed()
903 if not copied:
815 if not copied:
904 continue
816 continue
905 src, srcnode = copied
817 src, srcnode = copied
906 if src in p1 and p1[src].filenode() == srcnode:
818 if src in p1 and p1[src].filenode() == srcnode:
907 p1copies[dst] = src
819 p1copies[dst] = src
908 elif src in p2 and p2[src].filenode() == srcnode:
820 elif src in p2 and p2[src].filenode() == srcnode:
909 p2copies[dst] = src
821 p2copies[dst] = src
910 return p1copies, p2copies
822 return p1copies, p2copies
911
823
912
824
913 def encodecopies(files, copies):
825 def encodecopies(files, copies):
914 items = []
826 items = []
915 for i, dst in enumerate(files):
827 for i, dst in enumerate(files):
916 if dst in copies:
828 if dst in copies:
917 items.append(b'%d\0%s' % (i, copies[dst]))
829 items.append(b'%d\0%s' % (i, copies[dst]))
918 if len(items) != len(copies):
830 if len(items) != len(copies):
919 raise error.ProgrammingError(
831 raise error.ProgrammingError(
920 b'some copy targets missing from file list'
832 b'some copy targets missing from file list'
921 )
833 )
922 return b"\n".join(items)
834 return b"\n".join(items)
923
835
924
836
925 def decodecopies(files, data):
837 def decodecopies(files, data):
926 try:
838 try:
927 copies = {}
839 copies = {}
928 if not data:
840 if not data:
929 return copies
841 return copies
930 for l in data.split(b'\n'):
842 for l in data.split(b'\n'):
931 strindex, src = l.split(b'\0')
843 strindex, src = l.split(b'\0')
932 i = int(strindex)
844 i = int(strindex)
933 dst = files[i]
845 dst = files[i]
934 copies[dst] = src
846 copies[dst] = src
935 return copies
847 return copies
936 except (ValueError, IndexError):
848 except (ValueError, IndexError):
937 # Perhaps someone had chosen the same key name (e.g. "p1copies") and
849 # Perhaps someone had chosen the same key name (e.g. "p1copies") and
938 # used different syntax for the value.
850 # used different syntax for the value.
939 return None
851 return None
940
852
941
853
942 def encodefileindices(files, subset):
854 def encodefileindices(files, subset):
943 subset = set(subset)
855 subset = set(subset)
944 indices = []
856 indices = []
945 for i, f in enumerate(files):
857 for i, f in enumerate(files):
946 if f in subset:
858 if f in subset:
947 indices.append(b'%d' % i)
859 indices.append(b'%d' % i)
948 return b'\n'.join(indices)
860 return b'\n'.join(indices)
949
861
950
862
951 def decodefileindices(files, data):
863 def decodefileindices(files, data):
952 try:
864 try:
953 subset = []
865 subset = []
954 if not data:
866 if not data:
955 return subset
867 return subset
956 for strindex in data.split(b'\n'):
868 for strindex in data.split(b'\n'):
957 i = int(strindex)
869 i = int(strindex)
958 if i < 0 or i >= len(files):
870 if i < 0 or i >= len(files):
959 return None
871 return None
960 subset.append(files[i])
872 subset.append(files[i])
961 return subset
873 return subset
962 except (ValueError, IndexError):
874 except (ValueError, IndexError):
963 # Perhaps someone had chosen the same key name (e.g. "added") and
875 # Perhaps someone had chosen the same key name (e.g. "added") and
964 # used different syntax for the value.
876 # used different syntax for the value.
965 return None
877 return None
966
878
967
879
968 def _getsidedata(srcrepo, rev):
880 def _getsidedata(srcrepo, rev):
969 ctx = srcrepo[rev]
881 ctx = srcrepo[rev]
970 filescopies = computechangesetcopies(ctx)
882 filescopies = computechangesetcopies(ctx)
971 filesadded = computechangesetfilesadded(ctx)
883 filesadded = computechangesetfilesadded(ctx)
972 filesremoved = computechangesetfilesremoved(ctx)
884 filesremoved = computechangesetfilesremoved(ctx)
973 sidedata = {}
885 sidedata = {}
974 if any([filescopies, filesadded, filesremoved]):
886 if any([filescopies, filesadded, filesremoved]):
975 sortedfiles = sorted(ctx.files())
887 sortedfiles = sorted(ctx.files())
976 p1copies, p2copies = filescopies
888 p1copies, p2copies = filescopies
977 p1copies = encodecopies(sortedfiles, p1copies)
889 p1copies = encodecopies(sortedfiles, p1copies)
978 p2copies = encodecopies(sortedfiles, p2copies)
890 p2copies = encodecopies(sortedfiles, p2copies)
979 filesadded = encodefileindices(sortedfiles, filesadded)
891 filesadded = encodefileindices(sortedfiles, filesadded)
980 filesremoved = encodefileindices(sortedfiles, filesremoved)
892 filesremoved = encodefileindices(sortedfiles, filesremoved)
981 sidedata[sidedatamod.SD_P1COPIES] = p1copies
893 sidedata[sidedatamod.SD_P1COPIES] = p1copies
982 sidedata[sidedatamod.SD_P2COPIES] = p2copies
894 sidedata[sidedatamod.SD_P2COPIES] = p2copies
983 sidedata[sidedatamod.SD_FILESADDED] = filesadded
895 sidedata[sidedatamod.SD_FILESADDED] = filesadded
984 sidedata[sidedatamod.SD_FILESREMOVED] = filesremoved
896 sidedata[sidedatamod.SD_FILESREMOVED] = filesremoved
985 return sidedata
897 return sidedata
986
898
987
899
988 def getsidedataadder(srcrepo, destrepo):
900 def getsidedataadder(srcrepo, destrepo):
989 def sidedatacompanion(revlog, rev):
901 def sidedatacompanion(revlog, rev):
990 sidedata = {}
902 sidedata = {}
991 if util.safehasattr(revlog, 'filteredrevs'): # this is a changelog
903 if util.safehasattr(revlog, 'filteredrevs'): # this is a changelog
992 sidedata = _getsidedata(srcrepo, rev)
904 sidedata = _getsidedata(srcrepo, rev)
993 return False, (), sidedata
905 return False, (), sidedata
994
906
995 return sidedatacompanion
907 return sidedatacompanion
996
908
997
909
998 def getsidedataremover(srcrepo, destrepo):
910 def getsidedataremover(srcrepo, destrepo):
999 def sidedatacompanion(revlog, rev):
911 def sidedatacompanion(revlog, rev):
1000 f = ()
912 f = ()
1001 if util.safehasattr(revlog, 'filteredrevs'): # this is a changelog
913 if util.safehasattr(revlog, 'filteredrevs'): # this is a changelog
1002 if revlog.flags(rev) & REVIDX_SIDEDATA:
914 if revlog.flags(rev) & REVIDX_SIDEDATA:
1003 f = (
915 f = (
1004 sidedatamod.SD_P1COPIES,
916 sidedatamod.SD_P1COPIES,
1005 sidedatamod.SD_P2COPIES,
917 sidedatamod.SD_P2COPIES,
1006 sidedatamod.SD_FILESADDED,
918 sidedatamod.SD_FILESADDED,
1007 sidedatamod.SD_FILESREMOVED,
919 sidedatamod.SD_FILESREMOVED,
1008 )
920 )
1009 return False, f, {}
921 return False, f, {}
1010
922
1011 return sidedatacompanion
923 return sidedatacompanion
@@ -1,1695 +1,1694 b''
1
1
2 $ add()
2 $ add()
3 > {
3 > {
4 > echo $2 >> $1
4 > echo $2 >> $1
5 > }
5 > }
6 $ hg init t
6 $ hg init t
7 $ cd t
7 $ cd t
8
8
9 set up a boring main branch
9 set up a boring main branch
10
10
11 $ add a a
11 $ add a a
12 $ hg add a
12 $ hg add a
13 $ mkdir x
13 $ mkdir x
14 $ add x/x x
14 $ add x/x x
15 $ hg add x/x
15 $ hg add x/x
16 $ hg ci -m0
16 $ hg ci -m0
17 $ add a m1
17 $ add a m1
18 $ hg ci -m1
18 $ hg ci -m1
19 $ add a m2
19 $ add a m2
20 $ add x/y y1
20 $ add x/y y1
21 $ hg add x/y
21 $ hg add x/y
22 $ hg ci -m2
22 $ hg ci -m2
23 $ cd ..
23 $ cd ..
24
24
25 $ show()
25 $ show()
26 > {
26 > {
27 > echo "# $2:"
27 > echo "# $2:"
28 > echo
28 > echo
29 > echo "% hg st -C $1"
29 > echo "% hg st -C $1"
30 > hg st -C $1
30 > hg st -C $1
31 > echo
31 > echo
32 > echo "% hg diff --git $1"
32 > echo "% hg diff --git $1"
33 > hg diff --git $1
33 > hg diff --git $1
34 > echo
34 > echo
35 > }
35 > }
36 $ count=0
36 $ count=0
37
37
38 make a new branch and get diff/status output
38 make a new branch and get diff/status output
39 $1 - first commit
39 $1 - first commit
40 $2 - second commit
40 $2 - second commit
41 $3 - working dir action
41 $3 - working dir action
42
42
43 $ tb()
43 $ tb()
44 > {
44 > {
45 > hg clone -q t t2 ; cd t2
45 > hg clone -q t t2 ; cd t2
46 > hg co -q -C 0
46 > hg co -q -C 0
47 >
47 >
48 > echo % add a $count
48 > echo % add a $count
49 > add a $count
49 > add a $count
50 > count=`expr $count + 1`
50 > count=`expr $count + 1`
51 > echo % hg ci -m "t0"
51 > echo % hg ci -m "t0"
52 > hg ci -m "t0"
52 > hg ci -m "t0"
53 > echo % $1
53 > echo % $1
54 > $1
54 > $1
55 > echo % hg ci -m "t1"
55 > echo % hg ci -m "t1"
56 > hg ci -m "t1"
56 > hg ci -m "t1"
57 > echo % $2
57 > echo % $2
58 > $2
58 > $2
59 > echo % hg ci -m "t2"
59 > echo % hg ci -m "t2"
60 > hg ci -m "t2"
60 > hg ci -m "t2"
61 > echo % $3
61 > echo % $3
62 > $3
62 > $3
63 > echo
63 > echo
64 > show "" "working to parent"
64 > show "" "working to parent"
65 > show "--rev 0" "working to root"
65 > show "--rev 0" "working to root"
66 > show "--rev 2" "working to branch"
66 > show "--rev 2" "working to branch"
67 > show "--rev 0 --rev ." "root to parent"
67 > show "--rev 0 --rev ." "root to parent"
68 > show "--rev . --rev 0" "parent to root"
68 > show "--rev . --rev 0" "parent to root"
69 > show "--rev 2 --rev ." "branch to parent"
69 > show "--rev 2 --rev ." "branch to parent"
70 > show "--rev . --rev 2" "parent to branch"
70 > show "--rev . --rev 2" "parent to branch"
71 > echo
71 > echo
72 > cd ..
72 > cd ..
73 > rm -rf t2
73 > rm -rf t2
74 > }
74 > }
75
75
76 rename in working dir
76 rename in working dir
77
77
78 $ tb "add a a1" "add a a2" "hg mv a b"
78 $ tb "add a a1" "add a a2" "hg mv a b"
79 % add a 0
79 % add a 0
80 % hg ci -m t0
80 % hg ci -m t0
81 created new head
81 created new head
82 % add a a1
82 % add a a1
83 % hg ci -m t1
83 % hg ci -m t1
84 % add a a2
84 % add a a2
85 % hg ci -m t2
85 % hg ci -m t2
86 % hg mv a b
86 % hg mv a b
87
87
88 # working to parent:
88 # working to parent:
89
89
90 % hg st -C
90 % hg st -C
91 A b
91 A b
92 a
92 a
93 R a
93 R a
94
94
95 % hg diff --git
95 % hg diff --git
96 diff --git a/a b/b
96 diff --git a/a b/b
97 rename from a
97 rename from a
98 rename to b
98 rename to b
99
99
100 # working to root:
100 # working to root:
101
101
102 % hg st -C --rev 0
102 % hg st -C --rev 0
103 A b
103 A b
104 a
104 a
105 R a
105 R a
106
106
107 % hg diff --git --rev 0
107 % hg diff --git --rev 0
108 diff --git a/a b/b
108 diff --git a/a b/b
109 rename from a
109 rename from a
110 rename to b
110 rename to b
111 --- a/a
111 --- a/a
112 +++ b/b
112 +++ b/b
113 @@ -1,1 +1,4 @@
113 @@ -1,1 +1,4 @@
114 a
114 a
115 +0
115 +0
116 +a1
116 +a1
117 +a2
117 +a2
118
118
119 # working to branch:
119 # working to branch:
120
120
121 % hg st -C --rev 2
121 % hg st -C --rev 2
122 A b
122 A b
123 a
123 a
124 R a
124 R a
125 R x/y
125 R x/y
126
126
127 % hg diff --git --rev 2
127 % hg diff --git --rev 2
128 diff --git a/a b/b
128 diff --git a/a b/b
129 rename from a
129 rename from a
130 rename to b
130 rename to b
131 --- a/a
131 --- a/a
132 +++ b/b
132 +++ b/b
133 @@ -1,3 +1,4 @@
133 @@ -1,3 +1,4 @@
134 a
134 a
135 -m1
135 -m1
136 -m2
136 -m2
137 +0
137 +0
138 +a1
138 +a1
139 +a2
139 +a2
140 diff --git a/x/y b/x/y
140 diff --git a/x/y b/x/y
141 deleted file mode 100644
141 deleted file mode 100644
142 --- a/x/y
142 --- a/x/y
143 +++ /dev/null
143 +++ /dev/null
144 @@ -1,1 +0,0 @@
144 @@ -1,1 +0,0 @@
145 -y1
145 -y1
146
146
147 # root to parent:
147 # root to parent:
148
148
149 % hg st -C --rev 0 --rev .
149 % hg st -C --rev 0 --rev .
150 M a
150 M a
151
151
152 % hg diff --git --rev 0 --rev .
152 % hg diff --git --rev 0 --rev .
153 diff --git a/a b/a
153 diff --git a/a b/a
154 --- a/a
154 --- a/a
155 +++ b/a
155 +++ b/a
156 @@ -1,1 +1,4 @@
156 @@ -1,1 +1,4 @@
157 a
157 a
158 +0
158 +0
159 +a1
159 +a1
160 +a2
160 +a2
161
161
162 # parent to root:
162 # parent to root:
163
163
164 % hg st -C --rev . --rev 0
164 % hg st -C --rev . --rev 0
165 M a
165 M a
166
166
167 % hg diff --git --rev . --rev 0
167 % hg diff --git --rev . --rev 0
168 diff --git a/a b/a
168 diff --git a/a b/a
169 --- a/a
169 --- a/a
170 +++ b/a
170 +++ b/a
171 @@ -1,4 +1,1 @@
171 @@ -1,4 +1,1 @@
172 a
172 a
173 -0
173 -0
174 -a1
174 -a1
175 -a2
175 -a2
176
176
177 # branch to parent:
177 # branch to parent:
178
178
179 % hg st -C --rev 2 --rev .
179 % hg st -C --rev 2 --rev .
180 M a
180 M a
181 R x/y
181 R x/y
182
182
183 % hg diff --git --rev 2 --rev .
183 % hg diff --git --rev 2 --rev .
184 diff --git a/a b/a
184 diff --git a/a b/a
185 --- a/a
185 --- a/a
186 +++ b/a
186 +++ b/a
187 @@ -1,3 +1,4 @@
187 @@ -1,3 +1,4 @@
188 a
188 a
189 -m1
189 -m1
190 -m2
190 -m2
191 +0
191 +0
192 +a1
192 +a1
193 +a2
193 +a2
194 diff --git a/x/y b/x/y
194 diff --git a/x/y b/x/y
195 deleted file mode 100644
195 deleted file mode 100644
196 --- a/x/y
196 --- a/x/y
197 +++ /dev/null
197 +++ /dev/null
198 @@ -1,1 +0,0 @@
198 @@ -1,1 +0,0 @@
199 -y1
199 -y1
200
200
201 # parent to branch:
201 # parent to branch:
202
202
203 % hg st -C --rev . --rev 2
203 % hg st -C --rev . --rev 2
204 M a
204 M a
205 A x/y
205 A x/y
206
206
207 % hg diff --git --rev . --rev 2
207 % hg diff --git --rev . --rev 2
208 diff --git a/a b/a
208 diff --git a/a b/a
209 --- a/a
209 --- a/a
210 +++ b/a
210 +++ b/a
211 @@ -1,4 +1,3 @@
211 @@ -1,4 +1,3 @@
212 a
212 a
213 -0
213 -0
214 -a1
214 -a1
215 -a2
215 -a2
216 +m1
216 +m1
217 +m2
217 +m2
218 diff --git a/x/y b/x/y
218 diff --git a/x/y b/x/y
219 new file mode 100644
219 new file mode 100644
220 --- /dev/null
220 --- /dev/null
221 +++ b/x/y
221 +++ b/x/y
222 @@ -0,0 +1,1 @@
222 @@ -0,0 +1,1 @@
223 +y1
223 +y1
224
224
225
225
226 copy in working dir
226 copy in working dir
227
227
228 $ tb "add a a1" "add a a2" "hg cp a b"
228 $ tb "add a a1" "add a a2" "hg cp a b"
229 % add a 1
229 % add a 1
230 % hg ci -m t0
230 % hg ci -m t0
231 created new head
231 created new head
232 % add a a1
232 % add a a1
233 % hg ci -m t1
233 % hg ci -m t1
234 % add a a2
234 % add a a2
235 % hg ci -m t2
235 % hg ci -m t2
236 % hg cp a b
236 % hg cp a b
237
237
238 # working to parent:
238 # working to parent:
239
239
240 % hg st -C
240 % hg st -C
241 A b
241 A b
242 a
242 a
243
243
244 % hg diff --git
244 % hg diff --git
245 diff --git a/a b/b
245 diff --git a/a b/b
246 copy from a
246 copy from a
247 copy to b
247 copy to b
248
248
249 # working to root:
249 # working to root:
250
250
251 % hg st -C --rev 0
251 % hg st -C --rev 0
252 M a
252 M a
253 A b
253 A b
254 a
254 a
255
255
256 % hg diff --git --rev 0
256 % hg diff --git --rev 0
257 diff --git a/a b/a
257 diff --git a/a b/a
258 --- a/a
258 --- a/a
259 +++ b/a
259 +++ b/a
260 @@ -1,1 +1,4 @@
260 @@ -1,1 +1,4 @@
261 a
261 a
262 +1
262 +1
263 +a1
263 +a1
264 +a2
264 +a2
265 diff --git a/a b/b
265 diff --git a/a b/b
266 copy from a
266 copy from a
267 copy to b
267 copy to b
268 --- a/a
268 --- a/a
269 +++ b/b
269 +++ b/b
270 @@ -1,1 +1,4 @@
270 @@ -1,1 +1,4 @@
271 a
271 a
272 +1
272 +1
273 +a1
273 +a1
274 +a2
274 +a2
275
275
276 # working to branch:
276 # working to branch:
277
277
278 % hg st -C --rev 2
278 % hg st -C --rev 2
279 M a
279 M a
280 A b
280 A b
281 a
281 a
282 R x/y
282 R x/y
283
283
284 % hg diff --git --rev 2
284 % hg diff --git --rev 2
285 diff --git a/a b/a
285 diff --git a/a b/a
286 --- a/a
286 --- a/a
287 +++ b/a
287 +++ b/a
288 @@ -1,3 +1,4 @@
288 @@ -1,3 +1,4 @@
289 a
289 a
290 -m1
290 -m1
291 -m2
291 -m2
292 +1
292 +1
293 +a1
293 +a1
294 +a2
294 +a2
295 diff --git a/a b/b
295 diff --git a/a b/b
296 copy from a
296 copy from a
297 copy to b
297 copy to b
298 --- a/a
298 --- a/a
299 +++ b/b
299 +++ b/b
300 @@ -1,3 +1,4 @@
300 @@ -1,3 +1,4 @@
301 a
301 a
302 -m1
302 -m1
303 -m2
303 -m2
304 +1
304 +1
305 +a1
305 +a1
306 +a2
306 +a2
307 diff --git a/x/y b/x/y
307 diff --git a/x/y b/x/y
308 deleted file mode 100644
308 deleted file mode 100644
309 --- a/x/y
309 --- a/x/y
310 +++ /dev/null
310 +++ /dev/null
311 @@ -1,1 +0,0 @@
311 @@ -1,1 +0,0 @@
312 -y1
312 -y1
313
313
314 # root to parent:
314 # root to parent:
315
315
316 % hg st -C --rev 0 --rev .
316 % hg st -C --rev 0 --rev .
317 M a
317 M a
318
318
319 % hg diff --git --rev 0 --rev .
319 % hg diff --git --rev 0 --rev .
320 diff --git a/a b/a
320 diff --git a/a b/a
321 --- a/a
321 --- a/a
322 +++ b/a
322 +++ b/a
323 @@ -1,1 +1,4 @@
323 @@ -1,1 +1,4 @@
324 a
324 a
325 +1
325 +1
326 +a1
326 +a1
327 +a2
327 +a2
328
328
329 # parent to root:
329 # parent to root:
330
330
331 % hg st -C --rev . --rev 0
331 % hg st -C --rev . --rev 0
332 M a
332 M a
333
333
334 % hg diff --git --rev . --rev 0
334 % hg diff --git --rev . --rev 0
335 diff --git a/a b/a
335 diff --git a/a b/a
336 --- a/a
336 --- a/a
337 +++ b/a
337 +++ b/a
338 @@ -1,4 +1,1 @@
338 @@ -1,4 +1,1 @@
339 a
339 a
340 -1
340 -1
341 -a1
341 -a1
342 -a2
342 -a2
343
343
344 # branch to parent:
344 # branch to parent:
345
345
346 % hg st -C --rev 2 --rev .
346 % hg st -C --rev 2 --rev .
347 M a
347 M a
348 R x/y
348 R x/y
349
349
350 % hg diff --git --rev 2 --rev .
350 % hg diff --git --rev 2 --rev .
351 diff --git a/a b/a
351 diff --git a/a b/a
352 --- a/a
352 --- a/a
353 +++ b/a
353 +++ b/a
354 @@ -1,3 +1,4 @@
354 @@ -1,3 +1,4 @@
355 a
355 a
356 -m1
356 -m1
357 -m2
357 -m2
358 +1
358 +1
359 +a1
359 +a1
360 +a2
360 +a2
361 diff --git a/x/y b/x/y
361 diff --git a/x/y b/x/y
362 deleted file mode 100644
362 deleted file mode 100644
363 --- a/x/y
363 --- a/x/y
364 +++ /dev/null
364 +++ /dev/null
365 @@ -1,1 +0,0 @@
365 @@ -1,1 +0,0 @@
366 -y1
366 -y1
367
367
368 # parent to branch:
368 # parent to branch:
369
369
370 % hg st -C --rev . --rev 2
370 % hg st -C --rev . --rev 2
371 M a
371 M a
372 A x/y
372 A x/y
373
373
374 % hg diff --git --rev . --rev 2
374 % hg diff --git --rev . --rev 2
375 diff --git a/a b/a
375 diff --git a/a b/a
376 --- a/a
376 --- a/a
377 +++ b/a
377 +++ b/a
378 @@ -1,4 +1,3 @@
378 @@ -1,4 +1,3 @@
379 a
379 a
380 -1
380 -1
381 -a1
381 -a1
382 -a2
382 -a2
383 +m1
383 +m1
384 +m2
384 +m2
385 diff --git a/x/y b/x/y
385 diff --git a/x/y b/x/y
386 new file mode 100644
386 new file mode 100644
387 --- /dev/null
387 --- /dev/null
388 +++ b/x/y
388 +++ b/x/y
389 @@ -0,0 +1,1 @@
389 @@ -0,0 +1,1 @@
390 +y1
390 +y1
391
391
392
392
393 single rename
393 single rename
394
394
395 $ tb "hg mv a b" "add b b1" "add b w"
395 $ tb "hg mv a b" "add b b1" "add b w"
396 % add a 2
396 % add a 2
397 % hg ci -m t0
397 % hg ci -m t0
398 created new head
398 created new head
399 % hg mv a b
399 % hg mv a b
400 % hg ci -m t1
400 % hg ci -m t1
401 % add b b1
401 % add b b1
402 % hg ci -m t2
402 % hg ci -m t2
403 % add b w
403 % add b w
404
404
405 # working to parent:
405 # working to parent:
406
406
407 % hg st -C
407 % hg st -C
408 M b
408 M b
409
409
410 % hg diff --git
410 % hg diff --git
411 diff --git a/b b/b
411 diff --git a/b b/b
412 --- a/b
412 --- a/b
413 +++ b/b
413 +++ b/b
414 @@ -1,3 +1,4 @@
414 @@ -1,3 +1,4 @@
415 a
415 a
416 2
416 2
417 b1
417 b1
418 +w
418 +w
419
419
420 # working to root:
420 # working to root:
421
421
422 % hg st -C --rev 0
422 % hg st -C --rev 0
423 A b
423 A b
424 a
424 a
425 R a
425 R a
426
426
427 % hg diff --git --rev 0
427 % hg diff --git --rev 0
428 diff --git a/a b/b
428 diff --git a/a b/b
429 rename from a
429 rename from a
430 rename to b
430 rename to b
431 --- a/a
431 --- a/a
432 +++ b/b
432 +++ b/b
433 @@ -1,1 +1,4 @@
433 @@ -1,1 +1,4 @@
434 a
434 a
435 +2
435 +2
436 +b1
436 +b1
437 +w
437 +w
438
438
439 # working to branch:
439 # working to branch:
440
440
441 % hg st -C --rev 2
441 % hg st -C --rev 2
442 A b
442 A b
443 a
443 a
444 R a
444 R a
445 R x/y
445 R x/y
446
446
447 % hg diff --git --rev 2
447 % hg diff --git --rev 2
448 diff --git a/a b/b
448 diff --git a/a b/b
449 rename from a
449 rename from a
450 rename to b
450 rename to b
451 --- a/a
451 --- a/a
452 +++ b/b
452 +++ b/b
453 @@ -1,3 +1,4 @@
453 @@ -1,3 +1,4 @@
454 a
454 a
455 -m1
455 -m1
456 -m2
456 -m2
457 +2
457 +2
458 +b1
458 +b1
459 +w
459 +w
460 diff --git a/x/y b/x/y
460 diff --git a/x/y b/x/y
461 deleted file mode 100644
461 deleted file mode 100644
462 --- a/x/y
462 --- a/x/y
463 +++ /dev/null
463 +++ /dev/null
464 @@ -1,1 +0,0 @@
464 @@ -1,1 +0,0 @@
465 -y1
465 -y1
466
466
467 # root to parent:
467 # root to parent:
468
468
469 % hg st -C --rev 0 --rev .
469 % hg st -C --rev 0 --rev .
470 A b
470 A b
471 a
471 a
472 R a
472 R a
473
473
474 % hg diff --git --rev 0 --rev .
474 % hg diff --git --rev 0 --rev .
475 diff --git a/a b/b
475 diff --git a/a b/b
476 rename from a
476 rename from a
477 rename to b
477 rename to b
478 --- a/a
478 --- a/a
479 +++ b/b
479 +++ b/b
480 @@ -1,1 +1,3 @@
480 @@ -1,1 +1,3 @@
481 a
481 a
482 +2
482 +2
483 +b1
483 +b1
484
484
485 # parent to root:
485 # parent to root:
486
486
487 % hg st -C --rev . --rev 0
487 % hg st -C --rev . --rev 0
488 A a
488 A a
489 b
489 b
490 R b
490 R b
491
491
492 % hg diff --git --rev . --rev 0
492 % hg diff --git --rev . --rev 0
493 diff --git a/b b/a
493 diff --git a/b b/a
494 rename from b
494 rename from b
495 rename to a
495 rename to a
496 --- a/b
496 --- a/b
497 +++ b/a
497 +++ b/a
498 @@ -1,3 +1,1 @@
498 @@ -1,3 +1,1 @@
499 a
499 a
500 -2
500 -2
501 -b1
501 -b1
502
502
503 # branch to parent:
503 # branch to parent:
504
504
505 % hg st -C --rev 2 --rev .
505 % hg st -C --rev 2 --rev .
506 A b
506 A b
507 a
507 a
508 R a
508 R a
509 R x/y
509 R x/y
510
510
511 % hg diff --git --rev 2 --rev .
511 % hg diff --git --rev 2 --rev .
512 diff --git a/a b/b
512 diff --git a/a b/b
513 rename from a
513 rename from a
514 rename to b
514 rename to b
515 --- a/a
515 --- a/a
516 +++ b/b
516 +++ b/b
517 @@ -1,3 +1,3 @@
517 @@ -1,3 +1,3 @@
518 a
518 a
519 -m1
519 -m1
520 -m2
520 -m2
521 +2
521 +2
522 +b1
522 +b1
523 diff --git a/x/y b/x/y
523 diff --git a/x/y b/x/y
524 deleted file mode 100644
524 deleted file mode 100644
525 --- a/x/y
525 --- a/x/y
526 +++ /dev/null
526 +++ /dev/null
527 @@ -1,1 +0,0 @@
527 @@ -1,1 +0,0 @@
528 -y1
528 -y1
529
529
530 # parent to branch:
530 # parent to branch:
531
531
532 % hg st -C --rev . --rev 2
532 % hg st -C --rev . --rev 2
533 A a
533 A a
534 b
534 b
535 A x/y
535 A x/y
536 R b
536 R b
537
537
538 % hg diff --git --rev . --rev 2
538 % hg diff --git --rev . --rev 2
539 diff --git a/b b/a
539 diff --git a/b b/a
540 rename from b
540 rename from b
541 rename to a
541 rename to a
542 --- a/b
542 --- a/b
543 +++ b/a
543 +++ b/a
544 @@ -1,3 +1,3 @@
544 @@ -1,3 +1,3 @@
545 a
545 a
546 -2
546 -2
547 -b1
547 -b1
548 +m1
548 +m1
549 +m2
549 +m2
550 diff --git a/x/y b/x/y
550 diff --git a/x/y b/x/y
551 new file mode 100644
551 new file mode 100644
552 --- /dev/null
552 --- /dev/null
553 +++ b/x/y
553 +++ b/x/y
554 @@ -0,0 +1,1 @@
554 @@ -0,0 +1,1 @@
555 +y1
555 +y1
556
556
557
557
558 single copy
558 single copy
559
559
560 $ tb "hg cp a b" "add b b1" "add a w"
560 $ tb "hg cp a b" "add b b1" "add a w"
561 % add a 3
561 % add a 3
562 % hg ci -m t0
562 % hg ci -m t0
563 created new head
563 created new head
564 % hg cp a b
564 % hg cp a b
565 % hg ci -m t1
565 % hg ci -m t1
566 % add b b1
566 % add b b1
567 % hg ci -m t2
567 % hg ci -m t2
568 % add a w
568 % add a w
569
569
570 # working to parent:
570 # working to parent:
571
571
572 % hg st -C
572 % hg st -C
573 M a
573 M a
574
574
575 % hg diff --git
575 % hg diff --git
576 diff --git a/a b/a
576 diff --git a/a b/a
577 --- a/a
577 --- a/a
578 +++ b/a
578 +++ b/a
579 @@ -1,2 +1,3 @@
579 @@ -1,2 +1,3 @@
580 a
580 a
581 3
581 3
582 +w
582 +w
583
583
584 # working to root:
584 # working to root:
585
585
586 % hg st -C --rev 0
586 % hg st -C --rev 0
587 M a
587 M a
588 A b
588 A b
589 a
589 a
590
590
591 % hg diff --git --rev 0
591 % hg diff --git --rev 0
592 diff --git a/a b/a
592 diff --git a/a b/a
593 --- a/a
593 --- a/a
594 +++ b/a
594 +++ b/a
595 @@ -1,1 +1,3 @@
595 @@ -1,1 +1,3 @@
596 a
596 a
597 +3
597 +3
598 +w
598 +w
599 diff --git a/a b/b
599 diff --git a/a b/b
600 copy from a
600 copy from a
601 copy to b
601 copy to b
602 --- a/a
602 --- a/a
603 +++ b/b
603 +++ b/b
604 @@ -1,1 +1,3 @@
604 @@ -1,1 +1,3 @@
605 a
605 a
606 +3
606 +3
607 +b1
607 +b1
608
608
609 # working to branch:
609 # working to branch:
610
610
611 % hg st -C --rev 2
611 % hg st -C --rev 2
612 M a
612 M a
613 A b
613 A b
614 a
614 a
615 R x/y
615 R x/y
616
616
617 % hg diff --git --rev 2
617 % hg diff --git --rev 2
618 diff --git a/a b/a
618 diff --git a/a b/a
619 --- a/a
619 --- a/a
620 +++ b/a
620 +++ b/a
621 @@ -1,3 +1,3 @@
621 @@ -1,3 +1,3 @@
622 a
622 a
623 -m1
623 -m1
624 -m2
624 -m2
625 +3
625 +3
626 +w
626 +w
627 diff --git a/a b/b
627 diff --git a/a b/b
628 copy from a
628 copy from a
629 copy to b
629 copy to b
630 --- a/a
630 --- a/a
631 +++ b/b
631 +++ b/b
632 @@ -1,3 +1,3 @@
632 @@ -1,3 +1,3 @@
633 a
633 a
634 -m1
634 -m1
635 -m2
635 -m2
636 +3
636 +3
637 +b1
637 +b1
638 diff --git a/x/y b/x/y
638 diff --git a/x/y b/x/y
639 deleted file mode 100644
639 deleted file mode 100644
640 --- a/x/y
640 --- a/x/y
641 +++ /dev/null
641 +++ /dev/null
642 @@ -1,1 +0,0 @@
642 @@ -1,1 +0,0 @@
643 -y1
643 -y1
644
644
645 # root to parent:
645 # root to parent:
646
646
647 % hg st -C --rev 0 --rev .
647 % hg st -C --rev 0 --rev .
648 M a
648 M a
649 A b
649 A b
650 a
650 a
651
651
652 % hg diff --git --rev 0 --rev .
652 % hg diff --git --rev 0 --rev .
653 diff --git a/a b/a
653 diff --git a/a b/a
654 --- a/a
654 --- a/a
655 +++ b/a
655 +++ b/a
656 @@ -1,1 +1,2 @@
656 @@ -1,1 +1,2 @@
657 a
657 a
658 +3
658 +3
659 diff --git a/a b/b
659 diff --git a/a b/b
660 copy from a
660 copy from a
661 copy to b
661 copy to b
662 --- a/a
662 --- a/a
663 +++ b/b
663 +++ b/b
664 @@ -1,1 +1,3 @@
664 @@ -1,1 +1,3 @@
665 a
665 a
666 +3
666 +3
667 +b1
667 +b1
668
668
669 # parent to root:
669 # parent to root:
670
670
671 % hg st -C --rev . --rev 0
671 % hg st -C --rev . --rev 0
672 M a
672 M a
673 R b
673 R b
674
674
675 % hg diff --git --rev . --rev 0
675 % hg diff --git --rev . --rev 0
676 diff --git a/a b/a
676 diff --git a/a b/a
677 --- a/a
677 --- a/a
678 +++ b/a
678 +++ b/a
679 @@ -1,2 +1,1 @@
679 @@ -1,2 +1,1 @@
680 a
680 a
681 -3
681 -3
682 diff --git a/b b/b
682 diff --git a/b b/b
683 deleted file mode 100644
683 deleted file mode 100644
684 --- a/b
684 --- a/b
685 +++ /dev/null
685 +++ /dev/null
686 @@ -1,3 +0,0 @@
686 @@ -1,3 +0,0 @@
687 -a
687 -a
688 -3
688 -3
689 -b1
689 -b1
690
690
691 # branch to parent:
691 # branch to parent:
692
692
693 % hg st -C --rev 2 --rev .
693 % hg st -C --rev 2 --rev .
694 M a
694 M a
695 A b
695 A b
696 a
696 a
697 R x/y
697 R x/y
698
698
699 % hg diff --git --rev 2 --rev .
699 % hg diff --git --rev 2 --rev .
700 diff --git a/a b/a
700 diff --git a/a b/a
701 --- a/a
701 --- a/a
702 +++ b/a
702 +++ b/a
703 @@ -1,3 +1,2 @@
703 @@ -1,3 +1,2 @@
704 a
704 a
705 -m1
705 -m1
706 -m2
706 -m2
707 +3
707 +3
708 diff --git a/a b/b
708 diff --git a/a b/b
709 copy from a
709 copy from a
710 copy to b
710 copy to b
711 --- a/a
711 --- a/a
712 +++ b/b
712 +++ b/b
713 @@ -1,3 +1,3 @@
713 @@ -1,3 +1,3 @@
714 a
714 a
715 -m1
715 -m1
716 -m2
716 -m2
717 +3
717 +3
718 +b1
718 +b1
719 diff --git a/x/y b/x/y
719 diff --git a/x/y b/x/y
720 deleted file mode 100644
720 deleted file mode 100644
721 --- a/x/y
721 --- a/x/y
722 +++ /dev/null
722 +++ /dev/null
723 @@ -1,1 +0,0 @@
723 @@ -1,1 +0,0 @@
724 -y1
724 -y1
725
725
726 # parent to branch:
726 # parent to branch:
727
727
728 % hg st -C --rev . --rev 2
728 % hg st -C --rev . --rev 2
729 M a
729 M a
730 A x/y
730 A x/y
731 R b
731 R b
732
732
733 % hg diff --git --rev . --rev 2
733 % hg diff --git --rev . --rev 2
734 diff --git a/a b/a
734 diff --git a/a b/a
735 --- a/a
735 --- a/a
736 +++ b/a
736 +++ b/a
737 @@ -1,2 +1,3 @@
737 @@ -1,2 +1,3 @@
738 a
738 a
739 -3
739 -3
740 +m1
740 +m1
741 +m2
741 +m2
742 diff --git a/b b/b
742 diff --git a/b b/b
743 deleted file mode 100644
743 deleted file mode 100644
744 --- a/b
744 --- a/b
745 +++ /dev/null
745 +++ /dev/null
746 @@ -1,3 +0,0 @@
746 @@ -1,3 +0,0 @@
747 -a
747 -a
748 -3
748 -3
749 -b1
749 -b1
750 diff --git a/x/y b/x/y
750 diff --git a/x/y b/x/y
751 new file mode 100644
751 new file mode 100644
752 --- /dev/null
752 --- /dev/null
753 +++ b/x/y
753 +++ b/x/y
754 @@ -0,0 +1,1 @@
754 @@ -0,0 +1,1 @@
755 +y1
755 +y1
756
756
757
757
758 rename chain
758 rename chain
759
759
760 $ tb "hg mv a b" "hg mv b c" "hg mv c d"
760 $ tb "hg mv a b" "hg mv b c" "hg mv c d"
761 % add a 4
761 % add a 4
762 % hg ci -m t0
762 % hg ci -m t0
763 created new head
763 created new head
764 % hg mv a b
764 % hg mv a b
765 % hg ci -m t1
765 % hg ci -m t1
766 % hg mv b c
766 % hg mv b c
767 % hg ci -m t2
767 % hg ci -m t2
768 % hg mv c d
768 % hg mv c d
769
769
770 # working to parent:
770 # working to parent:
771
771
772 % hg st -C
772 % hg st -C
773 A d
773 A d
774 c
774 c
775 R c
775 R c
776
776
777 % hg diff --git
777 % hg diff --git
778 diff --git a/c b/d
778 diff --git a/c b/d
779 rename from c
779 rename from c
780 rename to d
780 rename to d
781
781
782 # working to root:
782 # working to root:
783
783
784 % hg st -C --rev 0
784 % hg st -C --rev 0
785 A d
785 A d
786 a
786 a
787 R a
787 R a
788
788
789 % hg diff --git --rev 0
789 % hg diff --git --rev 0
790 diff --git a/a b/d
790 diff --git a/a b/d
791 rename from a
791 rename from a
792 rename to d
792 rename to d
793 --- a/a
793 --- a/a
794 +++ b/d
794 +++ b/d
795 @@ -1,1 +1,2 @@
795 @@ -1,1 +1,2 @@
796 a
796 a
797 +4
797 +4
798
798
799 # working to branch:
799 # working to branch:
800
800
801 % hg st -C --rev 2
801 % hg st -C --rev 2
802 A d
802 A d
803 a
803 a
804 R a
804 R a
805 R x/y
805 R x/y
806
806
807 % hg diff --git --rev 2
807 % hg diff --git --rev 2
808 diff --git a/a b/d
808 diff --git a/a b/d
809 rename from a
809 rename from a
810 rename to d
810 rename to d
811 --- a/a
811 --- a/a
812 +++ b/d
812 +++ b/d
813 @@ -1,3 +1,2 @@
813 @@ -1,3 +1,2 @@
814 a
814 a
815 -m1
815 -m1
816 -m2
816 -m2
817 +4
817 +4
818 diff --git a/x/y b/x/y
818 diff --git a/x/y b/x/y
819 deleted file mode 100644
819 deleted file mode 100644
820 --- a/x/y
820 --- a/x/y
821 +++ /dev/null
821 +++ /dev/null
822 @@ -1,1 +0,0 @@
822 @@ -1,1 +0,0 @@
823 -y1
823 -y1
824
824
825 # root to parent:
825 # root to parent:
826
826
827 % hg st -C --rev 0 --rev .
827 % hg st -C --rev 0 --rev .
828 A c
828 A c
829 a
829 a
830 R a
830 R a
831
831
832 % hg diff --git --rev 0 --rev .
832 % hg diff --git --rev 0 --rev .
833 diff --git a/a b/c
833 diff --git a/a b/c
834 rename from a
834 rename from a
835 rename to c
835 rename to c
836 --- a/a
836 --- a/a
837 +++ b/c
837 +++ b/c
838 @@ -1,1 +1,2 @@
838 @@ -1,1 +1,2 @@
839 a
839 a
840 +4
840 +4
841
841
842 # parent to root:
842 # parent to root:
843
843
844 % hg st -C --rev . --rev 0
844 % hg st -C --rev . --rev 0
845 A a
845 A a
846 c
846 c
847 R c
847 R c
848
848
849 % hg diff --git --rev . --rev 0
849 % hg diff --git --rev . --rev 0
850 diff --git a/c b/a
850 diff --git a/c b/a
851 rename from c
851 rename from c
852 rename to a
852 rename to a
853 --- a/c
853 --- a/c
854 +++ b/a
854 +++ b/a
855 @@ -1,2 +1,1 @@
855 @@ -1,2 +1,1 @@
856 a
856 a
857 -4
857 -4
858
858
859 # branch to parent:
859 # branch to parent:
860
860
861 % hg st -C --rev 2 --rev .
861 % hg st -C --rev 2 --rev .
862 A c
862 A c
863 a
863 a
864 R a
864 R a
865 R x/y
865 R x/y
866
866
867 % hg diff --git --rev 2 --rev .
867 % hg diff --git --rev 2 --rev .
868 diff --git a/a b/c
868 diff --git a/a b/c
869 rename from a
869 rename from a
870 rename to c
870 rename to c
871 --- a/a
871 --- a/a
872 +++ b/c
872 +++ b/c
873 @@ -1,3 +1,2 @@
873 @@ -1,3 +1,2 @@
874 a
874 a
875 -m1
875 -m1
876 -m2
876 -m2
877 +4
877 +4
878 diff --git a/x/y b/x/y
878 diff --git a/x/y b/x/y
879 deleted file mode 100644
879 deleted file mode 100644
880 --- a/x/y
880 --- a/x/y
881 +++ /dev/null
881 +++ /dev/null
882 @@ -1,1 +0,0 @@
882 @@ -1,1 +0,0 @@
883 -y1
883 -y1
884
884
885 # parent to branch:
885 # parent to branch:
886
886
887 % hg st -C --rev . --rev 2
887 % hg st -C --rev . --rev 2
888 A a
888 A a
889 c
889 c
890 A x/y
890 A x/y
891 R c
891 R c
892
892
893 % hg diff --git --rev . --rev 2
893 % hg diff --git --rev . --rev 2
894 diff --git a/c b/a
894 diff --git a/c b/a
895 rename from c
895 rename from c
896 rename to a
896 rename to a
897 --- a/c
897 --- a/c
898 +++ b/a
898 +++ b/a
899 @@ -1,2 +1,3 @@
899 @@ -1,2 +1,3 @@
900 a
900 a
901 -4
901 -4
902 +m1
902 +m1
903 +m2
903 +m2
904 diff --git a/x/y b/x/y
904 diff --git a/x/y b/x/y
905 new file mode 100644
905 new file mode 100644
906 --- /dev/null
906 --- /dev/null
907 +++ b/x/y
907 +++ b/x/y
908 @@ -0,0 +1,1 @@
908 @@ -0,0 +1,1 @@
909 +y1
909 +y1
910
910
911
911
912 copy chain
912 copy chain
913
913
914 $ tb "hg cp a b" "hg cp b c" "hg cp c d"
914 $ tb "hg cp a b" "hg cp b c" "hg cp c d"
915 % add a 5
915 % add a 5
916 % hg ci -m t0
916 % hg ci -m t0
917 created new head
917 created new head
918 % hg cp a b
918 % hg cp a b
919 % hg ci -m t1
919 % hg ci -m t1
920 % hg cp b c
920 % hg cp b c
921 % hg ci -m t2
921 % hg ci -m t2
922 % hg cp c d
922 % hg cp c d
923
923
924 # working to parent:
924 # working to parent:
925
925
926 % hg st -C
926 % hg st -C
927 A d
927 A d
928 c
928 c
929
929
930 % hg diff --git
930 % hg diff --git
931 diff --git a/c b/d
931 diff --git a/c b/d
932 copy from c
932 copy from c
933 copy to d
933 copy to d
934
934
935 # working to root:
935 # working to root:
936
936
937 % hg st -C --rev 0
937 % hg st -C --rev 0
938 M a
938 M a
939 A b
939 A b
940 a
940 a
941 A c
941 A c
942 a
942 a
943 A d
943 A d
944 a
944 a
945
945
946 % hg diff --git --rev 0
946 % hg diff --git --rev 0
947 diff --git a/a b/a
947 diff --git a/a b/a
948 --- a/a
948 --- a/a
949 +++ b/a
949 +++ b/a
950 @@ -1,1 +1,2 @@
950 @@ -1,1 +1,2 @@
951 a
951 a
952 +5
952 +5
953 diff --git a/a b/b
953 diff --git a/a b/b
954 copy from a
954 copy from a
955 copy to b
955 copy to b
956 --- a/a
956 --- a/a
957 +++ b/b
957 +++ b/b
958 @@ -1,1 +1,2 @@
958 @@ -1,1 +1,2 @@
959 a
959 a
960 +5
960 +5
961 diff --git a/a b/c
961 diff --git a/a b/c
962 copy from a
962 copy from a
963 copy to c
963 copy to c
964 --- a/a
964 --- a/a
965 +++ b/c
965 +++ b/c
966 @@ -1,1 +1,2 @@
966 @@ -1,1 +1,2 @@
967 a
967 a
968 +5
968 +5
969 diff --git a/a b/d
969 diff --git a/a b/d
970 copy from a
970 copy from a
971 copy to d
971 copy to d
972 --- a/a
972 --- a/a
973 +++ b/d
973 +++ b/d
974 @@ -1,1 +1,2 @@
974 @@ -1,1 +1,2 @@
975 a
975 a
976 +5
976 +5
977
977
978 # working to branch:
978 # working to branch:
979
979
980 % hg st -C --rev 2
980 % hg st -C --rev 2
981 M a
981 M a
982 A b
982 A b
983 a
983 a
984 A c
984 A c
985 a
985 a
986 A d
986 A d
987 a
987 a
988 R x/y
988 R x/y
989
989
990 % hg diff --git --rev 2
990 % hg diff --git --rev 2
991 diff --git a/a b/a
991 diff --git a/a b/a
992 --- a/a
992 --- a/a
993 +++ b/a
993 +++ b/a
994 @@ -1,3 +1,2 @@
994 @@ -1,3 +1,2 @@
995 a
995 a
996 -m1
996 -m1
997 -m2
997 -m2
998 +5
998 +5
999 diff --git a/a b/b
999 diff --git a/a b/b
1000 copy from a
1000 copy from a
1001 copy to b
1001 copy to b
1002 --- a/a
1002 --- a/a
1003 +++ b/b
1003 +++ b/b
1004 @@ -1,3 +1,2 @@
1004 @@ -1,3 +1,2 @@
1005 a
1005 a
1006 -m1
1006 -m1
1007 -m2
1007 -m2
1008 +5
1008 +5
1009 diff --git a/a b/c
1009 diff --git a/a b/c
1010 copy from a
1010 copy from a
1011 copy to c
1011 copy to c
1012 --- a/a
1012 --- a/a
1013 +++ b/c
1013 +++ b/c
1014 @@ -1,3 +1,2 @@
1014 @@ -1,3 +1,2 @@
1015 a
1015 a
1016 -m1
1016 -m1
1017 -m2
1017 -m2
1018 +5
1018 +5
1019 diff --git a/a b/d
1019 diff --git a/a b/d
1020 copy from a
1020 copy from a
1021 copy to d
1021 copy to d
1022 --- a/a
1022 --- a/a
1023 +++ b/d
1023 +++ b/d
1024 @@ -1,3 +1,2 @@
1024 @@ -1,3 +1,2 @@
1025 a
1025 a
1026 -m1
1026 -m1
1027 -m2
1027 -m2
1028 +5
1028 +5
1029 diff --git a/x/y b/x/y
1029 diff --git a/x/y b/x/y
1030 deleted file mode 100644
1030 deleted file mode 100644
1031 --- a/x/y
1031 --- a/x/y
1032 +++ /dev/null
1032 +++ /dev/null
1033 @@ -1,1 +0,0 @@
1033 @@ -1,1 +0,0 @@
1034 -y1
1034 -y1
1035
1035
1036 # root to parent:
1036 # root to parent:
1037
1037
1038 % hg st -C --rev 0 --rev .
1038 % hg st -C --rev 0 --rev .
1039 M a
1039 M a
1040 A b
1040 A b
1041 a
1041 a
1042 A c
1042 A c
1043 a
1043 a
1044
1044
1045 % hg diff --git --rev 0 --rev .
1045 % hg diff --git --rev 0 --rev .
1046 diff --git a/a b/a
1046 diff --git a/a b/a
1047 --- a/a
1047 --- a/a
1048 +++ b/a
1048 +++ b/a
1049 @@ -1,1 +1,2 @@
1049 @@ -1,1 +1,2 @@
1050 a
1050 a
1051 +5
1051 +5
1052 diff --git a/a b/b
1052 diff --git a/a b/b
1053 copy from a
1053 copy from a
1054 copy to b
1054 copy to b
1055 --- a/a
1055 --- a/a
1056 +++ b/b
1056 +++ b/b
1057 @@ -1,1 +1,2 @@
1057 @@ -1,1 +1,2 @@
1058 a
1058 a
1059 +5
1059 +5
1060 diff --git a/a b/c
1060 diff --git a/a b/c
1061 copy from a
1061 copy from a
1062 copy to c
1062 copy to c
1063 --- a/a
1063 --- a/a
1064 +++ b/c
1064 +++ b/c
1065 @@ -1,1 +1,2 @@
1065 @@ -1,1 +1,2 @@
1066 a
1066 a
1067 +5
1067 +5
1068
1068
1069 # parent to root:
1069 # parent to root:
1070
1070
1071 % hg st -C --rev . --rev 0
1071 % hg st -C --rev . --rev 0
1072 M a
1072 M a
1073 R b
1073 R b
1074 R c
1074 R c
1075
1075
1076 % hg diff --git --rev . --rev 0
1076 % hg diff --git --rev . --rev 0
1077 diff --git a/a b/a
1077 diff --git a/a b/a
1078 --- a/a
1078 --- a/a
1079 +++ b/a
1079 +++ b/a
1080 @@ -1,2 +1,1 @@
1080 @@ -1,2 +1,1 @@
1081 a
1081 a
1082 -5
1082 -5
1083 diff --git a/b b/b
1083 diff --git a/b b/b
1084 deleted file mode 100644
1084 deleted file mode 100644
1085 --- a/b
1085 --- a/b
1086 +++ /dev/null
1086 +++ /dev/null
1087 @@ -1,2 +0,0 @@
1087 @@ -1,2 +0,0 @@
1088 -a
1088 -a
1089 -5
1089 -5
1090 diff --git a/c b/c
1090 diff --git a/c b/c
1091 deleted file mode 100644
1091 deleted file mode 100644
1092 --- a/c
1092 --- a/c
1093 +++ /dev/null
1093 +++ /dev/null
1094 @@ -1,2 +0,0 @@
1094 @@ -1,2 +0,0 @@
1095 -a
1095 -a
1096 -5
1096 -5
1097
1097
1098 # branch to parent:
1098 # branch to parent:
1099
1099
1100 % hg st -C --rev 2 --rev .
1100 % hg st -C --rev 2 --rev .
1101 M a
1101 M a
1102 A b
1102 A b
1103 a
1103 a
1104 A c
1104 A c
1105 a
1105 a
1106 R x/y
1106 R x/y
1107
1107
1108 % hg diff --git --rev 2 --rev .
1108 % hg diff --git --rev 2 --rev .
1109 diff --git a/a b/a
1109 diff --git a/a b/a
1110 --- a/a
1110 --- a/a
1111 +++ b/a
1111 +++ b/a
1112 @@ -1,3 +1,2 @@
1112 @@ -1,3 +1,2 @@
1113 a
1113 a
1114 -m1
1114 -m1
1115 -m2
1115 -m2
1116 +5
1116 +5
1117 diff --git a/a b/b
1117 diff --git a/a b/b
1118 copy from a
1118 copy from a
1119 copy to b
1119 copy to b
1120 --- a/a
1120 --- a/a
1121 +++ b/b
1121 +++ b/b
1122 @@ -1,3 +1,2 @@
1122 @@ -1,3 +1,2 @@
1123 a
1123 a
1124 -m1
1124 -m1
1125 -m2
1125 -m2
1126 +5
1126 +5
1127 diff --git a/a b/c
1127 diff --git a/a b/c
1128 copy from a
1128 copy from a
1129 copy to c
1129 copy to c
1130 --- a/a
1130 --- a/a
1131 +++ b/c
1131 +++ b/c
1132 @@ -1,3 +1,2 @@
1132 @@ -1,3 +1,2 @@
1133 a
1133 a
1134 -m1
1134 -m1
1135 -m2
1135 -m2
1136 +5
1136 +5
1137 diff --git a/x/y b/x/y
1137 diff --git a/x/y b/x/y
1138 deleted file mode 100644
1138 deleted file mode 100644
1139 --- a/x/y
1139 --- a/x/y
1140 +++ /dev/null
1140 +++ /dev/null
1141 @@ -1,1 +0,0 @@
1141 @@ -1,1 +0,0 @@
1142 -y1
1142 -y1
1143
1143
1144 # parent to branch:
1144 # parent to branch:
1145
1145
1146 % hg st -C --rev . --rev 2
1146 % hg st -C --rev . --rev 2
1147 M a
1147 M a
1148 A x/y
1148 A x/y
1149 R b
1149 R b
1150 R c
1150 R c
1151
1151
1152 % hg diff --git --rev . --rev 2
1152 % hg diff --git --rev . --rev 2
1153 diff --git a/a b/a
1153 diff --git a/a b/a
1154 --- a/a
1154 --- a/a
1155 +++ b/a
1155 +++ b/a
1156 @@ -1,2 +1,3 @@
1156 @@ -1,2 +1,3 @@
1157 a
1157 a
1158 -5
1158 -5
1159 +m1
1159 +m1
1160 +m2
1160 +m2
1161 diff --git a/b b/b
1161 diff --git a/b b/b
1162 deleted file mode 100644
1162 deleted file mode 100644
1163 --- a/b
1163 --- a/b
1164 +++ /dev/null
1164 +++ /dev/null
1165 @@ -1,2 +0,0 @@
1165 @@ -1,2 +0,0 @@
1166 -a
1166 -a
1167 -5
1167 -5
1168 diff --git a/c b/c
1168 diff --git a/c b/c
1169 deleted file mode 100644
1169 deleted file mode 100644
1170 --- a/c
1170 --- a/c
1171 +++ /dev/null
1171 +++ /dev/null
1172 @@ -1,2 +0,0 @@
1172 @@ -1,2 +0,0 @@
1173 -a
1173 -a
1174 -5
1174 -5
1175 diff --git a/x/y b/x/y
1175 diff --git a/x/y b/x/y
1176 new file mode 100644
1176 new file mode 100644
1177 --- /dev/null
1177 --- /dev/null
1178 +++ b/x/y
1178 +++ b/x/y
1179 @@ -0,0 +1,1 @@
1179 @@ -0,0 +1,1 @@
1180 +y1
1180 +y1
1181
1181
1182
1182
1183 circular rename
1183 circular rename
1184
1184
1185 $ tb "add a a1" "hg mv a b" "hg mv b a"
1185 $ tb "add a a1" "hg mv a b" "hg mv b a"
1186 % add a 6
1186 % add a 6
1187 % hg ci -m t0
1187 % hg ci -m t0
1188 created new head
1188 created new head
1189 % add a a1
1189 % add a a1
1190 % hg ci -m t1
1190 % hg ci -m t1
1191 % hg mv a b
1191 % hg mv a b
1192 % hg ci -m t2
1192 % hg ci -m t2
1193 % hg mv b a
1193 % hg mv b a
1194
1194
1195 # working to parent:
1195 # working to parent:
1196
1196
1197 % hg st -C
1197 % hg st -C
1198 A a
1198 A a
1199 b
1199 b
1200 R b
1200 R b
1201
1201
1202 % hg diff --git
1202 % hg diff --git
1203 diff --git a/b b/a
1203 diff --git a/b b/a
1204 rename from b
1204 rename from b
1205 rename to a
1205 rename to a
1206
1206
1207 # working to root:
1207 # working to root:
1208
1208
1209 % hg st -C --rev 0
1209 % hg st -C --rev 0
1210 M a
1210 M a
1211
1211
1212 % hg diff --git --rev 0
1212 % hg diff --git --rev 0
1213 diff --git a/a b/a
1213 diff --git a/a b/a
1214 --- a/a
1214 --- a/a
1215 +++ b/a
1215 +++ b/a
1216 @@ -1,1 +1,3 @@
1216 @@ -1,1 +1,3 @@
1217 a
1217 a
1218 +6
1218 +6
1219 +a1
1219 +a1
1220
1220
1221 # working to branch:
1221 # working to branch:
1222
1222
1223 % hg st -C --rev 2
1223 % hg st -C --rev 2
1224 M a
1224 M a
1225 R x/y
1225 R x/y
1226
1226
1227 % hg diff --git --rev 2
1227 % hg diff --git --rev 2
1228 diff --git a/a b/a
1228 diff --git a/a b/a
1229 --- a/a
1229 --- a/a
1230 +++ b/a
1230 +++ b/a
1231 @@ -1,3 +1,3 @@
1231 @@ -1,3 +1,3 @@
1232 a
1232 a
1233 -m1
1233 -m1
1234 -m2
1234 -m2
1235 +6
1235 +6
1236 +a1
1236 +a1
1237 diff --git a/x/y b/x/y
1237 diff --git a/x/y b/x/y
1238 deleted file mode 100644
1238 deleted file mode 100644
1239 --- a/x/y
1239 --- a/x/y
1240 +++ /dev/null
1240 +++ /dev/null
1241 @@ -1,1 +0,0 @@
1241 @@ -1,1 +0,0 @@
1242 -y1
1242 -y1
1243
1243
1244 # root to parent:
1244 # root to parent:
1245
1245
1246 % hg st -C --rev 0 --rev .
1246 % hg st -C --rev 0 --rev .
1247 A b
1247 A b
1248 a
1248 a
1249 R a
1249 R a
1250
1250
1251 % hg diff --git --rev 0 --rev .
1251 % hg diff --git --rev 0 --rev .
1252 diff --git a/a b/b
1252 diff --git a/a b/b
1253 rename from a
1253 rename from a
1254 rename to b
1254 rename to b
1255 --- a/a
1255 --- a/a
1256 +++ b/b
1256 +++ b/b
1257 @@ -1,1 +1,3 @@
1257 @@ -1,1 +1,3 @@
1258 a
1258 a
1259 +6
1259 +6
1260 +a1
1260 +a1
1261
1261
1262 # parent to root:
1262 # parent to root:
1263
1263
1264 % hg st -C --rev . --rev 0
1264 % hg st -C --rev . --rev 0
1265 A a
1265 A a
1266 b
1266 b
1267 R b
1267 R b
1268
1268
1269 % hg diff --git --rev . --rev 0
1269 % hg diff --git --rev . --rev 0
1270 diff --git a/b b/a
1270 diff --git a/b b/a
1271 rename from b
1271 rename from b
1272 rename to a
1272 rename to a
1273 --- a/b
1273 --- a/b
1274 +++ b/a
1274 +++ b/a
1275 @@ -1,3 +1,1 @@
1275 @@ -1,3 +1,1 @@
1276 a
1276 a
1277 -6
1277 -6
1278 -a1
1278 -a1
1279
1279
1280 # branch to parent:
1280 # branch to parent:
1281
1281
1282 % hg st -C --rev 2 --rev .
1282 % hg st -C --rev 2 --rev .
1283 A b
1283 A b
1284 a
1284 a
1285 R a
1285 R a
1286 R x/y
1286 R x/y
1287
1287
1288 % hg diff --git --rev 2 --rev .
1288 % hg diff --git --rev 2 --rev .
1289 diff --git a/a b/b
1289 diff --git a/a b/b
1290 rename from a
1290 rename from a
1291 rename to b
1291 rename to b
1292 --- a/a
1292 --- a/a
1293 +++ b/b
1293 +++ b/b
1294 @@ -1,3 +1,3 @@
1294 @@ -1,3 +1,3 @@
1295 a
1295 a
1296 -m1
1296 -m1
1297 -m2
1297 -m2
1298 +6
1298 +6
1299 +a1
1299 +a1
1300 diff --git a/x/y b/x/y
1300 diff --git a/x/y b/x/y
1301 deleted file mode 100644
1301 deleted file mode 100644
1302 --- a/x/y
1302 --- a/x/y
1303 +++ /dev/null
1303 +++ /dev/null
1304 @@ -1,1 +0,0 @@
1304 @@ -1,1 +0,0 @@
1305 -y1
1305 -y1
1306
1306
1307 # parent to branch:
1307 # parent to branch:
1308
1308
1309 % hg st -C --rev . --rev 2
1309 % hg st -C --rev . --rev 2
1310 A a
1310 A a
1311 b
1311 b
1312 A x/y
1312 A x/y
1313 R b
1313 R b
1314
1314
1315 % hg diff --git --rev . --rev 2
1315 % hg diff --git --rev . --rev 2
1316 diff --git a/b b/a
1316 diff --git a/b b/a
1317 rename from b
1317 rename from b
1318 rename to a
1318 rename to a
1319 --- a/b
1319 --- a/b
1320 +++ b/a
1320 +++ b/a
1321 @@ -1,3 +1,3 @@
1321 @@ -1,3 +1,3 @@
1322 a
1322 a
1323 -6
1323 -6
1324 -a1
1324 -a1
1325 +m1
1325 +m1
1326 +m2
1326 +m2
1327 diff --git a/x/y b/x/y
1327 diff --git a/x/y b/x/y
1328 new file mode 100644
1328 new file mode 100644
1329 --- /dev/null
1329 --- /dev/null
1330 +++ b/x/y
1330 +++ b/x/y
1331 @@ -0,0 +1,1 @@
1331 @@ -0,0 +1,1 @@
1332 +y1
1332 +y1
1333
1333
1334
1334
1335 directory move
1335 directory move
1336
1336
1337 $ tb "hg mv x y" "add y/x x1" "add y/x x2"
1337 $ tb "hg mv x y" "add y/x x1" "add y/x x2"
1338 % add a 7
1338 % add a 7
1339 % hg ci -m t0
1339 % hg ci -m t0
1340 created new head
1340 created new head
1341 % hg mv x y
1341 % hg mv x y
1342 moving x/x to y/x
1342 moving x/x to y/x
1343 % hg ci -m t1
1343 % hg ci -m t1
1344 % add y/x x1
1344 % add y/x x1
1345 % hg ci -m t2
1345 % hg ci -m t2
1346 % add y/x x2
1346 % add y/x x2
1347
1347
1348 # working to parent:
1348 # working to parent:
1349
1349
1350 % hg st -C
1350 % hg st -C
1351 M y/x
1351 M y/x
1352
1352
1353 % hg diff --git
1353 % hg diff --git
1354 diff --git a/y/x b/y/x
1354 diff --git a/y/x b/y/x
1355 --- a/y/x
1355 --- a/y/x
1356 +++ b/y/x
1356 +++ b/y/x
1357 @@ -1,2 +1,3 @@
1357 @@ -1,2 +1,3 @@
1358 x
1358 x
1359 x1
1359 x1
1360 +x2
1360 +x2
1361
1361
1362 # working to root:
1362 # working to root:
1363
1363
1364 % hg st -C --rev 0
1364 % hg st -C --rev 0
1365 M a
1365 M a
1366 A y/x
1366 A y/x
1367 x/x
1367 x/x
1368 R x/x
1368 R x/x
1369
1369
1370 % hg diff --git --rev 0
1370 % hg diff --git --rev 0
1371 diff --git a/a b/a
1371 diff --git a/a b/a
1372 --- a/a
1372 --- a/a
1373 +++ b/a
1373 +++ b/a
1374 @@ -1,1 +1,2 @@
1374 @@ -1,1 +1,2 @@
1375 a
1375 a
1376 +7
1376 +7
1377 diff --git a/x/x b/y/x
1377 diff --git a/x/x b/y/x
1378 rename from x/x
1378 rename from x/x
1379 rename to y/x
1379 rename to y/x
1380 --- a/x/x
1380 --- a/x/x
1381 +++ b/y/x
1381 +++ b/y/x
1382 @@ -1,1 +1,3 @@
1382 @@ -1,1 +1,3 @@
1383 x
1383 x
1384 +x1
1384 +x1
1385 +x2
1385 +x2
1386
1386
1387 # working to branch:
1387 # working to branch:
1388
1388
1389 % hg st -C --rev 2
1389 % hg st -C --rev 2
1390 M a
1390 M a
1391 A y/x
1391 A y/x
1392 x/x
1392 x/x
1393 R x/x
1393 R x/x
1394 R x/y
1394 R x/y
1395
1395
1396 % hg diff --git --rev 2
1396 % hg diff --git --rev 2
1397 diff --git a/a b/a
1397 diff --git a/a b/a
1398 --- a/a
1398 --- a/a
1399 +++ b/a
1399 +++ b/a
1400 @@ -1,3 +1,2 @@
1400 @@ -1,3 +1,2 @@
1401 a
1401 a
1402 -m1
1402 -m1
1403 -m2
1403 -m2
1404 +7
1404 +7
1405 diff --git a/x/y b/x/y
1405 diff --git a/x/y b/x/y
1406 deleted file mode 100644
1406 deleted file mode 100644
1407 --- a/x/y
1407 --- a/x/y
1408 +++ /dev/null
1408 +++ /dev/null
1409 @@ -1,1 +0,0 @@
1409 @@ -1,1 +0,0 @@
1410 -y1
1410 -y1
1411 diff --git a/x/x b/y/x
1411 diff --git a/x/x b/y/x
1412 rename from x/x
1412 rename from x/x
1413 rename to y/x
1413 rename to y/x
1414 --- a/x/x
1414 --- a/x/x
1415 +++ b/y/x
1415 +++ b/y/x
1416 @@ -1,1 +1,3 @@
1416 @@ -1,1 +1,3 @@
1417 x
1417 x
1418 +x1
1418 +x1
1419 +x2
1419 +x2
1420
1420
1421 # root to parent:
1421 # root to parent:
1422
1422
1423 % hg st -C --rev 0 --rev .
1423 % hg st -C --rev 0 --rev .
1424 M a
1424 M a
1425 A y/x
1425 A y/x
1426 x/x
1426 x/x
1427 R x/x
1427 R x/x
1428
1428
1429 % hg diff --git --rev 0 --rev .
1429 % hg diff --git --rev 0 --rev .
1430 diff --git a/a b/a
1430 diff --git a/a b/a
1431 --- a/a
1431 --- a/a
1432 +++ b/a
1432 +++ b/a
1433 @@ -1,1 +1,2 @@
1433 @@ -1,1 +1,2 @@
1434 a
1434 a
1435 +7
1435 +7
1436 diff --git a/x/x b/y/x
1436 diff --git a/x/x b/y/x
1437 rename from x/x
1437 rename from x/x
1438 rename to y/x
1438 rename to y/x
1439 --- a/x/x
1439 --- a/x/x
1440 +++ b/y/x
1440 +++ b/y/x
1441 @@ -1,1 +1,2 @@
1441 @@ -1,1 +1,2 @@
1442 x
1442 x
1443 +x1
1443 +x1
1444
1444
1445 # parent to root:
1445 # parent to root:
1446
1446
1447 % hg st -C --rev . --rev 0
1447 % hg st -C --rev . --rev 0
1448 M a
1448 M a
1449 A x/x
1449 A x/x
1450 y/x
1450 y/x
1451 R y/x
1451 R y/x
1452
1452
1453 % hg diff --git --rev . --rev 0
1453 % hg diff --git --rev . --rev 0
1454 diff --git a/a b/a
1454 diff --git a/a b/a
1455 --- a/a
1455 --- a/a
1456 +++ b/a
1456 +++ b/a
1457 @@ -1,2 +1,1 @@
1457 @@ -1,2 +1,1 @@
1458 a
1458 a
1459 -7
1459 -7
1460 diff --git a/y/x b/x/x
1460 diff --git a/y/x b/x/x
1461 rename from y/x
1461 rename from y/x
1462 rename to x/x
1462 rename to x/x
1463 --- a/y/x
1463 --- a/y/x
1464 +++ b/x/x
1464 +++ b/x/x
1465 @@ -1,2 +1,1 @@
1465 @@ -1,2 +1,1 @@
1466 x
1466 x
1467 -x1
1467 -x1
1468
1468
1469 # branch to parent:
1469 # branch to parent:
1470
1470
1471 % hg st -C --rev 2 --rev .
1471 % hg st -C --rev 2 --rev .
1472 M a
1472 M a
1473 A y/x
1473 A y/x
1474 x/x
1474 x/x
1475 R x/x
1475 R x/x
1476 R x/y
1476 R x/y
1477
1477
1478 % hg diff --git --rev 2 --rev .
1478 % hg diff --git --rev 2 --rev .
1479 diff --git a/a b/a
1479 diff --git a/a b/a
1480 --- a/a
1480 --- a/a
1481 +++ b/a
1481 +++ b/a
1482 @@ -1,3 +1,2 @@
1482 @@ -1,3 +1,2 @@
1483 a
1483 a
1484 -m1
1484 -m1
1485 -m2
1485 -m2
1486 +7
1486 +7
1487 diff --git a/x/y b/x/y
1487 diff --git a/x/y b/x/y
1488 deleted file mode 100644
1488 deleted file mode 100644
1489 --- a/x/y
1489 --- a/x/y
1490 +++ /dev/null
1490 +++ /dev/null
1491 @@ -1,1 +0,0 @@
1491 @@ -1,1 +0,0 @@
1492 -y1
1492 -y1
1493 diff --git a/x/x b/y/x
1493 diff --git a/x/x b/y/x
1494 rename from x/x
1494 rename from x/x
1495 rename to y/x
1495 rename to y/x
1496 --- a/x/x
1496 --- a/x/x
1497 +++ b/y/x
1497 +++ b/y/x
1498 @@ -1,1 +1,2 @@
1498 @@ -1,1 +1,2 @@
1499 x
1499 x
1500 +x1
1500 +x1
1501
1501
1502 # parent to branch:
1502 # parent to branch:
1503
1503
1504 % hg st -C --rev . --rev 2
1504 % hg st -C --rev . --rev 2
1505 M a
1505 M a
1506 A x/x
1506 A x/x
1507 y/x
1507 y/x
1508 A x/y
1508 A x/y
1509 R y/x
1509 R y/x
1510
1510
1511 % hg diff --git --rev . --rev 2
1511 % hg diff --git --rev . --rev 2
1512 diff --git a/a b/a
1512 diff --git a/a b/a
1513 --- a/a
1513 --- a/a
1514 +++ b/a
1514 +++ b/a
1515 @@ -1,2 +1,3 @@
1515 @@ -1,2 +1,3 @@
1516 a
1516 a
1517 -7
1517 -7
1518 +m1
1518 +m1
1519 +m2
1519 +m2
1520 diff --git a/y/x b/x/x
1520 diff --git a/y/x b/x/x
1521 rename from y/x
1521 rename from y/x
1522 rename to x/x
1522 rename to x/x
1523 --- a/y/x
1523 --- a/y/x
1524 +++ b/x/x
1524 +++ b/x/x
1525 @@ -1,2 +1,1 @@
1525 @@ -1,2 +1,1 @@
1526 x
1526 x
1527 -x1
1527 -x1
1528 diff --git a/x/y b/x/y
1528 diff --git a/x/y b/x/y
1529 new file mode 100644
1529 new file mode 100644
1530 --- /dev/null
1530 --- /dev/null
1531 +++ b/x/y
1531 +++ b/x/y
1532 @@ -0,0 +1,1 @@
1532 @@ -0,0 +1,1 @@
1533 +y1
1533 +y1
1534
1534
1535
1535
1536
1536
1537 Cannot implement unrelated branch with tb
1537 Cannot implement unrelated branch with tb
1538 testing copies with unrelated branch
1538 testing copies with unrelated branch
1539
1539
1540 $ hg init unrelated
1540 $ hg init unrelated
1541 $ cd unrelated
1541 $ cd unrelated
1542 $ echo a >> a
1542 $ echo a >> a
1543 $ hg ci -Am adda
1543 $ hg ci -Am adda
1544 adding a
1544 adding a
1545 $ hg mv a b
1545 $ hg mv a b
1546 $ hg ci -m movea
1546 $ hg ci -m movea
1547 $ hg up -C null
1547 $ hg up -C null
1548 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1548 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1549 $ echo a >> a
1549 $ echo a >> a
1550 $ hg ci -Am addunrelateda
1550 $ hg ci -Am addunrelateda
1551 adding a
1551 adding a
1552 created new head
1552 created new head
1553
1553
1554 unrelated branch diff
1554 unrelated branch diff
1555
1555
1556 $ hg diff --git -r 2 -r 1
1556 $ hg diff --git -r 2 -r 1
1557 diff --git a/a b/a
1557 diff --git a/a b/a
1558 deleted file mode 100644
1558 deleted file mode 100644
1559 --- a/a
1559 --- a/a
1560 +++ /dev/null
1560 +++ /dev/null
1561 @@ -1,1 +0,0 @@
1561 @@ -1,1 +0,0 @@
1562 -a
1562 -a
1563 diff --git a/b b/b
1563 diff --git a/b b/b
1564 new file mode 100644
1564 new file mode 100644
1565 --- /dev/null
1565 --- /dev/null
1566 +++ b/b
1566 +++ b/b
1567 @@ -0,0 +1,1 @@
1567 @@ -0,0 +1,1 @@
1568 +a
1568 +a
1569 $ cd ..
1569 $ cd ..
1570
1570
1571
1571
1572 test for case where we didn't look sufficiently far back to find rename ancestor
1572 test for case where we didn't look sufficiently far back to find rename ancestor
1573
1573
1574 $ hg init diffstop
1574 $ hg init diffstop
1575 $ cd diffstop
1575 $ cd diffstop
1576 $ echo > f
1576 $ echo > f
1577 $ hg ci -qAmf
1577 $ hg ci -qAmf
1578 $ hg mv f g
1578 $ hg mv f g
1579 $ hg ci -m'f->g'
1579 $ hg ci -m'f->g'
1580 $ hg up -qr0
1580 $ hg up -qr0
1581 $ touch x
1581 $ touch x
1582 $ hg ci -qAmx
1582 $ hg ci -qAmx
1583 $ echo f > f
1583 $ echo f > f
1584 $ hg ci -qmf=f
1584 $ hg ci -qmf=f
1585 $ hg merge -q
1585 $ hg merge -q
1586 $ hg ci -mmerge
1586 $ hg ci -mmerge
1587 $ hg log -G --template '{rev} {desc}'
1587 $ hg log -G --template '{rev} {desc}'
1588 @ 4 merge
1588 @ 4 merge
1589 |\
1589 |\
1590 | o 3 f=f
1590 | o 3 f=f
1591 | |
1591 | |
1592 | o 2 x
1592 | o 2 x
1593 | |
1593 | |
1594 o | 1 f->g
1594 o | 1 f->g
1595 |/
1595 |/
1596 o 0 f
1596 o 0 f
1597
1597
1598 $ hg diff --git -r 2
1598 $ hg diff --git -r 2
1599 diff --git a/f b/g
1599 diff --git a/f b/g
1600 rename from f
1600 rename from f
1601 rename to g
1601 rename to g
1602 --- a/f
1602 --- a/f
1603 +++ b/g
1603 +++ b/g
1604 @@ -1,1 +1,1 @@
1604 @@ -1,1 +1,1 @@
1605 -
1605 -
1606 +f
1606 +f
1607 $ cd ..
1607 $ cd ..
1608
1608
1609 Additional tricky linkrev case
1609 Additional tricky linkrev case
1610 ------------------------------
1610 ------------------------------
1611
1611
1612 If the first file revision after the diff base has a linkrev pointing to a
1612 If the first file revision after the diff base has a linkrev pointing to a
1613 changeset on another branch with a revision lower that the diff base, we can
1613 changeset on another branch with a revision lower that the diff base, we can
1614 jump past the copy detection limit and fail to detect the rename.
1614 jump past the copy detection limit and fail to detect the rename.
1615
1615
1616 $ hg init diffstoplinkrev
1616 $ hg init diffstoplinkrev
1617 $ cd diffstoplinkrev
1617 $ cd diffstoplinkrev
1618
1618
1619 $ touch f
1619 $ touch f
1620 $ hg ci -Aqm 'empty f'
1620 $ hg ci -Aqm 'empty f'
1621
1621
1622 Make a simple change
1622 Make a simple change
1623
1623
1624 $ echo change > f
1624 $ echo change > f
1625 $ hg ci -m 'change f'
1625 $ hg ci -m 'change f'
1626
1626
1627 Make a rename because we want to track renames. It is also important that the
1627 Make a rename because we want to track renames. It is also important that the
1628 faulty linkrev is not only the "start" commit to ensure the linkrev will be
1628 faulty linkrev is not only the "start" commit to ensure the linkrev will be
1629 used.
1629 used.
1630
1630
1631 $ hg mv f renamed
1631 $ hg mv f renamed
1632 $ hg ci -m renamed
1632 $ hg ci -m renamed
1633
1633
1634 Make a second branch, we use a named branch to create a simple commit
1634 Make a second branch, we use a named branch to create a simple commit
1635 that does not touch f.
1635 that does not touch f.
1636
1636
1637 $ hg up -qr 'desc(empty)'
1637 $ hg up -qr 'desc(empty)'
1638 $ hg branch -q dev
1638 $ hg branch -q dev
1639 $ hg ci -Aqm dev
1639 $ hg ci -Aqm dev
1640
1640
1641 Graft the initial change and the rename. As f was untouched, we reuse the same
1641 Graft the initial change and the rename. As f was untouched, we reuse the same
1642 entry and the linkrev point to the older branch.
1642 entry and the linkrev point to the older branch.
1643
1643
1644 $ hg graft -q 'desc(change)'
1644 $ hg graft -q 'desc(change)'
1645 $ hg graft -q 'desc(renamed)'
1645 $ hg graft -q 'desc(renamed)'
1646
1646
1647 $ hg log -G -T '{rev} {desc}'
1647 $ hg log -G -T '{rev} {desc}'
1648 @ 5 renamed
1648 @ 5 renamed
1649 |
1649 |
1650 o 4 change f
1650 o 4 change f
1651 |
1651 |
1652 o 3 dev
1652 o 3 dev
1653 |
1653 |
1654 | o 2 renamed
1654 | o 2 renamed
1655 | |
1655 | |
1656 | o 1 change f
1656 | o 1 change f
1657 |/
1657 |/
1658 o 0 empty f
1658 o 0 empty f
1659
1659
1660
1660
1661 The copy tracking should still reach rev 3 (branch creation).
1661 The copy tracking should still reach rev 3 (branch creation).
1662 accessing the parent of 5 (renamed) should not jump use to revision 1.
1662 accessing the parent of 5 (renamed) should not jump use to revision 1.
1663
1663
1664 $ hg diff --git -r 'desc(dev)' -r .
1664 $ hg diff --git -r 'desc(dev)' -r .
1665 diff --git a/f b/renamed
1665 diff --git a/f b/renamed
1666 rename from f
1666 rename from f
1667 rename to renamed
1667 rename to renamed
1668 --- a/f
1668 --- a/f
1669 +++ b/renamed
1669 +++ b/renamed
1670 @@ -0,0 +1,1 @@
1670 @@ -0,0 +1,1 @@
1671 +change
1671 +change
1672
1672
1673 Check debug output for copy tracing
1673 Check debug output for copy tracing
1674
1674
1675 $ hg status --copies --rev 'desc(dev)' --rev . --config devel.debug.copies=yes --debug
1675 $ hg status --copies --rev 'desc(dev)' --rev . --config devel.debug.copies=yes --debug
1676 debug.copies: searching copies from a51f36ab1704 to 1f4aa1fd627b
1676 debug.copies: searching copies from a51f36ab1704 to 1f4aa1fd627b
1677 debug.copies: search mode: forward
1677 debug.copies: search mode: forward
1678 debug.copies: looking into rename from a51f36ab1704 to 1f4aa1fd627b
1678 debug.copies: looking into rename from a51f36ab1704 to 1f4aa1fd627b
1679 debug.copies: search limit: 3
1680 debug.copies: missing files to search: 1
1679 debug.copies: missing files to search: 1
1681 debug.copies: tracing file: renamed
1680 debug.copies: tracing file: renamed
1682 debug.copies: rename of: f
1681 debug.copies: rename of: f
1683 debug.copies: time: * seconds (glob)
1682 debug.copies: time: * seconds (glob)
1684 A renamed
1683 A renamed
1685 f
1684 f
1686 R f
1685 R f
1687
1686
1688 Check that merging across the rename works
1687 Check that merging across the rename works
1689
1688
1690 $ echo modified >> renamed
1689 $ echo modified >> renamed
1691 $ hg co -m 4
1690 $ hg co -m 4
1692 merging renamed and f to f
1691 merging renamed and f to f
1693 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1692 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1694
1693
1695 $ cd ..
1694 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now