##// END OF EJS Templates
commit: use `dirstate.change_files` to scope the associated `addremove`...
marmoute -
r50924:28dfb2df default
parent child Browse files
Show More
@@ -1545,11 +1545,19 b' def overridesummary(orig, ui, repo, *pat'
1545 1545
1546 1546
1547 1547 @eh.wrapfunction(scmutil, b'addremove')
1548 def scmutiladdremove(orig, repo, matcher, prefix, uipathfn, opts=None):
1548 def scmutiladdremove(
1549 orig,
1550 repo,
1551 matcher,
1552 prefix,
1553 uipathfn,
1554 opts=None,
1555 open_tr=None,
1556 ):
1549 1557 if opts is None:
1550 1558 opts = {}
1551 1559 if not lfutil.islfilesrepo(repo):
1552 return orig(repo, matcher, prefix, uipathfn, opts)
1560 return orig(repo, matcher, prefix, uipathfn, opts, open_tr=open_tr)
1553 1561 # Get the list of missing largefiles so we can remove them
1554 1562 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
1555 1563 unsure, s, mtime_boundary = lfdirstate.status(
@@ -1560,6 +1568,10 b' def scmutiladdremove(orig, repo, matcher'
1560 1568 unknown=False,
1561 1569 )
1562 1570
1571 # open the transaction and changing_files context
1572 if open_tr is not None:
1573 open_tr()
1574
1563 1575 # Call into the normal remove code, but the removing of the standin, we want
1564 1576 # to have handled by original addremove. Monkey patching here makes sure
1565 1577 # we don't remove the standin in the largefiles code, preventing a very
@@ -1592,7 +1604,8 b' def scmutiladdremove(orig, repo, matcher'
1592 1604 # function to take care of the rest. Make sure it doesn't do anything with
1593 1605 # largefiles by passing a matcher that will ignore them.
1594 1606 matcher = composenormalfilematcher(matcher, repo[None].manifest(), added)
1595 return orig(repo, matcher, prefix, uipathfn, opts)
1607
1608 return orig(repo, matcher, prefix, uipathfn, opts, open_tr=open_tr)
1596 1609
1597 1610
1598 1611 # Calling purge with --all will cause the largefiles to be deleted.
@@ -29,7 +29,6 b' from . import ('
29 29 changelog,
30 30 copies,
31 31 crecord as crecordmod,
32 dirstateguard,
33 32 encoding,
34 33 error,
35 34 formatter,
@@ -2789,21 +2788,114 b' def cat(ui, repo, ctx, matcher, basefm, '
2789 2788 return err
2790 2789
2791 2790
2791 class _AddRemoveContext:
2792 """a small (hacky) context to deal with lazy opening of context
2793
2794 This is to be used in the `commit` function right below. This deals with
2795 lazily open a `changing_files` context inside a `transaction` that span the
2796 full commit operation.
2797
2798 We need :
2799 - a `changing_files` context to wrap the dirstate change within the
2800 "addremove" operation,
2801 - a transaction to make sure these change are not written right after the
2802 addremove, but when the commit operation succeed.
2803
2804 However it get complicated because:
2805 - opening a transaction "this early" shuffle hooks order, especially the
2806 `precommit` one happening after the `pretxtopen` one which I am not too
2807 enthusiastic about.
2808 - the `mq` extensions + the `record` extension stacks many layers of call
2809 to implement `qrefresh --interactive` and this result with `mq` calling a
2810 `strip` in the middle of this function. Which prevent the existence of
2811 transaction wrapping all of its function code. (however, `qrefresh` never
2812 call the `addremove` bits.
2813 - the largefile extensions (and maybe other extensions?) wraps `addremove`
2814 so slicing `addremove` in smaller bits is a complex endeavour.
2815
2816 So I eventually took a this shortcut that open the transaction if we
2817 actually needs it, not disturbing much of the rest of the code.
2818
2819 It will result in some hooks order change for `hg commit --addremove`,
2820 however it seems a corner case enough to ignore that for now (hopefully).
2821
2822 Notes that None of the above problems seems insurmountable, however I have
2823 been fighting with this specific piece of code for a couple of day already
2824 and I need a solution to keep moving forward on the bigger work around
2825 `changing_files` context that is being introduced at the same time as this
2826 hack.
2827
2828 Each problem seems to have a solution:
2829 - the hook order issue could be solved by refactoring the many-layer stack
2830 that currently composes a commit and calling them earlier,
2831 - the mq issue could be solved by refactoring `mq` so that the final strip
2832 is done after transaction closure. Be warned that the mq code is quite
2833 antic however.
2834 - large-file could be reworked in parallel of the `addremove` to be
2835 friendlier to this.
2836
2837 However each of these tasks are too much a diversion right now. In addition
2838 they will be much easier to undertake when the `changing_files` dust has
2839 settled."""
2840
2841 def __init__(self, repo):
2842 self._repo = repo
2843 self._transaction = None
2844 self._dirstate_context = None
2845 self._state = None
2846
2847 def __enter__(self):
2848 assert self._state is None
2849 self._state = True
2850 return self
2851
2852 def open_transaction(self):
2853 """open a `transaction` and `changing_files` context
2854
2855 Call this when you know that change to the dirstate will be needed and
2856 we need to open the transaction early
2857
2858 This will also open the dirstate `changing_files` context, so you should
2859 call `close_dirstate_context` when the distate changes are done.
2860 """
2861 assert self._state is not None
2862 if self._transaction is None:
2863 self._transaction = self._repo.transaction(b'commit')
2864 self._transaction.__enter__()
2865 if self._dirstate_context is None:
2866 self._dirstate_context = self._repo.dirstate.changing_files(
2867 self._repo
2868 )
2869 self._dirstate_context.__enter__()
2870
2871 def close_dirstate_context(self):
2872 """close the change_files if any
2873
2874 Call this after the (potential) `open_transaction` call to close the
2875 (potential) changing_files context.
2876 """
2877 if self._dirstate_context is not None:
2878 self._dirstate_context.__exit__(None, None, None)
2879 self._dirstate_context = None
2880
2881 def __exit__(self, *args):
2882 if self._dirstate_context is not None:
2883 self._dirstate_context.__exit__(*args)
2884 if self._transaction is not None:
2885 self._transaction.__exit__(*args)
2886
2887
2792 2888 def commit(ui, repo, commitfunc, pats, opts):
2793 2889 '''commit the specified files or all outstanding changes'''
2794 2890 date = opts.get(b'date')
2795 2891 if date:
2796 2892 opts[b'date'] = dateutil.parsedate(date)
2797 2893
2798 dsguard = None
2799 # extract addremove carefully -- this function can be called from a command
2800 # that doesn't support addremove
2801 if opts.get(b'addremove'):
2802 dsguard = dirstateguard.dirstateguard(repo, b'commit')
2803 with dsguard or util.nullcontextmanager():
2894 with repo.wlock(), repo.lock():
2804 2895 message = logmessage(ui, opts)
2805 2896 matcher = scmutil.match(repo[None], pats, opts)
2806 if True:
2897
2898 with _AddRemoveContext(repo) as c:
2807 2899 # extract addremove carefully -- this function can be called from a
2808 2900 # command that doesn't support addremove
2809 2901 if opts.get(b'addremove'):
@@ -2818,11 +2910,12 b' def commit(ui, repo, commitfunc, pats, o'
2818 2910 b"",
2819 2911 uipathfn,
2820 2912 opts,
2913 open_tr=c.open_transaction,
2821 2914 )
2822 2915 m = _(b"failed to mark all new/missing files as added/removed")
2823 2916 if r != 0:
2824 2917 raise error.Abort(m)
2825
2918 c.close_dirstate_context()
2826 2919 return commitfunc(ui, repo, message, matcher, opts)
2827 2920
2828 2921
@@ -1219,7 +1219,7 b' def cleanupnodes('
1219 1219 )
1220 1220
1221 1221
1222 def addremove(repo, matcher, prefix, uipathfn, opts=None):
1222 def addremove(repo, matcher, prefix, uipathfn, opts=None, open_tr=None):
1223 1223 if opts is None:
1224 1224 opts = {}
1225 1225 m = matcher
@@ -1279,7 +1279,9 b' def addremove(repo, matcher, prefix, uip'
1279 1279 repo, m, added + unknown, removed + deleted, similarity, uipathfn
1280 1280 )
1281 1281
1282 if not dry_run:
1282 if not dry_run and (unknown or forgotten or deleted or renames):
1283 if open_tr is not None:
1284 open_tr()
1283 1285 _markchanges(repo, unknown + forgotten, deleted, renames)
1284 1286
1285 1287 for f in rejected:
@@ -103,12 +103,10 b' Non store repo:'
103 103 .hg/phaseroots
104 104 .hg/requires
105 105 .hg/undo
106 .hg/undo.backup.dirstate
107 106 .hg/undo.backupfiles
108 107 .hg/undo.bookmarks
109 108 .hg/undo.branch
110 109 .hg/undo.desc
111 .hg/undo.dirstate
112 110 .hg/undo.phaseroots
113 111 .hg/wcache
114 112 .hg/wcache/checkisexec (execbit !)
@@ -147,11 +145,9 b' Non fncache repo:'
147 145 .hg/store/undo
148 146 .hg/store/undo.backupfiles
149 147 .hg/store/undo.phaseroots
150 .hg/undo.backup.dirstate
151 148 .hg/undo.bookmarks
152 149 .hg/undo.branch
153 150 .hg/undo.desc
154 .hg/undo.dirstate
155 151 .hg/wcache
156 152 .hg/wcache/checkisexec (execbit !)
157 153 .hg/wcache/checklink (symlink !)
@@ -95,11 +95,9 b' new directories are setgid'
95 95 00660 ./.hg/store/undo
96 96 00660 ./.hg/store/undo.backupfiles
97 97 00660 ./.hg/store/undo.phaseroots
98 00660 ./.hg/undo.backup.dirstate
99 98 00660 ./.hg/undo.bookmarks
100 99 00660 ./.hg/undo.branch
101 100 00660 ./.hg/undo.desc
102 00660 ./.hg/undo.dirstate
103 101 00770 ./.hg/wcache/
104 102 00711 ./.hg/wcache/checkisexec
105 103 007.. ./.hg/wcache/checklink (re)
@@ -106,10 +106,10 b' happen for the changelog (the linkrev sh'
106 106
107 107 #if fail-if-detected
108 108 $ cat .foo_commit_out
109 note: commit message saved in .hg/last-message.txt
110 note: use 'hg commit --logfile .hg/last-message.txt --edit' to reuse it
109 111 transaction abort!
110 112 rollback completed
111 note: commit message saved in .hg/last-message.txt
112 note: use 'hg commit --logfile .hg/last-message.txt --edit' to reuse it
113 113 abort: 00changelog.i: file cursor at position 249, expected 121
114 114 And no corruption in the changelog.
115 115 $ hg debugrevlogindex -c
General Comments 0
You need to be logged in to leave comments. Login now