Mercurial > hg
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 |