##// END OF EJS Templates
commit: use cmdutil.check_at_most_one_arg()...
Martin von Zweigbergk -
r44345:2e672ccc default
parent child Browse files
Show More
@@ -1,4036 +1,4034 b''
1 # cmdutil.py - help for command processing in mercurial
1 # cmdutil.py - help for command processing in mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
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
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import copy as copymod
10 import copy as copymod
11 import errno
11 import errno
12 import os
12 import os
13 import re
13 import re
14
14
15 from .i18n import _
15 from .i18n import _
16 from .node import (
16 from .node import (
17 hex,
17 hex,
18 nullid,
18 nullid,
19 nullrev,
19 nullrev,
20 short,
20 short,
21 )
21 )
22 from .pycompat import (
22 from .pycompat import (
23 getattr,
23 getattr,
24 open,
24 open,
25 setattr,
25 setattr,
26 )
26 )
27 from .thirdparty import attr
27 from .thirdparty import attr
28
28
29 from . import (
29 from . import (
30 bookmarks,
30 bookmarks,
31 changelog,
31 changelog,
32 copies,
32 copies,
33 crecord as crecordmod,
33 crecord as crecordmod,
34 dirstateguard,
34 dirstateguard,
35 encoding,
35 encoding,
36 error,
36 error,
37 formatter,
37 formatter,
38 logcmdutil,
38 logcmdutil,
39 match as matchmod,
39 match as matchmod,
40 merge as mergemod,
40 merge as mergemod,
41 mergeutil,
41 mergeutil,
42 obsolete,
42 obsolete,
43 patch,
43 patch,
44 pathutil,
44 pathutil,
45 phases,
45 phases,
46 pycompat,
46 pycompat,
47 repair,
47 repair,
48 revlog,
48 revlog,
49 rewriteutil,
49 rewriteutil,
50 scmutil,
50 scmutil,
51 smartset,
51 smartset,
52 state as statemod,
52 state as statemod,
53 subrepoutil,
53 subrepoutil,
54 templatekw,
54 templatekw,
55 templater,
55 templater,
56 util,
56 util,
57 vfs as vfsmod,
57 vfs as vfsmod,
58 )
58 )
59
59
60 from .utils import (
60 from .utils import (
61 dateutil,
61 dateutil,
62 stringutil,
62 stringutil,
63 )
63 )
64
64
65 if pycompat.TYPE_CHECKING:
65 if pycompat.TYPE_CHECKING:
66 from typing import (
66 from typing import (
67 Any,
67 Any,
68 Dict,
68 Dict,
69 )
69 )
70
70
71 for t in (Any, Dict):
71 for t in (Any, Dict):
72 assert t
72 assert t
73
73
74 stringio = util.stringio
74 stringio = util.stringio
75
75
76 # templates of common command options
76 # templates of common command options
77
77
78 dryrunopts = [
78 dryrunopts = [
79 (b'n', b'dry-run', None, _(b'do not perform actions, just print output')),
79 (b'n', b'dry-run', None, _(b'do not perform actions, just print output')),
80 ]
80 ]
81
81
82 confirmopts = [
82 confirmopts = [
83 (b'', b'confirm', None, _(b'ask before applying actions')),
83 (b'', b'confirm', None, _(b'ask before applying actions')),
84 ]
84 ]
85
85
86 remoteopts = [
86 remoteopts = [
87 (b'e', b'ssh', b'', _(b'specify ssh command to use'), _(b'CMD')),
87 (b'e', b'ssh', b'', _(b'specify ssh command to use'), _(b'CMD')),
88 (
88 (
89 b'',
89 b'',
90 b'remotecmd',
90 b'remotecmd',
91 b'',
91 b'',
92 _(b'specify hg command to run on the remote side'),
92 _(b'specify hg command to run on the remote side'),
93 _(b'CMD'),
93 _(b'CMD'),
94 ),
94 ),
95 (
95 (
96 b'',
96 b'',
97 b'insecure',
97 b'insecure',
98 None,
98 None,
99 _(b'do not verify server certificate (ignoring web.cacerts config)'),
99 _(b'do not verify server certificate (ignoring web.cacerts config)'),
100 ),
100 ),
101 ]
101 ]
102
102
103 walkopts = [
103 walkopts = [
104 (
104 (
105 b'I',
105 b'I',
106 b'include',
106 b'include',
107 [],
107 [],
108 _(b'include names matching the given patterns'),
108 _(b'include names matching the given patterns'),
109 _(b'PATTERN'),
109 _(b'PATTERN'),
110 ),
110 ),
111 (
111 (
112 b'X',
112 b'X',
113 b'exclude',
113 b'exclude',
114 [],
114 [],
115 _(b'exclude names matching the given patterns'),
115 _(b'exclude names matching the given patterns'),
116 _(b'PATTERN'),
116 _(b'PATTERN'),
117 ),
117 ),
118 ]
118 ]
119
119
120 commitopts = [
120 commitopts = [
121 (b'm', b'message', b'', _(b'use text as commit message'), _(b'TEXT')),
121 (b'm', b'message', b'', _(b'use text as commit message'), _(b'TEXT')),
122 (b'l', b'logfile', b'', _(b'read commit message from file'), _(b'FILE')),
122 (b'l', b'logfile', b'', _(b'read commit message from file'), _(b'FILE')),
123 ]
123 ]
124
124
125 commitopts2 = [
125 commitopts2 = [
126 (
126 (
127 b'd',
127 b'd',
128 b'date',
128 b'date',
129 b'',
129 b'',
130 _(b'record the specified date as commit date'),
130 _(b'record the specified date as commit date'),
131 _(b'DATE'),
131 _(b'DATE'),
132 ),
132 ),
133 (
133 (
134 b'u',
134 b'u',
135 b'user',
135 b'user',
136 b'',
136 b'',
137 _(b'record the specified user as committer'),
137 _(b'record the specified user as committer'),
138 _(b'USER'),
138 _(b'USER'),
139 ),
139 ),
140 ]
140 ]
141
141
142 commitopts3 = [
142 commitopts3 = [
143 (b'D', b'currentdate', None, _(b'record the current date as commit date')),
143 (b'D', b'currentdate', None, _(b'record the current date as commit date')),
144 (b'U', b'currentuser', None, _(b'record the current user as committer')),
144 (b'U', b'currentuser', None, _(b'record the current user as committer')),
145 ]
145 ]
146
146
147 formatteropts = [
147 formatteropts = [
148 (b'T', b'template', b'', _(b'display with template'), _(b'TEMPLATE')),
148 (b'T', b'template', b'', _(b'display with template'), _(b'TEMPLATE')),
149 ]
149 ]
150
150
151 templateopts = [
151 templateopts = [
152 (
152 (
153 b'',
153 b'',
154 b'style',
154 b'style',
155 b'',
155 b'',
156 _(b'display using template map file (DEPRECATED)'),
156 _(b'display using template map file (DEPRECATED)'),
157 _(b'STYLE'),
157 _(b'STYLE'),
158 ),
158 ),
159 (b'T', b'template', b'', _(b'display with template'), _(b'TEMPLATE')),
159 (b'T', b'template', b'', _(b'display with template'), _(b'TEMPLATE')),
160 ]
160 ]
161
161
162 logopts = [
162 logopts = [
163 (b'p', b'patch', None, _(b'show patch')),
163 (b'p', b'patch', None, _(b'show patch')),
164 (b'g', b'git', None, _(b'use git extended diff format')),
164 (b'g', b'git', None, _(b'use git extended diff format')),
165 (b'l', b'limit', b'', _(b'limit number of changes displayed'), _(b'NUM')),
165 (b'l', b'limit', b'', _(b'limit number of changes displayed'), _(b'NUM')),
166 (b'M', b'no-merges', None, _(b'do not show merges')),
166 (b'M', b'no-merges', None, _(b'do not show merges')),
167 (b'', b'stat', None, _(b'output diffstat-style summary of changes')),
167 (b'', b'stat', None, _(b'output diffstat-style summary of changes')),
168 (b'G', b'graph', None, _(b"show the revision DAG")),
168 (b'G', b'graph', None, _(b"show the revision DAG")),
169 ] + templateopts
169 ] + templateopts
170
170
171 diffopts = [
171 diffopts = [
172 (b'a', b'text', None, _(b'treat all files as text')),
172 (b'a', b'text', None, _(b'treat all files as text')),
173 (b'g', b'git', None, _(b'use git extended diff format')),
173 (b'g', b'git', None, _(b'use git extended diff format')),
174 (b'', b'binary', None, _(b'generate binary diffs in git mode (default)')),
174 (b'', b'binary', None, _(b'generate binary diffs in git mode (default)')),
175 (b'', b'nodates', None, _(b'omit dates from diff headers')),
175 (b'', b'nodates', None, _(b'omit dates from diff headers')),
176 ]
176 ]
177
177
178 diffwsopts = [
178 diffwsopts = [
179 (
179 (
180 b'w',
180 b'w',
181 b'ignore-all-space',
181 b'ignore-all-space',
182 None,
182 None,
183 _(b'ignore white space when comparing lines'),
183 _(b'ignore white space when comparing lines'),
184 ),
184 ),
185 (
185 (
186 b'b',
186 b'b',
187 b'ignore-space-change',
187 b'ignore-space-change',
188 None,
188 None,
189 _(b'ignore changes in the amount of white space'),
189 _(b'ignore changes in the amount of white space'),
190 ),
190 ),
191 (
191 (
192 b'B',
192 b'B',
193 b'ignore-blank-lines',
193 b'ignore-blank-lines',
194 None,
194 None,
195 _(b'ignore changes whose lines are all blank'),
195 _(b'ignore changes whose lines are all blank'),
196 ),
196 ),
197 (
197 (
198 b'Z',
198 b'Z',
199 b'ignore-space-at-eol',
199 b'ignore-space-at-eol',
200 None,
200 None,
201 _(b'ignore changes in whitespace at EOL'),
201 _(b'ignore changes in whitespace at EOL'),
202 ),
202 ),
203 ]
203 ]
204
204
205 diffopts2 = (
205 diffopts2 = (
206 [
206 [
207 (b'', b'noprefix', None, _(b'omit a/ and b/ prefixes from filenames')),
207 (b'', b'noprefix', None, _(b'omit a/ and b/ prefixes from filenames')),
208 (
208 (
209 b'p',
209 b'p',
210 b'show-function',
210 b'show-function',
211 None,
211 None,
212 _(b'show which function each change is in'),
212 _(b'show which function each change is in'),
213 ),
213 ),
214 (b'', b'reverse', None, _(b'produce a diff that undoes the changes')),
214 (b'', b'reverse', None, _(b'produce a diff that undoes the changes')),
215 ]
215 ]
216 + diffwsopts
216 + diffwsopts
217 + [
217 + [
218 (
218 (
219 b'U',
219 b'U',
220 b'unified',
220 b'unified',
221 b'',
221 b'',
222 _(b'number of lines of context to show'),
222 _(b'number of lines of context to show'),
223 _(b'NUM'),
223 _(b'NUM'),
224 ),
224 ),
225 (b'', b'stat', None, _(b'output diffstat-style summary of changes')),
225 (b'', b'stat', None, _(b'output diffstat-style summary of changes')),
226 (
226 (
227 b'',
227 b'',
228 b'root',
228 b'root',
229 b'',
229 b'',
230 _(b'produce diffs relative to subdirectory'),
230 _(b'produce diffs relative to subdirectory'),
231 _(b'DIR'),
231 _(b'DIR'),
232 ),
232 ),
233 ]
233 ]
234 )
234 )
235
235
236 mergetoolopts = [
236 mergetoolopts = [
237 (b't', b'tool', b'', _(b'specify merge tool'), _(b'TOOL')),
237 (b't', b'tool', b'', _(b'specify merge tool'), _(b'TOOL')),
238 ]
238 ]
239
239
240 similarityopts = [
240 similarityopts = [
241 (
241 (
242 b's',
242 b's',
243 b'similarity',
243 b'similarity',
244 b'',
244 b'',
245 _(b'guess renamed files by similarity (0<=s<=100)'),
245 _(b'guess renamed files by similarity (0<=s<=100)'),
246 _(b'SIMILARITY'),
246 _(b'SIMILARITY'),
247 )
247 )
248 ]
248 ]
249
249
250 subrepoopts = [(b'S', b'subrepos', None, _(b'recurse into subrepositories'))]
250 subrepoopts = [(b'S', b'subrepos', None, _(b'recurse into subrepositories'))]
251
251
252 debugrevlogopts = [
252 debugrevlogopts = [
253 (b'c', b'changelog', False, _(b'open changelog')),
253 (b'c', b'changelog', False, _(b'open changelog')),
254 (b'm', b'manifest', False, _(b'open manifest')),
254 (b'm', b'manifest', False, _(b'open manifest')),
255 (b'', b'dir', b'', _(b'open directory manifest')),
255 (b'', b'dir', b'', _(b'open directory manifest')),
256 ]
256 ]
257
257
258 # special string such that everything below this line will be ingored in the
258 # special string such that everything below this line will be ingored in the
259 # editor text
259 # editor text
260 _linebelow = b"^HG: ------------------------ >8 ------------------------$"
260 _linebelow = b"^HG: ------------------------ >8 ------------------------$"
261
261
262
262
263 def check_at_most_one_arg(opts, *args):
263 def check_at_most_one_arg(opts, *args):
264 """abort if more than one of the arguments are in opts"""
264 """abort if more than one of the arguments are in opts"""
265 previous = None
265 previous = None
266 for x in args:
266 for x in args:
267 if opts.get(x):
267 if opts.get(x):
268 if previous:
268 if previous:
269 raise error.Abort(
269 raise error.Abort(
270 _(b'cannot specify both --%s and --%s') % (previous, x)
270 _(b'cannot specify both --%s and --%s') % (previous, x)
271 )
271 )
272 previous = x
272 previous = x
273
273
274
274
275 def resolvecommitoptions(ui, opts):
275 def resolvecommitoptions(ui, opts):
276 """modify commit options dict to handle related options
276 """modify commit options dict to handle related options
277
277
278 The return value indicates that ``rewrite.update-timestamp`` is the reason
278 The return value indicates that ``rewrite.update-timestamp`` is the reason
279 the ``date`` option is set.
279 the ``date`` option is set.
280 """
280 """
281 if opts.get(b'date') and opts.get(b'currentdate'):
281 check_at_most_one_arg(opts, b'date', b'currentdate')
282 raise error.Abort(_(b'--date and --currentdate are mutually exclusive'))
282 check_at_most_one_arg(opts, b'user', b'currentuser')
283 if opts.get(b'user') and opts.get(b'currentuser'):
284 raise error.Abort(_(b'--user and --currentuser are mutually exclusive'))
285
283
286 datemaydiffer = False # date-only change should be ignored?
284 datemaydiffer = False # date-only change should be ignored?
287
285
288 if opts.get(b'currentdate'):
286 if opts.get(b'currentdate'):
289 opts[b'date'] = b'%d %d' % dateutil.makedate()
287 opts[b'date'] = b'%d %d' % dateutil.makedate()
290 elif (
288 elif (
291 not opts.get(b'date')
289 not opts.get(b'date')
292 and ui.configbool(b'rewrite', b'update-timestamp')
290 and ui.configbool(b'rewrite', b'update-timestamp')
293 and opts.get(b'currentdate') is None
291 and opts.get(b'currentdate') is None
294 ):
292 ):
295 opts[b'date'] = b'%d %d' % dateutil.makedate()
293 opts[b'date'] = b'%d %d' % dateutil.makedate()
296 datemaydiffer = True
294 datemaydiffer = True
297
295
298 if opts.get(b'currentuser'):
296 if opts.get(b'currentuser'):
299 opts[b'user'] = ui.username()
297 opts[b'user'] = ui.username()
300
298
301 return datemaydiffer
299 return datemaydiffer
302
300
303
301
304 def checknotesize(ui, opts):
302 def checknotesize(ui, opts):
305 """ make sure note is of valid format """
303 """ make sure note is of valid format """
306
304
307 note = opts.get(b'note')
305 note = opts.get(b'note')
308 if not note:
306 if not note:
309 return
307 return
310
308
311 if len(note) > 255:
309 if len(note) > 255:
312 raise error.Abort(_(b"cannot store a note of more than 255 bytes"))
310 raise error.Abort(_(b"cannot store a note of more than 255 bytes"))
313 if b'\n' in note:
311 if b'\n' in note:
314 raise error.Abort(_(b"note cannot contain a newline"))
312 raise error.Abort(_(b"note cannot contain a newline"))
315
313
316
314
317 def ishunk(x):
315 def ishunk(x):
318 hunkclasses = (crecordmod.uihunk, patch.recordhunk)
316 hunkclasses = (crecordmod.uihunk, patch.recordhunk)
319 return isinstance(x, hunkclasses)
317 return isinstance(x, hunkclasses)
320
318
321
319
322 def newandmodified(chunks, originalchunks):
320 def newandmodified(chunks, originalchunks):
323 newlyaddedandmodifiedfiles = set()
321 newlyaddedandmodifiedfiles = set()
324 alsorestore = set()
322 alsorestore = set()
325 for chunk in chunks:
323 for chunk in chunks:
326 if (
324 if (
327 ishunk(chunk)
325 ishunk(chunk)
328 and chunk.header.isnewfile()
326 and chunk.header.isnewfile()
329 and chunk not in originalchunks
327 and chunk not in originalchunks
330 ):
328 ):
331 newlyaddedandmodifiedfiles.add(chunk.header.filename())
329 newlyaddedandmodifiedfiles.add(chunk.header.filename())
332 alsorestore.update(
330 alsorestore.update(
333 set(chunk.header.files()) - {chunk.header.filename()}
331 set(chunk.header.files()) - {chunk.header.filename()}
334 )
332 )
335 return newlyaddedandmodifiedfiles, alsorestore
333 return newlyaddedandmodifiedfiles, alsorestore
336
334
337
335
338 def parsealiases(cmd):
336 def parsealiases(cmd):
339 return cmd.split(b"|")
337 return cmd.split(b"|")
340
338
341
339
342 def setupwrapcolorwrite(ui):
340 def setupwrapcolorwrite(ui):
343 # wrap ui.write so diff output can be labeled/colorized
341 # wrap ui.write so diff output can be labeled/colorized
344 def wrapwrite(orig, *args, **kw):
342 def wrapwrite(orig, *args, **kw):
345 label = kw.pop('label', b'')
343 label = kw.pop('label', b'')
346 for chunk, l in patch.difflabel(lambda: args):
344 for chunk, l in patch.difflabel(lambda: args):
347 orig(chunk, label=label + l)
345 orig(chunk, label=label + l)
348
346
349 oldwrite = ui.write
347 oldwrite = ui.write
350
348
351 def wrap(*args, **kwargs):
349 def wrap(*args, **kwargs):
352 return wrapwrite(oldwrite, *args, **kwargs)
350 return wrapwrite(oldwrite, *args, **kwargs)
353
351
354 setattr(ui, 'write', wrap)
352 setattr(ui, 'write', wrap)
355 return oldwrite
353 return oldwrite
356
354
357
355
358 def filterchunks(ui, originalhunks, usecurses, testfile, match, operation=None):
356 def filterchunks(ui, originalhunks, usecurses, testfile, match, operation=None):
359 try:
357 try:
360 if usecurses:
358 if usecurses:
361 if testfile:
359 if testfile:
362 recordfn = crecordmod.testdecorator(
360 recordfn = crecordmod.testdecorator(
363 testfile, crecordmod.testchunkselector
361 testfile, crecordmod.testchunkselector
364 )
362 )
365 else:
363 else:
366 recordfn = crecordmod.chunkselector
364 recordfn = crecordmod.chunkselector
367
365
368 return crecordmod.filterpatch(
366 return crecordmod.filterpatch(
369 ui, originalhunks, recordfn, operation
367 ui, originalhunks, recordfn, operation
370 )
368 )
371 except crecordmod.fallbackerror as e:
369 except crecordmod.fallbackerror as e:
372 ui.warn(b'%s\n' % e.message) # pytype: disable=attribute-error
370 ui.warn(b'%s\n' % e.message) # pytype: disable=attribute-error
373 ui.warn(_(b'falling back to text mode\n'))
371 ui.warn(_(b'falling back to text mode\n'))
374
372
375 return patch.filterpatch(ui, originalhunks, match, operation)
373 return patch.filterpatch(ui, originalhunks, match, operation)
376
374
377
375
378 def recordfilter(ui, originalhunks, match, operation=None):
376 def recordfilter(ui, originalhunks, match, operation=None):
379 """ Prompts the user to filter the originalhunks and return a list of
377 """ Prompts the user to filter the originalhunks and return a list of
380 selected hunks.
378 selected hunks.
381 *operation* is used for to build ui messages to indicate the user what
379 *operation* is used for to build ui messages to indicate the user what
382 kind of filtering they are doing: reverting, committing, shelving, etc.
380 kind of filtering they are doing: reverting, committing, shelving, etc.
383 (see patch.filterpatch).
381 (see patch.filterpatch).
384 """
382 """
385 usecurses = crecordmod.checkcurses(ui)
383 usecurses = crecordmod.checkcurses(ui)
386 testfile = ui.config(b'experimental', b'crecordtest')
384 testfile = ui.config(b'experimental', b'crecordtest')
387 oldwrite = setupwrapcolorwrite(ui)
385 oldwrite = setupwrapcolorwrite(ui)
388 try:
386 try:
389 newchunks, newopts = filterchunks(
387 newchunks, newopts = filterchunks(
390 ui, originalhunks, usecurses, testfile, match, operation
388 ui, originalhunks, usecurses, testfile, match, operation
391 )
389 )
392 finally:
390 finally:
393 ui.write = oldwrite
391 ui.write = oldwrite
394 return newchunks, newopts
392 return newchunks, newopts
395
393
396
394
397 def dorecord(
395 def dorecord(
398 ui, repo, commitfunc, cmdsuggest, backupall, filterfn, *pats, **opts
396 ui, repo, commitfunc, cmdsuggest, backupall, filterfn, *pats, **opts
399 ):
397 ):
400 opts = pycompat.byteskwargs(opts)
398 opts = pycompat.byteskwargs(opts)
401 if not ui.interactive():
399 if not ui.interactive():
402 if cmdsuggest:
400 if cmdsuggest:
403 msg = _(b'running non-interactively, use %s instead') % cmdsuggest
401 msg = _(b'running non-interactively, use %s instead') % cmdsuggest
404 else:
402 else:
405 msg = _(b'running non-interactively')
403 msg = _(b'running non-interactively')
406 raise error.Abort(msg)
404 raise error.Abort(msg)
407
405
408 # make sure username is set before going interactive
406 # make sure username is set before going interactive
409 if not opts.get(b'user'):
407 if not opts.get(b'user'):
410 ui.username() # raise exception, username not provided
408 ui.username() # raise exception, username not provided
411
409
412 def recordfunc(ui, repo, message, match, opts):
410 def recordfunc(ui, repo, message, match, opts):
413 """This is generic record driver.
411 """This is generic record driver.
414
412
415 Its job is to interactively filter local changes, and
413 Its job is to interactively filter local changes, and
416 accordingly prepare working directory into a state in which the
414 accordingly prepare working directory into a state in which the
417 job can be delegated to a non-interactive commit command such as
415 job can be delegated to a non-interactive commit command such as
418 'commit' or 'qrefresh'.
416 'commit' or 'qrefresh'.
419
417
420 After the actual job is done by non-interactive command, the
418 After the actual job is done by non-interactive command, the
421 working directory is restored to its original state.
419 working directory is restored to its original state.
422
420
423 In the end we'll record interesting changes, and everything else
421 In the end we'll record interesting changes, and everything else
424 will be left in place, so the user can continue working.
422 will be left in place, so the user can continue working.
425 """
423 """
426 if not opts.get(b'interactive-unshelve'):
424 if not opts.get(b'interactive-unshelve'):
427 checkunfinished(repo, commit=True)
425 checkunfinished(repo, commit=True)
428 wctx = repo[None]
426 wctx = repo[None]
429 merge = len(wctx.parents()) > 1
427 merge = len(wctx.parents()) > 1
430 if merge:
428 if merge:
431 raise error.Abort(
429 raise error.Abort(
432 _(
430 _(
433 b'cannot partially commit a merge '
431 b'cannot partially commit a merge '
434 b'(use "hg commit" instead)'
432 b'(use "hg commit" instead)'
435 )
433 )
436 )
434 )
437
435
438 def fail(f, msg):
436 def fail(f, msg):
439 raise error.Abort(b'%s: %s' % (f, msg))
437 raise error.Abort(b'%s: %s' % (f, msg))
440
438
441 force = opts.get(b'force')
439 force = opts.get(b'force')
442 if not force:
440 if not force:
443 match = matchmod.badmatch(match, fail)
441 match = matchmod.badmatch(match, fail)
444
442
445 status = repo.status(match=match)
443 status = repo.status(match=match)
446
444
447 overrides = {(b'ui', b'commitsubrepos'): True}
445 overrides = {(b'ui', b'commitsubrepos'): True}
448
446
449 with repo.ui.configoverride(overrides, b'record'):
447 with repo.ui.configoverride(overrides, b'record'):
450 # subrepoutil.precommit() modifies the status
448 # subrepoutil.precommit() modifies the status
451 tmpstatus = scmutil.status(
449 tmpstatus = scmutil.status(
452 copymod.copy(status.modified),
450 copymod.copy(status.modified),
453 copymod.copy(status.added),
451 copymod.copy(status.added),
454 copymod.copy(status.removed),
452 copymod.copy(status.removed),
455 copymod.copy(status.deleted),
453 copymod.copy(status.deleted),
456 copymod.copy(status.unknown),
454 copymod.copy(status.unknown),
457 copymod.copy(status.ignored),
455 copymod.copy(status.ignored),
458 copymod.copy(status.clean), # pytype: disable=wrong-arg-count
456 copymod.copy(status.clean), # pytype: disable=wrong-arg-count
459 )
457 )
460
458
461 # Force allows -X subrepo to skip the subrepo.
459 # Force allows -X subrepo to skip the subrepo.
462 subs, commitsubs, newstate = subrepoutil.precommit(
460 subs, commitsubs, newstate = subrepoutil.precommit(
463 repo.ui, wctx, tmpstatus, match, force=True
461 repo.ui, wctx, tmpstatus, match, force=True
464 )
462 )
465 for s in subs:
463 for s in subs:
466 if s in commitsubs:
464 if s in commitsubs:
467 dirtyreason = wctx.sub(s).dirtyreason(True)
465 dirtyreason = wctx.sub(s).dirtyreason(True)
468 raise error.Abort(dirtyreason)
466 raise error.Abort(dirtyreason)
469
467
470 if not force:
468 if not force:
471 repo.checkcommitpatterns(wctx, match, status, fail)
469 repo.checkcommitpatterns(wctx, match, status, fail)
472 diffopts = patch.difffeatureopts(
470 diffopts = patch.difffeatureopts(
473 ui,
471 ui,
474 opts=opts,
472 opts=opts,
475 whitespace=True,
473 whitespace=True,
476 section=b'commands',
474 section=b'commands',
477 configprefix=b'commit.interactive.',
475 configprefix=b'commit.interactive.',
478 )
476 )
479 diffopts.nodates = True
477 diffopts.nodates = True
480 diffopts.git = True
478 diffopts.git = True
481 diffopts.showfunc = True
479 diffopts.showfunc = True
482 originaldiff = patch.diff(repo, changes=status, opts=diffopts)
480 originaldiff = patch.diff(repo, changes=status, opts=diffopts)
483 originalchunks = patch.parsepatch(originaldiff)
481 originalchunks = patch.parsepatch(originaldiff)
484 match = scmutil.match(repo[None], pats)
482 match = scmutil.match(repo[None], pats)
485
483
486 # 1. filter patch, since we are intending to apply subset of it
484 # 1. filter patch, since we are intending to apply subset of it
487 try:
485 try:
488 chunks, newopts = filterfn(ui, originalchunks, match)
486 chunks, newopts = filterfn(ui, originalchunks, match)
489 except error.PatchError as err:
487 except error.PatchError as err:
490 raise error.Abort(_(b'error parsing patch: %s') % err)
488 raise error.Abort(_(b'error parsing patch: %s') % err)
491 opts.update(newopts)
489 opts.update(newopts)
492
490
493 # We need to keep a backup of files that have been newly added and
491 # We need to keep a backup of files that have been newly added and
494 # modified during the recording process because there is a previous
492 # modified during the recording process because there is a previous
495 # version without the edit in the workdir. We also will need to restore
493 # version without the edit in the workdir. We also will need to restore
496 # files that were the sources of renames so that the patch application
494 # files that were the sources of renames so that the patch application
497 # works.
495 # works.
498 newlyaddedandmodifiedfiles, alsorestore = newandmodified(
496 newlyaddedandmodifiedfiles, alsorestore = newandmodified(
499 chunks, originalchunks
497 chunks, originalchunks
500 )
498 )
501 contenders = set()
499 contenders = set()
502 for h in chunks:
500 for h in chunks:
503 try:
501 try:
504 contenders.update(set(h.files()))
502 contenders.update(set(h.files()))
505 except AttributeError:
503 except AttributeError:
506 pass
504 pass
507
505
508 changed = status.modified + status.added + status.removed
506 changed = status.modified + status.added + status.removed
509 newfiles = [f for f in changed if f in contenders]
507 newfiles = [f for f in changed if f in contenders]
510 if not newfiles:
508 if not newfiles:
511 ui.status(_(b'no changes to record\n'))
509 ui.status(_(b'no changes to record\n'))
512 return 0
510 return 0
513
511
514 modified = set(status.modified)
512 modified = set(status.modified)
515
513
516 # 2. backup changed files, so we can restore them in the end
514 # 2. backup changed files, so we can restore them in the end
517
515
518 if backupall:
516 if backupall:
519 tobackup = changed
517 tobackup = changed
520 else:
518 else:
521 tobackup = [
519 tobackup = [
522 f
520 f
523 for f in newfiles
521 for f in newfiles
524 if f in modified or f in newlyaddedandmodifiedfiles
522 if f in modified or f in newlyaddedandmodifiedfiles
525 ]
523 ]
526 backups = {}
524 backups = {}
527 if tobackup:
525 if tobackup:
528 backupdir = repo.vfs.join(b'record-backups')
526 backupdir = repo.vfs.join(b'record-backups')
529 try:
527 try:
530 os.mkdir(backupdir)
528 os.mkdir(backupdir)
531 except OSError as err:
529 except OSError as err:
532 if err.errno != errno.EEXIST:
530 if err.errno != errno.EEXIST:
533 raise
531 raise
534 try:
532 try:
535 # backup continues
533 # backup continues
536 for f in tobackup:
534 for f in tobackup:
537 fd, tmpname = pycompat.mkstemp(
535 fd, tmpname = pycompat.mkstemp(
538 prefix=f.replace(b'/', b'_') + b'.', dir=backupdir
536 prefix=f.replace(b'/', b'_') + b'.', dir=backupdir
539 )
537 )
540 os.close(fd)
538 os.close(fd)
541 ui.debug(b'backup %r as %r\n' % (f, tmpname))
539 ui.debug(b'backup %r as %r\n' % (f, tmpname))
542 util.copyfile(repo.wjoin(f), tmpname, copystat=True)
540 util.copyfile(repo.wjoin(f), tmpname, copystat=True)
543 backups[f] = tmpname
541 backups[f] = tmpname
544
542
545 fp = stringio()
543 fp = stringio()
546 for c in chunks:
544 for c in chunks:
547 fname = c.filename()
545 fname = c.filename()
548 if fname in backups:
546 if fname in backups:
549 c.write(fp)
547 c.write(fp)
550 dopatch = fp.tell()
548 dopatch = fp.tell()
551 fp.seek(0)
549 fp.seek(0)
552
550
553 # 2.5 optionally review / modify patch in text editor
551 # 2.5 optionally review / modify patch in text editor
554 if opts.get(b'review', False):
552 if opts.get(b'review', False):
555 patchtext = (
553 patchtext = (
556 crecordmod.diffhelptext
554 crecordmod.diffhelptext
557 + crecordmod.patchhelptext
555 + crecordmod.patchhelptext
558 + fp.read()
556 + fp.read()
559 )
557 )
560 reviewedpatch = ui.edit(
558 reviewedpatch = ui.edit(
561 patchtext, b"", action=b"diff", repopath=repo.path
559 patchtext, b"", action=b"diff", repopath=repo.path
562 )
560 )
563 fp.truncate(0)
561 fp.truncate(0)
564 fp.write(reviewedpatch)
562 fp.write(reviewedpatch)
565 fp.seek(0)
563 fp.seek(0)
566
564
567 [os.unlink(repo.wjoin(c)) for c in newlyaddedandmodifiedfiles]
565 [os.unlink(repo.wjoin(c)) for c in newlyaddedandmodifiedfiles]
568 # 3a. apply filtered patch to clean repo (clean)
566 # 3a. apply filtered patch to clean repo (clean)
569 if backups:
567 if backups:
570 # Equivalent to hg.revert
568 # Equivalent to hg.revert
571 m = scmutil.matchfiles(repo, set(backups.keys()) | alsorestore)
569 m = scmutil.matchfiles(repo, set(backups.keys()) | alsorestore)
572 mergemod.update(
570 mergemod.update(
573 repo,
571 repo,
574 repo.dirstate.p1(),
572 repo.dirstate.p1(),
575 branchmerge=False,
573 branchmerge=False,
576 force=True,
574 force=True,
577 matcher=m,
575 matcher=m,
578 )
576 )
579
577
580 # 3b. (apply)
578 # 3b. (apply)
581 if dopatch:
579 if dopatch:
582 try:
580 try:
583 ui.debug(b'applying patch\n')
581 ui.debug(b'applying patch\n')
584 ui.debug(fp.getvalue())
582 ui.debug(fp.getvalue())
585 patch.internalpatch(ui, repo, fp, 1, eolmode=None)
583 patch.internalpatch(ui, repo, fp, 1, eolmode=None)
586 except error.PatchError as err:
584 except error.PatchError as err:
587 raise error.Abort(pycompat.bytestr(err))
585 raise error.Abort(pycompat.bytestr(err))
588 del fp
586 del fp
589
587
590 # 4. We prepared working directory according to filtered
588 # 4. We prepared working directory according to filtered
591 # patch. Now is the time to delegate the job to
589 # patch. Now is the time to delegate the job to
592 # commit/qrefresh or the like!
590 # commit/qrefresh or the like!
593
591
594 # Make all of the pathnames absolute.
592 # Make all of the pathnames absolute.
595 newfiles = [repo.wjoin(nf) for nf in newfiles]
593 newfiles = [repo.wjoin(nf) for nf in newfiles]
596 return commitfunc(ui, repo, *newfiles, **pycompat.strkwargs(opts))
594 return commitfunc(ui, repo, *newfiles, **pycompat.strkwargs(opts))
597 finally:
595 finally:
598 # 5. finally restore backed-up files
596 # 5. finally restore backed-up files
599 try:
597 try:
600 dirstate = repo.dirstate
598 dirstate = repo.dirstate
601 for realname, tmpname in pycompat.iteritems(backups):
599 for realname, tmpname in pycompat.iteritems(backups):
602 ui.debug(b'restoring %r to %r\n' % (tmpname, realname))
600 ui.debug(b'restoring %r to %r\n' % (tmpname, realname))
603
601
604 if dirstate[realname] == b'n':
602 if dirstate[realname] == b'n':
605 # without normallookup, restoring timestamp
603 # without normallookup, restoring timestamp
606 # may cause partially committed files
604 # may cause partially committed files
607 # to be treated as unmodified
605 # to be treated as unmodified
608 dirstate.normallookup(realname)
606 dirstate.normallookup(realname)
609
607
610 # copystat=True here and above are a hack to trick any
608 # copystat=True here and above are a hack to trick any
611 # editors that have f open that we haven't modified them.
609 # editors that have f open that we haven't modified them.
612 #
610 #
613 # Also note that this racy as an editor could notice the
611 # Also note that this racy as an editor could notice the
614 # file's mtime before we've finished writing it.
612 # file's mtime before we've finished writing it.
615 util.copyfile(tmpname, repo.wjoin(realname), copystat=True)
613 util.copyfile(tmpname, repo.wjoin(realname), copystat=True)
616 os.unlink(tmpname)
614 os.unlink(tmpname)
617 if tobackup:
615 if tobackup:
618 os.rmdir(backupdir)
616 os.rmdir(backupdir)
619 except OSError:
617 except OSError:
620 pass
618 pass
621
619
622 def recordinwlock(ui, repo, message, match, opts):
620 def recordinwlock(ui, repo, message, match, opts):
623 with repo.wlock():
621 with repo.wlock():
624 return recordfunc(ui, repo, message, match, opts)
622 return recordfunc(ui, repo, message, match, opts)
625
623
626 return commit(ui, repo, recordinwlock, pats, opts)
624 return commit(ui, repo, recordinwlock, pats, opts)
627
625
628
626
629 class dirnode(object):
627 class dirnode(object):
630 """
628 """
631 Represent a directory in user working copy with information required for
629 Represent a directory in user working copy with information required for
632 the purpose of tersing its status.
630 the purpose of tersing its status.
633
631
634 path is the path to the directory, without a trailing '/'
632 path is the path to the directory, without a trailing '/'
635
633
636 statuses is a set of statuses of all files in this directory (this includes
634 statuses is a set of statuses of all files in this directory (this includes
637 all the files in all the subdirectories too)
635 all the files in all the subdirectories too)
638
636
639 files is a list of files which are direct child of this directory
637 files is a list of files which are direct child of this directory
640
638
641 subdirs is a dictionary of sub-directory name as the key and it's own
639 subdirs is a dictionary of sub-directory name as the key and it's own
642 dirnode object as the value
640 dirnode object as the value
643 """
641 """
644
642
645 def __init__(self, dirpath):
643 def __init__(self, dirpath):
646 self.path = dirpath
644 self.path = dirpath
647 self.statuses = set()
645 self.statuses = set()
648 self.files = []
646 self.files = []
649 self.subdirs = {}
647 self.subdirs = {}
650
648
651 def _addfileindir(self, filename, status):
649 def _addfileindir(self, filename, status):
652 """Add a file in this directory as a direct child."""
650 """Add a file in this directory as a direct child."""
653 self.files.append((filename, status))
651 self.files.append((filename, status))
654
652
655 def addfile(self, filename, status):
653 def addfile(self, filename, status):
656 """
654 """
657 Add a file to this directory or to its direct parent directory.
655 Add a file to this directory or to its direct parent directory.
658
656
659 If the file is not direct child of this directory, we traverse to the
657 If the file is not direct child of this directory, we traverse to the
660 directory of which this file is a direct child of and add the file
658 directory of which this file is a direct child of and add the file
661 there.
659 there.
662 """
660 """
663
661
664 # the filename contains a path separator, it means it's not the direct
662 # the filename contains a path separator, it means it's not the direct
665 # child of this directory
663 # child of this directory
666 if b'/' in filename:
664 if b'/' in filename:
667 subdir, filep = filename.split(b'/', 1)
665 subdir, filep = filename.split(b'/', 1)
668
666
669 # does the dirnode object for subdir exists
667 # does the dirnode object for subdir exists
670 if subdir not in self.subdirs:
668 if subdir not in self.subdirs:
671 subdirpath = pathutil.join(self.path, subdir)
669 subdirpath = pathutil.join(self.path, subdir)
672 self.subdirs[subdir] = dirnode(subdirpath)
670 self.subdirs[subdir] = dirnode(subdirpath)
673
671
674 # try adding the file in subdir
672 # try adding the file in subdir
675 self.subdirs[subdir].addfile(filep, status)
673 self.subdirs[subdir].addfile(filep, status)
676
674
677 else:
675 else:
678 self._addfileindir(filename, status)
676 self._addfileindir(filename, status)
679
677
680 if status not in self.statuses:
678 if status not in self.statuses:
681 self.statuses.add(status)
679 self.statuses.add(status)
682
680
683 def iterfilepaths(self):
681 def iterfilepaths(self):
684 """Yield (status, path) for files directly under this directory."""
682 """Yield (status, path) for files directly under this directory."""
685 for f, st in self.files:
683 for f, st in self.files:
686 yield st, pathutil.join(self.path, f)
684 yield st, pathutil.join(self.path, f)
687
685
688 def tersewalk(self, terseargs):
686 def tersewalk(self, terseargs):
689 """
687 """
690 Yield (status, path) obtained by processing the status of this
688 Yield (status, path) obtained by processing the status of this
691 dirnode.
689 dirnode.
692
690
693 terseargs is the string of arguments passed by the user with `--terse`
691 terseargs is the string of arguments passed by the user with `--terse`
694 flag.
692 flag.
695
693
696 Following are the cases which can happen:
694 Following are the cases which can happen:
697
695
698 1) All the files in the directory (including all the files in its
696 1) All the files in the directory (including all the files in its
699 subdirectories) share the same status and the user has asked us to terse
697 subdirectories) share the same status and the user has asked us to terse
700 that status. -> yield (status, dirpath). dirpath will end in '/'.
698 that status. -> yield (status, dirpath). dirpath will end in '/'.
701
699
702 2) Otherwise, we do following:
700 2) Otherwise, we do following:
703
701
704 a) Yield (status, filepath) for all the files which are in this
702 a) Yield (status, filepath) for all the files which are in this
705 directory (only the ones in this directory, not the subdirs)
703 directory (only the ones in this directory, not the subdirs)
706
704
707 b) Recurse the function on all the subdirectories of this
705 b) Recurse the function on all the subdirectories of this
708 directory
706 directory
709 """
707 """
710
708
711 if len(self.statuses) == 1:
709 if len(self.statuses) == 1:
712 onlyst = self.statuses.pop()
710 onlyst = self.statuses.pop()
713
711
714 # Making sure we terse only when the status abbreviation is
712 # Making sure we terse only when the status abbreviation is
715 # passed as terse argument
713 # passed as terse argument
716 if onlyst in terseargs:
714 if onlyst in terseargs:
717 yield onlyst, self.path + b'/'
715 yield onlyst, self.path + b'/'
718 return
716 return
719
717
720 # add the files to status list
718 # add the files to status list
721 for st, fpath in self.iterfilepaths():
719 for st, fpath in self.iterfilepaths():
722 yield st, fpath
720 yield st, fpath
723
721
724 # recurse on the subdirs
722 # recurse on the subdirs
725 for dirobj in self.subdirs.values():
723 for dirobj in self.subdirs.values():
726 for st, fpath in dirobj.tersewalk(terseargs):
724 for st, fpath in dirobj.tersewalk(terseargs):
727 yield st, fpath
725 yield st, fpath
728
726
729
727
730 def tersedir(statuslist, terseargs):
728 def tersedir(statuslist, terseargs):
731 """
729 """
732 Terse the status if all the files in a directory shares the same status.
730 Terse the status if all the files in a directory shares the same status.
733
731
734 statuslist is scmutil.status() object which contains a list of files for
732 statuslist is scmutil.status() object which contains a list of files for
735 each status.
733 each status.
736 terseargs is string which is passed by the user as the argument to `--terse`
734 terseargs is string which is passed by the user as the argument to `--terse`
737 flag.
735 flag.
738
736
739 The function makes a tree of objects of dirnode class, and at each node it
737 The function makes a tree of objects of dirnode class, and at each node it
740 stores the information required to know whether we can terse a certain
738 stores the information required to know whether we can terse a certain
741 directory or not.
739 directory or not.
742 """
740 """
743 # the order matters here as that is used to produce final list
741 # the order matters here as that is used to produce final list
744 allst = (b'm', b'a', b'r', b'd', b'u', b'i', b'c')
742 allst = (b'm', b'a', b'r', b'd', b'u', b'i', b'c')
745
743
746 # checking the argument validity
744 # checking the argument validity
747 for s in pycompat.bytestr(terseargs):
745 for s in pycompat.bytestr(terseargs):
748 if s not in allst:
746 if s not in allst:
749 raise error.Abort(_(b"'%s' not recognized") % s)
747 raise error.Abort(_(b"'%s' not recognized") % s)
750
748
751 # creating a dirnode object for the root of the repo
749 # creating a dirnode object for the root of the repo
752 rootobj = dirnode(b'')
750 rootobj = dirnode(b'')
753 pstatus = (
751 pstatus = (
754 b'modified',
752 b'modified',
755 b'added',
753 b'added',
756 b'deleted',
754 b'deleted',
757 b'clean',
755 b'clean',
758 b'unknown',
756 b'unknown',
759 b'ignored',
757 b'ignored',
760 b'removed',
758 b'removed',
761 )
759 )
762
760
763 tersedict = {}
761 tersedict = {}
764 for attrname in pstatus:
762 for attrname in pstatus:
765 statuschar = attrname[0:1]
763 statuschar = attrname[0:1]
766 for f in getattr(statuslist, attrname):
764 for f in getattr(statuslist, attrname):
767 rootobj.addfile(f, statuschar)
765 rootobj.addfile(f, statuschar)
768 tersedict[statuschar] = []
766 tersedict[statuschar] = []
769
767
770 # we won't be tersing the root dir, so add files in it
768 # we won't be tersing the root dir, so add files in it
771 for st, fpath in rootobj.iterfilepaths():
769 for st, fpath in rootobj.iterfilepaths():
772 tersedict[st].append(fpath)
770 tersedict[st].append(fpath)
773
771
774 # process each sub-directory and build tersedict
772 # process each sub-directory and build tersedict
775 for subdir in rootobj.subdirs.values():
773 for subdir in rootobj.subdirs.values():
776 for st, f in subdir.tersewalk(terseargs):
774 for st, f in subdir.tersewalk(terseargs):
777 tersedict[st].append(f)
775 tersedict[st].append(f)
778
776
779 tersedlist = []
777 tersedlist = []
780 for st in allst:
778 for st in allst:
781 tersedict[st].sort()
779 tersedict[st].sort()
782 tersedlist.append(tersedict[st])
780 tersedlist.append(tersedict[st])
783
781
784 return scmutil.status(*tersedlist)
782 return scmutil.status(*tersedlist)
785
783
786
784
787 def _commentlines(raw):
785 def _commentlines(raw):
788 '''Surround lineswith a comment char and a new line'''
786 '''Surround lineswith a comment char and a new line'''
789 lines = raw.splitlines()
787 lines = raw.splitlines()
790 commentedlines = [b'# %s' % line for line in lines]
788 commentedlines = [b'# %s' % line for line in lines]
791 return b'\n'.join(commentedlines) + b'\n'
789 return b'\n'.join(commentedlines) + b'\n'
792
790
793
791
794 @attr.s(frozen=True)
792 @attr.s(frozen=True)
795 class morestatus(object):
793 class morestatus(object):
796 reporoot = attr.ib()
794 reporoot = attr.ib()
797 unfinishedop = attr.ib()
795 unfinishedop = attr.ib()
798 unfinishedmsg = attr.ib()
796 unfinishedmsg = attr.ib()
799 inmergestate = attr.ib()
797 inmergestate = attr.ib()
800 unresolvedpaths = attr.ib()
798 unresolvedpaths = attr.ib()
801 _label = b'status.morestatus'
799 _label = b'status.morestatus'
802
800
803 def formatfile(self, path, fm):
801 def formatfile(self, path, fm):
804 if self.inmergestate and path in self.unresolvedpaths:
802 if self.inmergestate and path in self.unresolvedpaths:
805 fm.data(unresolved=True)
803 fm.data(unresolved=True)
806
804
807 def formatfooter(self, fm):
805 def formatfooter(self, fm):
808 fm.startitem()
806 fm.startitem()
809 fm.data(
807 fm.data(
810 itemtype=b'morestatus',
808 itemtype=b'morestatus',
811 unfinished=self.unfinishedop,
809 unfinished=self.unfinishedop,
812 unfinishedmsg=self.unfinishedmsg,
810 unfinishedmsg=self.unfinishedmsg,
813 )
811 )
814
812
815 statemsg = (
813 statemsg = (
816 _(b'The repository is in an unfinished *%s* state.')
814 _(b'The repository is in an unfinished *%s* state.')
817 % self.unfinishedop
815 % self.unfinishedop
818 )
816 )
819 fm.plain(b'%s\n' % _commentlines(statemsg), label=self._label)
817 fm.plain(b'%s\n' % _commentlines(statemsg), label=self._label)
820
818
821 self._formatconflicts(fm)
819 self._formatconflicts(fm)
822 if self.unfinishedmsg:
820 if self.unfinishedmsg:
823 fm.plain(
821 fm.plain(
824 b'%s\n' % _commentlines(self.unfinishedmsg), label=self._label
822 b'%s\n' % _commentlines(self.unfinishedmsg), label=self._label
825 )
823 )
826
824
827 def _formatconflicts(self, fm):
825 def _formatconflicts(self, fm):
828 if not self.inmergestate:
826 if not self.inmergestate:
829 return
827 return
830
828
831 if self.unresolvedpaths:
829 if self.unresolvedpaths:
832 mergeliststr = b'\n'.join(
830 mergeliststr = b'\n'.join(
833 [
831 [
834 b' %s'
832 b' %s'
835 % util.pathto(self.reporoot, encoding.getcwd(), path)
833 % util.pathto(self.reporoot, encoding.getcwd(), path)
836 for path in self.unresolvedpaths
834 for path in self.unresolvedpaths
837 ]
835 ]
838 )
836 )
839 msg = (
837 msg = (
840 _(
838 _(
841 '''Unresolved merge conflicts:
839 '''Unresolved merge conflicts:
842
840
843 %s
841 %s
844
842
845 To mark files as resolved: hg resolve --mark FILE'''
843 To mark files as resolved: hg resolve --mark FILE'''
846 )
844 )
847 % mergeliststr
845 % mergeliststr
848 )
846 )
849 else:
847 else:
850 msg = _(b'No unresolved merge conflicts.')
848 msg = _(b'No unresolved merge conflicts.')
851
849
852 fm.plain(b'%s\n' % _commentlines(msg), label=self._label)
850 fm.plain(b'%s\n' % _commentlines(msg), label=self._label)
853
851
854
852
855 def readmorestatus(repo):
853 def readmorestatus(repo):
856 """Returns a morestatus object if the repo has unfinished state."""
854 """Returns a morestatus object if the repo has unfinished state."""
857 statetuple = statemod.getrepostate(repo)
855 statetuple = statemod.getrepostate(repo)
858 if not statetuple:
856 if not statetuple:
859 return None
857 return None
860
858
861 unfinishedop, unfinishedmsg = statetuple
859 unfinishedop, unfinishedmsg = statetuple
862 mergestate = mergemod.mergestate.read(repo)
860 mergestate = mergemod.mergestate.read(repo)
863 unresolved = None
861 unresolved = None
864 if mergestate.active():
862 if mergestate.active():
865 unresolved = sorted(mergestate.unresolved())
863 unresolved = sorted(mergestate.unresolved())
866 return morestatus(
864 return morestatus(
867 repo.root,
865 repo.root,
868 unfinishedop,
866 unfinishedop,
869 unfinishedmsg,
867 unfinishedmsg,
870 unresolved is not None,
868 unresolved is not None,
871 unresolved,
869 unresolved,
872 )
870 )
873
871
874
872
875 def findpossible(cmd, table, strict=False):
873 def findpossible(cmd, table, strict=False):
876 """
874 """
877 Return cmd -> (aliases, command table entry)
875 Return cmd -> (aliases, command table entry)
878 for each matching command.
876 for each matching command.
879 Return debug commands (or their aliases) only if no normal command matches.
877 Return debug commands (or their aliases) only if no normal command matches.
880 """
878 """
881 choice = {}
879 choice = {}
882 debugchoice = {}
880 debugchoice = {}
883
881
884 if cmd in table:
882 if cmd in table:
885 # short-circuit exact matches, "log" alias beats "log|history"
883 # short-circuit exact matches, "log" alias beats "log|history"
886 keys = [cmd]
884 keys = [cmd]
887 else:
885 else:
888 keys = table.keys()
886 keys = table.keys()
889
887
890 allcmds = []
888 allcmds = []
891 for e in keys:
889 for e in keys:
892 aliases = parsealiases(e)
890 aliases = parsealiases(e)
893 allcmds.extend(aliases)
891 allcmds.extend(aliases)
894 found = None
892 found = None
895 if cmd in aliases:
893 if cmd in aliases:
896 found = cmd
894 found = cmd
897 elif not strict:
895 elif not strict:
898 for a in aliases:
896 for a in aliases:
899 if a.startswith(cmd):
897 if a.startswith(cmd):
900 found = a
898 found = a
901 break
899 break
902 if found is not None:
900 if found is not None:
903 if aliases[0].startswith(b"debug") or found.startswith(b"debug"):
901 if aliases[0].startswith(b"debug") or found.startswith(b"debug"):
904 debugchoice[found] = (aliases, table[e])
902 debugchoice[found] = (aliases, table[e])
905 else:
903 else:
906 choice[found] = (aliases, table[e])
904 choice[found] = (aliases, table[e])
907
905
908 if not choice and debugchoice:
906 if not choice and debugchoice:
909 choice = debugchoice
907 choice = debugchoice
910
908
911 return choice, allcmds
909 return choice, allcmds
912
910
913
911
914 def findcmd(cmd, table, strict=True):
912 def findcmd(cmd, table, strict=True):
915 """Return (aliases, command table entry) for command string."""
913 """Return (aliases, command table entry) for command string."""
916 choice, allcmds = findpossible(cmd, table, strict)
914 choice, allcmds = findpossible(cmd, table, strict)
917
915
918 if cmd in choice:
916 if cmd in choice:
919 return choice[cmd]
917 return choice[cmd]
920
918
921 if len(choice) > 1:
919 if len(choice) > 1:
922 clist = sorted(choice)
920 clist = sorted(choice)
923 raise error.AmbiguousCommand(cmd, clist)
921 raise error.AmbiguousCommand(cmd, clist)
924
922
925 if choice:
923 if choice:
926 return list(choice.values())[0]
924 return list(choice.values())[0]
927
925
928 raise error.UnknownCommand(cmd, allcmds)
926 raise error.UnknownCommand(cmd, allcmds)
929
927
930
928
931 def changebranch(ui, repo, revs, label):
929 def changebranch(ui, repo, revs, label):
932 """ Change the branch name of given revs to label """
930 """ Change the branch name of given revs to label """
933
931
934 with repo.wlock(), repo.lock(), repo.transaction(b'branches'):
932 with repo.wlock(), repo.lock(), repo.transaction(b'branches'):
935 # abort in case of uncommitted merge or dirty wdir
933 # abort in case of uncommitted merge or dirty wdir
936 bailifchanged(repo)
934 bailifchanged(repo)
937 revs = scmutil.revrange(repo, revs)
935 revs = scmutil.revrange(repo, revs)
938 if not revs:
936 if not revs:
939 raise error.Abort(b"empty revision set")
937 raise error.Abort(b"empty revision set")
940 roots = repo.revs(b'roots(%ld)', revs)
938 roots = repo.revs(b'roots(%ld)', revs)
941 if len(roots) > 1:
939 if len(roots) > 1:
942 raise error.Abort(
940 raise error.Abort(
943 _(b"cannot change branch of non-linear revisions")
941 _(b"cannot change branch of non-linear revisions")
944 )
942 )
945 rewriteutil.precheck(repo, revs, b'change branch of')
943 rewriteutil.precheck(repo, revs, b'change branch of')
946
944
947 root = repo[roots.first()]
945 root = repo[roots.first()]
948 rpb = {parent.branch() for parent in root.parents()}
946 rpb = {parent.branch() for parent in root.parents()}
949 if label not in rpb and label in repo.branchmap():
947 if label not in rpb and label in repo.branchmap():
950 raise error.Abort(_(b"a branch of the same name already exists"))
948 raise error.Abort(_(b"a branch of the same name already exists"))
951
949
952 if repo.revs(b'obsolete() and %ld', revs):
950 if repo.revs(b'obsolete() and %ld', revs):
953 raise error.Abort(
951 raise error.Abort(
954 _(b"cannot change branch of a obsolete changeset")
952 _(b"cannot change branch of a obsolete changeset")
955 )
953 )
956
954
957 # make sure only topological heads
955 # make sure only topological heads
958 if repo.revs(b'heads(%ld) - head()', revs):
956 if repo.revs(b'heads(%ld) - head()', revs):
959 raise error.Abort(_(b"cannot change branch in middle of a stack"))
957 raise error.Abort(_(b"cannot change branch in middle of a stack"))
960
958
961 replacements = {}
959 replacements = {}
962 # avoid import cycle mercurial.cmdutil -> mercurial.context ->
960 # avoid import cycle mercurial.cmdutil -> mercurial.context ->
963 # mercurial.subrepo -> mercurial.cmdutil
961 # mercurial.subrepo -> mercurial.cmdutil
964 from . import context
962 from . import context
965
963
966 for rev in revs:
964 for rev in revs:
967 ctx = repo[rev]
965 ctx = repo[rev]
968 oldbranch = ctx.branch()
966 oldbranch = ctx.branch()
969 # check if ctx has same branch
967 # check if ctx has same branch
970 if oldbranch == label:
968 if oldbranch == label:
971 continue
969 continue
972
970
973 def filectxfn(repo, newctx, path):
971 def filectxfn(repo, newctx, path):
974 try:
972 try:
975 return ctx[path]
973 return ctx[path]
976 except error.ManifestLookupError:
974 except error.ManifestLookupError:
977 return None
975 return None
978
976
979 ui.debug(
977 ui.debug(
980 b"changing branch of '%s' from '%s' to '%s'\n"
978 b"changing branch of '%s' from '%s' to '%s'\n"
981 % (hex(ctx.node()), oldbranch, label)
979 % (hex(ctx.node()), oldbranch, label)
982 )
980 )
983 extra = ctx.extra()
981 extra = ctx.extra()
984 extra[b'branch_change'] = hex(ctx.node())
982 extra[b'branch_change'] = hex(ctx.node())
985 # While changing branch of set of linear commits, make sure that
983 # While changing branch of set of linear commits, make sure that
986 # we base our commits on new parent rather than old parent which
984 # we base our commits on new parent rather than old parent which
987 # was obsoleted while changing the branch
985 # was obsoleted while changing the branch
988 p1 = ctx.p1().node()
986 p1 = ctx.p1().node()
989 p2 = ctx.p2().node()
987 p2 = ctx.p2().node()
990 if p1 in replacements:
988 if p1 in replacements:
991 p1 = replacements[p1][0]
989 p1 = replacements[p1][0]
992 if p2 in replacements:
990 if p2 in replacements:
993 p2 = replacements[p2][0]
991 p2 = replacements[p2][0]
994
992
995 mc = context.memctx(
993 mc = context.memctx(
996 repo,
994 repo,
997 (p1, p2),
995 (p1, p2),
998 ctx.description(),
996 ctx.description(),
999 ctx.files(),
997 ctx.files(),
1000 filectxfn,
998 filectxfn,
1001 user=ctx.user(),
999 user=ctx.user(),
1002 date=ctx.date(),
1000 date=ctx.date(),
1003 extra=extra,
1001 extra=extra,
1004 branch=label,
1002 branch=label,
1005 )
1003 )
1006
1004
1007 newnode = repo.commitctx(mc)
1005 newnode = repo.commitctx(mc)
1008 replacements[ctx.node()] = (newnode,)
1006 replacements[ctx.node()] = (newnode,)
1009 ui.debug(b'new node id is %s\n' % hex(newnode))
1007 ui.debug(b'new node id is %s\n' % hex(newnode))
1010
1008
1011 # create obsmarkers and move bookmarks
1009 # create obsmarkers and move bookmarks
1012 scmutil.cleanupnodes(
1010 scmutil.cleanupnodes(
1013 repo, replacements, b'branch-change', fixphase=True
1011 repo, replacements, b'branch-change', fixphase=True
1014 )
1012 )
1015
1013
1016 # move the working copy too
1014 # move the working copy too
1017 wctx = repo[None]
1015 wctx = repo[None]
1018 # in-progress merge is a bit too complex for now.
1016 # in-progress merge is a bit too complex for now.
1019 if len(wctx.parents()) == 1:
1017 if len(wctx.parents()) == 1:
1020 newid = replacements.get(wctx.p1().node())
1018 newid = replacements.get(wctx.p1().node())
1021 if newid is not None:
1019 if newid is not None:
1022 # avoid import cycle mercurial.cmdutil -> mercurial.hg ->
1020 # avoid import cycle mercurial.cmdutil -> mercurial.hg ->
1023 # mercurial.cmdutil
1021 # mercurial.cmdutil
1024 from . import hg
1022 from . import hg
1025
1023
1026 hg.update(repo, newid[0], quietempty=True)
1024 hg.update(repo, newid[0], quietempty=True)
1027
1025
1028 ui.status(_(b"changed branch on %d changesets\n") % len(replacements))
1026 ui.status(_(b"changed branch on %d changesets\n") % len(replacements))
1029
1027
1030
1028
1031 def findrepo(p):
1029 def findrepo(p):
1032 while not os.path.isdir(os.path.join(p, b".hg")):
1030 while not os.path.isdir(os.path.join(p, b".hg")):
1033 oldp, p = p, os.path.dirname(p)
1031 oldp, p = p, os.path.dirname(p)
1034 if p == oldp:
1032 if p == oldp:
1035 return None
1033 return None
1036
1034
1037 return p
1035 return p
1038
1036
1039
1037
1040 def bailifchanged(repo, merge=True, hint=None):
1038 def bailifchanged(repo, merge=True, hint=None):
1041 """ enforce the precondition that working directory must be clean.
1039 """ enforce the precondition that working directory must be clean.
1042
1040
1043 'merge' can be set to false if a pending uncommitted merge should be
1041 'merge' can be set to false if a pending uncommitted merge should be
1044 ignored (such as when 'update --check' runs).
1042 ignored (such as when 'update --check' runs).
1045
1043
1046 'hint' is the usual hint given to Abort exception.
1044 'hint' is the usual hint given to Abort exception.
1047 """
1045 """
1048
1046
1049 if merge and repo.dirstate.p2() != nullid:
1047 if merge and repo.dirstate.p2() != nullid:
1050 raise error.Abort(_(b'outstanding uncommitted merge'), hint=hint)
1048 raise error.Abort(_(b'outstanding uncommitted merge'), hint=hint)
1051 st = repo.status()
1049 st = repo.status()
1052 if st.modified or st.added or st.removed or st.deleted:
1050 if st.modified or st.added or st.removed or st.deleted:
1053 raise error.Abort(_(b'uncommitted changes'), hint=hint)
1051 raise error.Abort(_(b'uncommitted changes'), hint=hint)
1054 ctx = repo[None]
1052 ctx = repo[None]
1055 for s in sorted(ctx.substate):
1053 for s in sorted(ctx.substate):
1056 ctx.sub(s).bailifchanged(hint=hint)
1054 ctx.sub(s).bailifchanged(hint=hint)
1057
1055
1058
1056
1059 def logmessage(ui, opts):
1057 def logmessage(ui, opts):
1060 """ get the log message according to -m and -l option """
1058 """ get the log message according to -m and -l option """
1061 message = opts.get(b'message')
1059 message = opts.get(b'message')
1062 logfile = opts.get(b'logfile')
1060 logfile = opts.get(b'logfile')
1063
1061
1064 if message and logfile:
1062 if message and logfile:
1065 raise error.Abort(
1063 raise error.Abort(
1066 _(b'options --message and --logfile are mutually exclusive')
1064 _(b'options --message and --logfile are mutually exclusive')
1067 )
1065 )
1068 if not message and logfile:
1066 if not message and logfile:
1069 try:
1067 try:
1070 if isstdiofilename(logfile):
1068 if isstdiofilename(logfile):
1071 message = ui.fin.read()
1069 message = ui.fin.read()
1072 else:
1070 else:
1073 message = b'\n'.join(util.readfile(logfile).splitlines())
1071 message = b'\n'.join(util.readfile(logfile).splitlines())
1074 except IOError as inst:
1072 except IOError as inst:
1075 raise error.Abort(
1073 raise error.Abort(
1076 _(b"can't read commit message '%s': %s")
1074 _(b"can't read commit message '%s': %s")
1077 % (logfile, encoding.strtolocal(inst.strerror))
1075 % (logfile, encoding.strtolocal(inst.strerror))
1078 )
1076 )
1079 return message
1077 return message
1080
1078
1081
1079
1082 def mergeeditform(ctxorbool, baseformname):
1080 def mergeeditform(ctxorbool, baseformname):
1083 """return appropriate editform name (referencing a committemplate)
1081 """return appropriate editform name (referencing a committemplate)
1084
1082
1085 'ctxorbool' is either a ctx to be committed, or a bool indicating whether
1083 'ctxorbool' is either a ctx to be committed, or a bool indicating whether
1086 merging is committed.
1084 merging is committed.
1087
1085
1088 This returns baseformname with '.merge' appended if it is a merge,
1086 This returns baseformname with '.merge' appended if it is a merge,
1089 otherwise '.normal' is appended.
1087 otherwise '.normal' is appended.
1090 """
1088 """
1091 if isinstance(ctxorbool, bool):
1089 if isinstance(ctxorbool, bool):
1092 if ctxorbool:
1090 if ctxorbool:
1093 return baseformname + b".merge"
1091 return baseformname + b".merge"
1094 elif len(ctxorbool.parents()) > 1:
1092 elif len(ctxorbool.parents()) > 1:
1095 return baseformname + b".merge"
1093 return baseformname + b".merge"
1096
1094
1097 return baseformname + b".normal"
1095 return baseformname + b".normal"
1098
1096
1099
1097
1100 def getcommiteditor(
1098 def getcommiteditor(
1101 edit=False, finishdesc=None, extramsg=None, editform=b'', **opts
1099 edit=False, finishdesc=None, extramsg=None, editform=b'', **opts
1102 ):
1100 ):
1103 """get appropriate commit message editor according to '--edit' option
1101 """get appropriate commit message editor according to '--edit' option
1104
1102
1105 'finishdesc' is a function to be called with edited commit message
1103 'finishdesc' is a function to be called with edited commit message
1106 (= 'description' of the new changeset) just after editing, but
1104 (= 'description' of the new changeset) just after editing, but
1107 before checking empty-ness. It should return actual text to be
1105 before checking empty-ness. It should return actual text to be
1108 stored into history. This allows to change description before
1106 stored into history. This allows to change description before
1109 storing.
1107 storing.
1110
1108
1111 'extramsg' is a extra message to be shown in the editor instead of
1109 'extramsg' is a extra message to be shown in the editor instead of
1112 'Leave message empty to abort commit' line. 'HG: ' prefix and EOL
1110 'Leave message empty to abort commit' line. 'HG: ' prefix and EOL
1113 is automatically added.
1111 is automatically added.
1114
1112
1115 'editform' is a dot-separated list of names, to distinguish
1113 'editform' is a dot-separated list of names, to distinguish
1116 the purpose of commit text editing.
1114 the purpose of commit text editing.
1117
1115
1118 'getcommiteditor' returns 'commitforceeditor' regardless of
1116 'getcommiteditor' returns 'commitforceeditor' regardless of
1119 'edit', if one of 'finishdesc' or 'extramsg' is specified, because
1117 'edit', if one of 'finishdesc' or 'extramsg' is specified, because
1120 they are specific for usage in MQ.
1118 they are specific for usage in MQ.
1121 """
1119 """
1122 if edit or finishdesc or extramsg:
1120 if edit or finishdesc or extramsg:
1123 return lambda r, c, s: commitforceeditor(
1121 return lambda r, c, s: commitforceeditor(
1124 r, c, s, finishdesc=finishdesc, extramsg=extramsg, editform=editform
1122 r, c, s, finishdesc=finishdesc, extramsg=extramsg, editform=editform
1125 )
1123 )
1126 elif editform:
1124 elif editform:
1127 return lambda r, c, s: commiteditor(r, c, s, editform=editform)
1125 return lambda r, c, s: commiteditor(r, c, s, editform=editform)
1128 else:
1126 else:
1129 return commiteditor
1127 return commiteditor
1130
1128
1131
1129
1132 def _escapecommandtemplate(tmpl):
1130 def _escapecommandtemplate(tmpl):
1133 parts = []
1131 parts = []
1134 for typ, start, end in templater.scantemplate(tmpl, raw=True):
1132 for typ, start, end in templater.scantemplate(tmpl, raw=True):
1135 if typ == b'string':
1133 if typ == b'string':
1136 parts.append(stringutil.escapestr(tmpl[start:end]))
1134 parts.append(stringutil.escapestr(tmpl[start:end]))
1137 else:
1135 else:
1138 parts.append(tmpl[start:end])
1136 parts.append(tmpl[start:end])
1139 return b''.join(parts)
1137 return b''.join(parts)
1140
1138
1141
1139
1142 def rendercommandtemplate(ui, tmpl, props):
1140 def rendercommandtemplate(ui, tmpl, props):
1143 r"""Expand a literal template 'tmpl' in a way suitable for command line
1141 r"""Expand a literal template 'tmpl' in a way suitable for command line
1144
1142
1145 '\' in outermost string is not taken as an escape character because it
1143 '\' in outermost string is not taken as an escape character because it
1146 is a directory separator on Windows.
1144 is a directory separator on Windows.
1147
1145
1148 >>> from . import ui as uimod
1146 >>> from . import ui as uimod
1149 >>> ui = uimod.ui()
1147 >>> ui = uimod.ui()
1150 >>> rendercommandtemplate(ui, b'c:\\{path}', {b'path': b'foo'})
1148 >>> rendercommandtemplate(ui, b'c:\\{path}', {b'path': b'foo'})
1151 'c:\\foo'
1149 'c:\\foo'
1152 >>> rendercommandtemplate(ui, b'{"c:\\{path}"}', {'path': b'foo'})
1150 >>> rendercommandtemplate(ui, b'{"c:\\{path}"}', {'path': b'foo'})
1153 'c:{path}'
1151 'c:{path}'
1154 """
1152 """
1155 if not tmpl:
1153 if not tmpl:
1156 return tmpl
1154 return tmpl
1157 t = formatter.maketemplater(ui, _escapecommandtemplate(tmpl))
1155 t = formatter.maketemplater(ui, _escapecommandtemplate(tmpl))
1158 return t.renderdefault(props)
1156 return t.renderdefault(props)
1159
1157
1160
1158
1161 def rendertemplate(ctx, tmpl, props=None):
1159 def rendertemplate(ctx, tmpl, props=None):
1162 """Expand a literal template 'tmpl' byte-string against one changeset
1160 """Expand a literal template 'tmpl' byte-string against one changeset
1163
1161
1164 Each props item must be a stringify-able value or a callable returning
1162 Each props item must be a stringify-able value or a callable returning
1165 such value, i.e. no bare list nor dict should be passed.
1163 such value, i.e. no bare list nor dict should be passed.
1166 """
1164 """
1167 repo = ctx.repo()
1165 repo = ctx.repo()
1168 tres = formatter.templateresources(repo.ui, repo)
1166 tres = formatter.templateresources(repo.ui, repo)
1169 t = formatter.maketemplater(
1167 t = formatter.maketemplater(
1170 repo.ui, tmpl, defaults=templatekw.keywords, resources=tres
1168 repo.ui, tmpl, defaults=templatekw.keywords, resources=tres
1171 )
1169 )
1172 mapping = {b'ctx': ctx}
1170 mapping = {b'ctx': ctx}
1173 if props:
1171 if props:
1174 mapping.update(props)
1172 mapping.update(props)
1175 return t.renderdefault(mapping)
1173 return t.renderdefault(mapping)
1176
1174
1177
1175
1178 def _buildfntemplate(pat, total=None, seqno=None, revwidth=None, pathname=None):
1176 def _buildfntemplate(pat, total=None, seqno=None, revwidth=None, pathname=None):
1179 r"""Convert old-style filename format string to template string
1177 r"""Convert old-style filename format string to template string
1180
1178
1181 >>> _buildfntemplate(b'foo-%b-%n.patch', seqno=0)
1179 >>> _buildfntemplate(b'foo-%b-%n.patch', seqno=0)
1182 'foo-{reporoot|basename}-{seqno}.patch'
1180 'foo-{reporoot|basename}-{seqno}.patch'
1183 >>> _buildfntemplate(b'%R{tags % "{tag}"}%H')
1181 >>> _buildfntemplate(b'%R{tags % "{tag}"}%H')
1184 '{rev}{tags % "{tag}"}{node}'
1182 '{rev}{tags % "{tag}"}{node}'
1185
1183
1186 '\' in outermost strings has to be escaped because it is a directory
1184 '\' in outermost strings has to be escaped because it is a directory
1187 separator on Windows:
1185 separator on Windows:
1188
1186
1189 >>> _buildfntemplate(b'c:\\tmp\\%R\\%n.patch', seqno=0)
1187 >>> _buildfntemplate(b'c:\\tmp\\%R\\%n.patch', seqno=0)
1190 'c:\\\\tmp\\\\{rev}\\\\{seqno}.patch'
1188 'c:\\\\tmp\\\\{rev}\\\\{seqno}.patch'
1191 >>> _buildfntemplate(b'\\\\foo\\bar.patch')
1189 >>> _buildfntemplate(b'\\\\foo\\bar.patch')
1192 '\\\\\\\\foo\\\\bar.patch'
1190 '\\\\\\\\foo\\\\bar.patch'
1193 >>> _buildfntemplate(b'\\{tags % "{tag}"}')
1191 >>> _buildfntemplate(b'\\{tags % "{tag}"}')
1194 '\\\\{tags % "{tag}"}'
1192 '\\\\{tags % "{tag}"}'
1195
1193
1196 but inner strings follow the template rules (i.e. '\' is taken as an
1194 but inner strings follow the template rules (i.e. '\' is taken as an
1197 escape character):
1195 escape character):
1198
1196
1199 >>> _buildfntemplate(br'{"c:\tmp"}', seqno=0)
1197 >>> _buildfntemplate(br'{"c:\tmp"}', seqno=0)
1200 '{"c:\\tmp"}'
1198 '{"c:\\tmp"}'
1201 """
1199 """
1202 expander = {
1200 expander = {
1203 b'H': b'{node}',
1201 b'H': b'{node}',
1204 b'R': b'{rev}',
1202 b'R': b'{rev}',
1205 b'h': b'{node|short}',
1203 b'h': b'{node|short}',
1206 b'm': br'{sub(r"[^\w]", "_", desc|firstline)}',
1204 b'm': br'{sub(r"[^\w]", "_", desc|firstline)}',
1207 b'r': b'{if(revwidth, pad(rev, revwidth, "0", left=True), rev)}',
1205 b'r': b'{if(revwidth, pad(rev, revwidth, "0", left=True), rev)}',
1208 b'%': b'%',
1206 b'%': b'%',
1209 b'b': b'{reporoot|basename}',
1207 b'b': b'{reporoot|basename}',
1210 }
1208 }
1211 if total is not None:
1209 if total is not None:
1212 expander[b'N'] = b'{total}'
1210 expander[b'N'] = b'{total}'
1213 if seqno is not None:
1211 if seqno is not None:
1214 expander[b'n'] = b'{seqno}'
1212 expander[b'n'] = b'{seqno}'
1215 if total is not None and seqno is not None:
1213 if total is not None and seqno is not None:
1216 expander[b'n'] = b'{pad(seqno, total|stringify|count, "0", left=True)}'
1214 expander[b'n'] = b'{pad(seqno, total|stringify|count, "0", left=True)}'
1217 if pathname is not None:
1215 if pathname is not None:
1218 expander[b's'] = b'{pathname|basename}'
1216 expander[b's'] = b'{pathname|basename}'
1219 expander[b'd'] = b'{if(pathname|dirname, pathname|dirname, ".")}'
1217 expander[b'd'] = b'{if(pathname|dirname, pathname|dirname, ".")}'
1220 expander[b'p'] = b'{pathname}'
1218 expander[b'p'] = b'{pathname}'
1221
1219
1222 newname = []
1220 newname = []
1223 for typ, start, end in templater.scantemplate(pat, raw=True):
1221 for typ, start, end in templater.scantemplate(pat, raw=True):
1224 if typ != b'string':
1222 if typ != b'string':
1225 newname.append(pat[start:end])
1223 newname.append(pat[start:end])
1226 continue
1224 continue
1227 i = start
1225 i = start
1228 while i < end:
1226 while i < end:
1229 n = pat.find(b'%', i, end)
1227 n = pat.find(b'%', i, end)
1230 if n < 0:
1228 if n < 0:
1231 newname.append(stringutil.escapestr(pat[i:end]))
1229 newname.append(stringutil.escapestr(pat[i:end]))
1232 break
1230 break
1233 newname.append(stringutil.escapestr(pat[i:n]))
1231 newname.append(stringutil.escapestr(pat[i:n]))
1234 if n + 2 > end:
1232 if n + 2 > end:
1235 raise error.Abort(
1233 raise error.Abort(
1236 _(b"incomplete format spec in output filename")
1234 _(b"incomplete format spec in output filename")
1237 )
1235 )
1238 c = pat[n + 1 : n + 2]
1236 c = pat[n + 1 : n + 2]
1239 i = n + 2
1237 i = n + 2
1240 try:
1238 try:
1241 newname.append(expander[c])
1239 newname.append(expander[c])
1242 except KeyError:
1240 except KeyError:
1243 raise error.Abort(
1241 raise error.Abort(
1244 _(b"invalid format spec '%%%s' in output filename") % c
1242 _(b"invalid format spec '%%%s' in output filename") % c
1245 )
1243 )
1246 return b''.join(newname)
1244 return b''.join(newname)
1247
1245
1248
1246
1249 def makefilename(ctx, pat, **props):
1247 def makefilename(ctx, pat, **props):
1250 if not pat:
1248 if not pat:
1251 return pat
1249 return pat
1252 tmpl = _buildfntemplate(pat, **props)
1250 tmpl = _buildfntemplate(pat, **props)
1253 # BUG: alias expansion shouldn't be made against template fragments
1251 # BUG: alias expansion shouldn't be made against template fragments
1254 # rewritten from %-format strings, but we have no easy way to partially
1252 # rewritten from %-format strings, but we have no easy way to partially
1255 # disable the expansion.
1253 # disable the expansion.
1256 return rendertemplate(ctx, tmpl, pycompat.byteskwargs(props))
1254 return rendertemplate(ctx, tmpl, pycompat.byteskwargs(props))
1257
1255
1258
1256
1259 def isstdiofilename(pat):
1257 def isstdiofilename(pat):
1260 """True if the given pat looks like a filename denoting stdin/stdout"""
1258 """True if the given pat looks like a filename denoting stdin/stdout"""
1261 return not pat or pat == b'-'
1259 return not pat or pat == b'-'
1262
1260
1263
1261
1264 class _unclosablefile(object):
1262 class _unclosablefile(object):
1265 def __init__(self, fp):
1263 def __init__(self, fp):
1266 self._fp = fp
1264 self._fp = fp
1267
1265
1268 def close(self):
1266 def close(self):
1269 pass
1267 pass
1270
1268
1271 def __iter__(self):
1269 def __iter__(self):
1272 return iter(self._fp)
1270 return iter(self._fp)
1273
1271
1274 def __getattr__(self, attr):
1272 def __getattr__(self, attr):
1275 return getattr(self._fp, attr)
1273 return getattr(self._fp, attr)
1276
1274
1277 def __enter__(self):
1275 def __enter__(self):
1278 return self
1276 return self
1279
1277
1280 def __exit__(self, exc_type, exc_value, exc_tb):
1278 def __exit__(self, exc_type, exc_value, exc_tb):
1281 pass
1279 pass
1282
1280
1283
1281
1284 def makefileobj(ctx, pat, mode=b'wb', **props):
1282 def makefileobj(ctx, pat, mode=b'wb', **props):
1285 writable = mode not in (b'r', b'rb')
1283 writable = mode not in (b'r', b'rb')
1286
1284
1287 if isstdiofilename(pat):
1285 if isstdiofilename(pat):
1288 repo = ctx.repo()
1286 repo = ctx.repo()
1289 if writable:
1287 if writable:
1290 fp = repo.ui.fout
1288 fp = repo.ui.fout
1291 else:
1289 else:
1292 fp = repo.ui.fin
1290 fp = repo.ui.fin
1293 return _unclosablefile(fp)
1291 return _unclosablefile(fp)
1294 fn = makefilename(ctx, pat, **props)
1292 fn = makefilename(ctx, pat, **props)
1295 return open(fn, mode)
1293 return open(fn, mode)
1296
1294
1297
1295
1298 def openstorage(repo, cmd, file_, opts, returnrevlog=False):
1296 def openstorage(repo, cmd, file_, opts, returnrevlog=False):
1299 """opens the changelog, manifest, a filelog or a given revlog"""
1297 """opens the changelog, manifest, a filelog or a given revlog"""
1300 cl = opts[b'changelog']
1298 cl = opts[b'changelog']
1301 mf = opts[b'manifest']
1299 mf = opts[b'manifest']
1302 dir = opts[b'dir']
1300 dir = opts[b'dir']
1303 msg = None
1301 msg = None
1304 if cl and mf:
1302 if cl and mf:
1305 msg = _(b'cannot specify --changelog and --manifest at the same time')
1303 msg = _(b'cannot specify --changelog and --manifest at the same time')
1306 elif cl and dir:
1304 elif cl and dir:
1307 msg = _(b'cannot specify --changelog and --dir at the same time')
1305 msg = _(b'cannot specify --changelog and --dir at the same time')
1308 elif cl or mf or dir:
1306 elif cl or mf or dir:
1309 if file_:
1307 if file_:
1310 msg = _(b'cannot specify filename with --changelog or --manifest')
1308 msg = _(b'cannot specify filename with --changelog or --manifest')
1311 elif not repo:
1309 elif not repo:
1312 msg = _(
1310 msg = _(
1313 b'cannot specify --changelog or --manifest or --dir '
1311 b'cannot specify --changelog or --manifest or --dir '
1314 b'without a repository'
1312 b'without a repository'
1315 )
1313 )
1316 if msg:
1314 if msg:
1317 raise error.Abort(msg)
1315 raise error.Abort(msg)
1318
1316
1319 r = None
1317 r = None
1320 if repo:
1318 if repo:
1321 if cl:
1319 if cl:
1322 r = repo.unfiltered().changelog
1320 r = repo.unfiltered().changelog
1323 elif dir:
1321 elif dir:
1324 if b'treemanifest' not in repo.requirements:
1322 if b'treemanifest' not in repo.requirements:
1325 raise error.Abort(
1323 raise error.Abort(
1326 _(
1324 _(
1327 b"--dir can only be used on repos with "
1325 b"--dir can only be used on repos with "
1328 b"treemanifest enabled"
1326 b"treemanifest enabled"
1329 )
1327 )
1330 )
1328 )
1331 if not dir.endswith(b'/'):
1329 if not dir.endswith(b'/'):
1332 dir = dir + b'/'
1330 dir = dir + b'/'
1333 dirlog = repo.manifestlog.getstorage(dir)
1331 dirlog = repo.manifestlog.getstorage(dir)
1334 if len(dirlog):
1332 if len(dirlog):
1335 r = dirlog
1333 r = dirlog
1336 elif mf:
1334 elif mf:
1337 r = repo.manifestlog.getstorage(b'')
1335 r = repo.manifestlog.getstorage(b'')
1338 elif file_:
1336 elif file_:
1339 filelog = repo.file(file_)
1337 filelog = repo.file(file_)
1340 if len(filelog):
1338 if len(filelog):
1341 r = filelog
1339 r = filelog
1342
1340
1343 # Not all storage may be revlogs. If requested, try to return an actual
1341 # Not all storage may be revlogs. If requested, try to return an actual
1344 # revlog instance.
1342 # revlog instance.
1345 if returnrevlog:
1343 if returnrevlog:
1346 if isinstance(r, revlog.revlog):
1344 if isinstance(r, revlog.revlog):
1347 pass
1345 pass
1348 elif util.safehasattr(r, b'_revlog'):
1346 elif util.safehasattr(r, b'_revlog'):
1349 r = r._revlog # pytype: disable=attribute-error
1347 r = r._revlog # pytype: disable=attribute-error
1350 elif r is not None:
1348 elif r is not None:
1351 raise error.Abort(_(b'%r does not appear to be a revlog') % r)
1349 raise error.Abort(_(b'%r does not appear to be a revlog') % r)
1352
1350
1353 if not r:
1351 if not r:
1354 if not returnrevlog:
1352 if not returnrevlog:
1355 raise error.Abort(_(b'cannot give path to non-revlog'))
1353 raise error.Abort(_(b'cannot give path to non-revlog'))
1356
1354
1357 if not file_:
1355 if not file_:
1358 raise error.CommandError(cmd, _(b'invalid arguments'))
1356 raise error.CommandError(cmd, _(b'invalid arguments'))
1359 if not os.path.isfile(file_):
1357 if not os.path.isfile(file_):
1360 raise error.Abort(_(b"revlog '%s' not found") % file_)
1358 raise error.Abort(_(b"revlog '%s' not found") % file_)
1361 r = revlog.revlog(
1359 r = revlog.revlog(
1362 vfsmod.vfs(encoding.getcwd(), audit=False), file_[:-2] + b".i"
1360 vfsmod.vfs(encoding.getcwd(), audit=False), file_[:-2] + b".i"
1363 )
1361 )
1364 return r
1362 return r
1365
1363
1366
1364
1367 def openrevlog(repo, cmd, file_, opts):
1365 def openrevlog(repo, cmd, file_, opts):
1368 """Obtain a revlog backing storage of an item.
1366 """Obtain a revlog backing storage of an item.
1369
1367
1370 This is similar to ``openstorage()`` except it always returns a revlog.
1368 This is similar to ``openstorage()`` except it always returns a revlog.
1371
1369
1372 In most cases, a caller cares about the main storage object - not the
1370 In most cases, a caller cares about the main storage object - not the
1373 revlog backing it. Therefore, this function should only be used by code
1371 revlog backing it. Therefore, this function should only be used by code
1374 that needs to examine low-level revlog implementation details. e.g. debug
1372 that needs to examine low-level revlog implementation details. e.g. debug
1375 commands.
1373 commands.
1376 """
1374 """
1377 return openstorage(repo, cmd, file_, opts, returnrevlog=True)
1375 return openstorage(repo, cmd, file_, opts, returnrevlog=True)
1378
1376
1379
1377
1380 def copy(ui, repo, pats, opts, rename=False):
1378 def copy(ui, repo, pats, opts, rename=False):
1381 # called with the repo lock held
1379 # called with the repo lock held
1382 #
1380 #
1383 # hgsep => pathname that uses "/" to separate directories
1381 # hgsep => pathname that uses "/" to separate directories
1384 # ossep => pathname that uses os.sep to separate directories
1382 # ossep => pathname that uses os.sep to separate directories
1385 cwd = repo.getcwd()
1383 cwd = repo.getcwd()
1386 targets = {}
1384 targets = {}
1387 after = opts.get(b"after")
1385 after = opts.get(b"after")
1388 dryrun = opts.get(b"dry_run")
1386 dryrun = opts.get(b"dry_run")
1389 wctx = repo[None]
1387 wctx = repo[None]
1390
1388
1391 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
1389 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
1392
1390
1393 def walkpat(pat):
1391 def walkpat(pat):
1394 srcs = []
1392 srcs = []
1395 if after:
1393 if after:
1396 badstates = b'?'
1394 badstates = b'?'
1397 else:
1395 else:
1398 badstates = b'?r'
1396 badstates = b'?r'
1399 m = scmutil.match(wctx, [pat], opts, globbed=True)
1397 m = scmutil.match(wctx, [pat], opts, globbed=True)
1400 for abs in wctx.walk(m):
1398 for abs in wctx.walk(m):
1401 state = repo.dirstate[abs]
1399 state = repo.dirstate[abs]
1402 rel = uipathfn(abs)
1400 rel = uipathfn(abs)
1403 exact = m.exact(abs)
1401 exact = m.exact(abs)
1404 if state in badstates:
1402 if state in badstates:
1405 if exact and state == b'?':
1403 if exact and state == b'?':
1406 ui.warn(_(b'%s: not copying - file is not managed\n') % rel)
1404 ui.warn(_(b'%s: not copying - file is not managed\n') % rel)
1407 if exact and state == b'r':
1405 if exact and state == b'r':
1408 ui.warn(
1406 ui.warn(
1409 _(
1407 _(
1410 b'%s: not copying - file has been marked for'
1408 b'%s: not copying - file has been marked for'
1411 b' remove\n'
1409 b' remove\n'
1412 )
1410 )
1413 % rel
1411 % rel
1414 )
1412 )
1415 continue
1413 continue
1416 # abs: hgsep
1414 # abs: hgsep
1417 # rel: ossep
1415 # rel: ossep
1418 srcs.append((abs, rel, exact))
1416 srcs.append((abs, rel, exact))
1419 return srcs
1417 return srcs
1420
1418
1421 # abssrc: hgsep
1419 # abssrc: hgsep
1422 # relsrc: ossep
1420 # relsrc: ossep
1423 # otarget: ossep
1421 # otarget: ossep
1424 def copyfile(abssrc, relsrc, otarget, exact):
1422 def copyfile(abssrc, relsrc, otarget, exact):
1425 abstarget = pathutil.canonpath(repo.root, cwd, otarget)
1423 abstarget = pathutil.canonpath(repo.root, cwd, otarget)
1426 if b'/' in abstarget:
1424 if b'/' in abstarget:
1427 # We cannot normalize abstarget itself, this would prevent
1425 # We cannot normalize abstarget itself, this would prevent
1428 # case only renames, like a => A.
1426 # case only renames, like a => A.
1429 abspath, absname = abstarget.rsplit(b'/', 1)
1427 abspath, absname = abstarget.rsplit(b'/', 1)
1430 abstarget = repo.dirstate.normalize(abspath) + b'/' + absname
1428 abstarget = repo.dirstate.normalize(abspath) + b'/' + absname
1431 reltarget = repo.pathto(abstarget, cwd)
1429 reltarget = repo.pathto(abstarget, cwd)
1432 target = repo.wjoin(abstarget)
1430 target = repo.wjoin(abstarget)
1433 src = repo.wjoin(abssrc)
1431 src = repo.wjoin(abssrc)
1434 state = repo.dirstate[abstarget]
1432 state = repo.dirstate[abstarget]
1435
1433
1436 scmutil.checkportable(ui, abstarget)
1434 scmutil.checkportable(ui, abstarget)
1437
1435
1438 # check for collisions
1436 # check for collisions
1439 prevsrc = targets.get(abstarget)
1437 prevsrc = targets.get(abstarget)
1440 if prevsrc is not None:
1438 if prevsrc is not None:
1441 ui.warn(
1439 ui.warn(
1442 _(b'%s: not overwriting - %s collides with %s\n')
1440 _(b'%s: not overwriting - %s collides with %s\n')
1443 % (
1441 % (
1444 reltarget,
1442 reltarget,
1445 repo.pathto(abssrc, cwd),
1443 repo.pathto(abssrc, cwd),
1446 repo.pathto(prevsrc, cwd),
1444 repo.pathto(prevsrc, cwd),
1447 )
1445 )
1448 )
1446 )
1449 return True # report a failure
1447 return True # report a failure
1450
1448
1451 # check for overwrites
1449 # check for overwrites
1452 exists = os.path.lexists(target)
1450 exists = os.path.lexists(target)
1453 samefile = False
1451 samefile = False
1454 if exists and abssrc != abstarget:
1452 if exists and abssrc != abstarget:
1455 if repo.dirstate.normalize(abssrc) == repo.dirstate.normalize(
1453 if repo.dirstate.normalize(abssrc) == repo.dirstate.normalize(
1456 abstarget
1454 abstarget
1457 ):
1455 ):
1458 if not rename:
1456 if not rename:
1459 ui.warn(_(b"%s: can't copy - same file\n") % reltarget)
1457 ui.warn(_(b"%s: can't copy - same file\n") % reltarget)
1460 return True # report a failure
1458 return True # report a failure
1461 exists = False
1459 exists = False
1462 samefile = True
1460 samefile = True
1463
1461
1464 if not after and exists or after and state in b'mn':
1462 if not after and exists or after and state in b'mn':
1465 if not opts[b'force']:
1463 if not opts[b'force']:
1466 if state in b'mn':
1464 if state in b'mn':
1467 msg = _(b'%s: not overwriting - file already committed\n')
1465 msg = _(b'%s: not overwriting - file already committed\n')
1468 if after:
1466 if after:
1469 flags = b'--after --force'
1467 flags = b'--after --force'
1470 else:
1468 else:
1471 flags = b'--force'
1469 flags = b'--force'
1472 if rename:
1470 if rename:
1473 hint = (
1471 hint = (
1474 _(
1472 _(
1475 b"('hg rename %s' to replace the file by "
1473 b"('hg rename %s' to replace the file by "
1476 b'recording a rename)\n'
1474 b'recording a rename)\n'
1477 )
1475 )
1478 % flags
1476 % flags
1479 )
1477 )
1480 else:
1478 else:
1481 hint = (
1479 hint = (
1482 _(
1480 _(
1483 b"('hg copy %s' to replace the file by "
1481 b"('hg copy %s' to replace the file by "
1484 b'recording a copy)\n'
1482 b'recording a copy)\n'
1485 )
1483 )
1486 % flags
1484 % flags
1487 )
1485 )
1488 else:
1486 else:
1489 msg = _(b'%s: not overwriting - file exists\n')
1487 msg = _(b'%s: not overwriting - file exists\n')
1490 if rename:
1488 if rename:
1491 hint = _(
1489 hint = _(
1492 b"('hg rename --after' to record the rename)\n"
1490 b"('hg rename --after' to record the rename)\n"
1493 )
1491 )
1494 else:
1492 else:
1495 hint = _(b"('hg copy --after' to record the copy)\n")
1493 hint = _(b"('hg copy --after' to record the copy)\n")
1496 ui.warn(msg % reltarget)
1494 ui.warn(msg % reltarget)
1497 ui.warn(hint)
1495 ui.warn(hint)
1498 return True # report a failure
1496 return True # report a failure
1499
1497
1500 if after:
1498 if after:
1501 if not exists:
1499 if not exists:
1502 if rename:
1500 if rename:
1503 ui.warn(
1501 ui.warn(
1504 _(b'%s: not recording move - %s does not exist\n')
1502 _(b'%s: not recording move - %s does not exist\n')
1505 % (relsrc, reltarget)
1503 % (relsrc, reltarget)
1506 )
1504 )
1507 else:
1505 else:
1508 ui.warn(
1506 ui.warn(
1509 _(b'%s: not recording copy - %s does not exist\n')
1507 _(b'%s: not recording copy - %s does not exist\n')
1510 % (relsrc, reltarget)
1508 % (relsrc, reltarget)
1511 )
1509 )
1512 return True # report a failure
1510 return True # report a failure
1513 elif not dryrun:
1511 elif not dryrun:
1514 try:
1512 try:
1515 if exists:
1513 if exists:
1516 os.unlink(target)
1514 os.unlink(target)
1517 targetdir = os.path.dirname(target) or b'.'
1515 targetdir = os.path.dirname(target) or b'.'
1518 if not os.path.isdir(targetdir):
1516 if not os.path.isdir(targetdir):
1519 os.makedirs(targetdir)
1517 os.makedirs(targetdir)
1520 if samefile:
1518 if samefile:
1521 tmp = target + b"~hgrename"
1519 tmp = target + b"~hgrename"
1522 os.rename(src, tmp)
1520 os.rename(src, tmp)
1523 os.rename(tmp, target)
1521 os.rename(tmp, target)
1524 else:
1522 else:
1525 # Preserve stat info on renames, not on copies; this matches
1523 # Preserve stat info on renames, not on copies; this matches
1526 # Linux CLI behavior.
1524 # Linux CLI behavior.
1527 util.copyfile(src, target, copystat=rename)
1525 util.copyfile(src, target, copystat=rename)
1528 srcexists = True
1526 srcexists = True
1529 except IOError as inst:
1527 except IOError as inst:
1530 if inst.errno == errno.ENOENT:
1528 if inst.errno == errno.ENOENT:
1531 ui.warn(_(b'%s: deleted in working directory\n') % relsrc)
1529 ui.warn(_(b'%s: deleted in working directory\n') % relsrc)
1532 srcexists = False
1530 srcexists = False
1533 else:
1531 else:
1534 ui.warn(
1532 ui.warn(
1535 _(b'%s: cannot copy - %s\n')
1533 _(b'%s: cannot copy - %s\n')
1536 % (relsrc, encoding.strtolocal(inst.strerror))
1534 % (relsrc, encoding.strtolocal(inst.strerror))
1537 )
1535 )
1538 return True # report a failure
1536 return True # report a failure
1539
1537
1540 if ui.verbose or not exact:
1538 if ui.verbose or not exact:
1541 if rename:
1539 if rename:
1542 ui.status(_(b'moving %s to %s\n') % (relsrc, reltarget))
1540 ui.status(_(b'moving %s to %s\n') % (relsrc, reltarget))
1543 else:
1541 else:
1544 ui.status(_(b'copying %s to %s\n') % (relsrc, reltarget))
1542 ui.status(_(b'copying %s to %s\n') % (relsrc, reltarget))
1545
1543
1546 targets[abstarget] = abssrc
1544 targets[abstarget] = abssrc
1547
1545
1548 # fix up dirstate
1546 # fix up dirstate
1549 scmutil.dirstatecopy(
1547 scmutil.dirstatecopy(
1550 ui, repo, wctx, abssrc, abstarget, dryrun=dryrun, cwd=cwd
1548 ui, repo, wctx, abssrc, abstarget, dryrun=dryrun, cwd=cwd
1551 )
1549 )
1552 if rename and not dryrun:
1550 if rename and not dryrun:
1553 if not after and srcexists and not samefile:
1551 if not after and srcexists and not samefile:
1554 rmdir = repo.ui.configbool(b'experimental', b'removeemptydirs')
1552 rmdir = repo.ui.configbool(b'experimental', b'removeemptydirs')
1555 repo.wvfs.unlinkpath(abssrc, rmdir=rmdir)
1553 repo.wvfs.unlinkpath(abssrc, rmdir=rmdir)
1556 wctx.forget([abssrc])
1554 wctx.forget([abssrc])
1557
1555
1558 # pat: ossep
1556 # pat: ossep
1559 # dest ossep
1557 # dest ossep
1560 # srcs: list of (hgsep, hgsep, ossep, bool)
1558 # srcs: list of (hgsep, hgsep, ossep, bool)
1561 # return: function that takes hgsep and returns ossep
1559 # return: function that takes hgsep and returns ossep
1562 def targetpathfn(pat, dest, srcs):
1560 def targetpathfn(pat, dest, srcs):
1563 if os.path.isdir(pat):
1561 if os.path.isdir(pat):
1564 abspfx = pathutil.canonpath(repo.root, cwd, pat)
1562 abspfx = pathutil.canonpath(repo.root, cwd, pat)
1565 abspfx = util.localpath(abspfx)
1563 abspfx = util.localpath(abspfx)
1566 if destdirexists:
1564 if destdirexists:
1567 striplen = len(os.path.split(abspfx)[0])
1565 striplen = len(os.path.split(abspfx)[0])
1568 else:
1566 else:
1569 striplen = len(abspfx)
1567 striplen = len(abspfx)
1570 if striplen:
1568 if striplen:
1571 striplen += len(pycompat.ossep)
1569 striplen += len(pycompat.ossep)
1572 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
1570 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
1573 elif destdirexists:
1571 elif destdirexists:
1574 res = lambda p: os.path.join(
1572 res = lambda p: os.path.join(
1575 dest, os.path.basename(util.localpath(p))
1573 dest, os.path.basename(util.localpath(p))
1576 )
1574 )
1577 else:
1575 else:
1578 res = lambda p: dest
1576 res = lambda p: dest
1579 return res
1577 return res
1580
1578
1581 # pat: ossep
1579 # pat: ossep
1582 # dest ossep
1580 # dest ossep
1583 # srcs: list of (hgsep, hgsep, ossep, bool)
1581 # srcs: list of (hgsep, hgsep, ossep, bool)
1584 # return: function that takes hgsep and returns ossep
1582 # return: function that takes hgsep and returns ossep
1585 def targetpathafterfn(pat, dest, srcs):
1583 def targetpathafterfn(pat, dest, srcs):
1586 if matchmod.patkind(pat):
1584 if matchmod.patkind(pat):
1587 # a mercurial pattern
1585 # a mercurial pattern
1588 res = lambda p: os.path.join(
1586 res = lambda p: os.path.join(
1589 dest, os.path.basename(util.localpath(p))
1587 dest, os.path.basename(util.localpath(p))
1590 )
1588 )
1591 else:
1589 else:
1592 abspfx = pathutil.canonpath(repo.root, cwd, pat)
1590 abspfx = pathutil.canonpath(repo.root, cwd, pat)
1593 if len(abspfx) < len(srcs[0][0]):
1591 if len(abspfx) < len(srcs[0][0]):
1594 # A directory. Either the target path contains the last
1592 # A directory. Either the target path contains the last
1595 # component of the source path or it does not.
1593 # component of the source path or it does not.
1596 def evalpath(striplen):
1594 def evalpath(striplen):
1597 score = 0
1595 score = 0
1598 for s in srcs:
1596 for s in srcs:
1599 t = os.path.join(dest, util.localpath(s[0])[striplen:])
1597 t = os.path.join(dest, util.localpath(s[0])[striplen:])
1600 if os.path.lexists(t):
1598 if os.path.lexists(t):
1601 score += 1
1599 score += 1
1602 return score
1600 return score
1603
1601
1604 abspfx = util.localpath(abspfx)
1602 abspfx = util.localpath(abspfx)
1605 striplen = len(abspfx)
1603 striplen = len(abspfx)
1606 if striplen:
1604 if striplen:
1607 striplen += len(pycompat.ossep)
1605 striplen += len(pycompat.ossep)
1608 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
1606 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
1609 score = evalpath(striplen)
1607 score = evalpath(striplen)
1610 striplen1 = len(os.path.split(abspfx)[0])
1608 striplen1 = len(os.path.split(abspfx)[0])
1611 if striplen1:
1609 if striplen1:
1612 striplen1 += len(pycompat.ossep)
1610 striplen1 += len(pycompat.ossep)
1613 if evalpath(striplen1) > score:
1611 if evalpath(striplen1) > score:
1614 striplen = striplen1
1612 striplen = striplen1
1615 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
1613 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
1616 else:
1614 else:
1617 # a file
1615 # a file
1618 if destdirexists:
1616 if destdirexists:
1619 res = lambda p: os.path.join(
1617 res = lambda p: os.path.join(
1620 dest, os.path.basename(util.localpath(p))
1618 dest, os.path.basename(util.localpath(p))
1621 )
1619 )
1622 else:
1620 else:
1623 res = lambda p: dest
1621 res = lambda p: dest
1624 return res
1622 return res
1625
1623
1626 pats = scmutil.expandpats(pats)
1624 pats = scmutil.expandpats(pats)
1627 if not pats:
1625 if not pats:
1628 raise error.Abort(_(b'no source or destination specified'))
1626 raise error.Abort(_(b'no source or destination specified'))
1629 if len(pats) == 1:
1627 if len(pats) == 1:
1630 raise error.Abort(_(b'no destination specified'))
1628 raise error.Abort(_(b'no destination specified'))
1631 dest = pats.pop()
1629 dest = pats.pop()
1632 destdirexists = os.path.isdir(dest) and not os.path.islink(dest)
1630 destdirexists = os.path.isdir(dest) and not os.path.islink(dest)
1633 if not destdirexists:
1631 if not destdirexists:
1634 if len(pats) > 1 or matchmod.patkind(pats[0]):
1632 if len(pats) > 1 or matchmod.patkind(pats[0]):
1635 raise error.Abort(
1633 raise error.Abort(
1636 _(
1634 _(
1637 b'with multiple sources, destination must be an '
1635 b'with multiple sources, destination must be an '
1638 b'existing directory'
1636 b'existing directory'
1639 )
1637 )
1640 )
1638 )
1641 if util.endswithsep(dest):
1639 if util.endswithsep(dest):
1642 raise error.Abort(_(b'destination %s is not a directory') % dest)
1640 raise error.Abort(_(b'destination %s is not a directory') % dest)
1643
1641
1644 tfn = targetpathfn
1642 tfn = targetpathfn
1645 if after:
1643 if after:
1646 tfn = targetpathafterfn
1644 tfn = targetpathafterfn
1647 copylist = []
1645 copylist = []
1648 for pat in pats:
1646 for pat in pats:
1649 srcs = walkpat(pat)
1647 srcs = walkpat(pat)
1650 if not srcs:
1648 if not srcs:
1651 continue
1649 continue
1652 copylist.append((tfn(pat, dest, srcs), srcs))
1650 copylist.append((tfn(pat, dest, srcs), srcs))
1653 if not copylist:
1651 if not copylist:
1654 raise error.Abort(_(b'no files to copy'))
1652 raise error.Abort(_(b'no files to copy'))
1655
1653
1656 errors = 0
1654 errors = 0
1657 for targetpath, srcs in copylist:
1655 for targetpath, srcs in copylist:
1658 for abssrc, relsrc, exact in srcs:
1656 for abssrc, relsrc, exact in srcs:
1659 if copyfile(abssrc, relsrc, targetpath(abssrc), exact):
1657 if copyfile(abssrc, relsrc, targetpath(abssrc), exact):
1660 errors += 1
1658 errors += 1
1661
1659
1662 return errors != 0
1660 return errors != 0
1663
1661
1664
1662
1665 ## facility to let extension process additional data into an import patch
1663 ## facility to let extension process additional data into an import patch
1666 # list of identifier to be executed in order
1664 # list of identifier to be executed in order
1667 extrapreimport = [] # run before commit
1665 extrapreimport = [] # run before commit
1668 extrapostimport = [] # run after commit
1666 extrapostimport = [] # run after commit
1669 # mapping from identifier to actual import function
1667 # mapping from identifier to actual import function
1670 #
1668 #
1671 # 'preimport' are run before the commit is made and are provided the following
1669 # 'preimport' are run before the commit is made and are provided the following
1672 # arguments:
1670 # arguments:
1673 # - repo: the localrepository instance,
1671 # - repo: the localrepository instance,
1674 # - patchdata: data extracted from patch header (cf m.patch.patchheadermap),
1672 # - patchdata: data extracted from patch header (cf m.patch.patchheadermap),
1675 # - extra: the future extra dictionary of the changeset, please mutate it,
1673 # - extra: the future extra dictionary of the changeset, please mutate it,
1676 # - opts: the import options.
1674 # - opts: the import options.
1677 # XXX ideally, we would just pass an ctx ready to be computed, that would allow
1675 # XXX ideally, we would just pass an ctx ready to be computed, that would allow
1678 # mutation of in memory commit and more. Feel free to rework the code to get
1676 # mutation of in memory commit and more. Feel free to rework the code to get
1679 # there.
1677 # there.
1680 extrapreimportmap = {}
1678 extrapreimportmap = {}
1681 # 'postimport' are run after the commit is made and are provided the following
1679 # 'postimport' are run after the commit is made and are provided the following
1682 # argument:
1680 # argument:
1683 # - ctx: the changectx created by import.
1681 # - ctx: the changectx created by import.
1684 extrapostimportmap = {}
1682 extrapostimportmap = {}
1685
1683
1686
1684
1687 def tryimportone(ui, repo, patchdata, parents, opts, msgs, updatefunc):
1685 def tryimportone(ui, repo, patchdata, parents, opts, msgs, updatefunc):
1688 """Utility function used by commands.import to import a single patch
1686 """Utility function used by commands.import to import a single patch
1689
1687
1690 This function is explicitly defined here to help the evolve extension to
1688 This function is explicitly defined here to help the evolve extension to
1691 wrap this part of the import logic.
1689 wrap this part of the import logic.
1692
1690
1693 The API is currently a bit ugly because it a simple code translation from
1691 The API is currently a bit ugly because it a simple code translation from
1694 the import command. Feel free to make it better.
1692 the import command. Feel free to make it better.
1695
1693
1696 :patchdata: a dictionary containing parsed patch data (such as from
1694 :patchdata: a dictionary containing parsed patch data (such as from
1697 ``patch.extract()``)
1695 ``patch.extract()``)
1698 :parents: nodes that will be parent of the created commit
1696 :parents: nodes that will be parent of the created commit
1699 :opts: the full dict of option passed to the import command
1697 :opts: the full dict of option passed to the import command
1700 :msgs: list to save commit message to.
1698 :msgs: list to save commit message to.
1701 (used in case we need to save it when failing)
1699 (used in case we need to save it when failing)
1702 :updatefunc: a function that update a repo to a given node
1700 :updatefunc: a function that update a repo to a given node
1703 updatefunc(<repo>, <node>)
1701 updatefunc(<repo>, <node>)
1704 """
1702 """
1705 # avoid cycle context -> subrepo -> cmdutil
1703 # avoid cycle context -> subrepo -> cmdutil
1706 from . import context
1704 from . import context
1707
1705
1708 tmpname = patchdata.get(b'filename')
1706 tmpname = patchdata.get(b'filename')
1709 message = patchdata.get(b'message')
1707 message = patchdata.get(b'message')
1710 user = opts.get(b'user') or patchdata.get(b'user')
1708 user = opts.get(b'user') or patchdata.get(b'user')
1711 date = opts.get(b'date') or patchdata.get(b'date')
1709 date = opts.get(b'date') or patchdata.get(b'date')
1712 branch = patchdata.get(b'branch')
1710 branch = patchdata.get(b'branch')
1713 nodeid = patchdata.get(b'nodeid')
1711 nodeid = patchdata.get(b'nodeid')
1714 p1 = patchdata.get(b'p1')
1712 p1 = patchdata.get(b'p1')
1715 p2 = patchdata.get(b'p2')
1713 p2 = patchdata.get(b'p2')
1716
1714
1717 nocommit = opts.get(b'no_commit')
1715 nocommit = opts.get(b'no_commit')
1718 importbranch = opts.get(b'import_branch')
1716 importbranch = opts.get(b'import_branch')
1719 update = not opts.get(b'bypass')
1717 update = not opts.get(b'bypass')
1720 strip = opts[b"strip"]
1718 strip = opts[b"strip"]
1721 prefix = opts[b"prefix"]
1719 prefix = opts[b"prefix"]
1722 sim = float(opts.get(b'similarity') or 0)
1720 sim = float(opts.get(b'similarity') or 0)
1723
1721
1724 if not tmpname:
1722 if not tmpname:
1725 return None, None, False
1723 return None, None, False
1726
1724
1727 rejects = False
1725 rejects = False
1728
1726
1729 cmdline_message = logmessage(ui, opts)
1727 cmdline_message = logmessage(ui, opts)
1730 if cmdline_message:
1728 if cmdline_message:
1731 # pickup the cmdline msg
1729 # pickup the cmdline msg
1732 message = cmdline_message
1730 message = cmdline_message
1733 elif message:
1731 elif message:
1734 # pickup the patch msg
1732 # pickup the patch msg
1735 message = message.strip()
1733 message = message.strip()
1736 else:
1734 else:
1737 # launch the editor
1735 # launch the editor
1738 message = None
1736 message = None
1739 ui.debug(b'message:\n%s\n' % (message or b''))
1737 ui.debug(b'message:\n%s\n' % (message or b''))
1740
1738
1741 if len(parents) == 1:
1739 if len(parents) == 1:
1742 parents.append(repo[nullid])
1740 parents.append(repo[nullid])
1743 if opts.get(b'exact'):
1741 if opts.get(b'exact'):
1744 if not nodeid or not p1:
1742 if not nodeid or not p1:
1745 raise error.Abort(_(b'not a Mercurial patch'))
1743 raise error.Abort(_(b'not a Mercurial patch'))
1746 p1 = repo[p1]
1744 p1 = repo[p1]
1747 p2 = repo[p2 or nullid]
1745 p2 = repo[p2 or nullid]
1748 elif p2:
1746 elif p2:
1749 try:
1747 try:
1750 p1 = repo[p1]
1748 p1 = repo[p1]
1751 p2 = repo[p2]
1749 p2 = repo[p2]
1752 # Without any options, consider p2 only if the
1750 # Without any options, consider p2 only if the
1753 # patch is being applied on top of the recorded
1751 # patch is being applied on top of the recorded
1754 # first parent.
1752 # first parent.
1755 if p1 != parents[0]:
1753 if p1 != parents[0]:
1756 p1 = parents[0]
1754 p1 = parents[0]
1757 p2 = repo[nullid]
1755 p2 = repo[nullid]
1758 except error.RepoError:
1756 except error.RepoError:
1759 p1, p2 = parents
1757 p1, p2 = parents
1760 if p2.node() == nullid:
1758 if p2.node() == nullid:
1761 ui.warn(
1759 ui.warn(
1762 _(
1760 _(
1763 b"warning: import the patch as a normal revision\n"
1761 b"warning: import the patch as a normal revision\n"
1764 b"(use --exact to import the patch as a merge)\n"
1762 b"(use --exact to import the patch as a merge)\n"
1765 )
1763 )
1766 )
1764 )
1767 else:
1765 else:
1768 p1, p2 = parents
1766 p1, p2 = parents
1769
1767
1770 n = None
1768 n = None
1771 if update:
1769 if update:
1772 if p1 != parents[0]:
1770 if p1 != parents[0]:
1773 updatefunc(repo, p1.node())
1771 updatefunc(repo, p1.node())
1774 if p2 != parents[1]:
1772 if p2 != parents[1]:
1775 repo.setparents(p1.node(), p2.node())
1773 repo.setparents(p1.node(), p2.node())
1776
1774
1777 if opts.get(b'exact') or importbranch:
1775 if opts.get(b'exact') or importbranch:
1778 repo.dirstate.setbranch(branch or b'default')
1776 repo.dirstate.setbranch(branch or b'default')
1779
1777
1780 partial = opts.get(b'partial', False)
1778 partial = opts.get(b'partial', False)
1781 files = set()
1779 files = set()
1782 try:
1780 try:
1783 patch.patch(
1781 patch.patch(
1784 ui,
1782 ui,
1785 repo,
1783 repo,
1786 tmpname,
1784 tmpname,
1787 strip=strip,
1785 strip=strip,
1788 prefix=prefix,
1786 prefix=prefix,
1789 files=files,
1787 files=files,
1790 eolmode=None,
1788 eolmode=None,
1791 similarity=sim / 100.0,
1789 similarity=sim / 100.0,
1792 )
1790 )
1793 except error.PatchError as e:
1791 except error.PatchError as e:
1794 if not partial:
1792 if not partial:
1795 raise error.Abort(pycompat.bytestr(e))
1793 raise error.Abort(pycompat.bytestr(e))
1796 if partial:
1794 if partial:
1797 rejects = True
1795 rejects = True
1798
1796
1799 files = list(files)
1797 files = list(files)
1800 if nocommit:
1798 if nocommit:
1801 if message:
1799 if message:
1802 msgs.append(message)
1800 msgs.append(message)
1803 else:
1801 else:
1804 if opts.get(b'exact') or p2:
1802 if opts.get(b'exact') or p2:
1805 # If you got here, you either use --force and know what
1803 # If you got here, you either use --force and know what
1806 # you are doing or used --exact or a merge patch while
1804 # you are doing or used --exact or a merge patch while
1807 # being updated to its first parent.
1805 # being updated to its first parent.
1808 m = None
1806 m = None
1809 else:
1807 else:
1810 m = scmutil.matchfiles(repo, files or [])
1808 m = scmutil.matchfiles(repo, files or [])
1811 editform = mergeeditform(repo[None], b'import.normal')
1809 editform = mergeeditform(repo[None], b'import.normal')
1812 if opts.get(b'exact'):
1810 if opts.get(b'exact'):
1813 editor = None
1811 editor = None
1814 else:
1812 else:
1815 editor = getcommiteditor(
1813 editor = getcommiteditor(
1816 editform=editform, **pycompat.strkwargs(opts)
1814 editform=editform, **pycompat.strkwargs(opts)
1817 )
1815 )
1818 extra = {}
1816 extra = {}
1819 for idfunc in extrapreimport:
1817 for idfunc in extrapreimport:
1820 extrapreimportmap[idfunc](repo, patchdata, extra, opts)
1818 extrapreimportmap[idfunc](repo, patchdata, extra, opts)
1821 overrides = {}
1819 overrides = {}
1822 if partial:
1820 if partial:
1823 overrides[(b'ui', b'allowemptycommit')] = True
1821 overrides[(b'ui', b'allowemptycommit')] = True
1824 if opts.get(b'secret'):
1822 if opts.get(b'secret'):
1825 overrides[(b'phases', b'new-commit')] = b'secret'
1823 overrides[(b'phases', b'new-commit')] = b'secret'
1826 with repo.ui.configoverride(overrides, b'import'):
1824 with repo.ui.configoverride(overrides, b'import'):
1827 n = repo.commit(
1825 n = repo.commit(
1828 message, user, date, match=m, editor=editor, extra=extra
1826 message, user, date, match=m, editor=editor, extra=extra
1829 )
1827 )
1830 for idfunc in extrapostimport:
1828 for idfunc in extrapostimport:
1831 extrapostimportmap[idfunc](repo[n])
1829 extrapostimportmap[idfunc](repo[n])
1832 else:
1830 else:
1833 if opts.get(b'exact') or importbranch:
1831 if opts.get(b'exact') or importbranch:
1834 branch = branch or b'default'
1832 branch = branch or b'default'
1835 else:
1833 else:
1836 branch = p1.branch()
1834 branch = p1.branch()
1837 store = patch.filestore()
1835 store = patch.filestore()
1838 try:
1836 try:
1839 files = set()
1837 files = set()
1840 try:
1838 try:
1841 patch.patchrepo(
1839 patch.patchrepo(
1842 ui,
1840 ui,
1843 repo,
1841 repo,
1844 p1,
1842 p1,
1845 store,
1843 store,
1846 tmpname,
1844 tmpname,
1847 strip,
1845 strip,
1848 prefix,
1846 prefix,
1849 files,
1847 files,
1850 eolmode=None,
1848 eolmode=None,
1851 )
1849 )
1852 except error.PatchError as e:
1850 except error.PatchError as e:
1853 raise error.Abort(stringutil.forcebytestr(e))
1851 raise error.Abort(stringutil.forcebytestr(e))
1854 if opts.get(b'exact'):
1852 if opts.get(b'exact'):
1855 editor = None
1853 editor = None
1856 else:
1854 else:
1857 editor = getcommiteditor(editform=b'import.bypass')
1855 editor = getcommiteditor(editform=b'import.bypass')
1858 memctx = context.memctx(
1856 memctx = context.memctx(
1859 repo,
1857 repo,
1860 (p1.node(), p2.node()),
1858 (p1.node(), p2.node()),
1861 message,
1859 message,
1862 files=files,
1860 files=files,
1863 filectxfn=store,
1861 filectxfn=store,
1864 user=user,
1862 user=user,
1865 date=date,
1863 date=date,
1866 branch=branch,
1864 branch=branch,
1867 editor=editor,
1865 editor=editor,
1868 )
1866 )
1869 n = memctx.commit()
1867 n = memctx.commit()
1870 finally:
1868 finally:
1871 store.close()
1869 store.close()
1872 if opts.get(b'exact') and nocommit:
1870 if opts.get(b'exact') and nocommit:
1873 # --exact with --no-commit is still useful in that it does merge
1871 # --exact with --no-commit is still useful in that it does merge
1874 # and branch bits
1872 # and branch bits
1875 ui.warn(_(b"warning: can't check exact import with --no-commit\n"))
1873 ui.warn(_(b"warning: can't check exact import with --no-commit\n"))
1876 elif opts.get(b'exact') and (not n or hex(n) != nodeid):
1874 elif opts.get(b'exact') and (not n or hex(n) != nodeid):
1877 raise error.Abort(_(b'patch is damaged or loses information'))
1875 raise error.Abort(_(b'patch is damaged or loses information'))
1878 msg = _(b'applied to working directory')
1876 msg = _(b'applied to working directory')
1879 if n:
1877 if n:
1880 # i18n: refers to a short changeset id
1878 # i18n: refers to a short changeset id
1881 msg = _(b'created %s') % short(n)
1879 msg = _(b'created %s') % short(n)
1882 return msg, n, rejects
1880 return msg, n, rejects
1883
1881
1884
1882
1885 # facility to let extensions include additional data in an exported patch
1883 # facility to let extensions include additional data in an exported patch
1886 # list of identifiers to be executed in order
1884 # list of identifiers to be executed in order
1887 extraexport = []
1885 extraexport = []
1888 # mapping from identifier to actual export function
1886 # mapping from identifier to actual export function
1889 # function as to return a string to be added to the header or None
1887 # function as to return a string to be added to the header or None
1890 # it is given two arguments (sequencenumber, changectx)
1888 # it is given two arguments (sequencenumber, changectx)
1891 extraexportmap = {}
1889 extraexportmap = {}
1892
1890
1893
1891
1894 def _exportsingle(repo, ctx, fm, match, switch_parent, seqno, diffopts):
1892 def _exportsingle(repo, ctx, fm, match, switch_parent, seqno, diffopts):
1895 node = scmutil.binnode(ctx)
1893 node = scmutil.binnode(ctx)
1896 parents = [p.node() for p in ctx.parents() if p]
1894 parents = [p.node() for p in ctx.parents() if p]
1897 branch = ctx.branch()
1895 branch = ctx.branch()
1898 if switch_parent:
1896 if switch_parent:
1899 parents.reverse()
1897 parents.reverse()
1900
1898
1901 if parents:
1899 if parents:
1902 prev = parents[0]
1900 prev = parents[0]
1903 else:
1901 else:
1904 prev = nullid
1902 prev = nullid
1905
1903
1906 fm.context(ctx=ctx)
1904 fm.context(ctx=ctx)
1907 fm.plain(b'# HG changeset patch\n')
1905 fm.plain(b'# HG changeset patch\n')
1908 fm.write(b'user', b'# User %s\n', ctx.user())
1906 fm.write(b'user', b'# User %s\n', ctx.user())
1909 fm.plain(b'# Date %d %d\n' % ctx.date())
1907 fm.plain(b'# Date %d %d\n' % ctx.date())
1910 fm.write(b'date', b'# %s\n', fm.formatdate(ctx.date()))
1908 fm.write(b'date', b'# %s\n', fm.formatdate(ctx.date()))
1911 fm.condwrite(
1909 fm.condwrite(
1912 branch and branch != b'default', b'branch', b'# Branch %s\n', branch
1910 branch and branch != b'default', b'branch', b'# Branch %s\n', branch
1913 )
1911 )
1914 fm.write(b'node', b'# Node ID %s\n', hex(node))
1912 fm.write(b'node', b'# Node ID %s\n', hex(node))
1915 fm.plain(b'# Parent %s\n' % hex(prev))
1913 fm.plain(b'# Parent %s\n' % hex(prev))
1916 if len(parents) > 1:
1914 if len(parents) > 1:
1917 fm.plain(b'# Parent %s\n' % hex(parents[1]))
1915 fm.plain(b'# Parent %s\n' % hex(parents[1]))
1918 fm.data(parents=fm.formatlist(pycompat.maplist(hex, parents), name=b'node'))
1916 fm.data(parents=fm.formatlist(pycompat.maplist(hex, parents), name=b'node'))
1919
1917
1920 # TODO: redesign extraexportmap function to support formatter
1918 # TODO: redesign extraexportmap function to support formatter
1921 for headerid in extraexport:
1919 for headerid in extraexport:
1922 header = extraexportmap[headerid](seqno, ctx)
1920 header = extraexportmap[headerid](seqno, ctx)
1923 if header is not None:
1921 if header is not None:
1924 fm.plain(b'# %s\n' % header)
1922 fm.plain(b'# %s\n' % header)
1925
1923
1926 fm.write(b'desc', b'%s\n', ctx.description().rstrip())
1924 fm.write(b'desc', b'%s\n', ctx.description().rstrip())
1927 fm.plain(b'\n')
1925 fm.plain(b'\n')
1928
1926
1929 if fm.isplain():
1927 if fm.isplain():
1930 chunkiter = patch.diffui(repo, prev, node, match, opts=diffopts)
1928 chunkiter = patch.diffui(repo, prev, node, match, opts=diffopts)
1931 for chunk, label in chunkiter:
1929 for chunk, label in chunkiter:
1932 fm.plain(chunk, label=label)
1930 fm.plain(chunk, label=label)
1933 else:
1931 else:
1934 chunkiter = patch.diff(repo, prev, node, match, opts=diffopts)
1932 chunkiter = patch.diff(repo, prev, node, match, opts=diffopts)
1935 # TODO: make it structured?
1933 # TODO: make it structured?
1936 fm.data(diff=b''.join(chunkiter))
1934 fm.data(diff=b''.join(chunkiter))
1937
1935
1938
1936
1939 def _exportfile(repo, revs, fm, dest, switch_parent, diffopts, match):
1937 def _exportfile(repo, revs, fm, dest, switch_parent, diffopts, match):
1940 """Export changesets to stdout or a single file"""
1938 """Export changesets to stdout or a single file"""
1941 for seqno, rev in enumerate(revs, 1):
1939 for seqno, rev in enumerate(revs, 1):
1942 ctx = repo[rev]
1940 ctx = repo[rev]
1943 if not dest.startswith(b'<'):
1941 if not dest.startswith(b'<'):
1944 repo.ui.note(b"%s\n" % dest)
1942 repo.ui.note(b"%s\n" % dest)
1945 fm.startitem()
1943 fm.startitem()
1946 _exportsingle(repo, ctx, fm, match, switch_parent, seqno, diffopts)
1944 _exportsingle(repo, ctx, fm, match, switch_parent, seqno, diffopts)
1947
1945
1948
1946
1949 def _exportfntemplate(
1947 def _exportfntemplate(
1950 repo, revs, basefm, fntemplate, switch_parent, diffopts, match
1948 repo, revs, basefm, fntemplate, switch_parent, diffopts, match
1951 ):
1949 ):
1952 """Export changesets to possibly multiple files"""
1950 """Export changesets to possibly multiple files"""
1953 total = len(revs)
1951 total = len(revs)
1954 revwidth = max(len(str(rev)) for rev in revs)
1952 revwidth = max(len(str(rev)) for rev in revs)
1955 filemap = util.sortdict() # filename: [(seqno, rev), ...]
1953 filemap = util.sortdict() # filename: [(seqno, rev), ...]
1956
1954
1957 for seqno, rev in enumerate(revs, 1):
1955 for seqno, rev in enumerate(revs, 1):
1958 ctx = repo[rev]
1956 ctx = repo[rev]
1959 dest = makefilename(
1957 dest = makefilename(
1960 ctx, fntemplate, total=total, seqno=seqno, revwidth=revwidth
1958 ctx, fntemplate, total=total, seqno=seqno, revwidth=revwidth
1961 )
1959 )
1962 filemap.setdefault(dest, []).append((seqno, rev))
1960 filemap.setdefault(dest, []).append((seqno, rev))
1963
1961
1964 for dest in filemap:
1962 for dest in filemap:
1965 with formatter.maybereopen(basefm, dest) as fm:
1963 with formatter.maybereopen(basefm, dest) as fm:
1966 repo.ui.note(b"%s\n" % dest)
1964 repo.ui.note(b"%s\n" % dest)
1967 for seqno, rev in filemap[dest]:
1965 for seqno, rev in filemap[dest]:
1968 fm.startitem()
1966 fm.startitem()
1969 ctx = repo[rev]
1967 ctx = repo[rev]
1970 _exportsingle(
1968 _exportsingle(
1971 repo, ctx, fm, match, switch_parent, seqno, diffopts
1969 repo, ctx, fm, match, switch_parent, seqno, diffopts
1972 )
1970 )
1973
1971
1974
1972
1975 def _prefetchchangedfiles(repo, revs, match):
1973 def _prefetchchangedfiles(repo, revs, match):
1976 allfiles = set()
1974 allfiles = set()
1977 for rev in revs:
1975 for rev in revs:
1978 for file in repo[rev].files():
1976 for file in repo[rev].files():
1979 if not match or match(file):
1977 if not match or match(file):
1980 allfiles.add(file)
1978 allfiles.add(file)
1981 scmutil.prefetchfiles(repo, revs, scmutil.matchfiles(repo, allfiles))
1979 scmutil.prefetchfiles(repo, revs, scmutil.matchfiles(repo, allfiles))
1982
1980
1983
1981
1984 def export(
1982 def export(
1985 repo,
1983 repo,
1986 revs,
1984 revs,
1987 basefm,
1985 basefm,
1988 fntemplate=b'hg-%h.patch',
1986 fntemplate=b'hg-%h.patch',
1989 switch_parent=False,
1987 switch_parent=False,
1990 opts=None,
1988 opts=None,
1991 match=None,
1989 match=None,
1992 ):
1990 ):
1993 '''export changesets as hg patches
1991 '''export changesets as hg patches
1994
1992
1995 Args:
1993 Args:
1996 repo: The repository from which we're exporting revisions.
1994 repo: The repository from which we're exporting revisions.
1997 revs: A list of revisions to export as revision numbers.
1995 revs: A list of revisions to export as revision numbers.
1998 basefm: A formatter to which patches should be written.
1996 basefm: A formatter to which patches should be written.
1999 fntemplate: An optional string to use for generating patch file names.
1997 fntemplate: An optional string to use for generating patch file names.
2000 switch_parent: If True, show diffs against second parent when not nullid.
1998 switch_parent: If True, show diffs against second parent when not nullid.
2001 Default is false, which always shows diff against p1.
1999 Default is false, which always shows diff against p1.
2002 opts: diff options to use for generating the patch.
2000 opts: diff options to use for generating the patch.
2003 match: If specified, only export changes to files matching this matcher.
2001 match: If specified, only export changes to files matching this matcher.
2004
2002
2005 Returns:
2003 Returns:
2006 Nothing.
2004 Nothing.
2007
2005
2008 Side Effect:
2006 Side Effect:
2009 "HG Changeset Patch" data is emitted to one of the following
2007 "HG Changeset Patch" data is emitted to one of the following
2010 destinations:
2008 destinations:
2011 fntemplate specified: Each rev is written to a unique file named using
2009 fntemplate specified: Each rev is written to a unique file named using
2012 the given template.
2010 the given template.
2013 Otherwise: All revs will be written to basefm.
2011 Otherwise: All revs will be written to basefm.
2014 '''
2012 '''
2015 _prefetchchangedfiles(repo, revs, match)
2013 _prefetchchangedfiles(repo, revs, match)
2016
2014
2017 if not fntemplate:
2015 if not fntemplate:
2018 _exportfile(
2016 _exportfile(
2019 repo, revs, basefm, b'<unnamed>', switch_parent, opts, match
2017 repo, revs, basefm, b'<unnamed>', switch_parent, opts, match
2020 )
2018 )
2021 else:
2019 else:
2022 _exportfntemplate(
2020 _exportfntemplate(
2023 repo, revs, basefm, fntemplate, switch_parent, opts, match
2021 repo, revs, basefm, fntemplate, switch_parent, opts, match
2024 )
2022 )
2025
2023
2026
2024
2027 def exportfile(repo, revs, fp, switch_parent=False, opts=None, match=None):
2025 def exportfile(repo, revs, fp, switch_parent=False, opts=None, match=None):
2028 """Export changesets to the given file stream"""
2026 """Export changesets to the given file stream"""
2029 _prefetchchangedfiles(repo, revs, match)
2027 _prefetchchangedfiles(repo, revs, match)
2030
2028
2031 dest = getattr(fp, 'name', b'<unnamed>')
2029 dest = getattr(fp, 'name', b'<unnamed>')
2032 with formatter.formatter(repo.ui, fp, b'export', {}) as fm:
2030 with formatter.formatter(repo.ui, fp, b'export', {}) as fm:
2033 _exportfile(repo, revs, fm, dest, switch_parent, opts, match)
2031 _exportfile(repo, revs, fm, dest, switch_parent, opts, match)
2034
2032
2035
2033
2036 def showmarker(fm, marker, index=None):
2034 def showmarker(fm, marker, index=None):
2037 """utility function to display obsolescence marker in a readable way
2035 """utility function to display obsolescence marker in a readable way
2038
2036
2039 To be used by debug function."""
2037 To be used by debug function."""
2040 if index is not None:
2038 if index is not None:
2041 fm.write(b'index', b'%i ', index)
2039 fm.write(b'index', b'%i ', index)
2042 fm.write(b'prednode', b'%s ', hex(marker.prednode()))
2040 fm.write(b'prednode', b'%s ', hex(marker.prednode()))
2043 succs = marker.succnodes()
2041 succs = marker.succnodes()
2044 fm.condwrite(
2042 fm.condwrite(
2045 succs,
2043 succs,
2046 b'succnodes',
2044 b'succnodes',
2047 b'%s ',
2045 b'%s ',
2048 fm.formatlist(map(hex, succs), name=b'node'),
2046 fm.formatlist(map(hex, succs), name=b'node'),
2049 )
2047 )
2050 fm.write(b'flag', b'%X ', marker.flags())
2048 fm.write(b'flag', b'%X ', marker.flags())
2051 parents = marker.parentnodes()
2049 parents = marker.parentnodes()
2052 if parents is not None:
2050 if parents is not None:
2053 fm.write(
2051 fm.write(
2054 b'parentnodes',
2052 b'parentnodes',
2055 b'{%s} ',
2053 b'{%s} ',
2056 fm.formatlist(map(hex, parents), name=b'node', sep=b', '),
2054 fm.formatlist(map(hex, parents), name=b'node', sep=b', '),
2057 )
2055 )
2058 fm.write(b'date', b'(%s) ', fm.formatdate(marker.date()))
2056 fm.write(b'date', b'(%s) ', fm.formatdate(marker.date()))
2059 meta = marker.metadata().copy()
2057 meta = marker.metadata().copy()
2060 meta.pop(b'date', None)
2058 meta.pop(b'date', None)
2061 smeta = pycompat.rapply(pycompat.maybebytestr, meta)
2059 smeta = pycompat.rapply(pycompat.maybebytestr, meta)
2062 fm.write(
2060 fm.write(
2063 b'metadata', b'{%s}', fm.formatdict(smeta, fmt=b'%r: %r', sep=b', ')
2061 b'metadata', b'{%s}', fm.formatdict(smeta, fmt=b'%r: %r', sep=b', ')
2064 )
2062 )
2065 fm.plain(b'\n')
2063 fm.plain(b'\n')
2066
2064
2067
2065
2068 def finddate(ui, repo, date):
2066 def finddate(ui, repo, date):
2069 """Find the tipmost changeset that matches the given date spec"""
2067 """Find the tipmost changeset that matches the given date spec"""
2070
2068
2071 df = dateutil.matchdate(date)
2069 df = dateutil.matchdate(date)
2072 m = scmutil.matchall(repo)
2070 m = scmutil.matchall(repo)
2073 results = {}
2071 results = {}
2074
2072
2075 def prep(ctx, fns):
2073 def prep(ctx, fns):
2076 d = ctx.date()
2074 d = ctx.date()
2077 if df(d[0]):
2075 if df(d[0]):
2078 results[ctx.rev()] = d
2076 results[ctx.rev()] = d
2079
2077
2080 for ctx in walkchangerevs(repo, m, {b'rev': None}, prep):
2078 for ctx in walkchangerevs(repo, m, {b'rev': None}, prep):
2081 rev = ctx.rev()
2079 rev = ctx.rev()
2082 if rev in results:
2080 if rev in results:
2083 ui.status(
2081 ui.status(
2084 _(b"found revision %d from %s\n")
2082 _(b"found revision %d from %s\n")
2085 % (rev, dateutil.datestr(results[rev]))
2083 % (rev, dateutil.datestr(results[rev]))
2086 )
2084 )
2087 return b'%d' % rev
2085 return b'%d' % rev
2088
2086
2089 raise error.Abort(_(b"revision matching date not found"))
2087 raise error.Abort(_(b"revision matching date not found"))
2090
2088
2091
2089
2092 def increasingwindows(windowsize=8, sizelimit=512):
2090 def increasingwindows(windowsize=8, sizelimit=512):
2093 while True:
2091 while True:
2094 yield windowsize
2092 yield windowsize
2095 if windowsize < sizelimit:
2093 if windowsize < sizelimit:
2096 windowsize *= 2
2094 windowsize *= 2
2097
2095
2098
2096
2099 def _walkrevs(repo, opts):
2097 def _walkrevs(repo, opts):
2100 # Default --rev value depends on --follow but --follow behavior
2098 # Default --rev value depends on --follow but --follow behavior
2101 # depends on revisions resolved from --rev...
2099 # depends on revisions resolved from --rev...
2102 follow = opts.get(b'follow') or opts.get(b'follow_first')
2100 follow = opts.get(b'follow') or opts.get(b'follow_first')
2103 if opts.get(b'rev'):
2101 if opts.get(b'rev'):
2104 revs = scmutil.revrange(repo, opts[b'rev'])
2102 revs = scmutil.revrange(repo, opts[b'rev'])
2105 elif follow and repo.dirstate.p1() == nullid:
2103 elif follow and repo.dirstate.p1() == nullid:
2106 revs = smartset.baseset()
2104 revs = smartset.baseset()
2107 elif follow:
2105 elif follow:
2108 revs = repo.revs(b'reverse(:.)')
2106 revs = repo.revs(b'reverse(:.)')
2109 else:
2107 else:
2110 revs = smartset.spanset(repo)
2108 revs = smartset.spanset(repo)
2111 revs.reverse()
2109 revs.reverse()
2112 return revs
2110 return revs
2113
2111
2114
2112
2115 class FileWalkError(Exception):
2113 class FileWalkError(Exception):
2116 pass
2114 pass
2117
2115
2118
2116
2119 def walkfilerevs(repo, match, follow, revs, fncache):
2117 def walkfilerevs(repo, match, follow, revs, fncache):
2120 '''Walks the file history for the matched files.
2118 '''Walks the file history for the matched files.
2121
2119
2122 Returns the changeset revs that are involved in the file history.
2120 Returns the changeset revs that are involved in the file history.
2123
2121
2124 Throws FileWalkError if the file history can't be walked using
2122 Throws FileWalkError if the file history can't be walked using
2125 filelogs alone.
2123 filelogs alone.
2126 '''
2124 '''
2127 wanted = set()
2125 wanted = set()
2128 copies = []
2126 copies = []
2129 minrev, maxrev = min(revs), max(revs)
2127 minrev, maxrev = min(revs), max(revs)
2130
2128
2131 def filerevs(filelog, last):
2129 def filerevs(filelog, last):
2132 """
2130 """
2133 Only files, no patterns. Check the history of each file.
2131 Only files, no patterns. Check the history of each file.
2134
2132
2135 Examines filelog entries within minrev, maxrev linkrev range
2133 Examines filelog entries within minrev, maxrev linkrev range
2136 Returns an iterator yielding (linkrev, parentlinkrevs, copied)
2134 Returns an iterator yielding (linkrev, parentlinkrevs, copied)
2137 tuples in backwards order
2135 tuples in backwards order
2138 """
2136 """
2139 cl_count = len(repo)
2137 cl_count = len(repo)
2140 revs = []
2138 revs = []
2141 for j in pycompat.xrange(0, last + 1):
2139 for j in pycompat.xrange(0, last + 1):
2142 linkrev = filelog.linkrev(j)
2140 linkrev = filelog.linkrev(j)
2143 if linkrev < minrev:
2141 if linkrev < minrev:
2144 continue
2142 continue
2145 # only yield rev for which we have the changelog, it can
2143 # only yield rev for which we have the changelog, it can
2146 # happen while doing "hg log" during a pull or commit
2144 # happen while doing "hg log" during a pull or commit
2147 if linkrev >= cl_count:
2145 if linkrev >= cl_count:
2148 break
2146 break
2149
2147
2150 parentlinkrevs = []
2148 parentlinkrevs = []
2151 for p in filelog.parentrevs(j):
2149 for p in filelog.parentrevs(j):
2152 if p != nullrev:
2150 if p != nullrev:
2153 parentlinkrevs.append(filelog.linkrev(p))
2151 parentlinkrevs.append(filelog.linkrev(p))
2154 n = filelog.node(j)
2152 n = filelog.node(j)
2155 revs.append(
2153 revs.append(
2156 (linkrev, parentlinkrevs, follow and filelog.renamed(n))
2154 (linkrev, parentlinkrevs, follow and filelog.renamed(n))
2157 )
2155 )
2158
2156
2159 return reversed(revs)
2157 return reversed(revs)
2160
2158
2161 def iterfiles():
2159 def iterfiles():
2162 pctx = repo[b'.']
2160 pctx = repo[b'.']
2163 for filename in match.files():
2161 for filename in match.files():
2164 if follow:
2162 if follow:
2165 if filename not in pctx:
2163 if filename not in pctx:
2166 raise error.Abort(
2164 raise error.Abort(
2167 _(
2165 _(
2168 b'cannot follow file not in parent '
2166 b'cannot follow file not in parent '
2169 b'revision: "%s"'
2167 b'revision: "%s"'
2170 )
2168 )
2171 % filename
2169 % filename
2172 )
2170 )
2173 yield filename, pctx[filename].filenode()
2171 yield filename, pctx[filename].filenode()
2174 else:
2172 else:
2175 yield filename, None
2173 yield filename, None
2176 for filename_node in copies:
2174 for filename_node in copies:
2177 yield filename_node
2175 yield filename_node
2178
2176
2179 for file_, node in iterfiles():
2177 for file_, node in iterfiles():
2180 filelog = repo.file(file_)
2178 filelog = repo.file(file_)
2181 if not len(filelog):
2179 if not len(filelog):
2182 if node is None:
2180 if node is None:
2183 # A zero count may be a directory or deleted file, so
2181 # A zero count may be a directory or deleted file, so
2184 # try to find matching entries on the slow path.
2182 # try to find matching entries on the slow path.
2185 if follow:
2183 if follow:
2186 raise error.Abort(
2184 raise error.Abort(
2187 _(b'cannot follow nonexistent file: "%s"') % file_
2185 _(b'cannot follow nonexistent file: "%s"') % file_
2188 )
2186 )
2189 raise FileWalkError(b"Cannot walk via filelog")
2187 raise FileWalkError(b"Cannot walk via filelog")
2190 else:
2188 else:
2191 continue
2189 continue
2192
2190
2193 if node is None:
2191 if node is None:
2194 last = len(filelog) - 1
2192 last = len(filelog) - 1
2195 else:
2193 else:
2196 last = filelog.rev(node)
2194 last = filelog.rev(node)
2197
2195
2198 # keep track of all ancestors of the file
2196 # keep track of all ancestors of the file
2199 ancestors = {filelog.linkrev(last)}
2197 ancestors = {filelog.linkrev(last)}
2200
2198
2201 # iterate from latest to oldest revision
2199 # iterate from latest to oldest revision
2202 for rev, flparentlinkrevs, copied in filerevs(filelog, last):
2200 for rev, flparentlinkrevs, copied in filerevs(filelog, last):
2203 if not follow:
2201 if not follow:
2204 if rev > maxrev:
2202 if rev > maxrev:
2205 continue
2203 continue
2206 else:
2204 else:
2207 # Note that last might not be the first interesting
2205 # Note that last might not be the first interesting
2208 # rev to us:
2206 # rev to us:
2209 # if the file has been changed after maxrev, we'll
2207 # if the file has been changed after maxrev, we'll
2210 # have linkrev(last) > maxrev, and we still need
2208 # have linkrev(last) > maxrev, and we still need
2211 # to explore the file graph
2209 # to explore the file graph
2212 if rev not in ancestors:
2210 if rev not in ancestors:
2213 continue
2211 continue
2214 # XXX insert 1327 fix here
2212 # XXX insert 1327 fix here
2215 if flparentlinkrevs:
2213 if flparentlinkrevs:
2216 ancestors.update(flparentlinkrevs)
2214 ancestors.update(flparentlinkrevs)
2217
2215
2218 fncache.setdefault(rev, []).append(file_)
2216 fncache.setdefault(rev, []).append(file_)
2219 wanted.add(rev)
2217 wanted.add(rev)
2220 if copied:
2218 if copied:
2221 copies.append(copied)
2219 copies.append(copied)
2222
2220
2223 return wanted
2221 return wanted
2224
2222
2225
2223
2226 class _followfilter(object):
2224 class _followfilter(object):
2227 def __init__(self, repo, onlyfirst=False):
2225 def __init__(self, repo, onlyfirst=False):
2228 self.repo = repo
2226 self.repo = repo
2229 self.startrev = nullrev
2227 self.startrev = nullrev
2230 self.roots = set()
2228 self.roots = set()
2231 self.onlyfirst = onlyfirst
2229 self.onlyfirst = onlyfirst
2232
2230
2233 def match(self, rev):
2231 def match(self, rev):
2234 def realparents(rev):
2232 def realparents(rev):
2235 if self.onlyfirst:
2233 if self.onlyfirst:
2236 return self.repo.changelog.parentrevs(rev)[0:1]
2234 return self.repo.changelog.parentrevs(rev)[0:1]
2237 else:
2235 else:
2238 return filter(
2236 return filter(
2239 lambda x: x != nullrev, self.repo.changelog.parentrevs(rev)
2237 lambda x: x != nullrev, self.repo.changelog.parentrevs(rev)
2240 )
2238 )
2241
2239
2242 if self.startrev == nullrev:
2240 if self.startrev == nullrev:
2243 self.startrev = rev
2241 self.startrev = rev
2244 return True
2242 return True
2245
2243
2246 if rev > self.startrev:
2244 if rev > self.startrev:
2247 # forward: all descendants
2245 # forward: all descendants
2248 if not self.roots:
2246 if not self.roots:
2249 self.roots.add(self.startrev)
2247 self.roots.add(self.startrev)
2250 for parent in realparents(rev):
2248 for parent in realparents(rev):
2251 if parent in self.roots:
2249 if parent in self.roots:
2252 self.roots.add(rev)
2250 self.roots.add(rev)
2253 return True
2251 return True
2254 else:
2252 else:
2255 # backwards: all parents
2253 # backwards: all parents
2256 if not self.roots:
2254 if not self.roots:
2257 self.roots.update(realparents(self.startrev))
2255 self.roots.update(realparents(self.startrev))
2258 if rev in self.roots:
2256 if rev in self.roots:
2259 self.roots.remove(rev)
2257 self.roots.remove(rev)
2260 self.roots.update(realparents(rev))
2258 self.roots.update(realparents(rev))
2261 return True
2259 return True
2262
2260
2263 return False
2261 return False
2264
2262
2265
2263
2266 def walkchangerevs(repo, match, opts, prepare):
2264 def walkchangerevs(repo, match, opts, prepare):
2267 '''Iterate over files and the revs in which they changed.
2265 '''Iterate over files and the revs in which they changed.
2268
2266
2269 Callers most commonly need to iterate backwards over the history
2267 Callers most commonly need to iterate backwards over the history
2270 in which they are interested. Doing so has awful (quadratic-looking)
2268 in which they are interested. Doing so has awful (quadratic-looking)
2271 performance, so we use iterators in a "windowed" way.
2269 performance, so we use iterators in a "windowed" way.
2272
2270
2273 We walk a window of revisions in the desired order. Within the
2271 We walk a window of revisions in the desired order. Within the
2274 window, we first walk forwards to gather data, then in the desired
2272 window, we first walk forwards to gather data, then in the desired
2275 order (usually backwards) to display it.
2273 order (usually backwards) to display it.
2276
2274
2277 This function returns an iterator yielding contexts. Before
2275 This function returns an iterator yielding contexts. Before
2278 yielding each context, the iterator will first call the prepare
2276 yielding each context, the iterator will first call the prepare
2279 function on each context in the window in forward order.'''
2277 function on each context in the window in forward order.'''
2280
2278
2281 allfiles = opts.get(b'all_files')
2279 allfiles = opts.get(b'all_files')
2282 follow = opts.get(b'follow') or opts.get(b'follow_first')
2280 follow = opts.get(b'follow') or opts.get(b'follow_first')
2283 revs = _walkrevs(repo, opts)
2281 revs = _walkrevs(repo, opts)
2284 if not revs:
2282 if not revs:
2285 return []
2283 return []
2286 wanted = set()
2284 wanted = set()
2287 slowpath = match.anypats() or (not match.always() and opts.get(b'removed'))
2285 slowpath = match.anypats() or (not match.always() and opts.get(b'removed'))
2288 fncache = {}
2286 fncache = {}
2289 change = repo.__getitem__
2287 change = repo.__getitem__
2290
2288
2291 # First step is to fill wanted, the set of revisions that we want to yield.
2289 # First step is to fill wanted, the set of revisions that we want to yield.
2292 # When it does not induce extra cost, we also fill fncache for revisions in
2290 # When it does not induce extra cost, we also fill fncache for revisions in
2293 # wanted: a cache of filenames that were changed (ctx.files()) and that
2291 # wanted: a cache of filenames that were changed (ctx.files()) and that
2294 # match the file filtering conditions.
2292 # match the file filtering conditions.
2295
2293
2296 if match.always() or allfiles:
2294 if match.always() or allfiles:
2297 # No files, no patterns. Display all revs.
2295 # No files, no patterns. Display all revs.
2298 wanted = revs
2296 wanted = revs
2299 elif not slowpath:
2297 elif not slowpath:
2300 # We only have to read through the filelog to find wanted revisions
2298 # We only have to read through the filelog to find wanted revisions
2301
2299
2302 try:
2300 try:
2303 wanted = walkfilerevs(repo, match, follow, revs, fncache)
2301 wanted = walkfilerevs(repo, match, follow, revs, fncache)
2304 except FileWalkError:
2302 except FileWalkError:
2305 slowpath = True
2303 slowpath = True
2306
2304
2307 # We decided to fall back to the slowpath because at least one
2305 # We decided to fall back to the slowpath because at least one
2308 # of the paths was not a file. Check to see if at least one of them
2306 # of the paths was not a file. Check to see if at least one of them
2309 # existed in history, otherwise simply return
2307 # existed in history, otherwise simply return
2310 for path in match.files():
2308 for path in match.files():
2311 if path == b'.' or path in repo.store:
2309 if path == b'.' or path in repo.store:
2312 break
2310 break
2313 else:
2311 else:
2314 return []
2312 return []
2315
2313
2316 if slowpath:
2314 if slowpath:
2317 # We have to read the changelog to match filenames against
2315 # We have to read the changelog to match filenames against
2318 # changed files
2316 # changed files
2319
2317
2320 if follow:
2318 if follow:
2321 raise error.Abort(
2319 raise error.Abort(
2322 _(b'can only follow copies/renames for explicit filenames')
2320 _(b'can only follow copies/renames for explicit filenames')
2323 )
2321 )
2324
2322
2325 # The slow path checks files modified in every changeset.
2323 # The slow path checks files modified in every changeset.
2326 # This is really slow on large repos, so compute the set lazily.
2324 # This is really slow on large repos, so compute the set lazily.
2327 class lazywantedset(object):
2325 class lazywantedset(object):
2328 def __init__(self):
2326 def __init__(self):
2329 self.set = set()
2327 self.set = set()
2330 self.revs = set(revs)
2328 self.revs = set(revs)
2331
2329
2332 # No need to worry about locality here because it will be accessed
2330 # No need to worry about locality here because it will be accessed
2333 # in the same order as the increasing window below.
2331 # in the same order as the increasing window below.
2334 def __contains__(self, value):
2332 def __contains__(self, value):
2335 if value in self.set:
2333 if value in self.set:
2336 return True
2334 return True
2337 elif not value in self.revs:
2335 elif not value in self.revs:
2338 return False
2336 return False
2339 else:
2337 else:
2340 self.revs.discard(value)
2338 self.revs.discard(value)
2341 ctx = change(value)
2339 ctx = change(value)
2342 if allfiles:
2340 if allfiles:
2343 matches = list(ctx.manifest().walk(match))
2341 matches = list(ctx.manifest().walk(match))
2344 else:
2342 else:
2345 matches = [f for f in ctx.files() if match(f)]
2343 matches = [f for f in ctx.files() if match(f)]
2346 if matches:
2344 if matches:
2347 fncache[value] = matches
2345 fncache[value] = matches
2348 self.set.add(value)
2346 self.set.add(value)
2349 return True
2347 return True
2350 return False
2348 return False
2351
2349
2352 def discard(self, value):
2350 def discard(self, value):
2353 self.revs.discard(value)
2351 self.revs.discard(value)
2354 self.set.discard(value)
2352 self.set.discard(value)
2355
2353
2356 wanted = lazywantedset()
2354 wanted = lazywantedset()
2357
2355
2358 # it might be worthwhile to do this in the iterator if the rev range
2356 # it might be worthwhile to do this in the iterator if the rev range
2359 # is descending and the prune args are all within that range
2357 # is descending and the prune args are all within that range
2360 for rev in opts.get(b'prune', ()):
2358 for rev in opts.get(b'prune', ()):
2361 rev = repo[rev].rev()
2359 rev = repo[rev].rev()
2362 ff = _followfilter(repo)
2360 ff = _followfilter(repo)
2363 stop = min(revs[0], revs[-1])
2361 stop = min(revs[0], revs[-1])
2364 for x in pycompat.xrange(rev, stop - 1, -1):
2362 for x in pycompat.xrange(rev, stop - 1, -1):
2365 if ff.match(x):
2363 if ff.match(x):
2366 wanted = wanted - [x]
2364 wanted = wanted - [x]
2367
2365
2368 # Now that wanted is correctly initialized, we can iterate over the
2366 # Now that wanted is correctly initialized, we can iterate over the
2369 # revision range, yielding only revisions in wanted.
2367 # revision range, yielding only revisions in wanted.
2370 def iterate():
2368 def iterate():
2371 if follow and match.always():
2369 if follow and match.always():
2372 ff = _followfilter(repo, onlyfirst=opts.get(b'follow_first'))
2370 ff = _followfilter(repo, onlyfirst=opts.get(b'follow_first'))
2373
2371
2374 def want(rev):
2372 def want(rev):
2375 return ff.match(rev) and rev in wanted
2373 return ff.match(rev) and rev in wanted
2376
2374
2377 else:
2375 else:
2378
2376
2379 def want(rev):
2377 def want(rev):
2380 return rev in wanted
2378 return rev in wanted
2381
2379
2382 it = iter(revs)
2380 it = iter(revs)
2383 stopiteration = False
2381 stopiteration = False
2384 for windowsize in increasingwindows():
2382 for windowsize in increasingwindows():
2385 nrevs = []
2383 nrevs = []
2386 for i in pycompat.xrange(windowsize):
2384 for i in pycompat.xrange(windowsize):
2387 rev = next(it, None)
2385 rev = next(it, None)
2388 if rev is None:
2386 if rev is None:
2389 stopiteration = True
2387 stopiteration = True
2390 break
2388 break
2391 elif want(rev):
2389 elif want(rev):
2392 nrevs.append(rev)
2390 nrevs.append(rev)
2393 for rev in sorted(nrevs):
2391 for rev in sorted(nrevs):
2394 fns = fncache.get(rev)
2392 fns = fncache.get(rev)
2395 ctx = change(rev)
2393 ctx = change(rev)
2396 if not fns:
2394 if not fns:
2397
2395
2398 def fns_generator():
2396 def fns_generator():
2399 if allfiles:
2397 if allfiles:
2400 fiter = iter(ctx)
2398 fiter = iter(ctx)
2401 else:
2399 else:
2402 fiter = ctx.files()
2400 fiter = ctx.files()
2403 for f in fiter:
2401 for f in fiter:
2404 if match(f):
2402 if match(f):
2405 yield f
2403 yield f
2406
2404
2407 fns = fns_generator()
2405 fns = fns_generator()
2408 prepare(ctx, fns)
2406 prepare(ctx, fns)
2409 for rev in nrevs:
2407 for rev in nrevs:
2410 yield change(rev)
2408 yield change(rev)
2411
2409
2412 if stopiteration:
2410 if stopiteration:
2413 break
2411 break
2414
2412
2415 return iterate()
2413 return iterate()
2416
2414
2417
2415
2418 def add(ui, repo, match, prefix, uipathfn, explicitonly, **opts):
2416 def add(ui, repo, match, prefix, uipathfn, explicitonly, **opts):
2419 bad = []
2417 bad = []
2420
2418
2421 badfn = lambda x, y: bad.append(x) or match.bad(x, y)
2419 badfn = lambda x, y: bad.append(x) or match.bad(x, y)
2422 names = []
2420 names = []
2423 wctx = repo[None]
2421 wctx = repo[None]
2424 cca = None
2422 cca = None
2425 abort, warn = scmutil.checkportabilityalert(ui)
2423 abort, warn = scmutil.checkportabilityalert(ui)
2426 if abort or warn:
2424 if abort or warn:
2427 cca = scmutil.casecollisionauditor(ui, abort, repo.dirstate)
2425 cca = scmutil.casecollisionauditor(ui, abort, repo.dirstate)
2428
2426
2429 match = repo.narrowmatch(match, includeexact=True)
2427 match = repo.narrowmatch(match, includeexact=True)
2430 badmatch = matchmod.badmatch(match, badfn)
2428 badmatch = matchmod.badmatch(match, badfn)
2431 dirstate = repo.dirstate
2429 dirstate = repo.dirstate
2432 # We don't want to just call wctx.walk here, since it would return a lot of
2430 # We don't want to just call wctx.walk here, since it would return a lot of
2433 # clean files, which we aren't interested in and takes time.
2431 # clean files, which we aren't interested in and takes time.
2434 for f in sorted(
2432 for f in sorted(
2435 dirstate.walk(
2433 dirstate.walk(
2436 badmatch,
2434 badmatch,
2437 subrepos=sorted(wctx.substate),
2435 subrepos=sorted(wctx.substate),
2438 unknown=True,
2436 unknown=True,
2439 ignored=False,
2437 ignored=False,
2440 full=False,
2438 full=False,
2441 )
2439 )
2442 ):
2440 ):
2443 exact = match.exact(f)
2441 exact = match.exact(f)
2444 if exact or not explicitonly and f not in wctx and repo.wvfs.lexists(f):
2442 if exact or not explicitonly and f not in wctx and repo.wvfs.lexists(f):
2445 if cca:
2443 if cca:
2446 cca(f)
2444 cca(f)
2447 names.append(f)
2445 names.append(f)
2448 if ui.verbose or not exact:
2446 if ui.verbose or not exact:
2449 ui.status(
2447 ui.status(
2450 _(b'adding %s\n') % uipathfn(f), label=b'ui.addremove.added'
2448 _(b'adding %s\n') % uipathfn(f), label=b'ui.addremove.added'
2451 )
2449 )
2452
2450
2453 for subpath in sorted(wctx.substate):
2451 for subpath in sorted(wctx.substate):
2454 sub = wctx.sub(subpath)
2452 sub = wctx.sub(subpath)
2455 try:
2453 try:
2456 submatch = matchmod.subdirmatcher(subpath, match)
2454 submatch = matchmod.subdirmatcher(subpath, match)
2457 subprefix = repo.wvfs.reljoin(prefix, subpath)
2455 subprefix = repo.wvfs.reljoin(prefix, subpath)
2458 subuipathfn = scmutil.subdiruipathfn(subpath, uipathfn)
2456 subuipathfn = scmutil.subdiruipathfn(subpath, uipathfn)
2459 if opts.get('subrepos'):
2457 if opts.get('subrepos'):
2460 bad.extend(
2458 bad.extend(
2461 sub.add(ui, submatch, subprefix, subuipathfn, False, **opts)
2459 sub.add(ui, submatch, subprefix, subuipathfn, False, **opts)
2462 )
2460 )
2463 else:
2461 else:
2464 bad.extend(
2462 bad.extend(
2465 sub.add(ui, submatch, subprefix, subuipathfn, True, **opts)
2463 sub.add(ui, submatch, subprefix, subuipathfn, True, **opts)
2466 )
2464 )
2467 except error.LookupError:
2465 except error.LookupError:
2468 ui.status(
2466 ui.status(
2469 _(b"skipping missing subrepository: %s\n") % uipathfn(subpath)
2467 _(b"skipping missing subrepository: %s\n") % uipathfn(subpath)
2470 )
2468 )
2471
2469
2472 if not opts.get('dry_run'):
2470 if not opts.get('dry_run'):
2473 rejected = wctx.add(names, prefix)
2471 rejected = wctx.add(names, prefix)
2474 bad.extend(f for f in rejected if f in match.files())
2472 bad.extend(f for f in rejected if f in match.files())
2475 return bad
2473 return bad
2476
2474
2477
2475
2478 def addwebdirpath(repo, serverpath, webconf):
2476 def addwebdirpath(repo, serverpath, webconf):
2479 webconf[serverpath] = repo.root
2477 webconf[serverpath] = repo.root
2480 repo.ui.debug(b'adding %s = %s\n' % (serverpath, repo.root))
2478 repo.ui.debug(b'adding %s = %s\n' % (serverpath, repo.root))
2481
2479
2482 for r in repo.revs(b'filelog("path:.hgsub")'):
2480 for r in repo.revs(b'filelog("path:.hgsub")'):
2483 ctx = repo[r]
2481 ctx = repo[r]
2484 for subpath in ctx.substate:
2482 for subpath in ctx.substate:
2485 ctx.sub(subpath).addwebdirpath(serverpath, webconf)
2483 ctx.sub(subpath).addwebdirpath(serverpath, webconf)
2486
2484
2487
2485
2488 def forget(
2486 def forget(
2489 ui, repo, match, prefix, uipathfn, explicitonly, dryrun, interactive
2487 ui, repo, match, prefix, uipathfn, explicitonly, dryrun, interactive
2490 ):
2488 ):
2491 if dryrun and interactive:
2489 if dryrun and interactive:
2492 raise error.Abort(_(b"cannot specify both --dry-run and --interactive"))
2490 raise error.Abort(_(b"cannot specify both --dry-run and --interactive"))
2493 bad = []
2491 bad = []
2494 badfn = lambda x, y: bad.append(x) or match.bad(x, y)
2492 badfn = lambda x, y: bad.append(x) or match.bad(x, y)
2495 wctx = repo[None]
2493 wctx = repo[None]
2496 forgot = []
2494 forgot = []
2497
2495
2498 s = repo.status(match=matchmod.badmatch(match, badfn), clean=True)
2496 s = repo.status(match=matchmod.badmatch(match, badfn), clean=True)
2499 forget = sorted(s.modified + s.added + s.deleted + s.clean)
2497 forget = sorted(s.modified + s.added + s.deleted + s.clean)
2500 if explicitonly:
2498 if explicitonly:
2501 forget = [f for f in forget if match.exact(f)]
2499 forget = [f for f in forget if match.exact(f)]
2502
2500
2503 for subpath in sorted(wctx.substate):
2501 for subpath in sorted(wctx.substate):
2504 sub = wctx.sub(subpath)
2502 sub = wctx.sub(subpath)
2505 submatch = matchmod.subdirmatcher(subpath, match)
2503 submatch = matchmod.subdirmatcher(subpath, match)
2506 subprefix = repo.wvfs.reljoin(prefix, subpath)
2504 subprefix = repo.wvfs.reljoin(prefix, subpath)
2507 subuipathfn = scmutil.subdiruipathfn(subpath, uipathfn)
2505 subuipathfn = scmutil.subdiruipathfn(subpath, uipathfn)
2508 try:
2506 try:
2509 subbad, subforgot = sub.forget(
2507 subbad, subforgot = sub.forget(
2510 submatch,
2508 submatch,
2511 subprefix,
2509 subprefix,
2512 subuipathfn,
2510 subuipathfn,
2513 dryrun=dryrun,
2511 dryrun=dryrun,
2514 interactive=interactive,
2512 interactive=interactive,
2515 )
2513 )
2516 bad.extend([subpath + b'/' + f for f in subbad])
2514 bad.extend([subpath + b'/' + f for f in subbad])
2517 forgot.extend([subpath + b'/' + f for f in subforgot])
2515 forgot.extend([subpath + b'/' + f for f in subforgot])
2518 except error.LookupError:
2516 except error.LookupError:
2519 ui.status(
2517 ui.status(
2520 _(b"skipping missing subrepository: %s\n") % uipathfn(subpath)
2518 _(b"skipping missing subrepository: %s\n") % uipathfn(subpath)
2521 )
2519 )
2522
2520
2523 if not explicitonly:
2521 if not explicitonly:
2524 for f in match.files():
2522 for f in match.files():
2525 if f not in repo.dirstate and not repo.wvfs.isdir(f):
2523 if f not in repo.dirstate and not repo.wvfs.isdir(f):
2526 if f not in forgot:
2524 if f not in forgot:
2527 if repo.wvfs.exists(f):
2525 if repo.wvfs.exists(f):
2528 # Don't complain if the exact case match wasn't given.
2526 # Don't complain if the exact case match wasn't given.
2529 # But don't do this until after checking 'forgot', so
2527 # But don't do this until after checking 'forgot', so
2530 # that subrepo files aren't normalized, and this op is
2528 # that subrepo files aren't normalized, and this op is
2531 # purely from data cached by the status walk above.
2529 # purely from data cached by the status walk above.
2532 if repo.dirstate.normalize(f) in repo.dirstate:
2530 if repo.dirstate.normalize(f) in repo.dirstate:
2533 continue
2531 continue
2534 ui.warn(
2532 ui.warn(
2535 _(
2533 _(
2536 b'not removing %s: '
2534 b'not removing %s: '
2537 b'file is already untracked\n'
2535 b'file is already untracked\n'
2538 )
2536 )
2539 % uipathfn(f)
2537 % uipathfn(f)
2540 )
2538 )
2541 bad.append(f)
2539 bad.append(f)
2542
2540
2543 if interactive:
2541 if interactive:
2544 responses = _(
2542 responses = _(
2545 b'[Ynsa?]'
2543 b'[Ynsa?]'
2546 b'$$ &Yes, forget this file'
2544 b'$$ &Yes, forget this file'
2547 b'$$ &No, skip this file'
2545 b'$$ &No, skip this file'
2548 b'$$ &Skip remaining files'
2546 b'$$ &Skip remaining files'
2549 b'$$ Include &all remaining files'
2547 b'$$ Include &all remaining files'
2550 b'$$ &? (display help)'
2548 b'$$ &? (display help)'
2551 )
2549 )
2552 for filename in forget[:]:
2550 for filename in forget[:]:
2553 r = ui.promptchoice(
2551 r = ui.promptchoice(
2554 _(b'forget %s %s') % (uipathfn(filename), responses)
2552 _(b'forget %s %s') % (uipathfn(filename), responses)
2555 )
2553 )
2556 if r == 4: # ?
2554 if r == 4: # ?
2557 while r == 4:
2555 while r == 4:
2558 for c, t in ui.extractchoices(responses)[1]:
2556 for c, t in ui.extractchoices(responses)[1]:
2559 ui.write(b'%s - %s\n' % (c, encoding.lower(t)))
2557 ui.write(b'%s - %s\n' % (c, encoding.lower(t)))
2560 r = ui.promptchoice(
2558 r = ui.promptchoice(
2561 _(b'forget %s %s') % (uipathfn(filename), responses)
2559 _(b'forget %s %s') % (uipathfn(filename), responses)
2562 )
2560 )
2563 if r == 0: # yes
2561 if r == 0: # yes
2564 continue
2562 continue
2565 elif r == 1: # no
2563 elif r == 1: # no
2566 forget.remove(filename)
2564 forget.remove(filename)
2567 elif r == 2: # Skip
2565 elif r == 2: # Skip
2568 fnindex = forget.index(filename)
2566 fnindex = forget.index(filename)
2569 del forget[fnindex:]
2567 del forget[fnindex:]
2570 break
2568 break
2571 elif r == 3: # All
2569 elif r == 3: # All
2572 break
2570 break
2573
2571
2574 for f in forget:
2572 for f in forget:
2575 if ui.verbose or not match.exact(f) or interactive:
2573 if ui.verbose or not match.exact(f) or interactive:
2576 ui.status(
2574 ui.status(
2577 _(b'removing %s\n') % uipathfn(f), label=b'ui.addremove.removed'
2575 _(b'removing %s\n') % uipathfn(f), label=b'ui.addremove.removed'
2578 )
2576 )
2579
2577
2580 if not dryrun:
2578 if not dryrun:
2581 rejected = wctx.forget(forget, prefix)
2579 rejected = wctx.forget(forget, prefix)
2582 bad.extend(f for f in rejected if f in match.files())
2580 bad.extend(f for f in rejected if f in match.files())
2583 forgot.extend(f for f in forget if f not in rejected)
2581 forgot.extend(f for f in forget if f not in rejected)
2584 return bad, forgot
2582 return bad, forgot
2585
2583
2586
2584
2587 def files(ui, ctx, m, uipathfn, fm, fmt, subrepos):
2585 def files(ui, ctx, m, uipathfn, fm, fmt, subrepos):
2588 ret = 1
2586 ret = 1
2589
2587
2590 needsfctx = ui.verbose or {b'size', b'flags'} & fm.datahint()
2588 needsfctx = ui.verbose or {b'size', b'flags'} & fm.datahint()
2591 for f in ctx.matches(m):
2589 for f in ctx.matches(m):
2592 fm.startitem()
2590 fm.startitem()
2593 fm.context(ctx=ctx)
2591 fm.context(ctx=ctx)
2594 if needsfctx:
2592 if needsfctx:
2595 fc = ctx[f]
2593 fc = ctx[f]
2596 fm.write(b'size flags', b'% 10d % 1s ', fc.size(), fc.flags())
2594 fm.write(b'size flags', b'% 10d % 1s ', fc.size(), fc.flags())
2597 fm.data(path=f)
2595 fm.data(path=f)
2598 fm.plain(fmt % uipathfn(f))
2596 fm.plain(fmt % uipathfn(f))
2599 ret = 0
2597 ret = 0
2600
2598
2601 for subpath in sorted(ctx.substate):
2599 for subpath in sorted(ctx.substate):
2602 submatch = matchmod.subdirmatcher(subpath, m)
2600 submatch = matchmod.subdirmatcher(subpath, m)
2603 subuipathfn = scmutil.subdiruipathfn(subpath, uipathfn)
2601 subuipathfn = scmutil.subdiruipathfn(subpath, uipathfn)
2604 if subrepos or m.exact(subpath) or any(submatch.files()):
2602 if subrepos or m.exact(subpath) or any(submatch.files()):
2605 sub = ctx.sub(subpath)
2603 sub = ctx.sub(subpath)
2606 try:
2604 try:
2607 recurse = m.exact(subpath) or subrepos
2605 recurse = m.exact(subpath) or subrepos
2608 if (
2606 if (
2609 sub.printfiles(ui, submatch, subuipathfn, fm, fmt, recurse)
2607 sub.printfiles(ui, submatch, subuipathfn, fm, fmt, recurse)
2610 == 0
2608 == 0
2611 ):
2609 ):
2612 ret = 0
2610 ret = 0
2613 except error.LookupError:
2611 except error.LookupError:
2614 ui.status(
2612 ui.status(
2615 _(b"skipping missing subrepository: %s\n")
2613 _(b"skipping missing subrepository: %s\n")
2616 % uipathfn(subpath)
2614 % uipathfn(subpath)
2617 )
2615 )
2618
2616
2619 return ret
2617 return ret
2620
2618
2621
2619
2622 def remove(
2620 def remove(
2623 ui, repo, m, prefix, uipathfn, after, force, subrepos, dryrun, warnings=None
2621 ui, repo, m, prefix, uipathfn, after, force, subrepos, dryrun, warnings=None
2624 ):
2622 ):
2625 ret = 0
2623 ret = 0
2626 s = repo.status(match=m, clean=True)
2624 s = repo.status(match=m, clean=True)
2627 modified, added, deleted, clean = s.modified, s.added, s.deleted, s.clean
2625 modified, added, deleted, clean = s.modified, s.added, s.deleted, s.clean
2628
2626
2629 wctx = repo[None]
2627 wctx = repo[None]
2630
2628
2631 if warnings is None:
2629 if warnings is None:
2632 warnings = []
2630 warnings = []
2633 warn = True
2631 warn = True
2634 else:
2632 else:
2635 warn = False
2633 warn = False
2636
2634
2637 subs = sorted(wctx.substate)
2635 subs = sorted(wctx.substate)
2638 progress = ui.makeprogress(
2636 progress = ui.makeprogress(
2639 _(b'searching'), total=len(subs), unit=_(b'subrepos')
2637 _(b'searching'), total=len(subs), unit=_(b'subrepos')
2640 )
2638 )
2641 for subpath in subs:
2639 for subpath in subs:
2642 submatch = matchmod.subdirmatcher(subpath, m)
2640 submatch = matchmod.subdirmatcher(subpath, m)
2643 subprefix = repo.wvfs.reljoin(prefix, subpath)
2641 subprefix = repo.wvfs.reljoin(prefix, subpath)
2644 subuipathfn = scmutil.subdiruipathfn(subpath, uipathfn)
2642 subuipathfn = scmutil.subdiruipathfn(subpath, uipathfn)
2645 if subrepos or m.exact(subpath) or any(submatch.files()):
2643 if subrepos or m.exact(subpath) or any(submatch.files()):
2646 progress.increment()
2644 progress.increment()
2647 sub = wctx.sub(subpath)
2645 sub = wctx.sub(subpath)
2648 try:
2646 try:
2649 if sub.removefiles(
2647 if sub.removefiles(
2650 submatch,
2648 submatch,
2651 subprefix,
2649 subprefix,
2652 subuipathfn,
2650 subuipathfn,
2653 after,
2651 after,
2654 force,
2652 force,
2655 subrepos,
2653 subrepos,
2656 dryrun,
2654 dryrun,
2657 warnings,
2655 warnings,
2658 ):
2656 ):
2659 ret = 1
2657 ret = 1
2660 except error.LookupError:
2658 except error.LookupError:
2661 warnings.append(
2659 warnings.append(
2662 _(b"skipping missing subrepository: %s\n")
2660 _(b"skipping missing subrepository: %s\n")
2663 % uipathfn(subpath)
2661 % uipathfn(subpath)
2664 )
2662 )
2665 progress.complete()
2663 progress.complete()
2666
2664
2667 # warn about failure to delete explicit files/dirs
2665 # warn about failure to delete explicit files/dirs
2668 deleteddirs = pathutil.dirs(deleted)
2666 deleteddirs = pathutil.dirs(deleted)
2669 files = m.files()
2667 files = m.files()
2670 progress = ui.makeprogress(
2668 progress = ui.makeprogress(
2671 _(b'deleting'), total=len(files), unit=_(b'files')
2669 _(b'deleting'), total=len(files), unit=_(b'files')
2672 )
2670 )
2673 for f in files:
2671 for f in files:
2674
2672
2675 def insubrepo():
2673 def insubrepo():
2676 for subpath in wctx.substate:
2674 for subpath in wctx.substate:
2677 if f.startswith(subpath + b'/'):
2675 if f.startswith(subpath + b'/'):
2678 return True
2676 return True
2679 return False
2677 return False
2680
2678
2681 progress.increment()
2679 progress.increment()
2682 isdir = f in deleteddirs or wctx.hasdir(f)
2680 isdir = f in deleteddirs or wctx.hasdir(f)
2683 if f in repo.dirstate or isdir or f == b'.' or insubrepo() or f in subs:
2681 if f in repo.dirstate or isdir or f == b'.' or insubrepo() or f in subs:
2684 continue
2682 continue
2685
2683
2686 if repo.wvfs.exists(f):
2684 if repo.wvfs.exists(f):
2687 if repo.wvfs.isdir(f):
2685 if repo.wvfs.isdir(f):
2688 warnings.append(
2686 warnings.append(
2689 _(b'not removing %s: no tracked files\n') % uipathfn(f)
2687 _(b'not removing %s: no tracked files\n') % uipathfn(f)
2690 )
2688 )
2691 else:
2689 else:
2692 warnings.append(
2690 warnings.append(
2693 _(b'not removing %s: file is untracked\n') % uipathfn(f)
2691 _(b'not removing %s: file is untracked\n') % uipathfn(f)
2694 )
2692 )
2695 # missing files will generate a warning elsewhere
2693 # missing files will generate a warning elsewhere
2696 ret = 1
2694 ret = 1
2697 progress.complete()
2695 progress.complete()
2698
2696
2699 if force:
2697 if force:
2700 list = modified + deleted + clean + added
2698 list = modified + deleted + clean + added
2701 elif after:
2699 elif after:
2702 list = deleted
2700 list = deleted
2703 remaining = modified + added + clean
2701 remaining = modified + added + clean
2704 progress = ui.makeprogress(
2702 progress = ui.makeprogress(
2705 _(b'skipping'), total=len(remaining), unit=_(b'files')
2703 _(b'skipping'), total=len(remaining), unit=_(b'files')
2706 )
2704 )
2707 for f in remaining:
2705 for f in remaining:
2708 progress.increment()
2706 progress.increment()
2709 if ui.verbose or (f in files):
2707 if ui.verbose or (f in files):
2710 warnings.append(
2708 warnings.append(
2711 _(b'not removing %s: file still exists\n') % uipathfn(f)
2709 _(b'not removing %s: file still exists\n') % uipathfn(f)
2712 )
2710 )
2713 ret = 1
2711 ret = 1
2714 progress.complete()
2712 progress.complete()
2715 else:
2713 else:
2716 list = deleted + clean
2714 list = deleted + clean
2717 progress = ui.makeprogress(
2715 progress = ui.makeprogress(
2718 _(b'skipping'), total=(len(modified) + len(added)), unit=_(b'files')
2716 _(b'skipping'), total=(len(modified) + len(added)), unit=_(b'files')
2719 )
2717 )
2720 for f in modified:
2718 for f in modified:
2721 progress.increment()
2719 progress.increment()
2722 warnings.append(
2720 warnings.append(
2723 _(
2721 _(
2724 b'not removing %s: file is modified (use -f'
2722 b'not removing %s: file is modified (use -f'
2725 b' to force removal)\n'
2723 b' to force removal)\n'
2726 )
2724 )
2727 % uipathfn(f)
2725 % uipathfn(f)
2728 )
2726 )
2729 ret = 1
2727 ret = 1
2730 for f in added:
2728 for f in added:
2731 progress.increment()
2729 progress.increment()
2732 warnings.append(
2730 warnings.append(
2733 _(
2731 _(
2734 b"not removing %s: file has been marked for add"
2732 b"not removing %s: file has been marked for add"
2735 b" (use 'hg forget' to undo add)\n"
2733 b" (use 'hg forget' to undo add)\n"
2736 )
2734 )
2737 % uipathfn(f)
2735 % uipathfn(f)
2738 )
2736 )
2739 ret = 1
2737 ret = 1
2740 progress.complete()
2738 progress.complete()
2741
2739
2742 list = sorted(list)
2740 list = sorted(list)
2743 progress = ui.makeprogress(
2741 progress = ui.makeprogress(
2744 _(b'deleting'), total=len(list), unit=_(b'files')
2742 _(b'deleting'), total=len(list), unit=_(b'files')
2745 )
2743 )
2746 for f in list:
2744 for f in list:
2747 if ui.verbose or not m.exact(f):
2745 if ui.verbose or not m.exact(f):
2748 progress.increment()
2746 progress.increment()
2749 ui.status(
2747 ui.status(
2750 _(b'removing %s\n') % uipathfn(f), label=b'ui.addremove.removed'
2748 _(b'removing %s\n') % uipathfn(f), label=b'ui.addremove.removed'
2751 )
2749 )
2752 progress.complete()
2750 progress.complete()
2753
2751
2754 if not dryrun:
2752 if not dryrun:
2755 with repo.wlock():
2753 with repo.wlock():
2756 if not after:
2754 if not after:
2757 for f in list:
2755 for f in list:
2758 if f in added:
2756 if f in added:
2759 continue # we never unlink added files on remove
2757 continue # we never unlink added files on remove
2760 rmdir = repo.ui.configbool(
2758 rmdir = repo.ui.configbool(
2761 b'experimental', b'removeemptydirs'
2759 b'experimental', b'removeemptydirs'
2762 )
2760 )
2763 repo.wvfs.unlinkpath(f, ignoremissing=True, rmdir=rmdir)
2761 repo.wvfs.unlinkpath(f, ignoremissing=True, rmdir=rmdir)
2764 repo[None].forget(list)
2762 repo[None].forget(list)
2765
2763
2766 if warn:
2764 if warn:
2767 for warning in warnings:
2765 for warning in warnings:
2768 ui.warn(warning)
2766 ui.warn(warning)
2769
2767
2770 return ret
2768 return ret
2771
2769
2772
2770
2773 def _catfmtneedsdata(fm):
2771 def _catfmtneedsdata(fm):
2774 return not fm.datahint() or b'data' in fm.datahint()
2772 return not fm.datahint() or b'data' in fm.datahint()
2775
2773
2776
2774
2777 def _updatecatformatter(fm, ctx, matcher, path, decode):
2775 def _updatecatformatter(fm, ctx, matcher, path, decode):
2778 """Hook for adding data to the formatter used by ``hg cat``.
2776 """Hook for adding data to the formatter used by ``hg cat``.
2779
2777
2780 Extensions (e.g., lfs) can wrap this to inject keywords/data, but must call
2778 Extensions (e.g., lfs) can wrap this to inject keywords/data, but must call
2781 this method first."""
2779 this method first."""
2782
2780
2783 # data() can be expensive to fetch (e.g. lfs), so don't fetch it if it
2781 # data() can be expensive to fetch (e.g. lfs), so don't fetch it if it
2784 # wasn't requested.
2782 # wasn't requested.
2785 data = b''
2783 data = b''
2786 if _catfmtneedsdata(fm):
2784 if _catfmtneedsdata(fm):
2787 data = ctx[path].data()
2785 data = ctx[path].data()
2788 if decode:
2786 if decode:
2789 data = ctx.repo().wwritedata(path, data)
2787 data = ctx.repo().wwritedata(path, data)
2790 fm.startitem()
2788 fm.startitem()
2791 fm.context(ctx=ctx)
2789 fm.context(ctx=ctx)
2792 fm.write(b'data', b'%s', data)
2790 fm.write(b'data', b'%s', data)
2793 fm.data(path=path)
2791 fm.data(path=path)
2794
2792
2795
2793
2796 def cat(ui, repo, ctx, matcher, basefm, fntemplate, prefix, **opts):
2794 def cat(ui, repo, ctx, matcher, basefm, fntemplate, prefix, **opts):
2797 err = 1
2795 err = 1
2798 opts = pycompat.byteskwargs(opts)
2796 opts = pycompat.byteskwargs(opts)
2799
2797
2800 def write(path):
2798 def write(path):
2801 filename = None
2799 filename = None
2802 if fntemplate:
2800 if fntemplate:
2803 filename = makefilename(
2801 filename = makefilename(
2804 ctx, fntemplate, pathname=os.path.join(prefix, path)
2802 ctx, fntemplate, pathname=os.path.join(prefix, path)
2805 )
2803 )
2806 # attempt to create the directory if it does not already exist
2804 # attempt to create the directory if it does not already exist
2807 try:
2805 try:
2808 os.makedirs(os.path.dirname(filename))
2806 os.makedirs(os.path.dirname(filename))
2809 except OSError:
2807 except OSError:
2810 pass
2808 pass
2811 with formatter.maybereopen(basefm, filename) as fm:
2809 with formatter.maybereopen(basefm, filename) as fm:
2812 _updatecatformatter(fm, ctx, matcher, path, opts.get(b'decode'))
2810 _updatecatformatter(fm, ctx, matcher, path, opts.get(b'decode'))
2813
2811
2814 # Automation often uses hg cat on single files, so special case it
2812 # Automation often uses hg cat on single files, so special case it
2815 # for performance to avoid the cost of parsing the manifest.
2813 # for performance to avoid the cost of parsing the manifest.
2816 if len(matcher.files()) == 1 and not matcher.anypats():
2814 if len(matcher.files()) == 1 and not matcher.anypats():
2817 file = matcher.files()[0]
2815 file = matcher.files()[0]
2818 mfl = repo.manifestlog
2816 mfl = repo.manifestlog
2819 mfnode = ctx.manifestnode()
2817 mfnode = ctx.manifestnode()
2820 try:
2818 try:
2821 if mfnode and mfl[mfnode].find(file)[0]:
2819 if mfnode and mfl[mfnode].find(file)[0]:
2822 if _catfmtneedsdata(basefm):
2820 if _catfmtneedsdata(basefm):
2823 scmutil.prefetchfiles(repo, [ctx.rev()], matcher)
2821 scmutil.prefetchfiles(repo, [ctx.rev()], matcher)
2824 write(file)
2822 write(file)
2825 return 0
2823 return 0
2826 except KeyError:
2824 except KeyError:
2827 pass
2825 pass
2828
2826
2829 if _catfmtneedsdata(basefm):
2827 if _catfmtneedsdata(basefm):
2830 scmutil.prefetchfiles(repo, [ctx.rev()], matcher)
2828 scmutil.prefetchfiles(repo, [ctx.rev()], matcher)
2831
2829
2832 for abs in ctx.walk(matcher):
2830 for abs in ctx.walk(matcher):
2833 write(abs)
2831 write(abs)
2834 err = 0
2832 err = 0
2835
2833
2836 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
2834 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
2837 for subpath in sorted(ctx.substate):
2835 for subpath in sorted(ctx.substate):
2838 sub = ctx.sub(subpath)
2836 sub = ctx.sub(subpath)
2839 try:
2837 try:
2840 submatch = matchmod.subdirmatcher(subpath, matcher)
2838 submatch = matchmod.subdirmatcher(subpath, matcher)
2841 subprefix = os.path.join(prefix, subpath)
2839 subprefix = os.path.join(prefix, subpath)
2842 if not sub.cat(
2840 if not sub.cat(
2843 submatch,
2841 submatch,
2844 basefm,
2842 basefm,
2845 fntemplate,
2843 fntemplate,
2846 subprefix,
2844 subprefix,
2847 **pycompat.strkwargs(opts)
2845 **pycompat.strkwargs(opts)
2848 ):
2846 ):
2849 err = 0
2847 err = 0
2850 except error.RepoLookupError:
2848 except error.RepoLookupError:
2851 ui.status(
2849 ui.status(
2852 _(b"skipping missing subrepository: %s\n") % uipathfn(subpath)
2850 _(b"skipping missing subrepository: %s\n") % uipathfn(subpath)
2853 )
2851 )
2854
2852
2855 return err
2853 return err
2856
2854
2857
2855
2858 def commit(ui, repo, commitfunc, pats, opts):
2856 def commit(ui, repo, commitfunc, pats, opts):
2859 '''commit the specified files or all outstanding changes'''
2857 '''commit the specified files or all outstanding changes'''
2860 date = opts.get(b'date')
2858 date = opts.get(b'date')
2861 if date:
2859 if date:
2862 opts[b'date'] = dateutil.parsedate(date)
2860 opts[b'date'] = dateutil.parsedate(date)
2863 message = logmessage(ui, opts)
2861 message = logmessage(ui, opts)
2864 matcher = scmutil.match(repo[None], pats, opts)
2862 matcher = scmutil.match(repo[None], pats, opts)
2865
2863
2866 dsguard = None
2864 dsguard = None
2867 # extract addremove carefully -- this function can be called from a command
2865 # extract addremove carefully -- this function can be called from a command
2868 # that doesn't support addremove
2866 # that doesn't support addremove
2869 if opts.get(b'addremove'):
2867 if opts.get(b'addremove'):
2870 dsguard = dirstateguard.dirstateguard(repo, b'commit')
2868 dsguard = dirstateguard.dirstateguard(repo, b'commit')
2871 with dsguard or util.nullcontextmanager():
2869 with dsguard or util.nullcontextmanager():
2872 if dsguard:
2870 if dsguard:
2873 relative = scmutil.anypats(pats, opts)
2871 relative = scmutil.anypats(pats, opts)
2874 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=relative)
2872 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=relative)
2875 if scmutil.addremove(repo, matcher, b"", uipathfn, opts) != 0:
2873 if scmutil.addremove(repo, matcher, b"", uipathfn, opts) != 0:
2876 raise error.Abort(
2874 raise error.Abort(
2877 _(b"failed to mark all new/missing files as added/removed")
2875 _(b"failed to mark all new/missing files as added/removed")
2878 )
2876 )
2879
2877
2880 return commitfunc(ui, repo, message, matcher, opts)
2878 return commitfunc(ui, repo, message, matcher, opts)
2881
2879
2882
2880
2883 def samefile(f, ctx1, ctx2):
2881 def samefile(f, ctx1, ctx2):
2884 if f in ctx1.manifest():
2882 if f in ctx1.manifest():
2885 a = ctx1.filectx(f)
2883 a = ctx1.filectx(f)
2886 if f in ctx2.manifest():
2884 if f in ctx2.manifest():
2887 b = ctx2.filectx(f)
2885 b = ctx2.filectx(f)
2888 return not a.cmp(b) and a.flags() == b.flags()
2886 return not a.cmp(b) and a.flags() == b.flags()
2889 else:
2887 else:
2890 return False
2888 return False
2891 else:
2889 else:
2892 return f not in ctx2.manifest()
2890 return f not in ctx2.manifest()
2893
2891
2894
2892
2895 def amend(ui, repo, old, extra, pats, opts):
2893 def amend(ui, repo, old, extra, pats, opts):
2896 # avoid cycle context -> subrepo -> cmdutil
2894 # avoid cycle context -> subrepo -> cmdutil
2897 from . import context
2895 from . import context
2898
2896
2899 # amend will reuse the existing user if not specified, but the obsolete
2897 # amend will reuse the existing user if not specified, but the obsolete
2900 # marker creation requires that the current user's name is specified.
2898 # marker creation requires that the current user's name is specified.
2901 if obsolete.isenabled(repo, obsolete.createmarkersopt):
2899 if obsolete.isenabled(repo, obsolete.createmarkersopt):
2902 ui.username() # raise exception if username not set
2900 ui.username() # raise exception if username not set
2903
2901
2904 ui.note(_(b'amending changeset %s\n') % old)
2902 ui.note(_(b'amending changeset %s\n') % old)
2905 base = old.p1()
2903 base = old.p1()
2906
2904
2907 with repo.wlock(), repo.lock(), repo.transaction(b'amend'):
2905 with repo.wlock(), repo.lock(), repo.transaction(b'amend'):
2908 # Participating changesets:
2906 # Participating changesets:
2909 #
2907 #
2910 # wctx o - workingctx that contains changes from working copy
2908 # wctx o - workingctx that contains changes from working copy
2911 # | to go into amending commit
2909 # | to go into amending commit
2912 # |
2910 # |
2913 # old o - changeset to amend
2911 # old o - changeset to amend
2914 # |
2912 # |
2915 # base o - first parent of the changeset to amend
2913 # base o - first parent of the changeset to amend
2916 wctx = repo[None]
2914 wctx = repo[None]
2917
2915
2918 # Copy to avoid mutating input
2916 # Copy to avoid mutating input
2919 extra = extra.copy()
2917 extra = extra.copy()
2920 # Update extra dict from amended commit (e.g. to preserve graft
2918 # Update extra dict from amended commit (e.g. to preserve graft
2921 # source)
2919 # source)
2922 extra.update(old.extra())
2920 extra.update(old.extra())
2923
2921
2924 # Also update it from the from the wctx
2922 # Also update it from the from the wctx
2925 extra.update(wctx.extra())
2923 extra.update(wctx.extra())
2926
2924
2927 # date-only change should be ignored?
2925 # date-only change should be ignored?
2928 datemaydiffer = resolvecommitoptions(ui, opts)
2926 datemaydiffer = resolvecommitoptions(ui, opts)
2929
2927
2930 date = old.date()
2928 date = old.date()
2931 if opts.get(b'date'):
2929 if opts.get(b'date'):
2932 date = dateutil.parsedate(opts.get(b'date'))
2930 date = dateutil.parsedate(opts.get(b'date'))
2933 user = opts.get(b'user') or old.user()
2931 user = opts.get(b'user') or old.user()
2934
2932
2935 if len(old.parents()) > 1:
2933 if len(old.parents()) > 1:
2936 # ctx.files() isn't reliable for merges, so fall back to the
2934 # ctx.files() isn't reliable for merges, so fall back to the
2937 # slower repo.status() method
2935 # slower repo.status() method
2938 st = base.status(old)
2936 st = base.status(old)
2939 files = set(st.modified) | set(st.added) | set(st.removed)
2937 files = set(st.modified) | set(st.added) | set(st.removed)
2940 else:
2938 else:
2941 files = set(old.files())
2939 files = set(old.files())
2942
2940
2943 # add/remove the files to the working copy if the "addremove" option
2941 # add/remove the files to the working copy if the "addremove" option
2944 # was specified.
2942 # was specified.
2945 matcher = scmutil.match(wctx, pats, opts)
2943 matcher = scmutil.match(wctx, pats, opts)
2946 relative = scmutil.anypats(pats, opts)
2944 relative = scmutil.anypats(pats, opts)
2947 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=relative)
2945 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=relative)
2948 if opts.get(b'addremove') and scmutil.addremove(
2946 if opts.get(b'addremove') and scmutil.addremove(
2949 repo, matcher, b"", uipathfn, opts
2947 repo, matcher, b"", uipathfn, opts
2950 ):
2948 ):
2951 raise error.Abort(
2949 raise error.Abort(
2952 _(b"failed to mark all new/missing files as added/removed")
2950 _(b"failed to mark all new/missing files as added/removed")
2953 )
2951 )
2954
2952
2955 # Check subrepos. This depends on in-place wctx._status update in
2953 # Check subrepos. This depends on in-place wctx._status update in
2956 # subrepo.precommit(). To minimize the risk of this hack, we do
2954 # subrepo.precommit(). To minimize the risk of this hack, we do
2957 # nothing if .hgsub does not exist.
2955 # nothing if .hgsub does not exist.
2958 if b'.hgsub' in wctx or b'.hgsub' in old:
2956 if b'.hgsub' in wctx or b'.hgsub' in old:
2959 subs, commitsubs, newsubstate = subrepoutil.precommit(
2957 subs, commitsubs, newsubstate = subrepoutil.precommit(
2960 ui, wctx, wctx._status, matcher
2958 ui, wctx, wctx._status, matcher
2961 )
2959 )
2962 # amend should abort if commitsubrepos is enabled
2960 # amend should abort if commitsubrepos is enabled
2963 assert not commitsubs
2961 assert not commitsubs
2964 if subs:
2962 if subs:
2965 subrepoutil.writestate(repo, newsubstate)
2963 subrepoutil.writestate(repo, newsubstate)
2966
2964
2967 ms = mergemod.mergestate.read(repo)
2965 ms = mergemod.mergestate.read(repo)
2968 mergeutil.checkunresolved(ms)
2966 mergeutil.checkunresolved(ms)
2969
2967
2970 filestoamend = set(f for f in wctx.files() if matcher(f))
2968 filestoamend = set(f for f in wctx.files() if matcher(f))
2971
2969
2972 changes = len(filestoamend) > 0
2970 changes = len(filestoamend) > 0
2973 if changes:
2971 if changes:
2974 # Recompute copies (avoid recording a -> b -> a)
2972 # Recompute copies (avoid recording a -> b -> a)
2975 copied = copies.pathcopies(base, wctx, matcher)
2973 copied = copies.pathcopies(base, wctx, matcher)
2976 if old.p2:
2974 if old.p2:
2977 copied.update(copies.pathcopies(old.p2(), wctx, matcher))
2975 copied.update(copies.pathcopies(old.p2(), wctx, matcher))
2978
2976
2979 # Prune files which were reverted by the updates: if old
2977 # Prune files which were reverted by the updates: if old
2980 # introduced file X and the file was renamed in the working
2978 # introduced file X and the file was renamed in the working
2981 # copy, then those two files are the same and
2979 # copy, then those two files are the same and
2982 # we can discard X from our list of files. Likewise if X
2980 # we can discard X from our list of files. Likewise if X
2983 # was removed, it's no longer relevant. If X is missing (aka
2981 # was removed, it's no longer relevant. If X is missing (aka
2984 # deleted), old X must be preserved.
2982 # deleted), old X must be preserved.
2985 files.update(filestoamend)
2983 files.update(filestoamend)
2986 files = [
2984 files = [
2987 f
2985 f
2988 for f in files
2986 for f in files
2989 if (f not in filestoamend or not samefile(f, wctx, base))
2987 if (f not in filestoamend or not samefile(f, wctx, base))
2990 ]
2988 ]
2991
2989
2992 def filectxfn(repo, ctx_, path):
2990 def filectxfn(repo, ctx_, path):
2993 try:
2991 try:
2994 # If the file being considered is not amongst the files
2992 # If the file being considered is not amongst the files
2995 # to be amended, we should return the file context from the
2993 # to be amended, we should return the file context from the
2996 # old changeset. This avoids issues when only some files in
2994 # old changeset. This avoids issues when only some files in
2997 # the working copy are being amended but there are also
2995 # the working copy are being amended but there are also
2998 # changes to other files from the old changeset.
2996 # changes to other files from the old changeset.
2999 if path not in filestoamend:
2997 if path not in filestoamend:
3000 return old.filectx(path)
2998 return old.filectx(path)
3001
2999
3002 # Return None for removed files.
3000 # Return None for removed files.
3003 if path in wctx.removed():
3001 if path in wctx.removed():
3004 return None
3002 return None
3005
3003
3006 fctx = wctx[path]
3004 fctx = wctx[path]
3007 flags = fctx.flags()
3005 flags = fctx.flags()
3008 mctx = context.memfilectx(
3006 mctx = context.memfilectx(
3009 repo,
3007 repo,
3010 ctx_,
3008 ctx_,
3011 fctx.path(),
3009 fctx.path(),
3012 fctx.data(),
3010 fctx.data(),
3013 islink=b'l' in flags,
3011 islink=b'l' in flags,
3014 isexec=b'x' in flags,
3012 isexec=b'x' in flags,
3015 copysource=copied.get(path),
3013 copysource=copied.get(path),
3016 )
3014 )
3017 return mctx
3015 return mctx
3018 except KeyError:
3016 except KeyError:
3019 return None
3017 return None
3020
3018
3021 else:
3019 else:
3022 ui.note(_(b'copying changeset %s to %s\n') % (old, base))
3020 ui.note(_(b'copying changeset %s to %s\n') % (old, base))
3023
3021
3024 # Use version of files as in the old cset
3022 # Use version of files as in the old cset
3025 def filectxfn(repo, ctx_, path):
3023 def filectxfn(repo, ctx_, path):
3026 try:
3024 try:
3027 return old.filectx(path)
3025 return old.filectx(path)
3028 except KeyError:
3026 except KeyError:
3029 return None
3027 return None
3030
3028
3031 # See if we got a message from -m or -l, if not, open the editor with
3029 # See if we got a message from -m or -l, if not, open the editor with
3032 # the message of the changeset to amend.
3030 # the message of the changeset to amend.
3033 message = logmessage(ui, opts)
3031 message = logmessage(ui, opts)
3034
3032
3035 editform = mergeeditform(old, b'commit.amend')
3033 editform = mergeeditform(old, b'commit.amend')
3036
3034
3037 if not message:
3035 if not message:
3038 message = old.description()
3036 message = old.description()
3039 # Default if message isn't provided and --edit is not passed is to
3037 # Default if message isn't provided and --edit is not passed is to
3040 # invoke editor, but allow --no-edit. If somehow we don't have any
3038 # invoke editor, but allow --no-edit. If somehow we don't have any
3041 # description, let's always start the editor.
3039 # description, let's always start the editor.
3042 doedit = not message or opts.get(b'edit') in [True, None]
3040 doedit = not message or opts.get(b'edit') in [True, None]
3043 else:
3041 else:
3044 # Default if message is provided is to not invoke editor, but allow
3042 # Default if message is provided is to not invoke editor, but allow
3045 # --edit.
3043 # --edit.
3046 doedit = opts.get(b'edit') is True
3044 doedit = opts.get(b'edit') is True
3047 editor = getcommiteditor(edit=doedit, editform=editform)
3045 editor = getcommiteditor(edit=doedit, editform=editform)
3048
3046
3049 pureextra = extra.copy()
3047 pureextra = extra.copy()
3050 extra[b'amend_source'] = old.hex()
3048 extra[b'amend_source'] = old.hex()
3051
3049
3052 new = context.memctx(
3050 new = context.memctx(
3053 repo,
3051 repo,
3054 parents=[base.node(), old.p2().node()],
3052 parents=[base.node(), old.p2().node()],
3055 text=message,
3053 text=message,
3056 files=files,
3054 files=files,
3057 filectxfn=filectxfn,
3055 filectxfn=filectxfn,
3058 user=user,
3056 user=user,
3059 date=date,
3057 date=date,
3060 extra=extra,
3058 extra=extra,
3061 editor=editor,
3059 editor=editor,
3062 )
3060 )
3063
3061
3064 newdesc = changelog.stripdesc(new.description())
3062 newdesc = changelog.stripdesc(new.description())
3065 if (
3063 if (
3066 (not changes)
3064 (not changes)
3067 and newdesc == old.description()
3065 and newdesc == old.description()
3068 and user == old.user()
3066 and user == old.user()
3069 and (date == old.date() or datemaydiffer)
3067 and (date == old.date() or datemaydiffer)
3070 and pureextra == old.extra()
3068 and pureextra == old.extra()
3071 ):
3069 ):
3072 # nothing changed. continuing here would create a new node
3070 # nothing changed. continuing here would create a new node
3073 # anyway because of the amend_source noise.
3071 # anyway because of the amend_source noise.
3074 #
3072 #
3075 # This not what we expect from amend.
3073 # This not what we expect from amend.
3076 return old.node()
3074 return old.node()
3077
3075
3078 commitphase = None
3076 commitphase = None
3079 if opts.get(b'secret'):
3077 if opts.get(b'secret'):
3080 commitphase = phases.secret
3078 commitphase = phases.secret
3081 newid = repo.commitctx(new)
3079 newid = repo.commitctx(new)
3082
3080
3083 # Reroute the working copy parent to the new changeset
3081 # Reroute the working copy parent to the new changeset
3084 repo.setparents(newid, nullid)
3082 repo.setparents(newid, nullid)
3085 mapping = {old.node(): (newid,)}
3083 mapping = {old.node(): (newid,)}
3086 obsmetadata = None
3084 obsmetadata = None
3087 if opts.get(b'note'):
3085 if opts.get(b'note'):
3088 obsmetadata = {b'note': encoding.fromlocal(opts[b'note'])}
3086 obsmetadata = {b'note': encoding.fromlocal(opts[b'note'])}
3089 backup = ui.configbool(b'rewrite', b'backup-bundle')
3087 backup = ui.configbool(b'rewrite', b'backup-bundle')
3090 scmutil.cleanupnodes(
3088 scmutil.cleanupnodes(
3091 repo,
3089 repo,
3092 mapping,
3090 mapping,
3093 b'amend',
3091 b'amend',
3094 metadata=obsmetadata,
3092 metadata=obsmetadata,
3095 fixphase=True,
3093 fixphase=True,
3096 targetphase=commitphase,
3094 targetphase=commitphase,
3097 backup=backup,
3095 backup=backup,
3098 )
3096 )
3099
3097
3100 # Fixing the dirstate because localrepo.commitctx does not update
3098 # Fixing the dirstate because localrepo.commitctx does not update
3101 # it. This is rather convenient because we did not need to update
3099 # it. This is rather convenient because we did not need to update
3102 # the dirstate for all the files in the new commit which commitctx
3100 # the dirstate for all the files in the new commit which commitctx
3103 # could have done if it updated the dirstate. Now, we can
3101 # could have done if it updated the dirstate. Now, we can
3104 # selectively update the dirstate only for the amended files.
3102 # selectively update the dirstate only for the amended files.
3105 dirstate = repo.dirstate
3103 dirstate = repo.dirstate
3106
3104
3107 # Update the state of the files which were added and modified in the
3105 # Update the state of the files which were added and modified in the
3108 # amend to "normal" in the dirstate. We need to use "normallookup" since
3106 # amend to "normal" in the dirstate. We need to use "normallookup" since
3109 # the files may have changed since the command started; using "normal"
3107 # the files may have changed since the command started; using "normal"
3110 # would mark them as clean but with uncommitted contents.
3108 # would mark them as clean but with uncommitted contents.
3111 normalfiles = set(wctx.modified() + wctx.added()) & filestoamend
3109 normalfiles = set(wctx.modified() + wctx.added()) & filestoamend
3112 for f in normalfiles:
3110 for f in normalfiles:
3113 dirstate.normallookup(f)
3111 dirstate.normallookup(f)
3114
3112
3115 # Update the state of files which were removed in the amend
3113 # Update the state of files which were removed in the amend
3116 # to "removed" in the dirstate.
3114 # to "removed" in the dirstate.
3117 removedfiles = set(wctx.removed()) & filestoamend
3115 removedfiles = set(wctx.removed()) & filestoamend
3118 for f in removedfiles:
3116 for f in removedfiles:
3119 dirstate.drop(f)
3117 dirstate.drop(f)
3120
3118
3121 return newid
3119 return newid
3122
3120
3123
3121
3124 def commiteditor(repo, ctx, subs, editform=b''):
3122 def commiteditor(repo, ctx, subs, editform=b''):
3125 if ctx.description():
3123 if ctx.description():
3126 return ctx.description()
3124 return ctx.description()
3127 return commitforceeditor(
3125 return commitforceeditor(
3128 repo, ctx, subs, editform=editform, unchangedmessagedetection=True
3126 repo, ctx, subs, editform=editform, unchangedmessagedetection=True
3129 )
3127 )
3130
3128
3131
3129
3132 def commitforceeditor(
3130 def commitforceeditor(
3133 repo,
3131 repo,
3134 ctx,
3132 ctx,
3135 subs,
3133 subs,
3136 finishdesc=None,
3134 finishdesc=None,
3137 extramsg=None,
3135 extramsg=None,
3138 editform=b'',
3136 editform=b'',
3139 unchangedmessagedetection=False,
3137 unchangedmessagedetection=False,
3140 ):
3138 ):
3141 if not extramsg:
3139 if not extramsg:
3142 extramsg = _(b"Leave message empty to abort commit.")
3140 extramsg = _(b"Leave message empty to abort commit.")
3143
3141
3144 forms = [e for e in editform.split(b'.') if e]
3142 forms = [e for e in editform.split(b'.') if e]
3145 forms.insert(0, b'changeset')
3143 forms.insert(0, b'changeset')
3146 templatetext = None
3144 templatetext = None
3147 while forms:
3145 while forms:
3148 ref = b'.'.join(forms)
3146 ref = b'.'.join(forms)
3149 if repo.ui.config(b'committemplate', ref):
3147 if repo.ui.config(b'committemplate', ref):
3150 templatetext = committext = buildcommittemplate(
3148 templatetext = committext = buildcommittemplate(
3151 repo, ctx, subs, extramsg, ref
3149 repo, ctx, subs, extramsg, ref
3152 )
3150 )
3153 break
3151 break
3154 forms.pop()
3152 forms.pop()
3155 else:
3153 else:
3156 committext = buildcommittext(repo, ctx, subs, extramsg)
3154 committext = buildcommittext(repo, ctx, subs, extramsg)
3157
3155
3158 # run editor in the repository root
3156 # run editor in the repository root
3159 olddir = encoding.getcwd()
3157 olddir = encoding.getcwd()
3160 os.chdir(repo.root)
3158 os.chdir(repo.root)
3161
3159
3162 # make in-memory changes visible to external process
3160 # make in-memory changes visible to external process
3163 tr = repo.currenttransaction()
3161 tr = repo.currenttransaction()
3164 repo.dirstate.write(tr)
3162 repo.dirstate.write(tr)
3165 pending = tr and tr.writepending() and repo.root
3163 pending = tr and tr.writepending() and repo.root
3166
3164
3167 editortext = repo.ui.edit(
3165 editortext = repo.ui.edit(
3168 committext,
3166 committext,
3169 ctx.user(),
3167 ctx.user(),
3170 ctx.extra(),
3168 ctx.extra(),
3171 editform=editform,
3169 editform=editform,
3172 pending=pending,
3170 pending=pending,
3173 repopath=repo.path,
3171 repopath=repo.path,
3174 action=b'commit',
3172 action=b'commit',
3175 )
3173 )
3176 text = editortext
3174 text = editortext
3177
3175
3178 # strip away anything below this special string (used for editors that want
3176 # strip away anything below this special string (used for editors that want
3179 # to display the diff)
3177 # to display the diff)
3180 stripbelow = re.search(_linebelow, text, flags=re.MULTILINE)
3178 stripbelow = re.search(_linebelow, text, flags=re.MULTILINE)
3181 if stripbelow:
3179 if stripbelow:
3182 text = text[: stripbelow.start()]
3180 text = text[: stripbelow.start()]
3183
3181
3184 text = re.sub(b"(?m)^HG:.*(\n|$)", b"", text)
3182 text = re.sub(b"(?m)^HG:.*(\n|$)", b"", text)
3185 os.chdir(olddir)
3183 os.chdir(olddir)
3186
3184
3187 if finishdesc:
3185 if finishdesc:
3188 text = finishdesc(text)
3186 text = finishdesc(text)
3189 if not text.strip():
3187 if not text.strip():
3190 raise error.Abort(_(b"empty commit message"))
3188 raise error.Abort(_(b"empty commit message"))
3191 if unchangedmessagedetection and editortext == templatetext:
3189 if unchangedmessagedetection and editortext == templatetext:
3192 raise error.Abort(_(b"commit message unchanged"))
3190 raise error.Abort(_(b"commit message unchanged"))
3193
3191
3194 return text
3192 return text
3195
3193
3196
3194
3197 def buildcommittemplate(repo, ctx, subs, extramsg, ref):
3195 def buildcommittemplate(repo, ctx, subs, extramsg, ref):
3198 ui = repo.ui
3196 ui = repo.ui
3199 spec = formatter.templatespec(ref, None, None)
3197 spec = formatter.templatespec(ref, None, None)
3200 t = logcmdutil.changesettemplater(ui, repo, spec)
3198 t = logcmdutil.changesettemplater(ui, repo, spec)
3201 t.t.cache.update(
3199 t.t.cache.update(
3202 (k, templater.unquotestring(v))
3200 (k, templater.unquotestring(v))
3203 for k, v in repo.ui.configitems(b'committemplate')
3201 for k, v in repo.ui.configitems(b'committemplate')
3204 )
3202 )
3205
3203
3206 if not extramsg:
3204 if not extramsg:
3207 extramsg = b'' # ensure that extramsg is string
3205 extramsg = b'' # ensure that extramsg is string
3208
3206
3209 ui.pushbuffer()
3207 ui.pushbuffer()
3210 t.show(ctx, extramsg=extramsg)
3208 t.show(ctx, extramsg=extramsg)
3211 return ui.popbuffer()
3209 return ui.popbuffer()
3212
3210
3213
3211
3214 def hgprefix(msg):
3212 def hgprefix(msg):
3215 return b"\n".join([b"HG: %s" % a for a in msg.split(b"\n") if a])
3213 return b"\n".join([b"HG: %s" % a for a in msg.split(b"\n") if a])
3216
3214
3217
3215
3218 def buildcommittext(repo, ctx, subs, extramsg):
3216 def buildcommittext(repo, ctx, subs, extramsg):
3219 edittext = []
3217 edittext = []
3220 modified, added, removed = ctx.modified(), ctx.added(), ctx.removed()
3218 modified, added, removed = ctx.modified(), ctx.added(), ctx.removed()
3221 if ctx.description():
3219 if ctx.description():
3222 edittext.append(ctx.description())
3220 edittext.append(ctx.description())
3223 edittext.append(b"")
3221 edittext.append(b"")
3224 edittext.append(b"") # Empty line between message and comments.
3222 edittext.append(b"") # Empty line between message and comments.
3225 edittext.append(
3223 edittext.append(
3226 hgprefix(
3224 hgprefix(
3227 _(
3225 _(
3228 b"Enter commit message."
3226 b"Enter commit message."
3229 b" Lines beginning with 'HG:' are removed."
3227 b" Lines beginning with 'HG:' are removed."
3230 )
3228 )
3231 )
3229 )
3232 )
3230 )
3233 edittext.append(hgprefix(extramsg))
3231 edittext.append(hgprefix(extramsg))
3234 edittext.append(b"HG: --")
3232 edittext.append(b"HG: --")
3235 edittext.append(hgprefix(_(b"user: %s") % ctx.user()))
3233 edittext.append(hgprefix(_(b"user: %s") % ctx.user()))
3236 if ctx.p2():
3234 if ctx.p2():
3237 edittext.append(hgprefix(_(b"branch merge")))
3235 edittext.append(hgprefix(_(b"branch merge")))
3238 if ctx.branch():
3236 if ctx.branch():
3239 edittext.append(hgprefix(_(b"branch '%s'") % ctx.branch()))
3237 edittext.append(hgprefix(_(b"branch '%s'") % ctx.branch()))
3240 if bookmarks.isactivewdirparent(repo):
3238 if bookmarks.isactivewdirparent(repo):
3241 edittext.append(hgprefix(_(b"bookmark '%s'") % repo._activebookmark))
3239 edittext.append(hgprefix(_(b"bookmark '%s'") % repo._activebookmark))
3242 edittext.extend([hgprefix(_(b"subrepo %s") % s) for s in subs])
3240 edittext.extend([hgprefix(_(b"subrepo %s") % s) for s in subs])
3243 edittext.extend([hgprefix(_(b"added %s") % f) for f in added])
3241 edittext.extend([hgprefix(_(b"added %s") % f) for f in added])
3244 edittext.extend([hgprefix(_(b"changed %s") % f) for f in modified])
3242 edittext.extend([hgprefix(_(b"changed %s") % f) for f in modified])
3245 edittext.extend([hgprefix(_(b"removed %s") % f) for f in removed])
3243 edittext.extend([hgprefix(_(b"removed %s") % f) for f in removed])
3246 if not added and not modified and not removed:
3244 if not added and not modified and not removed:
3247 edittext.append(hgprefix(_(b"no files changed")))
3245 edittext.append(hgprefix(_(b"no files changed")))
3248 edittext.append(b"")
3246 edittext.append(b"")
3249
3247
3250 return b"\n".join(edittext)
3248 return b"\n".join(edittext)
3251
3249
3252
3250
3253 def commitstatus(repo, node, branch, bheads=None, opts=None):
3251 def commitstatus(repo, node, branch, bheads=None, opts=None):
3254 if opts is None:
3252 if opts is None:
3255 opts = {}
3253 opts = {}
3256 ctx = repo[node]
3254 ctx = repo[node]
3257 parents = ctx.parents()
3255 parents = ctx.parents()
3258
3256
3259 if (
3257 if (
3260 not opts.get(b'amend')
3258 not opts.get(b'amend')
3261 and bheads
3259 and bheads
3262 and node not in bheads
3260 and node not in bheads
3263 and not [
3261 and not [
3264 x for x in parents if x.node() in bheads and x.branch() == branch
3262 x for x in parents if x.node() in bheads and x.branch() == branch
3265 ]
3263 ]
3266 ):
3264 ):
3267 repo.ui.status(_(b'created new head\n'))
3265 repo.ui.status(_(b'created new head\n'))
3268 # The message is not printed for initial roots. For the other
3266 # The message is not printed for initial roots. For the other
3269 # changesets, it is printed in the following situations:
3267 # changesets, it is printed in the following situations:
3270 #
3268 #
3271 # Par column: for the 2 parents with ...
3269 # Par column: for the 2 parents with ...
3272 # N: null or no parent
3270 # N: null or no parent
3273 # B: parent is on another named branch
3271 # B: parent is on another named branch
3274 # C: parent is a regular non head changeset
3272 # C: parent is a regular non head changeset
3275 # H: parent was a branch head of the current branch
3273 # H: parent was a branch head of the current branch
3276 # Msg column: whether we print "created new head" message
3274 # Msg column: whether we print "created new head" message
3277 # In the following, it is assumed that there already exists some
3275 # In the following, it is assumed that there already exists some
3278 # initial branch heads of the current branch, otherwise nothing is
3276 # initial branch heads of the current branch, otherwise nothing is
3279 # printed anyway.
3277 # printed anyway.
3280 #
3278 #
3281 # Par Msg Comment
3279 # Par Msg Comment
3282 # N N y additional topo root
3280 # N N y additional topo root
3283 #
3281 #
3284 # B N y additional branch root
3282 # B N y additional branch root
3285 # C N y additional topo head
3283 # C N y additional topo head
3286 # H N n usual case
3284 # H N n usual case
3287 #
3285 #
3288 # B B y weird additional branch root
3286 # B B y weird additional branch root
3289 # C B y branch merge
3287 # C B y branch merge
3290 # H B n merge with named branch
3288 # H B n merge with named branch
3291 #
3289 #
3292 # C C y additional head from merge
3290 # C C y additional head from merge
3293 # C H n merge with a head
3291 # C H n merge with a head
3294 #
3292 #
3295 # H H n head merge: head count decreases
3293 # H H n head merge: head count decreases
3296
3294
3297 if not opts.get(b'close_branch'):
3295 if not opts.get(b'close_branch'):
3298 for r in parents:
3296 for r in parents:
3299 if r.closesbranch() and r.branch() == branch:
3297 if r.closesbranch() and r.branch() == branch:
3300 repo.ui.status(
3298 repo.ui.status(
3301 _(b'reopening closed branch head %d\n') % r.rev()
3299 _(b'reopening closed branch head %d\n') % r.rev()
3302 )
3300 )
3303
3301
3304 if repo.ui.debugflag:
3302 if repo.ui.debugflag:
3305 repo.ui.write(
3303 repo.ui.write(
3306 _(b'committed changeset %d:%s\n') % (ctx.rev(), ctx.hex())
3304 _(b'committed changeset %d:%s\n') % (ctx.rev(), ctx.hex())
3307 )
3305 )
3308 elif repo.ui.verbose:
3306 elif repo.ui.verbose:
3309 repo.ui.write(_(b'committed changeset %d:%s\n') % (ctx.rev(), ctx))
3307 repo.ui.write(_(b'committed changeset %d:%s\n') % (ctx.rev(), ctx))
3310
3308
3311
3309
3312 def postcommitstatus(repo, pats, opts):
3310 def postcommitstatus(repo, pats, opts):
3313 return repo.status(match=scmutil.match(repo[None], pats, opts))
3311 return repo.status(match=scmutil.match(repo[None], pats, opts))
3314
3312
3315
3313
3316 def revert(ui, repo, ctx, parents, *pats, **opts):
3314 def revert(ui, repo, ctx, parents, *pats, **opts):
3317 opts = pycompat.byteskwargs(opts)
3315 opts = pycompat.byteskwargs(opts)
3318 parent, p2 = parents
3316 parent, p2 = parents
3319 node = ctx.node()
3317 node = ctx.node()
3320
3318
3321 mf = ctx.manifest()
3319 mf = ctx.manifest()
3322 if node == p2:
3320 if node == p2:
3323 parent = p2
3321 parent = p2
3324
3322
3325 # need all matching names in dirstate and manifest of target rev,
3323 # need all matching names in dirstate and manifest of target rev,
3326 # so have to walk both. do not print errors if files exist in one
3324 # so have to walk both. do not print errors if files exist in one
3327 # but not other. in both cases, filesets should be evaluated against
3325 # but not other. in both cases, filesets should be evaluated against
3328 # workingctx to get consistent result (issue4497). this means 'set:**'
3326 # workingctx to get consistent result (issue4497). this means 'set:**'
3329 # cannot be used to select missing files from target rev.
3327 # cannot be used to select missing files from target rev.
3330
3328
3331 # `names` is a mapping for all elements in working copy and target revision
3329 # `names` is a mapping for all elements in working copy and target revision
3332 # The mapping is in the form:
3330 # The mapping is in the form:
3333 # <abs path in repo> -> (<path from CWD>, <exactly specified by matcher?>)
3331 # <abs path in repo> -> (<path from CWD>, <exactly specified by matcher?>)
3334 names = {}
3332 names = {}
3335 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
3333 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
3336
3334
3337 with repo.wlock():
3335 with repo.wlock():
3338 ## filling of the `names` mapping
3336 ## filling of the `names` mapping
3339 # walk dirstate to fill `names`
3337 # walk dirstate to fill `names`
3340
3338
3341 interactive = opts.get(b'interactive', False)
3339 interactive = opts.get(b'interactive', False)
3342 wctx = repo[None]
3340 wctx = repo[None]
3343 m = scmutil.match(wctx, pats, opts)
3341 m = scmutil.match(wctx, pats, opts)
3344
3342
3345 # we'll need this later
3343 # we'll need this later
3346 targetsubs = sorted(s for s in wctx.substate if m(s))
3344 targetsubs = sorted(s for s in wctx.substate if m(s))
3347
3345
3348 if not m.always():
3346 if not m.always():
3349 matcher = matchmod.badmatch(m, lambda x, y: False)
3347 matcher = matchmod.badmatch(m, lambda x, y: False)
3350 for abs in wctx.walk(matcher):
3348 for abs in wctx.walk(matcher):
3351 names[abs] = m.exact(abs)
3349 names[abs] = m.exact(abs)
3352
3350
3353 # walk target manifest to fill `names`
3351 # walk target manifest to fill `names`
3354
3352
3355 def badfn(path, msg):
3353 def badfn(path, msg):
3356 if path in names:
3354 if path in names:
3357 return
3355 return
3358 if path in ctx.substate:
3356 if path in ctx.substate:
3359 return
3357 return
3360 path_ = path + b'/'
3358 path_ = path + b'/'
3361 for f in names:
3359 for f in names:
3362 if f.startswith(path_):
3360 if f.startswith(path_):
3363 return
3361 return
3364 ui.warn(b"%s: %s\n" % (uipathfn(path), msg))
3362 ui.warn(b"%s: %s\n" % (uipathfn(path), msg))
3365
3363
3366 for abs in ctx.walk(matchmod.badmatch(m, badfn)):
3364 for abs in ctx.walk(matchmod.badmatch(m, badfn)):
3367 if abs not in names:
3365 if abs not in names:
3368 names[abs] = m.exact(abs)
3366 names[abs] = m.exact(abs)
3369
3367
3370 # Find status of all file in `names`.
3368 # Find status of all file in `names`.
3371 m = scmutil.matchfiles(repo, names)
3369 m = scmutil.matchfiles(repo, names)
3372
3370
3373 changes = repo.status(
3371 changes = repo.status(
3374 node1=node, match=m, unknown=True, ignored=True, clean=True
3372 node1=node, match=m, unknown=True, ignored=True, clean=True
3375 )
3373 )
3376 else:
3374 else:
3377 changes = repo.status(node1=node, match=m)
3375 changes = repo.status(node1=node, match=m)
3378 for kind in changes:
3376 for kind in changes:
3379 for abs in kind:
3377 for abs in kind:
3380 names[abs] = m.exact(abs)
3378 names[abs] = m.exact(abs)
3381
3379
3382 m = scmutil.matchfiles(repo, names)
3380 m = scmutil.matchfiles(repo, names)
3383
3381
3384 modified = set(changes.modified)
3382 modified = set(changes.modified)
3385 added = set(changes.added)
3383 added = set(changes.added)
3386 removed = set(changes.removed)
3384 removed = set(changes.removed)
3387 _deleted = set(changes.deleted)
3385 _deleted = set(changes.deleted)
3388 unknown = set(changes.unknown)
3386 unknown = set(changes.unknown)
3389 unknown.update(changes.ignored)
3387 unknown.update(changes.ignored)
3390 clean = set(changes.clean)
3388 clean = set(changes.clean)
3391 modadded = set()
3389 modadded = set()
3392
3390
3393 # We need to account for the state of the file in the dirstate,
3391 # We need to account for the state of the file in the dirstate,
3394 # even when we revert against something else than parent. This will
3392 # even when we revert against something else than parent. This will
3395 # slightly alter the behavior of revert (doing back up or not, delete
3393 # slightly alter the behavior of revert (doing back up or not, delete
3396 # or just forget etc).
3394 # or just forget etc).
3397 if parent == node:
3395 if parent == node:
3398 dsmodified = modified
3396 dsmodified = modified
3399 dsadded = added
3397 dsadded = added
3400 dsremoved = removed
3398 dsremoved = removed
3401 # store all local modifications, useful later for rename detection
3399 # store all local modifications, useful later for rename detection
3402 localchanges = dsmodified | dsadded
3400 localchanges = dsmodified | dsadded
3403 modified, added, removed = set(), set(), set()
3401 modified, added, removed = set(), set(), set()
3404 else:
3402 else:
3405 changes = repo.status(node1=parent, match=m)
3403 changes = repo.status(node1=parent, match=m)
3406 dsmodified = set(changes.modified)
3404 dsmodified = set(changes.modified)
3407 dsadded = set(changes.added)
3405 dsadded = set(changes.added)
3408 dsremoved = set(changes.removed)
3406 dsremoved = set(changes.removed)
3409 # store all local modifications, useful later for rename detection
3407 # store all local modifications, useful later for rename detection
3410 localchanges = dsmodified | dsadded
3408 localchanges = dsmodified | dsadded
3411
3409
3412 # only take into account for removes between wc and target
3410 # only take into account for removes between wc and target
3413 clean |= dsremoved - removed
3411 clean |= dsremoved - removed
3414 dsremoved &= removed
3412 dsremoved &= removed
3415 # distinct between dirstate remove and other
3413 # distinct between dirstate remove and other
3416 removed -= dsremoved
3414 removed -= dsremoved
3417
3415
3418 modadded = added & dsmodified
3416 modadded = added & dsmodified
3419 added -= modadded
3417 added -= modadded
3420
3418
3421 # tell newly modified apart.
3419 # tell newly modified apart.
3422 dsmodified &= modified
3420 dsmodified &= modified
3423 dsmodified |= modified & dsadded # dirstate added may need backup
3421 dsmodified |= modified & dsadded # dirstate added may need backup
3424 modified -= dsmodified
3422 modified -= dsmodified
3425
3423
3426 # We need to wait for some post-processing to update this set
3424 # We need to wait for some post-processing to update this set
3427 # before making the distinction. The dirstate will be used for
3425 # before making the distinction. The dirstate will be used for
3428 # that purpose.
3426 # that purpose.
3429 dsadded = added
3427 dsadded = added
3430
3428
3431 # in case of merge, files that are actually added can be reported as
3429 # in case of merge, files that are actually added can be reported as
3432 # modified, we need to post process the result
3430 # modified, we need to post process the result
3433 if p2 != nullid:
3431 if p2 != nullid:
3434 mergeadd = set(dsmodified)
3432 mergeadd = set(dsmodified)
3435 for path in dsmodified:
3433 for path in dsmodified:
3436 if path in mf:
3434 if path in mf:
3437 mergeadd.remove(path)
3435 mergeadd.remove(path)
3438 dsadded |= mergeadd
3436 dsadded |= mergeadd
3439 dsmodified -= mergeadd
3437 dsmodified -= mergeadd
3440
3438
3441 # if f is a rename, update `names` to also revert the source
3439 # if f is a rename, update `names` to also revert the source
3442 for f in localchanges:
3440 for f in localchanges:
3443 src = repo.dirstate.copied(f)
3441 src = repo.dirstate.copied(f)
3444 # XXX should we check for rename down to target node?
3442 # XXX should we check for rename down to target node?
3445 if src and src not in names and repo.dirstate[src] == b'r':
3443 if src and src not in names and repo.dirstate[src] == b'r':
3446 dsremoved.add(src)
3444 dsremoved.add(src)
3447 names[src] = True
3445 names[src] = True
3448
3446
3449 # determine the exact nature of the deleted changesets
3447 # determine the exact nature of the deleted changesets
3450 deladded = set(_deleted)
3448 deladded = set(_deleted)
3451 for path in _deleted:
3449 for path in _deleted:
3452 if path in mf:
3450 if path in mf:
3453 deladded.remove(path)
3451 deladded.remove(path)
3454 deleted = _deleted - deladded
3452 deleted = _deleted - deladded
3455
3453
3456 # distinguish between file to forget and the other
3454 # distinguish between file to forget and the other
3457 added = set()
3455 added = set()
3458 for abs in dsadded:
3456 for abs in dsadded:
3459 if repo.dirstate[abs] != b'a':
3457 if repo.dirstate[abs] != b'a':
3460 added.add(abs)
3458 added.add(abs)
3461 dsadded -= added
3459 dsadded -= added
3462
3460
3463 for abs in deladded:
3461 for abs in deladded:
3464 if repo.dirstate[abs] == b'a':
3462 if repo.dirstate[abs] == b'a':
3465 dsadded.add(abs)
3463 dsadded.add(abs)
3466 deladded -= dsadded
3464 deladded -= dsadded
3467
3465
3468 # For files marked as removed, we check if an unknown file is present at
3466 # For files marked as removed, we check if an unknown file is present at
3469 # the same path. If a such file exists it may need to be backed up.
3467 # the same path. If a such file exists it may need to be backed up.
3470 # Making the distinction at this stage helps have simpler backup
3468 # Making the distinction at this stage helps have simpler backup
3471 # logic.
3469 # logic.
3472 removunk = set()
3470 removunk = set()
3473 for abs in removed:
3471 for abs in removed:
3474 target = repo.wjoin(abs)
3472 target = repo.wjoin(abs)
3475 if os.path.lexists(target):
3473 if os.path.lexists(target):
3476 removunk.add(abs)
3474 removunk.add(abs)
3477 removed -= removunk
3475 removed -= removunk
3478
3476
3479 dsremovunk = set()
3477 dsremovunk = set()
3480 for abs in dsremoved:
3478 for abs in dsremoved:
3481 target = repo.wjoin(abs)
3479 target = repo.wjoin(abs)
3482 if os.path.lexists(target):
3480 if os.path.lexists(target):
3483 dsremovunk.add(abs)
3481 dsremovunk.add(abs)
3484 dsremoved -= dsremovunk
3482 dsremoved -= dsremovunk
3485
3483
3486 # action to be actually performed by revert
3484 # action to be actually performed by revert
3487 # (<list of file>, message>) tuple
3485 # (<list of file>, message>) tuple
3488 actions = {
3486 actions = {
3489 b'revert': ([], _(b'reverting %s\n')),
3487 b'revert': ([], _(b'reverting %s\n')),
3490 b'add': ([], _(b'adding %s\n')),
3488 b'add': ([], _(b'adding %s\n')),
3491 b'remove': ([], _(b'removing %s\n')),
3489 b'remove': ([], _(b'removing %s\n')),
3492 b'drop': ([], _(b'removing %s\n')),
3490 b'drop': ([], _(b'removing %s\n')),
3493 b'forget': ([], _(b'forgetting %s\n')),
3491 b'forget': ([], _(b'forgetting %s\n')),
3494 b'undelete': ([], _(b'undeleting %s\n')),
3492 b'undelete': ([], _(b'undeleting %s\n')),
3495 b'noop': (None, _(b'no changes needed to %s\n')),
3493 b'noop': (None, _(b'no changes needed to %s\n')),
3496 b'unknown': (None, _(b'file not managed: %s\n')),
3494 b'unknown': (None, _(b'file not managed: %s\n')),
3497 }
3495 }
3498
3496
3499 # "constant" that convey the backup strategy.
3497 # "constant" that convey the backup strategy.
3500 # All set to `discard` if `no-backup` is set do avoid checking
3498 # All set to `discard` if `no-backup` is set do avoid checking
3501 # no_backup lower in the code.
3499 # no_backup lower in the code.
3502 # These values are ordered for comparison purposes
3500 # These values are ordered for comparison purposes
3503 backupinteractive = 3 # do backup if interactively modified
3501 backupinteractive = 3 # do backup if interactively modified
3504 backup = 2 # unconditionally do backup
3502 backup = 2 # unconditionally do backup
3505 check = 1 # check if the existing file differs from target
3503 check = 1 # check if the existing file differs from target
3506 discard = 0 # never do backup
3504 discard = 0 # never do backup
3507 if opts.get(b'no_backup'):
3505 if opts.get(b'no_backup'):
3508 backupinteractive = backup = check = discard
3506 backupinteractive = backup = check = discard
3509 if interactive:
3507 if interactive:
3510 dsmodifiedbackup = backupinteractive
3508 dsmodifiedbackup = backupinteractive
3511 else:
3509 else:
3512 dsmodifiedbackup = backup
3510 dsmodifiedbackup = backup
3513 tobackup = set()
3511 tobackup = set()
3514
3512
3515 backupanddel = actions[b'remove']
3513 backupanddel = actions[b'remove']
3516 if not opts.get(b'no_backup'):
3514 if not opts.get(b'no_backup'):
3517 backupanddel = actions[b'drop']
3515 backupanddel = actions[b'drop']
3518
3516
3519 disptable = (
3517 disptable = (
3520 # dispatch table:
3518 # dispatch table:
3521 # file state
3519 # file state
3522 # action
3520 # action
3523 # make backup
3521 # make backup
3524 ## Sets that results that will change file on disk
3522 ## Sets that results that will change file on disk
3525 # Modified compared to target, no local change
3523 # Modified compared to target, no local change
3526 (modified, actions[b'revert'], discard),
3524 (modified, actions[b'revert'], discard),
3527 # Modified compared to target, but local file is deleted
3525 # Modified compared to target, but local file is deleted
3528 (deleted, actions[b'revert'], discard),
3526 (deleted, actions[b'revert'], discard),
3529 # Modified compared to target, local change
3527 # Modified compared to target, local change
3530 (dsmodified, actions[b'revert'], dsmodifiedbackup),
3528 (dsmodified, actions[b'revert'], dsmodifiedbackup),
3531 # Added since target
3529 # Added since target
3532 (added, actions[b'remove'], discard),
3530 (added, actions[b'remove'], discard),
3533 # Added in working directory
3531 # Added in working directory
3534 (dsadded, actions[b'forget'], discard),
3532 (dsadded, actions[b'forget'], discard),
3535 # Added since target, have local modification
3533 # Added since target, have local modification
3536 (modadded, backupanddel, backup),
3534 (modadded, backupanddel, backup),
3537 # Added since target but file is missing in working directory
3535 # Added since target but file is missing in working directory
3538 (deladded, actions[b'drop'], discard),
3536 (deladded, actions[b'drop'], discard),
3539 # Removed since target, before working copy parent
3537 # Removed since target, before working copy parent
3540 (removed, actions[b'add'], discard),
3538 (removed, actions[b'add'], discard),
3541 # Same as `removed` but an unknown file exists at the same path
3539 # Same as `removed` but an unknown file exists at the same path
3542 (removunk, actions[b'add'], check),
3540 (removunk, actions[b'add'], check),
3543 # Removed since targe, marked as such in working copy parent
3541 # Removed since targe, marked as such in working copy parent
3544 (dsremoved, actions[b'undelete'], discard),
3542 (dsremoved, actions[b'undelete'], discard),
3545 # Same as `dsremoved` but an unknown file exists at the same path
3543 # Same as `dsremoved` but an unknown file exists at the same path
3546 (dsremovunk, actions[b'undelete'], check),
3544 (dsremovunk, actions[b'undelete'], check),
3547 ## the following sets does not result in any file changes
3545 ## the following sets does not result in any file changes
3548 # File with no modification
3546 # File with no modification
3549 (clean, actions[b'noop'], discard),
3547 (clean, actions[b'noop'], discard),
3550 # Existing file, not tracked anywhere
3548 # Existing file, not tracked anywhere
3551 (unknown, actions[b'unknown'], discard),
3549 (unknown, actions[b'unknown'], discard),
3552 )
3550 )
3553
3551
3554 for abs, exact in sorted(names.items()):
3552 for abs, exact in sorted(names.items()):
3555 # target file to be touch on disk (relative to cwd)
3553 # target file to be touch on disk (relative to cwd)
3556 target = repo.wjoin(abs)
3554 target = repo.wjoin(abs)
3557 # search the entry in the dispatch table.
3555 # search the entry in the dispatch table.
3558 # if the file is in any of these sets, it was touched in the working
3556 # if the file is in any of these sets, it was touched in the working
3559 # directory parent and we are sure it needs to be reverted.
3557 # directory parent and we are sure it needs to be reverted.
3560 for table, (xlist, msg), dobackup in disptable:
3558 for table, (xlist, msg), dobackup in disptable:
3561 if abs not in table:
3559 if abs not in table:
3562 continue
3560 continue
3563 if xlist is not None:
3561 if xlist is not None:
3564 xlist.append(abs)
3562 xlist.append(abs)
3565 if dobackup:
3563 if dobackup:
3566 # If in interactive mode, don't automatically create
3564 # If in interactive mode, don't automatically create
3567 # .orig files (issue4793)
3565 # .orig files (issue4793)
3568 if dobackup == backupinteractive:
3566 if dobackup == backupinteractive:
3569 tobackup.add(abs)
3567 tobackup.add(abs)
3570 elif backup <= dobackup or wctx[abs].cmp(ctx[abs]):
3568 elif backup <= dobackup or wctx[abs].cmp(ctx[abs]):
3571 absbakname = scmutil.backuppath(ui, repo, abs)
3569 absbakname = scmutil.backuppath(ui, repo, abs)
3572 bakname = os.path.relpath(
3570 bakname = os.path.relpath(
3573 absbakname, start=repo.root
3571 absbakname, start=repo.root
3574 )
3572 )
3575 ui.note(
3573 ui.note(
3576 _(b'saving current version of %s as %s\n')
3574 _(b'saving current version of %s as %s\n')
3577 % (uipathfn(abs), uipathfn(bakname))
3575 % (uipathfn(abs), uipathfn(bakname))
3578 )
3576 )
3579 if not opts.get(b'dry_run'):
3577 if not opts.get(b'dry_run'):
3580 if interactive:
3578 if interactive:
3581 util.copyfile(target, absbakname)
3579 util.copyfile(target, absbakname)
3582 else:
3580 else:
3583 util.rename(target, absbakname)
3581 util.rename(target, absbakname)
3584 if opts.get(b'dry_run'):
3582 if opts.get(b'dry_run'):
3585 if ui.verbose or not exact:
3583 if ui.verbose or not exact:
3586 ui.status(msg % uipathfn(abs))
3584 ui.status(msg % uipathfn(abs))
3587 elif exact:
3585 elif exact:
3588 ui.warn(msg % uipathfn(abs))
3586 ui.warn(msg % uipathfn(abs))
3589 break
3587 break
3590
3588
3591 if not opts.get(b'dry_run'):
3589 if not opts.get(b'dry_run'):
3592 needdata = (b'revert', b'add', b'undelete')
3590 needdata = (b'revert', b'add', b'undelete')
3593 oplist = [actions[name][0] for name in needdata]
3591 oplist = [actions[name][0] for name in needdata]
3594 prefetch = scmutil.prefetchfiles
3592 prefetch = scmutil.prefetchfiles
3595 matchfiles = scmutil.matchfiles
3593 matchfiles = scmutil.matchfiles
3596 prefetch(
3594 prefetch(
3597 repo,
3595 repo,
3598 [ctx.rev()],
3596 [ctx.rev()],
3599 matchfiles(repo, [f for sublist in oplist for f in sublist]),
3597 matchfiles(repo, [f for sublist in oplist for f in sublist]),
3600 )
3598 )
3601 match = scmutil.match(repo[None], pats)
3599 match = scmutil.match(repo[None], pats)
3602 _performrevert(
3600 _performrevert(
3603 repo,
3601 repo,
3604 parents,
3602 parents,
3605 ctx,
3603 ctx,
3606 names,
3604 names,
3607 uipathfn,
3605 uipathfn,
3608 actions,
3606 actions,
3609 match,
3607 match,
3610 interactive,
3608 interactive,
3611 tobackup,
3609 tobackup,
3612 )
3610 )
3613
3611
3614 if targetsubs:
3612 if targetsubs:
3615 # Revert the subrepos on the revert list
3613 # Revert the subrepos on the revert list
3616 for sub in targetsubs:
3614 for sub in targetsubs:
3617 try:
3615 try:
3618 wctx.sub(sub).revert(
3616 wctx.sub(sub).revert(
3619 ctx.substate[sub], *pats, **pycompat.strkwargs(opts)
3617 ctx.substate[sub], *pats, **pycompat.strkwargs(opts)
3620 )
3618 )
3621 except KeyError:
3619 except KeyError:
3622 raise error.Abort(
3620 raise error.Abort(
3623 b"subrepository '%s' does not exist in %s!"
3621 b"subrepository '%s' does not exist in %s!"
3624 % (sub, short(ctx.node()))
3622 % (sub, short(ctx.node()))
3625 )
3623 )
3626
3624
3627
3625
3628 def _performrevert(
3626 def _performrevert(
3629 repo,
3627 repo,
3630 parents,
3628 parents,
3631 ctx,
3629 ctx,
3632 names,
3630 names,
3633 uipathfn,
3631 uipathfn,
3634 actions,
3632 actions,
3635 match,
3633 match,
3636 interactive=False,
3634 interactive=False,
3637 tobackup=None,
3635 tobackup=None,
3638 ):
3636 ):
3639 """function that actually perform all the actions computed for revert
3637 """function that actually perform all the actions computed for revert
3640
3638
3641 This is an independent function to let extension to plug in and react to
3639 This is an independent function to let extension to plug in and react to
3642 the imminent revert.
3640 the imminent revert.
3643
3641
3644 Make sure you have the working directory locked when calling this function.
3642 Make sure you have the working directory locked when calling this function.
3645 """
3643 """
3646 parent, p2 = parents
3644 parent, p2 = parents
3647 node = ctx.node()
3645 node = ctx.node()
3648 excluded_files = []
3646 excluded_files = []
3649
3647
3650 def checkout(f):
3648 def checkout(f):
3651 fc = ctx[f]
3649 fc = ctx[f]
3652 repo.wwrite(f, fc.data(), fc.flags())
3650 repo.wwrite(f, fc.data(), fc.flags())
3653
3651
3654 def doremove(f):
3652 def doremove(f):
3655 try:
3653 try:
3656 rmdir = repo.ui.configbool(b'experimental', b'removeemptydirs')
3654 rmdir = repo.ui.configbool(b'experimental', b'removeemptydirs')
3657 repo.wvfs.unlinkpath(f, rmdir=rmdir)
3655 repo.wvfs.unlinkpath(f, rmdir=rmdir)
3658 except OSError:
3656 except OSError:
3659 pass
3657 pass
3660 repo.dirstate.remove(f)
3658 repo.dirstate.remove(f)
3661
3659
3662 def prntstatusmsg(action, f):
3660 def prntstatusmsg(action, f):
3663 exact = names[f]
3661 exact = names[f]
3664 if repo.ui.verbose or not exact:
3662 if repo.ui.verbose or not exact:
3665 repo.ui.status(actions[action][1] % uipathfn(f))
3663 repo.ui.status(actions[action][1] % uipathfn(f))
3666
3664
3667 audit_path = pathutil.pathauditor(repo.root, cached=True)
3665 audit_path = pathutil.pathauditor(repo.root, cached=True)
3668 for f in actions[b'forget'][0]:
3666 for f in actions[b'forget'][0]:
3669 if interactive:
3667 if interactive:
3670 choice = repo.ui.promptchoice(
3668 choice = repo.ui.promptchoice(
3671 _(b"forget added file %s (Yn)?$$ &Yes $$ &No") % uipathfn(f)
3669 _(b"forget added file %s (Yn)?$$ &Yes $$ &No") % uipathfn(f)
3672 )
3670 )
3673 if choice == 0:
3671 if choice == 0:
3674 prntstatusmsg(b'forget', f)
3672 prntstatusmsg(b'forget', f)
3675 repo.dirstate.drop(f)
3673 repo.dirstate.drop(f)
3676 else:
3674 else:
3677 excluded_files.append(f)
3675 excluded_files.append(f)
3678 else:
3676 else:
3679 prntstatusmsg(b'forget', f)
3677 prntstatusmsg(b'forget', f)
3680 repo.dirstate.drop(f)
3678 repo.dirstate.drop(f)
3681 for f in actions[b'remove'][0]:
3679 for f in actions[b'remove'][0]:
3682 audit_path(f)
3680 audit_path(f)
3683 if interactive:
3681 if interactive:
3684 choice = repo.ui.promptchoice(
3682 choice = repo.ui.promptchoice(
3685 _(b"remove added file %s (Yn)?$$ &Yes $$ &No") % uipathfn(f)
3683 _(b"remove added file %s (Yn)?$$ &Yes $$ &No") % uipathfn(f)
3686 )
3684 )
3687 if choice == 0:
3685 if choice == 0:
3688 prntstatusmsg(b'remove', f)
3686 prntstatusmsg(b'remove', f)
3689 doremove(f)
3687 doremove(f)
3690 else:
3688 else:
3691 excluded_files.append(f)
3689 excluded_files.append(f)
3692 else:
3690 else:
3693 prntstatusmsg(b'remove', f)
3691 prntstatusmsg(b'remove', f)
3694 doremove(f)
3692 doremove(f)
3695 for f in actions[b'drop'][0]:
3693 for f in actions[b'drop'][0]:
3696 audit_path(f)
3694 audit_path(f)
3697 prntstatusmsg(b'drop', f)
3695 prntstatusmsg(b'drop', f)
3698 repo.dirstate.remove(f)
3696 repo.dirstate.remove(f)
3699
3697
3700 normal = None
3698 normal = None
3701 if node == parent:
3699 if node == parent:
3702 # We're reverting to our parent. If possible, we'd like status
3700 # We're reverting to our parent. If possible, we'd like status
3703 # to report the file as clean. We have to use normallookup for
3701 # to report the file as clean. We have to use normallookup for
3704 # merges to avoid losing information about merged/dirty files.
3702 # merges to avoid losing information about merged/dirty files.
3705 if p2 != nullid:
3703 if p2 != nullid:
3706 normal = repo.dirstate.normallookup
3704 normal = repo.dirstate.normallookup
3707 else:
3705 else:
3708 normal = repo.dirstate.normal
3706 normal = repo.dirstate.normal
3709
3707
3710 newlyaddedandmodifiedfiles = set()
3708 newlyaddedandmodifiedfiles = set()
3711 if interactive:
3709 if interactive:
3712 # Prompt the user for changes to revert
3710 # Prompt the user for changes to revert
3713 torevert = [f for f in actions[b'revert'][0] if f not in excluded_files]
3711 torevert = [f for f in actions[b'revert'][0] if f not in excluded_files]
3714 m = scmutil.matchfiles(repo, torevert)
3712 m = scmutil.matchfiles(repo, torevert)
3715 diffopts = patch.difffeatureopts(
3713 diffopts = patch.difffeatureopts(
3716 repo.ui,
3714 repo.ui,
3717 whitespace=True,
3715 whitespace=True,
3718 section=b'commands',
3716 section=b'commands',
3719 configprefix=b'revert.interactive.',
3717 configprefix=b'revert.interactive.',
3720 )
3718 )
3721 diffopts.nodates = True
3719 diffopts.nodates = True
3722 diffopts.git = True
3720 diffopts.git = True
3723 operation = b'apply'
3721 operation = b'apply'
3724 if node == parent:
3722 if node == parent:
3725 if repo.ui.configbool(
3723 if repo.ui.configbool(
3726 b'experimental', b'revert.interactive.select-to-keep'
3724 b'experimental', b'revert.interactive.select-to-keep'
3727 ):
3725 ):
3728 operation = b'keep'
3726 operation = b'keep'
3729 else:
3727 else:
3730 operation = b'discard'
3728 operation = b'discard'
3731
3729
3732 if operation == b'apply':
3730 if operation == b'apply':
3733 diff = patch.diff(repo, None, ctx.node(), m, opts=diffopts)
3731 diff = patch.diff(repo, None, ctx.node(), m, opts=diffopts)
3734 else:
3732 else:
3735 diff = patch.diff(repo, ctx.node(), None, m, opts=diffopts)
3733 diff = patch.diff(repo, ctx.node(), None, m, opts=diffopts)
3736 originalchunks = patch.parsepatch(diff)
3734 originalchunks = patch.parsepatch(diff)
3737
3735
3738 try:
3736 try:
3739
3737
3740 chunks, opts = recordfilter(
3738 chunks, opts = recordfilter(
3741 repo.ui, originalchunks, match, operation=operation
3739 repo.ui, originalchunks, match, operation=operation
3742 )
3740 )
3743 if operation == b'discard':
3741 if operation == b'discard':
3744 chunks = patch.reversehunks(chunks)
3742 chunks = patch.reversehunks(chunks)
3745
3743
3746 except error.PatchError as err:
3744 except error.PatchError as err:
3747 raise error.Abort(_(b'error parsing patch: %s') % err)
3745 raise error.Abort(_(b'error parsing patch: %s') % err)
3748
3746
3749 # FIXME: when doing an interactive revert of a copy, there's no way of
3747 # FIXME: when doing an interactive revert of a copy, there's no way of
3750 # performing a partial revert of the added file, the only option is
3748 # performing a partial revert of the added file, the only option is
3751 # "remove added file <name> (Yn)?", so we don't need to worry about the
3749 # "remove added file <name> (Yn)?", so we don't need to worry about the
3752 # alsorestore value. Ideally we'd be able to partially revert
3750 # alsorestore value. Ideally we'd be able to partially revert
3753 # copied/renamed files.
3751 # copied/renamed files.
3754 newlyaddedandmodifiedfiles, unusedalsorestore = newandmodified(
3752 newlyaddedandmodifiedfiles, unusedalsorestore = newandmodified(
3755 chunks, originalchunks
3753 chunks, originalchunks
3756 )
3754 )
3757 if tobackup is None:
3755 if tobackup is None:
3758 tobackup = set()
3756 tobackup = set()
3759 # Apply changes
3757 # Apply changes
3760 fp = stringio()
3758 fp = stringio()
3761 # chunks are serialized per file, but files aren't sorted
3759 # chunks are serialized per file, but files aren't sorted
3762 for f in sorted(set(c.header.filename() for c in chunks if ishunk(c))):
3760 for f in sorted(set(c.header.filename() for c in chunks if ishunk(c))):
3763 prntstatusmsg(b'revert', f)
3761 prntstatusmsg(b'revert', f)
3764 files = set()
3762 files = set()
3765 for c in chunks:
3763 for c in chunks:
3766 if ishunk(c):
3764 if ishunk(c):
3767 abs = c.header.filename()
3765 abs = c.header.filename()
3768 # Create a backup file only if this hunk should be backed up
3766 # Create a backup file only if this hunk should be backed up
3769 if c.header.filename() in tobackup:
3767 if c.header.filename() in tobackup:
3770 target = repo.wjoin(abs)
3768 target = repo.wjoin(abs)
3771 bakname = scmutil.backuppath(repo.ui, repo, abs)
3769 bakname = scmutil.backuppath(repo.ui, repo, abs)
3772 util.copyfile(target, bakname)
3770 util.copyfile(target, bakname)
3773 tobackup.remove(abs)
3771 tobackup.remove(abs)
3774 if abs not in files:
3772 if abs not in files:
3775 files.add(abs)
3773 files.add(abs)
3776 if operation == b'keep':
3774 if operation == b'keep':
3777 checkout(abs)
3775 checkout(abs)
3778 c.write(fp)
3776 c.write(fp)
3779 dopatch = fp.tell()
3777 dopatch = fp.tell()
3780 fp.seek(0)
3778 fp.seek(0)
3781 if dopatch:
3779 if dopatch:
3782 try:
3780 try:
3783 patch.internalpatch(repo.ui, repo, fp, 1, eolmode=None)
3781 patch.internalpatch(repo.ui, repo, fp, 1, eolmode=None)
3784 except error.PatchError as err:
3782 except error.PatchError as err:
3785 raise error.Abort(pycompat.bytestr(err))
3783 raise error.Abort(pycompat.bytestr(err))
3786 del fp
3784 del fp
3787 else:
3785 else:
3788 for f in actions[b'revert'][0]:
3786 for f in actions[b'revert'][0]:
3789 prntstatusmsg(b'revert', f)
3787 prntstatusmsg(b'revert', f)
3790 checkout(f)
3788 checkout(f)
3791 if normal:
3789 if normal:
3792 normal(f)
3790 normal(f)
3793
3791
3794 for f in actions[b'add'][0]:
3792 for f in actions[b'add'][0]:
3795 # Don't checkout modified files, they are already created by the diff
3793 # Don't checkout modified files, they are already created by the diff
3796 if f not in newlyaddedandmodifiedfiles:
3794 if f not in newlyaddedandmodifiedfiles:
3797 prntstatusmsg(b'add', f)
3795 prntstatusmsg(b'add', f)
3798 checkout(f)
3796 checkout(f)
3799 repo.dirstate.add(f)
3797 repo.dirstate.add(f)
3800
3798
3801 normal = repo.dirstate.normallookup
3799 normal = repo.dirstate.normallookup
3802 if node == parent and p2 == nullid:
3800 if node == parent and p2 == nullid:
3803 normal = repo.dirstate.normal
3801 normal = repo.dirstate.normal
3804 for f in actions[b'undelete'][0]:
3802 for f in actions[b'undelete'][0]:
3805 if interactive:
3803 if interactive:
3806 choice = repo.ui.promptchoice(
3804 choice = repo.ui.promptchoice(
3807 _(b"add back removed file %s (Yn)?$$ &Yes $$ &No") % f
3805 _(b"add back removed file %s (Yn)?$$ &Yes $$ &No") % f
3808 )
3806 )
3809 if choice == 0:
3807 if choice == 0:
3810 prntstatusmsg(b'undelete', f)
3808 prntstatusmsg(b'undelete', f)
3811 checkout(f)
3809 checkout(f)
3812 normal(f)
3810 normal(f)
3813 else:
3811 else:
3814 excluded_files.append(f)
3812 excluded_files.append(f)
3815 else:
3813 else:
3816 prntstatusmsg(b'undelete', f)
3814 prntstatusmsg(b'undelete', f)
3817 checkout(f)
3815 checkout(f)
3818 normal(f)
3816 normal(f)
3819
3817
3820 copied = copies.pathcopies(repo[parent], ctx)
3818 copied = copies.pathcopies(repo[parent], ctx)
3821
3819
3822 for f in (
3820 for f in (
3823 actions[b'add'][0] + actions[b'undelete'][0] + actions[b'revert'][0]
3821 actions[b'add'][0] + actions[b'undelete'][0] + actions[b'revert'][0]
3824 ):
3822 ):
3825 if f in copied:
3823 if f in copied:
3826 repo.dirstate.copy(copied[f], f)
3824 repo.dirstate.copy(copied[f], f)
3827
3825
3828
3826
3829 # a list of (ui, repo, otherpeer, opts, missing) functions called by
3827 # a list of (ui, repo, otherpeer, opts, missing) functions called by
3830 # commands.outgoing. "missing" is "missing" of the result of
3828 # commands.outgoing. "missing" is "missing" of the result of
3831 # "findcommonoutgoing()"
3829 # "findcommonoutgoing()"
3832 outgoinghooks = util.hooks()
3830 outgoinghooks = util.hooks()
3833
3831
3834 # a list of (ui, repo) functions called by commands.summary
3832 # a list of (ui, repo) functions called by commands.summary
3835 summaryhooks = util.hooks()
3833 summaryhooks = util.hooks()
3836
3834
3837 # a list of (ui, repo, opts, changes) functions called by commands.summary.
3835 # a list of (ui, repo, opts, changes) functions called by commands.summary.
3838 #
3836 #
3839 # functions should return tuple of booleans below, if 'changes' is None:
3837 # functions should return tuple of booleans below, if 'changes' is None:
3840 # (whether-incomings-are-needed, whether-outgoings-are-needed)
3838 # (whether-incomings-are-needed, whether-outgoings-are-needed)
3841 #
3839 #
3842 # otherwise, 'changes' is a tuple of tuples below:
3840 # otherwise, 'changes' is a tuple of tuples below:
3843 # - (sourceurl, sourcebranch, sourcepeer, incoming)
3841 # - (sourceurl, sourcebranch, sourcepeer, incoming)
3844 # - (desturl, destbranch, destpeer, outgoing)
3842 # - (desturl, destbranch, destpeer, outgoing)
3845 summaryremotehooks = util.hooks()
3843 summaryremotehooks = util.hooks()
3846
3844
3847
3845
3848 def checkunfinished(repo, commit=False, skipmerge=False):
3846 def checkunfinished(repo, commit=False, skipmerge=False):
3849 '''Look for an unfinished multistep operation, like graft, and abort
3847 '''Look for an unfinished multistep operation, like graft, and abort
3850 if found. It's probably good to check this right before
3848 if found. It's probably good to check this right before
3851 bailifchanged().
3849 bailifchanged().
3852 '''
3850 '''
3853 # Check for non-clearable states first, so things like rebase will take
3851 # Check for non-clearable states first, so things like rebase will take
3854 # precedence over update.
3852 # precedence over update.
3855 for state in statemod._unfinishedstates:
3853 for state in statemod._unfinishedstates:
3856 if (
3854 if (
3857 state._clearable
3855 state._clearable
3858 or (commit and state._allowcommit)
3856 or (commit and state._allowcommit)
3859 or state._reportonly
3857 or state._reportonly
3860 ):
3858 ):
3861 continue
3859 continue
3862 if state.isunfinished(repo):
3860 if state.isunfinished(repo):
3863 raise error.Abort(state.msg(), hint=state.hint())
3861 raise error.Abort(state.msg(), hint=state.hint())
3864
3862
3865 for s in statemod._unfinishedstates:
3863 for s in statemod._unfinishedstates:
3866 if (
3864 if (
3867 not s._clearable
3865 not s._clearable
3868 or (commit and s._allowcommit)
3866 or (commit and s._allowcommit)
3869 or (s._opname == b'merge' and skipmerge)
3867 or (s._opname == b'merge' and skipmerge)
3870 or s._reportonly
3868 or s._reportonly
3871 ):
3869 ):
3872 continue
3870 continue
3873 if s.isunfinished(repo):
3871 if s.isunfinished(repo):
3874 raise error.Abort(s.msg(), hint=s.hint())
3872 raise error.Abort(s.msg(), hint=s.hint())
3875
3873
3876
3874
3877 def clearunfinished(repo):
3875 def clearunfinished(repo):
3878 '''Check for unfinished operations (as above), and clear the ones
3876 '''Check for unfinished operations (as above), and clear the ones
3879 that are clearable.
3877 that are clearable.
3880 '''
3878 '''
3881 for state in statemod._unfinishedstates:
3879 for state in statemod._unfinishedstates:
3882 if state._reportonly:
3880 if state._reportonly:
3883 continue
3881 continue
3884 if not state._clearable and state.isunfinished(repo):
3882 if not state._clearable and state.isunfinished(repo):
3885 raise error.Abort(state.msg(), hint=state.hint())
3883 raise error.Abort(state.msg(), hint=state.hint())
3886
3884
3887 for s in statemod._unfinishedstates:
3885 for s in statemod._unfinishedstates:
3888 if s._opname == b'merge' or state._reportonly:
3886 if s._opname == b'merge' or state._reportonly:
3889 continue
3887 continue
3890 if s._clearable and s.isunfinished(repo):
3888 if s._clearable and s.isunfinished(repo):
3891 util.unlink(repo.vfs.join(s._fname))
3889 util.unlink(repo.vfs.join(s._fname))
3892
3890
3893
3891
3894 def getunfinishedstate(repo):
3892 def getunfinishedstate(repo):
3895 ''' Checks for unfinished operations and returns statecheck object
3893 ''' Checks for unfinished operations and returns statecheck object
3896 for it'''
3894 for it'''
3897 for state in statemod._unfinishedstates:
3895 for state in statemod._unfinishedstates:
3898 if state.isunfinished(repo):
3896 if state.isunfinished(repo):
3899 return state
3897 return state
3900 return None
3898 return None
3901
3899
3902
3900
3903 def howtocontinue(repo):
3901 def howtocontinue(repo):
3904 '''Check for an unfinished operation and return the command to finish
3902 '''Check for an unfinished operation and return the command to finish
3905 it.
3903 it.
3906
3904
3907 statemod._unfinishedstates list is checked for an unfinished operation
3905 statemod._unfinishedstates list is checked for an unfinished operation
3908 and the corresponding message to finish it is generated if a method to
3906 and the corresponding message to finish it is generated if a method to
3909 continue is supported by the operation.
3907 continue is supported by the operation.
3910
3908
3911 Returns a (msg, warning) tuple. 'msg' is a string and 'warning' is
3909 Returns a (msg, warning) tuple. 'msg' is a string and 'warning' is
3912 a boolean.
3910 a boolean.
3913 '''
3911 '''
3914 contmsg = _(b"continue: %s")
3912 contmsg = _(b"continue: %s")
3915 for state in statemod._unfinishedstates:
3913 for state in statemod._unfinishedstates:
3916 if not state._continueflag:
3914 if not state._continueflag:
3917 continue
3915 continue
3918 if state.isunfinished(repo):
3916 if state.isunfinished(repo):
3919 return contmsg % state.continuemsg(), True
3917 return contmsg % state.continuemsg(), True
3920 if repo[None].dirty(missing=True, merge=False, branch=False):
3918 if repo[None].dirty(missing=True, merge=False, branch=False):
3921 return contmsg % _(b"hg commit"), False
3919 return contmsg % _(b"hg commit"), False
3922 return None, None
3920 return None, None
3923
3921
3924
3922
3925 def checkafterresolved(repo):
3923 def checkafterresolved(repo):
3926 '''Inform the user about the next action after completing hg resolve
3924 '''Inform the user about the next action after completing hg resolve
3927
3925
3928 If there's a an unfinished operation that supports continue flag,
3926 If there's a an unfinished operation that supports continue flag,
3929 howtocontinue will yield repo.ui.warn as the reporter.
3927 howtocontinue will yield repo.ui.warn as the reporter.
3930
3928
3931 Otherwise, it will yield repo.ui.note.
3929 Otherwise, it will yield repo.ui.note.
3932 '''
3930 '''
3933 msg, warning = howtocontinue(repo)
3931 msg, warning = howtocontinue(repo)
3934 if msg is not None:
3932 if msg is not None:
3935 if warning:
3933 if warning:
3936 repo.ui.warn(b"%s\n" % msg)
3934 repo.ui.warn(b"%s\n" % msg)
3937 else:
3935 else:
3938 repo.ui.note(b"%s\n" % msg)
3936 repo.ui.note(b"%s\n" % msg)
3939
3937
3940
3938
3941 def wrongtooltocontinue(repo, task):
3939 def wrongtooltocontinue(repo, task):
3942 '''Raise an abort suggesting how to properly continue if there is an
3940 '''Raise an abort suggesting how to properly continue if there is an
3943 active task.
3941 active task.
3944
3942
3945 Uses howtocontinue() to find the active task.
3943 Uses howtocontinue() to find the active task.
3946
3944
3947 If there's no task (repo.ui.note for 'hg commit'), it does not offer
3945 If there's no task (repo.ui.note for 'hg commit'), it does not offer
3948 a hint.
3946 a hint.
3949 '''
3947 '''
3950 after = howtocontinue(repo)
3948 after = howtocontinue(repo)
3951 hint = None
3949 hint = None
3952 if after[1]:
3950 if after[1]:
3953 hint = after[0]
3951 hint = after[0]
3954 raise error.Abort(_(b'no %s in progress') % task, hint=hint)
3952 raise error.Abort(_(b'no %s in progress') % task, hint=hint)
3955
3953
3956
3954
3957 def abortgraft(ui, repo, graftstate):
3955 def abortgraft(ui, repo, graftstate):
3958 """abort the interrupted graft and rollbacks to the state before interrupted
3956 """abort the interrupted graft and rollbacks to the state before interrupted
3959 graft"""
3957 graft"""
3960 if not graftstate.exists():
3958 if not graftstate.exists():
3961 raise error.Abort(_(b"no interrupted graft to abort"))
3959 raise error.Abort(_(b"no interrupted graft to abort"))
3962 statedata = readgraftstate(repo, graftstate)
3960 statedata = readgraftstate(repo, graftstate)
3963 newnodes = statedata.get(b'newnodes')
3961 newnodes = statedata.get(b'newnodes')
3964 if newnodes is None:
3962 if newnodes is None:
3965 # and old graft state which does not have all the data required to abort
3963 # and old graft state which does not have all the data required to abort
3966 # the graft
3964 # the graft
3967 raise error.Abort(_(b"cannot abort using an old graftstate"))
3965 raise error.Abort(_(b"cannot abort using an old graftstate"))
3968
3966
3969 # changeset from which graft operation was started
3967 # changeset from which graft operation was started
3970 if len(newnodes) > 0:
3968 if len(newnodes) > 0:
3971 startctx = repo[newnodes[0]].p1()
3969 startctx = repo[newnodes[0]].p1()
3972 else:
3970 else:
3973 startctx = repo[b'.']
3971 startctx = repo[b'.']
3974 # whether to strip or not
3972 # whether to strip or not
3975 cleanup = False
3973 cleanup = False
3976 from . import hg
3974 from . import hg
3977
3975
3978 if newnodes:
3976 if newnodes:
3979 newnodes = [repo[r].rev() for r in newnodes]
3977 newnodes = [repo[r].rev() for r in newnodes]
3980 cleanup = True
3978 cleanup = True
3981 # checking that none of the newnodes turned public or is public
3979 # checking that none of the newnodes turned public or is public
3982 immutable = [c for c in newnodes if not repo[c].mutable()]
3980 immutable = [c for c in newnodes if not repo[c].mutable()]
3983 if immutable:
3981 if immutable:
3984 repo.ui.warn(
3982 repo.ui.warn(
3985 _(b"cannot clean up public changesets %s\n")
3983 _(b"cannot clean up public changesets %s\n")
3986 % b', '.join(bytes(repo[r]) for r in immutable),
3984 % b', '.join(bytes(repo[r]) for r in immutable),
3987 hint=_(b"see 'hg help phases' for details"),
3985 hint=_(b"see 'hg help phases' for details"),
3988 )
3986 )
3989 cleanup = False
3987 cleanup = False
3990
3988
3991 # checking that no new nodes are created on top of grafted revs
3989 # checking that no new nodes are created on top of grafted revs
3992 desc = set(repo.changelog.descendants(newnodes))
3990 desc = set(repo.changelog.descendants(newnodes))
3993 if desc - set(newnodes):
3991 if desc - set(newnodes):
3994 repo.ui.warn(
3992 repo.ui.warn(
3995 _(
3993 _(
3996 b"new changesets detected on destination "
3994 b"new changesets detected on destination "
3997 b"branch, can't strip\n"
3995 b"branch, can't strip\n"
3998 )
3996 )
3999 )
3997 )
4000 cleanup = False
3998 cleanup = False
4001
3999
4002 if cleanup:
4000 if cleanup:
4003 with repo.wlock(), repo.lock():
4001 with repo.wlock(), repo.lock():
4004 hg.updaterepo(repo, startctx.node(), overwrite=True)
4002 hg.updaterepo(repo, startctx.node(), overwrite=True)
4005 # stripping the new nodes created
4003 # stripping the new nodes created
4006 strippoints = [
4004 strippoints = [
4007 c.node() for c in repo.set(b"roots(%ld)", newnodes)
4005 c.node() for c in repo.set(b"roots(%ld)", newnodes)
4008 ]
4006 ]
4009 repair.strip(repo.ui, repo, strippoints, backup=False)
4007 repair.strip(repo.ui, repo, strippoints, backup=False)
4010
4008
4011 if not cleanup:
4009 if not cleanup:
4012 # we don't update to the startnode if we can't strip
4010 # we don't update to the startnode if we can't strip
4013 startctx = repo[b'.']
4011 startctx = repo[b'.']
4014 hg.updaterepo(repo, startctx.node(), overwrite=True)
4012 hg.updaterepo(repo, startctx.node(), overwrite=True)
4015
4013
4016 ui.status(_(b"graft aborted\n"))
4014 ui.status(_(b"graft aborted\n"))
4017 ui.status(_(b"working directory is now at %s\n") % startctx.hex()[:12])
4015 ui.status(_(b"working directory is now at %s\n") % startctx.hex()[:12])
4018 graftstate.delete()
4016 graftstate.delete()
4019 return 0
4017 return 0
4020
4018
4021
4019
4022 def readgraftstate(repo, graftstate):
4020 def readgraftstate(repo, graftstate):
4023 # type: (Any, statemod.cmdstate) -> Dict[bytes, Any]
4021 # type: (Any, statemod.cmdstate) -> Dict[bytes, Any]
4024 """read the graft state file and return a dict of the data stored in it"""
4022 """read the graft state file and return a dict of the data stored in it"""
4025 try:
4023 try:
4026 return graftstate.read()
4024 return graftstate.read()
4027 except error.CorruptedState:
4025 except error.CorruptedState:
4028 nodes = repo.vfs.read(b'graftstate').splitlines()
4026 nodes = repo.vfs.read(b'graftstate').splitlines()
4029 return {b'nodes': nodes}
4027 return {b'nodes': nodes}
4030
4028
4031
4029
4032 def hgabortgraft(ui, repo):
4030 def hgabortgraft(ui, repo):
4033 """ abort logic for aborting graft using 'hg abort'"""
4031 """ abort logic for aborting graft using 'hg abort'"""
4034 with repo.wlock():
4032 with repo.wlock():
4035 graftstate = statemod.cmdstate(repo, b'graftstate')
4033 graftstate = statemod.cmdstate(repo, b'graftstate')
4036 return abortgraft(ui, repo, graftstate)
4034 return abortgraft(ui, repo, graftstate)
@@ -1,508 +1,508 b''
1 #testcases obsstore-off obsstore-on
1 #testcases obsstore-off obsstore-on
2
2
3 $ cat << EOF >> $HGRCPATH
3 $ cat << EOF >> $HGRCPATH
4 > [extensions]
4 > [extensions]
5 > amend=
5 > amend=
6 > debugdrawdag=$TESTDIR/drawdag.py
6 > debugdrawdag=$TESTDIR/drawdag.py
7 > [diff]
7 > [diff]
8 > git=1
8 > git=1
9 > EOF
9 > EOF
10
10
11 #if obsstore-on
11 #if obsstore-on
12 $ cat << EOF >> $HGRCPATH
12 $ cat << EOF >> $HGRCPATH
13 > [experimental]
13 > [experimental]
14 > evolution.createmarkers=True
14 > evolution.createmarkers=True
15 > EOF
15 > EOF
16 #endif
16 #endif
17
17
18 Basic amend
18 Basic amend
19
19
20 $ hg init repo1
20 $ hg init repo1
21 $ cd repo1
21 $ cd repo1
22 $ hg debugdrawdag <<'EOS'
22 $ hg debugdrawdag <<'EOS'
23 > B
23 > B
24 > |
24 > |
25 > A
25 > A
26 > EOS
26 > EOS
27
27
28 $ hg update B -q
28 $ hg update B -q
29 $ echo 2 >> B
29 $ echo 2 >> B
30
30
31 $ hg amend
31 $ hg amend
32 saved backup bundle to $TESTTMP/repo1/.hg/strip-backup/112478962961-7e959a55-amend.hg (obsstore-off !)
32 saved backup bundle to $TESTTMP/repo1/.hg/strip-backup/112478962961-7e959a55-amend.hg (obsstore-off !)
33 #if obsstore-off
33 #if obsstore-off
34 $ hg log -p -G --hidden -T '{rev} {node|short} {desc}\n'
34 $ hg log -p -G --hidden -T '{rev} {node|short} {desc}\n'
35 @ 1 be169c7e8dbe B
35 @ 1 be169c7e8dbe B
36 | diff --git a/B b/B
36 | diff --git a/B b/B
37 | new file mode 100644
37 | new file mode 100644
38 | --- /dev/null
38 | --- /dev/null
39 | +++ b/B
39 | +++ b/B
40 | @@ -0,0 +1,1 @@
40 | @@ -0,0 +1,1 @@
41 | +B2
41 | +B2
42 |
42 |
43 o 0 426bada5c675 A
43 o 0 426bada5c675 A
44 diff --git a/A b/A
44 diff --git a/A b/A
45 new file mode 100644
45 new file mode 100644
46 --- /dev/null
46 --- /dev/null
47 +++ b/A
47 +++ b/A
48 @@ -0,0 +1,1 @@
48 @@ -0,0 +1,1 @@
49 +A
49 +A
50 \ No newline at end of file
50 \ No newline at end of file
51
51
52 #else
52 #else
53 $ hg log -p -G --hidden -T '{rev} {node|short} {desc}\n'
53 $ hg log -p -G --hidden -T '{rev} {node|short} {desc}\n'
54 @ 2 be169c7e8dbe B
54 @ 2 be169c7e8dbe B
55 | diff --git a/B b/B
55 | diff --git a/B b/B
56 | new file mode 100644
56 | new file mode 100644
57 | --- /dev/null
57 | --- /dev/null
58 | +++ b/B
58 | +++ b/B
59 | @@ -0,0 +1,1 @@
59 | @@ -0,0 +1,1 @@
60 | +B2
60 | +B2
61 |
61 |
62 | x 1 112478962961 B
62 | x 1 112478962961 B
63 |/ diff --git a/B b/B
63 |/ diff --git a/B b/B
64 | new file mode 100644
64 | new file mode 100644
65 | --- /dev/null
65 | --- /dev/null
66 | +++ b/B
66 | +++ b/B
67 | @@ -0,0 +1,1 @@
67 | @@ -0,0 +1,1 @@
68 | +B
68 | +B
69 | \ No newline at end of file
69 | \ No newline at end of file
70 |
70 |
71 o 0 426bada5c675 A
71 o 0 426bada5c675 A
72 diff --git a/A b/A
72 diff --git a/A b/A
73 new file mode 100644
73 new file mode 100644
74 --- /dev/null
74 --- /dev/null
75 +++ b/A
75 +++ b/A
76 @@ -0,0 +1,1 @@
76 @@ -0,0 +1,1 @@
77 +A
77 +A
78 \ No newline at end of file
78 \ No newline at end of file
79
79
80 #endif
80 #endif
81
81
82 Nothing changed
82 Nothing changed
83
83
84 $ hg amend
84 $ hg amend
85 nothing changed
85 nothing changed
86 [1]
86 [1]
87
87
88 $ hg amend -d "0 0"
88 $ hg amend -d "0 0"
89 nothing changed
89 nothing changed
90 [1]
90 [1]
91
91
92 $ hg amend -d "Thu Jan 01 00:00:00 1970 UTC"
92 $ hg amend -d "Thu Jan 01 00:00:00 1970 UTC"
93 nothing changed
93 nothing changed
94 [1]
94 [1]
95
95
96 Matcher and metadata options
96 Matcher and metadata options
97
97
98 $ echo 3 > C
98 $ echo 3 > C
99 $ echo 4 > D
99 $ echo 4 > D
100 $ hg add C D
100 $ hg add C D
101 $ hg amend -m NEWMESSAGE -I C
101 $ hg amend -m NEWMESSAGE -I C
102 saved backup bundle to $TESTTMP/repo1/.hg/strip-backup/be169c7e8dbe-7684ddc5-amend.hg (obsstore-off !)
102 saved backup bundle to $TESTTMP/repo1/.hg/strip-backup/be169c7e8dbe-7684ddc5-amend.hg (obsstore-off !)
103 $ hg log -r . -T '{node|short} {desc} {files}\n'
103 $ hg log -r . -T '{node|short} {desc} {files}\n'
104 c7ba14d9075b NEWMESSAGE B C
104 c7ba14d9075b NEWMESSAGE B C
105 $ echo 5 > E
105 $ echo 5 > E
106 $ rm C
106 $ rm C
107 $ hg amend -d '2000 1000' -u 'Foo <foo@example.com>' -A C D
107 $ hg amend -d '2000 1000' -u 'Foo <foo@example.com>' -A C D
108 saved backup bundle to $TESTTMP/repo1/.hg/strip-backup/c7ba14d9075b-b3e76daa-amend.hg (obsstore-off !)
108 saved backup bundle to $TESTTMP/repo1/.hg/strip-backup/c7ba14d9075b-b3e76daa-amend.hg (obsstore-off !)
109 $ hg log -r . -T '{node|short} {desc} {files} {author} {date}\n'
109 $ hg log -r . -T '{node|short} {desc} {files} {author} {date}\n'
110 14f6c4bcc865 NEWMESSAGE B D Foo <foo@example.com> 2000.01000
110 14f6c4bcc865 NEWMESSAGE B D Foo <foo@example.com> 2000.01000
111
111
112 Amend with editor
112 Amend with editor
113
113
114 $ cat > $TESTTMP/prefix.sh <<'EOF'
114 $ cat > $TESTTMP/prefix.sh <<'EOF'
115 > printf 'EDITED: ' > $TESTTMP/msg
115 > printf 'EDITED: ' > $TESTTMP/msg
116 > cat "$1" >> $TESTTMP/msg
116 > cat "$1" >> $TESTTMP/msg
117 > mv $TESTTMP/msg "$1"
117 > mv $TESTTMP/msg "$1"
118 > EOF
118 > EOF
119 $ chmod +x $TESTTMP/prefix.sh
119 $ chmod +x $TESTTMP/prefix.sh
120
120
121 $ HGEDITOR="sh $TESTTMP/prefix.sh" hg amend --edit
121 $ HGEDITOR="sh $TESTTMP/prefix.sh" hg amend --edit
122 saved backup bundle to $TESTTMP/repo1/.hg/strip-backup/14f6c4bcc865-6591f15d-amend.hg (obsstore-off !)
122 saved backup bundle to $TESTTMP/repo1/.hg/strip-backup/14f6c4bcc865-6591f15d-amend.hg (obsstore-off !)
123 $ hg log -r . -T '{node|short} {desc}\n'
123 $ hg log -r . -T '{node|short} {desc}\n'
124 298f085230c3 EDITED: NEWMESSAGE
124 298f085230c3 EDITED: NEWMESSAGE
125 $ HGEDITOR="sh $TESTTMP/prefix.sh" hg amend -e -m MSG
125 $ HGEDITOR="sh $TESTTMP/prefix.sh" hg amend -e -m MSG
126 saved backup bundle to $TESTTMP/repo1/.hg/strip-backup/298f085230c3-d81a6ad3-amend.hg (obsstore-off !)
126 saved backup bundle to $TESTTMP/repo1/.hg/strip-backup/298f085230c3-d81a6ad3-amend.hg (obsstore-off !)
127 $ hg log -r . -T '{node|short} {desc}\n'
127 $ hg log -r . -T '{node|short} {desc}\n'
128 974f07f28537 EDITED: MSG
128 974f07f28537 EDITED: MSG
129
129
130 $ echo FOO > $TESTTMP/msg
130 $ echo FOO > $TESTTMP/msg
131 $ hg amend -l $TESTTMP/msg -m BAR
131 $ hg amend -l $TESTTMP/msg -m BAR
132 abort: options --message and --logfile are mutually exclusive
132 abort: options --message and --logfile are mutually exclusive
133 [255]
133 [255]
134 $ hg amend -l $TESTTMP/msg
134 $ hg amend -l $TESTTMP/msg
135 saved backup bundle to $TESTTMP/repo1/.hg/strip-backup/974f07f28537-edb6470a-amend.hg (obsstore-off !)
135 saved backup bundle to $TESTTMP/repo1/.hg/strip-backup/974f07f28537-edb6470a-amend.hg (obsstore-off !)
136 $ hg log -r . -T '{node|short} {desc}\n'
136 $ hg log -r . -T '{node|short} {desc}\n'
137 507be9bdac71 FOO
137 507be9bdac71 FOO
138
138
139 Interactive mode
139 Interactive mode
140
140
141 $ touch F G
141 $ touch F G
142 $ hg add F G
142 $ hg add F G
143 $ cat <<EOS | hg amend -i --config ui.interactive=1
143 $ cat <<EOS | hg amend -i --config ui.interactive=1
144 > y
144 > y
145 > n
145 > n
146 > EOS
146 > EOS
147 diff --git a/F b/F
147 diff --git a/F b/F
148 new file mode 100644
148 new file mode 100644
149 examine changes to 'F'?
149 examine changes to 'F'?
150 (enter ? for help) [Ynesfdaq?] y
150 (enter ? for help) [Ynesfdaq?] y
151
151
152 diff --git a/G b/G
152 diff --git a/G b/G
153 new file mode 100644
153 new file mode 100644
154 examine changes to 'G'?
154 examine changes to 'G'?
155 (enter ? for help) [Ynesfdaq?] n
155 (enter ? for help) [Ynesfdaq?] n
156
156
157 saved backup bundle to $TESTTMP/repo1/.hg/strip-backup/507be9bdac71-c8077452-amend.hg (obsstore-off !)
157 saved backup bundle to $TESTTMP/repo1/.hg/strip-backup/507be9bdac71-c8077452-amend.hg (obsstore-off !)
158 $ hg log -r . -T '{files}\n'
158 $ hg log -r . -T '{files}\n'
159 B D F
159 B D F
160
160
161 Amend in the middle of a stack
161 Amend in the middle of a stack
162
162
163 $ hg init $TESTTMP/repo2
163 $ hg init $TESTTMP/repo2
164 $ cd $TESTTMP/repo2
164 $ cd $TESTTMP/repo2
165 $ hg debugdrawdag <<'EOS'
165 $ hg debugdrawdag <<'EOS'
166 > C
166 > C
167 > |
167 > |
168 > B
168 > B
169 > |
169 > |
170 > A
170 > A
171 > EOS
171 > EOS
172
172
173 $ hg update -q B
173 $ hg update -q B
174 $ echo 2 >> B
174 $ echo 2 >> B
175 $ hg amend
175 $ hg amend
176 abort: cannot amend changeset with children
176 abort: cannot amend changeset with children
177 [255]
177 [255]
178
178
179 #if obsstore-on
179 #if obsstore-on
180
180
181 With allowunstable, amend could work in the middle of a stack
181 With allowunstable, amend could work in the middle of a stack
182
182
183 $ cat >> $HGRCPATH <<EOF
183 $ cat >> $HGRCPATH <<EOF
184 > [experimental]
184 > [experimental]
185 > evolution.createmarkers=True
185 > evolution.createmarkers=True
186 > evolution.allowunstable=True
186 > evolution.allowunstable=True
187 > EOF
187 > EOF
188
188
189 $ hg amend
189 $ hg amend
190 1 new orphan changesets
190 1 new orphan changesets
191 $ hg log -T '{rev} {node|short} {desc}\n' -G
191 $ hg log -T '{rev} {node|short} {desc}\n' -G
192 @ 3 be169c7e8dbe B
192 @ 3 be169c7e8dbe B
193 |
193 |
194 | * 2 26805aba1e60 C
194 | * 2 26805aba1e60 C
195 | |
195 | |
196 | x 1 112478962961 B
196 | x 1 112478962961 B
197 |/
197 |/
198 o 0 426bada5c675 A
198 o 0 426bada5c675 A
199
199
200 Checking the note stored in the obsmarker
200 Checking the note stored in the obsmarker
201
201
202 $ echo foo > bar
202 $ echo foo > bar
203 $ hg add bar
203 $ hg add bar
204 $ hg amend --note 'yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy'
204 $ hg amend --note 'yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy'
205 abort: cannot store a note of more than 255 bytes
205 abort: cannot store a note of more than 255 bytes
206 [255]
206 [255]
207 $ hg amend --note "adding bar"
207 $ hg amend --note "adding bar"
208 $ hg debugobsolete -r .
208 $ hg debugobsolete -r .
209 112478962961147124edd43549aedd1a335e44bf be169c7e8dbe21cd10b3d79691cbe7f241e3c21c 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '8', 'operation': 'amend', 'user': 'test'}
209 112478962961147124edd43549aedd1a335e44bf be169c7e8dbe21cd10b3d79691cbe7f241e3c21c 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '8', 'operation': 'amend', 'user': 'test'}
210 be169c7e8dbe21cd10b3d79691cbe7f241e3c21c 16084da537dd8f84cfdb3055c633772269d62e1b 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '8', 'note': 'adding bar', 'operation': 'amend', 'user': 'test'}
210 be169c7e8dbe21cd10b3d79691cbe7f241e3c21c 16084da537dd8f84cfdb3055c633772269d62e1b 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '8', 'note': 'adding bar', 'operation': 'amend', 'user': 'test'}
211 #endif
211 #endif
212
212
213 Cannot amend public changeset
213 Cannot amend public changeset
214
214
215 $ hg phase -r A --public
215 $ hg phase -r A --public
216 $ hg update -C -q A
216 $ hg update -C -q A
217 $ hg amend -m AMEND
217 $ hg amend -m AMEND
218 abort: cannot amend public changesets
218 abort: cannot amend public changesets
219 (see 'hg help phases' for details)
219 (see 'hg help phases' for details)
220 [255]
220 [255]
221
221
222 Amend a merge changeset
222 Amend a merge changeset
223
223
224 $ hg init $TESTTMP/repo3
224 $ hg init $TESTTMP/repo3
225 $ cd $TESTTMP/repo3
225 $ cd $TESTTMP/repo3
226 $ hg debugdrawdag <<'EOS'
226 $ hg debugdrawdag <<'EOS'
227 > C
227 > C
228 > /|
228 > /|
229 > A B
229 > A B
230 > EOS
230 > EOS
231 $ hg update -q C
231 $ hg update -q C
232 $ hg amend -m FOO
232 $ hg amend -m FOO
233 saved backup bundle to $TESTTMP/repo3/.hg/strip-backup/a35c07e8a2a4-15ff4612-amend.hg (obsstore-off !)
233 saved backup bundle to $TESTTMP/repo3/.hg/strip-backup/a35c07e8a2a4-15ff4612-amend.hg (obsstore-off !)
234 $ rm .hg/localtags
234 $ rm .hg/localtags
235 $ hg log -G -T '{desc}\n'
235 $ hg log -G -T '{desc}\n'
236 @ FOO
236 @ FOO
237 |\
237 |\
238 | o B
238 | o B
239 |
239 |
240 o A
240 o A
241
241
242
242
243 More complete test for status changes (issue5732)
243 More complete test for status changes (issue5732)
244 -------------------------------------------------
244 -------------------------------------------------
245
245
246 Generates history of files having 3 states, r0_r1_wc:
246 Generates history of files having 3 states, r0_r1_wc:
247
247
248 r0: ground (content/missing)
248 r0: ground (content/missing)
249 r1: old state to be amended (content/missing, where missing means removed)
249 r1: old state to be amended (content/missing, where missing means removed)
250 wc: changes to be included in r1 (content/missing-tracked/untracked)
250 wc: changes to be included in r1 (content/missing-tracked/untracked)
251
251
252 $ hg init $TESTTMP/wcstates
252 $ hg init $TESTTMP/wcstates
253 $ cd $TESTTMP/wcstates
253 $ cd $TESTTMP/wcstates
254
254
255 $ "$PYTHON" $TESTDIR/generate-working-copy-states.py state 2 1
255 $ "$PYTHON" $TESTDIR/generate-working-copy-states.py state 2 1
256 $ hg addremove -q --similarity 0
256 $ hg addremove -q --similarity 0
257 $ hg commit -m0
257 $ hg commit -m0
258
258
259 $ "$PYTHON" $TESTDIR/generate-working-copy-states.py state 2 2
259 $ "$PYTHON" $TESTDIR/generate-working-copy-states.py state 2 2
260 $ hg addremove -q --similarity 0
260 $ hg addremove -q --similarity 0
261 $ hg commit -m1
261 $ hg commit -m1
262
262
263 $ "$PYTHON" $TESTDIR/generate-working-copy-states.py state 2 wc
263 $ "$PYTHON" $TESTDIR/generate-working-copy-states.py state 2 wc
264 $ hg addremove -q --similarity 0
264 $ hg addremove -q --similarity 0
265 $ hg forget *_*_*-untracked
265 $ hg forget *_*_*-untracked
266 $ rm *_*_missing-*
266 $ rm *_*_missing-*
267
267
268 amend r1 to include wc changes
268 amend r1 to include wc changes
269
269
270 $ hg amend
270 $ hg amend
271 saved backup bundle to * (glob) (obsstore-off !)
271 saved backup bundle to * (glob) (obsstore-off !)
272
272
273 clean/modified/removed/added states of the amended revision
273 clean/modified/removed/added states of the amended revision
274
274
275 $ hg status --all --change . 'glob:content1_*_content1-tracked'
275 $ hg status --all --change . 'glob:content1_*_content1-tracked'
276 C content1_content1_content1-tracked
276 C content1_content1_content1-tracked
277 C content1_content2_content1-tracked
277 C content1_content2_content1-tracked
278 C content1_missing_content1-tracked
278 C content1_missing_content1-tracked
279 $ hg status --all --change . 'glob:content1_*_content[23]-tracked'
279 $ hg status --all --change . 'glob:content1_*_content[23]-tracked'
280 M content1_content1_content3-tracked
280 M content1_content1_content3-tracked
281 M content1_content2_content2-tracked
281 M content1_content2_content2-tracked
282 M content1_content2_content3-tracked
282 M content1_content2_content3-tracked
283 M content1_missing_content3-tracked
283 M content1_missing_content3-tracked
284 $ hg status --all --change . 'glob:content1_*_missing-tracked'
284 $ hg status --all --change . 'glob:content1_*_missing-tracked'
285 M content1_content2_missing-tracked
285 M content1_content2_missing-tracked
286 R content1_missing_missing-tracked
286 R content1_missing_missing-tracked
287 C content1_content1_missing-tracked
287 C content1_content1_missing-tracked
288 $ hg status --all --change . 'glob:content1_*_*-untracked'
288 $ hg status --all --change . 'glob:content1_*_*-untracked'
289 R content1_content1_content1-untracked
289 R content1_content1_content1-untracked
290 R content1_content1_content3-untracked
290 R content1_content1_content3-untracked
291 R content1_content1_missing-untracked
291 R content1_content1_missing-untracked
292 R content1_content2_content1-untracked
292 R content1_content2_content1-untracked
293 R content1_content2_content2-untracked
293 R content1_content2_content2-untracked
294 R content1_content2_content3-untracked
294 R content1_content2_content3-untracked
295 R content1_content2_missing-untracked
295 R content1_content2_missing-untracked
296 R content1_missing_content1-untracked
296 R content1_missing_content1-untracked
297 R content1_missing_content3-untracked
297 R content1_missing_content3-untracked
298 R content1_missing_missing-untracked
298 R content1_missing_missing-untracked
299 $ hg status --all --change . 'glob:missing_content2_*'
299 $ hg status --all --change . 'glob:missing_content2_*'
300 A missing_content2_content2-tracked
300 A missing_content2_content2-tracked
301 A missing_content2_content3-tracked
301 A missing_content2_content3-tracked
302 A missing_content2_missing-tracked
302 A missing_content2_missing-tracked
303 $ hg status --all --change . 'glob:missing_missing_*'
303 $ hg status --all --change . 'glob:missing_missing_*'
304 A missing_missing_content3-tracked
304 A missing_missing_content3-tracked
305
305
306 working directory should be all clean (with some missing/untracked files)
306 working directory should be all clean (with some missing/untracked files)
307
307
308 $ hg status --all 'glob:*_content?-tracked'
308 $ hg status --all 'glob:*_content?-tracked'
309 C content1_content1_content1-tracked
309 C content1_content1_content1-tracked
310 C content1_content1_content3-tracked
310 C content1_content1_content3-tracked
311 C content1_content2_content1-tracked
311 C content1_content2_content1-tracked
312 C content1_content2_content2-tracked
312 C content1_content2_content2-tracked
313 C content1_content2_content3-tracked
313 C content1_content2_content3-tracked
314 C content1_missing_content1-tracked
314 C content1_missing_content1-tracked
315 C content1_missing_content3-tracked
315 C content1_missing_content3-tracked
316 C missing_content2_content2-tracked
316 C missing_content2_content2-tracked
317 C missing_content2_content3-tracked
317 C missing_content2_content3-tracked
318 C missing_missing_content3-tracked
318 C missing_missing_content3-tracked
319 $ hg status --all 'glob:*_missing-tracked'
319 $ hg status --all 'glob:*_missing-tracked'
320 ! content1_content1_missing-tracked
320 ! content1_content1_missing-tracked
321 ! content1_content2_missing-tracked
321 ! content1_content2_missing-tracked
322 ! content1_missing_missing-tracked
322 ! content1_missing_missing-tracked
323 ! missing_content2_missing-tracked
323 ! missing_content2_missing-tracked
324 ! missing_missing_missing-tracked
324 ! missing_missing_missing-tracked
325 $ hg status --all 'glob:*-untracked'
325 $ hg status --all 'glob:*-untracked'
326 ? content1_content1_content1-untracked
326 ? content1_content1_content1-untracked
327 ? content1_content1_content3-untracked
327 ? content1_content1_content3-untracked
328 ? content1_content2_content1-untracked
328 ? content1_content2_content1-untracked
329 ? content1_content2_content2-untracked
329 ? content1_content2_content2-untracked
330 ? content1_content2_content3-untracked
330 ? content1_content2_content3-untracked
331 ? content1_missing_content1-untracked
331 ? content1_missing_content1-untracked
332 ? content1_missing_content3-untracked
332 ? content1_missing_content3-untracked
333 ? missing_content2_content2-untracked
333 ? missing_content2_content2-untracked
334 ? missing_content2_content3-untracked
334 ? missing_content2_content3-untracked
335 ? missing_missing_content3-untracked
335 ? missing_missing_content3-untracked
336
336
337 =================================
337 =================================
338 Test backup-bundle config option|
338 Test backup-bundle config option|
339 =================================
339 =================================
340 $ hg init $TESTTMP/repo4
340 $ hg init $TESTTMP/repo4
341 $ cd $TESTTMP/repo4
341 $ cd $TESTTMP/repo4
342 $ echo a>a
342 $ echo a>a
343 $ hg ci -Aqma
343 $ hg ci -Aqma
344 $ echo oops>b
344 $ echo oops>b
345 $ hg ci -Aqm "b"
345 $ hg ci -Aqm "b"
346 $ echo partiallyfixed > b
346 $ echo partiallyfixed > b
347
347
348 #if obsstore-off
348 #if obsstore-off
349 $ hg amend
349 $ hg amend
350 saved backup bundle to $TESTTMP/repo4/.hg/strip-backup/95e899acf2ce-f11cb050-amend.hg
350 saved backup bundle to $TESTTMP/repo4/.hg/strip-backup/95e899acf2ce-f11cb050-amend.hg
351 When backup-bundle config option is set:
351 When backup-bundle config option is set:
352 $ cat << EOF >> $HGRCPATH
352 $ cat << EOF >> $HGRCPATH
353 > [rewrite]
353 > [rewrite]
354 > backup-bundle = False
354 > backup-bundle = False
355 > EOF
355 > EOF
356 $ echo fixed > b
356 $ echo fixed > b
357 $ hg amend
357 $ hg amend
358
358
359 #else
359 #else
360 $ hg amend
360 $ hg amend
361 When backup-bundle config option is set:
361 When backup-bundle config option is set:
362 $ cat << EOF >> $HGRCPATH
362 $ cat << EOF >> $HGRCPATH
363 > [rewrite]
363 > [rewrite]
364 > backup-bundle = False
364 > backup-bundle = False
365 > EOF
365 > EOF
366 $ echo fixed > b
366 $ echo fixed > b
367 $ hg amend
367 $ hg amend
368
368
369 #endif
369 #endif
370 ==========================================
370 ==========================================
371 Test update-timestamp config option|
371 Test update-timestamp config option|
372 ==========================================
372 ==========================================
373
373
374 $ cat >> $HGRCPATH << EOF
374 $ cat >> $HGRCPATH << EOF
375 > [extensions]
375 > [extensions]
376 > amend=
376 > amend=
377 > mockmakedate = $TESTDIR/mockmakedate.py
377 > mockmakedate = $TESTDIR/mockmakedate.py
378 > EOF
378 > EOF
379
379
380 $ hg init $TESTTMP/repo5
380 $ hg init $TESTTMP/repo5
381 $ cd $TESTTMP/repo5
381 $ cd $TESTTMP/repo5
382 $ cat <<'EOF' >> .hg/hgrc
382 $ cat <<'EOF' >> .hg/hgrc
383 > [ui]
383 > [ui]
384 > logtemplate = 'user: {user}
384 > logtemplate = 'user: {user}
385 > date: {date|date}
385 > date: {date|date}
386 > summary: {desc|firstline}\n'
386 > summary: {desc|firstline}\n'
387 > EOF
387 > EOF
388
388
389 $ echo a>a
389 $ echo a>a
390 $ hg ci -Am 'commit 1'
390 $ hg ci -Am 'commit 1'
391 adding a
391 adding a
392
392
393 When updatetimestamp is False
393 When updatetimestamp is False
394
394
395 $ hg amend --date '1997-1-1 0:1'
395 $ hg amend --date '1997-1-1 0:1'
396 $ hg log --limit 1
396 $ hg log --limit 1
397 user: test
397 user: test
398 date: Wed Jan 01 00:01:00 1997 +0000
398 date: Wed Jan 01 00:01:00 1997 +0000
399 summary: commit 1
399 summary: commit 1
400
400
401 When update-timestamp is True and no other change than the date
401 When update-timestamp is True and no other change than the date
402
402
403 $ hg amend --config rewrite.update-timestamp=True
403 $ hg amend --config rewrite.update-timestamp=True
404 nothing changed
404 nothing changed
405 [1]
405 [1]
406 $ hg log --limit 1
406 $ hg log --limit 1
407 user: test
407 user: test
408 date: Wed Jan 01 00:01:00 1997 +0000
408 date: Wed Jan 01 00:01:00 1997 +0000
409 summary: commit 1
409 summary: commit 1
410
410
411 When update-timestamp is True and there is other change than the date
411 When update-timestamp is True and there is other change than the date
412 $ hg amend --user foobar --config rewrite.update-timestamp=True
412 $ hg amend --user foobar --config rewrite.update-timestamp=True
413 $ hg log --limit 1
413 $ hg log --limit 1
414 user: foobar
414 user: foobar
415 date: Thu Jan 01 00:00:02 1970 +0000
415 date: Thu Jan 01 00:00:02 1970 +0000
416 summary: commit 1
416 summary: commit 1
417
417
418 When date option is applicable and update-timestamp is True
418 When date option is applicable and update-timestamp is True
419 $ hg amend --date '1998-1-1 0:1' --config rewrite.update-timestamp=True
419 $ hg amend --date '1998-1-1 0:1' --config rewrite.update-timestamp=True
420 $ hg log --limit 1
420 $ hg log --limit 1
421 user: foobar
421 user: foobar
422 date: Thu Jan 01 00:01:00 1998 +0000
422 date: Thu Jan 01 00:01:00 1998 +0000
423 summary: commit 1
423 summary: commit 1
424
424
425 Unlike rewrite.update-timestamp, -D/--currentdate always updates the timestamp
425 Unlike rewrite.update-timestamp, -D/--currentdate always updates the timestamp
426
426
427 $ hg amend -D
427 $ hg amend -D
428 $ hg log --limit 1
428 $ hg log --limit 1
429 user: foobar
429 user: foobar
430 date: Thu Jan 01 00:00:04 1970 +0000
430 date: Thu Jan 01 00:00:04 1970 +0000
431 summary: commit 1
431 summary: commit 1
432
432
433 $ hg amend -D --config rewrite.update-timestamp=True
433 $ hg amend -D --config rewrite.update-timestamp=True
434 $ hg log --limit 1
434 $ hg log --limit 1
435 user: foobar
435 user: foobar
436 date: Thu Jan 01 00:00:05 1970 +0000
436 date: Thu Jan 01 00:00:05 1970 +0000
437 summary: commit 1
437 summary: commit 1
438
438
439 rewrite.update-timestamp can be negated by --no-currentdate
439 rewrite.update-timestamp can be negated by --no-currentdate
440
440
441 $ hg amend --config rewrite.update-timestamp=True --no-currentdate -u baz
441 $ hg amend --config rewrite.update-timestamp=True --no-currentdate -u baz
442 $ hg log --limit 1
442 $ hg log --limit 1
443 user: baz
443 user: baz
444 date: Thu Jan 01 00:00:05 1970 +0000
444 date: Thu Jan 01 00:00:05 1970 +0000
445 summary: commit 1
445 summary: commit 1
446
446
447 Bad combination of date options:
447 Bad combination of date options:
448
448
449 $ hg amend -D --date '0 0'
449 $ hg amend -D --date '0 0'
450 abort: --date and --currentdate are mutually exclusive
450 abort: cannot specify both --date and --currentdate
451 [255]
451 [255]
452
452
453 Close branch
453 Close branch
454
454
455 $ hg amend --secret --close-branch
455 $ hg amend --secret --close-branch
456 $ hg log --limit 1 -T 'close={get(extras, "close")}\nphase={phase}\n'
456 $ hg log --limit 1 -T 'close={get(extras, "close")}\nphase={phase}\n'
457 close=1
457 close=1
458 phase=secret
458 phase=secret
459
459
460 $ cd ..
460 $ cd ..
461
461
462 Corner case of amend from issue6157:
462 Corner case of amend from issue6157:
463 - working copy parent has a change to file `a`
463 - working copy parent has a change to file `a`
464 - working copy has the inverse change
464 - working copy has the inverse change
465 - we amend the working copy parent for files other than `a`
465 - we amend the working copy parent for files other than `a`
466 hg used to include the changes to `a` anyway.
466 hg used to include the changes to `a` anyway.
467
467
468 $ hg init 6157; cd 6157
468 $ hg init 6157; cd 6157
469 $ echo a > a; echo b > b; hg commit -qAm_
469 $ echo a > a; echo b > b; hg commit -qAm_
470 $ echo a2 > a; hg commit -qm_
470 $ echo a2 > a; hg commit -qm_
471 $ hg diff --stat -c .
471 $ hg diff --stat -c .
472 a | 2 +-
472 a | 2 +-
473 1 files changed, 1 insertions(+), 1 deletions(-)
473 1 files changed, 1 insertions(+), 1 deletions(-)
474 $ echo a > a; echo b2 > b; hg amend -q b
474 $ echo a > a; echo b2 > b; hg amend -q b
475 $ hg diff --stat -c .
475 $ hg diff --stat -c .
476 a | 2 +-
476 a | 2 +-
477 b | 2 +-
477 b | 2 +-
478 2 files changed, 2 insertions(+), 2 deletions(-)
478 2 files changed, 2 insertions(+), 2 deletions(-)
479
479
480 Modifying a file while the editor is open can cause dirstate corruption
480 Modifying a file while the editor is open can cause dirstate corruption
481 (issue6233)
481 (issue6233)
482
482
483 $ cd $TESTTMP
483 $ cd $TESTTMP
484 $ hg init modify-during-amend; cd modify-during-amend
484 $ hg init modify-during-amend; cd modify-during-amend
485 $ echo r0 > foo; hg commit -qAm "r0"
485 $ echo r0 > foo; hg commit -qAm "r0"
486 $ echo alpha > foo; hg commit -qm "alpha"
486 $ echo alpha > foo; hg commit -qm "alpha"
487 $ echo beta >> foo
487 $ echo beta >> foo
488 $ cat > $TESTTMP/sleepy_editor.sh <<EOF
488 $ cat > $TESTTMP/sleepy_editor.sh <<EOF
489 > echo hi > "\$1"
489 > echo hi > "\$1"
490 > sleep 3
490 > sleep 3
491 > EOF
491 > EOF
492 $ HGEDITOR="sh $TESTTMP/sleepy_editor.sh" hg commit --amend &
492 $ HGEDITOR="sh $TESTTMP/sleepy_editor.sh" hg commit --amend &
493 $ sleep 1
493 $ sleep 1
494 $ echo delta >> foo
494 $ echo delta >> foo
495 $ sleep 3
495 $ sleep 3
496 $ if (hg diff -c . | grep 'delta' >/dev/null) || [ -n "$(hg status)" ]; then
496 $ if (hg diff -c . | grep 'delta' >/dev/null) || [ -n "$(hg status)" ]; then
497 > echo "OK."
497 > echo "OK."
498 > else
498 > else
499 > echo "Bug detected. 'delta' is not part of the commit OR the wdir"
499 > echo "Bug detected. 'delta' is not part of the commit OR the wdir"
500 > echo "Diff and status before rebuild:"
500 > echo "Diff and status before rebuild:"
501 > hg diff
501 > hg diff
502 > hg status
502 > hg status
503 > hg debugrebuilddirstate
503 > hg debugrebuilddirstate
504 > echo "Diff and status after rebuild:"
504 > echo "Diff and status after rebuild:"
505 > hg diff
505 > hg diff
506 > hg status
506 > hg status
507 > fi
507 > fi
508 OK.
508 OK.
@@ -1,2427 +1,2427 b''
1 #testcases abortcommand abortflag
1 #testcases abortcommand abortflag
2
2
3 $ cat >> $HGRCPATH <<EOF
3 $ cat >> $HGRCPATH <<EOF
4 > [extdiff]
4 > [extdiff]
5 > # for portability:
5 > # for portability:
6 > pdiff = sh "$RUNTESTDIR/pdiff"
6 > pdiff = sh "$RUNTESTDIR/pdiff"
7 > EOF
7 > EOF
8
8
9 #if abortflag
9 #if abortflag
10 $ cat >> $HGRCPATH <<EOF
10 $ cat >> $HGRCPATH <<EOF
11 > [alias]
11 > [alias]
12 > abort = graft --abort
12 > abort = graft --abort
13 > EOF
13 > EOF
14 #endif
14 #endif
15
15
16 Create a repo with some stuff in it:
16 Create a repo with some stuff in it:
17
17
18 $ hg init a
18 $ hg init a
19 $ cd a
19 $ cd a
20 $ echo a > a
20 $ echo a > a
21 $ echo a > d
21 $ echo a > d
22 $ echo a > e
22 $ echo a > e
23 $ hg ci -qAm0
23 $ hg ci -qAm0
24 $ echo b > a
24 $ echo b > a
25 $ hg ci -m1 -u bar
25 $ hg ci -m1 -u bar
26 $ hg mv a b
26 $ hg mv a b
27 $ hg ci -m2
27 $ hg ci -m2
28 $ hg cp b c
28 $ hg cp b c
29 $ hg ci -m3 -u baz
29 $ hg ci -m3 -u baz
30 $ echo b > d
30 $ echo b > d
31 $ echo f > e
31 $ echo f > e
32 $ hg ci -m4
32 $ hg ci -m4
33 $ hg up -q 3
33 $ hg up -q 3
34 $ echo b > e
34 $ echo b > e
35 $ hg branch -q stable
35 $ hg branch -q stable
36 $ hg ci -m5
36 $ hg ci -m5
37 $ hg merge -q default --tool internal:local # for conflicts in e, choose 5 and ignore 4
37 $ hg merge -q default --tool internal:local # for conflicts in e, choose 5 and ignore 4
38 $ hg branch -q default
38 $ hg branch -q default
39 $ hg ci -m6
39 $ hg ci -m6
40 $ hg phase --public 3
40 $ hg phase --public 3
41 $ hg phase --force --secret 6
41 $ hg phase --force --secret 6
42
42
43 $ hg log -G --template '{author}@{rev}.{phase}: {desc}\n'
43 $ hg log -G --template '{author}@{rev}.{phase}: {desc}\n'
44 @ test@6.secret: 6
44 @ test@6.secret: 6
45 |\
45 |\
46 | o test@5.draft: 5
46 | o test@5.draft: 5
47 | |
47 | |
48 o | test@4.draft: 4
48 o | test@4.draft: 4
49 |/
49 |/
50 o baz@3.public: 3
50 o baz@3.public: 3
51 |
51 |
52 o test@2.public: 2
52 o test@2.public: 2
53 |
53 |
54 o bar@1.public: 1
54 o bar@1.public: 1
55 |
55 |
56 o test@0.public: 0
56 o test@0.public: 0
57
57
58 Test --base for grafting the merge of 4 from the perspective of 5, thus only getting the change to d
58 Test --base for grafting the merge of 4 from the perspective of 5, thus only getting the change to d
59
59
60 $ hg up -cqr 3
60 $ hg up -cqr 3
61 $ hg graft -r 6 --base 5
61 $ hg graft -r 6 --base 5
62 grafting 6:25a2b029d3ae "6" (tip)
62 grafting 6:25a2b029d3ae "6" (tip)
63 merging e
63 merging e
64 $ hg st --change .
64 $ hg st --change .
65 M d
65 M d
66
66
67 $ hg -q strip . --config extensions.strip=
67 $ hg -q strip . --config extensions.strip=
68
68
69 Test --base for collapsing changesets 2 and 3, thus getting both b and c
69 Test --base for collapsing changesets 2 and 3, thus getting both b and c
70
70
71 $ hg up -cqr 0
71 $ hg up -cqr 0
72 $ hg graft -r 3 --base 1
72 $ hg graft -r 3 --base 1
73 grafting 3:4c60f11aa304 "3"
73 grafting 3:4c60f11aa304 "3"
74 merging a and b to b
74 merging a and b to b
75 merging a and c to c
75 merging a and c to c
76 $ hg st --change .
76 $ hg st --change .
77 A b
77 A b
78 A c
78 A c
79 R a
79 R a
80
80
81 $ hg -q strip . --config extensions.strip=
81 $ hg -q strip . --config extensions.strip=
82
82
83 Specifying child as --base revision fails safely (perhaps slightly confusing, but consistent)
83 Specifying child as --base revision fails safely (perhaps slightly confusing, but consistent)
84
84
85 $ hg graft -r 2 --base 3
85 $ hg graft -r 2 --base 3
86 grafting 2:5c095ad7e90f "2"
86 grafting 2:5c095ad7e90f "2"
87 note: possible conflict - c was deleted and renamed to:
87 note: possible conflict - c was deleted and renamed to:
88 a
88 a
89 note: graft of 2:5c095ad7e90f created no changes to commit
89 note: graft of 2:5c095ad7e90f created no changes to commit
90
90
91 Can't continue without starting:
91 Can't continue without starting:
92
92
93 $ hg -q up -cr tip
93 $ hg -q up -cr tip
94 $ hg rm -q e
94 $ hg rm -q e
95 $ hg graft --continue
95 $ hg graft --continue
96 abort: no graft in progress
96 abort: no graft in progress
97 [255]
97 [255]
98 $ hg revert -r . -q e
98 $ hg revert -r . -q e
99
99
100 Need to specify a rev:
100 Need to specify a rev:
101
101
102 $ hg graft
102 $ hg graft
103 abort: no revisions specified
103 abort: no revisions specified
104 [255]
104 [255]
105
105
106 Can't graft ancestor:
106 Can't graft ancestor:
107
107
108 $ hg graft 1 2
108 $ hg graft 1 2
109 skipping ancestor revision 1:5d205f8b35b6
109 skipping ancestor revision 1:5d205f8b35b6
110 skipping ancestor revision 2:5c095ad7e90f
110 skipping ancestor revision 2:5c095ad7e90f
111 [255]
111 [255]
112
112
113 Specify revisions with -r:
113 Specify revisions with -r:
114
114
115 $ hg graft -r 1 -r 2
115 $ hg graft -r 1 -r 2
116 skipping ancestor revision 1:5d205f8b35b6
116 skipping ancestor revision 1:5d205f8b35b6
117 skipping ancestor revision 2:5c095ad7e90f
117 skipping ancestor revision 2:5c095ad7e90f
118 [255]
118 [255]
119
119
120 $ hg graft -r 1 2
120 $ hg graft -r 1 2
121 warning: inconsistent use of --rev might give unexpected revision ordering!
121 warning: inconsistent use of --rev might give unexpected revision ordering!
122 skipping ancestor revision 2:5c095ad7e90f
122 skipping ancestor revision 2:5c095ad7e90f
123 skipping ancestor revision 1:5d205f8b35b6
123 skipping ancestor revision 1:5d205f8b35b6
124 [255]
124 [255]
125
125
126 Conflicting date/user options:
126 Conflicting date/user options:
127
127
128 $ hg up -q 0
128 $ hg up -q 0
129 $ hg graft -U --user foo 2
129 $ hg graft -U --user foo 2
130 abort: --user and --currentuser are mutually exclusive
130 abort: cannot specify both --user and --currentuser
131 [255]
131 [255]
132 $ hg graft -D --date '0 0' 2
132 $ hg graft -D --date '0 0' 2
133 abort: --date and --currentdate are mutually exclusive
133 abort: cannot specify both --date and --currentdate
134 [255]
134 [255]
135
135
136 Can't graft with dirty wd:
136 Can't graft with dirty wd:
137
137
138 $ hg up -q 0
138 $ hg up -q 0
139 $ echo foo > a
139 $ echo foo > a
140 $ hg graft 1
140 $ hg graft 1
141 abort: uncommitted changes
141 abort: uncommitted changes
142 [255]
142 [255]
143 $ hg revert a
143 $ hg revert a
144
144
145 Graft a rename:
145 Graft a rename:
146 (this also tests that editor is invoked if '--edit' is specified)
146 (this also tests that editor is invoked if '--edit' is specified)
147
147
148 $ hg status --rev "2^1" --rev 2
148 $ hg status --rev "2^1" --rev 2
149 A b
149 A b
150 R a
150 R a
151 $ HGEDITOR=cat hg graft 2 -u foo --edit
151 $ HGEDITOR=cat hg graft 2 -u foo --edit
152 grafting 2:5c095ad7e90f "2"
152 grafting 2:5c095ad7e90f "2"
153 merging a and b to b
153 merging a and b to b
154 2
154 2
155
155
156
156
157 HG: Enter commit message. Lines beginning with 'HG:' are removed.
157 HG: Enter commit message. Lines beginning with 'HG:' are removed.
158 HG: Leave message empty to abort commit.
158 HG: Leave message empty to abort commit.
159 HG: --
159 HG: --
160 HG: user: foo
160 HG: user: foo
161 HG: branch 'default'
161 HG: branch 'default'
162 HG: added b
162 HG: added b
163 HG: removed a
163 HG: removed a
164 $ hg export tip --git
164 $ hg export tip --git
165 # HG changeset patch
165 # HG changeset patch
166 # User foo
166 # User foo
167 # Date 0 0
167 # Date 0 0
168 # Thu Jan 01 00:00:00 1970 +0000
168 # Thu Jan 01 00:00:00 1970 +0000
169 # Node ID ef0ef43d49e79e81ddafdc7997401ba0041efc82
169 # Node ID ef0ef43d49e79e81ddafdc7997401ba0041efc82
170 # Parent 68795b066622ca79a25816a662041d8f78f3cd9e
170 # Parent 68795b066622ca79a25816a662041d8f78f3cd9e
171 2
171 2
172
172
173 diff --git a/a b/b
173 diff --git a/a b/b
174 rename from a
174 rename from a
175 rename to b
175 rename to b
176
176
177 Look for extra:source
177 Look for extra:source
178
178
179 $ hg log --debug -r tip
179 $ hg log --debug -r tip
180 changeset: 7:ef0ef43d49e79e81ddafdc7997401ba0041efc82
180 changeset: 7:ef0ef43d49e79e81ddafdc7997401ba0041efc82
181 tag: tip
181 tag: tip
182 phase: draft
182 phase: draft
183 parent: 0:68795b066622ca79a25816a662041d8f78f3cd9e
183 parent: 0:68795b066622ca79a25816a662041d8f78f3cd9e
184 parent: -1:0000000000000000000000000000000000000000
184 parent: -1:0000000000000000000000000000000000000000
185 manifest: 7:e59b6b228f9cbf9903d5e9abf996e083a1f533eb
185 manifest: 7:e59b6b228f9cbf9903d5e9abf996e083a1f533eb
186 user: foo
186 user: foo
187 date: Thu Jan 01 00:00:00 1970 +0000
187 date: Thu Jan 01 00:00:00 1970 +0000
188 files+: b
188 files+: b
189 files-: a
189 files-: a
190 extra: branch=default
190 extra: branch=default
191 extra: source=5c095ad7e90f871700f02dd1fa5012cb4498a2d4
191 extra: source=5c095ad7e90f871700f02dd1fa5012cb4498a2d4
192 description:
192 description:
193 2
193 2
194
194
195
195
196
196
197 Graft out of order, skipping a merge and a duplicate
197 Graft out of order, skipping a merge and a duplicate
198 (this also tests that editor is not invoked if '--edit' is not specified)
198 (this also tests that editor is not invoked if '--edit' is not specified)
199
199
200 $ hg graft 1 5 4 3 'merge()' 2 -n
200 $ hg graft 1 5 4 3 'merge()' 2 -n
201 skipping ungraftable merge revision 6
201 skipping ungraftable merge revision 6
202 skipping revision 2:5c095ad7e90f (already grafted to 7:ef0ef43d49e7)
202 skipping revision 2:5c095ad7e90f (already grafted to 7:ef0ef43d49e7)
203 grafting 1:5d205f8b35b6 "1"
203 grafting 1:5d205f8b35b6 "1"
204 grafting 5:97f8bfe72746 "5"
204 grafting 5:97f8bfe72746 "5"
205 grafting 4:9c233e8e184d "4"
205 grafting 4:9c233e8e184d "4"
206 grafting 3:4c60f11aa304 "3"
206 grafting 3:4c60f11aa304 "3"
207
207
208 $ HGEDITOR=cat hg graft 1 5 'merge()' 2 --debug
208 $ HGEDITOR=cat hg graft 1 5 'merge()' 2 --debug
209 skipping ungraftable merge revision 6
209 skipping ungraftable merge revision 6
210 scanning for duplicate grafts
210 scanning for duplicate grafts
211 skipping revision 2:5c095ad7e90f (already grafted to 7:ef0ef43d49e7)
211 skipping revision 2:5c095ad7e90f (already grafted to 7:ef0ef43d49e7)
212 grafting 1:5d205f8b35b6 "1"
212 grafting 1:5d205f8b35b6 "1"
213 unmatched files in local:
213 unmatched files in local:
214 b
214 b
215 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
215 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
216 src: 'a' -> dst: 'b' *
216 src: 'a' -> dst: 'b' *
217 checking for directory renames
217 checking for directory renames
218 resolving manifests
218 resolving manifests
219 branchmerge: True, force: True, partial: False
219 branchmerge: True, force: True, partial: False
220 ancestor: 68795b066622, local: ef0ef43d49e7+, remote: 5d205f8b35b6
220 ancestor: 68795b066622, local: ef0ef43d49e7+, remote: 5d205f8b35b6
221 preserving b for resolve of b
221 preserving b for resolve of b
222 starting 4 threads for background file closing (?)
222 starting 4 threads for background file closing (?)
223 b: local copied/moved from a -> m (premerge)
223 b: local copied/moved from a -> m (premerge)
224 picked tool ':merge' for b (binary False symlink False changedelete False)
224 picked tool ':merge' for b (binary False symlink False changedelete False)
225 merging b and a to b
225 merging b and a to b
226 my b@ef0ef43d49e7+ other a@5d205f8b35b6 ancestor a@68795b066622
226 my b@ef0ef43d49e7+ other a@5d205f8b35b6 ancestor a@68795b066622
227 premerge successful
227 premerge successful
228 committing files:
228 committing files:
229 b
229 b
230 committing manifest
230 committing manifest
231 committing changelog
231 committing changelog
232 updating the branch cache
232 updating the branch cache
233 grafting 5:97f8bfe72746 "5"
233 grafting 5:97f8bfe72746 "5"
234 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
234 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
235 src: 'c' -> dst: 'b'
235 src: 'c' -> dst: 'b'
236 checking for directory renames
236 checking for directory renames
237 resolving manifests
237 resolving manifests
238 branchmerge: True, force: True, partial: False
238 branchmerge: True, force: True, partial: False
239 ancestor: 4c60f11aa304, local: 6b9e5368ca4e+, remote: 97f8bfe72746
239 ancestor: 4c60f11aa304, local: 6b9e5368ca4e+, remote: 97f8bfe72746
240 e: remote is newer -> g
240 e: remote is newer -> g
241 getting e
241 getting e
242 committing files:
242 committing files:
243 e
243 e
244 committing manifest
244 committing manifest
245 committing changelog
245 committing changelog
246 updating the branch cache
246 updating the branch cache
247 $ HGEDITOR=cat hg graft 4 3 --log --debug
247 $ HGEDITOR=cat hg graft 4 3 --log --debug
248 scanning for duplicate grafts
248 scanning for duplicate grafts
249 grafting 4:9c233e8e184d "4"
249 grafting 4:9c233e8e184d "4"
250 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
250 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
251 src: 'c' -> dst: 'b'
251 src: 'c' -> dst: 'b'
252 checking for directory renames
252 checking for directory renames
253 resolving manifests
253 resolving manifests
254 branchmerge: True, force: True, partial: False
254 branchmerge: True, force: True, partial: False
255 ancestor: 4c60f11aa304, local: 1905859650ec+, remote: 9c233e8e184d
255 ancestor: 4c60f11aa304, local: 1905859650ec+, remote: 9c233e8e184d
256 preserving e for resolve of e
256 preserving e for resolve of e
257 d: remote is newer -> g
257 d: remote is newer -> g
258 getting d
258 getting d
259 e: versions differ -> m (premerge)
259 e: versions differ -> m (premerge)
260 picked tool ':merge' for e (binary False symlink False changedelete False)
260 picked tool ':merge' for e (binary False symlink False changedelete False)
261 merging e
261 merging e
262 my e@1905859650ec+ other e@9c233e8e184d ancestor e@4c60f11aa304
262 my e@1905859650ec+ other e@9c233e8e184d ancestor e@4c60f11aa304
263 e: versions differ -> m (merge)
263 e: versions differ -> m (merge)
264 picked tool ':merge' for e (binary False symlink False changedelete False)
264 picked tool ':merge' for e (binary False symlink False changedelete False)
265 my e@1905859650ec+ other e@9c233e8e184d ancestor e@4c60f11aa304
265 my e@1905859650ec+ other e@9c233e8e184d ancestor e@4c60f11aa304
266 warning: conflicts while merging e! (edit, then use 'hg resolve --mark')
266 warning: conflicts while merging e! (edit, then use 'hg resolve --mark')
267 abort: unresolved conflicts, can't continue
267 abort: unresolved conflicts, can't continue
268 (use 'hg resolve' and 'hg graft --continue')
268 (use 'hg resolve' and 'hg graft --continue')
269 [255]
269 [255]
270
270
271 Summary should mention graft:
271 Summary should mention graft:
272
272
273 $ hg summary |grep graft
273 $ hg summary |grep graft
274 commit: 2 modified, 2 unknown, 1 unresolved (graft in progress)
274 commit: 2 modified, 2 unknown, 1 unresolved (graft in progress)
275
275
276 Using status to get more context
276 Using status to get more context
277
277
278 $ hg status --verbose
278 $ hg status --verbose
279 M d
279 M d
280 M e
280 M e
281 ? a.orig
281 ? a.orig
282 ? e.orig
282 ? e.orig
283 # The repository is in an unfinished *graft* state.
283 # The repository is in an unfinished *graft* state.
284
284
285 # Unresolved merge conflicts:
285 # Unresolved merge conflicts:
286 #
286 #
287 # e
287 # e
288 #
288 #
289 # To mark files as resolved: hg resolve --mark FILE
289 # To mark files as resolved: hg resolve --mark FILE
290
290
291 # To continue: hg graft --continue
291 # To continue: hg graft --continue
292 # To abort: hg graft --abort
292 # To abort: hg graft --abort
293 # To stop: hg graft --stop
293 # To stop: hg graft --stop
294
294
295
295
296 Commit while interrupted should fail:
296 Commit while interrupted should fail:
297
297
298 $ hg ci -m 'commit interrupted graft'
298 $ hg ci -m 'commit interrupted graft'
299 abort: graft in progress
299 abort: graft in progress
300 (use 'hg graft --continue' or 'hg graft --stop' to stop)
300 (use 'hg graft --continue' or 'hg graft --stop' to stop)
301 [255]
301 [255]
302
302
303 Abort the graft and try committing:
303 Abort the graft and try committing:
304
304
305 $ hg up -C .
305 $ hg up -C .
306 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
306 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
307 $ echo c >> e
307 $ echo c >> e
308 $ hg ci -mtest
308 $ hg ci -mtest
309
309
310 $ hg strip . --config extensions.strip=
310 $ hg strip . --config extensions.strip=
311 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
311 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
312 saved backup bundle to $TESTTMP/a/.hg/strip-backup/*-backup.hg (glob)
312 saved backup bundle to $TESTTMP/a/.hg/strip-backup/*-backup.hg (glob)
313
313
314 Graft again:
314 Graft again:
315
315
316 $ hg graft 1 5 4 3 'merge()' 2
316 $ hg graft 1 5 4 3 'merge()' 2
317 skipping ungraftable merge revision 6
317 skipping ungraftable merge revision 6
318 skipping revision 2:5c095ad7e90f (already grafted to 7:ef0ef43d49e7)
318 skipping revision 2:5c095ad7e90f (already grafted to 7:ef0ef43d49e7)
319 skipping revision 1:5d205f8b35b6 (already grafted to 8:6b9e5368ca4e)
319 skipping revision 1:5d205f8b35b6 (already grafted to 8:6b9e5368ca4e)
320 skipping revision 5:97f8bfe72746 (already grafted to 9:1905859650ec)
320 skipping revision 5:97f8bfe72746 (already grafted to 9:1905859650ec)
321 grafting 4:9c233e8e184d "4"
321 grafting 4:9c233e8e184d "4"
322 merging e
322 merging e
323 warning: conflicts while merging e! (edit, then use 'hg resolve --mark')
323 warning: conflicts while merging e! (edit, then use 'hg resolve --mark')
324 abort: unresolved conflicts, can't continue
324 abort: unresolved conflicts, can't continue
325 (use 'hg resolve' and 'hg graft --continue')
325 (use 'hg resolve' and 'hg graft --continue')
326 [255]
326 [255]
327
327
328 Continue without resolve should fail:
328 Continue without resolve should fail:
329
329
330 $ hg graft -c
330 $ hg graft -c
331 grafting 4:9c233e8e184d "4"
331 grafting 4:9c233e8e184d "4"
332 abort: unresolved merge conflicts (see 'hg help resolve')
332 abort: unresolved merge conflicts (see 'hg help resolve')
333 [255]
333 [255]
334
334
335 Fix up:
335 Fix up:
336
336
337 $ echo b > e
337 $ echo b > e
338 $ hg resolve -m e
338 $ hg resolve -m e
339 (no more unresolved files)
339 (no more unresolved files)
340 continue: hg graft --continue
340 continue: hg graft --continue
341
341
342 Continue with a revision should fail:
342 Continue with a revision should fail:
343
343
344 $ hg graft -c 6
344 $ hg graft -c 6
345 abort: can't specify --continue and revisions
345 abort: can't specify --continue and revisions
346 [255]
346 [255]
347
347
348 $ hg graft -c -r 6
348 $ hg graft -c -r 6
349 abort: can't specify --continue and revisions
349 abort: can't specify --continue and revisions
350 [255]
350 [255]
351
351
352 Continue for real, clobber usernames
352 Continue for real, clobber usernames
353
353
354 $ hg graft -c -U
354 $ hg graft -c -U
355 grafting 4:9c233e8e184d "4"
355 grafting 4:9c233e8e184d "4"
356 grafting 3:4c60f11aa304 "3"
356 grafting 3:4c60f11aa304 "3"
357
357
358 Compare with original:
358 Compare with original:
359
359
360 $ hg diff -r 6
360 $ hg diff -r 6
361 $ hg status --rev 0:. -C
361 $ hg status --rev 0:. -C
362 M d
362 M d
363 M e
363 M e
364 A b
364 A b
365 a
365 a
366 A c
366 A c
367 a
367 a
368 R a
368 R a
369
369
370 View graph:
370 View graph:
371
371
372 $ hg log -G --template '{author}@{rev}.{phase}: {desc}\n'
372 $ hg log -G --template '{author}@{rev}.{phase}: {desc}\n'
373 @ test@11.draft: 3
373 @ test@11.draft: 3
374 |
374 |
375 o test@10.draft: 4
375 o test@10.draft: 4
376 |
376 |
377 o test@9.draft: 5
377 o test@9.draft: 5
378 |
378 |
379 o bar@8.draft: 1
379 o bar@8.draft: 1
380 |
380 |
381 o foo@7.draft: 2
381 o foo@7.draft: 2
382 |
382 |
383 | o test@6.secret: 6
383 | o test@6.secret: 6
384 | |\
384 | |\
385 | | o test@5.draft: 5
385 | | o test@5.draft: 5
386 | | |
386 | | |
387 | o | test@4.draft: 4
387 | o | test@4.draft: 4
388 | |/
388 | |/
389 | o baz@3.public: 3
389 | o baz@3.public: 3
390 | |
390 | |
391 | o test@2.public: 2
391 | o test@2.public: 2
392 | |
392 | |
393 | o bar@1.public: 1
393 | o bar@1.public: 1
394 |/
394 |/
395 o test@0.public: 0
395 o test@0.public: 0
396
396
397 Graft again onto another branch should preserve the original source
397 Graft again onto another branch should preserve the original source
398 $ hg up -q 0
398 $ hg up -q 0
399 $ echo 'g'>g
399 $ echo 'g'>g
400 $ hg add g
400 $ hg add g
401 $ hg ci -m 7
401 $ hg ci -m 7
402 created new head
402 created new head
403 $ hg graft 7
403 $ hg graft 7
404 grafting 7:ef0ef43d49e7 "2"
404 grafting 7:ef0ef43d49e7 "2"
405
405
406 $ hg log -r 7 --template '{rev}:{node}\n'
406 $ hg log -r 7 --template '{rev}:{node}\n'
407 7:ef0ef43d49e79e81ddafdc7997401ba0041efc82
407 7:ef0ef43d49e79e81ddafdc7997401ba0041efc82
408 $ hg log -r 2 --template '{rev}:{node}\n'
408 $ hg log -r 2 --template '{rev}:{node}\n'
409 2:5c095ad7e90f871700f02dd1fa5012cb4498a2d4
409 2:5c095ad7e90f871700f02dd1fa5012cb4498a2d4
410
410
411 $ hg log --debug -r tip
411 $ hg log --debug -r tip
412 changeset: 13:7a4785234d87ec1aa420ed6b11afe40fa73e12a9
412 changeset: 13:7a4785234d87ec1aa420ed6b11afe40fa73e12a9
413 tag: tip
413 tag: tip
414 phase: draft
414 phase: draft
415 parent: 12:b592ea63bb0c19a6c5c44685ee29a2284f9f1b8f
415 parent: 12:b592ea63bb0c19a6c5c44685ee29a2284f9f1b8f
416 parent: -1:0000000000000000000000000000000000000000
416 parent: -1:0000000000000000000000000000000000000000
417 manifest: 13:dc313617b8c32457c0d589e0dbbedfe71f3cd637
417 manifest: 13:dc313617b8c32457c0d589e0dbbedfe71f3cd637
418 user: foo
418 user: foo
419 date: Thu Jan 01 00:00:00 1970 +0000
419 date: Thu Jan 01 00:00:00 1970 +0000
420 files+: b
420 files+: b
421 files-: a
421 files-: a
422 extra: branch=default
422 extra: branch=default
423 extra: intermediate-source=ef0ef43d49e79e81ddafdc7997401ba0041efc82
423 extra: intermediate-source=ef0ef43d49e79e81ddafdc7997401ba0041efc82
424 extra: source=5c095ad7e90f871700f02dd1fa5012cb4498a2d4
424 extra: source=5c095ad7e90f871700f02dd1fa5012cb4498a2d4
425 description:
425 description:
426 2
426 2
427
427
428
428
429 Disallow grafting an already grafted cset onto its original branch
429 Disallow grafting an already grafted cset onto its original branch
430 $ hg up -q 6
430 $ hg up -q 6
431 $ hg graft 7
431 $ hg graft 7
432 skipping already grafted revision 7:ef0ef43d49e7 (was grafted from 2:5c095ad7e90f)
432 skipping already grafted revision 7:ef0ef43d49e7 (was grafted from 2:5c095ad7e90f)
433 [255]
433 [255]
434
434
435 $ hg pdiff --config extensions.extdiff= --patch -r 2 -r 13
435 $ hg pdiff --config extensions.extdiff= --patch -r 2 -r 13
436 --- */hg-5c095ad7e90f.patch * (glob)
436 --- */hg-5c095ad7e90f.patch * (glob)
437 +++ */hg-7a4785234d87.patch * (glob)
437 +++ */hg-7a4785234d87.patch * (glob)
438 @@ -1,18 +1,18 @@
438 @@ -1,18 +1,18 @@
439 # HG changeset patch
439 # HG changeset patch
440 -# User test
440 -# User test
441 +# User foo
441 +# User foo
442 # Date 0 0
442 # Date 0 0
443 # Thu Jan 01 00:00:00 1970 +0000
443 # Thu Jan 01 00:00:00 1970 +0000
444 -# Node ID 5c095ad7e90f871700f02dd1fa5012cb4498a2d4
444 -# Node ID 5c095ad7e90f871700f02dd1fa5012cb4498a2d4
445 -# Parent 5d205f8b35b66bc36375c9534ffd3237730e8f04
445 -# Parent 5d205f8b35b66bc36375c9534ffd3237730e8f04
446 +# Node ID 7a4785234d87ec1aa420ed6b11afe40fa73e12a9
446 +# Node ID 7a4785234d87ec1aa420ed6b11afe40fa73e12a9
447 +# Parent b592ea63bb0c19a6c5c44685ee29a2284f9f1b8f
447 +# Parent b592ea63bb0c19a6c5c44685ee29a2284f9f1b8f
448 2
448 2
449
449
450 -diff -r 5d205f8b35b6 -r 5c095ad7e90f a
450 -diff -r 5d205f8b35b6 -r 5c095ad7e90f a
451 +diff -r b592ea63bb0c -r 7a4785234d87 a
451 +diff -r b592ea63bb0c -r 7a4785234d87 a
452 --- a/a Thu Jan 01 00:00:00 1970 +0000
452 --- a/a Thu Jan 01 00:00:00 1970 +0000
453 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
453 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
454 @@ -1,1 +0,0 @@
454 @@ -1,1 +0,0 @@
455 --b
455 --b
456 -diff -r 5d205f8b35b6 -r 5c095ad7e90f b
456 -diff -r 5d205f8b35b6 -r 5c095ad7e90f b
457 +-a
457 +-a
458 +diff -r b592ea63bb0c -r 7a4785234d87 b
458 +diff -r b592ea63bb0c -r 7a4785234d87 b
459 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
459 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
460 +++ b/b Thu Jan 01 00:00:00 1970 +0000
460 +++ b/b Thu Jan 01 00:00:00 1970 +0000
461 @@ -0,0 +1,1 @@
461 @@ -0,0 +1,1 @@
462 -+b
462 -+b
463 ++a
463 ++a
464 [1]
464 [1]
465
465
466 $ hg pdiff --config extensions.extdiff= --patch -r 2 -r 13 -X .
466 $ hg pdiff --config extensions.extdiff= --patch -r 2 -r 13 -X .
467 --- */hg-5c095ad7e90f.patch * (glob)
467 --- */hg-5c095ad7e90f.patch * (glob)
468 +++ */hg-7a4785234d87.patch * (glob)
468 +++ */hg-7a4785234d87.patch * (glob)
469 @@ -1,8 +1,8 @@
469 @@ -1,8 +1,8 @@
470 # HG changeset patch
470 # HG changeset patch
471 -# User test
471 -# User test
472 +# User foo
472 +# User foo
473 # Date 0 0
473 # Date 0 0
474 # Thu Jan 01 00:00:00 1970 +0000
474 # Thu Jan 01 00:00:00 1970 +0000
475 -# Node ID 5c095ad7e90f871700f02dd1fa5012cb4498a2d4
475 -# Node ID 5c095ad7e90f871700f02dd1fa5012cb4498a2d4
476 -# Parent 5d205f8b35b66bc36375c9534ffd3237730e8f04
476 -# Parent 5d205f8b35b66bc36375c9534ffd3237730e8f04
477 +# Node ID 7a4785234d87ec1aa420ed6b11afe40fa73e12a9
477 +# Node ID 7a4785234d87ec1aa420ed6b11afe40fa73e12a9
478 +# Parent b592ea63bb0c19a6c5c44685ee29a2284f9f1b8f
478 +# Parent b592ea63bb0c19a6c5c44685ee29a2284f9f1b8f
479 2
479 2
480
480
481 [1]
481 [1]
482
482
483 Disallow grafting already grafted csets with the same origin onto each other
483 Disallow grafting already grafted csets with the same origin onto each other
484 $ hg up -q 13
484 $ hg up -q 13
485 $ hg graft 2
485 $ hg graft 2
486 skipping revision 2:5c095ad7e90f (already grafted to 13:7a4785234d87)
486 skipping revision 2:5c095ad7e90f (already grafted to 13:7a4785234d87)
487 [255]
487 [255]
488 $ hg graft 7
488 $ hg graft 7
489 skipping already grafted revision 7:ef0ef43d49e7 (13:7a4785234d87 also has origin 2:5c095ad7e90f)
489 skipping already grafted revision 7:ef0ef43d49e7 (13:7a4785234d87 also has origin 2:5c095ad7e90f)
490 [255]
490 [255]
491
491
492 $ hg up -q 7
492 $ hg up -q 7
493 $ hg graft 2
493 $ hg graft 2
494 skipping revision 2:5c095ad7e90f (already grafted to 7:ef0ef43d49e7)
494 skipping revision 2:5c095ad7e90f (already grafted to 7:ef0ef43d49e7)
495 [255]
495 [255]
496 $ hg graft tip
496 $ hg graft tip
497 skipping already grafted revision 13:7a4785234d87 (7:ef0ef43d49e7 also has origin 2:5c095ad7e90f)
497 skipping already grafted revision 13:7a4785234d87 (7:ef0ef43d49e7 also has origin 2:5c095ad7e90f)
498 [255]
498 [255]
499
499
500 Graft with --log
500 Graft with --log
501
501
502 $ hg up -Cq 1
502 $ hg up -Cq 1
503 $ hg graft 3 --log -u foo
503 $ hg graft 3 --log -u foo
504 grafting 3:4c60f11aa304 "3"
504 grafting 3:4c60f11aa304 "3"
505 warning: can't find ancestor for 'c' copied from 'b'!
505 warning: can't find ancestor for 'c' copied from 'b'!
506 $ hg log --template '{rev}:{node|short} {parents} {desc}\n' -r tip
506 $ hg log --template '{rev}:{node|short} {parents} {desc}\n' -r tip
507 14:0c921c65ef1e 1:5d205f8b35b6 3
507 14:0c921c65ef1e 1:5d205f8b35b6 3
508 (grafted from 4c60f11aa304a54ae1c199feb94e7fc771e51ed8)
508 (grafted from 4c60f11aa304a54ae1c199feb94e7fc771e51ed8)
509
509
510 Resolve conflicted graft
510 Resolve conflicted graft
511 $ hg up -q 0
511 $ hg up -q 0
512 $ echo b > a
512 $ echo b > a
513 $ hg ci -m 8
513 $ hg ci -m 8
514 created new head
514 created new head
515 $ echo c > a
515 $ echo c > a
516 $ hg ci -m 9
516 $ hg ci -m 9
517 $ hg graft 1 --tool internal:fail
517 $ hg graft 1 --tool internal:fail
518 grafting 1:5d205f8b35b6 "1"
518 grafting 1:5d205f8b35b6 "1"
519 abort: unresolved conflicts, can't continue
519 abort: unresolved conflicts, can't continue
520 (use 'hg resolve' and 'hg graft --continue')
520 (use 'hg resolve' and 'hg graft --continue')
521 [255]
521 [255]
522 $ hg resolve --all
522 $ hg resolve --all
523 merging a
523 merging a
524 warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
524 warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
525 [1]
525 [1]
526 $ cat a
526 $ cat a
527 <<<<<<< local: aaa4406d4f0a - test: 9
527 <<<<<<< local: aaa4406d4f0a - test: 9
528 c
528 c
529 =======
529 =======
530 b
530 b
531 >>>>>>> graft: 5d205f8b35b6 - bar: 1
531 >>>>>>> graft: 5d205f8b35b6 - bar: 1
532 $ echo b > a
532 $ echo b > a
533 $ hg resolve -m a
533 $ hg resolve -m a
534 (no more unresolved files)
534 (no more unresolved files)
535 continue: hg graft --continue
535 continue: hg graft --continue
536 $ hg graft -c
536 $ hg graft -c
537 grafting 1:5d205f8b35b6 "1"
537 grafting 1:5d205f8b35b6 "1"
538 $ hg export tip --git
538 $ hg export tip --git
539 # HG changeset patch
539 # HG changeset patch
540 # User bar
540 # User bar
541 # Date 0 0
541 # Date 0 0
542 # Thu Jan 01 00:00:00 1970 +0000
542 # Thu Jan 01 00:00:00 1970 +0000
543 # Node ID f67661df0c4804d301f064f332b57e7d5ddaf2be
543 # Node ID f67661df0c4804d301f064f332b57e7d5ddaf2be
544 # Parent aaa4406d4f0ae9befd6e58c82ec63706460cbca6
544 # Parent aaa4406d4f0ae9befd6e58c82ec63706460cbca6
545 1
545 1
546
546
547 diff --git a/a b/a
547 diff --git a/a b/a
548 --- a/a
548 --- a/a
549 +++ b/a
549 +++ b/a
550 @@ -1,1 +1,1 @@
550 @@ -1,1 +1,1 @@
551 -c
551 -c
552 +b
552 +b
553
553
554 Resolve conflicted graft with rename
554 Resolve conflicted graft with rename
555 $ echo c > a
555 $ echo c > a
556 $ hg ci -m 10
556 $ hg ci -m 10
557 $ hg graft 2 --tool internal:fail
557 $ hg graft 2 --tool internal:fail
558 grafting 2:5c095ad7e90f "2"
558 grafting 2:5c095ad7e90f "2"
559 abort: unresolved conflicts, can't continue
559 abort: unresolved conflicts, can't continue
560 (use 'hg resolve' and 'hg graft --continue')
560 (use 'hg resolve' and 'hg graft --continue')
561 [255]
561 [255]
562 $ hg resolve --all
562 $ hg resolve --all
563 merging a and b to b
563 merging a and b to b
564 (no more unresolved files)
564 (no more unresolved files)
565 continue: hg graft --continue
565 continue: hg graft --continue
566 $ hg graft -c
566 $ hg graft -c
567 grafting 2:5c095ad7e90f "2"
567 grafting 2:5c095ad7e90f "2"
568 $ hg export tip --git
568 $ hg export tip --git
569 # HG changeset patch
569 # HG changeset patch
570 # User test
570 # User test
571 # Date 0 0
571 # Date 0 0
572 # Thu Jan 01 00:00:00 1970 +0000
572 # Thu Jan 01 00:00:00 1970 +0000
573 # Node ID 9627f653b421c61fc1ea4c4e366745070fa3d2bc
573 # Node ID 9627f653b421c61fc1ea4c4e366745070fa3d2bc
574 # Parent ee295f490a40b97f3d18dd4c4f1c8936c233b612
574 # Parent ee295f490a40b97f3d18dd4c4f1c8936c233b612
575 2
575 2
576
576
577 diff --git a/a b/b
577 diff --git a/a b/b
578 rename from a
578 rename from a
579 rename to b
579 rename to b
580
580
581 Test simple origin(), with and without args
581 Test simple origin(), with and without args
582 $ hg log -r 'origin()'
582 $ hg log -r 'origin()'
583 changeset: 1:5d205f8b35b6
583 changeset: 1:5d205f8b35b6
584 user: bar
584 user: bar
585 date: Thu Jan 01 00:00:00 1970 +0000
585 date: Thu Jan 01 00:00:00 1970 +0000
586 summary: 1
586 summary: 1
587
587
588 changeset: 2:5c095ad7e90f
588 changeset: 2:5c095ad7e90f
589 user: test
589 user: test
590 date: Thu Jan 01 00:00:00 1970 +0000
590 date: Thu Jan 01 00:00:00 1970 +0000
591 summary: 2
591 summary: 2
592
592
593 changeset: 3:4c60f11aa304
593 changeset: 3:4c60f11aa304
594 user: baz
594 user: baz
595 date: Thu Jan 01 00:00:00 1970 +0000
595 date: Thu Jan 01 00:00:00 1970 +0000
596 summary: 3
596 summary: 3
597
597
598 changeset: 4:9c233e8e184d
598 changeset: 4:9c233e8e184d
599 user: test
599 user: test
600 date: Thu Jan 01 00:00:00 1970 +0000
600 date: Thu Jan 01 00:00:00 1970 +0000
601 summary: 4
601 summary: 4
602
602
603 changeset: 5:97f8bfe72746
603 changeset: 5:97f8bfe72746
604 branch: stable
604 branch: stable
605 parent: 3:4c60f11aa304
605 parent: 3:4c60f11aa304
606 user: test
606 user: test
607 date: Thu Jan 01 00:00:00 1970 +0000
607 date: Thu Jan 01 00:00:00 1970 +0000
608 summary: 5
608 summary: 5
609
609
610 $ hg log -r 'origin(7)'
610 $ hg log -r 'origin(7)'
611 changeset: 2:5c095ad7e90f
611 changeset: 2:5c095ad7e90f
612 user: test
612 user: test
613 date: Thu Jan 01 00:00:00 1970 +0000
613 date: Thu Jan 01 00:00:00 1970 +0000
614 summary: 2
614 summary: 2
615
615
616 Now transplant a graft to test following through copies
616 Now transplant a graft to test following through copies
617 $ hg up -q 0
617 $ hg up -q 0
618 $ hg branch -q dev
618 $ hg branch -q dev
619 $ hg ci -qm "dev branch"
619 $ hg ci -qm "dev branch"
620 $ hg --config extensions.transplant= transplant -q 7
620 $ hg --config extensions.transplant= transplant -q 7
621 $ hg log -r 'origin(.)'
621 $ hg log -r 'origin(.)'
622 changeset: 2:5c095ad7e90f
622 changeset: 2:5c095ad7e90f
623 user: test
623 user: test
624 date: Thu Jan 01 00:00:00 1970 +0000
624 date: Thu Jan 01 00:00:00 1970 +0000
625 summary: 2
625 summary: 2
626
626
627 Test that the graft and transplant markers in extra are converted, allowing
627 Test that the graft and transplant markers in extra are converted, allowing
628 origin() to still work. Note that these recheck the immediately preceeding two
628 origin() to still work. Note that these recheck the immediately preceeding two
629 tests.
629 tests.
630 $ hg --quiet --config extensions.convert= --config convert.hg.saverev=True convert . ../converted
630 $ hg --quiet --config extensions.convert= --config convert.hg.saverev=True convert . ../converted
631
631
632 The graft case
632 The graft case
633 $ hg -R ../converted log -r 7 --template "{rev}: {node}\n{join(extras, '\n')}\n"
633 $ hg -R ../converted log -r 7 --template "{rev}: {node}\n{join(extras, '\n')}\n"
634 7: 7ae846e9111fc8f57745634250c7b9ac0a60689b
634 7: 7ae846e9111fc8f57745634250c7b9ac0a60689b
635 branch=default
635 branch=default
636 convert_revision=ef0ef43d49e79e81ddafdc7997401ba0041efc82
636 convert_revision=ef0ef43d49e79e81ddafdc7997401ba0041efc82
637 source=e0213322b2c1a5d5d236c74e79666441bee67a7d
637 source=e0213322b2c1a5d5d236c74e79666441bee67a7d
638 $ hg -R ../converted log -r 'origin(7)'
638 $ hg -R ../converted log -r 'origin(7)'
639 changeset: 2:e0213322b2c1
639 changeset: 2:e0213322b2c1
640 user: test
640 user: test
641 date: Thu Jan 01 00:00:00 1970 +0000
641 date: Thu Jan 01 00:00:00 1970 +0000
642 summary: 2
642 summary: 2
643
643
644 Test that template correctly expands more than one 'extra' (issue4362), and that
644 Test that template correctly expands more than one 'extra' (issue4362), and that
645 'intermediate-source' is converted.
645 'intermediate-source' is converted.
646 $ hg -R ../converted log -r 13 --template "{extras % ' Extra: {extra}\n'}"
646 $ hg -R ../converted log -r 13 --template "{extras % ' Extra: {extra}\n'}"
647 Extra: branch=default
647 Extra: branch=default
648 Extra: convert_revision=7a4785234d87ec1aa420ed6b11afe40fa73e12a9
648 Extra: convert_revision=7a4785234d87ec1aa420ed6b11afe40fa73e12a9
649 Extra: intermediate-source=7ae846e9111fc8f57745634250c7b9ac0a60689b
649 Extra: intermediate-source=7ae846e9111fc8f57745634250c7b9ac0a60689b
650 Extra: source=e0213322b2c1a5d5d236c74e79666441bee67a7d
650 Extra: source=e0213322b2c1a5d5d236c74e79666441bee67a7d
651
651
652 The transplant case
652 The transplant case
653 $ hg -R ../converted log -r tip --template "{rev}: {node}\n{join(extras, '\n')}\n"
653 $ hg -R ../converted log -r tip --template "{rev}: {node}\n{join(extras, '\n')}\n"
654 21: fbb6c5cc81002f2b4b49c9d731404688bcae5ade
654 21: fbb6c5cc81002f2b4b49c9d731404688bcae5ade
655 branch=dev
655 branch=dev
656 convert_revision=7e61b508e709a11d28194a5359bc3532d910af21
656 convert_revision=7e61b508e709a11d28194a5359bc3532d910af21
657 transplant_source=z\xe8F\xe9\x11\x1f\xc8\xf5wEcBP\xc7\xb9\xac\n`h\x9b
657 transplant_source=z\xe8F\xe9\x11\x1f\xc8\xf5wEcBP\xc7\xb9\xac\n`h\x9b
658 $ hg -R ../converted log -r 'origin(tip)'
658 $ hg -R ../converted log -r 'origin(tip)'
659 changeset: 2:e0213322b2c1
659 changeset: 2:e0213322b2c1
660 user: test
660 user: test
661 date: Thu Jan 01 00:00:00 1970 +0000
661 date: Thu Jan 01 00:00:00 1970 +0000
662 summary: 2
662 summary: 2
663
663
664
664
665 Test simple destination
665 Test simple destination
666 $ hg log -r 'destination()'
666 $ hg log -r 'destination()'
667 changeset: 7:ef0ef43d49e7
667 changeset: 7:ef0ef43d49e7
668 parent: 0:68795b066622
668 parent: 0:68795b066622
669 user: foo
669 user: foo
670 date: Thu Jan 01 00:00:00 1970 +0000
670 date: Thu Jan 01 00:00:00 1970 +0000
671 summary: 2
671 summary: 2
672
672
673 changeset: 8:6b9e5368ca4e
673 changeset: 8:6b9e5368ca4e
674 user: bar
674 user: bar
675 date: Thu Jan 01 00:00:00 1970 +0000
675 date: Thu Jan 01 00:00:00 1970 +0000
676 summary: 1
676 summary: 1
677
677
678 changeset: 9:1905859650ec
678 changeset: 9:1905859650ec
679 user: test
679 user: test
680 date: Thu Jan 01 00:00:00 1970 +0000
680 date: Thu Jan 01 00:00:00 1970 +0000
681 summary: 5
681 summary: 5
682
682
683 changeset: 10:52dc0b4c6907
683 changeset: 10:52dc0b4c6907
684 user: test
684 user: test
685 date: Thu Jan 01 00:00:00 1970 +0000
685 date: Thu Jan 01 00:00:00 1970 +0000
686 summary: 4
686 summary: 4
687
687
688 changeset: 11:882b35362a6b
688 changeset: 11:882b35362a6b
689 user: test
689 user: test
690 date: Thu Jan 01 00:00:00 1970 +0000
690 date: Thu Jan 01 00:00:00 1970 +0000
691 summary: 3
691 summary: 3
692
692
693 changeset: 13:7a4785234d87
693 changeset: 13:7a4785234d87
694 user: foo
694 user: foo
695 date: Thu Jan 01 00:00:00 1970 +0000
695 date: Thu Jan 01 00:00:00 1970 +0000
696 summary: 2
696 summary: 2
697
697
698 changeset: 14:0c921c65ef1e
698 changeset: 14:0c921c65ef1e
699 parent: 1:5d205f8b35b6
699 parent: 1:5d205f8b35b6
700 user: foo
700 user: foo
701 date: Thu Jan 01 00:00:00 1970 +0000
701 date: Thu Jan 01 00:00:00 1970 +0000
702 summary: 3
702 summary: 3
703
703
704 changeset: 17:f67661df0c48
704 changeset: 17:f67661df0c48
705 user: bar
705 user: bar
706 date: Thu Jan 01 00:00:00 1970 +0000
706 date: Thu Jan 01 00:00:00 1970 +0000
707 summary: 1
707 summary: 1
708
708
709 changeset: 19:9627f653b421
709 changeset: 19:9627f653b421
710 user: test
710 user: test
711 date: Thu Jan 01 00:00:00 1970 +0000
711 date: Thu Jan 01 00:00:00 1970 +0000
712 summary: 2
712 summary: 2
713
713
714 changeset: 21:7e61b508e709
714 changeset: 21:7e61b508e709
715 branch: dev
715 branch: dev
716 tag: tip
716 tag: tip
717 user: foo
717 user: foo
718 date: Thu Jan 01 00:00:00 1970 +0000
718 date: Thu Jan 01 00:00:00 1970 +0000
719 summary: 2
719 summary: 2
720
720
721 $ hg log -r 'destination(2)'
721 $ hg log -r 'destination(2)'
722 changeset: 7:ef0ef43d49e7
722 changeset: 7:ef0ef43d49e7
723 parent: 0:68795b066622
723 parent: 0:68795b066622
724 user: foo
724 user: foo
725 date: Thu Jan 01 00:00:00 1970 +0000
725 date: Thu Jan 01 00:00:00 1970 +0000
726 summary: 2
726 summary: 2
727
727
728 changeset: 13:7a4785234d87
728 changeset: 13:7a4785234d87
729 user: foo
729 user: foo
730 date: Thu Jan 01 00:00:00 1970 +0000
730 date: Thu Jan 01 00:00:00 1970 +0000
731 summary: 2
731 summary: 2
732
732
733 changeset: 19:9627f653b421
733 changeset: 19:9627f653b421
734 user: test
734 user: test
735 date: Thu Jan 01 00:00:00 1970 +0000
735 date: Thu Jan 01 00:00:00 1970 +0000
736 summary: 2
736 summary: 2
737
737
738 changeset: 21:7e61b508e709
738 changeset: 21:7e61b508e709
739 branch: dev
739 branch: dev
740 tag: tip
740 tag: tip
741 user: foo
741 user: foo
742 date: Thu Jan 01 00:00:00 1970 +0000
742 date: Thu Jan 01 00:00:00 1970 +0000
743 summary: 2
743 summary: 2
744
744
745 Transplants of grafts can find a destination...
745 Transplants of grafts can find a destination...
746 $ hg log -r 'destination(7)'
746 $ hg log -r 'destination(7)'
747 changeset: 21:7e61b508e709
747 changeset: 21:7e61b508e709
748 branch: dev
748 branch: dev
749 tag: tip
749 tag: tip
750 user: foo
750 user: foo
751 date: Thu Jan 01 00:00:00 1970 +0000
751 date: Thu Jan 01 00:00:00 1970 +0000
752 summary: 2
752 summary: 2
753
753
754 ... grafts of grafts unfortunately can't
754 ... grafts of grafts unfortunately can't
755 $ hg graft -q 13 --debug
755 $ hg graft -q 13 --debug
756 scanning for duplicate grafts
756 scanning for duplicate grafts
757 grafting 13:7a4785234d87 "2"
757 grafting 13:7a4785234d87 "2"
758 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
758 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
759 src: 'a' -> dst: 'b' *
759 src: 'a' -> dst: 'b' *
760 checking for directory renames
760 checking for directory renames
761 resolving manifests
761 resolving manifests
762 branchmerge: True, force: True, partial: False
762 branchmerge: True, force: True, partial: False
763 ancestor: b592ea63bb0c, local: 7e61b508e709+, remote: 7a4785234d87
763 ancestor: b592ea63bb0c, local: 7e61b508e709+, remote: 7a4785234d87
764 starting 4 threads for background file closing (?)
764 starting 4 threads for background file closing (?)
765 committing files:
765 committing files:
766 b
766 b
767 warning: can't find ancestor for 'b' copied from 'a'!
767 warning: can't find ancestor for 'b' copied from 'a'!
768 reusing manifest from p1 (listed files actually unchanged)
768 reusing manifest from p1 (listed files actually unchanged)
769 committing changelog
769 committing changelog
770 updating the branch cache
770 updating the branch cache
771 $ hg log -r 'destination(13)'
771 $ hg log -r 'destination(13)'
772 All copies of a cset
772 All copies of a cset
773 $ hg log -r 'origin(13) or destination(origin(13))'
773 $ hg log -r 'origin(13) or destination(origin(13))'
774 changeset: 2:5c095ad7e90f
774 changeset: 2:5c095ad7e90f
775 user: test
775 user: test
776 date: Thu Jan 01 00:00:00 1970 +0000
776 date: Thu Jan 01 00:00:00 1970 +0000
777 summary: 2
777 summary: 2
778
778
779 changeset: 7:ef0ef43d49e7
779 changeset: 7:ef0ef43d49e7
780 parent: 0:68795b066622
780 parent: 0:68795b066622
781 user: foo
781 user: foo
782 date: Thu Jan 01 00:00:00 1970 +0000
782 date: Thu Jan 01 00:00:00 1970 +0000
783 summary: 2
783 summary: 2
784
784
785 changeset: 13:7a4785234d87
785 changeset: 13:7a4785234d87
786 user: foo
786 user: foo
787 date: Thu Jan 01 00:00:00 1970 +0000
787 date: Thu Jan 01 00:00:00 1970 +0000
788 summary: 2
788 summary: 2
789
789
790 changeset: 19:9627f653b421
790 changeset: 19:9627f653b421
791 user: test
791 user: test
792 date: Thu Jan 01 00:00:00 1970 +0000
792 date: Thu Jan 01 00:00:00 1970 +0000
793 summary: 2
793 summary: 2
794
794
795 changeset: 21:7e61b508e709
795 changeset: 21:7e61b508e709
796 branch: dev
796 branch: dev
797 user: foo
797 user: foo
798 date: Thu Jan 01 00:00:00 1970 +0000
798 date: Thu Jan 01 00:00:00 1970 +0000
799 summary: 2
799 summary: 2
800
800
801 changeset: 22:3a4e92d81b97
801 changeset: 22:3a4e92d81b97
802 branch: dev
802 branch: dev
803 tag: tip
803 tag: tip
804 user: foo
804 user: foo
805 date: Thu Jan 01 00:00:00 1970 +0000
805 date: Thu Jan 01 00:00:00 1970 +0000
806 summary: 2
806 summary: 2
807
807
808
808
809 graft works on complex revset
809 graft works on complex revset
810
810
811 $ hg graft 'origin(13) or destination(origin(13))'
811 $ hg graft 'origin(13) or destination(origin(13))'
812 skipping ancestor revision 21:7e61b508e709
812 skipping ancestor revision 21:7e61b508e709
813 skipping ancestor revision 22:3a4e92d81b97
813 skipping ancestor revision 22:3a4e92d81b97
814 skipping revision 2:5c095ad7e90f (already grafted to 22:3a4e92d81b97)
814 skipping revision 2:5c095ad7e90f (already grafted to 22:3a4e92d81b97)
815 grafting 7:ef0ef43d49e7 "2"
815 grafting 7:ef0ef43d49e7 "2"
816 warning: can't find ancestor for 'b' copied from 'a'!
816 warning: can't find ancestor for 'b' copied from 'a'!
817 grafting 13:7a4785234d87 "2"
817 grafting 13:7a4785234d87 "2"
818 warning: can't find ancestor for 'b' copied from 'a'!
818 warning: can't find ancestor for 'b' copied from 'a'!
819 grafting 19:9627f653b421 "2"
819 grafting 19:9627f653b421 "2"
820 merging b
820 merging b
821 warning: can't find ancestor for 'b' copied from 'a'!
821 warning: can't find ancestor for 'b' copied from 'a'!
822
822
823 graft with --force (still doesn't graft merges)
823 graft with --force (still doesn't graft merges)
824
824
825 $ hg graft 19 0 6
825 $ hg graft 19 0 6
826 skipping ungraftable merge revision 6
826 skipping ungraftable merge revision 6
827 skipping ancestor revision 0:68795b066622
827 skipping ancestor revision 0:68795b066622
828 skipping already grafted revision 19:9627f653b421 (22:3a4e92d81b97 also has origin 2:5c095ad7e90f)
828 skipping already grafted revision 19:9627f653b421 (22:3a4e92d81b97 also has origin 2:5c095ad7e90f)
829 [255]
829 [255]
830 $ hg graft 19 0 6 --force
830 $ hg graft 19 0 6 --force
831 skipping ungraftable merge revision 6
831 skipping ungraftable merge revision 6
832 grafting 19:9627f653b421 "2"
832 grafting 19:9627f653b421 "2"
833 merging b
833 merging b
834 warning: can't find ancestor for 'b' copied from 'a'!
834 warning: can't find ancestor for 'b' copied from 'a'!
835 grafting 0:68795b066622 "0"
835 grafting 0:68795b066622 "0"
836
836
837 graft --force after backout
837 graft --force after backout
838
838
839 $ echo abc > a
839 $ echo abc > a
840 $ hg ci -m 28
840 $ hg ci -m 28
841 $ hg backout 28
841 $ hg backout 28
842 reverting a
842 reverting a
843 changeset 29:9d95e865b00c backs out changeset 28:cc20d29aec8d
843 changeset 29:9d95e865b00c backs out changeset 28:cc20d29aec8d
844 $ hg graft 28
844 $ hg graft 28
845 skipping ancestor revision 28:cc20d29aec8d
845 skipping ancestor revision 28:cc20d29aec8d
846 [255]
846 [255]
847 $ hg graft 28 --force
847 $ hg graft 28 --force
848 grafting 28:cc20d29aec8d "28"
848 grafting 28:cc20d29aec8d "28"
849 merging a
849 merging a
850 $ cat a
850 $ cat a
851 abc
851 abc
852
852
853 graft --continue after --force
853 graft --continue after --force
854
854
855 $ echo def > a
855 $ echo def > a
856 $ hg ci -m 31
856 $ hg ci -m 31
857 $ hg graft 28 --force --tool internal:fail
857 $ hg graft 28 --force --tool internal:fail
858 grafting 28:cc20d29aec8d "28"
858 grafting 28:cc20d29aec8d "28"
859 abort: unresolved conflicts, can't continue
859 abort: unresolved conflicts, can't continue
860 (use 'hg resolve' and 'hg graft --continue')
860 (use 'hg resolve' and 'hg graft --continue')
861 [255]
861 [255]
862 $ hg resolve --all
862 $ hg resolve --all
863 merging a
863 merging a
864 warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
864 warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
865 [1]
865 [1]
866 $ echo abc > a
866 $ echo abc > a
867 $ hg resolve -m a
867 $ hg resolve -m a
868 (no more unresolved files)
868 (no more unresolved files)
869 continue: hg graft --continue
869 continue: hg graft --continue
870 $ hg graft -c
870 $ hg graft -c
871 grafting 28:cc20d29aec8d "28"
871 grafting 28:cc20d29aec8d "28"
872 $ cat a
872 $ cat a
873 abc
873 abc
874
874
875 Continue testing same origin policy, using revision numbers from test above
875 Continue testing same origin policy, using revision numbers from test above
876 but do some destructive editing of the repo:
876 but do some destructive editing of the repo:
877
877
878 $ hg up -qC 7
878 $ hg up -qC 7
879 $ hg tag -l -r 13 tmp
879 $ hg tag -l -r 13 tmp
880 $ hg --config extensions.strip= strip 2
880 $ hg --config extensions.strip= strip 2
881 saved backup bundle to $TESTTMP/a/.hg/strip-backup/5c095ad7e90f-d323a1e4-backup.hg
881 saved backup bundle to $TESTTMP/a/.hg/strip-backup/5c095ad7e90f-d323a1e4-backup.hg
882 $ hg graft tmp
882 $ hg graft tmp
883 skipping already grafted revision 8:7a4785234d87 (2:ef0ef43d49e7 also has unknown origin 5c095ad7e90f)
883 skipping already grafted revision 8:7a4785234d87 (2:ef0ef43d49e7 also has unknown origin 5c095ad7e90f)
884 [255]
884 [255]
885
885
886 Empty graft
886 Empty graft
887
887
888 $ hg up -qr 26
888 $ hg up -qr 26
889 $ hg tag -f something
889 $ hg tag -f something
890 $ hg graft -qr 27
890 $ hg graft -qr 27
891 $ hg graft -f 27
891 $ hg graft -f 27
892 grafting 27:17d42b8f5d50 "28"
892 grafting 27:17d42b8f5d50 "28"
893 note: graft of 27:17d42b8f5d50 created no changes to commit
893 note: graft of 27:17d42b8f5d50 created no changes to commit
894
894
895 $ cd ..
895 $ cd ..
896
896
897 Graft to duplicate a commit
897 Graft to duplicate a commit
898
898
899 $ hg init graftsibling
899 $ hg init graftsibling
900 $ cd graftsibling
900 $ cd graftsibling
901 $ touch a
901 $ touch a
902 $ hg commit -qAm a
902 $ hg commit -qAm a
903 $ touch b
903 $ touch b
904 $ hg commit -qAm b
904 $ hg commit -qAm b
905 $ hg log -G -T '{rev}\n'
905 $ hg log -G -T '{rev}\n'
906 @ 1
906 @ 1
907 |
907 |
908 o 0
908 o 0
909
909
910 $ hg up -q 0
910 $ hg up -q 0
911 $ hg graft -r 1
911 $ hg graft -r 1
912 grafting 1:0e067c57feba "b" (tip)
912 grafting 1:0e067c57feba "b" (tip)
913 $ hg log -G -T '{rev}\n'
913 $ hg log -G -T '{rev}\n'
914 @ 2
914 @ 2
915 |
915 |
916 | o 1
916 | o 1
917 |/
917 |/
918 o 0
918 o 0
919
919
920 Graft to duplicate a commit twice
920 Graft to duplicate a commit twice
921
921
922 $ hg up -q 0
922 $ hg up -q 0
923 $ hg graft -r 2
923 $ hg graft -r 2
924 grafting 2:044ec77f6389 "b" (tip)
924 grafting 2:044ec77f6389 "b" (tip)
925 $ hg log -G -T '{rev}\n'
925 $ hg log -G -T '{rev}\n'
926 @ 3
926 @ 3
927 |
927 |
928 | o 2
928 | o 2
929 |/
929 |/
930 | o 1
930 | o 1
931 |/
931 |/
932 o 0
932 o 0
933
933
934 Graft from behind a move or rename
934 Graft from behind a move or rename
935 ==================================
935 ==================================
936
936
937 NOTE: This is affected by issue5343, and will need updating when it's fixed
937 NOTE: This is affected by issue5343, and will need updating when it's fixed
938
938
939 Consider this topology for a regular graft:
939 Consider this topology for a regular graft:
940
940
941 o c1
941 o c1
942 |
942 |
943 | o c2
943 | o c2
944 | |
944 | |
945 | o ca # stands for "common ancestor"
945 | o ca # stands for "common ancestor"
946 |/
946 |/
947 o cta # stands for "common topological ancestor"
947 o cta # stands for "common topological ancestor"
948
948
949 Note that in issue5343, ca==cta.
949 Note that in issue5343, ca==cta.
950
950
951 The following table shows the possible cases. Here, "x->y" and, equivalently,
951 The following table shows the possible cases. Here, "x->y" and, equivalently,
952 "y<-x", where x is an ancestor of y, means that some copy happened from x to y.
952 "y<-x", where x is an ancestor of y, means that some copy happened from x to y.
953
953
954 name | c1<-cta | cta<->ca | ca->c2
954 name | c1<-cta | cta<->ca | ca->c2
955 A.0 | | |
955 A.0 | | |
956 A.1 | X | |
956 A.1 | X | |
957 A.2 | | X |
957 A.2 | | X |
958 A.3 | | | X
958 A.3 | | | X
959 A.4 | X | X |
959 A.4 | X | X |
960 A.5 | X | | X
960 A.5 | X | | X
961 A.6 | | X | X
961 A.6 | | X | X
962 A.7 | X | X | X
962 A.7 | X | X | X
963
963
964 A.0 is trivial, and doesn't need copy tracking.
964 A.0 is trivial, and doesn't need copy tracking.
965 For A.1, a forward rename is recorded in the c1 pass, to be followed later.
965 For A.1, a forward rename is recorded in the c1 pass, to be followed later.
966 In A.2, the rename is recorded in the c2 pass and followed backwards.
966 In A.2, the rename is recorded in the c2 pass and followed backwards.
967 A.3 is recorded in the c2 pass as a forward rename to be duplicated on target.
967 A.3 is recorded in the c2 pass as a forward rename to be duplicated on target.
968 In A.4, both passes of checkcopies record incomplete renames, which are
968 In A.4, both passes of checkcopies record incomplete renames, which are
969 then joined in mergecopies to record a rename to be followed.
969 then joined in mergecopies to record a rename to be followed.
970 In A.5 and A.7, the c1 pass records an incomplete rename, while the c2 pass
970 In A.5 and A.7, the c1 pass records an incomplete rename, while the c2 pass
971 records an incomplete divergence. The incomplete rename is then joined to the
971 records an incomplete divergence. The incomplete rename is then joined to the
972 appropriate side of the incomplete divergence, and the result is recorded as a
972 appropriate side of the incomplete divergence, and the result is recorded as a
973 divergence. The code doesn't distinguish at all between these two cases, since
973 divergence. The code doesn't distinguish at all between these two cases, since
974 the end result of them is the same: an incomplete divergence joined with an
974 the end result of them is the same: an incomplete divergence joined with an
975 incomplete rename into a divergence.
975 incomplete rename into a divergence.
976 Finally, A.6 records a divergence entirely in the c2 pass.
976 Finally, A.6 records a divergence entirely in the c2 pass.
977
977
978 A.4 has a degenerate case a<-b<-a->a, where checkcopies isn't needed at all.
978 A.4 has a degenerate case a<-b<-a->a, where checkcopies isn't needed at all.
979 A.5 has a special case a<-b<-b->a, which is treated like a<-b->a in a merge.
979 A.5 has a special case a<-b<-b->a, which is treated like a<-b->a in a merge.
980 A.5 has issue5343 as a special case.
980 A.5 has issue5343 as a special case.
981 A.6 has a special case a<-a<-b->a. Here, checkcopies will find a spurious
981 A.6 has a special case a<-a<-b->a. Here, checkcopies will find a spurious
982 incomplete divergence, which is in fact complete. This is handled later in
982 incomplete divergence, which is in fact complete. This is handled later in
983 mergecopies.
983 mergecopies.
984 A.7 has 4 special cases: a<-b<-a->b (the "ping-pong" case), a<-b<-c->b,
984 A.7 has 4 special cases: a<-b<-a->b (the "ping-pong" case), a<-b<-c->b,
985 a<-b<-a->c and a<-b<-c->a. Of these, only the "ping-pong" case is interesting,
985 a<-b<-a->c and a<-b<-c->a. Of these, only the "ping-pong" case is interesting,
986 the others are fairly trivial (a<-b<-c->b and a<-b<-a->c proceed like the base
986 the others are fairly trivial (a<-b<-c->b and a<-b<-a->c proceed like the base
987 case, a<-b<-c->a is treated the same as a<-b<-b->a).
987 case, a<-b<-c->a is treated the same as a<-b<-b->a).
988
988
989 f5a therefore tests the "ping-pong" rename case, where a file is renamed to the
989 f5a therefore tests the "ping-pong" rename case, where a file is renamed to the
990 same name on both branches, then the rename is backed out on one branch, and
990 same name on both branches, then the rename is backed out on one branch, and
991 the backout is grafted to the other branch. This creates a challenging rename
991 the backout is grafted to the other branch. This creates a challenging rename
992 sequence of a<-b<-a->b in the graft target, topological CA, graft CA and graft
992 sequence of a<-b<-a->b in the graft target, topological CA, graft CA and graft
993 source, respectively. Since rename detection will run on the c1 side for such a
993 source, respectively. Since rename detection will run on the c1 side for such a
994 sequence (as for technical reasons, we split the c1 and c2 sides not at the
994 sequence (as for technical reasons, we split the c1 and c2 sides not at the
995 graft CA, but rather at the topological CA), it will pick up a false rename,
995 graft CA, but rather at the topological CA), it will pick up a false rename,
996 and cause a spurious merge conflict. This false rename is always exactly the
996 and cause a spurious merge conflict. This false rename is always exactly the
997 reverse of the true rename that would be detected on the c2 side, so we can
997 reverse of the true rename that would be detected on the c2 side, so we can
998 correct for it by detecting this condition and reversing as necessary.
998 correct for it by detecting this condition and reversing as necessary.
999
999
1000 First, set up the repository with commits to be grafted
1000 First, set up the repository with commits to be grafted
1001
1001
1002 $ hg init ../graftmove
1002 $ hg init ../graftmove
1003 $ cd ../graftmove
1003 $ cd ../graftmove
1004 $ echo c1a > f1a
1004 $ echo c1a > f1a
1005 $ echo c2a > f2a
1005 $ echo c2a > f2a
1006 $ echo c3a > f3a
1006 $ echo c3a > f3a
1007 $ echo c4a > f4a
1007 $ echo c4a > f4a
1008 $ echo c5a > f5a
1008 $ echo c5a > f5a
1009 $ hg ci -qAm A0
1009 $ hg ci -qAm A0
1010 $ hg mv f1a f1b
1010 $ hg mv f1a f1b
1011 $ hg mv f3a f3b
1011 $ hg mv f3a f3b
1012 $ hg mv f5a f5b
1012 $ hg mv f5a f5b
1013 $ hg ci -qAm B0
1013 $ hg ci -qAm B0
1014 $ echo c1c > f1b
1014 $ echo c1c > f1b
1015 $ hg mv f2a f2c
1015 $ hg mv f2a f2c
1016 $ hg mv f5b f5a
1016 $ hg mv f5b f5a
1017 $ echo c5c > f5a
1017 $ echo c5c > f5a
1018 $ hg ci -qAm C0
1018 $ hg ci -qAm C0
1019 $ hg mv f3b f3d
1019 $ hg mv f3b f3d
1020 $ echo c4d > f4a
1020 $ echo c4d > f4a
1021 $ hg ci -qAm D0
1021 $ hg ci -qAm D0
1022 $ hg log -G
1022 $ hg log -G
1023 @ changeset: 3:b69f5839d2d9
1023 @ changeset: 3:b69f5839d2d9
1024 | tag: tip
1024 | tag: tip
1025 | user: test
1025 | user: test
1026 | date: Thu Jan 01 00:00:00 1970 +0000
1026 | date: Thu Jan 01 00:00:00 1970 +0000
1027 | summary: D0
1027 | summary: D0
1028 |
1028 |
1029 o changeset: 2:f58c7e2b28fa
1029 o changeset: 2:f58c7e2b28fa
1030 | user: test
1030 | user: test
1031 | date: Thu Jan 01 00:00:00 1970 +0000
1031 | date: Thu Jan 01 00:00:00 1970 +0000
1032 | summary: C0
1032 | summary: C0
1033 |
1033 |
1034 o changeset: 1:3d7bba921b5d
1034 o changeset: 1:3d7bba921b5d
1035 | user: test
1035 | user: test
1036 | date: Thu Jan 01 00:00:00 1970 +0000
1036 | date: Thu Jan 01 00:00:00 1970 +0000
1037 | summary: B0
1037 | summary: B0
1038 |
1038 |
1039 o changeset: 0:11f7a1b56675
1039 o changeset: 0:11f7a1b56675
1040 user: test
1040 user: test
1041 date: Thu Jan 01 00:00:00 1970 +0000
1041 date: Thu Jan 01 00:00:00 1970 +0000
1042 summary: A0
1042 summary: A0
1043
1043
1044
1044
1045 Test the cases A.2 (f1x), A.3 (f2x) and a special case of A.6 (f5x) where the
1045 Test the cases A.2 (f1x), A.3 (f2x) and a special case of A.6 (f5x) where the
1046 two renames actually converge to the same name (thus no actual divergence).
1046 two renames actually converge to the same name (thus no actual divergence).
1047
1047
1048 $ hg up -q 'desc("A0")'
1048 $ hg up -q 'desc("A0")'
1049 $ HGEDITOR="echo C1 >" hg graft -r 'desc("C0")' --edit
1049 $ HGEDITOR="echo C1 >" hg graft -r 'desc("C0")' --edit
1050 grafting 2:f58c7e2b28fa "C0"
1050 grafting 2:f58c7e2b28fa "C0"
1051 merging f1a and f1b to f1a
1051 merging f1a and f1b to f1a
1052 merging f5a
1052 merging f5a
1053 warning: can't find ancestor for 'f5a' copied from 'f5b'!
1053 warning: can't find ancestor for 'f5a' copied from 'f5b'!
1054 $ hg status --change .
1054 $ hg status --change .
1055 M f1a
1055 M f1a
1056 M f5a
1056 M f5a
1057 A f2c
1057 A f2c
1058 R f2a
1058 R f2a
1059 $ hg cat f1a
1059 $ hg cat f1a
1060 c1c
1060 c1c
1061 $ hg cat f1b
1061 $ hg cat f1b
1062 f1b: no such file in rev c9763722f9bd
1062 f1b: no such file in rev c9763722f9bd
1063 [1]
1063 [1]
1064
1064
1065 Test the cases A.0 (f4x) and A.6 (f3x)
1065 Test the cases A.0 (f4x) and A.6 (f3x)
1066
1066
1067 $ HGEDITOR="echo D1 >" hg graft -r 'desc("D0")' --edit
1067 $ HGEDITOR="echo D1 >" hg graft -r 'desc("D0")' --edit
1068 grafting 3:b69f5839d2d9 "D0"
1068 grafting 3:b69f5839d2d9 "D0"
1069 note: possible conflict - f3b was renamed multiple times to:
1069 note: possible conflict - f3b was renamed multiple times to:
1070 f3a
1070 f3a
1071 f3d
1071 f3d
1072 warning: can't find ancestor for 'f3d' copied from 'f3b'!
1072 warning: can't find ancestor for 'f3d' copied from 'f3b'!
1073
1073
1074 Set up the repository for some further tests
1074 Set up the repository for some further tests
1075
1075
1076 $ hg up -q "min(desc("A0"))"
1076 $ hg up -q "min(desc("A0"))"
1077 $ hg mv f1a f1e
1077 $ hg mv f1a f1e
1078 $ echo c2e > f2a
1078 $ echo c2e > f2a
1079 $ hg mv f3a f3e
1079 $ hg mv f3a f3e
1080 $ hg mv f4a f4e
1080 $ hg mv f4a f4e
1081 $ hg mv f5a f5b
1081 $ hg mv f5a f5b
1082 $ hg ci -qAm "E0"
1082 $ hg ci -qAm "E0"
1083 $ hg up -q "min(desc("A0"))"
1083 $ hg up -q "min(desc("A0"))"
1084 $ hg cp f1a f1f
1084 $ hg cp f1a f1f
1085 $ hg ci -qAm "F0"
1085 $ hg ci -qAm "F0"
1086 $ hg up -q "min(desc("A0"))"
1086 $ hg up -q "min(desc("A0"))"
1087 $ hg cp f1a f1g
1087 $ hg cp f1a f1g
1088 $ echo c1g > f1g
1088 $ echo c1g > f1g
1089 $ hg ci -qAm "G0"
1089 $ hg ci -qAm "G0"
1090 $ hg log -G
1090 $ hg log -G
1091 @ changeset: 8:ba67f08fb15a
1091 @ changeset: 8:ba67f08fb15a
1092 | tag: tip
1092 | tag: tip
1093 | parent: 0:11f7a1b56675
1093 | parent: 0:11f7a1b56675
1094 | user: test
1094 | user: test
1095 | date: Thu Jan 01 00:00:00 1970 +0000
1095 | date: Thu Jan 01 00:00:00 1970 +0000
1096 | summary: G0
1096 | summary: G0
1097 |
1097 |
1098 | o changeset: 7:d376ab0d7fda
1098 | o changeset: 7:d376ab0d7fda
1099 |/ parent: 0:11f7a1b56675
1099 |/ parent: 0:11f7a1b56675
1100 | user: test
1100 | user: test
1101 | date: Thu Jan 01 00:00:00 1970 +0000
1101 | date: Thu Jan 01 00:00:00 1970 +0000
1102 | summary: F0
1102 | summary: F0
1103 |
1103 |
1104 | o changeset: 6:6bd1736cab86
1104 | o changeset: 6:6bd1736cab86
1105 |/ parent: 0:11f7a1b56675
1105 |/ parent: 0:11f7a1b56675
1106 | user: test
1106 | user: test
1107 | date: Thu Jan 01 00:00:00 1970 +0000
1107 | date: Thu Jan 01 00:00:00 1970 +0000
1108 | summary: E0
1108 | summary: E0
1109 |
1109 |
1110 | o changeset: 5:560daee679da
1110 | o changeset: 5:560daee679da
1111 | | user: test
1111 | | user: test
1112 | | date: Thu Jan 01 00:00:00 1970 +0000
1112 | | date: Thu Jan 01 00:00:00 1970 +0000
1113 | | summary: D1
1113 | | summary: D1
1114 | |
1114 | |
1115 | o changeset: 4:c9763722f9bd
1115 | o changeset: 4:c9763722f9bd
1116 |/ parent: 0:11f7a1b56675
1116 |/ parent: 0:11f7a1b56675
1117 | user: test
1117 | user: test
1118 | date: Thu Jan 01 00:00:00 1970 +0000
1118 | date: Thu Jan 01 00:00:00 1970 +0000
1119 | summary: C1
1119 | summary: C1
1120 |
1120 |
1121 | o changeset: 3:b69f5839d2d9
1121 | o changeset: 3:b69f5839d2d9
1122 | | user: test
1122 | | user: test
1123 | | date: Thu Jan 01 00:00:00 1970 +0000
1123 | | date: Thu Jan 01 00:00:00 1970 +0000
1124 | | summary: D0
1124 | | summary: D0
1125 | |
1125 | |
1126 | o changeset: 2:f58c7e2b28fa
1126 | o changeset: 2:f58c7e2b28fa
1127 | | user: test
1127 | | user: test
1128 | | date: Thu Jan 01 00:00:00 1970 +0000
1128 | | date: Thu Jan 01 00:00:00 1970 +0000
1129 | | summary: C0
1129 | | summary: C0
1130 | |
1130 | |
1131 | o changeset: 1:3d7bba921b5d
1131 | o changeset: 1:3d7bba921b5d
1132 |/ user: test
1132 |/ user: test
1133 | date: Thu Jan 01 00:00:00 1970 +0000
1133 | date: Thu Jan 01 00:00:00 1970 +0000
1134 | summary: B0
1134 | summary: B0
1135 |
1135 |
1136 o changeset: 0:11f7a1b56675
1136 o changeset: 0:11f7a1b56675
1137 user: test
1137 user: test
1138 date: Thu Jan 01 00:00:00 1970 +0000
1138 date: Thu Jan 01 00:00:00 1970 +0000
1139 summary: A0
1139 summary: A0
1140
1140
1141
1141
1142 Test the cases A.4 (f1x), the "ping-pong" special case of A.7 (f5x),
1142 Test the cases A.4 (f1x), the "ping-pong" special case of A.7 (f5x),
1143 and A.3 with a local content change to be preserved (f2x).
1143 and A.3 with a local content change to be preserved (f2x).
1144
1144
1145 $ hg up -q "desc("E0")"
1145 $ hg up -q "desc("E0")"
1146 $ HGEDITOR="echo C2 >" hg graft -r 'desc("C0")' --edit
1146 $ HGEDITOR="echo C2 >" hg graft -r 'desc("C0")' --edit
1147 grafting 2:f58c7e2b28fa "C0"
1147 grafting 2:f58c7e2b28fa "C0"
1148 merging f1e and f1b to f1e
1148 merging f1e and f1b to f1e
1149 merging f2a and f2c to f2c
1149 merging f2a and f2c to f2c
1150
1150
1151 Test the cases A.1 (f4x) and A.7 (f3x).
1151 Test the cases A.1 (f4x) and A.7 (f3x).
1152
1152
1153 $ HGEDITOR="echo D2 >" hg graft -r 'desc("D0")' --edit
1153 $ HGEDITOR="echo D2 >" hg graft -r 'desc("D0")' --edit
1154 grafting 3:b69f5839d2d9 "D0"
1154 grafting 3:b69f5839d2d9 "D0"
1155 note: possible conflict - f3b was renamed multiple times to:
1155 note: possible conflict - f3b was renamed multiple times to:
1156 f3d
1156 f3d
1157 f3e
1157 f3e
1158 merging f4e and f4a to f4e
1158 merging f4e and f4a to f4e
1159 warning: can't find ancestor for 'f3d' copied from 'f3b'!
1159 warning: can't find ancestor for 'f3d' copied from 'f3b'!
1160
1160
1161 $ hg cat f2c
1161 $ hg cat f2c
1162 c2e
1162 c2e
1163
1163
1164 Test the case A.5 (move case, f1x).
1164 Test the case A.5 (move case, f1x).
1165
1165
1166 $ hg up -q "desc("C0")"
1166 $ hg up -q "desc("C0")"
1167 BROKEN: Shouldn't get the warning about missing ancestor
1167 BROKEN: Shouldn't get the warning about missing ancestor
1168 $ HGEDITOR="echo E1 >" hg graft -r 'desc("E0")' --edit
1168 $ HGEDITOR="echo E1 >" hg graft -r 'desc("E0")' --edit
1169 grafting 6:6bd1736cab86 "E0"
1169 grafting 6:6bd1736cab86 "E0"
1170 note: possible conflict - f1a was renamed multiple times to:
1170 note: possible conflict - f1a was renamed multiple times to:
1171 f1b
1171 f1b
1172 f1e
1172 f1e
1173 note: possible conflict - f3a was renamed multiple times to:
1173 note: possible conflict - f3a was renamed multiple times to:
1174 f3b
1174 f3b
1175 f3e
1175 f3e
1176 merging f2c and f2a to f2c
1176 merging f2c and f2a to f2c
1177 merging f5a and f5b to f5b
1177 merging f5a and f5b to f5b
1178 warning: can't find ancestor for 'f1e' copied from 'f1a'!
1178 warning: can't find ancestor for 'f1e' copied from 'f1a'!
1179 warning: can't find ancestor for 'f3e' copied from 'f3a'!
1179 warning: can't find ancestor for 'f3e' copied from 'f3a'!
1180 $ cat f1e
1180 $ cat f1e
1181 c1a
1181 c1a
1182
1182
1183 Test the case A.5 (copy case, f1x).
1183 Test the case A.5 (copy case, f1x).
1184
1184
1185 $ hg up -q "desc("C0")"
1185 $ hg up -q "desc("C0")"
1186 BROKEN: Shouldn't get the warning about missing ancestor
1186 BROKEN: Shouldn't get the warning about missing ancestor
1187 $ HGEDITOR="echo F1 >" hg graft -r 'desc("F0")' --edit
1187 $ HGEDITOR="echo F1 >" hg graft -r 'desc("F0")' --edit
1188 grafting 7:d376ab0d7fda "F0"
1188 grafting 7:d376ab0d7fda "F0"
1189 warning: can't find ancestor for 'f1f' copied from 'f1a'!
1189 warning: can't find ancestor for 'f1f' copied from 'f1a'!
1190 BROKEN: f1f should be marked a copy from f1b
1190 BROKEN: f1f should be marked a copy from f1b
1191 $ hg st --copies --change .
1191 $ hg st --copies --change .
1192 A f1f
1192 A f1f
1193 BROKEN: f1f should have the new content from f1b (i.e. "c1c")
1193 BROKEN: f1f should have the new content from f1b (i.e. "c1c")
1194 $ cat f1f
1194 $ cat f1f
1195 c1a
1195 c1a
1196
1196
1197 Test the case A.5 (copy+modify case, f1x).
1197 Test the case A.5 (copy+modify case, f1x).
1198
1198
1199 $ hg up -q "desc("C0")"
1199 $ hg up -q "desc("C0")"
1200 BROKEN: We should get a merge conflict from the 3-way merge between f1b in C0
1200 BROKEN: We should get a merge conflict from the 3-way merge between f1b in C0
1201 (content "c1c") and f1g in G0 (content "c1g") with f1a in A0 as base (content
1201 (content "c1c") and f1g in G0 (content "c1g") with f1a in A0 as base (content
1202 "c1a")
1202 "c1a")
1203 $ HGEDITOR="echo G1 >" hg graft -r 'desc("G0")' --edit
1203 $ HGEDITOR="echo G1 >" hg graft -r 'desc("G0")' --edit
1204 grafting 8:ba67f08fb15a "G0"
1204 grafting 8:ba67f08fb15a "G0"
1205 warning: can't find ancestor for 'f1g' copied from 'f1a'!
1205 warning: can't find ancestor for 'f1g' copied from 'f1a'!
1206
1206
1207 Check the results of the grafts tested
1207 Check the results of the grafts tested
1208
1208
1209 $ hg log -CGv --patch --git
1209 $ hg log -CGv --patch --git
1210 @ changeset: 13:ef3adf6c20a4
1210 @ changeset: 13:ef3adf6c20a4
1211 | tag: tip
1211 | tag: tip
1212 | parent: 2:f58c7e2b28fa
1212 | parent: 2:f58c7e2b28fa
1213 | user: test
1213 | user: test
1214 | date: Thu Jan 01 00:00:00 1970 +0000
1214 | date: Thu Jan 01 00:00:00 1970 +0000
1215 | files: f1g
1215 | files: f1g
1216 | description:
1216 | description:
1217 | G1
1217 | G1
1218 |
1218 |
1219 |
1219 |
1220 | diff --git a/f1g b/f1g
1220 | diff --git a/f1g b/f1g
1221 | new file mode 100644
1221 | new file mode 100644
1222 | --- /dev/null
1222 | --- /dev/null
1223 | +++ b/f1g
1223 | +++ b/f1g
1224 | @@ -0,0 +1,1 @@
1224 | @@ -0,0 +1,1 @@
1225 | +c1g
1225 | +c1g
1226 |
1226 |
1227 | o changeset: 12:b5542d755b54
1227 | o changeset: 12:b5542d755b54
1228 |/ parent: 2:f58c7e2b28fa
1228 |/ parent: 2:f58c7e2b28fa
1229 | user: test
1229 | user: test
1230 | date: Thu Jan 01 00:00:00 1970 +0000
1230 | date: Thu Jan 01 00:00:00 1970 +0000
1231 | files: f1f
1231 | files: f1f
1232 | description:
1232 | description:
1233 | F1
1233 | F1
1234 |
1234 |
1235 |
1235 |
1236 | diff --git a/f1f b/f1f
1236 | diff --git a/f1f b/f1f
1237 | new file mode 100644
1237 | new file mode 100644
1238 | --- /dev/null
1238 | --- /dev/null
1239 | +++ b/f1f
1239 | +++ b/f1f
1240 | @@ -0,0 +1,1 @@
1240 | @@ -0,0 +1,1 @@
1241 | +c1a
1241 | +c1a
1242 |
1242 |
1243 | o changeset: 11:f8a162271246
1243 | o changeset: 11:f8a162271246
1244 |/ parent: 2:f58c7e2b28fa
1244 |/ parent: 2:f58c7e2b28fa
1245 | user: test
1245 | user: test
1246 | date: Thu Jan 01 00:00:00 1970 +0000
1246 | date: Thu Jan 01 00:00:00 1970 +0000
1247 | files: f1e f2c f3e f4a f4e f5a f5b
1247 | files: f1e f2c f3e f4a f4e f5a f5b
1248 | copies: f4e (f4a) f5b (f5a)
1248 | copies: f4e (f4a) f5b (f5a)
1249 | description:
1249 | description:
1250 | E1
1250 | E1
1251 |
1251 |
1252 |
1252 |
1253 | diff --git a/f1e b/f1e
1253 | diff --git a/f1e b/f1e
1254 | new file mode 100644
1254 | new file mode 100644
1255 | --- /dev/null
1255 | --- /dev/null
1256 | +++ b/f1e
1256 | +++ b/f1e
1257 | @@ -0,0 +1,1 @@
1257 | @@ -0,0 +1,1 @@
1258 | +c1a
1258 | +c1a
1259 | diff --git a/f2c b/f2c
1259 | diff --git a/f2c b/f2c
1260 | --- a/f2c
1260 | --- a/f2c
1261 | +++ b/f2c
1261 | +++ b/f2c
1262 | @@ -1,1 +1,1 @@
1262 | @@ -1,1 +1,1 @@
1263 | -c2a
1263 | -c2a
1264 | +c2e
1264 | +c2e
1265 | diff --git a/f3e b/f3e
1265 | diff --git a/f3e b/f3e
1266 | new file mode 100644
1266 | new file mode 100644
1267 | --- /dev/null
1267 | --- /dev/null
1268 | +++ b/f3e
1268 | +++ b/f3e
1269 | @@ -0,0 +1,1 @@
1269 | @@ -0,0 +1,1 @@
1270 | +c3a
1270 | +c3a
1271 | diff --git a/f4a b/f4e
1271 | diff --git a/f4a b/f4e
1272 | rename from f4a
1272 | rename from f4a
1273 | rename to f4e
1273 | rename to f4e
1274 | diff --git a/f5a b/f5b
1274 | diff --git a/f5a b/f5b
1275 | rename from f5a
1275 | rename from f5a
1276 | rename to f5b
1276 | rename to f5b
1277 |
1277 |
1278 | o changeset: 10:93ee502e8b0a
1278 | o changeset: 10:93ee502e8b0a
1279 | | user: test
1279 | | user: test
1280 | | date: Thu Jan 01 00:00:00 1970 +0000
1280 | | date: Thu Jan 01 00:00:00 1970 +0000
1281 | | files: f3d f4e
1281 | | files: f3d f4e
1282 | | description:
1282 | | description:
1283 | | D2
1283 | | D2
1284 | |
1284 | |
1285 | |
1285 | |
1286 | | diff --git a/f3d b/f3d
1286 | | diff --git a/f3d b/f3d
1287 | | new file mode 100644
1287 | | new file mode 100644
1288 | | --- /dev/null
1288 | | --- /dev/null
1289 | | +++ b/f3d
1289 | | +++ b/f3d
1290 | | @@ -0,0 +1,1 @@
1290 | | @@ -0,0 +1,1 @@
1291 | | +c3a
1291 | | +c3a
1292 | | diff --git a/f4e b/f4e
1292 | | diff --git a/f4e b/f4e
1293 | | --- a/f4e
1293 | | --- a/f4e
1294 | | +++ b/f4e
1294 | | +++ b/f4e
1295 | | @@ -1,1 +1,1 @@
1295 | | @@ -1,1 +1,1 @@
1296 | | -c4a
1296 | | -c4a
1297 | | +c4d
1297 | | +c4d
1298 | |
1298 | |
1299 | o changeset: 9:539cf145f496
1299 | o changeset: 9:539cf145f496
1300 | | parent: 6:6bd1736cab86
1300 | | parent: 6:6bd1736cab86
1301 | | user: test
1301 | | user: test
1302 | | date: Thu Jan 01 00:00:00 1970 +0000
1302 | | date: Thu Jan 01 00:00:00 1970 +0000
1303 | | files: f1e f2a f2c f5a f5b
1303 | | files: f1e f2a f2c f5a f5b
1304 | | copies: f2c (f2a) f5a (f5b)
1304 | | copies: f2c (f2a) f5a (f5b)
1305 | | description:
1305 | | description:
1306 | | C2
1306 | | C2
1307 | |
1307 | |
1308 | |
1308 | |
1309 | | diff --git a/f1e b/f1e
1309 | | diff --git a/f1e b/f1e
1310 | | --- a/f1e
1310 | | --- a/f1e
1311 | | +++ b/f1e
1311 | | +++ b/f1e
1312 | | @@ -1,1 +1,1 @@
1312 | | @@ -1,1 +1,1 @@
1313 | | -c1a
1313 | | -c1a
1314 | | +c1c
1314 | | +c1c
1315 | | diff --git a/f2a b/f2c
1315 | | diff --git a/f2a b/f2c
1316 | | rename from f2a
1316 | | rename from f2a
1317 | | rename to f2c
1317 | | rename to f2c
1318 | | diff --git a/f5b b/f5a
1318 | | diff --git a/f5b b/f5a
1319 | | rename from f5b
1319 | | rename from f5b
1320 | | rename to f5a
1320 | | rename to f5a
1321 | | --- a/f5b
1321 | | --- a/f5b
1322 | | +++ b/f5a
1322 | | +++ b/f5a
1323 | | @@ -1,1 +1,1 @@
1323 | | @@ -1,1 +1,1 @@
1324 | | -c5a
1324 | | -c5a
1325 | | +c5c
1325 | | +c5c
1326 | |
1326 | |
1327 | | o changeset: 8:ba67f08fb15a
1327 | | o changeset: 8:ba67f08fb15a
1328 | | | parent: 0:11f7a1b56675
1328 | | | parent: 0:11f7a1b56675
1329 | | | user: test
1329 | | | user: test
1330 | | | date: Thu Jan 01 00:00:00 1970 +0000
1330 | | | date: Thu Jan 01 00:00:00 1970 +0000
1331 | | | files: f1g
1331 | | | files: f1g
1332 | | | copies: f1g (f1a)
1332 | | | copies: f1g (f1a)
1333 | | | description:
1333 | | | description:
1334 | | | G0
1334 | | | G0
1335 | | |
1335 | | |
1336 | | |
1336 | | |
1337 | | | diff --git a/f1a b/f1g
1337 | | | diff --git a/f1a b/f1g
1338 | | | copy from f1a
1338 | | | copy from f1a
1339 | | | copy to f1g
1339 | | | copy to f1g
1340 | | | --- a/f1a
1340 | | | --- a/f1a
1341 | | | +++ b/f1g
1341 | | | +++ b/f1g
1342 | | | @@ -1,1 +1,1 @@
1342 | | | @@ -1,1 +1,1 @@
1343 | | | -c1a
1343 | | | -c1a
1344 | | | +c1g
1344 | | | +c1g
1345 | | |
1345 | | |
1346 | | | o changeset: 7:d376ab0d7fda
1346 | | | o changeset: 7:d376ab0d7fda
1347 | | |/ parent: 0:11f7a1b56675
1347 | | |/ parent: 0:11f7a1b56675
1348 | | | user: test
1348 | | | user: test
1349 | | | date: Thu Jan 01 00:00:00 1970 +0000
1349 | | | date: Thu Jan 01 00:00:00 1970 +0000
1350 | | | files: f1f
1350 | | | files: f1f
1351 | | | copies: f1f (f1a)
1351 | | | copies: f1f (f1a)
1352 | | | description:
1352 | | | description:
1353 | | | F0
1353 | | | F0
1354 | | |
1354 | | |
1355 | | |
1355 | | |
1356 | | | diff --git a/f1a b/f1f
1356 | | | diff --git a/f1a b/f1f
1357 | | | copy from f1a
1357 | | | copy from f1a
1358 | | | copy to f1f
1358 | | | copy to f1f
1359 | | |
1359 | | |
1360 | o | changeset: 6:6bd1736cab86
1360 | o | changeset: 6:6bd1736cab86
1361 | |/ parent: 0:11f7a1b56675
1361 | |/ parent: 0:11f7a1b56675
1362 | | user: test
1362 | | user: test
1363 | | date: Thu Jan 01 00:00:00 1970 +0000
1363 | | date: Thu Jan 01 00:00:00 1970 +0000
1364 | | files: f1a f1e f2a f3a f3e f4a f4e f5a f5b
1364 | | files: f1a f1e f2a f3a f3e f4a f4e f5a f5b
1365 | | copies: f1e (f1a) f3e (f3a) f4e (f4a) f5b (f5a)
1365 | | copies: f1e (f1a) f3e (f3a) f4e (f4a) f5b (f5a)
1366 | | description:
1366 | | description:
1367 | | E0
1367 | | E0
1368 | |
1368 | |
1369 | |
1369 | |
1370 | | diff --git a/f1a b/f1e
1370 | | diff --git a/f1a b/f1e
1371 | | rename from f1a
1371 | | rename from f1a
1372 | | rename to f1e
1372 | | rename to f1e
1373 | | diff --git a/f2a b/f2a
1373 | | diff --git a/f2a b/f2a
1374 | | --- a/f2a
1374 | | --- a/f2a
1375 | | +++ b/f2a
1375 | | +++ b/f2a
1376 | | @@ -1,1 +1,1 @@
1376 | | @@ -1,1 +1,1 @@
1377 | | -c2a
1377 | | -c2a
1378 | | +c2e
1378 | | +c2e
1379 | | diff --git a/f3a b/f3e
1379 | | diff --git a/f3a b/f3e
1380 | | rename from f3a
1380 | | rename from f3a
1381 | | rename to f3e
1381 | | rename to f3e
1382 | | diff --git a/f4a b/f4e
1382 | | diff --git a/f4a b/f4e
1383 | | rename from f4a
1383 | | rename from f4a
1384 | | rename to f4e
1384 | | rename to f4e
1385 | | diff --git a/f5a b/f5b
1385 | | diff --git a/f5a b/f5b
1386 | | rename from f5a
1386 | | rename from f5a
1387 | | rename to f5b
1387 | | rename to f5b
1388 | |
1388 | |
1389 | | o changeset: 5:560daee679da
1389 | | o changeset: 5:560daee679da
1390 | | | user: test
1390 | | | user: test
1391 | | | date: Thu Jan 01 00:00:00 1970 +0000
1391 | | | date: Thu Jan 01 00:00:00 1970 +0000
1392 | | | files: f3d f4a
1392 | | | files: f3d f4a
1393 | | | description:
1393 | | | description:
1394 | | | D1
1394 | | | D1
1395 | | |
1395 | | |
1396 | | |
1396 | | |
1397 | | | diff --git a/f3d b/f3d
1397 | | | diff --git a/f3d b/f3d
1398 | | | new file mode 100644
1398 | | | new file mode 100644
1399 | | | --- /dev/null
1399 | | | --- /dev/null
1400 | | | +++ b/f3d
1400 | | | +++ b/f3d
1401 | | | @@ -0,0 +1,1 @@
1401 | | | @@ -0,0 +1,1 @@
1402 | | | +c3a
1402 | | | +c3a
1403 | | | diff --git a/f4a b/f4a
1403 | | | diff --git a/f4a b/f4a
1404 | | | --- a/f4a
1404 | | | --- a/f4a
1405 | | | +++ b/f4a
1405 | | | +++ b/f4a
1406 | | | @@ -1,1 +1,1 @@
1406 | | | @@ -1,1 +1,1 @@
1407 | | | -c4a
1407 | | | -c4a
1408 | | | +c4d
1408 | | | +c4d
1409 | | |
1409 | | |
1410 | | o changeset: 4:c9763722f9bd
1410 | | o changeset: 4:c9763722f9bd
1411 | |/ parent: 0:11f7a1b56675
1411 | |/ parent: 0:11f7a1b56675
1412 | | user: test
1412 | | user: test
1413 | | date: Thu Jan 01 00:00:00 1970 +0000
1413 | | date: Thu Jan 01 00:00:00 1970 +0000
1414 | | files: f1a f2a f2c f5a
1414 | | files: f1a f2a f2c f5a
1415 | | copies: f2c (f2a)
1415 | | copies: f2c (f2a)
1416 | | description:
1416 | | description:
1417 | | C1
1417 | | C1
1418 | |
1418 | |
1419 | |
1419 | |
1420 | | diff --git a/f1a b/f1a
1420 | | diff --git a/f1a b/f1a
1421 | | --- a/f1a
1421 | | --- a/f1a
1422 | | +++ b/f1a
1422 | | +++ b/f1a
1423 | | @@ -1,1 +1,1 @@
1423 | | @@ -1,1 +1,1 @@
1424 | | -c1a
1424 | | -c1a
1425 | | +c1c
1425 | | +c1c
1426 | | diff --git a/f2a b/f2c
1426 | | diff --git a/f2a b/f2c
1427 | | rename from f2a
1427 | | rename from f2a
1428 | | rename to f2c
1428 | | rename to f2c
1429 | | diff --git a/f5a b/f5a
1429 | | diff --git a/f5a b/f5a
1430 | | --- a/f5a
1430 | | --- a/f5a
1431 | | +++ b/f5a
1431 | | +++ b/f5a
1432 | | @@ -1,1 +1,1 @@
1432 | | @@ -1,1 +1,1 @@
1433 | | -c5a
1433 | | -c5a
1434 | | +c5c
1434 | | +c5c
1435 | |
1435 | |
1436 +---o changeset: 3:b69f5839d2d9
1436 +---o changeset: 3:b69f5839d2d9
1437 | | user: test
1437 | | user: test
1438 | | date: Thu Jan 01 00:00:00 1970 +0000
1438 | | date: Thu Jan 01 00:00:00 1970 +0000
1439 | | files: f3b f3d f4a
1439 | | files: f3b f3d f4a
1440 | | copies: f3d (f3b)
1440 | | copies: f3d (f3b)
1441 | | description:
1441 | | description:
1442 | | D0
1442 | | D0
1443 | |
1443 | |
1444 | |
1444 | |
1445 | | diff --git a/f3b b/f3d
1445 | | diff --git a/f3b b/f3d
1446 | | rename from f3b
1446 | | rename from f3b
1447 | | rename to f3d
1447 | | rename to f3d
1448 | | diff --git a/f4a b/f4a
1448 | | diff --git a/f4a b/f4a
1449 | | --- a/f4a
1449 | | --- a/f4a
1450 | | +++ b/f4a
1450 | | +++ b/f4a
1451 | | @@ -1,1 +1,1 @@
1451 | | @@ -1,1 +1,1 @@
1452 | | -c4a
1452 | | -c4a
1453 | | +c4d
1453 | | +c4d
1454 | |
1454 | |
1455 o | changeset: 2:f58c7e2b28fa
1455 o | changeset: 2:f58c7e2b28fa
1456 | | user: test
1456 | | user: test
1457 | | date: Thu Jan 01 00:00:00 1970 +0000
1457 | | date: Thu Jan 01 00:00:00 1970 +0000
1458 | | files: f1b f2a f2c f5a f5b
1458 | | files: f1b f2a f2c f5a f5b
1459 | | copies: f2c (f2a) f5a (f5b)
1459 | | copies: f2c (f2a) f5a (f5b)
1460 | | description:
1460 | | description:
1461 | | C0
1461 | | C0
1462 | |
1462 | |
1463 | |
1463 | |
1464 | | diff --git a/f1b b/f1b
1464 | | diff --git a/f1b b/f1b
1465 | | --- a/f1b
1465 | | --- a/f1b
1466 | | +++ b/f1b
1466 | | +++ b/f1b
1467 | | @@ -1,1 +1,1 @@
1467 | | @@ -1,1 +1,1 @@
1468 | | -c1a
1468 | | -c1a
1469 | | +c1c
1469 | | +c1c
1470 | | diff --git a/f2a b/f2c
1470 | | diff --git a/f2a b/f2c
1471 | | rename from f2a
1471 | | rename from f2a
1472 | | rename to f2c
1472 | | rename to f2c
1473 | | diff --git a/f5b b/f5a
1473 | | diff --git a/f5b b/f5a
1474 | | rename from f5b
1474 | | rename from f5b
1475 | | rename to f5a
1475 | | rename to f5a
1476 | | --- a/f5b
1476 | | --- a/f5b
1477 | | +++ b/f5a
1477 | | +++ b/f5a
1478 | | @@ -1,1 +1,1 @@
1478 | | @@ -1,1 +1,1 @@
1479 | | -c5a
1479 | | -c5a
1480 | | +c5c
1480 | | +c5c
1481 | |
1481 | |
1482 o | changeset: 1:3d7bba921b5d
1482 o | changeset: 1:3d7bba921b5d
1483 |/ user: test
1483 |/ user: test
1484 | date: Thu Jan 01 00:00:00 1970 +0000
1484 | date: Thu Jan 01 00:00:00 1970 +0000
1485 | files: f1a f1b f3a f3b f5a f5b
1485 | files: f1a f1b f3a f3b f5a f5b
1486 | copies: f1b (f1a) f3b (f3a) f5b (f5a)
1486 | copies: f1b (f1a) f3b (f3a) f5b (f5a)
1487 | description:
1487 | description:
1488 | B0
1488 | B0
1489 |
1489 |
1490 |
1490 |
1491 | diff --git a/f1a b/f1b
1491 | diff --git a/f1a b/f1b
1492 | rename from f1a
1492 | rename from f1a
1493 | rename to f1b
1493 | rename to f1b
1494 | diff --git a/f3a b/f3b
1494 | diff --git a/f3a b/f3b
1495 | rename from f3a
1495 | rename from f3a
1496 | rename to f3b
1496 | rename to f3b
1497 | diff --git a/f5a b/f5b
1497 | diff --git a/f5a b/f5b
1498 | rename from f5a
1498 | rename from f5a
1499 | rename to f5b
1499 | rename to f5b
1500 |
1500 |
1501 o changeset: 0:11f7a1b56675
1501 o changeset: 0:11f7a1b56675
1502 user: test
1502 user: test
1503 date: Thu Jan 01 00:00:00 1970 +0000
1503 date: Thu Jan 01 00:00:00 1970 +0000
1504 files: f1a f2a f3a f4a f5a
1504 files: f1a f2a f3a f4a f5a
1505 description:
1505 description:
1506 A0
1506 A0
1507
1507
1508
1508
1509 diff --git a/f1a b/f1a
1509 diff --git a/f1a b/f1a
1510 new file mode 100644
1510 new file mode 100644
1511 --- /dev/null
1511 --- /dev/null
1512 +++ b/f1a
1512 +++ b/f1a
1513 @@ -0,0 +1,1 @@
1513 @@ -0,0 +1,1 @@
1514 +c1a
1514 +c1a
1515 diff --git a/f2a b/f2a
1515 diff --git a/f2a b/f2a
1516 new file mode 100644
1516 new file mode 100644
1517 --- /dev/null
1517 --- /dev/null
1518 +++ b/f2a
1518 +++ b/f2a
1519 @@ -0,0 +1,1 @@
1519 @@ -0,0 +1,1 @@
1520 +c2a
1520 +c2a
1521 diff --git a/f3a b/f3a
1521 diff --git a/f3a b/f3a
1522 new file mode 100644
1522 new file mode 100644
1523 --- /dev/null
1523 --- /dev/null
1524 +++ b/f3a
1524 +++ b/f3a
1525 @@ -0,0 +1,1 @@
1525 @@ -0,0 +1,1 @@
1526 +c3a
1526 +c3a
1527 diff --git a/f4a b/f4a
1527 diff --git a/f4a b/f4a
1528 new file mode 100644
1528 new file mode 100644
1529 --- /dev/null
1529 --- /dev/null
1530 +++ b/f4a
1530 +++ b/f4a
1531 @@ -0,0 +1,1 @@
1531 @@ -0,0 +1,1 @@
1532 +c4a
1532 +c4a
1533 diff --git a/f5a b/f5a
1533 diff --git a/f5a b/f5a
1534 new file mode 100644
1534 new file mode 100644
1535 --- /dev/null
1535 --- /dev/null
1536 +++ b/f5a
1536 +++ b/f5a
1537 @@ -0,0 +1,1 @@
1537 @@ -0,0 +1,1 @@
1538 +c5a
1538 +c5a
1539
1539
1540 Check superfluous filemerge of files renamed in the past but untouched by graft
1540 Check superfluous filemerge of files renamed in the past but untouched by graft
1541
1541
1542 $ echo a > a
1542 $ echo a > a
1543 $ hg ci -qAma
1543 $ hg ci -qAma
1544 $ hg mv a b
1544 $ hg mv a b
1545 $ echo b > b
1545 $ echo b > b
1546 $ hg ci -qAmb
1546 $ hg ci -qAmb
1547 $ echo c > c
1547 $ echo c > c
1548 $ hg ci -qAmc
1548 $ hg ci -qAmc
1549 $ hg up -q .~2
1549 $ hg up -q .~2
1550 $ hg graft tip -qt:fail
1550 $ hg graft tip -qt:fail
1551
1551
1552 $ cd ..
1552 $ cd ..
1553
1553
1554 Graft a change into a new file previously grafted into a renamed directory
1554 Graft a change into a new file previously grafted into a renamed directory
1555
1555
1556 $ hg init dirmovenewfile
1556 $ hg init dirmovenewfile
1557 $ cd dirmovenewfile
1557 $ cd dirmovenewfile
1558 $ mkdir a
1558 $ mkdir a
1559 $ echo a > a/a
1559 $ echo a > a/a
1560 $ hg ci -qAma
1560 $ hg ci -qAma
1561 $ echo x > a/x
1561 $ echo x > a/x
1562 $ hg ci -qAmx
1562 $ hg ci -qAmx
1563 $ hg up -q 0
1563 $ hg up -q 0
1564 $ hg mv -q a b
1564 $ hg mv -q a b
1565 $ hg ci -qAmb
1565 $ hg ci -qAmb
1566 $ hg graft -q 1 # a/x grafted as b/x, but no copy information recorded
1566 $ hg graft -q 1 # a/x grafted as b/x, but no copy information recorded
1567 $ hg up -q 1
1567 $ hg up -q 1
1568 $ echo y > a/x
1568 $ echo y > a/x
1569 $ hg ci -qAmy
1569 $ hg ci -qAmy
1570 $ hg up -q 3
1570 $ hg up -q 3
1571 $ hg graft -q 4
1571 $ hg graft -q 4
1572 $ hg status --change .
1572 $ hg status --change .
1573 M b/x
1573 M b/x
1574
1574
1575 Prepare for test of skipped changesets and how merges can influence it:
1575 Prepare for test of skipped changesets and how merges can influence it:
1576
1576
1577 $ hg merge -q -r 1 --tool :local
1577 $ hg merge -q -r 1 --tool :local
1578 $ hg ci -m m
1578 $ hg ci -m m
1579 $ echo xx >> b/x
1579 $ echo xx >> b/x
1580 $ hg ci -m xx
1580 $ hg ci -m xx
1581
1581
1582 $ hg log -G -T '{rev} {desc|firstline}'
1582 $ hg log -G -T '{rev} {desc|firstline}'
1583 @ 7 xx
1583 @ 7 xx
1584 |
1584 |
1585 o 6 m
1585 o 6 m
1586 |\
1586 |\
1587 | o 5 y
1587 | o 5 y
1588 | |
1588 | |
1589 +---o 4 y
1589 +---o 4 y
1590 | |
1590 | |
1591 | o 3 x
1591 | o 3 x
1592 | |
1592 | |
1593 | o 2 b
1593 | o 2 b
1594 | |
1594 | |
1595 o | 1 x
1595 o | 1 x
1596 |/
1596 |/
1597 o 0 a
1597 o 0 a
1598
1598
1599 Grafting of plain changes correctly detects that 3 and 5 should be skipped:
1599 Grafting of plain changes correctly detects that 3 and 5 should be skipped:
1600
1600
1601 $ hg up -qCr 4
1601 $ hg up -qCr 4
1602 $ hg graft --tool :local -r 2::5
1602 $ hg graft --tool :local -r 2::5
1603 skipping already grafted revision 3:ca093ca2f1d9 (was grafted from 1:13ec5badbf2a)
1603 skipping already grafted revision 3:ca093ca2f1d9 (was grafted from 1:13ec5badbf2a)
1604 skipping already grafted revision 5:43e9eb70dab0 (was grafted from 4:6c9a1289e5f1)
1604 skipping already grafted revision 5:43e9eb70dab0 (was grafted from 4:6c9a1289e5f1)
1605 grafting 2:42127f193bcd "b"
1605 grafting 2:42127f193bcd "b"
1606
1606
1607 Extending the graft range to include a (skipped) merge of 3 will not prevent us from
1607 Extending the graft range to include a (skipped) merge of 3 will not prevent us from
1608 also detecting that both 3 and 5 should be skipped:
1608 also detecting that both 3 and 5 should be skipped:
1609
1609
1610 $ hg up -qCr 4
1610 $ hg up -qCr 4
1611 $ hg graft --tool :local -r 2::7
1611 $ hg graft --tool :local -r 2::7
1612 skipping ungraftable merge revision 6
1612 skipping ungraftable merge revision 6
1613 skipping already grafted revision 3:ca093ca2f1d9 (was grafted from 1:13ec5badbf2a)
1613 skipping already grafted revision 3:ca093ca2f1d9 (was grafted from 1:13ec5badbf2a)
1614 skipping already grafted revision 5:43e9eb70dab0 (was grafted from 4:6c9a1289e5f1)
1614 skipping already grafted revision 5:43e9eb70dab0 (was grafted from 4:6c9a1289e5f1)
1615 grafting 2:42127f193bcd "b"
1615 grafting 2:42127f193bcd "b"
1616 grafting 7:d3c3f2b38ecc "xx"
1616 grafting 7:d3c3f2b38ecc "xx"
1617 note: graft of 7:d3c3f2b38ecc created no changes to commit
1617 note: graft of 7:d3c3f2b38ecc created no changes to commit
1618
1618
1619 $ cd ..
1619 $ cd ..
1620
1620
1621 Grafted revision should be warned and skipped only once. (issue6024)
1621 Grafted revision should be warned and skipped only once. (issue6024)
1622
1622
1623 $ mkdir issue6024
1623 $ mkdir issue6024
1624 $ cd issue6024
1624 $ cd issue6024
1625
1625
1626 $ hg init base
1626 $ hg init base
1627 $ cd base
1627 $ cd base
1628 $ touch x
1628 $ touch x
1629 $ hg commit -qAminit
1629 $ hg commit -qAminit
1630 $ echo a > x
1630 $ echo a > x
1631 $ hg commit -mchange
1631 $ hg commit -mchange
1632 $ hg update -q 0
1632 $ hg update -q 0
1633 $ hg graft -r 1
1633 $ hg graft -r 1
1634 grafting 1:a0b923c546aa "change" (tip)
1634 grafting 1:a0b923c546aa "change" (tip)
1635 $ cd ..
1635 $ cd ..
1636
1636
1637 $ hg clone -qr 2 base clone
1637 $ hg clone -qr 2 base clone
1638 $ cd clone
1638 $ cd clone
1639 $ hg pull -q
1639 $ hg pull -q
1640 $ hg merge -q 2
1640 $ hg merge -q 2
1641 $ hg commit -mmerge
1641 $ hg commit -mmerge
1642 $ hg update -q 0
1642 $ hg update -q 0
1643 $ hg graft -r 1
1643 $ hg graft -r 1
1644 grafting 1:04fc6d444368 "change"
1644 grafting 1:04fc6d444368 "change"
1645 $ hg update -q 3
1645 $ hg update -q 3
1646 $ hg log -G -T '{rev}:{node|shortest} <- {extras.source|shortest}\n'
1646 $ hg log -G -T '{rev}:{node|shortest} <- {extras.source|shortest}\n'
1647 o 4:4e16 <- a0b9
1647 o 4:4e16 <- a0b9
1648 |
1648 |
1649 | @ 3:f0ac <-
1649 | @ 3:f0ac <-
1650 | |\
1650 | |\
1651 +---o 2:a0b9 <-
1651 +---o 2:a0b9 <-
1652 | |
1652 | |
1653 | o 1:04fc <- a0b9
1653 | o 1:04fc <- a0b9
1654 |/
1654 |/
1655 o 0:7848 <-
1655 o 0:7848 <-
1656
1656
1657
1657
1658 the source of rev 4 is an ancestor of the working parent, and was also
1658 the source of rev 4 is an ancestor of the working parent, and was also
1659 grafted as rev 1. it should be stripped from the target revisions only once.
1659 grafted as rev 1. it should be stripped from the target revisions only once.
1660
1660
1661 $ hg graft -r 4
1661 $ hg graft -r 4
1662 skipping already grafted revision 4:4e16bab40c9c (1:04fc6d444368 also has origin 2:a0b923c546aa)
1662 skipping already grafted revision 4:4e16bab40c9c (1:04fc6d444368 also has origin 2:a0b923c546aa)
1663 [255]
1663 [255]
1664
1664
1665 $ cd ../..
1665 $ cd ../..
1666
1666
1667 Testing the reading of old format graftstate file with newer mercurial
1667 Testing the reading of old format graftstate file with newer mercurial
1668
1668
1669 $ hg init oldgraft
1669 $ hg init oldgraft
1670 $ cd oldgraft
1670 $ cd oldgraft
1671 $ for ch in a b c; do echo foo > $ch; hg add $ch; hg ci -Aqm "added "$ch; done;
1671 $ for ch in a b c; do echo foo > $ch; hg add $ch; hg ci -Aqm "added "$ch; done;
1672 $ hg log -GT "{rev}:{node|short} {desc}\n"
1672 $ hg log -GT "{rev}:{node|short} {desc}\n"
1673 @ 2:8be98ac1a569 added c
1673 @ 2:8be98ac1a569 added c
1674 |
1674 |
1675 o 1:80e6d2c47cfe added b
1675 o 1:80e6d2c47cfe added b
1676 |
1676 |
1677 o 0:f7ad41964313 added a
1677 o 0:f7ad41964313 added a
1678
1678
1679 $ hg up 0
1679 $ hg up 0
1680 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
1680 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
1681 $ echo bar > b
1681 $ echo bar > b
1682 $ hg add b
1682 $ hg add b
1683 $ hg ci -m "bar to b"
1683 $ hg ci -m "bar to b"
1684 created new head
1684 created new head
1685 $ hg graft -r 1 -r 2
1685 $ hg graft -r 1 -r 2
1686 grafting 1:80e6d2c47cfe "added b"
1686 grafting 1:80e6d2c47cfe "added b"
1687 merging b
1687 merging b
1688 warning: conflicts while merging b! (edit, then use 'hg resolve --mark')
1688 warning: conflicts while merging b! (edit, then use 'hg resolve --mark')
1689 abort: unresolved conflicts, can't continue
1689 abort: unresolved conflicts, can't continue
1690 (use 'hg resolve' and 'hg graft --continue')
1690 (use 'hg resolve' and 'hg graft --continue')
1691 [255]
1691 [255]
1692
1692
1693 Writing the nodes in old format to graftstate
1693 Writing the nodes in old format to graftstate
1694
1694
1695 $ hg log -r 1 -r 2 -T '{node}\n' > .hg/graftstate
1695 $ hg log -r 1 -r 2 -T '{node}\n' > .hg/graftstate
1696 $ echo foo > b
1696 $ echo foo > b
1697 $ hg resolve -m
1697 $ hg resolve -m
1698 (no more unresolved files)
1698 (no more unresolved files)
1699 continue: hg graft --continue
1699 continue: hg graft --continue
1700 $ hg graft --continue
1700 $ hg graft --continue
1701 grafting 1:80e6d2c47cfe "added b"
1701 grafting 1:80e6d2c47cfe "added b"
1702 grafting 2:8be98ac1a569 "added c"
1702 grafting 2:8be98ac1a569 "added c"
1703
1703
1704 Testing that --user is preserved during conflicts and value is reused while
1704 Testing that --user is preserved during conflicts and value is reused while
1705 running `hg graft --continue`
1705 running `hg graft --continue`
1706
1706
1707 $ hg log -G
1707 $ hg log -G
1708 @ changeset: 5:711e9fa999f1
1708 @ changeset: 5:711e9fa999f1
1709 | tag: tip
1709 | tag: tip
1710 | user: test
1710 | user: test
1711 | date: Thu Jan 01 00:00:00 1970 +0000
1711 | date: Thu Jan 01 00:00:00 1970 +0000
1712 | summary: added c
1712 | summary: added c
1713 |
1713 |
1714 o changeset: 4:e5ad7353b408
1714 o changeset: 4:e5ad7353b408
1715 | user: test
1715 | user: test
1716 | date: Thu Jan 01 00:00:00 1970 +0000
1716 | date: Thu Jan 01 00:00:00 1970 +0000
1717 | summary: added b
1717 | summary: added b
1718 |
1718 |
1719 o changeset: 3:9e887f7a939c
1719 o changeset: 3:9e887f7a939c
1720 | parent: 0:f7ad41964313
1720 | parent: 0:f7ad41964313
1721 | user: test
1721 | user: test
1722 | date: Thu Jan 01 00:00:00 1970 +0000
1722 | date: Thu Jan 01 00:00:00 1970 +0000
1723 | summary: bar to b
1723 | summary: bar to b
1724 |
1724 |
1725 | o changeset: 2:8be98ac1a569
1725 | o changeset: 2:8be98ac1a569
1726 | | user: test
1726 | | user: test
1727 | | date: Thu Jan 01 00:00:00 1970 +0000
1727 | | date: Thu Jan 01 00:00:00 1970 +0000
1728 | | summary: added c
1728 | | summary: added c
1729 | |
1729 | |
1730 | o changeset: 1:80e6d2c47cfe
1730 | o changeset: 1:80e6d2c47cfe
1731 |/ user: test
1731 |/ user: test
1732 | date: Thu Jan 01 00:00:00 1970 +0000
1732 | date: Thu Jan 01 00:00:00 1970 +0000
1733 | summary: added b
1733 | summary: added b
1734 |
1734 |
1735 o changeset: 0:f7ad41964313
1735 o changeset: 0:f7ad41964313
1736 user: test
1736 user: test
1737 date: Thu Jan 01 00:00:00 1970 +0000
1737 date: Thu Jan 01 00:00:00 1970 +0000
1738 summary: added a
1738 summary: added a
1739
1739
1740
1740
1741 $ hg up '.^^'
1741 $ hg up '.^^'
1742 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
1742 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
1743
1743
1744 $ hg graft -r 1 -r 2 --user batman
1744 $ hg graft -r 1 -r 2 --user batman
1745 grafting 1:80e6d2c47cfe "added b"
1745 grafting 1:80e6d2c47cfe "added b"
1746 merging b
1746 merging b
1747 warning: conflicts while merging b! (edit, then use 'hg resolve --mark')
1747 warning: conflicts while merging b! (edit, then use 'hg resolve --mark')
1748 abort: unresolved conflicts, can't continue
1748 abort: unresolved conflicts, can't continue
1749 (use 'hg resolve' and 'hg graft --continue')
1749 (use 'hg resolve' and 'hg graft --continue')
1750 [255]
1750 [255]
1751
1751
1752 $ echo wat > b
1752 $ echo wat > b
1753 $ hg resolve -m
1753 $ hg resolve -m
1754 (no more unresolved files)
1754 (no more unresolved files)
1755 continue: hg graft --continue
1755 continue: hg graft --continue
1756
1756
1757 $ hg graft --continue
1757 $ hg graft --continue
1758 grafting 1:80e6d2c47cfe "added b"
1758 grafting 1:80e6d2c47cfe "added b"
1759 grafting 2:8be98ac1a569 "added c"
1759 grafting 2:8be98ac1a569 "added c"
1760
1760
1761 $ hg log -Gr 3::
1761 $ hg log -Gr 3::
1762 @ changeset: 7:11a36ffaacf2
1762 @ changeset: 7:11a36ffaacf2
1763 | tag: tip
1763 | tag: tip
1764 | user: batman
1764 | user: batman
1765 | date: Thu Jan 01 00:00:00 1970 +0000
1765 | date: Thu Jan 01 00:00:00 1970 +0000
1766 | summary: added c
1766 | summary: added c
1767 |
1767 |
1768 o changeset: 6:76803afc6511
1768 o changeset: 6:76803afc6511
1769 | parent: 3:9e887f7a939c
1769 | parent: 3:9e887f7a939c
1770 | user: batman
1770 | user: batman
1771 | date: Thu Jan 01 00:00:00 1970 +0000
1771 | date: Thu Jan 01 00:00:00 1970 +0000
1772 | summary: added b
1772 | summary: added b
1773 |
1773 |
1774 | o changeset: 5:711e9fa999f1
1774 | o changeset: 5:711e9fa999f1
1775 | | user: test
1775 | | user: test
1776 | | date: Thu Jan 01 00:00:00 1970 +0000
1776 | | date: Thu Jan 01 00:00:00 1970 +0000
1777 | | summary: added c
1777 | | summary: added c
1778 | |
1778 | |
1779 | o changeset: 4:e5ad7353b408
1779 | o changeset: 4:e5ad7353b408
1780 |/ user: test
1780 |/ user: test
1781 | date: Thu Jan 01 00:00:00 1970 +0000
1781 | date: Thu Jan 01 00:00:00 1970 +0000
1782 | summary: added b
1782 | summary: added b
1783 |
1783 |
1784 o changeset: 3:9e887f7a939c
1784 o changeset: 3:9e887f7a939c
1785 | parent: 0:f7ad41964313
1785 | parent: 0:f7ad41964313
1786 ~ user: test
1786 ~ user: test
1787 date: Thu Jan 01 00:00:00 1970 +0000
1787 date: Thu Jan 01 00:00:00 1970 +0000
1788 summary: bar to b
1788 summary: bar to b
1789
1789
1790 Test that --date is preserved and reused in `hg graft --continue`
1790 Test that --date is preserved and reused in `hg graft --continue`
1791
1791
1792 $ hg up '.^^'
1792 $ hg up '.^^'
1793 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
1793 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
1794 $ hg graft -r 1 -r 2 --date '1234560000 120'
1794 $ hg graft -r 1 -r 2 --date '1234560000 120'
1795 grafting 1:80e6d2c47cfe "added b"
1795 grafting 1:80e6d2c47cfe "added b"
1796 merging b
1796 merging b
1797 warning: conflicts while merging b! (edit, then use 'hg resolve --mark')
1797 warning: conflicts while merging b! (edit, then use 'hg resolve --mark')
1798 abort: unresolved conflicts, can't continue
1798 abort: unresolved conflicts, can't continue
1799 (use 'hg resolve' and 'hg graft --continue')
1799 (use 'hg resolve' and 'hg graft --continue')
1800 [255]
1800 [255]
1801
1801
1802 $ echo foobar > b
1802 $ echo foobar > b
1803 $ hg resolve -m
1803 $ hg resolve -m
1804 (no more unresolved files)
1804 (no more unresolved files)
1805 continue: hg graft --continue
1805 continue: hg graft --continue
1806 $ hg graft --continue
1806 $ hg graft --continue
1807 grafting 1:80e6d2c47cfe "added b"
1807 grafting 1:80e6d2c47cfe "added b"
1808 grafting 2:8be98ac1a569 "added c"
1808 grafting 2:8be98ac1a569 "added c"
1809
1809
1810 $ hg log -Gr '.^^::.'
1810 $ hg log -Gr '.^^::.'
1811 @ changeset: 9:1896b76e007a
1811 @ changeset: 9:1896b76e007a
1812 | tag: tip
1812 | tag: tip
1813 | user: test
1813 | user: test
1814 | date: Fri Feb 13 21:18:00 2009 -0002
1814 | date: Fri Feb 13 21:18:00 2009 -0002
1815 | summary: added c
1815 | summary: added c
1816 |
1816 |
1817 o changeset: 8:ce2b4f1632af
1817 o changeset: 8:ce2b4f1632af
1818 | parent: 3:9e887f7a939c
1818 | parent: 3:9e887f7a939c
1819 | user: test
1819 | user: test
1820 | date: Fri Feb 13 21:18:00 2009 -0002
1820 | date: Fri Feb 13 21:18:00 2009 -0002
1821 | summary: added b
1821 | summary: added b
1822 |
1822 |
1823 o changeset: 3:9e887f7a939c
1823 o changeset: 3:9e887f7a939c
1824 | parent: 0:f7ad41964313
1824 | parent: 0:f7ad41964313
1825 ~ user: test
1825 ~ user: test
1826 date: Thu Jan 01 00:00:00 1970 +0000
1826 date: Thu Jan 01 00:00:00 1970 +0000
1827 summary: bar to b
1827 summary: bar to b
1828
1828
1829 Test that --log is preserved and reused in `hg graft --continue`
1829 Test that --log is preserved and reused in `hg graft --continue`
1830
1830
1831 $ hg up '.^^'
1831 $ hg up '.^^'
1832 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
1832 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
1833 $ hg graft -r 1 -r 2 --log
1833 $ hg graft -r 1 -r 2 --log
1834 grafting 1:80e6d2c47cfe "added b"
1834 grafting 1:80e6d2c47cfe "added b"
1835 merging b
1835 merging b
1836 warning: conflicts while merging b! (edit, then use 'hg resolve --mark')
1836 warning: conflicts while merging b! (edit, then use 'hg resolve --mark')
1837 abort: unresolved conflicts, can't continue
1837 abort: unresolved conflicts, can't continue
1838 (use 'hg resolve' and 'hg graft --continue')
1838 (use 'hg resolve' and 'hg graft --continue')
1839 [255]
1839 [255]
1840
1840
1841 $ echo foobar > b
1841 $ echo foobar > b
1842 $ hg resolve -m
1842 $ hg resolve -m
1843 (no more unresolved files)
1843 (no more unresolved files)
1844 continue: hg graft --continue
1844 continue: hg graft --continue
1845
1845
1846 $ hg graft --continue
1846 $ hg graft --continue
1847 grafting 1:80e6d2c47cfe "added b"
1847 grafting 1:80e6d2c47cfe "added b"
1848 grafting 2:8be98ac1a569 "added c"
1848 grafting 2:8be98ac1a569 "added c"
1849
1849
1850 $ hg log -GT "{rev}:{node|short} {desc}" -r '.^^::.'
1850 $ hg log -GT "{rev}:{node|short} {desc}" -r '.^^::.'
1851 @ 11:30c1050a58b2 added c
1851 @ 11:30c1050a58b2 added c
1852 | (grafted from 8be98ac1a56990c2d9ca6861041b8390af7bd6f3)
1852 | (grafted from 8be98ac1a56990c2d9ca6861041b8390af7bd6f3)
1853 o 10:ec7eda2313e2 added b
1853 o 10:ec7eda2313e2 added b
1854 | (grafted from 80e6d2c47cfe5b3185519568327a17a061c7efb6)
1854 | (grafted from 80e6d2c47cfe5b3185519568327a17a061c7efb6)
1855 o 3:9e887f7a939c bar to b
1855 o 3:9e887f7a939c bar to b
1856 |
1856 |
1857 ~
1857 ~
1858
1858
1859 $ cd ..
1859 $ cd ..
1860
1860
1861 Testing the --stop flag of `hg graft` which stops the interrupted graft
1861 Testing the --stop flag of `hg graft` which stops the interrupted graft
1862
1862
1863 $ hg init stopgraft
1863 $ hg init stopgraft
1864 $ cd stopgraft
1864 $ cd stopgraft
1865 $ for ch in a b c d; do echo $ch > $ch; hg add $ch; hg ci -Aqm "added "$ch; done;
1865 $ for ch in a b c d; do echo $ch > $ch; hg add $ch; hg ci -Aqm "added "$ch; done;
1866
1866
1867 $ hg log -G
1867 $ hg log -G
1868 @ changeset: 3:9150fe93bec6
1868 @ changeset: 3:9150fe93bec6
1869 | tag: tip
1869 | tag: tip
1870 | user: test
1870 | user: test
1871 | date: Thu Jan 01 00:00:00 1970 +0000
1871 | date: Thu Jan 01 00:00:00 1970 +0000
1872 | summary: added d
1872 | summary: added d
1873 |
1873 |
1874 o changeset: 2:155349b645be
1874 o changeset: 2:155349b645be
1875 | user: test
1875 | user: test
1876 | date: Thu Jan 01 00:00:00 1970 +0000
1876 | date: Thu Jan 01 00:00:00 1970 +0000
1877 | summary: added c
1877 | summary: added c
1878 |
1878 |
1879 o changeset: 1:5f6d8a4bf34a
1879 o changeset: 1:5f6d8a4bf34a
1880 | user: test
1880 | user: test
1881 | date: Thu Jan 01 00:00:00 1970 +0000
1881 | date: Thu Jan 01 00:00:00 1970 +0000
1882 | summary: added b
1882 | summary: added b
1883 |
1883 |
1884 o changeset: 0:9092f1db7931
1884 o changeset: 0:9092f1db7931
1885 user: test
1885 user: test
1886 date: Thu Jan 01 00:00:00 1970 +0000
1886 date: Thu Jan 01 00:00:00 1970 +0000
1887 summary: added a
1887 summary: added a
1888
1888
1889 $ hg up '.^^'
1889 $ hg up '.^^'
1890 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
1890 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
1891
1891
1892 $ echo foo > d
1892 $ echo foo > d
1893 $ hg ci -Aqm "added foo to d"
1893 $ hg ci -Aqm "added foo to d"
1894
1894
1895 $ hg graft --stop
1895 $ hg graft --stop
1896 abort: no interrupted graft found
1896 abort: no interrupted graft found
1897 [255]
1897 [255]
1898
1898
1899 $ hg graft -r 3
1899 $ hg graft -r 3
1900 grafting 3:9150fe93bec6 "added d"
1900 grafting 3:9150fe93bec6 "added d"
1901 merging d
1901 merging d
1902 warning: conflicts while merging d! (edit, then use 'hg resolve --mark')
1902 warning: conflicts while merging d! (edit, then use 'hg resolve --mark')
1903 abort: unresolved conflicts, can't continue
1903 abort: unresolved conflicts, can't continue
1904 (use 'hg resolve' and 'hg graft --continue')
1904 (use 'hg resolve' and 'hg graft --continue')
1905 [255]
1905 [255]
1906
1906
1907 $ hg graft --stop --continue
1907 $ hg graft --stop --continue
1908 abort: cannot use '--continue' and '--stop' together
1908 abort: cannot use '--continue' and '--stop' together
1909 [255]
1909 [255]
1910
1910
1911 $ hg graft --stop -U
1911 $ hg graft --stop -U
1912 abort: cannot specify any other flag with '--stop'
1912 abort: cannot specify any other flag with '--stop'
1913 [255]
1913 [255]
1914 $ hg graft --stop --rev 4
1914 $ hg graft --stop --rev 4
1915 abort: cannot specify any other flag with '--stop'
1915 abort: cannot specify any other flag with '--stop'
1916 [255]
1916 [255]
1917 $ hg graft --stop --log
1917 $ hg graft --stop --log
1918 abort: cannot specify any other flag with '--stop'
1918 abort: cannot specify any other flag with '--stop'
1919 [255]
1919 [255]
1920
1920
1921 $ hg graft --stop
1921 $ hg graft --stop
1922 stopped the interrupted graft
1922 stopped the interrupted graft
1923 working directory is now at a0deacecd59d
1923 working directory is now at a0deacecd59d
1924
1924
1925 $ hg diff
1925 $ hg diff
1926
1926
1927 $ hg log -Gr '.'
1927 $ hg log -Gr '.'
1928 @ changeset: 4:a0deacecd59d
1928 @ changeset: 4:a0deacecd59d
1929 | tag: tip
1929 | tag: tip
1930 ~ parent: 1:5f6d8a4bf34a
1930 ~ parent: 1:5f6d8a4bf34a
1931 user: test
1931 user: test
1932 date: Thu Jan 01 00:00:00 1970 +0000
1932 date: Thu Jan 01 00:00:00 1970 +0000
1933 summary: added foo to d
1933 summary: added foo to d
1934
1934
1935 $ hg graft -r 2 -r 3
1935 $ hg graft -r 2 -r 3
1936 grafting 2:155349b645be "added c"
1936 grafting 2:155349b645be "added c"
1937 grafting 3:9150fe93bec6 "added d"
1937 grafting 3:9150fe93bec6 "added d"
1938 merging d
1938 merging d
1939 warning: conflicts while merging d! (edit, then use 'hg resolve --mark')
1939 warning: conflicts while merging d! (edit, then use 'hg resolve --mark')
1940 abort: unresolved conflicts, can't continue
1940 abort: unresolved conflicts, can't continue
1941 (use 'hg resolve' and 'hg graft --continue')
1941 (use 'hg resolve' and 'hg graft --continue')
1942 [255]
1942 [255]
1943
1943
1944 $ hg graft --stop
1944 $ hg graft --stop
1945 stopped the interrupted graft
1945 stopped the interrupted graft
1946 working directory is now at 75b447541a9e
1946 working directory is now at 75b447541a9e
1947
1947
1948 $ hg diff
1948 $ hg diff
1949
1949
1950 $ hg log -G -T "{rev}:{node|short} {desc}"
1950 $ hg log -G -T "{rev}:{node|short} {desc}"
1951 @ 5:75b447541a9e added c
1951 @ 5:75b447541a9e added c
1952 |
1952 |
1953 o 4:a0deacecd59d added foo to d
1953 o 4:a0deacecd59d added foo to d
1954 |
1954 |
1955 | o 3:9150fe93bec6 added d
1955 | o 3:9150fe93bec6 added d
1956 | |
1956 | |
1957 | o 2:155349b645be added c
1957 | o 2:155349b645be added c
1958 |/
1958 |/
1959 o 1:5f6d8a4bf34a added b
1959 o 1:5f6d8a4bf34a added b
1960 |
1960 |
1961 o 0:9092f1db7931 added a
1961 o 0:9092f1db7931 added a
1962
1962
1963 $ cd ..
1963 $ cd ..
1964
1964
1965 Testing the --abort flag for `hg graft` which aborts and rollback to state
1965 Testing the --abort flag for `hg graft` which aborts and rollback to state
1966 before the graft
1966 before the graft
1967
1967
1968 $ hg init abortgraft
1968 $ hg init abortgraft
1969 $ cd abortgraft
1969 $ cd abortgraft
1970 $ for ch in a b c d; do echo $ch > $ch; hg add $ch; hg ci -Aqm "added "$ch; done;
1970 $ for ch in a b c d; do echo $ch > $ch; hg add $ch; hg ci -Aqm "added "$ch; done;
1971
1971
1972 $ hg up '.^^'
1972 $ hg up '.^^'
1973 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
1973 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
1974
1974
1975 $ echo x > x
1975 $ echo x > x
1976 $ hg ci -Aqm "added x"
1976 $ hg ci -Aqm "added x"
1977 $ hg up '.^'
1977 $ hg up '.^'
1978 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1978 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1979 $ echo foo > c
1979 $ echo foo > c
1980 $ hg ci -Aqm "added foo to c"
1980 $ hg ci -Aqm "added foo to c"
1981
1981
1982 $ hg log -GT "{rev}:{node|short} {desc}"
1982 $ hg log -GT "{rev}:{node|short} {desc}"
1983 @ 5:36b793615f78 added foo to c
1983 @ 5:36b793615f78 added foo to c
1984 |
1984 |
1985 | o 4:863a25e1a9ea added x
1985 | o 4:863a25e1a9ea added x
1986 |/
1986 |/
1987 | o 3:9150fe93bec6 added d
1987 | o 3:9150fe93bec6 added d
1988 | |
1988 | |
1989 | o 2:155349b645be added c
1989 | o 2:155349b645be added c
1990 |/
1990 |/
1991 o 1:5f6d8a4bf34a added b
1991 o 1:5f6d8a4bf34a added b
1992 |
1992 |
1993 o 0:9092f1db7931 added a
1993 o 0:9092f1db7931 added a
1994
1994
1995 $ hg up 9150fe93bec6
1995 $ hg up 9150fe93bec6
1996 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
1996 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
1997
1997
1998 $ hg abort
1998 $ hg abort
1999 abort: no interrupted graft to abort (abortflag !)
1999 abort: no interrupted graft to abort (abortflag !)
2000 abort: no operation in progress (abortcommand !)
2000 abort: no operation in progress (abortcommand !)
2001 [255]
2001 [255]
2002
2002
2003 when stripping is required
2003 when stripping is required
2004 $ hg graft -r 4 -r 5
2004 $ hg graft -r 4 -r 5
2005 grafting 4:863a25e1a9ea "added x"
2005 grafting 4:863a25e1a9ea "added x"
2006 grafting 5:36b793615f78 "added foo to c" (tip)
2006 grafting 5:36b793615f78 "added foo to c" (tip)
2007 merging c
2007 merging c
2008 warning: conflicts while merging c! (edit, then use 'hg resolve --mark')
2008 warning: conflicts while merging c! (edit, then use 'hg resolve --mark')
2009 abort: unresolved conflicts, can't continue
2009 abort: unresolved conflicts, can't continue
2010 (use 'hg resolve' and 'hg graft --continue')
2010 (use 'hg resolve' and 'hg graft --continue')
2011 [255]
2011 [255]
2012
2012
2013 $ hg graft --continue --abort
2013 $ hg graft --continue --abort
2014 abort: cannot use '--continue' and '--abort' together
2014 abort: cannot use '--continue' and '--abort' together
2015 [255]
2015 [255]
2016
2016
2017 $ hg graft --abort --stop
2017 $ hg graft --abort --stop
2018 abort: cannot use '--abort' and '--stop' together
2018 abort: cannot use '--abort' and '--stop' together
2019 [255]
2019 [255]
2020
2020
2021 $ hg graft --abort --currentuser
2021 $ hg graft --abort --currentuser
2022 abort: cannot specify any other flag with '--abort'
2022 abort: cannot specify any other flag with '--abort'
2023 [255]
2023 [255]
2024
2024
2025 $ hg graft --abort --edit
2025 $ hg graft --abort --edit
2026 abort: cannot specify any other flag with '--abort'
2026 abort: cannot specify any other flag with '--abort'
2027 [255]
2027 [255]
2028
2028
2029 #if abortcommand
2029 #if abortcommand
2030 when in dry-run mode
2030 when in dry-run mode
2031 $ hg abort --dry-run
2031 $ hg abort --dry-run
2032 graft in progress, will be aborted
2032 graft in progress, will be aborted
2033 #endif
2033 #endif
2034
2034
2035 $ hg abort
2035 $ hg abort
2036 graft aborted
2036 graft aborted
2037 working directory is now at 9150fe93bec6
2037 working directory is now at 9150fe93bec6
2038 $ hg log -GT "{rev}:{node|short} {desc}"
2038 $ hg log -GT "{rev}:{node|short} {desc}"
2039 o 5:36b793615f78 added foo to c
2039 o 5:36b793615f78 added foo to c
2040 |
2040 |
2041 | o 4:863a25e1a9ea added x
2041 | o 4:863a25e1a9ea added x
2042 |/
2042 |/
2043 | @ 3:9150fe93bec6 added d
2043 | @ 3:9150fe93bec6 added d
2044 | |
2044 | |
2045 | o 2:155349b645be added c
2045 | o 2:155349b645be added c
2046 |/
2046 |/
2047 o 1:5f6d8a4bf34a added b
2047 o 1:5f6d8a4bf34a added b
2048 |
2048 |
2049 o 0:9092f1db7931 added a
2049 o 0:9092f1db7931 added a
2050
2050
2051 when stripping is not required
2051 when stripping is not required
2052 $ hg graft -r 5
2052 $ hg graft -r 5
2053 grafting 5:36b793615f78 "added foo to c" (tip)
2053 grafting 5:36b793615f78 "added foo to c" (tip)
2054 merging c
2054 merging c
2055 warning: conflicts while merging c! (edit, then use 'hg resolve --mark')
2055 warning: conflicts while merging c! (edit, then use 'hg resolve --mark')
2056 abort: unresolved conflicts, can't continue
2056 abort: unresolved conflicts, can't continue
2057 (use 'hg resolve' and 'hg graft --continue')
2057 (use 'hg resolve' and 'hg graft --continue')
2058 [255]
2058 [255]
2059
2059
2060 $ hg abort
2060 $ hg abort
2061 graft aborted
2061 graft aborted
2062 working directory is now at 9150fe93bec6
2062 working directory is now at 9150fe93bec6
2063 $ hg log -GT "{rev}:{node|short} {desc}"
2063 $ hg log -GT "{rev}:{node|short} {desc}"
2064 o 5:36b793615f78 added foo to c
2064 o 5:36b793615f78 added foo to c
2065 |
2065 |
2066 | o 4:863a25e1a9ea added x
2066 | o 4:863a25e1a9ea added x
2067 |/
2067 |/
2068 | @ 3:9150fe93bec6 added d
2068 | @ 3:9150fe93bec6 added d
2069 | |
2069 | |
2070 | o 2:155349b645be added c
2070 | o 2:155349b645be added c
2071 |/
2071 |/
2072 o 1:5f6d8a4bf34a added b
2072 o 1:5f6d8a4bf34a added b
2073 |
2073 |
2074 o 0:9092f1db7931 added a
2074 o 0:9092f1db7931 added a
2075
2075
2076 when some of the changesets became public
2076 when some of the changesets became public
2077
2077
2078 $ hg graft -r 4 -r 5
2078 $ hg graft -r 4 -r 5
2079 grafting 4:863a25e1a9ea "added x"
2079 grafting 4:863a25e1a9ea "added x"
2080 grafting 5:36b793615f78 "added foo to c" (tip)
2080 grafting 5:36b793615f78 "added foo to c" (tip)
2081 merging c
2081 merging c
2082 warning: conflicts while merging c! (edit, then use 'hg resolve --mark')
2082 warning: conflicts while merging c! (edit, then use 'hg resolve --mark')
2083 abort: unresolved conflicts, can't continue
2083 abort: unresolved conflicts, can't continue
2084 (use 'hg resolve' and 'hg graft --continue')
2084 (use 'hg resolve' and 'hg graft --continue')
2085 [255]
2085 [255]
2086
2086
2087 $ hg log -GT "{rev}:{node|short} {desc}"
2087 $ hg log -GT "{rev}:{node|short} {desc}"
2088 @ 6:6ec71c037d94 added x
2088 @ 6:6ec71c037d94 added x
2089 |
2089 |
2090 | o 5:36b793615f78 added foo to c
2090 | o 5:36b793615f78 added foo to c
2091 | |
2091 | |
2092 | | o 4:863a25e1a9ea added x
2092 | | o 4:863a25e1a9ea added x
2093 | |/
2093 | |/
2094 o | 3:9150fe93bec6 added d
2094 o | 3:9150fe93bec6 added d
2095 | |
2095 | |
2096 o | 2:155349b645be added c
2096 o | 2:155349b645be added c
2097 |/
2097 |/
2098 o 1:5f6d8a4bf34a added b
2098 o 1:5f6d8a4bf34a added b
2099 |
2099 |
2100 o 0:9092f1db7931 added a
2100 o 0:9092f1db7931 added a
2101
2101
2102 $ hg phase -r 6 --public
2102 $ hg phase -r 6 --public
2103
2103
2104 $ hg abort
2104 $ hg abort
2105 cannot clean up public changesets 6ec71c037d94
2105 cannot clean up public changesets 6ec71c037d94
2106 graft aborted
2106 graft aborted
2107 working directory is now at 6ec71c037d94
2107 working directory is now at 6ec71c037d94
2108
2108
2109 when we created new changesets on top of existing one
2109 when we created new changesets on top of existing one
2110
2110
2111 $ hg up '.^^'
2111 $ hg up '.^^'
2112 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
2112 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
2113 $ echo y > y
2113 $ echo y > y
2114 $ hg ci -Aqm "added y"
2114 $ hg ci -Aqm "added y"
2115 $ echo z > z
2115 $ echo z > z
2116 $ hg ci -Aqm "added z"
2116 $ hg ci -Aqm "added z"
2117
2117
2118 $ hg up 3
2118 $ hg up 3
2119 1 files updated, 0 files merged, 3 files removed, 0 files unresolved
2119 1 files updated, 0 files merged, 3 files removed, 0 files unresolved
2120 $ hg log -GT "{rev}:{node|short} {desc}"
2120 $ hg log -GT "{rev}:{node|short} {desc}"
2121 o 8:637f9e9bbfd4 added z
2121 o 8:637f9e9bbfd4 added z
2122 |
2122 |
2123 o 7:123221671fd4 added y
2123 o 7:123221671fd4 added y
2124 |
2124 |
2125 | o 6:6ec71c037d94 added x
2125 | o 6:6ec71c037d94 added x
2126 | |
2126 | |
2127 | | o 5:36b793615f78 added foo to c
2127 | | o 5:36b793615f78 added foo to c
2128 | | |
2128 | | |
2129 | | | o 4:863a25e1a9ea added x
2129 | | | o 4:863a25e1a9ea added x
2130 | | |/
2130 | | |/
2131 | @ | 3:9150fe93bec6 added d
2131 | @ | 3:9150fe93bec6 added d
2132 |/ /
2132 |/ /
2133 o / 2:155349b645be added c
2133 o / 2:155349b645be added c
2134 |/
2134 |/
2135 o 1:5f6d8a4bf34a added b
2135 o 1:5f6d8a4bf34a added b
2136 |
2136 |
2137 o 0:9092f1db7931 added a
2137 o 0:9092f1db7931 added a
2138
2138
2139 $ hg graft -r 8 -r 7 -r 5
2139 $ hg graft -r 8 -r 7 -r 5
2140 grafting 8:637f9e9bbfd4 "added z" (tip)
2140 grafting 8:637f9e9bbfd4 "added z" (tip)
2141 grafting 7:123221671fd4 "added y"
2141 grafting 7:123221671fd4 "added y"
2142 grafting 5:36b793615f78 "added foo to c"
2142 grafting 5:36b793615f78 "added foo to c"
2143 merging c
2143 merging c
2144 warning: conflicts while merging c! (edit, then use 'hg resolve --mark')
2144 warning: conflicts while merging c! (edit, then use 'hg resolve --mark')
2145 abort: unresolved conflicts, can't continue
2145 abort: unresolved conflicts, can't continue
2146 (use 'hg resolve' and 'hg graft --continue')
2146 (use 'hg resolve' and 'hg graft --continue')
2147 [255]
2147 [255]
2148
2148
2149 $ cd ..
2149 $ cd ..
2150 $ hg init pullrepo
2150 $ hg init pullrepo
2151 $ cd pullrepo
2151 $ cd pullrepo
2152 $ cat >> .hg/hgrc <<EOF
2152 $ cat >> .hg/hgrc <<EOF
2153 > [phases]
2153 > [phases]
2154 > publish=False
2154 > publish=False
2155 > EOF
2155 > EOF
2156 $ hg pull ../abortgraft --config phases.publish=False
2156 $ hg pull ../abortgraft --config phases.publish=False
2157 pulling from ../abortgraft
2157 pulling from ../abortgraft
2158 requesting all changes
2158 requesting all changes
2159 adding changesets
2159 adding changesets
2160 adding manifests
2160 adding manifests
2161 adding file changes
2161 adding file changes
2162 added 11 changesets with 9 changes to 8 files (+4 heads)
2162 added 11 changesets with 9 changes to 8 files (+4 heads)
2163 new changesets 9092f1db7931:6b98ff0062dd (6 drafts)
2163 new changesets 9092f1db7931:6b98ff0062dd (6 drafts)
2164 (run 'hg heads' to see heads, 'hg merge' to merge)
2164 (run 'hg heads' to see heads, 'hg merge' to merge)
2165 $ hg up 9
2165 $ hg up 9
2166 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
2166 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
2167 $ echo w > w
2167 $ echo w > w
2168 $ hg ci -Aqm "added w" --config phases.publish=False
2168 $ hg ci -Aqm "added w" --config phases.publish=False
2169
2169
2170 $ cd ../abortgraft
2170 $ cd ../abortgraft
2171 $ hg pull ../pullrepo
2171 $ hg pull ../pullrepo
2172 pulling from ../pullrepo
2172 pulling from ../pullrepo
2173 searching for changes
2173 searching for changes
2174 adding changesets
2174 adding changesets
2175 adding manifests
2175 adding manifests
2176 adding file changes
2176 adding file changes
2177 added 1 changesets with 1 changes to 1 files (+1 heads)
2177 added 1 changesets with 1 changes to 1 files (+1 heads)
2178 new changesets 311dfc6cf3bf (1 drafts)
2178 new changesets 311dfc6cf3bf (1 drafts)
2179 (run 'hg heads .' to see heads, 'hg merge' to merge)
2179 (run 'hg heads .' to see heads, 'hg merge' to merge)
2180
2180
2181 $ hg abort
2181 $ hg abort
2182 new changesets detected on destination branch, can't strip
2182 new changesets detected on destination branch, can't strip
2183 graft aborted
2183 graft aborted
2184 working directory is now at 6b98ff0062dd
2184 working directory is now at 6b98ff0062dd
2185
2185
2186 $ cd ..
2186 $ cd ..
2187
2187
2188 ============================
2188 ============================
2189 Testing --no-commit option:|
2189 Testing --no-commit option:|
2190 ============================
2190 ============================
2191
2191
2192 $ hg init nocommit
2192 $ hg init nocommit
2193 $ cd nocommit
2193 $ cd nocommit
2194 $ echo a > a
2194 $ echo a > a
2195 $ hg ci -qAma
2195 $ hg ci -qAma
2196 $ echo b > b
2196 $ echo b > b
2197 $ hg ci -qAmb
2197 $ hg ci -qAmb
2198 $ hg up -q 0
2198 $ hg up -q 0
2199 $ echo c > c
2199 $ echo c > c
2200 $ hg ci -qAmc
2200 $ hg ci -qAmc
2201 $ hg log -GT "{rev}:{node|short} {desc}\n"
2201 $ hg log -GT "{rev}:{node|short} {desc}\n"
2202 @ 2:d36c0562f908 c
2202 @ 2:d36c0562f908 c
2203 |
2203 |
2204 | o 1:d2ae7f538514 b
2204 | o 1:d2ae7f538514 b
2205 |/
2205 |/
2206 o 0:cb9a9f314b8b a
2206 o 0:cb9a9f314b8b a
2207
2207
2208
2208
2209 Check reporting when --no-commit used with non-applicable options:
2209 Check reporting when --no-commit used with non-applicable options:
2210
2210
2211 $ hg graft 1 --no-commit -e
2211 $ hg graft 1 --no-commit -e
2212 abort: cannot specify --no-commit and --edit together
2212 abort: cannot specify --no-commit and --edit together
2213 [255]
2213 [255]
2214
2214
2215 $ hg graft 1 --no-commit --log
2215 $ hg graft 1 --no-commit --log
2216 abort: cannot specify --no-commit and --log together
2216 abort: cannot specify --no-commit and --log together
2217 [255]
2217 [255]
2218
2218
2219 $ hg graft 1 --no-commit -D
2219 $ hg graft 1 --no-commit -D
2220 abort: cannot specify --no-commit and --currentdate together
2220 abort: cannot specify --no-commit and --currentdate together
2221 [255]
2221 [255]
2222
2222
2223 Test --no-commit is working:
2223 Test --no-commit is working:
2224 $ hg graft 1 --no-commit
2224 $ hg graft 1 --no-commit
2225 grafting 1:d2ae7f538514 "b"
2225 grafting 1:d2ae7f538514 "b"
2226
2226
2227 $ hg log -GT "{rev}:{node|short} {desc}\n"
2227 $ hg log -GT "{rev}:{node|short} {desc}\n"
2228 @ 2:d36c0562f908 c
2228 @ 2:d36c0562f908 c
2229 |
2229 |
2230 | o 1:d2ae7f538514 b
2230 | o 1:d2ae7f538514 b
2231 |/
2231 |/
2232 o 0:cb9a9f314b8b a
2232 o 0:cb9a9f314b8b a
2233
2233
2234
2234
2235 $ hg diff
2235 $ hg diff
2236 diff -r d36c0562f908 b
2236 diff -r d36c0562f908 b
2237 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2237 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2238 +++ b/b Thu Jan 01 00:00:00 1970 +0000
2238 +++ b/b Thu Jan 01 00:00:00 1970 +0000
2239 @@ -0,0 +1,1 @@
2239 @@ -0,0 +1,1 @@
2240 +b
2240 +b
2241
2241
2242 Prepare wrdir to check --no-commit is resepected after --continue:
2242 Prepare wrdir to check --no-commit is resepected after --continue:
2243
2243
2244 $ hg up -qC
2244 $ hg up -qC
2245 $ echo A>a
2245 $ echo A>a
2246 $ hg ci -qm "A in file a"
2246 $ hg ci -qm "A in file a"
2247 $ hg up -q 1
2247 $ hg up -q 1
2248 $ echo B>a
2248 $ echo B>a
2249 $ hg ci -qm "B in file a"
2249 $ hg ci -qm "B in file a"
2250 $ hg log -GT "{rev}:{node|short} {desc}\n"
2250 $ hg log -GT "{rev}:{node|short} {desc}\n"
2251 @ 4:2aa9ad1006ff B in file a
2251 @ 4:2aa9ad1006ff B in file a
2252 |
2252 |
2253 | o 3:09e253b87e17 A in file a
2253 | o 3:09e253b87e17 A in file a
2254 | |
2254 | |
2255 | o 2:d36c0562f908 c
2255 | o 2:d36c0562f908 c
2256 | |
2256 | |
2257 o | 1:d2ae7f538514 b
2257 o | 1:d2ae7f538514 b
2258 |/
2258 |/
2259 o 0:cb9a9f314b8b a
2259 o 0:cb9a9f314b8b a
2260
2260
2261
2261
2262 $ hg graft 3 --no-commit
2262 $ hg graft 3 --no-commit
2263 grafting 3:09e253b87e17 "A in file a"
2263 grafting 3:09e253b87e17 "A in file a"
2264 merging a
2264 merging a
2265 warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
2265 warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
2266 abort: unresolved conflicts, can't continue
2266 abort: unresolved conflicts, can't continue
2267 (use 'hg resolve' and 'hg graft --continue')
2267 (use 'hg resolve' and 'hg graft --continue')
2268 [255]
2268 [255]
2269
2269
2270 Resolve conflict:
2270 Resolve conflict:
2271 $ echo A>a
2271 $ echo A>a
2272 $ hg resolve --mark
2272 $ hg resolve --mark
2273 (no more unresolved files)
2273 (no more unresolved files)
2274 continue: hg graft --continue
2274 continue: hg graft --continue
2275
2275
2276 $ hg graft --continue
2276 $ hg graft --continue
2277 grafting 3:09e253b87e17 "A in file a"
2277 grafting 3:09e253b87e17 "A in file a"
2278 $ hg log -GT "{rev}:{node|short} {desc}\n"
2278 $ hg log -GT "{rev}:{node|short} {desc}\n"
2279 @ 4:2aa9ad1006ff B in file a
2279 @ 4:2aa9ad1006ff B in file a
2280 |
2280 |
2281 | o 3:09e253b87e17 A in file a
2281 | o 3:09e253b87e17 A in file a
2282 | |
2282 | |
2283 | o 2:d36c0562f908 c
2283 | o 2:d36c0562f908 c
2284 | |
2284 | |
2285 o | 1:d2ae7f538514 b
2285 o | 1:d2ae7f538514 b
2286 |/
2286 |/
2287 o 0:cb9a9f314b8b a
2287 o 0:cb9a9f314b8b a
2288
2288
2289 $ hg diff
2289 $ hg diff
2290 diff -r 2aa9ad1006ff a
2290 diff -r 2aa9ad1006ff a
2291 --- a/a Thu Jan 01 00:00:00 1970 +0000
2291 --- a/a Thu Jan 01 00:00:00 1970 +0000
2292 +++ b/a Thu Jan 01 00:00:00 1970 +0000
2292 +++ b/a Thu Jan 01 00:00:00 1970 +0000
2293 @@ -1,1 +1,1 @@
2293 @@ -1,1 +1,1 @@
2294 -B
2294 -B
2295 +A
2295 +A
2296
2296
2297 $ hg up -qC
2297 $ hg up -qC
2298
2298
2299 Check --no-commit is resepected when passed with --continue:
2299 Check --no-commit is resepected when passed with --continue:
2300
2300
2301 $ hg graft 3
2301 $ hg graft 3
2302 grafting 3:09e253b87e17 "A in file a"
2302 grafting 3:09e253b87e17 "A in file a"
2303 merging a
2303 merging a
2304 warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
2304 warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
2305 abort: unresolved conflicts, can't continue
2305 abort: unresolved conflicts, can't continue
2306 (use 'hg resolve' and 'hg graft --continue')
2306 (use 'hg resolve' and 'hg graft --continue')
2307 [255]
2307 [255]
2308
2308
2309 Resolve conflict:
2309 Resolve conflict:
2310 $ echo A>a
2310 $ echo A>a
2311 $ hg resolve --mark
2311 $ hg resolve --mark
2312 (no more unresolved files)
2312 (no more unresolved files)
2313 continue: hg graft --continue
2313 continue: hg graft --continue
2314
2314
2315 $ hg graft --continue --no-commit
2315 $ hg graft --continue --no-commit
2316 grafting 3:09e253b87e17 "A in file a"
2316 grafting 3:09e253b87e17 "A in file a"
2317 $ hg diff
2317 $ hg diff
2318 diff -r 2aa9ad1006ff a
2318 diff -r 2aa9ad1006ff a
2319 --- a/a Thu Jan 01 00:00:00 1970 +0000
2319 --- a/a Thu Jan 01 00:00:00 1970 +0000
2320 +++ b/a Thu Jan 01 00:00:00 1970 +0000
2320 +++ b/a Thu Jan 01 00:00:00 1970 +0000
2321 @@ -1,1 +1,1 @@
2321 @@ -1,1 +1,1 @@
2322 -B
2322 -B
2323 +A
2323 +A
2324
2324
2325 $ hg log -GT "{rev}:{node|short} {desc}\n"
2325 $ hg log -GT "{rev}:{node|short} {desc}\n"
2326 @ 4:2aa9ad1006ff B in file a
2326 @ 4:2aa9ad1006ff B in file a
2327 |
2327 |
2328 | o 3:09e253b87e17 A in file a
2328 | o 3:09e253b87e17 A in file a
2329 | |
2329 | |
2330 | o 2:d36c0562f908 c
2330 | o 2:d36c0562f908 c
2331 | |
2331 | |
2332 o | 1:d2ae7f538514 b
2332 o | 1:d2ae7f538514 b
2333 |/
2333 |/
2334 o 0:cb9a9f314b8b a
2334 o 0:cb9a9f314b8b a
2335
2335
2336 $ hg up -qC
2336 $ hg up -qC
2337
2337
2338 Test --no-commit when graft multiple revisions:
2338 Test --no-commit when graft multiple revisions:
2339 When there is conflict:
2339 When there is conflict:
2340 $ hg graft -r "2::3" --no-commit
2340 $ hg graft -r "2::3" --no-commit
2341 grafting 2:d36c0562f908 "c"
2341 grafting 2:d36c0562f908 "c"
2342 grafting 3:09e253b87e17 "A in file a"
2342 grafting 3:09e253b87e17 "A in file a"
2343 merging a
2343 merging a
2344 warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
2344 warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
2345 abort: unresolved conflicts, can't continue
2345 abort: unresolved conflicts, can't continue
2346 (use 'hg resolve' and 'hg graft --continue')
2346 (use 'hg resolve' and 'hg graft --continue')
2347 [255]
2347 [255]
2348
2348
2349 $ echo A>a
2349 $ echo A>a
2350 $ hg resolve --mark
2350 $ hg resolve --mark
2351 (no more unresolved files)
2351 (no more unresolved files)
2352 continue: hg graft --continue
2352 continue: hg graft --continue
2353 $ hg graft --continue
2353 $ hg graft --continue
2354 grafting 3:09e253b87e17 "A in file a"
2354 grafting 3:09e253b87e17 "A in file a"
2355 $ hg diff
2355 $ hg diff
2356 diff -r 2aa9ad1006ff a
2356 diff -r 2aa9ad1006ff a
2357 --- a/a Thu Jan 01 00:00:00 1970 +0000
2357 --- a/a Thu Jan 01 00:00:00 1970 +0000
2358 +++ b/a Thu Jan 01 00:00:00 1970 +0000
2358 +++ b/a Thu Jan 01 00:00:00 1970 +0000
2359 @@ -1,1 +1,1 @@
2359 @@ -1,1 +1,1 @@
2360 -B
2360 -B
2361 +A
2361 +A
2362 diff -r 2aa9ad1006ff c
2362 diff -r 2aa9ad1006ff c
2363 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2363 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2364 +++ b/c Thu Jan 01 00:00:00 1970 +0000
2364 +++ b/c Thu Jan 01 00:00:00 1970 +0000
2365 @@ -0,0 +1,1 @@
2365 @@ -0,0 +1,1 @@
2366 +c
2366 +c
2367
2367
2368 $ hg log -GT "{rev}:{node|short} {desc}\n"
2368 $ hg log -GT "{rev}:{node|short} {desc}\n"
2369 @ 4:2aa9ad1006ff B in file a
2369 @ 4:2aa9ad1006ff B in file a
2370 |
2370 |
2371 | o 3:09e253b87e17 A in file a
2371 | o 3:09e253b87e17 A in file a
2372 | |
2372 | |
2373 | o 2:d36c0562f908 c
2373 | o 2:d36c0562f908 c
2374 | |
2374 | |
2375 o | 1:d2ae7f538514 b
2375 o | 1:d2ae7f538514 b
2376 |/
2376 |/
2377 o 0:cb9a9f314b8b a
2377 o 0:cb9a9f314b8b a
2378
2378
2379 $ hg up -qC
2379 $ hg up -qC
2380
2380
2381 When there is no conflict:
2381 When there is no conflict:
2382 $ echo d>d
2382 $ echo d>d
2383 $ hg add d -q
2383 $ hg add d -q
2384 $ hg ci -qmd
2384 $ hg ci -qmd
2385 $ hg up 3 -q
2385 $ hg up 3 -q
2386 $ hg log -GT "{rev}:{node|short} {desc}\n"
2386 $ hg log -GT "{rev}:{node|short} {desc}\n"
2387 o 5:baefa8927fc0 d
2387 o 5:baefa8927fc0 d
2388 |
2388 |
2389 o 4:2aa9ad1006ff B in file a
2389 o 4:2aa9ad1006ff B in file a
2390 |
2390 |
2391 | @ 3:09e253b87e17 A in file a
2391 | @ 3:09e253b87e17 A in file a
2392 | |
2392 | |
2393 | o 2:d36c0562f908 c
2393 | o 2:d36c0562f908 c
2394 | |
2394 | |
2395 o | 1:d2ae7f538514 b
2395 o | 1:d2ae7f538514 b
2396 |/
2396 |/
2397 o 0:cb9a9f314b8b a
2397 o 0:cb9a9f314b8b a
2398
2398
2399
2399
2400 $ hg graft -r 1 -r 5 --no-commit
2400 $ hg graft -r 1 -r 5 --no-commit
2401 grafting 1:d2ae7f538514 "b"
2401 grafting 1:d2ae7f538514 "b"
2402 grafting 5:baefa8927fc0 "d" (tip)
2402 grafting 5:baefa8927fc0 "d" (tip)
2403 $ hg diff
2403 $ hg diff
2404 diff -r 09e253b87e17 b
2404 diff -r 09e253b87e17 b
2405 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2405 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2406 +++ b/b Thu Jan 01 00:00:00 1970 +0000
2406 +++ b/b Thu Jan 01 00:00:00 1970 +0000
2407 @@ -0,0 +1,1 @@
2407 @@ -0,0 +1,1 @@
2408 +b
2408 +b
2409 diff -r 09e253b87e17 d
2409 diff -r 09e253b87e17 d
2410 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2410 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2411 +++ b/d Thu Jan 01 00:00:00 1970 +0000
2411 +++ b/d Thu Jan 01 00:00:00 1970 +0000
2412 @@ -0,0 +1,1 @@
2412 @@ -0,0 +1,1 @@
2413 +d
2413 +d
2414 $ hg log -GT "{rev}:{node|short} {desc}\n"
2414 $ hg log -GT "{rev}:{node|short} {desc}\n"
2415 o 5:baefa8927fc0 d
2415 o 5:baefa8927fc0 d
2416 |
2416 |
2417 o 4:2aa9ad1006ff B in file a
2417 o 4:2aa9ad1006ff B in file a
2418 |
2418 |
2419 | @ 3:09e253b87e17 A in file a
2419 | @ 3:09e253b87e17 A in file a
2420 | |
2420 | |
2421 | o 2:d36c0562f908 c
2421 | o 2:d36c0562f908 c
2422 | |
2422 | |
2423 o | 1:d2ae7f538514 b
2423 o | 1:d2ae7f538514 b
2424 |/
2424 |/
2425 o 0:cb9a9f314b8b a
2425 o 0:cb9a9f314b8b a
2426
2426
2427 $ cd ..
2427 $ cd ..
@@ -1,595 +1,595 b''
1 Test uncommit - set up the config
1 Test uncommit - set up the config
2
2
3 $ cat >> $HGRCPATH <<EOF
3 $ cat >> $HGRCPATH <<EOF
4 > [experimental]
4 > [experimental]
5 > evolution.createmarkers=True
5 > evolution.createmarkers=True
6 > evolution.allowunstable=True
6 > evolution.allowunstable=True
7 > [extensions]
7 > [extensions]
8 > uncommit =
8 > uncommit =
9 > drawdag=$TESTDIR/drawdag.py
9 > drawdag=$TESTDIR/drawdag.py
10 > EOF
10 > EOF
11
11
12 Build up a repo
12 Build up a repo
13
13
14 $ hg init repo
14 $ hg init repo
15 $ cd repo
15 $ cd repo
16 $ hg bookmark foo
16 $ hg bookmark foo
17
17
18 Help for uncommit
18 Help for uncommit
19
19
20 $ hg help uncommit
20 $ hg help uncommit
21 hg uncommit [OPTION]... [FILE]...
21 hg uncommit [OPTION]... [FILE]...
22
22
23 uncommit part or all of a local changeset
23 uncommit part or all of a local changeset
24
24
25 This command undoes the effect of a local commit, returning the affected
25 This command undoes the effect of a local commit, returning the affected
26 files to their uncommitted state. This means that files modified or
26 files to their uncommitted state. This means that files modified or
27 deleted in the changeset will be left unchanged, and so will remain
27 deleted in the changeset will be left unchanged, and so will remain
28 modified in the working directory.
28 modified in the working directory.
29
29
30 If no files are specified, the commit will be pruned, unless --keep is
30 If no files are specified, the commit will be pruned, unless --keep is
31 given.
31 given.
32
32
33 (use 'hg help -e uncommit' to show help for the uncommit extension)
33 (use 'hg help -e uncommit' to show help for the uncommit extension)
34
34
35 options ([+] can be repeated):
35 options ([+] can be repeated):
36
36
37 --keep allow an empty commit after uncommitting
37 --keep allow an empty commit after uncommitting
38 --allow-dirty-working-copy allow uncommit with outstanding changes
38 --allow-dirty-working-copy allow uncommit with outstanding changes
39 -n --note TEXT store a note on uncommit
39 -n --note TEXT store a note on uncommit
40 -I --include PATTERN [+] include names matching the given patterns
40 -I --include PATTERN [+] include names matching the given patterns
41 -X --exclude PATTERN [+] exclude names matching the given patterns
41 -X --exclude PATTERN [+] exclude names matching the given patterns
42 -m --message TEXT use text as commit message
42 -m --message TEXT use text as commit message
43 -l --logfile FILE read commit message from file
43 -l --logfile FILE read commit message from file
44 -d --date DATE record the specified date as commit date
44 -d --date DATE record the specified date as commit date
45 -u --user USER record the specified user as committer
45 -u --user USER record the specified user as committer
46 -D --currentdate record the current date as commit date
46 -D --currentdate record the current date as commit date
47 -U --currentuser record the current user as committer
47 -U --currentuser record the current user as committer
48
48
49 (some details hidden, use --verbose to show complete help)
49 (some details hidden, use --verbose to show complete help)
50
50
51 Uncommit with no commits should fail
51 Uncommit with no commits should fail
52
52
53 $ hg uncommit
53 $ hg uncommit
54 abort: cannot uncommit null changeset
54 abort: cannot uncommit null changeset
55 (no changeset checked out)
55 (no changeset checked out)
56 [255]
56 [255]
57
57
58 Create some commits
58 Create some commits
59
59
60 $ touch files
60 $ touch files
61 $ hg add files
61 $ hg add files
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
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 $ ls
63 $ ls
64 file-a
64 file-a
65 file-ab
65 file-ab
66 file-abc
66 file-abc
67 file-abcd
67 file-abcd
68 file-abcde
68 file-abcde
69 files
69 files
70
70
71 $ hg log -G -T '{rev}:{node} {desc}' --hidden
71 $ hg log -G -T '{rev}:{node} {desc}' --hidden
72 @ 4:6c4fd43ed714e7fcd8adbaa7b16c953c2e985b60 added file-abcde
72 @ 4:6c4fd43ed714e7fcd8adbaa7b16c953c2e985b60 added file-abcde
73 |
73 |
74 o 3:6db330d65db434145c0b59d291853e9a84719b24 added file-abcd
74 o 3:6db330d65db434145c0b59d291853e9a84719b24 added file-abcd
75 |
75 |
76 o 2:abf2df566fc193b3ac34d946e63c1583e4d4732b added file-abc
76 o 2:abf2df566fc193b3ac34d946e63c1583e4d4732b added file-abc
77 |
77 |
78 o 1:69a232e754b08d568c4899475faf2eb44b857802 added file-ab
78 o 1:69a232e754b08d568c4899475faf2eb44b857802 added file-ab
79 |
79 |
80 o 0:3004d2d9b50883c1538fc754a3aeb55f1b4084f6 added file-a
80 o 0:3004d2d9b50883c1538fc754a3aeb55f1b4084f6 added file-a
81
81
82 Simple uncommit off the top, also moves bookmark
82 Simple uncommit off the top, also moves bookmark
83
83
84 $ hg bookmark
84 $ hg bookmark
85 * foo 4:6c4fd43ed714
85 * foo 4:6c4fd43ed714
86 $ hg uncommit
86 $ hg uncommit
87 $ hg status
87 $ hg status
88 M files
88 M files
89 A file-abcde
89 A file-abcde
90 $ hg bookmark
90 $ hg bookmark
91 * foo 3:6db330d65db4
91 * foo 3:6db330d65db4
92
92
93 $ hg log -G -T '{rev}:{node} {desc}' --hidden
93 $ hg log -G -T '{rev}:{node} {desc}' --hidden
94 x 4:6c4fd43ed714e7fcd8adbaa7b16c953c2e985b60 added file-abcde
94 x 4:6c4fd43ed714e7fcd8adbaa7b16c953c2e985b60 added file-abcde
95 |
95 |
96 @ 3:6db330d65db434145c0b59d291853e9a84719b24 added file-abcd
96 @ 3:6db330d65db434145c0b59d291853e9a84719b24 added file-abcd
97 |
97 |
98 o 2:abf2df566fc193b3ac34d946e63c1583e4d4732b added file-abc
98 o 2:abf2df566fc193b3ac34d946e63c1583e4d4732b added file-abc
99 |
99 |
100 o 1:69a232e754b08d568c4899475faf2eb44b857802 added file-ab
100 o 1:69a232e754b08d568c4899475faf2eb44b857802 added file-ab
101 |
101 |
102 o 0:3004d2d9b50883c1538fc754a3aeb55f1b4084f6 added file-a
102 o 0:3004d2d9b50883c1538fc754a3aeb55f1b4084f6 added file-a
103
103
104
104
105 Recommit
105 Recommit
106
106
107 $ hg commit -m 'new change abcde'
107 $ hg commit -m 'new change abcde'
108 $ hg status
108 $ hg status
109 $ hg heads -T '{rev}:{node} {desc}'
109 $ hg heads -T '{rev}:{node} {desc}'
110 5:0c07a3ccda771b25f1cb1edbd02e683723344ef1 new change abcde (no-eol)
110 5:0c07a3ccda771b25f1cb1edbd02e683723344ef1 new change abcde (no-eol)
111
111
112 Uncommit of non-existent and unchanged files aborts
112 Uncommit of non-existent and unchanged files aborts
113 $ hg uncommit nothinghere
113 $ hg uncommit nothinghere
114 abort: cannot uncommit "nothinghere"
114 abort: cannot uncommit "nothinghere"
115 (file does not exist)
115 (file does not exist)
116 [255]
116 [255]
117 $ hg status
117 $ hg status
118 $ hg uncommit file-abc
118 $ hg uncommit file-abc
119 abort: cannot uncommit "file-abc"
119 abort: cannot uncommit "file-abc"
120 (file was not changed in working directory parent)
120 (file was not changed in working directory parent)
121 [255]
121 [255]
122 $ hg status
122 $ hg status
123
123
124 Try partial uncommit, also moves bookmark
124 Try partial uncommit, also moves bookmark
125
125
126 $ hg bookmark
126 $ hg bookmark
127 * foo 5:0c07a3ccda77
127 * foo 5:0c07a3ccda77
128 $ hg uncommit files
128 $ hg uncommit files
129 $ hg status
129 $ hg status
130 M files
130 M files
131 $ hg bookmark
131 $ hg bookmark
132 * foo 6:3727deee06f7
132 * foo 6:3727deee06f7
133 $ hg heads -T '{rev}:{node} {desc}'
133 $ hg heads -T '{rev}:{node} {desc}'
134 6:3727deee06f72f5ffa8db792ee299cf39e3e190b new change abcde (no-eol)
134 6:3727deee06f72f5ffa8db792ee299cf39e3e190b new change abcde (no-eol)
135 $ hg log -r . -p -T '{rev}:{node} {desc}'
135 $ hg log -r . -p -T '{rev}:{node} {desc}'
136 6:3727deee06f72f5ffa8db792ee299cf39e3e190b new change abcdediff -r 6db330d65db4 -r 3727deee06f7 file-abcde
136 6:3727deee06f72f5ffa8db792ee299cf39e3e190b new change abcdediff -r 6db330d65db4 -r 3727deee06f7 file-abcde
137 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
137 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
138 +++ b/file-abcde Thu Jan 01 00:00:00 1970 +0000
138 +++ b/file-abcde Thu Jan 01 00:00:00 1970 +0000
139 @@ -0,0 +1,1 @@
139 @@ -0,0 +1,1 @@
140 +abcde
140 +abcde
141
141
142 $ hg log -G -T '{rev}:{node} {desc}' --hidden
142 $ hg log -G -T '{rev}:{node} {desc}' --hidden
143 @ 6:3727deee06f72f5ffa8db792ee299cf39e3e190b new change abcde
143 @ 6:3727deee06f72f5ffa8db792ee299cf39e3e190b new change abcde
144 |
144 |
145 | x 5:0c07a3ccda771b25f1cb1edbd02e683723344ef1 new change abcde
145 | x 5:0c07a3ccda771b25f1cb1edbd02e683723344ef1 new change abcde
146 |/
146 |/
147 | x 4:6c4fd43ed714e7fcd8adbaa7b16c953c2e985b60 added file-abcde
147 | x 4:6c4fd43ed714e7fcd8adbaa7b16c953c2e985b60 added file-abcde
148 |/
148 |/
149 o 3:6db330d65db434145c0b59d291853e9a84719b24 added file-abcd
149 o 3:6db330d65db434145c0b59d291853e9a84719b24 added file-abcd
150 |
150 |
151 o 2:abf2df566fc193b3ac34d946e63c1583e4d4732b added file-abc
151 o 2:abf2df566fc193b3ac34d946e63c1583e4d4732b added file-abc
152 |
152 |
153 o 1:69a232e754b08d568c4899475faf2eb44b857802 added file-ab
153 o 1:69a232e754b08d568c4899475faf2eb44b857802 added file-ab
154 |
154 |
155 o 0:3004d2d9b50883c1538fc754a3aeb55f1b4084f6 added file-a
155 o 0:3004d2d9b50883c1538fc754a3aeb55f1b4084f6 added file-a
156
156
157 $ hg commit -m 'update files for abcde'
157 $ hg commit -m 'update files for abcde'
158
158
159 Uncommit with dirty state
159 Uncommit with dirty state
160
160
161 $ echo "foo" >> files
161 $ echo "foo" >> files
162 $ cat files
162 $ cat files
163 abcde
163 abcde
164 foo
164 foo
165 $ hg status
165 $ hg status
166 M files
166 M files
167 $ hg uncommit
167 $ hg uncommit
168 abort: uncommitted changes
168 abort: uncommitted changes
169 (requires --allow-dirty-working-copy to uncommit)
169 (requires --allow-dirty-working-copy to uncommit)
170 [255]
170 [255]
171 $ hg uncommit files
171 $ hg uncommit files
172 abort: uncommitted changes
172 abort: uncommitted changes
173 (requires --allow-dirty-working-copy to uncommit)
173 (requires --allow-dirty-working-copy to uncommit)
174 [255]
174 [255]
175 $ cat files
175 $ cat files
176 abcde
176 abcde
177 foo
177 foo
178 $ hg commit --amend -m "files abcde + foo"
178 $ hg commit --amend -m "files abcde + foo"
179
179
180 Testing the 'experimental.uncommitondirtywdir' config
180 Testing the 'experimental.uncommitondirtywdir' config
181
181
182 $ echo "bar" >> files
182 $ echo "bar" >> files
183 $ hg uncommit
183 $ hg uncommit
184 abort: uncommitted changes
184 abort: uncommitted changes
185 (requires --allow-dirty-working-copy to uncommit)
185 (requires --allow-dirty-working-copy to uncommit)
186 [255]
186 [255]
187 $ hg uncommit --config experimental.uncommitondirtywdir=True
187 $ hg uncommit --config experimental.uncommitondirtywdir=True
188 $ hg commit -m "files abcde + foo"
188 $ hg commit -m "files abcde + foo"
189
189
190 Uncommit in the middle of a stack, does not move bookmark
190 Uncommit in the middle of a stack, does not move bookmark
191
191
192 $ hg checkout '.^^^'
192 $ hg checkout '.^^^'
193 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
193 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
194 (leaving bookmark foo)
194 (leaving bookmark foo)
195 $ hg log -r . -p -T '{rev}:{node} {desc}'
195 $ hg log -r . -p -T '{rev}:{node} {desc}'
196 2:abf2df566fc193b3ac34d946e63c1583e4d4732b added file-abcdiff -r 69a232e754b0 -r abf2df566fc1 file-abc
196 2:abf2df566fc193b3ac34d946e63c1583e4d4732b added file-abcdiff -r 69a232e754b0 -r abf2df566fc1 file-abc
197 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
197 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
198 +++ b/file-abc Thu Jan 01 00:00:00 1970 +0000
198 +++ b/file-abc Thu Jan 01 00:00:00 1970 +0000
199 @@ -0,0 +1,1 @@
199 @@ -0,0 +1,1 @@
200 +abc
200 +abc
201 diff -r 69a232e754b0 -r abf2df566fc1 files
201 diff -r 69a232e754b0 -r abf2df566fc1 files
202 --- a/files Thu Jan 01 00:00:00 1970 +0000
202 --- a/files Thu Jan 01 00:00:00 1970 +0000
203 +++ b/files Thu Jan 01 00:00:00 1970 +0000
203 +++ b/files Thu Jan 01 00:00:00 1970 +0000
204 @@ -1,1 +1,1 @@
204 @@ -1,1 +1,1 @@
205 -ab
205 -ab
206 +abc
206 +abc
207
207
208 $ hg bookmark
208 $ hg bookmark
209 foo 9:48e5bd7cd583
209 foo 9:48e5bd7cd583
210 $ hg uncommit
210 $ hg uncommit
211 3 new orphan changesets
211 3 new orphan changesets
212 $ hg status
212 $ hg status
213 M files
213 M files
214 A file-abc
214 A file-abc
215 $ hg heads -T '{rev}:{node} {desc}'
215 $ hg heads -T '{rev}:{node} {desc}'
216 9:48e5bd7cd583eb24164ef8b89185819c84c96ed7 files abcde + foo (no-eol)
216 9:48e5bd7cd583eb24164ef8b89185819c84c96ed7 files abcde + foo (no-eol)
217 $ hg bookmark
217 $ hg bookmark
218 foo 9:48e5bd7cd583
218 foo 9:48e5bd7cd583
219 $ hg commit -m 'new abc'
219 $ hg commit -m 'new abc'
220 created new head
220 created new head
221
221
222 Partial uncommit in the middle, does not move bookmark
222 Partial uncommit in the middle, does not move bookmark
223
223
224 $ hg checkout '.^'
224 $ hg checkout '.^'
225 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
225 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
226 $ hg log -r . -p -T '{rev}:{node} {desc}'
226 $ hg log -r . -p -T '{rev}:{node} {desc}'
227 1:69a232e754b08d568c4899475faf2eb44b857802 added file-abdiff -r 3004d2d9b508 -r 69a232e754b0 file-ab
227 1:69a232e754b08d568c4899475faf2eb44b857802 added file-abdiff -r 3004d2d9b508 -r 69a232e754b0 file-ab
228 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
228 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
229 +++ b/file-ab Thu Jan 01 00:00:00 1970 +0000
229 +++ b/file-ab Thu Jan 01 00:00:00 1970 +0000
230 @@ -0,0 +1,1 @@
230 @@ -0,0 +1,1 @@
231 +ab
231 +ab
232 diff -r 3004d2d9b508 -r 69a232e754b0 files
232 diff -r 3004d2d9b508 -r 69a232e754b0 files
233 --- a/files Thu Jan 01 00:00:00 1970 +0000
233 --- a/files Thu Jan 01 00:00:00 1970 +0000
234 +++ b/files Thu Jan 01 00:00:00 1970 +0000
234 +++ b/files Thu Jan 01 00:00:00 1970 +0000
235 @@ -1,1 +1,1 @@
235 @@ -1,1 +1,1 @@
236 -a
236 -a
237 +ab
237 +ab
238
238
239 $ hg bookmark
239 $ hg bookmark
240 foo 9:48e5bd7cd583
240 foo 9:48e5bd7cd583
241 $ hg uncommit file-ab
241 $ hg uncommit file-ab
242 1 new orphan changesets
242 1 new orphan changesets
243 $ hg status
243 $ hg status
244 A file-ab
244 A file-ab
245
245
246 $ hg heads -T '{rev}:{node} {desc}\n'
246 $ hg heads -T '{rev}:{node} {desc}\n'
247 11:8eb87968f2edb7f27f27fe676316e179de65fff6 added file-ab
247 11:8eb87968f2edb7f27f27fe676316e179de65fff6 added file-ab
248 10:5dc89ca4486f8a88716c5797fa9f498d13d7c2e1 new abc
248 10:5dc89ca4486f8a88716c5797fa9f498d13d7c2e1 new abc
249 9:48e5bd7cd583eb24164ef8b89185819c84c96ed7 files abcde + foo
249 9:48e5bd7cd583eb24164ef8b89185819c84c96ed7 files abcde + foo
250
250
251 $ hg bookmark
251 $ hg bookmark
252 foo 9:48e5bd7cd583
252 foo 9:48e5bd7cd583
253 $ hg commit -m 'update ab'
253 $ hg commit -m 'update ab'
254 $ hg status
254 $ hg status
255 $ hg heads -T '{rev}:{node} {desc}\n'
255 $ hg heads -T '{rev}:{node} {desc}\n'
256 12:f21039c59242b085491bb58f591afc4ed1c04c09 update ab
256 12:f21039c59242b085491bb58f591afc4ed1c04c09 update ab
257 10:5dc89ca4486f8a88716c5797fa9f498d13d7c2e1 new abc
257 10:5dc89ca4486f8a88716c5797fa9f498d13d7c2e1 new abc
258 9:48e5bd7cd583eb24164ef8b89185819c84c96ed7 files abcde + foo
258 9:48e5bd7cd583eb24164ef8b89185819c84c96ed7 files abcde + foo
259
259
260 $ hg log -G -T '{rev}:{node} {desc}' --hidden
260 $ hg log -G -T '{rev}:{node} {desc}' --hidden
261 @ 12:f21039c59242b085491bb58f591afc4ed1c04c09 update ab
261 @ 12:f21039c59242b085491bb58f591afc4ed1c04c09 update ab
262 |
262 |
263 o 11:8eb87968f2edb7f27f27fe676316e179de65fff6 added file-ab
263 o 11:8eb87968f2edb7f27f27fe676316e179de65fff6 added file-ab
264 |
264 |
265 | * 10:5dc89ca4486f8a88716c5797fa9f498d13d7c2e1 new abc
265 | * 10:5dc89ca4486f8a88716c5797fa9f498d13d7c2e1 new abc
266 | |
266 | |
267 | | * 9:48e5bd7cd583eb24164ef8b89185819c84c96ed7 files abcde + foo
267 | | * 9:48e5bd7cd583eb24164ef8b89185819c84c96ed7 files abcde + foo
268 | | |
268 | | |
269 | | | x 8:84beeba0ac30e19521c036e4d2dd3a5fa02586ff files abcde + foo
269 | | | x 8:84beeba0ac30e19521c036e4d2dd3a5fa02586ff files abcde + foo
270 | | |/
270 | | |/
271 | | | x 7:0977fa602c2fd7d8427ed4e7ee15ea13b84c9173 update files for abcde
271 | | | x 7:0977fa602c2fd7d8427ed4e7ee15ea13b84c9173 update files for abcde
272 | | |/
272 | | |/
273 | | * 6:3727deee06f72f5ffa8db792ee299cf39e3e190b new change abcde
273 | | * 6:3727deee06f72f5ffa8db792ee299cf39e3e190b new change abcde
274 | | |
274 | | |
275 | | | x 5:0c07a3ccda771b25f1cb1edbd02e683723344ef1 new change abcde
275 | | | x 5:0c07a3ccda771b25f1cb1edbd02e683723344ef1 new change abcde
276 | | |/
276 | | |/
277 | | | x 4:6c4fd43ed714e7fcd8adbaa7b16c953c2e985b60 added file-abcde
277 | | | x 4:6c4fd43ed714e7fcd8adbaa7b16c953c2e985b60 added file-abcde
278 | | |/
278 | | |/
279 | | * 3:6db330d65db434145c0b59d291853e9a84719b24 added file-abcd
279 | | * 3:6db330d65db434145c0b59d291853e9a84719b24 added file-abcd
280 | | |
280 | | |
281 | | x 2:abf2df566fc193b3ac34d946e63c1583e4d4732b added file-abc
281 | | x 2:abf2df566fc193b3ac34d946e63c1583e4d4732b added file-abc
282 | |/
282 | |/
283 | x 1:69a232e754b08d568c4899475faf2eb44b857802 added file-ab
283 | x 1:69a232e754b08d568c4899475faf2eb44b857802 added file-ab
284 |/
284 |/
285 o 0:3004d2d9b50883c1538fc754a3aeb55f1b4084f6 added file-a
285 o 0:3004d2d9b50883c1538fc754a3aeb55f1b4084f6 added file-a
286
286
287 Uncommit with draft parent
287 Uncommit with draft parent
288
288
289 $ hg uncommit
289 $ hg uncommit
290 $ hg phase -r .
290 $ hg phase -r .
291 11: draft
291 11: draft
292 $ hg commit -m 'update ab again'
292 $ hg commit -m 'update ab again'
293
293
294 Phase is preserved
294 Phase is preserved
295
295
296 $ hg uncommit --keep --config phases.new-commit=secret
296 $ hg uncommit --keep --config phases.new-commit=secret
297 note: keeping empty commit
297 note: keeping empty commit
298 $ hg phase -r .
298 $ hg phase -r .
299 14: draft
299 14: draft
300 $ hg commit --amend -m 'update ab again'
300 $ hg commit --amend -m 'update ab again'
301
301
302 Uncommit with public parent
302 Uncommit with public parent
303
303
304 $ hg phase -p "::.^"
304 $ hg phase -p "::.^"
305 $ hg uncommit
305 $ hg uncommit
306 $ hg phase -r .
306 $ hg phase -r .
307 11: public
307 11: public
308
308
309 Partial uncommit with public parent
309 Partial uncommit with public parent
310
310
311 $ echo xyz > xyz
311 $ echo xyz > xyz
312 $ hg add xyz
312 $ hg add xyz
313 $ hg commit -m "update ab and add xyz"
313 $ hg commit -m "update ab and add xyz"
314 $ hg uncommit xyz
314 $ hg uncommit xyz
315 $ hg status
315 $ hg status
316 A xyz
316 A xyz
317 $ hg phase -r .
317 $ hg phase -r .
318 17: draft
318 17: draft
319 $ hg phase -r ".^"
319 $ hg phase -r ".^"
320 11: public
320 11: public
321
321
322 Uncommit with --keep or experimental.uncommit.keep leaves an empty changeset
322 Uncommit with --keep or experimental.uncommit.keep leaves an empty changeset
323
323
324 $ cd $TESTTMP
324 $ cd $TESTTMP
325 $ hg init repo1
325 $ hg init repo1
326 $ cd repo1
326 $ cd repo1
327 $ hg debugdrawdag <<'EOS'
327 $ hg debugdrawdag <<'EOS'
328 > Q
328 > Q
329 > |
329 > |
330 > P
330 > P
331 > EOS
331 > EOS
332 $ hg up Q -q
332 $ hg up Q -q
333 $ hg uncommit --keep
333 $ hg uncommit --keep
334 note: keeping empty commit
334 note: keeping empty commit
335 $ hg log -G -T '{desc} FILES: {files}'
335 $ hg log -G -T '{desc} FILES: {files}'
336 @ Q FILES:
336 @ Q FILES:
337 |
337 |
338 | x Q FILES: Q
338 | x Q FILES: Q
339 |/
339 |/
340 o P FILES: P
340 o P FILES: P
341
341
342 $ cat >> .hg/hgrc <<EOF
342 $ cat >> .hg/hgrc <<EOF
343 > [experimental]
343 > [experimental]
344 > uncommit.keep=True
344 > uncommit.keep=True
345 > EOF
345 > EOF
346 $ hg ci --amend
346 $ hg ci --amend
347 $ hg uncommit
347 $ hg uncommit
348 note: keeping empty commit
348 note: keeping empty commit
349 $ hg log -G -T '{desc} FILES: {files}'
349 $ hg log -G -T '{desc} FILES: {files}'
350 @ Q FILES:
350 @ Q FILES:
351 |
351 |
352 | x Q FILES: Q
352 | x Q FILES: Q
353 |/
353 |/
354 o P FILES: P
354 o P FILES: P
355
355
356 $ hg status
356 $ hg status
357 A Q
357 A Q
358 $ hg ci --amend
358 $ hg ci --amend
359 $ hg uncommit --no-keep
359 $ hg uncommit --no-keep
360 $ hg log -G -T '{desc} FILES: {files}'
360 $ hg log -G -T '{desc} FILES: {files}'
361 x Q FILES: Q
361 x Q FILES: Q
362 |
362 |
363 @ P FILES: P
363 @ P FILES: P
364
364
365 $ hg status
365 $ hg status
366 A Q
366 A Q
367 $ cd ..
367 $ cd ..
368 $ rm -rf repo1
368 $ rm -rf repo1
369
369
370 Testing uncommit while merge
370 Testing uncommit while merge
371
371
372 $ hg init repo2
372 $ hg init repo2
373 $ cd repo2
373 $ cd repo2
374
374
375 Create some history
375 Create some history
376
376
377 $ touch a
377 $ touch a
378 $ hg add a
378 $ hg add a
379 $ for i in 1 2 3; do echo $i > a; hg commit -m "a $i"; done
379 $ for i in 1 2 3; do echo $i > a; hg commit -m "a $i"; done
380 $ hg checkout 0
380 $ hg checkout 0
381 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
381 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
382 $ touch b
382 $ touch b
383 $ hg add b
383 $ hg add b
384 $ for i in 1 2 3; do echo $i > b; hg commit -m "b $i"; done
384 $ for i in 1 2 3; do echo $i > b; hg commit -m "b $i"; done
385 created new head
385 created new head
386 $ hg log -G -T '{rev}:{node} {desc}' --hidden
386 $ hg log -G -T '{rev}:{node} {desc}' --hidden
387 @ 5:2cd56cdde163ded2fbb16ba2f918c96046ab0bf2 b 3
387 @ 5:2cd56cdde163ded2fbb16ba2f918c96046ab0bf2 b 3
388 |
388 |
389 o 4:c3a0d5bb3b15834ffd2ef9ef603e93ec65cf2037 b 2
389 o 4:c3a0d5bb3b15834ffd2ef9ef603e93ec65cf2037 b 2
390 |
390 |
391 o 3:49bb009ca26078726b8870f1edb29fae8f7618f5 b 1
391 o 3:49bb009ca26078726b8870f1edb29fae8f7618f5 b 1
392 |
392 |
393 | o 2:990982b7384266e691f1bc08ca36177adcd1c8a9 a 3
393 | o 2:990982b7384266e691f1bc08ca36177adcd1c8a9 a 3
394 | |
394 | |
395 | o 1:24d38e3cf160c7b6f5ffe82179332229886a6d34 a 2
395 | o 1:24d38e3cf160c7b6f5ffe82179332229886a6d34 a 2
396 |/
396 |/
397 o 0:ea4e33293d4d274a2ba73150733c2612231f398c a 1
397 o 0:ea4e33293d4d274a2ba73150733c2612231f398c a 1
398
398
399
399
400 Add and expect uncommit to fail on both merge working dir and merge changeset
400 Add and expect uncommit to fail on both merge working dir and merge changeset
401
401
402 $ hg merge 2
402 $ hg merge 2
403 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
403 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
404 (branch merge, don't forget to commit)
404 (branch merge, don't forget to commit)
405
405
406 $ hg uncommit
406 $ hg uncommit
407 abort: outstanding uncommitted merge
407 abort: outstanding uncommitted merge
408 (requires --allow-dirty-working-copy to uncommit)
408 (requires --allow-dirty-working-copy to uncommit)
409 [255]
409 [255]
410
410
411 $ hg uncommit --config experimental.uncommitondirtywdir=True
411 $ hg uncommit --config experimental.uncommitondirtywdir=True
412 abort: cannot uncommit while merging
412 abort: cannot uncommit while merging
413 [255]
413 [255]
414
414
415 $ hg status
415 $ hg status
416 M a
416 M a
417 $ hg commit -m 'merge a and b'
417 $ hg commit -m 'merge a and b'
418
418
419 $ hg uncommit
419 $ hg uncommit
420 abort: cannot uncommit merge changeset
420 abort: cannot uncommit merge changeset
421 [255]
421 [255]
422
422
423 $ hg status
423 $ hg status
424 $ hg log -G -T '{rev}:{node} {desc}' --hidden
424 $ hg log -G -T '{rev}:{node} {desc}' --hidden
425 @ 6:c03b9c37bc67bf504d4912061cfb527b47a63c6e merge a and b
425 @ 6:c03b9c37bc67bf504d4912061cfb527b47a63c6e merge a and b
426 |\
426 |\
427 | o 5:2cd56cdde163ded2fbb16ba2f918c96046ab0bf2 b 3
427 | o 5:2cd56cdde163ded2fbb16ba2f918c96046ab0bf2 b 3
428 | |
428 | |
429 | o 4:c3a0d5bb3b15834ffd2ef9ef603e93ec65cf2037 b 2
429 | o 4:c3a0d5bb3b15834ffd2ef9ef603e93ec65cf2037 b 2
430 | |
430 | |
431 | o 3:49bb009ca26078726b8870f1edb29fae8f7618f5 b 1
431 | o 3:49bb009ca26078726b8870f1edb29fae8f7618f5 b 1
432 | |
432 | |
433 o | 2:990982b7384266e691f1bc08ca36177adcd1c8a9 a 3
433 o | 2:990982b7384266e691f1bc08ca36177adcd1c8a9 a 3
434 | |
434 | |
435 o | 1:24d38e3cf160c7b6f5ffe82179332229886a6d34 a 2
435 o | 1:24d38e3cf160c7b6f5ffe82179332229886a6d34 a 2
436 |/
436 |/
437 o 0:ea4e33293d4d274a2ba73150733c2612231f398c a 1
437 o 0:ea4e33293d4d274a2ba73150733c2612231f398c a 1
438
438
439
439
440 Rename a->b, then remove b in working copy. Result should remove a.
440 Rename a->b, then remove b in working copy. Result should remove a.
441
441
442 $ hg co -q 0
442 $ hg co -q 0
443 $ hg mv a b
443 $ hg mv a b
444 $ hg ci -qm 'move a to b'
444 $ hg ci -qm 'move a to b'
445 $ hg rm b
445 $ hg rm b
446 $ hg uncommit --config experimental.uncommitondirtywdir=True
446 $ hg uncommit --config experimental.uncommitondirtywdir=True
447 $ hg st --copies
447 $ hg st --copies
448 R a
448 R a
449 $ hg revert a
449 $ hg revert a
450
450
451 Rename a->b, then rename b->c in working copy. Result should rename a->c.
451 Rename a->b, then rename b->c in working copy. Result should rename a->c.
452
452
453 $ hg co -q 0
453 $ hg co -q 0
454 $ hg mv a b
454 $ hg mv a b
455 $ hg ci -qm 'move a to b'
455 $ hg ci -qm 'move a to b'
456 $ hg mv b c
456 $ hg mv b c
457 $ hg uncommit --config experimental.uncommitondirtywdir=True
457 $ hg uncommit --config experimental.uncommitondirtywdir=True
458 $ hg st --copies
458 $ hg st --copies
459 A c
459 A c
460 a
460 a
461 R a
461 R a
462 $ hg revert a
462 $ hg revert a
463 $ hg forget c
463 $ hg forget c
464 $ rm c
464 $ rm c
465
465
466 Copy a->b1 and a->b2, then rename b1->c in working copy. Result should copy a->b2 and a->c.
466 Copy a->b1 and a->b2, then rename b1->c in working copy. Result should copy a->b2 and a->c.
467
467
468 $ hg co -q 0
468 $ hg co -q 0
469 $ hg cp a b1
469 $ hg cp a b1
470 $ hg cp a b2
470 $ hg cp a b2
471 $ hg ci -qm 'move a to b1 and b2'
471 $ hg ci -qm 'move a to b1 and b2'
472 $ hg mv b1 c
472 $ hg mv b1 c
473 $ hg uncommit --config experimental.uncommitondirtywdir=True
473 $ hg uncommit --config experimental.uncommitondirtywdir=True
474 $ hg st --copies
474 $ hg st --copies
475 A b2
475 A b2
476 a
476 a
477 A c
477 A c
478 a
478 a
479 $ cd ..
479 $ cd ..
480
480
481 --allow-dirty-working-copy should also work on a dirty PATH
481 --allow-dirty-working-copy should also work on a dirty PATH
482
482
483 $ hg init issue5977
483 $ hg init issue5977
484 $ cd issue5977
484 $ cd issue5977
485 $ echo 'super critical info!' > a
485 $ echo 'super critical info!' > a
486 $ hg ci -Am 'add a'
486 $ hg ci -Am 'add a'
487 adding a
487 adding a
488 $ echo 'foo' > b
488 $ echo 'foo' > b
489 $ hg add b
489 $ hg add b
490 $ hg status
490 $ hg status
491 A b
491 A b
492 $ hg unc a
492 $ hg unc a
493 note: keeping empty commit
493 note: keeping empty commit
494 $ cat a
494 $ cat a
495 super critical info!
495 super critical info!
496 $ hg log
496 $ hg log
497 changeset: 1:656ba143d384
497 changeset: 1:656ba143d384
498 tag: tip
498 tag: tip
499 parent: -1:000000000000
499 parent: -1:000000000000
500 user: test
500 user: test
501 date: Thu Jan 01 00:00:00 1970 +0000
501 date: Thu Jan 01 00:00:00 1970 +0000
502 summary: add a
502 summary: add a
503
503
504 $ hg ci -Am 'add b'
504 $ hg ci -Am 'add b'
505 $ echo 'foo bar' > b
505 $ echo 'foo bar' > b
506 $ hg unc b
506 $ hg unc b
507 abort: uncommitted changes
507 abort: uncommitted changes
508 (requires --allow-dirty-working-copy to uncommit)
508 (requires --allow-dirty-working-copy to uncommit)
509 [255]
509 [255]
510 $ hg unc --allow-dirty-working-copy b
510 $ hg unc --allow-dirty-working-copy b
511 $ hg log
511 $ hg log
512 changeset: 3:30fa958635b2
512 changeset: 3:30fa958635b2
513 tag: tip
513 tag: tip
514 parent: 1:656ba143d384
514 parent: 1:656ba143d384
515 user: test
515 user: test
516 date: Thu Jan 01 00:00:00 1970 +0000
516 date: Thu Jan 01 00:00:00 1970 +0000
517 summary: add b
517 summary: add b
518
518
519 changeset: 1:656ba143d384
519 changeset: 1:656ba143d384
520 parent: -1:000000000000
520 parent: -1:000000000000
521 user: test
521 user: test
522 date: Thu Jan 01 00:00:00 1970 +0000
522 date: Thu Jan 01 00:00:00 1970 +0000
523 summary: add a
523 summary: add a
524
524
525 Removes can be uncommitted
525 Removes can be uncommitted
526
526
527 $ hg ci -m 'modified b'
527 $ hg ci -m 'modified b'
528 $ hg rm b
528 $ hg rm b
529 $ hg ci -m 'remove b'
529 $ hg ci -m 'remove b'
530 $ hg uncommit b
530 $ hg uncommit b
531 note: keeping empty commit
531 note: keeping empty commit
532 $ hg status
532 $ hg status
533 R b
533 R b
534
534
535 Uncommitting a directory won't run afoul of the checks that an explicit file
535 Uncommitting a directory won't run afoul of the checks that an explicit file
536 can be uncommitted.
536 can be uncommitted.
537
537
538 $ mkdir dir
538 $ mkdir dir
539 $ echo 1 > dir/file.txt
539 $ echo 1 > dir/file.txt
540 $ hg ci -Aqm 'add file in directory'
540 $ hg ci -Aqm 'add file in directory'
541 $ hg uncommit dir -m 'uncommit with message' -u 'different user' \
541 $ hg uncommit dir -m 'uncommit with message' -u 'different user' \
542 > -d 'Jun 30 12:12:12 1980 +0000'
542 > -d 'Jun 30 12:12:12 1980 +0000'
543 $ hg status
543 $ hg status
544 A dir/file.txt
544 A dir/file.txt
545 $ hg log -r .
545 $ hg log -r .
546 changeset: 8:b4dd26dc42e0
546 changeset: 8:b4dd26dc42e0
547 tag: tip
547 tag: tip
548 parent: 6:2278a4c24330
548 parent: 6:2278a4c24330
549 user: different user
549 user: different user
550 date: Mon Jun 30 12:12:12 1980 +0000
550 date: Mon Jun 30 12:12:12 1980 +0000
551 summary: uncommit with message
551 summary: uncommit with message
552
552
553 Bad option combinations
553 Bad option combinations
554
554
555 $ hg rollback -q --config ui.rollback=True
555 $ hg rollback -q --config ui.rollback=True
556 $ hg uncommit -U --user 'user'
556 $ hg uncommit -U --user 'user'
557 abort: --user and --currentuser are mutually exclusive
557 abort: cannot specify both --user and --currentuser
558 [255]
558 [255]
559 $ hg uncommit -D --date today
559 $ hg uncommit -D --date today
560 abort: --date and --currentdate are mutually exclusive
560 abort: cannot specify both --date and --currentdate
561 [255]
561 [255]
562
562
563 `uncommit <dir>` and `cd <dir> && uncommit .` behave the same...
563 `uncommit <dir>` and `cd <dir> && uncommit .` behave the same...
564
564
565 $ echo 2 > dir/file2.txt
565 $ echo 2 > dir/file2.txt
566 $ hg ci -Aqm 'add file2 in directory'
566 $ hg ci -Aqm 'add file2 in directory'
567 $ hg uncommit dir
567 $ hg uncommit dir
568 note: keeping empty commit
568 note: keeping empty commit
569 $ hg status
569 $ hg status
570 A dir/file2.txt
570 A dir/file2.txt
571
571
572 $ hg rollback -q --config ui.rollback=True
572 $ hg rollback -q --config ui.rollback=True
573 $ cd dir
573 $ cd dir
574 $ hg uncommit . -n 'this is a note'
574 $ hg uncommit . -n 'this is a note'
575 note: keeping empty commit
575 note: keeping empty commit
576 $ hg status
576 $ hg status
577 A dir/file2.txt
577 A dir/file2.txt
578 $ cd ..
578 $ cd ..
579
579
580 ... and errors out the same way when nothing can be uncommitted
580 ... and errors out the same way when nothing can be uncommitted
581
581
582 $ hg rollback -q --config ui.rollback=True
582 $ hg rollback -q --config ui.rollback=True
583 $ mkdir emptydir
583 $ mkdir emptydir
584 $ hg uncommit emptydir
584 $ hg uncommit emptydir
585 abort: cannot uncommit "emptydir"
585 abort: cannot uncommit "emptydir"
586 (file was untracked in working directory parent)
586 (file was untracked in working directory parent)
587 [255]
587 [255]
588
588
589 $ cd emptydir
589 $ cd emptydir
590 $ hg uncommit .
590 $ hg uncommit .
591 abort: cannot uncommit "emptydir"
591 abort: cannot uncommit "emptydir"
592 (file was untracked in working directory parent)
592 (file was untracked in working directory parent)
593 [255]
593 [255]
594 $ hg status
594 $ hg status
595 $ cd ..
595 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now