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