##// END OF EJS Templates
chainsaw-update: log actual locks breaking...
Georges Racinet -
r52324:fe68a2dc default
parent child Browse files
Show More
@@ -1,156 +1,157 b''
1 # chainsaw.py
1 # chainsaw.py
2 #
2 #
3 # Copyright 2022 Georges Racinet <georges.racinet@octobus.net>
3 # Copyright 2022 Georges Racinet <georges.racinet@octobus.net>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7 """chainsaw is a collection of single-minded and dangerous tools. (EXPERIMENTAL)
7 """chainsaw is a collection of single-minded and dangerous tools. (EXPERIMENTAL)
8
8
9 "Don't use a chainsaw to cut your food!"
9 "Don't use a chainsaw to cut your food!"
10
10
11 The chainsaw extension provides commands that are so much geared towards a
11 The chainsaw extension provides commands that are so much geared towards a
12 specific use case in a specific context or environment that they are totally
12 specific use case in a specific context or environment that they are totally
13 inappropriate and **really dangerous** in other contexts.
13 inappropriate and **really dangerous** in other contexts.
14
14
15 The help text of each command explicitly summarizes its context of application
15 The help text of each command explicitly summarizes its context of application
16 and the wanted end result.
16 and the wanted end result.
17
17
18 It is recommended to run these commands with the ``HGPLAIN`` environment
18 It is recommended to run these commands with the ``HGPLAIN`` environment
19 variable (see :hg:`help scripting`).
19 variable (see :hg:`help scripting`).
20 """
20 """
21
21
22 import shutil
22 import shutil
23
23
24 from mercurial.i18n import _
24 from mercurial.i18n import _
25 from mercurial import (
25 from mercurial import (
26 cmdutil,
26 cmdutil,
27 commands,
27 commands,
28 error,
28 error,
29 registrar,
29 registrar,
30 )
30 )
31
31
32 cmdtable = {}
32 cmdtable = {}
33 command = registrar.command(cmdtable)
33 command = registrar.command(cmdtable)
34 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
34 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
35 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
35 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
36 # be specifying the version(s) of Mercurial they are tested with, or
36 # be specifying the version(s) of Mercurial they are tested with, or
37 # leave the attribute unspecified.
37 # leave the attribute unspecified.
38 testedwith = b'ships-with-hg-core'
38 testedwith = b'ships-with-hg-core'
39
39
40
40
41 @command(
41 @command(
42 b'admin::chainsaw-update',
42 b'admin::chainsaw-update',
43 [
43 [
44 (
44 (
45 b'',
45 b'',
46 b'purge-unknown',
46 b'purge-unknown',
47 True,
47 True,
48 _(
48 _(
49 b'Remove unversioned files before update. Disabling this can '
49 b'Remove unversioned files before update. Disabling this can '
50 b'in some cases interfere with the update.'
50 b'in some cases interfere with the update.'
51 b'See also :hg:`purge`.'
51 b'See also :hg:`purge`.'
52 ),
52 ),
53 ),
53 ),
54 (
54 (
55 b'',
55 b'',
56 b'purge-ignored',
56 b'purge-ignored',
57 True,
57 True,
58 _(
58 _(
59 b'Remove ignored files before update. Disable this for '
59 b'Remove ignored files before update. Disable this for '
60 b'instance to reuse previous compiler object files. '
60 b'instance to reuse previous compiler object files. '
61 b'See also :hg:`purge`.'
61 b'See also :hg:`purge`.'
62 ),
62 ),
63 ),
63 ),
64 (
64 (
65 b'',
65 b'',
66 b'rev',
66 b'rev',
67 b'',
67 b'',
68 _(b'revision to update to'),
68 _(b'revision to update to'),
69 ),
69 ),
70 (
70 (
71 b'',
71 b'',
72 b'source',
72 b'source',
73 b'',
73 b'',
74 _(b'repository to clone from'),
74 _(b'repository to clone from'),
75 ),
75 ),
76 ],
76 ],
77 _(b'hg admin::chainsaw-update [OPTION] --rev REV --source SOURCE...'),
77 _(b'hg admin::chainsaw-update [OPTION] --rev REV --source SOURCE...'),
78 helpbasic=True,
78 helpbasic=True,
79 )
79 )
80 def update(ui, repo, **opts):
80 def update(ui, repo, **opts):
81 """pull and update to a given revision, no matter what, (EXPERIMENTAL)
81 """pull and update to a given revision, no matter what, (EXPERIMENTAL)
82
82
83 Context of application: *some* Continuous Integration (CI) systems,
83 Context of application: *some* Continuous Integration (CI) systems,
84 packaging or deployment tools.
84 packaging or deployment tools.
85
85
86 Wanted end result: clean working directory updated at the given revision.
86 Wanted end result: clean working directory updated at the given revision.
87
87
88 chainsaw-update pulls from one source, then updates the working directory
88 chainsaw-update pulls from one source, then updates the working directory
89 to the given revision, overcoming anything that would stand in the way.
89 to the given revision, overcoming anything that would stand in the way.
90
90
91 By default, it will:
91 By default, it will:
92
92
93 - break locks if needed, leading to possible corruption if there
93 - break locks if needed, leading to possible corruption if there
94 is a concurrent write access.
94 is a concurrent write access.
95 - perform recovery actions if needed
95 - perform recovery actions if needed
96 - revert any local modification.
96 - revert any local modification.
97 - purge unknown and ignored files.
97 - purge unknown and ignored files.
98 - go as far as to reclone if everything else failed (not implemented yet).
98 - go as far as to reclone if everything else failed (not implemented yet).
99
99
100 DO NOT use it for anything else than performing a series
100 DO NOT use it for anything else than performing a series
101 of unattended updates, with full exclusive repository access each time
101 of unattended updates, with full exclusive repository access each time
102 and without any other local work than running build scripts.
102 and without any other local work than running build scripts.
103 In case the local repository is a share (see :hg:`help share`), exclusive
103 In case the local repository is a share (see :hg:`help share`), exclusive
104 write access to the share source is also mandatory.
104 write access to the share source is also mandatory.
105
105
106 It is recommended to run these commands with the ``HGPLAIN`` environment
106 It is recommended to run these commands with the ``HGPLAIN`` environment
107 variable (see :hg:`scripting`).
107 variable (see :hg:`scripting`).
108
108
109 Motivation: in Continuous Integration and Delivery systems (CI/CD), the
109 Motivation: in Continuous Integration and Delivery systems (CI/CD), the
110 occasional remnant or bogus lock are common sources of waste of time (both
110 occasional remnant or bogus lock are common sources of waste of time (both
111 working time and calendar time). CI/CD scripts tend to grow with counter-
111 working time and calendar time). CI/CD scripts tend to grow with counter-
112 measures, often done in urgency. Also, whilst it is neat to keep
112 measures, often done in urgency. Also, whilst it is neat to keep
113 repositories from one job to the next (especially with large
113 repositories from one job to the next (especially with large
114 repositories), an exceptional recloning is better than missing a release
114 repositories), an exceptional recloning is better than missing a release
115 deadline.
115 deadline.
116 """
116 """
117 rev = opts['rev']
117 rev = opts['rev']
118 source = opts['source']
118 source = opts['source']
119 if not rev:
119 if not rev:
120 raise error.InputError(_(b'specify a target revision with --rev'))
120 raise error.InputError(_(b'specify a target revision with --rev'))
121 if not source:
121 if not source:
122 raise error.InputError(_(b'specify a pull path with --source'))
122 raise error.InputError(_(b'specify a pull path with --source'))
123 ui.status(_(b'breaking locks, if any\n'))
123 if repo.svfs.tryunlink(b'lock'):
124 repo.svfs.tryunlink(b'lock')
124 ui.status(_(b'had to break store lock\n'))
125 repo.vfs.tryunlink(b'wlock')
125 if repo.vfs.tryunlink(b'wlock'):
126 ui.status(_(b'had to break working copy lock\n'))
126
127
127 ui.status(_(b'recovering after interrupted transaction, if any\n'))
128 ui.status(_(b'recovering after interrupted transaction, if any\n'))
128 repo.recover()
129 repo.recover()
129
130
130 ui.status(_(b'pulling from %s\n') % source)
131 ui.status(_(b'pulling from %s\n') % source)
131 overrides = {(b'ui', b'quiet'): True}
132 overrides = {(b'ui', b'quiet'): True}
132 with ui.configoverride(overrides, b'chainsaw-update'):
133 with ui.configoverride(overrides, b'chainsaw-update'):
133 pull = cmdutil.findcmd(b'pull', commands.table)[1][0]
134 pull = cmdutil.findcmd(b'pull', commands.table)[1][0]
134 pull(ui, repo, source, rev=[rev], remote_hidden=False)
135 pull(ui, repo, source, rev=[rev], remote_hidden=False)
135
136
136 purge = cmdutil.findcmd(b'purge', commands.table)[1][0]
137 purge = cmdutil.findcmd(b'purge', commands.table)[1][0]
137 purge(
138 purge(
138 ui,
139 ui,
139 repo,
140 repo,
140 dirs=True,
141 dirs=True,
141 all=opts.get('purge_ignored'),
142 all=opts.get('purge_ignored'),
142 files=opts.get('purge_unknown'),
143 files=opts.get('purge_unknown'),
143 confirm=False,
144 confirm=False,
144 )
145 )
145
146
146 ui.status(_(b'updating to revision \'%s\'\n') % rev)
147 ui.status(_(b'updating to revision \'%s\'\n') % rev)
147 update = cmdutil.findcmd(b'update', commands.table)[1][0]
148 update = cmdutil.findcmd(b'update', commands.table)[1][0]
148 update(ui, repo, rev=rev, clean=True)
149 update(ui, repo, rev=rev, clean=True)
149
150
150 ui.status(
151 ui.status(
151 _(
152 _(
152 b'chainsaw-update to revision \'%s\' '
153 b'chainsaw-update to revision \'%s\' '
153 b'for repository at \'%s\' done\n'
154 b'for repository at \'%s\' done\n'
154 )
155 )
155 % (rev, repo.root)
156 % (rev, repo.root)
156 )
157 )
@@ -1,105 +1,111 b''
1 ============================================
1 ============================================
2 Tests for the admin::chainsaw-update command
2 Tests for the admin::chainsaw-update command
3 ============================================
3 ============================================
4
4
5 setup
5 setup
6 =====
6 =====
7
7
8 $ cat >> $HGRCPATH << EOF
8 $ cat >> $HGRCPATH << EOF
9 > [extensions]
9 > [extensions]
10 > chainsaw=
10 > chainsaw=
11 > EOF
11 > EOF
12
12
13 $ hg init src
13 $ hg init src
14 $ cd src
14 $ cd src
15 $ echo 1 > foo
15 $ echo 1 > foo
16 $ hg ci -Am1
16 $ hg ci -Am1
17 adding foo
17 adding foo
18 $ cd ..
18 $ cd ..
19
19
20 Actual tests
20 Actual tests
21 ============
21 ============
22
22
23 Simple invocation
23 Simple invocation
24 -----------------
24 -----------------
25
25
26 $ hg init repo
26 $ hg init repo
27 $ cd repo
27 $ cd repo
28 $ hg admin::chainsaw-update --rev default --source ../src
28 $ hg admin::chainsaw-update --rev default --source ../src
29 breaking locks, if any
30 recovering after interrupted transaction, if any
29 recovering after interrupted transaction, if any
31 no interrupted transaction available
30 no interrupted transaction available
32 pulling from ../src
31 pulling from ../src
33 updating to revision 'default'
32 updating to revision 'default'
34 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
33 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
35 chainsaw-update to revision 'default' for repository at '$TESTTMP/repo' done
34 chainsaw-update to revision 'default' for repository at '$TESTTMP/repo' done
36
35
37 $ cat foo
36 $ cat foo
38 1
37 1
39
38
40 Test lock breacking capabilities
39 Test lock breacking capabilities
41 --------------------------------
40 --------------------------------
42
41
43 Demonstrate lock-breaking capabilities with locks that regular Mercurial
42 Demonstrate lock-breaking capabilities with locks that regular Mercurial
44 operation would not break, because the hostnames registered in locks differ
43 operation would not break, because the hostnames registered in locks differ
45 from the current hostname (happens a lot with succesive containers):
44 from the current hostname (happens a lot with succesive containers):
46
45
47 $ ln -s invalid.host.test/effffffc:171814 .hg/store/lock
46 $ ln -s invalid.host.test/effffffc:171814 .hg/store/lock
48 $ ln -s invalid.host.test/effffffc:171814 .hg/wlock
47 $ ln -s invalid.host.test/effffffc:171814 .hg/wlock
49 $ hg debuglock
48 $ hg debuglock
50 lock: (.*?), process 171814, host invalid.host.test/effffffc \((\d+)s\) (re)
49 lock: (.*?), process 171814, host invalid.host.test/effffffc \((\d+)s\) (re)
51 wlock: (.*?), process 171814, host invalid.host.test/effffffc \((\d+)s\) (re)
50 wlock: (.*?), process 171814, host invalid.host.test/effffffc \((\d+)s\) (re)
52 [2]
51 [2]
53
52
54 $ hg admin::chainsaw-update --no-purge-ignored --rev default --source ../src -q
53 $ hg admin::chainsaw-update --no-purge-ignored --rev default --source ../src
54 had to break store lock
55 had to break working copy lock
56 recovering after interrupted transaction, if any
55 no interrupted transaction available
57 no interrupted transaction available
58 pulling from ../src
59 updating to revision 'default'
60 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
61 chainsaw-update to revision 'default' for repository at '$TESTTMP/repo' done
56
62
57 Test file purging capabilities
63 Test file purging capabilities
58 ------------------------------
64 ------------------------------
59
65
60 Let's also add local modifications (tracked and untracked) to demonstrate the
66 Let's also add local modifications (tracked and untracked) to demonstrate the
61 purging.
67 purging.
62
68
63 $ echo untracked > bar
69 $ echo untracked > bar
64 $ echo modified > foo
70 $ echo modified > foo
65 $ hg status -A
71 $ hg status -A
66 M foo
72 M foo
67 ? bar
73 ? bar
68
74
69 $ echo 2 > ../src/foo
75 $ echo 2 > ../src/foo
70 $ hg -R ../src commit -m2
76 $ hg -R ../src commit -m2
71 $ hg admin::chainsaw-update --rev default --source ../src -q
77 $ hg admin::chainsaw-update --rev default --source ../src -q
72 no interrupted transaction available
78 no interrupted transaction available
73 $ hg status -A
79 $ hg status -A
74 C foo
80 C foo
75 $ cat foo
81 $ cat foo
76 2
82 2
77
83
78 Now behaviour with respect to ignored files: they are not purged if
84 Now behaviour with respect to ignored files: they are not purged if
79 the --no-purge-ignored flag is passed, but they are purged by default
85 the --no-purge-ignored flag is passed, but they are purged by default
80
86
81 $ echo bar > .hgignore
87 $ echo bar > .hgignore
82 $ hg ci -Aqm hgignore
88 $ hg ci -Aqm hgignore
83 $ echo ignored > bar
89 $ echo ignored > bar
84 $ hg status --all
90 $ hg status --all
85 I bar
91 I bar
86 C .hgignore
92 C .hgignore
87 C foo
93 C foo
88
94
89 $ hg admin::chainsaw-update --no-purge-ignored --rev default --source ../src -q
95 $ hg admin::chainsaw-update --no-purge-ignored --rev default --source ../src -q
90 no interrupted transaction available
96 no interrupted transaction available
91 $ hg status --all
97 $ hg status --all
92 I bar
98 I bar
93 C .hgignore
99 C .hgignore
94 C foo
100 C foo
95 $ cat bar
101 $ cat bar
96 ignored
102 ignored
97
103
98 $ hg admin::chainsaw-update --rev default --source ../src -q
104 $ hg admin::chainsaw-update --rev default --source ../src -q
99 no interrupted transaction available
105 no interrupted transaction available
100 $ hg status --all
106 $ hg status --all
101 C .hgignore
107 C .hgignore
102 C foo
108 C foo
103 $ test -f bar
109 $ test -f bar
104 [1]
110 [1]
105
111
General Comments 0
You need to be logged in to leave comments. Login now