##// END OF EJS Templates
shelve: add interactive mode...
Laurent Charignon -
r24478:95cbc77c default
parent child Browse files
Show More
@@ -1,725 +1,732 b''
1 1 # shelve.py - save/restore working directory state
2 2 #
3 3 # Copyright 2013 Facebook, Inc.
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 """save and restore changes to the working directory
9 9
10 10 The "hg shelve" command saves changes made to the working directory
11 11 and reverts those changes, resetting the working directory to a clean
12 12 state.
13 13
14 14 Later on, the "hg unshelve" command restores the changes saved by "hg
15 15 shelve". Changes can be restored even after updating to a different
16 16 parent, in which case Mercurial's merge machinery will resolve any
17 17 conflicts if necessary.
18 18
19 19 You can have more than one shelved change outstanding at a time; each
20 20 shelved change has a distinct name. For details, see the help for "hg
21 21 shelve".
22 22 """
23 23
24 24 from mercurial.i18n import _
25 25 from mercurial.node import nullid, nullrev, bin, hex
26 26 from mercurial import changegroup, cmdutil, scmutil, phases, commands
27 27 from mercurial import error, hg, mdiff, merge, patch, repair, util
28 28 from mercurial import templatefilters, exchange, bundlerepo
29 29 from mercurial import lock as lockmod
30 30 from hgext import rebase
31 31 import errno
32 32
33 33 cmdtable = {}
34 34 command = cmdutil.command(cmdtable)
35 35 testedwith = 'internal'
36 36
37 37 class shelvedfile(object):
38 38 """Helper for the file storing a single shelve
39 39
40 40 Handles common functions on shelve files (.hg/.patch) using
41 41 the vfs layer"""
42 42 def __init__(self, repo, name, filetype=None):
43 43 self.repo = repo
44 44 self.name = name
45 45 self.vfs = scmutil.vfs(repo.join('shelved'))
46 46 self.ui = self.repo.ui
47 47 if filetype:
48 48 self.fname = name + '.' + filetype
49 49 else:
50 50 self.fname = name
51 51
52 52 def exists(self):
53 53 return self.vfs.exists(self.fname)
54 54
55 55 def filename(self):
56 56 return self.vfs.join(self.fname)
57 57
58 58 def unlink(self):
59 59 util.unlink(self.filename())
60 60
61 61 def stat(self):
62 62 return self.vfs.stat(self.fname)
63 63
64 64 def opener(self, mode='rb'):
65 65 try:
66 66 return self.vfs(self.fname, mode)
67 67 except IOError, err:
68 68 if err.errno != errno.ENOENT:
69 69 raise
70 70 raise util.Abort(_("shelved change '%s' not found") % self.name)
71 71
72 72 def applybundle(self):
73 73 fp = self.opener()
74 74 try:
75 75 gen = exchange.readbundle(self.repo.ui, fp, self.fname, self.vfs)
76 76 changegroup.addchangegroup(self.repo, gen, 'unshelve',
77 77 'bundle:' + self.vfs.join(self.fname),
78 78 targetphase=phases.secret)
79 79 finally:
80 80 fp.close()
81 81
82 82 def bundlerepo(self):
83 83 return bundlerepo.bundlerepository(self.repo.baseui, self.repo.root,
84 84 self.vfs.join(self.fname))
85 85 def writebundle(self, cg):
86 86 changegroup.writebundle(self.ui, cg, self.fname, 'HG10UN', self.vfs)
87 87
88 88 class shelvedstate(object):
89 89 """Handle persistence during unshelving operations.
90 90
91 91 Handles saving and restoring a shelved state. Ensures that different
92 92 versions of a shelved state are possible and handles them appropriately.
93 93 """
94 94 _version = 1
95 95 _filename = 'shelvedstate'
96 96
97 97 @classmethod
98 98 def load(cls, repo):
99 99 fp = repo.vfs(cls._filename)
100 100 try:
101 101 version = int(fp.readline().strip())
102 102
103 103 if version != cls._version:
104 104 raise util.Abort(_('this version of shelve is incompatible '
105 105 'with the version used in this repo'))
106 106 name = fp.readline().strip()
107 107 wctx = fp.readline().strip()
108 108 pendingctx = fp.readline().strip()
109 109 parents = [bin(h) for h in fp.readline().split()]
110 110 stripnodes = [bin(h) for h in fp.readline().split()]
111 111 finally:
112 112 fp.close()
113 113
114 114 obj = cls()
115 115 obj.name = name
116 116 obj.wctx = repo[bin(wctx)]
117 117 obj.pendingctx = repo[bin(pendingctx)]
118 118 obj.parents = parents
119 119 obj.stripnodes = stripnodes
120 120
121 121 return obj
122 122
123 123 @classmethod
124 124 def save(cls, repo, name, originalwctx, pendingctx, stripnodes):
125 125 fp = repo.vfs(cls._filename, 'wb')
126 126 fp.write('%i\n' % cls._version)
127 127 fp.write('%s\n' % name)
128 128 fp.write('%s\n' % hex(originalwctx.node()))
129 129 fp.write('%s\n' % hex(pendingctx.node()))
130 130 fp.write('%s\n' % ' '.join([hex(p) for p in repo.dirstate.parents()]))
131 131 fp.write('%s\n' % ' '.join([hex(n) for n in stripnodes]))
132 132 fp.close()
133 133
134 134 @classmethod
135 135 def clear(cls, repo):
136 136 util.unlinkpath(repo.join(cls._filename), ignoremissing=True)
137 137
138 138 def createcmd(ui, repo, pats, opts):
139 139 """subcommand that creates a new shelve"""
140 140
141 141 def publicancestors(ctx):
142 142 """Compute the public ancestors of a commit.
143 143
144 144 Much faster than the revset ancestors(ctx) & draft()"""
145 145 seen = set([nullrev])
146 146 visit = util.deque()
147 147 visit.append(ctx)
148 148 while visit:
149 149 ctx = visit.popleft()
150 150 yield ctx.node()
151 151 for parent in ctx.parents():
152 152 rev = parent.rev()
153 153 if rev not in seen:
154 154 seen.add(rev)
155 155 if parent.mutable():
156 156 visit.append(parent)
157 157
158 158 wctx = repo[None]
159 159 parents = wctx.parents()
160 160 if len(parents) > 1:
161 161 raise util.Abort(_('cannot shelve while merging'))
162 162 parent = parents[0]
163 163
164 164 # we never need the user, so we use a generic user for all shelve operations
165 165 user = 'shelve@localhost'
166 166 label = repo._bookmarkcurrent or parent.branch() or 'default'
167 167
168 168 # slashes aren't allowed in filenames, therefore we rename it
169 169 label = label.replace('/', '_')
170 170
171 171 def gennames():
172 172 yield label
173 173 for i in xrange(1, 100):
174 174 yield '%s-%02d' % (label, i)
175 175
176 176 def commitfunc(ui, repo, message, match, opts):
177 177 hasmq = util.safehasattr(repo, 'mq')
178 178 if hasmq:
179 179 saved, repo.mq.checkapplied = repo.mq.checkapplied, False
180 180 backup = repo.ui.backupconfig('phases', 'new-commit')
181 181 try:
182 182 repo.ui. setconfig('phases', 'new-commit', phases.secret)
183 183 editor = cmdutil.getcommiteditor(editform='shelve.shelve', **opts)
184 184 return repo.commit(message, user, opts.get('date'), match,
185 185 editor=editor)
186 186 finally:
187 187 repo.ui.restoreconfig(backup)
188 188 if hasmq:
189 189 repo.mq.checkapplied = saved
190 190
191 191 if parent.node() != nullid:
192 192 desc = "changes to '%s'" % parent.description().split('\n', 1)[0]
193 193 else:
194 194 desc = '(changes in empty repository)'
195 195
196 196 if not opts['message']:
197 197 opts['message'] = desc
198 198
199 199 name = opts['name']
200 200
201 201 wlock = lock = tr = bms = None
202 202 try:
203 203 wlock = repo.wlock()
204 204 lock = repo.lock()
205 205
206 206 bms = repo._bookmarks.copy()
207 207 # use an uncommitted transaction to generate the bundle to avoid
208 208 # pull races. ensure we don't print the abort message to stderr.
209 209 tr = repo.transaction('commit', report=lambda x: None)
210 210
211 211 if name:
212 212 if shelvedfile(repo, name, 'hg').exists():
213 213 raise util.Abort(_("a shelved change named '%s' already exists")
214 214 % name)
215 215 else:
216 216 for n in gennames():
217 217 if not shelvedfile(repo, n, 'hg').exists():
218 218 name = n
219 219 break
220 220 else:
221 221 raise util.Abort(_("too many shelved changes named '%s'") %
222 222 label)
223 223
224 224 # ensure we are not creating a subdirectory or a hidden file
225 225 if '/' in name or '\\' in name:
226 226 raise util.Abort(_('shelved change names may not contain slashes'))
227 227 if name.startswith('.'):
228 228 raise util.Abort(_("shelved change names may not start with '.'"))
229 229 interactive = opts.get('interactive', False)
230 230
231 def interactivecommitfunc(ui, repo, *pats, **opts):
232 match = scmutil.match(repo['.'], pats, {})
233 message = opts['message']
234 return commitfunc(ui, repo, message, match, opts)
235 if not interactive:
231 236 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
232
237 else:
238 node = cmdutil.dorecord(ui, repo, interactivecommitfunc, 'commit',
239 False, cmdutil.recordfilter, *pats, **opts)
233 240 if not node:
234 241 stat = repo.status(match=scmutil.match(repo[None], pats, opts))
235 242 if stat.deleted:
236 243 ui.status(_("nothing changed (%d missing files, see "
237 244 "'hg status')\n") % len(stat.deleted))
238 245 else:
239 246 ui.status(_("nothing changed\n"))
240 247 return 1
241 248
242 249 bases = list(publicancestors(repo[node]))
243 250 cg = changegroup.changegroupsubset(repo, bases, [node], 'shelve')
244 251 shelvedfile(repo, name, 'hg').writebundle(cg)
245 252 cmdutil.export(repo, [node],
246 253 fp=shelvedfile(repo, name, 'patch').opener('wb'),
247 254 opts=mdiff.diffopts(git=True))
248 255
249 256
250 257 if ui.formatted():
251 258 desc = util.ellipsis(desc, ui.termwidth())
252 259 ui.status(_('shelved as %s\n') % name)
253 260 hg.update(repo, parent.node())
254 261 finally:
255 262 if bms:
256 263 # restore old bookmarks
257 264 repo._bookmarks.update(bms)
258 265 repo._bookmarks.write()
259 266 if tr:
260 267 tr.abort()
261 268 lockmod.release(lock, wlock)
262 269
263 270 def cleanupcmd(ui, repo):
264 271 """subcommand that deletes all shelves"""
265 272
266 273 wlock = None
267 274 try:
268 275 wlock = repo.wlock()
269 276 for (name, _type) in repo.vfs.readdir('shelved'):
270 277 suffix = name.rsplit('.', 1)[-1]
271 278 if suffix in ('hg', 'patch'):
272 279 shelvedfile(repo, name).unlink()
273 280 finally:
274 281 lockmod.release(wlock)
275 282
276 283 def deletecmd(ui, repo, pats):
277 284 """subcommand that deletes a specific shelve"""
278 285 if not pats:
279 286 raise util.Abort(_('no shelved changes specified!'))
280 287 wlock = None
281 288 try:
282 289 wlock = repo.wlock()
283 290 try:
284 291 for name in pats:
285 292 for suffix in 'hg patch'.split():
286 293 shelvedfile(repo, name, suffix).unlink()
287 294 except OSError, err:
288 295 if err.errno != errno.ENOENT:
289 296 raise
290 297 raise util.Abort(_("shelved change '%s' not found") % name)
291 298 finally:
292 299 lockmod.release(wlock)
293 300
294 301 def listshelves(repo):
295 302 """return all shelves in repo as list of (time, filename)"""
296 303 try:
297 304 names = repo.vfs.readdir('shelved')
298 305 except OSError, err:
299 306 if err.errno != errno.ENOENT:
300 307 raise
301 308 return []
302 309 info = []
303 310 for (name, _type) in names:
304 311 pfx, sfx = name.rsplit('.', 1)
305 312 if not pfx or sfx != 'patch':
306 313 continue
307 314 st = shelvedfile(repo, name).stat()
308 315 info.append((st.st_mtime, shelvedfile(repo, pfx).filename()))
309 316 return sorted(info, reverse=True)
310 317
311 318 def listcmd(ui, repo, pats, opts):
312 319 """subcommand that displays the list of shelves"""
313 320 pats = set(pats)
314 321 width = 80
315 322 if not ui.plain():
316 323 width = ui.termwidth()
317 324 namelabel = 'shelve.newest'
318 325 for mtime, name in listshelves(repo):
319 326 sname = util.split(name)[1]
320 327 if pats and sname not in pats:
321 328 continue
322 329 ui.write(sname, label=namelabel)
323 330 namelabel = 'shelve.name'
324 331 if ui.quiet:
325 332 ui.write('\n')
326 333 continue
327 334 ui.write(' ' * (16 - len(sname)))
328 335 used = 16
329 336 age = '(%s)' % templatefilters.age(util.makedate(mtime), abbrev=True)
330 337 ui.write(age, label='shelve.age')
331 338 ui.write(' ' * (12 - len(age)))
332 339 used += 12
333 340 fp = open(name + '.patch', 'rb')
334 341 try:
335 342 while True:
336 343 line = fp.readline()
337 344 if not line:
338 345 break
339 346 if not line.startswith('#'):
340 347 desc = line.rstrip()
341 348 if ui.formatted():
342 349 desc = util.ellipsis(desc, width - used)
343 350 ui.write(desc)
344 351 break
345 352 ui.write('\n')
346 353 if not (opts['patch'] or opts['stat']):
347 354 continue
348 355 difflines = fp.readlines()
349 356 if opts['patch']:
350 357 for chunk, label in patch.difflabel(iter, difflines):
351 358 ui.write(chunk, label=label)
352 359 if opts['stat']:
353 360 for chunk, label in patch.diffstatui(difflines, width=width,
354 361 git=True):
355 362 ui.write(chunk, label=label)
356 363 finally:
357 364 fp.close()
358 365
359 366 def checkparents(repo, state):
360 367 """check parent while resuming an unshelve"""
361 368 if state.parents != repo.dirstate.parents():
362 369 raise util.Abort(_('working directory parents do not match unshelve '
363 370 'state'))
364 371
365 372 def pathtofiles(repo, files):
366 373 cwd = repo.getcwd()
367 374 return [repo.pathto(f, cwd) for f in files]
368 375
369 376 def unshelveabort(ui, repo, state, opts):
370 377 """subcommand that abort an in-progress unshelve"""
371 378 wlock = repo.wlock()
372 379 lock = None
373 380 try:
374 381 checkparents(repo, state)
375 382
376 383 util.rename(repo.join('unshelverebasestate'),
377 384 repo.join('rebasestate'))
378 385 try:
379 386 rebase.rebase(ui, repo, **{
380 387 'abort' : True
381 388 })
382 389 except Exception:
383 390 util.rename(repo.join('rebasestate'),
384 391 repo.join('unshelverebasestate'))
385 392 raise
386 393
387 394 lock = repo.lock()
388 395
389 396 mergefiles(ui, repo, state.wctx, state.pendingctx)
390 397
391 398 repair.strip(ui, repo, state.stripnodes, backup=False, topic='shelve')
392 399 shelvedstate.clear(repo)
393 400 ui.warn(_("unshelve of '%s' aborted\n") % state.name)
394 401 finally:
395 402 lockmod.release(lock, wlock)
396 403
397 404 def mergefiles(ui, repo, wctx, shelvectx):
398 405 """updates to wctx and merges the changes from shelvectx into the
399 406 dirstate."""
400 407 oldquiet = ui.quiet
401 408 try:
402 409 ui.quiet = True
403 410 hg.update(repo, wctx.node())
404 411 files = []
405 412 files.extend(shelvectx.files())
406 413 files.extend(shelvectx.parents()[0].files())
407 414
408 415 # revert will overwrite unknown files, so move them out of the way
409 416 for file in repo.status(unknown=True).unknown:
410 417 if file in files:
411 418 util.rename(file, file + ".orig")
412 419 ui.pushbuffer(True)
413 420 cmdutil.revert(ui, repo, shelvectx, repo.dirstate.parents(),
414 421 *pathtofiles(repo, files),
415 422 **{'no_backup': True})
416 423 ui.popbuffer()
417 424 finally:
418 425 ui.quiet = oldquiet
419 426
420 427 def unshelvecleanup(ui, repo, name, opts):
421 428 """remove related files after an unshelve"""
422 429 if not opts['keep']:
423 430 for filetype in 'hg patch'.split():
424 431 shelvedfile(repo, name, filetype).unlink()
425 432
426 433 def unshelvecontinue(ui, repo, state, opts):
427 434 """subcommand to continue an in-progress unshelve"""
428 435 # We're finishing off a merge. First parent is our original
429 436 # parent, second is the temporary "fake" commit we're unshelving.
430 437 wlock = repo.wlock()
431 438 lock = None
432 439 try:
433 440 checkparents(repo, state)
434 441 ms = merge.mergestate(repo)
435 442 if [f for f in ms if ms[f] == 'u']:
436 443 raise util.Abort(
437 444 _("unresolved conflicts, can't continue"),
438 445 hint=_("see 'hg resolve', then 'hg unshelve --continue'"))
439 446
440 447 lock = repo.lock()
441 448
442 449 util.rename(repo.join('unshelverebasestate'),
443 450 repo.join('rebasestate'))
444 451 try:
445 452 rebase.rebase(ui, repo, **{
446 453 'continue' : True
447 454 })
448 455 except Exception:
449 456 util.rename(repo.join('rebasestate'),
450 457 repo.join('unshelverebasestate'))
451 458 raise
452 459
453 460 shelvectx = repo['tip']
454 461 if not shelvectx in state.pendingctx.children():
455 462 # rebase was a no-op, so it produced no child commit
456 463 shelvectx = state.pendingctx
457 464 else:
458 465 # only strip the shelvectx if the rebase produced it
459 466 state.stripnodes.append(shelvectx.node())
460 467
461 468 mergefiles(ui, repo, state.wctx, shelvectx)
462 469
463 470 repair.strip(ui, repo, state.stripnodes, backup=False, topic='shelve')
464 471 shelvedstate.clear(repo)
465 472 unshelvecleanup(ui, repo, state.name, opts)
466 473 ui.status(_("unshelve of '%s' complete\n") % state.name)
467 474 finally:
468 475 lockmod.release(lock, wlock)
469 476
470 477 @command('unshelve',
471 478 [('a', 'abort', None,
472 479 _('abort an incomplete unshelve operation')),
473 480 ('c', 'continue', None,
474 481 _('continue an incomplete unshelve operation')),
475 482 ('', 'keep', None,
476 483 _('keep shelve after unshelving')),
477 484 ('', 'date', '',
478 485 _('set date for temporary commits (DEPRECATED)'), _('DATE'))],
479 486 _('hg unshelve [SHELVED]'))
480 487 def unshelve(ui, repo, *shelved, **opts):
481 488 """restore a shelved change to the working directory
482 489
483 490 This command accepts an optional name of a shelved change to
484 491 restore. If none is given, the most recent shelved change is used.
485 492
486 493 If a shelved change is applied successfully, the bundle that
487 494 contains the shelved changes is deleted afterwards.
488 495
489 496 Since you can restore a shelved change on top of an arbitrary
490 497 commit, it is possible that unshelving will result in a conflict
491 498 between your changes and the commits you are unshelving onto. If
492 499 this occurs, you must resolve the conflict, then use
493 500 ``--continue`` to complete the unshelve operation. (The bundle
494 501 will not be deleted until you successfully complete the unshelve.)
495 502
496 503 (Alternatively, you can use ``--abort`` to abandon an unshelve
497 504 that causes a conflict. This reverts the unshelved changes, and
498 505 does not delete the bundle.)
499 506 """
500 507 abortf = opts['abort']
501 508 continuef = opts['continue']
502 509 if not abortf and not continuef:
503 510 cmdutil.checkunfinished(repo)
504 511
505 512 if abortf or continuef:
506 513 if abortf and continuef:
507 514 raise util.Abort(_('cannot use both abort and continue'))
508 515 if shelved:
509 516 raise util.Abort(_('cannot combine abort/continue with '
510 517 'naming a shelved change'))
511 518
512 519 try:
513 520 state = shelvedstate.load(repo)
514 521 except IOError, err:
515 522 if err.errno != errno.ENOENT:
516 523 raise
517 524 raise util.Abort(_('no unshelve operation underway'))
518 525
519 526 if abortf:
520 527 return unshelveabort(ui, repo, state, opts)
521 528 elif continuef:
522 529 return unshelvecontinue(ui, repo, state, opts)
523 530 elif len(shelved) > 1:
524 531 raise util.Abort(_('can only unshelve one change at a time'))
525 532 elif not shelved:
526 533 shelved = listshelves(repo)
527 534 if not shelved:
528 535 raise util.Abort(_('no shelved changes to apply!'))
529 536 basename = util.split(shelved[0][1])[1]
530 537 ui.status(_("unshelving change '%s'\n") % basename)
531 538 else:
532 539 basename = shelved[0]
533 540
534 541 if not shelvedfile(repo, basename, 'patch').exists():
535 542 raise util.Abort(_("shelved change '%s' not found") % basename)
536 543
537 544 oldquiet = ui.quiet
538 545 wlock = lock = tr = None
539 546 try:
540 547 lock = repo.lock()
541 548 wlock = repo.wlock()
542 549
543 550 tr = repo.transaction('unshelve', report=lambda x: None)
544 551 oldtiprev = len(repo)
545 552
546 553 pctx = repo['.']
547 554 tmpwctx = pctx
548 555 # The goal is to have a commit structure like so:
549 556 # ...-> pctx -> tmpwctx -> shelvectx
550 557 # where tmpwctx is an optional commit with the user's pending changes
551 558 # and shelvectx is the unshelved changes. Then we merge it all down
552 559 # to the original pctx.
553 560
554 561 # Store pending changes in a commit
555 562 s = repo.status()
556 563 if s.modified or s.added or s.removed or s.deleted:
557 564 ui.status(_("temporarily committing pending changes "
558 565 "(restore with 'hg unshelve --abort')\n"))
559 566 def commitfunc(ui, repo, message, match, opts):
560 567 hasmq = util.safehasattr(repo, 'mq')
561 568 if hasmq:
562 569 saved, repo.mq.checkapplied = repo.mq.checkapplied, False
563 570
564 571 backup = repo.ui.backupconfig('phases', 'new-commit')
565 572 try:
566 573 repo.ui. setconfig('phases', 'new-commit', phases.secret)
567 574 return repo.commit(message, 'shelve@localhost',
568 575 opts.get('date'), match)
569 576 finally:
570 577 repo.ui.restoreconfig(backup)
571 578 if hasmq:
572 579 repo.mq.checkapplied = saved
573 580
574 581 tempopts = {}
575 582 tempopts['message'] = "pending changes temporary commit"
576 583 tempopts['date'] = opts.get('date')
577 584 ui.quiet = True
578 585 node = cmdutil.commit(ui, repo, commitfunc, [], tempopts)
579 586 tmpwctx = repo[node]
580 587
581 588 ui.quiet = True
582 589 shelvedfile(repo, basename, 'hg').applybundle()
583 590
584 591 ui.quiet = oldquiet
585 592
586 593 shelvectx = repo['tip']
587 594
588 595 # If the shelve is not immediately on top of the commit
589 596 # we'll be merging with, rebase it to be on top.
590 597 if tmpwctx.node() != shelvectx.parents()[0].node():
591 598 ui.status(_('rebasing shelved changes\n'))
592 599 try:
593 600 rebase.rebase(ui, repo, **{
594 601 'rev' : [shelvectx.rev()],
595 602 'dest' : str(tmpwctx.rev()),
596 603 'keep' : True,
597 604 })
598 605 except error.InterventionRequired:
599 606 tr.close()
600 607
601 608 stripnodes = [repo.changelog.node(rev)
602 609 for rev in xrange(oldtiprev, len(repo))]
603 610 shelvedstate.save(repo, basename, pctx, tmpwctx, stripnodes)
604 611
605 612 util.rename(repo.join('rebasestate'),
606 613 repo.join('unshelverebasestate'))
607 614 raise error.InterventionRequired(
608 615 _("unresolved conflicts (see 'hg resolve', then "
609 616 "'hg unshelve --continue')"))
610 617
611 618 # refresh ctx after rebase completes
612 619 shelvectx = repo['tip']
613 620
614 621 if not shelvectx in tmpwctx.children():
615 622 # rebase was a no-op, so it produced no child commit
616 623 shelvectx = tmpwctx
617 624
618 625 mergefiles(ui, repo, pctx, shelvectx)
619 626 shelvedstate.clear(repo)
620 627
621 628 # The transaction aborting will strip all the commits for us,
622 629 # but it doesn't update the inmemory structures, so addchangegroup
623 630 # hooks still fire and try to operate on the missing commits.
624 631 # Clean up manually to prevent this.
625 632 repo.unfiltered().changelog.strip(oldtiprev, tr)
626 633
627 634 unshelvecleanup(ui, repo, basename, opts)
628 635 finally:
629 636 ui.quiet = oldquiet
630 637 if tr:
631 638 tr.release()
632 639 lockmod.release(lock, wlock)
633 640
634 641 @command('shelve',
635 642 [('A', 'addremove', None,
636 643 _('mark new/missing files as added/removed before shelving')),
637 644 ('', 'cleanup', None,
638 645 _('delete all shelved changes')),
639 646 ('', 'date', '',
640 647 _('shelve with the specified commit date'), _('DATE')),
641 648 ('d', 'delete', None,
642 649 _('delete the named shelved change(s)')),
643 650 ('e', 'edit', False,
644 651 _('invoke editor on commit messages')),
645 652 ('l', 'list', None,
646 653 _('list current shelves')),
647 654 ('m', 'message', '',
648 655 _('use text as shelve message'), _('TEXT')),
649 656 ('n', 'name', '',
650 657 _('use the given name for the shelved commit'), _('NAME')),
651 658 ('p', 'patch', None,
652 659 _('show patch')),
653 660 ('i', 'interactive', None,
654 661 _('interactive mode, only works while creating a shelve')),
655 662 ('', 'stat', None,
656 663 _('output diffstat-style summary of changes'))] + commands.walkopts,
657 664 _('hg shelve [OPTION]... [FILE]...'))
658 665 def shelvecmd(ui, repo, *pats, **opts):
659 666 '''save and set aside changes from the working directory
660 667
661 668 Shelving takes files that "hg status" reports as not clean, saves
662 669 the modifications to a bundle (a shelved change), and reverts the
663 670 files so that their state in the working directory becomes clean.
664 671
665 672 To restore these changes to the working directory, using "hg
666 673 unshelve"; this will work even if you switch to a different
667 674 commit.
668 675
669 676 When no files are specified, "hg shelve" saves all not-clean
670 677 files. If specific files or directories are named, only changes to
671 678 those files are shelved.
672 679
673 680 Each shelved change has a name that makes it easier to find later.
674 681 The name of a shelved change defaults to being based on the active
675 682 bookmark, or if there is no active bookmark, the current named
676 683 branch. To specify a different name, use ``--name``.
677 684
678 685 To see a list of existing shelved changes, use the ``--list``
679 686 option. For each shelved change, this will print its name, age,
680 687 and description; use ``--patch`` or ``--stat`` for more details.
681 688
682 689 To delete specific shelved changes, use ``--delete``. To delete
683 690 all shelved changes, use ``--cleanup``.
684 691 '''
685 692 cmdutil.checkunfinished(repo)
686 693
687 694 allowables = [
688 695 ('addremove', 'create'), # 'create' is pseudo action
689 696 ('cleanup', 'cleanup'),
690 697 # ('date', 'create'), # ignored for passing '--date "0 0"' in tests
691 698 ('delete', 'delete'),
692 699 ('edit', 'create'),
693 700 ('list', 'list'),
694 701 ('message', 'create'),
695 702 ('name', 'create'),
696 703 ('patch', 'list'),
697 704 ('stat', 'list'),
698 705 ]
699 706 def checkopt(opt):
700 707 if opts[opt]:
701 708 for i, allowable in allowables:
702 709 if opts[i] and opt != allowable:
703 710 raise util.Abort(_("options '--%s' and '--%s' may not be "
704 711 "used together") % (opt, i))
705 712 return True
706 713 if checkopt('cleanup'):
707 714 if pats:
708 715 raise util.Abort(_("cannot specify names when using '--cleanup'"))
709 716 return cleanupcmd(ui, repo)
710 717 elif checkopt('delete'):
711 718 return deletecmd(ui, repo, pats)
712 719 elif checkopt('list'):
713 720 return listcmd(ui, repo, pats, opts)
714 721 else:
715 722 for i in ('patch', 'stat'):
716 723 if opts[i]:
717 724 raise util.Abort(_("option '--%s' may not be "
718 725 "used when shelving a change") % (i,))
719 726 return createcmd(ui, repo, pats, opts)
720 727
721 728 def extsetup(ui):
722 729 cmdutil.unfinishedstates.append(
723 730 [shelvedstate._filename, False, False,
724 731 _('unshelve already in progress'),
725 732 _("use 'hg unshelve --continue' or 'hg unshelve --abort'")])
@@ -1,794 +1,866 b''
1 1 $ cat <<EOF >> $HGRCPATH
2 2 > [extensions]
3 3 > mq =
4 4 > shelve =
5 5 > [defaults]
6 6 > diff = --nodates --git
7 7 > qnew = --date '0 0'
8 8 > EOF
9 9
10 10 $ hg init repo
11 11 $ cd repo
12 12 $ mkdir a b
13 13 $ echo a > a/a
14 14 $ echo b > b/b
15 15 $ echo c > c
16 16 $ echo d > d
17 17 $ echo x > x
18 18 $ hg addremove -q
19 19
20 20 shelve has a help message
21 21 $ hg shelve -h
22 22 hg shelve [OPTION]... [FILE]...
23 23
24 24 save and set aside changes from the working directory
25 25
26 26 Shelving takes files that "hg status" reports as not clean, saves the
27 27 modifications to a bundle (a shelved change), and reverts the files so
28 28 that their state in the working directory becomes clean.
29 29
30 30 To restore these changes to the working directory, using "hg unshelve";
31 31 this will work even if you switch to a different commit.
32 32
33 33 When no files are specified, "hg shelve" saves all not-clean files. If
34 34 specific files or directories are named, only changes to those files are
35 35 shelved.
36 36
37 37 Each shelved change has a name that makes it easier to find later. The
38 38 name of a shelved change defaults to being based on the active bookmark,
39 39 or if there is no active bookmark, the current named branch. To specify a
40 40 different name, use "--name".
41 41
42 42 To see a list of existing shelved changes, use the "--list" option. For
43 43 each shelved change, this will print its name, age, and description; use "
44 44 --patch" or "--stat" for more details.
45 45
46 46 To delete specific shelved changes, use "--delete". To delete all shelved
47 47 changes, use "--cleanup".
48 48
49 49 (use "hg help -e shelve" to show help for the shelve extension)
50 50
51 51 options ([+] can be repeated):
52 52
53 53 -A --addremove mark new/missing files as added/removed before
54 54 shelving
55 55 --cleanup delete all shelved changes
56 56 --date DATE shelve with the specified commit date
57 57 -d --delete delete the named shelved change(s)
58 58 -e --edit invoke editor on commit messages
59 59 -l --list list current shelves
60 60 -m --message TEXT use text as shelve message
61 61 -n --name NAME use the given name for the shelved commit
62 62 -p --patch show patch
63 63 -i --interactive interactive mode, only works while creating a shelve
64 64 --stat output diffstat-style summary of changes
65 65 -I --include PATTERN [+] include names matching the given patterns
66 66 -X --exclude PATTERN [+] exclude names matching the given patterns
67 67 --mq operate on patch repository
68 68
69 69 (some details hidden, use --verbose to show complete help)
70 70
71 71 shelving in an empty repo should be possible
72 72 (this tests also that editor is not invoked, if '--edit' is not
73 73 specified)
74 74
75 75 $ HGEDITOR=cat hg shelve
76 76 shelved as default
77 77 0 files updated, 0 files merged, 5 files removed, 0 files unresolved
78 78
79 79 $ hg unshelve
80 80 unshelving change 'default'
81 81
82 82 $ hg commit -q -m 'initial commit'
83 83
84 84 $ hg shelve
85 85 nothing changed
86 86 [1]
87 87
88 88 create an mq patch - shelving should work fine with a patch applied
89 89
90 90 $ echo n > n
91 91 $ hg add n
92 92 $ hg commit n -m second
93 93 $ hg qnew second.patch
94 94
95 95 shelve a change that we will delete later
96 96
97 97 $ echo a >> a/a
98 98 $ hg shelve
99 99 shelved as default
100 100 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
101 101
102 102 set up some more complex changes to shelve
103 103
104 104 $ echo a >> a/a
105 105 $ hg mv b b.rename
106 106 moving b/b to b.rename/b (glob)
107 107 $ hg cp c c.copy
108 108 $ hg status -C
109 109 M a/a
110 110 A b.rename/b
111 111 b/b
112 112 A c.copy
113 113 c
114 114 R b/b
115 115
116 116 prevent some foot-shooting
117 117
118 118 $ hg shelve -n foo/bar
119 119 abort: shelved change names may not contain slashes
120 120 [255]
121 121 $ hg shelve -n .baz
122 122 abort: shelved change names may not start with '.'
123 123 [255]
124 124
125 125 the common case - no options or filenames
126 126
127 127 $ hg shelve
128 128 shelved as default-01
129 129 2 files updated, 0 files merged, 2 files removed, 0 files unresolved
130 130 $ hg status -C
131 131
132 132 ensure that our shelved changes exist
133 133
134 134 $ hg shelve -l
135 135 default-01 (*)* changes to '[mq]: second.patch' (glob)
136 136 default (*)* changes to '[mq]: second.patch' (glob)
137 137
138 138 $ hg shelve -l -p default
139 139 default (*)* changes to '[mq]: second.patch' (glob)
140 140
141 141 diff --git a/a/a b/a/a
142 142 --- a/a/a
143 143 +++ b/a/a
144 144 @@ -1,1 +1,2 @@
145 145 a
146 146 +a
147 147
148 148 $ hg shelve --list --addremove
149 149 abort: options '--list' and '--addremove' may not be used together
150 150 [255]
151 151
152 152 delete our older shelved change
153 153
154 154 $ hg shelve -d default
155 155 $ hg qfinish -a -q
156 156
157 157 local edits should not prevent a shelved change from applying
158 158
159 159 $ printf "z\na\n" > a/a
160 160 $ hg unshelve --keep
161 161 unshelving change 'default-01'
162 162 temporarily committing pending changes (restore with 'hg unshelve --abort')
163 163 rebasing shelved changes
164 164 rebasing 4:4702e8911fe0 "changes to '[mq]: second.patch'" (tip)
165 165 merging a/a
166 166
167 167 $ hg revert --all -q
168 168 $ rm a/a.orig b.rename/b c.copy
169 169
170 170 apply it and make sure our state is as expected
171 171
172 172 $ hg unshelve
173 173 unshelving change 'default-01'
174 174 $ hg status -C
175 175 M a/a
176 176 A b.rename/b
177 177 b/b
178 178 A c.copy
179 179 c
180 180 R b/b
181 181 $ hg shelve -l
182 182
183 183 $ hg unshelve
184 184 abort: no shelved changes to apply!
185 185 [255]
186 186 $ hg unshelve foo
187 187 abort: shelved change 'foo' not found
188 188 [255]
189 189
190 190 named shelves, specific filenames, and "commit messages" should all work
191 191 (this tests also that editor is invoked, if '--edit' is specified)
192 192
193 193 $ hg status -C
194 194 M a/a
195 195 A b.rename/b
196 196 b/b
197 197 A c.copy
198 198 c
199 199 R b/b
200 200 $ HGEDITOR=cat hg shelve -q -n wibble -m wat -e a
201 201 wat
202 202
203 203
204 204 HG: Enter commit message. Lines beginning with 'HG:' are removed.
205 205 HG: Leave message empty to abort commit.
206 206 HG: --
207 207 HG: user: shelve@localhost
208 208 HG: branch 'default'
209 209 HG: changed a/a
210 210
211 211 expect "a" to no longer be present, but status otherwise unchanged
212 212
213 213 $ hg status -C
214 214 A b.rename/b
215 215 b/b
216 216 A c.copy
217 217 c
218 218 R b/b
219 219 $ hg shelve -l --stat
220 220 wibble (*) wat (glob)
221 221 a/a | 1 +
222 222 1 files changed, 1 insertions(+), 0 deletions(-)
223 223
224 224 and now "a/a" should reappear
225 225
226 226 $ cd a
227 227 $ hg unshelve -q wibble
228 228 $ cd ..
229 229 $ hg status -C
230 230 M a/a
231 231 A b.rename/b
232 232 b/b
233 233 A c.copy
234 234 c
235 235 R b/b
236 236
237 237 cause unshelving to result in a merge with 'a' conflicting
238 238
239 239 $ hg shelve -q
240 240 $ echo c>>a/a
241 241 $ hg commit -m second
242 242 $ hg tip --template '{files}\n'
243 243 a/a
244 244
245 245 add an unrelated change that should be preserved
246 246
247 247 $ mkdir foo
248 248 $ echo foo > foo/foo
249 249 $ hg add foo/foo
250 250
251 251 force a conflicted merge to occur
252 252
253 253 $ hg unshelve
254 254 unshelving change 'default'
255 255 temporarily committing pending changes (restore with 'hg unshelve --abort')
256 256 rebasing shelved changes
257 257 rebasing 5:4702e8911fe0 "changes to '[mq]: second.patch'" (tip)
258 258 merging a/a
259 259 warning: conflicts during merge.
260 260 merging a/a incomplete! (edit conflicts, then use 'hg resolve --mark')
261 261 unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
262 262 [1]
263 263
264 264 ensure that we have a merge with unresolved conflicts
265 265
266 266 $ hg heads -q --template '{rev}\n'
267 267 5
268 268 4
269 269 $ hg parents -q --template '{rev}\n'
270 270 4
271 271 5
272 272 $ hg status
273 273 M a/a
274 274 M b.rename/b
275 275 M c.copy
276 276 R b/b
277 277 ? a/a.orig
278 278 $ hg diff
279 279 diff --git a/a/a b/a/a
280 280 --- a/a/a
281 281 +++ b/a/a
282 282 @@ -1,2 +1,6 @@
283 283 a
284 284 +<<<<<<< dest: * - shelve: pending changes temporary commit (glob)
285 285 c
286 286 +=======
287 287 +a
288 288 +>>>>>>> source: 4702e8911fe0 - shelve: changes to '[mq]: second.patch'
289 289 diff --git a/b/b b/b.rename/b
290 290 rename from b/b
291 291 rename to b.rename/b
292 292 diff --git a/c b/c.copy
293 293 copy from c
294 294 copy to c.copy
295 295 $ hg resolve -l
296 296 U a/a
297 297
298 298 $ hg shelve
299 299 abort: unshelve already in progress
300 300 (use 'hg unshelve --continue' or 'hg unshelve --abort')
301 301 [255]
302 302
303 303 abort the unshelve and be happy
304 304
305 305 $ hg status
306 306 M a/a
307 307 M b.rename/b
308 308 M c.copy
309 309 R b/b
310 310 ? a/a.orig
311 311 $ hg unshelve -a
312 312 rebase aborted
313 313 unshelve of 'default' aborted
314 314 $ hg heads -q
315 315 3:2e69b451d1ea
316 316 $ hg parents
317 317 changeset: 3:2e69b451d1ea
318 318 tag: tip
319 319 user: test
320 320 date: Thu Jan 01 00:00:00 1970 +0000
321 321 summary: second
322 322
323 323 $ hg resolve -l
324 324 $ hg status
325 325 A foo/foo
326 326 ? a/a.orig
327 327
328 328 try to continue with no unshelve underway
329 329
330 330 $ hg unshelve -c
331 331 abort: no unshelve operation underway
332 332 [255]
333 333 $ hg status
334 334 A foo/foo
335 335 ? a/a.orig
336 336
337 337 redo the unshelve to get a conflict
338 338
339 339 $ hg unshelve -q
340 340 warning: conflicts during merge.
341 341 merging a/a incomplete! (edit conflicts, then use 'hg resolve --mark')
342 342 unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
343 343 [1]
344 344
345 345 attempt to continue
346 346
347 347 $ hg unshelve -c
348 348 abort: unresolved conflicts, can't continue
349 349 (see 'hg resolve', then 'hg unshelve --continue')
350 350 [255]
351 351
352 352 $ hg revert -r . a/a
353 353 $ hg resolve -m a/a
354 354 (no more unresolved files)
355 355
356 356 $ hg commit -m 'commit while unshelve in progress'
357 357 abort: unshelve already in progress
358 358 (use 'hg unshelve --continue' or 'hg unshelve --abort')
359 359 [255]
360 360
361 361 $ hg unshelve -c
362 362 rebasing 5:4702e8911fe0 "changes to '[mq]: second.patch'" (tip)
363 363 unshelve of 'default' complete
364 364
365 365 ensure the repo is as we hope
366 366
367 367 $ hg parents
368 368 changeset: 3:2e69b451d1ea
369 369 tag: tip
370 370 user: test
371 371 date: Thu Jan 01 00:00:00 1970 +0000
372 372 summary: second
373 373
374 374 $ hg heads -q
375 375 3:2e69b451d1ea
376 376
377 377 $ hg status -C
378 378 A b.rename/b
379 379 b/b
380 380 A c.copy
381 381 c
382 382 A foo/foo
383 383 R b/b
384 384 ? a/a.orig
385 385
386 386 there should be no shelves left
387 387
388 388 $ hg shelve -l
389 389
390 390 #if execbit
391 391
392 392 ensure that metadata-only changes are shelved
393 393
394 394 $ chmod +x a/a
395 395 $ hg shelve -q -n execbit a/a
396 396 $ hg status a/a
397 397 $ hg unshelve -q execbit
398 398 $ hg status a/a
399 399 M a/a
400 400 $ hg revert a/a
401 401
402 402 #endif
403 403
404 404 #if symlink
405 405
406 406 $ rm a/a
407 407 $ ln -s foo a/a
408 408 $ hg shelve -q -n symlink a/a
409 409 $ hg status a/a
410 410 $ hg unshelve -q symlink
411 411 $ hg status a/a
412 412 M a/a
413 413 $ hg revert a/a
414 414
415 415 #endif
416 416
417 417 set up another conflict between a commit and a shelved change
418 418
419 419 $ hg revert -q -C -a
420 420 $ rm a/a.orig b.rename/b c.copy
421 421 $ echo a >> a/a
422 422 $ hg shelve -q
423 423 $ echo x >> a/a
424 424 $ hg ci -m 'create conflict'
425 425 $ hg add foo/foo
426 426
427 427 if we resolve a conflict while unshelving, the unshelve should succeed
428 428
429 429 $ HGMERGE=true hg unshelve
430 430 unshelving change 'default'
431 431 temporarily committing pending changes (restore with 'hg unshelve --abort')
432 432 rebasing shelved changes
433 433 rebasing 6:c5e6910e7601 "changes to 'second'" (tip)
434 434 merging a/a
435 435 note: rebase of 6:c5e6910e7601 created no changes to commit
436 436 $ hg parents -q
437 437 4:33f7f61e6c5e
438 438 $ hg shelve -l
439 439 $ hg status
440 440 A foo/foo
441 441 $ cat a/a
442 442 a
443 443 c
444 444 x
445 445
446 446 test keep and cleanup
447 447
448 448 $ hg shelve
449 449 shelved as default
450 450 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
451 451 $ hg shelve --list
452 452 default (*) changes to 'create conflict' (glob)
453 453 $ hg unshelve --keep
454 454 unshelving change 'default'
455 455 $ hg shelve --list
456 456 default (*) changes to 'create conflict' (glob)
457 457 $ hg shelve --cleanup
458 458 $ hg shelve --list
459 459
460 460 $ hg shelve --cleanup --delete
461 461 abort: options '--cleanup' and '--delete' may not be used together
462 462 [255]
463 463 $ hg shelve --cleanup --patch
464 464 abort: options '--cleanup' and '--patch' may not be used together
465 465 [255]
466 466 $ hg shelve --cleanup --message MESSAGE
467 467 abort: options '--cleanup' and '--message' may not be used together
468 468 [255]
469 469
470 470 test bookmarks
471 471
472 472 $ hg bookmark test
473 473 $ hg bookmark
474 474 * test 4:33f7f61e6c5e
475 475 $ hg shelve
476 476 shelved as test
477 477 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
478 478 $ hg bookmark
479 479 * test 4:33f7f61e6c5e
480 480 $ hg unshelve
481 481 unshelving change 'test'
482 482 $ hg bookmark
483 483 * test 4:33f7f61e6c5e
484 484
485 485 shelve should still work even if mq is disabled
486 486
487 487 $ hg --config extensions.mq=! shelve
488 488 shelved as test
489 489 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
490 490 $ hg --config extensions.mq=! shelve --list
491 491 test (*) changes to 'create conflict' (glob)
492 492 $ hg --config extensions.mq=! unshelve
493 493 unshelving change 'test'
494 494
495 495 shelve should leave dirstate clean (issue4055)
496 496
497 497 $ cd ..
498 498 $ hg init shelverebase
499 499 $ cd shelverebase
500 500 $ printf 'x\ny\n' > x
501 501 $ echo z > z
502 502 $ hg commit -Aqm xy
503 503 $ echo z >> x
504 504 $ hg commit -Aqm z
505 505 $ hg up 0
506 506 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
507 507 $ printf 'a\nx\ny\nz\n' > x
508 508 $ hg commit -Aqm xyz
509 509 $ echo c >> z
510 510 $ hg shelve
511 511 shelved as default
512 512 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
513 513 $ hg rebase -d 1 --config extensions.rebase=
514 514 rebasing 2:323bfa07f744 "xyz" (tip)
515 515 merging x
516 516 saved backup bundle to $TESTTMP/shelverebase/.hg/strip-backup/323bfa07f744-78114325-backup.hg (glob)
517 517 $ hg unshelve
518 518 unshelving change 'default'
519 519 rebasing shelved changes
520 520 rebasing 4:b8fefe789ed0 "changes to 'xyz'" (tip)
521 521 $ hg status
522 522 M z
523 523
524 524 $ cd ..
525 525
526 526 shelve should only unshelve pending changes (issue4068)
527 527
528 528 $ hg init onlypendingchanges
529 529 $ cd onlypendingchanges
530 530 $ touch a
531 531 $ hg ci -Aqm a
532 532 $ touch b
533 533 $ hg ci -Aqm b
534 534 $ hg up -q 0
535 535 $ touch c
536 536 $ hg ci -Aqm c
537 537
538 538 $ touch d
539 539 $ hg add d
540 540 $ hg shelve
541 541 shelved as default
542 542 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
543 543 $ hg up -q 1
544 544 $ hg unshelve
545 545 unshelving change 'default'
546 546 rebasing shelved changes
547 547 rebasing 3:0cae6656c016 "changes to 'c'" (tip)
548 548 $ hg status
549 549 A d
550 550
551 551 unshelve should work on an ancestor of the original commit
552 552
553 553 $ hg shelve
554 554 shelved as default
555 555 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
556 556 $ hg up 0
557 557 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
558 558 $ hg unshelve
559 559 unshelving change 'default'
560 560 rebasing shelved changes
561 561 rebasing 3:be58f65f55fb "changes to 'b'" (tip)
562 562 $ hg status
563 563 A d
564 564
565 565 test bug 4073 we need to enable obsolete markers for it
566 566
567 567 $ cat >> $HGRCPATH << EOF
568 568 > [experimental]
569 569 > evolution=createmarkers
570 570 > EOF
571 571 $ hg shelve
572 572 shelved as default
573 573 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
574 574 $ hg debugobsolete `hg --debug id -i -r 1`
575 575 $ hg unshelve
576 576 unshelving change 'default'
577 577
578 578 unshelve should leave unknown files alone (issue4113)
579 579
580 580 $ echo e > e
581 581 $ hg shelve
582 582 shelved as default
583 583 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
584 584 $ hg status
585 585 ? e
586 586 $ hg unshelve
587 587 unshelving change 'default'
588 588 $ hg status
589 589 A d
590 590 ? e
591 591 $ cat e
592 592 e
593 593
594 594 unshelve should keep a copy of unknown files
595 595
596 596 $ hg add e
597 597 $ hg shelve
598 598 shelved as default
599 599 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
600 600 $ echo z > e
601 601 $ hg unshelve
602 602 unshelving change 'default'
603 603 $ cat e
604 604 e
605 605 $ cat e.orig
606 606 z
607 607
608 608
609 609 unshelve and conflicts with tracked and untracked files
610 610
611 611 preparing:
612 612
613 613 $ rm *.orig
614 614 $ hg ci -qm 'commit stuff'
615 615 $ hg phase -p null:
616 616
617 617 no other changes - no merge:
618 618
619 619 $ echo f > f
620 620 $ hg add f
621 621 $ hg shelve
622 622 shelved as default
623 623 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
624 624 $ echo g > f
625 625 $ hg unshelve
626 626 unshelving change 'default'
627 627 $ hg st
628 628 A f
629 629 ? f.orig
630 630 $ cat f
631 631 f
632 632 $ cat f.orig
633 633 g
634 634
635 635 other uncommitted changes - merge:
636 636
637 637 $ hg st
638 638 A f
639 639 ? f.orig
640 640 $ hg shelve
641 641 shelved as default
642 642 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
643 643 $ hg log -G --template '{rev} {desc|firstline} {author}' -R bundle://.hg/shelved/default.hg -r 'bundle()'
644 644 o 4 changes to 'commit stuff' shelve@localhost
645 645 |
646 646 $ hg log -G --template '{rev} {desc|firstline} {author}'
647 647 @ 3 commit stuff test
648 648 |
649 649 | o 2 c test
650 650 |/
651 651 o 0 a test
652 652
653 653 $ mv f.orig f
654 654 $ echo 1 > a
655 655 $ hg unshelve --date '1073741824 0'
656 656 unshelving change 'default'
657 657 temporarily committing pending changes (restore with 'hg unshelve --abort')
658 658 rebasing shelved changes
659 659 rebasing 5:23b29cada8ba "changes to 'commit stuff'" (tip)
660 660 merging f
661 661 warning: conflicts during merge.
662 662 merging f incomplete! (edit conflicts, then use 'hg resolve --mark')
663 663 unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
664 664 [1]
665 665 $ hg log -G --template '{rev} {desc|firstline} {author} {date|isodate}'
666 666 @ 5 changes to 'commit stuff' shelve@localhost 1970-01-01 00:00 +0000
667 667 |
668 668 | @ 4 pending changes temporary commit shelve@localhost 2004-01-10 13:37 +0000
669 669 |/
670 670 o 3 commit stuff test 1970-01-01 00:00 +0000
671 671 |
672 672 | o 2 c test 1970-01-01 00:00 +0000
673 673 |/
674 674 o 0 a test 1970-01-01 00:00 +0000
675 675
676 676 $ hg st
677 677 M f
678 678 ? f.orig
679 679 $ cat f
680 680 <<<<<<< dest: 5f6b880e719b - shelve: pending changes temporary commit
681 681 g
682 682 =======
683 683 f
684 684 >>>>>>> source: 23b29cada8ba - shelve: changes to 'commit stuff'
685 685 $ cat f.orig
686 686 g
687 687 $ hg unshelve --abort
688 688 rebase aborted
689 689 unshelve of 'default' aborted
690 690 $ hg st
691 691 M a
692 692 ? f.orig
693 693 $ cat f.orig
694 694 g
695 695 $ hg unshelve
696 696 unshelving change 'default'
697 697 temporarily committing pending changes (restore with 'hg unshelve --abort')
698 698 rebasing shelved changes
699 699 rebasing 5:23b29cada8ba "changes to 'commit stuff'" (tip)
700 700 $ hg st
701 701 M a
702 702 A f
703 703 ? f.orig
704 704
705 705 other committed changes - merge:
706 706
707 707 $ hg shelve f
708 708 shelved as default
709 709 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
710 710 $ hg ci a -m 'intermediate other change'
711 711 $ mv f.orig f
712 712 $ hg unshelve
713 713 unshelving change 'default'
714 714 rebasing shelved changes
715 715 rebasing 5:23b29cada8ba "changes to 'commit stuff'" (tip)
716 716 merging f
717 717 warning: conflicts during merge.
718 718 merging f incomplete! (edit conflicts, then use 'hg resolve --mark')
719 719 unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
720 720 [1]
721 721 $ hg st
722 722 M f
723 723 ? f.orig
724 724 $ cat f
725 725 <<<<<<< dest: * - test: intermediate other change (glob)
726 726 g
727 727 =======
728 728 f
729 729 >>>>>>> source: 23b29cada8ba - shelve: changes to 'commit stuff'
730 730 $ cat f.orig
731 731 g
732 732 $ hg unshelve --abort
733 733 rebase aborted
734 734 unshelve of 'default' aborted
735 735 $ hg st
736 736 ? f.orig
737 737 $ cat f.orig
738 738 g
739 739 $ hg shelve --delete default
740 740
741 741 Recreate some conflict again
742 742
743 743 $ cd ../repo
744 744 $ hg up -C -r 3
745 745 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
746 746 (leaving bookmark test)
747 747 $ echo y >> a/a
748 748 $ hg shelve
749 749 shelved as default
750 750 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
751 751 $ hg up test
752 752 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
753 753 (activating bookmark test)
754 754 $ hg unshelve
755 755 unshelving change 'default'
756 756 rebasing shelved changes
757 757 rebasing 5:4b555fdb4e96 "changes to 'second'" (tip)
758 758 merging a/a
759 759 warning: conflicts during merge.
760 760 merging a/a incomplete! (edit conflicts, then use 'hg resolve --mark')
761 761 unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
762 762 [1]
763 763
764 764 Test that resolving all conflicts in one direction (so that the rebase
765 765 is a no-op), works (issue4398)
766 766
767 767 $ hg revert -a -r .
768 768 reverting a/a (glob)
769 769 $ hg resolve -m a/a
770 770 (no more unresolved files)
771 771 $ hg unshelve -c
772 772 rebasing 5:4b555fdb4e96 "changes to 'second'" (tip)
773 773 note: rebase of 5:4b555fdb4e96 created no changes to commit
774 774 unshelve of 'default' complete
775 775 $ hg diff
776 776 $ hg status
777 777 ? a/a.orig
778 778 ? foo/foo
779 779 $ hg summary
780 780 parent: 4:33f7f61e6c5e tip
781 781 create conflict
782 782 branch: default
783 783 bookmarks: *test
784 784 commit: 2 unknown (clean)
785 785 update: (current)
786 786
787 787 $ hg shelve --delete --stat
788 788 abort: options '--delete' and '--stat' may not be used together
789 789 [255]
790 790 $ hg shelve --delete --name NAME
791 791 abort: options '--delete' and '--name' may not be used together
792 792 [255]
793 793
794 Test interactive shelve
795 $ cat <<EOF >> $HGRCPATH
796 > [ui]
797 > interactive = true
798 > EOF
799 $ echo 'a' >> a/b
800 $ cat a/a >> a/b
801 $ echo 'x' >> a/b
802 $ mv a/b a/a
803 $ echo 'a' >> foo/foo
804 $ hg st
805 M a/a
806 ? a/a.orig
807 ? foo/foo
808 $ cat a/a
809 a
810 a
811 c
812 x
813 x
814 $ cat foo/foo
815 foo
816 a
817 $ hg shelve --interactive << EOF
818 > y
819 > y
820 > n
821 > EOF
822 diff --git a/a/a b/a/a
823 2 hunks, 2 lines changed
824 examine changes to 'a/a'? [Ynesfdaq?] y
825
826 @@ -1,3 +1,4 @@
827 +a
828 a
829 c
830 x
831 record change 1/2 to 'a/a'? [Ynesfdaq?] y
832
833 @@ -1,3 +2,4 @@
834 a
835 c
836 x
837 +x
838 record change 2/2 to 'a/a'? [Ynesfdaq?] n
839
840 shelved as test
841 merging a/a
842 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
843 $ cat a/a
844 a
845 c
846 x
847 x
848 $ cat foo/foo
849 foo
850 a
851 $ hg st
852 M a/a
853 ? foo/foo
854 $ hg unshelve
855 unshelving change 'test'
856 temporarily committing pending changes (restore with 'hg unshelve --abort')
857 rebasing shelved changes
858 rebasing 6:65b5d1c34c34 "changes to 'create conflict'" (tip)
859 merging a/a
860 $ cat a/a
861 a
862 a
863 c
864 x
865 x
794 866 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now