Show More
@@ -1545,11 +1545,19 b' def overridesummary(orig, ui, repo, *pat' | |||||
1545 |
|
1545 | |||
1546 |
|
1546 | |||
1547 | @eh.wrapfunction(scmutil, b'addremove') |
|
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 | if opts is None: |
|
1557 | if opts is None: | |
1550 | opts = {} |
|
1558 | opts = {} | |
1551 | if not lfutil.islfilesrepo(repo): |
|
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 | # Get the list of missing largefiles so we can remove them |
|
1561 | # Get the list of missing largefiles so we can remove them | |
1554 | lfdirstate = lfutil.openlfdirstate(repo.ui, repo) |
|
1562 | lfdirstate = lfutil.openlfdirstate(repo.ui, repo) | |
1555 | unsure, s, mtime_boundary = lfdirstate.status( |
|
1563 | unsure, s, mtime_boundary = lfdirstate.status( | |
@@ -1560,6 +1568,10 b' def scmutiladdremove(orig, repo, matcher' | |||||
1560 | unknown=False, |
|
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 | # Call into the normal remove code, but the removing of the standin, we want |
|
1575 | # Call into the normal remove code, but the removing of the standin, we want | |
1564 | # to have handled by original addremove. Monkey patching here makes sure |
|
1576 | # to have handled by original addremove. Monkey patching here makes sure | |
1565 | # we don't remove the standin in the largefiles code, preventing a very |
|
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 | # function to take care of the rest. Make sure it doesn't do anything with |
|
1604 | # function to take care of the rest. Make sure it doesn't do anything with | |
1593 | # largefiles by passing a matcher that will ignore them. |
|
1605 | # largefiles by passing a matcher that will ignore them. | |
1594 | matcher = composenormalfilematcher(matcher, repo[None].manifest(), added) |
|
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 | # Calling purge with --all will cause the largefiles to be deleted. |
|
1611 | # Calling purge with --all will cause the largefiles to be deleted. |
@@ -29,7 +29,6 b' from . import (' | |||||
29 | changelog, |
|
29 | changelog, | |
30 | copies, |
|
30 | copies, | |
31 | crecord as crecordmod, |
|
31 | crecord as crecordmod, | |
32 | dirstateguard, |
|
|||
33 | encoding, |
|
32 | encoding, | |
34 | error, |
|
33 | error, | |
35 | formatter, |
|
34 | formatter, | |
@@ -2789,21 +2788,114 b' def cat(ui, repo, ctx, matcher, basefm, ' | |||||
2789 | return err |
|
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 | def commit(ui, repo, commitfunc, pats, opts): |
|
2888 | def commit(ui, repo, commitfunc, pats, opts): | |
2793 | '''commit the specified files or all outstanding changes''' |
|
2889 | '''commit the specified files or all outstanding changes''' | |
2794 | date = opts.get(b'date') |
|
2890 | date = opts.get(b'date') | |
2795 | if date: |
|
2891 | if date: | |
2796 | opts[b'date'] = dateutil.parsedate(date) |
|
2892 | opts[b'date'] = dateutil.parsedate(date) | |
2797 |
|
2893 | |||
2798 | dsguard = None |
|
2894 | with repo.wlock(), repo.lock(): | |
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(): |
|
|||
2804 | message = logmessage(ui, opts) |
|
2895 | message = logmessage(ui, opts) | |
2805 | matcher = scmutil.match(repo[None], pats, opts) |
|
2896 | matcher = scmutil.match(repo[None], pats, opts) | |
2806 | if True: |
|
2897 | ||
|
2898 | with _AddRemoveContext(repo) as c: | |||
2807 | # extract addremove carefully -- this function can be called from a |
|
2899 | # extract addremove carefully -- this function can be called from a | |
2808 | # command that doesn't support addremove |
|
2900 | # command that doesn't support addremove | |
2809 | if opts.get(b'addremove'): |
|
2901 | if opts.get(b'addremove'): | |
@@ -2818,11 +2910,12 b' def commit(ui, repo, commitfunc, pats, o' | |||||
2818 | b"", |
|
2910 | b"", | |
2819 | uipathfn, |
|
2911 | uipathfn, | |
2820 | opts, |
|
2912 | opts, | |
|
2913 | open_tr=c.open_transaction, | |||
2821 | ) |
|
2914 | ) | |
2822 | m = _(b"failed to mark all new/missing files as added/removed") |
|
2915 | m = _(b"failed to mark all new/missing files as added/removed") | |
2823 | if r != 0: |
|
2916 | if r != 0: | |
2824 | raise error.Abort(m) |
|
2917 | raise error.Abort(m) | |
2825 |
|
2918 | c.close_dirstate_context() | ||
2826 | return commitfunc(ui, repo, message, matcher, opts) |
|
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 | if opts is None: |
|
1223 | if opts is None: | |
1224 | opts = {} |
|
1224 | opts = {} | |
1225 | m = matcher |
|
1225 | m = matcher | |
@@ -1279,7 +1279,9 b' def addremove(repo, matcher, prefix, uip' | |||||
1279 | repo, m, added + unknown, removed + deleted, similarity, uipathfn |
|
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 | _markchanges(repo, unknown + forgotten, deleted, renames) |
|
1285 | _markchanges(repo, unknown + forgotten, deleted, renames) | |
1284 |
|
1286 | |||
1285 | for f in rejected: |
|
1287 | for f in rejected: |
@@ -103,12 +103,10 b' Non store repo:' | |||||
103 | .hg/phaseroots |
|
103 | .hg/phaseroots | |
104 | .hg/requires |
|
104 | .hg/requires | |
105 | .hg/undo |
|
105 | .hg/undo | |
106 | .hg/undo.backup.dirstate |
|
|||
107 | .hg/undo.backupfiles |
|
106 | .hg/undo.backupfiles | |
108 | .hg/undo.bookmarks |
|
107 | .hg/undo.bookmarks | |
109 | .hg/undo.branch |
|
108 | .hg/undo.branch | |
110 | .hg/undo.desc |
|
109 | .hg/undo.desc | |
111 | .hg/undo.dirstate |
|
|||
112 | .hg/undo.phaseroots |
|
110 | .hg/undo.phaseroots | |
113 | .hg/wcache |
|
111 | .hg/wcache | |
114 | .hg/wcache/checkisexec (execbit !) |
|
112 | .hg/wcache/checkisexec (execbit !) | |
@@ -147,11 +145,9 b' Non fncache repo:' | |||||
147 | .hg/store/undo |
|
145 | .hg/store/undo | |
148 | .hg/store/undo.backupfiles |
|
146 | .hg/store/undo.backupfiles | |
149 | .hg/store/undo.phaseroots |
|
147 | .hg/store/undo.phaseroots | |
150 | .hg/undo.backup.dirstate |
|
|||
151 | .hg/undo.bookmarks |
|
148 | .hg/undo.bookmarks | |
152 | .hg/undo.branch |
|
149 | .hg/undo.branch | |
153 | .hg/undo.desc |
|
150 | .hg/undo.desc | |
154 | .hg/undo.dirstate |
|
|||
155 | .hg/wcache |
|
151 | .hg/wcache | |
156 | .hg/wcache/checkisexec (execbit !) |
|
152 | .hg/wcache/checkisexec (execbit !) | |
157 | .hg/wcache/checklink (symlink !) |
|
153 | .hg/wcache/checklink (symlink !) |
@@ -95,11 +95,9 b' new directories are setgid' | |||||
95 | 00660 ./.hg/store/undo |
|
95 | 00660 ./.hg/store/undo | |
96 | 00660 ./.hg/store/undo.backupfiles |
|
96 | 00660 ./.hg/store/undo.backupfiles | |
97 | 00660 ./.hg/store/undo.phaseroots |
|
97 | 00660 ./.hg/store/undo.phaseroots | |
98 | 00660 ./.hg/undo.backup.dirstate |
|
|||
99 | 00660 ./.hg/undo.bookmarks |
|
98 | 00660 ./.hg/undo.bookmarks | |
100 | 00660 ./.hg/undo.branch |
|
99 | 00660 ./.hg/undo.branch | |
101 | 00660 ./.hg/undo.desc |
|
100 | 00660 ./.hg/undo.desc | |
102 | 00660 ./.hg/undo.dirstate |
|
|||
103 | 00770 ./.hg/wcache/ |
|
101 | 00770 ./.hg/wcache/ | |
104 | 00711 ./.hg/wcache/checkisexec |
|
102 | 00711 ./.hg/wcache/checkisexec | |
105 | 007.. ./.hg/wcache/checklink (re) |
|
103 | 007.. ./.hg/wcache/checklink (re) |
@@ -106,10 +106,10 b' happen for the changelog (the linkrev sh' | |||||
106 |
|
106 | |||
107 | #if fail-if-detected |
|
107 | #if fail-if-detected | |
108 | $ cat .foo_commit_out |
|
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 | transaction abort! |
|
111 | transaction abort! | |
110 | rollback completed |
|
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 | abort: 00changelog.i: file cursor at position 249, expected 121 |
|
113 | abort: 00changelog.i: file cursor at position 249, expected 121 | |
114 | And no corruption in the changelog. |
|
114 | And no corruption in the changelog. | |
115 | $ hg debugrevlogindex -c |
|
115 | $ hg debugrevlogindex -c |
General Comments 0
You need to be logged in to leave comments.
Login now