comparison mercurial/shelve.py @ 46296:eec8899407f4

shelve: also create class representing whole directory of shelves It's a little annoying to have to create and pass in a vfs into `listshelves()`. This patch attempts to start addressing that by creating a class that represents a directory containing shelves (the directory can be either `.hg/shelved/` or `.hg/shelve-backup/`). Differential Revision: https://phab.mercurial-scm.org/D9743
author Martin von Zweigbergk <martinvonz@google.com>
date Tue, 12 Jan 2021 09:02:47 -0800
parents f8c5e6ecd008
children 82edad33fd81
comparison
equal deleted inserted replaced
46295:f8c5e6ecd008 46296:eec8899407f4
68 # we never need the user, so we use a 68 # we never need the user, so we use a
69 # generic user for all shelve operations 69 # generic user for all shelve operations
70 shelveuser = b'shelve@localhost' 70 shelveuser = b'shelve@localhost'
71 71
72 72
73 class ShelfDir(object):
74 def __init__(self, repo, for_backups=False):
75 if for_backups:
76 self.vfs = vfsmod.vfs(repo.vfs.join(backupdir))
77 else:
78 self.vfs = vfsmod.vfs(repo.vfs.join(shelvedir))
79
80 def get(self, name):
81 return Shelf(self.vfs, name)
82
83
73 class Shelf(object): 84 class Shelf(object):
74 """Represents a shelf, including possibly multiple files storing it. 85 """Represents a shelf, including possibly multiple files storing it.
75 86
76 Old shelves will have a .patch and a .hg file. Newer shelves will 87 Old shelves will have a .patch and a .hg file. Newer shelves will
77 also have a .shelve file. This class abstracts away some of the 88 also have a .shelve file. This class abstracts away some of the
79 """ 90 """
80 91
81 def __init__(self, vfs, name): 92 def __init__(self, vfs, name):
82 self.vfs = vfs 93 self.vfs = vfs
83 self.name = name 94 self.name = name
84
85 @staticmethod
86 def open(repo, name):
87 return Shelf(vfsmod.vfs(repo.vfs.join(shelvedir)), name)
88
89 @staticmethod
90 def open_backup(repo, name):
91 return Shelf(vfsmod.vfs(repo.vfs.join(backupdir)), name)
92 95
93 def exists(self): 96 def exists(self):
94 return self.vfs.exists(self.name + b'.patch') and self.vfs.exists( 97 return self.vfs.exists(self.name + b'.patch') and self.vfs.exists(
95 self.name + b'.hg' 98 self.name + b'.hg'
96 ) 99 )
379 # filenames must not start with '.' as it should not be hidden 382 # filenames must not start with '.' as it should not be hidden
380 if label.startswith(b'.'): 383 if label.startswith(b'.'):
381 label = label.replace(b'.', b'_', 1) 384 label = label.replace(b'.', b'_', 1)
382 385
383 if name: 386 if name:
384 if Shelf.open(repo, name).exists(): 387 if ShelfDir(repo).get(name).exists():
385 e = _(b"a shelved change named '%s' already exists") % name 388 e = _(b"a shelved change named '%s' already exists") % name
386 raise error.Abort(e) 389 raise error.Abort(e)
387 390
388 # ensure we are not creating a subdirectory or a hidden file 391 # ensure we are not creating a subdirectory or a hidden file
389 if b'/' in name or b'\\' in name: 392 if b'/' in name or b'\\' in name:
392 ) 395 )
393 if name.startswith(b'.'): 396 if name.startswith(b'.'):
394 raise error.Abort(_(b"shelved change names can not start with '.'")) 397 raise error.Abort(_(b"shelved change names can not start with '.'"))
395 398
396 else: 399 else:
400 shelf_dir = ShelfDir(repo)
397 for n in gennames(): 401 for n in gennames():
398 if not Shelf.open(repo, n).exists(): 402 if not shelf_dir.get(n).exists():
399 name = n 403 name = n
400 break 404 break
401 405
402 return name 406 return name
403 407
469 ui.status(_(b"nothing changed\n")) 473 ui.status(_(b"nothing changed\n"))
470 474
471 475
472 def _shelvecreatedcommit(repo, node, name, match): 476 def _shelvecreatedcommit(repo, node, name, match):
473 info = {b'node': hex(node)} 477 info = {b'node': hex(node)}
474 shelf = Shelf.open(repo, name) 478 shelf = ShelfDir(repo).get(name)
475 shelf.writeinfo(info) 479 shelf.writeinfo(info)
476 bases = list(mutableancestors(repo[node])) 480 bases = list(mutableancestors(repo[node]))
477 shelf.writebundle(repo, bases, node) 481 shelf.writebundle(repo, bases, node)
478 with shelf.open_patch(b'wb') as fp: 482 with shelf.open_patch(b'wb') as fp:
479 cmdutil.exportfile( 483 cmdutil.exportfile(
612 if not pats: 616 if not pats:
613 raise error.InputError(_(b'no shelved changes specified!')) 617 raise error.InputError(_(b'no shelved changes specified!'))
614 with repo.wlock(): 618 with repo.wlock():
615 backupvfs = vfsmod.vfs(repo.vfs.join(backupdir)) 619 backupvfs = vfsmod.vfs(repo.vfs.join(backupdir))
616 for name in pats: 620 for name in pats:
617 shelf = Shelf.open(repo, name) 621 shelf = ShelfDir(repo).get(name)
618 if not shelf.exists(): 622 if not shelf.exists():
619 raise error.InputError( 623 raise error.InputError(
620 _(b"shelved change '%s' not found") % name 624 _(b"shelved change '%s' not found") % name
621 ) 625 )
622 shelf.movetobackup(backupvfs) 626 shelf.movetobackup(backupvfs)
653 if not ui.plain(): 657 if not ui.plain():
654 width = ui.termwidth() 658 width = ui.termwidth()
655 namelabel = b'shelve.newest' 659 namelabel = b'shelve.newest'
656 ui.pager(b'shelve') 660 ui.pager(b'shelve')
657 vfs = vfsmod.vfs(repo.vfs.join(shelvedir)) 661 vfs = vfsmod.vfs(repo.vfs.join(shelvedir))
662 shelf_dir = ShelfDir(repo)
658 for mtime, name in listshelves(vfs): 663 for mtime, name in listshelves(vfs):
659 if pats and name not in pats: 664 if pats and name not in pats:
660 continue 665 continue
661 ui.write(name, label=namelabel) 666 ui.write(name, label=namelabel)
662 namelabel = b'shelve.name' 667 namelabel = b'shelve.name'
668 date = dateutil.makedate(mtime) 673 date = dateutil.makedate(mtime)
669 age = b'(%s)' % templatefilters.age(date, abbrev=True) 674 age = b'(%s)' % templatefilters.age(date, abbrev=True)
670 ui.write(age, label=b'shelve.age') 675 ui.write(age, label=b'shelve.age')
671 ui.write(b' ' * (12 - len(age))) 676 ui.write(b' ' * (12 - len(age)))
672 used += 12 677 used += 12
673 with Shelf.open(repo, name).open_patch() as fp: 678 with shelf_dir.get(name).open_patch() as fp:
674 while True: 679 while True:
675 line = fp.readline() 680 line = fp.readline()
676 if not line: 681 if not line:
677 break 682 break
678 if not line.startswith(b'#'): 683 if not line.startswith(b'#'):
701 if not shelves: 706 if not shelves:
702 raise error.Abort(_(b"there are no shelves to show")) 707 raise error.Abort(_(b"there are no shelves to show"))
703 mtime, name = shelves[0] 708 mtime, name = shelves[0]
704 pats = [name] 709 pats = [name]
705 710
711 shelf_dir = ShelfDir(repo)
706 for shelfname in pats: 712 for shelfname in pats:
707 if not Shelf.open(repo, shelfname).exists(): 713 if not shelf_dir.get(shelfname).exists():
708 raise error.Abort(_(b"cannot find shelf %s") % shelfname) 714 raise error.Abort(_(b"cannot find shelf %s") % shelfname)
709 715
710 listcmd(ui, repo, pats, opts) 716 listcmd(ui, repo, pats, opts)
711 717
712 718
794 800
795 def unshelvecleanup(ui, repo, name, opts): 801 def unshelvecleanup(ui, repo, name, opts):
796 """remove related files after an unshelve""" 802 """remove related files after an unshelve"""
797 if not opts.get(b'keep'): 803 if not opts.get(b'keep'):
798 backupvfs = vfsmod.vfs(repo.vfs.join(backupdir)) 804 backupvfs = vfsmod.vfs(repo.vfs.join(backupdir))
799 Shelf.open(repo, name).movetobackup(backupvfs) 805 ShelfDir(repo).get(name).movetobackup(backupvfs)
800 cleanupoldbackups(repo) 806 cleanupoldbackups(repo)
801 807
802 808
803 def unshelvecontinue(ui, repo, state, opts): 809 def unshelvecontinue(ui, repo, state, opts):
804 """subcommand to continue an in-progress unshelve""" 810 """subcommand to continue an in-progress unshelve"""
894 900
895 def _unshelverestorecommit(ui, repo, tr, basename): 901 def _unshelverestorecommit(ui, repo, tr, basename):
896 """Recreate commit in the repository during the unshelve""" 902 """Recreate commit in the repository during the unshelve"""
897 repo = repo.unfiltered() 903 repo = repo.unfiltered()
898 node = None 904 node = None
899 shelf = Shelf.open(repo, basename) 905 shelf = ShelfDir(repo).get(basename)
900 if shelf.hasinfo(): 906 if shelf.hasinfo():
901 node = shelf.readinfo()[b'node'] 907 node = shelf.readinfo()[b'node']
902 if node is None or node not in repo: 908 if node is None or node not in repo:
903 with ui.configoverride({(b'ui', b'quiet'): True}): 909 with ui.configoverride({(b'ui', b'quiet'): True}):
904 shelvectx = shelf.applybundle(repo, tr) 910 shelvectx = shelf.applybundle(repo, tr)
1124 basename = shelved[0][1] 1130 basename = shelved[0][1]
1125 ui.status(_(b"unshelving change '%s'\n") % basename) 1131 ui.status(_(b"unshelving change '%s'\n") % basename)
1126 else: 1132 else:
1127 basename = shelved[0] 1133 basename = shelved[0]
1128 1134
1129 if not Shelf.open(repo, basename).exists(): 1135 if not ShelfDir(repo).get(basename).exists():
1130 raise error.InputError(_(b"shelved change '%s' not found") % basename) 1136 raise error.InputError(_(b"shelved change '%s' not found") % basename)
1131 1137
1132 return _dounshelve(ui, repo, basename, opts) 1138 return _dounshelve(ui, repo, basename, opts)
1133 1139
1134 1140