2787 ) |
2786 ) |
2788 |
2787 |
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'): |
2810 relative = scmutil.anypats(pats, opts) |
2902 relative = scmutil.anypats(pats, opts) |
2811 uipathfn = scmutil.getuipathfn( |
2903 uipathfn = scmutil.getuipathfn( |