96 mtime = shelf.mtime() |
97 mtime = shelf.mtime() |
97 info.append((mtime, name)) |
98 info.append((mtime, name)) |
98 return sorted(info, reverse=True) |
99 return sorted(info, reverse=True) |
99 |
100 |
100 |
101 |
|
102 def _use_internal_phase(repo): |
|
103 return ( |
|
104 phases.supportinternal(repo) |
|
105 and repo.ui.config(b'shelve', b'store') == b'internal' |
|
106 ) |
|
107 |
|
108 |
|
109 def _target_phase(repo): |
|
110 return phases.internal if _use_internal_phase(repo) else phases.secret |
|
111 |
|
112 |
101 class Shelf: |
113 class Shelf: |
102 """Represents a shelf, including possibly multiple files storing it. |
114 """Represents a shelf, including possibly multiple files storing it. |
103 |
115 |
104 Old shelves will have a .patch and a .hg file. Newer shelves will |
116 Old shelves will have a .patch and a .hg file. Newer shelves will |
105 also have a .shelve file. This class abstracts away some of the |
117 also have a .shelve file. This class abstracts away some of the |
109 def __init__(self, vfs, name): |
121 def __init__(self, vfs, name): |
110 self.vfs = vfs |
122 self.vfs = vfs |
111 self.name = name |
123 self.name = name |
112 |
124 |
113 def exists(self): |
125 def exists(self): |
114 return self.vfs.exists(self.name + b'.patch') and self.vfs.exists( |
126 return self._exists(b'.shelve') or self._exists(b'.patch', b'.hg') |
115 self.name + b'.hg' |
127 |
116 ) |
128 def _exists(self, *exts): |
|
129 return all(self.vfs.exists(self.name + ext) for ext in exts) |
117 |
130 |
118 def mtime(self): |
131 def mtime(self): |
119 return self.vfs.stat(self.name + b'.patch')[stat.ST_MTIME] |
132 try: |
|
133 return self._stat(b'.shelve')[stat.ST_MTIME] |
|
134 except FileNotFoundError: |
|
135 return self._stat(b'.patch')[stat.ST_MTIME] |
|
136 |
|
137 def _stat(self, ext): |
|
138 return self.vfs.stat(self.name + ext) |
120 |
139 |
121 def writeinfo(self, info): |
140 def writeinfo(self, info): |
122 scmutil.simplekeyvaluefile(self.vfs, self.name + b'.shelve').write(info) |
141 scmutil.simplekeyvaluefile(self.vfs, self.name + b'.shelve').write(info) |
123 |
142 |
124 def hasinfo(self): |
143 def hasinfo(self): |
157 |
176 |
158 def applybundle(self, repo, tr): |
177 def applybundle(self, repo, tr): |
159 filename = self.name + b'.hg' |
178 filename = self.name + b'.hg' |
160 fp = self.vfs(filename) |
179 fp = self.vfs(filename) |
161 try: |
180 try: |
162 targetphase = phases.internal |
181 targetphase = _target_phase(repo) |
163 if not phases.supportinternal(repo): |
|
164 targetphase = phases.secret |
|
165 gen = exchange.readbundle(repo.ui, fp, filename, self.vfs) |
182 gen = exchange.readbundle(repo.ui, fp, filename, self.vfs) |
166 pretip = repo[b'tip'] |
183 pretip = repo[b'tip'] |
167 bundle2.applybundle( |
184 bundle2.applybundle( |
168 repo, |
185 repo, |
169 gen, |
186 gen, |
180 finally: |
197 finally: |
181 fp.close() |
198 fp.close() |
182 |
199 |
183 def open_patch(self, mode=b'rb'): |
200 def open_patch(self, mode=b'rb'): |
184 return self.vfs(self.name + b'.patch', mode) |
201 return self.vfs(self.name + b'.patch', mode) |
|
202 |
|
203 def patch_from_node(self, repo, node): |
|
204 repo = repo.unfiltered() |
|
205 match = _optimized_match(repo, node) |
|
206 fp = io.BytesIO() |
|
207 cmdutil.exportfile( |
|
208 repo, |
|
209 [node], |
|
210 fp, |
|
211 opts=mdiff.diffopts(git=True), |
|
212 match=match, |
|
213 ) |
|
214 fp.seek(0) |
|
215 return fp |
|
216 |
|
217 def load_patch(self, repo): |
|
218 try: |
|
219 # prefer node-based shelf |
|
220 return self.patch_from_node(repo, self.readinfo()[b'node']) |
|
221 except (FileNotFoundError, error.RepoLookupError): |
|
222 return self.open_patch() |
185 |
223 |
186 def _backupfilename(self, backupvfs, filename): |
224 def _backupfilename(self, backupvfs, filename): |
187 def gennames(base): |
225 def gennames(base): |
188 yield base |
226 yield base |
189 base, ext = base.rsplit(b'.', 1) |
227 base, ext = base.rsplit(b'.', 1) |
206 ) |
244 ) |
207 |
245 |
208 def delete(self): |
246 def delete(self): |
209 for ext in shelvefileextensions: |
247 for ext in shelvefileextensions: |
210 self.vfs.tryunlink(self.name + b'.' + ext) |
248 self.vfs.tryunlink(self.name + b'.' + ext) |
|
249 |
|
250 |
|
251 def _optimized_match(repo, node): |
|
252 """ |
|
253 Create a matcher so that prefetch doesn't attempt to fetch |
|
254 the entire repository pointlessly, and as an optimisation |
|
255 for movedirstate, if needed. |
|
256 """ |
|
257 return scmutil.matchfiles(repo, repo[node].files()) |
211 |
258 |
212 |
259 |
213 class shelvedstate: |
260 class shelvedstate: |
214 """Handle persistence during unshelving operations. |
261 """Handle persistence during unshelving operations. |
215 |
262 |
445 def commitfunc(ui, repo, message, match, opts): |
492 def commitfunc(ui, repo, message, match, opts): |
446 hasmq = util.safehasattr(repo, b'mq') |
493 hasmq = util.safehasattr(repo, b'mq') |
447 if hasmq: |
494 if hasmq: |
448 saved, repo.mq.checkapplied = repo.mq.checkapplied, False |
495 saved, repo.mq.checkapplied = repo.mq.checkapplied, False |
449 |
496 |
450 targetphase = phases.internal |
497 targetphase = _target_phase(repo) |
451 if not phases.supportinternal(repo): |
|
452 targetphase = phases.secret |
|
453 overrides = {(b'phases', b'new-commit'): targetphase} |
498 overrides = {(b'phases', b'new-commit'): targetphase} |
454 try: |
499 try: |
455 editor_ = False |
500 editor_ = False |
456 if editor: |
501 if editor: |
457 editor_ = cmdutil.getcommiteditor( |
502 editor_ = cmdutil.getcommiteditor( |
508 extra[b'shelve_unknown'] = b'\0'.join(s.unknown) |
553 extra[b'shelve_unknown'] = b'\0'.join(s.unknown) |
509 repo[None].add(s.unknown) |
554 repo[None].add(s.unknown) |
510 |
555 |
511 |
556 |
512 def _finishshelve(repo, tr): |
557 def _finishshelve(repo, tr): |
513 if phases.supportinternal(repo): |
558 if _use_internal_phase(repo): |
514 tr.close() |
559 tr.close() |
515 else: |
560 else: |
516 _aborttransaction(repo, tr) |
561 _aborttransaction(repo, tr) |
517 |
562 |
518 |
563 |
577 ) |
622 ) |
578 if not node: |
623 if not node: |
579 _nothingtoshelvemessaging(ui, repo, pats, opts) |
624 _nothingtoshelvemessaging(ui, repo, pats, opts) |
580 return 1 |
625 return 1 |
581 |
626 |
582 # Create a matcher so that prefetch doesn't attempt to fetch |
627 match = _optimized_match(repo, node) |
583 # the entire repository pointlessly, and as an optimisation |
|
584 # for movedirstate, if needed. |
|
585 match = scmutil.matchfiles(repo, repo[node].files()) |
|
586 _shelvecreatedcommit(repo, node, name, match) |
628 _shelvecreatedcommit(repo, node, name, match) |
587 |
629 |
588 ui.status(_(b'shelved as %s\n') % name) |
630 ui.status(_(b'shelved as %s\n') % name) |
589 if opts[b'keep']: |
631 if opts[b'keep']: |
590 with repo.dirstate.parentchange(): |
632 with repo.dirstate.parentchange(): |
666 date = dateutil.makedate(mtime) |
708 date = dateutil.makedate(mtime) |
667 age = b'(%s)' % templatefilters.age(date, abbrev=True) |
709 age = b'(%s)' % templatefilters.age(date, abbrev=True) |
668 ui.write(age, label=b'shelve.age') |
710 ui.write(age, label=b'shelve.age') |
669 ui.write(b' ' * (12 - len(age))) |
711 ui.write(b' ' * (12 - len(age))) |
670 used += 12 |
712 used += 12 |
671 with shelf_dir.get(name).open_patch() as fp: |
713 with shelf_dir.get(name).load_patch(repo) as fp: |
672 while True: |
714 while True: |
673 line = fp.readline() |
715 line = fp.readline() |
674 if not line: |
716 if not line: |
675 break |
717 break |
676 if not line.startswith(b'#'): |
718 if not line.startswith(b'#'): |
752 |
794 |
753 merge.clean_update(state.pendingctx) |
795 merge.clean_update(state.pendingctx) |
754 if state.activebookmark and state.activebookmark in repo._bookmarks: |
796 if state.activebookmark and state.activebookmark in repo._bookmarks: |
755 bookmarks.activate(repo, state.activebookmark) |
797 bookmarks.activate(repo, state.activebookmark) |
756 mergefiles(ui, repo, state.wctx, state.pendingctx) |
798 mergefiles(ui, repo, state.wctx, state.pendingctx) |
757 if not phases.supportinternal(repo): |
799 if not _use_internal_phase(repo): |
758 repair.strip( |
800 repair.strip( |
759 ui, repo, state.nodestoremove, backup=False, topic=b'shelve' |
801 ui, repo, state.nodestoremove, backup=False, topic=b'shelve' |
760 ) |
802 ) |
761 finally: |
803 finally: |
762 shelvedstate.clear(repo) |
804 shelvedstate.clear(repo) |
814 |
856 |
815 with repo.dirstate.parentchange(): |
857 with repo.dirstate.parentchange(): |
816 repo.setparents(state.pendingctx.node(), repo.nullid) |
858 repo.setparents(state.pendingctx.node(), repo.nullid) |
817 repo.dirstate.write(repo.currenttransaction()) |
859 repo.dirstate.write(repo.currenttransaction()) |
818 |
860 |
819 targetphase = phases.internal |
861 targetphase = _target_phase(repo) |
820 if not phases.supportinternal(repo): |
|
821 targetphase = phases.secret |
|
822 overrides = {(b'phases', b'new-commit'): targetphase} |
862 overrides = {(b'phases', b'new-commit'): targetphase} |
823 with repo.ui.configoverride(overrides, b'unshelve'): |
863 with repo.ui.configoverride(overrides, b'unshelve'): |
824 with repo.dirstate.parentchange(): |
864 with repo.dirstate.parentchange(): |
825 repo.setparents(state.parents[0], repo.nullid) |
865 repo.setparents(state.parents[0], repo.nullid) |
826 newnode, ispartialunshelve = _createunshelvectx( |
866 newnode, ispartialunshelve = _createunshelvectx( |
841 |
881 |
842 merge.update(pendingctx) |
882 merge.update(pendingctx) |
843 mergefiles(ui, repo, state.wctx, shelvectx) |
883 mergefiles(ui, repo, state.wctx, shelvectx) |
844 restorebranch(ui, repo, state.branchtorestore) |
884 restorebranch(ui, repo, state.branchtorestore) |
845 |
885 |
846 if not phases.supportinternal(repo): |
886 if not _use_internal_phase(repo): |
847 repair.strip( |
887 repair.strip( |
848 ui, repo, state.nodestoremove, backup=False, topic=b'shelve' |
888 ui, repo, state.nodestoremove, backup=False, topic=b'shelve' |
849 ) |
889 ) |
850 shelvedstate.clear(repo) |
890 shelvedstate.clear(repo) |
851 if not ispartialunshelve: |
891 if not ispartialunshelve: |
955 text=shelvectx.description(), |
995 text=shelvectx.description(), |
956 extra=shelvectx.extra(), |
996 extra=shelvectx.extra(), |
957 user=shelvectx.user(), |
997 user=shelvectx.user(), |
958 ) |
998 ) |
959 if snode: |
999 if snode: |
960 m = scmutil.matchfiles(repo, repo[snode].files()) |
1000 m = _optimized_match(repo, snode) |
961 _shelvecreatedcommit(repo, snode, basename, m) |
1001 _shelvecreatedcommit(repo, snode, basename, m) |
962 |
1002 |
963 return newnode, bool(snode) |
1003 return newnode, bool(snode) |
964 |
1004 |
965 |
1005 |
1135 lock = repo.lock() |
1175 lock = repo.lock() |
1136 tr = repo.transaction(b'unshelve', report=lambda x: None) |
1176 tr = repo.transaction(b'unshelve', report=lambda x: None) |
1137 oldtiprev = len(repo) |
1177 oldtiprev = len(repo) |
1138 |
1178 |
1139 pctx = repo[b'.'] |
1179 pctx = repo[b'.'] |
1140 tmpwctx = pctx |
|
1141 # The goal is to have a commit structure like so: |
1180 # The goal is to have a commit structure like so: |
1142 # ...-> pctx -> tmpwctx -> shelvectx |
1181 # ...-> pctx -> tmpwctx -> shelvectx |
1143 # where tmpwctx is an optional commit with the user's pending changes |
1182 # where tmpwctx is an optional commit with the user's pending changes |
1144 # and shelvectx is the unshelved changes. Then we merge it all down |
1183 # and shelvectx is the unshelved changes. Then we merge it all down |
1145 # to the original pctx. |
1184 # to the original pctx. |
1146 |
1185 |
1147 activebookmark = _backupactivebookmark(repo) |
1186 activebookmark = _backupactivebookmark(repo) |
1148 tmpwctx, addedbefore = _commitworkingcopychanges( |
1187 tmpwctx, addedbefore = _commitworkingcopychanges(ui, repo, opts, pctx) |
1149 ui, repo, opts, tmpwctx |
|
1150 ) |
|
1151 repo, shelvectx = _unshelverestorecommit(ui, repo, tr, basename) |
1188 repo, shelvectx = _unshelverestorecommit(ui, repo, tr, basename) |
1152 _checkunshelveuntrackedproblems(ui, repo, shelvectx) |
1189 _checkunshelveuntrackedproblems(ui, repo, shelvectx) |
1153 branchtorestore = b'' |
1190 branchtorestore = b'' |
1154 if shelvectx.branch() != shelvectx.p1().branch(): |
1191 if shelvectx.branch() != shelvectx.p1().branch(): |
1155 branchtorestore = shelvectx.branch() |
1192 branchtorestore = shelvectx.branch() |