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