##// END OF EJS Templates
merge: when rename was made on both sides, use ancestor as merge base...
Martin von Zweigbergk -
r45099:b4057d00 default
parent child Browse files
Show More
@@ -1,1172 +1,1169 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 multiprocessing
11 import multiprocessing
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 _filter(src, dst, t):
33 def _filter(src, dst, t):
34 """filters out invalid copies after chaining"""
34 """filters out invalid copies after chaining"""
35
35
36 # 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')
37 # 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
38 # 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
39 # 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
40 # then was renamed between 'mid' and 'dst'.
40 # then was renamed between 'mid' and 'dst'.
41 #
41 #
42 # case src mid dst result
42 # case src mid dst result
43 # 1 x y - -
43 # 1 x y - -
44 # 2 x y y x->y
44 # 2 x y y x->y
45 # 3 x y x -
45 # 3 x y x -
46 # 4 x y z x->z
46 # 4 x y z x->z
47 # 5 - x y -
47 # 5 - x y -
48 # 6 x x y x->y
48 # 6 x x y x->y
49 #
49 #
50 # _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
51 # 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
52 # 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.
53 # Cases 1, 3, and 5 are then removed by _filter().
53 # Cases 1, 3, and 5 are then removed by _filter().
54
54
55 for k, v in list(t.items()):
55 for k, v in list(t.items()):
56 # remove copies from files that didn't exist
56 # remove copies from files that didn't exist
57 if v not in src:
57 if v not in src:
58 del t[k]
58 del t[k]
59 # remove criss-crossed copies
59 # remove criss-crossed copies
60 elif k in src and v in dst:
60 elif k in src and v in dst:
61 del t[k]
61 del t[k]
62 # remove copies to files that were then removed
62 # remove copies to files that were then removed
63 elif k not in dst:
63 elif k not in dst:
64 del t[k]
64 del t[k]
65
65
66
66
67 def _chain(prefix, suffix):
67 def _chain(prefix, suffix):
68 """chain two sets of copies 'prefix' and 'suffix'"""
68 """chain two sets of copies 'prefix' and 'suffix'"""
69 result = prefix.copy()
69 result = prefix.copy()
70 for key, value in pycompat.iteritems(suffix):
70 for key, value in pycompat.iteritems(suffix):
71 result[key] = prefix.get(value, value)
71 result[key] = prefix.get(value, value)
72 return result
72 return result
73
73
74
74
75 def _tracefile(fctx, am, basemf):
75 def _tracefile(fctx, am, basemf):
76 """return file context that is the ancestor of fctx present in ancestor
76 """return file context that is the ancestor of fctx present in ancestor
77 manifest am
77 manifest am
78
78
79 Note: we used to try and stop after a given limit, however checking if that
79 Note: we used to try and stop after a given limit, however checking if that
80 limit is reached turned out to be very expensive. we are better off
80 limit is reached turned out to be very expensive. we are better off
81 disabling that feature."""
81 disabling that feature."""
82
82
83 for f in fctx.ancestors():
83 for f in fctx.ancestors():
84 path = f.path()
84 path = f.path()
85 if am.get(path, None) == f.filenode():
85 if am.get(path, None) == f.filenode():
86 return path
86 return path
87 if basemf and basemf.get(path, None) == f.filenode():
87 if basemf and basemf.get(path, None) == f.filenode():
88 return path
88 return path
89
89
90
90
91 def _dirstatecopies(repo, match=None):
91 def _dirstatecopies(repo, match=None):
92 ds = repo.dirstate
92 ds = repo.dirstate
93 c = ds.copies().copy()
93 c = ds.copies().copy()
94 for k in list(c):
94 for k in list(c):
95 if ds[k] not in b'anm' or (match and not match(k)):
95 if ds[k] not in b'anm' or (match and not match(k)):
96 del c[k]
96 del c[k]
97 return c
97 return c
98
98
99
99
100 def _computeforwardmissing(a, b, match=None):
100 def _computeforwardmissing(a, b, match=None):
101 """Computes which files are in b but not a.
101 """Computes which files are in b but not a.
102 This is its own function so extensions can easily wrap this call to see what
102 This is its own function so extensions can easily wrap this call to see what
103 files _forwardcopies is about to process.
103 files _forwardcopies is about to process.
104 """
104 """
105 ma = a.manifest()
105 ma = a.manifest()
106 mb = b.manifest()
106 mb = b.manifest()
107 return mb.filesnotin(ma, match=match)
107 return mb.filesnotin(ma, match=match)
108
108
109
109
110 def usechangesetcentricalgo(repo):
110 def usechangesetcentricalgo(repo):
111 """Checks if we should use changeset-centric copy algorithms"""
111 """Checks if we should use changeset-centric copy algorithms"""
112 if repo.filecopiesmode == b'changeset-sidedata':
112 if repo.filecopiesmode == b'changeset-sidedata':
113 return True
113 return True
114 readfrom = repo.ui.config(b'experimental', b'copies.read-from')
114 readfrom = repo.ui.config(b'experimental', b'copies.read-from')
115 changesetsource = (b'changeset-only', b'compatibility')
115 changesetsource = (b'changeset-only', b'compatibility')
116 return readfrom in changesetsource
116 return readfrom in changesetsource
117
117
118
118
119 def _committedforwardcopies(a, b, base, match):
119 def _committedforwardcopies(a, b, base, match):
120 """Like _forwardcopies(), but b.rev() cannot be None (working copy)"""
120 """Like _forwardcopies(), but b.rev() cannot be None (working copy)"""
121 # files might have to be traced back to the fctx parent of the last
121 # files might have to be traced back to the fctx parent of the last
122 # one-side-only changeset, but not further back than that
122 # one-side-only changeset, but not further back than that
123 repo = a._repo
123 repo = a._repo
124
124
125 if usechangesetcentricalgo(repo):
125 if usechangesetcentricalgo(repo):
126 return _changesetforwardcopies(a, b, match)
126 return _changesetforwardcopies(a, b, match)
127
127
128 debug = repo.ui.debugflag and repo.ui.configbool(b'devel', b'debug.copies')
128 debug = repo.ui.debugflag and repo.ui.configbool(b'devel', b'debug.copies')
129 dbg = repo.ui.debug
129 dbg = repo.ui.debug
130 if debug:
130 if debug:
131 dbg(b'debug.copies: looking into rename from %s to %s\n' % (a, b))
131 dbg(b'debug.copies: looking into rename from %s to %s\n' % (a, b))
132 am = a.manifest()
132 am = a.manifest()
133 basemf = None if base is None else base.manifest()
133 basemf = None if base is None else base.manifest()
134
134
135 # find where new files came from
135 # find where new files came from
136 # we currently don't try to find where old files went, too expensive
136 # we currently don't try to find where old files went, too expensive
137 # this means we can miss a case like 'hg rm b; hg cp a b'
137 # this means we can miss a case like 'hg rm b; hg cp a b'
138 cm = {}
138 cm = {}
139
139
140 # Computing the forward missing is quite expensive on large manifests, since
140 # Computing the forward missing is quite expensive on large manifests, since
141 # it compares the entire manifests. We can optimize it in the common use
141 # it compares the entire manifests. We can optimize it in the common use
142 # case of computing what copies are in a commit versus its parent (like
142 # case of computing what copies are in a commit versus its parent (like
143 # during a rebase or histedit). Note, we exclude merge commits from this
143 # during a rebase or histedit). Note, we exclude merge commits from this
144 # optimization, since the ctx.files() for a merge commit is not correct for
144 # optimization, since the ctx.files() for a merge commit is not correct for
145 # this comparison.
145 # this comparison.
146 forwardmissingmatch = match
146 forwardmissingmatch = match
147 if b.p1() == a and b.p2().node() == node.nullid:
147 if b.p1() == a and b.p2().node() == node.nullid:
148 filesmatcher = matchmod.exact(b.files())
148 filesmatcher = matchmod.exact(b.files())
149 forwardmissingmatch = matchmod.intersectmatchers(match, filesmatcher)
149 forwardmissingmatch = matchmod.intersectmatchers(match, filesmatcher)
150 missing = _computeforwardmissing(a, b, match=forwardmissingmatch)
150 missing = _computeforwardmissing(a, b, match=forwardmissingmatch)
151
151
152 ancestrycontext = a._repo.changelog.ancestors([b.rev()], inclusive=True)
152 ancestrycontext = a._repo.changelog.ancestors([b.rev()], inclusive=True)
153
153
154 if debug:
154 if debug:
155 dbg(b'debug.copies: missing files to search: %d\n' % len(missing))
155 dbg(b'debug.copies: missing files to search: %d\n' % len(missing))
156
156
157 for f in sorted(missing):
157 for f in sorted(missing):
158 if debug:
158 if debug:
159 dbg(b'debug.copies: tracing file: %s\n' % f)
159 dbg(b'debug.copies: tracing file: %s\n' % f)
160 fctx = b[f]
160 fctx = b[f]
161 fctx._ancestrycontext = ancestrycontext
161 fctx._ancestrycontext = ancestrycontext
162
162
163 if debug:
163 if debug:
164 start = util.timer()
164 start = util.timer()
165 opath = _tracefile(fctx, am, basemf)
165 opath = _tracefile(fctx, am, basemf)
166 if opath:
166 if opath:
167 if debug:
167 if debug:
168 dbg(b'debug.copies: rename of: %s\n' % opath)
168 dbg(b'debug.copies: rename of: %s\n' % opath)
169 cm[f] = opath
169 cm[f] = opath
170 if debug:
170 if debug:
171 dbg(
171 dbg(
172 b'debug.copies: time: %f seconds\n'
172 b'debug.copies: time: %f seconds\n'
173 % (util.timer() - start)
173 % (util.timer() - start)
174 )
174 )
175 return cm
175 return cm
176
176
177
177
178 def _revinfogetter(repo):
178 def _revinfogetter(repo):
179 """return a function that return multiple data given a <rev>"i
179 """return a function that return multiple data given a <rev>"i
180
180
181 * p1: revision number of first parent
181 * p1: revision number of first parent
182 * p2: revision number of first parent
182 * p2: revision number of first parent
183 * p1copies: mapping of copies from p1
183 * p1copies: mapping of copies from p1
184 * p2copies: mapping of copies from p2
184 * p2copies: mapping of copies from p2
185 * removed: a list of removed files
185 * removed: a list of removed files
186 """
186 """
187 cl = repo.changelog
187 cl = repo.changelog
188 parents = cl.parentrevs
188 parents = cl.parentrevs
189
189
190 if repo.filecopiesmode == b'changeset-sidedata':
190 if repo.filecopiesmode == b'changeset-sidedata':
191 changelogrevision = cl.changelogrevision
191 changelogrevision = cl.changelogrevision
192 flags = cl.flags
192 flags = cl.flags
193
193
194 # A small cache to avoid doing the work twice for merges
194 # A small cache to avoid doing the work twice for merges
195 #
195 #
196 # In the vast majority of cases, if we ask information for a revision
196 # In the vast majority of cases, if we ask information for a revision
197 # about 1 parent, we'll later ask it for the other. So it make sense to
197 # about 1 parent, we'll later ask it for the other. So it make sense to
198 # keep the information around when reaching the first parent of a merge
198 # keep the information around when reaching the first parent of a merge
199 # and dropping it after it was provided for the second parents.
199 # and dropping it after it was provided for the second parents.
200 #
200 #
201 # It exists cases were only one parent of the merge will be walked. It
201 # It exists cases were only one parent of the merge will be walked. It
202 # happens when the "destination" the copy tracing is descendant from a
202 # happens when the "destination" the copy tracing is descendant from a
203 # new root, not common with the "source". In that case, we will only walk
203 # new root, not common with the "source". In that case, we will only walk
204 # through merge parents that are descendant of changesets common
204 # through merge parents that are descendant of changesets common
205 # between "source" and "destination".
205 # between "source" and "destination".
206 #
206 #
207 # With the current case implementation if such changesets have a copy
207 # With the current case implementation if such changesets have a copy
208 # information, we'll keep them in memory until the end of
208 # information, we'll keep them in memory until the end of
209 # _changesetforwardcopies. We don't expect the case to be frequent
209 # _changesetforwardcopies. We don't expect the case to be frequent
210 # enough to matters.
210 # enough to matters.
211 #
211 #
212 # In addition, it would be possible to reach pathological case, were
212 # In addition, it would be possible to reach pathological case, were
213 # many first parent are met before any second parent is reached. In
213 # many first parent are met before any second parent is reached. In
214 # that case the cache could grow. If this even become an issue one can
214 # that case the cache could grow. If this even become an issue one can
215 # safely introduce a maximum cache size. This would trade extra CPU/IO
215 # safely introduce a maximum cache size. This would trade extra CPU/IO
216 # time to save memory.
216 # time to save memory.
217 merge_caches = {}
217 merge_caches = {}
218
218
219 def revinfo(rev):
219 def revinfo(rev):
220 p1, p2 = parents(rev)
220 p1, p2 = parents(rev)
221 if flags(rev) & REVIDX_SIDEDATA:
221 if flags(rev) & REVIDX_SIDEDATA:
222 e = merge_caches.pop(rev, None)
222 e = merge_caches.pop(rev, None)
223 if e is not None:
223 if e is not None:
224 return e
224 return e
225 c = changelogrevision(rev)
225 c = changelogrevision(rev)
226 p1copies = c.p1copies
226 p1copies = c.p1copies
227 p2copies = c.p2copies
227 p2copies = c.p2copies
228 removed = c.filesremoved
228 removed = c.filesremoved
229 if p1 != node.nullrev and p2 != node.nullrev:
229 if p1 != node.nullrev and p2 != node.nullrev:
230 # XXX some case we over cache, IGNORE
230 # XXX some case we over cache, IGNORE
231 merge_caches[rev] = (p1, p2, p1copies, p2copies, removed)
231 merge_caches[rev] = (p1, p2, p1copies, p2copies, removed)
232 else:
232 else:
233 p1copies = {}
233 p1copies = {}
234 p2copies = {}
234 p2copies = {}
235 removed = []
235 removed = []
236 return p1, p2, p1copies, p2copies, removed
236 return p1, p2, p1copies, p2copies, removed
237
237
238 else:
238 else:
239
239
240 def revinfo(rev):
240 def revinfo(rev):
241 p1, p2 = parents(rev)
241 p1, p2 = parents(rev)
242 ctx = repo[rev]
242 ctx = repo[rev]
243 p1copies, p2copies = ctx._copies
243 p1copies, p2copies = ctx._copies
244 removed = ctx.filesremoved()
244 removed = ctx.filesremoved()
245 return p1, p2, p1copies, p2copies, removed
245 return p1, p2, p1copies, p2copies, removed
246
246
247 return revinfo
247 return revinfo
248
248
249
249
250 def _changesetforwardcopies(a, b, match):
250 def _changesetforwardcopies(a, b, match):
251 if a.rev() in (node.nullrev, b.rev()):
251 if a.rev() in (node.nullrev, b.rev()):
252 return {}
252 return {}
253
253
254 repo = a.repo().unfiltered()
254 repo = a.repo().unfiltered()
255 children = {}
255 children = {}
256 revinfo = _revinfogetter(repo)
256 revinfo = _revinfogetter(repo)
257
257
258 cl = repo.changelog
258 cl = repo.changelog
259 missingrevs = cl.findmissingrevs(common=[a.rev()], heads=[b.rev()])
259 missingrevs = cl.findmissingrevs(common=[a.rev()], heads=[b.rev()])
260 mrset = set(missingrevs)
260 mrset = set(missingrevs)
261 roots = set()
261 roots = set()
262 for r in missingrevs:
262 for r in missingrevs:
263 for p in cl.parentrevs(r):
263 for p in cl.parentrevs(r):
264 if p == node.nullrev:
264 if p == node.nullrev:
265 continue
265 continue
266 if p not in children:
266 if p not in children:
267 children[p] = [r]
267 children[p] = [r]
268 else:
268 else:
269 children[p].append(r)
269 children[p].append(r)
270 if p not in mrset:
270 if p not in mrset:
271 roots.add(p)
271 roots.add(p)
272 if not roots:
272 if not roots:
273 # no common revision to track copies from
273 # no common revision to track copies from
274 return {}
274 return {}
275 min_root = min(roots)
275 min_root = min(roots)
276
276
277 from_head = set(
277 from_head = set(
278 cl.reachableroots(min_root, [b.rev()], list(roots), includepath=True)
278 cl.reachableroots(min_root, [b.rev()], list(roots), includepath=True)
279 )
279 )
280
280
281 iterrevs = set(from_head)
281 iterrevs = set(from_head)
282 iterrevs &= mrset
282 iterrevs &= mrset
283 iterrevs.update(roots)
283 iterrevs.update(roots)
284 iterrevs.remove(b.rev())
284 iterrevs.remove(b.rev())
285 revs = sorted(iterrevs)
285 revs = sorted(iterrevs)
286 return _combinechangesetcopies(revs, children, b.rev(), revinfo, match)
286 return _combinechangesetcopies(revs, children, b.rev(), revinfo, match)
287
287
288
288
289 def _combinechangesetcopies(revs, children, targetrev, revinfo, match):
289 def _combinechangesetcopies(revs, children, targetrev, revinfo, match):
290 """combine the copies information for each item of iterrevs
290 """combine the copies information for each item of iterrevs
291
291
292 revs: sorted iterable of revision to visit
292 revs: sorted iterable of revision to visit
293 children: a {parent: [children]} mapping.
293 children: a {parent: [children]} mapping.
294 targetrev: the final copies destination revision (not in iterrevs)
294 targetrev: the final copies destination revision (not in iterrevs)
295 revinfo(rev): a function that return (p1, p2, p1copies, p2copies, removed)
295 revinfo(rev): a function that return (p1, p2, p1copies, p2copies, removed)
296 match: a matcher
296 match: a matcher
297
297
298 It returns the aggregated copies information for `targetrev`.
298 It returns the aggregated copies information for `targetrev`.
299 """
299 """
300 all_copies = {}
300 all_copies = {}
301 alwaysmatch = match.always()
301 alwaysmatch = match.always()
302 for r in revs:
302 for r in revs:
303 copies = all_copies.pop(r, None)
303 copies = all_copies.pop(r, None)
304 if copies is None:
304 if copies is None:
305 # this is a root
305 # this is a root
306 copies = {}
306 copies = {}
307 for i, c in enumerate(children[r]):
307 for i, c in enumerate(children[r]):
308 p1, p2, p1copies, p2copies, removed = revinfo(c)
308 p1, p2, p1copies, p2copies, removed = revinfo(c)
309 if r == p1:
309 if r == p1:
310 parent = 1
310 parent = 1
311 childcopies = p1copies
311 childcopies = p1copies
312 else:
312 else:
313 assert r == p2
313 assert r == p2
314 parent = 2
314 parent = 2
315 childcopies = p2copies
315 childcopies = p2copies
316 if not alwaysmatch:
316 if not alwaysmatch:
317 childcopies = {
317 childcopies = {
318 dst: src for dst, src in childcopies.items() if match(dst)
318 dst: src for dst, src in childcopies.items() if match(dst)
319 }
319 }
320 newcopies = copies
320 newcopies = copies
321 if childcopies:
321 if childcopies:
322 newcopies = _chain(newcopies, childcopies)
322 newcopies = _chain(newcopies, childcopies)
323 # _chain makes a copies, we can avoid doing so in some
323 # _chain makes a copies, we can avoid doing so in some
324 # simple/linear cases.
324 # simple/linear cases.
325 assert newcopies is not copies
325 assert newcopies is not copies
326 for f in removed:
326 for f in removed:
327 if f in newcopies:
327 if f in newcopies:
328 if newcopies is copies:
328 if newcopies is copies:
329 # copy on write to avoid affecting potential other
329 # copy on write to avoid affecting potential other
330 # branches. when there are no other branches, this
330 # branches. when there are no other branches, this
331 # could be avoided.
331 # could be avoided.
332 newcopies = copies.copy()
332 newcopies = copies.copy()
333 del newcopies[f]
333 del newcopies[f]
334 othercopies = all_copies.get(c)
334 othercopies = all_copies.get(c)
335 if othercopies is None:
335 if othercopies is None:
336 all_copies[c] = newcopies
336 all_copies[c] = newcopies
337 else:
337 else:
338 # we are the second parent to work on c, we need to merge our
338 # we are the second parent to work on c, we need to merge our
339 # work with the other.
339 # work with the other.
340 #
340 #
341 # Unlike when copies are stored in the filelog, we consider
341 # Unlike when copies are stored in the filelog, we consider
342 # it a copy even if the destination already existed on the
342 # it a copy even if the destination already existed on the
343 # other branch. It's simply too expensive to check if the
343 # other branch. It's simply too expensive to check if the
344 # file existed in the manifest.
344 # file existed in the manifest.
345 #
345 #
346 # In case of conflict, parent 1 take precedence over parent 2.
346 # In case of conflict, parent 1 take precedence over parent 2.
347 # This is an arbitrary choice made anew when implementing
347 # This is an arbitrary choice made anew when implementing
348 # changeset based copies. It was made without regards with
348 # changeset based copies. It was made without regards with
349 # potential filelog related behavior.
349 # potential filelog related behavior.
350 if parent == 1:
350 if parent == 1:
351 othercopies.update(newcopies)
351 othercopies.update(newcopies)
352 else:
352 else:
353 newcopies.update(othercopies)
353 newcopies.update(othercopies)
354 all_copies[c] = newcopies
354 all_copies[c] = newcopies
355 return all_copies[targetrev]
355 return all_copies[targetrev]
356
356
357
357
358 def _forwardcopies(a, b, base=None, match=None):
358 def _forwardcopies(a, b, base=None, match=None):
359 """find {dst@b: src@a} copy mapping where a is an ancestor of b"""
359 """find {dst@b: src@a} copy mapping where a is an ancestor of b"""
360
360
361 if base is None:
361 if base is None:
362 base = a
362 base = a
363 match = a.repo().narrowmatch(match)
363 match = a.repo().narrowmatch(match)
364 # check for working copy
364 # check for working copy
365 if b.rev() is None:
365 if b.rev() is None:
366 cm = _committedforwardcopies(a, b.p1(), base, match)
366 cm = _committedforwardcopies(a, b.p1(), base, match)
367 # combine copies from dirstate if necessary
367 # combine copies from dirstate if necessary
368 copies = _chain(cm, _dirstatecopies(b._repo, match))
368 copies = _chain(cm, _dirstatecopies(b._repo, match))
369 else:
369 else:
370 copies = _committedforwardcopies(a, b, base, match)
370 copies = _committedforwardcopies(a, b, base, match)
371 return copies
371 return copies
372
372
373
373
374 def _backwardrenames(a, b, match):
374 def _backwardrenames(a, b, match):
375 if a._repo.ui.config(b'experimental', b'copytrace') == b'off':
375 if a._repo.ui.config(b'experimental', b'copytrace') == b'off':
376 return {}
376 return {}
377
377
378 # Even though we're not taking copies into account, 1:n rename situations
378 # Even though we're not taking copies into account, 1:n rename situations
379 # can still exist (e.g. hg cp a b; hg mv a c). In those cases we
379 # can still exist (e.g. hg cp a b; hg mv a c). In those cases we
380 # arbitrarily pick one of the renames.
380 # arbitrarily pick one of the renames.
381 # We don't want to pass in "match" here, since that would filter
381 # We don't want to pass in "match" here, since that would filter
382 # the destination by it. Since we're reversing the copies, we want
382 # the destination by it. Since we're reversing the copies, we want
383 # to filter the source instead.
383 # to filter the source instead.
384 f = _forwardcopies(b, a)
384 f = _forwardcopies(b, a)
385 r = {}
385 r = {}
386 for k, v in sorted(pycompat.iteritems(f)):
386 for k, v in sorted(pycompat.iteritems(f)):
387 if match and not match(v):
387 if match and not match(v):
388 continue
388 continue
389 # remove copies
389 # remove copies
390 if v in a:
390 if v in a:
391 continue
391 continue
392 r[v] = k
392 r[v] = k
393 return r
393 return r
394
394
395
395
396 def pathcopies(x, y, match=None):
396 def pathcopies(x, y, match=None):
397 """find {dst@y: src@x} copy mapping for directed compare"""
397 """find {dst@y: src@x} copy mapping for directed compare"""
398 repo = x._repo
398 repo = x._repo
399 debug = repo.ui.debugflag and repo.ui.configbool(b'devel', b'debug.copies')
399 debug = repo.ui.debugflag and repo.ui.configbool(b'devel', b'debug.copies')
400 if debug:
400 if debug:
401 repo.ui.debug(
401 repo.ui.debug(
402 b'debug.copies: searching copies from %s to %s\n' % (x, y)
402 b'debug.copies: searching copies from %s to %s\n' % (x, y)
403 )
403 )
404 if x == y or not x or not y:
404 if x == y or not x or not y:
405 return {}
405 return {}
406 a = y.ancestor(x)
406 a = y.ancestor(x)
407 if a == x:
407 if a == x:
408 if debug:
408 if debug:
409 repo.ui.debug(b'debug.copies: search mode: forward\n')
409 repo.ui.debug(b'debug.copies: search mode: forward\n')
410 if y.rev() is None and x == y.p1():
410 if y.rev() is None and x == y.p1():
411 # short-circuit to avoid issues with merge states
411 # short-circuit to avoid issues with merge states
412 return _dirstatecopies(repo, match)
412 return _dirstatecopies(repo, match)
413 copies = _forwardcopies(x, y, match=match)
413 copies = _forwardcopies(x, y, match=match)
414 elif a == y:
414 elif a == y:
415 if debug:
415 if debug:
416 repo.ui.debug(b'debug.copies: search mode: backward\n')
416 repo.ui.debug(b'debug.copies: search mode: backward\n')
417 copies = _backwardrenames(x, y, match=match)
417 copies = _backwardrenames(x, y, match=match)
418 else:
418 else:
419 if debug:
419 if debug:
420 repo.ui.debug(b'debug.copies: search mode: combined\n')
420 repo.ui.debug(b'debug.copies: search mode: combined\n')
421 base = None
421 base = None
422 if a.rev() != node.nullrev:
422 if a.rev() != node.nullrev:
423 base = x
423 base = x
424 copies = _chain(
424 copies = _chain(
425 _backwardrenames(x, a, match=match),
425 _backwardrenames(x, a, match=match),
426 _forwardcopies(a, y, base, match=match),
426 _forwardcopies(a, y, base, match=match),
427 )
427 )
428 _filter(x, y, copies)
428 _filter(x, y, copies)
429 return copies
429 return copies
430
430
431
431
432 def mergecopies(repo, c1, c2, base):
432 def mergecopies(repo, c1, c2, base):
433 """
433 """
434 Finds moves and copies between context c1 and c2 that are relevant for
434 Finds moves and copies between context c1 and c2 that are relevant for
435 merging. 'base' will be used as the merge base.
435 merging. 'base' will be used as the merge base.
436
436
437 Copytracing is used in commands like rebase, merge, unshelve, etc to merge
437 Copytracing is used in commands like rebase, merge, unshelve, etc to merge
438 files that were moved/ copied in one merge parent and modified in another.
438 files that were moved/ copied in one merge parent and modified in another.
439 For example:
439 For example:
440
440
441 o ---> 4 another commit
441 o ---> 4 another commit
442 |
442 |
443 | o ---> 3 commit that modifies a.txt
443 | o ---> 3 commit that modifies a.txt
444 | /
444 | /
445 o / ---> 2 commit that moves a.txt to b.txt
445 o / ---> 2 commit that moves a.txt to b.txt
446 |/
446 |/
447 o ---> 1 merge base
447 o ---> 1 merge base
448
448
449 If we try to rebase revision 3 on revision 4, since there is no a.txt in
449 If we try to rebase revision 3 on revision 4, since there is no a.txt in
450 revision 4, and if user have copytrace disabled, we prints the following
450 revision 4, and if user have copytrace disabled, we prints the following
451 message:
451 message:
452
452
453 ```other changed <file> which local deleted```
453 ```other changed <file> which local deleted```
454
454
455 Returns a tuple where:
455 Returns a tuple where:
456
456
457 "branch_copies" an instance of branch_copies.
457 "branch_copies" an instance of branch_copies.
458
458
459 "diverge" is a mapping of source name -> list of destination names
459 "diverge" is a mapping of source name -> list of destination names
460 for divergent renames.
460 for divergent renames.
461
461
462 This function calls different copytracing algorithms based on config.
462 This function calls different copytracing algorithms based on config.
463 """
463 """
464 # avoid silly behavior for update from empty dir
464 # avoid silly behavior for update from empty dir
465 if not c1 or not c2 or c1 == c2:
465 if not c1 or not c2 or c1 == c2:
466 return branch_copies(), branch_copies(), {}
466 return branch_copies(), branch_copies(), {}
467
467
468 narrowmatch = c1.repo().narrowmatch()
468 narrowmatch = c1.repo().narrowmatch()
469
469
470 # avoid silly behavior for parent -> working dir
470 # avoid silly behavior for parent -> working dir
471 if c2.node() is None and c1.node() == repo.dirstate.p1():
471 if c2.node() is None and c1.node() == repo.dirstate.p1():
472 return (
472 return (
473 branch_copies(_dirstatecopies(repo, narrowmatch)),
473 branch_copies(_dirstatecopies(repo, narrowmatch)),
474 branch_copies(),
474 branch_copies(),
475 {},
475 {},
476 )
476 )
477
477
478 copytracing = repo.ui.config(b'experimental', b'copytrace')
478 copytracing = repo.ui.config(b'experimental', b'copytrace')
479 if stringutil.parsebool(copytracing) is False:
479 if stringutil.parsebool(copytracing) is False:
480 # stringutil.parsebool() returns None when it is unable to parse the
480 # stringutil.parsebool() returns None when it is unable to parse the
481 # value, so we should rely on making sure copytracing is on such cases
481 # value, so we should rely on making sure copytracing is on such cases
482 return branch_copies(), branch_copies(), {}
482 return branch_copies(), branch_copies(), {}
483
483
484 if usechangesetcentricalgo(repo):
484 if usechangesetcentricalgo(repo):
485 # The heuristics don't make sense when we need changeset-centric algos
485 # The heuristics don't make sense when we need changeset-centric algos
486 return _fullcopytracing(repo, c1, c2, base)
486 return _fullcopytracing(repo, c1, c2, base)
487
487
488 # Copy trace disabling is explicitly below the node == p1 logic above
488 # Copy trace disabling is explicitly below the node == p1 logic above
489 # because the logic above is required for a simple copy to be kept across a
489 # because the logic above is required for a simple copy to be kept across a
490 # rebase.
490 # rebase.
491 if copytracing == b'heuristics':
491 if copytracing == b'heuristics':
492 # Do full copytracing if only non-public revisions are involved as
492 # Do full copytracing if only non-public revisions are involved as
493 # that will be fast enough and will also cover the copies which could
493 # that will be fast enough and will also cover the copies which could
494 # be missed by heuristics
494 # be missed by heuristics
495 if _isfullcopytraceable(repo, c1, base):
495 if _isfullcopytraceable(repo, c1, base):
496 return _fullcopytracing(repo, c1, c2, base)
496 return _fullcopytracing(repo, c1, c2, base)
497 return _heuristicscopytracing(repo, c1, c2, base)
497 return _heuristicscopytracing(repo, c1, c2, base)
498 else:
498 else:
499 return _fullcopytracing(repo, c1, c2, base)
499 return _fullcopytracing(repo, c1, c2, base)
500
500
501
501
502 def _isfullcopytraceable(repo, c1, base):
502 def _isfullcopytraceable(repo, c1, base):
503 """ Checks that if base, source and destination are all no-public branches,
503 """ Checks that if base, source and destination are all no-public branches,
504 if yes let's use the full copytrace algorithm for increased capabilities
504 if yes let's use the full copytrace algorithm for increased capabilities
505 since it will be fast enough.
505 since it will be fast enough.
506
506
507 `experimental.copytrace.sourcecommitlimit` can be used to set a limit for
507 `experimental.copytrace.sourcecommitlimit` can be used to set a limit for
508 number of changesets from c1 to base such that if number of changesets are
508 number of changesets from c1 to base such that if number of changesets are
509 more than the limit, full copytracing algorithm won't be used.
509 more than the limit, full copytracing algorithm won't be used.
510 """
510 """
511 if c1.rev() is None:
511 if c1.rev() is None:
512 c1 = c1.p1()
512 c1 = c1.p1()
513 if c1.mutable() and base.mutable():
513 if c1.mutable() and base.mutable():
514 sourcecommitlimit = repo.ui.configint(
514 sourcecommitlimit = repo.ui.configint(
515 b'experimental', b'copytrace.sourcecommitlimit'
515 b'experimental', b'copytrace.sourcecommitlimit'
516 )
516 )
517 commits = len(repo.revs(b'%d::%d', base.rev(), c1.rev()))
517 commits = len(repo.revs(b'%d::%d', base.rev(), c1.rev()))
518 return commits < sourcecommitlimit
518 return commits < sourcecommitlimit
519 return False
519 return False
520
520
521
521
522 def _checksinglesidecopies(
522 def _checksinglesidecopies(
523 src, dsts1, m1, m2, mb, c2, base, copy, renamedelete
523 src, dsts1, m1, m2, mb, c2, base, copy, renamedelete
524 ):
524 ):
525 if src not in m2:
525 if src not in m2:
526 # deleted on side 2
526 # deleted on side 2
527 if src not in m1:
527 if src not in m1:
528 # renamed on side 1, deleted on side 2
528 # renamed on side 1, deleted on side 2
529 renamedelete[src] = dsts1
529 renamedelete[src] = dsts1
530 elif src not in mb:
530 elif src not in mb:
531 # Work around the "short-circuit to avoid issues with merge states"
531 # Work around the "short-circuit to avoid issues with merge states"
532 # thing in pathcopies(): pathcopies(x, y) can return a copy where the
532 # thing in pathcopies(): pathcopies(x, y) can return a copy where the
533 # destination doesn't exist in y.
533 # destination doesn't exist in y.
534 pass
534 pass
535 elif m2[src] != mb[src]:
535 elif m2[src] != mb[src]:
536 if not _related(c2[src], base[src]):
536 if not _related(c2[src], base[src]):
537 return
537 return
538 # modified on side 2
538 # modified on side 2
539 for dst in dsts1:
539 for dst in dsts1:
540 if dst not in m2:
540 copy[dst] = src
541 # dst not added on side 2 (handle as regular
542 # "both created" case in manifestmerge otherwise)
543 copy[dst] = src
544
541
545
542
546 class branch_copies(object):
543 class branch_copies(object):
547 """Information about copies made on one side of a merge/graft.
544 """Information about copies made on one side of a merge/graft.
548
545
549 "copy" is a mapping from destination name -> source name,
546 "copy" is a mapping from destination name -> source name,
550 where source is in c1 and destination is in c2 or vice-versa.
547 where source is in c1 and destination is in c2 or vice-versa.
551
548
552 "movewithdir" is a mapping from source name -> destination name,
549 "movewithdir" is a mapping from source name -> destination name,
553 where the file at source present in one context but not the other
550 where the file at source present in one context but not the other
554 needs to be moved to destination by the merge process, because the
551 needs to be moved to destination by the merge process, because the
555 other context moved the directory it is in.
552 other context moved the directory it is in.
556
553
557 "renamedelete" is a mapping of source name -> list of destination
554 "renamedelete" is a mapping of source name -> list of destination
558 names for files deleted in c1 that were renamed in c2 or vice-versa.
555 names for files deleted in c1 that were renamed in c2 or vice-versa.
559
556
560 "dirmove" is a mapping of detected source dir -> destination dir renames.
557 "dirmove" is a mapping of detected source dir -> destination dir renames.
561 This is needed for handling changes to new files previously grafted into
558 This is needed for handling changes to new files previously grafted into
562 renamed directories.
559 renamed directories.
563 """
560 """
564
561
565 def __init__(
562 def __init__(
566 self, copy=None, renamedelete=None, dirmove=None, movewithdir=None
563 self, copy=None, renamedelete=None, dirmove=None, movewithdir=None
567 ):
564 ):
568 self.copy = {} if copy is None else copy
565 self.copy = {} if copy is None else copy
569 self.renamedelete = {} if renamedelete is None else renamedelete
566 self.renamedelete = {} if renamedelete is None else renamedelete
570 self.dirmove = {} if dirmove is None else dirmove
567 self.dirmove = {} if dirmove is None else dirmove
571 self.movewithdir = {} if movewithdir is None else movewithdir
568 self.movewithdir = {} if movewithdir is None else movewithdir
572
569
573
570
574 def _fullcopytracing(repo, c1, c2, base):
571 def _fullcopytracing(repo, c1, c2, base):
575 """ The full copytracing algorithm which finds all the new files that were
572 """ The full copytracing algorithm which finds all the new files that were
576 added from merge base up to the top commit and for each file it checks if
573 added from merge base up to the top commit and for each file it checks if
577 this file was copied from another file.
574 this file was copied from another file.
578
575
579 This is pretty slow when a lot of changesets are involved but will track all
576 This is pretty slow when a lot of changesets are involved but will track all
580 the copies.
577 the copies.
581 """
578 """
582 m1 = c1.manifest()
579 m1 = c1.manifest()
583 m2 = c2.manifest()
580 m2 = c2.manifest()
584 mb = base.manifest()
581 mb = base.manifest()
585
582
586 copies1 = pathcopies(base, c1)
583 copies1 = pathcopies(base, c1)
587 copies2 = pathcopies(base, c2)
584 copies2 = pathcopies(base, c2)
588
585
589 if not (copies1 or copies2):
586 if not (copies1 or copies2):
590 return branch_copies(), branch_copies(), {}
587 return branch_copies(), branch_copies(), {}
591
588
592 inversecopies1 = {}
589 inversecopies1 = {}
593 inversecopies2 = {}
590 inversecopies2 = {}
594 for dst, src in copies1.items():
591 for dst, src in copies1.items():
595 inversecopies1.setdefault(src, []).append(dst)
592 inversecopies1.setdefault(src, []).append(dst)
596 for dst, src in copies2.items():
593 for dst, src in copies2.items():
597 inversecopies2.setdefault(src, []).append(dst)
594 inversecopies2.setdefault(src, []).append(dst)
598
595
599 copy1 = {}
596 copy1 = {}
600 copy2 = {}
597 copy2 = {}
601 diverge = {}
598 diverge = {}
602 renamedelete1 = {}
599 renamedelete1 = {}
603 renamedelete2 = {}
600 renamedelete2 = {}
604 allsources = set(inversecopies1) | set(inversecopies2)
601 allsources = set(inversecopies1) | set(inversecopies2)
605 for src in allsources:
602 for src in allsources:
606 dsts1 = inversecopies1.get(src)
603 dsts1 = inversecopies1.get(src)
607 dsts2 = inversecopies2.get(src)
604 dsts2 = inversecopies2.get(src)
608 if dsts1 and dsts2:
605 if dsts1 and dsts2:
609 # copied/renamed on both sides
606 # copied/renamed on both sides
610 if src not in m1 and src not in m2:
607 if src not in m1 and src not in m2:
611 # renamed on both sides
608 # renamed on both sides
612 dsts1 = set(dsts1)
609 dsts1 = set(dsts1)
613 dsts2 = set(dsts2)
610 dsts2 = set(dsts2)
614 # If there's some overlap in the rename destinations, we
611 # If there's some overlap in the rename destinations, we
615 # consider it not divergent. For example, if side 1 copies 'a'
612 # consider it not divergent. For example, if side 1 copies 'a'
616 # to 'b' and 'c' and deletes 'a', and side 2 copies 'a' to 'c'
613 # to 'b' and 'c' and deletes 'a', and side 2 copies 'a' to 'c'
617 # and 'd' and deletes 'a'.
614 # and 'd' and deletes 'a'.
618 if dsts1 & dsts2:
615 if dsts1 & dsts2:
619 for dst in dsts1 & dsts2:
616 for dst in dsts1 & dsts2:
620 copy1[dst] = src
617 copy1[dst] = src
621 copy2[dst] = src
618 copy2[dst] = src
622 else:
619 else:
623 diverge[src] = sorted(dsts1 | dsts2)
620 diverge[src] = sorted(dsts1 | dsts2)
624 elif src in m1 and src in m2:
621 elif src in m1 and src in m2:
625 # copied on both sides
622 # copied on both sides
626 dsts1 = set(dsts1)
623 dsts1 = set(dsts1)
627 dsts2 = set(dsts2)
624 dsts2 = set(dsts2)
628 for dst in dsts1 & dsts2:
625 for dst in dsts1 & dsts2:
629 copy1[dst] = src
626 copy1[dst] = src
630 copy2[dst] = src
627 copy2[dst] = src
631 # TODO: Handle cases where it was renamed on one side and copied
628 # TODO: Handle cases where it was renamed on one side and copied
632 # on the other side
629 # on the other side
633 elif dsts1:
630 elif dsts1:
634 # copied/renamed only on side 1
631 # copied/renamed only on side 1
635 _checksinglesidecopies(
632 _checksinglesidecopies(
636 src, dsts1, m1, m2, mb, c2, base, copy1, renamedelete1
633 src, dsts1, m1, m2, mb, c2, base, copy1, renamedelete1
637 )
634 )
638 elif dsts2:
635 elif dsts2:
639 # copied/renamed only on side 2
636 # copied/renamed only on side 2
640 _checksinglesidecopies(
637 _checksinglesidecopies(
641 src, dsts2, m2, m1, mb, c1, base, copy2, renamedelete2
638 src, dsts2, m2, m1, mb, c1, base, copy2, renamedelete2
642 )
639 )
643
640
644 # find interesting file sets from manifests
641 # find interesting file sets from manifests
645 addedinm1 = m1.filesnotin(mb, repo.narrowmatch())
642 addedinm1 = m1.filesnotin(mb, repo.narrowmatch())
646 addedinm2 = m2.filesnotin(mb, repo.narrowmatch())
643 addedinm2 = m2.filesnotin(mb, repo.narrowmatch())
647 u1 = sorted(addedinm1 - addedinm2)
644 u1 = sorted(addedinm1 - addedinm2)
648 u2 = sorted(addedinm2 - addedinm1)
645 u2 = sorted(addedinm2 - addedinm1)
649
646
650 header = b" unmatched files in %s"
647 header = b" unmatched files in %s"
651 if u1:
648 if u1:
652 repo.ui.debug(b"%s:\n %s\n" % (header % b'local', b"\n ".join(u1)))
649 repo.ui.debug(b"%s:\n %s\n" % (header % b'local', b"\n ".join(u1)))
653 if u2:
650 if u2:
654 repo.ui.debug(b"%s:\n %s\n" % (header % b'other', b"\n ".join(u2)))
651 repo.ui.debug(b"%s:\n %s\n" % (header % b'other', b"\n ".join(u2)))
655
652
656 if repo.ui.debugflag:
653 if repo.ui.debugflag:
657 renamedeleteset = set()
654 renamedeleteset = set()
658 divergeset = set()
655 divergeset = set()
659 for dsts in diverge.values():
656 for dsts in diverge.values():
660 divergeset.update(dsts)
657 divergeset.update(dsts)
661 for dsts in renamedelete1.values():
658 for dsts in renamedelete1.values():
662 renamedeleteset.update(dsts)
659 renamedeleteset.update(dsts)
663 for dsts in renamedelete2.values():
660 for dsts in renamedelete2.values():
664 renamedeleteset.update(dsts)
661 renamedeleteset.update(dsts)
665
662
666 repo.ui.debug(
663 repo.ui.debug(
667 b" all copies found (* = to merge, ! = divergent, "
664 b" all copies found (* = to merge, ! = divergent, "
668 b"% = renamed and deleted):\n"
665 b"% = renamed and deleted):\n"
669 )
666 )
670 for side, copies in ((b"local", copies1), (b"remote", copies2)):
667 for side, copies in ((b"local", copies1), (b"remote", copies2)):
671 if not copies:
668 if not copies:
672 continue
669 continue
673 repo.ui.debug(b" on %s side:\n" % side)
670 repo.ui.debug(b" on %s side:\n" % side)
674 for f in sorted(copies):
671 for f in sorted(copies):
675 note = b""
672 note = b""
676 if f in copy1 or f in copy2:
673 if f in copy1 or f in copy2:
677 note += b"*"
674 note += b"*"
678 if f in divergeset:
675 if f in divergeset:
679 note += b"!"
676 note += b"!"
680 if f in renamedeleteset:
677 if f in renamedeleteset:
681 note += b"%"
678 note += b"%"
682 repo.ui.debug(
679 repo.ui.debug(
683 b" src: '%s' -> dst: '%s' %s\n" % (copies[f], f, note)
680 b" src: '%s' -> dst: '%s' %s\n" % (copies[f], f, note)
684 )
681 )
685 del renamedeleteset
682 del renamedeleteset
686 del divergeset
683 del divergeset
687
684
688 repo.ui.debug(b" checking for directory renames\n")
685 repo.ui.debug(b" checking for directory renames\n")
689
686
690 dirmove1, movewithdir2 = _dir_renames(repo, c1, copy1, copies1, u2)
687 dirmove1, movewithdir2 = _dir_renames(repo, c1, copy1, copies1, u2)
691 dirmove2, movewithdir1 = _dir_renames(repo, c2, copy2, copies2, u1)
688 dirmove2, movewithdir1 = _dir_renames(repo, c2, copy2, copies2, u1)
692
689
693 branch_copies1 = branch_copies(copy1, renamedelete1, dirmove1, movewithdir1)
690 branch_copies1 = branch_copies(copy1, renamedelete1, dirmove1, movewithdir1)
694 branch_copies2 = branch_copies(copy2, renamedelete2, dirmove2, movewithdir2)
691 branch_copies2 = branch_copies(copy2, renamedelete2, dirmove2, movewithdir2)
695
692
696 return branch_copies1, branch_copies2, diverge
693 return branch_copies1, branch_copies2, diverge
697
694
698
695
699 def _dir_renames(repo, ctx, copy, fullcopy, addedfiles):
696 def _dir_renames(repo, ctx, copy, fullcopy, addedfiles):
700 """Finds moved directories and files that should move with them.
697 """Finds moved directories and files that should move with them.
701
698
702 ctx: the context for one of the sides
699 ctx: the context for one of the sides
703 copy: files copied on the same side (as ctx)
700 copy: files copied on the same side (as ctx)
704 fullcopy: files copied on the same side (as ctx), including those that
701 fullcopy: files copied on the same side (as ctx), including those that
705 merge.manifestmerge() won't care about
702 merge.manifestmerge() won't care about
706 addedfiles: added files on the other side (compared to ctx)
703 addedfiles: added files on the other side (compared to ctx)
707 """
704 """
708 # generate a directory move map
705 # generate a directory move map
709 d = ctx.dirs()
706 d = ctx.dirs()
710 invalid = set()
707 invalid = set()
711 dirmove = {}
708 dirmove = {}
712
709
713 # examine each file copy for a potential directory move, which is
710 # examine each file copy for a potential directory move, which is
714 # when all the files in a directory are moved to a new directory
711 # when all the files in a directory are moved to a new directory
715 for dst, src in pycompat.iteritems(fullcopy):
712 for dst, src in pycompat.iteritems(fullcopy):
716 dsrc, ddst = pathutil.dirname(src), pathutil.dirname(dst)
713 dsrc, ddst = pathutil.dirname(src), pathutil.dirname(dst)
717 if dsrc in invalid:
714 if dsrc in invalid:
718 # already seen to be uninteresting
715 # already seen to be uninteresting
719 continue
716 continue
720 elif dsrc in d and ddst in d:
717 elif dsrc in d and ddst in d:
721 # directory wasn't entirely moved locally
718 # directory wasn't entirely moved locally
722 invalid.add(dsrc)
719 invalid.add(dsrc)
723 elif dsrc in dirmove and dirmove[dsrc] != ddst:
720 elif dsrc in dirmove and dirmove[dsrc] != ddst:
724 # files from the same directory moved to two different places
721 # files from the same directory moved to two different places
725 invalid.add(dsrc)
722 invalid.add(dsrc)
726 else:
723 else:
727 # looks good so far
724 # looks good so far
728 dirmove[dsrc] = ddst
725 dirmove[dsrc] = ddst
729
726
730 for i in invalid:
727 for i in invalid:
731 if i in dirmove:
728 if i in dirmove:
732 del dirmove[i]
729 del dirmove[i]
733 del d, invalid
730 del d, invalid
734
731
735 if not dirmove:
732 if not dirmove:
736 return {}, {}
733 return {}, {}
737
734
738 dirmove = {k + b"/": v + b"/" for k, v in pycompat.iteritems(dirmove)}
735 dirmove = {k + b"/": v + b"/" for k, v in pycompat.iteritems(dirmove)}
739
736
740 for d in dirmove:
737 for d in dirmove:
741 repo.ui.debug(
738 repo.ui.debug(
742 b" discovered dir src: '%s' -> dst: '%s'\n" % (d, dirmove[d])
739 b" discovered dir src: '%s' -> dst: '%s'\n" % (d, dirmove[d])
743 )
740 )
744
741
745 movewithdir = {}
742 movewithdir = {}
746 # check unaccounted nonoverlapping files against directory moves
743 # check unaccounted nonoverlapping files against directory moves
747 for f in addedfiles:
744 for f in addedfiles:
748 if f not in fullcopy:
745 if f not in fullcopy:
749 for d in dirmove:
746 for d in dirmove:
750 if f.startswith(d):
747 if f.startswith(d):
751 # new file added in a directory that was moved, move it
748 # new file added in a directory that was moved, move it
752 df = dirmove[d] + f[len(d) :]
749 df = dirmove[d] + f[len(d) :]
753 if df not in copy:
750 if df not in copy:
754 movewithdir[f] = df
751 movewithdir[f] = df
755 repo.ui.debug(
752 repo.ui.debug(
756 b" pending file src: '%s' -> dst: '%s'\n"
753 b" pending file src: '%s' -> dst: '%s'\n"
757 % (f, df)
754 % (f, df)
758 )
755 )
759 break
756 break
760
757
761 return dirmove, movewithdir
758 return dirmove, movewithdir
762
759
763
760
764 def _heuristicscopytracing(repo, c1, c2, base):
761 def _heuristicscopytracing(repo, c1, c2, base):
765 """ Fast copytracing using filename heuristics
762 """ Fast copytracing using filename heuristics
766
763
767 Assumes that moves or renames are of following two types:
764 Assumes that moves or renames are of following two types:
768
765
769 1) Inside a directory only (same directory name but different filenames)
766 1) Inside a directory only (same directory name but different filenames)
770 2) Move from one directory to another
767 2) Move from one directory to another
771 (same filenames but different directory names)
768 (same filenames but different directory names)
772
769
773 Works only when there are no merge commits in the "source branch".
770 Works only when there are no merge commits in the "source branch".
774 Source branch is commits from base up to c2 not including base.
771 Source branch is commits from base up to c2 not including base.
775
772
776 If merge is involved it fallbacks to _fullcopytracing().
773 If merge is involved it fallbacks to _fullcopytracing().
777
774
778 Can be used by setting the following config:
775 Can be used by setting the following config:
779
776
780 [experimental]
777 [experimental]
781 copytrace = heuristics
778 copytrace = heuristics
782
779
783 In some cases the copy/move candidates found by heuristics can be very large
780 In some cases the copy/move candidates found by heuristics can be very large
784 in number and that will make the algorithm slow. The number of possible
781 in number and that will make the algorithm slow. The number of possible
785 candidates to check can be limited by using the config
782 candidates to check can be limited by using the config
786 `experimental.copytrace.movecandidateslimit` which defaults to 100.
783 `experimental.copytrace.movecandidateslimit` which defaults to 100.
787 """
784 """
788
785
789 if c1.rev() is None:
786 if c1.rev() is None:
790 c1 = c1.p1()
787 c1 = c1.p1()
791 if c2.rev() is None:
788 if c2.rev() is None:
792 c2 = c2.p1()
789 c2 = c2.p1()
793
790
794 changedfiles = set()
791 changedfiles = set()
795 m1 = c1.manifest()
792 m1 = c1.manifest()
796 if not repo.revs(b'%d::%d', base.rev(), c2.rev()):
793 if not repo.revs(b'%d::%d', base.rev(), c2.rev()):
797 # If base is not in c2 branch, we switch to fullcopytracing
794 # If base is not in c2 branch, we switch to fullcopytracing
798 repo.ui.debug(
795 repo.ui.debug(
799 b"switching to full copytracing as base is not "
796 b"switching to full copytracing as base is not "
800 b"an ancestor of c2\n"
797 b"an ancestor of c2\n"
801 )
798 )
802 return _fullcopytracing(repo, c1, c2, base)
799 return _fullcopytracing(repo, c1, c2, base)
803
800
804 ctx = c2
801 ctx = c2
805 while ctx != base:
802 while ctx != base:
806 if len(ctx.parents()) == 2:
803 if len(ctx.parents()) == 2:
807 # To keep things simple let's not handle merges
804 # To keep things simple let's not handle merges
808 repo.ui.debug(b"switching to full copytracing because of merges\n")
805 repo.ui.debug(b"switching to full copytracing because of merges\n")
809 return _fullcopytracing(repo, c1, c2, base)
806 return _fullcopytracing(repo, c1, c2, base)
810 changedfiles.update(ctx.files())
807 changedfiles.update(ctx.files())
811 ctx = ctx.p1()
808 ctx = ctx.p1()
812
809
813 copies2 = {}
810 copies2 = {}
814 cp = _forwardcopies(base, c2)
811 cp = _forwardcopies(base, c2)
815 for dst, src in pycompat.iteritems(cp):
812 for dst, src in pycompat.iteritems(cp):
816 if src in m1:
813 if src in m1:
817 copies2[dst] = src
814 copies2[dst] = src
818
815
819 # file is missing if it isn't present in the destination, but is present in
816 # file is missing if it isn't present in the destination, but is present in
820 # the base and present in the source.
817 # the base and present in the source.
821 # Presence in the base is important to exclude added files, presence in the
818 # Presence in the base is important to exclude added files, presence in the
822 # source is important to exclude removed files.
819 # source is important to exclude removed files.
823 filt = lambda f: f not in m1 and f in base and f in c2
820 filt = lambda f: f not in m1 and f in base and f in c2
824 missingfiles = [f for f in changedfiles if filt(f)]
821 missingfiles = [f for f in changedfiles if filt(f)]
825
822
826 copies1 = {}
823 copies1 = {}
827 if missingfiles:
824 if missingfiles:
828 basenametofilename = collections.defaultdict(list)
825 basenametofilename = collections.defaultdict(list)
829 dirnametofilename = collections.defaultdict(list)
826 dirnametofilename = collections.defaultdict(list)
830
827
831 for f in m1.filesnotin(base.manifest()):
828 for f in m1.filesnotin(base.manifest()):
832 basename = os.path.basename(f)
829 basename = os.path.basename(f)
833 dirname = os.path.dirname(f)
830 dirname = os.path.dirname(f)
834 basenametofilename[basename].append(f)
831 basenametofilename[basename].append(f)
835 dirnametofilename[dirname].append(f)
832 dirnametofilename[dirname].append(f)
836
833
837 for f in missingfiles:
834 for f in missingfiles:
838 basename = os.path.basename(f)
835 basename = os.path.basename(f)
839 dirname = os.path.dirname(f)
836 dirname = os.path.dirname(f)
840 samebasename = basenametofilename[basename]
837 samebasename = basenametofilename[basename]
841 samedirname = dirnametofilename[dirname]
838 samedirname = dirnametofilename[dirname]
842 movecandidates = samebasename + samedirname
839 movecandidates = samebasename + samedirname
843 # f is guaranteed to be present in c2, that's why
840 # f is guaranteed to be present in c2, that's why
844 # c2.filectx(f) won't fail
841 # c2.filectx(f) won't fail
845 f2 = c2.filectx(f)
842 f2 = c2.filectx(f)
846 # we can have a lot of candidates which can slow down the heuristics
843 # we can have a lot of candidates which can slow down the heuristics
847 # config value to limit the number of candidates moves to check
844 # config value to limit the number of candidates moves to check
848 maxcandidates = repo.ui.configint(
845 maxcandidates = repo.ui.configint(
849 b'experimental', b'copytrace.movecandidateslimit'
846 b'experimental', b'copytrace.movecandidateslimit'
850 )
847 )
851
848
852 if len(movecandidates) > maxcandidates:
849 if len(movecandidates) > maxcandidates:
853 repo.ui.status(
850 repo.ui.status(
854 _(
851 _(
855 b"skipping copytracing for '%s', more "
852 b"skipping copytracing for '%s', more "
856 b"candidates than the limit: %d\n"
853 b"candidates than the limit: %d\n"
857 )
854 )
858 % (f, len(movecandidates))
855 % (f, len(movecandidates))
859 )
856 )
860 continue
857 continue
861
858
862 for candidate in movecandidates:
859 for candidate in movecandidates:
863 f1 = c1.filectx(candidate)
860 f1 = c1.filectx(candidate)
864 if _related(f1, f2):
861 if _related(f1, f2):
865 # if there are a few related copies then we'll merge
862 # if there are a few related copies then we'll merge
866 # changes into all of them. This matches the behaviour
863 # changes into all of them. This matches the behaviour
867 # of upstream copytracing
864 # of upstream copytracing
868 copies1[candidate] = f
865 copies1[candidate] = f
869
866
870 return branch_copies(copies1), branch_copies(copies2), {}
867 return branch_copies(copies1), branch_copies(copies2), {}
871
868
872
869
873 def _related(f1, f2):
870 def _related(f1, f2):
874 """return True if f1 and f2 filectx have a common ancestor
871 """return True if f1 and f2 filectx have a common ancestor
875
872
876 Walk back to common ancestor to see if the two files originate
873 Walk back to common ancestor to see if the two files originate
877 from the same file. Since workingfilectx's rev() is None it messes
874 from the same file. Since workingfilectx's rev() is None it messes
878 up the integer comparison logic, hence the pre-step check for
875 up the integer comparison logic, hence the pre-step check for
879 None (f1 and f2 can only be workingfilectx's initially).
876 None (f1 and f2 can only be workingfilectx's initially).
880 """
877 """
881
878
882 if f1 == f2:
879 if f1 == f2:
883 return True # a match
880 return True # a match
884
881
885 g1, g2 = f1.ancestors(), f2.ancestors()
882 g1, g2 = f1.ancestors(), f2.ancestors()
886 try:
883 try:
887 f1r, f2r = f1.linkrev(), f2.linkrev()
884 f1r, f2r = f1.linkrev(), f2.linkrev()
888
885
889 if f1r is None:
886 if f1r is None:
890 f1 = next(g1)
887 f1 = next(g1)
891 if f2r is None:
888 if f2r is None:
892 f2 = next(g2)
889 f2 = next(g2)
893
890
894 while True:
891 while True:
895 f1r, f2r = f1.linkrev(), f2.linkrev()
892 f1r, f2r = f1.linkrev(), f2.linkrev()
896 if f1r > f2r:
893 if f1r > f2r:
897 f1 = next(g1)
894 f1 = next(g1)
898 elif f2r > f1r:
895 elif f2r > f1r:
899 f2 = next(g2)
896 f2 = next(g2)
900 else: # f1 and f2 point to files in the same linkrev
897 else: # f1 and f2 point to files in the same linkrev
901 return f1 == f2 # true if they point to the same file
898 return f1 == f2 # true if they point to the same file
902 except StopIteration:
899 except StopIteration:
903 return False
900 return False
904
901
905
902
906 def graftcopies(wctx, ctx, base):
903 def graftcopies(wctx, ctx, base):
907 """reproduce copies between base and ctx in the wctx
904 """reproduce copies between base and ctx in the wctx
908
905
909 Unlike mergecopies(), this function will only consider copies between base
906 Unlike mergecopies(), this function will only consider copies between base
910 and ctx; it will ignore copies between base and wctx. Also unlike
907 and ctx; it will ignore copies between base and wctx. Also unlike
911 mergecopies(), this function will apply copies to the working copy (instead
908 mergecopies(), this function will apply copies to the working copy (instead
912 of just returning information about the copies). That makes it cheaper
909 of just returning information about the copies). That makes it cheaper
913 (especially in the common case of base==ctx.p1()) and useful also when
910 (especially in the common case of base==ctx.p1()) and useful also when
914 experimental.copytrace=off.
911 experimental.copytrace=off.
915
912
916 merge.update() will have already marked most copies, but it will only
913 merge.update() will have already marked most copies, but it will only
917 mark copies if it thinks the source files are related (see
914 mark copies if it thinks the source files are related (see
918 merge._related()). It will also not mark copies if the file wasn't modified
915 merge._related()). It will also not mark copies if the file wasn't modified
919 on the local side. This function adds the copies that were "missed"
916 on the local side. This function adds the copies that were "missed"
920 by merge.update().
917 by merge.update().
921 """
918 """
922 new_copies = pathcopies(base, ctx)
919 new_copies = pathcopies(base, ctx)
923 _filter(wctx.p1(), wctx, new_copies)
920 _filter(wctx.p1(), wctx, new_copies)
924 for dst, src in pycompat.iteritems(new_copies):
921 for dst, src in pycompat.iteritems(new_copies):
925 wctx[dst].markcopied(src)
922 wctx[dst].markcopied(src)
926
923
927
924
928 def computechangesetfilesadded(ctx):
925 def computechangesetfilesadded(ctx):
929 """return the list of files added in a changeset
926 """return the list of files added in a changeset
930 """
927 """
931 added = []
928 added = []
932 for f in ctx.files():
929 for f in ctx.files():
933 if not any(f in p for p in ctx.parents()):
930 if not any(f in p for p in ctx.parents()):
934 added.append(f)
931 added.append(f)
935 return added
932 return added
936
933
937
934
938 def computechangesetfilesremoved(ctx):
935 def computechangesetfilesremoved(ctx):
939 """return the list of files removed in a changeset
936 """return the list of files removed in a changeset
940 """
937 """
941 removed = []
938 removed = []
942 for f in ctx.files():
939 for f in ctx.files():
943 if f not in ctx:
940 if f not in ctx:
944 removed.append(f)
941 removed.append(f)
945 return removed
942 return removed
946
943
947
944
948 def computechangesetcopies(ctx):
945 def computechangesetcopies(ctx):
949 """return the copies data for a changeset
946 """return the copies data for a changeset
950
947
951 The copies data are returned as a pair of dictionnary (p1copies, p2copies).
948 The copies data are returned as a pair of dictionnary (p1copies, p2copies).
952
949
953 Each dictionnary are in the form: `{newname: oldname}`
950 Each dictionnary are in the form: `{newname: oldname}`
954 """
951 """
955 p1copies = {}
952 p1copies = {}
956 p2copies = {}
953 p2copies = {}
957 p1 = ctx.p1()
954 p1 = ctx.p1()
958 p2 = ctx.p2()
955 p2 = ctx.p2()
959 narrowmatch = ctx._repo.narrowmatch()
956 narrowmatch = ctx._repo.narrowmatch()
960 for dst in ctx.files():
957 for dst in ctx.files():
961 if not narrowmatch(dst) or dst not in ctx:
958 if not narrowmatch(dst) or dst not in ctx:
962 continue
959 continue
963 copied = ctx[dst].renamed()
960 copied = ctx[dst].renamed()
964 if not copied:
961 if not copied:
965 continue
962 continue
966 src, srcnode = copied
963 src, srcnode = copied
967 if src in p1 and p1[src].filenode() == srcnode:
964 if src in p1 and p1[src].filenode() == srcnode:
968 p1copies[dst] = src
965 p1copies[dst] = src
969 elif src in p2 and p2[src].filenode() == srcnode:
966 elif src in p2 and p2[src].filenode() == srcnode:
970 p2copies[dst] = src
967 p2copies[dst] = src
971 return p1copies, p2copies
968 return p1copies, p2copies
972
969
973
970
974 def encodecopies(files, copies):
971 def encodecopies(files, copies):
975 items = []
972 items = []
976 for i, dst in enumerate(files):
973 for i, dst in enumerate(files):
977 if dst in copies:
974 if dst in copies:
978 items.append(b'%d\0%s' % (i, copies[dst]))
975 items.append(b'%d\0%s' % (i, copies[dst]))
979 if len(items) != len(copies):
976 if len(items) != len(copies):
980 raise error.ProgrammingError(
977 raise error.ProgrammingError(
981 b'some copy targets missing from file list'
978 b'some copy targets missing from file list'
982 )
979 )
983 return b"\n".join(items)
980 return b"\n".join(items)
984
981
985
982
986 def decodecopies(files, data):
983 def decodecopies(files, data):
987 try:
984 try:
988 copies = {}
985 copies = {}
989 if not data:
986 if not data:
990 return copies
987 return copies
991 for l in data.split(b'\n'):
988 for l in data.split(b'\n'):
992 strindex, src = l.split(b'\0')
989 strindex, src = l.split(b'\0')
993 i = int(strindex)
990 i = int(strindex)
994 dst = files[i]
991 dst = files[i]
995 copies[dst] = src
992 copies[dst] = src
996 return copies
993 return copies
997 except (ValueError, IndexError):
994 except (ValueError, IndexError):
998 # Perhaps someone had chosen the same key name (e.g. "p1copies") and
995 # Perhaps someone had chosen the same key name (e.g. "p1copies") and
999 # used different syntax for the value.
996 # used different syntax for the value.
1000 return None
997 return None
1001
998
1002
999
1003 def encodefileindices(files, subset):
1000 def encodefileindices(files, subset):
1004 subset = set(subset)
1001 subset = set(subset)
1005 indices = []
1002 indices = []
1006 for i, f in enumerate(files):
1003 for i, f in enumerate(files):
1007 if f in subset:
1004 if f in subset:
1008 indices.append(b'%d' % i)
1005 indices.append(b'%d' % i)
1009 return b'\n'.join(indices)
1006 return b'\n'.join(indices)
1010
1007
1011
1008
1012 def decodefileindices(files, data):
1009 def decodefileindices(files, data):
1013 try:
1010 try:
1014 subset = []
1011 subset = []
1015 if not data:
1012 if not data:
1016 return subset
1013 return subset
1017 for strindex in data.split(b'\n'):
1014 for strindex in data.split(b'\n'):
1018 i = int(strindex)
1015 i = int(strindex)
1019 if i < 0 or i >= len(files):
1016 if i < 0 or i >= len(files):
1020 return None
1017 return None
1021 subset.append(files[i])
1018 subset.append(files[i])
1022 return subset
1019 return subset
1023 except (ValueError, IndexError):
1020 except (ValueError, IndexError):
1024 # Perhaps someone had chosen the same key name (e.g. "added") and
1021 # Perhaps someone had chosen the same key name (e.g. "added") and
1025 # used different syntax for the value.
1022 # used different syntax for the value.
1026 return None
1023 return None
1027
1024
1028
1025
1029 def _getsidedata(srcrepo, rev):
1026 def _getsidedata(srcrepo, rev):
1030 ctx = srcrepo[rev]
1027 ctx = srcrepo[rev]
1031 filescopies = computechangesetcopies(ctx)
1028 filescopies = computechangesetcopies(ctx)
1032 filesadded = computechangesetfilesadded(ctx)
1029 filesadded = computechangesetfilesadded(ctx)
1033 filesremoved = computechangesetfilesremoved(ctx)
1030 filesremoved = computechangesetfilesremoved(ctx)
1034 sidedata = {}
1031 sidedata = {}
1035 if any([filescopies, filesadded, filesremoved]):
1032 if any([filescopies, filesadded, filesremoved]):
1036 sortedfiles = sorted(ctx.files())
1033 sortedfiles = sorted(ctx.files())
1037 p1copies, p2copies = filescopies
1034 p1copies, p2copies = filescopies
1038 p1copies = encodecopies(sortedfiles, p1copies)
1035 p1copies = encodecopies(sortedfiles, p1copies)
1039 p2copies = encodecopies(sortedfiles, p2copies)
1036 p2copies = encodecopies(sortedfiles, p2copies)
1040 filesadded = encodefileindices(sortedfiles, filesadded)
1037 filesadded = encodefileindices(sortedfiles, filesadded)
1041 filesremoved = encodefileindices(sortedfiles, filesremoved)
1038 filesremoved = encodefileindices(sortedfiles, filesremoved)
1042 if p1copies:
1039 if p1copies:
1043 sidedata[sidedatamod.SD_P1COPIES] = p1copies
1040 sidedata[sidedatamod.SD_P1COPIES] = p1copies
1044 if p2copies:
1041 if p2copies:
1045 sidedata[sidedatamod.SD_P2COPIES] = p2copies
1042 sidedata[sidedatamod.SD_P2COPIES] = p2copies
1046 if filesadded:
1043 if filesadded:
1047 sidedata[sidedatamod.SD_FILESADDED] = filesadded
1044 sidedata[sidedatamod.SD_FILESADDED] = filesadded
1048 if filesremoved:
1045 if filesremoved:
1049 sidedata[sidedatamod.SD_FILESREMOVED] = filesremoved
1046 sidedata[sidedatamod.SD_FILESREMOVED] = filesremoved
1050 return sidedata
1047 return sidedata
1051
1048
1052
1049
1053 def getsidedataadder(srcrepo, destrepo):
1050 def getsidedataadder(srcrepo, destrepo):
1054 use_w = srcrepo.ui.configbool(b'experimental', b'worker.repository-upgrade')
1051 use_w = srcrepo.ui.configbool(b'experimental', b'worker.repository-upgrade')
1055 if pycompat.iswindows or not use_w:
1052 if pycompat.iswindows or not use_w:
1056 return _get_simple_sidedata_adder(srcrepo, destrepo)
1053 return _get_simple_sidedata_adder(srcrepo, destrepo)
1057 else:
1054 else:
1058 return _get_worker_sidedata_adder(srcrepo, destrepo)
1055 return _get_worker_sidedata_adder(srcrepo, destrepo)
1059
1056
1060
1057
1061 def _sidedata_worker(srcrepo, revs_queue, sidedata_queue, tokens):
1058 def _sidedata_worker(srcrepo, revs_queue, sidedata_queue, tokens):
1062 """The function used by worker precomputing sidedata
1059 """The function used by worker precomputing sidedata
1063
1060
1064 It read an input queue containing revision numbers
1061 It read an input queue containing revision numbers
1065 It write in an output queue containing (rev, <sidedata-map>)
1062 It write in an output queue containing (rev, <sidedata-map>)
1066
1063
1067 The `None` input value is used as a stop signal.
1064 The `None` input value is used as a stop signal.
1068
1065
1069 The `tokens` semaphore is user to avoid having too many unprocessed
1066 The `tokens` semaphore is user to avoid having too many unprocessed
1070 entries. The workers needs to acquire one token before fetching a task.
1067 entries. The workers needs to acquire one token before fetching a task.
1071 They will be released by the consumer of the produced data.
1068 They will be released by the consumer of the produced data.
1072 """
1069 """
1073 tokens.acquire()
1070 tokens.acquire()
1074 rev = revs_queue.get()
1071 rev = revs_queue.get()
1075 while rev is not None:
1072 while rev is not None:
1076 data = _getsidedata(srcrepo, rev)
1073 data = _getsidedata(srcrepo, rev)
1077 sidedata_queue.put((rev, data))
1074 sidedata_queue.put((rev, data))
1078 tokens.acquire()
1075 tokens.acquire()
1079 rev = revs_queue.get()
1076 rev = revs_queue.get()
1080 # processing of `None` is completed, release the token.
1077 # processing of `None` is completed, release the token.
1081 tokens.release()
1078 tokens.release()
1082
1079
1083
1080
1084 BUFF_PER_WORKER = 50
1081 BUFF_PER_WORKER = 50
1085
1082
1086
1083
1087 def _get_worker_sidedata_adder(srcrepo, destrepo):
1084 def _get_worker_sidedata_adder(srcrepo, destrepo):
1088 """The parallel version of the sidedata computation
1085 """The parallel version of the sidedata computation
1089
1086
1090 This code spawn a pool of worker that precompute a buffer of sidedata
1087 This code spawn a pool of worker that precompute a buffer of sidedata
1091 before we actually need them"""
1088 before we actually need them"""
1092 # avoid circular import copies -> scmutil -> worker -> copies
1089 # avoid circular import copies -> scmutil -> worker -> copies
1093 from . import worker
1090 from . import worker
1094
1091
1095 nbworkers = worker._numworkers(srcrepo.ui)
1092 nbworkers = worker._numworkers(srcrepo.ui)
1096
1093
1097 tokens = multiprocessing.BoundedSemaphore(nbworkers * BUFF_PER_WORKER)
1094 tokens = multiprocessing.BoundedSemaphore(nbworkers * BUFF_PER_WORKER)
1098 revsq = multiprocessing.Queue()
1095 revsq = multiprocessing.Queue()
1099 sidedataq = multiprocessing.Queue()
1096 sidedataq = multiprocessing.Queue()
1100
1097
1101 assert srcrepo.filtername is None
1098 assert srcrepo.filtername is None
1102 # queue all tasks beforehand, revision numbers are small and it make
1099 # queue all tasks beforehand, revision numbers are small and it make
1103 # synchronisation simpler
1100 # synchronisation simpler
1104 #
1101 #
1105 # Since the computation for each node can be quite expensive, the overhead
1102 # Since the computation for each node can be quite expensive, the overhead
1106 # of using a single queue is not revelant. In practice, most computation
1103 # of using a single queue is not revelant. In practice, most computation
1107 # are fast but some are very expensive and dominate all the other smaller
1104 # are fast but some are very expensive and dominate all the other smaller
1108 # cost.
1105 # cost.
1109 for r in srcrepo.changelog.revs():
1106 for r in srcrepo.changelog.revs():
1110 revsq.put(r)
1107 revsq.put(r)
1111 # queue the "no more tasks" markers
1108 # queue the "no more tasks" markers
1112 for i in range(nbworkers):
1109 for i in range(nbworkers):
1113 revsq.put(None)
1110 revsq.put(None)
1114
1111
1115 allworkers = []
1112 allworkers = []
1116 for i in range(nbworkers):
1113 for i in range(nbworkers):
1117 args = (srcrepo, revsq, sidedataq, tokens)
1114 args = (srcrepo, revsq, sidedataq, tokens)
1118 w = multiprocessing.Process(target=_sidedata_worker, args=args)
1115 w = multiprocessing.Process(target=_sidedata_worker, args=args)
1119 allworkers.append(w)
1116 allworkers.append(w)
1120 w.start()
1117 w.start()
1121
1118
1122 # dictionnary to store results for revision higher than we one we are
1119 # dictionnary to store results for revision higher than we one we are
1123 # looking for. For example, if we need the sidedatamap for 42, and 43 is
1120 # looking for. For example, if we need the sidedatamap for 42, and 43 is
1124 # received, when shelve 43 for later use.
1121 # received, when shelve 43 for later use.
1125 staging = {}
1122 staging = {}
1126
1123
1127 def sidedata_companion(revlog, rev):
1124 def sidedata_companion(revlog, rev):
1128 sidedata = {}
1125 sidedata = {}
1129 if util.safehasattr(revlog, b'filteredrevs'): # this is a changelog
1126 if util.safehasattr(revlog, b'filteredrevs'): # this is a changelog
1130 # Is the data previously shelved ?
1127 # Is the data previously shelved ?
1131 sidedata = staging.pop(rev, None)
1128 sidedata = staging.pop(rev, None)
1132 if sidedata is None:
1129 if sidedata is None:
1133 # look at the queued result until we find the one we are lookig
1130 # look at the queued result until we find the one we are lookig
1134 # for (shelve the other ones)
1131 # for (shelve the other ones)
1135 r, sidedata = sidedataq.get()
1132 r, sidedata = sidedataq.get()
1136 while r != rev:
1133 while r != rev:
1137 staging[r] = sidedata
1134 staging[r] = sidedata
1138 r, sidedata = sidedataq.get()
1135 r, sidedata = sidedataq.get()
1139 tokens.release()
1136 tokens.release()
1140 return False, (), sidedata
1137 return False, (), sidedata
1141
1138
1142 return sidedata_companion
1139 return sidedata_companion
1143
1140
1144
1141
1145 def _get_simple_sidedata_adder(srcrepo, destrepo):
1142 def _get_simple_sidedata_adder(srcrepo, destrepo):
1146 """The simple version of the sidedata computation
1143 """The simple version of the sidedata computation
1147
1144
1148 It just compute it in the same thread on request"""
1145 It just compute it in the same thread on request"""
1149
1146
1150 def sidedatacompanion(revlog, rev):
1147 def sidedatacompanion(revlog, rev):
1151 sidedata = {}
1148 sidedata = {}
1152 if util.safehasattr(revlog, 'filteredrevs'): # this is a changelog
1149 if util.safehasattr(revlog, 'filteredrevs'): # this is a changelog
1153 sidedata = _getsidedata(srcrepo, rev)
1150 sidedata = _getsidedata(srcrepo, rev)
1154 return False, (), sidedata
1151 return False, (), sidedata
1155
1152
1156 return sidedatacompanion
1153 return sidedatacompanion
1157
1154
1158
1155
1159 def getsidedataremover(srcrepo, destrepo):
1156 def getsidedataremover(srcrepo, destrepo):
1160 def sidedatacompanion(revlog, rev):
1157 def sidedatacompanion(revlog, rev):
1161 f = ()
1158 f = ()
1162 if util.safehasattr(revlog, 'filteredrevs'): # this is a changelog
1159 if util.safehasattr(revlog, 'filteredrevs'): # this is a changelog
1163 if revlog.flags(rev) & REVIDX_SIDEDATA:
1160 if revlog.flags(rev) & REVIDX_SIDEDATA:
1164 f = (
1161 f = (
1165 sidedatamod.SD_P1COPIES,
1162 sidedatamod.SD_P1COPIES,
1166 sidedatamod.SD_P2COPIES,
1163 sidedatamod.SD_P2COPIES,
1167 sidedatamod.SD_FILESADDED,
1164 sidedatamod.SD_FILESADDED,
1168 sidedatamod.SD_FILESREMOVED,
1165 sidedatamod.SD_FILESREMOVED,
1169 )
1166 )
1170 return False, f, {}
1167 return False, f, {}
1171
1168
1172 return sidedatacompanion
1169 return sidedatacompanion
@@ -1,262 +1,236 b''
1 $ hg init
1 $ hg init
2
2
3 $ echo "[merge]" >> .hg/hgrc
3 $ echo "[merge]" >> .hg/hgrc
4 $ echo "followcopies = 1" >> .hg/hgrc
4 $ echo "followcopies = 1" >> .hg/hgrc
5
5
6 $ echo foo > a
6 $ echo foo > a
7 $ echo foo > a2
7 $ echo foo > a2
8 $ hg add a a2
8 $ hg add a a2
9 $ hg ci -m "start"
9 $ hg ci -m "start"
10
10
11 $ hg mv a b
11 $ hg mv a b
12 $ hg mv a2 b2
12 $ hg mv a2 b2
13 $ hg ci -m "rename"
13 $ hg ci -m "rename"
14
14
15 $ hg co 0
15 $ hg co 0
16 2 files updated, 0 files merged, 2 files removed, 0 files unresolved
16 2 files updated, 0 files merged, 2 files removed, 0 files unresolved
17
17
18 $ echo blahblah > a
18 $ echo blahblah > a
19 $ echo blahblah > a2
19 $ echo blahblah > a2
20 $ hg mv a2 c2
20 $ hg mv a2 c2
21 $ hg ci -m "modify"
21 $ hg ci -m "modify"
22 created new head
22 created new head
23
23
24 $ hg merge -y --debug
24 $ hg merge -y --debug
25 unmatched files in local:
25 unmatched files in local:
26 c2
26 c2
27 unmatched files in other:
27 unmatched files in other:
28 b
28 b
29 b2
29 b2
30 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
30 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
31 on local side:
31 on local side:
32 src: 'a2' -> dst: 'c2' !
32 src: 'a2' -> dst: 'c2' !
33 on remote side:
33 on remote side:
34 src: 'a' -> dst: 'b' *
34 src: 'a' -> dst: 'b' *
35 src: 'a2' -> dst: 'b2' !
35 src: 'a2' -> dst: 'b2' !
36 checking for directory renames
36 checking for directory renames
37 resolving manifests
37 resolving manifests
38 branchmerge: True, force: False, partial: False
38 branchmerge: True, force: False, partial: False
39 ancestor: af1939970a1c, local: 044f8520aeeb+, remote: 85c198ef2f6c
39 ancestor: af1939970a1c, local: 044f8520aeeb+, remote: 85c198ef2f6c
40 note: possible conflict - a2 was renamed multiple times to:
40 note: possible conflict - a2 was renamed multiple times to:
41 b2
41 b2
42 c2
42 c2
43 preserving a for resolve of b
43 preserving a for resolve of b
44 removing a
44 removing a
45 b2: remote created -> g
45 b2: remote created -> g
46 getting b2
46 getting b2
47 b: remote moved from a -> m (premerge)
47 b: remote moved from a -> m (premerge)
48 picked tool ':merge' for b (binary False symlink False changedelete False)
48 picked tool ':merge' for b (binary False symlink False changedelete False)
49 merging a and b to b
49 merging a and b to b
50 my b@044f8520aeeb+ other b@85c198ef2f6c ancestor a@af1939970a1c
50 my b@044f8520aeeb+ other b@85c198ef2f6c ancestor a@af1939970a1c
51 premerge successful
51 premerge successful
52 1 files updated, 1 files merged, 0 files removed, 0 files unresolved
52 1 files updated, 1 files merged, 0 files removed, 0 files unresolved
53 (branch merge, don't forget to commit)
53 (branch merge, don't forget to commit)
54
54
55 $ hg status -AC
55 $ hg status -AC
56 M b
56 M b
57 a
57 a
58 M b2
58 M b2
59 R a
59 R a
60 C c2
60 C c2
61
61
62 $ cat b
62 $ cat b
63 blahblah
63 blahblah
64
64
65 $ hg ci -m "merge"
65 $ hg ci -m "merge"
66
66
67 $ hg debugindex b
67 $ hg debugindex b
68 rev linkrev nodeid p1 p2
68 rev linkrev nodeid p1 p2
69 0 1 57eacc201a7f 000000000000 000000000000
69 0 1 57eacc201a7f 000000000000 000000000000
70 1 3 4727ba907962 000000000000 57eacc201a7f
70 1 3 4727ba907962 000000000000 57eacc201a7f
71
71
72 $ hg debugrename b
72 $ hg debugrename b
73 b renamed from a:dd03b83622e78778b403775d0d074b9ac7387a66
73 b renamed from a:dd03b83622e78778b403775d0d074b9ac7387a66
74
74
75 This used to trigger a "divergent renames" warning, despite no renames
75 This used to trigger a "divergent renames" warning, despite no renames
76
76
77 $ hg cp b b3
77 $ hg cp b b3
78 $ hg cp b b4
78 $ hg cp b b4
79 $ hg ci -A -m 'copy b twice'
79 $ hg ci -A -m 'copy b twice'
80 $ hg up '.^'
80 $ hg up '.^'
81 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
81 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
82 $ hg up
82 $ hg up
83 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
83 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
84 $ hg rm b3 b4
84 $ hg rm b3 b4
85 $ hg ci -m 'clean up a bit of our mess'
85 $ hg ci -m 'clean up a bit of our mess'
86
86
87 We'd rather not warn on divergent renames done in the same changeset (issue2113)
87 We'd rather not warn on divergent renames done in the same changeset (issue2113)
88
88
89 $ hg cp b b3
89 $ hg cp b b3
90 $ hg mv b b4
90 $ hg mv b b4
91 $ hg ci -A -m 'divergent renames in same changeset'
91 $ hg ci -A -m 'divergent renames in same changeset'
92 $ hg up '.^'
92 $ hg up '.^'
93 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
93 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
94 $ hg up
94 $ hg up
95 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
95 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
96
96
97 Check for issue2642
97 Check for issue2642
98
98
99 $ hg init t
99 $ hg init t
100 $ cd t
100 $ cd t
101
101
102 $ echo c0 > f1
102 $ echo c0 > f1
103 $ hg ci -Aqm0
103 $ hg ci -Aqm0
104
104
105 $ hg up null -q
105 $ hg up null -q
106 $ echo c1 > f1 # backport
106 $ echo c1 > f1 # backport
107 $ hg ci -Aqm1
107 $ hg ci -Aqm1
108 $ hg mv f1 f2
108 $ hg mv f1 f2
109 $ hg ci -qm2
109 $ hg ci -qm2
110
110
111 $ hg up 0 -q
111 $ hg up 0 -q
112 $ hg merge 1 -q --tool internal:local
112 $ hg merge 1 -q --tool internal:local
113 $ hg ci -qm3
113 $ hg ci -qm3
114
114
115 $ hg merge 2
115 $ hg merge 2
116 merging f1 and f2 to f2
116 merging f1 and f2 to f2
117 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
117 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
118 (branch merge, don't forget to commit)
118 (branch merge, don't forget to commit)
119
119
120 $ cat f2
120 $ cat f2
121 c0
121 c0
122
122
123 $ cd ..
123 $ cd ..
124
124
125 Check for issue2089
125 Check for issue2089
126
126
127 $ hg init repo2089
127 $ hg init repo2089
128 $ cd repo2089
128 $ cd repo2089
129
129
130 $ echo c0 > f1
130 $ echo c0 > f1
131 $ hg ci -Aqm0
131 $ hg ci -Aqm0
132
132
133 $ hg up null -q
133 $ hg up null -q
134 $ echo c1 > f1
134 $ echo c1 > f1
135 $ hg ci -Aqm1
135 $ hg ci -Aqm1
136
136
137 $ hg up 0 -q
137 $ hg up 0 -q
138 $ hg merge 1 -q --tool internal:local
138 $ hg merge 1 -q --tool internal:local
139 $ echo c2 > f1
139 $ echo c2 > f1
140 $ hg ci -qm2
140 $ hg ci -qm2
141
141
142 $ hg up 1 -q
142 $ hg up 1 -q
143 $ hg mv f1 f2
143 $ hg mv f1 f2
144 $ hg ci -Aqm3
144 $ hg ci -Aqm3
145
145
146 $ hg up 2 -q
146 $ hg up 2 -q
147 $ hg merge 3
147 $ hg merge 3
148 merging f1 and f2 to f2
148 merging f1 and f2 to f2
149 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
149 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
150 (branch merge, don't forget to commit)
150 (branch merge, don't forget to commit)
151
151
152 $ cat f2
152 $ cat f2
153 c2
153 c2
154
154
155 $ cd ..
155 $ cd ..
156
156
157 Check for issue3074
157 Check for issue3074
158
158
159 $ hg init repo3074
159 $ hg init repo3074
160 $ cd repo3074
160 $ cd repo3074
161 $ echo foo > file
161 $ echo foo > file
162 $ hg add file
162 $ hg add file
163 $ hg commit -m "added file"
163 $ hg commit -m "added file"
164 $ hg mv file newfile
164 $ hg mv file newfile
165 $ hg commit -m "renamed file"
165 $ hg commit -m "renamed file"
166 $ hg update 0
166 $ hg update 0
167 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
167 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
168 $ hg rm file
168 $ hg rm file
169 $ hg commit -m "deleted file"
169 $ hg commit -m "deleted file"
170 created new head
170 created new head
171 $ hg merge --debug
171 $ hg merge --debug
172 unmatched files in other:
172 unmatched files in other:
173 newfile
173 newfile
174 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
174 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
175 on remote side:
175 on remote side:
176 src: 'file' -> dst: 'newfile' %
176 src: 'file' -> dst: 'newfile' %
177 checking for directory renames
177 checking for directory renames
178 resolving manifests
178 resolving manifests
179 branchmerge: True, force: False, partial: False
179 branchmerge: True, force: False, partial: False
180 ancestor: 19d7f95df299, local: 0084274f6b67+, remote: 5d32493049f0
180 ancestor: 19d7f95df299, local: 0084274f6b67+, remote: 5d32493049f0
181 note: possible conflict - file was deleted and renamed to:
181 note: possible conflict - file was deleted and renamed to:
182 newfile
182 newfile
183 newfile: remote created -> g
183 newfile: remote created -> g
184 getting newfile
184 getting newfile
185 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
185 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
186 (branch merge, don't forget to commit)
186 (branch merge, don't forget to commit)
187 $ hg status
187 $ hg status
188 M newfile
188 M newfile
189 $ cd ..
189 $ cd ..
190
190
191 Create x and y, then modify y and rename x to z on one side of merge, and
191 Create x and y, then modify y and rename x to z on one side of merge, and
192 modify x and rename y to z on the other side.
192 modify x and rename y to z on the other side.
193 $ hg init conflicting-target
193 $ hg init conflicting-target
194 $ cd conflicting-target
194 $ cd conflicting-target
195 $ echo x > x
195 $ echo x > x
196 $ echo y > y
196 $ echo y > y
197 $ hg ci -Aqm 'add x and y'
197 $ hg ci -Aqm 'add x and y'
198 $ hg mv x z
198 $ hg mv x z
199 $ echo foo >> y
199 $ echo foo >> y
200 $ hg ci -qm 'modify y, rename x to z'
200 $ hg ci -qm 'modify y, rename x to z'
201 $ hg co -q 0
201 $ hg co -q 0
202 $ hg mv y z
202 $ hg mv y z
203 $ echo foo >> x
203 $ echo foo >> x
204 $ hg ci -qm 'modify x, rename y to z'
204 $ hg ci -qm 'modify x, rename y to z'
205 # We should probably tell the user about the conflicting rename sources.
205 # We should probably tell the user about the conflicting rename sources.
206 # Depending on which side they pick, we should take that rename and get
206 # Depending on which side they pick, we should take that rename and get
207 # the changes to the source from the other side. The unchanged file should
207 # the changes to the source from the other side. The unchanged file should
208 # remain.
208 # remain.
209 # we should not get the prompts about modify/delete conflicts
210 $ hg merge --debug 1 -t :merge3
209 $ hg merge --debug 1 -t :merge3
211 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
210 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
212 on local side:
211 on local side:
213 src: 'y' -> dst: 'z'
212 src: 'y' -> dst: 'z' *
214 on remote side:
213 on remote side:
215 src: 'x' -> dst: 'z'
214 src: 'x' -> dst: 'z' *
216 checking for directory renames
215 checking for directory renames
217 resolving manifests
216 resolving manifests
218 branchmerge: True, force: False, partial: False
217 branchmerge: True, force: False, partial: False
219 ancestor: 5151c134577e, local: 07fcbc9a74ed+, remote: f21419739508
218 ancestor: 5151c134577e, local: 07fcbc9a74ed+, remote: f21419739508
220 preserving x for resolve of x
221 preserving z for resolve of z
219 preserving z for resolve of z
222 starting 4 threads for background file closing (?)
220 starting 4 threads for background file closing (?)
223 x: prompt changed/deleted -> m (premerge)
221 z: both renamed from y -> m (premerge)
224 picked tool ':prompt' for x (binary False symlink False changedelete True)
225 file 'x' was deleted in other [merge rev] but was modified in local [working copy].
226 You can use (c)hanged version, (d)elete, or leave (u)nresolved.
227 What do you want to do? u
228 y: prompt deleted/changed -> m (premerge)
229 picked tool ':prompt' for y (binary False symlink False changedelete True)
230 file 'y' was deleted in local [working copy] but was modified in other [merge rev].
231 You can use (c)hanged version, leave (d)eleted, or leave (u)nresolved.
232 What do you want to do? u
233 z: both created -> m (premerge)
234 picked tool ':merge3' for z (binary False symlink False changedelete False)
222 picked tool ':merge3' for z (binary False symlink False changedelete False)
235 merging z
223 merging z
236 my z@07fcbc9a74ed+ other z@f21419739508 ancestor z@000000000000
224 my z@07fcbc9a74ed+ other z@f21419739508 ancestor y@5151c134577e
237 z: both created -> m (merge)
225 premerge successful
238 picked tool ':merge3' for z (binary False symlink False changedelete False)
226 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
239 my z@07fcbc9a74ed+ other z@f21419739508 ancestor z@000000000000
227 (branch merge, don't forget to commit)
240 warning: conflicts while merging z! (edit, then use 'hg resolve --mark')
241 0 files updated, 0 files merged, 0 files removed, 3 files unresolved
242 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
243 [1]
244 $ ls
228 $ ls
245 x
229 x
246 y
247 z
230 z
248 z.orig
249 $ cat x
231 $ cat x
250 x
232 x
251 foo
233 foo
252 $ cat y
253 y
254 foo
255 # 'z' should have had the added 'foo' line
234 # 'z' should have had the added 'foo' line
256 $ cat z
235 $ cat z
257 <<<<<<< working copy: 07fcbc9a74ed - test: modify x, rename y to z
258 y
259 ||||||| base
260 =======
261 x
236 x
262 >>>>>>> merge rev: f21419739508 - test: modify y, rename x to z
@@ -1,1082 +1,1048 b''
1
1
2 $ mkdir -p t
2 $ mkdir -p t
3 $ cd t
3 $ cd t
4 $ cat <<EOF > merge
4 $ cat <<EOF > merge
5 > import sys, os
5 > import sys, os
6 > f = open(sys.argv[1], "w")
6 > f = open(sys.argv[1], "w")
7 > f.write("merge %s %s %s" % (sys.argv[1], sys.argv[2], sys.argv[3]))
7 > f.write("merge %s %s %s" % (sys.argv[1], sys.argv[2], sys.argv[3]))
8 > f.close()
8 > f.close()
9 > EOF
9 > EOF
10
10
11 perform a test merge with possible renaming
11 perform a test merge with possible renaming
12 args:
12 args:
13 $1 = action in local branch
13 $1 = action in local branch
14 $2 = action in remote branch
14 $2 = action in remote branch
15 $3 = action in working dir
15 $3 = action in working dir
16 $4 = expected result
16 $4 = expected result
17
17
18 $ tm()
18 $ tm()
19 > {
19 > {
20 > hg init t
20 > hg init t
21 > cd t
21 > cd t
22 > echo "[merge]" >> .hg/hgrc
22 > echo "[merge]" >> .hg/hgrc
23 > echo "followcopies = 1" >> .hg/hgrc
23 > echo "followcopies = 1" >> .hg/hgrc
24 >
24 >
25 > # base
25 > # base
26 > echo base > a
26 > echo base > a
27 > echo base > rev # used to force commits
27 > echo base > rev # used to force commits
28 > hg add a rev
28 > hg add a rev
29 > hg ci -m "base"
29 > hg ci -m "base"
30 >
30 >
31 > # remote
31 > # remote
32 > echo remote > rev
32 > echo remote > rev
33 > if [ "$2" != "" ] ; then $2 ; fi
33 > if [ "$2" != "" ] ; then $2 ; fi
34 > hg ci -m "remote"
34 > hg ci -m "remote"
35 >
35 >
36 > # local
36 > # local
37 > hg co -q 0
37 > hg co -q 0
38 > echo local > rev
38 > echo local > rev
39 > if [ "$1" != "" ] ; then $1 ; fi
39 > if [ "$1" != "" ] ; then $1 ; fi
40 > hg ci -m "local"
40 > hg ci -m "local"
41 >
41 >
42 > # working dir
42 > # working dir
43 > echo local > rev
43 > echo local > rev
44 > if [ "$3" != "" ] ; then $3 ; fi
44 > if [ "$3" != "" ] ; then $3 ; fi
45 >
45 >
46 > # merge
46 > # merge
47 > echo "--------------"
47 > echo "--------------"
48 > echo "test L:$1 R:$2 W:$3 - $4"
48 > echo "test L:$1 R:$2 W:$3 - $4"
49 > echo "--------------"
49 > echo "--------------"
50 > hg merge -y --debug --traceback --tool="\"$PYTHON\" ../merge"
50 > hg merge -y --debug --traceback --tool="\"$PYTHON\" ../merge"
51 >
51 >
52 > echo "--------------"
52 > echo "--------------"
53 > hg status -camC -X rev
53 > hg status -camC -X rev
54 >
54 >
55 > hg ci -m "merge"
55 > hg ci -m "merge"
56 >
56 >
57 > echo "--------------"
57 > echo "--------------"
58 > echo
58 > echo
59 >
59 >
60 > cd ..
60 > cd ..
61 > rm -r t
61 > rm -r t
62 > }
62 > }
63 $ up() {
63 $ up() {
64 > cp rev $1
64 > cp rev $1
65 > hg add $1 2> /dev/null
65 > hg add $1 2> /dev/null
66 > if [ "$2" != "" ] ; then
66 > if [ "$2" != "" ] ; then
67 > cp rev $2
67 > cp rev $2
68 > hg add $2 2> /dev/null
68 > hg add $2 2> /dev/null
69 > fi
69 > fi
70 > }
70 > }
71 $ um() { up $1; hg mv $1 $2; }
71 $ um() { up $1; hg mv $1 $2; }
72 $ nc() { hg cp $1 $2; } # just copy
72 $ nc() { hg cp $1 $2; } # just copy
73 $ nm() { hg mv $1 $2; } # just move
73 $ nm() { hg mv $1 $2; } # just move
74 $ tm "up a " "nc a b" " " "1 get local a to b"
74 $ tm "up a " "nc a b" " " "1 get local a to b"
75 created new head
75 created new head
76 --------------
76 --------------
77 test L:up a R:nc a b W: - 1 get local a to b
77 test L:up a R:nc a b W: - 1 get local a to b
78 --------------
78 --------------
79 unmatched files in other:
79 unmatched files in other:
80 b
80 b
81 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
81 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
82 on remote side:
82 on remote side:
83 src: 'a' -> dst: 'b' *
83 src: 'a' -> dst: 'b' *
84 checking for directory renames
84 checking for directory renames
85 resolving manifests
85 resolving manifests
86 branchmerge: True, force: False, partial: False
86 branchmerge: True, force: False, partial: False
87 ancestor: 924404dff337, local: e300d1c794ec+, remote: 4ce40f5aca24
87 ancestor: 924404dff337, local: e300d1c794ec+, remote: 4ce40f5aca24
88 preserving a for resolve of b
88 preserving a for resolve of b
89 preserving rev for resolve of rev
89 preserving rev for resolve of rev
90 starting 4 threads for background file closing (?)
90 starting 4 threads for background file closing (?)
91 b: remote copied from a -> m (premerge)
91 b: remote copied from a -> m (premerge)
92 picked tool '* ../merge' for b (binary False symlink False changedelete False) (glob)
92 picked tool '* ../merge' for b (binary False symlink False changedelete False) (glob)
93 merging a and b to b
93 merging a and b to b
94 my b@e300d1c794ec+ other b@4ce40f5aca24 ancestor a@924404dff337
94 my b@e300d1c794ec+ other b@4ce40f5aca24 ancestor a@924404dff337
95 premerge successful
95 premerge successful
96 rev: versions differ -> m (premerge)
96 rev: versions differ -> m (premerge)
97 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
97 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
98 merging rev
98 merging rev
99 my rev@e300d1c794ec+ other rev@4ce40f5aca24 ancestor rev@924404dff337
99 my rev@e300d1c794ec+ other rev@4ce40f5aca24 ancestor rev@924404dff337
100 rev: versions differ -> m (merge)
100 rev: versions differ -> m (merge)
101 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
101 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
102 my rev@e300d1c794ec+ other rev@4ce40f5aca24 ancestor rev@924404dff337
102 my rev@e300d1c794ec+ other rev@4ce40f5aca24 ancestor rev@924404dff337
103 launching merge tool: * ../merge *$TESTTMP/t/t/rev* * * (glob)
103 launching merge tool: * ../merge *$TESTTMP/t/t/rev* * * (glob)
104 merge tool returned: 0
104 merge tool returned: 0
105 0 files updated, 2 files merged, 0 files removed, 0 files unresolved
105 0 files updated, 2 files merged, 0 files removed, 0 files unresolved
106 (branch merge, don't forget to commit)
106 (branch merge, don't forget to commit)
107 --------------
107 --------------
108 M b
108 M b
109 a
109 a
110 C a
110 C a
111 --------------
111 --------------
112
112
113 $ tm "nc a b" "up a " " " "2 get rem change to a and b"
113 $ tm "nc a b" "up a " " " "2 get rem change to a and b"
114 created new head
114 created new head
115 --------------
115 --------------
116 test L:nc a b R:up a W: - 2 get rem change to a and b
116 test L:nc a b R:up a W: - 2 get rem change to a and b
117 --------------
117 --------------
118 unmatched files in local:
118 unmatched files in local:
119 b
119 b
120 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
120 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
121 on local side:
121 on local side:
122 src: 'a' -> dst: 'b' *
122 src: 'a' -> dst: 'b' *
123 checking for directory renames
123 checking for directory renames
124 resolving manifests
124 resolving manifests
125 branchmerge: True, force: False, partial: False
125 branchmerge: True, force: False, partial: False
126 ancestor: 924404dff337, local: 86a2aa42fc76+, remote: f4db7e329e71
126 ancestor: 924404dff337, local: 86a2aa42fc76+, remote: f4db7e329e71
127 preserving b for resolve of b
127 preserving b for resolve of b
128 preserving rev for resolve of rev
128 preserving rev for resolve of rev
129 a: remote is newer -> g
129 a: remote is newer -> g
130 getting a
130 getting a
131 b: local copied/moved from a -> m (premerge)
131 b: local copied/moved from a -> m (premerge)
132 picked tool '* ../merge' for b (binary False symlink False changedelete False) (glob)
132 picked tool '* ../merge' for b (binary False symlink False changedelete False) (glob)
133 merging b and a to b
133 merging b and a to b
134 my b@86a2aa42fc76+ other a@f4db7e329e71 ancestor a@924404dff337
134 my b@86a2aa42fc76+ other a@f4db7e329e71 ancestor a@924404dff337
135 premerge successful
135 premerge successful
136 rev: versions differ -> m (premerge)
136 rev: versions differ -> m (premerge)
137 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
137 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
138 merging rev
138 merging rev
139 my rev@86a2aa42fc76+ other rev@f4db7e329e71 ancestor rev@924404dff337
139 my rev@86a2aa42fc76+ other rev@f4db7e329e71 ancestor rev@924404dff337
140 rev: versions differ -> m (merge)
140 rev: versions differ -> m (merge)
141 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
141 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
142 my rev@86a2aa42fc76+ other rev@f4db7e329e71 ancestor rev@924404dff337
142 my rev@86a2aa42fc76+ other rev@f4db7e329e71 ancestor rev@924404dff337
143 launching merge tool: * ../merge *$TESTTMP/t/t/rev* * * (glob)
143 launching merge tool: * ../merge *$TESTTMP/t/t/rev* * * (glob)
144 merge tool returned: 0
144 merge tool returned: 0
145 1 files updated, 2 files merged, 0 files removed, 0 files unresolved
145 1 files updated, 2 files merged, 0 files removed, 0 files unresolved
146 (branch merge, don't forget to commit)
146 (branch merge, don't forget to commit)
147 --------------
147 --------------
148 M a
148 M a
149 M b
149 M b
150 a
150 a
151 --------------
151 --------------
152
152
153 $ tm "up a " "nm a b" " " "3 get local a change to b, remove a"
153 $ tm "up a " "nm a b" " " "3 get local a change to b, remove a"
154 created new head
154 created new head
155 --------------
155 --------------
156 test L:up a R:nm a b W: - 3 get local a change to b, remove a
156 test L:up a R:nm a b W: - 3 get local a change to b, remove a
157 --------------
157 --------------
158 unmatched files in other:
158 unmatched files in other:
159 b
159 b
160 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
160 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
161 on remote side:
161 on remote side:
162 src: 'a' -> dst: 'b' *
162 src: 'a' -> dst: 'b' *
163 checking for directory renames
163 checking for directory renames
164 resolving manifests
164 resolving manifests
165 branchmerge: True, force: False, partial: False
165 branchmerge: True, force: False, partial: False
166 ancestor: 924404dff337, local: e300d1c794ec+, remote: bdb19105162a
166 ancestor: 924404dff337, local: e300d1c794ec+, remote: bdb19105162a
167 preserving a for resolve of b
167 preserving a for resolve of b
168 preserving rev for resolve of rev
168 preserving rev for resolve of rev
169 removing a
169 removing a
170 starting 4 threads for background file closing (?)
170 starting 4 threads for background file closing (?)
171 b: remote moved from a -> m (premerge)
171 b: remote moved from a -> m (premerge)
172 picked tool '* ../merge' for b (binary False symlink False changedelete False) (glob)
172 picked tool '* ../merge' for b (binary False symlink False changedelete False) (glob)
173 merging a and b to b
173 merging a and b to b
174 my b@e300d1c794ec+ other b@bdb19105162a ancestor a@924404dff337
174 my b@e300d1c794ec+ other b@bdb19105162a ancestor a@924404dff337
175 premerge successful
175 premerge successful
176 rev: versions differ -> m (premerge)
176 rev: versions differ -> m (premerge)
177 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
177 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
178 merging rev
178 merging rev
179 my rev@e300d1c794ec+ other rev@bdb19105162a ancestor rev@924404dff337
179 my rev@e300d1c794ec+ other rev@bdb19105162a ancestor rev@924404dff337
180 rev: versions differ -> m (merge)
180 rev: versions differ -> m (merge)
181 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
181 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
182 my rev@e300d1c794ec+ other rev@bdb19105162a ancestor rev@924404dff337
182 my rev@e300d1c794ec+ other rev@bdb19105162a ancestor rev@924404dff337
183 launching merge tool: * ../merge *$TESTTMP/t/t/rev* * * (glob)
183 launching merge tool: * ../merge *$TESTTMP/t/t/rev* * * (glob)
184 merge tool returned: 0
184 merge tool returned: 0
185 0 files updated, 2 files merged, 0 files removed, 0 files unresolved
185 0 files updated, 2 files merged, 0 files removed, 0 files unresolved
186 (branch merge, don't forget to commit)
186 (branch merge, don't forget to commit)
187 --------------
187 --------------
188 M b
188 M b
189 a
189 a
190 --------------
190 --------------
191
191
192 $ tm "nm a b" "up a " " " "4 get remote change to b"
192 $ tm "nm a b" "up a " " " "4 get remote change to b"
193 created new head
193 created new head
194 --------------
194 --------------
195 test L:nm a b R:up a W: - 4 get remote change to b
195 test L:nm a b R:up a W: - 4 get remote change to b
196 --------------
196 --------------
197 unmatched files in local:
197 unmatched files in local:
198 b
198 b
199 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
199 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
200 on local side:
200 on local side:
201 src: 'a' -> dst: 'b' *
201 src: 'a' -> dst: 'b' *
202 checking for directory renames
202 checking for directory renames
203 resolving manifests
203 resolving manifests
204 branchmerge: True, force: False, partial: False
204 branchmerge: True, force: False, partial: False
205 ancestor: 924404dff337, local: 02963e448370+, remote: f4db7e329e71
205 ancestor: 924404dff337, local: 02963e448370+, remote: f4db7e329e71
206 preserving b for resolve of b
206 preserving b for resolve of b
207 preserving rev for resolve of rev
207 preserving rev for resolve of rev
208 starting 4 threads for background file closing (?)
208 starting 4 threads for background file closing (?)
209 b: local copied/moved from a -> m (premerge)
209 b: local copied/moved from a -> m (premerge)
210 picked tool '* ../merge' for b (binary False symlink False changedelete False) (glob)
210 picked tool '* ../merge' for b (binary False symlink False changedelete False) (glob)
211 merging b and a to b
211 merging b and a to b
212 my b@02963e448370+ other a@f4db7e329e71 ancestor a@924404dff337
212 my b@02963e448370+ other a@f4db7e329e71 ancestor a@924404dff337
213 premerge successful
213 premerge successful
214 rev: versions differ -> m (premerge)
214 rev: versions differ -> m (premerge)
215 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
215 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
216 merging rev
216 merging rev
217 my rev@02963e448370+ other rev@f4db7e329e71 ancestor rev@924404dff337
217 my rev@02963e448370+ other rev@f4db7e329e71 ancestor rev@924404dff337
218 rev: versions differ -> m (merge)
218 rev: versions differ -> m (merge)
219 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
219 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
220 my rev@02963e448370+ other rev@f4db7e329e71 ancestor rev@924404dff337
220 my rev@02963e448370+ other rev@f4db7e329e71 ancestor rev@924404dff337
221 launching merge tool: * ../merge *$TESTTMP/t/t/rev* * * (glob)
221 launching merge tool: * ../merge *$TESTTMP/t/t/rev* * * (glob)
222 merge tool returned: 0
222 merge tool returned: 0
223 0 files updated, 2 files merged, 0 files removed, 0 files unresolved
223 0 files updated, 2 files merged, 0 files removed, 0 files unresolved
224 (branch merge, don't forget to commit)
224 (branch merge, don't forget to commit)
225 --------------
225 --------------
226 M b
226 M b
227 a
227 a
228 --------------
228 --------------
229
229
230 $ tm " " "nc a b" " " "5 get b"
230 $ tm " " "nc a b" " " "5 get b"
231 created new head
231 created new head
232 --------------
232 --------------
233 test L: R:nc a b W: - 5 get b
233 test L: R:nc a b W: - 5 get b
234 --------------
234 --------------
235 unmatched files in other:
235 unmatched files in other:
236 b
236 b
237 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
237 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
238 on remote side:
238 on remote side:
239 src: 'a' -> dst: 'b'
239 src: 'a' -> dst: 'b'
240 checking for directory renames
240 checking for directory renames
241 resolving manifests
241 resolving manifests
242 branchmerge: True, force: False, partial: False
242 branchmerge: True, force: False, partial: False
243 ancestor: 924404dff337, local: 94b33a1b7f2d+, remote: 4ce40f5aca24
243 ancestor: 924404dff337, local: 94b33a1b7f2d+, remote: 4ce40f5aca24
244 preserving rev for resolve of rev
244 preserving rev for resolve of rev
245 b: remote created -> g
245 b: remote created -> g
246 getting b
246 getting b
247 rev: versions differ -> m (premerge)
247 rev: versions differ -> m (premerge)
248 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
248 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
249 merging rev
249 merging rev
250 my rev@94b33a1b7f2d+ other rev@4ce40f5aca24 ancestor rev@924404dff337
250 my rev@94b33a1b7f2d+ other rev@4ce40f5aca24 ancestor rev@924404dff337
251 rev: versions differ -> m (merge)
251 rev: versions differ -> m (merge)
252 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
252 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
253 my rev@94b33a1b7f2d+ other rev@4ce40f5aca24 ancestor rev@924404dff337
253 my rev@94b33a1b7f2d+ other rev@4ce40f5aca24 ancestor rev@924404dff337
254 launching merge tool: * ../merge *$TESTTMP/t/t/rev* * * (glob)
254 launching merge tool: * ../merge *$TESTTMP/t/t/rev* * * (glob)
255 merge tool returned: 0
255 merge tool returned: 0
256 1 files updated, 1 files merged, 0 files removed, 0 files unresolved
256 1 files updated, 1 files merged, 0 files removed, 0 files unresolved
257 (branch merge, don't forget to commit)
257 (branch merge, don't forget to commit)
258 --------------
258 --------------
259 M b
259 M b
260 C a
260 C a
261 --------------
261 --------------
262
262
263 $ tm "nc a b" " " " " "6 nothing"
263 $ tm "nc a b" " " " " "6 nothing"
264 created new head
264 created new head
265 --------------
265 --------------
266 test L:nc a b R: W: - 6 nothing
266 test L:nc a b R: W: - 6 nothing
267 --------------
267 --------------
268 unmatched files in local:
268 unmatched files in local:
269 b
269 b
270 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
270 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
271 on local side:
271 on local side:
272 src: 'a' -> dst: 'b'
272 src: 'a' -> dst: 'b'
273 checking for directory renames
273 checking for directory renames
274 resolving manifests
274 resolving manifests
275 branchmerge: True, force: False, partial: False
275 branchmerge: True, force: False, partial: False
276 ancestor: 924404dff337, local: 86a2aa42fc76+, remote: 97c705ade336
276 ancestor: 924404dff337, local: 86a2aa42fc76+, remote: 97c705ade336
277 preserving rev for resolve of rev
277 preserving rev for resolve of rev
278 starting 4 threads for background file closing (?)
278 starting 4 threads for background file closing (?)
279 rev: versions differ -> m (premerge)
279 rev: versions differ -> m (premerge)
280 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
280 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
281 merging rev
281 merging rev
282 my rev@86a2aa42fc76+ other rev@97c705ade336 ancestor rev@924404dff337
282 my rev@86a2aa42fc76+ other rev@97c705ade336 ancestor rev@924404dff337
283 rev: versions differ -> m (merge)
283 rev: versions differ -> m (merge)
284 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
284 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
285 my rev@86a2aa42fc76+ other rev@97c705ade336 ancestor rev@924404dff337
285 my rev@86a2aa42fc76+ other rev@97c705ade336 ancestor rev@924404dff337
286 launching merge tool: * ../merge *$TESTTMP/t/t/rev* * * (glob)
286 launching merge tool: * ../merge *$TESTTMP/t/t/rev* * * (glob)
287 merge tool returned: 0
287 merge tool returned: 0
288 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
288 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
289 (branch merge, don't forget to commit)
289 (branch merge, don't forget to commit)
290 --------------
290 --------------
291 C a
291 C a
292 C b
292 C b
293 --------------
293 --------------
294
294
295 $ tm " " "nm a b" " " "7 get b"
295 $ tm " " "nm a b" " " "7 get b"
296 created new head
296 created new head
297 --------------
297 --------------
298 test L: R:nm a b W: - 7 get b
298 test L: R:nm a b W: - 7 get b
299 --------------
299 --------------
300 unmatched files in other:
300 unmatched files in other:
301 b
301 b
302 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
302 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
303 on remote side:
303 on remote side:
304 src: 'a' -> dst: 'b'
304 src: 'a' -> dst: 'b'
305 checking for directory renames
305 checking for directory renames
306 resolving manifests
306 resolving manifests
307 branchmerge: True, force: False, partial: False
307 branchmerge: True, force: False, partial: False
308 ancestor: 924404dff337, local: 94b33a1b7f2d+, remote: bdb19105162a
308 ancestor: 924404dff337, local: 94b33a1b7f2d+, remote: bdb19105162a
309 preserving rev for resolve of rev
309 preserving rev for resolve of rev
310 a: other deleted -> r
310 a: other deleted -> r
311 removing a
311 removing a
312 b: remote created -> g
312 b: remote created -> g
313 getting b
313 getting b
314 rev: versions differ -> m (premerge)
314 rev: versions differ -> m (premerge)
315 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
315 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
316 merging rev
316 merging rev
317 my rev@94b33a1b7f2d+ other rev@bdb19105162a ancestor rev@924404dff337
317 my rev@94b33a1b7f2d+ other rev@bdb19105162a ancestor rev@924404dff337
318 rev: versions differ -> m (merge)
318 rev: versions differ -> m (merge)
319 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
319 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
320 my rev@94b33a1b7f2d+ other rev@bdb19105162a ancestor rev@924404dff337
320 my rev@94b33a1b7f2d+ other rev@bdb19105162a ancestor rev@924404dff337
321 launching merge tool: * ../merge *$TESTTMP/t/t/rev* * * (glob)
321 launching merge tool: * ../merge *$TESTTMP/t/t/rev* * * (glob)
322 merge tool returned: 0
322 merge tool returned: 0
323 1 files updated, 1 files merged, 1 files removed, 0 files unresolved
323 1 files updated, 1 files merged, 1 files removed, 0 files unresolved
324 (branch merge, don't forget to commit)
324 (branch merge, don't forget to commit)
325 --------------
325 --------------
326 M b
326 M b
327 --------------
327 --------------
328
328
329 $ tm "nm a b" " " " " "8 nothing"
329 $ tm "nm a b" " " " " "8 nothing"
330 created new head
330 created new head
331 --------------
331 --------------
332 test L:nm a b R: W: - 8 nothing
332 test L:nm a b R: W: - 8 nothing
333 --------------
333 --------------
334 unmatched files in local:
334 unmatched files in local:
335 b
335 b
336 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
336 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
337 on local side:
337 on local side:
338 src: 'a' -> dst: 'b'
338 src: 'a' -> dst: 'b'
339 checking for directory renames
339 checking for directory renames
340 resolving manifests
340 resolving manifests
341 branchmerge: True, force: False, partial: False
341 branchmerge: True, force: False, partial: False
342 ancestor: 924404dff337, local: 02963e448370+, remote: 97c705ade336
342 ancestor: 924404dff337, local: 02963e448370+, remote: 97c705ade336
343 preserving rev for resolve of rev
343 preserving rev for resolve of rev
344 starting 4 threads for background file closing (?)
344 starting 4 threads for background file closing (?)
345 rev: versions differ -> m (premerge)
345 rev: versions differ -> m (premerge)
346 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
346 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
347 merging rev
347 merging rev
348 my rev@02963e448370+ other rev@97c705ade336 ancestor rev@924404dff337
348 my rev@02963e448370+ other rev@97c705ade336 ancestor rev@924404dff337
349 rev: versions differ -> m (merge)
349 rev: versions differ -> m (merge)
350 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
350 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
351 my rev@02963e448370+ other rev@97c705ade336 ancestor rev@924404dff337
351 my rev@02963e448370+ other rev@97c705ade336 ancestor rev@924404dff337
352 launching merge tool: * ../merge *$TESTTMP/t/t/rev* * * (glob)
352 launching merge tool: * ../merge *$TESTTMP/t/t/rev* * * (glob)
353 merge tool returned: 0
353 merge tool returned: 0
354 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
354 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
355 (branch merge, don't forget to commit)
355 (branch merge, don't forget to commit)
356 --------------
356 --------------
357 C b
357 C b
358 --------------
358 --------------
359
359
360 $ tm "um a b" "um a b" " " "9 do merge with ancestor in a"
360 $ tm "um a b" "um a b" " " "9 do merge with ancestor in a"
361 created new head
361 created new head
362 --------------
362 --------------
363 test L:um a b R:um a b W: - 9 do merge with ancestor in a
363 test L:um a b R:um a b W: - 9 do merge with ancestor in a
364 --------------
364 --------------
365 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
365 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
366 on local side:
366 on local side:
367 src: 'a' -> dst: 'b' *
367 src: 'a' -> dst: 'b' *
368 on remote side:
368 on remote side:
369 src: 'a' -> dst: 'b' *
369 src: 'a' -> dst: 'b' *
370 checking for directory renames
370 checking for directory renames
371 resolving manifests
371 resolving manifests
372 branchmerge: True, force: False, partial: False
372 branchmerge: True, force: False, partial: False
373 ancestor: 924404dff337, local: 62e7bf090eba+, remote: 49b6d8032493
373 ancestor: 924404dff337, local: 62e7bf090eba+, remote: 49b6d8032493
374 preserving b for resolve of b
374 preserving b for resolve of b
375 preserving rev for resolve of rev
375 preserving rev for resolve of rev
376 starting 4 threads for background file closing (?)
376 starting 4 threads for background file closing (?)
377 b: both renamed from a -> m (premerge)
377 b: both renamed from a -> m (premerge)
378 picked tool '* ../merge' for b (binary False symlink False changedelete False) (glob)
378 picked tool '* ../merge' for b (binary False symlink False changedelete False) (glob)
379 merging b
379 merging b
380 my b@62e7bf090eba+ other b@49b6d8032493 ancestor a@924404dff337
380 my b@62e7bf090eba+ other b@49b6d8032493 ancestor a@924404dff337
381 rev: versions differ -> m (premerge)
381 rev: versions differ -> m (premerge)
382 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
382 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
383 merging rev
383 merging rev
384 my rev@62e7bf090eba+ other rev@49b6d8032493 ancestor rev@924404dff337
384 my rev@62e7bf090eba+ other rev@49b6d8032493 ancestor rev@924404dff337
385 b: both renamed from a -> m (merge)
385 b: both renamed from a -> m (merge)
386 picked tool '* ../merge' for b (binary False symlink False changedelete False) (glob)
386 picked tool '* ../merge' for b (binary False symlink False changedelete False) (glob)
387 my b@62e7bf090eba+ other b@49b6d8032493 ancestor a@924404dff337
387 my b@62e7bf090eba+ other b@49b6d8032493 ancestor a@924404dff337
388 launching merge tool: * ../merge *$TESTTMP/t/t/b* * * (glob)
388 launching merge tool: * ../merge *$TESTTMP/t/t/b* * * (glob)
389 merge tool returned: 0
389 merge tool returned: 0
390 rev: versions differ -> m (merge)
390 rev: versions differ -> m (merge)
391 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
391 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
392 my rev@62e7bf090eba+ other rev@49b6d8032493 ancestor rev@924404dff337
392 my rev@62e7bf090eba+ other rev@49b6d8032493 ancestor rev@924404dff337
393 launching merge tool: * ../merge *$TESTTMP/t/t/rev* * * (glob)
393 launching merge tool: * ../merge *$TESTTMP/t/t/rev* * * (glob)
394 merge tool returned: 0
394 merge tool returned: 0
395 0 files updated, 2 files merged, 0 files removed, 0 files unresolved
395 0 files updated, 2 files merged, 0 files removed, 0 files unresolved
396 (branch merge, don't forget to commit)
396 (branch merge, don't forget to commit)
397 --------------
397 --------------
398 M b
398 M b
399 --------------
399 --------------
400
400
401
401
402 m "um a c" "um x c" " " "10 do merge with no ancestor"
402 m "um a c" "um x c" " " "10 do merge with no ancestor"
403
403
404 $ tm "nm a b" "nm a c" " " "11 get c, keep b"
404 $ tm "nm a b" "nm a c" " " "11 get c, keep b"
405 created new head
405 created new head
406 --------------
406 --------------
407 test L:nm a b R:nm a c W: - 11 get c, keep b
407 test L:nm a b R:nm a c W: - 11 get c, keep b
408 --------------
408 --------------
409 unmatched files in local:
409 unmatched files in local:
410 b
410 b
411 unmatched files in other:
411 unmatched files in other:
412 c
412 c
413 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
413 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
414 on local side:
414 on local side:
415 src: 'a' -> dst: 'b' !
415 src: 'a' -> dst: 'b' !
416 on remote side:
416 on remote side:
417 src: 'a' -> dst: 'c' !
417 src: 'a' -> dst: 'c' !
418 checking for directory renames
418 checking for directory renames
419 resolving manifests
419 resolving manifests
420 branchmerge: True, force: False, partial: False
420 branchmerge: True, force: False, partial: False
421 ancestor: 924404dff337, local: 02963e448370+, remote: fe905ef2c33e
421 ancestor: 924404dff337, local: 02963e448370+, remote: fe905ef2c33e
422 note: possible conflict - a was renamed multiple times to:
422 note: possible conflict - a was renamed multiple times to:
423 b
423 b
424 c
424 c
425 preserving rev for resolve of rev
425 preserving rev for resolve of rev
426 c: remote created -> g
426 c: remote created -> g
427 getting c
427 getting c
428 rev: versions differ -> m (premerge)
428 rev: versions differ -> m (premerge)
429 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
429 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
430 merging rev
430 merging rev
431 my rev@02963e448370+ other rev@fe905ef2c33e ancestor rev@924404dff337
431 my rev@02963e448370+ other rev@fe905ef2c33e ancestor rev@924404dff337
432 rev: versions differ -> m (merge)
432 rev: versions differ -> m (merge)
433 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
433 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
434 my rev@02963e448370+ other rev@fe905ef2c33e ancestor rev@924404dff337
434 my rev@02963e448370+ other rev@fe905ef2c33e ancestor rev@924404dff337
435 launching merge tool: * ../merge *$TESTTMP/t/t/rev* * * (glob)
435 launching merge tool: * ../merge *$TESTTMP/t/t/rev* * * (glob)
436 merge tool returned: 0
436 merge tool returned: 0
437 1 files updated, 1 files merged, 0 files removed, 0 files unresolved
437 1 files updated, 1 files merged, 0 files removed, 0 files unresolved
438 (branch merge, don't forget to commit)
438 (branch merge, don't forget to commit)
439 --------------
439 --------------
440 M c
440 M c
441 C b
441 C b
442 --------------
442 --------------
443
443
444 $ tm "nc a b" "up b " " " "12 merge b no ancestor"
444 $ tm "nc a b" "up b " " " "12 merge b no ancestor"
445 created new head
445 created new head
446 --------------
446 --------------
447 test L:nc a b R:up b W: - 12 merge b no ancestor
447 test L:nc a b R:up b W: - 12 merge b no ancestor
448 --------------
448 --------------
449 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
449 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
450 on local side:
450 on local side:
451 src: 'a' -> dst: 'b'
451 src: 'a' -> dst: 'b'
452 checking for directory renames
452 checking for directory renames
453 resolving manifests
453 resolving manifests
454 branchmerge: True, force: False, partial: False
454 branchmerge: True, force: False, partial: False
455 ancestor: 924404dff337, local: 86a2aa42fc76+, remote: af30c7647fc7
455 ancestor: 924404dff337, local: 86a2aa42fc76+, remote: af30c7647fc7
456 preserving b for resolve of b
456 preserving b for resolve of b
457 preserving rev for resolve of rev
457 preserving rev for resolve of rev
458 starting 4 threads for background file closing (?)
458 starting 4 threads for background file closing (?)
459 b: both created -> m (premerge)
459 b: both created -> m (premerge)
460 picked tool '* ../merge' for b (binary False symlink False changedelete False) (glob)
460 picked tool '* ../merge' for b (binary False symlink False changedelete False) (glob)
461 merging b
461 merging b
462 my b@86a2aa42fc76+ other b@af30c7647fc7 ancestor b@000000000000
462 my b@86a2aa42fc76+ other b@af30c7647fc7 ancestor b@000000000000
463 rev: versions differ -> m (premerge)
463 rev: versions differ -> m (premerge)
464 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
464 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
465 merging rev
465 merging rev
466 my rev@86a2aa42fc76+ other rev@af30c7647fc7 ancestor rev@924404dff337
466 my rev@86a2aa42fc76+ other rev@af30c7647fc7 ancestor rev@924404dff337
467 b: both created -> m (merge)
467 b: both created -> m (merge)
468 picked tool '* ../merge' for b (binary False symlink False changedelete False) (glob)
468 picked tool '* ../merge' for b (binary False symlink False changedelete False) (glob)
469 my b@86a2aa42fc76+ other b@af30c7647fc7 ancestor b@000000000000
469 my b@86a2aa42fc76+ other b@af30c7647fc7 ancestor b@000000000000
470 launching merge tool: * ../merge *$TESTTMP/t/t/b* * * (glob)
470 launching merge tool: * ../merge *$TESTTMP/t/t/b* * * (glob)
471 merge tool returned: 0
471 merge tool returned: 0
472 rev: versions differ -> m (merge)
472 rev: versions differ -> m (merge)
473 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
473 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
474 my rev@86a2aa42fc76+ other rev@af30c7647fc7 ancestor rev@924404dff337
474 my rev@86a2aa42fc76+ other rev@af30c7647fc7 ancestor rev@924404dff337
475 launching merge tool: * ../merge *$TESTTMP/t/t/rev* * * (glob)
475 launching merge tool: * ../merge *$TESTTMP/t/t/rev* * * (glob)
476 merge tool returned: 0
476 merge tool returned: 0
477 0 files updated, 2 files merged, 0 files removed, 0 files unresolved
477 0 files updated, 2 files merged, 0 files removed, 0 files unresolved
478 (branch merge, don't forget to commit)
478 (branch merge, don't forget to commit)
479 --------------
479 --------------
480 M b
480 M b
481 C a
481 C a
482 --------------
482 --------------
483
483
484 $ tm "up b " "nm a b" " " "13 merge b no ancestor"
484 $ tm "up b " "nm a b" " " "13 merge b no ancestor"
485 created new head
485 created new head
486 --------------
486 --------------
487 test L:up b R:nm a b W: - 13 merge b no ancestor
487 test L:up b R:nm a b W: - 13 merge b no ancestor
488 --------------
488 --------------
489 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
489 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
490 on remote side:
490 on remote side:
491 src: 'a' -> dst: 'b'
491 src: 'a' -> dst: 'b'
492 checking for directory renames
492 checking for directory renames
493 resolving manifests
493 resolving manifests
494 branchmerge: True, force: False, partial: False
494 branchmerge: True, force: False, partial: False
495 ancestor: 924404dff337, local: 59318016310c+, remote: bdb19105162a
495 ancestor: 924404dff337, local: 59318016310c+, remote: bdb19105162a
496 preserving b for resolve of b
496 preserving b for resolve of b
497 preserving rev for resolve of rev
497 preserving rev for resolve of rev
498 a: other deleted -> r
498 a: other deleted -> r
499 removing a
499 removing a
500 starting 4 threads for background file closing (?)
500 starting 4 threads for background file closing (?)
501 b: both created -> m (premerge)
501 b: both created -> m (premerge)
502 picked tool '* ../merge' for b (binary False symlink False changedelete False) (glob)
502 picked tool '* ../merge' for b (binary False symlink False changedelete False) (glob)
503 merging b
503 merging b
504 my b@59318016310c+ other b@bdb19105162a ancestor b@000000000000
504 my b@59318016310c+ other b@bdb19105162a ancestor b@000000000000
505 rev: versions differ -> m (premerge)
505 rev: versions differ -> m (premerge)
506 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
506 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
507 merging rev
507 merging rev
508 my rev@59318016310c+ other rev@bdb19105162a ancestor rev@924404dff337
508 my rev@59318016310c+ other rev@bdb19105162a ancestor rev@924404dff337
509 b: both created -> m (merge)
509 b: both created -> m (merge)
510 picked tool '* ../merge' for b (binary False symlink False changedelete False) (glob)
510 picked tool '* ../merge' for b (binary False symlink False changedelete False) (glob)
511 my b@59318016310c+ other b@bdb19105162a ancestor b@000000000000
511 my b@59318016310c+ other b@bdb19105162a ancestor b@000000000000
512 launching merge tool: * ../merge *$TESTTMP/t/t/b* * * (glob)
512 launching merge tool: * ../merge *$TESTTMP/t/t/b* * * (glob)
513 merge tool returned: 0
513 merge tool returned: 0
514 rev: versions differ -> m (merge)
514 rev: versions differ -> m (merge)
515 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
515 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
516 my rev@59318016310c+ other rev@bdb19105162a ancestor rev@924404dff337
516 my rev@59318016310c+ other rev@bdb19105162a ancestor rev@924404dff337
517 launching merge tool: * ../merge *$TESTTMP/t/t/rev* * * (glob)
517 launching merge tool: * ../merge *$TESTTMP/t/t/rev* * * (glob)
518 merge tool returned: 0
518 merge tool returned: 0
519 0 files updated, 2 files merged, 1 files removed, 0 files unresolved
519 0 files updated, 2 files merged, 1 files removed, 0 files unresolved
520 (branch merge, don't forget to commit)
520 (branch merge, don't forget to commit)
521 --------------
521 --------------
522 M b
522 M b
523 --------------
523 --------------
524
524
525 $ tm "nc a b" "up a b" " " "14 merge b no ancestor"
525 $ tm "nc a b" "up a b" " " "14 merge b no ancestor"
526 created new head
526 created new head
527 --------------
527 --------------
528 test L:nc a b R:up a b W: - 14 merge b no ancestor
528 test L:nc a b R:up a b W: - 14 merge b no ancestor
529 --------------
529 --------------
530 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
530 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
531 on local side:
531 on local side:
532 src: 'a' -> dst: 'b'
532 src: 'a' -> dst: 'b' *
533 checking for directory renames
533 checking for directory renames
534 resolving manifests
534 resolving manifests
535 branchmerge: True, force: False, partial: False
535 branchmerge: True, force: False, partial: False
536 ancestor: 924404dff337, local: 86a2aa42fc76+, remote: 8dbce441892a
536 ancestor: 924404dff337, local: 86a2aa42fc76+, remote: 8dbce441892a
537 preserving b for resolve of b
537 preserving b for resolve of b
538 preserving rev for resolve of rev
538 preserving rev for resolve of rev
539 a: remote is newer -> g
539 a: remote is newer -> g
540 getting a
540 getting a
541 b: both created -> m (premerge)
541 b: both renamed from a -> m (premerge)
542 picked tool '* ../merge' for b (binary False symlink False changedelete False) (glob)
542 picked tool '* ../merge' for b (binary False symlink False changedelete False) (glob)
543 merging b
543 merging b
544 my b@86a2aa42fc76+ other b@8dbce441892a ancestor b@000000000000
544 my b@86a2aa42fc76+ other b@8dbce441892a ancestor a@924404dff337
545 premerge successful
545 rev: versions differ -> m (premerge)
546 rev: versions differ -> m (premerge)
546 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
547 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
547 merging rev
548 merging rev
548 my rev@86a2aa42fc76+ other rev@8dbce441892a ancestor rev@924404dff337
549 my rev@86a2aa42fc76+ other rev@8dbce441892a ancestor rev@924404dff337
549 b: both created -> m (merge)
550 picked tool '* ../merge' for b (binary False symlink False changedelete False) (glob)
551 my b@86a2aa42fc76+ other b@8dbce441892a ancestor b@000000000000
552 launching merge tool: * ../merge *$TESTTMP/t/t/b* * * (glob)
553 merge tool returned: 0
554 rev: versions differ -> m (merge)
550 rev: versions differ -> m (merge)
555 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
551 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
556 my rev@86a2aa42fc76+ other rev@8dbce441892a ancestor rev@924404dff337
552 my rev@86a2aa42fc76+ other rev@8dbce441892a ancestor rev@924404dff337
557 launching merge tool: * ../merge *$TESTTMP/t/t/rev* * * (glob)
553 launching merge tool: * ../merge *$TESTTMP/t/t/rev* * * (glob)
558 merge tool returned: 0
554 merge tool returned: 0
559 1 files updated, 2 files merged, 0 files removed, 0 files unresolved
555 1 files updated, 2 files merged, 0 files removed, 0 files unresolved
560 (branch merge, don't forget to commit)
556 (branch merge, don't forget to commit)
561 --------------
557 --------------
562 M a
558 M a
563 M b
559 M b
564 --------------
560 --------------
565
561
566 $ tm "up b " "nm a b" " " "15 merge b no ancestor, remove a"
562 $ tm "up b " "nm a b" " " "15 merge b no ancestor, remove a"
567 created new head
563 created new head
568 --------------
564 --------------
569 test L:up b R:nm a b W: - 15 merge b no ancestor, remove a
565 test L:up b R:nm a b W: - 15 merge b no ancestor, remove a
570 --------------
566 --------------
571 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
567 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
572 on remote side:
568 on remote side:
573 src: 'a' -> dst: 'b'
569 src: 'a' -> dst: 'b'
574 checking for directory renames
570 checking for directory renames
575 resolving manifests
571 resolving manifests
576 branchmerge: True, force: False, partial: False
572 branchmerge: True, force: False, partial: False
577 ancestor: 924404dff337, local: 59318016310c+, remote: bdb19105162a
573 ancestor: 924404dff337, local: 59318016310c+, remote: bdb19105162a
578 preserving b for resolve of b
574 preserving b for resolve of b
579 preserving rev for resolve of rev
575 preserving rev for resolve of rev
580 a: other deleted -> r
576 a: other deleted -> r
581 removing a
577 removing a
582 starting 4 threads for background file closing (?)
578 starting 4 threads for background file closing (?)
583 b: both created -> m (premerge)
579 b: both created -> m (premerge)
584 picked tool '* ../merge' for b (binary False symlink False changedelete False) (glob)
580 picked tool '* ../merge' for b (binary False symlink False changedelete False) (glob)
585 merging b
581 merging b
586 my b@59318016310c+ other b@bdb19105162a ancestor b@000000000000
582 my b@59318016310c+ other b@bdb19105162a ancestor b@000000000000
587 rev: versions differ -> m (premerge)
583 rev: versions differ -> m (premerge)
588 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
584 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
589 merging rev
585 merging rev
590 my rev@59318016310c+ other rev@bdb19105162a ancestor rev@924404dff337
586 my rev@59318016310c+ other rev@bdb19105162a ancestor rev@924404dff337
591 b: both created -> m (merge)
587 b: both created -> m (merge)
592 picked tool '* ../merge' for b (binary False symlink False changedelete False) (glob)
588 picked tool '* ../merge' for b (binary False symlink False changedelete False) (glob)
593 my b@59318016310c+ other b@bdb19105162a ancestor b@000000000000
589 my b@59318016310c+ other b@bdb19105162a ancestor b@000000000000
594 launching merge tool: * ../merge *$TESTTMP/t/t/b* * * (glob)
590 launching merge tool: * ../merge *$TESTTMP/t/t/b* * * (glob)
595 merge tool returned: 0
591 merge tool returned: 0
596 rev: versions differ -> m (merge)
592 rev: versions differ -> m (merge)
597 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
593 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
598 my rev@59318016310c+ other rev@bdb19105162a ancestor rev@924404dff337
594 my rev@59318016310c+ other rev@bdb19105162a ancestor rev@924404dff337
599 launching merge tool: * ../merge *$TESTTMP/t/t/rev* * * (glob)
595 launching merge tool: * ../merge *$TESTTMP/t/t/rev* * * (glob)
600 merge tool returned: 0
596 merge tool returned: 0
601 0 files updated, 2 files merged, 1 files removed, 0 files unresolved
597 0 files updated, 2 files merged, 1 files removed, 0 files unresolved
602 (branch merge, don't forget to commit)
598 (branch merge, don't forget to commit)
603 --------------
599 --------------
604 M b
600 M b
605 --------------
601 --------------
606
602
607 $ tm "nc a b" "up a b" " " "16 get a, merge b no ancestor"
603 $ tm "nc a b" "up a b" " " "16 get a, merge b no ancestor"
608 created new head
604 created new head
609 --------------
605 --------------
610 test L:nc a b R:up a b W: - 16 get a, merge b no ancestor
606 test L:nc a b R:up a b W: - 16 get a, merge b no ancestor
611 --------------
607 --------------
612 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
608 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
613 on local side:
609 on local side:
614 src: 'a' -> dst: 'b'
610 src: 'a' -> dst: 'b' *
615 checking for directory renames
611 checking for directory renames
616 resolving manifests
612 resolving manifests
617 branchmerge: True, force: False, partial: False
613 branchmerge: True, force: False, partial: False
618 ancestor: 924404dff337, local: 86a2aa42fc76+, remote: 8dbce441892a
614 ancestor: 924404dff337, local: 86a2aa42fc76+, remote: 8dbce441892a
619 preserving b for resolve of b
615 preserving b for resolve of b
620 preserving rev for resolve of rev
616 preserving rev for resolve of rev
621 a: remote is newer -> g
617 a: remote is newer -> g
622 getting a
618 getting a
623 b: both created -> m (premerge)
619 b: both renamed from a -> m (premerge)
624 picked tool '* ../merge' for b (binary False symlink False changedelete False) (glob)
620 picked tool '* ../merge' for b (binary False symlink False changedelete False) (glob)
625 merging b
621 merging b
626 my b@86a2aa42fc76+ other b@8dbce441892a ancestor b@000000000000
622 my b@86a2aa42fc76+ other b@8dbce441892a ancestor a@924404dff337
623 premerge successful
627 rev: versions differ -> m (premerge)
624 rev: versions differ -> m (premerge)
628 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
625 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
629 merging rev
626 merging rev
630 my rev@86a2aa42fc76+ other rev@8dbce441892a ancestor rev@924404dff337
627 my rev@86a2aa42fc76+ other rev@8dbce441892a ancestor rev@924404dff337
631 b: both created -> m (merge)
632 picked tool '* ../merge' for b (binary False symlink False changedelete False) (glob)
633 my b@86a2aa42fc76+ other b@8dbce441892a ancestor b@000000000000
634 launching merge tool: * ../merge *$TESTTMP/t/t/b* * * (glob)
635 merge tool returned: 0
636 rev: versions differ -> m (merge)
628 rev: versions differ -> m (merge)
637 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
629 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
638 my rev@86a2aa42fc76+ other rev@8dbce441892a ancestor rev@924404dff337
630 my rev@86a2aa42fc76+ other rev@8dbce441892a ancestor rev@924404dff337
639 launching merge tool: * ../merge *$TESTTMP/t/t/rev* * * (glob)
631 launching merge tool: * ../merge *$TESTTMP/t/t/rev* * * (glob)
640 merge tool returned: 0
632 merge tool returned: 0
641 1 files updated, 2 files merged, 0 files removed, 0 files unresolved
633 1 files updated, 2 files merged, 0 files removed, 0 files unresolved
642 (branch merge, don't forget to commit)
634 (branch merge, don't forget to commit)
643 --------------
635 --------------
644 M a
636 M a
645 M b
637 M b
646 --------------
638 --------------
647
639
648 $ tm "up a b" "nc a b" " " "17 keep a, merge b no ancestor"
640 $ tm "up a b" "nc a b" " " "17 keep a, merge b no ancestor"
649 created new head
641 created new head
650 --------------
642 --------------
651 test L:up a b R:nc a b W: - 17 keep a, merge b no ancestor
643 test L:up a b R:nc a b W: - 17 keep a, merge b no ancestor
652 --------------
644 --------------
653 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
645 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
654 on remote side:
646 on remote side:
655 src: 'a' -> dst: 'b'
647 src: 'a' -> dst: 'b' *
656 checking for directory renames
648 checking for directory renames
657 resolving manifests
649 resolving manifests
658 branchmerge: True, force: False, partial: False
650 branchmerge: True, force: False, partial: False
659 ancestor: 924404dff337, local: 0b76e65c8289+, remote: 4ce40f5aca24
651 ancestor: 924404dff337, local: 0b76e65c8289+, remote: 4ce40f5aca24
660 preserving b for resolve of b
652 preserving b for resolve of b
661 preserving rev for resolve of rev
653 preserving rev for resolve of rev
662 starting 4 threads for background file closing (?)
654 starting 4 threads for background file closing (?)
663 b: both created -> m (premerge)
655 b: both renamed from a -> m (premerge)
664 picked tool '* ../merge' for b (binary False symlink False changedelete False) (glob)
656 picked tool '* ../merge' for b (binary False symlink False changedelete False) (glob)
665 merging b
657 merging b
666 my b@0b76e65c8289+ other b@4ce40f5aca24 ancestor b@000000000000
658 my b@0b76e65c8289+ other b@4ce40f5aca24 ancestor a@924404dff337
659 premerge successful
667 rev: versions differ -> m (premerge)
660 rev: versions differ -> m (premerge)
668 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
661 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
669 merging rev
662 merging rev
670 my rev@0b76e65c8289+ other rev@4ce40f5aca24 ancestor rev@924404dff337
663 my rev@0b76e65c8289+ other rev@4ce40f5aca24 ancestor rev@924404dff337
671 b: both created -> m (merge)
672 picked tool '* ../merge' for b (binary False symlink False changedelete False) (glob)
673 my b@0b76e65c8289+ other b@4ce40f5aca24 ancestor b@000000000000
674 launching merge tool: * ../merge *$TESTTMP/t/t/b* * * (glob)
675 merge tool returned: 0
676 rev: versions differ -> m (merge)
664 rev: versions differ -> m (merge)
677 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
665 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
678 my rev@0b76e65c8289+ other rev@4ce40f5aca24 ancestor rev@924404dff337
666 my rev@0b76e65c8289+ other rev@4ce40f5aca24 ancestor rev@924404dff337
679 launching merge tool: * ../merge *$TESTTMP/t/t/rev* * * (glob)
667 launching merge tool: * ../merge *$TESTTMP/t/t/rev* * * (glob)
680 merge tool returned: 0
668 merge tool returned: 0
681 0 files updated, 2 files merged, 0 files removed, 0 files unresolved
669 0 files updated, 2 files merged, 0 files removed, 0 files unresolved
682 (branch merge, don't forget to commit)
670 (branch merge, don't forget to commit)
683 --------------
671 --------------
684 M b
672 M b
685 C a
673 C a
686 --------------
674 --------------
687
675
688 $ tm "nm a b" "up a b" " " "18 merge b no ancestor"
676 $ tm "nm a b" "up a b" " " "18 merge b no ancestor"
689 created new head
677 created new head
690 --------------
678 --------------
691 test L:nm a b R:up a b W: - 18 merge b no ancestor
679 test L:nm a b R:up a b W: - 18 merge b no ancestor
692 --------------
680 --------------
693 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
681 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
694 on local side:
682 on local side:
695 src: 'a' -> dst: 'b'
683 src: 'a' -> dst: 'b' *
696 checking for directory renames
684 checking for directory renames
697 resolving manifests
685 resolving manifests
698 branchmerge: True, force: False, partial: False
686 branchmerge: True, force: False, partial: False
699 ancestor: 924404dff337, local: 02963e448370+, remote: 8dbce441892a
687 ancestor: 924404dff337, local: 02963e448370+, remote: 8dbce441892a
700 preserving b for resolve of b
688 preserving b for resolve of b
701 preserving rev for resolve of rev
689 preserving rev for resolve of rev
702 starting 4 threads for background file closing (?)
690 starting 4 threads for background file closing (?)
703 a: prompt deleted/changed -> m (premerge)
691 b: both renamed from a -> m (premerge)
704 picked tool ':prompt' for a (binary False symlink False changedelete True)
705 file 'a' was deleted in local [working copy] but was modified in other [merge rev].
706 You can use (c)hanged version, leave (d)eleted, or leave (u)nresolved.
707 What do you want to do? u
708 b: both created -> m (premerge)
709 picked tool '* ../merge' for b (binary False symlink False changedelete False) (glob)
692 picked tool '* ../merge' for b (binary False symlink False changedelete False) (glob)
710 merging b
693 merging b
711 my b@02963e448370+ other b@8dbce441892a ancestor b@000000000000
694 my b@02963e448370+ other b@8dbce441892a ancestor a@924404dff337
695 premerge successful
712 rev: versions differ -> m (premerge)
696 rev: versions differ -> m (premerge)
713 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
697 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
714 merging rev
698 merging rev
715 my rev@02963e448370+ other rev@8dbce441892a ancestor rev@924404dff337
699 my rev@02963e448370+ other rev@8dbce441892a ancestor rev@924404dff337
716 b: both created -> m (merge)
717 picked tool '* ../merge' for b (binary False symlink False changedelete False) (glob)
718 my b@02963e448370+ other b@8dbce441892a ancestor b@000000000000
719 launching merge tool: * ../merge *$TESTTMP/t/t/b* * * (glob)
720 merge tool returned: 0
721 rev: versions differ -> m (merge)
700 rev: versions differ -> m (merge)
722 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
701 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
723 my rev@02963e448370+ other rev@8dbce441892a ancestor rev@924404dff337
702 my rev@02963e448370+ other rev@8dbce441892a ancestor rev@924404dff337
724 launching merge tool: * ../merge *$TESTTMP/t/t/rev* * * (glob)
703 launching merge tool: * ../merge *$TESTTMP/t/t/rev* * * (glob)
725 merge tool returned: 0
704 merge tool returned: 0
726 0 files updated, 2 files merged, 0 files removed, 1 files unresolved
705 0 files updated, 2 files merged, 0 files removed, 0 files unresolved
727 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
706 (branch merge, don't forget to commit)
728 --------------
707 --------------
729 M a
730 M b
708 M b
731 abort: unresolved merge conflicts (see 'hg help resolve')
732 --------------
709 --------------
733
710
734 $ tm "up a b" "nm a b" " " "19 merge b no ancestor, prompt remove a"
711 $ tm "up a b" "nm a b" " " "19 merge b no ancestor, prompt remove a"
735 created new head
712 created new head
736 --------------
713 --------------
737 test L:up a b R:nm a b W: - 19 merge b no ancestor, prompt remove a
714 test L:up a b R:nm a b W: - 19 merge b no ancestor, prompt remove a
738 --------------
715 --------------
739 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
716 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
740 on remote side:
717 on remote side:
741 src: 'a' -> dst: 'b'
718 src: 'a' -> dst: 'b' *
742 checking for directory renames
719 checking for directory renames
743 resolving manifests
720 resolving manifests
744 branchmerge: True, force: False, partial: False
721 branchmerge: True, force: False, partial: False
745 ancestor: 924404dff337, local: 0b76e65c8289+, remote: bdb19105162a
722 ancestor: 924404dff337, local: 0b76e65c8289+, remote: bdb19105162a
746 preserving a for resolve of a
747 preserving b for resolve of b
723 preserving b for resolve of b
748 preserving rev for resolve of rev
724 preserving rev for resolve of rev
725 b: both renamed from a -> m (premerge)
749 starting 4 threads for background file closing (?)
726 starting 4 threads for background file closing (?)
750 a: prompt changed/deleted -> m (premerge)
751 picked tool ':prompt' for a (binary False symlink False changedelete True)
752 file 'a' was deleted in other [merge rev] but was modified in local [working copy].
753 You can use (c)hanged version, (d)elete, or leave (u)nresolved.
754 What do you want to do? u
755 b: both created -> m (premerge)
756 picked tool '* ../merge' for b (binary False symlink False changedelete False) (glob)
727 picked tool '* ../merge' for b (binary False symlink False changedelete False) (glob)
757 merging b
728 merging b
758 my b@0b76e65c8289+ other b@bdb19105162a ancestor b@000000000000
729 my b@0b76e65c8289+ other b@bdb19105162a ancestor a@924404dff337
730 premerge successful
759 rev: versions differ -> m (premerge)
731 rev: versions differ -> m (premerge)
760 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
732 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
761 merging rev
733 merging rev
762 my rev@0b76e65c8289+ other rev@bdb19105162a ancestor rev@924404dff337
734 my rev@0b76e65c8289+ other rev@bdb19105162a ancestor rev@924404dff337
763 b: both created -> m (merge)
764 picked tool '* ../merge' for b (binary False symlink False changedelete False) (glob)
765 my b@0b76e65c8289+ other b@bdb19105162a ancestor b@000000000000
766 launching merge tool: * ../merge *$TESTTMP/t/t/b* * * (glob)
767 merge tool returned: 0
768 rev: versions differ -> m (merge)
735 rev: versions differ -> m (merge)
769 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
736 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
770 my rev@0b76e65c8289+ other rev@bdb19105162a ancestor rev@924404dff337
737 my rev@0b76e65c8289+ other rev@bdb19105162a ancestor rev@924404dff337
771 launching merge tool: * ../merge *$TESTTMP/t/t/rev* * * (glob)
738 launching merge tool: * ../merge *$TESTTMP/t/t/rev* * * (glob)
772 merge tool returned: 0
739 merge tool returned: 0
773 0 files updated, 2 files merged, 0 files removed, 1 files unresolved
740 0 files updated, 2 files merged, 0 files removed, 0 files unresolved
774 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
741 (branch merge, don't forget to commit)
775 --------------
742 --------------
776 M b
743 M b
777 C a
744 C a
778 abort: unresolved merge conflicts (see 'hg help resolve')
779 --------------
745 --------------
780
746
781 $ tm "up a " "um a b" " " "20 merge a and b to b, remove a"
747 $ tm "up a " "um a b" " " "20 merge a and b to b, remove a"
782 created new head
748 created new head
783 --------------
749 --------------
784 test L:up a R:um a b W: - 20 merge a and b to b, remove a
750 test L:up a R:um a b W: - 20 merge a and b to b, remove a
785 --------------
751 --------------
786 unmatched files in other:
752 unmatched files in other:
787 b
753 b
788 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
754 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
789 on remote side:
755 on remote side:
790 src: 'a' -> dst: 'b' *
756 src: 'a' -> dst: 'b' *
791 checking for directory renames
757 checking for directory renames
792 resolving manifests
758 resolving manifests
793 branchmerge: True, force: False, partial: False
759 branchmerge: True, force: False, partial: False
794 ancestor: 924404dff337, local: e300d1c794ec+, remote: 49b6d8032493
760 ancestor: 924404dff337, local: e300d1c794ec+, remote: 49b6d8032493
795 preserving a for resolve of b
761 preserving a for resolve of b
796 preserving rev for resolve of rev
762 preserving rev for resolve of rev
797 removing a
763 removing a
798 starting 4 threads for background file closing (?)
764 starting 4 threads for background file closing (?)
799 b: remote moved from a -> m (premerge)
765 b: remote moved from a -> m (premerge)
800 picked tool '* ../merge' for b (binary False symlink False changedelete False) (glob)
766 picked tool '* ../merge' for b (binary False symlink False changedelete False) (glob)
801 merging a and b to b
767 merging a and b to b
802 my b@e300d1c794ec+ other b@49b6d8032493 ancestor a@924404dff337
768 my b@e300d1c794ec+ other b@49b6d8032493 ancestor a@924404dff337
803 rev: versions differ -> m (premerge)
769 rev: versions differ -> m (premerge)
804 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
770 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
805 merging rev
771 merging rev
806 my rev@e300d1c794ec+ other rev@49b6d8032493 ancestor rev@924404dff337
772 my rev@e300d1c794ec+ other rev@49b6d8032493 ancestor rev@924404dff337
807 b: remote moved from a -> m (merge)
773 b: remote moved from a -> m (merge)
808 picked tool '* ../merge' for b (binary False symlink False changedelete False) (glob)
774 picked tool '* ../merge' for b (binary False symlink False changedelete False) (glob)
809 my b@e300d1c794ec+ other b@49b6d8032493 ancestor a@924404dff337
775 my b@e300d1c794ec+ other b@49b6d8032493 ancestor a@924404dff337
810 launching merge tool: * ../merge *$TESTTMP/t/t/b* * * (glob)
776 launching merge tool: * ../merge *$TESTTMP/t/t/b* * * (glob)
811 merge tool returned: 0
777 merge tool returned: 0
812 rev: versions differ -> m (merge)
778 rev: versions differ -> m (merge)
813 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
779 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
814 my rev@e300d1c794ec+ other rev@49b6d8032493 ancestor rev@924404dff337
780 my rev@e300d1c794ec+ other rev@49b6d8032493 ancestor rev@924404dff337
815 launching merge tool: * ../merge *$TESTTMP/t/t/rev* * * (glob)
781 launching merge tool: * ../merge *$TESTTMP/t/t/rev* * * (glob)
816 merge tool returned: 0
782 merge tool returned: 0
817 0 files updated, 2 files merged, 0 files removed, 0 files unresolved
783 0 files updated, 2 files merged, 0 files removed, 0 files unresolved
818 (branch merge, don't forget to commit)
784 (branch merge, don't forget to commit)
819 --------------
785 --------------
820 M b
786 M b
821 a
787 a
822 --------------
788 --------------
823
789
824 $ tm "um a b" "up a " " " "21 merge a and b to b"
790 $ tm "um a b" "up a " " " "21 merge a and b to b"
825 created new head
791 created new head
826 --------------
792 --------------
827 test L:um a b R:up a W: - 21 merge a and b to b
793 test L:um a b R:up a W: - 21 merge a and b to b
828 --------------
794 --------------
829 unmatched files in local:
795 unmatched files in local:
830 b
796 b
831 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
797 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
832 on local side:
798 on local side:
833 src: 'a' -> dst: 'b' *
799 src: 'a' -> dst: 'b' *
834 checking for directory renames
800 checking for directory renames
835 resolving manifests
801 resolving manifests
836 branchmerge: True, force: False, partial: False
802 branchmerge: True, force: False, partial: False
837 ancestor: 924404dff337, local: 62e7bf090eba+, remote: f4db7e329e71
803 ancestor: 924404dff337, local: 62e7bf090eba+, remote: f4db7e329e71
838 preserving b for resolve of b
804 preserving b for resolve of b
839 preserving rev for resolve of rev
805 preserving rev for resolve of rev
840 starting 4 threads for background file closing (?)
806 starting 4 threads for background file closing (?)
841 b: local copied/moved from a -> m (premerge)
807 b: local copied/moved from a -> m (premerge)
842 picked tool '* ../merge' for b (binary False symlink False changedelete False) (glob)
808 picked tool '* ../merge' for b (binary False symlink False changedelete False) (glob)
843 merging b and a to b
809 merging b and a to b
844 my b@62e7bf090eba+ other a@f4db7e329e71 ancestor a@924404dff337
810 my b@62e7bf090eba+ other a@f4db7e329e71 ancestor a@924404dff337
845 rev: versions differ -> m (premerge)
811 rev: versions differ -> m (premerge)
846 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
812 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
847 merging rev
813 merging rev
848 my rev@62e7bf090eba+ other rev@f4db7e329e71 ancestor rev@924404dff337
814 my rev@62e7bf090eba+ other rev@f4db7e329e71 ancestor rev@924404dff337
849 b: local copied/moved from a -> m (merge)
815 b: local copied/moved from a -> m (merge)
850 picked tool '* ../merge' for b (binary False symlink False changedelete False) (glob)
816 picked tool '* ../merge' for b (binary False symlink False changedelete False) (glob)
851 my b@62e7bf090eba+ other a@f4db7e329e71 ancestor a@924404dff337
817 my b@62e7bf090eba+ other a@f4db7e329e71 ancestor a@924404dff337
852 launching merge tool: * ../merge *$TESTTMP/t/t/b* * * (glob)
818 launching merge tool: * ../merge *$TESTTMP/t/t/b* * * (glob)
853 merge tool returned: 0
819 merge tool returned: 0
854 rev: versions differ -> m (merge)
820 rev: versions differ -> m (merge)
855 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
821 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
856 my rev@62e7bf090eba+ other rev@f4db7e329e71 ancestor rev@924404dff337
822 my rev@62e7bf090eba+ other rev@f4db7e329e71 ancestor rev@924404dff337
857 launching merge tool: * ../merge *$TESTTMP/t/t/rev* * * (glob)
823 launching merge tool: * ../merge *$TESTTMP/t/t/rev* * * (glob)
858 merge tool returned: 0
824 merge tool returned: 0
859 0 files updated, 2 files merged, 0 files removed, 0 files unresolved
825 0 files updated, 2 files merged, 0 files removed, 0 files unresolved
860 (branch merge, don't forget to commit)
826 (branch merge, don't forget to commit)
861 --------------
827 --------------
862 M b
828 M b
863 a
829 a
864 --------------
830 --------------
865
831
866
832
867 m "nm a b" "um x a" " " "22 get a, keep b"
833 m "nm a b" "um x a" " " "22 get a, keep b"
868
834
869 $ tm "nm a b" "up a c" " " "23 get c, keep b"
835 $ tm "nm a b" "up a c" " " "23 get c, keep b"
870 created new head
836 created new head
871 --------------
837 --------------
872 test L:nm a b R:up a c W: - 23 get c, keep b
838 test L:nm a b R:up a c W: - 23 get c, keep b
873 --------------
839 --------------
874 unmatched files in local:
840 unmatched files in local:
875 b
841 b
876 unmatched files in other:
842 unmatched files in other:
877 c
843 c
878 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
844 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
879 on local side:
845 on local side:
880 src: 'a' -> dst: 'b' *
846 src: 'a' -> dst: 'b' *
881 checking for directory renames
847 checking for directory renames
882 resolving manifests
848 resolving manifests
883 branchmerge: True, force: False, partial: False
849 branchmerge: True, force: False, partial: False
884 ancestor: 924404dff337, local: 02963e448370+, remote: 2b958612230f
850 ancestor: 924404dff337, local: 02963e448370+, remote: 2b958612230f
885 preserving b for resolve of b
851 preserving b for resolve of b
886 preserving rev for resolve of rev
852 preserving rev for resolve of rev
887 c: remote created -> g
853 c: remote created -> g
888 getting c
854 getting c
889 b: local copied/moved from a -> m (premerge)
855 b: local copied/moved from a -> m (premerge)
890 picked tool '* ../merge' for b (binary False symlink False changedelete False) (glob)
856 picked tool '* ../merge' for b (binary False symlink False changedelete False) (glob)
891 merging b and a to b
857 merging b and a to b
892 my b@02963e448370+ other a@2b958612230f ancestor a@924404dff337
858 my b@02963e448370+ other a@2b958612230f ancestor a@924404dff337
893 premerge successful
859 premerge successful
894 rev: versions differ -> m (premerge)
860 rev: versions differ -> m (premerge)
895 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
861 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
896 merging rev
862 merging rev
897 my rev@02963e448370+ other rev@2b958612230f ancestor rev@924404dff337
863 my rev@02963e448370+ other rev@2b958612230f ancestor rev@924404dff337
898 rev: versions differ -> m (merge)
864 rev: versions differ -> m (merge)
899 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
865 picked tool '* ../merge' for rev (binary False symlink False changedelete False) (glob)
900 my rev@02963e448370+ other rev@2b958612230f ancestor rev@924404dff337
866 my rev@02963e448370+ other rev@2b958612230f ancestor rev@924404dff337
901 launching merge tool: * ../merge *$TESTTMP/t/t/rev* * * (glob)
867 launching merge tool: * ../merge *$TESTTMP/t/t/rev* * * (glob)
902 merge tool returned: 0
868 merge tool returned: 0
903 1 files updated, 2 files merged, 0 files removed, 0 files unresolved
869 1 files updated, 2 files merged, 0 files removed, 0 files unresolved
904 (branch merge, don't forget to commit)
870 (branch merge, don't forget to commit)
905 --------------
871 --------------
906 M b
872 M b
907 a
873 a
908 M c
874 M c
909 --------------
875 --------------
910
876
911
877
912 $ cd ..
878 $ cd ..
913
879
914
880
915 Systematic and terse testing of merge merges and ancestor calculation:
881 Systematic and terse testing of merge merges and ancestor calculation:
916
882
917 Expected result:
883 Expected result:
918
884
919 \ a m1 m2 dst
885 \ a m1 m2 dst
920 0 - f f f "versions differ"
886 0 - f f f "versions differ"
921 1 f g g g "versions differ"
887 1 f g g g "versions differ"
922 2 f f f f "versions differ"
888 2 f f f f "versions differ"
923 3 f f g f+g "remote copied to " + f
889 3 f f g f+g "remote copied to " + f
924 4 f f g g "remote moved to " + f
890 4 f f g g "remote moved to " + f
925 5 f g f f+g "local copied to " + f2
891 5 f g f f+g "local copied to " + f2
926 6 f g f g "local moved to " + f2
892 6 f g f g "local moved to " + f2
927 7 - (f) f f "remote differs from untracked local"
893 7 - (f) f f "remote differs from untracked local"
928 8 f (f) f f "remote differs from untracked local"
894 8 f (f) f f "remote differs from untracked local"
929
895
930 $ hg init ancestortest
896 $ hg init ancestortest
931 $ cd ancestortest
897 $ cd ancestortest
932 $ for x in 1 2 3 4 5 6 8; do mkdir $x; echo a > $x/f; done
898 $ for x in 1 2 3 4 5 6 8; do mkdir $x; echo a > $x/f; done
933 $ hg ci -Aqm "a"
899 $ hg ci -Aqm "a"
934 $ mkdir 0
900 $ mkdir 0
935 $ touch 0/f
901 $ touch 0/f
936 $ hg mv 1/f 1/g
902 $ hg mv 1/f 1/g
937 $ hg cp 5/f 5/g
903 $ hg cp 5/f 5/g
938 $ hg mv 6/f 6/g
904 $ hg mv 6/f 6/g
939 $ hg rm 8/f
905 $ hg rm 8/f
940 $ for x in */*; do echo m1 > $x; done
906 $ for x in */*; do echo m1 > $x; done
941 $ hg ci -Aqm "m1"
907 $ hg ci -Aqm "m1"
942 $ hg up -qr0
908 $ hg up -qr0
943 $ mkdir 0 7
909 $ mkdir 0 7
944 $ touch 0/f 7/f
910 $ touch 0/f 7/f
945 $ hg mv 1/f 1/g
911 $ hg mv 1/f 1/g
946 $ hg cp 3/f 3/g
912 $ hg cp 3/f 3/g
947 $ hg mv 4/f 4/g
913 $ hg mv 4/f 4/g
948 $ for x in */*; do echo m2 > $x; done
914 $ for x in */*; do echo m2 > $x; done
949 $ hg ci -Aqm "m2"
915 $ hg ci -Aqm "m2"
950 $ hg up -qr1
916 $ hg up -qr1
951 $ mkdir 7 8
917 $ mkdir 7 8
952 $ echo m > 7/f
918 $ echo m > 7/f
953 $ echo m > 8/f
919 $ echo m > 8/f
954 $ hg merge -f --tool internal:dump -v --debug -r2 | sed '/^resolving manifests/,$d' 2> /dev/null
920 $ hg merge -f --tool internal:dump -v --debug -r2 | sed '/^resolving manifests/,$d' 2> /dev/null
955 unmatched files in local:
921 unmatched files in local:
956 5/g
922 5/g
957 6/g
923 6/g
958 unmatched files in other:
924 unmatched files in other:
959 3/g
925 3/g
960 4/g
926 4/g
961 7/f
927 7/f
962 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
928 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
963 on local side:
929 on local side:
964 src: '1/f' -> dst: '1/g' *
930 src: '1/f' -> dst: '1/g' *
965 src: '5/f' -> dst: '5/g' *
931 src: '5/f' -> dst: '5/g' *
966 src: '6/f' -> dst: '6/g' *
932 src: '6/f' -> dst: '6/g' *
967 on remote side:
933 on remote side:
968 src: '1/f' -> dst: '1/g' *
934 src: '1/f' -> dst: '1/g' *
969 src: '3/f' -> dst: '3/g' *
935 src: '3/f' -> dst: '3/g' *
970 src: '4/f' -> dst: '4/g' *
936 src: '4/f' -> dst: '4/g' *
971 checking for directory renames
937 checking for directory renames
972 $ hg mani
938 $ hg mani
973 0/f
939 0/f
974 1/g
940 1/g
975 2/f
941 2/f
976 3/f
942 3/f
977 4/f
943 4/f
978 5/f
944 5/f
979 5/g
945 5/g
980 6/g
946 6/g
981 $ for f in */*; do echo $f:; cat $f; done
947 $ for f in */*; do echo $f:; cat $f; done
982 0/f:
948 0/f:
983 m1
949 m1
984 0/f.base:
950 0/f.base:
985 0/f.local:
951 0/f.local:
986 m1
952 m1
987 0/f.orig:
953 0/f.orig:
988 m1
954 m1
989 0/f.other:
955 0/f.other:
990 m2
956 m2
991 1/g:
957 1/g:
992 m1
958 m1
993 1/g.base:
959 1/g.base:
994 a
960 a
995 1/g.local:
961 1/g.local:
996 m1
962 m1
997 1/g.orig:
963 1/g.orig:
998 m1
964 m1
999 1/g.other:
965 1/g.other:
1000 m2
966 m2
1001 2/f:
967 2/f:
1002 m1
968 m1
1003 2/f.base:
969 2/f.base:
1004 a
970 a
1005 2/f.local:
971 2/f.local:
1006 m1
972 m1
1007 2/f.orig:
973 2/f.orig:
1008 m1
974 m1
1009 2/f.other:
975 2/f.other:
1010 m2
976 m2
1011 3/f:
977 3/f:
1012 m1
978 m1
1013 3/f.base:
979 3/f.base:
1014 a
980 a
1015 3/f.local:
981 3/f.local:
1016 m1
982 m1
1017 3/f.orig:
983 3/f.orig:
1018 m1
984 m1
1019 3/f.other:
985 3/f.other:
1020 m2
986 m2
1021 3/g:
987 3/g:
1022 m1
988 m1
1023 3/g.base:
989 3/g.base:
1024 a
990 a
1025 3/g.local:
991 3/g.local:
1026 m1
992 m1
1027 3/g.orig:
993 3/g.orig:
1028 m1
994 m1
1029 3/g.other:
995 3/g.other:
1030 m2
996 m2
1031 4/g:
997 4/g:
1032 m1
998 m1
1033 4/g.base:
999 4/g.base:
1034 a
1000 a
1035 4/g.local:
1001 4/g.local:
1036 m1
1002 m1
1037 4/g.orig:
1003 4/g.orig:
1038 m1
1004 m1
1039 4/g.other:
1005 4/g.other:
1040 m2
1006 m2
1041 5/f:
1007 5/f:
1042 m1
1008 m1
1043 5/f.base:
1009 5/f.base:
1044 a
1010 a
1045 5/f.local:
1011 5/f.local:
1046 m1
1012 m1
1047 5/f.orig:
1013 5/f.orig:
1048 m1
1014 m1
1049 5/f.other:
1015 5/f.other:
1050 m2
1016 m2
1051 5/g:
1017 5/g:
1052 m1
1018 m1
1053 5/g.base:
1019 5/g.base:
1054 a
1020 a
1055 5/g.local:
1021 5/g.local:
1056 m1
1022 m1
1057 5/g.orig:
1023 5/g.orig:
1058 m1
1024 m1
1059 5/g.other:
1025 5/g.other:
1060 m2
1026 m2
1061 6/g:
1027 6/g:
1062 m1
1028 m1
1063 6/g.base:
1029 6/g.base:
1064 a
1030 a
1065 6/g.local:
1031 6/g.local:
1066 m1
1032 m1
1067 6/g.orig:
1033 6/g.orig:
1068 m1
1034 m1
1069 6/g.other:
1035 6/g.other:
1070 m2
1036 m2
1071 7/f:
1037 7/f:
1072 m
1038 m
1073 7/f.base:
1039 7/f.base:
1074 7/f.local:
1040 7/f.local:
1075 m
1041 m
1076 7/f.orig:
1042 7/f.orig:
1077 m
1043 m
1078 7/f.other:
1044 7/f.other:
1079 m2
1045 m2
1080 8/f:
1046 8/f:
1081 m2
1047 m2
1082 $ cd ..
1048 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now