##// END OF EJS Templates
revert: always show relative path to .orig backup...
Martin von Zweigbergk -
r41731:4d484244 default
parent child Browse files
Show More
@@ -1,3332 +1,3333 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 errno
10 import errno
11 import os
11 import os
12 import re
12 import re
13
13
14 from .i18n import _
14 from .i18n import _
15 from .node import (
15 from .node import (
16 hex,
16 hex,
17 nullid,
17 nullid,
18 nullrev,
18 nullrev,
19 short,
19 short,
20 )
20 )
21
21
22 from . import (
22 from . import (
23 bookmarks,
23 bookmarks,
24 changelog,
24 changelog,
25 copies,
25 copies,
26 crecord as crecordmod,
26 crecord as crecordmod,
27 dirstateguard,
27 dirstateguard,
28 encoding,
28 encoding,
29 error,
29 error,
30 formatter,
30 formatter,
31 logcmdutil,
31 logcmdutil,
32 match as matchmod,
32 match as matchmod,
33 merge as mergemod,
33 merge as mergemod,
34 mergeutil,
34 mergeutil,
35 obsolete,
35 obsolete,
36 patch,
36 patch,
37 pathutil,
37 pathutil,
38 phases,
38 phases,
39 pycompat,
39 pycompat,
40 revlog,
40 revlog,
41 rewriteutil,
41 rewriteutil,
42 scmutil,
42 scmutil,
43 smartset,
43 smartset,
44 subrepoutil,
44 subrepoutil,
45 templatekw,
45 templatekw,
46 templater,
46 templater,
47 util,
47 util,
48 vfs as vfsmod,
48 vfs as vfsmod,
49 )
49 )
50
50
51 from .utils import (
51 from .utils import (
52 dateutil,
52 dateutil,
53 stringutil,
53 stringutil,
54 )
54 )
55
55
56 stringio = util.stringio
56 stringio = util.stringio
57
57
58 # templates of common command options
58 # templates of common command options
59
59
60 dryrunopts = [
60 dryrunopts = [
61 ('n', 'dry-run', None,
61 ('n', 'dry-run', None,
62 _('do not perform actions, just print output')),
62 _('do not perform actions, just print output')),
63 ]
63 ]
64
64
65 confirmopts = [
65 confirmopts = [
66 ('', 'confirm', None,
66 ('', 'confirm', None,
67 _('ask before applying actions')),
67 _('ask before applying actions')),
68 ]
68 ]
69
69
70 remoteopts = [
70 remoteopts = [
71 ('e', 'ssh', '',
71 ('e', 'ssh', '',
72 _('specify ssh command to use'), _('CMD')),
72 _('specify ssh command to use'), _('CMD')),
73 ('', 'remotecmd', '',
73 ('', 'remotecmd', '',
74 _('specify hg command to run on the remote side'), _('CMD')),
74 _('specify hg command to run on the remote side'), _('CMD')),
75 ('', 'insecure', None,
75 ('', 'insecure', None,
76 _('do not verify server certificate (ignoring web.cacerts config)')),
76 _('do not verify server certificate (ignoring web.cacerts config)')),
77 ]
77 ]
78
78
79 walkopts = [
79 walkopts = [
80 ('I', 'include', [],
80 ('I', 'include', [],
81 _('include names matching the given patterns'), _('PATTERN')),
81 _('include names matching the given patterns'), _('PATTERN')),
82 ('X', 'exclude', [],
82 ('X', 'exclude', [],
83 _('exclude names matching the given patterns'), _('PATTERN')),
83 _('exclude names matching the given patterns'), _('PATTERN')),
84 ]
84 ]
85
85
86 commitopts = [
86 commitopts = [
87 ('m', 'message', '',
87 ('m', 'message', '',
88 _('use text as commit message'), _('TEXT')),
88 _('use text as commit message'), _('TEXT')),
89 ('l', 'logfile', '',
89 ('l', 'logfile', '',
90 _('read commit message from file'), _('FILE')),
90 _('read commit message from file'), _('FILE')),
91 ]
91 ]
92
92
93 commitopts2 = [
93 commitopts2 = [
94 ('d', 'date', '',
94 ('d', 'date', '',
95 _('record the specified date as commit date'), _('DATE')),
95 _('record the specified date as commit date'), _('DATE')),
96 ('u', 'user', '',
96 ('u', 'user', '',
97 _('record the specified user as committer'), _('USER')),
97 _('record the specified user as committer'), _('USER')),
98 ]
98 ]
99
99
100 formatteropts = [
100 formatteropts = [
101 ('T', 'template', '',
101 ('T', 'template', '',
102 _('display with template'), _('TEMPLATE')),
102 _('display with template'), _('TEMPLATE')),
103 ]
103 ]
104
104
105 templateopts = [
105 templateopts = [
106 ('', 'style', '',
106 ('', 'style', '',
107 _('display using template map file (DEPRECATED)'), _('STYLE')),
107 _('display using template map file (DEPRECATED)'), _('STYLE')),
108 ('T', 'template', '',
108 ('T', 'template', '',
109 _('display with template'), _('TEMPLATE')),
109 _('display with template'), _('TEMPLATE')),
110 ]
110 ]
111
111
112 logopts = [
112 logopts = [
113 ('p', 'patch', None, _('show patch')),
113 ('p', 'patch', None, _('show patch')),
114 ('g', 'git', None, _('use git extended diff format')),
114 ('g', 'git', None, _('use git extended diff format')),
115 ('l', 'limit', '',
115 ('l', 'limit', '',
116 _('limit number of changes displayed'), _('NUM')),
116 _('limit number of changes displayed'), _('NUM')),
117 ('M', 'no-merges', None, _('do not show merges')),
117 ('M', 'no-merges', None, _('do not show merges')),
118 ('', 'stat', None, _('output diffstat-style summary of changes')),
118 ('', 'stat', None, _('output diffstat-style summary of changes')),
119 ('G', 'graph', None, _("show the revision DAG")),
119 ('G', 'graph', None, _("show the revision DAG")),
120 ] + templateopts
120 ] + templateopts
121
121
122 diffopts = [
122 diffopts = [
123 ('a', 'text', None, _('treat all files as text')),
123 ('a', 'text', None, _('treat all files as text')),
124 ('g', 'git', None, _('use git extended diff format')),
124 ('g', 'git', None, _('use git extended diff format')),
125 ('', 'binary', None, _('generate binary diffs in git mode (default)')),
125 ('', 'binary', None, _('generate binary diffs in git mode (default)')),
126 ('', 'nodates', None, _('omit dates from diff headers'))
126 ('', 'nodates', None, _('omit dates from diff headers'))
127 ]
127 ]
128
128
129 diffwsopts = [
129 diffwsopts = [
130 ('w', 'ignore-all-space', None,
130 ('w', 'ignore-all-space', None,
131 _('ignore white space when comparing lines')),
131 _('ignore white space when comparing lines')),
132 ('b', 'ignore-space-change', None,
132 ('b', 'ignore-space-change', None,
133 _('ignore changes in the amount of white space')),
133 _('ignore changes in the amount of white space')),
134 ('B', 'ignore-blank-lines', None,
134 ('B', 'ignore-blank-lines', None,
135 _('ignore changes whose lines are all blank')),
135 _('ignore changes whose lines are all blank')),
136 ('Z', 'ignore-space-at-eol', None,
136 ('Z', 'ignore-space-at-eol', None,
137 _('ignore changes in whitespace at EOL')),
137 _('ignore changes in whitespace at EOL')),
138 ]
138 ]
139
139
140 diffopts2 = [
140 diffopts2 = [
141 ('', 'noprefix', None, _('omit a/ and b/ prefixes from filenames')),
141 ('', 'noprefix', None, _('omit a/ and b/ prefixes from filenames')),
142 ('p', 'show-function', None, _('show which function each change is in')),
142 ('p', 'show-function', None, _('show which function each change is in')),
143 ('', 'reverse', None, _('produce a diff that undoes the changes')),
143 ('', 'reverse', None, _('produce a diff that undoes the changes')),
144 ] + diffwsopts + [
144 ] + diffwsopts + [
145 ('U', 'unified', '',
145 ('U', 'unified', '',
146 _('number of lines of context to show'), _('NUM')),
146 _('number of lines of context to show'), _('NUM')),
147 ('', 'stat', None, _('output diffstat-style summary of changes')),
147 ('', 'stat', None, _('output diffstat-style summary of changes')),
148 ('', 'root', '', _('produce diffs relative to subdirectory'), _('DIR')),
148 ('', 'root', '', _('produce diffs relative to subdirectory'), _('DIR')),
149 ]
149 ]
150
150
151 mergetoolopts = [
151 mergetoolopts = [
152 ('t', 'tool', '', _('specify merge tool'), _('TOOL')),
152 ('t', 'tool', '', _('specify merge tool'), _('TOOL')),
153 ]
153 ]
154
154
155 similarityopts = [
155 similarityopts = [
156 ('s', 'similarity', '',
156 ('s', 'similarity', '',
157 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
157 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
158 ]
158 ]
159
159
160 subrepoopts = [
160 subrepoopts = [
161 ('S', 'subrepos', None,
161 ('S', 'subrepos', None,
162 _('recurse into subrepositories'))
162 _('recurse into subrepositories'))
163 ]
163 ]
164
164
165 debugrevlogopts = [
165 debugrevlogopts = [
166 ('c', 'changelog', False, _('open changelog')),
166 ('c', 'changelog', False, _('open changelog')),
167 ('m', 'manifest', False, _('open manifest')),
167 ('m', 'manifest', False, _('open manifest')),
168 ('', 'dir', '', _('open directory manifest')),
168 ('', 'dir', '', _('open directory manifest')),
169 ]
169 ]
170
170
171 # special string such that everything below this line will be ingored in the
171 # special string such that everything below this line will be ingored in the
172 # editor text
172 # editor text
173 _linebelow = "^HG: ------------------------ >8 ------------------------$"
173 _linebelow = "^HG: ------------------------ >8 ------------------------$"
174
174
175 def ishunk(x):
175 def ishunk(x):
176 hunkclasses = (crecordmod.uihunk, patch.recordhunk)
176 hunkclasses = (crecordmod.uihunk, patch.recordhunk)
177 return isinstance(x, hunkclasses)
177 return isinstance(x, hunkclasses)
178
178
179 def newandmodified(chunks, originalchunks):
179 def newandmodified(chunks, originalchunks):
180 newlyaddedandmodifiedfiles = set()
180 newlyaddedandmodifiedfiles = set()
181 for chunk in chunks:
181 for chunk in chunks:
182 if ishunk(chunk) and chunk.header.isnewfile() and chunk not in \
182 if ishunk(chunk) and chunk.header.isnewfile() and chunk not in \
183 originalchunks:
183 originalchunks:
184 newlyaddedandmodifiedfiles.add(chunk.header.filename())
184 newlyaddedandmodifiedfiles.add(chunk.header.filename())
185 return newlyaddedandmodifiedfiles
185 return newlyaddedandmodifiedfiles
186
186
187 def parsealiases(cmd):
187 def parsealiases(cmd):
188 return cmd.split("|")
188 return cmd.split("|")
189
189
190 def setupwrapcolorwrite(ui):
190 def setupwrapcolorwrite(ui):
191 # wrap ui.write so diff output can be labeled/colorized
191 # wrap ui.write so diff output can be labeled/colorized
192 def wrapwrite(orig, *args, **kw):
192 def wrapwrite(orig, *args, **kw):
193 label = kw.pop(r'label', '')
193 label = kw.pop(r'label', '')
194 for chunk, l in patch.difflabel(lambda: args):
194 for chunk, l in patch.difflabel(lambda: args):
195 orig(chunk, label=label + l)
195 orig(chunk, label=label + l)
196
196
197 oldwrite = ui.write
197 oldwrite = ui.write
198 def wrap(*args, **kwargs):
198 def wrap(*args, **kwargs):
199 return wrapwrite(oldwrite, *args, **kwargs)
199 return wrapwrite(oldwrite, *args, **kwargs)
200 setattr(ui, 'write', wrap)
200 setattr(ui, 'write', wrap)
201 return oldwrite
201 return oldwrite
202
202
203 def filterchunks(ui, originalhunks, usecurses, testfile, operation=None):
203 def filterchunks(ui, originalhunks, usecurses, testfile, operation=None):
204 try:
204 try:
205 if usecurses:
205 if usecurses:
206 if testfile:
206 if testfile:
207 recordfn = crecordmod.testdecorator(
207 recordfn = crecordmod.testdecorator(
208 testfile, crecordmod.testchunkselector)
208 testfile, crecordmod.testchunkselector)
209 else:
209 else:
210 recordfn = crecordmod.chunkselector
210 recordfn = crecordmod.chunkselector
211
211
212 return crecordmod.filterpatch(ui, originalhunks, recordfn,
212 return crecordmod.filterpatch(ui, originalhunks, recordfn,
213 operation)
213 operation)
214 except crecordmod.fallbackerror as e:
214 except crecordmod.fallbackerror as e:
215 ui.warn('%s\n' % e.message)
215 ui.warn('%s\n' % e.message)
216 ui.warn(_('falling back to text mode\n'))
216 ui.warn(_('falling back to text mode\n'))
217
217
218 return patch.filterpatch(ui, originalhunks, operation)
218 return patch.filterpatch(ui, originalhunks, operation)
219
219
220 def recordfilter(ui, originalhunks, operation=None):
220 def recordfilter(ui, originalhunks, operation=None):
221 """ Prompts the user to filter the originalhunks and return a list of
221 """ Prompts the user to filter the originalhunks and return a list of
222 selected hunks.
222 selected hunks.
223 *operation* is used for to build ui messages to indicate the user what
223 *operation* is used for to build ui messages to indicate the user what
224 kind of filtering they are doing: reverting, committing, shelving, etc.
224 kind of filtering they are doing: reverting, committing, shelving, etc.
225 (see patch.filterpatch).
225 (see patch.filterpatch).
226 """
226 """
227 usecurses = crecordmod.checkcurses(ui)
227 usecurses = crecordmod.checkcurses(ui)
228 testfile = ui.config('experimental', 'crecordtest')
228 testfile = ui.config('experimental', 'crecordtest')
229 oldwrite = setupwrapcolorwrite(ui)
229 oldwrite = setupwrapcolorwrite(ui)
230 try:
230 try:
231 newchunks, newopts = filterchunks(ui, originalhunks, usecurses,
231 newchunks, newopts = filterchunks(ui, originalhunks, usecurses,
232 testfile, operation)
232 testfile, operation)
233 finally:
233 finally:
234 ui.write = oldwrite
234 ui.write = oldwrite
235 return newchunks, newopts
235 return newchunks, newopts
236
236
237 def dorecord(ui, repo, commitfunc, cmdsuggest, backupall,
237 def dorecord(ui, repo, commitfunc, cmdsuggest, backupall,
238 filterfn, *pats, **opts):
238 filterfn, *pats, **opts):
239 opts = pycompat.byteskwargs(opts)
239 opts = pycompat.byteskwargs(opts)
240 if not ui.interactive():
240 if not ui.interactive():
241 if cmdsuggest:
241 if cmdsuggest:
242 msg = _('running non-interactively, use %s instead') % cmdsuggest
242 msg = _('running non-interactively, use %s instead') % cmdsuggest
243 else:
243 else:
244 msg = _('running non-interactively')
244 msg = _('running non-interactively')
245 raise error.Abort(msg)
245 raise error.Abort(msg)
246
246
247 # make sure username is set before going interactive
247 # make sure username is set before going interactive
248 if not opts.get('user'):
248 if not opts.get('user'):
249 ui.username() # raise exception, username not provided
249 ui.username() # raise exception, username not provided
250
250
251 def recordfunc(ui, repo, message, match, opts):
251 def recordfunc(ui, repo, message, match, opts):
252 """This is generic record driver.
252 """This is generic record driver.
253
253
254 Its job is to interactively filter local changes, and
254 Its job is to interactively filter local changes, and
255 accordingly prepare working directory into a state in which the
255 accordingly prepare working directory into a state in which the
256 job can be delegated to a non-interactive commit command such as
256 job can be delegated to a non-interactive commit command such as
257 'commit' or 'qrefresh'.
257 'commit' or 'qrefresh'.
258
258
259 After the actual job is done by non-interactive command, the
259 After the actual job is done by non-interactive command, the
260 working directory is restored to its original state.
260 working directory is restored to its original state.
261
261
262 In the end we'll record interesting changes, and everything else
262 In the end we'll record interesting changes, and everything else
263 will be left in place, so the user can continue working.
263 will be left in place, so the user can continue working.
264 """
264 """
265
265
266 checkunfinished(repo, commit=True)
266 checkunfinished(repo, commit=True)
267 wctx = repo[None]
267 wctx = repo[None]
268 merge = len(wctx.parents()) > 1
268 merge = len(wctx.parents()) > 1
269 if merge:
269 if merge:
270 raise error.Abort(_('cannot partially commit a merge '
270 raise error.Abort(_('cannot partially commit a merge '
271 '(use "hg commit" instead)'))
271 '(use "hg commit" instead)'))
272
272
273 def fail(f, msg):
273 def fail(f, msg):
274 raise error.Abort('%s: %s' % (f, msg))
274 raise error.Abort('%s: %s' % (f, msg))
275
275
276 force = opts.get('force')
276 force = opts.get('force')
277 if not force:
277 if not force:
278 vdirs = []
278 vdirs = []
279 match.explicitdir = vdirs.append
279 match.explicitdir = vdirs.append
280 match.bad = fail
280 match.bad = fail
281
281
282 status = repo.status(match=match)
282 status = repo.status(match=match)
283 if not force:
283 if not force:
284 repo.checkcommitpatterns(wctx, vdirs, match, status, fail)
284 repo.checkcommitpatterns(wctx, vdirs, match, status, fail)
285 diffopts = patch.difffeatureopts(ui, opts=opts, whitespace=True,
285 diffopts = patch.difffeatureopts(ui, opts=opts, whitespace=True,
286 section='commands',
286 section='commands',
287 configprefix='commit.interactive.')
287 configprefix='commit.interactive.')
288 diffopts.nodates = True
288 diffopts.nodates = True
289 diffopts.git = True
289 diffopts.git = True
290 diffopts.showfunc = True
290 diffopts.showfunc = True
291 originaldiff = patch.diff(repo, changes=status, opts=diffopts)
291 originaldiff = patch.diff(repo, changes=status, opts=diffopts)
292 originalchunks = patch.parsepatch(originaldiff)
292 originalchunks = patch.parsepatch(originaldiff)
293
293
294 # 1. filter patch, since we are intending to apply subset of it
294 # 1. filter patch, since we are intending to apply subset of it
295 try:
295 try:
296 chunks, newopts = filterfn(ui, originalchunks)
296 chunks, newopts = filterfn(ui, originalchunks)
297 except error.PatchError as err:
297 except error.PatchError as err:
298 raise error.Abort(_('error parsing patch: %s') % err)
298 raise error.Abort(_('error parsing patch: %s') % err)
299 opts.update(newopts)
299 opts.update(newopts)
300
300
301 # We need to keep a backup of files that have been newly added and
301 # We need to keep a backup of files that have been newly added and
302 # modified during the recording process because there is a previous
302 # modified during the recording process because there is a previous
303 # version without the edit in the workdir
303 # version without the edit in the workdir
304 newlyaddedandmodifiedfiles = newandmodified(chunks, originalchunks)
304 newlyaddedandmodifiedfiles = newandmodified(chunks, originalchunks)
305 contenders = set()
305 contenders = set()
306 for h in chunks:
306 for h in chunks:
307 try:
307 try:
308 contenders.update(set(h.files()))
308 contenders.update(set(h.files()))
309 except AttributeError:
309 except AttributeError:
310 pass
310 pass
311
311
312 changed = status.modified + status.added + status.removed
312 changed = status.modified + status.added + status.removed
313 newfiles = [f for f in changed if f in contenders]
313 newfiles = [f for f in changed if f in contenders]
314 if not newfiles:
314 if not newfiles:
315 ui.status(_('no changes to record\n'))
315 ui.status(_('no changes to record\n'))
316 return 0
316 return 0
317
317
318 modified = set(status.modified)
318 modified = set(status.modified)
319
319
320 # 2. backup changed files, so we can restore them in the end
320 # 2. backup changed files, so we can restore them in the end
321
321
322 if backupall:
322 if backupall:
323 tobackup = changed
323 tobackup = changed
324 else:
324 else:
325 tobackup = [f for f in newfiles if f in modified or f in \
325 tobackup = [f for f in newfiles if f in modified or f in \
326 newlyaddedandmodifiedfiles]
326 newlyaddedandmodifiedfiles]
327 backups = {}
327 backups = {}
328 if tobackup:
328 if tobackup:
329 backupdir = repo.vfs.join('record-backups')
329 backupdir = repo.vfs.join('record-backups')
330 try:
330 try:
331 os.mkdir(backupdir)
331 os.mkdir(backupdir)
332 except OSError as err:
332 except OSError as err:
333 if err.errno != errno.EEXIST:
333 if err.errno != errno.EEXIST:
334 raise
334 raise
335 try:
335 try:
336 # backup continues
336 # backup continues
337 for f in tobackup:
337 for f in tobackup:
338 fd, tmpname = pycompat.mkstemp(prefix=f.replace('/', '_') + '.',
338 fd, tmpname = pycompat.mkstemp(prefix=f.replace('/', '_') + '.',
339 dir=backupdir)
339 dir=backupdir)
340 os.close(fd)
340 os.close(fd)
341 ui.debug('backup %r as %r\n' % (f, tmpname))
341 ui.debug('backup %r as %r\n' % (f, tmpname))
342 util.copyfile(repo.wjoin(f), tmpname, copystat=True)
342 util.copyfile(repo.wjoin(f), tmpname, copystat=True)
343 backups[f] = tmpname
343 backups[f] = tmpname
344
344
345 fp = stringio()
345 fp = stringio()
346 for c in chunks:
346 for c in chunks:
347 fname = c.filename()
347 fname = c.filename()
348 if fname in backups:
348 if fname in backups:
349 c.write(fp)
349 c.write(fp)
350 dopatch = fp.tell()
350 dopatch = fp.tell()
351 fp.seek(0)
351 fp.seek(0)
352
352
353 # 2.5 optionally review / modify patch in text editor
353 # 2.5 optionally review / modify patch in text editor
354 if opts.get('review', False):
354 if opts.get('review', False):
355 patchtext = (crecordmod.diffhelptext
355 patchtext = (crecordmod.diffhelptext
356 + crecordmod.patchhelptext
356 + crecordmod.patchhelptext
357 + fp.read())
357 + fp.read())
358 reviewedpatch = ui.edit(patchtext, "",
358 reviewedpatch = ui.edit(patchtext, "",
359 action="diff",
359 action="diff",
360 repopath=repo.path)
360 repopath=repo.path)
361 fp.truncate(0)
361 fp.truncate(0)
362 fp.write(reviewedpatch)
362 fp.write(reviewedpatch)
363 fp.seek(0)
363 fp.seek(0)
364
364
365 [os.unlink(repo.wjoin(c)) for c in newlyaddedandmodifiedfiles]
365 [os.unlink(repo.wjoin(c)) for c in newlyaddedandmodifiedfiles]
366 # 3a. apply filtered patch to clean repo (clean)
366 # 3a. apply filtered patch to clean repo (clean)
367 if backups:
367 if backups:
368 # Equivalent to hg.revert
368 # Equivalent to hg.revert
369 m = scmutil.matchfiles(repo, backups.keys())
369 m = scmutil.matchfiles(repo, backups.keys())
370 mergemod.update(repo, repo.dirstate.p1(), branchmerge=False,
370 mergemod.update(repo, repo.dirstate.p1(), branchmerge=False,
371 force=True, matcher=m)
371 force=True, matcher=m)
372
372
373 # 3b. (apply)
373 # 3b. (apply)
374 if dopatch:
374 if dopatch:
375 try:
375 try:
376 ui.debug('applying patch\n')
376 ui.debug('applying patch\n')
377 ui.debug(fp.getvalue())
377 ui.debug(fp.getvalue())
378 patch.internalpatch(ui, repo, fp, 1, eolmode=None)
378 patch.internalpatch(ui, repo, fp, 1, eolmode=None)
379 except error.PatchError as err:
379 except error.PatchError as err:
380 raise error.Abort(pycompat.bytestr(err))
380 raise error.Abort(pycompat.bytestr(err))
381 del fp
381 del fp
382
382
383 # 4. We prepared working directory according to filtered
383 # 4. We prepared working directory according to filtered
384 # patch. Now is the time to delegate the job to
384 # patch. Now is the time to delegate the job to
385 # commit/qrefresh or the like!
385 # commit/qrefresh or the like!
386
386
387 # Make all of the pathnames absolute.
387 # Make all of the pathnames absolute.
388 newfiles = [repo.wjoin(nf) for nf in newfiles]
388 newfiles = [repo.wjoin(nf) for nf in newfiles]
389 return commitfunc(ui, repo, *newfiles, **pycompat.strkwargs(opts))
389 return commitfunc(ui, repo, *newfiles, **pycompat.strkwargs(opts))
390 finally:
390 finally:
391 # 5. finally restore backed-up files
391 # 5. finally restore backed-up files
392 try:
392 try:
393 dirstate = repo.dirstate
393 dirstate = repo.dirstate
394 for realname, tmpname in backups.iteritems():
394 for realname, tmpname in backups.iteritems():
395 ui.debug('restoring %r to %r\n' % (tmpname, realname))
395 ui.debug('restoring %r to %r\n' % (tmpname, realname))
396
396
397 if dirstate[realname] == 'n':
397 if dirstate[realname] == 'n':
398 # without normallookup, restoring timestamp
398 # without normallookup, restoring timestamp
399 # may cause partially committed files
399 # may cause partially committed files
400 # to be treated as unmodified
400 # to be treated as unmodified
401 dirstate.normallookup(realname)
401 dirstate.normallookup(realname)
402
402
403 # copystat=True here and above are a hack to trick any
403 # copystat=True here and above are a hack to trick any
404 # editors that have f open that we haven't modified them.
404 # editors that have f open that we haven't modified them.
405 #
405 #
406 # Also note that this racy as an editor could notice the
406 # Also note that this racy as an editor could notice the
407 # file's mtime before we've finished writing it.
407 # file's mtime before we've finished writing it.
408 util.copyfile(tmpname, repo.wjoin(realname), copystat=True)
408 util.copyfile(tmpname, repo.wjoin(realname), copystat=True)
409 os.unlink(tmpname)
409 os.unlink(tmpname)
410 if tobackup:
410 if tobackup:
411 os.rmdir(backupdir)
411 os.rmdir(backupdir)
412 except OSError:
412 except OSError:
413 pass
413 pass
414
414
415 def recordinwlock(ui, repo, message, match, opts):
415 def recordinwlock(ui, repo, message, match, opts):
416 with repo.wlock():
416 with repo.wlock():
417 return recordfunc(ui, repo, message, match, opts)
417 return recordfunc(ui, repo, message, match, opts)
418
418
419 return commit(ui, repo, recordinwlock, pats, opts)
419 return commit(ui, repo, recordinwlock, pats, opts)
420
420
421 class dirnode(object):
421 class dirnode(object):
422 """
422 """
423 Represent a directory in user working copy with information required for
423 Represent a directory in user working copy with information required for
424 the purpose of tersing its status.
424 the purpose of tersing its status.
425
425
426 path is the path to the directory, without a trailing '/'
426 path is the path to the directory, without a trailing '/'
427
427
428 statuses is a set of statuses of all files in this directory (this includes
428 statuses is a set of statuses of all files in this directory (this includes
429 all the files in all the subdirectories too)
429 all the files in all the subdirectories too)
430
430
431 files is a list of files which are direct child of this directory
431 files is a list of files which are direct child of this directory
432
432
433 subdirs is a dictionary of sub-directory name as the key and it's own
433 subdirs is a dictionary of sub-directory name as the key and it's own
434 dirnode object as the value
434 dirnode object as the value
435 """
435 """
436
436
437 def __init__(self, dirpath):
437 def __init__(self, dirpath):
438 self.path = dirpath
438 self.path = dirpath
439 self.statuses = set([])
439 self.statuses = set([])
440 self.files = []
440 self.files = []
441 self.subdirs = {}
441 self.subdirs = {}
442
442
443 def _addfileindir(self, filename, status):
443 def _addfileindir(self, filename, status):
444 """Add a file in this directory as a direct child."""
444 """Add a file in this directory as a direct child."""
445 self.files.append((filename, status))
445 self.files.append((filename, status))
446
446
447 def addfile(self, filename, status):
447 def addfile(self, filename, status):
448 """
448 """
449 Add a file to this directory or to its direct parent directory.
449 Add a file to this directory or to its direct parent directory.
450
450
451 If the file is not direct child of this directory, we traverse to the
451 If the file is not direct child of this directory, we traverse to the
452 directory of which this file is a direct child of and add the file
452 directory of which this file is a direct child of and add the file
453 there.
453 there.
454 """
454 """
455
455
456 # the filename contains a path separator, it means it's not the direct
456 # the filename contains a path separator, it means it's not the direct
457 # child of this directory
457 # child of this directory
458 if '/' in filename:
458 if '/' in filename:
459 subdir, filep = filename.split('/', 1)
459 subdir, filep = filename.split('/', 1)
460
460
461 # does the dirnode object for subdir exists
461 # does the dirnode object for subdir exists
462 if subdir not in self.subdirs:
462 if subdir not in self.subdirs:
463 subdirpath = pathutil.join(self.path, subdir)
463 subdirpath = pathutil.join(self.path, subdir)
464 self.subdirs[subdir] = dirnode(subdirpath)
464 self.subdirs[subdir] = dirnode(subdirpath)
465
465
466 # try adding the file in subdir
466 # try adding the file in subdir
467 self.subdirs[subdir].addfile(filep, status)
467 self.subdirs[subdir].addfile(filep, status)
468
468
469 else:
469 else:
470 self._addfileindir(filename, status)
470 self._addfileindir(filename, status)
471
471
472 if status not in self.statuses:
472 if status not in self.statuses:
473 self.statuses.add(status)
473 self.statuses.add(status)
474
474
475 def iterfilepaths(self):
475 def iterfilepaths(self):
476 """Yield (status, path) for files directly under this directory."""
476 """Yield (status, path) for files directly under this directory."""
477 for f, st in self.files:
477 for f, st in self.files:
478 yield st, pathutil.join(self.path, f)
478 yield st, pathutil.join(self.path, f)
479
479
480 def tersewalk(self, terseargs):
480 def tersewalk(self, terseargs):
481 """
481 """
482 Yield (status, path) obtained by processing the status of this
482 Yield (status, path) obtained by processing the status of this
483 dirnode.
483 dirnode.
484
484
485 terseargs is the string of arguments passed by the user with `--terse`
485 terseargs is the string of arguments passed by the user with `--terse`
486 flag.
486 flag.
487
487
488 Following are the cases which can happen:
488 Following are the cases which can happen:
489
489
490 1) All the files in the directory (including all the files in its
490 1) All the files in the directory (including all the files in its
491 subdirectories) share the same status and the user has asked us to terse
491 subdirectories) share the same status and the user has asked us to terse
492 that status. -> yield (status, dirpath). dirpath will end in '/'.
492 that status. -> yield (status, dirpath). dirpath will end in '/'.
493
493
494 2) Otherwise, we do following:
494 2) Otherwise, we do following:
495
495
496 a) Yield (status, filepath) for all the files which are in this
496 a) Yield (status, filepath) for all the files which are in this
497 directory (only the ones in this directory, not the subdirs)
497 directory (only the ones in this directory, not the subdirs)
498
498
499 b) Recurse the function on all the subdirectories of this
499 b) Recurse the function on all the subdirectories of this
500 directory
500 directory
501 """
501 """
502
502
503 if len(self.statuses) == 1:
503 if len(self.statuses) == 1:
504 onlyst = self.statuses.pop()
504 onlyst = self.statuses.pop()
505
505
506 # Making sure we terse only when the status abbreviation is
506 # Making sure we terse only when the status abbreviation is
507 # passed as terse argument
507 # passed as terse argument
508 if onlyst in terseargs:
508 if onlyst in terseargs:
509 yield onlyst, self.path + '/'
509 yield onlyst, self.path + '/'
510 return
510 return
511
511
512 # add the files to status list
512 # add the files to status list
513 for st, fpath in self.iterfilepaths():
513 for st, fpath in self.iterfilepaths():
514 yield st, fpath
514 yield st, fpath
515
515
516 #recurse on the subdirs
516 #recurse on the subdirs
517 for dirobj in self.subdirs.values():
517 for dirobj in self.subdirs.values():
518 for st, fpath in dirobj.tersewalk(terseargs):
518 for st, fpath in dirobj.tersewalk(terseargs):
519 yield st, fpath
519 yield st, fpath
520
520
521 def tersedir(statuslist, terseargs):
521 def tersedir(statuslist, terseargs):
522 """
522 """
523 Terse the status if all the files in a directory shares the same status.
523 Terse the status if all the files in a directory shares the same status.
524
524
525 statuslist is scmutil.status() object which contains a list of files for
525 statuslist is scmutil.status() object which contains a list of files for
526 each status.
526 each status.
527 terseargs is string which is passed by the user as the argument to `--terse`
527 terseargs is string which is passed by the user as the argument to `--terse`
528 flag.
528 flag.
529
529
530 The function makes a tree of objects of dirnode class, and at each node it
530 The function makes a tree of objects of dirnode class, and at each node it
531 stores the information required to know whether we can terse a certain
531 stores the information required to know whether we can terse a certain
532 directory or not.
532 directory or not.
533 """
533 """
534 # the order matters here as that is used to produce final list
534 # the order matters here as that is used to produce final list
535 allst = ('m', 'a', 'r', 'd', 'u', 'i', 'c')
535 allst = ('m', 'a', 'r', 'd', 'u', 'i', 'c')
536
536
537 # checking the argument validity
537 # checking the argument validity
538 for s in pycompat.bytestr(terseargs):
538 for s in pycompat.bytestr(terseargs):
539 if s not in allst:
539 if s not in allst:
540 raise error.Abort(_("'%s' not recognized") % s)
540 raise error.Abort(_("'%s' not recognized") % s)
541
541
542 # creating a dirnode object for the root of the repo
542 # creating a dirnode object for the root of the repo
543 rootobj = dirnode('')
543 rootobj = dirnode('')
544 pstatus = ('modified', 'added', 'deleted', 'clean', 'unknown',
544 pstatus = ('modified', 'added', 'deleted', 'clean', 'unknown',
545 'ignored', 'removed')
545 'ignored', 'removed')
546
546
547 tersedict = {}
547 tersedict = {}
548 for attrname in pstatus:
548 for attrname in pstatus:
549 statuschar = attrname[0:1]
549 statuschar = attrname[0:1]
550 for f in getattr(statuslist, attrname):
550 for f in getattr(statuslist, attrname):
551 rootobj.addfile(f, statuschar)
551 rootobj.addfile(f, statuschar)
552 tersedict[statuschar] = []
552 tersedict[statuschar] = []
553
553
554 # we won't be tersing the root dir, so add files in it
554 # we won't be tersing the root dir, so add files in it
555 for st, fpath in rootobj.iterfilepaths():
555 for st, fpath in rootobj.iterfilepaths():
556 tersedict[st].append(fpath)
556 tersedict[st].append(fpath)
557
557
558 # process each sub-directory and build tersedict
558 # process each sub-directory and build tersedict
559 for subdir in rootobj.subdirs.values():
559 for subdir in rootobj.subdirs.values():
560 for st, f in subdir.tersewalk(terseargs):
560 for st, f in subdir.tersewalk(terseargs):
561 tersedict[st].append(f)
561 tersedict[st].append(f)
562
562
563 tersedlist = []
563 tersedlist = []
564 for st in allst:
564 for st in allst:
565 tersedict[st].sort()
565 tersedict[st].sort()
566 tersedlist.append(tersedict[st])
566 tersedlist.append(tersedict[st])
567
567
568 return tersedlist
568 return tersedlist
569
569
570 def _commentlines(raw):
570 def _commentlines(raw):
571 '''Surround lineswith a comment char and a new line'''
571 '''Surround lineswith a comment char and a new line'''
572 lines = raw.splitlines()
572 lines = raw.splitlines()
573 commentedlines = ['# %s' % line for line in lines]
573 commentedlines = ['# %s' % line for line in lines]
574 return '\n'.join(commentedlines) + '\n'
574 return '\n'.join(commentedlines) + '\n'
575
575
576 def _conflictsmsg(repo):
576 def _conflictsmsg(repo):
577 mergestate = mergemod.mergestate.read(repo)
577 mergestate = mergemod.mergestate.read(repo)
578 if not mergestate.active():
578 if not mergestate.active():
579 return
579 return
580
580
581 m = scmutil.match(repo[None])
581 m = scmutil.match(repo[None])
582 unresolvedlist = [f for f in mergestate.unresolved() if m(f)]
582 unresolvedlist = [f for f in mergestate.unresolved() if m(f)]
583 if unresolvedlist:
583 if unresolvedlist:
584 mergeliststr = '\n'.join(
584 mergeliststr = '\n'.join(
585 [' %s' % util.pathto(repo.root, encoding.getcwd(), path)
585 [' %s' % util.pathto(repo.root, encoding.getcwd(), path)
586 for path in sorted(unresolvedlist)])
586 for path in sorted(unresolvedlist)])
587 msg = _('''Unresolved merge conflicts:
587 msg = _('''Unresolved merge conflicts:
588
588
589 %s
589 %s
590
590
591 To mark files as resolved: hg resolve --mark FILE''') % mergeliststr
591 To mark files as resolved: hg resolve --mark FILE''') % mergeliststr
592 else:
592 else:
593 msg = _('No unresolved merge conflicts.')
593 msg = _('No unresolved merge conflicts.')
594
594
595 return _commentlines(msg)
595 return _commentlines(msg)
596
596
597 def _helpmessage(continuecmd, abortcmd):
597 def _helpmessage(continuecmd, abortcmd):
598 msg = _('To continue: %s\n'
598 msg = _('To continue: %s\n'
599 'To abort: %s') % (continuecmd, abortcmd)
599 'To abort: %s') % (continuecmd, abortcmd)
600 return _commentlines(msg)
600 return _commentlines(msg)
601
601
602 def _rebasemsg():
602 def _rebasemsg():
603 return _helpmessage('hg rebase --continue', 'hg rebase --abort')
603 return _helpmessage('hg rebase --continue', 'hg rebase --abort')
604
604
605 def _histeditmsg():
605 def _histeditmsg():
606 return _helpmessage('hg histedit --continue', 'hg histedit --abort')
606 return _helpmessage('hg histedit --continue', 'hg histedit --abort')
607
607
608 def _unshelvemsg():
608 def _unshelvemsg():
609 return _helpmessage('hg unshelve --continue', 'hg unshelve --abort')
609 return _helpmessage('hg unshelve --continue', 'hg unshelve --abort')
610
610
611 def _graftmsg():
611 def _graftmsg():
612 return _helpmessage('hg graft --continue', 'hg graft --abort')
612 return _helpmessage('hg graft --continue', 'hg graft --abort')
613
613
614 def _mergemsg():
614 def _mergemsg():
615 return _helpmessage('hg commit', 'hg merge --abort')
615 return _helpmessage('hg commit', 'hg merge --abort')
616
616
617 def _bisectmsg():
617 def _bisectmsg():
618 msg = _('To mark the changeset good: hg bisect --good\n'
618 msg = _('To mark the changeset good: hg bisect --good\n'
619 'To mark the changeset bad: hg bisect --bad\n'
619 'To mark the changeset bad: hg bisect --bad\n'
620 'To abort: hg bisect --reset\n')
620 'To abort: hg bisect --reset\n')
621 return _commentlines(msg)
621 return _commentlines(msg)
622
622
623 def fileexistspredicate(filename):
623 def fileexistspredicate(filename):
624 return lambda repo: repo.vfs.exists(filename)
624 return lambda repo: repo.vfs.exists(filename)
625
625
626 def _mergepredicate(repo):
626 def _mergepredicate(repo):
627 return len(repo[None].parents()) > 1
627 return len(repo[None].parents()) > 1
628
628
629 STATES = (
629 STATES = (
630 # (state, predicate to detect states, helpful message function)
630 # (state, predicate to detect states, helpful message function)
631 ('histedit', fileexistspredicate('histedit-state'), _histeditmsg),
631 ('histedit', fileexistspredicate('histedit-state'), _histeditmsg),
632 ('bisect', fileexistspredicate('bisect.state'), _bisectmsg),
632 ('bisect', fileexistspredicate('bisect.state'), _bisectmsg),
633 ('graft', fileexistspredicate('graftstate'), _graftmsg),
633 ('graft', fileexistspredicate('graftstate'), _graftmsg),
634 ('unshelve', fileexistspredicate('shelvedstate'), _unshelvemsg),
634 ('unshelve', fileexistspredicate('shelvedstate'), _unshelvemsg),
635 ('rebase', fileexistspredicate('rebasestate'), _rebasemsg),
635 ('rebase', fileexistspredicate('rebasestate'), _rebasemsg),
636 # The merge state is part of a list that will be iterated over.
636 # The merge state is part of a list that will be iterated over.
637 # They need to be last because some of the other unfinished states may also
637 # They need to be last because some of the other unfinished states may also
638 # be in a merge or update state (eg. rebase, histedit, graft, etc).
638 # be in a merge or update state (eg. rebase, histedit, graft, etc).
639 # We want those to have priority.
639 # We want those to have priority.
640 ('merge', _mergepredicate, _mergemsg),
640 ('merge', _mergepredicate, _mergemsg),
641 )
641 )
642
642
643 def _getrepostate(repo):
643 def _getrepostate(repo):
644 # experimental config: commands.status.skipstates
644 # experimental config: commands.status.skipstates
645 skip = set(repo.ui.configlist('commands', 'status.skipstates'))
645 skip = set(repo.ui.configlist('commands', 'status.skipstates'))
646 for state, statedetectionpredicate, msgfn in STATES:
646 for state, statedetectionpredicate, msgfn in STATES:
647 if state in skip:
647 if state in skip:
648 continue
648 continue
649 if statedetectionpredicate(repo):
649 if statedetectionpredicate(repo):
650 return (state, statedetectionpredicate, msgfn)
650 return (state, statedetectionpredicate, msgfn)
651
651
652 def morestatus(repo, fm):
652 def morestatus(repo, fm):
653 statetuple = _getrepostate(repo)
653 statetuple = _getrepostate(repo)
654 label = 'status.morestatus'
654 label = 'status.morestatus'
655 if statetuple:
655 if statetuple:
656 state, statedetectionpredicate, helpfulmsg = statetuple
656 state, statedetectionpredicate, helpfulmsg = statetuple
657 statemsg = _('The repository is in an unfinished *%s* state.') % state
657 statemsg = _('The repository is in an unfinished *%s* state.') % state
658 fm.plain('%s\n' % _commentlines(statemsg), label=label)
658 fm.plain('%s\n' % _commentlines(statemsg), label=label)
659 conmsg = _conflictsmsg(repo)
659 conmsg = _conflictsmsg(repo)
660 if conmsg:
660 if conmsg:
661 fm.plain('%s\n' % conmsg, label=label)
661 fm.plain('%s\n' % conmsg, label=label)
662 if helpfulmsg:
662 if helpfulmsg:
663 helpmsg = helpfulmsg()
663 helpmsg = helpfulmsg()
664 fm.plain('%s\n' % helpmsg, label=label)
664 fm.plain('%s\n' % helpmsg, label=label)
665
665
666 def findpossible(cmd, table, strict=False):
666 def findpossible(cmd, table, strict=False):
667 """
667 """
668 Return cmd -> (aliases, command table entry)
668 Return cmd -> (aliases, command table entry)
669 for each matching command.
669 for each matching command.
670 Return debug commands (or their aliases) only if no normal command matches.
670 Return debug commands (or their aliases) only if no normal command matches.
671 """
671 """
672 choice = {}
672 choice = {}
673 debugchoice = {}
673 debugchoice = {}
674
674
675 if cmd in table:
675 if cmd in table:
676 # short-circuit exact matches, "log" alias beats "log|history"
676 # short-circuit exact matches, "log" alias beats "log|history"
677 keys = [cmd]
677 keys = [cmd]
678 else:
678 else:
679 keys = table.keys()
679 keys = table.keys()
680
680
681 allcmds = []
681 allcmds = []
682 for e in keys:
682 for e in keys:
683 aliases = parsealiases(e)
683 aliases = parsealiases(e)
684 allcmds.extend(aliases)
684 allcmds.extend(aliases)
685 found = None
685 found = None
686 if cmd in aliases:
686 if cmd in aliases:
687 found = cmd
687 found = cmd
688 elif not strict:
688 elif not strict:
689 for a in aliases:
689 for a in aliases:
690 if a.startswith(cmd):
690 if a.startswith(cmd):
691 found = a
691 found = a
692 break
692 break
693 if found is not None:
693 if found is not None:
694 if aliases[0].startswith("debug") or found.startswith("debug"):
694 if aliases[0].startswith("debug") or found.startswith("debug"):
695 debugchoice[found] = (aliases, table[e])
695 debugchoice[found] = (aliases, table[e])
696 else:
696 else:
697 choice[found] = (aliases, table[e])
697 choice[found] = (aliases, table[e])
698
698
699 if not choice and debugchoice:
699 if not choice and debugchoice:
700 choice = debugchoice
700 choice = debugchoice
701
701
702 return choice, allcmds
702 return choice, allcmds
703
703
704 def findcmd(cmd, table, strict=True):
704 def findcmd(cmd, table, strict=True):
705 """Return (aliases, command table entry) for command string."""
705 """Return (aliases, command table entry) for command string."""
706 choice, allcmds = findpossible(cmd, table, strict)
706 choice, allcmds = findpossible(cmd, table, strict)
707
707
708 if cmd in choice:
708 if cmd in choice:
709 return choice[cmd]
709 return choice[cmd]
710
710
711 if len(choice) > 1:
711 if len(choice) > 1:
712 clist = sorted(choice)
712 clist = sorted(choice)
713 raise error.AmbiguousCommand(cmd, clist)
713 raise error.AmbiguousCommand(cmd, clist)
714
714
715 if choice:
715 if choice:
716 return list(choice.values())[0]
716 return list(choice.values())[0]
717
717
718 raise error.UnknownCommand(cmd, allcmds)
718 raise error.UnknownCommand(cmd, allcmds)
719
719
720 def changebranch(ui, repo, revs, label):
720 def changebranch(ui, repo, revs, label):
721 """ Change the branch name of given revs to label """
721 """ Change the branch name of given revs to label """
722
722
723 with repo.wlock(), repo.lock(), repo.transaction('branches'):
723 with repo.wlock(), repo.lock(), repo.transaction('branches'):
724 # abort in case of uncommitted merge or dirty wdir
724 # abort in case of uncommitted merge or dirty wdir
725 bailifchanged(repo)
725 bailifchanged(repo)
726 revs = scmutil.revrange(repo, revs)
726 revs = scmutil.revrange(repo, revs)
727 if not revs:
727 if not revs:
728 raise error.Abort("empty revision set")
728 raise error.Abort("empty revision set")
729 roots = repo.revs('roots(%ld)', revs)
729 roots = repo.revs('roots(%ld)', revs)
730 if len(roots) > 1:
730 if len(roots) > 1:
731 raise error.Abort(_("cannot change branch of non-linear revisions"))
731 raise error.Abort(_("cannot change branch of non-linear revisions"))
732 rewriteutil.precheck(repo, revs, 'change branch of')
732 rewriteutil.precheck(repo, revs, 'change branch of')
733
733
734 root = repo[roots.first()]
734 root = repo[roots.first()]
735 rpb = {parent.branch() for parent in root.parents()}
735 rpb = {parent.branch() for parent in root.parents()}
736 if label not in rpb and label in repo.branchmap():
736 if label not in rpb and label in repo.branchmap():
737 raise error.Abort(_("a branch of the same name already exists"))
737 raise error.Abort(_("a branch of the same name already exists"))
738
738
739 if repo.revs('obsolete() and %ld', revs):
739 if repo.revs('obsolete() and %ld', revs):
740 raise error.Abort(_("cannot change branch of a obsolete changeset"))
740 raise error.Abort(_("cannot change branch of a obsolete changeset"))
741
741
742 # make sure only topological heads
742 # make sure only topological heads
743 if repo.revs('heads(%ld) - head()', revs):
743 if repo.revs('heads(%ld) - head()', revs):
744 raise error.Abort(_("cannot change branch in middle of a stack"))
744 raise error.Abort(_("cannot change branch in middle of a stack"))
745
745
746 replacements = {}
746 replacements = {}
747 # avoid import cycle mercurial.cmdutil -> mercurial.context ->
747 # avoid import cycle mercurial.cmdutil -> mercurial.context ->
748 # mercurial.subrepo -> mercurial.cmdutil
748 # mercurial.subrepo -> mercurial.cmdutil
749 from . import context
749 from . import context
750 for rev in revs:
750 for rev in revs:
751 ctx = repo[rev]
751 ctx = repo[rev]
752 oldbranch = ctx.branch()
752 oldbranch = ctx.branch()
753 # check if ctx has same branch
753 # check if ctx has same branch
754 if oldbranch == label:
754 if oldbranch == label:
755 continue
755 continue
756
756
757 def filectxfn(repo, newctx, path):
757 def filectxfn(repo, newctx, path):
758 try:
758 try:
759 return ctx[path]
759 return ctx[path]
760 except error.ManifestLookupError:
760 except error.ManifestLookupError:
761 return None
761 return None
762
762
763 ui.debug("changing branch of '%s' from '%s' to '%s'\n"
763 ui.debug("changing branch of '%s' from '%s' to '%s'\n"
764 % (hex(ctx.node()), oldbranch, label))
764 % (hex(ctx.node()), oldbranch, label))
765 extra = ctx.extra()
765 extra = ctx.extra()
766 extra['branch_change'] = hex(ctx.node())
766 extra['branch_change'] = hex(ctx.node())
767 # While changing branch of set of linear commits, make sure that
767 # While changing branch of set of linear commits, make sure that
768 # we base our commits on new parent rather than old parent which
768 # we base our commits on new parent rather than old parent which
769 # was obsoleted while changing the branch
769 # was obsoleted while changing the branch
770 p1 = ctx.p1().node()
770 p1 = ctx.p1().node()
771 p2 = ctx.p2().node()
771 p2 = ctx.p2().node()
772 if p1 in replacements:
772 if p1 in replacements:
773 p1 = replacements[p1][0]
773 p1 = replacements[p1][0]
774 if p2 in replacements:
774 if p2 in replacements:
775 p2 = replacements[p2][0]
775 p2 = replacements[p2][0]
776
776
777 mc = context.memctx(repo, (p1, p2),
777 mc = context.memctx(repo, (p1, p2),
778 ctx.description(),
778 ctx.description(),
779 ctx.files(),
779 ctx.files(),
780 filectxfn,
780 filectxfn,
781 user=ctx.user(),
781 user=ctx.user(),
782 date=ctx.date(),
782 date=ctx.date(),
783 extra=extra,
783 extra=extra,
784 branch=label)
784 branch=label)
785
785
786 newnode = repo.commitctx(mc)
786 newnode = repo.commitctx(mc)
787 replacements[ctx.node()] = (newnode,)
787 replacements[ctx.node()] = (newnode,)
788 ui.debug('new node id is %s\n' % hex(newnode))
788 ui.debug('new node id is %s\n' % hex(newnode))
789
789
790 # create obsmarkers and move bookmarks
790 # create obsmarkers and move bookmarks
791 scmutil.cleanupnodes(repo, replacements, 'branch-change', fixphase=True)
791 scmutil.cleanupnodes(repo, replacements, 'branch-change', fixphase=True)
792
792
793 # move the working copy too
793 # move the working copy too
794 wctx = repo[None]
794 wctx = repo[None]
795 # in-progress merge is a bit too complex for now.
795 # in-progress merge is a bit too complex for now.
796 if len(wctx.parents()) == 1:
796 if len(wctx.parents()) == 1:
797 newid = replacements.get(wctx.p1().node())
797 newid = replacements.get(wctx.p1().node())
798 if newid is not None:
798 if newid is not None:
799 # avoid import cycle mercurial.cmdutil -> mercurial.hg ->
799 # avoid import cycle mercurial.cmdutil -> mercurial.hg ->
800 # mercurial.cmdutil
800 # mercurial.cmdutil
801 from . import hg
801 from . import hg
802 hg.update(repo, newid[0], quietempty=True)
802 hg.update(repo, newid[0], quietempty=True)
803
803
804 ui.status(_("changed branch on %d changesets\n") % len(replacements))
804 ui.status(_("changed branch on %d changesets\n") % len(replacements))
805
805
806 def findrepo(p):
806 def findrepo(p):
807 while not os.path.isdir(os.path.join(p, ".hg")):
807 while not os.path.isdir(os.path.join(p, ".hg")):
808 oldp, p = p, os.path.dirname(p)
808 oldp, p = p, os.path.dirname(p)
809 if p == oldp:
809 if p == oldp:
810 return None
810 return None
811
811
812 return p
812 return p
813
813
814 def bailifchanged(repo, merge=True, hint=None):
814 def bailifchanged(repo, merge=True, hint=None):
815 """ enforce the precondition that working directory must be clean.
815 """ enforce the precondition that working directory must be clean.
816
816
817 'merge' can be set to false if a pending uncommitted merge should be
817 'merge' can be set to false if a pending uncommitted merge should be
818 ignored (such as when 'update --check' runs).
818 ignored (such as when 'update --check' runs).
819
819
820 'hint' is the usual hint given to Abort exception.
820 'hint' is the usual hint given to Abort exception.
821 """
821 """
822
822
823 if merge and repo.dirstate.p2() != nullid:
823 if merge and repo.dirstate.p2() != nullid:
824 raise error.Abort(_('outstanding uncommitted merge'), hint=hint)
824 raise error.Abort(_('outstanding uncommitted merge'), hint=hint)
825 modified, added, removed, deleted = repo.status()[:4]
825 modified, added, removed, deleted = repo.status()[:4]
826 if modified or added or removed or deleted:
826 if modified or added or removed or deleted:
827 raise error.Abort(_('uncommitted changes'), hint=hint)
827 raise error.Abort(_('uncommitted changes'), hint=hint)
828 ctx = repo[None]
828 ctx = repo[None]
829 for s in sorted(ctx.substate):
829 for s in sorted(ctx.substate):
830 ctx.sub(s).bailifchanged(hint=hint)
830 ctx.sub(s).bailifchanged(hint=hint)
831
831
832 def logmessage(ui, opts):
832 def logmessage(ui, opts):
833 """ get the log message according to -m and -l option """
833 """ get the log message according to -m and -l option """
834 message = opts.get('message')
834 message = opts.get('message')
835 logfile = opts.get('logfile')
835 logfile = opts.get('logfile')
836
836
837 if message and logfile:
837 if message and logfile:
838 raise error.Abort(_('options --message and --logfile are mutually '
838 raise error.Abort(_('options --message and --logfile are mutually '
839 'exclusive'))
839 'exclusive'))
840 if not message and logfile:
840 if not message and logfile:
841 try:
841 try:
842 if isstdiofilename(logfile):
842 if isstdiofilename(logfile):
843 message = ui.fin.read()
843 message = ui.fin.read()
844 else:
844 else:
845 message = '\n'.join(util.readfile(logfile).splitlines())
845 message = '\n'.join(util.readfile(logfile).splitlines())
846 except IOError as inst:
846 except IOError as inst:
847 raise error.Abort(_("can't read commit message '%s': %s") %
847 raise error.Abort(_("can't read commit message '%s': %s") %
848 (logfile, encoding.strtolocal(inst.strerror)))
848 (logfile, encoding.strtolocal(inst.strerror)))
849 return message
849 return message
850
850
851 def mergeeditform(ctxorbool, baseformname):
851 def mergeeditform(ctxorbool, baseformname):
852 """return appropriate editform name (referencing a committemplate)
852 """return appropriate editform name (referencing a committemplate)
853
853
854 'ctxorbool' is either a ctx to be committed, or a bool indicating whether
854 'ctxorbool' is either a ctx to be committed, or a bool indicating whether
855 merging is committed.
855 merging is committed.
856
856
857 This returns baseformname with '.merge' appended if it is a merge,
857 This returns baseformname with '.merge' appended if it is a merge,
858 otherwise '.normal' is appended.
858 otherwise '.normal' is appended.
859 """
859 """
860 if isinstance(ctxorbool, bool):
860 if isinstance(ctxorbool, bool):
861 if ctxorbool:
861 if ctxorbool:
862 return baseformname + ".merge"
862 return baseformname + ".merge"
863 elif len(ctxorbool.parents()) > 1:
863 elif len(ctxorbool.parents()) > 1:
864 return baseformname + ".merge"
864 return baseformname + ".merge"
865
865
866 return baseformname + ".normal"
866 return baseformname + ".normal"
867
867
868 def getcommiteditor(edit=False, finishdesc=None, extramsg=None,
868 def getcommiteditor(edit=False, finishdesc=None, extramsg=None,
869 editform='', **opts):
869 editform='', **opts):
870 """get appropriate commit message editor according to '--edit' option
870 """get appropriate commit message editor according to '--edit' option
871
871
872 'finishdesc' is a function to be called with edited commit message
872 'finishdesc' is a function to be called with edited commit message
873 (= 'description' of the new changeset) just after editing, but
873 (= 'description' of the new changeset) just after editing, but
874 before checking empty-ness. It should return actual text to be
874 before checking empty-ness. It should return actual text to be
875 stored into history. This allows to change description before
875 stored into history. This allows to change description before
876 storing.
876 storing.
877
877
878 'extramsg' is a extra message to be shown in the editor instead of
878 'extramsg' is a extra message to be shown in the editor instead of
879 'Leave message empty to abort commit' line. 'HG: ' prefix and EOL
879 'Leave message empty to abort commit' line. 'HG: ' prefix and EOL
880 is automatically added.
880 is automatically added.
881
881
882 'editform' is a dot-separated list of names, to distinguish
882 'editform' is a dot-separated list of names, to distinguish
883 the purpose of commit text editing.
883 the purpose of commit text editing.
884
884
885 'getcommiteditor' returns 'commitforceeditor' regardless of
885 'getcommiteditor' returns 'commitforceeditor' regardless of
886 'edit', if one of 'finishdesc' or 'extramsg' is specified, because
886 'edit', if one of 'finishdesc' or 'extramsg' is specified, because
887 they are specific for usage in MQ.
887 they are specific for usage in MQ.
888 """
888 """
889 if edit or finishdesc or extramsg:
889 if edit or finishdesc or extramsg:
890 return lambda r, c, s: commitforceeditor(r, c, s,
890 return lambda r, c, s: commitforceeditor(r, c, s,
891 finishdesc=finishdesc,
891 finishdesc=finishdesc,
892 extramsg=extramsg,
892 extramsg=extramsg,
893 editform=editform)
893 editform=editform)
894 elif editform:
894 elif editform:
895 return lambda r, c, s: commiteditor(r, c, s, editform=editform)
895 return lambda r, c, s: commiteditor(r, c, s, editform=editform)
896 else:
896 else:
897 return commiteditor
897 return commiteditor
898
898
899 def _escapecommandtemplate(tmpl):
899 def _escapecommandtemplate(tmpl):
900 parts = []
900 parts = []
901 for typ, start, end in templater.scantemplate(tmpl, raw=True):
901 for typ, start, end in templater.scantemplate(tmpl, raw=True):
902 if typ == b'string':
902 if typ == b'string':
903 parts.append(stringutil.escapestr(tmpl[start:end]))
903 parts.append(stringutil.escapestr(tmpl[start:end]))
904 else:
904 else:
905 parts.append(tmpl[start:end])
905 parts.append(tmpl[start:end])
906 return b''.join(parts)
906 return b''.join(parts)
907
907
908 def rendercommandtemplate(ui, tmpl, props):
908 def rendercommandtemplate(ui, tmpl, props):
909 r"""Expand a literal template 'tmpl' in a way suitable for command line
909 r"""Expand a literal template 'tmpl' in a way suitable for command line
910
910
911 '\' in outermost string is not taken as an escape character because it
911 '\' in outermost string is not taken as an escape character because it
912 is a directory separator on Windows.
912 is a directory separator on Windows.
913
913
914 >>> from . import ui as uimod
914 >>> from . import ui as uimod
915 >>> ui = uimod.ui()
915 >>> ui = uimod.ui()
916 >>> rendercommandtemplate(ui, b'c:\\{path}', {b'path': b'foo'})
916 >>> rendercommandtemplate(ui, b'c:\\{path}', {b'path': b'foo'})
917 'c:\\foo'
917 'c:\\foo'
918 >>> rendercommandtemplate(ui, b'{"c:\\{path}"}', {'path': b'foo'})
918 >>> rendercommandtemplate(ui, b'{"c:\\{path}"}', {'path': b'foo'})
919 'c:{path}'
919 'c:{path}'
920 """
920 """
921 if not tmpl:
921 if not tmpl:
922 return tmpl
922 return tmpl
923 t = formatter.maketemplater(ui, _escapecommandtemplate(tmpl))
923 t = formatter.maketemplater(ui, _escapecommandtemplate(tmpl))
924 return t.renderdefault(props)
924 return t.renderdefault(props)
925
925
926 def rendertemplate(ctx, tmpl, props=None):
926 def rendertemplate(ctx, tmpl, props=None):
927 """Expand a literal template 'tmpl' byte-string against one changeset
927 """Expand a literal template 'tmpl' byte-string against one changeset
928
928
929 Each props item must be a stringify-able value or a callable returning
929 Each props item must be a stringify-able value or a callable returning
930 such value, i.e. no bare list nor dict should be passed.
930 such value, i.e. no bare list nor dict should be passed.
931 """
931 """
932 repo = ctx.repo()
932 repo = ctx.repo()
933 tres = formatter.templateresources(repo.ui, repo)
933 tres = formatter.templateresources(repo.ui, repo)
934 t = formatter.maketemplater(repo.ui, tmpl, defaults=templatekw.keywords,
934 t = formatter.maketemplater(repo.ui, tmpl, defaults=templatekw.keywords,
935 resources=tres)
935 resources=tres)
936 mapping = {'ctx': ctx}
936 mapping = {'ctx': ctx}
937 if props:
937 if props:
938 mapping.update(props)
938 mapping.update(props)
939 return t.renderdefault(mapping)
939 return t.renderdefault(mapping)
940
940
941 def _buildfntemplate(pat, total=None, seqno=None, revwidth=None, pathname=None):
941 def _buildfntemplate(pat, total=None, seqno=None, revwidth=None, pathname=None):
942 r"""Convert old-style filename format string to template string
942 r"""Convert old-style filename format string to template string
943
943
944 >>> _buildfntemplate(b'foo-%b-%n.patch', seqno=0)
944 >>> _buildfntemplate(b'foo-%b-%n.patch', seqno=0)
945 'foo-{reporoot|basename}-{seqno}.patch'
945 'foo-{reporoot|basename}-{seqno}.patch'
946 >>> _buildfntemplate(b'%R{tags % "{tag}"}%H')
946 >>> _buildfntemplate(b'%R{tags % "{tag}"}%H')
947 '{rev}{tags % "{tag}"}{node}'
947 '{rev}{tags % "{tag}"}{node}'
948
948
949 '\' in outermost strings has to be escaped because it is a directory
949 '\' in outermost strings has to be escaped because it is a directory
950 separator on Windows:
950 separator on Windows:
951
951
952 >>> _buildfntemplate(b'c:\\tmp\\%R\\%n.patch', seqno=0)
952 >>> _buildfntemplate(b'c:\\tmp\\%R\\%n.patch', seqno=0)
953 'c:\\\\tmp\\\\{rev}\\\\{seqno}.patch'
953 'c:\\\\tmp\\\\{rev}\\\\{seqno}.patch'
954 >>> _buildfntemplate(b'\\\\foo\\bar.patch')
954 >>> _buildfntemplate(b'\\\\foo\\bar.patch')
955 '\\\\\\\\foo\\\\bar.patch'
955 '\\\\\\\\foo\\\\bar.patch'
956 >>> _buildfntemplate(b'\\{tags % "{tag}"}')
956 >>> _buildfntemplate(b'\\{tags % "{tag}"}')
957 '\\\\{tags % "{tag}"}'
957 '\\\\{tags % "{tag}"}'
958
958
959 but inner strings follow the template rules (i.e. '\' is taken as an
959 but inner strings follow the template rules (i.e. '\' is taken as an
960 escape character):
960 escape character):
961
961
962 >>> _buildfntemplate(br'{"c:\tmp"}', seqno=0)
962 >>> _buildfntemplate(br'{"c:\tmp"}', seqno=0)
963 '{"c:\\tmp"}'
963 '{"c:\\tmp"}'
964 """
964 """
965 expander = {
965 expander = {
966 b'H': b'{node}',
966 b'H': b'{node}',
967 b'R': b'{rev}',
967 b'R': b'{rev}',
968 b'h': b'{node|short}',
968 b'h': b'{node|short}',
969 b'm': br'{sub(r"[^\w]", "_", desc|firstline)}',
969 b'm': br'{sub(r"[^\w]", "_", desc|firstline)}',
970 b'r': b'{if(revwidth, pad(rev, revwidth, "0", left=True), rev)}',
970 b'r': b'{if(revwidth, pad(rev, revwidth, "0", left=True), rev)}',
971 b'%': b'%',
971 b'%': b'%',
972 b'b': b'{reporoot|basename}',
972 b'b': b'{reporoot|basename}',
973 }
973 }
974 if total is not None:
974 if total is not None:
975 expander[b'N'] = b'{total}'
975 expander[b'N'] = b'{total}'
976 if seqno is not None:
976 if seqno is not None:
977 expander[b'n'] = b'{seqno}'
977 expander[b'n'] = b'{seqno}'
978 if total is not None and seqno is not None:
978 if total is not None and seqno is not None:
979 expander[b'n'] = b'{pad(seqno, total|stringify|count, "0", left=True)}'
979 expander[b'n'] = b'{pad(seqno, total|stringify|count, "0", left=True)}'
980 if pathname is not None:
980 if pathname is not None:
981 expander[b's'] = b'{pathname|basename}'
981 expander[b's'] = b'{pathname|basename}'
982 expander[b'd'] = b'{if(pathname|dirname, pathname|dirname, ".")}'
982 expander[b'd'] = b'{if(pathname|dirname, pathname|dirname, ".")}'
983 expander[b'p'] = b'{pathname}'
983 expander[b'p'] = b'{pathname}'
984
984
985 newname = []
985 newname = []
986 for typ, start, end in templater.scantemplate(pat, raw=True):
986 for typ, start, end in templater.scantemplate(pat, raw=True):
987 if typ != b'string':
987 if typ != b'string':
988 newname.append(pat[start:end])
988 newname.append(pat[start:end])
989 continue
989 continue
990 i = start
990 i = start
991 while i < end:
991 while i < end:
992 n = pat.find(b'%', i, end)
992 n = pat.find(b'%', i, end)
993 if n < 0:
993 if n < 0:
994 newname.append(stringutil.escapestr(pat[i:end]))
994 newname.append(stringutil.escapestr(pat[i:end]))
995 break
995 break
996 newname.append(stringutil.escapestr(pat[i:n]))
996 newname.append(stringutil.escapestr(pat[i:n]))
997 if n + 2 > end:
997 if n + 2 > end:
998 raise error.Abort(_("incomplete format spec in output "
998 raise error.Abort(_("incomplete format spec in output "
999 "filename"))
999 "filename"))
1000 c = pat[n + 1:n + 2]
1000 c = pat[n + 1:n + 2]
1001 i = n + 2
1001 i = n + 2
1002 try:
1002 try:
1003 newname.append(expander[c])
1003 newname.append(expander[c])
1004 except KeyError:
1004 except KeyError:
1005 raise error.Abort(_("invalid format spec '%%%s' in output "
1005 raise error.Abort(_("invalid format spec '%%%s' in output "
1006 "filename") % c)
1006 "filename") % c)
1007 return ''.join(newname)
1007 return ''.join(newname)
1008
1008
1009 def makefilename(ctx, pat, **props):
1009 def makefilename(ctx, pat, **props):
1010 if not pat:
1010 if not pat:
1011 return pat
1011 return pat
1012 tmpl = _buildfntemplate(pat, **props)
1012 tmpl = _buildfntemplate(pat, **props)
1013 # BUG: alias expansion shouldn't be made against template fragments
1013 # BUG: alias expansion shouldn't be made against template fragments
1014 # rewritten from %-format strings, but we have no easy way to partially
1014 # rewritten from %-format strings, but we have no easy way to partially
1015 # disable the expansion.
1015 # disable the expansion.
1016 return rendertemplate(ctx, tmpl, pycompat.byteskwargs(props))
1016 return rendertemplate(ctx, tmpl, pycompat.byteskwargs(props))
1017
1017
1018 def isstdiofilename(pat):
1018 def isstdiofilename(pat):
1019 """True if the given pat looks like a filename denoting stdin/stdout"""
1019 """True if the given pat looks like a filename denoting stdin/stdout"""
1020 return not pat or pat == '-'
1020 return not pat or pat == '-'
1021
1021
1022 class _unclosablefile(object):
1022 class _unclosablefile(object):
1023 def __init__(self, fp):
1023 def __init__(self, fp):
1024 self._fp = fp
1024 self._fp = fp
1025
1025
1026 def close(self):
1026 def close(self):
1027 pass
1027 pass
1028
1028
1029 def __iter__(self):
1029 def __iter__(self):
1030 return iter(self._fp)
1030 return iter(self._fp)
1031
1031
1032 def __getattr__(self, attr):
1032 def __getattr__(self, attr):
1033 return getattr(self._fp, attr)
1033 return getattr(self._fp, attr)
1034
1034
1035 def __enter__(self):
1035 def __enter__(self):
1036 return self
1036 return self
1037
1037
1038 def __exit__(self, exc_type, exc_value, exc_tb):
1038 def __exit__(self, exc_type, exc_value, exc_tb):
1039 pass
1039 pass
1040
1040
1041 def makefileobj(ctx, pat, mode='wb', **props):
1041 def makefileobj(ctx, pat, mode='wb', **props):
1042 writable = mode not in ('r', 'rb')
1042 writable = mode not in ('r', 'rb')
1043
1043
1044 if isstdiofilename(pat):
1044 if isstdiofilename(pat):
1045 repo = ctx.repo()
1045 repo = ctx.repo()
1046 if writable:
1046 if writable:
1047 fp = repo.ui.fout
1047 fp = repo.ui.fout
1048 else:
1048 else:
1049 fp = repo.ui.fin
1049 fp = repo.ui.fin
1050 return _unclosablefile(fp)
1050 return _unclosablefile(fp)
1051 fn = makefilename(ctx, pat, **props)
1051 fn = makefilename(ctx, pat, **props)
1052 return open(fn, mode)
1052 return open(fn, mode)
1053
1053
1054 def openstorage(repo, cmd, file_, opts, returnrevlog=False):
1054 def openstorage(repo, cmd, file_, opts, returnrevlog=False):
1055 """opens the changelog, manifest, a filelog or a given revlog"""
1055 """opens the changelog, manifest, a filelog or a given revlog"""
1056 cl = opts['changelog']
1056 cl = opts['changelog']
1057 mf = opts['manifest']
1057 mf = opts['manifest']
1058 dir = opts['dir']
1058 dir = opts['dir']
1059 msg = None
1059 msg = None
1060 if cl and mf:
1060 if cl and mf:
1061 msg = _('cannot specify --changelog and --manifest at the same time')
1061 msg = _('cannot specify --changelog and --manifest at the same time')
1062 elif cl and dir:
1062 elif cl and dir:
1063 msg = _('cannot specify --changelog and --dir at the same time')
1063 msg = _('cannot specify --changelog and --dir at the same time')
1064 elif cl or mf or dir:
1064 elif cl or mf or dir:
1065 if file_:
1065 if file_:
1066 msg = _('cannot specify filename with --changelog or --manifest')
1066 msg = _('cannot specify filename with --changelog or --manifest')
1067 elif not repo:
1067 elif not repo:
1068 msg = _('cannot specify --changelog or --manifest or --dir '
1068 msg = _('cannot specify --changelog or --manifest or --dir '
1069 'without a repository')
1069 'without a repository')
1070 if msg:
1070 if msg:
1071 raise error.Abort(msg)
1071 raise error.Abort(msg)
1072
1072
1073 r = None
1073 r = None
1074 if repo:
1074 if repo:
1075 if cl:
1075 if cl:
1076 r = repo.unfiltered().changelog
1076 r = repo.unfiltered().changelog
1077 elif dir:
1077 elif dir:
1078 if 'treemanifest' not in repo.requirements:
1078 if 'treemanifest' not in repo.requirements:
1079 raise error.Abort(_("--dir can only be used on repos with "
1079 raise error.Abort(_("--dir can only be used on repos with "
1080 "treemanifest enabled"))
1080 "treemanifest enabled"))
1081 if not dir.endswith('/'):
1081 if not dir.endswith('/'):
1082 dir = dir + '/'
1082 dir = dir + '/'
1083 dirlog = repo.manifestlog.getstorage(dir)
1083 dirlog = repo.manifestlog.getstorage(dir)
1084 if len(dirlog):
1084 if len(dirlog):
1085 r = dirlog
1085 r = dirlog
1086 elif mf:
1086 elif mf:
1087 r = repo.manifestlog.getstorage(b'')
1087 r = repo.manifestlog.getstorage(b'')
1088 elif file_:
1088 elif file_:
1089 filelog = repo.file(file_)
1089 filelog = repo.file(file_)
1090 if len(filelog):
1090 if len(filelog):
1091 r = filelog
1091 r = filelog
1092
1092
1093 # Not all storage may be revlogs. If requested, try to return an actual
1093 # Not all storage may be revlogs. If requested, try to return an actual
1094 # revlog instance.
1094 # revlog instance.
1095 if returnrevlog:
1095 if returnrevlog:
1096 if isinstance(r, revlog.revlog):
1096 if isinstance(r, revlog.revlog):
1097 pass
1097 pass
1098 elif util.safehasattr(r, '_revlog'):
1098 elif util.safehasattr(r, '_revlog'):
1099 r = r._revlog
1099 r = r._revlog
1100 elif r is not None:
1100 elif r is not None:
1101 raise error.Abort(_('%r does not appear to be a revlog') % r)
1101 raise error.Abort(_('%r does not appear to be a revlog') % r)
1102
1102
1103 if not r:
1103 if not r:
1104 if not returnrevlog:
1104 if not returnrevlog:
1105 raise error.Abort(_('cannot give path to non-revlog'))
1105 raise error.Abort(_('cannot give path to non-revlog'))
1106
1106
1107 if not file_:
1107 if not file_:
1108 raise error.CommandError(cmd, _('invalid arguments'))
1108 raise error.CommandError(cmd, _('invalid arguments'))
1109 if not os.path.isfile(file_):
1109 if not os.path.isfile(file_):
1110 raise error.Abort(_("revlog '%s' not found") % file_)
1110 raise error.Abort(_("revlog '%s' not found") % file_)
1111 r = revlog.revlog(vfsmod.vfs(encoding.getcwd(), audit=False),
1111 r = revlog.revlog(vfsmod.vfs(encoding.getcwd(), audit=False),
1112 file_[:-2] + ".i")
1112 file_[:-2] + ".i")
1113 return r
1113 return r
1114
1114
1115 def openrevlog(repo, cmd, file_, opts):
1115 def openrevlog(repo, cmd, file_, opts):
1116 """Obtain a revlog backing storage of an item.
1116 """Obtain a revlog backing storage of an item.
1117
1117
1118 This is similar to ``openstorage()`` except it always returns a revlog.
1118 This is similar to ``openstorage()`` except it always returns a revlog.
1119
1119
1120 In most cases, a caller cares about the main storage object - not the
1120 In most cases, a caller cares about the main storage object - not the
1121 revlog backing it. Therefore, this function should only be used by code
1121 revlog backing it. Therefore, this function should only be used by code
1122 that needs to examine low-level revlog implementation details. e.g. debug
1122 that needs to examine low-level revlog implementation details. e.g. debug
1123 commands.
1123 commands.
1124 """
1124 """
1125 return openstorage(repo, cmd, file_, opts, returnrevlog=True)
1125 return openstorage(repo, cmd, file_, opts, returnrevlog=True)
1126
1126
1127 def copy(ui, repo, pats, opts, rename=False):
1127 def copy(ui, repo, pats, opts, rename=False):
1128 # called with the repo lock held
1128 # called with the repo lock held
1129 #
1129 #
1130 # hgsep => pathname that uses "/" to separate directories
1130 # hgsep => pathname that uses "/" to separate directories
1131 # ossep => pathname that uses os.sep to separate directories
1131 # ossep => pathname that uses os.sep to separate directories
1132 cwd = repo.getcwd()
1132 cwd = repo.getcwd()
1133 targets = {}
1133 targets = {}
1134 after = opts.get("after")
1134 after = opts.get("after")
1135 dryrun = opts.get("dry_run")
1135 dryrun = opts.get("dry_run")
1136 wctx = repo[None]
1136 wctx = repo[None]
1137
1137
1138 def walkpat(pat):
1138 def walkpat(pat):
1139 srcs = []
1139 srcs = []
1140 if after:
1140 if after:
1141 badstates = '?'
1141 badstates = '?'
1142 else:
1142 else:
1143 badstates = '?r'
1143 badstates = '?r'
1144 m = scmutil.match(wctx, [pat], opts, globbed=True)
1144 m = scmutil.match(wctx, [pat], opts, globbed=True)
1145 for abs in wctx.walk(m):
1145 for abs in wctx.walk(m):
1146 state = repo.dirstate[abs]
1146 state = repo.dirstate[abs]
1147 rel = m.rel(abs)
1147 rel = m.rel(abs)
1148 exact = m.exact(abs)
1148 exact = m.exact(abs)
1149 if state in badstates:
1149 if state in badstates:
1150 if exact and state == '?':
1150 if exact and state == '?':
1151 ui.warn(_('%s: not copying - file is not managed\n') % rel)
1151 ui.warn(_('%s: not copying - file is not managed\n') % rel)
1152 if exact and state == 'r':
1152 if exact and state == 'r':
1153 ui.warn(_('%s: not copying - file has been marked for'
1153 ui.warn(_('%s: not copying - file has been marked for'
1154 ' remove\n') % rel)
1154 ' remove\n') % rel)
1155 continue
1155 continue
1156 # abs: hgsep
1156 # abs: hgsep
1157 # rel: ossep
1157 # rel: ossep
1158 srcs.append((abs, rel, exact))
1158 srcs.append((abs, rel, exact))
1159 return srcs
1159 return srcs
1160
1160
1161 # abssrc: hgsep
1161 # abssrc: hgsep
1162 # relsrc: ossep
1162 # relsrc: ossep
1163 # otarget: ossep
1163 # otarget: ossep
1164 def copyfile(abssrc, relsrc, otarget, exact):
1164 def copyfile(abssrc, relsrc, otarget, exact):
1165 abstarget = pathutil.canonpath(repo.root, cwd, otarget)
1165 abstarget = pathutil.canonpath(repo.root, cwd, otarget)
1166 if '/' in abstarget:
1166 if '/' in abstarget:
1167 # We cannot normalize abstarget itself, this would prevent
1167 # We cannot normalize abstarget itself, this would prevent
1168 # case only renames, like a => A.
1168 # case only renames, like a => A.
1169 abspath, absname = abstarget.rsplit('/', 1)
1169 abspath, absname = abstarget.rsplit('/', 1)
1170 abstarget = repo.dirstate.normalize(abspath) + '/' + absname
1170 abstarget = repo.dirstate.normalize(abspath) + '/' + absname
1171 reltarget = repo.pathto(abstarget, cwd)
1171 reltarget = repo.pathto(abstarget, cwd)
1172 target = repo.wjoin(abstarget)
1172 target = repo.wjoin(abstarget)
1173 src = repo.wjoin(abssrc)
1173 src = repo.wjoin(abssrc)
1174 state = repo.dirstate[abstarget]
1174 state = repo.dirstate[abstarget]
1175
1175
1176 scmutil.checkportable(ui, abstarget)
1176 scmutil.checkportable(ui, abstarget)
1177
1177
1178 # check for collisions
1178 # check for collisions
1179 prevsrc = targets.get(abstarget)
1179 prevsrc = targets.get(abstarget)
1180 if prevsrc is not None:
1180 if prevsrc is not None:
1181 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
1181 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
1182 (reltarget, repo.pathto(abssrc, cwd),
1182 (reltarget, repo.pathto(abssrc, cwd),
1183 repo.pathto(prevsrc, cwd)))
1183 repo.pathto(prevsrc, cwd)))
1184 return True # report a failure
1184 return True # report a failure
1185
1185
1186 # check for overwrites
1186 # check for overwrites
1187 exists = os.path.lexists(target)
1187 exists = os.path.lexists(target)
1188 samefile = False
1188 samefile = False
1189 if exists and abssrc != abstarget:
1189 if exists and abssrc != abstarget:
1190 if (repo.dirstate.normalize(abssrc) ==
1190 if (repo.dirstate.normalize(abssrc) ==
1191 repo.dirstate.normalize(abstarget)):
1191 repo.dirstate.normalize(abstarget)):
1192 if not rename:
1192 if not rename:
1193 ui.warn(_("%s: can't copy - same file\n") % reltarget)
1193 ui.warn(_("%s: can't copy - same file\n") % reltarget)
1194 return True # report a failure
1194 return True # report a failure
1195 exists = False
1195 exists = False
1196 samefile = True
1196 samefile = True
1197
1197
1198 if not after and exists or after and state in 'mn':
1198 if not after and exists or after and state in 'mn':
1199 if not opts['force']:
1199 if not opts['force']:
1200 if state in 'mn':
1200 if state in 'mn':
1201 msg = _('%s: not overwriting - file already committed\n')
1201 msg = _('%s: not overwriting - file already committed\n')
1202 if after:
1202 if after:
1203 flags = '--after --force'
1203 flags = '--after --force'
1204 else:
1204 else:
1205 flags = '--force'
1205 flags = '--force'
1206 if rename:
1206 if rename:
1207 hint = _("('hg rename %s' to replace the file by "
1207 hint = _("('hg rename %s' to replace the file by "
1208 'recording a rename)\n') % flags
1208 'recording a rename)\n') % flags
1209 else:
1209 else:
1210 hint = _("('hg copy %s' to replace the file by "
1210 hint = _("('hg copy %s' to replace the file by "
1211 'recording a copy)\n') % flags
1211 'recording a copy)\n') % flags
1212 else:
1212 else:
1213 msg = _('%s: not overwriting - file exists\n')
1213 msg = _('%s: not overwriting - file exists\n')
1214 if rename:
1214 if rename:
1215 hint = _("('hg rename --after' to record the rename)\n")
1215 hint = _("('hg rename --after' to record the rename)\n")
1216 else:
1216 else:
1217 hint = _("('hg copy --after' to record the copy)\n")
1217 hint = _("('hg copy --after' to record the copy)\n")
1218 ui.warn(msg % reltarget)
1218 ui.warn(msg % reltarget)
1219 ui.warn(hint)
1219 ui.warn(hint)
1220 return True # report a failure
1220 return True # report a failure
1221
1221
1222 if after:
1222 if after:
1223 if not exists:
1223 if not exists:
1224 if rename:
1224 if rename:
1225 ui.warn(_('%s: not recording move - %s does not exist\n') %
1225 ui.warn(_('%s: not recording move - %s does not exist\n') %
1226 (relsrc, reltarget))
1226 (relsrc, reltarget))
1227 else:
1227 else:
1228 ui.warn(_('%s: not recording copy - %s does not exist\n') %
1228 ui.warn(_('%s: not recording copy - %s does not exist\n') %
1229 (relsrc, reltarget))
1229 (relsrc, reltarget))
1230 return True # report a failure
1230 return True # report a failure
1231 elif not dryrun:
1231 elif not dryrun:
1232 try:
1232 try:
1233 if exists:
1233 if exists:
1234 os.unlink(target)
1234 os.unlink(target)
1235 targetdir = os.path.dirname(target) or '.'
1235 targetdir = os.path.dirname(target) or '.'
1236 if not os.path.isdir(targetdir):
1236 if not os.path.isdir(targetdir):
1237 os.makedirs(targetdir)
1237 os.makedirs(targetdir)
1238 if samefile:
1238 if samefile:
1239 tmp = target + "~hgrename"
1239 tmp = target + "~hgrename"
1240 os.rename(src, tmp)
1240 os.rename(src, tmp)
1241 os.rename(tmp, target)
1241 os.rename(tmp, target)
1242 else:
1242 else:
1243 # Preserve stat info on renames, not on copies; this matches
1243 # Preserve stat info on renames, not on copies; this matches
1244 # Linux CLI behavior.
1244 # Linux CLI behavior.
1245 util.copyfile(src, target, copystat=rename)
1245 util.copyfile(src, target, copystat=rename)
1246 srcexists = True
1246 srcexists = True
1247 except IOError as inst:
1247 except IOError as inst:
1248 if inst.errno == errno.ENOENT:
1248 if inst.errno == errno.ENOENT:
1249 ui.warn(_('%s: deleted in working directory\n') % relsrc)
1249 ui.warn(_('%s: deleted in working directory\n') % relsrc)
1250 srcexists = False
1250 srcexists = False
1251 else:
1251 else:
1252 ui.warn(_('%s: cannot copy - %s\n') %
1252 ui.warn(_('%s: cannot copy - %s\n') %
1253 (relsrc, encoding.strtolocal(inst.strerror)))
1253 (relsrc, encoding.strtolocal(inst.strerror)))
1254 return True # report a failure
1254 return True # report a failure
1255
1255
1256 if ui.verbose or not exact:
1256 if ui.verbose or not exact:
1257 if rename:
1257 if rename:
1258 ui.status(_('moving %s to %s\n') % (relsrc, reltarget))
1258 ui.status(_('moving %s to %s\n') % (relsrc, reltarget))
1259 else:
1259 else:
1260 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
1260 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
1261
1261
1262 targets[abstarget] = abssrc
1262 targets[abstarget] = abssrc
1263
1263
1264 # fix up dirstate
1264 # fix up dirstate
1265 scmutil.dirstatecopy(ui, repo, wctx, abssrc, abstarget,
1265 scmutil.dirstatecopy(ui, repo, wctx, abssrc, abstarget,
1266 dryrun=dryrun, cwd=cwd)
1266 dryrun=dryrun, cwd=cwd)
1267 if rename and not dryrun:
1267 if rename and not dryrun:
1268 if not after and srcexists and not samefile:
1268 if not after and srcexists and not samefile:
1269 rmdir = repo.ui.configbool('experimental', 'removeemptydirs')
1269 rmdir = repo.ui.configbool('experimental', 'removeemptydirs')
1270 repo.wvfs.unlinkpath(abssrc, rmdir=rmdir)
1270 repo.wvfs.unlinkpath(abssrc, rmdir=rmdir)
1271 wctx.forget([abssrc])
1271 wctx.forget([abssrc])
1272
1272
1273 # pat: ossep
1273 # pat: ossep
1274 # dest ossep
1274 # dest ossep
1275 # srcs: list of (hgsep, hgsep, ossep, bool)
1275 # srcs: list of (hgsep, hgsep, ossep, bool)
1276 # return: function that takes hgsep and returns ossep
1276 # return: function that takes hgsep and returns ossep
1277 def targetpathfn(pat, dest, srcs):
1277 def targetpathfn(pat, dest, srcs):
1278 if os.path.isdir(pat):
1278 if os.path.isdir(pat):
1279 abspfx = pathutil.canonpath(repo.root, cwd, pat)
1279 abspfx = pathutil.canonpath(repo.root, cwd, pat)
1280 abspfx = util.localpath(abspfx)
1280 abspfx = util.localpath(abspfx)
1281 if destdirexists:
1281 if destdirexists:
1282 striplen = len(os.path.split(abspfx)[0])
1282 striplen = len(os.path.split(abspfx)[0])
1283 else:
1283 else:
1284 striplen = len(abspfx)
1284 striplen = len(abspfx)
1285 if striplen:
1285 if striplen:
1286 striplen += len(pycompat.ossep)
1286 striplen += len(pycompat.ossep)
1287 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
1287 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
1288 elif destdirexists:
1288 elif destdirexists:
1289 res = lambda p: os.path.join(dest,
1289 res = lambda p: os.path.join(dest,
1290 os.path.basename(util.localpath(p)))
1290 os.path.basename(util.localpath(p)))
1291 else:
1291 else:
1292 res = lambda p: dest
1292 res = lambda p: dest
1293 return res
1293 return res
1294
1294
1295 # pat: ossep
1295 # pat: ossep
1296 # dest ossep
1296 # dest ossep
1297 # srcs: list of (hgsep, hgsep, ossep, bool)
1297 # srcs: list of (hgsep, hgsep, ossep, bool)
1298 # return: function that takes hgsep and returns ossep
1298 # return: function that takes hgsep and returns ossep
1299 def targetpathafterfn(pat, dest, srcs):
1299 def targetpathafterfn(pat, dest, srcs):
1300 if matchmod.patkind(pat):
1300 if matchmod.patkind(pat):
1301 # a mercurial pattern
1301 # a mercurial pattern
1302 res = lambda p: os.path.join(dest,
1302 res = lambda p: os.path.join(dest,
1303 os.path.basename(util.localpath(p)))
1303 os.path.basename(util.localpath(p)))
1304 else:
1304 else:
1305 abspfx = pathutil.canonpath(repo.root, cwd, pat)
1305 abspfx = pathutil.canonpath(repo.root, cwd, pat)
1306 if len(abspfx) < len(srcs[0][0]):
1306 if len(abspfx) < len(srcs[0][0]):
1307 # A directory. Either the target path contains the last
1307 # A directory. Either the target path contains the last
1308 # component of the source path or it does not.
1308 # component of the source path or it does not.
1309 def evalpath(striplen):
1309 def evalpath(striplen):
1310 score = 0
1310 score = 0
1311 for s in srcs:
1311 for s in srcs:
1312 t = os.path.join(dest, util.localpath(s[0])[striplen:])
1312 t = os.path.join(dest, util.localpath(s[0])[striplen:])
1313 if os.path.lexists(t):
1313 if os.path.lexists(t):
1314 score += 1
1314 score += 1
1315 return score
1315 return score
1316
1316
1317 abspfx = util.localpath(abspfx)
1317 abspfx = util.localpath(abspfx)
1318 striplen = len(abspfx)
1318 striplen = len(abspfx)
1319 if striplen:
1319 if striplen:
1320 striplen += len(pycompat.ossep)
1320 striplen += len(pycompat.ossep)
1321 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
1321 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
1322 score = evalpath(striplen)
1322 score = evalpath(striplen)
1323 striplen1 = len(os.path.split(abspfx)[0])
1323 striplen1 = len(os.path.split(abspfx)[0])
1324 if striplen1:
1324 if striplen1:
1325 striplen1 += len(pycompat.ossep)
1325 striplen1 += len(pycompat.ossep)
1326 if evalpath(striplen1) > score:
1326 if evalpath(striplen1) > score:
1327 striplen = striplen1
1327 striplen = striplen1
1328 res = lambda p: os.path.join(dest,
1328 res = lambda p: os.path.join(dest,
1329 util.localpath(p)[striplen:])
1329 util.localpath(p)[striplen:])
1330 else:
1330 else:
1331 # a file
1331 # a file
1332 if destdirexists:
1332 if destdirexists:
1333 res = lambda p: os.path.join(dest,
1333 res = lambda p: os.path.join(dest,
1334 os.path.basename(util.localpath(p)))
1334 os.path.basename(util.localpath(p)))
1335 else:
1335 else:
1336 res = lambda p: dest
1336 res = lambda p: dest
1337 return res
1337 return res
1338
1338
1339 pats = scmutil.expandpats(pats)
1339 pats = scmutil.expandpats(pats)
1340 if not pats:
1340 if not pats:
1341 raise error.Abort(_('no source or destination specified'))
1341 raise error.Abort(_('no source or destination specified'))
1342 if len(pats) == 1:
1342 if len(pats) == 1:
1343 raise error.Abort(_('no destination specified'))
1343 raise error.Abort(_('no destination specified'))
1344 dest = pats.pop()
1344 dest = pats.pop()
1345 destdirexists = os.path.isdir(dest) and not os.path.islink(dest)
1345 destdirexists = os.path.isdir(dest) and not os.path.islink(dest)
1346 if not destdirexists:
1346 if not destdirexists:
1347 if len(pats) > 1 or matchmod.patkind(pats[0]):
1347 if len(pats) > 1 or matchmod.patkind(pats[0]):
1348 raise error.Abort(_('with multiple sources, destination must be an '
1348 raise error.Abort(_('with multiple sources, destination must be an '
1349 'existing directory'))
1349 'existing directory'))
1350 if util.endswithsep(dest):
1350 if util.endswithsep(dest):
1351 raise error.Abort(_('destination %s is not a directory') % dest)
1351 raise error.Abort(_('destination %s is not a directory') % dest)
1352
1352
1353 tfn = targetpathfn
1353 tfn = targetpathfn
1354 if after:
1354 if after:
1355 tfn = targetpathafterfn
1355 tfn = targetpathafterfn
1356 copylist = []
1356 copylist = []
1357 for pat in pats:
1357 for pat in pats:
1358 srcs = walkpat(pat)
1358 srcs = walkpat(pat)
1359 if not srcs:
1359 if not srcs:
1360 continue
1360 continue
1361 copylist.append((tfn(pat, dest, srcs), srcs))
1361 copylist.append((tfn(pat, dest, srcs), srcs))
1362 if not copylist:
1362 if not copylist:
1363 raise error.Abort(_('no files to copy'))
1363 raise error.Abort(_('no files to copy'))
1364
1364
1365 errors = 0
1365 errors = 0
1366 for targetpath, srcs in copylist:
1366 for targetpath, srcs in copylist:
1367 for abssrc, relsrc, exact in srcs:
1367 for abssrc, relsrc, exact in srcs:
1368 if copyfile(abssrc, relsrc, targetpath(abssrc), exact):
1368 if copyfile(abssrc, relsrc, targetpath(abssrc), exact):
1369 errors += 1
1369 errors += 1
1370
1370
1371 return errors != 0
1371 return errors != 0
1372
1372
1373 ## facility to let extension process additional data into an import patch
1373 ## facility to let extension process additional data into an import patch
1374 # list of identifier to be executed in order
1374 # list of identifier to be executed in order
1375 extrapreimport = [] # run before commit
1375 extrapreimport = [] # run before commit
1376 extrapostimport = [] # run after commit
1376 extrapostimport = [] # run after commit
1377 # mapping from identifier to actual import function
1377 # mapping from identifier to actual import function
1378 #
1378 #
1379 # 'preimport' are run before the commit is made and are provided the following
1379 # 'preimport' are run before the commit is made and are provided the following
1380 # arguments:
1380 # arguments:
1381 # - repo: the localrepository instance,
1381 # - repo: the localrepository instance,
1382 # - patchdata: data extracted from patch header (cf m.patch.patchheadermap),
1382 # - patchdata: data extracted from patch header (cf m.patch.patchheadermap),
1383 # - extra: the future extra dictionary of the changeset, please mutate it,
1383 # - extra: the future extra dictionary of the changeset, please mutate it,
1384 # - opts: the import options.
1384 # - opts: the import options.
1385 # XXX ideally, we would just pass an ctx ready to be computed, that would allow
1385 # XXX ideally, we would just pass an ctx ready to be computed, that would allow
1386 # mutation of in memory commit and more. Feel free to rework the code to get
1386 # mutation of in memory commit and more. Feel free to rework the code to get
1387 # there.
1387 # there.
1388 extrapreimportmap = {}
1388 extrapreimportmap = {}
1389 # 'postimport' are run after the commit is made and are provided the following
1389 # 'postimport' are run after the commit is made and are provided the following
1390 # argument:
1390 # argument:
1391 # - ctx: the changectx created by import.
1391 # - ctx: the changectx created by import.
1392 extrapostimportmap = {}
1392 extrapostimportmap = {}
1393
1393
1394 def tryimportone(ui, repo, patchdata, parents, opts, msgs, updatefunc):
1394 def tryimportone(ui, repo, patchdata, parents, opts, msgs, updatefunc):
1395 """Utility function used by commands.import to import a single patch
1395 """Utility function used by commands.import to import a single patch
1396
1396
1397 This function is explicitly defined here to help the evolve extension to
1397 This function is explicitly defined here to help the evolve extension to
1398 wrap this part of the import logic.
1398 wrap this part of the import logic.
1399
1399
1400 The API is currently a bit ugly because it a simple code translation from
1400 The API is currently a bit ugly because it a simple code translation from
1401 the import command. Feel free to make it better.
1401 the import command. Feel free to make it better.
1402
1402
1403 :patchdata: a dictionary containing parsed patch data (such as from
1403 :patchdata: a dictionary containing parsed patch data (such as from
1404 ``patch.extract()``)
1404 ``patch.extract()``)
1405 :parents: nodes that will be parent of the created commit
1405 :parents: nodes that will be parent of the created commit
1406 :opts: the full dict of option passed to the import command
1406 :opts: the full dict of option passed to the import command
1407 :msgs: list to save commit message to.
1407 :msgs: list to save commit message to.
1408 (used in case we need to save it when failing)
1408 (used in case we need to save it when failing)
1409 :updatefunc: a function that update a repo to a given node
1409 :updatefunc: a function that update a repo to a given node
1410 updatefunc(<repo>, <node>)
1410 updatefunc(<repo>, <node>)
1411 """
1411 """
1412 # avoid cycle context -> subrepo -> cmdutil
1412 # avoid cycle context -> subrepo -> cmdutil
1413 from . import context
1413 from . import context
1414
1414
1415 tmpname = patchdata.get('filename')
1415 tmpname = patchdata.get('filename')
1416 message = patchdata.get('message')
1416 message = patchdata.get('message')
1417 user = opts.get('user') or patchdata.get('user')
1417 user = opts.get('user') or patchdata.get('user')
1418 date = opts.get('date') or patchdata.get('date')
1418 date = opts.get('date') or patchdata.get('date')
1419 branch = patchdata.get('branch')
1419 branch = patchdata.get('branch')
1420 nodeid = patchdata.get('nodeid')
1420 nodeid = patchdata.get('nodeid')
1421 p1 = patchdata.get('p1')
1421 p1 = patchdata.get('p1')
1422 p2 = patchdata.get('p2')
1422 p2 = patchdata.get('p2')
1423
1423
1424 nocommit = opts.get('no_commit')
1424 nocommit = opts.get('no_commit')
1425 importbranch = opts.get('import_branch')
1425 importbranch = opts.get('import_branch')
1426 update = not opts.get('bypass')
1426 update = not opts.get('bypass')
1427 strip = opts["strip"]
1427 strip = opts["strip"]
1428 prefix = opts["prefix"]
1428 prefix = opts["prefix"]
1429 sim = float(opts.get('similarity') or 0)
1429 sim = float(opts.get('similarity') or 0)
1430
1430
1431 if not tmpname:
1431 if not tmpname:
1432 return None, None, False
1432 return None, None, False
1433
1433
1434 rejects = False
1434 rejects = False
1435
1435
1436 cmdline_message = logmessage(ui, opts)
1436 cmdline_message = logmessage(ui, opts)
1437 if cmdline_message:
1437 if cmdline_message:
1438 # pickup the cmdline msg
1438 # pickup the cmdline msg
1439 message = cmdline_message
1439 message = cmdline_message
1440 elif message:
1440 elif message:
1441 # pickup the patch msg
1441 # pickup the patch msg
1442 message = message.strip()
1442 message = message.strip()
1443 else:
1443 else:
1444 # launch the editor
1444 # launch the editor
1445 message = None
1445 message = None
1446 ui.debug('message:\n%s\n' % (message or ''))
1446 ui.debug('message:\n%s\n' % (message or ''))
1447
1447
1448 if len(parents) == 1:
1448 if len(parents) == 1:
1449 parents.append(repo[nullid])
1449 parents.append(repo[nullid])
1450 if opts.get('exact'):
1450 if opts.get('exact'):
1451 if not nodeid or not p1:
1451 if not nodeid or not p1:
1452 raise error.Abort(_('not a Mercurial patch'))
1452 raise error.Abort(_('not a Mercurial patch'))
1453 p1 = repo[p1]
1453 p1 = repo[p1]
1454 p2 = repo[p2 or nullid]
1454 p2 = repo[p2 or nullid]
1455 elif p2:
1455 elif p2:
1456 try:
1456 try:
1457 p1 = repo[p1]
1457 p1 = repo[p1]
1458 p2 = repo[p2]
1458 p2 = repo[p2]
1459 # Without any options, consider p2 only if the
1459 # Without any options, consider p2 only if the
1460 # patch is being applied on top of the recorded
1460 # patch is being applied on top of the recorded
1461 # first parent.
1461 # first parent.
1462 if p1 != parents[0]:
1462 if p1 != parents[0]:
1463 p1 = parents[0]
1463 p1 = parents[0]
1464 p2 = repo[nullid]
1464 p2 = repo[nullid]
1465 except error.RepoError:
1465 except error.RepoError:
1466 p1, p2 = parents
1466 p1, p2 = parents
1467 if p2.node() == nullid:
1467 if p2.node() == nullid:
1468 ui.warn(_("warning: import the patch as a normal revision\n"
1468 ui.warn(_("warning: import the patch as a normal revision\n"
1469 "(use --exact to import the patch as a merge)\n"))
1469 "(use --exact to import the patch as a merge)\n"))
1470 else:
1470 else:
1471 p1, p2 = parents
1471 p1, p2 = parents
1472
1472
1473 n = None
1473 n = None
1474 if update:
1474 if update:
1475 if p1 != parents[0]:
1475 if p1 != parents[0]:
1476 updatefunc(repo, p1.node())
1476 updatefunc(repo, p1.node())
1477 if p2 != parents[1]:
1477 if p2 != parents[1]:
1478 repo.setparents(p1.node(), p2.node())
1478 repo.setparents(p1.node(), p2.node())
1479
1479
1480 if opts.get('exact') or importbranch:
1480 if opts.get('exact') or importbranch:
1481 repo.dirstate.setbranch(branch or 'default')
1481 repo.dirstate.setbranch(branch or 'default')
1482
1482
1483 partial = opts.get('partial', False)
1483 partial = opts.get('partial', False)
1484 files = set()
1484 files = set()
1485 try:
1485 try:
1486 patch.patch(ui, repo, tmpname, strip=strip, prefix=prefix,
1486 patch.patch(ui, repo, tmpname, strip=strip, prefix=prefix,
1487 files=files, eolmode=None, similarity=sim / 100.0)
1487 files=files, eolmode=None, similarity=sim / 100.0)
1488 except error.PatchError as e:
1488 except error.PatchError as e:
1489 if not partial:
1489 if not partial:
1490 raise error.Abort(pycompat.bytestr(e))
1490 raise error.Abort(pycompat.bytestr(e))
1491 if partial:
1491 if partial:
1492 rejects = True
1492 rejects = True
1493
1493
1494 files = list(files)
1494 files = list(files)
1495 if nocommit:
1495 if nocommit:
1496 if message:
1496 if message:
1497 msgs.append(message)
1497 msgs.append(message)
1498 else:
1498 else:
1499 if opts.get('exact') or p2:
1499 if opts.get('exact') or p2:
1500 # If you got here, you either use --force and know what
1500 # If you got here, you either use --force and know what
1501 # you are doing or used --exact or a merge patch while
1501 # you are doing or used --exact or a merge patch while
1502 # being updated to its first parent.
1502 # being updated to its first parent.
1503 m = None
1503 m = None
1504 else:
1504 else:
1505 m = scmutil.matchfiles(repo, files or [])
1505 m = scmutil.matchfiles(repo, files or [])
1506 editform = mergeeditform(repo[None], 'import.normal')
1506 editform = mergeeditform(repo[None], 'import.normal')
1507 if opts.get('exact'):
1507 if opts.get('exact'):
1508 editor = None
1508 editor = None
1509 else:
1509 else:
1510 editor = getcommiteditor(editform=editform,
1510 editor = getcommiteditor(editform=editform,
1511 **pycompat.strkwargs(opts))
1511 **pycompat.strkwargs(opts))
1512 extra = {}
1512 extra = {}
1513 for idfunc in extrapreimport:
1513 for idfunc in extrapreimport:
1514 extrapreimportmap[idfunc](repo, patchdata, extra, opts)
1514 extrapreimportmap[idfunc](repo, patchdata, extra, opts)
1515 overrides = {}
1515 overrides = {}
1516 if partial:
1516 if partial:
1517 overrides[('ui', 'allowemptycommit')] = True
1517 overrides[('ui', 'allowemptycommit')] = True
1518 with repo.ui.configoverride(overrides, 'import'):
1518 with repo.ui.configoverride(overrides, 'import'):
1519 n = repo.commit(message, user,
1519 n = repo.commit(message, user,
1520 date, match=m,
1520 date, match=m,
1521 editor=editor, extra=extra)
1521 editor=editor, extra=extra)
1522 for idfunc in extrapostimport:
1522 for idfunc in extrapostimport:
1523 extrapostimportmap[idfunc](repo[n])
1523 extrapostimportmap[idfunc](repo[n])
1524 else:
1524 else:
1525 if opts.get('exact') or importbranch:
1525 if opts.get('exact') or importbranch:
1526 branch = branch or 'default'
1526 branch = branch or 'default'
1527 else:
1527 else:
1528 branch = p1.branch()
1528 branch = p1.branch()
1529 store = patch.filestore()
1529 store = patch.filestore()
1530 try:
1530 try:
1531 files = set()
1531 files = set()
1532 try:
1532 try:
1533 patch.patchrepo(ui, repo, p1, store, tmpname, strip, prefix,
1533 patch.patchrepo(ui, repo, p1, store, tmpname, strip, prefix,
1534 files, eolmode=None)
1534 files, eolmode=None)
1535 except error.PatchError as e:
1535 except error.PatchError as e:
1536 raise error.Abort(stringutil.forcebytestr(e))
1536 raise error.Abort(stringutil.forcebytestr(e))
1537 if opts.get('exact'):
1537 if opts.get('exact'):
1538 editor = None
1538 editor = None
1539 else:
1539 else:
1540 editor = getcommiteditor(editform='import.bypass')
1540 editor = getcommiteditor(editform='import.bypass')
1541 memctx = context.memctx(repo, (p1.node(), p2.node()),
1541 memctx = context.memctx(repo, (p1.node(), p2.node()),
1542 message,
1542 message,
1543 files=files,
1543 files=files,
1544 filectxfn=store,
1544 filectxfn=store,
1545 user=user,
1545 user=user,
1546 date=date,
1546 date=date,
1547 branch=branch,
1547 branch=branch,
1548 editor=editor)
1548 editor=editor)
1549 n = memctx.commit()
1549 n = memctx.commit()
1550 finally:
1550 finally:
1551 store.close()
1551 store.close()
1552 if opts.get('exact') and nocommit:
1552 if opts.get('exact') and nocommit:
1553 # --exact with --no-commit is still useful in that it does merge
1553 # --exact with --no-commit is still useful in that it does merge
1554 # and branch bits
1554 # and branch bits
1555 ui.warn(_("warning: can't check exact import with --no-commit\n"))
1555 ui.warn(_("warning: can't check exact import with --no-commit\n"))
1556 elif opts.get('exact') and (not n or hex(n) != nodeid):
1556 elif opts.get('exact') and (not n or hex(n) != nodeid):
1557 raise error.Abort(_('patch is damaged or loses information'))
1557 raise error.Abort(_('patch is damaged or loses information'))
1558 msg = _('applied to working directory')
1558 msg = _('applied to working directory')
1559 if n:
1559 if n:
1560 # i18n: refers to a short changeset id
1560 # i18n: refers to a short changeset id
1561 msg = _('created %s') % short(n)
1561 msg = _('created %s') % short(n)
1562 return msg, n, rejects
1562 return msg, n, rejects
1563
1563
1564 # facility to let extensions include additional data in an exported patch
1564 # facility to let extensions include additional data in an exported patch
1565 # list of identifiers to be executed in order
1565 # list of identifiers to be executed in order
1566 extraexport = []
1566 extraexport = []
1567 # mapping from identifier to actual export function
1567 # mapping from identifier to actual export function
1568 # function as to return a string to be added to the header or None
1568 # function as to return a string to be added to the header or None
1569 # it is given two arguments (sequencenumber, changectx)
1569 # it is given two arguments (sequencenumber, changectx)
1570 extraexportmap = {}
1570 extraexportmap = {}
1571
1571
1572 def _exportsingle(repo, ctx, fm, match, switch_parent, seqno, diffopts):
1572 def _exportsingle(repo, ctx, fm, match, switch_parent, seqno, diffopts):
1573 node = scmutil.binnode(ctx)
1573 node = scmutil.binnode(ctx)
1574 parents = [p.node() for p in ctx.parents() if p]
1574 parents = [p.node() for p in ctx.parents() if p]
1575 branch = ctx.branch()
1575 branch = ctx.branch()
1576 if switch_parent:
1576 if switch_parent:
1577 parents.reverse()
1577 parents.reverse()
1578
1578
1579 if parents:
1579 if parents:
1580 prev = parents[0]
1580 prev = parents[0]
1581 else:
1581 else:
1582 prev = nullid
1582 prev = nullid
1583
1583
1584 fm.context(ctx=ctx)
1584 fm.context(ctx=ctx)
1585 fm.plain('# HG changeset patch\n')
1585 fm.plain('# HG changeset patch\n')
1586 fm.write('user', '# User %s\n', ctx.user())
1586 fm.write('user', '# User %s\n', ctx.user())
1587 fm.plain('# Date %d %d\n' % ctx.date())
1587 fm.plain('# Date %d %d\n' % ctx.date())
1588 fm.write('date', '# %s\n', fm.formatdate(ctx.date()))
1588 fm.write('date', '# %s\n', fm.formatdate(ctx.date()))
1589 fm.condwrite(branch and branch != 'default',
1589 fm.condwrite(branch and branch != 'default',
1590 'branch', '# Branch %s\n', branch)
1590 'branch', '# Branch %s\n', branch)
1591 fm.write('node', '# Node ID %s\n', hex(node))
1591 fm.write('node', '# Node ID %s\n', hex(node))
1592 fm.plain('# Parent %s\n' % hex(prev))
1592 fm.plain('# Parent %s\n' % hex(prev))
1593 if len(parents) > 1:
1593 if len(parents) > 1:
1594 fm.plain('# Parent %s\n' % hex(parents[1]))
1594 fm.plain('# Parent %s\n' % hex(parents[1]))
1595 fm.data(parents=fm.formatlist(pycompat.maplist(hex, parents), name='node'))
1595 fm.data(parents=fm.formatlist(pycompat.maplist(hex, parents), name='node'))
1596
1596
1597 # TODO: redesign extraexportmap function to support formatter
1597 # TODO: redesign extraexportmap function to support formatter
1598 for headerid in extraexport:
1598 for headerid in extraexport:
1599 header = extraexportmap[headerid](seqno, ctx)
1599 header = extraexportmap[headerid](seqno, ctx)
1600 if header is not None:
1600 if header is not None:
1601 fm.plain('# %s\n' % header)
1601 fm.plain('# %s\n' % header)
1602
1602
1603 fm.write('desc', '%s\n', ctx.description().rstrip())
1603 fm.write('desc', '%s\n', ctx.description().rstrip())
1604 fm.plain('\n')
1604 fm.plain('\n')
1605
1605
1606 if fm.isplain():
1606 if fm.isplain():
1607 chunkiter = patch.diffui(repo, prev, node, match, opts=diffopts)
1607 chunkiter = patch.diffui(repo, prev, node, match, opts=diffopts)
1608 for chunk, label in chunkiter:
1608 for chunk, label in chunkiter:
1609 fm.plain(chunk, label=label)
1609 fm.plain(chunk, label=label)
1610 else:
1610 else:
1611 chunkiter = patch.diff(repo, prev, node, match, opts=diffopts)
1611 chunkiter = patch.diff(repo, prev, node, match, opts=diffopts)
1612 # TODO: make it structured?
1612 # TODO: make it structured?
1613 fm.data(diff=b''.join(chunkiter))
1613 fm.data(diff=b''.join(chunkiter))
1614
1614
1615 def _exportfile(repo, revs, fm, dest, switch_parent, diffopts, match):
1615 def _exportfile(repo, revs, fm, dest, switch_parent, diffopts, match):
1616 """Export changesets to stdout or a single file"""
1616 """Export changesets to stdout or a single file"""
1617 for seqno, rev in enumerate(revs, 1):
1617 for seqno, rev in enumerate(revs, 1):
1618 ctx = repo[rev]
1618 ctx = repo[rev]
1619 if not dest.startswith('<'):
1619 if not dest.startswith('<'):
1620 repo.ui.note("%s\n" % dest)
1620 repo.ui.note("%s\n" % dest)
1621 fm.startitem()
1621 fm.startitem()
1622 _exportsingle(repo, ctx, fm, match, switch_parent, seqno, diffopts)
1622 _exportsingle(repo, ctx, fm, match, switch_parent, seqno, diffopts)
1623
1623
1624 def _exportfntemplate(repo, revs, basefm, fntemplate, switch_parent, diffopts,
1624 def _exportfntemplate(repo, revs, basefm, fntemplate, switch_parent, diffopts,
1625 match):
1625 match):
1626 """Export changesets to possibly multiple files"""
1626 """Export changesets to possibly multiple files"""
1627 total = len(revs)
1627 total = len(revs)
1628 revwidth = max(len(str(rev)) for rev in revs)
1628 revwidth = max(len(str(rev)) for rev in revs)
1629 filemap = util.sortdict() # filename: [(seqno, rev), ...]
1629 filemap = util.sortdict() # filename: [(seqno, rev), ...]
1630
1630
1631 for seqno, rev in enumerate(revs, 1):
1631 for seqno, rev in enumerate(revs, 1):
1632 ctx = repo[rev]
1632 ctx = repo[rev]
1633 dest = makefilename(ctx, fntemplate,
1633 dest = makefilename(ctx, fntemplate,
1634 total=total, seqno=seqno, revwidth=revwidth)
1634 total=total, seqno=seqno, revwidth=revwidth)
1635 filemap.setdefault(dest, []).append((seqno, rev))
1635 filemap.setdefault(dest, []).append((seqno, rev))
1636
1636
1637 for dest in filemap:
1637 for dest in filemap:
1638 with formatter.maybereopen(basefm, dest) as fm:
1638 with formatter.maybereopen(basefm, dest) as fm:
1639 repo.ui.note("%s\n" % dest)
1639 repo.ui.note("%s\n" % dest)
1640 for seqno, rev in filemap[dest]:
1640 for seqno, rev in filemap[dest]:
1641 fm.startitem()
1641 fm.startitem()
1642 ctx = repo[rev]
1642 ctx = repo[rev]
1643 _exportsingle(repo, ctx, fm, match, switch_parent, seqno,
1643 _exportsingle(repo, ctx, fm, match, switch_parent, seqno,
1644 diffopts)
1644 diffopts)
1645
1645
1646 def export(repo, revs, basefm, fntemplate='hg-%h.patch', switch_parent=False,
1646 def export(repo, revs, basefm, fntemplate='hg-%h.patch', switch_parent=False,
1647 opts=None, match=None):
1647 opts=None, match=None):
1648 '''export changesets as hg patches
1648 '''export changesets as hg patches
1649
1649
1650 Args:
1650 Args:
1651 repo: The repository from which we're exporting revisions.
1651 repo: The repository from which we're exporting revisions.
1652 revs: A list of revisions to export as revision numbers.
1652 revs: A list of revisions to export as revision numbers.
1653 basefm: A formatter to which patches should be written.
1653 basefm: A formatter to which patches should be written.
1654 fntemplate: An optional string to use for generating patch file names.
1654 fntemplate: An optional string to use for generating patch file names.
1655 switch_parent: If True, show diffs against second parent when not nullid.
1655 switch_parent: If True, show diffs against second parent when not nullid.
1656 Default is false, which always shows diff against p1.
1656 Default is false, which always shows diff against p1.
1657 opts: diff options to use for generating the patch.
1657 opts: diff options to use for generating the patch.
1658 match: If specified, only export changes to files matching this matcher.
1658 match: If specified, only export changes to files matching this matcher.
1659
1659
1660 Returns:
1660 Returns:
1661 Nothing.
1661 Nothing.
1662
1662
1663 Side Effect:
1663 Side Effect:
1664 "HG Changeset Patch" data is emitted to one of the following
1664 "HG Changeset Patch" data is emitted to one of the following
1665 destinations:
1665 destinations:
1666 fntemplate specified: Each rev is written to a unique file named using
1666 fntemplate specified: Each rev is written to a unique file named using
1667 the given template.
1667 the given template.
1668 Otherwise: All revs will be written to basefm.
1668 Otherwise: All revs will be written to basefm.
1669 '''
1669 '''
1670 scmutil.prefetchfiles(repo, revs, match)
1670 scmutil.prefetchfiles(repo, revs, match)
1671
1671
1672 if not fntemplate:
1672 if not fntemplate:
1673 _exportfile(repo, revs, basefm, '<unnamed>', switch_parent, opts, match)
1673 _exportfile(repo, revs, basefm, '<unnamed>', switch_parent, opts, match)
1674 else:
1674 else:
1675 _exportfntemplate(repo, revs, basefm, fntemplate, switch_parent, opts,
1675 _exportfntemplate(repo, revs, basefm, fntemplate, switch_parent, opts,
1676 match)
1676 match)
1677
1677
1678 def exportfile(repo, revs, fp, switch_parent=False, opts=None, match=None):
1678 def exportfile(repo, revs, fp, switch_parent=False, opts=None, match=None):
1679 """Export changesets to the given file stream"""
1679 """Export changesets to the given file stream"""
1680 scmutil.prefetchfiles(repo, revs, match)
1680 scmutil.prefetchfiles(repo, revs, match)
1681
1681
1682 dest = getattr(fp, 'name', '<unnamed>')
1682 dest = getattr(fp, 'name', '<unnamed>')
1683 with formatter.formatter(repo.ui, fp, 'export', {}) as fm:
1683 with formatter.formatter(repo.ui, fp, 'export', {}) as fm:
1684 _exportfile(repo, revs, fm, dest, switch_parent, opts, match)
1684 _exportfile(repo, revs, fm, dest, switch_parent, opts, match)
1685
1685
1686 def showmarker(fm, marker, index=None):
1686 def showmarker(fm, marker, index=None):
1687 """utility function to display obsolescence marker in a readable way
1687 """utility function to display obsolescence marker in a readable way
1688
1688
1689 To be used by debug function."""
1689 To be used by debug function."""
1690 if index is not None:
1690 if index is not None:
1691 fm.write('index', '%i ', index)
1691 fm.write('index', '%i ', index)
1692 fm.write('prednode', '%s ', hex(marker.prednode()))
1692 fm.write('prednode', '%s ', hex(marker.prednode()))
1693 succs = marker.succnodes()
1693 succs = marker.succnodes()
1694 fm.condwrite(succs, 'succnodes', '%s ',
1694 fm.condwrite(succs, 'succnodes', '%s ',
1695 fm.formatlist(map(hex, succs), name='node'))
1695 fm.formatlist(map(hex, succs), name='node'))
1696 fm.write('flag', '%X ', marker.flags())
1696 fm.write('flag', '%X ', marker.flags())
1697 parents = marker.parentnodes()
1697 parents = marker.parentnodes()
1698 if parents is not None:
1698 if parents is not None:
1699 fm.write('parentnodes', '{%s} ',
1699 fm.write('parentnodes', '{%s} ',
1700 fm.formatlist(map(hex, parents), name='node', sep=', '))
1700 fm.formatlist(map(hex, parents), name='node', sep=', '))
1701 fm.write('date', '(%s) ', fm.formatdate(marker.date()))
1701 fm.write('date', '(%s) ', fm.formatdate(marker.date()))
1702 meta = marker.metadata().copy()
1702 meta = marker.metadata().copy()
1703 meta.pop('date', None)
1703 meta.pop('date', None)
1704 smeta = pycompat.rapply(pycompat.maybebytestr, meta)
1704 smeta = pycompat.rapply(pycompat.maybebytestr, meta)
1705 fm.write('metadata', '{%s}', fm.formatdict(smeta, fmt='%r: %r', sep=', '))
1705 fm.write('metadata', '{%s}', fm.formatdict(smeta, fmt='%r: %r', sep=', '))
1706 fm.plain('\n')
1706 fm.plain('\n')
1707
1707
1708 def finddate(ui, repo, date):
1708 def finddate(ui, repo, date):
1709 """Find the tipmost changeset that matches the given date spec"""
1709 """Find the tipmost changeset that matches the given date spec"""
1710
1710
1711 df = dateutil.matchdate(date)
1711 df = dateutil.matchdate(date)
1712 m = scmutil.matchall(repo)
1712 m = scmutil.matchall(repo)
1713 results = {}
1713 results = {}
1714
1714
1715 def prep(ctx, fns):
1715 def prep(ctx, fns):
1716 d = ctx.date()
1716 d = ctx.date()
1717 if df(d[0]):
1717 if df(d[0]):
1718 results[ctx.rev()] = d
1718 results[ctx.rev()] = d
1719
1719
1720 for ctx in walkchangerevs(repo, m, {'rev': None}, prep):
1720 for ctx in walkchangerevs(repo, m, {'rev': None}, prep):
1721 rev = ctx.rev()
1721 rev = ctx.rev()
1722 if rev in results:
1722 if rev in results:
1723 ui.status(_("found revision %s from %s\n") %
1723 ui.status(_("found revision %s from %s\n") %
1724 (rev, dateutil.datestr(results[rev])))
1724 (rev, dateutil.datestr(results[rev])))
1725 return '%d' % rev
1725 return '%d' % rev
1726
1726
1727 raise error.Abort(_("revision matching date not found"))
1727 raise error.Abort(_("revision matching date not found"))
1728
1728
1729 def increasingwindows(windowsize=8, sizelimit=512):
1729 def increasingwindows(windowsize=8, sizelimit=512):
1730 while True:
1730 while True:
1731 yield windowsize
1731 yield windowsize
1732 if windowsize < sizelimit:
1732 if windowsize < sizelimit:
1733 windowsize *= 2
1733 windowsize *= 2
1734
1734
1735 def _walkrevs(repo, opts):
1735 def _walkrevs(repo, opts):
1736 # Default --rev value depends on --follow but --follow behavior
1736 # Default --rev value depends on --follow but --follow behavior
1737 # depends on revisions resolved from --rev...
1737 # depends on revisions resolved from --rev...
1738 follow = opts.get('follow') or opts.get('follow_first')
1738 follow = opts.get('follow') or opts.get('follow_first')
1739 if opts.get('rev'):
1739 if opts.get('rev'):
1740 revs = scmutil.revrange(repo, opts['rev'])
1740 revs = scmutil.revrange(repo, opts['rev'])
1741 elif follow and repo.dirstate.p1() == nullid:
1741 elif follow and repo.dirstate.p1() == nullid:
1742 revs = smartset.baseset()
1742 revs = smartset.baseset()
1743 elif follow:
1743 elif follow:
1744 revs = repo.revs('reverse(:.)')
1744 revs = repo.revs('reverse(:.)')
1745 else:
1745 else:
1746 revs = smartset.spanset(repo)
1746 revs = smartset.spanset(repo)
1747 revs.reverse()
1747 revs.reverse()
1748 return revs
1748 return revs
1749
1749
1750 class FileWalkError(Exception):
1750 class FileWalkError(Exception):
1751 pass
1751 pass
1752
1752
1753 def walkfilerevs(repo, match, follow, revs, fncache):
1753 def walkfilerevs(repo, match, follow, revs, fncache):
1754 '''Walks the file history for the matched files.
1754 '''Walks the file history for the matched files.
1755
1755
1756 Returns the changeset revs that are involved in the file history.
1756 Returns the changeset revs that are involved in the file history.
1757
1757
1758 Throws FileWalkError if the file history can't be walked using
1758 Throws FileWalkError if the file history can't be walked using
1759 filelogs alone.
1759 filelogs alone.
1760 '''
1760 '''
1761 wanted = set()
1761 wanted = set()
1762 copies = []
1762 copies = []
1763 minrev, maxrev = min(revs), max(revs)
1763 minrev, maxrev = min(revs), max(revs)
1764 def filerevgen(filelog, last):
1764 def filerevgen(filelog, last):
1765 """
1765 """
1766 Only files, no patterns. Check the history of each file.
1766 Only files, no patterns. Check the history of each file.
1767
1767
1768 Examines filelog entries within minrev, maxrev linkrev range
1768 Examines filelog entries within minrev, maxrev linkrev range
1769 Returns an iterator yielding (linkrev, parentlinkrevs, copied)
1769 Returns an iterator yielding (linkrev, parentlinkrevs, copied)
1770 tuples in backwards order
1770 tuples in backwards order
1771 """
1771 """
1772 cl_count = len(repo)
1772 cl_count = len(repo)
1773 revs = []
1773 revs = []
1774 for j in pycompat.xrange(0, last + 1):
1774 for j in pycompat.xrange(0, last + 1):
1775 linkrev = filelog.linkrev(j)
1775 linkrev = filelog.linkrev(j)
1776 if linkrev < minrev:
1776 if linkrev < minrev:
1777 continue
1777 continue
1778 # only yield rev for which we have the changelog, it can
1778 # only yield rev for which we have the changelog, it can
1779 # happen while doing "hg log" during a pull or commit
1779 # happen while doing "hg log" during a pull or commit
1780 if linkrev >= cl_count:
1780 if linkrev >= cl_count:
1781 break
1781 break
1782
1782
1783 parentlinkrevs = []
1783 parentlinkrevs = []
1784 for p in filelog.parentrevs(j):
1784 for p in filelog.parentrevs(j):
1785 if p != nullrev:
1785 if p != nullrev:
1786 parentlinkrevs.append(filelog.linkrev(p))
1786 parentlinkrevs.append(filelog.linkrev(p))
1787 n = filelog.node(j)
1787 n = filelog.node(j)
1788 revs.append((linkrev, parentlinkrevs,
1788 revs.append((linkrev, parentlinkrevs,
1789 follow and filelog.renamed(n)))
1789 follow and filelog.renamed(n)))
1790
1790
1791 return reversed(revs)
1791 return reversed(revs)
1792 def iterfiles():
1792 def iterfiles():
1793 pctx = repo['.']
1793 pctx = repo['.']
1794 for filename in match.files():
1794 for filename in match.files():
1795 if follow:
1795 if follow:
1796 if filename not in pctx:
1796 if filename not in pctx:
1797 raise error.Abort(_('cannot follow file not in parent '
1797 raise error.Abort(_('cannot follow file not in parent '
1798 'revision: "%s"') % filename)
1798 'revision: "%s"') % filename)
1799 yield filename, pctx[filename].filenode()
1799 yield filename, pctx[filename].filenode()
1800 else:
1800 else:
1801 yield filename, None
1801 yield filename, None
1802 for filename_node in copies:
1802 for filename_node in copies:
1803 yield filename_node
1803 yield filename_node
1804
1804
1805 for file_, node in iterfiles():
1805 for file_, node in iterfiles():
1806 filelog = repo.file(file_)
1806 filelog = repo.file(file_)
1807 if not len(filelog):
1807 if not len(filelog):
1808 if node is None:
1808 if node is None:
1809 # A zero count may be a directory or deleted file, so
1809 # A zero count may be a directory or deleted file, so
1810 # try to find matching entries on the slow path.
1810 # try to find matching entries on the slow path.
1811 if follow:
1811 if follow:
1812 raise error.Abort(
1812 raise error.Abort(
1813 _('cannot follow nonexistent file: "%s"') % file_)
1813 _('cannot follow nonexistent file: "%s"') % file_)
1814 raise FileWalkError("Cannot walk via filelog")
1814 raise FileWalkError("Cannot walk via filelog")
1815 else:
1815 else:
1816 continue
1816 continue
1817
1817
1818 if node is None:
1818 if node is None:
1819 last = len(filelog) - 1
1819 last = len(filelog) - 1
1820 else:
1820 else:
1821 last = filelog.rev(node)
1821 last = filelog.rev(node)
1822
1822
1823 # keep track of all ancestors of the file
1823 # keep track of all ancestors of the file
1824 ancestors = {filelog.linkrev(last)}
1824 ancestors = {filelog.linkrev(last)}
1825
1825
1826 # iterate from latest to oldest revision
1826 # iterate from latest to oldest revision
1827 for rev, flparentlinkrevs, copied in filerevgen(filelog, last):
1827 for rev, flparentlinkrevs, copied in filerevgen(filelog, last):
1828 if not follow:
1828 if not follow:
1829 if rev > maxrev:
1829 if rev > maxrev:
1830 continue
1830 continue
1831 else:
1831 else:
1832 # Note that last might not be the first interesting
1832 # Note that last might not be the first interesting
1833 # rev to us:
1833 # rev to us:
1834 # if the file has been changed after maxrev, we'll
1834 # if the file has been changed after maxrev, we'll
1835 # have linkrev(last) > maxrev, and we still need
1835 # have linkrev(last) > maxrev, and we still need
1836 # to explore the file graph
1836 # to explore the file graph
1837 if rev not in ancestors:
1837 if rev not in ancestors:
1838 continue
1838 continue
1839 # XXX insert 1327 fix here
1839 # XXX insert 1327 fix here
1840 if flparentlinkrevs:
1840 if flparentlinkrevs:
1841 ancestors.update(flparentlinkrevs)
1841 ancestors.update(flparentlinkrevs)
1842
1842
1843 fncache.setdefault(rev, []).append(file_)
1843 fncache.setdefault(rev, []).append(file_)
1844 wanted.add(rev)
1844 wanted.add(rev)
1845 if copied:
1845 if copied:
1846 copies.append(copied)
1846 copies.append(copied)
1847
1847
1848 return wanted
1848 return wanted
1849
1849
1850 class _followfilter(object):
1850 class _followfilter(object):
1851 def __init__(self, repo, onlyfirst=False):
1851 def __init__(self, repo, onlyfirst=False):
1852 self.repo = repo
1852 self.repo = repo
1853 self.startrev = nullrev
1853 self.startrev = nullrev
1854 self.roots = set()
1854 self.roots = set()
1855 self.onlyfirst = onlyfirst
1855 self.onlyfirst = onlyfirst
1856
1856
1857 def match(self, rev):
1857 def match(self, rev):
1858 def realparents(rev):
1858 def realparents(rev):
1859 if self.onlyfirst:
1859 if self.onlyfirst:
1860 return self.repo.changelog.parentrevs(rev)[0:1]
1860 return self.repo.changelog.parentrevs(rev)[0:1]
1861 else:
1861 else:
1862 return filter(lambda x: x != nullrev,
1862 return filter(lambda x: x != nullrev,
1863 self.repo.changelog.parentrevs(rev))
1863 self.repo.changelog.parentrevs(rev))
1864
1864
1865 if self.startrev == nullrev:
1865 if self.startrev == nullrev:
1866 self.startrev = rev
1866 self.startrev = rev
1867 return True
1867 return True
1868
1868
1869 if rev > self.startrev:
1869 if rev > self.startrev:
1870 # forward: all descendants
1870 # forward: all descendants
1871 if not self.roots:
1871 if not self.roots:
1872 self.roots.add(self.startrev)
1872 self.roots.add(self.startrev)
1873 for parent in realparents(rev):
1873 for parent in realparents(rev):
1874 if parent in self.roots:
1874 if parent in self.roots:
1875 self.roots.add(rev)
1875 self.roots.add(rev)
1876 return True
1876 return True
1877 else:
1877 else:
1878 # backwards: all parents
1878 # backwards: all parents
1879 if not self.roots:
1879 if not self.roots:
1880 self.roots.update(realparents(self.startrev))
1880 self.roots.update(realparents(self.startrev))
1881 if rev in self.roots:
1881 if rev in self.roots:
1882 self.roots.remove(rev)
1882 self.roots.remove(rev)
1883 self.roots.update(realparents(rev))
1883 self.roots.update(realparents(rev))
1884 return True
1884 return True
1885
1885
1886 return False
1886 return False
1887
1887
1888 def walkchangerevs(repo, match, opts, prepare):
1888 def walkchangerevs(repo, match, opts, prepare):
1889 '''Iterate over files and the revs in which they changed.
1889 '''Iterate over files and the revs in which they changed.
1890
1890
1891 Callers most commonly need to iterate backwards over the history
1891 Callers most commonly need to iterate backwards over the history
1892 in which they are interested. Doing so has awful (quadratic-looking)
1892 in which they are interested. Doing so has awful (quadratic-looking)
1893 performance, so we use iterators in a "windowed" way.
1893 performance, so we use iterators in a "windowed" way.
1894
1894
1895 We walk a window of revisions in the desired order. Within the
1895 We walk a window of revisions in the desired order. Within the
1896 window, we first walk forwards to gather data, then in the desired
1896 window, we first walk forwards to gather data, then in the desired
1897 order (usually backwards) to display it.
1897 order (usually backwards) to display it.
1898
1898
1899 This function returns an iterator yielding contexts. Before
1899 This function returns an iterator yielding contexts. Before
1900 yielding each context, the iterator will first call the prepare
1900 yielding each context, the iterator will first call the prepare
1901 function on each context in the window in forward order.'''
1901 function on each context in the window in forward order.'''
1902
1902
1903 allfiles = opts.get('all_files')
1903 allfiles = opts.get('all_files')
1904 follow = opts.get('follow') or opts.get('follow_first')
1904 follow = opts.get('follow') or opts.get('follow_first')
1905 revs = _walkrevs(repo, opts)
1905 revs = _walkrevs(repo, opts)
1906 if not revs:
1906 if not revs:
1907 return []
1907 return []
1908 wanted = set()
1908 wanted = set()
1909 slowpath = match.anypats() or (not match.always() and opts.get('removed'))
1909 slowpath = match.anypats() or (not match.always() and opts.get('removed'))
1910 fncache = {}
1910 fncache = {}
1911 change = repo.__getitem__
1911 change = repo.__getitem__
1912
1912
1913 # First step is to fill wanted, the set of revisions that we want to yield.
1913 # First step is to fill wanted, the set of revisions that we want to yield.
1914 # When it does not induce extra cost, we also fill fncache for revisions in
1914 # When it does not induce extra cost, we also fill fncache for revisions in
1915 # wanted: a cache of filenames that were changed (ctx.files()) and that
1915 # wanted: a cache of filenames that were changed (ctx.files()) and that
1916 # match the file filtering conditions.
1916 # match the file filtering conditions.
1917
1917
1918 if match.always() or allfiles:
1918 if match.always() or allfiles:
1919 # No files, no patterns. Display all revs.
1919 # No files, no patterns. Display all revs.
1920 wanted = revs
1920 wanted = revs
1921 elif not slowpath:
1921 elif not slowpath:
1922 # We only have to read through the filelog to find wanted revisions
1922 # We only have to read through the filelog to find wanted revisions
1923
1923
1924 try:
1924 try:
1925 wanted = walkfilerevs(repo, match, follow, revs, fncache)
1925 wanted = walkfilerevs(repo, match, follow, revs, fncache)
1926 except FileWalkError:
1926 except FileWalkError:
1927 slowpath = True
1927 slowpath = True
1928
1928
1929 # We decided to fall back to the slowpath because at least one
1929 # We decided to fall back to the slowpath because at least one
1930 # of the paths was not a file. Check to see if at least one of them
1930 # of the paths was not a file. Check to see if at least one of them
1931 # existed in history, otherwise simply return
1931 # existed in history, otherwise simply return
1932 for path in match.files():
1932 for path in match.files():
1933 if path == '.' or path in repo.store:
1933 if path == '.' or path in repo.store:
1934 break
1934 break
1935 else:
1935 else:
1936 return []
1936 return []
1937
1937
1938 if slowpath:
1938 if slowpath:
1939 # We have to read the changelog to match filenames against
1939 # We have to read the changelog to match filenames against
1940 # changed files
1940 # changed files
1941
1941
1942 if follow:
1942 if follow:
1943 raise error.Abort(_('can only follow copies/renames for explicit '
1943 raise error.Abort(_('can only follow copies/renames for explicit '
1944 'filenames'))
1944 'filenames'))
1945
1945
1946 # The slow path checks files modified in every changeset.
1946 # The slow path checks files modified in every changeset.
1947 # This is really slow on large repos, so compute the set lazily.
1947 # This is really slow on large repos, so compute the set lazily.
1948 class lazywantedset(object):
1948 class lazywantedset(object):
1949 def __init__(self):
1949 def __init__(self):
1950 self.set = set()
1950 self.set = set()
1951 self.revs = set(revs)
1951 self.revs = set(revs)
1952
1952
1953 # No need to worry about locality here because it will be accessed
1953 # No need to worry about locality here because it will be accessed
1954 # in the same order as the increasing window below.
1954 # in the same order as the increasing window below.
1955 def __contains__(self, value):
1955 def __contains__(self, value):
1956 if value in self.set:
1956 if value in self.set:
1957 return True
1957 return True
1958 elif not value in self.revs:
1958 elif not value in self.revs:
1959 return False
1959 return False
1960 else:
1960 else:
1961 self.revs.discard(value)
1961 self.revs.discard(value)
1962 ctx = change(value)
1962 ctx = change(value)
1963 matches = [f for f in ctx.files() if match(f)]
1963 matches = [f for f in ctx.files() if match(f)]
1964 if matches:
1964 if matches:
1965 fncache[value] = matches
1965 fncache[value] = matches
1966 self.set.add(value)
1966 self.set.add(value)
1967 return True
1967 return True
1968 return False
1968 return False
1969
1969
1970 def discard(self, value):
1970 def discard(self, value):
1971 self.revs.discard(value)
1971 self.revs.discard(value)
1972 self.set.discard(value)
1972 self.set.discard(value)
1973
1973
1974 wanted = lazywantedset()
1974 wanted = lazywantedset()
1975
1975
1976 # it might be worthwhile to do this in the iterator if the rev range
1976 # it might be worthwhile to do this in the iterator if the rev range
1977 # is descending and the prune args are all within that range
1977 # is descending and the prune args are all within that range
1978 for rev in opts.get('prune', ()):
1978 for rev in opts.get('prune', ()):
1979 rev = repo[rev].rev()
1979 rev = repo[rev].rev()
1980 ff = _followfilter(repo)
1980 ff = _followfilter(repo)
1981 stop = min(revs[0], revs[-1])
1981 stop = min(revs[0], revs[-1])
1982 for x in pycompat.xrange(rev, stop - 1, -1):
1982 for x in pycompat.xrange(rev, stop - 1, -1):
1983 if ff.match(x):
1983 if ff.match(x):
1984 wanted = wanted - [x]
1984 wanted = wanted - [x]
1985
1985
1986 # Now that wanted is correctly initialized, we can iterate over the
1986 # Now that wanted is correctly initialized, we can iterate over the
1987 # revision range, yielding only revisions in wanted.
1987 # revision range, yielding only revisions in wanted.
1988 def iterate():
1988 def iterate():
1989 if follow and match.always():
1989 if follow and match.always():
1990 ff = _followfilter(repo, onlyfirst=opts.get('follow_first'))
1990 ff = _followfilter(repo, onlyfirst=opts.get('follow_first'))
1991 def want(rev):
1991 def want(rev):
1992 return ff.match(rev) and rev in wanted
1992 return ff.match(rev) and rev in wanted
1993 else:
1993 else:
1994 def want(rev):
1994 def want(rev):
1995 return rev in wanted
1995 return rev in wanted
1996
1996
1997 it = iter(revs)
1997 it = iter(revs)
1998 stopiteration = False
1998 stopiteration = False
1999 for windowsize in increasingwindows():
1999 for windowsize in increasingwindows():
2000 nrevs = []
2000 nrevs = []
2001 for i in pycompat.xrange(windowsize):
2001 for i in pycompat.xrange(windowsize):
2002 rev = next(it, None)
2002 rev = next(it, None)
2003 if rev is None:
2003 if rev is None:
2004 stopiteration = True
2004 stopiteration = True
2005 break
2005 break
2006 elif want(rev):
2006 elif want(rev):
2007 nrevs.append(rev)
2007 nrevs.append(rev)
2008 for rev in sorted(nrevs):
2008 for rev in sorted(nrevs):
2009 fns = fncache.get(rev)
2009 fns = fncache.get(rev)
2010 ctx = change(rev)
2010 ctx = change(rev)
2011 if not fns:
2011 if not fns:
2012 def fns_generator():
2012 def fns_generator():
2013 if allfiles:
2013 if allfiles:
2014 fiter = iter(ctx)
2014 fiter = iter(ctx)
2015 else:
2015 else:
2016 fiter = ctx.files()
2016 fiter = ctx.files()
2017 for f in fiter:
2017 for f in fiter:
2018 if match(f):
2018 if match(f):
2019 yield f
2019 yield f
2020 fns = fns_generator()
2020 fns = fns_generator()
2021 prepare(ctx, fns)
2021 prepare(ctx, fns)
2022 for rev in nrevs:
2022 for rev in nrevs:
2023 yield change(rev)
2023 yield change(rev)
2024
2024
2025 if stopiteration:
2025 if stopiteration:
2026 break
2026 break
2027
2027
2028 return iterate()
2028 return iterate()
2029
2029
2030 def add(ui, repo, match, prefix, explicitonly, **opts):
2030 def add(ui, repo, match, prefix, explicitonly, **opts):
2031 join = lambda f: os.path.join(prefix, f)
2031 join = lambda f: os.path.join(prefix, f)
2032 bad = []
2032 bad = []
2033
2033
2034 badfn = lambda x, y: bad.append(x) or match.bad(x, y)
2034 badfn = lambda x, y: bad.append(x) or match.bad(x, y)
2035 names = []
2035 names = []
2036 wctx = repo[None]
2036 wctx = repo[None]
2037 cca = None
2037 cca = None
2038 abort, warn = scmutil.checkportabilityalert(ui)
2038 abort, warn = scmutil.checkportabilityalert(ui)
2039 if abort or warn:
2039 if abort or warn:
2040 cca = scmutil.casecollisionauditor(ui, abort, repo.dirstate)
2040 cca = scmutil.casecollisionauditor(ui, abort, repo.dirstate)
2041
2041
2042 match = repo.narrowmatch(match, includeexact=True)
2042 match = repo.narrowmatch(match, includeexact=True)
2043 badmatch = matchmod.badmatch(match, badfn)
2043 badmatch = matchmod.badmatch(match, badfn)
2044 dirstate = repo.dirstate
2044 dirstate = repo.dirstate
2045 # We don't want to just call wctx.walk here, since it would return a lot of
2045 # We don't want to just call wctx.walk here, since it would return a lot of
2046 # clean files, which we aren't interested in and takes time.
2046 # clean files, which we aren't interested in and takes time.
2047 for f in sorted(dirstate.walk(badmatch, subrepos=sorted(wctx.substate),
2047 for f in sorted(dirstate.walk(badmatch, subrepos=sorted(wctx.substate),
2048 unknown=True, ignored=False, full=False)):
2048 unknown=True, ignored=False, full=False)):
2049 exact = match.exact(f)
2049 exact = match.exact(f)
2050 if exact or not explicitonly and f not in wctx and repo.wvfs.lexists(f):
2050 if exact or not explicitonly and f not in wctx and repo.wvfs.lexists(f):
2051 if cca:
2051 if cca:
2052 cca(f)
2052 cca(f)
2053 names.append(f)
2053 names.append(f)
2054 if ui.verbose or not exact:
2054 if ui.verbose or not exact:
2055 ui.status(_('adding %s\n') % match.rel(f),
2055 ui.status(_('adding %s\n') % match.rel(f),
2056 label='ui.addremove.added')
2056 label='ui.addremove.added')
2057
2057
2058 for subpath in sorted(wctx.substate):
2058 for subpath in sorted(wctx.substate):
2059 sub = wctx.sub(subpath)
2059 sub = wctx.sub(subpath)
2060 try:
2060 try:
2061 submatch = matchmod.subdirmatcher(subpath, match)
2061 submatch = matchmod.subdirmatcher(subpath, match)
2062 if opts.get(r'subrepos'):
2062 if opts.get(r'subrepos'):
2063 bad.extend(sub.add(ui, submatch, prefix, False, **opts))
2063 bad.extend(sub.add(ui, submatch, prefix, False, **opts))
2064 else:
2064 else:
2065 bad.extend(sub.add(ui, submatch, prefix, True, **opts))
2065 bad.extend(sub.add(ui, submatch, prefix, True, **opts))
2066 except error.LookupError:
2066 except error.LookupError:
2067 ui.status(_("skipping missing subrepository: %s\n")
2067 ui.status(_("skipping missing subrepository: %s\n")
2068 % join(subpath))
2068 % join(subpath))
2069
2069
2070 if not opts.get(r'dry_run'):
2070 if not opts.get(r'dry_run'):
2071 rejected = wctx.add(names, prefix)
2071 rejected = wctx.add(names, prefix)
2072 bad.extend(f for f in rejected if f in match.files())
2072 bad.extend(f for f in rejected if f in match.files())
2073 return bad
2073 return bad
2074
2074
2075 def addwebdirpath(repo, serverpath, webconf):
2075 def addwebdirpath(repo, serverpath, webconf):
2076 webconf[serverpath] = repo.root
2076 webconf[serverpath] = repo.root
2077 repo.ui.debug('adding %s = %s\n' % (serverpath, repo.root))
2077 repo.ui.debug('adding %s = %s\n' % (serverpath, repo.root))
2078
2078
2079 for r in repo.revs('filelog("path:.hgsub")'):
2079 for r in repo.revs('filelog("path:.hgsub")'):
2080 ctx = repo[r]
2080 ctx = repo[r]
2081 for subpath in ctx.substate:
2081 for subpath in ctx.substate:
2082 ctx.sub(subpath).addwebdirpath(serverpath, webconf)
2082 ctx.sub(subpath).addwebdirpath(serverpath, webconf)
2083
2083
2084 def forget(ui, repo, match, prefix, explicitonly, dryrun, interactive):
2084 def forget(ui, repo, match, prefix, explicitonly, dryrun, interactive):
2085 if dryrun and interactive:
2085 if dryrun and interactive:
2086 raise error.Abort(_("cannot specify both --dry-run and --interactive"))
2086 raise error.Abort(_("cannot specify both --dry-run and --interactive"))
2087 join = lambda f: os.path.join(prefix, f)
2087 join = lambda f: os.path.join(prefix, f)
2088 bad = []
2088 bad = []
2089 badfn = lambda x, y: bad.append(x) or match.bad(x, y)
2089 badfn = lambda x, y: bad.append(x) or match.bad(x, y)
2090 wctx = repo[None]
2090 wctx = repo[None]
2091 forgot = []
2091 forgot = []
2092
2092
2093 s = repo.status(match=matchmod.badmatch(match, badfn), clean=True)
2093 s = repo.status(match=matchmod.badmatch(match, badfn), clean=True)
2094 forget = sorted(s.modified + s.added + s.deleted + s.clean)
2094 forget = sorted(s.modified + s.added + s.deleted + s.clean)
2095 if explicitonly:
2095 if explicitonly:
2096 forget = [f for f in forget if match.exact(f)]
2096 forget = [f for f in forget if match.exact(f)]
2097
2097
2098 for subpath in sorted(wctx.substate):
2098 for subpath in sorted(wctx.substate):
2099 sub = wctx.sub(subpath)
2099 sub = wctx.sub(subpath)
2100 try:
2100 try:
2101 submatch = matchmod.subdirmatcher(subpath, match)
2101 submatch = matchmod.subdirmatcher(subpath, match)
2102 subbad, subforgot = sub.forget(submatch, prefix, dryrun=dryrun,
2102 subbad, subforgot = sub.forget(submatch, prefix, dryrun=dryrun,
2103 interactive=interactive)
2103 interactive=interactive)
2104 bad.extend([subpath + '/' + f for f in subbad])
2104 bad.extend([subpath + '/' + f for f in subbad])
2105 forgot.extend([subpath + '/' + f for f in subforgot])
2105 forgot.extend([subpath + '/' + f for f in subforgot])
2106 except error.LookupError:
2106 except error.LookupError:
2107 ui.status(_("skipping missing subrepository: %s\n")
2107 ui.status(_("skipping missing subrepository: %s\n")
2108 % join(subpath))
2108 % join(subpath))
2109
2109
2110 if not explicitonly:
2110 if not explicitonly:
2111 for f in match.files():
2111 for f in match.files():
2112 if f not in repo.dirstate and not repo.wvfs.isdir(f):
2112 if f not in repo.dirstate and not repo.wvfs.isdir(f):
2113 if f not in forgot:
2113 if f not in forgot:
2114 if repo.wvfs.exists(f):
2114 if repo.wvfs.exists(f):
2115 # Don't complain if the exact case match wasn't given.
2115 # Don't complain if the exact case match wasn't given.
2116 # But don't do this until after checking 'forgot', so
2116 # But don't do this until after checking 'forgot', so
2117 # that subrepo files aren't normalized, and this op is
2117 # that subrepo files aren't normalized, and this op is
2118 # purely from data cached by the status walk above.
2118 # purely from data cached by the status walk above.
2119 if repo.dirstate.normalize(f) in repo.dirstate:
2119 if repo.dirstate.normalize(f) in repo.dirstate:
2120 continue
2120 continue
2121 ui.warn(_('not removing %s: '
2121 ui.warn(_('not removing %s: '
2122 'file is already untracked\n')
2122 'file is already untracked\n')
2123 % match.rel(f))
2123 % match.rel(f))
2124 bad.append(f)
2124 bad.append(f)
2125
2125
2126 if interactive:
2126 if interactive:
2127 responses = _('[Ynsa?]'
2127 responses = _('[Ynsa?]'
2128 '$$ &Yes, forget this file'
2128 '$$ &Yes, forget this file'
2129 '$$ &No, skip this file'
2129 '$$ &No, skip this file'
2130 '$$ &Skip remaining files'
2130 '$$ &Skip remaining files'
2131 '$$ Include &all remaining files'
2131 '$$ Include &all remaining files'
2132 '$$ &? (display help)')
2132 '$$ &? (display help)')
2133 for filename in forget[:]:
2133 for filename in forget[:]:
2134 r = ui.promptchoice(_('forget %s %s') % (filename, responses))
2134 r = ui.promptchoice(_('forget %s %s') % (filename, responses))
2135 if r == 4: # ?
2135 if r == 4: # ?
2136 while r == 4:
2136 while r == 4:
2137 for c, t in ui.extractchoices(responses)[1]:
2137 for c, t in ui.extractchoices(responses)[1]:
2138 ui.write('%s - %s\n' % (c, encoding.lower(t)))
2138 ui.write('%s - %s\n' % (c, encoding.lower(t)))
2139 r = ui.promptchoice(_('forget %s %s') % (filename,
2139 r = ui.promptchoice(_('forget %s %s') % (filename,
2140 responses))
2140 responses))
2141 if r == 0: # yes
2141 if r == 0: # yes
2142 continue
2142 continue
2143 elif r == 1: # no
2143 elif r == 1: # no
2144 forget.remove(filename)
2144 forget.remove(filename)
2145 elif r == 2: # Skip
2145 elif r == 2: # Skip
2146 fnindex = forget.index(filename)
2146 fnindex = forget.index(filename)
2147 del forget[fnindex:]
2147 del forget[fnindex:]
2148 break
2148 break
2149 elif r == 3: # All
2149 elif r == 3: # All
2150 break
2150 break
2151
2151
2152 for f in forget:
2152 for f in forget:
2153 if ui.verbose or not match.exact(f) or interactive:
2153 if ui.verbose or not match.exact(f) or interactive:
2154 ui.status(_('removing %s\n') % match.rel(f),
2154 ui.status(_('removing %s\n') % match.rel(f),
2155 label='ui.addremove.removed')
2155 label='ui.addremove.removed')
2156
2156
2157 if not dryrun:
2157 if not dryrun:
2158 rejected = wctx.forget(forget, prefix)
2158 rejected = wctx.forget(forget, prefix)
2159 bad.extend(f for f in rejected if f in match.files())
2159 bad.extend(f for f in rejected if f in match.files())
2160 forgot.extend(f for f in forget if f not in rejected)
2160 forgot.extend(f for f in forget if f not in rejected)
2161 return bad, forgot
2161 return bad, forgot
2162
2162
2163 def files(ui, ctx, m, fm, fmt, subrepos):
2163 def files(ui, ctx, m, fm, fmt, subrepos):
2164 ret = 1
2164 ret = 1
2165
2165
2166 needsfctx = ui.verbose or {'size', 'flags'} & fm.datahint()
2166 needsfctx = ui.verbose or {'size', 'flags'} & fm.datahint()
2167 uipathfn = scmutil.getuipathfn(ctx.repo(), legacyrelativevalue=True)
2167 uipathfn = scmutil.getuipathfn(ctx.repo(), legacyrelativevalue=True)
2168 for f in ctx.matches(m):
2168 for f in ctx.matches(m):
2169 fm.startitem()
2169 fm.startitem()
2170 fm.context(ctx=ctx)
2170 fm.context(ctx=ctx)
2171 if needsfctx:
2171 if needsfctx:
2172 fc = ctx[f]
2172 fc = ctx[f]
2173 fm.write('size flags', '% 10d % 1s ', fc.size(), fc.flags())
2173 fm.write('size flags', '% 10d % 1s ', fc.size(), fc.flags())
2174 fm.data(path=f)
2174 fm.data(path=f)
2175 fm.plain(fmt % uipathfn(f))
2175 fm.plain(fmt % uipathfn(f))
2176 ret = 0
2176 ret = 0
2177
2177
2178 for subpath in sorted(ctx.substate):
2178 for subpath in sorted(ctx.substate):
2179 submatch = matchmod.subdirmatcher(subpath, m)
2179 submatch = matchmod.subdirmatcher(subpath, m)
2180 if (subrepos or m.exact(subpath) or any(submatch.files())):
2180 if (subrepos or m.exact(subpath) or any(submatch.files())):
2181 sub = ctx.sub(subpath)
2181 sub = ctx.sub(subpath)
2182 try:
2182 try:
2183 recurse = m.exact(subpath) or subrepos
2183 recurse = m.exact(subpath) or subrepos
2184 if sub.printfiles(ui, submatch, fm, fmt, recurse) == 0:
2184 if sub.printfiles(ui, submatch, fm, fmt, recurse) == 0:
2185 ret = 0
2185 ret = 0
2186 except error.LookupError:
2186 except error.LookupError:
2187 ui.status(_("skipping missing subrepository: %s\n")
2187 ui.status(_("skipping missing subrepository: %s\n")
2188 % m.abs(subpath))
2188 % m.abs(subpath))
2189
2189
2190 return ret
2190 return ret
2191
2191
2192 def remove(ui, repo, m, prefix, after, force, subrepos, dryrun, warnings=None):
2192 def remove(ui, repo, m, prefix, after, force, subrepos, dryrun, warnings=None):
2193 join = lambda f: os.path.join(prefix, f)
2193 join = lambda f: os.path.join(prefix, f)
2194 ret = 0
2194 ret = 0
2195 s = repo.status(match=m, clean=True)
2195 s = repo.status(match=m, clean=True)
2196 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
2196 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
2197
2197
2198 wctx = repo[None]
2198 wctx = repo[None]
2199
2199
2200 if warnings is None:
2200 if warnings is None:
2201 warnings = []
2201 warnings = []
2202 warn = True
2202 warn = True
2203 else:
2203 else:
2204 warn = False
2204 warn = False
2205
2205
2206 subs = sorted(wctx.substate)
2206 subs = sorted(wctx.substate)
2207 progress = ui.makeprogress(_('searching'), total=len(subs),
2207 progress = ui.makeprogress(_('searching'), total=len(subs),
2208 unit=_('subrepos'))
2208 unit=_('subrepos'))
2209 for subpath in subs:
2209 for subpath in subs:
2210 submatch = matchmod.subdirmatcher(subpath, m)
2210 submatch = matchmod.subdirmatcher(subpath, m)
2211 if subrepos or m.exact(subpath) or any(submatch.files()):
2211 if subrepos or m.exact(subpath) or any(submatch.files()):
2212 progress.increment()
2212 progress.increment()
2213 sub = wctx.sub(subpath)
2213 sub = wctx.sub(subpath)
2214 try:
2214 try:
2215 if sub.removefiles(submatch, prefix, after, force, subrepos,
2215 if sub.removefiles(submatch, prefix, after, force, subrepos,
2216 dryrun, warnings):
2216 dryrun, warnings):
2217 ret = 1
2217 ret = 1
2218 except error.LookupError:
2218 except error.LookupError:
2219 warnings.append(_("skipping missing subrepository: %s\n")
2219 warnings.append(_("skipping missing subrepository: %s\n")
2220 % join(subpath))
2220 % join(subpath))
2221 progress.complete()
2221 progress.complete()
2222
2222
2223 # warn about failure to delete explicit files/dirs
2223 # warn about failure to delete explicit files/dirs
2224 deleteddirs = util.dirs(deleted)
2224 deleteddirs = util.dirs(deleted)
2225 files = m.files()
2225 files = m.files()
2226 progress = ui.makeprogress(_('deleting'), total=len(files),
2226 progress = ui.makeprogress(_('deleting'), total=len(files),
2227 unit=_('files'))
2227 unit=_('files'))
2228 for f in files:
2228 for f in files:
2229 def insubrepo():
2229 def insubrepo():
2230 for subpath in wctx.substate:
2230 for subpath in wctx.substate:
2231 if f.startswith(subpath + '/'):
2231 if f.startswith(subpath + '/'):
2232 return True
2232 return True
2233 return False
2233 return False
2234
2234
2235 progress.increment()
2235 progress.increment()
2236 isdir = f in deleteddirs or wctx.hasdir(f)
2236 isdir = f in deleteddirs or wctx.hasdir(f)
2237 if (f in repo.dirstate or isdir or f == '.'
2237 if (f in repo.dirstate or isdir or f == '.'
2238 or insubrepo() or f in subs):
2238 or insubrepo() or f in subs):
2239 continue
2239 continue
2240
2240
2241 if repo.wvfs.exists(f):
2241 if repo.wvfs.exists(f):
2242 if repo.wvfs.isdir(f):
2242 if repo.wvfs.isdir(f):
2243 warnings.append(_('not removing %s: no tracked files\n')
2243 warnings.append(_('not removing %s: no tracked files\n')
2244 % m.rel(f))
2244 % m.rel(f))
2245 else:
2245 else:
2246 warnings.append(_('not removing %s: file is untracked\n')
2246 warnings.append(_('not removing %s: file is untracked\n')
2247 % m.rel(f))
2247 % m.rel(f))
2248 # missing files will generate a warning elsewhere
2248 # missing files will generate a warning elsewhere
2249 ret = 1
2249 ret = 1
2250 progress.complete()
2250 progress.complete()
2251
2251
2252 if force:
2252 if force:
2253 list = modified + deleted + clean + added
2253 list = modified + deleted + clean + added
2254 elif after:
2254 elif after:
2255 list = deleted
2255 list = deleted
2256 remaining = modified + added + clean
2256 remaining = modified + added + clean
2257 progress = ui.makeprogress(_('skipping'), total=len(remaining),
2257 progress = ui.makeprogress(_('skipping'), total=len(remaining),
2258 unit=_('files'))
2258 unit=_('files'))
2259 for f in remaining:
2259 for f in remaining:
2260 progress.increment()
2260 progress.increment()
2261 if ui.verbose or (f in files):
2261 if ui.verbose or (f in files):
2262 warnings.append(_('not removing %s: file still exists\n')
2262 warnings.append(_('not removing %s: file still exists\n')
2263 % m.rel(f))
2263 % m.rel(f))
2264 ret = 1
2264 ret = 1
2265 progress.complete()
2265 progress.complete()
2266 else:
2266 else:
2267 list = deleted + clean
2267 list = deleted + clean
2268 progress = ui.makeprogress(_('skipping'),
2268 progress = ui.makeprogress(_('skipping'),
2269 total=(len(modified) + len(added)),
2269 total=(len(modified) + len(added)),
2270 unit=_('files'))
2270 unit=_('files'))
2271 for f in modified:
2271 for f in modified:
2272 progress.increment()
2272 progress.increment()
2273 warnings.append(_('not removing %s: file is modified (use -f'
2273 warnings.append(_('not removing %s: file is modified (use -f'
2274 ' to force removal)\n') % m.rel(f))
2274 ' to force removal)\n') % m.rel(f))
2275 ret = 1
2275 ret = 1
2276 for f in added:
2276 for f in added:
2277 progress.increment()
2277 progress.increment()
2278 warnings.append(_("not removing %s: file has been marked for add"
2278 warnings.append(_("not removing %s: file has been marked for add"
2279 " (use 'hg forget' to undo add)\n") % m.rel(f))
2279 " (use 'hg forget' to undo add)\n") % m.rel(f))
2280 ret = 1
2280 ret = 1
2281 progress.complete()
2281 progress.complete()
2282
2282
2283 list = sorted(list)
2283 list = sorted(list)
2284 progress = ui.makeprogress(_('deleting'), total=len(list),
2284 progress = ui.makeprogress(_('deleting'), total=len(list),
2285 unit=_('files'))
2285 unit=_('files'))
2286 for f in list:
2286 for f in list:
2287 if ui.verbose or not m.exact(f):
2287 if ui.verbose or not m.exact(f):
2288 progress.increment()
2288 progress.increment()
2289 ui.status(_('removing %s\n') % m.rel(f),
2289 ui.status(_('removing %s\n') % m.rel(f),
2290 label='ui.addremove.removed')
2290 label='ui.addremove.removed')
2291 progress.complete()
2291 progress.complete()
2292
2292
2293 if not dryrun:
2293 if not dryrun:
2294 with repo.wlock():
2294 with repo.wlock():
2295 if not after:
2295 if not after:
2296 for f in list:
2296 for f in list:
2297 if f in added:
2297 if f in added:
2298 continue # we never unlink added files on remove
2298 continue # we never unlink added files on remove
2299 rmdir = repo.ui.configbool('experimental',
2299 rmdir = repo.ui.configbool('experimental',
2300 'removeemptydirs')
2300 'removeemptydirs')
2301 repo.wvfs.unlinkpath(f, ignoremissing=True, rmdir=rmdir)
2301 repo.wvfs.unlinkpath(f, ignoremissing=True, rmdir=rmdir)
2302 repo[None].forget(list)
2302 repo[None].forget(list)
2303
2303
2304 if warn:
2304 if warn:
2305 for warning in warnings:
2305 for warning in warnings:
2306 ui.warn(warning)
2306 ui.warn(warning)
2307
2307
2308 return ret
2308 return ret
2309
2309
2310 def _updatecatformatter(fm, ctx, matcher, path, decode):
2310 def _updatecatformatter(fm, ctx, matcher, path, decode):
2311 """Hook for adding data to the formatter used by ``hg cat``.
2311 """Hook for adding data to the formatter used by ``hg cat``.
2312
2312
2313 Extensions (e.g., lfs) can wrap this to inject keywords/data, but must call
2313 Extensions (e.g., lfs) can wrap this to inject keywords/data, but must call
2314 this method first."""
2314 this method first."""
2315 data = ctx[path].data()
2315 data = ctx[path].data()
2316 if decode:
2316 if decode:
2317 data = ctx.repo().wwritedata(path, data)
2317 data = ctx.repo().wwritedata(path, data)
2318 fm.startitem()
2318 fm.startitem()
2319 fm.context(ctx=ctx)
2319 fm.context(ctx=ctx)
2320 fm.write('data', '%s', data)
2320 fm.write('data', '%s', data)
2321 fm.data(path=path)
2321 fm.data(path=path)
2322
2322
2323 def cat(ui, repo, ctx, matcher, basefm, fntemplate, prefix, **opts):
2323 def cat(ui, repo, ctx, matcher, basefm, fntemplate, prefix, **opts):
2324 err = 1
2324 err = 1
2325 opts = pycompat.byteskwargs(opts)
2325 opts = pycompat.byteskwargs(opts)
2326
2326
2327 def write(path):
2327 def write(path):
2328 filename = None
2328 filename = None
2329 if fntemplate:
2329 if fntemplate:
2330 filename = makefilename(ctx, fntemplate,
2330 filename = makefilename(ctx, fntemplate,
2331 pathname=os.path.join(prefix, path))
2331 pathname=os.path.join(prefix, path))
2332 # attempt to create the directory if it does not already exist
2332 # attempt to create the directory if it does not already exist
2333 try:
2333 try:
2334 os.makedirs(os.path.dirname(filename))
2334 os.makedirs(os.path.dirname(filename))
2335 except OSError:
2335 except OSError:
2336 pass
2336 pass
2337 with formatter.maybereopen(basefm, filename) as fm:
2337 with formatter.maybereopen(basefm, filename) as fm:
2338 _updatecatformatter(fm, ctx, matcher, path, opts.get('decode'))
2338 _updatecatformatter(fm, ctx, matcher, path, opts.get('decode'))
2339
2339
2340 # Automation often uses hg cat on single files, so special case it
2340 # Automation often uses hg cat on single files, so special case it
2341 # for performance to avoid the cost of parsing the manifest.
2341 # for performance to avoid the cost of parsing the manifest.
2342 if len(matcher.files()) == 1 and not matcher.anypats():
2342 if len(matcher.files()) == 1 and not matcher.anypats():
2343 file = matcher.files()[0]
2343 file = matcher.files()[0]
2344 mfl = repo.manifestlog
2344 mfl = repo.manifestlog
2345 mfnode = ctx.manifestnode()
2345 mfnode = ctx.manifestnode()
2346 try:
2346 try:
2347 if mfnode and mfl[mfnode].find(file)[0]:
2347 if mfnode and mfl[mfnode].find(file)[0]:
2348 scmutil.prefetchfiles(repo, [ctx.rev()], matcher)
2348 scmutil.prefetchfiles(repo, [ctx.rev()], matcher)
2349 write(file)
2349 write(file)
2350 return 0
2350 return 0
2351 except KeyError:
2351 except KeyError:
2352 pass
2352 pass
2353
2353
2354 scmutil.prefetchfiles(repo, [ctx.rev()], matcher)
2354 scmutil.prefetchfiles(repo, [ctx.rev()], matcher)
2355
2355
2356 for abs in ctx.walk(matcher):
2356 for abs in ctx.walk(matcher):
2357 write(abs)
2357 write(abs)
2358 err = 0
2358 err = 0
2359
2359
2360 for subpath in sorted(ctx.substate):
2360 for subpath in sorted(ctx.substate):
2361 sub = ctx.sub(subpath)
2361 sub = ctx.sub(subpath)
2362 try:
2362 try:
2363 submatch = matchmod.subdirmatcher(subpath, matcher)
2363 submatch = matchmod.subdirmatcher(subpath, matcher)
2364
2364
2365 if not sub.cat(submatch, basefm, fntemplate,
2365 if not sub.cat(submatch, basefm, fntemplate,
2366 os.path.join(prefix, sub._path),
2366 os.path.join(prefix, sub._path),
2367 **pycompat.strkwargs(opts)):
2367 **pycompat.strkwargs(opts)):
2368 err = 0
2368 err = 0
2369 except error.RepoLookupError:
2369 except error.RepoLookupError:
2370 ui.status(_("skipping missing subrepository: %s\n")
2370 ui.status(_("skipping missing subrepository: %s\n")
2371 % os.path.join(prefix, subpath))
2371 % os.path.join(prefix, subpath))
2372
2372
2373 return err
2373 return err
2374
2374
2375 def commit(ui, repo, commitfunc, pats, opts):
2375 def commit(ui, repo, commitfunc, pats, opts):
2376 '''commit the specified files or all outstanding changes'''
2376 '''commit the specified files or all outstanding changes'''
2377 date = opts.get('date')
2377 date = opts.get('date')
2378 if date:
2378 if date:
2379 opts['date'] = dateutil.parsedate(date)
2379 opts['date'] = dateutil.parsedate(date)
2380 message = logmessage(ui, opts)
2380 message = logmessage(ui, opts)
2381 matcher = scmutil.match(repo[None], pats, opts)
2381 matcher = scmutil.match(repo[None], pats, opts)
2382
2382
2383 dsguard = None
2383 dsguard = None
2384 # extract addremove carefully -- this function can be called from a command
2384 # extract addremove carefully -- this function can be called from a command
2385 # that doesn't support addremove
2385 # that doesn't support addremove
2386 if opts.get('addremove'):
2386 if opts.get('addremove'):
2387 dsguard = dirstateguard.dirstateguard(repo, 'commit')
2387 dsguard = dirstateguard.dirstateguard(repo, 'commit')
2388 with dsguard or util.nullcontextmanager():
2388 with dsguard or util.nullcontextmanager():
2389 if dsguard:
2389 if dsguard:
2390 if scmutil.addremove(repo, matcher, "", opts) != 0:
2390 if scmutil.addremove(repo, matcher, "", opts) != 0:
2391 raise error.Abort(
2391 raise error.Abort(
2392 _("failed to mark all new/missing files as added/removed"))
2392 _("failed to mark all new/missing files as added/removed"))
2393
2393
2394 return commitfunc(ui, repo, message, matcher, opts)
2394 return commitfunc(ui, repo, message, matcher, opts)
2395
2395
2396 def samefile(f, ctx1, ctx2):
2396 def samefile(f, ctx1, ctx2):
2397 if f in ctx1.manifest():
2397 if f in ctx1.manifest():
2398 a = ctx1.filectx(f)
2398 a = ctx1.filectx(f)
2399 if f in ctx2.manifest():
2399 if f in ctx2.manifest():
2400 b = ctx2.filectx(f)
2400 b = ctx2.filectx(f)
2401 return (not a.cmp(b)
2401 return (not a.cmp(b)
2402 and a.flags() == b.flags())
2402 and a.flags() == b.flags())
2403 else:
2403 else:
2404 return False
2404 return False
2405 else:
2405 else:
2406 return f not in ctx2.manifest()
2406 return f not in ctx2.manifest()
2407
2407
2408 def amend(ui, repo, old, extra, pats, opts):
2408 def amend(ui, repo, old, extra, pats, opts):
2409 # avoid cycle context -> subrepo -> cmdutil
2409 # avoid cycle context -> subrepo -> cmdutil
2410 from . import context
2410 from . import context
2411
2411
2412 # amend will reuse the existing user if not specified, but the obsolete
2412 # amend will reuse the existing user if not specified, but the obsolete
2413 # marker creation requires that the current user's name is specified.
2413 # marker creation requires that the current user's name is specified.
2414 if obsolete.isenabled(repo, obsolete.createmarkersopt):
2414 if obsolete.isenabled(repo, obsolete.createmarkersopt):
2415 ui.username() # raise exception if username not set
2415 ui.username() # raise exception if username not set
2416
2416
2417 ui.note(_('amending changeset %s\n') % old)
2417 ui.note(_('amending changeset %s\n') % old)
2418 base = old.p1()
2418 base = old.p1()
2419
2419
2420 with repo.wlock(), repo.lock(), repo.transaction('amend'):
2420 with repo.wlock(), repo.lock(), repo.transaction('amend'):
2421 # Participating changesets:
2421 # Participating changesets:
2422 #
2422 #
2423 # wctx o - workingctx that contains changes from working copy
2423 # wctx o - workingctx that contains changes from working copy
2424 # | to go into amending commit
2424 # | to go into amending commit
2425 # |
2425 # |
2426 # old o - changeset to amend
2426 # old o - changeset to amend
2427 # |
2427 # |
2428 # base o - first parent of the changeset to amend
2428 # base o - first parent of the changeset to amend
2429 wctx = repo[None]
2429 wctx = repo[None]
2430
2430
2431 # Copy to avoid mutating input
2431 # Copy to avoid mutating input
2432 extra = extra.copy()
2432 extra = extra.copy()
2433 # Update extra dict from amended commit (e.g. to preserve graft
2433 # Update extra dict from amended commit (e.g. to preserve graft
2434 # source)
2434 # source)
2435 extra.update(old.extra())
2435 extra.update(old.extra())
2436
2436
2437 # Also update it from the from the wctx
2437 # Also update it from the from the wctx
2438 extra.update(wctx.extra())
2438 extra.update(wctx.extra())
2439
2439
2440 user = opts.get('user') or old.user()
2440 user = opts.get('user') or old.user()
2441
2441
2442 datemaydiffer = False # date-only change should be ignored?
2442 datemaydiffer = False # date-only change should be ignored?
2443 if opts.get('date') and opts.get('currentdate'):
2443 if opts.get('date') and opts.get('currentdate'):
2444 raise error.Abort(_('--date and --currentdate are mutually '
2444 raise error.Abort(_('--date and --currentdate are mutually '
2445 'exclusive'))
2445 'exclusive'))
2446 if opts.get('date'):
2446 if opts.get('date'):
2447 date = dateutil.parsedate(opts.get('date'))
2447 date = dateutil.parsedate(opts.get('date'))
2448 elif opts.get('currentdate'):
2448 elif opts.get('currentdate'):
2449 date = dateutil.makedate()
2449 date = dateutil.makedate()
2450 elif (ui.configbool('rewrite', 'update-timestamp')
2450 elif (ui.configbool('rewrite', 'update-timestamp')
2451 and opts.get('currentdate') is None):
2451 and opts.get('currentdate') is None):
2452 date = dateutil.makedate()
2452 date = dateutil.makedate()
2453 datemaydiffer = True
2453 datemaydiffer = True
2454 else:
2454 else:
2455 date = old.date()
2455 date = old.date()
2456
2456
2457 if len(old.parents()) > 1:
2457 if len(old.parents()) > 1:
2458 # ctx.files() isn't reliable for merges, so fall back to the
2458 # ctx.files() isn't reliable for merges, so fall back to the
2459 # slower repo.status() method
2459 # slower repo.status() method
2460 files = set([fn for st in base.status(old)[:3]
2460 files = set([fn for st in base.status(old)[:3]
2461 for fn in st])
2461 for fn in st])
2462 else:
2462 else:
2463 files = set(old.files())
2463 files = set(old.files())
2464
2464
2465 # add/remove the files to the working copy if the "addremove" option
2465 # add/remove the files to the working copy if the "addremove" option
2466 # was specified.
2466 # was specified.
2467 matcher = scmutil.match(wctx, pats, opts)
2467 matcher = scmutil.match(wctx, pats, opts)
2468 if (opts.get('addremove')
2468 if (opts.get('addremove')
2469 and scmutil.addremove(repo, matcher, "", opts)):
2469 and scmutil.addremove(repo, matcher, "", opts)):
2470 raise error.Abort(
2470 raise error.Abort(
2471 _("failed to mark all new/missing files as added/removed"))
2471 _("failed to mark all new/missing files as added/removed"))
2472
2472
2473 # Check subrepos. This depends on in-place wctx._status update in
2473 # Check subrepos. This depends on in-place wctx._status update in
2474 # subrepo.precommit(). To minimize the risk of this hack, we do
2474 # subrepo.precommit(). To minimize the risk of this hack, we do
2475 # nothing if .hgsub does not exist.
2475 # nothing if .hgsub does not exist.
2476 if '.hgsub' in wctx or '.hgsub' in old:
2476 if '.hgsub' in wctx or '.hgsub' in old:
2477 subs, commitsubs, newsubstate = subrepoutil.precommit(
2477 subs, commitsubs, newsubstate = subrepoutil.precommit(
2478 ui, wctx, wctx._status, matcher)
2478 ui, wctx, wctx._status, matcher)
2479 # amend should abort if commitsubrepos is enabled
2479 # amend should abort if commitsubrepos is enabled
2480 assert not commitsubs
2480 assert not commitsubs
2481 if subs:
2481 if subs:
2482 subrepoutil.writestate(repo, newsubstate)
2482 subrepoutil.writestate(repo, newsubstate)
2483
2483
2484 ms = mergemod.mergestate.read(repo)
2484 ms = mergemod.mergestate.read(repo)
2485 mergeutil.checkunresolved(ms)
2485 mergeutil.checkunresolved(ms)
2486
2486
2487 filestoamend = set(f for f in wctx.files() if matcher(f))
2487 filestoamend = set(f for f in wctx.files() if matcher(f))
2488
2488
2489 changes = (len(filestoamend) > 0)
2489 changes = (len(filestoamend) > 0)
2490 if changes:
2490 if changes:
2491 # Recompute copies (avoid recording a -> b -> a)
2491 # Recompute copies (avoid recording a -> b -> a)
2492 copied = copies.pathcopies(base, wctx, matcher)
2492 copied = copies.pathcopies(base, wctx, matcher)
2493 if old.p2:
2493 if old.p2:
2494 copied.update(copies.pathcopies(old.p2(), wctx, matcher))
2494 copied.update(copies.pathcopies(old.p2(), wctx, matcher))
2495
2495
2496 # Prune files which were reverted by the updates: if old
2496 # Prune files which were reverted by the updates: if old
2497 # introduced file X and the file was renamed in the working
2497 # introduced file X and the file was renamed in the working
2498 # copy, then those two files are the same and
2498 # copy, then those two files are the same and
2499 # we can discard X from our list of files. Likewise if X
2499 # we can discard X from our list of files. Likewise if X
2500 # was removed, it's no longer relevant. If X is missing (aka
2500 # was removed, it's no longer relevant. If X is missing (aka
2501 # deleted), old X must be preserved.
2501 # deleted), old X must be preserved.
2502 files.update(filestoamend)
2502 files.update(filestoamend)
2503 files = [f for f in files if (not samefile(f, wctx, base)
2503 files = [f for f in files if (not samefile(f, wctx, base)
2504 or f in wctx.deleted())]
2504 or f in wctx.deleted())]
2505
2505
2506 def filectxfn(repo, ctx_, path):
2506 def filectxfn(repo, ctx_, path):
2507 try:
2507 try:
2508 # If the file being considered is not amongst the files
2508 # If the file being considered is not amongst the files
2509 # to be amended, we should return the file context from the
2509 # to be amended, we should return the file context from the
2510 # old changeset. This avoids issues when only some files in
2510 # old changeset. This avoids issues when only some files in
2511 # the working copy are being amended but there are also
2511 # the working copy are being amended but there are also
2512 # changes to other files from the old changeset.
2512 # changes to other files from the old changeset.
2513 if path not in filestoamend:
2513 if path not in filestoamend:
2514 return old.filectx(path)
2514 return old.filectx(path)
2515
2515
2516 # Return None for removed files.
2516 # Return None for removed files.
2517 if path in wctx.removed():
2517 if path in wctx.removed():
2518 return None
2518 return None
2519
2519
2520 fctx = wctx[path]
2520 fctx = wctx[path]
2521 flags = fctx.flags()
2521 flags = fctx.flags()
2522 mctx = context.memfilectx(repo, ctx_,
2522 mctx = context.memfilectx(repo, ctx_,
2523 fctx.path(), fctx.data(),
2523 fctx.path(), fctx.data(),
2524 islink='l' in flags,
2524 islink='l' in flags,
2525 isexec='x' in flags,
2525 isexec='x' in flags,
2526 copied=copied.get(path))
2526 copied=copied.get(path))
2527 return mctx
2527 return mctx
2528 except KeyError:
2528 except KeyError:
2529 return None
2529 return None
2530 else:
2530 else:
2531 ui.note(_('copying changeset %s to %s\n') % (old, base))
2531 ui.note(_('copying changeset %s to %s\n') % (old, base))
2532
2532
2533 # Use version of files as in the old cset
2533 # Use version of files as in the old cset
2534 def filectxfn(repo, ctx_, path):
2534 def filectxfn(repo, ctx_, path):
2535 try:
2535 try:
2536 return old.filectx(path)
2536 return old.filectx(path)
2537 except KeyError:
2537 except KeyError:
2538 return None
2538 return None
2539
2539
2540 # See if we got a message from -m or -l, if not, open the editor with
2540 # See if we got a message from -m or -l, if not, open the editor with
2541 # the message of the changeset to amend.
2541 # the message of the changeset to amend.
2542 message = logmessage(ui, opts)
2542 message = logmessage(ui, opts)
2543
2543
2544 editform = mergeeditform(old, 'commit.amend')
2544 editform = mergeeditform(old, 'commit.amend')
2545 editor = getcommiteditor(editform=editform,
2545 editor = getcommiteditor(editform=editform,
2546 **pycompat.strkwargs(opts))
2546 **pycompat.strkwargs(opts))
2547
2547
2548 if not message:
2548 if not message:
2549 editor = getcommiteditor(edit=True, editform=editform)
2549 editor = getcommiteditor(edit=True, editform=editform)
2550 message = old.description()
2550 message = old.description()
2551
2551
2552 pureextra = extra.copy()
2552 pureextra = extra.copy()
2553 extra['amend_source'] = old.hex()
2553 extra['amend_source'] = old.hex()
2554
2554
2555 new = context.memctx(repo,
2555 new = context.memctx(repo,
2556 parents=[base.node(), old.p2().node()],
2556 parents=[base.node(), old.p2().node()],
2557 text=message,
2557 text=message,
2558 files=files,
2558 files=files,
2559 filectxfn=filectxfn,
2559 filectxfn=filectxfn,
2560 user=user,
2560 user=user,
2561 date=date,
2561 date=date,
2562 extra=extra,
2562 extra=extra,
2563 editor=editor)
2563 editor=editor)
2564
2564
2565 newdesc = changelog.stripdesc(new.description())
2565 newdesc = changelog.stripdesc(new.description())
2566 if ((not changes)
2566 if ((not changes)
2567 and newdesc == old.description()
2567 and newdesc == old.description()
2568 and user == old.user()
2568 and user == old.user()
2569 and (date == old.date() or datemaydiffer)
2569 and (date == old.date() or datemaydiffer)
2570 and pureextra == old.extra()):
2570 and pureextra == old.extra()):
2571 # nothing changed. continuing here would create a new node
2571 # nothing changed. continuing here would create a new node
2572 # anyway because of the amend_source noise.
2572 # anyway because of the amend_source noise.
2573 #
2573 #
2574 # This not what we expect from amend.
2574 # This not what we expect from amend.
2575 return old.node()
2575 return old.node()
2576
2576
2577 commitphase = None
2577 commitphase = None
2578 if opts.get('secret'):
2578 if opts.get('secret'):
2579 commitphase = phases.secret
2579 commitphase = phases.secret
2580 newid = repo.commitctx(new)
2580 newid = repo.commitctx(new)
2581
2581
2582 # Reroute the working copy parent to the new changeset
2582 # Reroute the working copy parent to the new changeset
2583 repo.setparents(newid, nullid)
2583 repo.setparents(newid, nullid)
2584 mapping = {old.node(): (newid,)}
2584 mapping = {old.node(): (newid,)}
2585 obsmetadata = None
2585 obsmetadata = None
2586 if opts.get('note'):
2586 if opts.get('note'):
2587 obsmetadata = {'note': encoding.fromlocal(opts['note'])}
2587 obsmetadata = {'note': encoding.fromlocal(opts['note'])}
2588 backup = ui.configbool('rewrite', 'backup-bundle')
2588 backup = ui.configbool('rewrite', 'backup-bundle')
2589 scmutil.cleanupnodes(repo, mapping, 'amend', metadata=obsmetadata,
2589 scmutil.cleanupnodes(repo, mapping, 'amend', metadata=obsmetadata,
2590 fixphase=True, targetphase=commitphase,
2590 fixphase=True, targetphase=commitphase,
2591 backup=backup)
2591 backup=backup)
2592
2592
2593 # Fixing the dirstate because localrepo.commitctx does not update
2593 # Fixing the dirstate because localrepo.commitctx does not update
2594 # it. This is rather convenient because we did not need to update
2594 # it. This is rather convenient because we did not need to update
2595 # the dirstate for all the files in the new commit which commitctx
2595 # the dirstate for all the files in the new commit which commitctx
2596 # could have done if it updated the dirstate. Now, we can
2596 # could have done if it updated the dirstate. Now, we can
2597 # selectively update the dirstate only for the amended files.
2597 # selectively update the dirstate only for the amended files.
2598 dirstate = repo.dirstate
2598 dirstate = repo.dirstate
2599
2599
2600 # Update the state of the files which were added and
2600 # Update the state of the files which were added and
2601 # and modified in the amend to "normal" in the dirstate.
2601 # and modified in the amend to "normal" in the dirstate.
2602 normalfiles = set(wctx.modified() + wctx.added()) & filestoamend
2602 normalfiles = set(wctx.modified() + wctx.added()) & filestoamend
2603 for f in normalfiles:
2603 for f in normalfiles:
2604 dirstate.normal(f)
2604 dirstate.normal(f)
2605
2605
2606 # Update the state of files which were removed in the amend
2606 # Update the state of files which were removed in the amend
2607 # to "removed" in the dirstate.
2607 # to "removed" in the dirstate.
2608 removedfiles = set(wctx.removed()) & filestoamend
2608 removedfiles = set(wctx.removed()) & filestoamend
2609 for f in removedfiles:
2609 for f in removedfiles:
2610 dirstate.drop(f)
2610 dirstate.drop(f)
2611
2611
2612 return newid
2612 return newid
2613
2613
2614 def commiteditor(repo, ctx, subs, editform=''):
2614 def commiteditor(repo, ctx, subs, editform=''):
2615 if ctx.description():
2615 if ctx.description():
2616 return ctx.description()
2616 return ctx.description()
2617 return commitforceeditor(repo, ctx, subs, editform=editform,
2617 return commitforceeditor(repo, ctx, subs, editform=editform,
2618 unchangedmessagedetection=True)
2618 unchangedmessagedetection=True)
2619
2619
2620 def commitforceeditor(repo, ctx, subs, finishdesc=None, extramsg=None,
2620 def commitforceeditor(repo, ctx, subs, finishdesc=None, extramsg=None,
2621 editform='', unchangedmessagedetection=False):
2621 editform='', unchangedmessagedetection=False):
2622 if not extramsg:
2622 if not extramsg:
2623 extramsg = _("Leave message empty to abort commit.")
2623 extramsg = _("Leave message empty to abort commit.")
2624
2624
2625 forms = [e for e in editform.split('.') if e]
2625 forms = [e for e in editform.split('.') if e]
2626 forms.insert(0, 'changeset')
2626 forms.insert(0, 'changeset')
2627 templatetext = None
2627 templatetext = None
2628 while forms:
2628 while forms:
2629 ref = '.'.join(forms)
2629 ref = '.'.join(forms)
2630 if repo.ui.config('committemplate', ref):
2630 if repo.ui.config('committemplate', ref):
2631 templatetext = committext = buildcommittemplate(
2631 templatetext = committext = buildcommittemplate(
2632 repo, ctx, subs, extramsg, ref)
2632 repo, ctx, subs, extramsg, ref)
2633 break
2633 break
2634 forms.pop()
2634 forms.pop()
2635 else:
2635 else:
2636 committext = buildcommittext(repo, ctx, subs, extramsg)
2636 committext = buildcommittext(repo, ctx, subs, extramsg)
2637
2637
2638 # run editor in the repository root
2638 # run editor in the repository root
2639 olddir = encoding.getcwd()
2639 olddir = encoding.getcwd()
2640 os.chdir(repo.root)
2640 os.chdir(repo.root)
2641
2641
2642 # make in-memory changes visible to external process
2642 # make in-memory changes visible to external process
2643 tr = repo.currenttransaction()
2643 tr = repo.currenttransaction()
2644 repo.dirstate.write(tr)
2644 repo.dirstate.write(tr)
2645 pending = tr and tr.writepending() and repo.root
2645 pending = tr and tr.writepending() and repo.root
2646
2646
2647 editortext = repo.ui.edit(committext, ctx.user(), ctx.extra(),
2647 editortext = repo.ui.edit(committext, ctx.user(), ctx.extra(),
2648 editform=editform, pending=pending,
2648 editform=editform, pending=pending,
2649 repopath=repo.path, action='commit')
2649 repopath=repo.path, action='commit')
2650 text = editortext
2650 text = editortext
2651
2651
2652 # strip away anything below this special string (used for editors that want
2652 # strip away anything below this special string (used for editors that want
2653 # to display the diff)
2653 # to display the diff)
2654 stripbelow = re.search(_linebelow, text, flags=re.MULTILINE)
2654 stripbelow = re.search(_linebelow, text, flags=re.MULTILINE)
2655 if stripbelow:
2655 if stripbelow:
2656 text = text[:stripbelow.start()]
2656 text = text[:stripbelow.start()]
2657
2657
2658 text = re.sub("(?m)^HG:.*(\n|$)", "", text)
2658 text = re.sub("(?m)^HG:.*(\n|$)", "", text)
2659 os.chdir(olddir)
2659 os.chdir(olddir)
2660
2660
2661 if finishdesc:
2661 if finishdesc:
2662 text = finishdesc(text)
2662 text = finishdesc(text)
2663 if not text.strip():
2663 if not text.strip():
2664 raise error.Abort(_("empty commit message"))
2664 raise error.Abort(_("empty commit message"))
2665 if unchangedmessagedetection and editortext == templatetext:
2665 if unchangedmessagedetection and editortext == templatetext:
2666 raise error.Abort(_("commit message unchanged"))
2666 raise error.Abort(_("commit message unchanged"))
2667
2667
2668 return text
2668 return text
2669
2669
2670 def buildcommittemplate(repo, ctx, subs, extramsg, ref):
2670 def buildcommittemplate(repo, ctx, subs, extramsg, ref):
2671 ui = repo.ui
2671 ui = repo.ui
2672 spec = formatter.templatespec(ref, None, None)
2672 spec = formatter.templatespec(ref, None, None)
2673 t = logcmdutil.changesettemplater(ui, repo, spec)
2673 t = logcmdutil.changesettemplater(ui, repo, spec)
2674 t.t.cache.update((k, templater.unquotestring(v))
2674 t.t.cache.update((k, templater.unquotestring(v))
2675 for k, v in repo.ui.configitems('committemplate'))
2675 for k, v in repo.ui.configitems('committemplate'))
2676
2676
2677 if not extramsg:
2677 if not extramsg:
2678 extramsg = '' # ensure that extramsg is string
2678 extramsg = '' # ensure that extramsg is string
2679
2679
2680 ui.pushbuffer()
2680 ui.pushbuffer()
2681 t.show(ctx, extramsg=extramsg)
2681 t.show(ctx, extramsg=extramsg)
2682 return ui.popbuffer()
2682 return ui.popbuffer()
2683
2683
2684 def hgprefix(msg):
2684 def hgprefix(msg):
2685 return "\n".join(["HG: %s" % a for a in msg.split("\n") if a])
2685 return "\n".join(["HG: %s" % a for a in msg.split("\n") if a])
2686
2686
2687 def buildcommittext(repo, ctx, subs, extramsg):
2687 def buildcommittext(repo, ctx, subs, extramsg):
2688 edittext = []
2688 edittext = []
2689 modified, added, removed = ctx.modified(), ctx.added(), ctx.removed()
2689 modified, added, removed = ctx.modified(), ctx.added(), ctx.removed()
2690 if ctx.description():
2690 if ctx.description():
2691 edittext.append(ctx.description())
2691 edittext.append(ctx.description())
2692 edittext.append("")
2692 edittext.append("")
2693 edittext.append("") # Empty line between message and comments.
2693 edittext.append("") # Empty line between message and comments.
2694 edittext.append(hgprefix(_("Enter commit message."
2694 edittext.append(hgprefix(_("Enter commit message."
2695 " Lines beginning with 'HG:' are removed.")))
2695 " Lines beginning with 'HG:' are removed.")))
2696 edittext.append(hgprefix(extramsg))
2696 edittext.append(hgprefix(extramsg))
2697 edittext.append("HG: --")
2697 edittext.append("HG: --")
2698 edittext.append(hgprefix(_("user: %s") % ctx.user()))
2698 edittext.append(hgprefix(_("user: %s") % ctx.user()))
2699 if ctx.p2():
2699 if ctx.p2():
2700 edittext.append(hgprefix(_("branch merge")))
2700 edittext.append(hgprefix(_("branch merge")))
2701 if ctx.branch():
2701 if ctx.branch():
2702 edittext.append(hgprefix(_("branch '%s'") % ctx.branch()))
2702 edittext.append(hgprefix(_("branch '%s'") % ctx.branch()))
2703 if bookmarks.isactivewdirparent(repo):
2703 if bookmarks.isactivewdirparent(repo):
2704 edittext.append(hgprefix(_("bookmark '%s'") % repo._activebookmark))
2704 edittext.append(hgprefix(_("bookmark '%s'") % repo._activebookmark))
2705 edittext.extend([hgprefix(_("subrepo %s") % s) for s in subs])
2705 edittext.extend([hgprefix(_("subrepo %s") % s) for s in subs])
2706 edittext.extend([hgprefix(_("added %s") % f) for f in added])
2706 edittext.extend([hgprefix(_("added %s") % f) for f in added])
2707 edittext.extend([hgprefix(_("changed %s") % f) for f in modified])
2707 edittext.extend([hgprefix(_("changed %s") % f) for f in modified])
2708 edittext.extend([hgprefix(_("removed %s") % f) for f in removed])
2708 edittext.extend([hgprefix(_("removed %s") % f) for f in removed])
2709 if not added and not modified and not removed:
2709 if not added and not modified and not removed:
2710 edittext.append(hgprefix(_("no files changed")))
2710 edittext.append(hgprefix(_("no files changed")))
2711 edittext.append("")
2711 edittext.append("")
2712
2712
2713 return "\n".join(edittext)
2713 return "\n".join(edittext)
2714
2714
2715 def commitstatus(repo, node, branch, bheads=None, opts=None):
2715 def commitstatus(repo, node, branch, bheads=None, opts=None):
2716 if opts is None:
2716 if opts is None:
2717 opts = {}
2717 opts = {}
2718 ctx = repo[node]
2718 ctx = repo[node]
2719 parents = ctx.parents()
2719 parents = ctx.parents()
2720
2720
2721 if (not opts.get('amend') and bheads and node not in bheads and not
2721 if (not opts.get('amend') and bheads and node not in bheads and not
2722 [x for x in parents if x.node() in bheads and x.branch() == branch]):
2722 [x for x in parents if x.node() in bheads and x.branch() == branch]):
2723 repo.ui.status(_('created new head\n'))
2723 repo.ui.status(_('created new head\n'))
2724 # The message is not printed for initial roots. For the other
2724 # The message is not printed for initial roots. For the other
2725 # changesets, it is printed in the following situations:
2725 # changesets, it is printed in the following situations:
2726 #
2726 #
2727 # Par column: for the 2 parents with ...
2727 # Par column: for the 2 parents with ...
2728 # N: null or no parent
2728 # N: null or no parent
2729 # B: parent is on another named branch
2729 # B: parent is on another named branch
2730 # C: parent is a regular non head changeset
2730 # C: parent is a regular non head changeset
2731 # H: parent was a branch head of the current branch
2731 # H: parent was a branch head of the current branch
2732 # Msg column: whether we print "created new head" message
2732 # Msg column: whether we print "created new head" message
2733 # In the following, it is assumed that there already exists some
2733 # In the following, it is assumed that there already exists some
2734 # initial branch heads of the current branch, otherwise nothing is
2734 # initial branch heads of the current branch, otherwise nothing is
2735 # printed anyway.
2735 # printed anyway.
2736 #
2736 #
2737 # Par Msg Comment
2737 # Par Msg Comment
2738 # N N y additional topo root
2738 # N N y additional topo root
2739 #
2739 #
2740 # B N y additional branch root
2740 # B N y additional branch root
2741 # C N y additional topo head
2741 # C N y additional topo head
2742 # H N n usual case
2742 # H N n usual case
2743 #
2743 #
2744 # B B y weird additional branch root
2744 # B B y weird additional branch root
2745 # C B y branch merge
2745 # C B y branch merge
2746 # H B n merge with named branch
2746 # H B n merge with named branch
2747 #
2747 #
2748 # C C y additional head from merge
2748 # C C y additional head from merge
2749 # C H n merge with a head
2749 # C H n merge with a head
2750 #
2750 #
2751 # H H n head merge: head count decreases
2751 # H H n head merge: head count decreases
2752
2752
2753 if not opts.get('close_branch'):
2753 if not opts.get('close_branch'):
2754 for r in parents:
2754 for r in parents:
2755 if r.closesbranch() and r.branch() == branch:
2755 if r.closesbranch() and r.branch() == branch:
2756 repo.ui.status(_('reopening closed branch head %d\n') % r.rev())
2756 repo.ui.status(_('reopening closed branch head %d\n') % r.rev())
2757
2757
2758 if repo.ui.debugflag:
2758 if repo.ui.debugflag:
2759 repo.ui.write(_('committed changeset %d:%s\n') % (ctx.rev(), ctx.hex()))
2759 repo.ui.write(_('committed changeset %d:%s\n') % (ctx.rev(), ctx.hex()))
2760 elif repo.ui.verbose:
2760 elif repo.ui.verbose:
2761 repo.ui.write(_('committed changeset %d:%s\n') % (ctx.rev(), ctx))
2761 repo.ui.write(_('committed changeset %d:%s\n') % (ctx.rev(), ctx))
2762
2762
2763 def postcommitstatus(repo, pats, opts):
2763 def postcommitstatus(repo, pats, opts):
2764 return repo.status(match=scmutil.match(repo[None], pats, opts))
2764 return repo.status(match=scmutil.match(repo[None], pats, opts))
2765
2765
2766 def revert(ui, repo, ctx, parents, *pats, **opts):
2766 def revert(ui, repo, ctx, parents, *pats, **opts):
2767 opts = pycompat.byteskwargs(opts)
2767 opts = pycompat.byteskwargs(opts)
2768 parent, p2 = parents
2768 parent, p2 = parents
2769 node = ctx.node()
2769 node = ctx.node()
2770
2770
2771 mf = ctx.manifest()
2771 mf = ctx.manifest()
2772 if node == p2:
2772 if node == p2:
2773 parent = p2
2773 parent = p2
2774
2774
2775 # need all matching names in dirstate and manifest of target rev,
2775 # need all matching names in dirstate and manifest of target rev,
2776 # so have to walk both. do not print errors if files exist in one
2776 # so have to walk both. do not print errors if files exist in one
2777 # but not other. in both cases, filesets should be evaluated against
2777 # but not other. in both cases, filesets should be evaluated against
2778 # workingctx to get consistent result (issue4497). this means 'set:**'
2778 # workingctx to get consistent result (issue4497). this means 'set:**'
2779 # cannot be used to select missing files from target rev.
2779 # cannot be used to select missing files from target rev.
2780
2780
2781 # `names` is a mapping for all elements in working copy and target revision
2781 # `names` is a mapping for all elements in working copy and target revision
2782 # The mapping is in the form:
2782 # The mapping is in the form:
2783 # <abs path in repo> -> (<path from CWD>, <exactly specified by matcher?>)
2783 # <abs path in repo> -> (<path from CWD>, <exactly specified by matcher?>)
2784 names = {}
2784 names = {}
2785
2785
2786 with repo.wlock():
2786 with repo.wlock():
2787 ## filling of the `names` mapping
2787 ## filling of the `names` mapping
2788 # walk dirstate to fill `names`
2788 # walk dirstate to fill `names`
2789
2789
2790 interactive = opts.get('interactive', False)
2790 interactive = opts.get('interactive', False)
2791 wctx = repo[None]
2791 wctx = repo[None]
2792 m = scmutil.match(wctx, pats, opts)
2792 m = scmutil.match(wctx, pats, opts)
2793
2793
2794 # we'll need this later
2794 # we'll need this later
2795 targetsubs = sorted(s for s in wctx.substate if m(s))
2795 targetsubs = sorted(s for s in wctx.substate if m(s))
2796
2796
2797 if not m.always():
2797 if not m.always():
2798 matcher = matchmod.badmatch(m, lambda x, y: False)
2798 matcher = matchmod.badmatch(m, lambda x, y: False)
2799 for abs in wctx.walk(matcher):
2799 for abs in wctx.walk(matcher):
2800 names[abs] = m.rel(abs), m.exact(abs)
2800 names[abs] = m.rel(abs), m.exact(abs)
2801
2801
2802 # walk target manifest to fill `names`
2802 # walk target manifest to fill `names`
2803
2803
2804 def badfn(path, msg):
2804 def badfn(path, msg):
2805 if path in names:
2805 if path in names:
2806 return
2806 return
2807 if path in ctx.substate:
2807 if path in ctx.substate:
2808 return
2808 return
2809 path_ = path + '/'
2809 path_ = path + '/'
2810 for f in names:
2810 for f in names:
2811 if f.startswith(path_):
2811 if f.startswith(path_):
2812 return
2812 return
2813 ui.warn("%s: %s\n" % (m.rel(path), msg))
2813 ui.warn("%s: %s\n" % (m.rel(path), msg))
2814
2814
2815 for abs in ctx.walk(matchmod.badmatch(m, badfn)):
2815 for abs in ctx.walk(matchmod.badmatch(m, badfn)):
2816 if abs not in names:
2816 if abs not in names:
2817 names[abs] = m.rel(abs), m.exact(abs)
2817 names[abs] = m.rel(abs), m.exact(abs)
2818
2818
2819 # Find status of all file in `names`.
2819 # Find status of all file in `names`.
2820 m = scmutil.matchfiles(repo, names)
2820 m = scmutil.matchfiles(repo, names)
2821
2821
2822 changes = repo.status(node1=node, match=m,
2822 changes = repo.status(node1=node, match=m,
2823 unknown=True, ignored=True, clean=True)
2823 unknown=True, ignored=True, clean=True)
2824 else:
2824 else:
2825 changes = repo.status(node1=node, match=m)
2825 changes = repo.status(node1=node, match=m)
2826 for kind in changes:
2826 for kind in changes:
2827 for abs in kind:
2827 for abs in kind:
2828 names[abs] = m.rel(abs), m.exact(abs)
2828 names[abs] = m.rel(abs), m.exact(abs)
2829
2829
2830 m = scmutil.matchfiles(repo, names)
2830 m = scmutil.matchfiles(repo, names)
2831
2831
2832 modified = set(changes.modified)
2832 modified = set(changes.modified)
2833 added = set(changes.added)
2833 added = set(changes.added)
2834 removed = set(changes.removed)
2834 removed = set(changes.removed)
2835 _deleted = set(changes.deleted)
2835 _deleted = set(changes.deleted)
2836 unknown = set(changes.unknown)
2836 unknown = set(changes.unknown)
2837 unknown.update(changes.ignored)
2837 unknown.update(changes.ignored)
2838 clean = set(changes.clean)
2838 clean = set(changes.clean)
2839 modadded = set()
2839 modadded = set()
2840
2840
2841 # We need to account for the state of the file in the dirstate,
2841 # We need to account for the state of the file in the dirstate,
2842 # even when we revert against something else than parent. This will
2842 # even when we revert against something else than parent. This will
2843 # slightly alter the behavior of revert (doing back up or not, delete
2843 # slightly alter the behavior of revert (doing back up or not, delete
2844 # or just forget etc).
2844 # or just forget etc).
2845 if parent == node:
2845 if parent == node:
2846 dsmodified = modified
2846 dsmodified = modified
2847 dsadded = added
2847 dsadded = added
2848 dsremoved = removed
2848 dsremoved = removed
2849 # store all local modifications, useful later for rename detection
2849 # store all local modifications, useful later for rename detection
2850 localchanges = dsmodified | dsadded
2850 localchanges = dsmodified | dsadded
2851 modified, added, removed = set(), set(), set()
2851 modified, added, removed = set(), set(), set()
2852 else:
2852 else:
2853 changes = repo.status(node1=parent, match=m)
2853 changes = repo.status(node1=parent, match=m)
2854 dsmodified = set(changes.modified)
2854 dsmodified = set(changes.modified)
2855 dsadded = set(changes.added)
2855 dsadded = set(changes.added)
2856 dsremoved = set(changes.removed)
2856 dsremoved = set(changes.removed)
2857 # store all local modifications, useful later for rename detection
2857 # store all local modifications, useful later for rename detection
2858 localchanges = dsmodified | dsadded
2858 localchanges = dsmodified | dsadded
2859
2859
2860 # only take into account for removes between wc and target
2860 # only take into account for removes between wc and target
2861 clean |= dsremoved - removed
2861 clean |= dsremoved - removed
2862 dsremoved &= removed
2862 dsremoved &= removed
2863 # distinct between dirstate remove and other
2863 # distinct between dirstate remove and other
2864 removed -= dsremoved
2864 removed -= dsremoved
2865
2865
2866 modadded = added & dsmodified
2866 modadded = added & dsmodified
2867 added -= modadded
2867 added -= modadded
2868
2868
2869 # tell newly modified apart.
2869 # tell newly modified apart.
2870 dsmodified &= modified
2870 dsmodified &= modified
2871 dsmodified |= modified & dsadded # dirstate added may need backup
2871 dsmodified |= modified & dsadded # dirstate added may need backup
2872 modified -= dsmodified
2872 modified -= dsmodified
2873
2873
2874 # We need to wait for some post-processing to update this set
2874 # We need to wait for some post-processing to update this set
2875 # before making the distinction. The dirstate will be used for
2875 # before making the distinction. The dirstate will be used for
2876 # that purpose.
2876 # that purpose.
2877 dsadded = added
2877 dsadded = added
2878
2878
2879 # in case of merge, files that are actually added can be reported as
2879 # in case of merge, files that are actually added can be reported as
2880 # modified, we need to post process the result
2880 # modified, we need to post process the result
2881 if p2 != nullid:
2881 if p2 != nullid:
2882 mergeadd = set(dsmodified)
2882 mergeadd = set(dsmodified)
2883 for path in dsmodified:
2883 for path in dsmodified:
2884 if path in mf:
2884 if path in mf:
2885 mergeadd.remove(path)
2885 mergeadd.remove(path)
2886 dsadded |= mergeadd
2886 dsadded |= mergeadd
2887 dsmodified -= mergeadd
2887 dsmodified -= mergeadd
2888
2888
2889 # if f is a rename, update `names` to also revert the source
2889 # if f is a rename, update `names` to also revert the source
2890 cwd = repo.getcwd()
2890 cwd = repo.getcwd()
2891 for f in localchanges:
2891 for f in localchanges:
2892 src = repo.dirstate.copied(f)
2892 src = repo.dirstate.copied(f)
2893 # XXX should we check for rename down to target node?
2893 # XXX should we check for rename down to target node?
2894 if src and src not in names and repo.dirstate[src] == 'r':
2894 if src and src not in names and repo.dirstate[src] == 'r':
2895 dsremoved.add(src)
2895 dsremoved.add(src)
2896 names[src] = (repo.pathto(src, cwd), True)
2896 names[src] = (repo.pathto(src, cwd), True)
2897
2897
2898 # determine the exact nature of the deleted changesets
2898 # determine the exact nature of the deleted changesets
2899 deladded = set(_deleted)
2899 deladded = set(_deleted)
2900 for path in _deleted:
2900 for path in _deleted:
2901 if path in mf:
2901 if path in mf:
2902 deladded.remove(path)
2902 deladded.remove(path)
2903 deleted = _deleted - deladded
2903 deleted = _deleted - deladded
2904
2904
2905 # distinguish between file to forget and the other
2905 # distinguish between file to forget and the other
2906 added = set()
2906 added = set()
2907 for abs in dsadded:
2907 for abs in dsadded:
2908 if repo.dirstate[abs] != 'a':
2908 if repo.dirstate[abs] != 'a':
2909 added.add(abs)
2909 added.add(abs)
2910 dsadded -= added
2910 dsadded -= added
2911
2911
2912 for abs in deladded:
2912 for abs in deladded:
2913 if repo.dirstate[abs] == 'a':
2913 if repo.dirstate[abs] == 'a':
2914 dsadded.add(abs)
2914 dsadded.add(abs)
2915 deladded -= dsadded
2915 deladded -= dsadded
2916
2916
2917 # For files marked as removed, we check if an unknown file is present at
2917 # For files marked as removed, we check if an unknown file is present at
2918 # the same path. If a such file exists it may need to be backed up.
2918 # the same path. If a such file exists it may need to be backed up.
2919 # Making the distinction at this stage helps have simpler backup
2919 # Making the distinction at this stage helps have simpler backup
2920 # logic.
2920 # logic.
2921 removunk = set()
2921 removunk = set()
2922 for abs in removed:
2922 for abs in removed:
2923 target = repo.wjoin(abs)
2923 target = repo.wjoin(abs)
2924 if os.path.lexists(target):
2924 if os.path.lexists(target):
2925 removunk.add(abs)
2925 removunk.add(abs)
2926 removed -= removunk
2926 removed -= removunk
2927
2927
2928 dsremovunk = set()
2928 dsremovunk = set()
2929 for abs in dsremoved:
2929 for abs in dsremoved:
2930 target = repo.wjoin(abs)
2930 target = repo.wjoin(abs)
2931 if os.path.lexists(target):
2931 if os.path.lexists(target):
2932 dsremovunk.add(abs)
2932 dsremovunk.add(abs)
2933 dsremoved -= dsremovunk
2933 dsremoved -= dsremovunk
2934
2934
2935 # action to be actually performed by revert
2935 # action to be actually performed by revert
2936 # (<list of file>, message>) tuple
2936 # (<list of file>, message>) tuple
2937 actions = {'revert': ([], _('reverting %s\n')),
2937 actions = {'revert': ([], _('reverting %s\n')),
2938 'add': ([], _('adding %s\n')),
2938 'add': ([], _('adding %s\n')),
2939 'remove': ([], _('removing %s\n')),
2939 'remove': ([], _('removing %s\n')),
2940 'drop': ([], _('removing %s\n')),
2940 'drop': ([], _('removing %s\n')),
2941 'forget': ([], _('forgetting %s\n')),
2941 'forget': ([], _('forgetting %s\n')),
2942 'undelete': ([], _('undeleting %s\n')),
2942 'undelete': ([], _('undeleting %s\n')),
2943 'noop': (None, _('no changes needed to %s\n')),
2943 'noop': (None, _('no changes needed to %s\n')),
2944 'unknown': (None, _('file not managed: %s\n')),
2944 'unknown': (None, _('file not managed: %s\n')),
2945 }
2945 }
2946
2946
2947 # "constant" that convey the backup strategy.
2947 # "constant" that convey the backup strategy.
2948 # All set to `discard` if `no-backup` is set do avoid checking
2948 # All set to `discard` if `no-backup` is set do avoid checking
2949 # no_backup lower in the code.
2949 # no_backup lower in the code.
2950 # These values are ordered for comparison purposes
2950 # These values are ordered for comparison purposes
2951 backupinteractive = 3 # do backup if interactively modified
2951 backupinteractive = 3 # do backup if interactively modified
2952 backup = 2 # unconditionally do backup
2952 backup = 2 # unconditionally do backup
2953 check = 1 # check if the existing file differs from target
2953 check = 1 # check if the existing file differs from target
2954 discard = 0 # never do backup
2954 discard = 0 # never do backup
2955 if opts.get('no_backup'):
2955 if opts.get('no_backup'):
2956 backupinteractive = backup = check = discard
2956 backupinteractive = backup = check = discard
2957 if interactive:
2957 if interactive:
2958 dsmodifiedbackup = backupinteractive
2958 dsmodifiedbackup = backupinteractive
2959 else:
2959 else:
2960 dsmodifiedbackup = backup
2960 dsmodifiedbackup = backup
2961 tobackup = set()
2961 tobackup = set()
2962
2962
2963 backupanddel = actions['remove']
2963 backupanddel = actions['remove']
2964 if not opts.get('no_backup'):
2964 if not opts.get('no_backup'):
2965 backupanddel = actions['drop']
2965 backupanddel = actions['drop']
2966
2966
2967 disptable = (
2967 disptable = (
2968 # dispatch table:
2968 # dispatch table:
2969 # file state
2969 # file state
2970 # action
2970 # action
2971 # make backup
2971 # make backup
2972
2972
2973 ## Sets that results that will change file on disk
2973 ## Sets that results that will change file on disk
2974 # Modified compared to target, no local change
2974 # Modified compared to target, no local change
2975 (modified, actions['revert'], discard),
2975 (modified, actions['revert'], discard),
2976 # Modified compared to target, but local file is deleted
2976 # Modified compared to target, but local file is deleted
2977 (deleted, actions['revert'], discard),
2977 (deleted, actions['revert'], discard),
2978 # Modified compared to target, local change
2978 # Modified compared to target, local change
2979 (dsmodified, actions['revert'], dsmodifiedbackup),
2979 (dsmodified, actions['revert'], dsmodifiedbackup),
2980 # Added since target
2980 # Added since target
2981 (added, actions['remove'], discard),
2981 (added, actions['remove'], discard),
2982 # Added in working directory
2982 # Added in working directory
2983 (dsadded, actions['forget'], discard),
2983 (dsadded, actions['forget'], discard),
2984 # Added since target, have local modification
2984 # Added since target, have local modification
2985 (modadded, backupanddel, backup),
2985 (modadded, backupanddel, backup),
2986 # Added since target but file is missing in working directory
2986 # Added since target but file is missing in working directory
2987 (deladded, actions['drop'], discard),
2987 (deladded, actions['drop'], discard),
2988 # Removed since target, before working copy parent
2988 # Removed since target, before working copy parent
2989 (removed, actions['add'], discard),
2989 (removed, actions['add'], discard),
2990 # Same as `removed` but an unknown file exists at the same path
2990 # Same as `removed` but an unknown file exists at the same path
2991 (removunk, actions['add'], check),
2991 (removunk, actions['add'], check),
2992 # Removed since targe, marked as such in working copy parent
2992 # Removed since targe, marked as such in working copy parent
2993 (dsremoved, actions['undelete'], discard),
2993 (dsremoved, actions['undelete'], discard),
2994 # Same as `dsremoved` but an unknown file exists at the same path
2994 # Same as `dsremoved` but an unknown file exists at the same path
2995 (dsremovunk, actions['undelete'], check),
2995 (dsremovunk, actions['undelete'], check),
2996 ## the following sets does not result in any file changes
2996 ## the following sets does not result in any file changes
2997 # File with no modification
2997 # File with no modification
2998 (clean, actions['noop'], discard),
2998 (clean, actions['noop'], discard),
2999 # Existing file, not tracked anywhere
2999 # Existing file, not tracked anywhere
3000 (unknown, actions['unknown'], discard),
3000 (unknown, actions['unknown'], discard),
3001 )
3001 )
3002
3002
3003 for abs, (rel, exact) in sorted(names.items()):
3003 for abs, (rel, exact) in sorted(names.items()):
3004 # target file to be touch on disk (relative to cwd)
3004 # target file to be touch on disk (relative to cwd)
3005 target = repo.wjoin(abs)
3005 target = repo.wjoin(abs)
3006 # search the entry in the dispatch table.
3006 # search the entry in the dispatch table.
3007 # if the file is in any of these sets, it was touched in the working
3007 # if the file is in any of these sets, it was touched in the working
3008 # directory parent and we are sure it needs to be reverted.
3008 # directory parent and we are sure it needs to be reverted.
3009 for table, (xlist, msg), dobackup in disptable:
3009 for table, (xlist, msg), dobackup in disptable:
3010 if abs not in table:
3010 if abs not in table:
3011 continue
3011 continue
3012 if xlist is not None:
3012 if xlist is not None:
3013 xlist.append(abs)
3013 xlist.append(abs)
3014 if dobackup:
3014 if dobackup:
3015 # If in interactive mode, don't automatically create
3015 # If in interactive mode, don't automatically create
3016 # .orig files (issue4793)
3016 # .orig files (issue4793)
3017 if dobackup == backupinteractive:
3017 if dobackup == backupinteractive:
3018 tobackup.add(abs)
3018 tobackup.add(abs)
3019 elif (backup <= dobackup or wctx[abs].cmp(ctx[abs])):
3019 elif (backup <= dobackup or wctx[abs].cmp(ctx[abs])):
3020 bakname = scmutil.origpath(ui, repo, rel)
3020 bakname = scmutil.origpath(ui, repo, rel)
3021 relbakname = os.path.relpath(bakname)
3021 ui.note(_('saving current version of %s as %s\n') %
3022 ui.note(_('saving current version of %s as %s\n') %
3022 (rel, bakname))
3023 (rel, relbakname))
3023 if not opts.get('dry_run'):
3024 if not opts.get('dry_run'):
3024 if interactive:
3025 if interactive:
3025 util.copyfile(target, bakname)
3026 util.copyfile(target, bakname)
3026 else:
3027 else:
3027 util.rename(target, bakname)
3028 util.rename(target, bakname)
3028 if opts.get('dry_run'):
3029 if opts.get('dry_run'):
3029 if ui.verbose or not exact:
3030 if ui.verbose or not exact:
3030 ui.status(msg % rel)
3031 ui.status(msg % rel)
3031 elif exact:
3032 elif exact:
3032 ui.warn(msg % rel)
3033 ui.warn(msg % rel)
3033 break
3034 break
3034
3035
3035 if not opts.get('dry_run'):
3036 if not opts.get('dry_run'):
3036 needdata = ('revert', 'add', 'undelete')
3037 needdata = ('revert', 'add', 'undelete')
3037 oplist = [actions[name][0] for name in needdata]
3038 oplist = [actions[name][0] for name in needdata]
3038 prefetch = scmutil.prefetchfiles
3039 prefetch = scmutil.prefetchfiles
3039 matchfiles = scmutil.matchfiles
3040 matchfiles = scmutil.matchfiles
3040 prefetch(repo, [ctx.rev()],
3041 prefetch(repo, [ctx.rev()],
3041 matchfiles(repo,
3042 matchfiles(repo,
3042 [f for sublist in oplist for f in sublist]))
3043 [f for sublist in oplist for f in sublist]))
3043 _performrevert(repo, parents, ctx, names, actions, interactive,
3044 _performrevert(repo, parents, ctx, names, actions, interactive,
3044 tobackup)
3045 tobackup)
3045
3046
3046 if targetsubs:
3047 if targetsubs:
3047 # Revert the subrepos on the revert list
3048 # Revert the subrepos on the revert list
3048 for sub in targetsubs:
3049 for sub in targetsubs:
3049 try:
3050 try:
3050 wctx.sub(sub).revert(ctx.substate[sub], *pats,
3051 wctx.sub(sub).revert(ctx.substate[sub], *pats,
3051 **pycompat.strkwargs(opts))
3052 **pycompat.strkwargs(opts))
3052 except KeyError:
3053 except KeyError:
3053 raise error.Abort("subrepository '%s' does not exist in %s!"
3054 raise error.Abort("subrepository '%s' does not exist in %s!"
3054 % (sub, short(ctx.node())))
3055 % (sub, short(ctx.node())))
3055
3056
3056 def _performrevert(repo, parents, ctx, names, actions, interactive=False,
3057 def _performrevert(repo, parents, ctx, names, actions, interactive=False,
3057 tobackup=None):
3058 tobackup=None):
3058 """function that actually perform all the actions computed for revert
3059 """function that actually perform all the actions computed for revert
3059
3060
3060 This is an independent function to let extension to plug in and react to
3061 This is an independent function to let extension to plug in and react to
3061 the imminent revert.
3062 the imminent revert.
3062
3063
3063 Make sure you have the working directory locked when calling this function.
3064 Make sure you have the working directory locked when calling this function.
3064 """
3065 """
3065 parent, p2 = parents
3066 parent, p2 = parents
3066 node = ctx.node()
3067 node = ctx.node()
3067 excluded_files = []
3068 excluded_files = []
3068
3069
3069 def checkout(f):
3070 def checkout(f):
3070 fc = ctx[f]
3071 fc = ctx[f]
3071 repo.wwrite(f, fc.data(), fc.flags())
3072 repo.wwrite(f, fc.data(), fc.flags())
3072
3073
3073 def doremove(f):
3074 def doremove(f):
3074 try:
3075 try:
3075 rmdir = repo.ui.configbool('experimental', 'removeemptydirs')
3076 rmdir = repo.ui.configbool('experimental', 'removeemptydirs')
3076 repo.wvfs.unlinkpath(f, rmdir=rmdir)
3077 repo.wvfs.unlinkpath(f, rmdir=rmdir)
3077 except OSError:
3078 except OSError:
3078 pass
3079 pass
3079 repo.dirstate.remove(f)
3080 repo.dirstate.remove(f)
3080
3081
3081 def prntstatusmsg(action, f):
3082 def prntstatusmsg(action, f):
3082 rel, exact = names[f]
3083 rel, exact = names[f]
3083 if repo.ui.verbose or not exact:
3084 if repo.ui.verbose or not exact:
3084 repo.ui.status(actions[action][1] % rel)
3085 repo.ui.status(actions[action][1] % rel)
3085
3086
3086 audit_path = pathutil.pathauditor(repo.root, cached=True)
3087 audit_path = pathutil.pathauditor(repo.root, cached=True)
3087 for f in actions['forget'][0]:
3088 for f in actions['forget'][0]:
3088 if interactive:
3089 if interactive:
3089 choice = repo.ui.promptchoice(
3090 choice = repo.ui.promptchoice(
3090 _("forget added file %s (Yn)?$$ &Yes $$ &No") % f)
3091 _("forget added file %s (Yn)?$$ &Yes $$ &No") % f)
3091 if choice == 0:
3092 if choice == 0:
3092 prntstatusmsg('forget', f)
3093 prntstatusmsg('forget', f)
3093 repo.dirstate.drop(f)
3094 repo.dirstate.drop(f)
3094 else:
3095 else:
3095 excluded_files.append(f)
3096 excluded_files.append(f)
3096 else:
3097 else:
3097 prntstatusmsg('forget', f)
3098 prntstatusmsg('forget', f)
3098 repo.dirstate.drop(f)
3099 repo.dirstate.drop(f)
3099 for f in actions['remove'][0]:
3100 for f in actions['remove'][0]:
3100 audit_path(f)
3101 audit_path(f)
3101 if interactive:
3102 if interactive:
3102 choice = repo.ui.promptchoice(
3103 choice = repo.ui.promptchoice(
3103 _("remove added file %s (Yn)?$$ &Yes $$ &No") % f)
3104 _("remove added file %s (Yn)?$$ &Yes $$ &No") % f)
3104 if choice == 0:
3105 if choice == 0:
3105 prntstatusmsg('remove', f)
3106 prntstatusmsg('remove', f)
3106 doremove(f)
3107 doremove(f)
3107 else:
3108 else:
3108 excluded_files.append(f)
3109 excluded_files.append(f)
3109 else:
3110 else:
3110 prntstatusmsg('remove', f)
3111 prntstatusmsg('remove', f)
3111 doremove(f)
3112 doremove(f)
3112 for f in actions['drop'][0]:
3113 for f in actions['drop'][0]:
3113 audit_path(f)
3114 audit_path(f)
3114 prntstatusmsg('drop', f)
3115 prntstatusmsg('drop', f)
3115 repo.dirstate.remove(f)
3116 repo.dirstate.remove(f)
3116
3117
3117 normal = None
3118 normal = None
3118 if node == parent:
3119 if node == parent:
3119 # We're reverting to our parent. If possible, we'd like status
3120 # We're reverting to our parent. If possible, we'd like status
3120 # to report the file as clean. We have to use normallookup for
3121 # to report the file as clean. We have to use normallookup for
3121 # merges to avoid losing information about merged/dirty files.
3122 # merges to avoid losing information about merged/dirty files.
3122 if p2 != nullid:
3123 if p2 != nullid:
3123 normal = repo.dirstate.normallookup
3124 normal = repo.dirstate.normallookup
3124 else:
3125 else:
3125 normal = repo.dirstate.normal
3126 normal = repo.dirstate.normal
3126
3127
3127 newlyaddedandmodifiedfiles = set()
3128 newlyaddedandmodifiedfiles = set()
3128 if interactive:
3129 if interactive:
3129 # Prompt the user for changes to revert
3130 # Prompt the user for changes to revert
3130 torevert = [f for f in actions['revert'][0] if f not in excluded_files]
3131 torevert = [f for f in actions['revert'][0] if f not in excluded_files]
3131 m = scmutil.matchfiles(repo, torevert)
3132 m = scmutil.matchfiles(repo, torevert)
3132 diffopts = patch.difffeatureopts(repo.ui, whitespace=True,
3133 diffopts = patch.difffeatureopts(repo.ui, whitespace=True,
3133 section='commands',
3134 section='commands',
3134 configprefix='revert.interactive.')
3135 configprefix='revert.interactive.')
3135 diffopts.nodates = True
3136 diffopts.nodates = True
3136 diffopts.git = True
3137 diffopts.git = True
3137 operation = 'discard'
3138 operation = 'discard'
3138 reversehunks = True
3139 reversehunks = True
3139 if node != parent:
3140 if node != parent:
3140 operation = 'apply'
3141 operation = 'apply'
3141 reversehunks = False
3142 reversehunks = False
3142 if reversehunks:
3143 if reversehunks:
3143 diff = patch.diff(repo, ctx.node(), None, m, opts=diffopts)
3144 diff = patch.diff(repo, ctx.node(), None, m, opts=diffopts)
3144 else:
3145 else:
3145 diff = patch.diff(repo, None, ctx.node(), m, opts=diffopts)
3146 diff = patch.diff(repo, None, ctx.node(), m, opts=diffopts)
3146 originalchunks = patch.parsepatch(diff)
3147 originalchunks = patch.parsepatch(diff)
3147
3148
3148 try:
3149 try:
3149
3150
3150 chunks, opts = recordfilter(repo.ui, originalchunks,
3151 chunks, opts = recordfilter(repo.ui, originalchunks,
3151 operation=operation)
3152 operation=operation)
3152 if reversehunks:
3153 if reversehunks:
3153 chunks = patch.reversehunks(chunks)
3154 chunks = patch.reversehunks(chunks)
3154
3155
3155 except error.PatchError as err:
3156 except error.PatchError as err:
3156 raise error.Abort(_('error parsing patch: %s') % err)
3157 raise error.Abort(_('error parsing patch: %s') % err)
3157
3158
3158 newlyaddedandmodifiedfiles = newandmodified(chunks, originalchunks)
3159 newlyaddedandmodifiedfiles = newandmodified(chunks, originalchunks)
3159 if tobackup is None:
3160 if tobackup is None:
3160 tobackup = set()
3161 tobackup = set()
3161 # Apply changes
3162 # Apply changes
3162 fp = stringio()
3163 fp = stringio()
3163 # chunks are serialized per file, but files aren't sorted
3164 # chunks are serialized per file, but files aren't sorted
3164 for f in sorted(set(c.header.filename() for c in chunks if ishunk(c))):
3165 for f in sorted(set(c.header.filename() for c in chunks if ishunk(c))):
3165 prntstatusmsg('revert', f)
3166 prntstatusmsg('revert', f)
3166 for c in chunks:
3167 for c in chunks:
3167 if ishunk(c):
3168 if ishunk(c):
3168 abs = c.header.filename()
3169 abs = c.header.filename()
3169 # Create a backup file only if this hunk should be backed up
3170 # Create a backup file only if this hunk should be backed up
3170 if c.header.filename() in tobackup:
3171 if c.header.filename() in tobackup:
3171 target = repo.wjoin(abs)
3172 target = repo.wjoin(abs)
3172 bakname = scmutil.origpath(repo.ui, repo, m.rel(abs))
3173 bakname = scmutil.origpath(repo.ui, repo, m.rel(abs))
3173 util.copyfile(target, bakname)
3174 util.copyfile(target, bakname)
3174 tobackup.remove(abs)
3175 tobackup.remove(abs)
3175 c.write(fp)
3176 c.write(fp)
3176 dopatch = fp.tell()
3177 dopatch = fp.tell()
3177 fp.seek(0)
3178 fp.seek(0)
3178 if dopatch:
3179 if dopatch:
3179 try:
3180 try:
3180 patch.internalpatch(repo.ui, repo, fp, 1, eolmode=None)
3181 patch.internalpatch(repo.ui, repo, fp, 1, eolmode=None)
3181 except error.PatchError as err:
3182 except error.PatchError as err:
3182 raise error.Abort(pycompat.bytestr(err))
3183 raise error.Abort(pycompat.bytestr(err))
3183 del fp
3184 del fp
3184 else:
3185 else:
3185 for f in actions['revert'][0]:
3186 for f in actions['revert'][0]:
3186 prntstatusmsg('revert', f)
3187 prntstatusmsg('revert', f)
3187 checkout(f)
3188 checkout(f)
3188 if normal:
3189 if normal:
3189 normal(f)
3190 normal(f)
3190
3191
3191 for f in actions['add'][0]:
3192 for f in actions['add'][0]:
3192 # Don't checkout modified files, they are already created by the diff
3193 # Don't checkout modified files, they are already created by the diff
3193 if f not in newlyaddedandmodifiedfiles:
3194 if f not in newlyaddedandmodifiedfiles:
3194 prntstatusmsg('add', f)
3195 prntstatusmsg('add', f)
3195 checkout(f)
3196 checkout(f)
3196 repo.dirstate.add(f)
3197 repo.dirstate.add(f)
3197
3198
3198 normal = repo.dirstate.normallookup
3199 normal = repo.dirstate.normallookup
3199 if node == parent and p2 == nullid:
3200 if node == parent and p2 == nullid:
3200 normal = repo.dirstate.normal
3201 normal = repo.dirstate.normal
3201 for f in actions['undelete'][0]:
3202 for f in actions['undelete'][0]:
3202 if interactive:
3203 if interactive:
3203 choice = repo.ui.promptchoice(
3204 choice = repo.ui.promptchoice(
3204 _("add back removed file %s (Yn)?$$ &Yes $$ &No") % f)
3205 _("add back removed file %s (Yn)?$$ &Yes $$ &No") % f)
3205 if choice == 0:
3206 if choice == 0:
3206 prntstatusmsg('undelete', f)
3207 prntstatusmsg('undelete', f)
3207 checkout(f)
3208 checkout(f)
3208 normal(f)
3209 normal(f)
3209 else:
3210 else:
3210 excluded_files.append(f)
3211 excluded_files.append(f)
3211 else:
3212 else:
3212 prntstatusmsg('undelete', f)
3213 prntstatusmsg('undelete', f)
3213 checkout(f)
3214 checkout(f)
3214 normal(f)
3215 normal(f)
3215
3216
3216 copied = copies.pathcopies(repo[parent], ctx)
3217 copied = copies.pathcopies(repo[parent], ctx)
3217
3218
3218 for f in actions['add'][0] + actions['undelete'][0] + actions['revert'][0]:
3219 for f in actions['add'][0] + actions['undelete'][0] + actions['revert'][0]:
3219 if f in copied:
3220 if f in copied:
3220 repo.dirstate.copy(copied[f], f)
3221 repo.dirstate.copy(copied[f], f)
3221
3222
3222 # a list of (ui, repo, otherpeer, opts, missing) functions called by
3223 # a list of (ui, repo, otherpeer, opts, missing) functions called by
3223 # commands.outgoing. "missing" is "missing" of the result of
3224 # commands.outgoing. "missing" is "missing" of the result of
3224 # "findcommonoutgoing()"
3225 # "findcommonoutgoing()"
3225 outgoinghooks = util.hooks()
3226 outgoinghooks = util.hooks()
3226
3227
3227 # a list of (ui, repo) functions called by commands.summary
3228 # a list of (ui, repo) functions called by commands.summary
3228 summaryhooks = util.hooks()
3229 summaryhooks = util.hooks()
3229
3230
3230 # a list of (ui, repo, opts, changes) functions called by commands.summary.
3231 # a list of (ui, repo, opts, changes) functions called by commands.summary.
3231 #
3232 #
3232 # functions should return tuple of booleans below, if 'changes' is None:
3233 # functions should return tuple of booleans below, if 'changes' is None:
3233 # (whether-incomings-are-needed, whether-outgoings-are-needed)
3234 # (whether-incomings-are-needed, whether-outgoings-are-needed)
3234 #
3235 #
3235 # otherwise, 'changes' is a tuple of tuples below:
3236 # otherwise, 'changes' is a tuple of tuples below:
3236 # - (sourceurl, sourcebranch, sourcepeer, incoming)
3237 # - (sourceurl, sourcebranch, sourcepeer, incoming)
3237 # - (desturl, destbranch, destpeer, outgoing)
3238 # - (desturl, destbranch, destpeer, outgoing)
3238 summaryremotehooks = util.hooks()
3239 summaryremotehooks = util.hooks()
3239
3240
3240 # A list of state files kept by multistep operations like graft.
3241 # A list of state files kept by multistep operations like graft.
3241 # Since graft cannot be aborted, it is considered 'clearable' by update.
3242 # Since graft cannot be aborted, it is considered 'clearable' by update.
3242 # note: bisect is intentionally excluded
3243 # note: bisect is intentionally excluded
3243 # (state file, clearable, allowcommit, error, hint)
3244 # (state file, clearable, allowcommit, error, hint)
3244 unfinishedstates = [
3245 unfinishedstates = [
3245 ('graftstate', True, False, _('graft in progress'),
3246 ('graftstate', True, False, _('graft in progress'),
3246 _("use 'hg graft --continue' or 'hg graft --stop' to stop")),
3247 _("use 'hg graft --continue' or 'hg graft --stop' to stop")),
3247 ('updatestate', True, False, _('last update was interrupted'),
3248 ('updatestate', True, False, _('last update was interrupted'),
3248 _("use 'hg update' to get a consistent checkout"))
3249 _("use 'hg update' to get a consistent checkout"))
3249 ]
3250 ]
3250
3251
3251 def checkunfinished(repo, commit=False):
3252 def checkunfinished(repo, commit=False):
3252 '''Look for an unfinished multistep operation, like graft, and abort
3253 '''Look for an unfinished multistep operation, like graft, and abort
3253 if found. It's probably good to check this right before
3254 if found. It's probably good to check this right before
3254 bailifchanged().
3255 bailifchanged().
3255 '''
3256 '''
3256 # Check for non-clearable states first, so things like rebase will take
3257 # Check for non-clearable states first, so things like rebase will take
3257 # precedence over update.
3258 # precedence over update.
3258 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3259 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3259 if clearable or (commit and allowcommit):
3260 if clearable or (commit and allowcommit):
3260 continue
3261 continue
3261 if repo.vfs.exists(f):
3262 if repo.vfs.exists(f):
3262 raise error.Abort(msg, hint=hint)
3263 raise error.Abort(msg, hint=hint)
3263
3264
3264 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3265 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3265 if not clearable or (commit and allowcommit):
3266 if not clearable or (commit and allowcommit):
3266 continue
3267 continue
3267 if repo.vfs.exists(f):
3268 if repo.vfs.exists(f):
3268 raise error.Abort(msg, hint=hint)
3269 raise error.Abort(msg, hint=hint)
3269
3270
3270 def clearunfinished(repo):
3271 def clearunfinished(repo):
3271 '''Check for unfinished operations (as above), and clear the ones
3272 '''Check for unfinished operations (as above), and clear the ones
3272 that are clearable.
3273 that are clearable.
3273 '''
3274 '''
3274 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3275 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3275 if not clearable and repo.vfs.exists(f):
3276 if not clearable and repo.vfs.exists(f):
3276 raise error.Abort(msg, hint=hint)
3277 raise error.Abort(msg, hint=hint)
3277 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3278 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3278 if clearable and repo.vfs.exists(f):
3279 if clearable and repo.vfs.exists(f):
3279 util.unlink(repo.vfs.join(f))
3280 util.unlink(repo.vfs.join(f))
3280
3281
3281 afterresolvedstates = [
3282 afterresolvedstates = [
3282 ('graftstate',
3283 ('graftstate',
3283 _('hg graft --continue')),
3284 _('hg graft --continue')),
3284 ]
3285 ]
3285
3286
3286 def howtocontinue(repo):
3287 def howtocontinue(repo):
3287 '''Check for an unfinished operation and return the command to finish
3288 '''Check for an unfinished operation and return the command to finish
3288 it.
3289 it.
3289
3290
3290 afterresolvedstates tuples define a .hg/{file} and the corresponding
3291 afterresolvedstates tuples define a .hg/{file} and the corresponding
3291 command needed to finish it.
3292 command needed to finish it.
3292
3293
3293 Returns a (msg, warning) tuple. 'msg' is a string and 'warning' is
3294 Returns a (msg, warning) tuple. 'msg' is a string and 'warning' is
3294 a boolean.
3295 a boolean.
3295 '''
3296 '''
3296 contmsg = _("continue: %s")
3297 contmsg = _("continue: %s")
3297 for f, msg in afterresolvedstates:
3298 for f, msg in afterresolvedstates:
3298 if repo.vfs.exists(f):
3299 if repo.vfs.exists(f):
3299 return contmsg % msg, True
3300 return contmsg % msg, True
3300 if repo[None].dirty(missing=True, merge=False, branch=False):
3301 if repo[None].dirty(missing=True, merge=False, branch=False):
3301 return contmsg % _("hg commit"), False
3302 return contmsg % _("hg commit"), False
3302 return None, None
3303 return None, None
3303
3304
3304 def checkafterresolved(repo):
3305 def checkafterresolved(repo):
3305 '''Inform the user about the next action after completing hg resolve
3306 '''Inform the user about the next action after completing hg resolve
3306
3307
3307 If there's a matching afterresolvedstates, howtocontinue will yield
3308 If there's a matching afterresolvedstates, howtocontinue will yield
3308 repo.ui.warn as the reporter.
3309 repo.ui.warn as the reporter.
3309
3310
3310 Otherwise, it will yield repo.ui.note.
3311 Otherwise, it will yield repo.ui.note.
3311 '''
3312 '''
3312 msg, warning = howtocontinue(repo)
3313 msg, warning = howtocontinue(repo)
3313 if msg is not None:
3314 if msg is not None:
3314 if warning:
3315 if warning:
3315 repo.ui.warn("%s\n" % msg)
3316 repo.ui.warn("%s\n" % msg)
3316 else:
3317 else:
3317 repo.ui.note("%s\n" % msg)
3318 repo.ui.note("%s\n" % msg)
3318
3319
3319 def wrongtooltocontinue(repo, task):
3320 def wrongtooltocontinue(repo, task):
3320 '''Raise an abort suggesting how to properly continue if there is an
3321 '''Raise an abort suggesting how to properly continue if there is an
3321 active task.
3322 active task.
3322
3323
3323 Uses howtocontinue() to find the active task.
3324 Uses howtocontinue() to find the active task.
3324
3325
3325 If there's no task (repo.ui.note for 'hg commit'), it does not offer
3326 If there's no task (repo.ui.note for 'hg commit'), it does not offer
3326 a hint.
3327 a hint.
3327 '''
3328 '''
3328 after = howtocontinue(repo)
3329 after = howtocontinue(repo)
3329 hint = None
3330 hint = None
3330 if after[1]:
3331 if after[1]:
3331 hint = after[0]
3332 hint = after[0]
3332 raise error.Abort(_('no %s in progress') % task, hint=hint)
3333 raise error.Abort(_('no %s in progress') % task, hint=hint)
@@ -1,1315 +1,1315 b''
1 This file contains testcases that tend to be related to special cases or less
1 This file contains testcases that tend to be related to special cases or less
2 common commands affecting largefile.
2 common commands affecting largefile.
3
3
4 $ hg init requirements
4 $ hg init requirements
5 $ cd requirements
5 $ cd requirements
6
6
7 # largefiles not loaded by default.
7 # largefiles not loaded by default.
8
8
9 $ hg config extensions
9 $ hg config extensions
10 [1]
10 [1]
11
11
12 # Adding largefiles to requires file will auto-load largefiles extension.
12 # Adding largefiles to requires file will auto-load largefiles extension.
13
13
14 $ echo largefiles >> .hg/requires
14 $ echo largefiles >> .hg/requires
15 $ hg config extensions
15 $ hg config extensions
16 extensions.largefiles=
16 extensions.largefiles=
17
17
18 # But only if there is no config entry for the extension already.
18 # But only if there is no config entry for the extension already.
19
19
20 $ cat > .hg/hgrc << EOF
20 $ cat > .hg/hgrc << EOF
21 > [extensions]
21 > [extensions]
22 > largefiles=!
22 > largefiles=!
23 > EOF
23 > EOF
24
24
25 $ hg config extensions
25 $ hg config extensions
26 abort: repository requires features unknown to this Mercurial: largefiles!
26 abort: repository requires features unknown to this Mercurial: largefiles!
27 (see https://mercurial-scm.org/wiki/MissingRequirement for more information)
27 (see https://mercurial-scm.org/wiki/MissingRequirement for more information)
28 [255]
28 [255]
29
29
30 $ cat > .hg/hgrc << EOF
30 $ cat > .hg/hgrc << EOF
31 > [extensions]
31 > [extensions]
32 > largefiles=
32 > largefiles=
33 > EOF
33 > EOF
34
34
35 $ hg config extensions
35 $ hg config extensions
36 extensions.largefiles=
36 extensions.largefiles=
37
37
38 $ cat > .hg/hgrc << EOF
38 $ cat > .hg/hgrc << EOF
39 > [extensions]
39 > [extensions]
40 > largefiles = missing.py
40 > largefiles = missing.py
41 > EOF
41 > EOF
42
42
43 $ hg config extensions
43 $ hg config extensions
44 *** failed to import extension largefiles from missing.py: [Errno 2] $ENOENT$: 'missing.py'
44 *** failed to import extension largefiles from missing.py: [Errno 2] $ENOENT$: 'missing.py'
45 abort: repository requires features unknown to this Mercurial: largefiles!
45 abort: repository requires features unknown to this Mercurial: largefiles!
46 (see https://mercurial-scm.org/wiki/MissingRequirement for more information)
46 (see https://mercurial-scm.org/wiki/MissingRequirement for more information)
47 [255]
47 [255]
48
48
49 $ cd ..
49 $ cd ..
50
50
51 Each sections should be independent of each others.
51 Each sections should be independent of each others.
52
52
53 $ USERCACHE="$TESTTMP/cache"; export USERCACHE
53 $ USERCACHE="$TESTTMP/cache"; export USERCACHE
54 $ mkdir "${USERCACHE}"
54 $ mkdir "${USERCACHE}"
55 $ cat >> $HGRCPATH <<EOF
55 $ cat >> $HGRCPATH <<EOF
56 > [extensions]
56 > [extensions]
57 > largefiles=
57 > largefiles=
58 > purge=
58 > purge=
59 > rebase=
59 > rebase=
60 > transplant=
60 > transplant=
61 > [phases]
61 > [phases]
62 > publish=False
62 > publish=False
63 > [largefiles]
63 > [largefiles]
64 > minsize=2
64 > minsize=2
65 > patterns=glob:**.dat
65 > patterns=glob:**.dat
66 > usercache=${USERCACHE}
66 > usercache=${USERCACHE}
67 > [hooks]
67 > [hooks]
68 > precommit=sh -c "echo \\"Invoking status precommit hook\\"; hg status"
68 > precommit=sh -c "echo \\"Invoking status precommit hook\\"; hg status"
69 > EOF
69 > EOF
70
70
71
71
72
72
73 Test copies and moves from a directory other than root (issue3516)
73 Test copies and moves from a directory other than root (issue3516)
74 =========================================================================
74 =========================================================================
75
75
76 $ hg init lf_cpmv
76 $ hg init lf_cpmv
77 $ cd lf_cpmv
77 $ cd lf_cpmv
78 $ mkdir dira
78 $ mkdir dira
79 $ mkdir dira/dirb
79 $ mkdir dira/dirb
80 $ touch dira/dirb/largefile
80 $ touch dira/dirb/largefile
81 $ hg add --large dira/dirb/largefile
81 $ hg add --large dira/dirb/largefile
82 $ hg commit -m "added"
82 $ hg commit -m "added"
83 Invoking status precommit hook
83 Invoking status precommit hook
84 A dira/dirb/largefile
84 A dira/dirb/largefile
85 $ cd dira
85 $ cd dira
86 $ hg cp dirb/largefile foo/largefile
86 $ hg cp dirb/largefile foo/largefile
87
87
88 TODO: Ideally, this should mention the largefile, not the standin
88 TODO: Ideally, this should mention the largefile, not the standin
89 $ hg log -T '{rev}\n' --stat 'set:clean()'
89 $ hg log -T '{rev}\n' --stat 'set:clean()'
90 0
90 0
91 .hglf/dira/dirb/largefile | 1 +
91 .hglf/dira/dirb/largefile | 1 +
92 1 files changed, 1 insertions(+), 0 deletions(-)
92 1 files changed, 1 insertions(+), 0 deletions(-)
93
93
94 $ hg ci -m "deep copy"
94 $ hg ci -m "deep copy"
95 Invoking status precommit hook
95 Invoking status precommit hook
96 A dira/foo/largefile
96 A dira/foo/largefile
97 $ find . | sort
97 $ find . | sort
98 .
98 .
99 ./dirb
99 ./dirb
100 ./dirb/largefile
100 ./dirb/largefile
101 ./foo
101 ./foo
102 ./foo/largefile
102 ./foo/largefile
103 $ hg mv foo/largefile baz/largefile
103 $ hg mv foo/largefile baz/largefile
104 $ hg ci -m "moved"
104 $ hg ci -m "moved"
105 Invoking status precommit hook
105 Invoking status precommit hook
106 A dira/baz/largefile
106 A dira/baz/largefile
107 R dira/foo/largefile
107 R dira/foo/largefile
108 $ find . | sort
108 $ find . | sort
109 .
109 .
110 ./baz
110 ./baz
111 ./baz/largefile
111 ./baz/largefile
112 ./dirb
112 ./dirb
113 ./dirb/largefile
113 ./dirb/largefile
114 $ cd ..
114 $ cd ..
115 $ hg mv dira dirc
115 $ hg mv dira dirc
116 moving .hglf/dira/baz/largefile to .hglf/dirc/baz/largefile
116 moving .hglf/dira/baz/largefile to .hglf/dirc/baz/largefile
117 moving .hglf/dira/dirb/largefile to .hglf/dirc/dirb/largefile
117 moving .hglf/dira/dirb/largefile to .hglf/dirc/dirb/largefile
118 $ find * | sort
118 $ find * | sort
119 dirc
119 dirc
120 dirc/baz
120 dirc/baz
121 dirc/baz/largefile
121 dirc/baz/largefile
122 dirc/dirb
122 dirc/dirb
123 dirc/dirb/largefile
123 dirc/dirb/largefile
124
124
125 $ hg clone -q . ../fetch
125 $ hg clone -q . ../fetch
126 $ hg --config extensions.fetch= fetch ../fetch
126 $ hg --config extensions.fetch= fetch ../fetch
127 abort: uncommitted changes
127 abort: uncommitted changes
128 [255]
128 [255]
129 $ hg up -qC
129 $ hg up -qC
130 $ cd ..
130 $ cd ..
131
131
132 Clone a local repository owned by another user
132 Clone a local repository owned by another user
133 ===================================================
133 ===================================================
134
134
135 #if unix-permissions
135 #if unix-permissions
136
136
137 We have to simulate that here by setting $HOME and removing write permissions
137 We have to simulate that here by setting $HOME and removing write permissions
138 $ ORIGHOME="$HOME"
138 $ ORIGHOME="$HOME"
139 $ mkdir alice
139 $ mkdir alice
140 $ HOME="`pwd`/alice"
140 $ HOME="`pwd`/alice"
141 $ cd alice
141 $ cd alice
142 $ hg init pubrepo
142 $ hg init pubrepo
143 $ cd pubrepo
143 $ cd pubrepo
144 $ dd if=/dev/zero bs=1k count=11k > a-large-file 2> /dev/null
144 $ dd if=/dev/zero bs=1k count=11k > a-large-file 2> /dev/null
145 $ hg add --large a-large-file
145 $ hg add --large a-large-file
146 $ hg commit -m "Add a large file"
146 $ hg commit -m "Add a large file"
147 Invoking status precommit hook
147 Invoking status precommit hook
148 A a-large-file
148 A a-large-file
149 $ cd ..
149 $ cd ..
150 $ chmod -R a-w pubrepo
150 $ chmod -R a-w pubrepo
151 $ cd ..
151 $ cd ..
152 $ mkdir bob
152 $ mkdir bob
153 $ HOME="`pwd`/bob"
153 $ HOME="`pwd`/bob"
154 $ cd bob
154 $ cd bob
155 $ hg clone --pull ../alice/pubrepo pubrepo
155 $ hg clone --pull ../alice/pubrepo pubrepo
156 requesting all changes
156 requesting all changes
157 adding changesets
157 adding changesets
158 adding manifests
158 adding manifests
159 adding file changes
159 adding file changes
160 added 1 changesets with 1 changes to 1 files
160 added 1 changesets with 1 changes to 1 files
161 new changesets 09a186cfa6da (1 drafts)
161 new changesets 09a186cfa6da (1 drafts)
162 updating to branch default
162 updating to branch default
163 getting changed largefiles
163 getting changed largefiles
164 1 largefiles updated, 0 removed
164 1 largefiles updated, 0 removed
165 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
165 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
166 $ cd ..
166 $ cd ..
167 $ chmod -R u+w alice/pubrepo
167 $ chmod -R u+w alice/pubrepo
168 $ HOME="$ORIGHOME"
168 $ HOME="$ORIGHOME"
169
169
170 #endif
170 #endif
171
171
172
172
173 Symlink to a large largefile should behave the same as a symlink to a normal file
173 Symlink to a large largefile should behave the same as a symlink to a normal file
174 =====================================================================================
174 =====================================================================================
175
175
176 #if symlink
176 #if symlink
177
177
178 $ hg init largesymlink
178 $ hg init largesymlink
179 $ cd largesymlink
179 $ cd largesymlink
180 $ dd if=/dev/zero bs=1k count=10k of=largefile 2>/dev/null
180 $ dd if=/dev/zero bs=1k count=10k of=largefile 2>/dev/null
181 $ hg add --large largefile
181 $ hg add --large largefile
182 $ hg commit -m "commit a large file"
182 $ hg commit -m "commit a large file"
183 Invoking status precommit hook
183 Invoking status precommit hook
184 A largefile
184 A largefile
185 $ ln -s largefile largelink
185 $ ln -s largefile largelink
186 $ hg add largelink
186 $ hg add largelink
187 $ hg commit -m "commit a large symlink"
187 $ hg commit -m "commit a large symlink"
188 Invoking status precommit hook
188 Invoking status precommit hook
189 A largelink
189 A largelink
190 $ rm -f largelink
190 $ rm -f largelink
191 $ hg up >/dev/null
191 $ hg up >/dev/null
192 $ test -f largelink
192 $ test -f largelink
193 [1]
193 [1]
194 $ test -L largelink
194 $ test -L largelink
195 [1]
195 [1]
196 $ rm -f largelink # make next part of the test independent of the previous
196 $ rm -f largelink # make next part of the test independent of the previous
197 $ hg up -C >/dev/null
197 $ hg up -C >/dev/null
198 $ test -f largelink
198 $ test -f largelink
199 $ test -L largelink
199 $ test -L largelink
200 $ cd ..
200 $ cd ..
201
201
202 #endif
202 #endif
203
203
204
204
205 test for pattern matching on 'hg status':
205 test for pattern matching on 'hg status':
206 ==============================================
206 ==============================================
207
207
208
208
209 to boost performance, largefiles checks whether specified patterns are
209 to boost performance, largefiles checks whether specified patterns are
210 related to largefiles in working directory (NOT to STANDIN) or not.
210 related to largefiles in working directory (NOT to STANDIN) or not.
211
211
212 $ hg init statusmatch
212 $ hg init statusmatch
213 $ cd statusmatch
213 $ cd statusmatch
214
214
215 $ mkdir -p a/b/c/d
215 $ mkdir -p a/b/c/d
216 $ echo normal > a/b/c/d/e.normal.txt
216 $ echo normal > a/b/c/d/e.normal.txt
217 $ hg add a/b/c/d/e.normal.txt
217 $ hg add a/b/c/d/e.normal.txt
218 $ echo large > a/b/c/d/e.large.txt
218 $ echo large > a/b/c/d/e.large.txt
219 $ hg add --large a/b/c/d/e.large.txt
219 $ hg add --large a/b/c/d/e.large.txt
220 $ mkdir -p a/b/c/x
220 $ mkdir -p a/b/c/x
221 $ echo normal > a/b/c/x/y.normal.txt
221 $ echo normal > a/b/c/x/y.normal.txt
222 $ hg add a/b/c/x/y.normal.txt
222 $ hg add a/b/c/x/y.normal.txt
223 $ hg commit -m 'add files'
223 $ hg commit -m 'add files'
224 Invoking status precommit hook
224 Invoking status precommit hook
225 A a/b/c/d/e.large.txt
225 A a/b/c/d/e.large.txt
226 A a/b/c/d/e.normal.txt
226 A a/b/c/d/e.normal.txt
227 A a/b/c/x/y.normal.txt
227 A a/b/c/x/y.normal.txt
228
228
229 (1) no pattern: no performance boost
229 (1) no pattern: no performance boost
230 $ hg status -A
230 $ hg status -A
231 C a/b/c/d/e.large.txt
231 C a/b/c/d/e.large.txt
232 C a/b/c/d/e.normal.txt
232 C a/b/c/d/e.normal.txt
233 C a/b/c/x/y.normal.txt
233 C a/b/c/x/y.normal.txt
234
234
235 (2) pattern not related to largefiles: performance boost
235 (2) pattern not related to largefiles: performance boost
236 $ hg status -A a/b/c/x
236 $ hg status -A a/b/c/x
237 C a/b/c/x/y.normal.txt
237 C a/b/c/x/y.normal.txt
238
238
239 (3) pattern related to largefiles: no performance boost
239 (3) pattern related to largefiles: no performance boost
240 $ hg status -A a/b/c/d
240 $ hg status -A a/b/c/d
241 C a/b/c/d/e.large.txt
241 C a/b/c/d/e.large.txt
242 C a/b/c/d/e.normal.txt
242 C a/b/c/d/e.normal.txt
243
243
244 (4) pattern related to STANDIN (not to largefiles): performance boost
244 (4) pattern related to STANDIN (not to largefiles): performance boost
245 $ hg status -A .hglf/a
245 $ hg status -A .hglf/a
246 C .hglf/a/b/c/d/e.large.txt
246 C .hglf/a/b/c/d/e.large.txt
247
247
248 (5) mixed case: no performance boost
248 (5) mixed case: no performance boost
249 $ hg status -A a/b/c/x a/b/c/d
249 $ hg status -A a/b/c/x a/b/c/d
250 C a/b/c/d/e.large.txt
250 C a/b/c/d/e.large.txt
251 C a/b/c/d/e.normal.txt
251 C a/b/c/d/e.normal.txt
252 C a/b/c/x/y.normal.txt
252 C a/b/c/x/y.normal.txt
253
253
254 verify that largefiles doesn't break filesets
254 verify that largefiles doesn't break filesets
255
255
256 $ hg log --rev . --exclude "set:binary()"
256 $ hg log --rev . --exclude "set:binary()"
257 changeset: 0:41bd42f10efa
257 changeset: 0:41bd42f10efa
258 tag: tip
258 tag: tip
259 user: test
259 user: test
260 date: Thu Jan 01 00:00:00 1970 +0000
260 date: Thu Jan 01 00:00:00 1970 +0000
261 summary: add files
261 summary: add files
262
262
263 sharing a largefile repo automatically enables largefiles on the share
263 sharing a largefile repo automatically enables largefiles on the share
264
264
265 $ hg share --config extensions.share= . ../shared_lfrepo
265 $ hg share --config extensions.share= . ../shared_lfrepo
266 updating working directory
266 updating working directory
267 getting changed largefiles
267 getting changed largefiles
268 1 largefiles updated, 0 removed
268 1 largefiles updated, 0 removed
269 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
269 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
270 $ grep largefiles ../shared_lfrepo/.hg/requires
270 $ grep largefiles ../shared_lfrepo/.hg/requires
271 largefiles
271 largefiles
272
272
273 verify that large files in subrepos handled properly
273 verify that large files in subrepos handled properly
274 $ hg init subrepo
274 $ hg init subrepo
275 $ echo "subrepo = subrepo" > .hgsub
275 $ echo "subrepo = subrepo" > .hgsub
276 $ hg add .hgsub
276 $ hg add .hgsub
277 $ hg ci -m "add subrepo"
277 $ hg ci -m "add subrepo"
278 Invoking status precommit hook
278 Invoking status precommit hook
279 A .hgsub
279 A .hgsub
280 ? .hgsubstate
280 ? .hgsubstate
281 $ echo "rev 1" > subrepo/large.txt
281 $ echo "rev 1" > subrepo/large.txt
282 $ hg add --large subrepo/large.txt
282 $ hg add --large subrepo/large.txt
283 $ hg sum
283 $ hg sum
284 parent: 1:8ee150ea2e9c tip
284 parent: 1:8ee150ea2e9c tip
285 add subrepo
285 add subrepo
286 branch: default
286 branch: default
287 commit: 1 subrepos
287 commit: 1 subrepos
288 update: (current)
288 update: (current)
289 phases: 2 draft
289 phases: 2 draft
290 $ hg st
290 $ hg st
291 $ hg st -S
291 $ hg st -S
292 A subrepo/large.txt
292 A subrepo/large.txt
293 $ hg ci -S -m "commit top repo"
293 $ hg ci -S -m "commit top repo"
294 committing subrepository subrepo
294 committing subrepository subrepo
295 Invoking status precommit hook
295 Invoking status precommit hook
296 A large.txt
296 A large.txt
297 Invoking status precommit hook
297 Invoking status precommit hook
298 M .hgsubstate
298 M .hgsubstate
299 # No differences
299 # No differences
300 $ hg st -S
300 $ hg st -S
301 $ hg sum
301 $ hg sum
302 parent: 2:ce4cd0c527a6 tip
302 parent: 2:ce4cd0c527a6 tip
303 commit top repo
303 commit top repo
304 branch: default
304 branch: default
305 commit: (clean)
305 commit: (clean)
306 update: (current)
306 update: (current)
307 phases: 3 draft
307 phases: 3 draft
308 $ echo "rev 2" > subrepo/large.txt
308 $ echo "rev 2" > subrepo/large.txt
309 $ hg st -S
309 $ hg st -S
310 M subrepo/large.txt
310 M subrepo/large.txt
311 $ hg sum
311 $ hg sum
312 parent: 2:ce4cd0c527a6 tip
312 parent: 2:ce4cd0c527a6 tip
313 commit top repo
313 commit top repo
314 branch: default
314 branch: default
315 commit: 1 subrepos
315 commit: 1 subrepos
316 update: (current)
316 update: (current)
317 phases: 3 draft
317 phases: 3 draft
318 $ hg ci -m "this commit should fail without -S"
318 $ hg ci -m "this commit should fail without -S"
319 abort: uncommitted changes in subrepository "subrepo"
319 abort: uncommitted changes in subrepository "subrepo"
320 (use --subrepos for recursive commit)
320 (use --subrepos for recursive commit)
321 [255]
321 [255]
322
322
323 Add a normal file to the subrepo, then test archiving
323 Add a normal file to the subrepo, then test archiving
324
324
325 $ echo 'normal file' > subrepo/normal.txt
325 $ echo 'normal file' > subrepo/normal.txt
326 $ touch large.dat
326 $ touch large.dat
327 $ mv subrepo/large.txt subrepo/renamed-large.txt
327 $ mv subrepo/large.txt subrepo/renamed-large.txt
328 $ hg addremove -S --dry-run
328 $ hg addremove -S --dry-run
329 adding large.dat as a largefile
329 adding large.dat as a largefile
330 removing subrepo/large.txt
330 removing subrepo/large.txt
331 adding subrepo/normal.txt
331 adding subrepo/normal.txt
332 adding subrepo/renamed-large.txt
332 adding subrepo/renamed-large.txt
333 $ hg status -S
333 $ hg status -S
334 ! subrepo/large.txt
334 ! subrepo/large.txt
335 ? large.dat
335 ? large.dat
336 ? subrepo/normal.txt
336 ? subrepo/normal.txt
337 ? subrepo/renamed-large.txt
337 ? subrepo/renamed-large.txt
338
338
339 $ hg addremove --dry-run subrepo
339 $ hg addremove --dry-run subrepo
340 removing subrepo/large.txt
340 removing subrepo/large.txt
341 adding subrepo/normal.txt
341 adding subrepo/normal.txt
342 adding subrepo/renamed-large.txt
342 adding subrepo/renamed-large.txt
343 $ hg status -S
343 $ hg status -S
344 ! subrepo/large.txt
344 ! subrepo/large.txt
345 ? large.dat
345 ? large.dat
346 ? subrepo/normal.txt
346 ? subrepo/normal.txt
347 ? subrepo/renamed-large.txt
347 ? subrepo/renamed-large.txt
348 $ cd ..
348 $ cd ..
349
349
350 $ hg -R statusmatch addremove --dry-run statusmatch/subrepo
350 $ hg -R statusmatch addremove --dry-run statusmatch/subrepo
351 removing statusmatch/subrepo/large.txt
351 removing statusmatch/subrepo/large.txt
352 adding statusmatch/subrepo/normal.txt
352 adding statusmatch/subrepo/normal.txt
353 adding statusmatch/subrepo/renamed-large.txt
353 adding statusmatch/subrepo/renamed-large.txt
354 $ hg -R statusmatch status -S
354 $ hg -R statusmatch status -S
355 ! subrepo/large.txt
355 ! subrepo/large.txt
356 ? large.dat
356 ? large.dat
357 ? subrepo/normal.txt
357 ? subrepo/normal.txt
358 ? subrepo/renamed-large.txt
358 ? subrepo/renamed-large.txt
359
359
360 $ hg -R statusmatch addremove --dry-run -S
360 $ hg -R statusmatch addremove --dry-run -S
361 adding large.dat as a largefile
361 adding large.dat as a largefile
362 removing subrepo/large.txt
362 removing subrepo/large.txt
363 adding subrepo/normal.txt
363 adding subrepo/normal.txt
364 adding subrepo/renamed-large.txt
364 adding subrepo/renamed-large.txt
365 $ cd statusmatch
365 $ cd statusmatch
366
366
367 $ mv subrepo/renamed-large.txt subrepo/large.txt
367 $ mv subrepo/renamed-large.txt subrepo/large.txt
368 $ hg addremove subrepo
368 $ hg addremove subrepo
369 adding subrepo/normal.txt
369 adding subrepo/normal.txt
370 $ hg forget subrepo/normal.txt
370 $ hg forget subrepo/normal.txt
371
371
372 $ hg addremove -S
372 $ hg addremove -S
373 adding large.dat as a largefile
373 adding large.dat as a largefile
374 adding subrepo/normal.txt
374 adding subrepo/normal.txt
375 $ rm large.dat
375 $ rm large.dat
376
376
377 $ hg addremove subrepo
377 $ hg addremove subrepo
378 $ hg addremove -S
378 $ hg addremove -S
379 removing large.dat
379 removing large.dat
380
380
381 Lock in subrepo, otherwise the change isn't archived
381 Lock in subrepo, otherwise the change isn't archived
382
382
383 $ hg ci -S -m "add normal file to top level"
383 $ hg ci -S -m "add normal file to top level"
384 committing subrepository subrepo
384 committing subrepository subrepo
385 Invoking status precommit hook
385 Invoking status precommit hook
386 M large.txt
386 M large.txt
387 A normal.txt
387 A normal.txt
388 Invoking status precommit hook
388 Invoking status precommit hook
389 M .hgsubstate
389 M .hgsubstate
390 $ hg archive -S ../lf_subrepo_archive
390 $ hg archive -S ../lf_subrepo_archive
391 $ find ../lf_subrepo_archive | sort
391 $ find ../lf_subrepo_archive | sort
392 ../lf_subrepo_archive
392 ../lf_subrepo_archive
393 ../lf_subrepo_archive/.hg_archival.txt
393 ../lf_subrepo_archive/.hg_archival.txt
394 ../lf_subrepo_archive/.hgsub
394 ../lf_subrepo_archive/.hgsub
395 ../lf_subrepo_archive/.hgsubstate
395 ../lf_subrepo_archive/.hgsubstate
396 ../lf_subrepo_archive/a
396 ../lf_subrepo_archive/a
397 ../lf_subrepo_archive/a/b
397 ../lf_subrepo_archive/a/b
398 ../lf_subrepo_archive/a/b/c
398 ../lf_subrepo_archive/a/b/c
399 ../lf_subrepo_archive/a/b/c/d
399 ../lf_subrepo_archive/a/b/c/d
400 ../lf_subrepo_archive/a/b/c/d/e.large.txt
400 ../lf_subrepo_archive/a/b/c/d/e.large.txt
401 ../lf_subrepo_archive/a/b/c/d/e.normal.txt
401 ../lf_subrepo_archive/a/b/c/d/e.normal.txt
402 ../lf_subrepo_archive/a/b/c/x
402 ../lf_subrepo_archive/a/b/c/x
403 ../lf_subrepo_archive/a/b/c/x/y.normal.txt
403 ../lf_subrepo_archive/a/b/c/x/y.normal.txt
404 ../lf_subrepo_archive/subrepo
404 ../lf_subrepo_archive/subrepo
405 ../lf_subrepo_archive/subrepo/large.txt
405 ../lf_subrepo_archive/subrepo/large.txt
406 ../lf_subrepo_archive/subrepo/normal.txt
406 ../lf_subrepo_archive/subrepo/normal.txt
407 $ cat ../lf_subrepo_archive/.hg_archival.txt
407 $ cat ../lf_subrepo_archive/.hg_archival.txt
408 repo: 41bd42f10efa43698cc02052ea0977771cba506d
408 repo: 41bd42f10efa43698cc02052ea0977771cba506d
409 node: d56a95e6522858bc08a724c4fe2bdee066d1c30b
409 node: d56a95e6522858bc08a724c4fe2bdee066d1c30b
410 branch: default
410 branch: default
411 latesttag: null
411 latesttag: null
412 latesttagdistance: 4
412 latesttagdistance: 4
413 changessincelatesttag: 4
413 changessincelatesttag: 4
414
414
415 Test update with subrepos.
415 Test update with subrepos.
416
416
417 $ hg update 0
417 $ hg update 0
418 getting changed largefiles
418 getting changed largefiles
419 0 largefiles updated, 1 removed
419 0 largefiles updated, 1 removed
420 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
420 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
421 $ hg status -S
421 $ hg status -S
422 $ hg update tip
422 $ hg update tip
423 getting changed largefiles
423 getting changed largefiles
424 1 largefiles updated, 0 removed
424 1 largefiles updated, 0 removed
425 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
425 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
426 $ hg status -S
426 $ hg status -S
427 # modify a large file
427 # modify a large file
428 $ echo "modified" > subrepo/large.txt
428 $ echo "modified" > subrepo/large.txt
429 $ hg st -S
429 $ hg st -S
430 M subrepo/large.txt
430 M subrepo/large.txt
431 # update -C should revert the change.
431 # update -C should revert the change.
432 $ hg update -C
432 $ hg update -C
433 getting changed largefiles
433 getting changed largefiles
434 1 largefiles updated, 0 removed
434 1 largefiles updated, 0 removed
435 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
435 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
436 $ hg status -S
436 $ hg status -S
437
437
438 Forget doesn't change the content of the file
438 Forget doesn't change the content of the file
439 $ echo 'pre-forget content' > subrepo/large.txt
439 $ echo 'pre-forget content' > subrepo/large.txt
440 $ hg forget -v subrepo/large.txt
440 $ hg forget -v subrepo/large.txt
441 removing subrepo/large.txt
441 removing subrepo/large.txt
442 $ cat subrepo/large.txt
442 $ cat subrepo/large.txt
443 pre-forget content
443 pre-forget content
444
444
445 Test reverting a forgotten file
445 Test reverting a forgotten file
446 $ hg revert -R subrepo subrepo/large.txt
446 $ hg revert -R subrepo subrepo/large.txt
447 $ hg status -SA subrepo/large.txt
447 $ hg status -SA subrepo/large.txt
448 C subrepo/large.txt
448 C subrepo/large.txt
449
449
450 $ hg rm -v subrepo/large.txt
450 $ hg rm -v subrepo/large.txt
451 removing subrepo/large.txt
451 removing subrepo/large.txt
452 $ hg revert -R subrepo subrepo/large.txt
452 $ hg revert -R subrepo subrepo/large.txt
453 $ rm subrepo/large.txt
453 $ rm subrepo/large.txt
454 $ hg addremove -S
454 $ hg addremove -S
455 removing subrepo/large.txt
455 removing subrepo/large.txt
456 $ hg st -S
456 $ hg st -S
457 R subrepo/large.txt
457 R subrepo/large.txt
458
458
459 Test archiving a revision that references a subrepo that is not yet
459 Test archiving a revision that references a subrepo that is not yet
460 cloned (see test-subrepo-recursion.t):
460 cloned (see test-subrepo-recursion.t):
461
461
462 $ hg clone -U . ../empty
462 $ hg clone -U . ../empty
463 $ cd ../empty
463 $ cd ../empty
464 $ hg archive --subrepos -r tip ../archive.tar.gz
464 $ hg archive --subrepos -r tip ../archive.tar.gz
465 cloning subrepo subrepo from $TESTTMP/statusmatch/subrepo
465 cloning subrepo subrepo from $TESTTMP/statusmatch/subrepo
466 $ cd ..
466 $ cd ..
467
467
468
468
469
469
470
470
471
471
472
472
473 Test addremove, forget and others
473 Test addremove, forget and others
474 ==============================================
474 ==============================================
475
475
476 Test that addremove picks up largefiles prior to the initial commit (issue3541)
476 Test that addremove picks up largefiles prior to the initial commit (issue3541)
477
477
478 $ hg init addrm2
478 $ hg init addrm2
479 $ cd addrm2
479 $ cd addrm2
480 $ touch large.dat
480 $ touch large.dat
481 $ touch large2.dat
481 $ touch large2.dat
482 $ touch normal
482 $ touch normal
483 $ hg add --large large.dat
483 $ hg add --large large.dat
484 $ hg addremove -v
484 $ hg addremove -v
485 adding large2.dat as a largefile
485 adding large2.dat as a largefile
486 adding normal
486 adding normal
487
487
488 Test that forgetting all largefiles reverts to islfilesrepo() == False
488 Test that forgetting all largefiles reverts to islfilesrepo() == False
489 (addremove will add *.dat as normal files now)
489 (addremove will add *.dat as normal files now)
490 $ hg forget large.dat
490 $ hg forget large.dat
491 $ hg forget large2.dat
491 $ hg forget large2.dat
492 $ hg addremove -v
492 $ hg addremove -v
493 adding large.dat
493 adding large.dat
494 adding large2.dat
494 adding large2.dat
495
495
496 Test commit's addremove option prior to the first commit
496 Test commit's addremove option prior to the first commit
497 $ hg forget large.dat
497 $ hg forget large.dat
498 $ hg forget large2.dat
498 $ hg forget large2.dat
499 $ hg add --large large.dat
499 $ hg add --large large.dat
500 $ hg ci -Am "commit"
500 $ hg ci -Am "commit"
501 adding large2.dat as a largefile
501 adding large2.dat as a largefile
502 Invoking status precommit hook
502 Invoking status precommit hook
503 A large.dat
503 A large.dat
504 A large2.dat
504 A large2.dat
505 A normal
505 A normal
506 $ find .hglf | sort
506 $ find .hglf | sort
507 .hglf
507 .hglf
508 .hglf/large.dat
508 .hglf/large.dat
509 .hglf/large2.dat
509 .hglf/large2.dat
510
510
511 Test actions on largefiles using relative paths from subdir
511 Test actions on largefiles using relative paths from subdir
512
512
513 $ mkdir sub
513 $ mkdir sub
514 $ cd sub
514 $ cd sub
515 $ echo anotherlarge > anotherlarge
515 $ echo anotherlarge > anotherlarge
516 $ hg add --large anotherlarge
516 $ hg add --large anotherlarge
517 $ hg st
517 $ hg st
518 A sub/anotherlarge
518 A sub/anotherlarge
519 $ hg st anotherlarge
519 $ hg st anotherlarge
520 A anotherlarge
520 A anotherlarge
521 $ hg commit -m anotherlarge anotherlarge
521 $ hg commit -m anotherlarge anotherlarge
522 Invoking status precommit hook
522 Invoking status precommit hook
523 A sub/anotherlarge
523 A sub/anotherlarge
524 $ hg log anotherlarge
524 $ hg log anotherlarge
525 changeset: 1:9627a577c5e9
525 changeset: 1:9627a577c5e9
526 tag: tip
526 tag: tip
527 user: test
527 user: test
528 date: Thu Jan 01 00:00:00 1970 +0000
528 date: Thu Jan 01 00:00:00 1970 +0000
529 summary: anotherlarge
529 summary: anotherlarge
530
530
531 $ hg --debug log -T '{rev}: {desc}\n' ../sub/anotherlarge
531 $ hg --debug log -T '{rev}: {desc}\n' ../sub/anotherlarge
532 updated patterns: ../.hglf/sub/../sub/anotherlarge, ../sub/anotherlarge
532 updated patterns: ../.hglf/sub/../sub/anotherlarge, ../sub/anotherlarge
533 1: anotherlarge
533 1: anotherlarge
534
534
535 $ hg log -G anotherlarge
535 $ hg log -G anotherlarge
536 @ changeset: 1:9627a577c5e9
536 @ changeset: 1:9627a577c5e9
537 | tag: tip
537 | tag: tip
538 ~ user: test
538 ~ user: test
539 date: Thu Jan 01 00:00:00 1970 +0000
539 date: Thu Jan 01 00:00:00 1970 +0000
540 summary: anotherlarge
540 summary: anotherlarge
541
541
542
542
543 $ hg log glob:another*
543 $ hg log glob:another*
544 changeset: 1:9627a577c5e9
544 changeset: 1:9627a577c5e9
545 tag: tip
545 tag: tip
546 user: test
546 user: test
547 date: Thu Jan 01 00:00:00 1970 +0000
547 date: Thu Jan 01 00:00:00 1970 +0000
548 summary: anotherlarge
548 summary: anotherlarge
549
549
550 $ hg --debug log -T '{rev}: {desc}\n' -G glob:another*
550 $ hg --debug log -T '{rev}: {desc}\n' -G glob:another*
551 updated patterns: glob:../.hglf/sub/another*, glob:another*
551 updated patterns: glob:../.hglf/sub/another*, glob:another*
552 @ 1: anotherlarge
552 @ 1: anotherlarge
553 |
553 |
554 ~
554 ~
555
555
556 #if no-msys
556 #if no-msys
557 $ hg --debug log -T '{rev}: {desc}\n' 'glob:../.hglf/sub/another*' # no-msys
557 $ hg --debug log -T '{rev}: {desc}\n' 'glob:../.hglf/sub/another*' # no-msys
558 updated patterns: glob:../.hglf/sub/another*
558 updated patterns: glob:../.hglf/sub/another*
559 1: anotherlarge
559 1: anotherlarge
560
560
561 $ hg --debug log -G -T '{rev}: {desc}\n' 'glob:../.hglf/sub/another*' # no-msys
561 $ hg --debug log -G -T '{rev}: {desc}\n' 'glob:../.hglf/sub/another*' # no-msys
562 updated patterns: glob:../.hglf/sub/another*
562 updated patterns: glob:../.hglf/sub/another*
563 @ 1: anotherlarge
563 @ 1: anotherlarge
564 |
564 |
565 ~
565 ~
566 #endif
566 #endif
567
567
568 $ echo more >> anotherlarge
568 $ echo more >> anotherlarge
569 $ hg st .
569 $ hg st .
570 M anotherlarge
570 M anotherlarge
571 $ hg cat anotherlarge
571 $ hg cat anotherlarge
572 anotherlarge
572 anotherlarge
573 $ hg revert anotherlarge
573 $ hg revert anotherlarge
574 $ hg st
574 $ hg st
575 ? sub/anotherlarge.orig
575 ? sub/anotherlarge.orig
576
576
577 Test orig files go where we want them
577 Test orig files go where we want them
578 $ echo moremore >> anotherlarge
578 $ echo moremore >> anotherlarge
579 $ hg revert anotherlarge -v --config 'ui.origbackuppath=.hg/origbackups'
579 $ hg revert anotherlarge -v --config 'ui.origbackuppath=.hg/origbackups'
580 creating directory: $TESTTMP/addrm2/.hg/origbackups/.hglf/sub
580 creating directory: $TESTTMP/addrm2/.hg/origbackups/.hglf/sub
581 saving current version of ../.hglf/sub/anotherlarge as $TESTTMP/addrm2/.hg/origbackups/.hglf/sub/anotherlarge
581 saving current version of ../.hglf/sub/anotherlarge as ../.hg/origbackups/.hglf/sub/anotherlarge
582 reverting ../.hglf/sub/anotherlarge
582 reverting ../.hglf/sub/anotherlarge
583 creating directory: $TESTTMP/addrm2/.hg/origbackups/sub
583 creating directory: $TESTTMP/addrm2/.hg/origbackups/sub
584 found 90c622cf65cebe75c5842f9136c459333faf392e in store
584 found 90c622cf65cebe75c5842f9136c459333faf392e in store
585 found 90c622cf65cebe75c5842f9136c459333faf392e in store
585 found 90c622cf65cebe75c5842f9136c459333faf392e in store
586 $ ls ../.hg/origbackups/sub
586 $ ls ../.hg/origbackups/sub
587 anotherlarge
587 anotherlarge
588 $ cd ..
588 $ cd ..
589
589
590 Test glob logging from the root dir
590 Test glob logging from the root dir
591 $ hg log glob:**another*
591 $ hg log glob:**another*
592 changeset: 1:9627a577c5e9
592 changeset: 1:9627a577c5e9
593 tag: tip
593 tag: tip
594 user: test
594 user: test
595 date: Thu Jan 01 00:00:00 1970 +0000
595 date: Thu Jan 01 00:00:00 1970 +0000
596 summary: anotherlarge
596 summary: anotherlarge
597
597
598 $ hg log -G glob:**another*
598 $ hg log -G glob:**another*
599 @ changeset: 1:9627a577c5e9
599 @ changeset: 1:9627a577c5e9
600 | tag: tip
600 | tag: tip
601 ~ user: test
601 ~ user: test
602 date: Thu Jan 01 00:00:00 1970 +0000
602 date: Thu Jan 01 00:00:00 1970 +0000
603 summary: anotherlarge
603 summary: anotherlarge
604
604
605
605
606 $ cd ..
606 $ cd ..
607
607
608 Log from outer space
608 Log from outer space
609 $ hg --debug log -R addrm2 -T '{rev}: {desc}\n' 'addrm2/sub/anotherlarge'
609 $ hg --debug log -R addrm2 -T '{rev}: {desc}\n' 'addrm2/sub/anotherlarge'
610 updated patterns: addrm2/.hglf/sub/anotherlarge, addrm2/sub/anotherlarge
610 updated patterns: addrm2/.hglf/sub/anotherlarge, addrm2/sub/anotherlarge
611 1: anotherlarge
611 1: anotherlarge
612 $ hg --debug log -R addrm2 -T '{rev}: {desc}\n' 'addrm2/.hglf/sub/anotherlarge'
612 $ hg --debug log -R addrm2 -T '{rev}: {desc}\n' 'addrm2/.hglf/sub/anotherlarge'
613 updated patterns: addrm2/.hglf/sub/anotherlarge
613 updated patterns: addrm2/.hglf/sub/anotherlarge
614 1: anotherlarge
614 1: anotherlarge
615
615
616
616
617 Check error message while exchange
617 Check error message while exchange
618 =========================================================
618 =========================================================
619
619
620 issue3651: summary/outgoing with largefiles shows "no remote repo"
620 issue3651: summary/outgoing with largefiles shows "no remote repo"
621 unexpectedly
621 unexpectedly
622
622
623 $ mkdir issue3651
623 $ mkdir issue3651
624 $ cd issue3651
624 $ cd issue3651
625
625
626 $ hg init src
626 $ hg init src
627 $ echo a > src/a
627 $ echo a > src/a
628 $ hg -R src add --large src/a
628 $ hg -R src add --large src/a
629 $ hg -R src commit -m '#0'
629 $ hg -R src commit -m '#0'
630 Invoking status precommit hook
630 Invoking status precommit hook
631 A a
631 A a
632
632
633 check messages when no remote repository is specified:
633 check messages when no remote repository is specified:
634 "no remote repo" route for "hg outgoing --large" is not tested here,
634 "no remote repo" route for "hg outgoing --large" is not tested here,
635 because it can't be reproduced easily.
635 because it can't be reproduced easily.
636
636
637 $ hg init clone1
637 $ hg init clone1
638 $ hg -R clone1 -q pull src
638 $ hg -R clone1 -q pull src
639 $ hg -R clone1 -q update
639 $ hg -R clone1 -q update
640 $ hg -R clone1 paths | grep default
640 $ hg -R clone1 paths | grep default
641 [1]
641 [1]
642
642
643 $ hg -R clone1 summary --large
643 $ hg -R clone1 summary --large
644 parent: 0:fc0bd45326d3 tip
644 parent: 0:fc0bd45326d3 tip
645 #0
645 #0
646 branch: default
646 branch: default
647 commit: (clean)
647 commit: (clean)
648 update: (current)
648 update: (current)
649 phases: 1 draft
649 phases: 1 draft
650 largefiles: (no remote repo)
650 largefiles: (no remote repo)
651
651
652 check messages when there is no files to upload:
652 check messages when there is no files to upload:
653
653
654 $ hg -q clone src clone2
654 $ hg -q clone src clone2
655 $ hg -R clone2 paths | grep default
655 $ hg -R clone2 paths | grep default
656 default = $TESTTMP/issue3651/src
656 default = $TESTTMP/issue3651/src
657
657
658 $ hg -R clone2 summary --large
658 $ hg -R clone2 summary --large
659 parent: 0:fc0bd45326d3 tip
659 parent: 0:fc0bd45326d3 tip
660 #0
660 #0
661 branch: default
661 branch: default
662 commit: (clean)
662 commit: (clean)
663 update: (current)
663 update: (current)
664 phases: 1 draft
664 phases: 1 draft
665 largefiles: (no files to upload)
665 largefiles: (no files to upload)
666 $ hg -R clone2 outgoing --large
666 $ hg -R clone2 outgoing --large
667 comparing with $TESTTMP/issue3651/src
667 comparing with $TESTTMP/issue3651/src
668 searching for changes
668 searching for changes
669 no changes found
669 no changes found
670 largefiles: no files to upload
670 largefiles: no files to upload
671 [1]
671 [1]
672
672
673 $ hg -R clone2 outgoing --large --graph --template "{rev}"
673 $ hg -R clone2 outgoing --large --graph --template "{rev}"
674 comparing with $TESTTMP/issue3651/src
674 comparing with $TESTTMP/issue3651/src
675 searching for changes
675 searching for changes
676 no changes found
676 no changes found
677 largefiles: no files to upload
677 largefiles: no files to upload
678
678
679 check messages when there are files to upload:
679 check messages when there are files to upload:
680
680
681 $ echo b > clone2/b
681 $ echo b > clone2/b
682 $ hg -R clone2 add --large clone2/b
682 $ hg -R clone2 add --large clone2/b
683 $ hg -R clone2 commit -m '#1'
683 $ hg -R clone2 commit -m '#1'
684 Invoking status precommit hook
684 Invoking status precommit hook
685 A b
685 A b
686 $ hg -R clone2 summary --large
686 $ hg -R clone2 summary --large
687 parent: 1:1acbe71ce432 tip
687 parent: 1:1acbe71ce432 tip
688 #1
688 #1
689 branch: default
689 branch: default
690 commit: (clean)
690 commit: (clean)
691 update: (current)
691 update: (current)
692 phases: 2 draft
692 phases: 2 draft
693 largefiles: 1 entities for 1 files to upload
693 largefiles: 1 entities for 1 files to upload
694 $ hg -R clone2 outgoing --large
694 $ hg -R clone2 outgoing --large
695 comparing with $TESTTMP/issue3651/src
695 comparing with $TESTTMP/issue3651/src
696 searching for changes
696 searching for changes
697 changeset: 1:1acbe71ce432
697 changeset: 1:1acbe71ce432
698 tag: tip
698 tag: tip
699 user: test
699 user: test
700 date: Thu Jan 01 00:00:00 1970 +0000
700 date: Thu Jan 01 00:00:00 1970 +0000
701 summary: #1
701 summary: #1
702
702
703 largefiles to upload (1 entities):
703 largefiles to upload (1 entities):
704 b
704 b
705
705
706 $ hg -R clone2 outgoing --large --graph --template "{rev}"
706 $ hg -R clone2 outgoing --large --graph --template "{rev}"
707 comparing with $TESTTMP/issue3651/src
707 comparing with $TESTTMP/issue3651/src
708 searching for changes
708 searching for changes
709 @ 1
709 @ 1
710
710
711 largefiles to upload (1 entities):
711 largefiles to upload (1 entities):
712 b
712 b
713
713
714
714
715 $ cp clone2/b clone2/b1
715 $ cp clone2/b clone2/b1
716 $ cp clone2/b clone2/b2
716 $ cp clone2/b clone2/b2
717 $ hg -R clone2 add --large clone2/b1 clone2/b2
717 $ hg -R clone2 add --large clone2/b1 clone2/b2
718 $ hg -R clone2 commit -m '#2: add largefiles referring same entity'
718 $ hg -R clone2 commit -m '#2: add largefiles referring same entity'
719 Invoking status precommit hook
719 Invoking status precommit hook
720 A b1
720 A b1
721 A b2
721 A b2
722 $ hg -R clone2 summary --large
722 $ hg -R clone2 summary --large
723 parent: 2:6095d0695d70 tip
723 parent: 2:6095d0695d70 tip
724 #2: add largefiles referring same entity
724 #2: add largefiles referring same entity
725 branch: default
725 branch: default
726 commit: (clean)
726 commit: (clean)
727 update: (current)
727 update: (current)
728 phases: 3 draft
728 phases: 3 draft
729 largefiles: 1 entities for 3 files to upload
729 largefiles: 1 entities for 3 files to upload
730 $ hg -R clone2 outgoing --large -T "{rev}:{node|short}\n"
730 $ hg -R clone2 outgoing --large -T "{rev}:{node|short}\n"
731 comparing with $TESTTMP/issue3651/src
731 comparing with $TESTTMP/issue3651/src
732 searching for changes
732 searching for changes
733 1:1acbe71ce432
733 1:1acbe71ce432
734 2:6095d0695d70
734 2:6095d0695d70
735 largefiles to upload (1 entities):
735 largefiles to upload (1 entities):
736 b
736 b
737 b1
737 b1
738 b2
738 b2
739
739
740 $ hg -R clone2 cat -r 1 clone2/.hglf/b
740 $ hg -R clone2 cat -r 1 clone2/.hglf/b
741 89e6c98d92887913cadf06b2adb97f26cde4849b
741 89e6c98d92887913cadf06b2adb97f26cde4849b
742 $ hg -R clone2 outgoing --large -T "{rev}:{node|short}\n" --debug --config progress.debug=true
742 $ hg -R clone2 outgoing --large -T "{rev}:{node|short}\n" --debug --config progress.debug=true
743 comparing with $TESTTMP/issue3651/src
743 comparing with $TESTTMP/issue3651/src
744 query 1; heads
744 query 1; heads
745 searching for changes
745 searching for changes
746 all remote heads known locally
746 all remote heads known locally
747 1:1acbe71ce432
747 1:1acbe71ce432
748 2:6095d0695d70
748 2:6095d0695d70
749 finding outgoing largefiles: 0/2 revisions (0.00%)
749 finding outgoing largefiles: 0/2 revisions (0.00%)
750 finding outgoing largefiles: 1/2 revisions (50.00%)
750 finding outgoing largefiles: 1/2 revisions (50.00%)
751 largefiles to upload (1 entities):
751 largefiles to upload (1 entities):
752 b
752 b
753 89e6c98d92887913cadf06b2adb97f26cde4849b
753 89e6c98d92887913cadf06b2adb97f26cde4849b
754 b1
754 b1
755 89e6c98d92887913cadf06b2adb97f26cde4849b
755 89e6c98d92887913cadf06b2adb97f26cde4849b
756 b2
756 b2
757 89e6c98d92887913cadf06b2adb97f26cde4849b
757 89e6c98d92887913cadf06b2adb97f26cde4849b
758
758
759
759
760 $ echo bbb > clone2/b
760 $ echo bbb > clone2/b
761 $ hg -R clone2 commit -m '#3: add new largefile entity as existing file'
761 $ hg -R clone2 commit -m '#3: add new largefile entity as existing file'
762 Invoking status precommit hook
762 Invoking status precommit hook
763 M b
763 M b
764 $ echo bbbb > clone2/b
764 $ echo bbbb > clone2/b
765 $ hg -R clone2 commit -m '#4: add new largefile entity as existing file'
765 $ hg -R clone2 commit -m '#4: add new largefile entity as existing file'
766 Invoking status precommit hook
766 Invoking status precommit hook
767 M b
767 M b
768 $ cp clone2/b1 clone2/b
768 $ cp clone2/b1 clone2/b
769 $ hg -R clone2 commit -m '#5: refer existing largefile entity again'
769 $ hg -R clone2 commit -m '#5: refer existing largefile entity again'
770 Invoking status precommit hook
770 Invoking status precommit hook
771 M b
771 M b
772 $ hg -R clone2 summary --large
772 $ hg -R clone2 summary --large
773 parent: 5:036794ea641c tip
773 parent: 5:036794ea641c tip
774 #5: refer existing largefile entity again
774 #5: refer existing largefile entity again
775 branch: default
775 branch: default
776 commit: (clean)
776 commit: (clean)
777 update: (current)
777 update: (current)
778 phases: 6 draft
778 phases: 6 draft
779 largefiles: 3 entities for 3 files to upload
779 largefiles: 3 entities for 3 files to upload
780 $ hg -R clone2 outgoing --large -T "{rev}:{node|short}\n"
780 $ hg -R clone2 outgoing --large -T "{rev}:{node|short}\n"
781 comparing with $TESTTMP/issue3651/src
781 comparing with $TESTTMP/issue3651/src
782 searching for changes
782 searching for changes
783 1:1acbe71ce432
783 1:1acbe71ce432
784 2:6095d0695d70
784 2:6095d0695d70
785 3:7983dce246cc
785 3:7983dce246cc
786 4:233f12ada4ae
786 4:233f12ada4ae
787 5:036794ea641c
787 5:036794ea641c
788 largefiles to upload (3 entities):
788 largefiles to upload (3 entities):
789 b
789 b
790 b1
790 b1
791 b2
791 b2
792
792
793 $ hg -R clone2 cat -r 3 clone2/.hglf/b
793 $ hg -R clone2 cat -r 3 clone2/.hglf/b
794 c801c9cfe94400963fcb683246217d5db77f9a9a
794 c801c9cfe94400963fcb683246217d5db77f9a9a
795 $ hg -R clone2 cat -r 4 clone2/.hglf/b
795 $ hg -R clone2 cat -r 4 clone2/.hglf/b
796 13f9ed0898e315bf59dc2973fec52037b6f441a2
796 13f9ed0898e315bf59dc2973fec52037b6f441a2
797 $ hg -R clone2 outgoing --large -T "{rev}:{node|short}\n" --debug --config progress.debug=true
797 $ hg -R clone2 outgoing --large -T "{rev}:{node|short}\n" --debug --config progress.debug=true
798 comparing with $TESTTMP/issue3651/src
798 comparing with $TESTTMP/issue3651/src
799 query 1; heads
799 query 1; heads
800 searching for changes
800 searching for changes
801 all remote heads known locally
801 all remote heads known locally
802 1:1acbe71ce432
802 1:1acbe71ce432
803 2:6095d0695d70
803 2:6095d0695d70
804 3:7983dce246cc
804 3:7983dce246cc
805 4:233f12ada4ae
805 4:233f12ada4ae
806 5:036794ea641c
806 5:036794ea641c
807 finding outgoing largefiles: 0/5 revisions (0.00%)
807 finding outgoing largefiles: 0/5 revisions (0.00%)
808 finding outgoing largefiles: 1/5 revisions (20.00%)
808 finding outgoing largefiles: 1/5 revisions (20.00%)
809 finding outgoing largefiles: 2/5 revisions (40.00%)
809 finding outgoing largefiles: 2/5 revisions (40.00%)
810 finding outgoing largefiles: 3/5 revisions (60.00%)
810 finding outgoing largefiles: 3/5 revisions (60.00%)
811 finding outgoing largefiles: 4/5 revisions (80.00%)
811 finding outgoing largefiles: 4/5 revisions (80.00%)
812 largefiles to upload (3 entities):
812 largefiles to upload (3 entities):
813 b
813 b
814 13f9ed0898e315bf59dc2973fec52037b6f441a2
814 13f9ed0898e315bf59dc2973fec52037b6f441a2
815 89e6c98d92887913cadf06b2adb97f26cde4849b
815 89e6c98d92887913cadf06b2adb97f26cde4849b
816 c801c9cfe94400963fcb683246217d5db77f9a9a
816 c801c9cfe94400963fcb683246217d5db77f9a9a
817 b1
817 b1
818 89e6c98d92887913cadf06b2adb97f26cde4849b
818 89e6c98d92887913cadf06b2adb97f26cde4849b
819 b2
819 b2
820 89e6c98d92887913cadf06b2adb97f26cde4849b
820 89e6c98d92887913cadf06b2adb97f26cde4849b
821
821
822
822
823 Pushing revision #1 causes uploading entity 89e6c98d9288, which is
823 Pushing revision #1 causes uploading entity 89e6c98d9288, which is
824 shared also by largefiles b1, b2 in revision #2 and b in revision #5.
824 shared also by largefiles b1, b2 in revision #2 and b in revision #5.
825
825
826 Then, entity 89e6c98d9288 is not treated as "outgoing entity" at "hg
826 Then, entity 89e6c98d9288 is not treated as "outgoing entity" at "hg
827 summary" and "hg outgoing", even though files in outgoing revision #2
827 summary" and "hg outgoing", even though files in outgoing revision #2
828 and #5 refer it.
828 and #5 refer it.
829
829
830 $ hg -R clone2 push -r 1 -q
830 $ hg -R clone2 push -r 1 -q
831 $ hg -R clone2 summary --large
831 $ hg -R clone2 summary --large
832 parent: 5:036794ea641c tip
832 parent: 5:036794ea641c tip
833 #5: refer existing largefile entity again
833 #5: refer existing largefile entity again
834 branch: default
834 branch: default
835 commit: (clean)
835 commit: (clean)
836 update: (current)
836 update: (current)
837 phases: 6 draft
837 phases: 6 draft
838 largefiles: 2 entities for 1 files to upload
838 largefiles: 2 entities for 1 files to upload
839 $ hg -R clone2 outgoing --large -T "{rev}:{node|short}\n"
839 $ hg -R clone2 outgoing --large -T "{rev}:{node|short}\n"
840 comparing with $TESTTMP/issue3651/src
840 comparing with $TESTTMP/issue3651/src
841 searching for changes
841 searching for changes
842 2:6095d0695d70
842 2:6095d0695d70
843 3:7983dce246cc
843 3:7983dce246cc
844 4:233f12ada4ae
844 4:233f12ada4ae
845 5:036794ea641c
845 5:036794ea641c
846 largefiles to upload (2 entities):
846 largefiles to upload (2 entities):
847 b
847 b
848
848
849 $ hg -R clone2 outgoing --large -T "{rev}:{node|short}\n" --debug --config progress.debug=true
849 $ hg -R clone2 outgoing --large -T "{rev}:{node|short}\n" --debug --config progress.debug=true
850 comparing with $TESTTMP/issue3651/src
850 comparing with $TESTTMP/issue3651/src
851 query 1; heads
851 query 1; heads
852 searching for changes
852 searching for changes
853 all remote heads known locally
853 all remote heads known locally
854 2:6095d0695d70
854 2:6095d0695d70
855 3:7983dce246cc
855 3:7983dce246cc
856 4:233f12ada4ae
856 4:233f12ada4ae
857 5:036794ea641c
857 5:036794ea641c
858 finding outgoing largefiles: 0/4 revisions (0.00%)
858 finding outgoing largefiles: 0/4 revisions (0.00%)
859 finding outgoing largefiles: 1/4 revisions (25.00%)
859 finding outgoing largefiles: 1/4 revisions (25.00%)
860 finding outgoing largefiles: 2/4 revisions (50.00%)
860 finding outgoing largefiles: 2/4 revisions (50.00%)
861 finding outgoing largefiles: 3/4 revisions (75.00%)
861 finding outgoing largefiles: 3/4 revisions (75.00%)
862 largefiles to upload (2 entities):
862 largefiles to upload (2 entities):
863 b
863 b
864 13f9ed0898e315bf59dc2973fec52037b6f441a2
864 13f9ed0898e315bf59dc2973fec52037b6f441a2
865 c801c9cfe94400963fcb683246217d5db77f9a9a
865 c801c9cfe94400963fcb683246217d5db77f9a9a
866
866
867
867
868 $ cd ..
868 $ cd ..
869
869
870 merge action 'd' for 'local renamed directory to d2/g' which has no filename
870 merge action 'd' for 'local renamed directory to d2/g' which has no filename
871 ==================================================================================
871 ==================================================================================
872
872
873 $ hg init merge-action
873 $ hg init merge-action
874 $ cd merge-action
874 $ cd merge-action
875 $ touch l
875 $ touch l
876 $ hg add --large l
876 $ hg add --large l
877 $ mkdir d1
877 $ mkdir d1
878 $ touch d1/f
878 $ touch d1/f
879 $ hg ci -Aqm0
879 $ hg ci -Aqm0
880 Invoking status precommit hook
880 Invoking status precommit hook
881 A d1/f
881 A d1/f
882 A l
882 A l
883 $ echo > d1/f
883 $ echo > d1/f
884 $ touch d1/g
884 $ touch d1/g
885 $ hg ci -Aqm1
885 $ hg ci -Aqm1
886 Invoking status precommit hook
886 Invoking status precommit hook
887 M d1/f
887 M d1/f
888 A d1/g
888 A d1/g
889 $ hg up -qr0
889 $ hg up -qr0
890 $ hg mv d1 d2
890 $ hg mv d1 d2
891 moving d1/f to d2/f
891 moving d1/f to d2/f
892 $ hg ci -qm2
892 $ hg ci -qm2
893 Invoking status precommit hook
893 Invoking status precommit hook
894 A d2/f
894 A d2/f
895 R d1/f
895 R d1/f
896 $ hg merge
896 $ hg merge
897 merging d2/f and d1/f to d2/f
897 merging d2/f and d1/f to d2/f
898 1 files updated, 1 files merged, 0 files removed, 0 files unresolved
898 1 files updated, 1 files merged, 0 files removed, 0 files unresolved
899 (branch merge, don't forget to commit)
899 (branch merge, don't forget to commit)
900 $ cd ..
900 $ cd ..
901
901
902
902
903 Merge conflicts:
903 Merge conflicts:
904 =====================
904 =====================
905
905
906 $ hg init merge
906 $ hg init merge
907 $ cd merge
907 $ cd merge
908 $ echo 0 > f-different
908 $ echo 0 > f-different
909 $ echo 0 > f-same
909 $ echo 0 > f-same
910 $ echo 0 > f-unchanged-1
910 $ echo 0 > f-unchanged-1
911 $ echo 0 > f-unchanged-2
911 $ echo 0 > f-unchanged-2
912 $ hg add --large *
912 $ hg add --large *
913 $ hg ci -m0
913 $ hg ci -m0
914 Invoking status precommit hook
914 Invoking status precommit hook
915 A f-different
915 A f-different
916 A f-same
916 A f-same
917 A f-unchanged-1
917 A f-unchanged-1
918 A f-unchanged-2
918 A f-unchanged-2
919 $ echo tmp1 > f-unchanged-1
919 $ echo tmp1 > f-unchanged-1
920 $ echo tmp1 > f-unchanged-2
920 $ echo tmp1 > f-unchanged-2
921 $ echo tmp1 > f-same
921 $ echo tmp1 > f-same
922 $ hg ci -m1
922 $ hg ci -m1
923 Invoking status precommit hook
923 Invoking status precommit hook
924 M f-same
924 M f-same
925 M f-unchanged-1
925 M f-unchanged-1
926 M f-unchanged-2
926 M f-unchanged-2
927 $ echo 2 > f-different
927 $ echo 2 > f-different
928 $ echo 0 > f-unchanged-1
928 $ echo 0 > f-unchanged-1
929 $ echo 1 > f-unchanged-2
929 $ echo 1 > f-unchanged-2
930 $ echo 1 > f-same
930 $ echo 1 > f-same
931 $ hg ci -m2
931 $ hg ci -m2
932 Invoking status precommit hook
932 Invoking status precommit hook
933 M f-different
933 M f-different
934 M f-same
934 M f-same
935 M f-unchanged-1
935 M f-unchanged-1
936 M f-unchanged-2
936 M f-unchanged-2
937 $ hg up -qr0
937 $ hg up -qr0
938 $ echo tmp2 > f-unchanged-1
938 $ echo tmp2 > f-unchanged-1
939 $ echo tmp2 > f-unchanged-2
939 $ echo tmp2 > f-unchanged-2
940 $ echo tmp2 > f-same
940 $ echo tmp2 > f-same
941 $ hg ci -m3
941 $ hg ci -m3
942 Invoking status precommit hook
942 Invoking status precommit hook
943 M f-same
943 M f-same
944 M f-unchanged-1
944 M f-unchanged-1
945 M f-unchanged-2
945 M f-unchanged-2
946 created new head
946 created new head
947 $ echo 1 > f-different
947 $ echo 1 > f-different
948 $ echo 1 > f-unchanged-1
948 $ echo 1 > f-unchanged-1
949 $ echo 0 > f-unchanged-2
949 $ echo 0 > f-unchanged-2
950 $ echo 1 > f-same
950 $ echo 1 > f-same
951 $ hg ci -m4
951 $ hg ci -m4
952 Invoking status precommit hook
952 Invoking status precommit hook
953 M f-different
953 M f-different
954 M f-same
954 M f-same
955 M f-unchanged-1
955 M f-unchanged-1
956 M f-unchanged-2
956 M f-unchanged-2
957 $ hg merge
957 $ hg merge
958 largefile f-different has a merge conflict
958 largefile f-different has a merge conflict
959 ancestor was 09d2af8dd22201dd8d48e5dcfcaed281ff9422c7
959 ancestor was 09d2af8dd22201dd8d48e5dcfcaed281ff9422c7
960 keep (l)ocal e5fa44f2b31c1fb553b6021e7360d07d5d91ff5e or
960 keep (l)ocal e5fa44f2b31c1fb553b6021e7360d07d5d91ff5e or
961 take (o)ther 7448d8798a4380162d4b56f9b452e2f6f9e24e7a? l
961 take (o)ther 7448d8798a4380162d4b56f9b452e2f6f9e24e7a? l
962 getting changed largefiles
962 getting changed largefiles
963 1 largefiles updated, 0 removed
963 1 largefiles updated, 0 removed
964 0 files updated, 4 files merged, 0 files removed, 0 files unresolved
964 0 files updated, 4 files merged, 0 files removed, 0 files unresolved
965 (branch merge, don't forget to commit)
965 (branch merge, don't forget to commit)
966 $ cat f-different
966 $ cat f-different
967 1
967 1
968 $ cat f-same
968 $ cat f-same
969 1
969 1
970 $ cat f-unchanged-1
970 $ cat f-unchanged-1
971 1
971 1
972 $ cat f-unchanged-2
972 $ cat f-unchanged-2
973 1
973 1
974 $ cd ..
974 $ cd ..
975
975
976 Test largefile insulation (do not enabled a side effect
976 Test largefile insulation (do not enabled a side effect
977 ========================================================
977 ========================================================
978
978
979 Check whether "largefiles" feature is supported only in repositories
979 Check whether "largefiles" feature is supported only in repositories
980 enabling largefiles extension.
980 enabling largefiles extension.
981
981
982 $ mkdir individualenabling
982 $ mkdir individualenabling
983 $ cd individualenabling
983 $ cd individualenabling
984
984
985 $ hg init enabledlocally
985 $ hg init enabledlocally
986 $ echo large > enabledlocally/large
986 $ echo large > enabledlocally/large
987 $ hg -R enabledlocally add --large enabledlocally/large
987 $ hg -R enabledlocally add --large enabledlocally/large
988 $ hg -R enabledlocally commit -m '#0'
988 $ hg -R enabledlocally commit -m '#0'
989 Invoking status precommit hook
989 Invoking status precommit hook
990 A large
990 A large
991
991
992 $ hg init notenabledlocally
992 $ hg init notenabledlocally
993 $ echo large > notenabledlocally/large
993 $ echo large > notenabledlocally/large
994 $ hg -R notenabledlocally add --large notenabledlocally/large
994 $ hg -R notenabledlocally add --large notenabledlocally/large
995 $ hg -R notenabledlocally commit -m '#0'
995 $ hg -R notenabledlocally commit -m '#0'
996 Invoking status precommit hook
996 Invoking status precommit hook
997 A large
997 A large
998
998
999 $ cat >> $HGRCPATH <<EOF
999 $ cat >> $HGRCPATH <<EOF
1000 > [extensions]
1000 > [extensions]
1001 > # disable globally
1001 > # disable globally
1002 > largefiles=!
1002 > largefiles=!
1003 > EOF
1003 > EOF
1004 $ cat >> enabledlocally/.hg/hgrc <<EOF
1004 $ cat >> enabledlocally/.hg/hgrc <<EOF
1005 > [extensions]
1005 > [extensions]
1006 > # enable locally
1006 > # enable locally
1007 > largefiles=
1007 > largefiles=
1008 > EOF
1008 > EOF
1009 $ hg -R enabledlocally root
1009 $ hg -R enabledlocally root
1010 $TESTTMP/individualenabling/enabledlocally
1010 $TESTTMP/individualenabling/enabledlocally
1011 $ hg -R notenabledlocally root
1011 $ hg -R notenabledlocally root
1012 abort: repository requires features unknown to this Mercurial: largefiles!
1012 abort: repository requires features unknown to this Mercurial: largefiles!
1013 (see https://mercurial-scm.org/wiki/MissingRequirement for more information)
1013 (see https://mercurial-scm.org/wiki/MissingRequirement for more information)
1014 [255]
1014 [255]
1015
1015
1016 $ hg init push-dst
1016 $ hg init push-dst
1017 $ hg -R enabledlocally push push-dst
1017 $ hg -R enabledlocally push push-dst
1018 pushing to push-dst
1018 pushing to push-dst
1019 abort: required features are not supported in the destination: largefiles
1019 abort: required features are not supported in the destination: largefiles
1020 [255]
1020 [255]
1021
1021
1022 $ hg init pull-src
1022 $ hg init pull-src
1023 $ hg -R pull-src pull enabledlocally
1023 $ hg -R pull-src pull enabledlocally
1024 pulling from enabledlocally
1024 pulling from enabledlocally
1025 abort: required features are not supported in the destination: largefiles
1025 abort: required features are not supported in the destination: largefiles
1026 [255]
1026 [255]
1027
1027
1028 $ hg clone enabledlocally clone-dst
1028 $ hg clone enabledlocally clone-dst
1029 abort: repository requires features unknown to this Mercurial: largefiles!
1029 abort: repository requires features unknown to this Mercurial: largefiles!
1030 (see https://mercurial-scm.org/wiki/MissingRequirement for more information)
1030 (see https://mercurial-scm.org/wiki/MissingRequirement for more information)
1031 [255]
1031 [255]
1032 $ test -d clone-dst
1032 $ test -d clone-dst
1033 [1]
1033 [1]
1034 $ hg clone --pull enabledlocally clone-pull-dst
1034 $ hg clone --pull enabledlocally clone-pull-dst
1035 abort: required features are not supported in the destination: largefiles
1035 abort: required features are not supported in the destination: largefiles
1036 [255]
1036 [255]
1037 $ test -d clone-pull-dst
1037 $ test -d clone-pull-dst
1038 [1]
1038 [1]
1039
1039
1040 #if serve
1040 #if serve
1041
1041
1042 Test largefiles specific peer setup, when largefiles is enabled
1042 Test largefiles specific peer setup, when largefiles is enabled
1043 locally (issue4109)
1043 locally (issue4109)
1044
1044
1045 $ hg showconfig extensions | grep largefiles
1045 $ hg showconfig extensions | grep largefiles
1046 extensions.largefiles=!
1046 extensions.largefiles=!
1047 $ mkdir -p $TESTTMP/individualenabling/usercache
1047 $ mkdir -p $TESTTMP/individualenabling/usercache
1048
1048
1049 $ hg serve -R enabledlocally -d -p $HGPORT --pid-file hg.pid
1049 $ hg serve -R enabledlocally -d -p $HGPORT --pid-file hg.pid
1050 $ cat hg.pid >> $DAEMON_PIDS
1050 $ cat hg.pid >> $DAEMON_PIDS
1051
1051
1052 $ hg init pull-dst
1052 $ hg init pull-dst
1053 $ cat > pull-dst/.hg/hgrc <<EOF
1053 $ cat > pull-dst/.hg/hgrc <<EOF
1054 > [extensions]
1054 > [extensions]
1055 > # enable locally
1055 > # enable locally
1056 > largefiles=
1056 > largefiles=
1057 > [largefiles]
1057 > [largefiles]
1058 > # ignore system cache to force largefiles specific wire proto access
1058 > # ignore system cache to force largefiles specific wire proto access
1059 > usercache=$TESTTMP/individualenabling/usercache
1059 > usercache=$TESTTMP/individualenabling/usercache
1060 > EOF
1060 > EOF
1061 $ hg -R pull-dst -q pull -u http://localhost:$HGPORT
1061 $ hg -R pull-dst -q pull -u http://localhost:$HGPORT
1062
1062
1063 $ killdaemons.py
1063 $ killdaemons.py
1064 #endif
1064 #endif
1065
1065
1066 Test overridden functions work correctly even for repos disabling
1066 Test overridden functions work correctly even for repos disabling
1067 largefiles (issue4547)
1067 largefiles (issue4547)
1068
1068
1069 $ hg showconfig extensions | grep largefiles
1069 $ hg showconfig extensions | grep largefiles
1070 extensions.largefiles=!
1070 extensions.largefiles=!
1071
1071
1072 (test updating implied by clone)
1072 (test updating implied by clone)
1073
1073
1074 $ hg init enabled-but-no-largefiles
1074 $ hg init enabled-but-no-largefiles
1075 $ echo normal1 > enabled-but-no-largefiles/normal1
1075 $ echo normal1 > enabled-but-no-largefiles/normal1
1076 $ hg -R enabled-but-no-largefiles add enabled-but-no-largefiles/normal1
1076 $ hg -R enabled-but-no-largefiles add enabled-but-no-largefiles/normal1
1077 $ hg -R enabled-but-no-largefiles commit -m '#0@enabled-but-no-largefiles'
1077 $ hg -R enabled-but-no-largefiles commit -m '#0@enabled-but-no-largefiles'
1078 Invoking status precommit hook
1078 Invoking status precommit hook
1079 A normal1
1079 A normal1
1080 $ cat >> enabled-but-no-largefiles/.hg/hgrc <<EOF
1080 $ cat >> enabled-but-no-largefiles/.hg/hgrc <<EOF
1081 > [extensions]
1081 > [extensions]
1082 > # enable locally
1082 > # enable locally
1083 > largefiles=
1083 > largefiles=
1084 > EOF
1084 > EOF
1085 $ hg clone -q enabled-but-no-largefiles no-largefiles
1085 $ hg clone -q enabled-but-no-largefiles no-largefiles
1086
1086
1087 $ echo normal2 > enabled-but-no-largefiles/normal2
1087 $ echo normal2 > enabled-but-no-largefiles/normal2
1088 $ hg -R enabled-but-no-largefiles add enabled-but-no-largefiles/normal2
1088 $ hg -R enabled-but-no-largefiles add enabled-but-no-largefiles/normal2
1089 $ hg -R enabled-but-no-largefiles commit -m '#1@enabled-but-no-largefiles'
1089 $ hg -R enabled-but-no-largefiles commit -m '#1@enabled-but-no-largefiles'
1090 Invoking status precommit hook
1090 Invoking status precommit hook
1091 A normal2
1091 A normal2
1092
1092
1093 $ echo normal3 > no-largefiles/normal3
1093 $ echo normal3 > no-largefiles/normal3
1094 $ hg -R no-largefiles add no-largefiles/normal3
1094 $ hg -R no-largefiles add no-largefiles/normal3
1095 $ hg -R no-largefiles commit -m '#1@no-largefiles'
1095 $ hg -R no-largefiles commit -m '#1@no-largefiles'
1096 Invoking status precommit hook
1096 Invoking status precommit hook
1097 A normal3
1097 A normal3
1098
1098
1099 $ hg -R no-largefiles -q pull --rebase
1099 $ hg -R no-largefiles -q pull --rebase
1100 Invoking status precommit hook
1100 Invoking status precommit hook
1101 A normal3
1101 A normal3
1102
1102
1103 (test reverting)
1103 (test reverting)
1104
1104
1105 $ hg init subrepo-root
1105 $ hg init subrepo-root
1106 $ cat >> subrepo-root/.hg/hgrc <<EOF
1106 $ cat >> subrepo-root/.hg/hgrc <<EOF
1107 > [extensions]
1107 > [extensions]
1108 > # enable locally
1108 > # enable locally
1109 > largefiles=
1109 > largefiles=
1110 > EOF
1110 > EOF
1111 $ echo large > subrepo-root/large
1111 $ echo large > subrepo-root/large
1112 $ mkdir -p subrepo-root/dir/subdir
1112 $ mkdir -p subrepo-root/dir/subdir
1113 $ echo large2 > subrepo-root/dir/subdir/large.bin
1113 $ echo large2 > subrepo-root/dir/subdir/large.bin
1114 $ hg -R subrepo-root add --large subrepo-root/large subrepo-root/dir/subdir/large.bin
1114 $ hg -R subrepo-root add --large subrepo-root/large subrepo-root/dir/subdir/large.bin
1115 $ hg clone -q no-largefiles subrepo-root/no-largefiles
1115 $ hg clone -q no-largefiles subrepo-root/no-largefiles
1116 $ cat > subrepo-root/.hgsub <<EOF
1116 $ cat > subrepo-root/.hgsub <<EOF
1117 > no-largefiles = no-largefiles
1117 > no-largefiles = no-largefiles
1118 > EOF
1118 > EOF
1119 $ hg -R subrepo-root add subrepo-root/.hgsub
1119 $ hg -R subrepo-root add subrepo-root/.hgsub
1120 $ hg -R subrepo-root commit -m '#0'
1120 $ hg -R subrepo-root commit -m '#0'
1121 Invoking status precommit hook
1121 Invoking status precommit hook
1122 A .hgsub
1122 A .hgsub
1123 A dir/subdir/large.bin
1123 A dir/subdir/large.bin
1124 A large
1124 A large
1125 ? .hgsubstate
1125 ? .hgsubstate
1126 $ echo dirty >> subrepo-root/large
1126 $ echo dirty >> subrepo-root/large
1127 $ echo dirty >> subrepo-root/no-largefiles/normal1
1127 $ echo dirty >> subrepo-root/no-largefiles/normal1
1128 $ hg -R subrepo-root status -S
1128 $ hg -R subrepo-root status -S
1129 M large
1129 M large
1130 M no-largefiles/normal1
1130 M no-largefiles/normal1
1131 $ hg -R subrepo-root extdiff -p echo -S --config extensions.extdiff=
1131 $ hg -R subrepo-root extdiff -p echo -S --config extensions.extdiff=
1132 "*\\no-largefiles\\normal1" "*\\no-largefiles\\normal1" (glob) (windows !)
1132 "*\\no-largefiles\\normal1" "*\\no-largefiles\\normal1" (glob) (windows !)
1133 */no-largefiles/normal1 */no-largefiles/normal1 (glob) (no-windows !)
1133 */no-largefiles/normal1 */no-largefiles/normal1 (glob) (no-windows !)
1134 [1]
1134 [1]
1135 $ hg -R subrepo-root revert --all
1135 $ hg -R subrepo-root revert --all
1136 reverting subrepo-root/.hglf/large
1136 reverting subrepo-root/.hglf/large
1137 reverting subrepo no-largefiles
1137 reverting subrepo no-largefiles
1138 reverting subrepo-root/no-largefiles/normal1
1138 reverting subrepo-root/no-largefiles/normal1
1139
1139
1140 Move (and then undo) a directory move with only largefiles.
1140 Move (and then undo) a directory move with only largefiles.
1141
1141
1142 $ cd subrepo-root
1142 $ cd subrepo-root
1143 $ "$PYTHON" $TESTDIR/list-tree.py .hglf dir* large*
1143 $ "$PYTHON" $TESTDIR/list-tree.py .hglf dir* large*
1144 .hglf/
1144 .hglf/
1145 .hglf/dir/
1145 .hglf/dir/
1146 .hglf/dir/subdir/
1146 .hglf/dir/subdir/
1147 .hglf/dir/subdir/large.bin
1147 .hglf/dir/subdir/large.bin
1148 .hglf/large
1148 .hglf/large
1149 dir/
1149 dir/
1150 dir/subdir/
1150 dir/subdir/
1151 dir/subdir/large.bin
1151 dir/subdir/large.bin
1152 large
1152 large
1153 large.orig
1153 large.orig
1154
1154
1155 $ hg mv dir/subdir dir/subdir2
1155 $ hg mv dir/subdir dir/subdir2
1156 moving .hglf/dir/subdir/large.bin to .hglf/dir/subdir2/large.bin
1156 moving .hglf/dir/subdir/large.bin to .hglf/dir/subdir2/large.bin
1157
1157
1158 $ "$PYTHON" $TESTDIR/list-tree.py .hglf dir* large*
1158 $ "$PYTHON" $TESTDIR/list-tree.py .hglf dir* large*
1159 .hglf/
1159 .hglf/
1160 .hglf/dir/
1160 .hglf/dir/
1161 .hglf/dir/subdir2/
1161 .hglf/dir/subdir2/
1162 .hglf/dir/subdir2/large.bin
1162 .hglf/dir/subdir2/large.bin
1163 .hglf/large
1163 .hglf/large
1164 dir/
1164 dir/
1165 dir/subdir2/
1165 dir/subdir2/
1166 dir/subdir2/large.bin
1166 dir/subdir2/large.bin
1167 large
1167 large
1168 large.orig
1168 large.orig
1169 $ hg status -C
1169 $ hg status -C
1170 A dir/subdir2/large.bin
1170 A dir/subdir2/large.bin
1171 dir/subdir/large.bin
1171 dir/subdir/large.bin
1172 R dir/subdir/large.bin
1172 R dir/subdir/large.bin
1173 ? large.orig
1173 ? large.orig
1174
1174
1175 $ echo 'modified' > dir/subdir2/large.bin
1175 $ echo 'modified' > dir/subdir2/large.bin
1176 $ hg status -C
1176 $ hg status -C
1177 A dir/subdir2/large.bin
1177 A dir/subdir2/large.bin
1178 dir/subdir/large.bin
1178 dir/subdir/large.bin
1179 R dir/subdir/large.bin
1179 R dir/subdir/large.bin
1180 ? large.orig
1180 ? large.orig
1181
1181
1182 $ hg revert --all
1182 $ hg revert --all
1183 forgetting .hglf/dir/subdir2/large.bin
1183 forgetting .hglf/dir/subdir2/large.bin
1184 undeleting .hglf/dir/subdir/large.bin
1184 undeleting .hglf/dir/subdir/large.bin
1185 reverting subrepo no-largefiles
1185 reverting subrepo no-largefiles
1186
1186
1187 $ hg status -C
1187 $ hg status -C
1188 ? dir/subdir2/large.bin
1188 ? dir/subdir2/large.bin
1189 ? large.orig
1189 ? large.orig
1190
1190
1191 The content of the forgotten file shouldn't be clobbered
1191 The content of the forgotten file shouldn't be clobbered
1192
1192
1193 $ cat dir/subdir2/large.bin
1193 $ cat dir/subdir2/large.bin
1194 modified
1194 modified
1195
1195
1196 The standin for subdir2 should be deleted, not just dropped
1196 The standin for subdir2 should be deleted, not just dropped
1197
1197
1198 $ "$PYTHON" $TESTDIR/list-tree.py .hglf dir* large*
1198 $ "$PYTHON" $TESTDIR/list-tree.py .hglf dir* large*
1199 .hglf/
1199 .hglf/
1200 .hglf/dir/
1200 .hglf/dir/
1201 .hglf/dir/subdir/
1201 .hglf/dir/subdir/
1202 .hglf/dir/subdir/large.bin
1202 .hglf/dir/subdir/large.bin
1203 .hglf/large
1203 .hglf/large
1204 dir/
1204 dir/
1205 dir/subdir/
1205 dir/subdir/
1206 dir/subdir/large.bin
1206 dir/subdir/large.bin
1207 dir/subdir2/
1207 dir/subdir2/
1208 dir/subdir2/large.bin
1208 dir/subdir2/large.bin
1209 large
1209 large
1210 large.orig
1210 large.orig
1211
1211
1212 $ rm -r dir/subdir2
1212 $ rm -r dir/subdir2
1213
1213
1214 'subdir' should not be in the destination. It would be if the subdir2 directory
1214 'subdir' should not be in the destination. It would be if the subdir2 directory
1215 existed under .hglf/.
1215 existed under .hglf/.
1216 $ hg mv dir/subdir dir/subdir2
1216 $ hg mv dir/subdir dir/subdir2
1217 moving .hglf/dir/subdir/large.bin to .hglf/dir/subdir2/large.bin
1217 moving .hglf/dir/subdir/large.bin to .hglf/dir/subdir2/large.bin
1218
1218
1219 $ hg status -C
1219 $ hg status -C
1220 A dir/subdir2/large.bin
1220 A dir/subdir2/large.bin
1221 dir/subdir/large.bin
1221 dir/subdir/large.bin
1222 R dir/subdir/large.bin
1222 R dir/subdir/large.bin
1223 ? large.orig
1223 ? large.orig
1224
1224
1225 $ "$PYTHON" $TESTDIR/list-tree.py .hglf dir* large*
1225 $ "$PYTHON" $TESTDIR/list-tree.py .hglf dir* large*
1226 .hglf/
1226 .hglf/
1227 .hglf/dir/
1227 .hglf/dir/
1228 .hglf/dir/subdir2/
1228 .hglf/dir/subdir2/
1229 .hglf/dir/subdir2/large.bin
1229 .hglf/dir/subdir2/large.bin
1230 .hglf/large
1230 .hglf/large
1231 dir/
1231 dir/
1232 dir/subdir2/
1232 dir/subdir2/
1233 dir/subdir2/large.bin
1233 dir/subdir2/large.bin
1234 large
1234 large
1235 large.orig
1235 large.orig
1236
1236
1237 Start from scratch, and rename something other than the final path component.
1237 Start from scratch, and rename something other than the final path component.
1238
1238
1239 $ hg up -qC .
1239 $ hg up -qC .
1240 $ hg --config extensions.purge= purge
1240 $ hg --config extensions.purge= purge
1241
1241
1242 $ hg mv dir/subdir dir2/subdir
1242 $ hg mv dir/subdir dir2/subdir
1243 moving .hglf/dir/subdir/large.bin to .hglf/dir2/subdir/large.bin
1243 moving .hglf/dir/subdir/large.bin to .hglf/dir2/subdir/large.bin
1244
1244
1245 $ hg status -C
1245 $ hg status -C
1246 A dir2/subdir/large.bin
1246 A dir2/subdir/large.bin
1247 dir/subdir/large.bin
1247 dir/subdir/large.bin
1248 R dir/subdir/large.bin
1248 R dir/subdir/large.bin
1249
1249
1250 $ "$PYTHON" $TESTDIR/list-tree.py .hglf dir* large*
1250 $ "$PYTHON" $TESTDIR/list-tree.py .hglf dir* large*
1251 .hglf/
1251 .hglf/
1252 .hglf/dir2/
1252 .hglf/dir2/
1253 .hglf/dir2/subdir/
1253 .hglf/dir2/subdir/
1254 .hglf/dir2/subdir/large.bin
1254 .hglf/dir2/subdir/large.bin
1255 .hglf/large
1255 .hglf/large
1256 dir2/
1256 dir2/
1257 dir2/subdir/
1257 dir2/subdir/
1258 dir2/subdir/large.bin
1258 dir2/subdir/large.bin
1259 large
1259 large
1260
1260
1261 $ hg revert --all
1261 $ hg revert --all
1262 forgetting .hglf/dir2/subdir/large.bin
1262 forgetting .hglf/dir2/subdir/large.bin
1263 undeleting .hglf/dir/subdir/large.bin
1263 undeleting .hglf/dir/subdir/large.bin
1264 reverting subrepo no-largefiles
1264 reverting subrepo no-largefiles
1265
1265
1266 $ hg status -C
1266 $ hg status -C
1267 ? dir2/subdir/large.bin
1267 ? dir2/subdir/large.bin
1268
1268
1269 $ "$PYTHON" $TESTDIR/list-tree.py .hglf dir* large*
1269 $ "$PYTHON" $TESTDIR/list-tree.py .hglf dir* large*
1270 .hglf/
1270 .hglf/
1271 .hglf/dir/
1271 .hglf/dir/
1272 .hglf/dir/subdir/
1272 .hglf/dir/subdir/
1273 .hglf/dir/subdir/large.bin
1273 .hglf/dir/subdir/large.bin
1274 .hglf/large
1274 .hglf/large
1275 dir/
1275 dir/
1276 dir/subdir/
1276 dir/subdir/
1277 dir/subdir/large.bin
1277 dir/subdir/large.bin
1278 dir2/
1278 dir2/
1279 dir2/subdir/
1279 dir2/subdir/
1280 dir2/subdir/large.bin
1280 dir2/subdir/large.bin
1281 large
1281 large
1282
1282
1283 $ cd ../..
1283 $ cd ../..
1284
1284
1285 Test "pull --rebase" when rebase is enabled before largefiles (issue3861)
1285 Test "pull --rebase" when rebase is enabled before largefiles (issue3861)
1286 =========================================================================
1286 =========================================================================
1287
1287
1288 $ hg showconfig extensions | grep largefiles
1288 $ hg showconfig extensions | grep largefiles
1289 extensions.largefiles=!
1289 extensions.largefiles=!
1290
1290
1291 $ mkdir issue3861
1291 $ mkdir issue3861
1292 $ cd issue3861
1292 $ cd issue3861
1293 $ hg init src
1293 $ hg init src
1294 $ hg clone -q src dst
1294 $ hg clone -q src dst
1295 $ echo a > src/a
1295 $ echo a > src/a
1296 $ hg -R src commit -Aqm "#0"
1296 $ hg -R src commit -Aqm "#0"
1297 Invoking status precommit hook
1297 Invoking status precommit hook
1298 A a
1298 A a
1299
1299
1300 $ cat >> dst/.hg/hgrc <<EOF
1300 $ cat >> dst/.hg/hgrc <<EOF
1301 > [extensions]
1301 > [extensions]
1302 > largefiles=
1302 > largefiles=
1303 > EOF
1303 > EOF
1304 $ hg -R dst pull --rebase
1304 $ hg -R dst pull --rebase
1305 pulling from $TESTTMP/issue3861/src
1305 pulling from $TESTTMP/issue3861/src
1306 requesting all changes
1306 requesting all changes
1307 adding changesets
1307 adding changesets
1308 adding manifests
1308 adding manifests
1309 adding file changes
1309 adding file changes
1310 added 1 changesets with 1 changes to 1 files
1310 added 1 changesets with 1 changes to 1 files
1311 new changesets bf5e395ced2c (1 drafts)
1311 new changesets bf5e395ced2c (1 drafts)
1312 nothing to rebase - updating instead
1312 nothing to rebase - updating instead
1313 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1313 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1314
1314
1315 $ cd ..
1315 $ cd ..
@@ -1,1148 +1,1148 b''
1 $ hg init repo
1 $ hg init repo
2 $ cd repo
2 $ cd repo
3 $ echo 123 > a
3 $ echo 123 > a
4 $ echo 123 > c
4 $ echo 123 > c
5 $ echo 123 > e
5 $ echo 123 > e
6 $ hg add a c e
6 $ hg add a c e
7 $ hg commit -m "first" a c e
7 $ hg commit -m "first" a c e
8
8
9 nothing changed
9 nothing changed
10
10
11 $ hg revert
11 $ hg revert
12 abort: no files or directories specified
12 abort: no files or directories specified
13 (use --all to revert all files)
13 (use --all to revert all files)
14 [255]
14 [255]
15 $ hg revert --all
15 $ hg revert --all
16
16
17 Introduce some changes and revert them
17 Introduce some changes and revert them
18 --------------------------------------
18 --------------------------------------
19
19
20 $ echo 123 > b
20 $ echo 123 > b
21
21
22 $ hg status
22 $ hg status
23 ? b
23 ? b
24 $ echo 12 > c
24 $ echo 12 > c
25
25
26 $ hg status
26 $ hg status
27 M c
27 M c
28 ? b
28 ? b
29 $ hg add b
29 $ hg add b
30
30
31 $ hg status
31 $ hg status
32 M c
32 M c
33 A b
33 A b
34 $ hg rm a
34 $ hg rm a
35
35
36 $ hg status
36 $ hg status
37 M c
37 M c
38 A b
38 A b
39 R a
39 R a
40
40
41 revert removal of a file
41 revert removal of a file
42
42
43 $ hg revert a
43 $ hg revert a
44 $ hg status
44 $ hg status
45 M c
45 M c
46 A b
46 A b
47
47
48 revert addition of a file
48 revert addition of a file
49
49
50 $ hg revert b
50 $ hg revert b
51 $ hg status
51 $ hg status
52 M c
52 M c
53 ? b
53 ? b
54
54
55 revert modification of a file (--no-backup)
55 revert modification of a file (--no-backup)
56
56
57 $ hg revert --no-backup c
57 $ hg revert --no-backup c
58 $ hg status
58 $ hg status
59 ? b
59 ? b
60
60
61 revert deletion (! status) of a added file
61 revert deletion (! status) of a added file
62 ------------------------------------------
62 ------------------------------------------
63
63
64 $ hg add b
64 $ hg add b
65
65
66 $ hg status b
66 $ hg status b
67 A b
67 A b
68 $ rm b
68 $ rm b
69 $ hg status b
69 $ hg status b
70 ! b
70 ! b
71 $ hg revert -v b
71 $ hg revert -v b
72 forgetting b
72 forgetting b
73 $ hg status b
73 $ hg status b
74 b: * (glob)
74 b: * (glob)
75
75
76 $ ls
76 $ ls
77 a
77 a
78 c
78 c
79 e
79 e
80
80
81 Test creation of backup (.orig) files
81 Test creation of backup (.orig) files
82 -------------------------------------
82 -------------------------------------
83
83
84 $ echo z > e
84 $ echo z > e
85 $ hg revert --all -v
85 $ hg revert --all -v
86 saving current version of e as e.orig
86 saving current version of e as e.orig
87 reverting e
87 reverting e
88
88
89 Test creation of backup (.orig) file in configured file location
89 Test creation of backup (.orig) file in configured file location
90 ----------------------------------------------------------------
90 ----------------------------------------------------------------
91
91
92 $ echo z > e
92 $ echo z > e
93 $ hg revert --all -v --config 'ui.origbackuppath=.hg/origbackups'
93 $ hg revert --all -v --config 'ui.origbackuppath=.hg/origbackups'
94 creating directory: $TESTTMP/repo/.hg/origbackups
94 creating directory: $TESTTMP/repo/.hg/origbackups
95 saving current version of e as $TESTTMP/repo/.hg/origbackups/e
95 saving current version of e as .hg/origbackups/e
96 reverting e
96 reverting e
97 $ rm -rf .hg/origbackups
97 $ rm -rf .hg/origbackups
98
98
99 revert on clean file (no change)
99 revert on clean file (no change)
100 --------------------------------
100 --------------------------------
101
101
102 $ hg revert a
102 $ hg revert a
103 no changes needed to a
103 no changes needed to a
104
104
105 revert on an untracked file
105 revert on an untracked file
106 ---------------------------
106 ---------------------------
107
107
108 $ echo q > q
108 $ echo q > q
109 $ hg revert q
109 $ hg revert q
110 file not managed: q
110 file not managed: q
111 $ rm q
111 $ rm q
112
112
113 revert on file that does not exists
113 revert on file that does not exists
114 -----------------------------------
114 -----------------------------------
115
115
116 $ hg revert notfound
116 $ hg revert notfound
117 notfound: no such file in rev 334a9e57682c
117 notfound: no such file in rev 334a9e57682c
118 $ touch d
118 $ touch d
119 $ hg add d
119 $ hg add d
120 $ hg rm a
120 $ hg rm a
121 $ hg commit -m "second"
121 $ hg commit -m "second"
122 $ echo z > z
122 $ echo z > z
123 $ hg add z
123 $ hg add z
124 $ hg st
124 $ hg st
125 A z
125 A z
126 ? e.orig
126 ? e.orig
127
127
128 revert to another revision (--rev)
128 revert to another revision (--rev)
129 ----------------------------------
129 ----------------------------------
130
130
131 $ hg revert --all -r0
131 $ hg revert --all -r0
132 forgetting z
132 forgetting z
133 removing d
133 removing d
134 adding a
134 adding a
135
135
136 revert explicitly to parent (--rev)
136 revert explicitly to parent (--rev)
137 -----------------------------------
137 -----------------------------------
138
138
139 $ hg revert --all -rtip
139 $ hg revert --all -rtip
140 forgetting a
140 forgetting a
141 undeleting d
141 undeleting d
142 $ rm a *.orig
142 $ rm a *.orig
143
143
144 revert to another revision (--rev) and exact match
144 revert to another revision (--rev) and exact match
145 --------------------------------------------------
145 --------------------------------------------------
146
146
147 exact match are more silent
147 exact match are more silent
148
148
149 $ hg revert -r0 a
149 $ hg revert -r0 a
150 $ hg st a
150 $ hg st a
151 A a
151 A a
152 $ hg rm d
152 $ hg rm d
153 $ hg st d
153 $ hg st d
154 R d
154 R d
155
155
156 should keep d removed
156 should keep d removed
157
157
158 $ hg revert -r0 d
158 $ hg revert -r0 d
159 no changes needed to d
159 no changes needed to d
160 $ hg st d
160 $ hg st d
161 R d
161 R d
162
162
163 $ hg update -C
163 $ hg update -C
164 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
164 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
165
165
166 revert of exec bit
166 revert of exec bit
167 ------------------
167 ------------------
168
168
169 #if execbit
169 #if execbit
170 $ chmod +x c
170 $ chmod +x c
171 $ hg revert --all
171 $ hg revert --all
172 reverting c
172 reverting c
173
173
174 $ test -x c || echo non-executable
174 $ test -x c || echo non-executable
175 non-executable
175 non-executable
176
176
177 $ chmod +x c
177 $ chmod +x c
178 $ hg commit -m exe
178 $ hg commit -m exe
179
179
180 $ chmod -x c
180 $ chmod -x c
181 $ hg revert --all
181 $ hg revert --all
182 reverting c
182 reverting c
183
183
184 $ test -x c && echo executable
184 $ test -x c && echo executable
185 executable
185 executable
186 #endif
186 #endif
187
187
188 Test that files reverted to other than the parent are treated as
188 Test that files reverted to other than the parent are treated as
189 "modified", even if none of mode, size and timestamp of it isn't
189 "modified", even if none of mode, size and timestamp of it isn't
190 changed on the filesystem (see also issue4583).
190 changed on the filesystem (see also issue4583).
191
191
192 $ echo 321 > e
192 $ echo 321 > e
193 $ hg diff --git
193 $ hg diff --git
194 diff --git a/e b/e
194 diff --git a/e b/e
195 --- a/e
195 --- a/e
196 +++ b/e
196 +++ b/e
197 @@ -1,1 +1,1 @@
197 @@ -1,1 +1,1 @@
198 -123
198 -123
199 +321
199 +321
200 $ hg commit -m 'ambiguity from size'
200 $ hg commit -m 'ambiguity from size'
201
201
202 $ cat e
202 $ cat e
203 321
203 321
204 $ touch -t 200001010000 e
204 $ touch -t 200001010000 e
205 $ hg debugrebuildstate
205 $ hg debugrebuildstate
206
206
207 $ cat >> .hg/hgrc <<EOF
207 $ cat >> .hg/hgrc <<EOF
208 > [fakedirstatewritetime]
208 > [fakedirstatewritetime]
209 > # emulate invoking dirstate.write() via repo.status()
209 > # emulate invoking dirstate.write() via repo.status()
210 > # at 2000-01-01 00:00
210 > # at 2000-01-01 00:00
211 > fakenow = 200001010000
211 > fakenow = 200001010000
212 >
212 >
213 > [extensions]
213 > [extensions]
214 > fakedirstatewritetime = $TESTDIR/fakedirstatewritetime.py
214 > fakedirstatewritetime = $TESTDIR/fakedirstatewritetime.py
215 > EOF
215 > EOF
216 $ hg revert -r 0 e
216 $ hg revert -r 0 e
217 $ cat >> .hg/hgrc <<EOF
217 $ cat >> .hg/hgrc <<EOF
218 > [extensions]
218 > [extensions]
219 > fakedirstatewritetime = !
219 > fakedirstatewritetime = !
220 > EOF
220 > EOF
221
221
222 $ cat e
222 $ cat e
223 123
223 123
224 $ touch -t 200001010000 e
224 $ touch -t 200001010000 e
225 $ hg status -A e
225 $ hg status -A e
226 M e
226 M e
227
227
228 $ cd ..
228 $ cd ..
229
229
230
230
231 Issue241: update and revert produces inconsistent repositories
231 Issue241: update and revert produces inconsistent repositories
232 --------------------------------------------------------------
232 --------------------------------------------------------------
233
233
234 $ hg init a
234 $ hg init a
235 $ cd a
235 $ cd a
236 $ echo a >> a
236 $ echo a >> a
237 $ hg commit -A -d '1 0' -m a
237 $ hg commit -A -d '1 0' -m a
238 adding a
238 adding a
239 $ echo a >> a
239 $ echo a >> a
240 $ hg commit -d '2 0' -m a
240 $ hg commit -d '2 0' -m a
241 $ hg update 0
241 $ hg update 0
242 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
242 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
243 $ mkdir b
243 $ mkdir b
244 $ echo b > b/b
244 $ echo b > b/b
245
245
246 call `hg revert` with no file specified
246 call `hg revert` with no file specified
247 ---------------------------------------
247 ---------------------------------------
248
248
249 $ hg revert -rtip
249 $ hg revert -rtip
250 abort: no files or directories specified
250 abort: no files or directories specified
251 (use --all to revert all files, or 'hg update 1' to update)
251 (use --all to revert all files, or 'hg update 1' to update)
252 [255]
252 [255]
253
253
254 call `hg revert` with -I
254 call `hg revert` with -I
255 ---------------------------
255 ---------------------------
256
256
257 $ echo a >> a
257 $ echo a >> a
258 $ hg revert -I a
258 $ hg revert -I a
259 reverting a
259 reverting a
260
260
261 call `hg revert` with -X
261 call `hg revert` with -X
262 ---------------------------
262 ---------------------------
263
263
264 $ echo a >> a
264 $ echo a >> a
265 $ hg revert -X d
265 $ hg revert -X d
266 reverting a
266 reverting a
267
267
268 call `hg revert` with --all
268 call `hg revert` with --all
269 ---------------------------
269 ---------------------------
270
270
271 $ hg revert --all -rtip
271 $ hg revert --all -rtip
272 reverting a
272 reverting a
273 $ rm *.orig
273 $ rm *.orig
274
274
275 Issue332: confusing message when reverting directory
275 Issue332: confusing message when reverting directory
276 ----------------------------------------------------
276 ----------------------------------------------------
277
277
278 $ hg ci -A -m b
278 $ hg ci -A -m b
279 adding b/b
279 adding b/b
280 created new head
280 created new head
281 $ echo foobar > b/b
281 $ echo foobar > b/b
282 $ mkdir newdir
282 $ mkdir newdir
283 $ echo foo > newdir/newfile
283 $ echo foo > newdir/newfile
284 $ hg add newdir/newfile
284 $ hg add newdir/newfile
285 $ hg revert b newdir
285 $ hg revert b newdir
286 forgetting newdir/newfile
286 forgetting newdir/newfile
287 reverting b/b
287 reverting b/b
288 $ echo foobar > b/b
288 $ echo foobar > b/b
289 $ hg revert .
289 $ hg revert .
290 reverting b/b
290 reverting b/b
291
291
292
292
293 reverting a rename target should revert the source
293 reverting a rename target should revert the source
294 --------------------------------------------------
294 --------------------------------------------------
295
295
296 $ hg mv a newa
296 $ hg mv a newa
297 $ hg revert newa
297 $ hg revert newa
298 $ hg st a newa
298 $ hg st a newa
299 ? newa
299 ? newa
300
300
301 Also true for move overwriting an existing file
301 Also true for move overwriting an existing file
302
302
303 $ hg mv --force a b/b
303 $ hg mv --force a b/b
304 $ hg revert b/b
304 $ hg revert b/b
305 $ hg status a b/b
305 $ hg status a b/b
306
306
307 $ cd ..
307 $ cd ..
308
308
309 $ hg init ignored
309 $ hg init ignored
310 $ cd ignored
310 $ cd ignored
311 $ echo '^ignored$' > .hgignore
311 $ echo '^ignored$' > .hgignore
312 $ echo '^ignoreddir$' >> .hgignore
312 $ echo '^ignoreddir$' >> .hgignore
313 $ echo '^removed$' >> .hgignore
313 $ echo '^removed$' >> .hgignore
314
314
315 $ mkdir ignoreddir
315 $ mkdir ignoreddir
316 $ touch ignoreddir/file
316 $ touch ignoreddir/file
317 $ touch ignoreddir/removed
317 $ touch ignoreddir/removed
318 $ touch ignored
318 $ touch ignored
319 $ touch removed
319 $ touch removed
320
320
321 4 ignored files (we will add/commit everything)
321 4 ignored files (we will add/commit everything)
322
322
323 $ hg st -A -X .hgignore
323 $ hg st -A -X .hgignore
324 I ignored
324 I ignored
325 I ignoreddir/file
325 I ignoreddir/file
326 I ignoreddir/removed
326 I ignoreddir/removed
327 I removed
327 I removed
328 $ hg ci -qAm 'add files' ignored ignoreddir/file ignoreddir/removed removed
328 $ hg ci -qAm 'add files' ignored ignoreddir/file ignoreddir/removed removed
329
329
330 $ echo >> ignored
330 $ echo >> ignored
331 $ echo >> ignoreddir/file
331 $ echo >> ignoreddir/file
332 $ hg rm removed ignoreddir/removed
332 $ hg rm removed ignoreddir/removed
333
333
334 should revert ignored* and undelete *removed
334 should revert ignored* and undelete *removed
335 --------------------------------------------
335 --------------------------------------------
336
336
337 $ hg revert -a --no-backup
337 $ hg revert -a --no-backup
338 reverting ignored
338 reverting ignored
339 reverting ignoreddir/file
339 reverting ignoreddir/file
340 undeleting ignoreddir/removed
340 undeleting ignoreddir/removed
341 undeleting removed
341 undeleting removed
342 $ hg st -mardi
342 $ hg st -mardi
343
343
344 $ hg up -qC
344 $ hg up -qC
345 $ echo >> ignored
345 $ echo >> ignored
346 $ hg rm removed
346 $ hg rm removed
347
347
348 should silently revert the named files
348 should silently revert the named files
349 --------------------------------------
349 --------------------------------------
350
350
351 $ hg revert --no-backup ignored removed
351 $ hg revert --no-backup ignored removed
352 $ hg st -mardi
352 $ hg st -mardi
353
353
354 Reverting copy (issue3920)
354 Reverting copy (issue3920)
355 --------------------------
355 --------------------------
356
356
357 someone set up us the copies
357 someone set up us the copies
358
358
359 $ rm .hgignore
359 $ rm .hgignore
360 $ hg update -C
360 $ hg update -C
361 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
361 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
362 $ hg mv ignored allyour
362 $ hg mv ignored allyour
363 $ hg copy removed base
363 $ hg copy removed base
364 $ hg commit -m rename
364 $ hg commit -m rename
365
365
366 copies and renames, you have no chance to survive make your time (issue3920)
366 copies and renames, you have no chance to survive make your time (issue3920)
367
367
368 $ hg update '.^'
368 $ hg update '.^'
369 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
369 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
370 $ hg revert -rtip -a
370 $ hg revert -rtip -a
371 removing ignored
371 removing ignored
372 adding allyour
372 adding allyour
373 adding base
373 adding base
374 $ hg status -C
374 $ hg status -C
375 A allyour
375 A allyour
376 ignored
376 ignored
377 A base
377 A base
378 removed
378 removed
379 R ignored
379 R ignored
380
380
381 Test revert of a file added by one side of the merge
381 Test revert of a file added by one side of the merge
382 ====================================================
382 ====================================================
383
383
384 remove any pending change
384 remove any pending change
385
385
386 $ hg revert --all
386 $ hg revert --all
387 forgetting allyour
387 forgetting allyour
388 forgetting base
388 forgetting base
389 undeleting ignored
389 undeleting ignored
390 $ hg purge --all --config extensions.purge=
390 $ hg purge --all --config extensions.purge=
391
391
392 Adds a new commit
392 Adds a new commit
393
393
394 $ echo foo > newadd
394 $ echo foo > newadd
395 $ hg add newadd
395 $ hg add newadd
396 $ hg commit -m 'other adds'
396 $ hg commit -m 'other adds'
397 created new head
397 created new head
398
398
399
399
400 merge it with the other head
400 merge it with the other head
401
401
402 $ hg merge # merge 1 into 2
402 $ hg merge # merge 1 into 2
403 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
403 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
404 (branch merge, don't forget to commit)
404 (branch merge, don't forget to commit)
405 $ hg summary
405 $ hg summary
406 parent: 2:b8ec310b2d4e tip
406 parent: 2:b8ec310b2d4e tip
407 other adds
407 other adds
408 parent: 1:f6180deb8fbe
408 parent: 1:f6180deb8fbe
409 rename
409 rename
410 branch: default
410 branch: default
411 commit: 2 modified, 1 removed (merge)
411 commit: 2 modified, 1 removed (merge)
412 update: (current)
412 update: (current)
413 phases: 3 draft
413 phases: 3 draft
414
414
415 clarifies who added what
415 clarifies who added what
416
416
417 $ hg status
417 $ hg status
418 M allyour
418 M allyour
419 M base
419 M base
420 R ignored
420 R ignored
421 $ hg status --change 'p1()'
421 $ hg status --change 'p1()'
422 A newadd
422 A newadd
423 $ hg status --change 'p2()'
423 $ hg status --change 'p2()'
424 A allyour
424 A allyour
425 A base
425 A base
426 R ignored
426 R ignored
427
427
428 revert file added by p1() to p1() state
428 revert file added by p1() to p1() state
429 -----------------------------------------
429 -----------------------------------------
430
430
431 $ hg revert -r 'p1()' 'glob:newad?'
431 $ hg revert -r 'p1()' 'glob:newad?'
432 $ hg status
432 $ hg status
433 M allyour
433 M allyour
434 M base
434 M base
435 R ignored
435 R ignored
436
436
437 revert file added by p1() to p2() state
437 revert file added by p1() to p2() state
438 ------------------------------------------
438 ------------------------------------------
439
439
440 $ hg revert -r 'p2()' 'glob:newad?'
440 $ hg revert -r 'p2()' 'glob:newad?'
441 removing newadd
441 removing newadd
442 $ hg status
442 $ hg status
443 M allyour
443 M allyour
444 M base
444 M base
445 R ignored
445 R ignored
446 R newadd
446 R newadd
447
447
448 revert file added by p2() to p2() state
448 revert file added by p2() to p2() state
449 ------------------------------------------
449 ------------------------------------------
450
450
451 $ hg revert -r 'p2()' 'glob:allyou?'
451 $ hg revert -r 'p2()' 'glob:allyou?'
452 $ hg status
452 $ hg status
453 M allyour
453 M allyour
454 M base
454 M base
455 R ignored
455 R ignored
456 R newadd
456 R newadd
457
457
458 revert file added by p2() to p1() state
458 revert file added by p2() to p1() state
459 ------------------------------------------
459 ------------------------------------------
460
460
461 $ hg revert -r 'p1()' 'glob:allyou?'
461 $ hg revert -r 'p1()' 'glob:allyou?'
462 removing allyour
462 removing allyour
463 $ hg status
463 $ hg status
464 M base
464 M base
465 R allyour
465 R allyour
466 R ignored
466 R ignored
467 R newadd
467 R newadd
468
468
469 Systematic behavior validation of most possible cases
469 Systematic behavior validation of most possible cases
470 =====================================================
470 =====================================================
471
471
472 This section tests most of the possible combinations of revision states and
472 This section tests most of the possible combinations of revision states and
473 working directory states. The number of possible cases is significant but they
473 working directory states. The number of possible cases is significant but they
474 but they all have a slightly different handling. So this section commits to
474 but they all have a slightly different handling. So this section commits to
475 and testing all of them to allow safe refactoring of the revert code.
475 and testing all of them to allow safe refactoring of the revert code.
476
476
477 A python script is used to generate a file history for each combination of
477 A python script is used to generate a file history for each combination of
478 states, on one side the content (or lack thereof) in two revisions, and
478 states, on one side the content (or lack thereof) in two revisions, and
479 on the other side, the content and "tracked-ness" of the working directory. The
479 on the other side, the content and "tracked-ness" of the working directory. The
480 three states generated are:
480 three states generated are:
481
481
482 - a "base" revision
482 - a "base" revision
483 - a "parent" revision
483 - a "parent" revision
484 - the working directory (based on "parent")
484 - the working directory (based on "parent")
485
485
486 The files generated have names of the form:
486 The files generated have names of the form:
487
487
488 <rev1-content>_<rev2-content>_<working-copy-content>-<tracked-ness>
488 <rev1-content>_<rev2-content>_<working-copy-content>-<tracked-ness>
489
489
490 All known states are not tested yet. See inline documentation for details.
490 All known states are not tested yet. See inline documentation for details.
491 Special cases from merge and rename are not tested by this section.
491 Special cases from merge and rename are not tested by this section.
492
492
493 Write the python script to disk
493 Write the python script to disk
494 -------------------------------
494 -------------------------------
495
495
496 check list of planned files
496 check list of planned files
497
497
498 $ "$PYTHON" $TESTDIR/generate-working-copy-states.py filelist 2
498 $ "$PYTHON" $TESTDIR/generate-working-copy-states.py filelist 2
499 content1_content1_content1-tracked
499 content1_content1_content1-tracked
500 content1_content1_content1-untracked
500 content1_content1_content1-untracked
501 content1_content1_content3-tracked
501 content1_content1_content3-tracked
502 content1_content1_content3-untracked
502 content1_content1_content3-untracked
503 content1_content1_missing-tracked
503 content1_content1_missing-tracked
504 content1_content1_missing-untracked
504 content1_content1_missing-untracked
505 content1_content2_content1-tracked
505 content1_content2_content1-tracked
506 content1_content2_content1-untracked
506 content1_content2_content1-untracked
507 content1_content2_content2-tracked
507 content1_content2_content2-tracked
508 content1_content2_content2-untracked
508 content1_content2_content2-untracked
509 content1_content2_content3-tracked
509 content1_content2_content3-tracked
510 content1_content2_content3-untracked
510 content1_content2_content3-untracked
511 content1_content2_missing-tracked
511 content1_content2_missing-tracked
512 content1_content2_missing-untracked
512 content1_content2_missing-untracked
513 content1_missing_content1-tracked
513 content1_missing_content1-tracked
514 content1_missing_content1-untracked
514 content1_missing_content1-untracked
515 content1_missing_content3-tracked
515 content1_missing_content3-tracked
516 content1_missing_content3-untracked
516 content1_missing_content3-untracked
517 content1_missing_missing-tracked
517 content1_missing_missing-tracked
518 content1_missing_missing-untracked
518 content1_missing_missing-untracked
519 missing_content2_content2-tracked
519 missing_content2_content2-tracked
520 missing_content2_content2-untracked
520 missing_content2_content2-untracked
521 missing_content2_content3-tracked
521 missing_content2_content3-tracked
522 missing_content2_content3-untracked
522 missing_content2_content3-untracked
523 missing_content2_missing-tracked
523 missing_content2_missing-tracked
524 missing_content2_missing-untracked
524 missing_content2_missing-untracked
525 missing_missing_content3-tracked
525 missing_missing_content3-tracked
526 missing_missing_content3-untracked
526 missing_missing_content3-untracked
527 missing_missing_missing-tracked
527 missing_missing_missing-tracked
528 missing_missing_missing-untracked
528 missing_missing_missing-untracked
529
529
530 Script to make a simple text version of the content
530 Script to make a simple text version of the content
531 ---------------------------------------------------
531 ---------------------------------------------------
532
532
533 $ cat << EOF >> dircontent.py
533 $ cat << EOF >> dircontent.py
534 > # generate a simple text view of the directory for easy comparison
534 > # generate a simple text view of the directory for easy comparison
535 > from __future__ import print_function
535 > from __future__ import print_function
536 > import os
536 > import os
537 > files = os.listdir('.')
537 > files = os.listdir('.')
538 > files.sort()
538 > files.sort()
539 > for filename in files:
539 > for filename in files:
540 > if os.path.isdir(filename):
540 > if os.path.isdir(filename):
541 > continue
541 > continue
542 > content = open(filename).read()
542 > content = open(filename).read()
543 > print('%-6s %s' % (content.strip(), filename))
543 > print('%-6s %s' % (content.strip(), filename))
544 > EOF
544 > EOF
545
545
546 Generate appropriate repo state
546 Generate appropriate repo state
547 -------------------------------
547 -------------------------------
548
548
549 $ hg init revert-ref
549 $ hg init revert-ref
550 $ cd revert-ref
550 $ cd revert-ref
551
551
552 Generate base changeset
552 Generate base changeset
553
553
554 $ "$PYTHON" $TESTDIR/generate-working-copy-states.py state 2 1
554 $ "$PYTHON" $TESTDIR/generate-working-copy-states.py state 2 1
555 $ hg addremove --similarity 0
555 $ hg addremove --similarity 0
556 adding content1_content1_content1-tracked
556 adding content1_content1_content1-tracked
557 adding content1_content1_content1-untracked
557 adding content1_content1_content1-untracked
558 adding content1_content1_content3-tracked
558 adding content1_content1_content3-tracked
559 adding content1_content1_content3-untracked
559 adding content1_content1_content3-untracked
560 adding content1_content1_missing-tracked
560 adding content1_content1_missing-tracked
561 adding content1_content1_missing-untracked
561 adding content1_content1_missing-untracked
562 adding content1_content2_content1-tracked
562 adding content1_content2_content1-tracked
563 adding content1_content2_content1-untracked
563 adding content1_content2_content1-untracked
564 adding content1_content2_content2-tracked
564 adding content1_content2_content2-tracked
565 adding content1_content2_content2-untracked
565 adding content1_content2_content2-untracked
566 adding content1_content2_content3-tracked
566 adding content1_content2_content3-tracked
567 adding content1_content2_content3-untracked
567 adding content1_content2_content3-untracked
568 adding content1_content2_missing-tracked
568 adding content1_content2_missing-tracked
569 adding content1_content2_missing-untracked
569 adding content1_content2_missing-untracked
570 adding content1_missing_content1-tracked
570 adding content1_missing_content1-tracked
571 adding content1_missing_content1-untracked
571 adding content1_missing_content1-untracked
572 adding content1_missing_content3-tracked
572 adding content1_missing_content3-tracked
573 adding content1_missing_content3-untracked
573 adding content1_missing_content3-untracked
574 adding content1_missing_missing-tracked
574 adding content1_missing_missing-tracked
575 adding content1_missing_missing-untracked
575 adding content1_missing_missing-untracked
576 $ hg status
576 $ hg status
577 A content1_content1_content1-tracked
577 A content1_content1_content1-tracked
578 A content1_content1_content1-untracked
578 A content1_content1_content1-untracked
579 A content1_content1_content3-tracked
579 A content1_content1_content3-tracked
580 A content1_content1_content3-untracked
580 A content1_content1_content3-untracked
581 A content1_content1_missing-tracked
581 A content1_content1_missing-tracked
582 A content1_content1_missing-untracked
582 A content1_content1_missing-untracked
583 A content1_content2_content1-tracked
583 A content1_content2_content1-tracked
584 A content1_content2_content1-untracked
584 A content1_content2_content1-untracked
585 A content1_content2_content2-tracked
585 A content1_content2_content2-tracked
586 A content1_content2_content2-untracked
586 A content1_content2_content2-untracked
587 A content1_content2_content3-tracked
587 A content1_content2_content3-tracked
588 A content1_content2_content3-untracked
588 A content1_content2_content3-untracked
589 A content1_content2_missing-tracked
589 A content1_content2_missing-tracked
590 A content1_content2_missing-untracked
590 A content1_content2_missing-untracked
591 A content1_missing_content1-tracked
591 A content1_missing_content1-tracked
592 A content1_missing_content1-untracked
592 A content1_missing_content1-untracked
593 A content1_missing_content3-tracked
593 A content1_missing_content3-tracked
594 A content1_missing_content3-untracked
594 A content1_missing_content3-untracked
595 A content1_missing_missing-tracked
595 A content1_missing_missing-tracked
596 A content1_missing_missing-untracked
596 A content1_missing_missing-untracked
597 $ hg commit -m 'base'
597 $ hg commit -m 'base'
598
598
599 (create a simple text version of the content)
599 (create a simple text version of the content)
600
600
601 $ "$PYTHON" ../dircontent.py > ../content-base.txt
601 $ "$PYTHON" ../dircontent.py > ../content-base.txt
602 $ cat ../content-base.txt
602 $ cat ../content-base.txt
603 content1 content1_content1_content1-tracked
603 content1 content1_content1_content1-tracked
604 content1 content1_content1_content1-untracked
604 content1 content1_content1_content1-untracked
605 content1 content1_content1_content3-tracked
605 content1 content1_content1_content3-tracked
606 content1 content1_content1_content3-untracked
606 content1 content1_content1_content3-untracked
607 content1 content1_content1_missing-tracked
607 content1 content1_content1_missing-tracked
608 content1 content1_content1_missing-untracked
608 content1 content1_content1_missing-untracked
609 content1 content1_content2_content1-tracked
609 content1 content1_content2_content1-tracked
610 content1 content1_content2_content1-untracked
610 content1 content1_content2_content1-untracked
611 content1 content1_content2_content2-tracked
611 content1 content1_content2_content2-tracked
612 content1 content1_content2_content2-untracked
612 content1 content1_content2_content2-untracked
613 content1 content1_content2_content3-tracked
613 content1 content1_content2_content3-tracked
614 content1 content1_content2_content3-untracked
614 content1 content1_content2_content3-untracked
615 content1 content1_content2_missing-tracked
615 content1 content1_content2_missing-tracked
616 content1 content1_content2_missing-untracked
616 content1 content1_content2_missing-untracked
617 content1 content1_missing_content1-tracked
617 content1 content1_missing_content1-tracked
618 content1 content1_missing_content1-untracked
618 content1 content1_missing_content1-untracked
619 content1 content1_missing_content3-tracked
619 content1 content1_missing_content3-tracked
620 content1 content1_missing_content3-untracked
620 content1 content1_missing_content3-untracked
621 content1 content1_missing_missing-tracked
621 content1 content1_missing_missing-tracked
622 content1 content1_missing_missing-untracked
622 content1 content1_missing_missing-untracked
623
623
624 Create parent changeset
624 Create parent changeset
625
625
626 $ "$PYTHON" $TESTDIR/generate-working-copy-states.py state 2 2
626 $ "$PYTHON" $TESTDIR/generate-working-copy-states.py state 2 2
627 $ hg addremove --similarity 0
627 $ hg addremove --similarity 0
628 removing content1_missing_content1-tracked
628 removing content1_missing_content1-tracked
629 removing content1_missing_content1-untracked
629 removing content1_missing_content1-untracked
630 removing content1_missing_content3-tracked
630 removing content1_missing_content3-tracked
631 removing content1_missing_content3-untracked
631 removing content1_missing_content3-untracked
632 removing content1_missing_missing-tracked
632 removing content1_missing_missing-tracked
633 removing content1_missing_missing-untracked
633 removing content1_missing_missing-untracked
634 adding missing_content2_content2-tracked
634 adding missing_content2_content2-tracked
635 adding missing_content2_content2-untracked
635 adding missing_content2_content2-untracked
636 adding missing_content2_content3-tracked
636 adding missing_content2_content3-tracked
637 adding missing_content2_content3-untracked
637 adding missing_content2_content3-untracked
638 adding missing_content2_missing-tracked
638 adding missing_content2_missing-tracked
639 adding missing_content2_missing-untracked
639 adding missing_content2_missing-untracked
640 $ hg status
640 $ hg status
641 M content1_content2_content1-tracked
641 M content1_content2_content1-tracked
642 M content1_content2_content1-untracked
642 M content1_content2_content1-untracked
643 M content1_content2_content2-tracked
643 M content1_content2_content2-tracked
644 M content1_content2_content2-untracked
644 M content1_content2_content2-untracked
645 M content1_content2_content3-tracked
645 M content1_content2_content3-tracked
646 M content1_content2_content3-untracked
646 M content1_content2_content3-untracked
647 M content1_content2_missing-tracked
647 M content1_content2_missing-tracked
648 M content1_content2_missing-untracked
648 M content1_content2_missing-untracked
649 A missing_content2_content2-tracked
649 A missing_content2_content2-tracked
650 A missing_content2_content2-untracked
650 A missing_content2_content2-untracked
651 A missing_content2_content3-tracked
651 A missing_content2_content3-tracked
652 A missing_content2_content3-untracked
652 A missing_content2_content3-untracked
653 A missing_content2_missing-tracked
653 A missing_content2_missing-tracked
654 A missing_content2_missing-untracked
654 A missing_content2_missing-untracked
655 R content1_missing_content1-tracked
655 R content1_missing_content1-tracked
656 R content1_missing_content1-untracked
656 R content1_missing_content1-untracked
657 R content1_missing_content3-tracked
657 R content1_missing_content3-tracked
658 R content1_missing_content3-untracked
658 R content1_missing_content3-untracked
659 R content1_missing_missing-tracked
659 R content1_missing_missing-tracked
660 R content1_missing_missing-untracked
660 R content1_missing_missing-untracked
661 $ hg commit -m 'parent'
661 $ hg commit -m 'parent'
662
662
663 (create a simple text version of the content)
663 (create a simple text version of the content)
664
664
665 $ "$PYTHON" ../dircontent.py > ../content-parent.txt
665 $ "$PYTHON" ../dircontent.py > ../content-parent.txt
666 $ cat ../content-parent.txt
666 $ cat ../content-parent.txt
667 content1 content1_content1_content1-tracked
667 content1 content1_content1_content1-tracked
668 content1 content1_content1_content1-untracked
668 content1 content1_content1_content1-untracked
669 content1 content1_content1_content3-tracked
669 content1 content1_content1_content3-tracked
670 content1 content1_content1_content3-untracked
670 content1 content1_content1_content3-untracked
671 content1 content1_content1_missing-tracked
671 content1 content1_content1_missing-tracked
672 content1 content1_content1_missing-untracked
672 content1 content1_content1_missing-untracked
673 content2 content1_content2_content1-tracked
673 content2 content1_content2_content1-tracked
674 content2 content1_content2_content1-untracked
674 content2 content1_content2_content1-untracked
675 content2 content1_content2_content2-tracked
675 content2 content1_content2_content2-tracked
676 content2 content1_content2_content2-untracked
676 content2 content1_content2_content2-untracked
677 content2 content1_content2_content3-tracked
677 content2 content1_content2_content3-tracked
678 content2 content1_content2_content3-untracked
678 content2 content1_content2_content3-untracked
679 content2 content1_content2_missing-tracked
679 content2 content1_content2_missing-tracked
680 content2 content1_content2_missing-untracked
680 content2 content1_content2_missing-untracked
681 content2 missing_content2_content2-tracked
681 content2 missing_content2_content2-tracked
682 content2 missing_content2_content2-untracked
682 content2 missing_content2_content2-untracked
683 content2 missing_content2_content3-tracked
683 content2 missing_content2_content3-tracked
684 content2 missing_content2_content3-untracked
684 content2 missing_content2_content3-untracked
685 content2 missing_content2_missing-tracked
685 content2 missing_content2_missing-tracked
686 content2 missing_content2_missing-untracked
686 content2 missing_content2_missing-untracked
687
687
688 Setup working directory
688 Setup working directory
689
689
690 $ "$PYTHON" $TESTDIR/generate-working-copy-states.py state 2 wc
690 $ "$PYTHON" $TESTDIR/generate-working-copy-states.py state 2 wc
691 $ hg addremove --similarity 0
691 $ hg addremove --similarity 0
692 adding content1_missing_content1-tracked
692 adding content1_missing_content1-tracked
693 adding content1_missing_content1-untracked
693 adding content1_missing_content1-untracked
694 adding content1_missing_content3-tracked
694 adding content1_missing_content3-tracked
695 adding content1_missing_content3-untracked
695 adding content1_missing_content3-untracked
696 adding content1_missing_missing-tracked
696 adding content1_missing_missing-tracked
697 adding content1_missing_missing-untracked
697 adding content1_missing_missing-untracked
698 adding missing_missing_content3-tracked
698 adding missing_missing_content3-tracked
699 adding missing_missing_content3-untracked
699 adding missing_missing_content3-untracked
700 adding missing_missing_missing-tracked
700 adding missing_missing_missing-tracked
701 adding missing_missing_missing-untracked
701 adding missing_missing_missing-untracked
702 $ hg forget *_*_*-untracked
702 $ hg forget *_*_*-untracked
703 $ rm *_*_missing-*
703 $ rm *_*_missing-*
704 $ hg status
704 $ hg status
705 M content1_content1_content3-tracked
705 M content1_content1_content3-tracked
706 M content1_content2_content1-tracked
706 M content1_content2_content1-tracked
707 M content1_content2_content3-tracked
707 M content1_content2_content3-tracked
708 M missing_content2_content3-tracked
708 M missing_content2_content3-tracked
709 A content1_missing_content1-tracked
709 A content1_missing_content1-tracked
710 A content1_missing_content3-tracked
710 A content1_missing_content3-tracked
711 A missing_missing_content3-tracked
711 A missing_missing_content3-tracked
712 R content1_content1_content1-untracked
712 R content1_content1_content1-untracked
713 R content1_content1_content3-untracked
713 R content1_content1_content3-untracked
714 R content1_content1_missing-untracked
714 R content1_content1_missing-untracked
715 R content1_content2_content1-untracked
715 R content1_content2_content1-untracked
716 R content1_content2_content2-untracked
716 R content1_content2_content2-untracked
717 R content1_content2_content3-untracked
717 R content1_content2_content3-untracked
718 R content1_content2_missing-untracked
718 R content1_content2_missing-untracked
719 R missing_content2_content2-untracked
719 R missing_content2_content2-untracked
720 R missing_content2_content3-untracked
720 R missing_content2_content3-untracked
721 R missing_content2_missing-untracked
721 R missing_content2_missing-untracked
722 ! content1_content1_missing-tracked
722 ! content1_content1_missing-tracked
723 ! content1_content2_missing-tracked
723 ! content1_content2_missing-tracked
724 ! content1_missing_missing-tracked
724 ! content1_missing_missing-tracked
725 ! missing_content2_missing-tracked
725 ! missing_content2_missing-tracked
726 ! missing_missing_missing-tracked
726 ! missing_missing_missing-tracked
727 ? content1_missing_content1-untracked
727 ? content1_missing_content1-untracked
728 ? content1_missing_content3-untracked
728 ? content1_missing_content3-untracked
729 ? missing_missing_content3-untracked
729 ? missing_missing_content3-untracked
730
730
731 $ hg status --rev 'desc("base")'
731 $ hg status --rev 'desc("base")'
732 M content1_content1_content3-tracked
732 M content1_content1_content3-tracked
733 M content1_content2_content2-tracked
733 M content1_content2_content2-tracked
734 M content1_content2_content3-tracked
734 M content1_content2_content3-tracked
735 M content1_missing_content3-tracked
735 M content1_missing_content3-tracked
736 A missing_content2_content2-tracked
736 A missing_content2_content2-tracked
737 A missing_content2_content3-tracked
737 A missing_content2_content3-tracked
738 A missing_missing_content3-tracked
738 A missing_missing_content3-tracked
739 R content1_content1_content1-untracked
739 R content1_content1_content1-untracked
740 R content1_content1_content3-untracked
740 R content1_content1_content3-untracked
741 R content1_content1_missing-untracked
741 R content1_content1_missing-untracked
742 R content1_content2_content1-untracked
742 R content1_content2_content1-untracked
743 R content1_content2_content2-untracked
743 R content1_content2_content2-untracked
744 R content1_content2_content3-untracked
744 R content1_content2_content3-untracked
745 R content1_content2_missing-untracked
745 R content1_content2_missing-untracked
746 R content1_missing_content1-untracked
746 R content1_missing_content1-untracked
747 R content1_missing_content3-untracked
747 R content1_missing_content3-untracked
748 R content1_missing_missing-untracked
748 R content1_missing_missing-untracked
749 ! content1_content1_missing-tracked
749 ! content1_content1_missing-tracked
750 ! content1_content2_missing-tracked
750 ! content1_content2_missing-tracked
751 ! content1_missing_missing-tracked
751 ! content1_missing_missing-tracked
752 ! missing_content2_missing-tracked
752 ! missing_content2_missing-tracked
753 ! missing_missing_missing-tracked
753 ! missing_missing_missing-tracked
754 ? missing_missing_content3-untracked
754 ? missing_missing_content3-untracked
755
755
756 (create a simple text version of the content)
756 (create a simple text version of the content)
757
757
758 $ "$PYTHON" ../dircontent.py > ../content-wc.txt
758 $ "$PYTHON" ../dircontent.py > ../content-wc.txt
759 $ cat ../content-wc.txt
759 $ cat ../content-wc.txt
760 content1 content1_content1_content1-tracked
760 content1 content1_content1_content1-tracked
761 content1 content1_content1_content1-untracked
761 content1 content1_content1_content1-untracked
762 content3 content1_content1_content3-tracked
762 content3 content1_content1_content3-tracked
763 content3 content1_content1_content3-untracked
763 content3 content1_content1_content3-untracked
764 content1 content1_content2_content1-tracked
764 content1 content1_content2_content1-tracked
765 content1 content1_content2_content1-untracked
765 content1 content1_content2_content1-untracked
766 content2 content1_content2_content2-tracked
766 content2 content1_content2_content2-tracked
767 content2 content1_content2_content2-untracked
767 content2 content1_content2_content2-untracked
768 content3 content1_content2_content3-tracked
768 content3 content1_content2_content3-tracked
769 content3 content1_content2_content3-untracked
769 content3 content1_content2_content3-untracked
770 content1 content1_missing_content1-tracked
770 content1 content1_missing_content1-tracked
771 content1 content1_missing_content1-untracked
771 content1 content1_missing_content1-untracked
772 content3 content1_missing_content3-tracked
772 content3 content1_missing_content3-tracked
773 content3 content1_missing_content3-untracked
773 content3 content1_missing_content3-untracked
774 content2 missing_content2_content2-tracked
774 content2 missing_content2_content2-tracked
775 content2 missing_content2_content2-untracked
775 content2 missing_content2_content2-untracked
776 content3 missing_content2_content3-tracked
776 content3 missing_content2_content3-tracked
777 content3 missing_content2_content3-untracked
777 content3 missing_content2_content3-untracked
778 content3 missing_missing_content3-tracked
778 content3 missing_missing_content3-tracked
779 content3 missing_missing_content3-untracked
779 content3 missing_missing_content3-untracked
780
780
781 $ cd ..
781 $ cd ..
782
782
783 Test revert --all to parent content
783 Test revert --all to parent content
784 -----------------------------------
784 -----------------------------------
785
785
786 (setup from reference repo)
786 (setup from reference repo)
787
787
788 $ cp -R revert-ref revert-parent-all
788 $ cp -R revert-ref revert-parent-all
789 $ cd revert-parent-all
789 $ cd revert-parent-all
790
790
791 check revert output
791 check revert output
792
792
793 $ hg revert --all
793 $ hg revert --all
794 forgetting content1_missing_content1-tracked
794 forgetting content1_missing_content1-tracked
795 forgetting content1_missing_content3-tracked
795 forgetting content1_missing_content3-tracked
796 forgetting content1_missing_missing-tracked
796 forgetting content1_missing_missing-tracked
797 forgetting missing_missing_content3-tracked
797 forgetting missing_missing_content3-tracked
798 forgetting missing_missing_missing-tracked
798 forgetting missing_missing_missing-tracked
799 reverting content1_content1_content3-tracked
799 reverting content1_content1_content3-tracked
800 reverting content1_content1_missing-tracked
800 reverting content1_content1_missing-tracked
801 reverting content1_content2_content1-tracked
801 reverting content1_content2_content1-tracked
802 reverting content1_content2_content3-tracked
802 reverting content1_content2_content3-tracked
803 reverting content1_content2_missing-tracked
803 reverting content1_content2_missing-tracked
804 reverting missing_content2_content3-tracked
804 reverting missing_content2_content3-tracked
805 reverting missing_content2_missing-tracked
805 reverting missing_content2_missing-tracked
806 undeleting content1_content1_content1-untracked
806 undeleting content1_content1_content1-untracked
807 undeleting content1_content1_content3-untracked
807 undeleting content1_content1_content3-untracked
808 undeleting content1_content1_missing-untracked
808 undeleting content1_content1_missing-untracked
809 undeleting content1_content2_content1-untracked
809 undeleting content1_content2_content1-untracked
810 undeleting content1_content2_content2-untracked
810 undeleting content1_content2_content2-untracked
811 undeleting content1_content2_content3-untracked
811 undeleting content1_content2_content3-untracked
812 undeleting content1_content2_missing-untracked
812 undeleting content1_content2_missing-untracked
813 undeleting missing_content2_content2-untracked
813 undeleting missing_content2_content2-untracked
814 undeleting missing_content2_content3-untracked
814 undeleting missing_content2_content3-untracked
815 undeleting missing_content2_missing-untracked
815 undeleting missing_content2_missing-untracked
816
816
817 Compare resulting directory with revert target.
817 Compare resulting directory with revert target.
818
818
819 The diff is filtered to include change only. The only difference should be
819 The diff is filtered to include change only. The only difference should be
820 additional `.orig` backup file when applicable.
820 additional `.orig` backup file when applicable.
821
821
822 $ "$PYTHON" ../dircontent.py > ../content-parent-all.txt
822 $ "$PYTHON" ../dircontent.py > ../content-parent-all.txt
823 $ cd ..
823 $ cd ..
824 $ diff -U 0 -- content-parent.txt content-parent-all.txt | grep _
824 $ diff -U 0 -- content-parent.txt content-parent-all.txt | grep _
825 +content3 content1_content1_content3-tracked.orig
825 +content3 content1_content1_content3-tracked.orig
826 +content3 content1_content1_content3-untracked.orig
826 +content3 content1_content1_content3-untracked.orig
827 +content1 content1_content2_content1-tracked.orig
827 +content1 content1_content2_content1-tracked.orig
828 +content1 content1_content2_content1-untracked.orig
828 +content1 content1_content2_content1-untracked.orig
829 +content3 content1_content2_content3-tracked.orig
829 +content3 content1_content2_content3-tracked.orig
830 +content3 content1_content2_content3-untracked.orig
830 +content3 content1_content2_content3-untracked.orig
831 +content1 content1_missing_content1-tracked
831 +content1 content1_missing_content1-tracked
832 +content1 content1_missing_content1-untracked
832 +content1 content1_missing_content1-untracked
833 +content3 content1_missing_content3-tracked
833 +content3 content1_missing_content3-tracked
834 +content3 content1_missing_content3-untracked
834 +content3 content1_missing_content3-untracked
835 +content3 missing_content2_content3-tracked.orig
835 +content3 missing_content2_content3-tracked.orig
836 +content3 missing_content2_content3-untracked.orig
836 +content3 missing_content2_content3-untracked.orig
837 +content3 missing_missing_content3-tracked
837 +content3 missing_missing_content3-tracked
838 +content3 missing_missing_content3-untracked
838 +content3 missing_missing_content3-untracked
839
839
840 Test revert --all to "base" content
840 Test revert --all to "base" content
841 -----------------------------------
841 -----------------------------------
842
842
843 (setup from reference repo)
843 (setup from reference repo)
844
844
845 $ cp -R revert-ref revert-base-all
845 $ cp -R revert-ref revert-base-all
846 $ cd revert-base-all
846 $ cd revert-base-all
847
847
848 check revert output
848 check revert output
849
849
850 $ hg revert --all --rev 'desc(base)'
850 $ hg revert --all --rev 'desc(base)'
851 forgetting missing_missing_content3-tracked
851 forgetting missing_missing_content3-tracked
852 forgetting missing_missing_missing-tracked
852 forgetting missing_missing_missing-tracked
853 removing missing_content2_content2-tracked
853 removing missing_content2_content2-tracked
854 removing missing_content2_content3-tracked
854 removing missing_content2_content3-tracked
855 removing missing_content2_missing-tracked
855 removing missing_content2_missing-tracked
856 reverting content1_content1_content3-tracked
856 reverting content1_content1_content3-tracked
857 reverting content1_content1_missing-tracked
857 reverting content1_content1_missing-tracked
858 reverting content1_content2_content2-tracked
858 reverting content1_content2_content2-tracked
859 reverting content1_content2_content3-tracked
859 reverting content1_content2_content3-tracked
860 reverting content1_content2_missing-tracked
860 reverting content1_content2_missing-tracked
861 reverting content1_missing_content3-tracked
861 reverting content1_missing_content3-tracked
862 reverting content1_missing_missing-tracked
862 reverting content1_missing_missing-tracked
863 adding content1_missing_content1-untracked
863 adding content1_missing_content1-untracked
864 adding content1_missing_content3-untracked
864 adding content1_missing_content3-untracked
865 adding content1_missing_missing-untracked
865 adding content1_missing_missing-untracked
866 undeleting content1_content1_content1-untracked
866 undeleting content1_content1_content1-untracked
867 undeleting content1_content1_content3-untracked
867 undeleting content1_content1_content3-untracked
868 undeleting content1_content1_missing-untracked
868 undeleting content1_content1_missing-untracked
869 undeleting content1_content2_content1-untracked
869 undeleting content1_content2_content1-untracked
870 undeleting content1_content2_content2-untracked
870 undeleting content1_content2_content2-untracked
871 undeleting content1_content2_content3-untracked
871 undeleting content1_content2_content3-untracked
872 undeleting content1_content2_missing-untracked
872 undeleting content1_content2_missing-untracked
873
873
874 Compare resulting directory with revert target.
874 Compare resulting directory with revert target.
875
875
876 The diff is filtered to include change only. The only difference should be
876 The diff is filtered to include change only. The only difference should be
877 additional `.orig` backup file when applicable.
877 additional `.orig` backup file when applicable.
878
878
879 $ "$PYTHON" ../dircontent.py > ../content-base-all.txt
879 $ "$PYTHON" ../dircontent.py > ../content-base-all.txt
880 $ cd ..
880 $ cd ..
881 $ diff -U 0 -- content-base.txt content-base-all.txt | grep _
881 $ diff -U 0 -- content-base.txt content-base-all.txt | grep _
882 +content3 content1_content1_content3-tracked.orig
882 +content3 content1_content1_content3-tracked.orig
883 +content3 content1_content1_content3-untracked.orig
883 +content3 content1_content1_content3-untracked.orig
884 +content2 content1_content2_content2-untracked.orig
884 +content2 content1_content2_content2-untracked.orig
885 +content3 content1_content2_content3-tracked.orig
885 +content3 content1_content2_content3-tracked.orig
886 +content3 content1_content2_content3-untracked.orig
886 +content3 content1_content2_content3-untracked.orig
887 +content3 content1_missing_content3-tracked.orig
887 +content3 content1_missing_content3-tracked.orig
888 +content3 content1_missing_content3-untracked.orig
888 +content3 content1_missing_content3-untracked.orig
889 +content2 missing_content2_content2-untracked
889 +content2 missing_content2_content2-untracked
890 +content3 missing_content2_content3-tracked.orig
890 +content3 missing_content2_content3-tracked.orig
891 +content3 missing_content2_content3-untracked
891 +content3 missing_content2_content3-untracked
892 +content3 missing_missing_content3-tracked
892 +content3 missing_missing_content3-tracked
893 +content3 missing_missing_content3-untracked
893 +content3 missing_missing_content3-untracked
894
894
895 Test revert to parent content with explicit file name
895 Test revert to parent content with explicit file name
896 -----------------------------------------------------
896 -----------------------------------------------------
897
897
898 (setup from reference repo)
898 (setup from reference repo)
899
899
900 $ cp -R revert-ref revert-parent-explicit
900 $ cp -R revert-ref revert-parent-explicit
901 $ cd revert-parent-explicit
901 $ cd revert-parent-explicit
902
902
903 revert all files individually and check the output
903 revert all files individually and check the output
904 (output is expected to be different than in the --all case)
904 (output is expected to be different than in the --all case)
905
905
906 $ for file in `"$PYTHON" $TESTDIR/generate-working-copy-states.py filelist 2`; do
906 $ for file in `"$PYTHON" $TESTDIR/generate-working-copy-states.py filelist 2`; do
907 > echo '### revert for:' $file;
907 > echo '### revert for:' $file;
908 > hg revert $file;
908 > hg revert $file;
909 > echo
909 > echo
910 > done
910 > done
911 ### revert for: content1_content1_content1-tracked
911 ### revert for: content1_content1_content1-tracked
912 no changes needed to content1_content1_content1-tracked
912 no changes needed to content1_content1_content1-tracked
913
913
914 ### revert for: content1_content1_content1-untracked
914 ### revert for: content1_content1_content1-untracked
915
915
916 ### revert for: content1_content1_content3-tracked
916 ### revert for: content1_content1_content3-tracked
917
917
918 ### revert for: content1_content1_content3-untracked
918 ### revert for: content1_content1_content3-untracked
919
919
920 ### revert for: content1_content1_missing-tracked
920 ### revert for: content1_content1_missing-tracked
921
921
922 ### revert for: content1_content1_missing-untracked
922 ### revert for: content1_content1_missing-untracked
923
923
924 ### revert for: content1_content2_content1-tracked
924 ### revert for: content1_content2_content1-tracked
925
925
926 ### revert for: content1_content2_content1-untracked
926 ### revert for: content1_content2_content1-untracked
927
927
928 ### revert for: content1_content2_content2-tracked
928 ### revert for: content1_content2_content2-tracked
929 no changes needed to content1_content2_content2-tracked
929 no changes needed to content1_content2_content2-tracked
930
930
931 ### revert for: content1_content2_content2-untracked
931 ### revert for: content1_content2_content2-untracked
932
932
933 ### revert for: content1_content2_content3-tracked
933 ### revert for: content1_content2_content3-tracked
934
934
935 ### revert for: content1_content2_content3-untracked
935 ### revert for: content1_content2_content3-untracked
936
936
937 ### revert for: content1_content2_missing-tracked
937 ### revert for: content1_content2_missing-tracked
938
938
939 ### revert for: content1_content2_missing-untracked
939 ### revert for: content1_content2_missing-untracked
940
940
941 ### revert for: content1_missing_content1-tracked
941 ### revert for: content1_missing_content1-tracked
942
942
943 ### revert for: content1_missing_content1-untracked
943 ### revert for: content1_missing_content1-untracked
944 file not managed: content1_missing_content1-untracked
944 file not managed: content1_missing_content1-untracked
945
945
946 ### revert for: content1_missing_content3-tracked
946 ### revert for: content1_missing_content3-tracked
947
947
948 ### revert for: content1_missing_content3-untracked
948 ### revert for: content1_missing_content3-untracked
949 file not managed: content1_missing_content3-untracked
949 file not managed: content1_missing_content3-untracked
950
950
951 ### revert for: content1_missing_missing-tracked
951 ### revert for: content1_missing_missing-tracked
952
952
953 ### revert for: content1_missing_missing-untracked
953 ### revert for: content1_missing_missing-untracked
954 content1_missing_missing-untracked: no such file in rev * (glob)
954 content1_missing_missing-untracked: no such file in rev * (glob)
955
955
956 ### revert for: missing_content2_content2-tracked
956 ### revert for: missing_content2_content2-tracked
957 no changes needed to missing_content2_content2-tracked
957 no changes needed to missing_content2_content2-tracked
958
958
959 ### revert for: missing_content2_content2-untracked
959 ### revert for: missing_content2_content2-untracked
960
960
961 ### revert for: missing_content2_content3-tracked
961 ### revert for: missing_content2_content3-tracked
962
962
963 ### revert for: missing_content2_content3-untracked
963 ### revert for: missing_content2_content3-untracked
964
964
965 ### revert for: missing_content2_missing-tracked
965 ### revert for: missing_content2_missing-tracked
966
966
967 ### revert for: missing_content2_missing-untracked
967 ### revert for: missing_content2_missing-untracked
968
968
969 ### revert for: missing_missing_content3-tracked
969 ### revert for: missing_missing_content3-tracked
970
970
971 ### revert for: missing_missing_content3-untracked
971 ### revert for: missing_missing_content3-untracked
972 file not managed: missing_missing_content3-untracked
972 file not managed: missing_missing_content3-untracked
973
973
974 ### revert for: missing_missing_missing-tracked
974 ### revert for: missing_missing_missing-tracked
975
975
976 ### revert for: missing_missing_missing-untracked
976 ### revert for: missing_missing_missing-untracked
977 missing_missing_missing-untracked: no such file in rev * (glob)
977 missing_missing_missing-untracked: no such file in rev * (glob)
978
978
979
979
980 check resulting directory against the --all run
980 check resulting directory against the --all run
981 (There should be no difference)
981 (There should be no difference)
982
982
983 $ "$PYTHON" ../dircontent.py > ../content-parent-explicit.txt
983 $ "$PYTHON" ../dircontent.py > ../content-parent-explicit.txt
984 $ cd ..
984 $ cd ..
985 $ diff -U 0 -- content-parent-all.txt content-parent-explicit.txt | grep _
985 $ diff -U 0 -- content-parent-all.txt content-parent-explicit.txt | grep _
986 [1]
986 [1]
987
987
988 Test revert to "base" content with explicit file name
988 Test revert to "base" content with explicit file name
989 -----------------------------------------------------
989 -----------------------------------------------------
990
990
991 (setup from reference repo)
991 (setup from reference repo)
992
992
993 $ cp -R revert-ref revert-base-explicit
993 $ cp -R revert-ref revert-base-explicit
994 $ cd revert-base-explicit
994 $ cd revert-base-explicit
995
995
996 revert all files individually and check the output
996 revert all files individually and check the output
997 (output is expected to be different than in the --all case)
997 (output is expected to be different than in the --all case)
998
998
999 $ for file in `"$PYTHON" $TESTDIR/generate-working-copy-states.py filelist 2`; do
999 $ for file in `"$PYTHON" $TESTDIR/generate-working-copy-states.py filelist 2`; do
1000 > echo '### revert for:' $file;
1000 > echo '### revert for:' $file;
1001 > hg revert $file --rev 'desc(base)';
1001 > hg revert $file --rev 'desc(base)';
1002 > echo
1002 > echo
1003 > done
1003 > done
1004 ### revert for: content1_content1_content1-tracked
1004 ### revert for: content1_content1_content1-tracked
1005 no changes needed to content1_content1_content1-tracked
1005 no changes needed to content1_content1_content1-tracked
1006
1006
1007 ### revert for: content1_content1_content1-untracked
1007 ### revert for: content1_content1_content1-untracked
1008
1008
1009 ### revert for: content1_content1_content3-tracked
1009 ### revert for: content1_content1_content3-tracked
1010
1010
1011 ### revert for: content1_content1_content3-untracked
1011 ### revert for: content1_content1_content3-untracked
1012
1012
1013 ### revert for: content1_content1_missing-tracked
1013 ### revert for: content1_content1_missing-tracked
1014
1014
1015 ### revert for: content1_content1_missing-untracked
1015 ### revert for: content1_content1_missing-untracked
1016
1016
1017 ### revert for: content1_content2_content1-tracked
1017 ### revert for: content1_content2_content1-tracked
1018 no changes needed to content1_content2_content1-tracked
1018 no changes needed to content1_content2_content1-tracked
1019
1019
1020 ### revert for: content1_content2_content1-untracked
1020 ### revert for: content1_content2_content1-untracked
1021
1021
1022 ### revert for: content1_content2_content2-tracked
1022 ### revert for: content1_content2_content2-tracked
1023
1023
1024 ### revert for: content1_content2_content2-untracked
1024 ### revert for: content1_content2_content2-untracked
1025
1025
1026 ### revert for: content1_content2_content3-tracked
1026 ### revert for: content1_content2_content3-tracked
1027
1027
1028 ### revert for: content1_content2_content3-untracked
1028 ### revert for: content1_content2_content3-untracked
1029
1029
1030 ### revert for: content1_content2_missing-tracked
1030 ### revert for: content1_content2_missing-tracked
1031
1031
1032 ### revert for: content1_content2_missing-untracked
1032 ### revert for: content1_content2_missing-untracked
1033
1033
1034 ### revert for: content1_missing_content1-tracked
1034 ### revert for: content1_missing_content1-tracked
1035 no changes needed to content1_missing_content1-tracked
1035 no changes needed to content1_missing_content1-tracked
1036
1036
1037 ### revert for: content1_missing_content1-untracked
1037 ### revert for: content1_missing_content1-untracked
1038
1038
1039 ### revert for: content1_missing_content3-tracked
1039 ### revert for: content1_missing_content3-tracked
1040
1040
1041 ### revert for: content1_missing_content3-untracked
1041 ### revert for: content1_missing_content3-untracked
1042
1042
1043 ### revert for: content1_missing_missing-tracked
1043 ### revert for: content1_missing_missing-tracked
1044
1044
1045 ### revert for: content1_missing_missing-untracked
1045 ### revert for: content1_missing_missing-untracked
1046
1046
1047 ### revert for: missing_content2_content2-tracked
1047 ### revert for: missing_content2_content2-tracked
1048
1048
1049 ### revert for: missing_content2_content2-untracked
1049 ### revert for: missing_content2_content2-untracked
1050 no changes needed to missing_content2_content2-untracked
1050 no changes needed to missing_content2_content2-untracked
1051
1051
1052 ### revert for: missing_content2_content3-tracked
1052 ### revert for: missing_content2_content3-tracked
1053
1053
1054 ### revert for: missing_content2_content3-untracked
1054 ### revert for: missing_content2_content3-untracked
1055 no changes needed to missing_content2_content3-untracked
1055 no changes needed to missing_content2_content3-untracked
1056
1056
1057 ### revert for: missing_content2_missing-tracked
1057 ### revert for: missing_content2_missing-tracked
1058
1058
1059 ### revert for: missing_content2_missing-untracked
1059 ### revert for: missing_content2_missing-untracked
1060 no changes needed to missing_content2_missing-untracked
1060 no changes needed to missing_content2_missing-untracked
1061
1061
1062 ### revert for: missing_missing_content3-tracked
1062 ### revert for: missing_missing_content3-tracked
1063
1063
1064 ### revert for: missing_missing_content3-untracked
1064 ### revert for: missing_missing_content3-untracked
1065 file not managed: missing_missing_content3-untracked
1065 file not managed: missing_missing_content3-untracked
1066
1066
1067 ### revert for: missing_missing_missing-tracked
1067 ### revert for: missing_missing_missing-tracked
1068
1068
1069 ### revert for: missing_missing_missing-untracked
1069 ### revert for: missing_missing_missing-untracked
1070 missing_missing_missing-untracked: no such file in rev * (glob)
1070 missing_missing_missing-untracked: no such file in rev * (glob)
1071
1071
1072
1072
1073 check resulting directory against the --all run
1073 check resulting directory against the --all run
1074 (There should be no difference)
1074 (There should be no difference)
1075
1075
1076 $ "$PYTHON" ../dircontent.py > ../content-base-explicit.txt
1076 $ "$PYTHON" ../dircontent.py > ../content-base-explicit.txt
1077 $ cd ..
1077 $ cd ..
1078 $ diff -U 0 -- content-base-all.txt content-base-explicit.txt | grep _
1078 $ diff -U 0 -- content-base-all.txt content-base-explicit.txt | grep _
1079 [1]
1079 [1]
1080
1080
1081 Revert to an ancestor of P2 during a merge (issue5052)
1081 Revert to an ancestor of P2 during a merge (issue5052)
1082 -----------------------------------------------------
1082 -----------------------------------------------------
1083
1083
1084 (prepare the repository)
1084 (prepare the repository)
1085
1085
1086 $ hg init issue5052
1086 $ hg init issue5052
1087 $ cd issue5052
1087 $ cd issue5052
1088 $ echo '.\.orig' > .hgignore
1088 $ echo '.\.orig' > .hgignore
1089 $ echo 0 > root
1089 $ echo 0 > root
1090 $ hg ci -qAm C0
1090 $ hg ci -qAm C0
1091 $ echo 0 > A
1091 $ echo 0 > A
1092 $ hg ci -qAm C1
1092 $ hg ci -qAm C1
1093 $ echo 1 >> A
1093 $ echo 1 >> A
1094 $ hg ci -qm C2
1094 $ hg ci -qm C2
1095 $ hg up -q 0
1095 $ hg up -q 0
1096 $ echo 1 > B
1096 $ echo 1 > B
1097 $ hg ci -qAm C3
1097 $ hg ci -qAm C3
1098 $ hg status --rev 'ancestor(.,2)' --rev 2
1098 $ hg status --rev 'ancestor(.,2)' --rev 2
1099 A A
1099 A A
1100 $ hg log -G -T '{rev} ({files})\n'
1100 $ hg log -G -T '{rev} ({files})\n'
1101 @ 3 (B)
1101 @ 3 (B)
1102 |
1102 |
1103 | o 2 (A)
1103 | o 2 (A)
1104 | |
1104 | |
1105 | o 1 (A)
1105 | o 1 (A)
1106 |/
1106 |/
1107 o 0 (.hgignore root)
1107 o 0 (.hgignore root)
1108
1108
1109
1109
1110 actual tests: reverting to something else than a merge parent
1110 actual tests: reverting to something else than a merge parent
1111
1111
1112 $ hg merge
1112 $ hg merge
1113 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1113 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1114 (branch merge, don't forget to commit)
1114 (branch merge, don't forget to commit)
1115
1115
1116 $ hg status --rev 'p1()'
1116 $ hg status --rev 'p1()'
1117 M A
1117 M A
1118 $ hg status --rev 'p2()'
1118 $ hg status --rev 'p2()'
1119 A B
1119 A B
1120 $ hg status --rev '1'
1120 $ hg status --rev '1'
1121 M A
1121 M A
1122 A B
1122 A B
1123 $ hg revert --rev 1 --all
1123 $ hg revert --rev 1 --all
1124 removing B
1124 removing B
1125 reverting A
1125 reverting A
1126 $ hg status --rev 1
1126 $ hg status --rev 1
1127
1127
1128 From the other parents
1128 From the other parents
1129
1129
1130 $ hg up -C 'p2()'
1130 $ hg up -C 'p2()'
1131 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1131 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1132 $ hg merge
1132 $ hg merge
1133 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1133 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1134 (branch merge, don't forget to commit)
1134 (branch merge, don't forget to commit)
1135
1135
1136 $ hg status --rev 'p1()'
1136 $ hg status --rev 'p1()'
1137 M B
1137 M B
1138 $ hg status --rev 'p2()'
1138 $ hg status --rev 'p2()'
1139 A A
1139 A A
1140 $ hg status --rev '1'
1140 $ hg status --rev '1'
1141 M A
1141 M A
1142 A B
1142 A B
1143 $ hg revert --rev 1 --all
1143 $ hg revert --rev 1 --all
1144 removing B
1144 removing B
1145 reverting A
1145 reverting A
1146 $ hg status --rev 1
1146 $ hg status --rev 1
1147
1147
1148 $ cd ..
1148 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now