##// END OF EJS Templates
errors: use InputError in uncommit extension...
Martin von Zweigbergk -
r47192:0e2becd1 default
parent child Browse files
Show More
@@ -1,318 +1,318 b''
1 1 # uncommit - undo the actions of a commit
2 2 #
3 3 # Copyright 2011 Peter Arrenbrecht <peter.arrenbrecht@gmail.com>
4 4 # Logilab SA <contact@logilab.fr>
5 5 # Pierre-Yves David <pierre-yves.david@ens-lyon.org>
6 6 # Patrick Mezard <patrick@mezard.eu>
7 7 # Copyright 2016 Facebook, Inc.
8 8 #
9 9 # This software may be used and distributed according to the terms of the
10 10 # GNU General Public License version 2 or any later version.
11 11
12 12 """uncommit part or all of a local changeset (EXPERIMENTAL)
13 13
14 14 This command undoes the effect of a local commit, returning the affected
15 15 files to their uncommitted state. This means that files modified, added or
16 16 removed in the changeset will be left unchanged, and so will remain modified,
17 17 added and removed in the working directory.
18 18 """
19 19
20 20 from __future__ import absolute_import
21 21
22 22 from mercurial.i18n import _
23 23 from mercurial.node import nullid
24 24
25 25 from mercurial import (
26 26 cmdutil,
27 27 commands,
28 28 context,
29 29 copies as copiesmod,
30 30 error,
31 31 obsutil,
32 32 pathutil,
33 33 pycompat,
34 34 registrar,
35 35 rewriteutil,
36 36 scmutil,
37 37 )
38 38
39 39 cmdtable = {}
40 40 command = registrar.command(cmdtable)
41 41
42 42 configtable = {}
43 43 configitem = registrar.configitem(configtable)
44 44
45 45 configitem(
46 46 b'experimental',
47 47 b'uncommitondirtywdir',
48 48 default=False,
49 49 )
50 50 configitem(
51 51 b'experimental',
52 52 b'uncommit.keep',
53 53 default=False,
54 54 )
55 55
56 56 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
57 57 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
58 58 # be specifying the version(s) of Mercurial they are tested with, or
59 59 # leave the attribute unspecified.
60 60 testedwith = b'ships-with-hg-core'
61 61
62 62
63 63 def _commitfiltered(
64 64 repo, ctx, match, keepcommit, message=None, user=None, date=None
65 65 ):
66 66 """Recommit ctx with changed files not in match. Return the new
67 67 node identifier, or None if nothing changed.
68 68 """
69 69 base = ctx.p1()
70 70 # ctx
71 71 initialfiles = set(ctx.files())
72 72 exclude = {f for f in initialfiles if match(f)}
73 73
74 74 # No files matched commit, so nothing excluded
75 75 if not exclude:
76 76 return None
77 77
78 78 # return the p1 so that we don't create an obsmarker later
79 79 if not keepcommit:
80 80 return ctx.p1().node()
81 81
82 82 files = initialfiles - exclude
83 83 # Filter copies
84 84 copied = copiesmod.pathcopies(base, ctx)
85 85 copied = {
86 86 dst: src for dst, src in pycompat.iteritems(copied) if dst in files
87 87 }
88 88
89 89 def filectxfn(repo, memctx, path, contentctx=ctx, redirect=()):
90 90 if path not in contentctx:
91 91 return None
92 92 fctx = contentctx[path]
93 93 mctx = context.memfilectx(
94 94 repo,
95 95 memctx,
96 96 fctx.path(),
97 97 fctx.data(),
98 98 fctx.islink(),
99 99 fctx.isexec(),
100 100 copysource=copied.get(path),
101 101 )
102 102 return mctx
103 103
104 104 if not files:
105 105 repo.ui.status(_(b"note: keeping empty commit\n"))
106 106
107 107 if message is None:
108 108 message = ctx.description()
109 109 if not user:
110 110 user = ctx.user()
111 111 if not date:
112 112 date = ctx.date()
113 113
114 114 new = context.memctx(
115 115 repo,
116 116 parents=[base.node(), nullid],
117 117 text=message,
118 118 files=files,
119 119 filectxfn=filectxfn,
120 120 user=user,
121 121 date=date,
122 122 extra=ctx.extra(),
123 123 )
124 124 return repo.commitctx(new)
125 125
126 126
127 127 @command(
128 128 b'uncommit',
129 129 [
130 130 (b'', b'keep', None, _(b'allow an empty commit after uncommitting')),
131 131 (
132 132 b'',
133 133 b'allow-dirty-working-copy',
134 134 False,
135 135 _(b'allow uncommit with outstanding changes'),
136 136 ),
137 137 (b'n', b'note', b'', _(b'store a note on uncommit'), _(b'TEXT')),
138 138 ]
139 139 + commands.walkopts
140 140 + commands.commitopts
141 141 + commands.commitopts2
142 142 + commands.commitopts3,
143 143 _(b'[OPTION]... [FILE]...'),
144 144 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
145 145 )
146 146 def uncommit(ui, repo, *pats, **opts):
147 147 """uncommit part or all of a local changeset
148 148
149 149 This command undoes the effect of a local commit, returning the affected
150 150 files to their uncommitted state. This means that files modified or
151 151 deleted in the changeset will be left unchanged, and so will remain
152 152 modified in the working directory.
153 153
154 154 If no files are specified, the commit will be pruned, unless --keep is
155 155 given.
156 156 """
157 157 opts = pycompat.byteskwargs(opts)
158 158
159 159 cmdutil.checknotesize(ui, opts)
160 160 cmdutil.resolvecommitoptions(ui, opts)
161 161
162 162 with repo.wlock(), repo.lock():
163 163
164 164 st = repo.status()
165 165 m, a, r, d = st.modified, st.added, st.removed, st.deleted
166 166 isdirtypath = any(set(m + a + r + d) & set(pats))
167 167 allowdirtywcopy = opts[
168 168 b'allow_dirty_working_copy'
169 169 ] or repo.ui.configbool(b'experimental', b'uncommitondirtywdir')
170 170 if not allowdirtywcopy and (not pats or isdirtypath):
171 171 cmdutil.bailifchanged(
172 172 repo,
173 173 hint=_(b'requires --allow-dirty-working-copy to uncommit'),
174 174 )
175 175 old = repo[b'.']
176 176 rewriteutil.precheck(repo, [old.rev()], b'uncommit')
177 177 if len(old.parents()) > 1:
178 raise error.Abort(_(b"cannot uncommit merge changeset"))
178 raise error.InputError(_(b"cannot uncommit merge changeset"))
179 179
180 180 match = scmutil.match(old, pats, opts)
181 181
182 182 # Check all explicitly given files; abort if there's a problem.
183 183 if match.files():
184 184 s = old.status(old.p1(), match, listclean=True)
185 185 eligible = set(s.added) | set(s.modified) | set(s.removed)
186 186
187 187 badfiles = set(match.files()) - eligible
188 188
189 189 # Naming a parent directory of an eligible file is OK, even
190 190 # if not everything tracked in that directory can be
191 191 # uncommitted.
192 192 if badfiles:
193 193 badfiles -= {f for f in pathutil.dirs(eligible)}
194 194
195 195 for f in sorted(badfiles):
196 196 if f in s.clean:
197 197 hint = _(
198 198 b"file was not changed in working directory parent"
199 199 )
200 200 elif repo.wvfs.exists(f):
201 201 hint = _(b"file was untracked in working directory parent")
202 202 else:
203 203 hint = _(b"file does not exist")
204 204
205 raise error.Abort(
205 raise error.InputError(
206 206 _(b'cannot uncommit "%s"') % scmutil.getuipathfn(repo)(f),
207 207 hint=hint,
208 208 )
209 209
210 210 with repo.transaction(b'uncommit'):
211 211 if not (opts[b'message'] or opts[b'logfile']):
212 212 opts[b'message'] = old.description()
213 213 message = cmdutil.logmessage(ui, opts)
214 214
215 215 keepcommit = pats
216 216 if not keepcommit:
217 217 if opts.get(b'keep') is not None:
218 218 keepcommit = opts.get(b'keep')
219 219 else:
220 220 keepcommit = ui.configbool(
221 221 b'experimental', b'uncommit.keep'
222 222 )
223 223 newid = _commitfiltered(
224 224 repo,
225 225 old,
226 226 match,
227 227 keepcommit,
228 228 message=message,
229 229 user=opts.get(b'user'),
230 230 date=opts.get(b'date'),
231 231 )
232 232 if newid is None:
233 233 ui.status(_(b"nothing to uncommit\n"))
234 234 return 1
235 235
236 236 mapping = {}
237 237 if newid != old.p1().node():
238 238 # Move local changes on filtered changeset
239 239 mapping[old.node()] = (newid,)
240 240 else:
241 241 # Fully removed the old commit
242 242 mapping[old.node()] = ()
243 243
244 244 with repo.dirstate.parentchange():
245 245 scmutil.movedirstate(repo, repo[newid], match)
246 246
247 247 scmutil.cleanupnodes(repo, mapping, b'uncommit', fixphase=True)
248 248
249 249
250 250 def predecessormarkers(ctx):
251 251 """yields the obsolete markers marking the given changeset as a successor"""
252 252 for data in ctx.repo().obsstore.predecessors.get(ctx.node(), ()):
253 253 yield obsutil.marker(ctx.repo(), data)
254 254
255 255
256 256 @command(
257 257 b'unamend',
258 258 [],
259 259 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
260 260 helpbasic=True,
261 261 )
262 262 def unamend(ui, repo, **opts):
263 263 """undo the most recent amend operation on a current changeset
264 264
265 265 This command will roll back to the previous version of a changeset,
266 266 leaving working directory in state in which it was before running
267 267 `hg amend` (e.g. files modified as part of an amend will be
268 268 marked as modified `hg status`)
269 269 """
270 270
271 271 unfi = repo.unfiltered()
272 272 with repo.wlock(), repo.lock(), repo.transaction(b'unamend'):
273 273
274 274 # identify the commit from which to unamend
275 275 curctx = repo[b'.']
276 276
277 277 rewriteutil.precheck(repo, [curctx.rev()], b'unamend')
278 278
279 279 # identify the commit to which to unamend
280 280 markers = list(predecessormarkers(curctx))
281 281 if len(markers) != 1:
282 282 e = _(b"changeset must have one predecessor, found %i predecessors")
283 raise error.Abort(e % len(markers))
283 raise error.InputError(e % len(markers))
284 284
285 285 prednode = markers[0].prednode()
286 286 predctx = unfi[prednode]
287 287
288 288 # add an extra so that we get a new hash
289 289 # note: allowing unamend to undo an unamend is an intentional feature
290 290 extras = predctx.extra()
291 291 extras[b'unamend_source'] = curctx.hex()
292 292
293 293 def filectxfn(repo, ctx_, path):
294 294 try:
295 295 return predctx.filectx(path)
296 296 except KeyError:
297 297 return None
298 298
299 299 # Make a new commit same as predctx
300 300 newctx = context.memctx(
301 301 repo,
302 302 parents=(predctx.p1(), predctx.p2()),
303 303 text=predctx.description(),
304 304 files=predctx.files(),
305 305 filectxfn=filectxfn,
306 306 user=predctx.user(),
307 307 date=predctx.date(),
308 308 extra=extras,
309 309 )
310 310 newprednode = repo.commitctx(newctx)
311 311 newpredctx = repo[newprednode]
312 312 dirstate = repo.dirstate
313 313
314 314 with dirstate.parentchange():
315 315 scmutil.movedirstate(repo, newpredctx)
316 316
317 317 mapping = {curctx.node(): (newprednode,)}
318 318 scmutil.cleanupnodes(repo, mapping, b'unamend', fixphase=True)
@@ -1,421 +1,421 b''
1 1 Test for command `hg unamend` which lives in uncommit extension
2 2 ===============================================================
3 3
4 4 $ cat >> $HGRCPATH << EOF
5 5 > [alias]
6 6 > glog = log -G -T '{rev}:{node|short} {desc}'
7 7 > [experimental]
8 8 > evolution = createmarkers, allowunstable
9 9 > [extensions]
10 10 > rebase =
11 11 > amend =
12 12 > uncommit =
13 13 > EOF
14 14
15 15 Repo Setup
16 16
17 17 $ hg init repo
18 18 $ cd repo
19 19 $ for ch in a b c d e f g h; do touch $ch; echo "foo" >> $ch; hg ci -Aqm "Added "$ch; done
20 20
21 21 $ hg glog
22 22 @ 7:ec2426147f0e Added h
23 23 |
24 24 o 6:87d6d6676308 Added g
25 25 |
26 26 o 5:825660c69f0c Added f
27 27 |
28 28 o 4:aa98ab95a928 Added e
29 29 |
30 30 o 3:62615734edd5 Added d
31 31 |
32 32 o 2:28ad74487de9 Added c
33 33 |
34 34 o 1:29becc82797a Added b
35 35 |
36 36 o 0:18d04c59bb5d Added a
37 37
38 38 Trying to unamend when there was no amend done
39 39
40 40 $ hg unamend
41 41 abort: changeset must have one predecessor, found 0 predecessors
42 [255]
42 [10]
43 43
44 44 Unamend on clean wdir and tip
45 45
46 46 $ echo "bar" >> h
47 47 $ hg amend
48 48
49 49 $ hg exp
50 50 # HG changeset patch
51 51 # User test
52 52 # Date 0 0
53 53 # Thu Jan 01 00:00:00 1970 +0000
54 54 # Node ID c9fa1a715c1b7661c0fafb362a9f30bd75878d7d
55 55 # Parent 87d6d66763085b629e6d7ed56778c79827273022
56 56 Added h
57 57
58 58 diff -r 87d6d6676308 -r c9fa1a715c1b h
59 59 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
60 60 +++ b/h Thu Jan 01 00:00:00 1970 +0000
61 61 @@ -0,0 +1,2 @@
62 62 +foo
63 63 +bar
64 64
65 65 $ hg glog --hidden
66 66 @ 8:c9fa1a715c1b Added h
67 67 |
68 68 | x 7:ec2426147f0e Added h
69 69 |/
70 70 o 6:87d6d6676308 Added g
71 71 |
72 72 o 5:825660c69f0c Added f
73 73 |
74 74 o 4:aa98ab95a928 Added e
75 75 |
76 76 o 3:62615734edd5 Added d
77 77 |
78 78 o 2:28ad74487de9 Added c
79 79 |
80 80 o 1:29becc82797a Added b
81 81 |
82 82 o 0:18d04c59bb5d Added a
83 83
84 84 $ hg unamend
85 85 $ hg glog --hidden
86 86 @ 9:46d02d47eec6 Added h
87 87 |
88 88 | x 8:c9fa1a715c1b Added h
89 89 |/
90 90 | x 7:ec2426147f0e Added h
91 91 |/
92 92 o 6:87d6d6676308 Added g
93 93 |
94 94 o 5:825660c69f0c Added f
95 95 |
96 96 o 4:aa98ab95a928 Added e
97 97 |
98 98 o 3:62615734edd5 Added d
99 99 |
100 100 o 2:28ad74487de9 Added c
101 101 |
102 102 o 1:29becc82797a Added b
103 103 |
104 104 o 0:18d04c59bb5d Added a
105 105
106 106 $ hg diff
107 107 diff -r 46d02d47eec6 h
108 108 --- a/h Thu Jan 01 00:00:00 1970 +0000
109 109 +++ b/h Thu Jan 01 00:00:00 1970 +0000
110 110 @@ -1,1 +1,2 @@
111 111 foo
112 112 +bar
113 113
114 114 $ hg exp
115 115 # HG changeset patch
116 116 # User test
117 117 # Date 0 0
118 118 # Thu Jan 01 00:00:00 1970 +0000
119 119 # Node ID 46d02d47eec6ca096b8dcab3f8f5579c40c3dd9a
120 120 # Parent 87d6d66763085b629e6d7ed56778c79827273022
121 121 Added h
122 122
123 123 diff -r 87d6d6676308 -r 46d02d47eec6 h
124 124 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
125 125 +++ b/h Thu Jan 01 00:00:00 1970 +0000
126 126 @@ -0,0 +1,1 @@
127 127 +foo
128 128
129 129 $ hg status
130 130 M h
131 131
132 132 $ hg log -r . -T '{extras % "{extra}\n"}' --config alias.log=log
133 133 branch=default
134 134 unamend_source=c9fa1a715c1b7661c0fafb362a9f30bd75878d7d
135 135
136 136 Using unamend to undo an unamed (intentional)
137 137
138 138 $ hg unamend
139 139 $ hg exp
140 140 # HG changeset patch
141 141 # User test
142 142 # Date 0 0
143 143 # Thu Jan 01 00:00:00 1970 +0000
144 144 # Node ID 850ddfc1bc662997ec6094ada958f01f0cc8070a
145 145 # Parent 87d6d66763085b629e6d7ed56778c79827273022
146 146 Added h
147 147
148 148 diff -r 87d6d6676308 -r 850ddfc1bc66 h
149 149 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
150 150 +++ b/h Thu Jan 01 00:00:00 1970 +0000
151 151 @@ -0,0 +1,2 @@
152 152 +foo
153 153 +bar
154 154 $ hg diff
155 155
156 156 Unamend on a dirty working directory
157 157
158 158 $ echo "bar" >> a
159 159 $ hg amend
160 160 $ echo "foobar" >> a
161 161 $ echo "bar" >> b
162 162 $ hg status
163 163 M a
164 164 M b
165 165
166 166 $ hg unamend
167 167
168 168 $ hg status
169 169 M a
170 170 M b
171 171
172 172 $ hg diff
173 173 diff -r ec338db45d51 a
174 174 --- a/a Thu Jan 01 00:00:00 1970 +0000
175 175 +++ b/a Thu Jan 01 00:00:00 1970 +0000
176 176 @@ -1,1 +1,3 @@
177 177 foo
178 178 +bar
179 179 +foobar
180 180 diff -r ec338db45d51 b
181 181 --- a/b Thu Jan 01 00:00:00 1970 +0000
182 182 +++ b/b Thu Jan 01 00:00:00 1970 +0000
183 183 @@ -1,1 +1,2 @@
184 184 foo
185 185 +bar
186 186
187 187 Unamending an added file
188 188
189 189 $ hg ci -m "Added things to a and b"
190 190 $ echo foo > bar
191 191 $ hg add bar
192 192 $ hg amend
193 193
194 194 $ hg unamend
195 195 $ hg status
196 196 A bar
197 197
198 198 $ hg revert --all
199 199 forgetting bar
200 200
201 201 Unamending a removed file
202 202
203 203 $ hg remove a
204 204 $ hg amend
205 205
206 206 $ hg unamend
207 207 $ hg status
208 208 R a
209 209 ? bar
210 210
211 211 $ hg revert --all
212 212 undeleting a
213 213
214 214 Unamending an added file with dirty wdir status
215 215
216 216 $ hg add bar
217 217 $ hg amend
218 218 $ echo bar >> bar
219 219 $ hg status
220 220 M bar
221 221
222 222 $ hg unamend
223 223 $ hg status
224 224 A bar
225 225 $ hg diff
226 226 diff -r 7f79409af972 bar
227 227 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
228 228 +++ b/bar Thu Jan 01 00:00:00 1970 +0000
229 229 @@ -0,0 +1,2 @@
230 230 +foo
231 231 +bar
232 232
233 233 $ hg revert --all
234 234 forgetting bar
235 235 $ rm bar
236 236
237 237 Unamending in middle of a stack
238 238
239 239 $ hg glog
240 240 @ 19:7f79409af972 Added things to a and b
241 241 |
242 242 o 12:ec338db45d51 Added h
243 243 |
244 244 o 6:87d6d6676308 Added g
245 245 |
246 246 o 5:825660c69f0c Added f
247 247 |
248 248 o 4:aa98ab95a928 Added e
249 249 |
250 250 o 3:62615734edd5 Added d
251 251 |
252 252 o 2:28ad74487de9 Added c
253 253 |
254 254 o 1:29becc82797a Added b
255 255 |
256 256 o 0:18d04c59bb5d Added a
257 257
258 258 $ hg up 5
259 259 2 files updated, 0 files merged, 2 files removed, 0 files unresolved
260 260 $ echo bar >> f
261 261 $ hg amend
262 262 3 new orphan changesets
263 263 $ hg rebase -s 6 -d . -q
264 264
265 265 $ hg glog
266 266 o 23:03ddd6fc5af1 Added things to a and b
267 267 |
268 268 o 22:3e7b64ee157b Added h
269 269 |
270 270 o 21:49635b68477e Added g
271 271 |
272 272 @ 20:93f0e8ffab32 Added f
273 273 |
274 274 o 4:aa98ab95a928 Added e
275 275 |
276 276 o 3:62615734edd5 Added d
277 277 |
278 278 o 2:28ad74487de9 Added c
279 279 |
280 280 o 1:29becc82797a Added b
281 281 |
282 282 o 0:18d04c59bb5d Added a
283 283
284 284
285 285 $ hg --config experimental.evolution=createmarkers unamend
286 286 abort: cannot unamend changeset with children
287 287 [10]
288 288
289 289 $ hg unamend
290 290 3 new orphan changesets
291 291
292 292 Trying to unamend a public changeset
293 293
294 294 $ hg up -C 23
295 295 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
296 296 $ hg phase -r . -p
297 297 1 new phase-divergent changesets
298 298 $ hg unamend
299 299 abort: cannot unamend public changesets
300 300 (see 'hg help phases' for details)
301 301 [10]
302 302
303 303 Testing whether unamend retains copies or not
304 304
305 305 $ hg status
306 306
307 307 $ hg mv a foo
308 308
309 309 $ hg ci -m "Moved a to foo"
310 310 $ hg exp --git
311 311 # HG changeset patch
312 312 # User test
313 313 # Date 0 0
314 314 # Thu Jan 01 00:00:00 1970 +0000
315 315 # Node ID cfef290346fbee5126313d7e1aab51d877679b09
316 316 # Parent 03ddd6fc5af19e028c44a2fd6d790dd22712f231
317 317 Moved a to foo
318 318
319 319 diff --git a/a b/foo
320 320 rename from a
321 321 rename to foo
322 322
323 323 $ hg mv b foobar
324 324 $ hg diff --git
325 325 diff --git a/b b/foobar
326 326 rename from b
327 327 rename to foobar
328 328 $ hg amend
329 329
330 330 $ hg exp --git
331 331 # HG changeset patch
332 332 # User test
333 333 # Date 0 0
334 334 # Thu Jan 01 00:00:00 1970 +0000
335 335 # Node ID eca050985275bb271ce3092b54e56ea5c85d29a3
336 336 # Parent 03ddd6fc5af19e028c44a2fd6d790dd22712f231
337 337 Moved a to foo
338 338
339 339 diff --git a/a b/foo
340 340 rename from a
341 341 rename to foo
342 342 diff --git a/b b/foobar
343 343 rename from b
344 344 rename to foobar
345 345
346 346 $ hg mv c wat
347 347 $ hg unamend
348 348
349 349 $ hg verify -v
350 350 repository uses revlog format 1
351 351 checking changesets
352 352 checking manifests
353 353 crosschecking files in changesets and manifests
354 354 checking files
355 355 checked 28 changesets with 16 changes to 11 files
356 356
357 357 Retained copies in new prdecessor commit
358 358
359 359 $ hg exp --git
360 360 # HG changeset patch
361 361 # User test
362 362 # Date 0 0
363 363 # Thu Jan 01 00:00:00 1970 +0000
364 364 # Node ID 552e3af4f01f620f88ca27be1f898316235b736a
365 365 # Parent 03ddd6fc5af19e028c44a2fd6d790dd22712f231
366 366 Moved a to foo
367 367
368 368 diff --git a/a b/foo
369 369 rename from a
370 370 rename to foo
371 371
372 372 Retained copies in working directoy
373 373
374 374 $ hg diff --git
375 375 diff --git a/b b/foobar
376 376 rename from b
377 377 rename to foobar
378 378 diff --git a/c b/wat
379 379 rename from c
380 380 rename to wat
381 381 $ hg revert -qa
382 382 $ rm foobar wat
383 383
384 384 Rename a->b, then amend b->c. After unamend, should look like b->c.
385 385
386 386 $ hg co -q 0
387 387 $ hg mv a b
388 388 $ hg ci -qm 'move to a b'
389 389 $ hg mv b c
390 390 $ hg amend
391 391 $ hg unamend
392 392 $ hg st --copies --change .
393 393 A b
394 394 a
395 395 R a
396 396 $ hg st --copies
397 397 A c
398 398 b
399 399 R b
400 400 $ hg revert -qa
401 401 $ rm c
402 402
403 403 Rename a->b, then amend b->c, and working copy change c->d. After unamend, should look like b->d
404 404
405 405 $ hg co -q 0
406 406 $ hg mv a b
407 407 $ hg ci -qm 'move to a b'
408 408 warning: commit already existed in the repository!
409 409 $ hg mv b c
410 410 $ hg amend
411 411 warning: commit already existed in the repository!
412 412 $ hg mv c d
413 413 $ hg unamend
414 414 $ hg st --copies --change .
415 415 A b
416 416 a
417 417 R a
418 418 $ hg st --copies
419 419 A d
420 420 b
421 421 R b
@@ -1,596 +1,596 b''
1 1 Test uncommit - set up the config
2 2
3 3 $ cat >> $HGRCPATH <<EOF
4 4 > [experimental]
5 5 > evolution.createmarkers=True
6 6 > evolution.allowunstable=True
7 7 > [extensions]
8 8 > uncommit =
9 9 > drawdag=$TESTDIR/drawdag.py
10 10 > EOF
11 11
12 12 Build up a repo
13 13
14 14 $ hg init repo
15 15 $ cd repo
16 16 $ hg bookmark foo
17 17
18 18 Help for uncommit
19 19
20 20 $ hg help uncommit
21 21 hg uncommit [OPTION]... [FILE]...
22 22
23 23 uncommit part or all of a local changeset
24 24
25 25 This command undoes the effect of a local commit, returning the affected
26 26 files to their uncommitted state. This means that files modified or
27 27 deleted in the changeset will be left unchanged, and so will remain
28 28 modified in the working directory.
29 29
30 30 If no files are specified, the commit will be pruned, unless --keep is
31 31 given.
32 32
33 33 (use 'hg help -e uncommit' to show help for the uncommit extension)
34 34
35 35 options ([+] can be repeated):
36 36
37 37 --keep allow an empty commit after uncommitting
38 38 --allow-dirty-working-copy allow uncommit with outstanding changes
39 39 -n --note TEXT store a note on uncommit
40 40 -I --include PATTERN [+] include names matching the given patterns
41 41 -X --exclude PATTERN [+] exclude names matching the given patterns
42 42 -m --message TEXT use text as commit message
43 43 -l --logfile FILE read commit message from file
44 44 -d --date DATE record the specified date as commit date
45 45 -u --user USER record the specified user as committer
46 46 -D --currentdate record the current date as commit date
47 47 -U --currentuser record the current user as committer
48 48
49 49 (some details hidden, use --verbose to show complete help)
50 50
51 51 Uncommit with no commits should fail
52 52
53 53 $ hg uncommit
54 54 abort: cannot uncommit null changeset
55 55 (no changeset checked out)
56 56 [10]
57 57
58 58 Create some commits
59 59
60 60 $ touch files
61 61 $ hg add files
62 62 $ for i in a ab abc abcd abcde; do echo $i > files; echo $i > file-$i; hg add file-$i; hg commit -m "added file-$i"; done
63 63 $ ls -A
64 64 .hg
65 65 file-a
66 66 file-ab
67 67 file-abc
68 68 file-abcd
69 69 file-abcde
70 70 files
71 71
72 72 $ hg log -G -T '{rev}:{node} {desc}' --hidden
73 73 @ 4:6c4fd43ed714e7fcd8adbaa7b16c953c2e985b60 added file-abcde
74 74 |
75 75 o 3:6db330d65db434145c0b59d291853e9a84719b24 added file-abcd
76 76 |
77 77 o 2:abf2df566fc193b3ac34d946e63c1583e4d4732b added file-abc
78 78 |
79 79 o 1:69a232e754b08d568c4899475faf2eb44b857802 added file-ab
80 80 |
81 81 o 0:3004d2d9b50883c1538fc754a3aeb55f1b4084f6 added file-a
82 82
83 83 Simple uncommit off the top, also moves bookmark
84 84
85 85 $ hg bookmark
86 86 * foo 4:6c4fd43ed714
87 87 $ hg uncommit
88 88 $ hg status
89 89 M files
90 90 A file-abcde
91 91 $ hg bookmark
92 92 * foo 3:6db330d65db4
93 93
94 94 $ hg log -G -T '{rev}:{node} {desc}' --hidden
95 95 x 4:6c4fd43ed714e7fcd8adbaa7b16c953c2e985b60 added file-abcde
96 96 |
97 97 @ 3:6db330d65db434145c0b59d291853e9a84719b24 added file-abcd
98 98 |
99 99 o 2:abf2df566fc193b3ac34d946e63c1583e4d4732b added file-abc
100 100 |
101 101 o 1:69a232e754b08d568c4899475faf2eb44b857802 added file-ab
102 102 |
103 103 o 0:3004d2d9b50883c1538fc754a3aeb55f1b4084f6 added file-a
104 104
105 105
106 106 Recommit
107 107
108 108 $ hg commit -m 'new change abcde'
109 109 $ hg status
110 110 $ hg heads -T '{rev}:{node} {desc}'
111 111 5:0c07a3ccda771b25f1cb1edbd02e683723344ef1 new change abcde (no-eol)
112 112
113 113 Uncommit of non-existent and unchanged files aborts
114 114 $ hg uncommit nothinghere
115 115 abort: cannot uncommit "nothinghere"
116 116 (file does not exist)
117 [255]
117 [10]
118 118 $ hg status
119 119 $ hg uncommit file-abc
120 120 abort: cannot uncommit "file-abc"
121 121 (file was not changed in working directory parent)
122 [255]
122 [10]
123 123 $ hg status
124 124
125 125 Try partial uncommit, also moves bookmark
126 126
127 127 $ hg bookmark
128 128 * foo 5:0c07a3ccda77
129 129 $ hg uncommit files
130 130 $ hg status
131 131 M files
132 132 $ hg bookmark
133 133 * foo 6:3727deee06f7
134 134 $ hg heads -T '{rev}:{node} {desc}'
135 135 6:3727deee06f72f5ffa8db792ee299cf39e3e190b new change abcde (no-eol)
136 136 $ hg log -r . -p -T '{rev}:{node} {desc}'
137 137 6:3727deee06f72f5ffa8db792ee299cf39e3e190b new change abcdediff -r 6db330d65db4 -r 3727deee06f7 file-abcde
138 138 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
139 139 +++ b/file-abcde Thu Jan 01 00:00:00 1970 +0000
140 140 @@ -0,0 +1,1 @@
141 141 +abcde
142 142
143 143 $ hg log -G -T '{rev}:{node} {desc}' --hidden
144 144 @ 6:3727deee06f72f5ffa8db792ee299cf39e3e190b new change abcde
145 145 |
146 146 | x 5:0c07a3ccda771b25f1cb1edbd02e683723344ef1 new change abcde
147 147 |/
148 148 | x 4:6c4fd43ed714e7fcd8adbaa7b16c953c2e985b60 added file-abcde
149 149 |/
150 150 o 3:6db330d65db434145c0b59d291853e9a84719b24 added file-abcd
151 151 |
152 152 o 2:abf2df566fc193b3ac34d946e63c1583e4d4732b added file-abc
153 153 |
154 154 o 1:69a232e754b08d568c4899475faf2eb44b857802 added file-ab
155 155 |
156 156 o 0:3004d2d9b50883c1538fc754a3aeb55f1b4084f6 added file-a
157 157
158 158 $ hg commit -m 'update files for abcde'
159 159
160 160 Uncommit with dirty state
161 161
162 162 $ echo "foo" >> files
163 163 $ cat files
164 164 abcde
165 165 foo
166 166 $ hg status
167 167 M files
168 168 $ hg uncommit
169 169 abort: uncommitted changes
170 170 (requires --allow-dirty-working-copy to uncommit)
171 171 [20]
172 172 $ hg uncommit files
173 173 abort: uncommitted changes
174 174 (requires --allow-dirty-working-copy to uncommit)
175 175 [20]
176 176 $ cat files
177 177 abcde
178 178 foo
179 179 $ hg commit --amend -m "files abcde + foo"
180 180
181 181 Testing the 'experimental.uncommitondirtywdir' config
182 182
183 183 $ echo "bar" >> files
184 184 $ hg uncommit
185 185 abort: uncommitted changes
186 186 (requires --allow-dirty-working-copy to uncommit)
187 187 [20]
188 188 $ hg uncommit --config experimental.uncommitondirtywdir=True
189 189 $ hg commit -m "files abcde + foo"
190 190
191 191 Uncommit in the middle of a stack, does not move bookmark
192 192
193 193 $ hg checkout '.^^^'
194 194 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
195 195 (leaving bookmark foo)
196 196 $ hg log -r . -p -T '{rev}:{node} {desc}'
197 197 2:abf2df566fc193b3ac34d946e63c1583e4d4732b added file-abcdiff -r 69a232e754b0 -r abf2df566fc1 file-abc
198 198 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
199 199 +++ b/file-abc Thu Jan 01 00:00:00 1970 +0000
200 200 @@ -0,0 +1,1 @@
201 201 +abc
202 202 diff -r 69a232e754b0 -r abf2df566fc1 files
203 203 --- a/files Thu Jan 01 00:00:00 1970 +0000
204 204 +++ b/files Thu Jan 01 00:00:00 1970 +0000
205 205 @@ -1,1 +1,1 @@
206 206 -ab
207 207 +abc
208 208
209 209 $ hg bookmark
210 210 foo 9:48e5bd7cd583
211 211 $ hg uncommit
212 212 3 new orphan changesets
213 213 $ hg status
214 214 M files
215 215 A file-abc
216 216 $ hg heads -T '{rev}:{node} {desc}'
217 217 9:48e5bd7cd583eb24164ef8b89185819c84c96ed7 files abcde + foo (no-eol)
218 218 $ hg bookmark
219 219 foo 9:48e5bd7cd583
220 220 $ hg commit -m 'new abc'
221 221 created new head
222 222
223 223 Partial uncommit in the middle, does not move bookmark
224 224
225 225 $ hg checkout '.^'
226 226 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
227 227 $ hg log -r . -p -T '{rev}:{node} {desc}'
228 228 1:69a232e754b08d568c4899475faf2eb44b857802 added file-abdiff -r 3004d2d9b508 -r 69a232e754b0 file-ab
229 229 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
230 230 +++ b/file-ab Thu Jan 01 00:00:00 1970 +0000
231 231 @@ -0,0 +1,1 @@
232 232 +ab
233 233 diff -r 3004d2d9b508 -r 69a232e754b0 files
234 234 --- a/files Thu Jan 01 00:00:00 1970 +0000
235 235 +++ b/files Thu Jan 01 00:00:00 1970 +0000
236 236 @@ -1,1 +1,1 @@
237 237 -a
238 238 +ab
239 239
240 240 $ hg bookmark
241 241 foo 9:48e5bd7cd583
242 242 $ hg uncommit file-ab
243 243 1 new orphan changesets
244 244 $ hg status
245 245 A file-ab
246 246
247 247 $ hg heads -T '{rev}:{node} {desc}\n'
248 248 11:8eb87968f2edb7f27f27fe676316e179de65fff6 added file-ab
249 249 10:5dc89ca4486f8a88716c5797fa9f498d13d7c2e1 new abc
250 250 9:48e5bd7cd583eb24164ef8b89185819c84c96ed7 files abcde + foo
251 251
252 252 $ hg bookmark
253 253 foo 9:48e5bd7cd583
254 254 $ hg commit -m 'update ab'
255 255 $ hg status
256 256 $ hg heads -T '{rev}:{node} {desc}\n'
257 257 12:f21039c59242b085491bb58f591afc4ed1c04c09 update ab
258 258 10:5dc89ca4486f8a88716c5797fa9f498d13d7c2e1 new abc
259 259 9:48e5bd7cd583eb24164ef8b89185819c84c96ed7 files abcde + foo
260 260
261 261 $ hg log -G -T '{rev}:{node} {desc}' --hidden
262 262 @ 12:f21039c59242b085491bb58f591afc4ed1c04c09 update ab
263 263 |
264 264 o 11:8eb87968f2edb7f27f27fe676316e179de65fff6 added file-ab
265 265 |
266 266 | * 10:5dc89ca4486f8a88716c5797fa9f498d13d7c2e1 new abc
267 267 | |
268 268 | | * 9:48e5bd7cd583eb24164ef8b89185819c84c96ed7 files abcde + foo
269 269 | | |
270 270 | | | x 8:84beeba0ac30e19521c036e4d2dd3a5fa02586ff files abcde + foo
271 271 | | |/
272 272 | | | x 7:0977fa602c2fd7d8427ed4e7ee15ea13b84c9173 update files for abcde
273 273 | | |/
274 274 | | * 6:3727deee06f72f5ffa8db792ee299cf39e3e190b new change abcde
275 275 | | |
276 276 | | | x 5:0c07a3ccda771b25f1cb1edbd02e683723344ef1 new change abcde
277 277 | | |/
278 278 | | | x 4:6c4fd43ed714e7fcd8adbaa7b16c953c2e985b60 added file-abcde
279 279 | | |/
280 280 | | * 3:6db330d65db434145c0b59d291853e9a84719b24 added file-abcd
281 281 | | |
282 282 | | x 2:abf2df566fc193b3ac34d946e63c1583e4d4732b added file-abc
283 283 | |/
284 284 | x 1:69a232e754b08d568c4899475faf2eb44b857802 added file-ab
285 285 |/
286 286 o 0:3004d2d9b50883c1538fc754a3aeb55f1b4084f6 added file-a
287 287
288 288 Uncommit with draft parent
289 289
290 290 $ hg uncommit
291 291 $ hg phase -r .
292 292 11: draft
293 293 $ hg commit -m 'update ab again'
294 294
295 295 Phase is preserved
296 296
297 297 $ hg uncommit --keep --config phases.new-commit=secret
298 298 note: keeping empty commit
299 299 $ hg phase -r .
300 300 14: draft
301 301 $ hg commit --amend -m 'update ab again'
302 302
303 303 Uncommit with public parent
304 304
305 305 $ hg phase -p "::.^"
306 306 $ hg uncommit
307 307 $ hg phase -r .
308 308 11: public
309 309
310 310 Partial uncommit with public parent
311 311
312 312 $ echo xyz > xyz
313 313 $ hg add xyz
314 314 $ hg commit -m "update ab and add xyz"
315 315 $ hg uncommit xyz
316 316 $ hg status
317 317 A xyz
318 318 $ hg phase -r .
319 319 17: draft
320 320 $ hg phase -r ".^"
321 321 11: public
322 322
323 323 Uncommit with --keep or experimental.uncommit.keep leaves an empty changeset
324 324
325 325 $ cd $TESTTMP
326 326 $ hg init repo1
327 327 $ cd repo1
328 328 $ hg debugdrawdag <<'EOS'
329 329 > Q
330 330 > |
331 331 > P
332 332 > EOS
333 333 $ hg up Q -q
334 334 $ hg uncommit --keep
335 335 note: keeping empty commit
336 336 $ hg log -G -T '{desc} FILES: {files}'
337 337 @ Q FILES:
338 338 |
339 339 | x Q FILES: Q
340 340 |/
341 341 o P FILES: P
342 342
343 343 $ cat >> .hg/hgrc <<EOF
344 344 > [experimental]
345 345 > uncommit.keep=True
346 346 > EOF
347 347 $ hg ci --amend
348 348 $ hg uncommit
349 349 note: keeping empty commit
350 350 $ hg log -G -T '{desc} FILES: {files}'
351 351 @ Q FILES:
352 352 |
353 353 | x Q FILES: Q
354 354 |/
355 355 o P FILES: P
356 356
357 357 $ hg status
358 358 A Q
359 359 $ hg ci --amend
360 360 $ hg uncommit --no-keep
361 361 $ hg log -G -T '{desc} FILES: {files}'
362 362 x Q FILES: Q
363 363 |
364 364 @ P FILES: P
365 365
366 366 $ hg status
367 367 A Q
368 368 $ cd ..
369 369 $ rm -rf repo1
370 370
371 371 Testing uncommit while merge
372 372
373 373 $ hg init repo2
374 374 $ cd repo2
375 375
376 376 Create some history
377 377
378 378 $ touch a
379 379 $ hg add a
380 380 $ for i in 1 2 3; do echo $i > a; hg commit -m "a $i"; done
381 381 $ hg checkout 0
382 382 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
383 383 $ touch b
384 384 $ hg add b
385 385 $ for i in 1 2 3; do echo $i > b; hg commit -m "b $i"; done
386 386 created new head
387 387 $ hg log -G -T '{rev}:{node} {desc}' --hidden
388 388 @ 5:2cd56cdde163ded2fbb16ba2f918c96046ab0bf2 b 3
389 389 |
390 390 o 4:c3a0d5bb3b15834ffd2ef9ef603e93ec65cf2037 b 2
391 391 |
392 392 o 3:49bb009ca26078726b8870f1edb29fae8f7618f5 b 1
393 393 |
394 394 | o 2:990982b7384266e691f1bc08ca36177adcd1c8a9 a 3
395 395 | |
396 396 | o 1:24d38e3cf160c7b6f5ffe82179332229886a6d34 a 2
397 397 |/
398 398 o 0:ea4e33293d4d274a2ba73150733c2612231f398c a 1
399 399
400 400
401 401 Add and expect uncommit to fail on both merge working dir and merge changeset
402 402
403 403 $ hg merge 2
404 404 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
405 405 (branch merge, don't forget to commit)
406 406
407 407 $ hg uncommit
408 408 abort: outstanding uncommitted merge
409 409 (requires --allow-dirty-working-copy to uncommit)
410 410 [20]
411 411
412 412 $ hg uncommit --config experimental.uncommitondirtywdir=True
413 413 abort: cannot uncommit while merging
414 414 [20]
415 415
416 416 $ hg status
417 417 M a
418 418 $ hg commit -m 'merge a and b'
419 419
420 420 $ hg uncommit
421 421 abort: cannot uncommit merge changeset
422 [255]
422 [10]
423 423
424 424 $ hg status
425 425 $ hg log -G -T '{rev}:{node} {desc}' --hidden
426 426 @ 6:c03b9c37bc67bf504d4912061cfb527b47a63c6e merge a and b
427 427 |\
428 428 | o 5:2cd56cdde163ded2fbb16ba2f918c96046ab0bf2 b 3
429 429 | |
430 430 | o 4:c3a0d5bb3b15834ffd2ef9ef603e93ec65cf2037 b 2
431 431 | |
432 432 | o 3:49bb009ca26078726b8870f1edb29fae8f7618f5 b 1
433 433 | |
434 434 o | 2:990982b7384266e691f1bc08ca36177adcd1c8a9 a 3
435 435 | |
436 436 o | 1:24d38e3cf160c7b6f5ffe82179332229886a6d34 a 2
437 437 |/
438 438 o 0:ea4e33293d4d274a2ba73150733c2612231f398c a 1
439 439
440 440
441 441 Rename a->b, then remove b in working copy. Result should remove a.
442 442
443 443 $ hg co -q 0
444 444 $ hg mv a b
445 445 $ hg ci -qm 'move a to b'
446 446 $ hg rm b
447 447 $ hg uncommit --config experimental.uncommitondirtywdir=True
448 448 $ hg st --copies
449 449 R a
450 450 $ hg revert a
451 451
452 452 Rename a->b, then rename b->c in working copy. Result should rename a->c.
453 453
454 454 $ hg co -q 0
455 455 $ hg mv a b
456 456 $ hg ci -qm 'move a to b'
457 457 $ hg mv b c
458 458 $ hg uncommit --config experimental.uncommitondirtywdir=True
459 459 $ hg st --copies
460 460 A c
461 461 a
462 462 R a
463 463 $ hg revert a
464 464 $ hg forget c
465 465 $ rm c
466 466
467 467 Copy a->b1 and a->b2, then rename b1->c in working copy. Result should copy a->b2 and a->c.
468 468
469 469 $ hg co -q 0
470 470 $ hg cp a b1
471 471 $ hg cp a b2
472 472 $ hg ci -qm 'move a to b1 and b2'
473 473 $ hg mv b1 c
474 474 $ hg uncommit --config experimental.uncommitondirtywdir=True
475 475 $ hg st --copies
476 476 A b2
477 477 a
478 478 A c
479 479 a
480 480 $ cd ..
481 481
482 482 --allow-dirty-working-copy should also work on a dirty PATH
483 483
484 484 $ hg init issue5977
485 485 $ cd issue5977
486 486 $ echo 'super critical info!' > a
487 487 $ hg ci -Am 'add a'
488 488 adding a
489 489 $ echo 'foo' > b
490 490 $ hg add b
491 491 $ hg status
492 492 A b
493 493 $ hg uncommit a
494 494 note: keeping empty commit
495 495 $ cat a
496 496 super critical info!
497 497 $ hg log
498 498 changeset: 1:656ba143d384
499 499 tag: tip
500 500 parent: -1:000000000000
501 501 user: test
502 502 date: Thu Jan 01 00:00:00 1970 +0000
503 503 summary: add a
504 504
505 505 $ hg ci -Am 'add b'
506 506 $ echo 'foo bar' > b
507 507 $ hg uncommit b
508 508 abort: uncommitted changes
509 509 (requires --allow-dirty-working-copy to uncommit)
510 510 [20]
511 511 $ hg uncommit --allow-dirty-working-copy b
512 512 $ hg log
513 513 changeset: 3:30fa958635b2
514 514 tag: tip
515 515 parent: 1:656ba143d384
516 516 user: test
517 517 date: Thu Jan 01 00:00:00 1970 +0000
518 518 summary: add b
519 519
520 520 changeset: 1:656ba143d384
521 521 parent: -1:000000000000
522 522 user: test
523 523 date: Thu Jan 01 00:00:00 1970 +0000
524 524 summary: add a
525 525
526 526 Removes can be uncommitted
527 527
528 528 $ hg ci -m 'modified b'
529 529 $ hg rm b
530 530 $ hg ci -m 'remove b'
531 531 $ hg uncommit b
532 532 note: keeping empty commit
533 533 $ hg status
534 534 R b
535 535
536 536 Uncommitting a directory won't run afoul of the checks that an explicit file
537 537 can be uncommitted.
538 538
539 539 $ mkdir dir
540 540 $ echo 1 > dir/file.txt
541 541 $ hg ci -Aqm 'add file in directory'
542 542 $ hg uncommit dir -m 'uncommit with message' -u 'different user' \
543 543 > -d 'Jun 30 12:12:12 1980 +0000'
544 544 $ hg status
545 545 A dir/file.txt
546 546 $ hg log -r .
547 547 changeset: 8:b4dd26dc42e0
548 548 tag: tip
549 549 parent: 6:2278a4c24330
550 550 user: different user
551 551 date: Mon Jun 30 12:12:12 1980 +0000
552 552 summary: uncommit with message
553 553
554 554 Bad option combinations
555 555
556 556 $ hg rollback -q --config ui.rollback=True
557 557 $ hg uncommit -U --user 'user'
558 558 abort: cannot specify both --user and --currentuser
559 559 [10]
560 560 $ hg uncommit -D --date today
561 561 abort: cannot specify both --date and --currentdate
562 562 [10]
563 563
564 564 `uncommit <dir>` and `cd <dir> && uncommit .` behave the same...
565 565
566 566 $ echo 2 > dir/file2.txt
567 567 $ hg ci -Aqm 'add file2 in directory'
568 568 $ hg uncommit dir
569 569 note: keeping empty commit
570 570 $ hg status
571 571 A dir/file2.txt
572 572
573 573 $ hg rollback -q --config ui.rollback=True
574 574 $ cd dir
575 575 $ hg uncommit . -n 'this is a note'
576 576 note: keeping empty commit
577 577 $ hg status
578 578 A dir/file2.txt
579 579 $ cd ..
580 580
581 581 ... and errors out the same way when nothing can be uncommitted
582 582
583 583 $ hg rollback -q --config ui.rollback=True
584 584 $ mkdir emptydir
585 585 $ hg uncommit emptydir
586 586 abort: cannot uncommit "emptydir"
587 587 (file was untracked in working directory parent)
588 [255]
588 [10]
589 589
590 590 $ cd emptydir
591 591 $ hg uncommit .
592 592 abort: cannot uncommit "emptydir"
593 593 (file was untracked in working directory parent)
594 [255]
594 [10]
595 595 $ hg status
596 596 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now