##// END OF EJS Templates
copies: fix crash when copy source is not in graft base...
Martin von Zweigbergk -
r44691:d0c3eead default
parent child Browse files
Show More
@@ -1,1167 +1,1172 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:
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
533 # destination doesn't exist in y.
534 pass
530 elif m2[src] != mb[src]:
535 elif m2[src] != mb[src]:
531 if not _related(c2[src], base[src]):
536 if not _related(c2[src], base[src]):
532 return
537 return
533 # modified on side 2
538 # modified on side 2
534 for dst in dsts1:
539 for dst in dsts1:
535 if dst not in m2:
540 if dst not in m2:
536 # dst not added on side 2 (handle as regular
541 # dst not added on side 2 (handle as regular
537 # "both created" case in manifestmerge otherwise)
542 # "both created" case in manifestmerge otherwise)
538 copy[dst] = src
543 copy[dst] = src
539
544
540
545
541 class branch_copies(object):
546 class branch_copies(object):
542 """Information about copies made on one side of a merge/graft.
547 """Information about copies made on one side of a merge/graft.
543
548
544 "copy" is a mapping from destination name -> source name,
549 "copy" is a mapping from destination name -> source name,
545 where source is in c1 and destination is in c2 or vice-versa.
550 where source is in c1 and destination is in c2 or vice-versa.
546
551
547 "movewithdir" is a mapping from source name -> destination name,
552 "movewithdir" is a mapping from source name -> destination name,
548 where the file at source present in one context but not the other
553 where the file at source present in one context but not the other
549 needs to be moved to destination by the merge process, because the
554 needs to be moved to destination by the merge process, because the
550 other context moved the directory it is in.
555 other context moved the directory it is in.
551
556
552 "renamedelete" is a mapping of source name -> list of destination
557 "renamedelete" is a mapping of source name -> list of destination
553 names for files deleted in c1 that were renamed in c2 or vice-versa.
558 names for files deleted in c1 that were renamed in c2 or vice-versa.
554
559
555 "dirmove" is a mapping of detected source dir -> destination dir renames.
560 "dirmove" is a mapping of detected source dir -> destination dir renames.
556 This is needed for handling changes to new files previously grafted into
561 This is needed for handling changes to new files previously grafted into
557 renamed directories.
562 renamed directories.
558 """
563 """
559
564
560 def __init__(
565 def __init__(
561 self, copy=None, renamedelete=None, dirmove=None, movewithdir=None
566 self, copy=None, renamedelete=None, dirmove=None, movewithdir=None
562 ):
567 ):
563 self.copy = {} if copy is None else copy
568 self.copy = {} if copy is None else copy
564 self.renamedelete = {} if renamedelete is None else renamedelete
569 self.renamedelete = {} if renamedelete is None else renamedelete
565 self.dirmove = {} if dirmove is None else dirmove
570 self.dirmove = {} if dirmove is None else dirmove
566 self.movewithdir = {} if movewithdir is None else movewithdir
571 self.movewithdir = {} if movewithdir is None else movewithdir
567
572
568
573
569 def _fullcopytracing(repo, c1, c2, base):
574 def _fullcopytracing(repo, c1, c2, base):
570 """ The full copytracing algorithm which finds all the new files that were
575 """ The full copytracing algorithm which finds all the new files that were
571 added from merge base up to the top commit and for each file it checks if
576 added from merge base up to the top commit and for each file it checks if
572 this file was copied from another file.
577 this file was copied from another file.
573
578
574 This is pretty slow when a lot of changesets are involved but will track all
579 This is pretty slow when a lot of changesets are involved but will track all
575 the copies.
580 the copies.
576 """
581 """
577 m1 = c1.manifest()
582 m1 = c1.manifest()
578 m2 = c2.manifest()
583 m2 = c2.manifest()
579 mb = base.manifest()
584 mb = base.manifest()
580
585
581 copies1 = pathcopies(base, c1)
586 copies1 = pathcopies(base, c1)
582 copies2 = pathcopies(base, c2)
587 copies2 = pathcopies(base, c2)
583
588
584 if not (copies1 or copies2):
589 if not (copies1 or copies2):
585 return branch_copies(), branch_copies(), {}
590 return branch_copies(), branch_copies(), {}
586
591
587 inversecopies1 = {}
592 inversecopies1 = {}
588 inversecopies2 = {}
593 inversecopies2 = {}
589 for dst, src in copies1.items():
594 for dst, src in copies1.items():
590 inversecopies1.setdefault(src, []).append(dst)
595 inversecopies1.setdefault(src, []).append(dst)
591 for dst, src in copies2.items():
596 for dst, src in copies2.items():
592 inversecopies2.setdefault(src, []).append(dst)
597 inversecopies2.setdefault(src, []).append(dst)
593
598
594 copy1 = {}
599 copy1 = {}
595 copy2 = {}
600 copy2 = {}
596 diverge = {}
601 diverge = {}
597 renamedelete1 = {}
602 renamedelete1 = {}
598 renamedelete2 = {}
603 renamedelete2 = {}
599 allsources = set(inversecopies1) | set(inversecopies2)
604 allsources = set(inversecopies1) | set(inversecopies2)
600 for src in allsources:
605 for src in allsources:
601 dsts1 = inversecopies1.get(src)
606 dsts1 = inversecopies1.get(src)
602 dsts2 = inversecopies2.get(src)
607 dsts2 = inversecopies2.get(src)
603 if dsts1 and dsts2:
608 if dsts1 and dsts2:
604 # copied/renamed on both sides
609 # copied/renamed on both sides
605 if src not in m1 and src not in m2:
610 if src not in m1 and src not in m2:
606 # renamed on both sides
611 # renamed on both sides
607 dsts1 = set(dsts1)
612 dsts1 = set(dsts1)
608 dsts2 = set(dsts2)
613 dsts2 = set(dsts2)
609 # If there's some overlap in the rename destinations, we
614 # If there's some overlap in the rename destinations, we
610 # consider it not divergent. For example, if side 1 copies 'a'
615 # consider it not divergent. For example, if side 1 copies 'a'
611 # to 'b' and 'c' and deletes 'a', and side 2 copies 'a' to 'c'
616 # to 'b' and 'c' and deletes 'a', and side 2 copies 'a' to 'c'
612 # and 'd' and deletes 'a'.
617 # and 'd' and deletes 'a'.
613 if dsts1 & dsts2:
618 if dsts1 & dsts2:
614 for dst in dsts1 & dsts2:
619 for dst in dsts1 & dsts2:
615 copy1[dst] = src
620 copy1[dst] = src
616 copy2[dst] = src
621 copy2[dst] = src
617 else:
622 else:
618 diverge[src] = sorted(dsts1 | dsts2)
623 diverge[src] = sorted(dsts1 | dsts2)
619 elif src in m1 and src in m2:
624 elif src in m1 and src in m2:
620 # copied on both sides
625 # copied on both sides
621 dsts1 = set(dsts1)
626 dsts1 = set(dsts1)
622 dsts2 = set(dsts2)
627 dsts2 = set(dsts2)
623 for dst in dsts1 & dsts2:
628 for dst in dsts1 & dsts2:
624 copy1[dst] = src
629 copy1[dst] = src
625 copy2[dst] = src
630 copy2[dst] = src
626 # TODO: Handle cases where it was renamed on one side and copied
631 # TODO: Handle cases where it was renamed on one side and copied
627 # on the other side
632 # on the other side
628 elif dsts1:
633 elif dsts1:
629 # copied/renamed only on side 1
634 # copied/renamed only on side 1
630 _checksinglesidecopies(
635 _checksinglesidecopies(
631 src, dsts1, m1, m2, mb, c2, base, copy1, renamedelete1
636 src, dsts1, m1, m2, mb, c2, base, copy1, renamedelete1
632 )
637 )
633 elif dsts2:
638 elif dsts2:
634 # copied/renamed only on side 2
639 # copied/renamed only on side 2
635 _checksinglesidecopies(
640 _checksinglesidecopies(
636 src, dsts2, m2, m1, mb, c1, base, copy2, renamedelete2
641 src, dsts2, m2, m1, mb, c1, base, copy2, renamedelete2
637 )
642 )
638
643
639 # find interesting file sets from manifests
644 # find interesting file sets from manifests
640 addedinm1 = m1.filesnotin(mb, repo.narrowmatch())
645 addedinm1 = m1.filesnotin(mb, repo.narrowmatch())
641 addedinm2 = m2.filesnotin(mb, repo.narrowmatch())
646 addedinm2 = m2.filesnotin(mb, repo.narrowmatch())
642 u1 = sorted(addedinm1 - addedinm2)
647 u1 = sorted(addedinm1 - addedinm2)
643 u2 = sorted(addedinm2 - addedinm1)
648 u2 = sorted(addedinm2 - addedinm1)
644
649
645 header = b" unmatched files in %s"
650 header = b" unmatched files in %s"
646 if u1:
651 if u1:
647 repo.ui.debug(b"%s:\n %s\n" % (header % b'local', b"\n ".join(u1)))
652 repo.ui.debug(b"%s:\n %s\n" % (header % b'local', b"\n ".join(u1)))
648 if u2:
653 if u2:
649 repo.ui.debug(b"%s:\n %s\n" % (header % b'other', b"\n ".join(u2)))
654 repo.ui.debug(b"%s:\n %s\n" % (header % b'other', b"\n ".join(u2)))
650
655
651 if repo.ui.debugflag:
656 if repo.ui.debugflag:
652 renamedeleteset = set()
657 renamedeleteset = set()
653 divergeset = set()
658 divergeset = set()
654 for dsts in diverge.values():
659 for dsts in diverge.values():
655 divergeset.update(dsts)
660 divergeset.update(dsts)
656 for dsts in renamedelete1.values():
661 for dsts in renamedelete1.values():
657 renamedeleteset.update(dsts)
662 renamedeleteset.update(dsts)
658 for dsts in renamedelete2.values():
663 for dsts in renamedelete2.values():
659 renamedeleteset.update(dsts)
664 renamedeleteset.update(dsts)
660
665
661 repo.ui.debug(
666 repo.ui.debug(
662 b" all copies found (* = to merge, ! = divergent, "
667 b" all copies found (* = to merge, ! = divergent, "
663 b"% = renamed and deleted):\n"
668 b"% = renamed and deleted):\n"
664 )
669 )
665 for side, copies in ((b"local", copies1), (b"remote", copies2)):
670 for side, copies in ((b"local", copies1), (b"remote", copies2)):
666 if not copies:
671 if not copies:
667 continue
672 continue
668 repo.ui.debug(b" on %s side:\n" % side)
673 repo.ui.debug(b" on %s side:\n" % side)
669 for f in sorted(copies):
674 for f in sorted(copies):
670 note = b""
675 note = b""
671 if f in copy1 or f in copy2:
676 if f in copy1 or f in copy2:
672 note += b"*"
677 note += b"*"
673 if f in divergeset:
678 if f in divergeset:
674 note += b"!"
679 note += b"!"
675 if f in renamedeleteset:
680 if f in renamedeleteset:
676 note += b"%"
681 note += b"%"
677 repo.ui.debug(
682 repo.ui.debug(
678 b" src: '%s' -> dst: '%s' %s\n" % (copies[f], f, note)
683 b" src: '%s' -> dst: '%s' %s\n" % (copies[f], f, note)
679 )
684 )
680 del renamedeleteset
685 del renamedeleteset
681 del divergeset
686 del divergeset
682
687
683 repo.ui.debug(b" checking for directory renames\n")
688 repo.ui.debug(b" checking for directory renames\n")
684
689
685 dirmove1, movewithdir2 = _dir_renames(repo, c1, copy1, copies1, u2)
690 dirmove1, movewithdir2 = _dir_renames(repo, c1, copy1, copies1, u2)
686 dirmove2, movewithdir1 = _dir_renames(repo, c2, copy2, copies2, u1)
691 dirmove2, movewithdir1 = _dir_renames(repo, c2, copy2, copies2, u1)
687
692
688 branch_copies1 = branch_copies(copy1, renamedelete1, dirmove1, movewithdir1)
693 branch_copies1 = branch_copies(copy1, renamedelete1, dirmove1, movewithdir1)
689 branch_copies2 = branch_copies(copy2, renamedelete2, dirmove2, movewithdir2)
694 branch_copies2 = branch_copies(copy2, renamedelete2, dirmove2, movewithdir2)
690
695
691 return branch_copies1, branch_copies2, diverge
696 return branch_copies1, branch_copies2, diverge
692
697
693
698
694 def _dir_renames(repo, ctx, copy, fullcopy, addedfiles):
699 def _dir_renames(repo, ctx, copy, fullcopy, addedfiles):
695 """Finds moved directories and files that should move with them.
700 """Finds moved directories and files that should move with them.
696
701
697 ctx: the context for one of the sides
702 ctx: the context for one of the sides
698 copy: files copied on the same side (as ctx)
703 copy: files copied on the same side (as ctx)
699 fullcopy: files copied on the same side (as ctx), including those that
704 fullcopy: files copied on the same side (as ctx), including those that
700 merge.manifestmerge() won't care about
705 merge.manifestmerge() won't care about
701 addedfiles: added files on the other side (compared to ctx)
706 addedfiles: added files on the other side (compared to ctx)
702 """
707 """
703 # generate a directory move map
708 # generate a directory move map
704 d = ctx.dirs()
709 d = ctx.dirs()
705 invalid = set()
710 invalid = set()
706 dirmove = {}
711 dirmove = {}
707
712
708 # examine each file copy for a potential directory move, which is
713 # examine each file copy for a potential directory move, which is
709 # when all the files in a directory are moved to a new directory
714 # when all the files in a directory are moved to a new directory
710 for dst, src in pycompat.iteritems(fullcopy):
715 for dst, src in pycompat.iteritems(fullcopy):
711 dsrc, ddst = pathutil.dirname(src), pathutil.dirname(dst)
716 dsrc, ddst = pathutil.dirname(src), pathutil.dirname(dst)
712 if dsrc in invalid:
717 if dsrc in invalid:
713 # already seen to be uninteresting
718 # already seen to be uninteresting
714 continue
719 continue
715 elif dsrc in d and ddst in d:
720 elif dsrc in d and ddst in d:
716 # directory wasn't entirely moved locally
721 # directory wasn't entirely moved locally
717 invalid.add(dsrc)
722 invalid.add(dsrc)
718 elif dsrc in dirmove and dirmove[dsrc] != ddst:
723 elif dsrc in dirmove and dirmove[dsrc] != ddst:
719 # files from the same directory moved to two different places
724 # files from the same directory moved to two different places
720 invalid.add(dsrc)
725 invalid.add(dsrc)
721 else:
726 else:
722 # looks good so far
727 # looks good so far
723 dirmove[dsrc] = ddst
728 dirmove[dsrc] = ddst
724
729
725 for i in invalid:
730 for i in invalid:
726 if i in dirmove:
731 if i in dirmove:
727 del dirmove[i]
732 del dirmove[i]
728 del d, invalid
733 del d, invalid
729
734
730 if not dirmove:
735 if not dirmove:
731 return {}, {}
736 return {}, {}
732
737
733 dirmove = {k + b"/": v + b"/" for k, v in pycompat.iteritems(dirmove)}
738 dirmove = {k + b"/": v + b"/" for k, v in pycompat.iteritems(dirmove)}
734
739
735 for d in dirmove:
740 for d in dirmove:
736 repo.ui.debug(
741 repo.ui.debug(
737 b" discovered dir src: '%s' -> dst: '%s'\n" % (d, dirmove[d])
742 b" discovered dir src: '%s' -> dst: '%s'\n" % (d, dirmove[d])
738 )
743 )
739
744
740 movewithdir = {}
745 movewithdir = {}
741 # check unaccounted nonoverlapping files against directory moves
746 # check unaccounted nonoverlapping files against directory moves
742 for f in addedfiles:
747 for f in addedfiles:
743 if f not in fullcopy:
748 if f not in fullcopy:
744 for d in dirmove:
749 for d in dirmove:
745 if f.startswith(d):
750 if f.startswith(d):
746 # new file added in a directory that was moved, move it
751 # new file added in a directory that was moved, move it
747 df = dirmove[d] + f[len(d) :]
752 df = dirmove[d] + f[len(d) :]
748 if df not in copy:
753 if df not in copy:
749 movewithdir[f] = df
754 movewithdir[f] = df
750 repo.ui.debug(
755 repo.ui.debug(
751 b" pending file src: '%s' -> dst: '%s'\n"
756 b" pending file src: '%s' -> dst: '%s'\n"
752 % (f, df)
757 % (f, df)
753 )
758 )
754 break
759 break
755
760
756 return dirmove, movewithdir
761 return dirmove, movewithdir
757
762
758
763
759 def _heuristicscopytracing(repo, c1, c2, base):
764 def _heuristicscopytracing(repo, c1, c2, base):
760 """ Fast copytracing using filename heuristics
765 """ Fast copytracing using filename heuristics
761
766
762 Assumes that moves or renames are of following two types:
767 Assumes that moves or renames are of following two types:
763
768
764 1) Inside a directory only (same directory name but different filenames)
769 1) Inside a directory only (same directory name but different filenames)
765 2) Move from one directory to another
770 2) Move from one directory to another
766 (same filenames but different directory names)
771 (same filenames but different directory names)
767
772
768 Works only when there are no merge commits in the "source branch".
773 Works only when there are no merge commits in the "source branch".
769 Source branch is commits from base up to c2 not including base.
774 Source branch is commits from base up to c2 not including base.
770
775
771 If merge is involved it fallbacks to _fullcopytracing().
776 If merge is involved it fallbacks to _fullcopytracing().
772
777
773 Can be used by setting the following config:
778 Can be used by setting the following config:
774
779
775 [experimental]
780 [experimental]
776 copytrace = heuristics
781 copytrace = heuristics
777
782
778 In some cases the copy/move candidates found by heuristics can be very large
783 In some cases the copy/move candidates found by heuristics can be very large
779 in number and that will make the algorithm slow. The number of possible
784 in number and that will make the algorithm slow. The number of possible
780 candidates to check can be limited by using the config
785 candidates to check can be limited by using the config
781 `experimental.copytrace.movecandidateslimit` which defaults to 100.
786 `experimental.copytrace.movecandidateslimit` which defaults to 100.
782 """
787 """
783
788
784 if c1.rev() is None:
789 if c1.rev() is None:
785 c1 = c1.p1()
790 c1 = c1.p1()
786 if c2.rev() is None:
791 if c2.rev() is None:
787 c2 = c2.p1()
792 c2 = c2.p1()
788
793
789 changedfiles = set()
794 changedfiles = set()
790 m1 = c1.manifest()
795 m1 = c1.manifest()
791 if not repo.revs(b'%d::%d', base.rev(), c2.rev()):
796 if not repo.revs(b'%d::%d', base.rev(), c2.rev()):
792 # If base is not in c2 branch, we switch to fullcopytracing
797 # If base is not in c2 branch, we switch to fullcopytracing
793 repo.ui.debug(
798 repo.ui.debug(
794 b"switching to full copytracing as base is not "
799 b"switching to full copytracing as base is not "
795 b"an ancestor of c2\n"
800 b"an ancestor of c2\n"
796 )
801 )
797 return _fullcopytracing(repo, c1, c2, base)
802 return _fullcopytracing(repo, c1, c2, base)
798
803
799 ctx = c2
804 ctx = c2
800 while ctx != base:
805 while ctx != base:
801 if len(ctx.parents()) == 2:
806 if len(ctx.parents()) == 2:
802 # To keep things simple let's not handle merges
807 # To keep things simple let's not handle merges
803 repo.ui.debug(b"switching to full copytracing because of merges\n")
808 repo.ui.debug(b"switching to full copytracing because of merges\n")
804 return _fullcopytracing(repo, c1, c2, base)
809 return _fullcopytracing(repo, c1, c2, base)
805 changedfiles.update(ctx.files())
810 changedfiles.update(ctx.files())
806 ctx = ctx.p1()
811 ctx = ctx.p1()
807
812
808 copies2 = {}
813 copies2 = {}
809 cp = _forwardcopies(base, c2)
814 cp = _forwardcopies(base, c2)
810 for dst, src in pycompat.iteritems(cp):
815 for dst, src in pycompat.iteritems(cp):
811 if src in m1:
816 if src in m1:
812 copies2[dst] = src
817 copies2[dst] = src
813
818
814 # file is missing if it isn't present in the destination, but is present in
819 # file is missing if it isn't present in the destination, but is present in
815 # the base and present in the source.
820 # the base and present in the source.
816 # Presence in the base is important to exclude added files, presence in the
821 # Presence in the base is important to exclude added files, presence in the
817 # source is important to exclude removed files.
822 # source is important to exclude removed files.
818 filt = lambda f: f not in m1 and f in base and f in c2
823 filt = lambda f: f not in m1 and f in base and f in c2
819 missingfiles = [f for f in changedfiles if filt(f)]
824 missingfiles = [f for f in changedfiles if filt(f)]
820
825
821 copies1 = {}
826 copies1 = {}
822 if missingfiles:
827 if missingfiles:
823 basenametofilename = collections.defaultdict(list)
828 basenametofilename = collections.defaultdict(list)
824 dirnametofilename = collections.defaultdict(list)
829 dirnametofilename = collections.defaultdict(list)
825
830
826 for f in m1.filesnotin(base.manifest()):
831 for f in m1.filesnotin(base.manifest()):
827 basename = os.path.basename(f)
832 basename = os.path.basename(f)
828 dirname = os.path.dirname(f)
833 dirname = os.path.dirname(f)
829 basenametofilename[basename].append(f)
834 basenametofilename[basename].append(f)
830 dirnametofilename[dirname].append(f)
835 dirnametofilename[dirname].append(f)
831
836
832 for f in missingfiles:
837 for f in missingfiles:
833 basename = os.path.basename(f)
838 basename = os.path.basename(f)
834 dirname = os.path.dirname(f)
839 dirname = os.path.dirname(f)
835 samebasename = basenametofilename[basename]
840 samebasename = basenametofilename[basename]
836 samedirname = dirnametofilename[dirname]
841 samedirname = dirnametofilename[dirname]
837 movecandidates = samebasename + samedirname
842 movecandidates = samebasename + samedirname
838 # f is guaranteed to be present in c2, that's why
843 # f is guaranteed to be present in c2, that's why
839 # c2.filectx(f) won't fail
844 # c2.filectx(f) won't fail
840 f2 = c2.filectx(f)
845 f2 = c2.filectx(f)
841 # we can have a lot of candidates which can slow down the heuristics
846 # we can have a lot of candidates which can slow down the heuristics
842 # config value to limit the number of candidates moves to check
847 # config value to limit the number of candidates moves to check
843 maxcandidates = repo.ui.configint(
848 maxcandidates = repo.ui.configint(
844 b'experimental', b'copytrace.movecandidateslimit'
849 b'experimental', b'copytrace.movecandidateslimit'
845 )
850 )
846
851
847 if len(movecandidates) > maxcandidates:
852 if len(movecandidates) > maxcandidates:
848 repo.ui.status(
853 repo.ui.status(
849 _(
854 _(
850 b"skipping copytracing for '%s', more "
855 b"skipping copytracing for '%s', more "
851 b"candidates than the limit: %d\n"
856 b"candidates than the limit: %d\n"
852 )
857 )
853 % (f, len(movecandidates))
858 % (f, len(movecandidates))
854 )
859 )
855 continue
860 continue
856
861
857 for candidate in movecandidates:
862 for candidate in movecandidates:
858 f1 = c1.filectx(candidate)
863 f1 = c1.filectx(candidate)
859 if _related(f1, f2):
864 if _related(f1, f2):
860 # if there are a few related copies then we'll merge
865 # if there are a few related copies then we'll merge
861 # changes into all of them. This matches the behaviour
866 # changes into all of them. This matches the behaviour
862 # of upstream copytracing
867 # of upstream copytracing
863 copies1[candidate] = f
868 copies1[candidate] = f
864
869
865 return branch_copies(copies1), branch_copies(copies2), {}
870 return branch_copies(copies1), branch_copies(copies2), {}
866
871
867
872
868 def _related(f1, f2):
873 def _related(f1, f2):
869 """return True if f1 and f2 filectx have a common ancestor
874 """return True if f1 and f2 filectx have a common ancestor
870
875
871 Walk back to common ancestor to see if the two files originate
876 Walk back to common ancestor to see if the two files originate
872 from the same file. Since workingfilectx's rev() is None it messes
877 from the same file. Since workingfilectx's rev() is None it messes
873 up the integer comparison logic, hence the pre-step check for
878 up the integer comparison logic, hence the pre-step check for
874 None (f1 and f2 can only be workingfilectx's initially).
879 None (f1 and f2 can only be workingfilectx's initially).
875 """
880 """
876
881
877 if f1 == f2:
882 if f1 == f2:
878 return True # a match
883 return True # a match
879
884
880 g1, g2 = f1.ancestors(), f2.ancestors()
885 g1, g2 = f1.ancestors(), f2.ancestors()
881 try:
886 try:
882 f1r, f2r = f1.linkrev(), f2.linkrev()
887 f1r, f2r = f1.linkrev(), f2.linkrev()
883
888
884 if f1r is None:
889 if f1r is None:
885 f1 = next(g1)
890 f1 = next(g1)
886 if f2r is None:
891 if f2r is None:
887 f2 = next(g2)
892 f2 = next(g2)
888
893
889 while True:
894 while True:
890 f1r, f2r = f1.linkrev(), f2.linkrev()
895 f1r, f2r = f1.linkrev(), f2.linkrev()
891 if f1r > f2r:
896 if f1r > f2r:
892 f1 = next(g1)
897 f1 = next(g1)
893 elif f2r > f1r:
898 elif f2r > f1r:
894 f2 = next(g2)
899 f2 = next(g2)
895 else: # f1 and f2 point to files in the same linkrev
900 else: # f1 and f2 point to files in the same linkrev
896 return f1 == f2 # true if they point to the same file
901 return f1 == f2 # true if they point to the same file
897 except StopIteration:
902 except StopIteration:
898 return False
903 return False
899
904
900
905
901 def graftcopies(wctx, ctx, base):
906 def graftcopies(wctx, ctx, base):
902 """reproduce copies between base and ctx in the wctx
907 """reproduce copies between base and ctx in the wctx
903
908
904 Unlike mergecopies(), this function will only consider copies between base
909 Unlike mergecopies(), this function will only consider copies between base
905 and ctx; it will ignore copies between base and wctx. Also unlike
910 and ctx; it will ignore copies between base and wctx. Also unlike
906 mergecopies(), this function will apply copies to the working copy (instead
911 mergecopies(), this function will apply copies to the working copy (instead
907 of just returning information about the copies). That makes it cheaper
912 of just returning information about the copies). That makes it cheaper
908 (especially in the common case of base==ctx.p1()) and useful also when
913 (especially in the common case of base==ctx.p1()) and useful also when
909 experimental.copytrace=off.
914 experimental.copytrace=off.
910
915
911 merge.update() will have already marked most copies, but it will only
916 merge.update() will have already marked most copies, but it will only
912 mark copies if it thinks the source files are related (see
917 mark copies if it thinks the source files are related (see
913 merge._related()). It will also not mark copies if the file wasn't modified
918 merge._related()). It will also not mark copies if the file wasn't modified
914 on the local side. This function adds the copies that were "missed"
919 on the local side. This function adds the copies that were "missed"
915 by merge.update().
920 by merge.update().
916 """
921 """
917 new_copies = pathcopies(base, ctx)
922 new_copies = pathcopies(base, ctx)
918 _filter(wctx.p1(), wctx, new_copies)
923 _filter(wctx.p1(), wctx, new_copies)
919 for dst, src in pycompat.iteritems(new_copies):
924 for dst, src in pycompat.iteritems(new_copies):
920 wctx[dst].markcopied(src)
925 wctx[dst].markcopied(src)
921
926
922
927
923 def computechangesetfilesadded(ctx):
928 def computechangesetfilesadded(ctx):
924 """return the list of files added in a changeset
929 """return the list of files added in a changeset
925 """
930 """
926 added = []
931 added = []
927 for f in ctx.files():
932 for f in ctx.files():
928 if not any(f in p for p in ctx.parents()):
933 if not any(f in p for p in ctx.parents()):
929 added.append(f)
934 added.append(f)
930 return added
935 return added
931
936
932
937
933 def computechangesetfilesremoved(ctx):
938 def computechangesetfilesremoved(ctx):
934 """return the list of files removed in a changeset
939 """return the list of files removed in a changeset
935 """
940 """
936 removed = []
941 removed = []
937 for f in ctx.files():
942 for f in ctx.files():
938 if f not in ctx:
943 if f not in ctx:
939 removed.append(f)
944 removed.append(f)
940 return removed
945 return removed
941
946
942
947
943 def computechangesetcopies(ctx):
948 def computechangesetcopies(ctx):
944 """return the copies data for a changeset
949 """return the copies data for a changeset
945
950
946 The copies data are returned as a pair of dictionnary (p1copies, p2copies).
951 The copies data are returned as a pair of dictionnary (p1copies, p2copies).
947
952
948 Each dictionnary are in the form: `{newname: oldname}`
953 Each dictionnary are in the form: `{newname: oldname}`
949 """
954 """
950 p1copies = {}
955 p1copies = {}
951 p2copies = {}
956 p2copies = {}
952 p1 = ctx.p1()
957 p1 = ctx.p1()
953 p2 = ctx.p2()
958 p2 = ctx.p2()
954 narrowmatch = ctx._repo.narrowmatch()
959 narrowmatch = ctx._repo.narrowmatch()
955 for dst in ctx.files():
960 for dst in ctx.files():
956 if not narrowmatch(dst) or dst not in ctx:
961 if not narrowmatch(dst) or dst not in ctx:
957 continue
962 continue
958 copied = ctx[dst].renamed()
963 copied = ctx[dst].renamed()
959 if not copied:
964 if not copied:
960 continue
965 continue
961 src, srcnode = copied
966 src, srcnode = copied
962 if src in p1 and p1[src].filenode() == srcnode:
967 if src in p1 and p1[src].filenode() == srcnode:
963 p1copies[dst] = src
968 p1copies[dst] = src
964 elif src in p2 and p2[src].filenode() == srcnode:
969 elif src in p2 and p2[src].filenode() == srcnode:
965 p2copies[dst] = src
970 p2copies[dst] = src
966 return p1copies, p2copies
971 return p1copies, p2copies
967
972
968
973
969 def encodecopies(files, copies):
974 def encodecopies(files, copies):
970 items = []
975 items = []
971 for i, dst in enumerate(files):
976 for i, dst in enumerate(files):
972 if dst in copies:
977 if dst in copies:
973 items.append(b'%d\0%s' % (i, copies[dst]))
978 items.append(b'%d\0%s' % (i, copies[dst]))
974 if len(items) != len(copies):
979 if len(items) != len(copies):
975 raise error.ProgrammingError(
980 raise error.ProgrammingError(
976 b'some copy targets missing from file list'
981 b'some copy targets missing from file list'
977 )
982 )
978 return b"\n".join(items)
983 return b"\n".join(items)
979
984
980
985
981 def decodecopies(files, data):
986 def decodecopies(files, data):
982 try:
987 try:
983 copies = {}
988 copies = {}
984 if not data:
989 if not data:
985 return copies
990 return copies
986 for l in data.split(b'\n'):
991 for l in data.split(b'\n'):
987 strindex, src = l.split(b'\0')
992 strindex, src = l.split(b'\0')
988 i = int(strindex)
993 i = int(strindex)
989 dst = files[i]
994 dst = files[i]
990 copies[dst] = src
995 copies[dst] = src
991 return copies
996 return copies
992 except (ValueError, IndexError):
997 except (ValueError, IndexError):
993 # Perhaps someone had chosen the same key name (e.g. "p1copies") and
998 # Perhaps someone had chosen the same key name (e.g. "p1copies") and
994 # used different syntax for the value.
999 # used different syntax for the value.
995 return None
1000 return None
996
1001
997
1002
998 def encodefileindices(files, subset):
1003 def encodefileindices(files, subset):
999 subset = set(subset)
1004 subset = set(subset)
1000 indices = []
1005 indices = []
1001 for i, f in enumerate(files):
1006 for i, f in enumerate(files):
1002 if f in subset:
1007 if f in subset:
1003 indices.append(b'%d' % i)
1008 indices.append(b'%d' % i)
1004 return b'\n'.join(indices)
1009 return b'\n'.join(indices)
1005
1010
1006
1011
1007 def decodefileindices(files, data):
1012 def decodefileindices(files, data):
1008 try:
1013 try:
1009 subset = []
1014 subset = []
1010 if not data:
1015 if not data:
1011 return subset
1016 return subset
1012 for strindex in data.split(b'\n'):
1017 for strindex in data.split(b'\n'):
1013 i = int(strindex)
1018 i = int(strindex)
1014 if i < 0 or i >= len(files):
1019 if i < 0 or i >= len(files):
1015 return None
1020 return None
1016 subset.append(files[i])
1021 subset.append(files[i])
1017 return subset
1022 return subset
1018 except (ValueError, IndexError):
1023 except (ValueError, IndexError):
1019 # Perhaps someone had chosen the same key name (e.g. "added") and
1024 # Perhaps someone had chosen the same key name (e.g. "added") and
1020 # used different syntax for the value.
1025 # used different syntax for the value.
1021 return None
1026 return None
1022
1027
1023
1028
1024 def _getsidedata(srcrepo, rev):
1029 def _getsidedata(srcrepo, rev):
1025 ctx = srcrepo[rev]
1030 ctx = srcrepo[rev]
1026 filescopies = computechangesetcopies(ctx)
1031 filescopies = computechangesetcopies(ctx)
1027 filesadded = computechangesetfilesadded(ctx)
1032 filesadded = computechangesetfilesadded(ctx)
1028 filesremoved = computechangesetfilesremoved(ctx)
1033 filesremoved = computechangesetfilesremoved(ctx)
1029 sidedata = {}
1034 sidedata = {}
1030 if any([filescopies, filesadded, filesremoved]):
1035 if any([filescopies, filesadded, filesremoved]):
1031 sortedfiles = sorted(ctx.files())
1036 sortedfiles = sorted(ctx.files())
1032 p1copies, p2copies = filescopies
1037 p1copies, p2copies = filescopies
1033 p1copies = encodecopies(sortedfiles, p1copies)
1038 p1copies = encodecopies(sortedfiles, p1copies)
1034 p2copies = encodecopies(sortedfiles, p2copies)
1039 p2copies = encodecopies(sortedfiles, p2copies)
1035 filesadded = encodefileindices(sortedfiles, filesadded)
1040 filesadded = encodefileindices(sortedfiles, filesadded)
1036 filesremoved = encodefileindices(sortedfiles, filesremoved)
1041 filesremoved = encodefileindices(sortedfiles, filesremoved)
1037 if p1copies:
1042 if p1copies:
1038 sidedata[sidedatamod.SD_P1COPIES] = p1copies
1043 sidedata[sidedatamod.SD_P1COPIES] = p1copies
1039 if p2copies:
1044 if p2copies:
1040 sidedata[sidedatamod.SD_P2COPIES] = p2copies
1045 sidedata[sidedatamod.SD_P2COPIES] = p2copies
1041 if filesadded:
1046 if filesadded:
1042 sidedata[sidedatamod.SD_FILESADDED] = filesadded
1047 sidedata[sidedatamod.SD_FILESADDED] = filesadded
1043 if filesremoved:
1048 if filesremoved:
1044 sidedata[sidedatamod.SD_FILESREMOVED] = filesremoved
1049 sidedata[sidedatamod.SD_FILESREMOVED] = filesremoved
1045 return sidedata
1050 return sidedata
1046
1051
1047
1052
1048 def getsidedataadder(srcrepo, destrepo):
1053 def getsidedataadder(srcrepo, destrepo):
1049 use_w = srcrepo.ui.configbool(b'experimental', b'worker.repository-upgrade')
1054 use_w = srcrepo.ui.configbool(b'experimental', b'worker.repository-upgrade')
1050 if pycompat.iswindows or not use_w:
1055 if pycompat.iswindows or not use_w:
1051 return _get_simple_sidedata_adder(srcrepo, destrepo)
1056 return _get_simple_sidedata_adder(srcrepo, destrepo)
1052 else:
1057 else:
1053 return _get_worker_sidedata_adder(srcrepo, destrepo)
1058 return _get_worker_sidedata_adder(srcrepo, destrepo)
1054
1059
1055
1060
1056 def _sidedata_worker(srcrepo, revs_queue, sidedata_queue, tokens):
1061 def _sidedata_worker(srcrepo, revs_queue, sidedata_queue, tokens):
1057 """The function used by worker precomputing sidedata
1062 """The function used by worker precomputing sidedata
1058
1063
1059 It read an input queue containing revision numbers
1064 It read an input queue containing revision numbers
1060 It write in an output queue containing (rev, <sidedata-map>)
1065 It write in an output queue containing (rev, <sidedata-map>)
1061
1066
1062 The `None` input value is used as a stop signal.
1067 The `None` input value is used as a stop signal.
1063
1068
1064 The `tokens` semaphore is user to avoid having too many unprocessed
1069 The `tokens` semaphore is user to avoid having too many unprocessed
1065 entries. The workers needs to acquire one token before fetching a task.
1070 entries. The workers needs to acquire one token before fetching a task.
1066 They will be released by the consumer of the produced data.
1071 They will be released by the consumer of the produced data.
1067 """
1072 """
1068 tokens.acquire()
1073 tokens.acquire()
1069 rev = revs_queue.get()
1074 rev = revs_queue.get()
1070 while rev is not None:
1075 while rev is not None:
1071 data = _getsidedata(srcrepo, rev)
1076 data = _getsidedata(srcrepo, rev)
1072 sidedata_queue.put((rev, data))
1077 sidedata_queue.put((rev, data))
1073 tokens.acquire()
1078 tokens.acquire()
1074 rev = revs_queue.get()
1079 rev = revs_queue.get()
1075 # processing of `None` is completed, release the token.
1080 # processing of `None` is completed, release the token.
1076 tokens.release()
1081 tokens.release()
1077
1082
1078
1083
1079 BUFF_PER_WORKER = 50
1084 BUFF_PER_WORKER = 50
1080
1085
1081
1086
1082 def _get_worker_sidedata_adder(srcrepo, destrepo):
1087 def _get_worker_sidedata_adder(srcrepo, destrepo):
1083 """The parallel version of the sidedata computation
1088 """The parallel version of the sidedata computation
1084
1089
1085 This code spawn a pool of worker that precompute a buffer of sidedata
1090 This code spawn a pool of worker that precompute a buffer of sidedata
1086 before we actually need them"""
1091 before we actually need them"""
1087 # avoid circular import copies -> scmutil -> worker -> copies
1092 # avoid circular import copies -> scmutil -> worker -> copies
1088 from . import worker
1093 from . import worker
1089
1094
1090 nbworkers = worker._numworkers(srcrepo.ui)
1095 nbworkers = worker._numworkers(srcrepo.ui)
1091
1096
1092 tokens = multiprocessing.BoundedSemaphore(nbworkers * BUFF_PER_WORKER)
1097 tokens = multiprocessing.BoundedSemaphore(nbworkers * BUFF_PER_WORKER)
1093 revsq = multiprocessing.Queue()
1098 revsq = multiprocessing.Queue()
1094 sidedataq = multiprocessing.Queue()
1099 sidedataq = multiprocessing.Queue()
1095
1100
1096 assert srcrepo.filtername is None
1101 assert srcrepo.filtername is None
1097 # queue all tasks beforehand, revision numbers are small and it make
1102 # queue all tasks beforehand, revision numbers are small and it make
1098 # synchronisation simpler
1103 # synchronisation simpler
1099 #
1104 #
1100 # Since the computation for each node can be quite expensive, the overhead
1105 # Since the computation for each node can be quite expensive, the overhead
1101 # of using a single queue is not revelant. In practice, most computation
1106 # of using a single queue is not revelant. In practice, most computation
1102 # are fast but some are very expensive and dominate all the other smaller
1107 # are fast but some are very expensive and dominate all the other smaller
1103 # cost.
1108 # cost.
1104 for r in srcrepo.changelog.revs():
1109 for r in srcrepo.changelog.revs():
1105 revsq.put(r)
1110 revsq.put(r)
1106 # queue the "no more tasks" markers
1111 # queue the "no more tasks" markers
1107 for i in range(nbworkers):
1112 for i in range(nbworkers):
1108 revsq.put(None)
1113 revsq.put(None)
1109
1114
1110 allworkers = []
1115 allworkers = []
1111 for i in range(nbworkers):
1116 for i in range(nbworkers):
1112 args = (srcrepo, revsq, sidedataq, tokens)
1117 args = (srcrepo, revsq, sidedataq, tokens)
1113 w = multiprocessing.Process(target=_sidedata_worker, args=args)
1118 w = multiprocessing.Process(target=_sidedata_worker, args=args)
1114 allworkers.append(w)
1119 allworkers.append(w)
1115 w.start()
1120 w.start()
1116
1121
1117 # dictionnary to store results for revision higher than we one we are
1122 # dictionnary to store results for revision higher than we one we are
1118 # looking for. For example, if we need the sidedatamap for 42, and 43 is
1123 # looking for. For example, if we need the sidedatamap for 42, and 43 is
1119 # received, when shelve 43 for later use.
1124 # received, when shelve 43 for later use.
1120 staging = {}
1125 staging = {}
1121
1126
1122 def sidedata_companion(revlog, rev):
1127 def sidedata_companion(revlog, rev):
1123 sidedata = {}
1128 sidedata = {}
1124 if util.safehasattr(revlog, b'filteredrevs'): # this is a changelog
1129 if util.safehasattr(revlog, b'filteredrevs'): # this is a changelog
1125 # Is the data previously shelved ?
1130 # Is the data previously shelved ?
1126 sidedata = staging.pop(rev, None)
1131 sidedata = staging.pop(rev, None)
1127 if sidedata is None:
1132 if sidedata is None:
1128 # look at the queued result until we find the one we are lookig
1133 # look at the queued result until we find the one we are lookig
1129 # for (shelve the other ones)
1134 # for (shelve the other ones)
1130 r, sidedata = sidedataq.get()
1135 r, sidedata = sidedataq.get()
1131 while r != rev:
1136 while r != rev:
1132 staging[r] = sidedata
1137 staging[r] = sidedata
1133 r, sidedata = sidedataq.get()
1138 r, sidedata = sidedataq.get()
1134 tokens.release()
1139 tokens.release()
1135 return False, (), sidedata
1140 return False, (), sidedata
1136
1141
1137 return sidedata_companion
1142 return sidedata_companion
1138
1143
1139
1144
1140 def _get_simple_sidedata_adder(srcrepo, destrepo):
1145 def _get_simple_sidedata_adder(srcrepo, destrepo):
1141 """The simple version of the sidedata computation
1146 """The simple version of the sidedata computation
1142
1147
1143 It just compute it in the same thread on request"""
1148 It just compute it in the same thread on request"""
1144
1149
1145 def sidedatacompanion(revlog, rev):
1150 def sidedatacompanion(revlog, rev):
1146 sidedata = {}
1151 sidedata = {}
1147 if util.safehasattr(revlog, 'filteredrevs'): # this is a changelog
1152 if util.safehasattr(revlog, 'filteredrevs'): # this is a changelog
1148 sidedata = _getsidedata(srcrepo, rev)
1153 sidedata = _getsidedata(srcrepo, rev)
1149 return False, (), sidedata
1154 return False, (), sidedata
1150
1155
1151 return sidedatacompanion
1156 return sidedatacompanion
1152
1157
1153
1158
1154 def getsidedataremover(srcrepo, destrepo):
1159 def getsidedataremover(srcrepo, destrepo):
1155 def sidedatacompanion(revlog, rev):
1160 def sidedatacompanion(revlog, rev):
1156 f = ()
1161 f = ()
1157 if util.safehasattr(revlog, 'filteredrevs'): # this is a changelog
1162 if util.safehasattr(revlog, 'filteredrevs'): # this is a changelog
1158 if revlog.flags(rev) & REVIDX_SIDEDATA:
1163 if revlog.flags(rev) & REVIDX_SIDEDATA:
1159 f = (
1164 f = (
1160 sidedatamod.SD_P1COPIES,
1165 sidedatamod.SD_P1COPIES,
1161 sidedatamod.SD_P2COPIES,
1166 sidedatamod.SD_P2COPIES,
1162 sidedatamod.SD_FILESADDED,
1167 sidedatamod.SD_FILESADDED,
1163 sidedatamod.SD_FILESREMOVED,
1168 sidedatamod.SD_FILESREMOVED,
1164 )
1169 )
1165 return False, f, {}
1170 return False, f, {}
1166
1171
1167 return sidedatacompanion
1172 return sidedatacompanion
@@ -1,1515 +1,1510 b''
1 #testcases stripbased phasebased
1 #testcases stripbased phasebased
2
2
3 $ cat <<EOF >> $HGRCPATH
3 $ cat <<EOF >> $HGRCPATH
4 > [extensions]
4 > [extensions]
5 > mq =
5 > mq =
6 > [defaults]
6 > [defaults]
7 > diff = --nodates --git
7 > diff = --nodates --git
8 > qnew = --date '0 0'
8 > qnew = --date '0 0'
9 > [shelve]
9 > [shelve]
10 > maxbackups = 2
10 > maxbackups = 2
11 > EOF
11 > EOF
12
12
13 #if phasebased
13 #if phasebased
14
14
15 $ cat <<EOF >> $HGRCPATH
15 $ cat <<EOF >> $HGRCPATH
16 > [format]
16 > [format]
17 > internal-phase = yes
17 > internal-phase = yes
18 > EOF
18 > EOF
19
19
20 #endif
20 #endif
21
21
22 $ hg init repo
22 $ hg init repo
23 $ cd repo
23 $ cd repo
24 $ mkdir a b
24 $ mkdir a b
25 $ echo a > a/a
25 $ echo a > a/a
26 $ echo b > b/b
26 $ echo b > b/b
27 $ echo c > c
27 $ echo c > c
28 $ echo d > d
28 $ echo d > d
29 $ echo x > x
29 $ echo x > x
30 $ hg addremove -q
30 $ hg addremove -q
31
31
32 shelve has a help message
32 shelve has a help message
33 $ hg shelve -h
33 $ hg shelve -h
34 hg shelve [OPTION]... [FILE]...
34 hg shelve [OPTION]... [FILE]...
35
35
36 save and set aside changes from the working directory
36 save and set aside changes from the working directory
37
37
38 Shelving takes files that "hg status" reports as not clean, saves the
38 Shelving takes files that "hg status" reports as not clean, saves the
39 modifications to a bundle (a shelved change), and reverts the files so
39 modifications to a bundle (a shelved change), and reverts the files so
40 that their state in the working directory becomes clean.
40 that their state in the working directory becomes clean.
41
41
42 To restore these changes to the working directory, using "hg unshelve";
42 To restore these changes to the working directory, using "hg unshelve";
43 this will work even if you switch to a different commit.
43 this will work even if you switch to a different commit.
44
44
45 When no files are specified, "hg shelve" saves all not-clean files. If
45 When no files are specified, "hg shelve" saves all not-clean files. If
46 specific files or directories are named, only changes to those files are
46 specific files or directories are named, only changes to those files are
47 shelved.
47 shelved.
48
48
49 In bare shelve (when no files are specified, without interactive, include
49 In bare shelve (when no files are specified, without interactive, include
50 and exclude option), shelving remembers information if the working
50 and exclude option), shelving remembers information if the working
51 directory was on newly created branch, in other words working directory
51 directory was on newly created branch, in other words working directory
52 was on different branch than its first parent. In this situation
52 was on different branch than its first parent. In this situation
53 unshelving restores branch information to the working directory.
53 unshelving restores branch information to the working directory.
54
54
55 Each shelved change has a name that makes it easier to find later. The
55 Each shelved change has a name that makes it easier to find later. The
56 name of a shelved change defaults to being based on the active bookmark,
56 name of a shelved change defaults to being based on the active bookmark,
57 or if there is no active bookmark, the current named branch. To specify a
57 or if there is no active bookmark, the current named branch. To specify a
58 different name, use "--name".
58 different name, use "--name".
59
59
60 To see a list of existing shelved changes, use the "--list" option. For
60 To see a list of existing shelved changes, use the "--list" option. For
61 each shelved change, this will print its name, age, and description; use "
61 each shelved change, this will print its name, age, and description; use "
62 --patch" or "--stat" for more details.
62 --patch" or "--stat" for more details.
63
63
64 To delete specific shelved changes, use "--delete". To delete all shelved
64 To delete specific shelved changes, use "--delete". To delete all shelved
65 changes, use "--cleanup".
65 changes, use "--cleanup".
66
66
67 options ([+] can be repeated):
67 options ([+] can be repeated):
68
68
69 -A --addremove mark new/missing files as added/removed before
69 -A --addremove mark new/missing files as added/removed before
70 shelving
70 shelving
71 -u --unknown store unknown files in the shelve
71 -u --unknown store unknown files in the shelve
72 --cleanup delete all shelved changes
72 --cleanup delete all shelved changes
73 --date DATE shelve with the specified commit date
73 --date DATE shelve with the specified commit date
74 -d --delete delete the named shelved change(s)
74 -d --delete delete the named shelved change(s)
75 -e --edit invoke editor on commit messages
75 -e --edit invoke editor on commit messages
76 -k --keep shelve, but keep changes in the working directory
76 -k --keep shelve, but keep changes in the working directory
77 -l --list list current shelves
77 -l --list list current shelves
78 -m --message TEXT use text as shelve message
78 -m --message TEXT use text as shelve message
79 -n --name NAME use the given name for the shelved commit
79 -n --name NAME use the given name for the shelved commit
80 -p --patch output patches for changes (provide the names of the
80 -p --patch output patches for changes (provide the names of the
81 shelved changes as positional arguments)
81 shelved changes as positional arguments)
82 -i --interactive interactive mode
82 -i --interactive interactive mode
83 --stat output diffstat-style summary of changes (provide
83 --stat output diffstat-style summary of changes (provide
84 the names of the shelved changes as positional
84 the names of the shelved changes as positional
85 arguments)
85 arguments)
86 -I --include PATTERN [+] include names matching the given patterns
86 -I --include PATTERN [+] include names matching the given patterns
87 -X --exclude PATTERN [+] exclude names matching the given patterns
87 -X --exclude PATTERN [+] exclude names matching the given patterns
88 --mq operate on patch repository
88 --mq operate on patch repository
89
89
90 (some details hidden, use --verbose to show complete help)
90 (some details hidden, use --verbose to show complete help)
91
91
92 shelving in an empty repo should be possible
92 shelving in an empty repo should be possible
93 (this tests also that editor is not invoked, if '--edit' is not
93 (this tests also that editor is not invoked, if '--edit' is not
94 specified)
94 specified)
95
95
96 $ HGEDITOR=cat hg shelve
96 $ HGEDITOR=cat hg shelve
97 shelved as default
97 shelved as default
98 0 files updated, 0 files merged, 5 files removed, 0 files unresolved
98 0 files updated, 0 files merged, 5 files removed, 0 files unresolved
99
99
100 $ hg unshelve
100 $ hg unshelve
101 unshelving change 'default'
101 unshelving change 'default'
102
102
103 $ hg commit -q -m 'initial commit'
103 $ hg commit -q -m 'initial commit'
104
104
105 $ hg shelve
105 $ hg shelve
106 nothing changed
106 nothing changed
107 [1]
107 [1]
108
108
109 make sure shelve files were backed up
109 make sure shelve files were backed up
110
110
111 $ ls .hg/shelve-backup
111 $ ls .hg/shelve-backup
112 default.hg
112 default.hg
113 default.patch
113 default.patch
114 default.shelve
114 default.shelve
115
115
116 checks to make sure we dont create a directory or
116 checks to make sure we dont create a directory or
117 hidden file while choosing a new shelve name
117 hidden file while choosing a new shelve name
118
118
119 when we are given a name
119 when we are given a name
120
120
121 $ hg shelve -n foo/bar
121 $ hg shelve -n foo/bar
122 abort: shelved change names can not contain slashes
122 abort: shelved change names can not contain slashes
123 [255]
123 [255]
124 $ hg shelve -n .baz
124 $ hg shelve -n .baz
125 abort: shelved change names can not start with '.'
125 abort: shelved change names can not start with '.'
126 [255]
126 [255]
127 $ hg shelve -n foo\\bar
127 $ hg shelve -n foo\\bar
128 abort: shelved change names can not contain slashes
128 abort: shelved change names can not contain slashes
129 [255]
129 [255]
130
130
131 when shelve has to choose itself
131 when shelve has to choose itself
132
132
133 $ hg branch x/y -q
133 $ hg branch x/y -q
134 $ hg commit -q -m "Branch commit 0"
134 $ hg commit -q -m "Branch commit 0"
135 $ hg shelve
135 $ hg shelve
136 nothing changed
136 nothing changed
137 [1]
137 [1]
138 $ hg branch .x -q
138 $ hg branch .x -q
139 $ hg commit -q -m "Branch commit 1"
139 $ hg commit -q -m "Branch commit 1"
140 $ hg shelve
140 $ hg shelve
141 nothing changed
141 nothing changed
142 [1]
142 [1]
143 $ hg branch x\\y -q
143 $ hg branch x\\y -q
144 $ hg commit -q -m "Branch commit 2"
144 $ hg commit -q -m "Branch commit 2"
145 $ hg shelve
145 $ hg shelve
146 nothing changed
146 nothing changed
147 [1]
147 [1]
148
148
149 cleaning the branches made for name checking tests
149 cleaning the branches made for name checking tests
150
150
151 $ hg up default -q
151 $ hg up default -q
152 $ hg strip e9177275307e+6a6d231f43d+882bae7c62c2 -q
152 $ hg strip e9177275307e+6a6d231f43d+882bae7c62c2 -q
153
153
154 create an mq patch - shelving should work fine with a patch applied
154 create an mq patch - shelving should work fine with a patch applied
155
155
156 $ echo n > n
156 $ echo n > n
157 $ hg add n
157 $ hg add n
158 $ hg commit n -m second
158 $ hg commit n -m second
159 $ hg qnew second.patch
159 $ hg qnew second.patch
160
160
161 shelve a change that we will delete later
161 shelve a change that we will delete later
162
162
163 $ echo a >> a/a
163 $ echo a >> a/a
164 $ hg shelve
164 $ hg shelve
165 shelved as default
165 shelved as default
166 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
166 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
167
167
168 set up some more complex changes to shelve
168 set up some more complex changes to shelve
169
169
170 $ echo a >> a/a
170 $ echo a >> a/a
171 $ hg mv b b.rename
171 $ hg mv b b.rename
172 moving b/b to b.rename/b
172 moving b/b to b.rename/b
173 $ hg cp c c.copy
173 $ hg cp c c.copy
174 $ hg mv d ghost
174 $ hg mv d ghost
175 $ rm ghost
175 $ rm ghost
176 $ hg status -C
176 $ hg status -C
177 M a/a
177 M a/a
178 A b.rename/b
178 A b.rename/b
179 b/b
179 b/b
180 A c.copy
180 A c.copy
181 c
181 c
182 R b/b
182 R b/b
183 R d
183 R d
184 ! ghost
184 ! ghost
185 d
185 d
186
186
187 the common case - no options or filenames
187 the common case - no options or filenames
188
188
189 $ hg shelve 2>&1 | grep KeyError
189 $ hg shelve
190 KeyError: 'No such manifest entry.' (no-pure !)
190 shelved as default-01
191 raise KeyError (pure !)
191 3 files updated, 0 files merged, 2 files removed, 0 files unresolved
192 KeyError (pure !)
193 # Get out of the broken state so later tests work
194 $ hg forget b.rename/b c.copy ghost
195 $ hg revert a/a b/b d
196 $ rm a/a.orig b.rename/b c.copy
197 $ hg status -C
192 $ hg status -C
198
193
199 ensure that our shelved changes exist
194 ensure that our shelved changes exist
200
195
201 $ hg shelve -l
196 $ hg shelve -l
202 default-01 (*)* changes to: [mq]: second.patch (glob)
197 default-01 (*)* changes to: [mq]: second.patch (glob)
203 default (*)* changes to: [mq]: second.patch (glob)
198 default (*)* changes to: [mq]: second.patch (glob)
204
199
205 $ hg shelve -l -p default
200 $ hg shelve -l -p default
206 default (*)* changes to: [mq]: second.patch (glob)
201 default (*)* changes to: [mq]: second.patch (glob)
207
202
208 diff --git a/a/a b/a/a
203 diff --git a/a/a b/a/a
209 --- a/a/a
204 --- a/a/a
210 +++ b/a/a
205 +++ b/a/a
211 @@ -1,1 +1,2 @@
206 @@ -1,1 +1,2 @@
212 a
207 a
213 +a
208 +a
214
209
215 $ hg shelve --list --addremove
210 $ hg shelve --list --addremove
216 abort: options '--list' and '--addremove' may not be used together
211 abort: options '--list' and '--addremove' may not be used together
217 [255]
212 [255]
218
213
219 delete our older shelved change
214 delete our older shelved change
220
215
221 $ hg shelve -d default
216 $ hg shelve -d default
222 $ hg qfinish -a -q
217 $ hg qfinish -a -q
223
218
224 ensure shelve backups aren't overwritten
219 ensure shelve backups aren't overwritten
225
220
226 $ ls .hg/shelve-backup/
221 $ ls .hg/shelve-backup/
227 default-1.hg
222 default-1.hg
228 default-1.patch
223 default-1.patch
229 default-1.shelve
224 default-1.shelve
230 default.hg
225 default.hg
231 default.patch
226 default.patch
232 default.shelve
227 default.shelve
233
228
234 local edits should not prevent a shelved change from applying
229 local edits should not prevent a shelved change from applying
235
230
236 $ printf "z\na\n" > a/a
231 $ printf "z\na\n" > a/a
237 $ hg unshelve --keep
232 $ hg unshelve --keep
238 unshelving change 'default-01'
233 unshelving change 'default-01'
239 temporarily committing pending changes (restore with 'hg unshelve --abort')
234 temporarily committing pending changes (restore with 'hg unshelve --abort')
240 rebasing shelved changes
235 rebasing shelved changes
241 merging a/a
236 merging a/a
242
237
243 $ hg revert --all -q
238 $ hg revert --all -q
244 $ rm a/a.orig b.rename/b c.copy
239 $ rm a/a.orig b.rename/b c.copy
245
240
246 apply it and make sure our state is as expected
241 apply it and make sure our state is as expected
247
242
248 (this also tests that same timestamp prevents backups from being
243 (this also tests that same timestamp prevents backups from being
249 removed, even though there are more than 'maxbackups' backups)
244 removed, even though there are more than 'maxbackups' backups)
250
245
251 $ f -t .hg/shelve-backup/default.patch
246 $ f -t .hg/shelve-backup/default.patch
252 .hg/shelve-backup/default.patch: file
247 .hg/shelve-backup/default.patch: file
253 $ touch -t 200001010000 .hg/shelve-backup/default.patch
248 $ touch -t 200001010000 .hg/shelve-backup/default.patch
254 $ f -t .hg/shelve-backup/default-1.patch
249 $ f -t .hg/shelve-backup/default-1.patch
255 .hg/shelve-backup/default-1.patch: file
250 .hg/shelve-backup/default-1.patch: file
256 $ touch -t 200001010000 .hg/shelve-backup/default-1.patch
251 $ touch -t 200001010000 .hg/shelve-backup/default-1.patch
257
252
258 $ hg unshelve
253 $ hg unshelve
259 unshelving change 'default-01'
254 unshelving change 'default-01'
260 $ hg status -C
255 $ hg status -C
261 M a/a
256 M a/a
262 A b.rename/b
257 A b.rename/b
263 b/b
258 b/b
264 A c.copy
259 A c.copy
265 c
260 c
266 R b/b
261 R b/b
267 R d
262 R d
268 $ hg shelve -l
263 $ hg shelve -l
269
264
270 (both of default.hg and default-1.hg should be still kept, because it
265 (both of default.hg and default-1.hg should be still kept, because it
271 is difficult to decide actual order of them from same timestamp)
266 is difficult to decide actual order of them from same timestamp)
272
267
273 $ ls .hg/shelve-backup/
268 $ ls .hg/shelve-backup/
274 default-01.hg
269 default-01.hg
275 default-01.patch
270 default-01.patch
276 default-01.shelve
271 default-01.shelve
277 default-1.hg
272 default-1.hg
278 default-1.patch
273 default-1.patch
279 default-1.shelve
274 default-1.shelve
280 default.hg
275 default.hg
281 default.patch
276 default.patch
282 default.shelve
277 default.shelve
283
278
284 $ hg unshelve
279 $ hg unshelve
285 abort: no shelved changes to apply!
280 abort: no shelved changes to apply!
286 [255]
281 [255]
287 $ hg unshelve foo
282 $ hg unshelve foo
288 abort: shelved change 'foo' not found
283 abort: shelved change 'foo' not found
289 [255]
284 [255]
290
285
291 named shelves, specific filenames, and "commit messages" should all work
286 named shelves, specific filenames, and "commit messages" should all work
292 (this tests also that editor is invoked, if '--edit' is specified)
287 (this tests also that editor is invoked, if '--edit' is specified)
293
288
294 $ hg status -C
289 $ hg status -C
295 M a/a
290 M a/a
296 A b.rename/b
291 A b.rename/b
297 b/b
292 b/b
298 A c.copy
293 A c.copy
299 c
294 c
300 R b/b
295 R b/b
301 R d
296 R d
302 $ HGEDITOR=cat hg shelve -q -n wibble -m wat -e a
297 $ HGEDITOR=cat hg shelve -q -n wibble -m wat -e a
303 wat
298 wat
304
299
305
300
306 HG: Enter commit message. Lines beginning with 'HG:' are removed.
301 HG: Enter commit message. Lines beginning with 'HG:' are removed.
307 HG: Leave message empty to abort commit.
302 HG: Leave message empty to abort commit.
308 HG: --
303 HG: --
309 HG: user: shelve@localhost
304 HG: user: shelve@localhost
310 HG: branch 'default'
305 HG: branch 'default'
311 HG: changed a/a
306 HG: changed a/a
312
307
313 expect "a" to no longer be present, but status otherwise unchanged
308 expect "a" to no longer be present, but status otherwise unchanged
314
309
315 $ hg status -C
310 $ hg status -C
316 A b.rename/b
311 A b.rename/b
317 b/b
312 b/b
318 A c.copy
313 A c.copy
319 c
314 c
320 R b/b
315 R b/b
321 R d
316 R d
322 $ hg shelve -l --stat
317 $ hg shelve -l --stat
323 wibble (*) wat (glob)
318 wibble (*) wat (glob)
324 a/a | 1 +
319 a/a | 1 +
325 1 files changed, 1 insertions(+), 0 deletions(-)
320 1 files changed, 1 insertions(+), 0 deletions(-)
326
321
327 and now "a/a" should reappear
322 and now "a/a" should reappear
328
323
329 $ cd a
324 $ cd a
330 $ hg unshelve -q wibble
325 $ hg unshelve -q wibble
331 $ cd ..
326 $ cd ..
332 $ hg status -C
327 $ hg status -C
333 M a/a
328 M a/a
334 A b.rename/b
329 A b.rename/b
335 b/b
330 b/b
336 A c.copy
331 A c.copy
337 c
332 c
338 R b/b
333 R b/b
339 R d
334 R d
340
335
341 ensure old shelve backups are being deleted automatically
336 ensure old shelve backups are being deleted automatically
342
337
343 $ ls .hg/shelve-backup/
338 $ ls .hg/shelve-backup/
344 default-01.hg
339 default-01.hg
345 default-01.patch
340 default-01.patch
346 default-01.shelve
341 default-01.shelve
347 wibble.hg
342 wibble.hg
348 wibble.patch
343 wibble.patch
349 wibble.shelve
344 wibble.shelve
350
345
351 cause unshelving to result in a merge with 'a' conflicting
346 cause unshelving to result in a merge with 'a' conflicting
352
347
353 $ hg shelve -q
348 $ hg shelve -q
354 $ echo c>>a/a
349 $ echo c>>a/a
355 $ hg commit -m second
350 $ hg commit -m second
356 $ hg tip --template '{files}\n'
351 $ hg tip --template '{files}\n'
357 a/a
352 a/a
358
353
359 add an unrelated change that should be preserved
354 add an unrelated change that should be preserved
360
355
361 $ mkdir foo
356 $ mkdir foo
362 $ echo foo > foo/foo
357 $ echo foo > foo/foo
363 $ hg add foo/foo
358 $ hg add foo/foo
364
359
365 force a conflicted merge to occur
360 force a conflicted merge to occur
366
361
367 $ hg unshelve
362 $ hg unshelve
368 unshelving change 'default'
363 unshelving change 'default'
369 temporarily committing pending changes (restore with 'hg unshelve --abort')
364 temporarily committing pending changes (restore with 'hg unshelve --abort')
370 rebasing shelved changes
365 rebasing shelved changes
371 merging a/a
366 merging a/a
372 warning: conflicts while merging a/a! (edit, then use 'hg resolve --mark')
367 warning: conflicts while merging a/a! (edit, then use 'hg resolve --mark')
373 unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
368 unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
374 [1]
369 [1]
375 $ hg status -v
370 $ hg status -v
376 M a/a
371 M a/a
377 M b.rename/b
372 M b.rename/b
378 M c.copy
373 M c.copy
379 R b/b
374 R b/b
380 R d
375 R d
381 ? a/a.orig
376 ? a/a.orig
382 # The repository is in an unfinished *unshelve* state.
377 # The repository is in an unfinished *unshelve* state.
383
378
384 # Unresolved merge conflicts:
379 # Unresolved merge conflicts:
385 #
380 #
386 # a/a
381 # a/a
387 #
382 #
388 # To mark files as resolved: hg resolve --mark FILE
383 # To mark files as resolved: hg resolve --mark FILE
389
384
390 # To continue: hg unshelve --continue
385 # To continue: hg unshelve --continue
391 # To abort: hg unshelve --abort
386 # To abort: hg unshelve --abort
392
387
393
388
394 ensure that we have a merge with unresolved conflicts
389 ensure that we have a merge with unresolved conflicts
395
390
396 #if phasebased
391 #if phasebased
397 $ hg heads -q --template '{rev}\n'
392 $ hg heads -q --template '{rev}\n'
398 8
393 8
399 6
394 5
400 $ hg parents -q --template '{rev}\n'
395 $ hg parents -q --template '{rev}\n'
401 8
396 8
402 6
397 5
403 #endif
398 #endif
404
399
405 #if stripbased
400 #if stripbased
406 $ hg heads -q --template '{rev}\n'
401 $ hg heads -q --template '{rev}\n'
407 5
402 5
408 4
403 4
409 $ hg parents -q --template '{rev}\n'
404 $ hg parents -q --template '{rev}\n'
410 4
405 4
411 5
406 5
412 #endif
407 #endif
413
408
414 $ hg status
409 $ hg status
415 M a/a
410 M a/a
416 M b.rename/b
411 M b.rename/b
417 M c.copy
412 M c.copy
418 R b/b
413 R b/b
419 R d
414 R d
420 ? a/a.orig
415 ? a/a.orig
421 $ hg diff
416 $ hg diff
422 diff --git a/a/a b/a/a
417 diff --git a/a/a b/a/a
423 --- a/a/a
418 --- a/a/a
424 +++ b/a/a
419 +++ b/a/a
425 @@ -1,2 +1,6 @@
420 @@ -1,2 +1,6 @@
426 a
421 a
427 +<<<<<<< shelve: 2377350b6337 - shelve: pending changes temporary commit
422 +<<<<<<< shelve: 2377350b6337 - shelve: pending changes temporary commit
428 c
423 c
429 +=======
424 +=======
430 +a
425 +a
431 +>>>>>>> working-copy: 203c9f771d2b - shelve: changes to: [mq]: second.patch
426 +>>>>>>> working-copy: 203c9f771d2b - shelve: changes to: [mq]: second.patch
432 diff --git a/b/b b/b.rename/b
427 diff --git a/b/b b/b.rename/b
433 rename from b/b
428 rename from b/b
434 rename to b.rename/b
429 rename to b.rename/b
435 diff --git a/c b/c.copy
430 diff --git a/c b/c.copy
436 copy from c
431 copy from c
437 copy to c.copy
432 copy to c.copy
438 diff --git a/d b/d
433 diff --git a/d b/d
439 deleted file mode 100644
434 deleted file mode 100644
440 --- a/d
435 --- a/d
441 +++ /dev/null
436 +++ /dev/null
442 @@ -1,1 +0,0 @@
437 @@ -1,1 +0,0 @@
443 -d
438 -d
444 $ hg resolve -l
439 $ hg resolve -l
445 U a/a
440 U a/a
446
441
447 $ hg shelve
442 $ hg shelve
448 abort: unshelve already in progress
443 abort: unshelve already in progress
449 (use 'hg unshelve --continue' or 'hg unshelve --abort')
444 (use 'hg unshelve --continue' or 'hg unshelve --abort')
450 [255]
445 [255]
451
446
452 abort the unshelve and be happy
447 abort the unshelve and be happy
453
448
454 $ hg status
449 $ hg status
455 M a/a
450 M a/a
456 M b.rename/b
451 M b.rename/b
457 M c.copy
452 M c.copy
458 R b/b
453 R b/b
459 R d
454 R d
460 ? a/a.orig
455 ? a/a.orig
461 $ hg unshelve -a
456 $ hg unshelve -a
462 unshelve of 'default' aborted
457 unshelve of 'default' aborted
463 $ hg heads -q
458 $ hg heads -q
464 [37]:2e69b451d1ea (re)
459 [37]:2e69b451d1ea (re)
465 $ hg parents
460 $ hg parents
466 changeset: [37]:2e69b451d1ea (re)
461 changeset: [37]:2e69b451d1ea (re)
467 tag: tip
462 tag: tip
468 parent: 3:509104101065 (?)
463 parent: 3:509104101065 (?)
469 user: test
464 user: test
470 date: Thu Jan 01 00:00:00 1970 +0000
465 date: Thu Jan 01 00:00:00 1970 +0000
471 summary: second
466 summary: second
472
467
473 $ hg resolve -l
468 $ hg resolve -l
474 $ hg status
469 $ hg status
475 A foo/foo
470 A foo/foo
476 ? a/a.orig
471 ? a/a.orig
477
472
478 try to continue with no unshelve underway
473 try to continue with no unshelve underway
479
474
480 $ hg unshelve -c
475 $ hg unshelve -c
481 abort: no unshelve in progress
476 abort: no unshelve in progress
482 [255]
477 [255]
483 $ hg status
478 $ hg status
484 A foo/foo
479 A foo/foo
485 ? a/a.orig
480 ? a/a.orig
486
481
487 redo the unshelve to get a conflict
482 redo the unshelve to get a conflict
488
483
489 $ hg unshelve -q
484 $ hg unshelve -q
490 warning: conflicts while merging a/a! (edit, then use 'hg resolve --mark')
485 warning: conflicts while merging a/a! (edit, then use 'hg resolve --mark')
491 unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
486 unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
492 [1]
487 [1]
493
488
494 attempt to continue
489 attempt to continue
495
490
496 $ hg unshelve -c
491 $ hg unshelve -c
497 abort: unresolved conflicts, can't continue
492 abort: unresolved conflicts, can't continue
498 (see 'hg resolve', then 'hg unshelve --continue')
493 (see 'hg resolve', then 'hg unshelve --continue')
499 [255]
494 [255]
500
495
501 $ hg revert -r . a/a
496 $ hg revert -r . a/a
502 $ hg resolve -m a/a
497 $ hg resolve -m a/a
503 (no more unresolved files)
498 (no more unresolved files)
504 continue: hg unshelve --continue
499 continue: hg unshelve --continue
505
500
506 $ hg commit -m 'commit while unshelve in progress'
501 $ hg commit -m 'commit while unshelve in progress'
507 abort: unshelve already in progress
502 abort: unshelve already in progress
508 (use 'hg unshelve --continue' or 'hg unshelve --abort')
503 (use 'hg unshelve --continue' or 'hg unshelve --abort')
509 [255]
504 [255]
510
505
511 $ hg graft --continue
506 $ hg graft --continue
512 abort: no graft in progress
507 abort: no graft in progress
513 (continue: hg unshelve --continue)
508 (continue: hg unshelve --continue)
514 [255]
509 [255]
515 $ hg unshelve -c
510 $ hg unshelve -c
516 unshelve of 'default' complete
511 unshelve of 'default' complete
517
512
518 ensure the repo is as we hope
513 ensure the repo is as we hope
519
514
520 $ hg parents
515 $ hg parents
521 changeset: [37]:2e69b451d1ea (re)
516 changeset: [37]:2e69b451d1ea (re)
522 tag: tip
517 tag: tip
523 parent: 3:509104101065 (?)
518 parent: 3:509104101065 (?)
524 user: test
519 user: test
525 date: Thu Jan 01 00:00:00 1970 +0000
520 date: Thu Jan 01 00:00:00 1970 +0000
526 summary: second
521 summary: second
527
522
528 $ hg heads -q
523 $ hg heads -q
529 [37]:2e69b451d1ea (re)
524 [37]:2e69b451d1ea (re)
530
525
531 $ hg status -C
526 $ hg status -C
532 A b.rename/b
527 A b.rename/b
533 b/b
528 b/b
534 A c.copy
529 A c.copy
535 c
530 c
536 A foo/foo
531 A foo/foo
537 R b/b
532 R b/b
538 R d
533 R d
539 ? a/a.orig
534 ? a/a.orig
540
535
541 there should be no shelves left
536 there should be no shelves left
542
537
543 $ hg shelve -l
538 $ hg shelve -l
544
539
545 #if execbit
540 #if execbit
546
541
547 ensure that metadata-only changes are shelved
542 ensure that metadata-only changes are shelved
548
543
549 $ chmod +x a/a
544 $ chmod +x a/a
550 $ hg shelve -q -n execbit a/a
545 $ hg shelve -q -n execbit a/a
551 $ hg status a/a
546 $ hg status a/a
552 $ hg unshelve -q execbit
547 $ hg unshelve -q execbit
553 $ hg status a/a
548 $ hg status a/a
554 M a/a
549 M a/a
555 $ hg revert a/a
550 $ hg revert a/a
556
551
557 #else
552 #else
558
553
559 Dummy shelve op, to keep rev numbers aligned
554 Dummy shelve op, to keep rev numbers aligned
560
555
561 $ echo foo > a/a
556 $ echo foo > a/a
562 $ hg shelve -q -n dummy a/a
557 $ hg shelve -q -n dummy a/a
563 $ hg unshelve -q dummy
558 $ hg unshelve -q dummy
564 $ hg revert a/a
559 $ hg revert a/a
565
560
566 #endif
561 #endif
567
562
568 #if symlink
563 #if symlink
569
564
570 $ rm a/a
565 $ rm a/a
571 $ ln -s foo a/a
566 $ ln -s foo a/a
572 $ hg shelve -q -n symlink a/a
567 $ hg shelve -q -n symlink a/a
573 $ hg status a/a
568 $ hg status a/a
574 $ hg unshelve -q -n symlink
569 $ hg unshelve -q -n symlink
575 $ hg status a/a
570 $ hg status a/a
576 M a/a
571 M a/a
577 $ hg revert a/a
572 $ hg revert a/a
578
573
579 #else
574 #else
580
575
581 Dummy shelve op, to keep rev numbers aligned
576 Dummy shelve op, to keep rev numbers aligned
582
577
583 $ echo bar > a/a
578 $ echo bar > a/a
584 $ hg shelve -q -n dummy a/a
579 $ hg shelve -q -n dummy a/a
585 $ hg unshelve -q dummy
580 $ hg unshelve -q dummy
586 $ hg revert a/a
581 $ hg revert a/a
587
582
588 #endif
583 #endif
589
584
590 set up another conflict between a commit and a shelved change
585 set up another conflict between a commit and a shelved change
591
586
592 $ hg revert -q -C -a
587 $ hg revert -q -C -a
593 $ rm a/a.orig b.rename/b c.copy
588 $ rm a/a.orig b.rename/b c.copy
594 $ echo a >> a/a
589 $ echo a >> a/a
595 $ hg shelve -q
590 $ hg shelve -q
596 $ echo x >> a/a
591 $ echo x >> a/a
597 $ hg ci -m 'create conflict'
592 $ hg ci -m 'create conflict'
598 $ hg add foo/foo
593 $ hg add foo/foo
599
594
600 if we resolve a conflict while unshelving, the unshelve should succeed
595 if we resolve a conflict while unshelving, the unshelve should succeed
601
596
602 $ hg unshelve --tool :merge-other --keep
597 $ hg unshelve --tool :merge-other --keep
603 unshelving change 'default'
598 unshelving change 'default'
604 temporarily committing pending changes (restore with 'hg unshelve --abort')
599 temporarily committing pending changes (restore with 'hg unshelve --abort')
605 rebasing shelved changes
600 rebasing shelved changes
606 merging a/a
601 merging a/a
607 $ hg parents -q
602 $ hg parents -q
608 (4|13):33f7f61e6c5e (re)
603 (4|13):33f7f61e6c5e (re)
609 $ hg shelve -l
604 $ hg shelve -l
610 default (*)* changes to: second (glob)
605 default (*)* changes to: second (glob)
611 $ hg status
606 $ hg status
612 M a/a
607 M a/a
613 A foo/foo
608 A foo/foo
614 $ cat a/a
609 $ cat a/a
615 a
610 a
616 c
611 c
617 a
612 a
618 $ cat > a/a << EOF
613 $ cat > a/a << EOF
619 > a
614 > a
620 > c
615 > c
621 > x
616 > x
622 > EOF
617 > EOF
623
618
624 $ HGMERGE=true hg unshelve
619 $ HGMERGE=true hg unshelve
625 unshelving change 'default'
620 unshelving change 'default'
626 temporarily committing pending changes (restore with 'hg unshelve --abort')
621 temporarily committing pending changes (restore with 'hg unshelve --abort')
627 rebasing shelved changes
622 rebasing shelved changes
628 merging a/a
623 merging a/a
629 note: unshelved changes already existed in the working copy
624 note: unshelved changes already existed in the working copy
630 $ hg parents -q
625 $ hg parents -q
631 (4|13):33f7f61e6c5e (re)
626 (4|13):33f7f61e6c5e (re)
632 $ hg shelve -l
627 $ hg shelve -l
633 $ hg status
628 $ hg status
634 A foo/foo
629 A foo/foo
635 $ cat a/a
630 $ cat a/a
636 a
631 a
637 c
632 c
638 x
633 x
639
634
640 test keep and cleanup
635 test keep and cleanup
641
636
642 $ hg shelve
637 $ hg shelve
643 shelved as default
638 shelved as default
644 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
639 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
645 $ hg shelve --list
640 $ hg shelve --list
646 default (*)* changes to: create conflict (glob)
641 default (*)* changes to: create conflict (glob)
647 $ hg unshelve -k
642 $ hg unshelve -k
648 unshelving change 'default'
643 unshelving change 'default'
649 $ hg shelve --list
644 $ hg shelve --list
650 default (*)* changes to: create conflict (glob)
645 default (*)* changes to: create conflict (glob)
651 $ hg shelve --cleanup
646 $ hg shelve --cleanup
652 $ hg shelve --list
647 $ hg shelve --list
653
648
654 $ hg shelve --cleanup --delete
649 $ hg shelve --cleanup --delete
655 abort: options '--cleanup' and '--delete' may not be used together
650 abort: options '--cleanup' and '--delete' may not be used together
656 [255]
651 [255]
657 $ hg shelve --cleanup --patch
652 $ hg shelve --cleanup --patch
658 abort: options '--cleanup' and '--patch' may not be used together
653 abort: options '--cleanup' and '--patch' may not be used together
659 [255]
654 [255]
660 $ hg shelve --cleanup --message MESSAGE
655 $ hg shelve --cleanup --message MESSAGE
661 abort: options '--cleanup' and '--message' may not be used together
656 abort: options '--cleanup' and '--message' may not be used together
662 [255]
657 [255]
663
658
664 test bookmarks
659 test bookmarks
665
660
666 $ hg bookmark test
661 $ hg bookmark test
667 $ hg bookmark
662 $ hg bookmark
668 \* test (4|13):33f7f61e6c5e (re)
663 \* test (4|13):33f7f61e6c5e (re)
669 $ hg shelve
664 $ hg shelve
670 shelved as test
665 shelved as test
671 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
666 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
672 $ hg bookmark
667 $ hg bookmark
673 \* test (4|13):33f7f61e6c5e (re)
668 \* test (4|13):33f7f61e6c5e (re)
674 $ hg unshelve
669 $ hg unshelve
675 unshelving change 'test'
670 unshelving change 'test'
676 $ hg bookmark
671 $ hg bookmark
677 \* test (4|13):33f7f61e6c5e (re)
672 \* test (4|13):33f7f61e6c5e (re)
678
673
679 shelve should still work even if mq is disabled
674 shelve should still work even if mq is disabled
680
675
681 $ hg --config extensions.mq=! shelve
676 $ hg --config extensions.mq=! shelve
682 shelved as test
677 shelved as test
683 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
678 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
684 $ hg --config extensions.mq=! shelve --list
679 $ hg --config extensions.mq=! shelve --list
685 test (*)* changes to: create conflict (glob)
680 test (*)* changes to: create conflict (glob)
686 $ hg bookmark
681 $ hg bookmark
687 \* test (4|13):33f7f61e6c5e (re)
682 \* test (4|13):33f7f61e6c5e (re)
688 $ hg --config extensions.mq=! unshelve
683 $ hg --config extensions.mq=! unshelve
689 unshelving change 'test'
684 unshelving change 'test'
690 $ hg bookmark
685 $ hg bookmark
691 \* test (4|13):33f7f61e6c5e (re)
686 \* test (4|13):33f7f61e6c5e (re)
692
687
693 Recreate some conflict again
688 Recreate some conflict again
694
689
695 $ hg up -C -r 2e69b451d1ea
690 $ hg up -C -r 2e69b451d1ea
696 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
691 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
697 (leaving bookmark test)
692 (leaving bookmark test)
698 $ echo y >> a/a
693 $ echo y >> a/a
699 $ hg shelve
694 $ hg shelve
700 shelved as default
695 shelved as default
701 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
696 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
702 $ hg up test
697 $ hg up test
703 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
698 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
704 (activating bookmark test)
699 (activating bookmark test)
705 $ hg bookmark
700 $ hg bookmark
706 \* test (4|13):33f7f61e6c5e (re)
701 \* test (4|13):33f7f61e6c5e (re)
707 $ hg unshelve
702 $ hg unshelve
708 unshelving change 'default'
703 unshelving change 'default'
709 rebasing shelved changes
704 rebasing shelved changes
710 merging a/a
705 merging a/a
711 warning: conflicts while merging a/a! (edit, then use 'hg resolve --mark')
706 warning: conflicts while merging a/a! (edit, then use 'hg resolve --mark')
712 unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
707 unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
713 [1]
708 [1]
714 $ hg bookmark
709 $ hg bookmark
715 test (4|13):33f7f61e6c5e (re)
710 test (4|13):33f7f61e6c5e (re)
716
711
717 Test that resolving all conflicts in one direction (so that the rebase
712 Test that resolving all conflicts in one direction (so that the rebase
718 is a no-op), works (issue4398)
713 is a no-op), works (issue4398)
719
714
720 $ hg revert -a -r .
715 $ hg revert -a -r .
721 reverting a/a
716 reverting a/a
722 $ hg resolve -m a/a
717 $ hg resolve -m a/a
723 (no more unresolved files)
718 (no more unresolved files)
724 continue: hg unshelve --continue
719 continue: hg unshelve --continue
725 $ hg unshelve -c
720 $ hg unshelve -c
726 note: unshelved changes already existed in the working copy
721 note: unshelved changes already existed in the working copy
727 unshelve of 'default' complete
722 unshelve of 'default' complete
728 $ hg bookmark
723 $ hg bookmark
729 \* test (4|13):33f7f61e6c5e (re)
724 \* test (4|13):33f7f61e6c5e (re)
730 $ hg diff
725 $ hg diff
731 $ hg status
726 $ hg status
732 ? a/a.orig
727 ? a/a.orig
733 ? foo/foo
728 ? foo/foo
734 $ hg summary
729 $ hg summary
735 parent: (4|13):33f7f61e6c5e tip (re)
730 parent: (4|13):33f7f61e6c5e tip (re)
736 create conflict
731 create conflict
737 branch: default
732 branch: default
738 bookmarks: *test
733 bookmarks: *test
739 commit: 2 unknown (clean)
734 commit: 2 unknown (clean)
740 update: (current)
735 update: (current)
741 phases: 5 draft
736 phases: 5 draft
742
737
743 $ hg shelve --delete --stat
738 $ hg shelve --delete --stat
744 abort: options '--delete' and '--stat' may not be used together
739 abort: options '--delete' and '--stat' may not be used together
745 [255]
740 [255]
746 $ hg shelve --delete --name NAME
741 $ hg shelve --delete --name NAME
747 abort: options '--delete' and '--name' may not be used together
742 abort: options '--delete' and '--name' may not be used together
748 [255]
743 [255]
749
744
750 Test interactive shelve
745 Test interactive shelve
751 $ cat <<EOF >> $HGRCPATH
746 $ cat <<EOF >> $HGRCPATH
752 > [ui]
747 > [ui]
753 > interactive = true
748 > interactive = true
754 > EOF
749 > EOF
755 $ echo 'a' >> a/b
750 $ echo 'a' >> a/b
756 $ cat a/a >> a/b
751 $ cat a/a >> a/b
757 $ echo 'x' >> a/b
752 $ echo 'x' >> a/b
758 $ mv a/b a/a
753 $ mv a/b a/a
759 $ echo 'a' >> foo/foo
754 $ echo 'a' >> foo/foo
760 $ hg st
755 $ hg st
761 M a/a
756 M a/a
762 ? a/a.orig
757 ? a/a.orig
763 ? foo/foo
758 ? foo/foo
764 $ cat a/a
759 $ cat a/a
765 a
760 a
766 a
761 a
767 c
762 c
768 x
763 x
769 x
764 x
770 $ cat foo/foo
765 $ cat foo/foo
771 foo
766 foo
772 a
767 a
773 $ hg shelve --interactive --config ui.interactive=false
768 $ hg shelve --interactive --config ui.interactive=false
774 abort: running non-interactively
769 abort: running non-interactively
775 [255]
770 [255]
776 $ hg shelve --interactive << EOF
771 $ hg shelve --interactive << EOF
777 > y
772 > y
778 > y
773 > y
779 > n
774 > n
780 > EOF
775 > EOF
781 diff --git a/a/a b/a/a
776 diff --git a/a/a b/a/a
782 2 hunks, 2 lines changed
777 2 hunks, 2 lines changed
783 examine changes to 'a/a'?
778 examine changes to 'a/a'?
784 (enter ? for help) [Ynesfdaq?] y
779 (enter ? for help) [Ynesfdaq?] y
785
780
786 @@ -1,3 +1,4 @@
781 @@ -1,3 +1,4 @@
787 +a
782 +a
788 a
783 a
789 c
784 c
790 x
785 x
791 record change 1/2 to 'a/a'?
786 record change 1/2 to 'a/a'?
792 (enter ? for help) [Ynesfdaq?] y
787 (enter ? for help) [Ynesfdaq?] y
793
788
794 @@ -1,3 +2,4 @@
789 @@ -1,3 +2,4 @@
795 a
790 a
796 c
791 c
797 x
792 x
798 +x
793 +x
799 record change 2/2 to 'a/a'?
794 record change 2/2 to 'a/a'?
800 (enter ? for help) [Ynesfdaq?] n
795 (enter ? for help) [Ynesfdaq?] n
801
796
802 shelved as test
797 shelved as test
803 merging a/a
798 merging a/a
804 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
799 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
805 $ cat a/a
800 $ cat a/a
806 a
801 a
807 c
802 c
808 x
803 x
809 x
804 x
810 $ cat foo/foo
805 $ cat foo/foo
811 foo
806 foo
812 a
807 a
813 $ hg st
808 $ hg st
814 M a/a
809 M a/a
815 ? foo/foo
810 ? foo/foo
816 $ hg bookmark
811 $ hg bookmark
817 \* test (4|13):33f7f61e6c5e (re)
812 \* test (4|13):33f7f61e6c5e (re)
818 $ hg unshelve
813 $ hg unshelve
819 unshelving change 'test'
814 unshelving change 'test'
820 temporarily committing pending changes (restore with 'hg unshelve --abort')
815 temporarily committing pending changes (restore with 'hg unshelve --abort')
821 rebasing shelved changes
816 rebasing shelved changes
822 merging a/a
817 merging a/a
823 $ hg bookmark
818 $ hg bookmark
824 \* test (4|13):33f7f61e6c5e (re)
819 \* test (4|13):33f7f61e6c5e (re)
825 $ cat a/a
820 $ cat a/a
826 a
821 a
827 a
822 a
828 c
823 c
829 x
824 x
830 x
825 x
831
826
832 shelve --patch and shelve --stat should work with valid shelfnames
827 shelve --patch and shelve --stat should work with valid shelfnames
833
828
834 $ hg up --clean .
829 $ hg up --clean .
835 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
830 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
836 (leaving bookmark test)
831 (leaving bookmark test)
837 $ hg shelve --list
832 $ hg shelve --list
838 $ echo 'patch a' > shelf-patch-a
833 $ echo 'patch a' > shelf-patch-a
839 $ hg add shelf-patch-a
834 $ hg add shelf-patch-a
840 $ hg shelve
835 $ hg shelve
841 shelved as default
836 shelved as default
842 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
837 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
843 $ echo 'patch b' > shelf-patch-b
838 $ echo 'patch b' > shelf-patch-b
844 $ hg add shelf-patch-b
839 $ hg add shelf-patch-b
845 $ hg shelve
840 $ hg shelve
846 shelved as default-01
841 shelved as default-01
847 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
842 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
848 $ hg shelve --patch default default-01
843 $ hg shelve --patch default default-01
849 default-01 (*)* changes to: create conflict (glob)
844 default-01 (*)* changes to: create conflict (glob)
850
845
851 diff --git a/shelf-patch-b b/shelf-patch-b
846 diff --git a/shelf-patch-b b/shelf-patch-b
852 new file mode 100644
847 new file mode 100644
853 --- /dev/null
848 --- /dev/null
854 +++ b/shelf-patch-b
849 +++ b/shelf-patch-b
855 @@ -0,0 +1,1 @@
850 @@ -0,0 +1,1 @@
856 +patch b
851 +patch b
857 default (*)* changes to: create conflict (glob)
852 default (*)* changes to: create conflict (glob)
858
853
859 diff --git a/shelf-patch-a b/shelf-patch-a
854 diff --git a/shelf-patch-a b/shelf-patch-a
860 new file mode 100644
855 new file mode 100644
861 --- /dev/null
856 --- /dev/null
862 +++ b/shelf-patch-a
857 +++ b/shelf-patch-a
863 @@ -0,0 +1,1 @@
858 @@ -0,0 +1,1 @@
864 +patch a
859 +patch a
865 $ hg shelve --stat default default-01
860 $ hg shelve --stat default default-01
866 default-01 (*)* changes to: create conflict (glob)
861 default-01 (*)* changes to: create conflict (glob)
867 shelf-patch-b | 1 +
862 shelf-patch-b | 1 +
868 1 files changed, 1 insertions(+), 0 deletions(-)
863 1 files changed, 1 insertions(+), 0 deletions(-)
869 default (*)* changes to: create conflict (glob)
864 default (*)* changes to: create conflict (glob)
870 shelf-patch-a | 1 +
865 shelf-patch-a | 1 +
871 1 files changed, 1 insertions(+), 0 deletions(-)
866 1 files changed, 1 insertions(+), 0 deletions(-)
872 $ hg shelve --patch default
867 $ hg shelve --patch default
873 default (*)* changes to: create conflict (glob)
868 default (*)* changes to: create conflict (glob)
874
869
875 diff --git a/shelf-patch-a b/shelf-patch-a
870 diff --git a/shelf-patch-a b/shelf-patch-a
876 new file mode 100644
871 new file mode 100644
877 --- /dev/null
872 --- /dev/null
878 +++ b/shelf-patch-a
873 +++ b/shelf-patch-a
879 @@ -0,0 +1,1 @@
874 @@ -0,0 +1,1 @@
880 +patch a
875 +patch a
881 $ hg shelve --stat default
876 $ hg shelve --stat default
882 default (*)* changes to: create conflict (glob)
877 default (*)* changes to: create conflict (glob)
883 shelf-patch-a | 1 +
878 shelf-patch-a | 1 +
884 1 files changed, 1 insertions(+), 0 deletions(-)
879 1 files changed, 1 insertions(+), 0 deletions(-)
885 $ hg shelve --patch nonexistentshelf
880 $ hg shelve --patch nonexistentshelf
886 abort: cannot find shelf nonexistentshelf
881 abort: cannot find shelf nonexistentshelf
887 [255]
882 [255]
888 $ hg shelve --stat nonexistentshelf
883 $ hg shelve --stat nonexistentshelf
889 abort: cannot find shelf nonexistentshelf
884 abort: cannot find shelf nonexistentshelf
890 [255]
885 [255]
891 $ hg shelve --patch default nonexistentshelf
886 $ hg shelve --patch default nonexistentshelf
892 abort: cannot find shelf nonexistentshelf
887 abort: cannot find shelf nonexistentshelf
893 [255]
888 [255]
894
889
895 when the user asks for a patch, we assume they want the most recent shelve if
890 when the user asks for a patch, we assume they want the most recent shelve if
896 they don't provide a shelve name
891 they don't provide a shelve name
897
892
898 $ hg shelve --patch
893 $ hg shelve --patch
899 default-01 (*)* changes to: create conflict (glob)
894 default-01 (*)* changes to: create conflict (glob)
900
895
901 diff --git a/shelf-patch-b b/shelf-patch-b
896 diff --git a/shelf-patch-b b/shelf-patch-b
902 new file mode 100644
897 new file mode 100644
903 --- /dev/null
898 --- /dev/null
904 +++ b/shelf-patch-b
899 +++ b/shelf-patch-b
905 @@ -0,0 +1,1 @@
900 @@ -0,0 +1,1 @@
906 +patch b
901 +patch b
907
902
908 $ cd ..
903 $ cd ..
909
904
910 Shelve from general delta repo uses bundle2 on disk
905 Shelve from general delta repo uses bundle2 on disk
911 --------------------------------------------------
906 --------------------------------------------------
912
907
913 no general delta
908 no general delta
914
909
915 $ hg clone --pull repo bundle1 --config format.usegeneraldelta=0
910 $ hg clone --pull repo bundle1 --config format.usegeneraldelta=0
916 requesting all changes
911 requesting all changes
917 adding changesets
912 adding changesets
918 adding manifests
913 adding manifests
919 adding file changes
914 adding file changes
920 added 5 changesets with 8 changes to 6 files
915 added 5 changesets with 8 changes to 6 files
921 new changesets cc01e2b0c59f:33f7f61e6c5e
916 new changesets cc01e2b0c59f:33f7f61e6c5e
922 updating to branch default
917 updating to branch default
923 6 files updated, 0 files merged, 0 files removed, 0 files unresolved
918 6 files updated, 0 files merged, 0 files removed, 0 files unresolved
924 $ cd bundle1
919 $ cd bundle1
925 $ echo babar > jungle
920 $ echo babar > jungle
926 $ hg add jungle
921 $ hg add jungle
927 $ hg shelve
922 $ hg shelve
928 shelved as default
923 shelved as default
929 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
924 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
930 $ hg debugbundle .hg/shelved/*.hg
925 $ hg debugbundle .hg/shelved/*.hg
931 330882a04d2ce8487636b1fb292e5beea77fa1e3
926 330882a04d2ce8487636b1fb292e5beea77fa1e3
932 $ cd ..
927 $ cd ..
933
928
934 with general delta
929 with general delta
935
930
936 $ hg clone --pull repo bundle2 --config format.usegeneraldelta=1
931 $ hg clone --pull repo bundle2 --config format.usegeneraldelta=1
937 requesting all changes
932 requesting all changes
938 adding changesets
933 adding changesets
939 adding manifests
934 adding manifests
940 adding file changes
935 adding file changes
941 added 5 changesets with 8 changes to 6 files
936 added 5 changesets with 8 changes to 6 files
942 new changesets cc01e2b0c59f:33f7f61e6c5e
937 new changesets cc01e2b0c59f:33f7f61e6c5e
943 updating to branch default
938 updating to branch default
944 6 files updated, 0 files merged, 0 files removed, 0 files unresolved
939 6 files updated, 0 files merged, 0 files removed, 0 files unresolved
945 $ cd bundle2
940 $ cd bundle2
946 $ echo babar > jungle
941 $ echo babar > jungle
947 $ hg add jungle
942 $ hg add jungle
948 $ hg shelve
943 $ hg shelve
949 shelved as default
944 shelved as default
950 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
945 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
951 $ hg debugbundle .hg/shelved/*.hg
946 $ hg debugbundle .hg/shelved/*.hg
952 Stream params: {Compression: BZ}
947 Stream params: {Compression: BZ}
953 changegroup -- {nbchanges: 1, version: 02} (mandatory: True)
948 changegroup -- {nbchanges: 1, version: 02} (mandatory: True)
954 330882a04d2ce8487636b1fb292e5beea77fa1e3
949 330882a04d2ce8487636b1fb292e5beea77fa1e3
955
950
956 Test shelve --keep
951 Test shelve --keep
957
952
958 $ hg unshelve
953 $ hg unshelve
959 unshelving change 'default'
954 unshelving change 'default'
960 $ hg shelve --keep --list
955 $ hg shelve --keep --list
961 abort: options '--list' and '--keep' may not be used together
956 abort: options '--list' and '--keep' may not be used together
962 [255]
957 [255]
963 $ hg shelve --keep --patch
958 $ hg shelve --keep --patch
964 abort: options '--patch' and '--keep' may not be used together
959 abort: options '--patch' and '--keep' may not be used together
965 [255]
960 [255]
966 $ hg shelve --keep --delete
961 $ hg shelve --keep --delete
967 abort: options '--delete' and '--keep' may not be used together
962 abort: options '--delete' and '--keep' may not be used together
968 [255]
963 [255]
969 $ hg shelve --keep
964 $ hg shelve --keep
970 shelved as default
965 shelved as default
971 $ hg diff
966 $ hg diff
972 diff --git a/jungle b/jungle
967 diff --git a/jungle b/jungle
973 new file mode 100644
968 new file mode 100644
974 --- /dev/null
969 --- /dev/null
975 +++ b/jungle
970 +++ b/jungle
976 @@ -0,0 +1,1 @@
971 @@ -0,0 +1,1 @@
977 +babar
972 +babar
978
973
979 Test shelve --delete
974 Test shelve --delete
980
975
981 $ hg shelve --list
976 $ hg shelve --list
982 default (*s ago) changes to: create conflict (glob)
977 default (*s ago) changes to: create conflict (glob)
983 $ hg shelve --delete doesnotexist
978 $ hg shelve --delete doesnotexist
984 abort: shelved change 'doesnotexist' not found
979 abort: shelved change 'doesnotexist' not found
985 [255]
980 [255]
986 $ hg shelve --delete default
981 $ hg shelve --delete default
987
982
988 $ cd ..
983 $ cd ..
989
984
990 Test visibility of in-memory changes inside transaction to external hook
985 Test visibility of in-memory changes inside transaction to external hook
991 ------------------------------------------------------------------------
986 ------------------------------------------------------------------------
992
987
993 $ cd repo
988 $ cd repo
994
989
995 $ echo xxxx >> x
990 $ echo xxxx >> x
996 $ hg commit -m "#5: changes to invoke rebase"
991 $ hg commit -m "#5: changes to invoke rebase"
997
992
998 $ cat > $TESTTMP/checkvisibility.sh <<EOF
993 $ cat > $TESTTMP/checkvisibility.sh <<EOF
999 > echo "==== \$1:"
994 > echo "==== \$1:"
1000 > hg parents --template "VISIBLE {rev}:{node|short}\n"
995 > hg parents --template "VISIBLE {rev}:{node|short}\n"
1001 > # test that pending changes are hidden
996 > # test that pending changes are hidden
1002 > unset HG_PENDING
997 > unset HG_PENDING
1003 > hg parents --template "ACTUAL {rev}:{node|short}\n"
998 > hg parents --template "ACTUAL {rev}:{node|short}\n"
1004 > echo "===="
999 > echo "===="
1005 > EOF
1000 > EOF
1006
1001
1007 $ cat >> .hg/hgrc <<EOF
1002 $ cat >> .hg/hgrc <<EOF
1008 > [defaults]
1003 > [defaults]
1009 > # to fix hash id of temporary revisions
1004 > # to fix hash id of temporary revisions
1010 > unshelve = --date '0 0'
1005 > unshelve = --date '0 0'
1011 > EOF
1006 > EOF
1012
1007
1013 "hg unshelve" at REV5 implies steps below:
1008 "hg unshelve" at REV5 implies steps below:
1014
1009
1015 (1) commit changes in the working directory (REV6)
1010 (1) commit changes in the working directory (REV6)
1016 (2) unbundle shelved revision (REV7)
1011 (2) unbundle shelved revision (REV7)
1017 (3) rebase: merge REV7 into REV6 (REV6 => REV6, REV7)
1012 (3) rebase: merge REV7 into REV6 (REV6 => REV6, REV7)
1018 (4) rebase: commit merged revision (REV8)
1013 (4) rebase: commit merged revision (REV8)
1019 (5) rebase: update to REV6 (REV8 => REV6)
1014 (5) rebase: update to REV6 (REV8 => REV6)
1020 (6) update to REV5 (REV6 => REV5)
1015 (6) update to REV5 (REV6 => REV5)
1021 (7) abort transaction
1016 (7) abort transaction
1022
1017
1023 == test visibility to external preupdate hook
1018 == test visibility to external preupdate hook
1024
1019
1025 $ cat >> .hg/hgrc <<EOF
1020 $ cat >> .hg/hgrc <<EOF
1026 > [hooks]
1021 > [hooks]
1027 > preupdate.visibility = sh $TESTTMP/checkvisibility.sh preupdate
1022 > preupdate.visibility = sh $TESTTMP/checkvisibility.sh preupdate
1028 > EOF
1023 > EOF
1029
1024
1030 $ echo nnnn >> n
1025 $ echo nnnn >> n
1031
1026
1032 $ sh $TESTTMP/checkvisibility.sh before-unshelving
1027 $ sh $TESTTMP/checkvisibility.sh before-unshelving
1033 ==== before-unshelving:
1028 ==== before-unshelving:
1034 VISIBLE (5|19):703117a2acfb (re)
1029 VISIBLE (5|19):703117a2acfb (re)
1035 ACTUAL (5|19):703117a2acfb (re)
1030 ACTUAL (5|19):703117a2acfb (re)
1036 ====
1031 ====
1037
1032
1038 $ hg unshelve --keep default
1033 $ hg unshelve --keep default
1039 temporarily committing pending changes (restore with 'hg unshelve --abort')
1034 temporarily committing pending changes (restore with 'hg unshelve --abort')
1040 rebasing shelved changes
1035 rebasing shelved changes
1041 ==== preupdate:
1036 ==== preupdate:
1042 VISIBLE (6|20):54c00d20fb3f (re)
1037 VISIBLE (6|20):54c00d20fb3f (re)
1043 ACTUAL (5|19):703117a2acfb (re)
1038 ACTUAL (5|19):703117a2acfb (re)
1044 ====
1039 ====
1045 ==== preupdate:
1040 ==== preupdate:
1046 VISIBLE (8|21):8efe6f7537dc (re)
1041 VISIBLE (8|21):8efe6f7537dc (re)
1047 ACTUAL (5|19):703117a2acfb (re)
1042 ACTUAL (5|19):703117a2acfb (re)
1048 ====
1043 ====
1049 ==== preupdate:
1044 ==== preupdate:
1050 VISIBLE (6|20):54c00d20fb3f (re)
1045 VISIBLE (6|20):54c00d20fb3f (re)
1051 ACTUAL (5|19):703117a2acfb (re)
1046 ACTUAL (5|19):703117a2acfb (re)
1052 ====
1047 ====
1053
1048
1054 $ cat >> .hg/hgrc <<EOF
1049 $ cat >> .hg/hgrc <<EOF
1055 > [hooks]
1050 > [hooks]
1056 > preupdate.visibility =
1051 > preupdate.visibility =
1057 > EOF
1052 > EOF
1058
1053
1059 $ sh $TESTTMP/checkvisibility.sh after-unshelving
1054 $ sh $TESTTMP/checkvisibility.sh after-unshelving
1060 ==== after-unshelving:
1055 ==== after-unshelving:
1061 VISIBLE (5|19):703117a2acfb (re)
1056 VISIBLE (5|19):703117a2acfb (re)
1062 ACTUAL (5|19):703117a2acfb (re)
1057 ACTUAL (5|19):703117a2acfb (re)
1063 ====
1058 ====
1064
1059
1065 == test visibility to external update hook
1060 == test visibility to external update hook
1066
1061
1067 $ hg update -q -C 703117a2acfb
1062 $ hg update -q -C 703117a2acfb
1068
1063
1069 $ cat >> .hg/hgrc <<EOF
1064 $ cat >> .hg/hgrc <<EOF
1070 > [hooks]
1065 > [hooks]
1071 > update.visibility = sh $TESTTMP/checkvisibility.sh update
1066 > update.visibility = sh $TESTTMP/checkvisibility.sh update
1072 > EOF
1067 > EOF
1073
1068
1074 $ echo nnnn >> n
1069 $ echo nnnn >> n
1075
1070
1076 $ sh $TESTTMP/checkvisibility.sh before-unshelving
1071 $ sh $TESTTMP/checkvisibility.sh before-unshelving
1077 ==== before-unshelving:
1072 ==== before-unshelving:
1078 VISIBLE (5|19):703117a2acfb (re)
1073 VISIBLE (5|19):703117a2acfb (re)
1079 ACTUAL (5|19):703117a2acfb (re)
1074 ACTUAL (5|19):703117a2acfb (re)
1080 ====
1075 ====
1081
1076
1082 $ hg unshelve --keep default
1077 $ hg unshelve --keep default
1083 temporarily committing pending changes (restore with 'hg unshelve --abort')
1078 temporarily committing pending changes (restore with 'hg unshelve --abort')
1084 rebasing shelved changes
1079 rebasing shelved changes
1085 ==== update:
1080 ==== update:
1086 VISIBLE (6|20):54c00d20fb3f (re)
1081 VISIBLE (6|20):54c00d20fb3f (re)
1087 VISIBLE 1?7:492ed9d705e5 (re)
1082 VISIBLE 1?7:492ed9d705e5 (re)
1088 ACTUAL (5|19):703117a2acfb (re)
1083 ACTUAL (5|19):703117a2acfb (re)
1089 ====
1084 ====
1090 ==== update:
1085 ==== update:
1091 VISIBLE (6|20):54c00d20fb3f (re)
1086 VISIBLE (6|20):54c00d20fb3f (re)
1092 ACTUAL (5|19):703117a2acfb (re)
1087 ACTUAL (5|19):703117a2acfb (re)
1093 ====
1088 ====
1094 ==== update:
1089 ==== update:
1095 VISIBLE (5|19):703117a2acfb (re)
1090 VISIBLE (5|19):703117a2acfb (re)
1096 ACTUAL (5|19):703117a2acfb (re)
1091 ACTUAL (5|19):703117a2acfb (re)
1097 ====
1092 ====
1098
1093
1099 $ cat >> .hg/hgrc <<EOF
1094 $ cat >> .hg/hgrc <<EOF
1100 > [hooks]
1095 > [hooks]
1101 > update.visibility =
1096 > update.visibility =
1102 > EOF
1097 > EOF
1103
1098
1104 $ sh $TESTTMP/checkvisibility.sh after-unshelving
1099 $ sh $TESTTMP/checkvisibility.sh after-unshelving
1105 ==== after-unshelving:
1100 ==== after-unshelving:
1106 VISIBLE (5|19):703117a2acfb (re)
1101 VISIBLE (5|19):703117a2acfb (re)
1107 ACTUAL (5|19):703117a2acfb (re)
1102 ACTUAL (5|19):703117a2acfb (re)
1108 ====
1103 ====
1109
1104
1110 $ cd ..
1105 $ cd ..
1111
1106
1112 Keep active bookmark while (un)shelving even on shared repo (issue4940)
1107 Keep active bookmark while (un)shelving even on shared repo (issue4940)
1113 -----------------------------------------------------------------------
1108 -----------------------------------------------------------------------
1114
1109
1115 $ cat <<EOF >> $HGRCPATH
1110 $ cat <<EOF >> $HGRCPATH
1116 > [extensions]
1111 > [extensions]
1117 > share =
1112 > share =
1118 > EOF
1113 > EOF
1119
1114
1120 $ hg bookmarks -R repo
1115 $ hg bookmarks -R repo
1121 test (4|13):33f7f61e6c5e (re)
1116 test (4|13):33f7f61e6c5e (re)
1122 $ hg share -B repo share
1117 $ hg share -B repo share
1123 updating working directory
1118 updating working directory
1124 6 files updated, 0 files merged, 0 files removed, 0 files unresolved
1119 6 files updated, 0 files merged, 0 files removed, 0 files unresolved
1125 $ cd share
1120 $ cd share
1126
1121
1127 $ hg bookmarks
1122 $ hg bookmarks
1128 test (4|13):33f7f61e6c5e (re)
1123 test (4|13):33f7f61e6c5e (re)
1129 $ hg bookmarks foo
1124 $ hg bookmarks foo
1130 $ hg bookmarks
1125 $ hg bookmarks
1131 \* foo (5|19):703117a2acfb (re)
1126 \* foo (5|19):703117a2acfb (re)
1132 test (4|13):33f7f61e6c5e (re)
1127 test (4|13):33f7f61e6c5e (re)
1133 $ echo x >> x
1128 $ echo x >> x
1134 $ hg shelve
1129 $ hg shelve
1135 shelved as foo
1130 shelved as foo
1136 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1131 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1137 $ hg bookmarks
1132 $ hg bookmarks
1138 \* foo (5|19):703117a2acfb (re)
1133 \* foo (5|19):703117a2acfb (re)
1139 test (4|13):33f7f61e6c5e (re)
1134 test (4|13):33f7f61e6c5e (re)
1140
1135
1141 $ hg unshelve
1136 $ hg unshelve
1142 unshelving change 'foo'
1137 unshelving change 'foo'
1143 $ hg bookmarks
1138 $ hg bookmarks
1144 \* foo (5|19):703117a2acfb (re)
1139 \* foo (5|19):703117a2acfb (re)
1145 test (4|13):33f7f61e6c5e (re)
1140 test (4|13):33f7f61e6c5e (re)
1146
1141
1147 $ cd ..
1142 $ cd ..
1148
1143
1149 Abort unshelve while merging (issue5123)
1144 Abort unshelve while merging (issue5123)
1150 ----------------------------------------
1145 ----------------------------------------
1151
1146
1152 $ hg init issue5123
1147 $ hg init issue5123
1153 $ cd issue5123
1148 $ cd issue5123
1154 $ echo > a
1149 $ echo > a
1155 $ hg ci -Am a
1150 $ hg ci -Am a
1156 adding a
1151 adding a
1157 $ hg co null
1152 $ hg co null
1158 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1153 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1159 $ echo > b
1154 $ echo > b
1160 $ hg ci -Am b
1155 $ hg ci -Am b
1161 adding b
1156 adding b
1162 created new head
1157 created new head
1163 $ echo > c
1158 $ echo > c
1164 $ hg add c
1159 $ hg add c
1165 $ hg shelve
1160 $ hg shelve
1166 shelved as default
1161 shelved as default
1167 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1162 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1168 $ hg co 1
1163 $ hg co 1
1169 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1164 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1170 $ hg merge 0
1165 $ hg merge 0
1171 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1166 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1172 (branch merge, don't forget to commit)
1167 (branch merge, don't forget to commit)
1173 -- successful merge with two parents
1168 -- successful merge with two parents
1174 $ hg log -G
1169 $ hg log -G
1175 @ changeset: 1:406bf70c274f
1170 @ changeset: 1:406bf70c274f
1176 tag: tip
1171 tag: tip
1177 parent: -1:000000000000
1172 parent: -1:000000000000
1178 user: test
1173 user: test
1179 date: Thu Jan 01 00:00:00 1970 +0000
1174 date: Thu Jan 01 00:00:00 1970 +0000
1180 summary: b
1175 summary: b
1181
1176
1182 @ changeset: 0:ada8c9eb8252
1177 @ changeset: 0:ada8c9eb8252
1183 user: test
1178 user: test
1184 date: Thu Jan 01 00:00:00 1970 +0000
1179 date: Thu Jan 01 00:00:00 1970 +0000
1185 summary: a
1180 summary: a
1186
1181
1187 -- trying to pull in the shelve bits
1182 -- trying to pull in the shelve bits
1188 -- unshelve should abort otherwise, it'll eat my second parent.
1183 -- unshelve should abort otherwise, it'll eat my second parent.
1189 $ hg unshelve
1184 $ hg unshelve
1190 abort: outstanding uncommitted merge
1185 abort: outstanding uncommitted merge
1191 (use 'hg commit' or 'hg merge --abort')
1186 (use 'hg commit' or 'hg merge --abort')
1192 [255]
1187 [255]
1193
1188
1194 $ cd ..
1189 $ cd ..
1195
1190
1196 -- test for interactive mode on unshelve
1191 -- test for interactive mode on unshelve
1197
1192
1198 $ hg init a
1193 $ hg init a
1199 $ cd a
1194 $ cd a
1200 $ echo > b
1195 $ echo > b
1201 $ hg ci -Am b
1196 $ hg ci -Am b
1202 adding b
1197 adding b
1203 $ echo > c
1198 $ echo > c
1204 $ echo > d
1199 $ echo > d
1205 $ hg add .
1200 $ hg add .
1206 adding c
1201 adding c
1207 adding d
1202 adding d
1208 $ hg shelve
1203 $ hg shelve
1209 shelved as default
1204 shelved as default
1210 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
1205 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
1211 $ echo > e
1206 $ echo > e
1212 $ hg add e
1207 $ hg add e
1213 $ hg ci -m e
1208 $ hg ci -m e
1214 $ hg shelve --patch
1209 $ hg shelve --patch
1215 default (*s ago) changes to: b (glob)
1210 default (*s ago) changes to: b (glob)
1216
1211
1217 diff --git a/c b/c
1212 diff --git a/c b/c
1218 new file mode 100644
1213 new file mode 100644
1219 --- /dev/null
1214 --- /dev/null
1220 +++ b/c
1215 +++ b/c
1221 @@ -0,0 +1,1 @@
1216 @@ -0,0 +1,1 @@
1222 +
1217 +
1223 diff --git a/d b/d
1218 diff --git a/d b/d
1224 new file mode 100644
1219 new file mode 100644
1225 --- /dev/null
1220 --- /dev/null
1226 +++ b/d
1221 +++ b/d
1227 @@ -0,0 +1,1 @@
1222 @@ -0,0 +1,1 @@
1228 +
1223 +
1229 $ hg unshelve -i <<EOF
1224 $ hg unshelve -i <<EOF
1230 > y
1225 > y
1231 > y
1226 > y
1232 > y
1227 > y
1233 > n
1228 > n
1234 > EOF
1229 > EOF
1235 unshelving change 'default'
1230 unshelving change 'default'
1236 rebasing shelved changes
1231 rebasing shelved changes
1237 diff --git a/c b/c
1232 diff --git a/c b/c
1238 new file mode 100644
1233 new file mode 100644
1239 examine changes to 'c'?
1234 examine changes to 'c'?
1240 (enter ? for help) [Ynesfdaq?] y
1235 (enter ? for help) [Ynesfdaq?] y
1241
1236
1242 @@ -0,0 +1,1 @@
1237 @@ -0,0 +1,1 @@
1243 +
1238 +
1244 record change 1/2 to 'c'?
1239 record change 1/2 to 'c'?
1245 (enter ? for help) [Ynesfdaq?] y
1240 (enter ? for help) [Ynesfdaq?] y
1246
1241
1247 diff --git a/d b/d
1242 diff --git a/d b/d
1248 new file mode 100644
1243 new file mode 100644
1249 examine changes to 'd'?
1244 examine changes to 'd'?
1250 (enter ? for help) [Ynesfdaq?] y
1245 (enter ? for help) [Ynesfdaq?] y
1251
1246
1252 @@ -0,0 +1,1 @@
1247 @@ -0,0 +1,1 @@
1253 +
1248 +
1254 record change 2/2 to 'd'?
1249 record change 2/2 to 'd'?
1255 (enter ? for help) [Ynesfdaq?] n
1250 (enter ? for help) [Ynesfdaq?] n
1256
1251
1257 $ ls
1252 $ ls
1258 b
1253 b
1259 c
1254 c
1260 e
1255 e
1261 -- shelve should not contain `c` now
1256 -- shelve should not contain `c` now
1262 $ hg shelve --patch
1257 $ hg shelve --patch
1263 default (*s ago) changes to: b (glob)
1258 default (*s ago) changes to: b (glob)
1264
1259
1265 diff --git a/d b/d
1260 diff --git a/d b/d
1266 new file mode 100644
1261 new file mode 100644
1267 --- /dev/null
1262 --- /dev/null
1268 +++ b/d
1263 +++ b/d
1269 @@ -0,0 +1,1 @@
1264 @@ -0,0 +1,1 @@
1270 +
1265 +
1271 $ hg unshelve -i <<EOF
1266 $ hg unshelve -i <<EOF
1272 > y
1267 > y
1273 > y
1268 > y
1274 > EOF
1269 > EOF
1275 unshelving change 'default'
1270 unshelving change 'default'
1276 temporarily committing pending changes (restore with 'hg unshelve --abort')
1271 temporarily committing pending changes (restore with 'hg unshelve --abort')
1277 rebasing shelved changes
1272 rebasing shelved changes
1278 diff --git a/d b/d
1273 diff --git a/d b/d
1279 new file mode 100644
1274 new file mode 100644
1280 examine changes to 'd'?
1275 examine changes to 'd'?
1281 (enter ? for help) [Ynesfdaq?] y
1276 (enter ? for help) [Ynesfdaq?] y
1282
1277
1283 @@ -0,0 +1,1 @@
1278 @@ -0,0 +1,1 @@
1284 +
1279 +
1285 record this change to 'd'?
1280 record this change to 'd'?
1286 (enter ? for help) [Ynesfdaq?] y
1281 (enter ? for help) [Ynesfdaq?] y
1287
1282
1288
1283
1289 $ hg status -v
1284 $ hg status -v
1290 A c
1285 A c
1291 A d
1286 A d
1292 $ ls
1287 $ ls
1293 b
1288 b
1294 c
1289 c
1295 d
1290 d
1296 e
1291 e
1297 $ hg shelve --list
1292 $ hg shelve --list
1298
1293
1299 -- now, unshelve selected changes from a file
1294 -- now, unshelve selected changes from a file
1300
1295
1301 $ echo B > foo
1296 $ echo B > foo
1302 $ hg add foo
1297 $ hg add foo
1303 $ hg ci -m 'add B to foo'
1298 $ hg ci -m 'add B to foo'
1304 $ cat > foo <<EOF
1299 $ cat > foo <<EOF
1305 > A
1300 > A
1306 > B
1301 > B
1307 > C
1302 > C
1308 > EOF
1303 > EOF
1309 $ echo > garbage
1304 $ echo > garbage
1310 $ hg st
1305 $ hg st
1311 M foo
1306 M foo
1312 ? garbage
1307 ? garbage
1313 $ hg shelve --unknown
1308 $ hg shelve --unknown
1314 shelved as default
1309 shelved as default
1315 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
1310 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
1316 $ cat foo
1311 $ cat foo
1317 B
1312 B
1318 $ hg unshelve -i <<EOF
1313 $ hg unshelve -i <<EOF
1319 > y
1314 > y
1320 > y
1315 > y
1321 > n
1316 > n
1322 > y
1317 > y
1323 > y
1318 > y
1324 > EOF
1319 > EOF
1325 unshelving change 'default'
1320 unshelving change 'default'
1326 rebasing shelved changes
1321 rebasing shelved changes
1327 diff --git a/foo b/foo
1322 diff --git a/foo b/foo
1328 2 hunks, 2 lines changed
1323 2 hunks, 2 lines changed
1329 examine changes to 'foo'?
1324 examine changes to 'foo'?
1330 (enter ? for help) [Ynesfdaq?] y
1325 (enter ? for help) [Ynesfdaq?] y
1331
1326
1332 @@ -1,1 +1,2 @@
1327 @@ -1,1 +1,2 @@
1333 +A
1328 +A
1334 B
1329 B
1335 record change 1/3 to 'foo'?
1330 record change 1/3 to 'foo'?
1336 (enter ? for help) [Ynesfdaq?] y
1331 (enter ? for help) [Ynesfdaq?] y
1337
1332
1338 @@ -1,1 +2,2 @@
1333 @@ -1,1 +2,2 @@
1339 B
1334 B
1340 +C
1335 +C
1341 record change 2/3 to 'foo'?
1336 record change 2/3 to 'foo'?
1342 (enter ? for help) [Ynesfdaq?] n
1337 (enter ? for help) [Ynesfdaq?] n
1343
1338
1344 diff --git a/garbage b/garbage
1339 diff --git a/garbage b/garbage
1345 new file mode 100644
1340 new file mode 100644
1346 examine changes to 'garbage'?
1341 examine changes to 'garbage'?
1347 (enter ? for help) [Ynesfdaq?] y
1342 (enter ? for help) [Ynesfdaq?] y
1348
1343
1349 @@ -0,0 +1,1 @@
1344 @@ -0,0 +1,1 @@
1350 +
1345 +
1351 record change 3/3 to 'garbage'?
1346 record change 3/3 to 'garbage'?
1352 (enter ? for help) [Ynesfdaq?] y
1347 (enter ? for help) [Ynesfdaq?] y
1353
1348
1354 $ hg st
1349 $ hg st
1355 M foo
1350 M foo
1356 ? garbage
1351 ? garbage
1357 $ cat foo
1352 $ cat foo
1358 A
1353 A
1359 B
1354 B
1360 $ hg shelve --patch
1355 $ hg shelve --patch
1361 default (*s ago) changes to: add B to foo (glob)
1356 default (*s ago) changes to: add B to foo (glob)
1362
1357
1363 diff --git a/foo b/foo
1358 diff --git a/foo b/foo
1364 --- a/foo
1359 --- a/foo
1365 +++ b/foo
1360 +++ b/foo
1366 @@ -1,2 +1,3 @@
1361 @@ -1,2 +1,3 @@
1367 A
1362 A
1368 B
1363 B
1369 +C
1364 +C
1370
1365
1371 -- unshelve interactive on conflicts
1366 -- unshelve interactive on conflicts
1372
1367
1373 $ echo A >> bar1
1368 $ echo A >> bar1
1374 $ echo A >> bar2
1369 $ echo A >> bar2
1375 $ hg add bar1 bar2
1370 $ hg add bar1 bar2
1376 $ hg ci -m 'add A to bars'
1371 $ hg ci -m 'add A to bars'
1377 $ echo B >> bar1
1372 $ echo B >> bar1
1378 $ echo B >> bar2
1373 $ echo B >> bar2
1379 $ hg shelve
1374 $ hg shelve
1380 shelved as default-01
1375 shelved as default-01
1381 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
1376 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
1382 $ echo C >> bar1
1377 $ echo C >> bar1
1383 $ echo C >> bar2
1378 $ echo C >> bar2
1384 $ hg ci -m 'add C to bars'
1379 $ hg ci -m 'add C to bars'
1385 $ hg unshelve -i
1380 $ hg unshelve -i
1386 unshelving change 'default-01'
1381 unshelving change 'default-01'
1387 rebasing shelved changes
1382 rebasing shelved changes
1388 merging bar1
1383 merging bar1
1389 merging bar2
1384 merging bar2
1390 warning: conflicts while merging bar1! (edit, then use 'hg resolve --mark')
1385 warning: conflicts while merging bar1! (edit, then use 'hg resolve --mark')
1391 warning: conflicts while merging bar2! (edit, then use 'hg resolve --mark')
1386 warning: conflicts while merging bar2! (edit, then use 'hg resolve --mark')
1392 unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
1387 unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
1393 [1]
1388 [1]
1394
1389
1395 $ cat > bar1 <<EOF
1390 $ cat > bar1 <<EOF
1396 > A
1391 > A
1397 > B
1392 > B
1398 > C
1393 > C
1399 > EOF
1394 > EOF
1400 $ cat > bar2 <<EOF
1395 $ cat > bar2 <<EOF
1401 > A
1396 > A
1402 > B
1397 > B
1403 > C
1398 > C
1404 > EOF
1399 > EOF
1405 $ hg resolve -m bar1 bar2
1400 $ hg resolve -m bar1 bar2
1406 (no more unresolved files)
1401 (no more unresolved files)
1407 continue: hg unshelve --continue
1402 continue: hg unshelve --continue
1408
1403
1409 -- using --continue with --interactive should throw an error
1404 -- using --continue with --interactive should throw an error
1410 $ hg unshelve --continue -i
1405 $ hg unshelve --continue -i
1411 abort: cannot use both continue and interactive
1406 abort: cannot use both continue and interactive
1412 [255]
1407 [255]
1413
1408
1414 $ cat bar1
1409 $ cat bar1
1415 A
1410 A
1416 B
1411 B
1417 C
1412 C
1418
1413
1419 #if stripbased
1414 #if stripbased
1420 $ hg log -r 3:: -G
1415 $ hg log -r 3:: -G
1421 @ changeset: 5:f1d5f53e397b
1416 @ changeset: 5:f1d5f53e397b
1422 | tag: tip
1417 | tag: tip
1423 | parent: 3:e28fd7fa7938
1418 | parent: 3:e28fd7fa7938
1424 | user: shelve@localhost
1419 | user: shelve@localhost
1425 | date: Thu Jan 01 00:00:00 1970 +0000
1420 | date: Thu Jan 01 00:00:00 1970 +0000
1426 | summary: changes to: add A to bars
1421 | summary: changes to: add A to bars
1427 |
1422 |
1428 | @ changeset: 4:fe451a778c81
1423 | @ changeset: 4:fe451a778c81
1429 |/ user: test
1424 |/ user: test
1430 | date: Thu Jan 01 00:00:00 1970 +0000
1425 | date: Thu Jan 01 00:00:00 1970 +0000
1431 | summary: add C to bars
1426 | summary: add C to bars
1432 |
1427 |
1433 o changeset: 3:e28fd7fa7938
1428 o changeset: 3:e28fd7fa7938
1434 | user: test
1429 | user: test
1435 ~ date: Thu Jan 01 00:00:00 1970 +0000
1430 ~ date: Thu Jan 01 00:00:00 1970 +0000
1436 summary: add A to bars
1431 summary: add A to bars
1437
1432
1438 #endif
1433 #endif
1439
1434
1440 $ hg unshelve --continue <<EOF
1435 $ hg unshelve --continue <<EOF
1441 > y
1436 > y
1442 > y
1437 > y
1443 > y
1438 > y
1444 > n
1439 > n
1445 > EOF
1440 > EOF
1446 diff --git a/bar1 b/bar1
1441 diff --git a/bar1 b/bar1
1447 1 hunks, 1 lines changed
1442 1 hunks, 1 lines changed
1448 examine changes to 'bar1'?
1443 examine changes to 'bar1'?
1449 (enter ? for help) [Ynesfdaq?] y
1444 (enter ? for help) [Ynesfdaq?] y
1450
1445
1451 @@ -1,2 +1,3 @@
1446 @@ -1,2 +1,3 @@
1452 A
1447 A
1453 +B
1448 +B
1454 C
1449 C
1455 record change 1/2 to 'bar1'?
1450 record change 1/2 to 'bar1'?
1456 (enter ? for help) [Ynesfdaq?] y
1451 (enter ? for help) [Ynesfdaq?] y
1457
1452
1458 diff --git a/bar2 b/bar2
1453 diff --git a/bar2 b/bar2
1459 1 hunks, 1 lines changed
1454 1 hunks, 1 lines changed
1460 examine changes to 'bar2'?
1455 examine changes to 'bar2'?
1461 (enter ? for help) [Ynesfdaq?] y
1456 (enter ? for help) [Ynesfdaq?] y
1462
1457
1463 @@ -1,2 +1,3 @@
1458 @@ -1,2 +1,3 @@
1464 A
1459 A
1465 +B
1460 +B
1466 C
1461 C
1467 record change 2/2 to 'bar2'?
1462 record change 2/2 to 'bar2'?
1468 (enter ? for help) [Ynesfdaq?] n
1463 (enter ? for help) [Ynesfdaq?] n
1469
1464
1470 unshelve of 'default-01' complete
1465 unshelve of 'default-01' complete
1471
1466
1472 #if stripbased
1467 #if stripbased
1473 $ hg log -r 3:: -G
1468 $ hg log -r 3:: -G
1474 @ changeset: 4:fe451a778c81
1469 @ changeset: 4:fe451a778c81
1475 | tag: tip
1470 | tag: tip
1476 | user: test
1471 | user: test
1477 | date: Thu Jan 01 00:00:00 1970 +0000
1472 | date: Thu Jan 01 00:00:00 1970 +0000
1478 | summary: add C to bars
1473 | summary: add C to bars
1479 |
1474 |
1480 o changeset: 3:e28fd7fa7938
1475 o changeset: 3:e28fd7fa7938
1481 | user: test
1476 | user: test
1482 ~ date: Thu Jan 01 00:00:00 1970 +0000
1477 ~ date: Thu Jan 01 00:00:00 1970 +0000
1483 summary: add A to bars
1478 summary: add A to bars
1484
1479
1485 #endif
1480 #endif
1486
1481
1487 $ hg unshelve --continue
1482 $ hg unshelve --continue
1488 abort: no unshelve in progress
1483 abort: no unshelve in progress
1489 [255]
1484 [255]
1490
1485
1491 $ hg shelve --list
1486 $ hg shelve --list
1492 default-01 (*)* changes to: add A to bars (glob)
1487 default-01 (*)* changes to: add A to bars (glob)
1493 default (*)* changes to: add B to foo (glob)
1488 default (*)* changes to: add B to foo (glob)
1494 $ hg unshelve -n default-01 -i <<EOF
1489 $ hg unshelve -n default-01 -i <<EOF
1495 > y
1490 > y
1496 > y
1491 > y
1497 > EOF
1492 > EOF
1498 temporarily committing pending changes (restore with 'hg unshelve --abort')
1493 temporarily committing pending changes (restore with 'hg unshelve --abort')
1499 rebasing shelved changes
1494 rebasing shelved changes
1500 diff --git a/bar2 b/bar2
1495 diff --git a/bar2 b/bar2
1501 1 hunks, 1 lines changed
1496 1 hunks, 1 lines changed
1502 examine changes to 'bar2'?
1497 examine changes to 'bar2'?
1503 (enter ? for help) [Ynesfdaq?] y
1498 (enter ? for help) [Ynesfdaq?] y
1504
1499
1505 @@ -1,2 +1,3 @@
1500 @@ -1,2 +1,3 @@
1506 A
1501 A
1507 +B
1502 +B
1508 C
1503 C
1509 record this change to 'bar2'?
1504 record this change to 'bar2'?
1510 (enter ? for help) [Ynesfdaq?] y
1505 (enter ? for help) [Ynesfdaq?] y
1511
1506
1512 -- test for --interactive --keep
1507 -- test for --interactive --keep
1513 $ hg unshelve -i --keep
1508 $ hg unshelve -i --keep
1514 abort: --keep on --interactive is not yet supported
1509 abort: --keep on --interactive is not yet supported
1515 [255]
1510 [255]
General Comments 0
You need to be logged in to leave comments. Login now