##// END OF EJS Templates
chainsaw-update: taking care of initial cloning...
Georges Racinet -
r52326:d36a81d7 default
parent child Browse files
Show More
@@ -1,157 +1,217 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 localrepo,
29 registrar,
30 registrar,
30 )
31 )
32 from mercurial.utils import (
33 urlutil,
34 )
31
35
32 cmdtable = {}
36 cmdtable = {}
33 command = registrar.command(cmdtable)
37 command = registrar.command(cmdtable)
34 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
38 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
35 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
39 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
36 # be specifying the version(s) of Mercurial they are tested with, or
40 # be specifying the version(s) of Mercurial they are tested with, or
37 # leave the attribute unspecified.
41 # leave the attribute unspecified.
38 testedwith = b'ships-with-hg-core'
42 testedwith = b'ships-with-hg-core'
39
43
40
44
41 @command(
45 @command(
42 b'admin::chainsaw-update',
46 b'admin::chainsaw-update',
43 [
47 [
44 (
48 (
45 b'',
49 b'',
46 b'purge-unknown',
50 b'purge-unknown',
47 True,
51 True,
48 _(
52 _(
49 b'Remove unversioned files before update. Disabling this can '
53 b'Remove unversioned files before update. Disabling this can '
50 b'in some cases interfere with the update.'
54 b'in some cases interfere with the update.'
51 b'See also :hg:`purge`.'
55 b'See also :hg:`purge`.'
52 ),
56 ),
53 ),
57 ),
54 (
58 (
55 b'',
59 b'',
56 b'purge-ignored',
60 b'purge-ignored',
57 True,
61 True,
58 _(
62 _(
59 b'Remove ignored files before update. Disable this for '
63 b'Remove ignored files before update. Disable this for '
60 b'instance to reuse previous compiler object files. '
64 b'instance to reuse previous compiler object files. '
61 b'See also :hg:`purge`.'
65 b'See also :hg:`purge`.'
62 ),
66 ),
63 ),
67 ),
64 (
68 (
65 b'',
69 b'',
66 b'rev',
70 b'rev',
67 b'',
71 b'',
68 _(b'revision to update to'),
72 _(b'revision to update to'),
69 ),
73 ),
70 (
74 (
71 b'',
75 b'',
72 b'source',
76 b'source',
73 b'',
77 b'',
74 _(b'repository to clone from'),
78 _(b'repository to clone from'),
75 ),
79 ),
80 (
81 b'',
82 b'dest',
83 b'',
84 _(b'repository to update to REV (possibly cloning)'),
85 ),
86 (
87 b'',
88 b'initial-clone-minimal',
89 False,
90 _(
91 b'Pull only the prescribed revision upon initial cloning. '
92 b'This has the side effect of ignoring clone-bundles, '
93 b'which if often slower on the client side and stressful '
94 b'to the server than applying available clone bundles.'
95 ),
96 ),
76 ],
97 ],
77 _(b'hg admin::chainsaw-update [OPTION] --rev REV --source SOURCE...'),
98 _(
99 b'hg admin::chainsaw-update [OPTION] --rev REV --source SOURCE --dest DEST'
100 ),
78 helpbasic=True,
101 helpbasic=True,
102 norepo=True,
79 )
103 )
80 def update(ui, repo, **opts):
104 def update(ui, **opts):
81 """pull and update to a given revision, no matter what, (EXPERIMENTAL)
105 """pull and update to a given revision, no matter what, (EXPERIMENTAL)
82
106
83 Context of application: *some* Continuous Integration (CI) systems,
107 Context of application: *some* Continuous Integration (CI) systems,
84 packaging or deployment tools.
108 packaging or deployment tools.
85
109
86 Wanted end result: clean working directory updated at the given revision.
110 Wanted end result: local repository at the given REPO_PATH, having the
111 latest changes to the given revision and with a clean working directory
112 updated at the given revision.
87
113
88 chainsaw-update pulls from one source, then updates the working directory
114 chainsaw-update pulls from one source, then updates the working directory
89 to the given revision, overcoming anything that would stand in the way.
115 to the given revision, overcoming anything that would stand in the way.
90
116
91 By default, it will:
117 By default, it will:
92
118
119 - clone if the local repo does not exist yet, **removing any directory
120 at the given path** that would not be a Mercurial repository.
121 The initial clone is full by default, so that clonebundles can be
122 applied. Use the --initial-clone-minimal flag to avoid this.
93 - break locks if needed, leading to possible corruption if there
123 - break locks if needed, leading to possible corruption if there
94 is a concurrent write access.
124 is a concurrent write access.
95 - perform recovery actions if needed
125 - perform recovery actions if needed
96 - revert any local modification.
126 - revert any local modification.
97 - purge unknown and ignored files.
127 - purge unknown and ignored files.
98 - go as far as to reclone if everything else failed (not implemented yet).
128 - go as far as to reclone if everything else failed (not implemented yet).
99
129
100 DO NOT use it for anything else than performing a series
130 DO NOT use it for anything else than performing a series
101 of unattended updates, with full exclusive repository access each time
131 of unattended updates, with full exclusive repository access each time
102 and without any other local work than running build scripts.
132 and without any other local work than running build scripts.
103 In case the local repository is a share (see :hg:`help share`), exclusive
133 In case the local repository is a share (see :hg:`help share`), exclusive
104 write access to the share source is also mandatory.
134 write access to the share source is also mandatory.
105
135
106 It is recommended to run these commands with the ``HGPLAIN`` environment
136 It is recommended to run these commands with the ``HGPLAIN`` environment
107 variable (see :hg:`scripting`).
137 variable (see :hg:`scripting`).
108
138
109 Motivation: in Continuous Integration and Delivery systems (CI/CD), the
139 Motivation: in Continuous Integration and Delivery systems (CI/CD), the
110 occasional remnant or bogus lock are common sources of waste of time (both
140 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-
141 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
142 measures, often done in urgency. Also, whilst it is neat to keep
113 repositories from one job to the next (especially with large
143 repositories from one job to the next (especially with large
114 repositories), an exceptional recloning is better than missing a release
144 repositories), an exceptional recloning is better than missing a release
115 deadline.
145 deadline.
116 """
146 """
117 rev = opts['rev']
147 rev = opts['rev']
118 source = opts['source']
148 source = opts['source']
149 repo_path = opts['dest']
119 if not rev:
150 if not rev:
120 raise error.InputError(_(b'specify a target revision with --rev'))
151 raise error.InputError(_(b'specify a target revision with --rev'))
121 if not source:
152 if not source:
122 raise error.InputError(_(b'specify a pull path with --source'))
153 raise error.InputError(_(b'specify a pull path with --source'))
154 if not repo_path:
155 raise error.InputError(_(b'specify a repo path with --dest'))
156 repo_path = urlutil.urllocalpath(repo_path)
157
158 try:
159 repo = localrepo.instance(ui, repo_path, create=False)
160 repo_created = False
161 ui.status(_(b'loaded repository at "%s"\n' % repo_path))
162 except error.RepoError:
163 try:
164 shutil.rmtree(repo_path)
165 except FileNotFoundError:
166 ui.status(_(b'no such directory: "%s"\n' % repo_path))
167 else:
168 ui.status(
169 _(
170 b'removed non-repository file or directory '
171 b'at "%s"' % repo_path
172 )
173 )
174
175 ui.status(_(b'creating repository at "%s"\n' % repo_path))
176 repo = localrepo.instance(ui, repo_path, create=True)
177 repo_created = True
178
123 if repo.svfs.tryunlink(b'lock'):
179 if repo.svfs.tryunlink(b'lock'):
124 ui.status(_(b'had to break store lock\n'))
180 ui.status(_(b'had to break store lock\n'))
125 if repo.vfs.tryunlink(b'wlock'):
181 if repo.vfs.tryunlink(b'wlock'):
126 ui.status(_(b'had to break working copy lock\n'))
182 ui.status(_(b'had to break working copy lock\n'))
127
183
128 ui.status(_(b'recovering after interrupted transaction, if any\n'))
184 ui.status(_(b'recovering after interrupted transaction, if any\n'))
129 repo.recover()
185 repo.recover()
130
186
131 ui.status(_(b'pulling from %s\n') % source)
187 ui.status(_(b'pulling from %s\n') % source)
188 if repo_created and not opts.get('initial_clone_minimal'):
189 pull_revs = []
190 else:
191 pull_revs = [rev]
132 overrides = {(b'ui', b'quiet'): True}
192 overrides = {(b'ui', b'quiet'): True}
133 with ui.configoverride(overrides, b'chainsaw-update'):
193 with repo.ui.configoverride(overrides, b'chainsaw-update'):
134 pull = cmdutil.findcmd(b'pull', commands.table)[1][0]
194 pull = cmdutil.findcmd(b'pull', commands.table)[1][0]
135 pull(ui, repo, source, rev=[rev], remote_hidden=False)
195 pull(repo.ui, repo, source, rev=pull_revs, remote_hidden=False)
136
196
137 purge = cmdutil.findcmd(b'purge', commands.table)[1][0]
197 purge = cmdutil.findcmd(b'purge', commands.table)[1][0]
138 purge(
198 purge(
139 ui,
199 ui,
140 repo,
200 repo,
141 dirs=True,
201 dirs=True,
142 all=opts.get('purge_ignored'),
202 all=opts.get('purge_ignored'),
143 files=opts.get('purge_unknown'),
203 files=opts.get('purge_unknown'),
144 confirm=False,
204 confirm=False,
145 )
205 )
146
206
147 ui.status(_(b'updating to revision \'%s\'\n') % rev)
207 ui.status(_(b'updating to revision \'%s\'\n') % rev)
148 update = cmdutil.findcmd(b'update', commands.table)[1][0]
208 update = cmdutil.findcmd(b'update', commands.table)[1][0]
149 update(ui, repo, rev=rev, clean=True)
209 update(ui, repo, rev=rev, clean=True)
150
210
151 ui.status(
211 ui.status(
152 _(
212 _(
153 b'chainsaw-update to revision \'%s\' '
213 b'chainsaw-update to revision \'%s\' '
154 b'for repository at \'%s\' done\n'
214 b'for repository at \'%s\' done\n'
155 )
215 )
156 % (rev, repo.root)
216 % (rev, repo.root)
157 )
217 )
@@ -1,169 +1,255 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 > root
15 $ echo 1 > root
16 $ hg add root
16 $ hg add root
17 $ hg ci -Am R_0
17 $ hg ci -Am R_0
18 $ hg branch A
18 $ hg branch A
19 marked working directory as branch A
19 marked working directory as branch A
20 (branches are permanent and global, did you want a bookmark?)
20 (branches are permanent and global, did you want a bookmark?)
21 $ echo 42 > bar
21 $ echo 42 > bar
22 $ hg add bar
22 $ hg add bar
23 $ hg ci -Am A_0
23 $ hg ci -Am A_0
24 $ echo 1337 > bar
24 $ echo 1337 > bar
25 $ hg ci -Am A_1
25 $ hg ci -Am A_1
26 $ hg update 'desc(R_0)'
26 $ hg update 'desc(R_0)'
27 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
27 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
28 $ echo 1 > foo
28 $ echo 1 > foo
29 $ hg add foo
29 $ hg add foo
30 $ hg ci -Am B_0
30 $ hg ci -Am B_0
31 $ hg log -G
31 $ hg log -G
32 @ changeset: 3:bfcb8e629987
32 @ changeset: 3:bfcb8e629987
33 | tag: tip
33 | tag: tip
34 | parent: 0:06f48e4098b8
34 | parent: 0:06f48e4098b8
35 | user: test
35 | user: test
36 | date: Thu Jan 01 00:00:00 1970 +0000
36 | date: Thu Jan 01 00:00:00 1970 +0000
37 | summary: B_0
37 | summary: B_0
38 |
38 |
39 | o changeset: 2:7fd8de258aa4
39 | o changeset: 2:7fd8de258aa4
40 | | branch: A
40 | | branch: A
41 | | user: test
41 | | user: test
42 | | date: Thu Jan 01 00:00:00 1970 +0000
42 | | date: Thu Jan 01 00:00:00 1970 +0000
43 | | summary: A_1
43 | | summary: A_1
44 | |
44 | |
45 | o changeset: 1:ae1692b8aadb
45 | o changeset: 1:ae1692b8aadb
46 |/ branch: A
46 |/ branch: A
47 | user: test
47 | user: test
48 | date: Thu Jan 01 00:00:00 1970 +0000
48 | date: Thu Jan 01 00:00:00 1970 +0000
49 | summary: A_0
49 | summary: A_0
50 |
50 |
51 o changeset: 0:06f48e4098b8
51 o changeset: 0:06f48e4098b8
52 user: test
52 user: test
53 date: Thu Jan 01 00:00:00 1970 +0000
53 date: Thu Jan 01 00:00:00 1970 +0000
54 summary: R_0
54 summary: R_0
55
55
56 $ cd ..
56 $ cd ..
57
57
58 Actual tests
58 Actual tests
59 ============
59 ============
60
60
61 Simple invocation
61 Initial cloning if needed
62 -----------------
62 -------------------------
63
63
64 $ hg init repo
64 $ hg admin::chainsaw-update --dest repo --rev default --source ./src
65 $ cd repo
65 no such directory: "repo"
66 $ hg admin::chainsaw-update --rev default --source ../src
66 creating repository at "repo"
67 recovering after interrupted transaction, if any
67 recovering after interrupted transaction, if any
68 no interrupted transaction available
68 no interrupted transaction available
69 pulling from ../src
69 pulling from ./src
70 updating to revision 'default'
70 updating to revision 'default'
71 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
71 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
72 chainsaw-update to revision 'default' for repository at '$TESTTMP/repo' done
72 chainsaw-update to revision 'default' for repository at '$TESTTMP/repo' done
73
73
74 $ cd repo
74 $ hg log -G
75 $ hg log -G
75 @ changeset: 1:bfcb8e629987
76 @ changeset: 3:bfcb8e629987
76 | tag: tip
77 | tag: tip
78 | parent: 0:06f48e4098b8
77 | user: test
79 | user: test
78 | date: Thu Jan 01 00:00:00 1970 +0000
80 | date: Thu Jan 01 00:00:00 1970 +0000
79 | summary: B_0
81 | summary: B_0
80 |
82 |
83 | o changeset: 2:7fd8de258aa4
84 | | branch: A
85 | | user: test
86 | | date: Thu Jan 01 00:00:00 1970 +0000
87 | | summary: A_1
88 | |
89 | o changeset: 1:ae1692b8aadb
90 |/ branch: A
91 | user: test
92 | date: Thu Jan 01 00:00:00 1970 +0000
93 | summary: A_0
94 |
81 o changeset: 0:06f48e4098b8
95 o changeset: 0:06f48e4098b8
82 user: test
96 user: test
83 date: Thu Jan 01 00:00:00 1970 +0000
97 date: Thu Jan 01 00:00:00 1970 +0000
84 summary: R_0
98 summary: R_0
85
99
86 $ hg status -A
100 $ hg status -A
87 C foo
101 C foo
88 C root
102 C root
89 $ cat foo
103 $ cat foo
90 1
104 1
91
105
92 Test lock breacking capabilities
106 Test lock breacking capabilities
93 --------------------------------
107 --------------------------------
94
108
95 Demonstrate lock-breaking capabilities with locks that regular Mercurial
109 Demonstrate lock-breaking capabilities with locks that regular Mercurial
96 operation would not break, because the hostnames registered in locks differ
110 operation would not break, because the hostnames registered in locks differ
97 from the current hostname (happens a lot with succesive containers):
111 from the current hostname (happens a lot with succesive containers):
98
112
99 $ ln -s invalid.host.test/effffffc:171814 .hg/store/lock
113 $ ln -s invalid.host.test/effffffc:171814 .hg/store/lock
100 $ ln -s invalid.host.test/effffffc:171814 .hg/wlock
114 $ ln -s invalid.host.test/effffffc:171814 .hg/wlock
101 $ hg debuglock
115 $ hg debuglock
102 lock: (.*?), process 171814, host invalid.host.test/effffffc \((\d+)s\) (re)
116 lock: (.*?), process 171814, host invalid.host.test/effffffc \((\d+)s\) (re)
103 wlock: (.*?), process 171814, host invalid.host.test/effffffc \((\d+)s\) (re)
117 wlock: (.*?), process 171814, host invalid.host.test/effffffc \((\d+)s\) (re)
104 [2]
118 [2]
105
119
106 $ hg admin::chainsaw-update --no-purge-ignored --rev default --source ../src
120 $ hg admin::chainsaw-update --no-purge-ignored --dest . --rev default --source ../src
121 loaded repository at "."
107 had to break store lock
122 had to break store lock
108 had to break working copy lock
123 had to break working copy lock
109 recovering after interrupted transaction, if any
124 recovering after interrupted transaction, if any
110 no interrupted transaction available
125 no interrupted transaction available
111 pulling from ../src
126 pulling from ../src
112 updating to revision 'default'
127 updating to revision 'default'
113 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
128 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
114 chainsaw-update to revision 'default' for repository at '$TESTTMP/repo' done
129 chainsaw-update to revision 'default' for repository at '$TESTTMP/repo' done
115
130
116 Test file purging capabilities
131 Test file purging capabilities
117 ------------------------------
132 ------------------------------
118
133
119 Let's also add local modifications (tracked and untracked) to demonstrate the
134 Let's also add local modifications (tracked and untracked) to demonstrate the
120 purging.
135 purging.
121
136
122 $ echo untracked > bar
137 $ echo untracked > bar
123 $ echo modified > foo
138 $ echo modified > foo
124 $ hg status -A
139 $ hg status -A
125 M foo
140 M foo
126 ? bar
141 ? bar
127 C root
142 C root
128
143
129 $ echo 2 > ../src/foo
144 $ echo 2 > ../src/foo
130 $ hg -R ../src commit -m2
145 $ hg -R ../src commit -mB_1
131 $ hg admin::chainsaw-update --rev default --source ../src -q
146 $ hg admin::chainsaw-update --dest . --rev default --source ../src -q
132 no interrupted transaction available
147 no interrupted transaction available
148 $ hg log -G
149 @ changeset: 4:973ab81c95fb
150 | tag: tip
151 | user: test
152 | date: Thu Jan 01 00:00:00 1970 +0000
153 | summary: B_1
154 |
155 o changeset: 3:bfcb8e629987
156 | parent: 0:06f48e4098b8
157 | user: test
158 | date: Thu Jan 01 00:00:00 1970 +0000
159 | summary: B_0
160 |
161 | o changeset: 2:7fd8de258aa4
162 | | branch: A
163 | | user: test
164 | | date: Thu Jan 01 00:00:00 1970 +0000
165 | | summary: A_1
166 | |
167 | o changeset: 1:ae1692b8aadb
168 |/ branch: A
169 | user: test
170 | date: Thu Jan 01 00:00:00 1970 +0000
171 | summary: A_0
172 |
173 o changeset: 0:06f48e4098b8
174 user: test
175 date: Thu Jan 01 00:00:00 1970 +0000
176 summary: R_0
177
133 $ hg status -A
178 $ hg status -A
134 C foo
179 C foo
135 C root
180 C root
136 $ cat foo
181 $ cat foo
137 2
182 2
138
183
139 Now behaviour with respect to ignored files: they are not purged if
184 Now behaviour with respect to ignored files: they are not purged if
140 the --no-purge-ignored flag is passed, but they are purged by default
185 the --no-purge-ignored flag is passed, but they are purged by default
141
186
142 $ echo bar > .hgignore
187 $ echo bar > .hgignore
143 $ hg ci -Aqm hgignore
188 $ hg ci -Aqm hgignore
144 $ echo ignored > bar
189 $ echo ignored > bar
145 $ hg status --all
190 $ hg status --all
146 I bar
191 I bar
147 C .hgignore
192 C .hgignore
148 C foo
193 C foo
149 C root
194 C root
150
195
151 $ hg admin::chainsaw-update --no-purge-ignored --rev default --source ../src -q
196 $ hg admin::chainsaw-update --no-purge-ignored --dest . --rev default --source ../src -q
152 no interrupted transaction available
197 no interrupted transaction available
153 $ hg status --all
198 $ hg status --all
154 I bar
199 I bar
155 C .hgignore
200 C .hgignore
156 C foo
201 C foo
157 C root
202 C root
158 $ cat bar
203 $ cat bar
159 ignored
204 ignored
160
205
161 $ hg admin::chainsaw-update --rev default --source ../src -q
206 $ hg admin::chainsaw-update --dest . --rev default --source ../src -q
162 no interrupted transaction available
207 no interrupted transaction available
163 $ hg status --all
208 $ hg status --all
164 C .hgignore
209 C .hgignore
165 C foo
210 C foo
166 C root
211 C root
167 $ test -f bar
212 $ test -f bar
168 [1]
213 [1]
169
214
215 test --minimal-initial-cloning variant
216 --------------------------------------
217
218 With `--minimal-initial-cloning`, there is no "requesting all changes"
219 message. Hence clone bundles would be bypassed (TODO test both cases
220 # with an actual clone-bundle)
221
222 $ cd ..
223 $ hg admin::chainsaw-update --dest repo2 --rev default --source src --initial-clone-minimal
224 no such directory: "repo2"
225 creating repository at "repo2"
226 recovering after interrupted transaction, if any
227 no interrupted transaction available
228 pulling from src
229 updating to revision 'default'
230 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
231 chainsaw-update to revision 'default' for repository at '$TESTTMP/repo2' done
232
233 $ cd repo2
234 $ hg log -G
235 @ changeset: 2:973ab81c95fb
236 | tag: tip
237 | user: test
238 | date: Thu Jan 01 00:00:00 1970 +0000
239 | summary: B_1
240 |
241 o changeset: 1:bfcb8e629987
242 | user: test
243 | date: Thu Jan 01 00:00:00 1970 +0000
244 | summary: B_0
245 |
246 o changeset: 0:06f48e4098b8
247 user: test
248 date: Thu Jan 01 00:00:00 1970 +0000
249 summary: R_0
250
251 $ hg status -A
252 C foo
253 C root
254 $ cat foo
255 2
General Comments 0
You need to be logged in to leave comments. Login now