##// END OF EJS Templates
mdiff: add a --ignore-space-at-eol option...
David Soria Parra -
r34015:da07367d default
parent child Browse files
Show More
@@ -1,3867 +1,3869 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 itertools
11 import itertools
12 import os
12 import os
13 import re
13 import re
14 import tempfile
14 import tempfile
15
15
16 from .i18n import _
16 from .i18n import _
17 from .node import (
17 from .node import (
18 hex,
18 hex,
19 nullid,
19 nullid,
20 nullrev,
20 nullrev,
21 short,
21 short,
22 )
22 )
23
23
24 from . import (
24 from . import (
25 bookmarks,
25 bookmarks,
26 changelog,
26 changelog,
27 copies,
27 copies,
28 crecord as crecordmod,
28 crecord as crecordmod,
29 dirstateguard,
29 dirstateguard,
30 encoding,
30 encoding,
31 error,
31 error,
32 formatter,
32 formatter,
33 graphmod,
33 graphmod,
34 match as matchmod,
34 match as matchmod,
35 obsolete,
35 obsolete,
36 patch,
36 patch,
37 pathutil,
37 pathutil,
38 phases,
38 phases,
39 pycompat,
39 pycompat,
40 registrar,
40 registrar,
41 revlog,
41 revlog,
42 revset,
42 revset,
43 scmutil,
43 scmutil,
44 smartset,
44 smartset,
45 templatekw,
45 templatekw,
46 templater,
46 templater,
47 util,
47 util,
48 vfs as vfsmod,
48 vfs as vfsmod,
49 )
49 )
50 stringio = util.stringio
50 stringio = util.stringio
51
51
52 # templates of common command options
52 # templates of common command options
53
53
54 dryrunopts = [
54 dryrunopts = [
55 ('n', 'dry-run', None,
55 ('n', 'dry-run', None,
56 _('do not perform actions, just print output')),
56 _('do not perform actions, just print output')),
57 ]
57 ]
58
58
59 remoteopts = [
59 remoteopts = [
60 ('e', 'ssh', '',
60 ('e', 'ssh', '',
61 _('specify ssh command to use'), _('CMD')),
61 _('specify ssh command to use'), _('CMD')),
62 ('', 'remotecmd', '',
62 ('', 'remotecmd', '',
63 _('specify hg command to run on the remote side'), _('CMD')),
63 _('specify hg command to run on the remote side'), _('CMD')),
64 ('', 'insecure', None,
64 ('', 'insecure', None,
65 _('do not verify server certificate (ignoring web.cacerts config)')),
65 _('do not verify server certificate (ignoring web.cacerts config)')),
66 ]
66 ]
67
67
68 walkopts = [
68 walkopts = [
69 ('I', 'include', [],
69 ('I', 'include', [],
70 _('include names matching the given patterns'), _('PATTERN')),
70 _('include names matching the given patterns'), _('PATTERN')),
71 ('X', 'exclude', [],
71 ('X', 'exclude', [],
72 _('exclude names matching the given patterns'), _('PATTERN')),
72 _('exclude names matching the given patterns'), _('PATTERN')),
73 ]
73 ]
74
74
75 commitopts = [
75 commitopts = [
76 ('m', 'message', '',
76 ('m', 'message', '',
77 _('use text as commit message'), _('TEXT')),
77 _('use text as commit message'), _('TEXT')),
78 ('l', 'logfile', '',
78 ('l', 'logfile', '',
79 _('read commit message from file'), _('FILE')),
79 _('read commit message from file'), _('FILE')),
80 ]
80 ]
81
81
82 commitopts2 = [
82 commitopts2 = [
83 ('d', 'date', '',
83 ('d', 'date', '',
84 _('record the specified date as commit date'), _('DATE')),
84 _('record the specified date as commit date'), _('DATE')),
85 ('u', 'user', '',
85 ('u', 'user', '',
86 _('record the specified user as committer'), _('USER')),
86 _('record the specified user as committer'), _('USER')),
87 ]
87 ]
88
88
89 # hidden for now
89 # hidden for now
90 formatteropts = [
90 formatteropts = [
91 ('T', 'template', '',
91 ('T', 'template', '',
92 _('display with template (EXPERIMENTAL)'), _('TEMPLATE')),
92 _('display with template (EXPERIMENTAL)'), _('TEMPLATE')),
93 ]
93 ]
94
94
95 templateopts = [
95 templateopts = [
96 ('', 'style', '',
96 ('', 'style', '',
97 _('display using template map file (DEPRECATED)'), _('STYLE')),
97 _('display using template map file (DEPRECATED)'), _('STYLE')),
98 ('T', 'template', '',
98 ('T', 'template', '',
99 _('display with template'), _('TEMPLATE')),
99 _('display with template'), _('TEMPLATE')),
100 ]
100 ]
101
101
102 logopts = [
102 logopts = [
103 ('p', 'patch', None, _('show patch')),
103 ('p', 'patch', None, _('show patch')),
104 ('g', 'git', None, _('use git extended diff format')),
104 ('g', 'git', None, _('use git extended diff format')),
105 ('l', 'limit', '',
105 ('l', 'limit', '',
106 _('limit number of changes displayed'), _('NUM')),
106 _('limit number of changes displayed'), _('NUM')),
107 ('M', 'no-merges', None, _('do not show merges')),
107 ('M', 'no-merges', None, _('do not show merges')),
108 ('', 'stat', None, _('output diffstat-style summary of changes')),
108 ('', 'stat', None, _('output diffstat-style summary of changes')),
109 ('G', 'graph', None, _("show the revision DAG")),
109 ('G', 'graph', None, _("show the revision DAG")),
110 ] + templateopts
110 ] + templateopts
111
111
112 diffopts = [
112 diffopts = [
113 ('a', 'text', None, _('treat all files as text')),
113 ('a', 'text', None, _('treat all files as text')),
114 ('g', 'git', None, _('use git extended diff format')),
114 ('g', 'git', None, _('use git extended diff format')),
115 ('', 'binary', None, _('generate binary diffs in git mode (default)')),
115 ('', 'binary', None, _('generate binary diffs in git mode (default)')),
116 ('', 'nodates', None, _('omit dates from diff headers'))
116 ('', 'nodates', None, _('omit dates from diff headers'))
117 ]
117 ]
118
118
119 diffwsopts = [
119 diffwsopts = [
120 ('w', 'ignore-all-space', None,
120 ('w', 'ignore-all-space', None,
121 _('ignore white space when comparing lines')),
121 _('ignore white space when comparing lines')),
122 ('b', 'ignore-space-change', None,
122 ('b', 'ignore-space-change', None,
123 _('ignore changes in the amount of white space')),
123 _('ignore changes in the amount of white space')),
124 ('B', 'ignore-blank-lines', None,
124 ('B', 'ignore-blank-lines', None,
125 _('ignore changes whose lines are all blank')),
125 _('ignore changes whose lines are all blank')),
126 ('Z', 'ignore-space-at-eol', None,
127 _('ignore changes in whitespace at EOL')),
126 ]
128 ]
127
129
128 diffopts2 = [
130 diffopts2 = [
129 ('', 'noprefix', None, _('omit a/ and b/ prefixes from filenames')),
131 ('', 'noprefix', None, _('omit a/ and b/ prefixes from filenames')),
130 ('p', 'show-function', None, _('show which function each change is in')),
132 ('p', 'show-function', None, _('show which function each change is in')),
131 ('', 'reverse', None, _('produce a diff that undoes the changes')),
133 ('', 'reverse', None, _('produce a diff that undoes the changes')),
132 ] + diffwsopts + [
134 ] + diffwsopts + [
133 ('U', 'unified', '',
135 ('U', 'unified', '',
134 _('number of lines of context to show'), _('NUM')),
136 _('number of lines of context to show'), _('NUM')),
135 ('', 'stat', None, _('output diffstat-style summary of changes')),
137 ('', 'stat', None, _('output diffstat-style summary of changes')),
136 ('', 'root', '', _('produce diffs relative to subdirectory'), _('DIR')),
138 ('', 'root', '', _('produce diffs relative to subdirectory'), _('DIR')),
137 ]
139 ]
138
140
139 mergetoolopts = [
141 mergetoolopts = [
140 ('t', 'tool', '', _('specify merge tool')),
142 ('t', 'tool', '', _('specify merge tool')),
141 ]
143 ]
142
144
143 similarityopts = [
145 similarityopts = [
144 ('s', 'similarity', '',
146 ('s', 'similarity', '',
145 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
147 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
146 ]
148 ]
147
149
148 subrepoopts = [
150 subrepoopts = [
149 ('S', 'subrepos', None,
151 ('S', 'subrepos', None,
150 _('recurse into subrepositories'))
152 _('recurse into subrepositories'))
151 ]
153 ]
152
154
153 debugrevlogopts = [
155 debugrevlogopts = [
154 ('c', 'changelog', False, _('open changelog')),
156 ('c', 'changelog', False, _('open changelog')),
155 ('m', 'manifest', False, _('open manifest')),
157 ('m', 'manifest', False, _('open manifest')),
156 ('', 'dir', '', _('open directory manifest')),
158 ('', 'dir', '', _('open directory manifest')),
157 ]
159 ]
158
160
159 # special string such that everything below this line will be ingored in the
161 # special string such that everything below this line will be ingored in the
160 # editor text
162 # editor text
161 _linebelow = "^HG: ------------------------ >8 ------------------------$"
163 _linebelow = "^HG: ------------------------ >8 ------------------------$"
162
164
163 def ishunk(x):
165 def ishunk(x):
164 hunkclasses = (crecordmod.uihunk, patch.recordhunk)
166 hunkclasses = (crecordmod.uihunk, patch.recordhunk)
165 return isinstance(x, hunkclasses)
167 return isinstance(x, hunkclasses)
166
168
167 def newandmodified(chunks, originalchunks):
169 def newandmodified(chunks, originalchunks):
168 newlyaddedandmodifiedfiles = set()
170 newlyaddedandmodifiedfiles = set()
169 for chunk in chunks:
171 for chunk in chunks:
170 if ishunk(chunk) and chunk.header.isnewfile() and chunk not in \
172 if ishunk(chunk) and chunk.header.isnewfile() and chunk not in \
171 originalchunks:
173 originalchunks:
172 newlyaddedandmodifiedfiles.add(chunk.header.filename())
174 newlyaddedandmodifiedfiles.add(chunk.header.filename())
173 return newlyaddedandmodifiedfiles
175 return newlyaddedandmodifiedfiles
174
176
175 def parsealiases(cmd):
177 def parsealiases(cmd):
176 return cmd.lstrip("^").split("|")
178 return cmd.lstrip("^").split("|")
177
179
178 def setupwrapcolorwrite(ui):
180 def setupwrapcolorwrite(ui):
179 # wrap ui.write so diff output can be labeled/colorized
181 # wrap ui.write so diff output can be labeled/colorized
180 def wrapwrite(orig, *args, **kw):
182 def wrapwrite(orig, *args, **kw):
181 label = kw.pop('label', '')
183 label = kw.pop('label', '')
182 for chunk, l in patch.difflabel(lambda: args):
184 for chunk, l in patch.difflabel(lambda: args):
183 orig(chunk, label=label + l)
185 orig(chunk, label=label + l)
184
186
185 oldwrite = ui.write
187 oldwrite = ui.write
186 def wrap(*args, **kwargs):
188 def wrap(*args, **kwargs):
187 return wrapwrite(oldwrite, *args, **kwargs)
189 return wrapwrite(oldwrite, *args, **kwargs)
188 setattr(ui, 'write', wrap)
190 setattr(ui, 'write', wrap)
189 return oldwrite
191 return oldwrite
190
192
191 def filterchunks(ui, originalhunks, usecurses, testfile, operation=None):
193 def filterchunks(ui, originalhunks, usecurses, testfile, operation=None):
192 if usecurses:
194 if usecurses:
193 if testfile:
195 if testfile:
194 recordfn = crecordmod.testdecorator(testfile,
196 recordfn = crecordmod.testdecorator(testfile,
195 crecordmod.testchunkselector)
197 crecordmod.testchunkselector)
196 else:
198 else:
197 recordfn = crecordmod.chunkselector
199 recordfn = crecordmod.chunkselector
198
200
199 return crecordmod.filterpatch(ui, originalhunks, recordfn, operation)
201 return crecordmod.filterpatch(ui, originalhunks, recordfn, operation)
200
202
201 else:
203 else:
202 return patch.filterpatch(ui, originalhunks, operation)
204 return patch.filterpatch(ui, originalhunks, operation)
203
205
204 def recordfilter(ui, originalhunks, operation=None):
206 def recordfilter(ui, originalhunks, operation=None):
205 """ Prompts the user to filter the originalhunks and return a list of
207 """ Prompts the user to filter the originalhunks and return a list of
206 selected hunks.
208 selected hunks.
207 *operation* is used for to build ui messages to indicate the user what
209 *operation* is used for to build ui messages to indicate the user what
208 kind of filtering they are doing: reverting, committing, shelving, etc.
210 kind of filtering they are doing: reverting, committing, shelving, etc.
209 (see patch.filterpatch).
211 (see patch.filterpatch).
210 """
212 """
211 usecurses = crecordmod.checkcurses(ui)
213 usecurses = crecordmod.checkcurses(ui)
212 testfile = ui.config('experimental', 'crecordtest')
214 testfile = ui.config('experimental', 'crecordtest')
213 oldwrite = setupwrapcolorwrite(ui)
215 oldwrite = setupwrapcolorwrite(ui)
214 try:
216 try:
215 newchunks, newopts = filterchunks(ui, originalhunks, usecurses,
217 newchunks, newopts = filterchunks(ui, originalhunks, usecurses,
216 testfile, operation)
218 testfile, operation)
217 finally:
219 finally:
218 ui.write = oldwrite
220 ui.write = oldwrite
219 return newchunks, newopts
221 return newchunks, newopts
220
222
221 def dorecord(ui, repo, commitfunc, cmdsuggest, backupall,
223 def dorecord(ui, repo, commitfunc, cmdsuggest, backupall,
222 filterfn, *pats, **opts):
224 filterfn, *pats, **opts):
223 from . import merge as mergemod
225 from . import merge as mergemod
224 opts = pycompat.byteskwargs(opts)
226 opts = pycompat.byteskwargs(opts)
225 if not ui.interactive():
227 if not ui.interactive():
226 if cmdsuggest:
228 if cmdsuggest:
227 msg = _('running non-interactively, use %s instead') % cmdsuggest
229 msg = _('running non-interactively, use %s instead') % cmdsuggest
228 else:
230 else:
229 msg = _('running non-interactively')
231 msg = _('running non-interactively')
230 raise error.Abort(msg)
232 raise error.Abort(msg)
231
233
232 # make sure username is set before going interactive
234 # make sure username is set before going interactive
233 if not opts.get('user'):
235 if not opts.get('user'):
234 ui.username() # raise exception, username not provided
236 ui.username() # raise exception, username not provided
235
237
236 def recordfunc(ui, repo, message, match, opts):
238 def recordfunc(ui, repo, message, match, opts):
237 """This is generic record driver.
239 """This is generic record driver.
238
240
239 Its job is to interactively filter local changes, and
241 Its job is to interactively filter local changes, and
240 accordingly prepare working directory into a state in which the
242 accordingly prepare working directory into a state in which the
241 job can be delegated to a non-interactive commit command such as
243 job can be delegated to a non-interactive commit command such as
242 'commit' or 'qrefresh'.
244 'commit' or 'qrefresh'.
243
245
244 After the actual job is done by non-interactive command, the
246 After the actual job is done by non-interactive command, the
245 working directory is restored to its original state.
247 working directory is restored to its original state.
246
248
247 In the end we'll record interesting changes, and everything else
249 In the end we'll record interesting changes, and everything else
248 will be left in place, so the user can continue working.
250 will be left in place, so the user can continue working.
249 """
251 """
250
252
251 checkunfinished(repo, commit=True)
253 checkunfinished(repo, commit=True)
252 wctx = repo[None]
254 wctx = repo[None]
253 merge = len(wctx.parents()) > 1
255 merge = len(wctx.parents()) > 1
254 if merge:
256 if merge:
255 raise error.Abort(_('cannot partially commit a merge '
257 raise error.Abort(_('cannot partially commit a merge '
256 '(use "hg commit" instead)'))
258 '(use "hg commit" instead)'))
257
259
258 def fail(f, msg):
260 def fail(f, msg):
259 raise error.Abort('%s: %s' % (f, msg))
261 raise error.Abort('%s: %s' % (f, msg))
260
262
261 force = opts.get('force')
263 force = opts.get('force')
262 if not force:
264 if not force:
263 vdirs = []
265 vdirs = []
264 match.explicitdir = vdirs.append
266 match.explicitdir = vdirs.append
265 match.bad = fail
267 match.bad = fail
266
268
267 status = repo.status(match=match)
269 status = repo.status(match=match)
268 if not force:
270 if not force:
269 repo.checkcommitpatterns(wctx, vdirs, match, status, fail)
271 repo.checkcommitpatterns(wctx, vdirs, match, status, fail)
270 diffopts = patch.difffeatureopts(ui, opts=opts, whitespace=True)
272 diffopts = patch.difffeatureopts(ui, opts=opts, whitespace=True)
271 diffopts.nodates = True
273 diffopts.nodates = True
272 diffopts.git = True
274 diffopts.git = True
273 diffopts.showfunc = True
275 diffopts.showfunc = True
274 originaldiff = patch.diff(repo, changes=status, opts=diffopts)
276 originaldiff = patch.diff(repo, changes=status, opts=diffopts)
275 originalchunks = patch.parsepatch(originaldiff)
277 originalchunks = patch.parsepatch(originaldiff)
276
278
277 # 1. filter patch, since we are intending to apply subset of it
279 # 1. filter patch, since we are intending to apply subset of it
278 try:
280 try:
279 chunks, newopts = filterfn(ui, originalchunks)
281 chunks, newopts = filterfn(ui, originalchunks)
280 except patch.PatchError as err:
282 except patch.PatchError as err:
281 raise error.Abort(_('error parsing patch: %s') % err)
283 raise error.Abort(_('error parsing patch: %s') % err)
282 opts.update(newopts)
284 opts.update(newopts)
283
285
284 # We need to keep a backup of files that have been newly added and
286 # We need to keep a backup of files that have been newly added and
285 # modified during the recording process because there is a previous
287 # modified during the recording process because there is a previous
286 # version without the edit in the workdir
288 # version without the edit in the workdir
287 newlyaddedandmodifiedfiles = newandmodified(chunks, originalchunks)
289 newlyaddedandmodifiedfiles = newandmodified(chunks, originalchunks)
288 contenders = set()
290 contenders = set()
289 for h in chunks:
291 for h in chunks:
290 try:
292 try:
291 contenders.update(set(h.files()))
293 contenders.update(set(h.files()))
292 except AttributeError:
294 except AttributeError:
293 pass
295 pass
294
296
295 changed = status.modified + status.added + status.removed
297 changed = status.modified + status.added + status.removed
296 newfiles = [f for f in changed if f in contenders]
298 newfiles = [f for f in changed if f in contenders]
297 if not newfiles:
299 if not newfiles:
298 ui.status(_('no changes to record\n'))
300 ui.status(_('no changes to record\n'))
299 return 0
301 return 0
300
302
301 modified = set(status.modified)
303 modified = set(status.modified)
302
304
303 # 2. backup changed files, so we can restore them in the end
305 # 2. backup changed files, so we can restore them in the end
304
306
305 if backupall:
307 if backupall:
306 tobackup = changed
308 tobackup = changed
307 else:
309 else:
308 tobackup = [f for f in newfiles if f in modified or f in \
310 tobackup = [f for f in newfiles if f in modified or f in \
309 newlyaddedandmodifiedfiles]
311 newlyaddedandmodifiedfiles]
310 backups = {}
312 backups = {}
311 if tobackup:
313 if tobackup:
312 backupdir = repo.vfs.join('record-backups')
314 backupdir = repo.vfs.join('record-backups')
313 try:
315 try:
314 os.mkdir(backupdir)
316 os.mkdir(backupdir)
315 except OSError as err:
317 except OSError as err:
316 if err.errno != errno.EEXIST:
318 if err.errno != errno.EEXIST:
317 raise
319 raise
318 try:
320 try:
319 # backup continues
321 # backup continues
320 for f in tobackup:
322 for f in tobackup:
321 fd, tmpname = tempfile.mkstemp(prefix=f.replace('/', '_')+'.',
323 fd, tmpname = tempfile.mkstemp(prefix=f.replace('/', '_')+'.',
322 dir=backupdir)
324 dir=backupdir)
323 os.close(fd)
325 os.close(fd)
324 ui.debug('backup %r as %r\n' % (f, tmpname))
326 ui.debug('backup %r as %r\n' % (f, tmpname))
325 util.copyfile(repo.wjoin(f), tmpname, copystat=True)
327 util.copyfile(repo.wjoin(f), tmpname, copystat=True)
326 backups[f] = tmpname
328 backups[f] = tmpname
327
329
328 fp = stringio()
330 fp = stringio()
329 for c in chunks:
331 for c in chunks:
330 fname = c.filename()
332 fname = c.filename()
331 if fname in backups:
333 if fname in backups:
332 c.write(fp)
334 c.write(fp)
333 dopatch = fp.tell()
335 dopatch = fp.tell()
334 fp.seek(0)
336 fp.seek(0)
335
337
336 # 2.5 optionally review / modify patch in text editor
338 # 2.5 optionally review / modify patch in text editor
337 if opts.get('review', False):
339 if opts.get('review', False):
338 patchtext = (crecordmod.diffhelptext
340 patchtext = (crecordmod.diffhelptext
339 + crecordmod.patchhelptext
341 + crecordmod.patchhelptext
340 + fp.read())
342 + fp.read())
341 reviewedpatch = ui.edit(patchtext, "",
343 reviewedpatch = ui.edit(patchtext, "",
342 extra={"suffix": ".diff"},
344 extra={"suffix": ".diff"},
343 repopath=repo.path)
345 repopath=repo.path)
344 fp.truncate(0)
346 fp.truncate(0)
345 fp.write(reviewedpatch)
347 fp.write(reviewedpatch)
346 fp.seek(0)
348 fp.seek(0)
347
349
348 [os.unlink(repo.wjoin(c)) for c in newlyaddedandmodifiedfiles]
350 [os.unlink(repo.wjoin(c)) for c in newlyaddedandmodifiedfiles]
349 # 3a. apply filtered patch to clean repo (clean)
351 # 3a. apply filtered patch to clean repo (clean)
350 if backups:
352 if backups:
351 # Equivalent to hg.revert
353 # Equivalent to hg.revert
352 m = scmutil.matchfiles(repo, backups.keys())
354 m = scmutil.matchfiles(repo, backups.keys())
353 mergemod.update(repo, repo.dirstate.p1(),
355 mergemod.update(repo, repo.dirstate.p1(),
354 False, True, matcher=m)
356 False, True, matcher=m)
355
357
356 # 3b. (apply)
358 # 3b. (apply)
357 if dopatch:
359 if dopatch:
358 try:
360 try:
359 ui.debug('applying patch\n')
361 ui.debug('applying patch\n')
360 ui.debug(fp.getvalue())
362 ui.debug(fp.getvalue())
361 patch.internalpatch(ui, repo, fp, 1, eolmode=None)
363 patch.internalpatch(ui, repo, fp, 1, eolmode=None)
362 except patch.PatchError as err:
364 except patch.PatchError as err:
363 raise error.Abort(str(err))
365 raise error.Abort(str(err))
364 del fp
366 del fp
365
367
366 # 4. We prepared working directory according to filtered
368 # 4. We prepared working directory according to filtered
367 # patch. Now is the time to delegate the job to
369 # patch. Now is the time to delegate the job to
368 # commit/qrefresh or the like!
370 # commit/qrefresh or the like!
369
371
370 # Make all of the pathnames absolute.
372 # Make all of the pathnames absolute.
371 newfiles = [repo.wjoin(nf) for nf in newfiles]
373 newfiles = [repo.wjoin(nf) for nf in newfiles]
372 return commitfunc(ui, repo, *newfiles, **opts)
374 return commitfunc(ui, repo, *newfiles, **opts)
373 finally:
375 finally:
374 # 5. finally restore backed-up files
376 # 5. finally restore backed-up files
375 try:
377 try:
376 dirstate = repo.dirstate
378 dirstate = repo.dirstate
377 for realname, tmpname in backups.iteritems():
379 for realname, tmpname in backups.iteritems():
378 ui.debug('restoring %r to %r\n' % (tmpname, realname))
380 ui.debug('restoring %r to %r\n' % (tmpname, realname))
379
381
380 if dirstate[realname] == 'n':
382 if dirstate[realname] == 'n':
381 # without normallookup, restoring timestamp
383 # without normallookup, restoring timestamp
382 # may cause partially committed files
384 # may cause partially committed files
383 # to be treated as unmodified
385 # to be treated as unmodified
384 dirstate.normallookup(realname)
386 dirstate.normallookup(realname)
385
387
386 # copystat=True here and above are a hack to trick any
388 # copystat=True here and above are a hack to trick any
387 # editors that have f open that we haven't modified them.
389 # editors that have f open that we haven't modified them.
388 #
390 #
389 # Also note that this racy as an editor could notice the
391 # Also note that this racy as an editor could notice the
390 # file's mtime before we've finished writing it.
392 # file's mtime before we've finished writing it.
391 util.copyfile(tmpname, repo.wjoin(realname), copystat=True)
393 util.copyfile(tmpname, repo.wjoin(realname), copystat=True)
392 os.unlink(tmpname)
394 os.unlink(tmpname)
393 if tobackup:
395 if tobackup:
394 os.rmdir(backupdir)
396 os.rmdir(backupdir)
395 except OSError:
397 except OSError:
396 pass
398 pass
397
399
398 def recordinwlock(ui, repo, message, match, opts):
400 def recordinwlock(ui, repo, message, match, opts):
399 with repo.wlock():
401 with repo.wlock():
400 return recordfunc(ui, repo, message, match, opts)
402 return recordfunc(ui, repo, message, match, opts)
401
403
402 return commit(ui, repo, recordinwlock, pats, opts)
404 return commit(ui, repo, recordinwlock, pats, opts)
403
405
404 def tersestatus(root, statlist, status, ignorefn, ignore):
406 def tersestatus(root, statlist, status, ignorefn, ignore):
405 """
407 """
406 Returns a list of statuses with directory collapsed if all the files in the
408 Returns a list of statuses with directory collapsed if all the files in the
407 directory has the same status.
409 directory has the same status.
408 """
410 """
409
411
410 def numfiles(dirname):
412 def numfiles(dirname):
411 """
413 """
412 Calculates the number of tracked files in a given directory which also
414 Calculates the number of tracked files in a given directory which also
413 includes files which were removed or deleted. Considers ignored files
415 includes files which were removed or deleted. Considers ignored files
414 if ignore argument is True or 'i' is present in status argument.
416 if ignore argument is True or 'i' is present in status argument.
415 """
417 """
416 if lencache.get(dirname):
418 if lencache.get(dirname):
417 return lencache[dirname]
419 return lencache[dirname]
418 if 'i' in status or ignore:
420 if 'i' in status or ignore:
419 def match(localpath):
421 def match(localpath):
420 absolutepath = os.path.join(root, localpath)
422 absolutepath = os.path.join(root, localpath)
421 if os.path.isdir(absolutepath) and isemptydir(absolutepath):
423 if os.path.isdir(absolutepath) and isemptydir(absolutepath):
422 return True
424 return True
423 return False
425 return False
424 else:
426 else:
425 def match(localpath):
427 def match(localpath):
426 # there can be directory whose all the files are ignored and
428 # there can be directory whose all the files are ignored and
427 # hence the drectory should also be ignored while counting
429 # hence the drectory should also be ignored while counting
428 # number of files or subdirs in it's parent directory. This
430 # number of files or subdirs in it's parent directory. This
429 # checks the same.
431 # checks the same.
430 # XXX: We need a better logic here.
432 # XXX: We need a better logic here.
431 if os.path.isdir(os.path.join(root, localpath)):
433 if os.path.isdir(os.path.join(root, localpath)):
432 return isignoreddir(localpath)
434 return isignoreddir(localpath)
433 else:
435 else:
434 # XXX: there can be files which have the ignored pattern but
436 # XXX: there can be files which have the ignored pattern but
435 # are not ignored. That leads to bug in counting number of
437 # are not ignored. That leads to bug in counting number of
436 # tracked files in the directory.
438 # tracked files in the directory.
437 return ignorefn(localpath)
439 return ignorefn(localpath)
438 lendir = 0
440 lendir = 0
439 abspath = os.path.join(root, dirname)
441 abspath = os.path.join(root, dirname)
440 # There might be cases when a directory does not exists as the whole
442 # There might be cases when a directory does not exists as the whole
441 # directory can be removed and/or deleted.
443 # directory can be removed and/or deleted.
442 try:
444 try:
443 for f in os.listdir(abspath):
445 for f in os.listdir(abspath):
444 localpath = os.path.join(dirname, f)
446 localpath = os.path.join(dirname, f)
445 if not match(localpath):
447 if not match(localpath):
446 lendir += 1
448 lendir += 1
447 except OSError:
449 except OSError:
448 pass
450 pass
449 lendir += len(absentdir.get(dirname, []))
451 lendir += len(absentdir.get(dirname, []))
450 lencache[dirname] = lendir
452 lencache[dirname] = lendir
451 return lendir
453 return lendir
452
454
453 def isemptydir(abspath):
455 def isemptydir(abspath):
454 """
456 """
455 Check whether a directory is empty or not, i.e. there is no files in the
457 Check whether a directory is empty or not, i.e. there is no files in the
456 directory and all its subdirectories.
458 directory and all its subdirectories.
457 """
459 """
458 for f in os.listdir(abspath):
460 for f in os.listdir(abspath):
459 fullpath = os.path.join(abspath, f)
461 fullpath = os.path.join(abspath, f)
460 if os.path.isdir(fullpath):
462 if os.path.isdir(fullpath):
461 # recursion here
463 # recursion here
462 ret = isemptydir(fullpath)
464 ret = isemptydir(fullpath)
463 if not ret:
465 if not ret:
464 return False
466 return False
465 else:
467 else:
466 return False
468 return False
467 return True
469 return True
468
470
469 def isignoreddir(localpath):
471 def isignoreddir(localpath):
470 """Return True if `localpath` directory is ignored or contains only
472 """Return True if `localpath` directory is ignored or contains only
471 ignored files and should hence be considered ignored.
473 ignored files and should hence be considered ignored.
472 """
474 """
473 dirpath = os.path.join(root, localpath)
475 dirpath = os.path.join(root, localpath)
474 if ignorefn(dirpath):
476 if ignorefn(dirpath):
475 return True
477 return True
476 for f in os.listdir(dirpath):
478 for f in os.listdir(dirpath):
477 filepath = os.path.join(dirpath, f)
479 filepath = os.path.join(dirpath, f)
478 if os.path.isdir(filepath):
480 if os.path.isdir(filepath):
479 # recursion here
481 # recursion here
480 ret = isignoreddir(os.path.join(localpath, f))
482 ret = isignoreddir(os.path.join(localpath, f))
481 if not ret:
483 if not ret:
482 return False
484 return False
483 else:
485 else:
484 if not ignorefn(os.path.join(localpath, f)):
486 if not ignorefn(os.path.join(localpath, f)):
485 return False
487 return False
486 return True
488 return True
487
489
488 def absentones(removedfiles, missingfiles):
490 def absentones(removedfiles, missingfiles):
489 """
491 """
490 Returns a dictionary of directories with files in it which are either
492 Returns a dictionary of directories with files in it which are either
491 removed or missing (deleted) in them.
493 removed or missing (deleted) in them.
492 """
494 """
493 absentdir = {}
495 absentdir = {}
494 absentfiles = removedfiles + missingfiles
496 absentfiles = removedfiles + missingfiles
495 while absentfiles:
497 while absentfiles:
496 f = absentfiles.pop()
498 f = absentfiles.pop()
497 par = os.path.dirname(f)
499 par = os.path.dirname(f)
498 if par == '':
500 if par == '':
499 continue
501 continue
500 # we need to store files rather than number of files as some files
502 # we need to store files rather than number of files as some files
501 # or subdirectories in a directory can be counted twice. This is
503 # or subdirectories in a directory can be counted twice. This is
502 # also we have used sets here.
504 # also we have used sets here.
503 try:
505 try:
504 absentdir[par].add(f)
506 absentdir[par].add(f)
505 except KeyError:
507 except KeyError:
506 absentdir[par] = set([f])
508 absentdir[par] = set([f])
507 absentfiles.append(par)
509 absentfiles.append(par)
508 return absentdir
510 return absentdir
509
511
510 indexes = {'m': 0, 'a': 1, 'r': 2, 'd': 3, 'u': 4, 'i': 5, 'c': 6}
512 indexes = {'m': 0, 'a': 1, 'r': 2, 'd': 3, 'u': 4, 'i': 5, 'c': 6}
511 # get a dictonary of directories and files which are missing as os.listdir()
513 # get a dictonary of directories and files which are missing as os.listdir()
512 # won't be able to list them.
514 # won't be able to list them.
513 absentdir = absentones(statlist[2], statlist[3])
515 absentdir = absentones(statlist[2], statlist[3])
514 finalrs = [[]] * len(indexes)
516 finalrs = [[]] * len(indexes)
515 didsomethingchanged = False
517 didsomethingchanged = False
516 # dictionary to store number of files and subdir in a directory so that we
518 # dictionary to store number of files and subdir in a directory so that we
517 # don't compute that again.
519 # don't compute that again.
518 lencache = {}
520 lencache = {}
519
521
520 for st in pycompat.bytestr(status):
522 for st in pycompat.bytestr(status):
521
523
522 try:
524 try:
523 ind = indexes[st]
525 ind = indexes[st]
524 except KeyError:
526 except KeyError:
525 # TODO: Need a better error message here
527 # TODO: Need a better error message here
526 raise error.Abort("'%s' not recognized" % st)
528 raise error.Abort("'%s' not recognized" % st)
527
529
528 sfiles = statlist[ind]
530 sfiles = statlist[ind]
529 if not sfiles:
531 if not sfiles:
530 continue
532 continue
531 pardict = {}
533 pardict = {}
532 for a in sfiles:
534 for a in sfiles:
533 par = os.path.dirname(a)
535 par = os.path.dirname(a)
534 pardict.setdefault(par, []).append(a)
536 pardict.setdefault(par, []).append(a)
535
537
536 rs = []
538 rs = []
537 newls = []
539 newls = []
538 for par, files in pardict.iteritems():
540 for par, files in pardict.iteritems():
539 lenpar = numfiles(par)
541 lenpar = numfiles(par)
540 if lenpar == len(files):
542 if lenpar == len(files):
541 newls.append(par)
543 newls.append(par)
542
544
543 if not newls:
545 if not newls:
544 continue
546 continue
545
547
546 while newls:
548 while newls:
547 newel = newls.pop()
549 newel = newls.pop()
548 if newel == '':
550 if newel == '':
549 continue
551 continue
550 parn = os.path.dirname(newel)
552 parn = os.path.dirname(newel)
551 pardict[newel] = []
553 pardict[newel] = []
552 # Adding pycompat.ossep as newel is a directory.
554 # Adding pycompat.ossep as newel is a directory.
553 pardict.setdefault(parn, []).append(newel + pycompat.ossep)
555 pardict.setdefault(parn, []).append(newel + pycompat.ossep)
554 lenpar = numfiles(parn)
556 lenpar = numfiles(parn)
555 if lenpar == len(pardict[parn]):
557 if lenpar == len(pardict[parn]):
556 newls.append(parn)
558 newls.append(parn)
557
559
558 # dict.values() for Py3 compatibility
560 # dict.values() for Py3 compatibility
559 for files in pardict.values():
561 for files in pardict.values():
560 rs.extend(files)
562 rs.extend(files)
561
563
562 rs.sort()
564 rs.sort()
563 finalrs[ind] = rs
565 finalrs[ind] = rs
564 didsomethingchanged = True
566 didsomethingchanged = True
565
567
566 # If nothing is changed, make sure the order of files is preserved.
568 # If nothing is changed, make sure the order of files is preserved.
567 if not didsomethingchanged:
569 if not didsomethingchanged:
568 return statlist
570 return statlist
569
571
570 for x in xrange(len(indexes)):
572 for x in xrange(len(indexes)):
571 if not finalrs[x]:
573 if not finalrs[x]:
572 finalrs[x] = statlist[x]
574 finalrs[x] = statlist[x]
573
575
574 return finalrs
576 return finalrs
575
577
576 def _commentlines(raw):
578 def _commentlines(raw):
577 '''Surround lineswith a comment char and a new line'''
579 '''Surround lineswith a comment char and a new line'''
578 lines = raw.splitlines()
580 lines = raw.splitlines()
579 commentedlines = ['# %s' % line for line in lines]
581 commentedlines = ['# %s' % line for line in lines]
580 return '\n'.join(commentedlines) + '\n'
582 return '\n'.join(commentedlines) + '\n'
581
583
582 def _conflictsmsg(repo):
584 def _conflictsmsg(repo):
583 # avoid merge cycle
585 # avoid merge cycle
584 from . import merge as mergemod
586 from . import merge as mergemod
585 mergestate = mergemod.mergestate.read(repo)
587 mergestate = mergemod.mergestate.read(repo)
586 if not mergestate.active():
588 if not mergestate.active():
587 return
589 return
588
590
589 m = scmutil.match(repo[None])
591 m = scmutil.match(repo[None])
590 unresolvedlist = [f for f in mergestate.unresolved() if m(f)]
592 unresolvedlist = [f for f in mergestate.unresolved() if m(f)]
591 if unresolvedlist:
593 if unresolvedlist:
592 mergeliststr = '\n'.join(
594 mergeliststr = '\n'.join(
593 [' %s' % os.path.relpath(
595 [' %s' % os.path.relpath(
594 os.path.join(repo.root, path),
596 os.path.join(repo.root, path),
595 pycompat.getcwd()) for path in unresolvedlist])
597 pycompat.getcwd()) for path in unresolvedlist])
596 msg = _('''Unresolved merge conflicts:
598 msg = _('''Unresolved merge conflicts:
597
599
598 %s
600 %s
599
601
600 To mark files as resolved: hg resolve --mark FILE''') % mergeliststr
602 To mark files as resolved: hg resolve --mark FILE''') % mergeliststr
601 else:
603 else:
602 msg = _('No unresolved merge conflicts.')
604 msg = _('No unresolved merge conflicts.')
603
605
604 return _commentlines(msg)
606 return _commentlines(msg)
605
607
606 def _helpmessage(continuecmd, abortcmd):
608 def _helpmessage(continuecmd, abortcmd):
607 msg = _('To continue: %s\n'
609 msg = _('To continue: %s\n'
608 'To abort: %s') % (continuecmd, abortcmd)
610 'To abort: %s') % (continuecmd, abortcmd)
609 return _commentlines(msg)
611 return _commentlines(msg)
610
612
611 def _rebasemsg():
613 def _rebasemsg():
612 return _helpmessage('hg rebase --continue', 'hg rebase --abort')
614 return _helpmessage('hg rebase --continue', 'hg rebase --abort')
613
615
614 def _histeditmsg():
616 def _histeditmsg():
615 return _helpmessage('hg histedit --continue', 'hg histedit --abort')
617 return _helpmessage('hg histedit --continue', 'hg histedit --abort')
616
618
617 def _unshelvemsg():
619 def _unshelvemsg():
618 return _helpmessage('hg unshelve --continue', 'hg unshelve --abort')
620 return _helpmessage('hg unshelve --continue', 'hg unshelve --abort')
619
621
620 def _updatecleanmsg(dest=None):
622 def _updatecleanmsg(dest=None):
621 warning = _('warning: this will discard uncommitted changes')
623 warning = _('warning: this will discard uncommitted changes')
622 return 'hg update --clean %s (%s)' % (dest or '.', warning)
624 return 'hg update --clean %s (%s)' % (dest or '.', warning)
623
625
624 def _graftmsg():
626 def _graftmsg():
625 # tweakdefaults requires `update` to have a rev hence the `.`
627 # tweakdefaults requires `update` to have a rev hence the `.`
626 return _helpmessage('hg graft --continue', _updatecleanmsg())
628 return _helpmessage('hg graft --continue', _updatecleanmsg())
627
629
628 def _mergemsg():
630 def _mergemsg():
629 # tweakdefaults requires `update` to have a rev hence the `.`
631 # tweakdefaults requires `update` to have a rev hence the `.`
630 return _helpmessage('hg commit', _updatecleanmsg())
632 return _helpmessage('hg commit', _updatecleanmsg())
631
633
632 def _bisectmsg():
634 def _bisectmsg():
633 msg = _('To mark the changeset good: hg bisect --good\n'
635 msg = _('To mark the changeset good: hg bisect --good\n'
634 'To mark the changeset bad: hg bisect --bad\n'
636 'To mark the changeset bad: hg bisect --bad\n'
635 'To abort: hg bisect --reset\n')
637 'To abort: hg bisect --reset\n')
636 return _commentlines(msg)
638 return _commentlines(msg)
637
639
638 def fileexistspredicate(filename):
640 def fileexistspredicate(filename):
639 return lambda repo: repo.vfs.exists(filename)
641 return lambda repo: repo.vfs.exists(filename)
640
642
641 def _mergepredicate(repo):
643 def _mergepredicate(repo):
642 return len(repo[None].parents()) > 1
644 return len(repo[None].parents()) > 1
643
645
644 STATES = (
646 STATES = (
645 # (state, predicate to detect states, helpful message function)
647 # (state, predicate to detect states, helpful message function)
646 ('histedit', fileexistspredicate('histedit-state'), _histeditmsg),
648 ('histedit', fileexistspredicate('histedit-state'), _histeditmsg),
647 ('bisect', fileexistspredicate('bisect.state'), _bisectmsg),
649 ('bisect', fileexistspredicate('bisect.state'), _bisectmsg),
648 ('graft', fileexistspredicate('graftstate'), _graftmsg),
650 ('graft', fileexistspredicate('graftstate'), _graftmsg),
649 ('unshelve', fileexistspredicate('unshelverebasestate'), _unshelvemsg),
651 ('unshelve', fileexistspredicate('unshelverebasestate'), _unshelvemsg),
650 ('rebase', fileexistspredicate('rebasestate'), _rebasemsg),
652 ('rebase', fileexistspredicate('rebasestate'), _rebasemsg),
651 # The merge state is part of a list that will be iterated over.
653 # The merge state is part of a list that will be iterated over.
652 # They need to be last because some of the other unfinished states may also
654 # They need to be last because some of the other unfinished states may also
653 # be in a merge or update state (eg. rebase, histedit, graft, etc).
655 # be in a merge or update state (eg. rebase, histedit, graft, etc).
654 # We want those to have priority.
656 # We want those to have priority.
655 ('merge', _mergepredicate, _mergemsg),
657 ('merge', _mergepredicate, _mergemsg),
656 )
658 )
657
659
658 def _getrepostate(repo):
660 def _getrepostate(repo):
659 # experimental config: commands.status.skipstates
661 # experimental config: commands.status.skipstates
660 skip = set(repo.ui.configlist('commands', 'status.skipstates'))
662 skip = set(repo.ui.configlist('commands', 'status.skipstates'))
661 for state, statedetectionpredicate, msgfn in STATES:
663 for state, statedetectionpredicate, msgfn in STATES:
662 if state in skip:
664 if state in skip:
663 continue
665 continue
664 if statedetectionpredicate(repo):
666 if statedetectionpredicate(repo):
665 return (state, statedetectionpredicate, msgfn)
667 return (state, statedetectionpredicate, msgfn)
666
668
667 def morestatus(repo, fm):
669 def morestatus(repo, fm):
668 statetuple = _getrepostate(repo)
670 statetuple = _getrepostate(repo)
669 label = 'status.morestatus'
671 label = 'status.morestatus'
670 if statetuple:
672 if statetuple:
671 fm.startitem()
673 fm.startitem()
672 state, statedetectionpredicate, helpfulmsg = statetuple
674 state, statedetectionpredicate, helpfulmsg = statetuple
673 statemsg = _('The repository is in an unfinished *%s* state.') % state
675 statemsg = _('The repository is in an unfinished *%s* state.') % state
674 fm.write('statemsg', '%s\n', _commentlines(statemsg), label=label)
676 fm.write('statemsg', '%s\n', _commentlines(statemsg), label=label)
675 conmsg = _conflictsmsg(repo)
677 conmsg = _conflictsmsg(repo)
676 if conmsg:
678 if conmsg:
677 fm.write('conflictsmsg', '%s\n', conmsg, label=label)
679 fm.write('conflictsmsg', '%s\n', conmsg, label=label)
678 if helpfulmsg:
680 if helpfulmsg:
679 helpmsg = helpfulmsg()
681 helpmsg = helpfulmsg()
680 fm.write('helpmsg', '%s\n', helpmsg, label=label)
682 fm.write('helpmsg', '%s\n', helpmsg, label=label)
681
683
682 def findpossible(cmd, table, strict=False):
684 def findpossible(cmd, table, strict=False):
683 """
685 """
684 Return cmd -> (aliases, command table entry)
686 Return cmd -> (aliases, command table entry)
685 for each matching command.
687 for each matching command.
686 Return debug commands (or their aliases) only if no normal command matches.
688 Return debug commands (or their aliases) only if no normal command matches.
687 """
689 """
688 choice = {}
690 choice = {}
689 debugchoice = {}
691 debugchoice = {}
690
692
691 if cmd in table:
693 if cmd in table:
692 # short-circuit exact matches, "log" alias beats "^log|history"
694 # short-circuit exact matches, "log" alias beats "^log|history"
693 keys = [cmd]
695 keys = [cmd]
694 else:
696 else:
695 keys = table.keys()
697 keys = table.keys()
696
698
697 allcmds = []
699 allcmds = []
698 for e in keys:
700 for e in keys:
699 aliases = parsealiases(e)
701 aliases = parsealiases(e)
700 allcmds.extend(aliases)
702 allcmds.extend(aliases)
701 found = None
703 found = None
702 if cmd in aliases:
704 if cmd in aliases:
703 found = cmd
705 found = cmd
704 elif not strict:
706 elif not strict:
705 for a in aliases:
707 for a in aliases:
706 if a.startswith(cmd):
708 if a.startswith(cmd):
707 found = a
709 found = a
708 break
710 break
709 if found is not None:
711 if found is not None:
710 if aliases[0].startswith("debug") or found.startswith("debug"):
712 if aliases[0].startswith("debug") or found.startswith("debug"):
711 debugchoice[found] = (aliases, table[e])
713 debugchoice[found] = (aliases, table[e])
712 else:
714 else:
713 choice[found] = (aliases, table[e])
715 choice[found] = (aliases, table[e])
714
716
715 if not choice and debugchoice:
717 if not choice and debugchoice:
716 choice = debugchoice
718 choice = debugchoice
717
719
718 return choice, allcmds
720 return choice, allcmds
719
721
720 def findcmd(cmd, table, strict=True):
722 def findcmd(cmd, table, strict=True):
721 """Return (aliases, command table entry) for command string."""
723 """Return (aliases, command table entry) for command string."""
722 choice, allcmds = findpossible(cmd, table, strict)
724 choice, allcmds = findpossible(cmd, table, strict)
723
725
724 if cmd in choice:
726 if cmd in choice:
725 return choice[cmd]
727 return choice[cmd]
726
728
727 if len(choice) > 1:
729 if len(choice) > 1:
728 clist = sorted(choice)
730 clist = sorted(choice)
729 raise error.AmbiguousCommand(cmd, clist)
731 raise error.AmbiguousCommand(cmd, clist)
730
732
731 if choice:
733 if choice:
732 return list(choice.values())[0]
734 return list(choice.values())[0]
733
735
734 raise error.UnknownCommand(cmd, allcmds)
736 raise error.UnknownCommand(cmd, allcmds)
735
737
736 def findrepo(p):
738 def findrepo(p):
737 while not os.path.isdir(os.path.join(p, ".hg")):
739 while not os.path.isdir(os.path.join(p, ".hg")):
738 oldp, p = p, os.path.dirname(p)
740 oldp, p = p, os.path.dirname(p)
739 if p == oldp:
741 if p == oldp:
740 return None
742 return None
741
743
742 return p
744 return p
743
745
744 def bailifchanged(repo, merge=True, hint=None):
746 def bailifchanged(repo, merge=True, hint=None):
745 """ enforce the precondition that working directory must be clean.
747 """ enforce the precondition that working directory must be clean.
746
748
747 'merge' can be set to false if a pending uncommitted merge should be
749 'merge' can be set to false if a pending uncommitted merge should be
748 ignored (such as when 'update --check' runs).
750 ignored (such as when 'update --check' runs).
749
751
750 'hint' is the usual hint given to Abort exception.
752 'hint' is the usual hint given to Abort exception.
751 """
753 """
752
754
753 if merge and repo.dirstate.p2() != nullid:
755 if merge and repo.dirstate.p2() != nullid:
754 raise error.Abort(_('outstanding uncommitted merge'), hint=hint)
756 raise error.Abort(_('outstanding uncommitted merge'), hint=hint)
755 modified, added, removed, deleted = repo.status()[:4]
757 modified, added, removed, deleted = repo.status()[:4]
756 if modified or added or removed or deleted:
758 if modified or added or removed or deleted:
757 raise error.Abort(_('uncommitted changes'), hint=hint)
759 raise error.Abort(_('uncommitted changes'), hint=hint)
758 ctx = repo[None]
760 ctx = repo[None]
759 for s in sorted(ctx.substate):
761 for s in sorted(ctx.substate):
760 ctx.sub(s).bailifchanged(hint=hint)
762 ctx.sub(s).bailifchanged(hint=hint)
761
763
762 def logmessage(ui, opts):
764 def logmessage(ui, opts):
763 """ get the log message according to -m and -l option """
765 """ get the log message according to -m and -l option """
764 message = opts.get('message')
766 message = opts.get('message')
765 logfile = opts.get('logfile')
767 logfile = opts.get('logfile')
766
768
767 if message and logfile:
769 if message and logfile:
768 raise error.Abort(_('options --message and --logfile are mutually '
770 raise error.Abort(_('options --message and --logfile are mutually '
769 'exclusive'))
771 'exclusive'))
770 if not message and logfile:
772 if not message and logfile:
771 try:
773 try:
772 if isstdiofilename(logfile):
774 if isstdiofilename(logfile):
773 message = ui.fin.read()
775 message = ui.fin.read()
774 else:
776 else:
775 message = '\n'.join(util.readfile(logfile).splitlines())
777 message = '\n'.join(util.readfile(logfile).splitlines())
776 except IOError as inst:
778 except IOError as inst:
777 raise error.Abort(_("can't read commit message '%s': %s") %
779 raise error.Abort(_("can't read commit message '%s': %s") %
778 (logfile, inst.strerror))
780 (logfile, inst.strerror))
779 return message
781 return message
780
782
781 def mergeeditform(ctxorbool, baseformname):
783 def mergeeditform(ctxorbool, baseformname):
782 """return appropriate editform name (referencing a committemplate)
784 """return appropriate editform name (referencing a committemplate)
783
785
784 'ctxorbool' is either a ctx to be committed, or a bool indicating whether
786 'ctxorbool' is either a ctx to be committed, or a bool indicating whether
785 merging is committed.
787 merging is committed.
786
788
787 This returns baseformname with '.merge' appended if it is a merge,
789 This returns baseformname with '.merge' appended if it is a merge,
788 otherwise '.normal' is appended.
790 otherwise '.normal' is appended.
789 """
791 """
790 if isinstance(ctxorbool, bool):
792 if isinstance(ctxorbool, bool):
791 if ctxorbool:
793 if ctxorbool:
792 return baseformname + ".merge"
794 return baseformname + ".merge"
793 elif 1 < len(ctxorbool.parents()):
795 elif 1 < len(ctxorbool.parents()):
794 return baseformname + ".merge"
796 return baseformname + ".merge"
795
797
796 return baseformname + ".normal"
798 return baseformname + ".normal"
797
799
798 def getcommiteditor(edit=False, finishdesc=None, extramsg=None,
800 def getcommiteditor(edit=False, finishdesc=None, extramsg=None,
799 editform='', **opts):
801 editform='', **opts):
800 """get appropriate commit message editor according to '--edit' option
802 """get appropriate commit message editor according to '--edit' option
801
803
802 'finishdesc' is a function to be called with edited commit message
804 'finishdesc' is a function to be called with edited commit message
803 (= 'description' of the new changeset) just after editing, but
805 (= 'description' of the new changeset) just after editing, but
804 before checking empty-ness. It should return actual text to be
806 before checking empty-ness. It should return actual text to be
805 stored into history. This allows to change description before
807 stored into history. This allows to change description before
806 storing.
808 storing.
807
809
808 'extramsg' is a extra message to be shown in the editor instead of
810 'extramsg' is a extra message to be shown in the editor instead of
809 'Leave message empty to abort commit' line. 'HG: ' prefix and EOL
811 'Leave message empty to abort commit' line. 'HG: ' prefix and EOL
810 is automatically added.
812 is automatically added.
811
813
812 'editform' is a dot-separated list of names, to distinguish
814 'editform' is a dot-separated list of names, to distinguish
813 the purpose of commit text editing.
815 the purpose of commit text editing.
814
816
815 'getcommiteditor' returns 'commitforceeditor' regardless of
817 'getcommiteditor' returns 'commitforceeditor' regardless of
816 'edit', if one of 'finishdesc' or 'extramsg' is specified, because
818 'edit', if one of 'finishdesc' or 'extramsg' is specified, because
817 they are specific for usage in MQ.
819 they are specific for usage in MQ.
818 """
820 """
819 if edit or finishdesc or extramsg:
821 if edit or finishdesc or extramsg:
820 return lambda r, c, s: commitforceeditor(r, c, s,
822 return lambda r, c, s: commitforceeditor(r, c, s,
821 finishdesc=finishdesc,
823 finishdesc=finishdesc,
822 extramsg=extramsg,
824 extramsg=extramsg,
823 editform=editform)
825 editform=editform)
824 elif editform:
826 elif editform:
825 return lambda r, c, s: commiteditor(r, c, s, editform=editform)
827 return lambda r, c, s: commiteditor(r, c, s, editform=editform)
826 else:
828 else:
827 return commiteditor
829 return commiteditor
828
830
829 def loglimit(opts):
831 def loglimit(opts):
830 """get the log limit according to option -l/--limit"""
832 """get the log limit according to option -l/--limit"""
831 limit = opts.get('limit')
833 limit = opts.get('limit')
832 if limit:
834 if limit:
833 try:
835 try:
834 limit = int(limit)
836 limit = int(limit)
835 except ValueError:
837 except ValueError:
836 raise error.Abort(_('limit must be a positive integer'))
838 raise error.Abort(_('limit must be a positive integer'))
837 if limit <= 0:
839 if limit <= 0:
838 raise error.Abort(_('limit must be positive'))
840 raise error.Abort(_('limit must be positive'))
839 else:
841 else:
840 limit = None
842 limit = None
841 return limit
843 return limit
842
844
843 def makefilename(repo, pat, node, desc=None,
845 def makefilename(repo, pat, node, desc=None,
844 total=None, seqno=None, revwidth=None, pathname=None):
846 total=None, seqno=None, revwidth=None, pathname=None):
845 node_expander = {
847 node_expander = {
846 'H': lambda: hex(node),
848 'H': lambda: hex(node),
847 'R': lambda: str(repo.changelog.rev(node)),
849 'R': lambda: str(repo.changelog.rev(node)),
848 'h': lambda: short(node),
850 'h': lambda: short(node),
849 'm': lambda: re.sub('[^\w]', '_', str(desc))
851 'm': lambda: re.sub('[^\w]', '_', str(desc))
850 }
852 }
851 expander = {
853 expander = {
852 '%': lambda: '%',
854 '%': lambda: '%',
853 'b': lambda: os.path.basename(repo.root),
855 'b': lambda: os.path.basename(repo.root),
854 }
856 }
855
857
856 try:
858 try:
857 if node:
859 if node:
858 expander.update(node_expander)
860 expander.update(node_expander)
859 if node:
861 if node:
860 expander['r'] = (lambda:
862 expander['r'] = (lambda:
861 str(repo.changelog.rev(node)).zfill(revwidth or 0))
863 str(repo.changelog.rev(node)).zfill(revwidth or 0))
862 if total is not None:
864 if total is not None:
863 expander['N'] = lambda: str(total)
865 expander['N'] = lambda: str(total)
864 if seqno is not None:
866 if seqno is not None:
865 expander['n'] = lambda: str(seqno)
867 expander['n'] = lambda: str(seqno)
866 if total is not None and seqno is not None:
868 if total is not None and seqno is not None:
867 expander['n'] = lambda: str(seqno).zfill(len(str(total)))
869 expander['n'] = lambda: str(seqno).zfill(len(str(total)))
868 if pathname is not None:
870 if pathname is not None:
869 expander['s'] = lambda: os.path.basename(pathname)
871 expander['s'] = lambda: os.path.basename(pathname)
870 expander['d'] = lambda: os.path.dirname(pathname) or '.'
872 expander['d'] = lambda: os.path.dirname(pathname) or '.'
871 expander['p'] = lambda: pathname
873 expander['p'] = lambda: pathname
872
874
873 newname = []
875 newname = []
874 patlen = len(pat)
876 patlen = len(pat)
875 i = 0
877 i = 0
876 while i < patlen:
878 while i < patlen:
877 c = pat[i:i + 1]
879 c = pat[i:i + 1]
878 if c == '%':
880 if c == '%':
879 i += 1
881 i += 1
880 c = pat[i:i + 1]
882 c = pat[i:i + 1]
881 c = expander[c]()
883 c = expander[c]()
882 newname.append(c)
884 newname.append(c)
883 i += 1
885 i += 1
884 return ''.join(newname)
886 return ''.join(newname)
885 except KeyError as inst:
887 except KeyError as inst:
886 raise error.Abort(_("invalid format spec '%%%s' in output filename") %
888 raise error.Abort(_("invalid format spec '%%%s' in output filename") %
887 inst.args[0])
889 inst.args[0])
888
890
889 def isstdiofilename(pat):
891 def isstdiofilename(pat):
890 """True if the given pat looks like a filename denoting stdin/stdout"""
892 """True if the given pat looks like a filename denoting stdin/stdout"""
891 return not pat or pat == '-'
893 return not pat or pat == '-'
892
894
893 class _unclosablefile(object):
895 class _unclosablefile(object):
894 def __init__(self, fp):
896 def __init__(self, fp):
895 self._fp = fp
897 self._fp = fp
896
898
897 def close(self):
899 def close(self):
898 pass
900 pass
899
901
900 def __iter__(self):
902 def __iter__(self):
901 return iter(self._fp)
903 return iter(self._fp)
902
904
903 def __getattr__(self, attr):
905 def __getattr__(self, attr):
904 return getattr(self._fp, attr)
906 return getattr(self._fp, attr)
905
907
906 def __enter__(self):
908 def __enter__(self):
907 return self
909 return self
908
910
909 def __exit__(self, exc_type, exc_value, exc_tb):
911 def __exit__(self, exc_type, exc_value, exc_tb):
910 pass
912 pass
911
913
912 def makefileobj(repo, pat, node=None, desc=None, total=None,
914 def makefileobj(repo, pat, node=None, desc=None, total=None,
913 seqno=None, revwidth=None, mode='wb', modemap=None,
915 seqno=None, revwidth=None, mode='wb', modemap=None,
914 pathname=None):
916 pathname=None):
915
917
916 writable = mode not in ('r', 'rb')
918 writable = mode not in ('r', 'rb')
917
919
918 if isstdiofilename(pat):
920 if isstdiofilename(pat):
919 if writable:
921 if writable:
920 fp = repo.ui.fout
922 fp = repo.ui.fout
921 else:
923 else:
922 fp = repo.ui.fin
924 fp = repo.ui.fin
923 return _unclosablefile(fp)
925 return _unclosablefile(fp)
924 fn = makefilename(repo, pat, node, desc, total, seqno, revwidth, pathname)
926 fn = makefilename(repo, pat, node, desc, total, seqno, revwidth, pathname)
925 if modemap is not None:
927 if modemap is not None:
926 mode = modemap.get(fn, mode)
928 mode = modemap.get(fn, mode)
927 if mode == 'wb':
929 if mode == 'wb':
928 modemap[fn] = 'ab'
930 modemap[fn] = 'ab'
929 return open(fn, mode)
931 return open(fn, mode)
930
932
931 def openrevlog(repo, cmd, file_, opts):
933 def openrevlog(repo, cmd, file_, opts):
932 """opens the changelog, manifest, a filelog or a given revlog"""
934 """opens the changelog, manifest, a filelog or a given revlog"""
933 cl = opts['changelog']
935 cl = opts['changelog']
934 mf = opts['manifest']
936 mf = opts['manifest']
935 dir = opts['dir']
937 dir = opts['dir']
936 msg = None
938 msg = None
937 if cl and mf:
939 if cl and mf:
938 msg = _('cannot specify --changelog and --manifest at the same time')
940 msg = _('cannot specify --changelog and --manifest at the same time')
939 elif cl and dir:
941 elif cl and dir:
940 msg = _('cannot specify --changelog and --dir at the same time')
942 msg = _('cannot specify --changelog and --dir at the same time')
941 elif cl or mf or dir:
943 elif cl or mf or dir:
942 if file_:
944 if file_:
943 msg = _('cannot specify filename with --changelog or --manifest')
945 msg = _('cannot specify filename with --changelog or --manifest')
944 elif not repo:
946 elif not repo:
945 msg = _('cannot specify --changelog or --manifest or --dir '
947 msg = _('cannot specify --changelog or --manifest or --dir '
946 'without a repository')
948 'without a repository')
947 if msg:
949 if msg:
948 raise error.Abort(msg)
950 raise error.Abort(msg)
949
951
950 r = None
952 r = None
951 if repo:
953 if repo:
952 if cl:
954 if cl:
953 r = repo.unfiltered().changelog
955 r = repo.unfiltered().changelog
954 elif dir:
956 elif dir:
955 if 'treemanifest' not in repo.requirements:
957 if 'treemanifest' not in repo.requirements:
956 raise error.Abort(_("--dir can only be used on repos with "
958 raise error.Abort(_("--dir can only be used on repos with "
957 "treemanifest enabled"))
959 "treemanifest enabled"))
958 dirlog = repo.manifestlog._revlog.dirlog(dir)
960 dirlog = repo.manifestlog._revlog.dirlog(dir)
959 if len(dirlog):
961 if len(dirlog):
960 r = dirlog
962 r = dirlog
961 elif mf:
963 elif mf:
962 r = repo.manifestlog._revlog
964 r = repo.manifestlog._revlog
963 elif file_:
965 elif file_:
964 filelog = repo.file(file_)
966 filelog = repo.file(file_)
965 if len(filelog):
967 if len(filelog):
966 r = filelog
968 r = filelog
967 if not r:
969 if not r:
968 if not file_:
970 if not file_:
969 raise error.CommandError(cmd, _('invalid arguments'))
971 raise error.CommandError(cmd, _('invalid arguments'))
970 if not os.path.isfile(file_):
972 if not os.path.isfile(file_):
971 raise error.Abort(_("revlog '%s' not found") % file_)
973 raise error.Abort(_("revlog '%s' not found") % file_)
972 r = revlog.revlog(vfsmod.vfs(pycompat.getcwd(), audit=False),
974 r = revlog.revlog(vfsmod.vfs(pycompat.getcwd(), audit=False),
973 file_[:-2] + ".i")
975 file_[:-2] + ".i")
974 return r
976 return r
975
977
976 def copy(ui, repo, pats, opts, rename=False):
978 def copy(ui, repo, pats, opts, rename=False):
977 # called with the repo lock held
979 # called with the repo lock held
978 #
980 #
979 # hgsep => pathname that uses "/" to separate directories
981 # hgsep => pathname that uses "/" to separate directories
980 # ossep => pathname that uses os.sep to separate directories
982 # ossep => pathname that uses os.sep to separate directories
981 cwd = repo.getcwd()
983 cwd = repo.getcwd()
982 targets = {}
984 targets = {}
983 after = opts.get("after")
985 after = opts.get("after")
984 dryrun = opts.get("dry_run")
986 dryrun = opts.get("dry_run")
985 wctx = repo[None]
987 wctx = repo[None]
986
988
987 def walkpat(pat):
989 def walkpat(pat):
988 srcs = []
990 srcs = []
989 if after:
991 if after:
990 badstates = '?'
992 badstates = '?'
991 else:
993 else:
992 badstates = '?r'
994 badstates = '?r'
993 m = scmutil.match(wctx, [pat], opts, globbed=True)
995 m = scmutil.match(wctx, [pat], opts, globbed=True)
994 for abs in wctx.walk(m):
996 for abs in wctx.walk(m):
995 state = repo.dirstate[abs]
997 state = repo.dirstate[abs]
996 rel = m.rel(abs)
998 rel = m.rel(abs)
997 exact = m.exact(abs)
999 exact = m.exact(abs)
998 if state in badstates:
1000 if state in badstates:
999 if exact and state == '?':
1001 if exact and state == '?':
1000 ui.warn(_('%s: not copying - file is not managed\n') % rel)
1002 ui.warn(_('%s: not copying - file is not managed\n') % rel)
1001 if exact and state == 'r':
1003 if exact and state == 'r':
1002 ui.warn(_('%s: not copying - file has been marked for'
1004 ui.warn(_('%s: not copying - file has been marked for'
1003 ' remove\n') % rel)
1005 ' remove\n') % rel)
1004 continue
1006 continue
1005 # abs: hgsep
1007 # abs: hgsep
1006 # rel: ossep
1008 # rel: ossep
1007 srcs.append((abs, rel, exact))
1009 srcs.append((abs, rel, exact))
1008 return srcs
1010 return srcs
1009
1011
1010 # abssrc: hgsep
1012 # abssrc: hgsep
1011 # relsrc: ossep
1013 # relsrc: ossep
1012 # otarget: ossep
1014 # otarget: ossep
1013 def copyfile(abssrc, relsrc, otarget, exact):
1015 def copyfile(abssrc, relsrc, otarget, exact):
1014 abstarget = pathutil.canonpath(repo.root, cwd, otarget)
1016 abstarget = pathutil.canonpath(repo.root, cwd, otarget)
1015 if '/' in abstarget:
1017 if '/' in abstarget:
1016 # We cannot normalize abstarget itself, this would prevent
1018 # We cannot normalize abstarget itself, this would prevent
1017 # case only renames, like a => A.
1019 # case only renames, like a => A.
1018 abspath, absname = abstarget.rsplit('/', 1)
1020 abspath, absname = abstarget.rsplit('/', 1)
1019 abstarget = repo.dirstate.normalize(abspath) + '/' + absname
1021 abstarget = repo.dirstate.normalize(abspath) + '/' + absname
1020 reltarget = repo.pathto(abstarget, cwd)
1022 reltarget = repo.pathto(abstarget, cwd)
1021 target = repo.wjoin(abstarget)
1023 target = repo.wjoin(abstarget)
1022 src = repo.wjoin(abssrc)
1024 src = repo.wjoin(abssrc)
1023 state = repo.dirstate[abstarget]
1025 state = repo.dirstate[abstarget]
1024
1026
1025 scmutil.checkportable(ui, abstarget)
1027 scmutil.checkportable(ui, abstarget)
1026
1028
1027 # check for collisions
1029 # check for collisions
1028 prevsrc = targets.get(abstarget)
1030 prevsrc = targets.get(abstarget)
1029 if prevsrc is not None:
1031 if prevsrc is not None:
1030 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
1032 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
1031 (reltarget, repo.pathto(abssrc, cwd),
1033 (reltarget, repo.pathto(abssrc, cwd),
1032 repo.pathto(prevsrc, cwd)))
1034 repo.pathto(prevsrc, cwd)))
1033 return
1035 return
1034
1036
1035 # check for overwrites
1037 # check for overwrites
1036 exists = os.path.lexists(target)
1038 exists = os.path.lexists(target)
1037 samefile = False
1039 samefile = False
1038 if exists and abssrc != abstarget:
1040 if exists and abssrc != abstarget:
1039 if (repo.dirstate.normalize(abssrc) ==
1041 if (repo.dirstate.normalize(abssrc) ==
1040 repo.dirstate.normalize(abstarget)):
1042 repo.dirstate.normalize(abstarget)):
1041 if not rename:
1043 if not rename:
1042 ui.warn(_("%s: can't copy - same file\n") % reltarget)
1044 ui.warn(_("%s: can't copy - same file\n") % reltarget)
1043 return
1045 return
1044 exists = False
1046 exists = False
1045 samefile = True
1047 samefile = True
1046
1048
1047 if not after and exists or after and state in 'mn':
1049 if not after and exists or after and state in 'mn':
1048 if not opts['force']:
1050 if not opts['force']:
1049 if state in 'mn':
1051 if state in 'mn':
1050 msg = _('%s: not overwriting - file already committed\n')
1052 msg = _('%s: not overwriting - file already committed\n')
1051 if after:
1053 if after:
1052 flags = '--after --force'
1054 flags = '--after --force'
1053 else:
1055 else:
1054 flags = '--force'
1056 flags = '--force'
1055 if rename:
1057 if rename:
1056 hint = _('(hg rename %s to replace the file by '
1058 hint = _('(hg rename %s to replace the file by '
1057 'recording a rename)\n') % flags
1059 'recording a rename)\n') % flags
1058 else:
1060 else:
1059 hint = _('(hg copy %s to replace the file by '
1061 hint = _('(hg copy %s to replace the file by '
1060 'recording a copy)\n') % flags
1062 'recording a copy)\n') % flags
1061 else:
1063 else:
1062 msg = _('%s: not overwriting - file exists\n')
1064 msg = _('%s: not overwriting - file exists\n')
1063 if rename:
1065 if rename:
1064 hint = _('(hg rename --after to record the rename)\n')
1066 hint = _('(hg rename --after to record the rename)\n')
1065 else:
1067 else:
1066 hint = _('(hg copy --after to record the copy)\n')
1068 hint = _('(hg copy --after to record the copy)\n')
1067 ui.warn(msg % reltarget)
1069 ui.warn(msg % reltarget)
1068 ui.warn(hint)
1070 ui.warn(hint)
1069 return
1071 return
1070
1072
1071 if after:
1073 if after:
1072 if not exists:
1074 if not exists:
1073 if rename:
1075 if rename:
1074 ui.warn(_('%s: not recording move - %s does not exist\n') %
1076 ui.warn(_('%s: not recording move - %s does not exist\n') %
1075 (relsrc, reltarget))
1077 (relsrc, reltarget))
1076 else:
1078 else:
1077 ui.warn(_('%s: not recording copy - %s does not exist\n') %
1079 ui.warn(_('%s: not recording copy - %s does not exist\n') %
1078 (relsrc, reltarget))
1080 (relsrc, reltarget))
1079 return
1081 return
1080 elif not dryrun:
1082 elif not dryrun:
1081 try:
1083 try:
1082 if exists:
1084 if exists:
1083 os.unlink(target)
1085 os.unlink(target)
1084 targetdir = os.path.dirname(target) or '.'
1086 targetdir = os.path.dirname(target) or '.'
1085 if not os.path.isdir(targetdir):
1087 if not os.path.isdir(targetdir):
1086 os.makedirs(targetdir)
1088 os.makedirs(targetdir)
1087 if samefile:
1089 if samefile:
1088 tmp = target + "~hgrename"
1090 tmp = target + "~hgrename"
1089 os.rename(src, tmp)
1091 os.rename(src, tmp)
1090 os.rename(tmp, target)
1092 os.rename(tmp, target)
1091 else:
1093 else:
1092 util.copyfile(src, target)
1094 util.copyfile(src, target)
1093 srcexists = True
1095 srcexists = True
1094 except IOError as inst:
1096 except IOError as inst:
1095 if inst.errno == errno.ENOENT:
1097 if inst.errno == errno.ENOENT:
1096 ui.warn(_('%s: deleted in working directory\n') % relsrc)
1098 ui.warn(_('%s: deleted in working directory\n') % relsrc)
1097 srcexists = False
1099 srcexists = False
1098 else:
1100 else:
1099 ui.warn(_('%s: cannot copy - %s\n') %
1101 ui.warn(_('%s: cannot copy - %s\n') %
1100 (relsrc, inst.strerror))
1102 (relsrc, inst.strerror))
1101 return True # report a failure
1103 return True # report a failure
1102
1104
1103 if ui.verbose or not exact:
1105 if ui.verbose or not exact:
1104 if rename:
1106 if rename:
1105 ui.status(_('moving %s to %s\n') % (relsrc, reltarget))
1107 ui.status(_('moving %s to %s\n') % (relsrc, reltarget))
1106 else:
1108 else:
1107 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
1109 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
1108
1110
1109 targets[abstarget] = abssrc
1111 targets[abstarget] = abssrc
1110
1112
1111 # fix up dirstate
1113 # fix up dirstate
1112 scmutil.dirstatecopy(ui, repo, wctx, abssrc, abstarget,
1114 scmutil.dirstatecopy(ui, repo, wctx, abssrc, abstarget,
1113 dryrun=dryrun, cwd=cwd)
1115 dryrun=dryrun, cwd=cwd)
1114 if rename and not dryrun:
1116 if rename and not dryrun:
1115 if not after and srcexists and not samefile:
1117 if not after and srcexists and not samefile:
1116 repo.wvfs.unlinkpath(abssrc)
1118 repo.wvfs.unlinkpath(abssrc)
1117 wctx.forget([abssrc])
1119 wctx.forget([abssrc])
1118
1120
1119 # pat: ossep
1121 # pat: ossep
1120 # dest ossep
1122 # dest ossep
1121 # srcs: list of (hgsep, hgsep, ossep, bool)
1123 # srcs: list of (hgsep, hgsep, ossep, bool)
1122 # return: function that takes hgsep and returns ossep
1124 # return: function that takes hgsep and returns ossep
1123 def targetpathfn(pat, dest, srcs):
1125 def targetpathfn(pat, dest, srcs):
1124 if os.path.isdir(pat):
1126 if os.path.isdir(pat):
1125 abspfx = pathutil.canonpath(repo.root, cwd, pat)
1127 abspfx = pathutil.canonpath(repo.root, cwd, pat)
1126 abspfx = util.localpath(abspfx)
1128 abspfx = util.localpath(abspfx)
1127 if destdirexists:
1129 if destdirexists:
1128 striplen = len(os.path.split(abspfx)[0])
1130 striplen = len(os.path.split(abspfx)[0])
1129 else:
1131 else:
1130 striplen = len(abspfx)
1132 striplen = len(abspfx)
1131 if striplen:
1133 if striplen:
1132 striplen += len(pycompat.ossep)
1134 striplen += len(pycompat.ossep)
1133 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
1135 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
1134 elif destdirexists:
1136 elif destdirexists:
1135 res = lambda p: os.path.join(dest,
1137 res = lambda p: os.path.join(dest,
1136 os.path.basename(util.localpath(p)))
1138 os.path.basename(util.localpath(p)))
1137 else:
1139 else:
1138 res = lambda p: dest
1140 res = lambda p: dest
1139 return res
1141 return res
1140
1142
1141 # pat: ossep
1143 # pat: ossep
1142 # dest ossep
1144 # dest ossep
1143 # srcs: list of (hgsep, hgsep, ossep, bool)
1145 # srcs: list of (hgsep, hgsep, ossep, bool)
1144 # return: function that takes hgsep and returns ossep
1146 # return: function that takes hgsep and returns ossep
1145 def targetpathafterfn(pat, dest, srcs):
1147 def targetpathafterfn(pat, dest, srcs):
1146 if matchmod.patkind(pat):
1148 if matchmod.patkind(pat):
1147 # a mercurial pattern
1149 # a mercurial pattern
1148 res = lambda p: os.path.join(dest,
1150 res = lambda p: os.path.join(dest,
1149 os.path.basename(util.localpath(p)))
1151 os.path.basename(util.localpath(p)))
1150 else:
1152 else:
1151 abspfx = pathutil.canonpath(repo.root, cwd, pat)
1153 abspfx = pathutil.canonpath(repo.root, cwd, pat)
1152 if len(abspfx) < len(srcs[0][0]):
1154 if len(abspfx) < len(srcs[0][0]):
1153 # A directory. Either the target path contains the last
1155 # A directory. Either the target path contains the last
1154 # component of the source path or it does not.
1156 # component of the source path or it does not.
1155 def evalpath(striplen):
1157 def evalpath(striplen):
1156 score = 0
1158 score = 0
1157 for s in srcs:
1159 for s in srcs:
1158 t = os.path.join(dest, util.localpath(s[0])[striplen:])
1160 t = os.path.join(dest, util.localpath(s[0])[striplen:])
1159 if os.path.lexists(t):
1161 if os.path.lexists(t):
1160 score += 1
1162 score += 1
1161 return score
1163 return score
1162
1164
1163 abspfx = util.localpath(abspfx)
1165 abspfx = util.localpath(abspfx)
1164 striplen = len(abspfx)
1166 striplen = len(abspfx)
1165 if striplen:
1167 if striplen:
1166 striplen += len(pycompat.ossep)
1168 striplen += len(pycompat.ossep)
1167 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
1169 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
1168 score = evalpath(striplen)
1170 score = evalpath(striplen)
1169 striplen1 = len(os.path.split(abspfx)[0])
1171 striplen1 = len(os.path.split(abspfx)[0])
1170 if striplen1:
1172 if striplen1:
1171 striplen1 += len(pycompat.ossep)
1173 striplen1 += len(pycompat.ossep)
1172 if evalpath(striplen1) > score:
1174 if evalpath(striplen1) > score:
1173 striplen = striplen1
1175 striplen = striplen1
1174 res = lambda p: os.path.join(dest,
1176 res = lambda p: os.path.join(dest,
1175 util.localpath(p)[striplen:])
1177 util.localpath(p)[striplen:])
1176 else:
1178 else:
1177 # a file
1179 # a file
1178 if destdirexists:
1180 if destdirexists:
1179 res = lambda p: os.path.join(dest,
1181 res = lambda p: os.path.join(dest,
1180 os.path.basename(util.localpath(p)))
1182 os.path.basename(util.localpath(p)))
1181 else:
1183 else:
1182 res = lambda p: dest
1184 res = lambda p: dest
1183 return res
1185 return res
1184
1186
1185 pats = scmutil.expandpats(pats)
1187 pats = scmutil.expandpats(pats)
1186 if not pats:
1188 if not pats:
1187 raise error.Abort(_('no source or destination specified'))
1189 raise error.Abort(_('no source or destination specified'))
1188 if len(pats) == 1:
1190 if len(pats) == 1:
1189 raise error.Abort(_('no destination specified'))
1191 raise error.Abort(_('no destination specified'))
1190 dest = pats.pop()
1192 dest = pats.pop()
1191 destdirexists = os.path.isdir(dest) and not os.path.islink(dest)
1193 destdirexists = os.path.isdir(dest) and not os.path.islink(dest)
1192 if not destdirexists:
1194 if not destdirexists:
1193 if len(pats) > 1 or matchmod.patkind(pats[0]):
1195 if len(pats) > 1 or matchmod.patkind(pats[0]):
1194 raise error.Abort(_('with multiple sources, destination must be an '
1196 raise error.Abort(_('with multiple sources, destination must be an '
1195 'existing directory'))
1197 'existing directory'))
1196 if util.endswithsep(dest):
1198 if util.endswithsep(dest):
1197 raise error.Abort(_('destination %s is not a directory') % dest)
1199 raise error.Abort(_('destination %s is not a directory') % dest)
1198
1200
1199 tfn = targetpathfn
1201 tfn = targetpathfn
1200 if after:
1202 if after:
1201 tfn = targetpathafterfn
1203 tfn = targetpathafterfn
1202 copylist = []
1204 copylist = []
1203 for pat in pats:
1205 for pat in pats:
1204 srcs = walkpat(pat)
1206 srcs = walkpat(pat)
1205 if not srcs:
1207 if not srcs:
1206 continue
1208 continue
1207 copylist.append((tfn(pat, dest, srcs), srcs))
1209 copylist.append((tfn(pat, dest, srcs), srcs))
1208 if not copylist:
1210 if not copylist:
1209 raise error.Abort(_('no files to copy'))
1211 raise error.Abort(_('no files to copy'))
1210
1212
1211 errors = 0
1213 errors = 0
1212 for targetpath, srcs in copylist:
1214 for targetpath, srcs in copylist:
1213 for abssrc, relsrc, exact in srcs:
1215 for abssrc, relsrc, exact in srcs:
1214 if copyfile(abssrc, relsrc, targetpath(abssrc), exact):
1216 if copyfile(abssrc, relsrc, targetpath(abssrc), exact):
1215 errors += 1
1217 errors += 1
1216
1218
1217 if errors:
1219 if errors:
1218 ui.warn(_('(consider using --after)\n'))
1220 ui.warn(_('(consider using --after)\n'))
1219
1221
1220 return errors != 0
1222 return errors != 0
1221
1223
1222 ## facility to let extension process additional data into an import patch
1224 ## facility to let extension process additional data into an import patch
1223 # list of identifier to be executed in order
1225 # list of identifier to be executed in order
1224 extrapreimport = [] # run before commit
1226 extrapreimport = [] # run before commit
1225 extrapostimport = [] # run after commit
1227 extrapostimport = [] # run after commit
1226 # mapping from identifier to actual import function
1228 # mapping from identifier to actual import function
1227 #
1229 #
1228 # 'preimport' are run before the commit is made and are provided the following
1230 # 'preimport' are run before the commit is made and are provided the following
1229 # arguments:
1231 # arguments:
1230 # - repo: the localrepository instance,
1232 # - repo: the localrepository instance,
1231 # - patchdata: data extracted from patch header (cf m.patch.patchheadermap),
1233 # - patchdata: data extracted from patch header (cf m.patch.patchheadermap),
1232 # - extra: the future extra dictionary of the changeset, please mutate it,
1234 # - extra: the future extra dictionary of the changeset, please mutate it,
1233 # - opts: the import options.
1235 # - opts: the import options.
1234 # XXX ideally, we would just pass an ctx ready to be computed, that would allow
1236 # XXX ideally, we would just pass an ctx ready to be computed, that would allow
1235 # mutation of in memory commit and more. Feel free to rework the code to get
1237 # mutation of in memory commit and more. Feel free to rework the code to get
1236 # there.
1238 # there.
1237 extrapreimportmap = {}
1239 extrapreimportmap = {}
1238 # 'postimport' are run after the commit is made and are provided the following
1240 # 'postimport' are run after the commit is made and are provided the following
1239 # argument:
1241 # argument:
1240 # - ctx: the changectx created by import.
1242 # - ctx: the changectx created by import.
1241 extrapostimportmap = {}
1243 extrapostimportmap = {}
1242
1244
1243 def tryimportone(ui, repo, hunk, parents, opts, msgs, updatefunc):
1245 def tryimportone(ui, repo, hunk, parents, opts, msgs, updatefunc):
1244 """Utility function used by commands.import to import a single patch
1246 """Utility function used by commands.import to import a single patch
1245
1247
1246 This function is explicitly defined here to help the evolve extension to
1248 This function is explicitly defined here to help the evolve extension to
1247 wrap this part of the import logic.
1249 wrap this part of the import logic.
1248
1250
1249 The API is currently a bit ugly because it a simple code translation from
1251 The API is currently a bit ugly because it a simple code translation from
1250 the import command. Feel free to make it better.
1252 the import command. Feel free to make it better.
1251
1253
1252 :hunk: a patch (as a binary string)
1254 :hunk: a patch (as a binary string)
1253 :parents: nodes that will be parent of the created commit
1255 :parents: nodes that will be parent of the created commit
1254 :opts: the full dict of option passed to the import command
1256 :opts: the full dict of option passed to the import command
1255 :msgs: list to save commit message to.
1257 :msgs: list to save commit message to.
1256 (used in case we need to save it when failing)
1258 (used in case we need to save it when failing)
1257 :updatefunc: a function that update a repo to a given node
1259 :updatefunc: a function that update a repo to a given node
1258 updatefunc(<repo>, <node>)
1260 updatefunc(<repo>, <node>)
1259 """
1261 """
1260 # avoid cycle context -> subrepo -> cmdutil
1262 # avoid cycle context -> subrepo -> cmdutil
1261 from . import context
1263 from . import context
1262 extractdata = patch.extract(ui, hunk)
1264 extractdata = patch.extract(ui, hunk)
1263 tmpname = extractdata.get('filename')
1265 tmpname = extractdata.get('filename')
1264 message = extractdata.get('message')
1266 message = extractdata.get('message')
1265 user = opts.get('user') or extractdata.get('user')
1267 user = opts.get('user') or extractdata.get('user')
1266 date = opts.get('date') or extractdata.get('date')
1268 date = opts.get('date') or extractdata.get('date')
1267 branch = extractdata.get('branch')
1269 branch = extractdata.get('branch')
1268 nodeid = extractdata.get('nodeid')
1270 nodeid = extractdata.get('nodeid')
1269 p1 = extractdata.get('p1')
1271 p1 = extractdata.get('p1')
1270 p2 = extractdata.get('p2')
1272 p2 = extractdata.get('p2')
1271
1273
1272 nocommit = opts.get('no_commit')
1274 nocommit = opts.get('no_commit')
1273 importbranch = opts.get('import_branch')
1275 importbranch = opts.get('import_branch')
1274 update = not opts.get('bypass')
1276 update = not opts.get('bypass')
1275 strip = opts["strip"]
1277 strip = opts["strip"]
1276 prefix = opts["prefix"]
1278 prefix = opts["prefix"]
1277 sim = float(opts.get('similarity') or 0)
1279 sim = float(opts.get('similarity') or 0)
1278 if not tmpname:
1280 if not tmpname:
1279 return (None, None, False)
1281 return (None, None, False)
1280
1282
1281 rejects = False
1283 rejects = False
1282
1284
1283 try:
1285 try:
1284 cmdline_message = logmessage(ui, opts)
1286 cmdline_message = logmessage(ui, opts)
1285 if cmdline_message:
1287 if cmdline_message:
1286 # pickup the cmdline msg
1288 # pickup the cmdline msg
1287 message = cmdline_message
1289 message = cmdline_message
1288 elif message:
1290 elif message:
1289 # pickup the patch msg
1291 # pickup the patch msg
1290 message = message.strip()
1292 message = message.strip()
1291 else:
1293 else:
1292 # launch the editor
1294 # launch the editor
1293 message = None
1295 message = None
1294 ui.debug('message:\n%s\n' % message)
1296 ui.debug('message:\n%s\n' % message)
1295
1297
1296 if len(parents) == 1:
1298 if len(parents) == 1:
1297 parents.append(repo[nullid])
1299 parents.append(repo[nullid])
1298 if opts.get('exact'):
1300 if opts.get('exact'):
1299 if not nodeid or not p1:
1301 if not nodeid or not p1:
1300 raise error.Abort(_('not a Mercurial patch'))
1302 raise error.Abort(_('not a Mercurial patch'))
1301 p1 = repo[p1]
1303 p1 = repo[p1]
1302 p2 = repo[p2 or nullid]
1304 p2 = repo[p2 or nullid]
1303 elif p2:
1305 elif p2:
1304 try:
1306 try:
1305 p1 = repo[p1]
1307 p1 = repo[p1]
1306 p2 = repo[p2]
1308 p2 = repo[p2]
1307 # Without any options, consider p2 only if the
1309 # Without any options, consider p2 only if the
1308 # patch is being applied on top of the recorded
1310 # patch is being applied on top of the recorded
1309 # first parent.
1311 # first parent.
1310 if p1 != parents[0]:
1312 if p1 != parents[0]:
1311 p1 = parents[0]
1313 p1 = parents[0]
1312 p2 = repo[nullid]
1314 p2 = repo[nullid]
1313 except error.RepoError:
1315 except error.RepoError:
1314 p1, p2 = parents
1316 p1, p2 = parents
1315 if p2.node() == nullid:
1317 if p2.node() == nullid:
1316 ui.warn(_("warning: import the patch as a normal revision\n"
1318 ui.warn(_("warning: import the patch as a normal revision\n"
1317 "(use --exact to import the patch as a merge)\n"))
1319 "(use --exact to import the patch as a merge)\n"))
1318 else:
1320 else:
1319 p1, p2 = parents
1321 p1, p2 = parents
1320
1322
1321 n = None
1323 n = None
1322 if update:
1324 if update:
1323 if p1 != parents[0]:
1325 if p1 != parents[0]:
1324 updatefunc(repo, p1.node())
1326 updatefunc(repo, p1.node())
1325 if p2 != parents[1]:
1327 if p2 != parents[1]:
1326 repo.setparents(p1.node(), p2.node())
1328 repo.setparents(p1.node(), p2.node())
1327
1329
1328 if opts.get('exact') or importbranch:
1330 if opts.get('exact') or importbranch:
1329 repo.dirstate.setbranch(branch or 'default')
1331 repo.dirstate.setbranch(branch or 'default')
1330
1332
1331 partial = opts.get('partial', False)
1333 partial = opts.get('partial', False)
1332 files = set()
1334 files = set()
1333 try:
1335 try:
1334 patch.patch(ui, repo, tmpname, strip=strip, prefix=prefix,
1336 patch.patch(ui, repo, tmpname, strip=strip, prefix=prefix,
1335 files=files, eolmode=None, similarity=sim / 100.0)
1337 files=files, eolmode=None, similarity=sim / 100.0)
1336 except patch.PatchError as e:
1338 except patch.PatchError as e:
1337 if not partial:
1339 if not partial:
1338 raise error.Abort(str(e))
1340 raise error.Abort(str(e))
1339 if partial:
1341 if partial:
1340 rejects = True
1342 rejects = True
1341
1343
1342 files = list(files)
1344 files = list(files)
1343 if nocommit:
1345 if nocommit:
1344 if message:
1346 if message:
1345 msgs.append(message)
1347 msgs.append(message)
1346 else:
1348 else:
1347 if opts.get('exact') or p2:
1349 if opts.get('exact') or p2:
1348 # If you got here, you either use --force and know what
1350 # If you got here, you either use --force and know what
1349 # you are doing or used --exact or a merge patch while
1351 # you are doing or used --exact or a merge patch while
1350 # being updated to its first parent.
1352 # being updated to its first parent.
1351 m = None
1353 m = None
1352 else:
1354 else:
1353 m = scmutil.matchfiles(repo, files or [])
1355 m = scmutil.matchfiles(repo, files or [])
1354 editform = mergeeditform(repo[None], 'import.normal')
1356 editform = mergeeditform(repo[None], 'import.normal')
1355 if opts.get('exact'):
1357 if opts.get('exact'):
1356 editor = None
1358 editor = None
1357 else:
1359 else:
1358 editor = getcommiteditor(editform=editform, **opts)
1360 editor = getcommiteditor(editform=editform, **opts)
1359 extra = {}
1361 extra = {}
1360 for idfunc in extrapreimport:
1362 for idfunc in extrapreimport:
1361 extrapreimportmap[idfunc](repo, extractdata, extra, opts)
1363 extrapreimportmap[idfunc](repo, extractdata, extra, opts)
1362 overrides = {}
1364 overrides = {}
1363 if partial:
1365 if partial:
1364 overrides[('ui', 'allowemptycommit')] = True
1366 overrides[('ui', 'allowemptycommit')] = True
1365 with repo.ui.configoverride(overrides, 'import'):
1367 with repo.ui.configoverride(overrides, 'import'):
1366 n = repo.commit(message, user,
1368 n = repo.commit(message, user,
1367 date, match=m,
1369 date, match=m,
1368 editor=editor, extra=extra)
1370 editor=editor, extra=extra)
1369 for idfunc in extrapostimport:
1371 for idfunc in extrapostimport:
1370 extrapostimportmap[idfunc](repo[n])
1372 extrapostimportmap[idfunc](repo[n])
1371 else:
1373 else:
1372 if opts.get('exact') or importbranch:
1374 if opts.get('exact') or importbranch:
1373 branch = branch or 'default'
1375 branch = branch or 'default'
1374 else:
1376 else:
1375 branch = p1.branch()
1377 branch = p1.branch()
1376 store = patch.filestore()
1378 store = patch.filestore()
1377 try:
1379 try:
1378 files = set()
1380 files = set()
1379 try:
1381 try:
1380 patch.patchrepo(ui, repo, p1, store, tmpname, strip, prefix,
1382 patch.patchrepo(ui, repo, p1, store, tmpname, strip, prefix,
1381 files, eolmode=None)
1383 files, eolmode=None)
1382 except patch.PatchError as e:
1384 except patch.PatchError as e:
1383 raise error.Abort(str(e))
1385 raise error.Abort(str(e))
1384 if opts.get('exact'):
1386 if opts.get('exact'):
1385 editor = None
1387 editor = None
1386 else:
1388 else:
1387 editor = getcommiteditor(editform='import.bypass')
1389 editor = getcommiteditor(editform='import.bypass')
1388 memctx = context.memctx(repo, (p1.node(), p2.node()),
1390 memctx = context.memctx(repo, (p1.node(), p2.node()),
1389 message,
1391 message,
1390 files=files,
1392 files=files,
1391 filectxfn=store,
1393 filectxfn=store,
1392 user=user,
1394 user=user,
1393 date=date,
1395 date=date,
1394 branch=branch,
1396 branch=branch,
1395 editor=editor)
1397 editor=editor)
1396 n = memctx.commit()
1398 n = memctx.commit()
1397 finally:
1399 finally:
1398 store.close()
1400 store.close()
1399 if opts.get('exact') and nocommit:
1401 if opts.get('exact') and nocommit:
1400 # --exact with --no-commit is still useful in that it does merge
1402 # --exact with --no-commit is still useful in that it does merge
1401 # and branch bits
1403 # and branch bits
1402 ui.warn(_("warning: can't check exact import with --no-commit\n"))
1404 ui.warn(_("warning: can't check exact import with --no-commit\n"))
1403 elif opts.get('exact') and hex(n) != nodeid:
1405 elif opts.get('exact') and hex(n) != nodeid:
1404 raise error.Abort(_('patch is damaged or loses information'))
1406 raise error.Abort(_('patch is damaged or loses information'))
1405 msg = _('applied to working directory')
1407 msg = _('applied to working directory')
1406 if n:
1408 if n:
1407 # i18n: refers to a short changeset id
1409 # i18n: refers to a short changeset id
1408 msg = _('created %s') % short(n)
1410 msg = _('created %s') % short(n)
1409 return (msg, n, rejects)
1411 return (msg, n, rejects)
1410 finally:
1412 finally:
1411 os.unlink(tmpname)
1413 os.unlink(tmpname)
1412
1414
1413 # facility to let extensions include additional data in an exported patch
1415 # facility to let extensions include additional data in an exported patch
1414 # list of identifiers to be executed in order
1416 # list of identifiers to be executed in order
1415 extraexport = []
1417 extraexport = []
1416 # mapping from identifier to actual export function
1418 # mapping from identifier to actual export function
1417 # function as to return a string to be added to the header or None
1419 # function as to return a string to be added to the header or None
1418 # it is given two arguments (sequencenumber, changectx)
1420 # it is given two arguments (sequencenumber, changectx)
1419 extraexportmap = {}
1421 extraexportmap = {}
1420
1422
1421 def _exportsingle(repo, ctx, match, switch_parent, rev, seqno, write, diffopts):
1423 def _exportsingle(repo, ctx, match, switch_parent, rev, seqno, write, diffopts):
1422 node = scmutil.binnode(ctx)
1424 node = scmutil.binnode(ctx)
1423 parents = [p.node() for p in ctx.parents() if p]
1425 parents = [p.node() for p in ctx.parents() if p]
1424 branch = ctx.branch()
1426 branch = ctx.branch()
1425 if switch_parent:
1427 if switch_parent:
1426 parents.reverse()
1428 parents.reverse()
1427
1429
1428 if parents:
1430 if parents:
1429 prev = parents[0]
1431 prev = parents[0]
1430 else:
1432 else:
1431 prev = nullid
1433 prev = nullid
1432
1434
1433 write("# HG changeset patch\n")
1435 write("# HG changeset patch\n")
1434 write("# User %s\n" % ctx.user())
1436 write("# User %s\n" % ctx.user())
1435 write("# Date %d %d\n" % ctx.date())
1437 write("# Date %d %d\n" % ctx.date())
1436 write("# %s\n" % util.datestr(ctx.date()))
1438 write("# %s\n" % util.datestr(ctx.date()))
1437 if branch and branch != 'default':
1439 if branch and branch != 'default':
1438 write("# Branch %s\n" % branch)
1440 write("# Branch %s\n" % branch)
1439 write("# Node ID %s\n" % hex(node))
1441 write("# Node ID %s\n" % hex(node))
1440 write("# Parent %s\n" % hex(prev))
1442 write("# Parent %s\n" % hex(prev))
1441 if len(parents) > 1:
1443 if len(parents) > 1:
1442 write("# Parent %s\n" % hex(parents[1]))
1444 write("# Parent %s\n" % hex(parents[1]))
1443
1445
1444 for headerid in extraexport:
1446 for headerid in extraexport:
1445 header = extraexportmap[headerid](seqno, ctx)
1447 header = extraexportmap[headerid](seqno, ctx)
1446 if header is not None:
1448 if header is not None:
1447 write('# %s\n' % header)
1449 write('# %s\n' % header)
1448 write(ctx.description().rstrip())
1450 write(ctx.description().rstrip())
1449 write("\n\n")
1451 write("\n\n")
1450
1452
1451 for chunk, label in patch.diffui(repo, prev, node, match, opts=diffopts):
1453 for chunk, label in patch.diffui(repo, prev, node, match, opts=diffopts):
1452 write(chunk, label=label)
1454 write(chunk, label=label)
1453
1455
1454 def export(repo, revs, fntemplate='hg-%h.patch', fp=None, switch_parent=False,
1456 def export(repo, revs, fntemplate='hg-%h.patch', fp=None, switch_parent=False,
1455 opts=None, match=None):
1457 opts=None, match=None):
1456 '''export changesets as hg patches
1458 '''export changesets as hg patches
1457
1459
1458 Args:
1460 Args:
1459 repo: The repository from which we're exporting revisions.
1461 repo: The repository from which we're exporting revisions.
1460 revs: A list of revisions to export as revision numbers.
1462 revs: A list of revisions to export as revision numbers.
1461 fntemplate: An optional string to use for generating patch file names.
1463 fntemplate: An optional string to use for generating patch file names.
1462 fp: An optional file-like object to which patches should be written.
1464 fp: An optional file-like object to which patches should be written.
1463 switch_parent: If True, show diffs against second parent when not nullid.
1465 switch_parent: If True, show diffs against second parent when not nullid.
1464 Default is false, which always shows diff against p1.
1466 Default is false, which always shows diff against p1.
1465 opts: diff options to use for generating the patch.
1467 opts: diff options to use for generating the patch.
1466 match: If specified, only export changes to files matching this matcher.
1468 match: If specified, only export changes to files matching this matcher.
1467
1469
1468 Returns:
1470 Returns:
1469 Nothing.
1471 Nothing.
1470
1472
1471 Side Effect:
1473 Side Effect:
1472 "HG Changeset Patch" data is emitted to one of the following
1474 "HG Changeset Patch" data is emitted to one of the following
1473 destinations:
1475 destinations:
1474 fp is specified: All revs are written to the specified
1476 fp is specified: All revs are written to the specified
1475 file-like object.
1477 file-like object.
1476 fntemplate specified: Each rev is written to a unique file named using
1478 fntemplate specified: Each rev is written to a unique file named using
1477 the given template.
1479 the given template.
1478 Neither fp nor template specified: All revs written to repo.ui.write()
1480 Neither fp nor template specified: All revs written to repo.ui.write()
1479 '''
1481 '''
1480
1482
1481 total = len(revs)
1483 total = len(revs)
1482 revwidth = max(len(str(rev)) for rev in revs)
1484 revwidth = max(len(str(rev)) for rev in revs)
1483 filemode = {}
1485 filemode = {}
1484
1486
1485 write = None
1487 write = None
1486 dest = '<unnamed>'
1488 dest = '<unnamed>'
1487 if fp:
1489 if fp:
1488 dest = getattr(fp, 'name', dest)
1490 dest = getattr(fp, 'name', dest)
1489 def write(s, **kw):
1491 def write(s, **kw):
1490 fp.write(s)
1492 fp.write(s)
1491 elif not fntemplate:
1493 elif not fntemplate:
1492 write = repo.ui.write
1494 write = repo.ui.write
1493
1495
1494 for seqno, rev in enumerate(revs, 1):
1496 for seqno, rev in enumerate(revs, 1):
1495 ctx = repo[rev]
1497 ctx = repo[rev]
1496 fo = None
1498 fo = None
1497 if not fp and fntemplate:
1499 if not fp and fntemplate:
1498 desc_lines = ctx.description().rstrip().split('\n')
1500 desc_lines = ctx.description().rstrip().split('\n')
1499 desc = desc_lines[0] #Commit always has a first line.
1501 desc = desc_lines[0] #Commit always has a first line.
1500 fo = makefileobj(repo, fntemplate, ctx.node(), desc=desc,
1502 fo = makefileobj(repo, fntemplate, ctx.node(), desc=desc,
1501 total=total, seqno=seqno, revwidth=revwidth,
1503 total=total, seqno=seqno, revwidth=revwidth,
1502 mode='wb', modemap=filemode)
1504 mode='wb', modemap=filemode)
1503 dest = fo.name
1505 dest = fo.name
1504 def write(s, **kw):
1506 def write(s, **kw):
1505 fo.write(s)
1507 fo.write(s)
1506 if not dest.startswith('<'):
1508 if not dest.startswith('<'):
1507 repo.ui.note("%s\n" % dest)
1509 repo.ui.note("%s\n" % dest)
1508 _exportsingle(
1510 _exportsingle(
1509 repo, ctx, match, switch_parent, rev, seqno, write, opts)
1511 repo, ctx, match, switch_parent, rev, seqno, write, opts)
1510 if fo is not None:
1512 if fo is not None:
1511 fo.close()
1513 fo.close()
1512
1514
1513 def diffordiffstat(ui, repo, diffopts, node1, node2, match,
1515 def diffordiffstat(ui, repo, diffopts, node1, node2, match,
1514 changes=None, stat=False, fp=None, prefix='',
1516 changes=None, stat=False, fp=None, prefix='',
1515 root='', listsubrepos=False):
1517 root='', listsubrepos=False):
1516 '''show diff or diffstat.'''
1518 '''show diff or diffstat.'''
1517 if fp is None:
1519 if fp is None:
1518 write = ui.write
1520 write = ui.write
1519 else:
1521 else:
1520 def write(s, **kw):
1522 def write(s, **kw):
1521 fp.write(s)
1523 fp.write(s)
1522
1524
1523 if root:
1525 if root:
1524 relroot = pathutil.canonpath(repo.root, repo.getcwd(), root)
1526 relroot = pathutil.canonpath(repo.root, repo.getcwd(), root)
1525 else:
1527 else:
1526 relroot = ''
1528 relroot = ''
1527 if relroot != '':
1529 if relroot != '':
1528 # XXX relative roots currently don't work if the root is within a
1530 # XXX relative roots currently don't work if the root is within a
1529 # subrepo
1531 # subrepo
1530 uirelroot = match.uipath(relroot)
1532 uirelroot = match.uipath(relroot)
1531 relroot += '/'
1533 relroot += '/'
1532 for matchroot in match.files():
1534 for matchroot in match.files():
1533 if not matchroot.startswith(relroot):
1535 if not matchroot.startswith(relroot):
1534 ui.warn(_('warning: %s not inside relative root %s\n') % (
1536 ui.warn(_('warning: %s not inside relative root %s\n') % (
1535 match.uipath(matchroot), uirelroot))
1537 match.uipath(matchroot), uirelroot))
1536
1538
1537 if stat:
1539 if stat:
1538 diffopts = diffopts.copy(context=0)
1540 diffopts = diffopts.copy(context=0)
1539 width = 80
1541 width = 80
1540 if not ui.plain():
1542 if not ui.plain():
1541 width = ui.termwidth()
1543 width = ui.termwidth()
1542 chunks = patch.diff(repo, node1, node2, match, changes, diffopts,
1544 chunks = patch.diff(repo, node1, node2, match, changes, diffopts,
1543 prefix=prefix, relroot=relroot)
1545 prefix=prefix, relroot=relroot)
1544 for chunk, label in patch.diffstatui(util.iterlines(chunks),
1546 for chunk, label in patch.diffstatui(util.iterlines(chunks),
1545 width=width):
1547 width=width):
1546 write(chunk, label=label)
1548 write(chunk, label=label)
1547 else:
1549 else:
1548 for chunk, label in patch.diffui(repo, node1, node2, match,
1550 for chunk, label in patch.diffui(repo, node1, node2, match,
1549 changes, diffopts, prefix=prefix,
1551 changes, diffopts, prefix=prefix,
1550 relroot=relroot):
1552 relroot=relroot):
1551 write(chunk, label=label)
1553 write(chunk, label=label)
1552
1554
1553 if listsubrepos:
1555 if listsubrepos:
1554 ctx1 = repo[node1]
1556 ctx1 = repo[node1]
1555 ctx2 = repo[node2]
1557 ctx2 = repo[node2]
1556 for subpath, sub in scmutil.itersubrepos(ctx1, ctx2):
1558 for subpath, sub in scmutil.itersubrepos(ctx1, ctx2):
1557 tempnode2 = node2
1559 tempnode2 = node2
1558 try:
1560 try:
1559 if node2 is not None:
1561 if node2 is not None:
1560 tempnode2 = ctx2.substate[subpath][1]
1562 tempnode2 = ctx2.substate[subpath][1]
1561 except KeyError:
1563 except KeyError:
1562 # A subrepo that existed in node1 was deleted between node1 and
1564 # A subrepo that existed in node1 was deleted between node1 and
1563 # node2 (inclusive). Thus, ctx2's substate won't contain that
1565 # node2 (inclusive). Thus, ctx2's substate won't contain that
1564 # subpath. The best we can do is to ignore it.
1566 # subpath. The best we can do is to ignore it.
1565 tempnode2 = None
1567 tempnode2 = None
1566 submatch = matchmod.subdirmatcher(subpath, match)
1568 submatch = matchmod.subdirmatcher(subpath, match)
1567 sub.diff(ui, diffopts, tempnode2, submatch, changes=changes,
1569 sub.diff(ui, diffopts, tempnode2, submatch, changes=changes,
1568 stat=stat, fp=fp, prefix=prefix)
1570 stat=stat, fp=fp, prefix=prefix)
1569
1571
1570 def _changesetlabels(ctx):
1572 def _changesetlabels(ctx):
1571 labels = ['log.changeset', 'changeset.%s' % ctx.phasestr()]
1573 labels = ['log.changeset', 'changeset.%s' % ctx.phasestr()]
1572 if ctx.obsolete():
1574 if ctx.obsolete():
1573 labels.append('changeset.obsolete')
1575 labels.append('changeset.obsolete')
1574 if ctx.isunstable():
1576 if ctx.isunstable():
1575 labels.append('changeset.unstable')
1577 labels.append('changeset.unstable')
1576 for instability in ctx.instabilities():
1578 for instability in ctx.instabilities():
1577 labels.append('instability.%s' % instability)
1579 labels.append('instability.%s' % instability)
1578 return ' '.join(labels)
1580 return ' '.join(labels)
1579
1581
1580 class changeset_printer(object):
1582 class changeset_printer(object):
1581 '''show changeset information when templating not requested.'''
1583 '''show changeset information when templating not requested.'''
1582
1584
1583 def __init__(self, ui, repo, matchfn, diffopts, buffered):
1585 def __init__(self, ui, repo, matchfn, diffopts, buffered):
1584 self.ui = ui
1586 self.ui = ui
1585 self.repo = repo
1587 self.repo = repo
1586 self.buffered = buffered
1588 self.buffered = buffered
1587 self.matchfn = matchfn
1589 self.matchfn = matchfn
1588 self.diffopts = diffopts
1590 self.diffopts = diffopts
1589 self.header = {}
1591 self.header = {}
1590 self.hunk = {}
1592 self.hunk = {}
1591 self.lastheader = None
1593 self.lastheader = None
1592 self.footer = None
1594 self.footer = None
1593
1595
1594 def flush(self, ctx):
1596 def flush(self, ctx):
1595 rev = ctx.rev()
1597 rev = ctx.rev()
1596 if rev in self.header:
1598 if rev in self.header:
1597 h = self.header[rev]
1599 h = self.header[rev]
1598 if h != self.lastheader:
1600 if h != self.lastheader:
1599 self.lastheader = h
1601 self.lastheader = h
1600 self.ui.write(h)
1602 self.ui.write(h)
1601 del self.header[rev]
1603 del self.header[rev]
1602 if rev in self.hunk:
1604 if rev in self.hunk:
1603 self.ui.write(self.hunk[rev])
1605 self.ui.write(self.hunk[rev])
1604 del self.hunk[rev]
1606 del self.hunk[rev]
1605 return 1
1607 return 1
1606 return 0
1608 return 0
1607
1609
1608 def close(self):
1610 def close(self):
1609 if self.footer:
1611 if self.footer:
1610 self.ui.write(self.footer)
1612 self.ui.write(self.footer)
1611
1613
1612 def show(self, ctx, copies=None, matchfn=None, **props):
1614 def show(self, ctx, copies=None, matchfn=None, **props):
1613 props = pycompat.byteskwargs(props)
1615 props = pycompat.byteskwargs(props)
1614 if self.buffered:
1616 if self.buffered:
1615 self.ui.pushbuffer(labeled=True)
1617 self.ui.pushbuffer(labeled=True)
1616 self._show(ctx, copies, matchfn, props)
1618 self._show(ctx, copies, matchfn, props)
1617 self.hunk[ctx.rev()] = self.ui.popbuffer()
1619 self.hunk[ctx.rev()] = self.ui.popbuffer()
1618 else:
1620 else:
1619 self._show(ctx, copies, matchfn, props)
1621 self._show(ctx, copies, matchfn, props)
1620
1622
1621 def _show(self, ctx, copies, matchfn, props):
1623 def _show(self, ctx, copies, matchfn, props):
1622 '''show a single changeset or file revision'''
1624 '''show a single changeset or file revision'''
1623 changenode = ctx.node()
1625 changenode = ctx.node()
1624 rev = ctx.rev()
1626 rev = ctx.rev()
1625 if self.ui.debugflag:
1627 if self.ui.debugflag:
1626 hexfunc = hex
1628 hexfunc = hex
1627 else:
1629 else:
1628 hexfunc = short
1630 hexfunc = short
1629 # as of now, wctx.node() and wctx.rev() return None, but we want to
1631 # as of now, wctx.node() and wctx.rev() return None, but we want to
1630 # show the same values as {node} and {rev} templatekw
1632 # show the same values as {node} and {rev} templatekw
1631 revnode = (scmutil.intrev(ctx), hexfunc(scmutil.binnode(ctx)))
1633 revnode = (scmutil.intrev(ctx), hexfunc(scmutil.binnode(ctx)))
1632
1634
1633 if self.ui.quiet:
1635 if self.ui.quiet:
1634 self.ui.write("%d:%s\n" % revnode, label='log.node')
1636 self.ui.write("%d:%s\n" % revnode, label='log.node')
1635 return
1637 return
1636
1638
1637 date = util.datestr(ctx.date())
1639 date = util.datestr(ctx.date())
1638
1640
1639 # i18n: column positioning for "hg log"
1641 # i18n: column positioning for "hg log"
1640 self.ui.write(_("changeset: %d:%s\n") % revnode,
1642 self.ui.write(_("changeset: %d:%s\n") % revnode,
1641 label=_changesetlabels(ctx))
1643 label=_changesetlabels(ctx))
1642
1644
1643 # branches are shown first before any other names due to backwards
1645 # branches are shown first before any other names due to backwards
1644 # compatibility
1646 # compatibility
1645 branch = ctx.branch()
1647 branch = ctx.branch()
1646 # don't show the default branch name
1648 # don't show the default branch name
1647 if branch != 'default':
1649 if branch != 'default':
1648 # i18n: column positioning for "hg log"
1650 # i18n: column positioning for "hg log"
1649 self.ui.write(_("branch: %s\n") % branch,
1651 self.ui.write(_("branch: %s\n") % branch,
1650 label='log.branch')
1652 label='log.branch')
1651
1653
1652 for nsname, ns in self.repo.names.iteritems():
1654 for nsname, ns in self.repo.names.iteritems():
1653 # branches has special logic already handled above, so here we just
1655 # branches has special logic already handled above, so here we just
1654 # skip it
1656 # skip it
1655 if nsname == 'branches':
1657 if nsname == 'branches':
1656 continue
1658 continue
1657 # we will use the templatename as the color name since those two
1659 # we will use the templatename as the color name since those two
1658 # should be the same
1660 # should be the same
1659 for name in ns.names(self.repo, changenode):
1661 for name in ns.names(self.repo, changenode):
1660 self.ui.write(ns.logfmt % name,
1662 self.ui.write(ns.logfmt % name,
1661 label='log.%s' % ns.colorname)
1663 label='log.%s' % ns.colorname)
1662 if self.ui.debugflag:
1664 if self.ui.debugflag:
1663 # i18n: column positioning for "hg log"
1665 # i18n: column positioning for "hg log"
1664 self.ui.write(_("phase: %s\n") % ctx.phasestr(),
1666 self.ui.write(_("phase: %s\n") % ctx.phasestr(),
1665 label='log.phase')
1667 label='log.phase')
1666 for pctx in scmutil.meaningfulparents(self.repo, ctx):
1668 for pctx in scmutil.meaningfulparents(self.repo, ctx):
1667 label = 'log.parent changeset.%s' % pctx.phasestr()
1669 label = 'log.parent changeset.%s' % pctx.phasestr()
1668 # i18n: column positioning for "hg log"
1670 # i18n: column positioning for "hg log"
1669 self.ui.write(_("parent: %d:%s\n")
1671 self.ui.write(_("parent: %d:%s\n")
1670 % (pctx.rev(), hexfunc(pctx.node())),
1672 % (pctx.rev(), hexfunc(pctx.node())),
1671 label=label)
1673 label=label)
1672
1674
1673 if self.ui.debugflag and rev is not None:
1675 if self.ui.debugflag and rev is not None:
1674 mnode = ctx.manifestnode()
1676 mnode = ctx.manifestnode()
1675 # i18n: column positioning for "hg log"
1677 # i18n: column positioning for "hg log"
1676 self.ui.write(_("manifest: %d:%s\n") %
1678 self.ui.write(_("manifest: %d:%s\n") %
1677 (self.repo.manifestlog._revlog.rev(mnode),
1679 (self.repo.manifestlog._revlog.rev(mnode),
1678 hex(mnode)),
1680 hex(mnode)),
1679 label='ui.debug log.manifest')
1681 label='ui.debug log.manifest')
1680 # i18n: column positioning for "hg log"
1682 # i18n: column positioning for "hg log"
1681 self.ui.write(_("user: %s\n") % ctx.user(),
1683 self.ui.write(_("user: %s\n") % ctx.user(),
1682 label='log.user')
1684 label='log.user')
1683 # i18n: column positioning for "hg log"
1685 # i18n: column positioning for "hg log"
1684 self.ui.write(_("date: %s\n") % date,
1686 self.ui.write(_("date: %s\n") % date,
1685 label='log.date')
1687 label='log.date')
1686
1688
1687 if ctx.isunstable():
1689 if ctx.isunstable():
1688 # i18n: column positioning for "hg log"
1690 # i18n: column positioning for "hg log"
1689 instabilities = ctx.instabilities()
1691 instabilities = ctx.instabilities()
1690 self.ui.write(_("instability: %s\n") % ', '.join(instabilities),
1692 self.ui.write(_("instability: %s\n") % ', '.join(instabilities),
1691 label='log.instability')
1693 label='log.instability')
1692
1694
1693 self._exthook(ctx)
1695 self._exthook(ctx)
1694
1696
1695 if self.ui.debugflag:
1697 if self.ui.debugflag:
1696 files = ctx.p1().status(ctx)[:3]
1698 files = ctx.p1().status(ctx)[:3]
1697 for key, value in zip([# i18n: column positioning for "hg log"
1699 for key, value in zip([# i18n: column positioning for "hg log"
1698 _("files:"),
1700 _("files:"),
1699 # i18n: column positioning for "hg log"
1701 # i18n: column positioning for "hg log"
1700 _("files+:"),
1702 _("files+:"),
1701 # i18n: column positioning for "hg log"
1703 # i18n: column positioning for "hg log"
1702 _("files-:")], files):
1704 _("files-:")], files):
1703 if value:
1705 if value:
1704 self.ui.write("%-12s %s\n" % (key, " ".join(value)),
1706 self.ui.write("%-12s %s\n" % (key, " ".join(value)),
1705 label='ui.debug log.files')
1707 label='ui.debug log.files')
1706 elif ctx.files() and self.ui.verbose:
1708 elif ctx.files() and self.ui.verbose:
1707 # i18n: column positioning for "hg log"
1709 # i18n: column positioning for "hg log"
1708 self.ui.write(_("files: %s\n") % " ".join(ctx.files()),
1710 self.ui.write(_("files: %s\n") % " ".join(ctx.files()),
1709 label='ui.note log.files')
1711 label='ui.note log.files')
1710 if copies and self.ui.verbose:
1712 if copies and self.ui.verbose:
1711 copies = ['%s (%s)' % c for c in copies]
1713 copies = ['%s (%s)' % c for c in copies]
1712 # i18n: column positioning for "hg log"
1714 # i18n: column positioning for "hg log"
1713 self.ui.write(_("copies: %s\n") % ' '.join(copies),
1715 self.ui.write(_("copies: %s\n") % ' '.join(copies),
1714 label='ui.note log.copies')
1716 label='ui.note log.copies')
1715
1717
1716 extra = ctx.extra()
1718 extra = ctx.extra()
1717 if extra and self.ui.debugflag:
1719 if extra and self.ui.debugflag:
1718 for key, value in sorted(extra.items()):
1720 for key, value in sorted(extra.items()):
1719 # i18n: column positioning for "hg log"
1721 # i18n: column positioning for "hg log"
1720 self.ui.write(_("extra: %s=%s\n")
1722 self.ui.write(_("extra: %s=%s\n")
1721 % (key, util.escapestr(value)),
1723 % (key, util.escapestr(value)),
1722 label='ui.debug log.extra')
1724 label='ui.debug log.extra')
1723
1725
1724 description = ctx.description().strip()
1726 description = ctx.description().strip()
1725 if description:
1727 if description:
1726 if self.ui.verbose:
1728 if self.ui.verbose:
1727 self.ui.write(_("description:\n"),
1729 self.ui.write(_("description:\n"),
1728 label='ui.note log.description')
1730 label='ui.note log.description')
1729 self.ui.write(description,
1731 self.ui.write(description,
1730 label='ui.note log.description')
1732 label='ui.note log.description')
1731 self.ui.write("\n\n")
1733 self.ui.write("\n\n")
1732 else:
1734 else:
1733 # i18n: column positioning for "hg log"
1735 # i18n: column positioning for "hg log"
1734 self.ui.write(_("summary: %s\n") %
1736 self.ui.write(_("summary: %s\n") %
1735 description.splitlines()[0],
1737 description.splitlines()[0],
1736 label='log.summary')
1738 label='log.summary')
1737 self.ui.write("\n")
1739 self.ui.write("\n")
1738
1740
1739 self.showpatch(ctx, matchfn)
1741 self.showpatch(ctx, matchfn)
1740
1742
1741 def _exthook(self, ctx):
1743 def _exthook(self, ctx):
1742 '''empty method used by extension as a hook point
1744 '''empty method used by extension as a hook point
1743 '''
1745 '''
1744 pass
1746 pass
1745
1747
1746 def showpatch(self, ctx, matchfn):
1748 def showpatch(self, ctx, matchfn):
1747 if not matchfn:
1749 if not matchfn:
1748 matchfn = self.matchfn
1750 matchfn = self.matchfn
1749 if matchfn:
1751 if matchfn:
1750 stat = self.diffopts.get('stat')
1752 stat = self.diffopts.get('stat')
1751 diff = self.diffopts.get('patch')
1753 diff = self.diffopts.get('patch')
1752 diffopts = patch.diffallopts(self.ui, self.diffopts)
1754 diffopts = patch.diffallopts(self.ui, self.diffopts)
1753 node = ctx.node()
1755 node = ctx.node()
1754 prev = ctx.p1().node()
1756 prev = ctx.p1().node()
1755 if stat:
1757 if stat:
1756 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1758 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1757 match=matchfn, stat=True)
1759 match=matchfn, stat=True)
1758 if diff:
1760 if diff:
1759 if stat:
1761 if stat:
1760 self.ui.write("\n")
1762 self.ui.write("\n")
1761 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1763 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1762 match=matchfn, stat=False)
1764 match=matchfn, stat=False)
1763 self.ui.write("\n")
1765 self.ui.write("\n")
1764
1766
1765 class jsonchangeset(changeset_printer):
1767 class jsonchangeset(changeset_printer):
1766 '''format changeset information.'''
1768 '''format changeset information.'''
1767
1769
1768 def __init__(self, ui, repo, matchfn, diffopts, buffered):
1770 def __init__(self, ui, repo, matchfn, diffopts, buffered):
1769 changeset_printer.__init__(self, ui, repo, matchfn, diffopts, buffered)
1771 changeset_printer.__init__(self, ui, repo, matchfn, diffopts, buffered)
1770 self.cache = {}
1772 self.cache = {}
1771 self._first = True
1773 self._first = True
1772
1774
1773 def close(self):
1775 def close(self):
1774 if not self._first:
1776 if not self._first:
1775 self.ui.write("\n]\n")
1777 self.ui.write("\n]\n")
1776 else:
1778 else:
1777 self.ui.write("[]\n")
1779 self.ui.write("[]\n")
1778
1780
1779 def _show(self, ctx, copies, matchfn, props):
1781 def _show(self, ctx, copies, matchfn, props):
1780 '''show a single changeset or file revision'''
1782 '''show a single changeset or file revision'''
1781 rev = ctx.rev()
1783 rev = ctx.rev()
1782 if rev is None:
1784 if rev is None:
1783 jrev = jnode = 'null'
1785 jrev = jnode = 'null'
1784 else:
1786 else:
1785 jrev = '%d' % rev
1787 jrev = '%d' % rev
1786 jnode = '"%s"' % hex(ctx.node())
1788 jnode = '"%s"' % hex(ctx.node())
1787 j = encoding.jsonescape
1789 j = encoding.jsonescape
1788
1790
1789 if self._first:
1791 if self._first:
1790 self.ui.write("[\n {")
1792 self.ui.write("[\n {")
1791 self._first = False
1793 self._first = False
1792 else:
1794 else:
1793 self.ui.write(",\n {")
1795 self.ui.write(",\n {")
1794
1796
1795 if self.ui.quiet:
1797 if self.ui.quiet:
1796 self.ui.write(('\n "rev": %s') % jrev)
1798 self.ui.write(('\n "rev": %s') % jrev)
1797 self.ui.write((',\n "node": %s') % jnode)
1799 self.ui.write((',\n "node": %s') % jnode)
1798 self.ui.write('\n }')
1800 self.ui.write('\n }')
1799 return
1801 return
1800
1802
1801 self.ui.write(('\n "rev": %s') % jrev)
1803 self.ui.write(('\n "rev": %s') % jrev)
1802 self.ui.write((',\n "node": %s') % jnode)
1804 self.ui.write((',\n "node": %s') % jnode)
1803 self.ui.write((',\n "branch": "%s"') % j(ctx.branch()))
1805 self.ui.write((',\n "branch": "%s"') % j(ctx.branch()))
1804 self.ui.write((',\n "phase": "%s"') % ctx.phasestr())
1806 self.ui.write((',\n "phase": "%s"') % ctx.phasestr())
1805 self.ui.write((',\n "user": "%s"') % j(ctx.user()))
1807 self.ui.write((',\n "user": "%s"') % j(ctx.user()))
1806 self.ui.write((',\n "date": [%d, %d]') % ctx.date())
1808 self.ui.write((',\n "date": [%d, %d]') % ctx.date())
1807 self.ui.write((',\n "desc": "%s"') % j(ctx.description()))
1809 self.ui.write((',\n "desc": "%s"') % j(ctx.description()))
1808
1810
1809 self.ui.write((',\n "bookmarks": [%s]') %
1811 self.ui.write((',\n "bookmarks": [%s]') %
1810 ", ".join('"%s"' % j(b) for b in ctx.bookmarks()))
1812 ", ".join('"%s"' % j(b) for b in ctx.bookmarks()))
1811 self.ui.write((',\n "tags": [%s]') %
1813 self.ui.write((',\n "tags": [%s]') %
1812 ", ".join('"%s"' % j(t) for t in ctx.tags()))
1814 ", ".join('"%s"' % j(t) for t in ctx.tags()))
1813 self.ui.write((',\n "parents": [%s]') %
1815 self.ui.write((',\n "parents": [%s]') %
1814 ", ".join('"%s"' % c.hex() for c in ctx.parents()))
1816 ", ".join('"%s"' % c.hex() for c in ctx.parents()))
1815
1817
1816 if self.ui.debugflag:
1818 if self.ui.debugflag:
1817 if rev is None:
1819 if rev is None:
1818 jmanifestnode = 'null'
1820 jmanifestnode = 'null'
1819 else:
1821 else:
1820 jmanifestnode = '"%s"' % hex(ctx.manifestnode())
1822 jmanifestnode = '"%s"' % hex(ctx.manifestnode())
1821 self.ui.write((',\n "manifest": %s') % jmanifestnode)
1823 self.ui.write((',\n "manifest": %s') % jmanifestnode)
1822
1824
1823 self.ui.write((',\n "extra": {%s}') %
1825 self.ui.write((',\n "extra": {%s}') %
1824 ", ".join('"%s": "%s"' % (j(k), j(v))
1826 ", ".join('"%s": "%s"' % (j(k), j(v))
1825 for k, v in ctx.extra().items()))
1827 for k, v in ctx.extra().items()))
1826
1828
1827 files = ctx.p1().status(ctx)
1829 files = ctx.p1().status(ctx)
1828 self.ui.write((',\n "modified": [%s]') %
1830 self.ui.write((',\n "modified": [%s]') %
1829 ", ".join('"%s"' % j(f) for f in files[0]))
1831 ", ".join('"%s"' % j(f) for f in files[0]))
1830 self.ui.write((',\n "added": [%s]') %
1832 self.ui.write((',\n "added": [%s]') %
1831 ", ".join('"%s"' % j(f) for f in files[1]))
1833 ", ".join('"%s"' % j(f) for f in files[1]))
1832 self.ui.write((',\n "removed": [%s]') %
1834 self.ui.write((',\n "removed": [%s]') %
1833 ", ".join('"%s"' % j(f) for f in files[2]))
1835 ", ".join('"%s"' % j(f) for f in files[2]))
1834
1836
1835 elif self.ui.verbose:
1837 elif self.ui.verbose:
1836 self.ui.write((',\n "files": [%s]') %
1838 self.ui.write((',\n "files": [%s]') %
1837 ", ".join('"%s"' % j(f) for f in ctx.files()))
1839 ", ".join('"%s"' % j(f) for f in ctx.files()))
1838
1840
1839 if copies:
1841 if copies:
1840 self.ui.write((',\n "copies": {%s}') %
1842 self.ui.write((',\n "copies": {%s}') %
1841 ", ".join('"%s": "%s"' % (j(k), j(v))
1843 ", ".join('"%s": "%s"' % (j(k), j(v))
1842 for k, v in copies))
1844 for k, v in copies))
1843
1845
1844 matchfn = self.matchfn
1846 matchfn = self.matchfn
1845 if matchfn:
1847 if matchfn:
1846 stat = self.diffopts.get('stat')
1848 stat = self.diffopts.get('stat')
1847 diff = self.diffopts.get('patch')
1849 diff = self.diffopts.get('patch')
1848 diffopts = patch.difffeatureopts(self.ui, self.diffopts, git=True)
1850 diffopts = patch.difffeatureopts(self.ui, self.diffopts, git=True)
1849 node, prev = ctx.node(), ctx.p1().node()
1851 node, prev = ctx.node(), ctx.p1().node()
1850 if stat:
1852 if stat:
1851 self.ui.pushbuffer()
1853 self.ui.pushbuffer()
1852 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1854 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1853 match=matchfn, stat=True)
1855 match=matchfn, stat=True)
1854 self.ui.write((',\n "diffstat": "%s"')
1856 self.ui.write((',\n "diffstat": "%s"')
1855 % j(self.ui.popbuffer()))
1857 % j(self.ui.popbuffer()))
1856 if diff:
1858 if diff:
1857 self.ui.pushbuffer()
1859 self.ui.pushbuffer()
1858 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1860 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1859 match=matchfn, stat=False)
1861 match=matchfn, stat=False)
1860 self.ui.write((',\n "diff": "%s"') % j(self.ui.popbuffer()))
1862 self.ui.write((',\n "diff": "%s"') % j(self.ui.popbuffer()))
1861
1863
1862 self.ui.write("\n }")
1864 self.ui.write("\n }")
1863
1865
1864 class changeset_templater(changeset_printer):
1866 class changeset_templater(changeset_printer):
1865 '''format changeset information.'''
1867 '''format changeset information.'''
1866
1868
1867 # Arguments before "buffered" used to be positional. Consider not
1869 # Arguments before "buffered" used to be positional. Consider not
1868 # adding/removing arguments before "buffered" to not break callers.
1870 # adding/removing arguments before "buffered" to not break callers.
1869 def __init__(self, ui, repo, tmplspec, matchfn=None, diffopts=None,
1871 def __init__(self, ui, repo, tmplspec, matchfn=None, diffopts=None,
1870 buffered=False):
1872 buffered=False):
1871 diffopts = diffopts or {}
1873 diffopts = diffopts or {}
1872
1874
1873 changeset_printer.__init__(self, ui, repo, matchfn, diffopts, buffered)
1875 changeset_printer.__init__(self, ui, repo, matchfn, diffopts, buffered)
1874 self.t = formatter.loadtemplater(ui, tmplspec,
1876 self.t = formatter.loadtemplater(ui, tmplspec,
1875 cache=templatekw.defaulttempl)
1877 cache=templatekw.defaulttempl)
1876 self._counter = itertools.count()
1878 self._counter = itertools.count()
1877 self.cache = {}
1879 self.cache = {}
1878
1880
1879 self._tref = tmplspec.ref
1881 self._tref = tmplspec.ref
1880 self._parts = {'header': '', 'footer': '',
1882 self._parts = {'header': '', 'footer': '',
1881 tmplspec.ref: tmplspec.ref,
1883 tmplspec.ref: tmplspec.ref,
1882 'docheader': '', 'docfooter': '',
1884 'docheader': '', 'docfooter': '',
1883 'separator': ''}
1885 'separator': ''}
1884 if tmplspec.mapfile:
1886 if tmplspec.mapfile:
1885 # find correct templates for current mode, for backward
1887 # find correct templates for current mode, for backward
1886 # compatibility with 'log -v/-q/--debug' using a mapfile
1888 # compatibility with 'log -v/-q/--debug' using a mapfile
1887 tmplmodes = [
1889 tmplmodes = [
1888 (True, ''),
1890 (True, ''),
1889 (self.ui.verbose, '_verbose'),
1891 (self.ui.verbose, '_verbose'),
1890 (self.ui.quiet, '_quiet'),
1892 (self.ui.quiet, '_quiet'),
1891 (self.ui.debugflag, '_debug'),
1893 (self.ui.debugflag, '_debug'),
1892 ]
1894 ]
1893 for mode, postfix in tmplmodes:
1895 for mode, postfix in tmplmodes:
1894 for t in self._parts:
1896 for t in self._parts:
1895 cur = t + postfix
1897 cur = t + postfix
1896 if mode and cur in self.t:
1898 if mode and cur in self.t:
1897 self._parts[t] = cur
1899 self._parts[t] = cur
1898 else:
1900 else:
1899 partnames = [p for p in self._parts.keys() if p != tmplspec.ref]
1901 partnames = [p for p in self._parts.keys() if p != tmplspec.ref]
1900 m = formatter.templatepartsmap(tmplspec, self.t, partnames)
1902 m = formatter.templatepartsmap(tmplspec, self.t, partnames)
1901 self._parts.update(m)
1903 self._parts.update(m)
1902
1904
1903 if self._parts['docheader']:
1905 if self._parts['docheader']:
1904 self.ui.write(templater.stringify(self.t(self._parts['docheader'])))
1906 self.ui.write(templater.stringify(self.t(self._parts['docheader'])))
1905
1907
1906 def close(self):
1908 def close(self):
1907 if self._parts['docfooter']:
1909 if self._parts['docfooter']:
1908 if not self.footer:
1910 if not self.footer:
1909 self.footer = ""
1911 self.footer = ""
1910 self.footer += templater.stringify(self.t(self._parts['docfooter']))
1912 self.footer += templater.stringify(self.t(self._parts['docfooter']))
1911 return super(changeset_templater, self).close()
1913 return super(changeset_templater, self).close()
1912
1914
1913 def _show(self, ctx, copies, matchfn, props):
1915 def _show(self, ctx, copies, matchfn, props):
1914 '''show a single changeset or file revision'''
1916 '''show a single changeset or file revision'''
1915 props = props.copy()
1917 props = props.copy()
1916 props.update(templatekw.keywords)
1918 props.update(templatekw.keywords)
1917 props['templ'] = self.t
1919 props['templ'] = self.t
1918 props['ctx'] = ctx
1920 props['ctx'] = ctx
1919 props['repo'] = self.repo
1921 props['repo'] = self.repo
1920 props['ui'] = self.repo.ui
1922 props['ui'] = self.repo.ui
1921 props['index'] = index = next(self._counter)
1923 props['index'] = index = next(self._counter)
1922 props['revcache'] = {'copies': copies}
1924 props['revcache'] = {'copies': copies}
1923 props['cache'] = self.cache
1925 props['cache'] = self.cache
1924 props = pycompat.strkwargs(props)
1926 props = pycompat.strkwargs(props)
1925
1927
1926 # write separator, which wouldn't work well with the header part below
1928 # write separator, which wouldn't work well with the header part below
1927 # since there's inherently a conflict between header (across items) and
1929 # since there's inherently a conflict between header (across items) and
1928 # separator (per item)
1930 # separator (per item)
1929 if self._parts['separator'] and index > 0:
1931 if self._parts['separator'] and index > 0:
1930 self.ui.write(templater.stringify(self.t(self._parts['separator'])))
1932 self.ui.write(templater.stringify(self.t(self._parts['separator'])))
1931
1933
1932 # write header
1934 # write header
1933 if self._parts['header']:
1935 if self._parts['header']:
1934 h = templater.stringify(self.t(self._parts['header'], **props))
1936 h = templater.stringify(self.t(self._parts['header'], **props))
1935 if self.buffered:
1937 if self.buffered:
1936 self.header[ctx.rev()] = h
1938 self.header[ctx.rev()] = h
1937 else:
1939 else:
1938 if self.lastheader != h:
1940 if self.lastheader != h:
1939 self.lastheader = h
1941 self.lastheader = h
1940 self.ui.write(h)
1942 self.ui.write(h)
1941
1943
1942 # write changeset metadata, then patch if requested
1944 # write changeset metadata, then patch if requested
1943 key = self._parts[self._tref]
1945 key = self._parts[self._tref]
1944 self.ui.write(templater.stringify(self.t(key, **props)))
1946 self.ui.write(templater.stringify(self.t(key, **props)))
1945 self.showpatch(ctx, matchfn)
1947 self.showpatch(ctx, matchfn)
1946
1948
1947 if self._parts['footer']:
1949 if self._parts['footer']:
1948 if not self.footer:
1950 if not self.footer:
1949 self.footer = templater.stringify(
1951 self.footer = templater.stringify(
1950 self.t(self._parts['footer'], **props))
1952 self.t(self._parts['footer'], **props))
1951
1953
1952 def logtemplatespec(tmpl, mapfile):
1954 def logtemplatespec(tmpl, mapfile):
1953 if mapfile:
1955 if mapfile:
1954 return formatter.templatespec('changeset', tmpl, mapfile)
1956 return formatter.templatespec('changeset', tmpl, mapfile)
1955 else:
1957 else:
1956 return formatter.templatespec('', tmpl, None)
1958 return formatter.templatespec('', tmpl, None)
1957
1959
1958 def _lookuplogtemplate(ui, tmpl, style):
1960 def _lookuplogtemplate(ui, tmpl, style):
1959 """Find the template matching the given template spec or style
1961 """Find the template matching the given template spec or style
1960
1962
1961 See formatter.lookuptemplate() for details.
1963 See formatter.lookuptemplate() for details.
1962 """
1964 """
1963
1965
1964 # ui settings
1966 # ui settings
1965 if not tmpl and not style: # template are stronger than style
1967 if not tmpl and not style: # template are stronger than style
1966 tmpl = ui.config('ui', 'logtemplate')
1968 tmpl = ui.config('ui', 'logtemplate')
1967 if tmpl:
1969 if tmpl:
1968 return logtemplatespec(templater.unquotestring(tmpl), None)
1970 return logtemplatespec(templater.unquotestring(tmpl), None)
1969 else:
1971 else:
1970 style = util.expandpath(ui.config('ui', 'style'))
1972 style = util.expandpath(ui.config('ui', 'style'))
1971
1973
1972 if not tmpl and style:
1974 if not tmpl and style:
1973 mapfile = style
1975 mapfile = style
1974 if not os.path.split(mapfile)[0]:
1976 if not os.path.split(mapfile)[0]:
1975 mapname = (templater.templatepath('map-cmdline.' + mapfile)
1977 mapname = (templater.templatepath('map-cmdline.' + mapfile)
1976 or templater.templatepath(mapfile))
1978 or templater.templatepath(mapfile))
1977 if mapname:
1979 if mapname:
1978 mapfile = mapname
1980 mapfile = mapname
1979 return logtemplatespec(None, mapfile)
1981 return logtemplatespec(None, mapfile)
1980
1982
1981 if not tmpl:
1983 if not tmpl:
1982 return logtemplatespec(None, None)
1984 return logtemplatespec(None, None)
1983
1985
1984 return formatter.lookuptemplate(ui, 'changeset', tmpl)
1986 return formatter.lookuptemplate(ui, 'changeset', tmpl)
1985
1987
1986 def makelogtemplater(ui, repo, tmpl, buffered=False):
1988 def makelogtemplater(ui, repo, tmpl, buffered=False):
1987 """Create a changeset_templater from a literal template 'tmpl'"""
1989 """Create a changeset_templater from a literal template 'tmpl'"""
1988 spec = logtemplatespec(tmpl, None)
1990 spec = logtemplatespec(tmpl, None)
1989 return changeset_templater(ui, repo, spec, buffered=buffered)
1991 return changeset_templater(ui, repo, spec, buffered=buffered)
1990
1992
1991 def show_changeset(ui, repo, opts, buffered=False):
1993 def show_changeset(ui, repo, opts, buffered=False):
1992 """show one changeset using template or regular display.
1994 """show one changeset using template or regular display.
1993
1995
1994 Display format will be the first non-empty hit of:
1996 Display format will be the first non-empty hit of:
1995 1. option 'template'
1997 1. option 'template'
1996 2. option 'style'
1998 2. option 'style'
1997 3. [ui] setting 'logtemplate'
1999 3. [ui] setting 'logtemplate'
1998 4. [ui] setting 'style'
2000 4. [ui] setting 'style'
1999 If all of these values are either the unset or the empty string,
2001 If all of these values are either the unset or the empty string,
2000 regular display via changeset_printer() is done.
2002 regular display via changeset_printer() is done.
2001 """
2003 """
2002 # options
2004 # options
2003 matchfn = None
2005 matchfn = None
2004 if opts.get('patch') or opts.get('stat'):
2006 if opts.get('patch') or opts.get('stat'):
2005 matchfn = scmutil.matchall(repo)
2007 matchfn = scmutil.matchall(repo)
2006
2008
2007 if opts.get('template') == 'json':
2009 if opts.get('template') == 'json':
2008 return jsonchangeset(ui, repo, matchfn, opts, buffered)
2010 return jsonchangeset(ui, repo, matchfn, opts, buffered)
2009
2011
2010 spec = _lookuplogtemplate(ui, opts.get('template'), opts.get('style'))
2012 spec = _lookuplogtemplate(ui, opts.get('template'), opts.get('style'))
2011
2013
2012 if not spec.ref and not spec.tmpl and not spec.mapfile:
2014 if not spec.ref and not spec.tmpl and not spec.mapfile:
2013 return changeset_printer(ui, repo, matchfn, opts, buffered)
2015 return changeset_printer(ui, repo, matchfn, opts, buffered)
2014
2016
2015 return changeset_templater(ui, repo, spec, matchfn, opts, buffered)
2017 return changeset_templater(ui, repo, spec, matchfn, opts, buffered)
2016
2018
2017 def showmarker(fm, marker, index=None):
2019 def showmarker(fm, marker, index=None):
2018 """utility function to display obsolescence marker in a readable way
2020 """utility function to display obsolescence marker in a readable way
2019
2021
2020 To be used by debug function."""
2022 To be used by debug function."""
2021 if index is not None:
2023 if index is not None:
2022 fm.write('index', '%i ', index)
2024 fm.write('index', '%i ', index)
2023 fm.write('prednode', '%s ', hex(marker.prednode()))
2025 fm.write('prednode', '%s ', hex(marker.prednode()))
2024 succs = marker.succnodes()
2026 succs = marker.succnodes()
2025 fm.condwrite(succs, 'succnodes', '%s ',
2027 fm.condwrite(succs, 'succnodes', '%s ',
2026 fm.formatlist(map(hex, succs), name='node'))
2028 fm.formatlist(map(hex, succs), name='node'))
2027 fm.write('flag', '%X ', marker.flags())
2029 fm.write('flag', '%X ', marker.flags())
2028 parents = marker.parentnodes()
2030 parents = marker.parentnodes()
2029 if parents is not None:
2031 if parents is not None:
2030 fm.write('parentnodes', '{%s} ',
2032 fm.write('parentnodes', '{%s} ',
2031 fm.formatlist(map(hex, parents), name='node', sep=', '))
2033 fm.formatlist(map(hex, parents), name='node', sep=', '))
2032 fm.write('date', '(%s) ', fm.formatdate(marker.date()))
2034 fm.write('date', '(%s) ', fm.formatdate(marker.date()))
2033 meta = marker.metadata().copy()
2035 meta = marker.metadata().copy()
2034 meta.pop('date', None)
2036 meta.pop('date', None)
2035 fm.write('metadata', '{%s}', fm.formatdict(meta, fmt='%r: %r', sep=', '))
2037 fm.write('metadata', '{%s}', fm.formatdict(meta, fmt='%r: %r', sep=', '))
2036 fm.plain('\n')
2038 fm.plain('\n')
2037
2039
2038 def finddate(ui, repo, date):
2040 def finddate(ui, repo, date):
2039 """Find the tipmost changeset that matches the given date spec"""
2041 """Find the tipmost changeset that matches the given date spec"""
2040
2042
2041 df = util.matchdate(date)
2043 df = util.matchdate(date)
2042 m = scmutil.matchall(repo)
2044 m = scmutil.matchall(repo)
2043 results = {}
2045 results = {}
2044
2046
2045 def prep(ctx, fns):
2047 def prep(ctx, fns):
2046 d = ctx.date()
2048 d = ctx.date()
2047 if df(d[0]):
2049 if df(d[0]):
2048 results[ctx.rev()] = d
2050 results[ctx.rev()] = d
2049
2051
2050 for ctx in walkchangerevs(repo, m, {'rev': None}, prep):
2052 for ctx in walkchangerevs(repo, m, {'rev': None}, prep):
2051 rev = ctx.rev()
2053 rev = ctx.rev()
2052 if rev in results:
2054 if rev in results:
2053 ui.status(_("found revision %s from %s\n") %
2055 ui.status(_("found revision %s from %s\n") %
2054 (rev, util.datestr(results[rev])))
2056 (rev, util.datestr(results[rev])))
2055 return '%d' % rev
2057 return '%d' % rev
2056
2058
2057 raise error.Abort(_("revision matching date not found"))
2059 raise error.Abort(_("revision matching date not found"))
2058
2060
2059 def increasingwindows(windowsize=8, sizelimit=512):
2061 def increasingwindows(windowsize=8, sizelimit=512):
2060 while True:
2062 while True:
2061 yield windowsize
2063 yield windowsize
2062 if windowsize < sizelimit:
2064 if windowsize < sizelimit:
2063 windowsize *= 2
2065 windowsize *= 2
2064
2066
2065 class FileWalkError(Exception):
2067 class FileWalkError(Exception):
2066 pass
2068 pass
2067
2069
2068 def walkfilerevs(repo, match, follow, revs, fncache):
2070 def walkfilerevs(repo, match, follow, revs, fncache):
2069 '''Walks the file history for the matched files.
2071 '''Walks the file history for the matched files.
2070
2072
2071 Returns the changeset revs that are involved in the file history.
2073 Returns the changeset revs that are involved in the file history.
2072
2074
2073 Throws FileWalkError if the file history can't be walked using
2075 Throws FileWalkError if the file history can't be walked using
2074 filelogs alone.
2076 filelogs alone.
2075 '''
2077 '''
2076 wanted = set()
2078 wanted = set()
2077 copies = []
2079 copies = []
2078 minrev, maxrev = min(revs), max(revs)
2080 minrev, maxrev = min(revs), max(revs)
2079 def filerevgen(filelog, last):
2081 def filerevgen(filelog, last):
2080 """
2082 """
2081 Only files, no patterns. Check the history of each file.
2083 Only files, no patterns. Check the history of each file.
2082
2084
2083 Examines filelog entries within minrev, maxrev linkrev range
2085 Examines filelog entries within minrev, maxrev linkrev range
2084 Returns an iterator yielding (linkrev, parentlinkrevs, copied)
2086 Returns an iterator yielding (linkrev, parentlinkrevs, copied)
2085 tuples in backwards order
2087 tuples in backwards order
2086 """
2088 """
2087 cl_count = len(repo)
2089 cl_count = len(repo)
2088 revs = []
2090 revs = []
2089 for j in xrange(0, last + 1):
2091 for j in xrange(0, last + 1):
2090 linkrev = filelog.linkrev(j)
2092 linkrev = filelog.linkrev(j)
2091 if linkrev < minrev:
2093 if linkrev < minrev:
2092 continue
2094 continue
2093 # only yield rev for which we have the changelog, it can
2095 # only yield rev for which we have the changelog, it can
2094 # happen while doing "hg log" during a pull or commit
2096 # happen while doing "hg log" during a pull or commit
2095 if linkrev >= cl_count:
2097 if linkrev >= cl_count:
2096 break
2098 break
2097
2099
2098 parentlinkrevs = []
2100 parentlinkrevs = []
2099 for p in filelog.parentrevs(j):
2101 for p in filelog.parentrevs(j):
2100 if p != nullrev:
2102 if p != nullrev:
2101 parentlinkrevs.append(filelog.linkrev(p))
2103 parentlinkrevs.append(filelog.linkrev(p))
2102 n = filelog.node(j)
2104 n = filelog.node(j)
2103 revs.append((linkrev, parentlinkrevs,
2105 revs.append((linkrev, parentlinkrevs,
2104 follow and filelog.renamed(n)))
2106 follow and filelog.renamed(n)))
2105
2107
2106 return reversed(revs)
2108 return reversed(revs)
2107 def iterfiles():
2109 def iterfiles():
2108 pctx = repo['.']
2110 pctx = repo['.']
2109 for filename in match.files():
2111 for filename in match.files():
2110 if follow:
2112 if follow:
2111 if filename not in pctx:
2113 if filename not in pctx:
2112 raise error.Abort(_('cannot follow file not in parent '
2114 raise error.Abort(_('cannot follow file not in parent '
2113 'revision: "%s"') % filename)
2115 'revision: "%s"') % filename)
2114 yield filename, pctx[filename].filenode()
2116 yield filename, pctx[filename].filenode()
2115 else:
2117 else:
2116 yield filename, None
2118 yield filename, None
2117 for filename_node in copies:
2119 for filename_node in copies:
2118 yield filename_node
2120 yield filename_node
2119
2121
2120 for file_, node in iterfiles():
2122 for file_, node in iterfiles():
2121 filelog = repo.file(file_)
2123 filelog = repo.file(file_)
2122 if not len(filelog):
2124 if not len(filelog):
2123 if node is None:
2125 if node is None:
2124 # A zero count may be a directory or deleted file, so
2126 # A zero count may be a directory or deleted file, so
2125 # try to find matching entries on the slow path.
2127 # try to find matching entries on the slow path.
2126 if follow:
2128 if follow:
2127 raise error.Abort(
2129 raise error.Abort(
2128 _('cannot follow nonexistent file: "%s"') % file_)
2130 _('cannot follow nonexistent file: "%s"') % file_)
2129 raise FileWalkError("Cannot walk via filelog")
2131 raise FileWalkError("Cannot walk via filelog")
2130 else:
2132 else:
2131 continue
2133 continue
2132
2134
2133 if node is None:
2135 if node is None:
2134 last = len(filelog) - 1
2136 last = len(filelog) - 1
2135 else:
2137 else:
2136 last = filelog.rev(node)
2138 last = filelog.rev(node)
2137
2139
2138 # keep track of all ancestors of the file
2140 # keep track of all ancestors of the file
2139 ancestors = {filelog.linkrev(last)}
2141 ancestors = {filelog.linkrev(last)}
2140
2142
2141 # iterate from latest to oldest revision
2143 # iterate from latest to oldest revision
2142 for rev, flparentlinkrevs, copied in filerevgen(filelog, last):
2144 for rev, flparentlinkrevs, copied in filerevgen(filelog, last):
2143 if not follow:
2145 if not follow:
2144 if rev > maxrev:
2146 if rev > maxrev:
2145 continue
2147 continue
2146 else:
2148 else:
2147 # Note that last might not be the first interesting
2149 # Note that last might not be the first interesting
2148 # rev to us:
2150 # rev to us:
2149 # if the file has been changed after maxrev, we'll
2151 # if the file has been changed after maxrev, we'll
2150 # have linkrev(last) > maxrev, and we still need
2152 # have linkrev(last) > maxrev, and we still need
2151 # to explore the file graph
2153 # to explore the file graph
2152 if rev not in ancestors:
2154 if rev not in ancestors:
2153 continue
2155 continue
2154 # XXX insert 1327 fix here
2156 # XXX insert 1327 fix here
2155 if flparentlinkrevs:
2157 if flparentlinkrevs:
2156 ancestors.update(flparentlinkrevs)
2158 ancestors.update(flparentlinkrevs)
2157
2159
2158 fncache.setdefault(rev, []).append(file_)
2160 fncache.setdefault(rev, []).append(file_)
2159 wanted.add(rev)
2161 wanted.add(rev)
2160 if copied:
2162 if copied:
2161 copies.append(copied)
2163 copies.append(copied)
2162
2164
2163 return wanted
2165 return wanted
2164
2166
2165 class _followfilter(object):
2167 class _followfilter(object):
2166 def __init__(self, repo, onlyfirst=False):
2168 def __init__(self, repo, onlyfirst=False):
2167 self.repo = repo
2169 self.repo = repo
2168 self.startrev = nullrev
2170 self.startrev = nullrev
2169 self.roots = set()
2171 self.roots = set()
2170 self.onlyfirst = onlyfirst
2172 self.onlyfirst = onlyfirst
2171
2173
2172 def match(self, rev):
2174 def match(self, rev):
2173 def realparents(rev):
2175 def realparents(rev):
2174 if self.onlyfirst:
2176 if self.onlyfirst:
2175 return self.repo.changelog.parentrevs(rev)[0:1]
2177 return self.repo.changelog.parentrevs(rev)[0:1]
2176 else:
2178 else:
2177 return filter(lambda x: x != nullrev,
2179 return filter(lambda x: x != nullrev,
2178 self.repo.changelog.parentrevs(rev))
2180 self.repo.changelog.parentrevs(rev))
2179
2181
2180 if self.startrev == nullrev:
2182 if self.startrev == nullrev:
2181 self.startrev = rev
2183 self.startrev = rev
2182 return True
2184 return True
2183
2185
2184 if rev > self.startrev:
2186 if rev > self.startrev:
2185 # forward: all descendants
2187 # forward: all descendants
2186 if not self.roots:
2188 if not self.roots:
2187 self.roots.add(self.startrev)
2189 self.roots.add(self.startrev)
2188 for parent in realparents(rev):
2190 for parent in realparents(rev):
2189 if parent in self.roots:
2191 if parent in self.roots:
2190 self.roots.add(rev)
2192 self.roots.add(rev)
2191 return True
2193 return True
2192 else:
2194 else:
2193 # backwards: all parents
2195 # backwards: all parents
2194 if not self.roots:
2196 if not self.roots:
2195 self.roots.update(realparents(self.startrev))
2197 self.roots.update(realparents(self.startrev))
2196 if rev in self.roots:
2198 if rev in self.roots:
2197 self.roots.remove(rev)
2199 self.roots.remove(rev)
2198 self.roots.update(realparents(rev))
2200 self.roots.update(realparents(rev))
2199 return True
2201 return True
2200
2202
2201 return False
2203 return False
2202
2204
2203 def walkchangerevs(repo, match, opts, prepare):
2205 def walkchangerevs(repo, match, opts, prepare):
2204 '''Iterate over files and the revs in which they changed.
2206 '''Iterate over files and the revs in which they changed.
2205
2207
2206 Callers most commonly need to iterate backwards over the history
2208 Callers most commonly need to iterate backwards over the history
2207 in which they are interested. Doing so has awful (quadratic-looking)
2209 in which they are interested. Doing so has awful (quadratic-looking)
2208 performance, so we use iterators in a "windowed" way.
2210 performance, so we use iterators in a "windowed" way.
2209
2211
2210 We walk a window of revisions in the desired order. Within the
2212 We walk a window of revisions in the desired order. Within the
2211 window, we first walk forwards to gather data, then in the desired
2213 window, we first walk forwards to gather data, then in the desired
2212 order (usually backwards) to display it.
2214 order (usually backwards) to display it.
2213
2215
2214 This function returns an iterator yielding contexts. Before
2216 This function returns an iterator yielding contexts. Before
2215 yielding each context, the iterator will first call the prepare
2217 yielding each context, the iterator will first call the prepare
2216 function on each context in the window in forward order.'''
2218 function on each context in the window in forward order.'''
2217
2219
2218 follow = opts.get('follow') or opts.get('follow_first')
2220 follow = opts.get('follow') or opts.get('follow_first')
2219 revs = _logrevs(repo, opts)
2221 revs = _logrevs(repo, opts)
2220 if not revs:
2222 if not revs:
2221 return []
2223 return []
2222 wanted = set()
2224 wanted = set()
2223 slowpath = match.anypats() or ((match.isexact() or match.prefix()) and
2225 slowpath = match.anypats() or ((match.isexact() or match.prefix()) and
2224 opts.get('removed'))
2226 opts.get('removed'))
2225 fncache = {}
2227 fncache = {}
2226 change = repo.changectx
2228 change = repo.changectx
2227
2229
2228 # First step is to fill wanted, the set of revisions that we want to yield.
2230 # First step is to fill wanted, the set of revisions that we want to yield.
2229 # When it does not induce extra cost, we also fill fncache for revisions in
2231 # When it does not induce extra cost, we also fill fncache for revisions in
2230 # wanted: a cache of filenames that were changed (ctx.files()) and that
2232 # wanted: a cache of filenames that were changed (ctx.files()) and that
2231 # match the file filtering conditions.
2233 # match the file filtering conditions.
2232
2234
2233 if match.always():
2235 if match.always():
2234 # No files, no patterns. Display all revs.
2236 # No files, no patterns. Display all revs.
2235 wanted = revs
2237 wanted = revs
2236 elif not slowpath:
2238 elif not slowpath:
2237 # We only have to read through the filelog to find wanted revisions
2239 # We only have to read through the filelog to find wanted revisions
2238
2240
2239 try:
2241 try:
2240 wanted = walkfilerevs(repo, match, follow, revs, fncache)
2242 wanted = walkfilerevs(repo, match, follow, revs, fncache)
2241 except FileWalkError:
2243 except FileWalkError:
2242 slowpath = True
2244 slowpath = True
2243
2245
2244 # We decided to fall back to the slowpath because at least one
2246 # We decided to fall back to the slowpath because at least one
2245 # of the paths was not a file. Check to see if at least one of them
2247 # of the paths was not a file. Check to see if at least one of them
2246 # existed in history, otherwise simply return
2248 # existed in history, otherwise simply return
2247 for path in match.files():
2249 for path in match.files():
2248 if path == '.' or path in repo.store:
2250 if path == '.' or path in repo.store:
2249 break
2251 break
2250 else:
2252 else:
2251 return []
2253 return []
2252
2254
2253 if slowpath:
2255 if slowpath:
2254 # We have to read the changelog to match filenames against
2256 # We have to read the changelog to match filenames against
2255 # changed files
2257 # changed files
2256
2258
2257 if follow:
2259 if follow:
2258 raise error.Abort(_('can only follow copies/renames for explicit '
2260 raise error.Abort(_('can only follow copies/renames for explicit '
2259 'filenames'))
2261 'filenames'))
2260
2262
2261 # The slow path checks files modified in every changeset.
2263 # The slow path checks files modified in every changeset.
2262 # This is really slow on large repos, so compute the set lazily.
2264 # This is really slow on large repos, so compute the set lazily.
2263 class lazywantedset(object):
2265 class lazywantedset(object):
2264 def __init__(self):
2266 def __init__(self):
2265 self.set = set()
2267 self.set = set()
2266 self.revs = set(revs)
2268 self.revs = set(revs)
2267
2269
2268 # No need to worry about locality here because it will be accessed
2270 # No need to worry about locality here because it will be accessed
2269 # in the same order as the increasing window below.
2271 # in the same order as the increasing window below.
2270 def __contains__(self, value):
2272 def __contains__(self, value):
2271 if value in self.set:
2273 if value in self.set:
2272 return True
2274 return True
2273 elif not value in self.revs:
2275 elif not value in self.revs:
2274 return False
2276 return False
2275 else:
2277 else:
2276 self.revs.discard(value)
2278 self.revs.discard(value)
2277 ctx = change(value)
2279 ctx = change(value)
2278 matches = filter(match, ctx.files())
2280 matches = filter(match, ctx.files())
2279 if matches:
2281 if matches:
2280 fncache[value] = matches
2282 fncache[value] = matches
2281 self.set.add(value)
2283 self.set.add(value)
2282 return True
2284 return True
2283 return False
2285 return False
2284
2286
2285 def discard(self, value):
2287 def discard(self, value):
2286 self.revs.discard(value)
2288 self.revs.discard(value)
2287 self.set.discard(value)
2289 self.set.discard(value)
2288
2290
2289 wanted = lazywantedset()
2291 wanted = lazywantedset()
2290
2292
2291 # it might be worthwhile to do this in the iterator if the rev range
2293 # it might be worthwhile to do this in the iterator if the rev range
2292 # is descending and the prune args are all within that range
2294 # is descending and the prune args are all within that range
2293 for rev in opts.get('prune', ()):
2295 for rev in opts.get('prune', ()):
2294 rev = repo[rev].rev()
2296 rev = repo[rev].rev()
2295 ff = _followfilter(repo)
2297 ff = _followfilter(repo)
2296 stop = min(revs[0], revs[-1])
2298 stop = min(revs[0], revs[-1])
2297 for x in xrange(rev, stop - 1, -1):
2299 for x in xrange(rev, stop - 1, -1):
2298 if ff.match(x):
2300 if ff.match(x):
2299 wanted = wanted - [x]
2301 wanted = wanted - [x]
2300
2302
2301 # Now that wanted is correctly initialized, we can iterate over the
2303 # Now that wanted is correctly initialized, we can iterate over the
2302 # revision range, yielding only revisions in wanted.
2304 # revision range, yielding only revisions in wanted.
2303 def iterate():
2305 def iterate():
2304 if follow and match.always():
2306 if follow and match.always():
2305 ff = _followfilter(repo, onlyfirst=opts.get('follow_first'))
2307 ff = _followfilter(repo, onlyfirst=opts.get('follow_first'))
2306 def want(rev):
2308 def want(rev):
2307 return ff.match(rev) and rev in wanted
2309 return ff.match(rev) and rev in wanted
2308 else:
2310 else:
2309 def want(rev):
2311 def want(rev):
2310 return rev in wanted
2312 return rev in wanted
2311
2313
2312 it = iter(revs)
2314 it = iter(revs)
2313 stopiteration = False
2315 stopiteration = False
2314 for windowsize in increasingwindows():
2316 for windowsize in increasingwindows():
2315 nrevs = []
2317 nrevs = []
2316 for i in xrange(windowsize):
2318 for i in xrange(windowsize):
2317 rev = next(it, None)
2319 rev = next(it, None)
2318 if rev is None:
2320 if rev is None:
2319 stopiteration = True
2321 stopiteration = True
2320 break
2322 break
2321 elif want(rev):
2323 elif want(rev):
2322 nrevs.append(rev)
2324 nrevs.append(rev)
2323 for rev in sorted(nrevs):
2325 for rev in sorted(nrevs):
2324 fns = fncache.get(rev)
2326 fns = fncache.get(rev)
2325 ctx = change(rev)
2327 ctx = change(rev)
2326 if not fns:
2328 if not fns:
2327 def fns_generator():
2329 def fns_generator():
2328 for f in ctx.files():
2330 for f in ctx.files():
2329 if match(f):
2331 if match(f):
2330 yield f
2332 yield f
2331 fns = fns_generator()
2333 fns = fns_generator()
2332 prepare(ctx, fns)
2334 prepare(ctx, fns)
2333 for rev in nrevs:
2335 for rev in nrevs:
2334 yield change(rev)
2336 yield change(rev)
2335
2337
2336 if stopiteration:
2338 if stopiteration:
2337 break
2339 break
2338
2340
2339 return iterate()
2341 return iterate()
2340
2342
2341 def _makefollowlogfilematcher(repo, files, followfirst):
2343 def _makefollowlogfilematcher(repo, files, followfirst):
2342 # When displaying a revision with --patch --follow FILE, we have
2344 # When displaying a revision with --patch --follow FILE, we have
2343 # to know which file of the revision must be diffed. With
2345 # to know which file of the revision must be diffed. With
2344 # --follow, we want the names of the ancestors of FILE in the
2346 # --follow, we want the names of the ancestors of FILE in the
2345 # revision, stored in "fcache". "fcache" is populated by
2347 # revision, stored in "fcache". "fcache" is populated by
2346 # reproducing the graph traversal already done by --follow revset
2348 # reproducing the graph traversal already done by --follow revset
2347 # and relating revs to file names (which is not "correct" but
2349 # and relating revs to file names (which is not "correct" but
2348 # good enough).
2350 # good enough).
2349 fcache = {}
2351 fcache = {}
2350 fcacheready = [False]
2352 fcacheready = [False]
2351 pctx = repo['.']
2353 pctx = repo['.']
2352
2354
2353 def populate():
2355 def populate():
2354 for fn in files:
2356 for fn in files:
2355 fctx = pctx[fn]
2357 fctx = pctx[fn]
2356 fcache.setdefault(fctx.introrev(), set()).add(fctx.path())
2358 fcache.setdefault(fctx.introrev(), set()).add(fctx.path())
2357 for c in fctx.ancestors(followfirst=followfirst):
2359 for c in fctx.ancestors(followfirst=followfirst):
2358 fcache.setdefault(c.rev(), set()).add(c.path())
2360 fcache.setdefault(c.rev(), set()).add(c.path())
2359
2361
2360 def filematcher(rev):
2362 def filematcher(rev):
2361 if not fcacheready[0]:
2363 if not fcacheready[0]:
2362 # Lazy initialization
2364 # Lazy initialization
2363 fcacheready[0] = True
2365 fcacheready[0] = True
2364 populate()
2366 populate()
2365 return scmutil.matchfiles(repo, fcache.get(rev, []))
2367 return scmutil.matchfiles(repo, fcache.get(rev, []))
2366
2368
2367 return filematcher
2369 return filematcher
2368
2370
2369 def _makenofollowlogfilematcher(repo, pats, opts):
2371 def _makenofollowlogfilematcher(repo, pats, opts):
2370 '''hook for extensions to override the filematcher for non-follow cases'''
2372 '''hook for extensions to override the filematcher for non-follow cases'''
2371 return None
2373 return None
2372
2374
2373 def _makelogrevset(repo, pats, opts, revs):
2375 def _makelogrevset(repo, pats, opts, revs):
2374 """Return (expr, filematcher) where expr is a revset string built
2376 """Return (expr, filematcher) where expr is a revset string built
2375 from log options and file patterns or None. If --stat or --patch
2377 from log options and file patterns or None. If --stat or --patch
2376 are not passed filematcher is None. Otherwise it is a callable
2378 are not passed filematcher is None. Otherwise it is a callable
2377 taking a revision number and returning a match objects filtering
2379 taking a revision number and returning a match objects filtering
2378 the files to be detailed when displaying the revision.
2380 the files to be detailed when displaying the revision.
2379 """
2381 """
2380 opt2revset = {
2382 opt2revset = {
2381 'no_merges': ('not merge()', None),
2383 'no_merges': ('not merge()', None),
2382 'only_merges': ('merge()', None),
2384 'only_merges': ('merge()', None),
2383 '_ancestors': ('ancestors(%(val)s)', None),
2385 '_ancestors': ('ancestors(%(val)s)', None),
2384 '_fancestors': ('_firstancestors(%(val)s)', None),
2386 '_fancestors': ('_firstancestors(%(val)s)', None),
2385 '_descendants': ('descendants(%(val)s)', None),
2387 '_descendants': ('descendants(%(val)s)', None),
2386 '_fdescendants': ('_firstdescendants(%(val)s)', None),
2388 '_fdescendants': ('_firstdescendants(%(val)s)', None),
2387 '_matchfiles': ('_matchfiles(%(val)s)', None),
2389 '_matchfiles': ('_matchfiles(%(val)s)', None),
2388 'date': ('date(%(val)r)', None),
2390 'date': ('date(%(val)r)', None),
2389 'branch': ('branch(%(val)r)', ' or '),
2391 'branch': ('branch(%(val)r)', ' or '),
2390 '_patslog': ('filelog(%(val)r)', ' or '),
2392 '_patslog': ('filelog(%(val)r)', ' or '),
2391 '_patsfollow': ('follow(%(val)r)', ' or '),
2393 '_patsfollow': ('follow(%(val)r)', ' or '),
2392 '_patsfollowfirst': ('_followfirst(%(val)r)', ' or '),
2394 '_patsfollowfirst': ('_followfirst(%(val)r)', ' or '),
2393 'keyword': ('keyword(%(val)r)', ' or '),
2395 'keyword': ('keyword(%(val)r)', ' or '),
2394 'prune': ('not (%(val)r or ancestors(%(val)r))', ' and '),
2396 'prune': ('not (%(val)r or ancestors(%(val)r))', ' and '),
2395 'user': ('user(%(val)r)', ' or '),
2397 'user': ('user(%(val)r)', ' or '),
2396 }
2398 }
2397
2399
2398 opts = dict(opts)
2400 opts = dict(opts)
2399 # follow or not follow?
2401 # follow or not follow?
2400 follow = opts.get('follow') or opts.get('follow_first')
2402 follow = opts.get('follow') or opts.get('follow_first')
2401 if opts.get('follow_first'):
2403 if opts.get('follow_first'):
2402 followfirst = 1
2404 followfirst = 1
2403 else:
2405 else:
2404 followfirst = 0
2406 followfirst = 0
2405 # --follow with FILE behavior depends on revs...
2407 # --follow with FILE behavior depends on revs...
2406 it = iter(revs)
2408 it = iter(revs)
2407 startrev = next(it)
2409 startrev = next(it)
2408 followdescendants = startrev < next(it, startrev)
2410 followdescendants = startrev < next(it, startrev)
2409
2411
2410 # branch and only_branch are really aliases and must be handled at
2412 # branch and only_branch are really aliases and must be handled at
2411 # the same time
2413 # the same time
2412 opts['branch'] = opts.get('branch', []) + opts.get('only_branch', [])
2414 opts['branch'] = opts.get('branch', []) + opts.get('only_branch', [])
2413 opts['branch'] = [repo.lookupbranch(b) for b in opts['branch']]
2415 opts['branch'] = [repo.lookupbranch(b) for b in opts['branch']]
2414 # pats/include/exclude are passed to match.match() directly in
2416 # pats/include/exclude are passed to match.match() directly in
2415 # _matchfiles() revset but walkchangerevs() builds its matcher with
2417 # _matchfiles() revset but walkchangerevs() builds its matcher with
2416 # scmutil.match(). The difference is input pats are globbed on
2418 # scmutil.match(). The difference is input pats are globbed on
2417 # platforms without shell expansion (windows).
2419 # platforms without shell expansion (windows).
2418 wctx = repo[None]
2420 wctx = repo[None]
2419 match, pats = scmutil.matchandpats(wctx, pats, opts)
2421 match, pats = scmutil.matchandpats(wctx, pats, opts)
2420 slowpath = match.anypats() or ((match.isexact() or match.prefix()) and
2422 slowpath = match.anypats() or ((match.isexact() or match.prefix()) and
2421 opts.get('removed'))
2423 opts.get('removed'))
2422 if not slowpath:
2424 if not slowpath:
2423 for f in match.files():
2425 for f in match.files():
2424 if follow and f not in wctx:
2426 if follow and f not in wctx:
2425 # If the file exists, it may be a directory, so let it
2427 # If the file exists, it may be a directory, so let it
2426 # take the slow path.
2428 # take the slow path.
2427 if os.path.exists(repo.wjoin(f)):
2429 if os.path.exists(repo.wjoin(f)):
2428 slowpath = True
2430 slowpath = True
2429 continue
2431 continue
2430 else:
2432 else:
2431 raise error.Abort(_('cannot follow file not in parent '
2433 raise error.Abort(_('cannot follow file not in parent '
2432 'revision: "%s"') % f)
2434 'revision: "%s"') % f)
2433 filelog = repo.file(f)
2435 filelog = repo.file(f)
2434 if not filelog:
2436 if not filelog:
2435 # A zero count may be a directory or deleted file, so
2437 # A zero count may be a directory or deleted file, so
2436 # try to find matching entries on the slow path.
2438 # try to find matching entries on the slow path.
2437 if follow:
2439 if follow:
2438 raise error.Abort(
2440 raise error.Abort(
2439 _('cannot follow nonexistent file: "%s"') % f)
2441 _('cannot follow nonexistent file: "%s"') % f)
2440 slowpath = True
2442 slowpath = True
2441
2443
2442 # We decided to fall back to the slowpath because at least one
2444 # We decided to fall back to the slowpath because at least one
2443 # of the paths was not a file. Check to see if at least one of them
2445 # of the paths was not a file. Check to see if at least one of them
2444 # existed in history - in that case, we'll continue down the
2446 # existed in history - in that case, we'll continue down the
2445 # slowpath; otherwise, we can turn off the slowpath
2447 # slowpath; otherwise, we can turn off the slowpath
2446 if slowpath:
2448 if slowpath:
2447 for path in match.files():
2449 for path in match.files():
2448 if path == '.' or path in repo.store:
2450 if path == '.' or path in repo.store:
2449 break
2451 break
2450 else:
2452 else:
2451 slowpath = False
2453 slowpath = False
2452
2454
2453 fpats = ('_patsfollow', '_patsfollowfirst')
2455 fpats = ('_patsfollow', '_patsfollowfirst')
2454 fnopats = (('_ancestors', '_fancestors'),
2456 fnopats = (('_ancestors', '_fancestors'),
2455 ('_descendants', '_fdescendants'))
2457 ('_descendants', '_fdescendants'))
2456 if slowpath:
2458 if slowpath:
2457 # See walkchangerevs() slow path.
2459 # See walkchangerevs() slow path.
2458 #
2460 #
2459 # pats/include/exclude cannot be represented as separate
2461 # pats/include/exclude cannot be represented as separate
2460 # revset expressions as their filtering logic applies at file
2462 # revset expressions as their filtering logic applies at file
2461 # level. For instance "-I a -X a" matches a revision touching
2463 # level. For instance "-I a -X a" matches a revision touching
2462 # "a" and "b" while "file(a) and not file(b)" does
2464 # "a" and "b" while "file(a) and not file(b)" does
2463 # not. Besides, filesets are evaluated against the working
2465 # not. Besides, filesets are evaluated against the working
2464 # directory.
2466 # directory.
2465 matchargs = ['r:', 'd:relpath']
2467 matchargs = ['r:', 'd:relpath']
2466 for p in pats:
2468 for p in pats:
2467 matchargs.append('p:' + p)
2469 matchargs.append('p:' + p)
2468 for p in opts.get('include', []):
2470 for p in opts.get('include', []):
2469 matchargs.append('i:' + p)
2471 matchargs.append('i:' + p)
2470 for p in opts.get('exclude', []):
2472 for p in opts.get('exclude', []):
2471 matchargs.append('x:' + p)
2473 matchargs.append('x:' + p)
2472 matchargs = ','.join(('%r' % p) for p in matchargs)
2474 matchargs = ','.join(('%r' % p) for p in matchargs)
2473 opts['_matchfiles'] = matchargs
2475 opts['_matchfiles'] = matchargs
2474 if follow:
2476 if follow:
2475 opts[fnopats[0][followfirst]] = '.'
2477 opts[fnopats[0][followfirst]] = '.'
2476 else:
2478 else:
2477 if follow:
2479 if follow:
2478 if pats:
2480 if pats:
2479 # follow() revset interprets its file argument as a
2481 # follow() revset interprets its file argument as a
2480 # manifest entry, so use match.files(), not pats.
2482 # manifest entry, so use match.files(), not pats.
2481 opts[fpats[followfirst]] = list(match.files())
2483 opts[fpats[followfirst]] = list(match.files())
2482 else:
2484 else:
2483 op = fnopats[followdescendants][followfirst]
2485 op = fnopats[followdescendants][followfirst]
2484 opts[op] = 'rev(%d)' % startrev
2486 opts[op] = 'rev(%d)' % startrev
2485 else:
2487 else:
2486 opts['_patslog'] = list(pats)
2488 opts['_patslog'] = list(pats)
2487
2489
2488 filematcher = None
2490 filematcher = None
2489 if opts.get('patch') or opts.get('stat'):
2491 if opts.get('patch') or opts.get('stat'):
2490 # When following files, track renames via a special matcher.
2492 # When following files, track renames via a special matcher.
2491 # If we're forced to take the slowpath it means we're following
2493 # If we're forced to take the slowpath it means we're following
2492 # at least one pattern/directory, so don't bother with rename tracking.
2494 # at least one pattern/directory, so don't bother with rename tracking.
2493 if follow and not match.always() and not slowpath:
2495 if follow and not match.always() and not slowpath:
2494 # _makefollowlogfilematcher expects its files argument to be
2496 # _makefollowlogfilematcher expects its files argument to be
2495 # relative to the repo root, so use match.files(), not pats.
2497 # relative to the repo root, so use match.files(), not pats.
2496 filematcher = _makefollowlogfilematcher(repo, match.files(),
2498 filematcher = _makefollowlogfilematcher(repo, match.files(),
2497 followfirst)
2499 followfirst)
2498 else:
2500 else:
2499 filematcher = _makenofollowlogfilematcher(repo, pats, opts)
2501 filematcher = _makenofollowlogfilematcher(repo, pats, opts)
2500 if filematcher is None:
2502 if filematcher is None:
2501 filematcher = lambda rev: match
2503 filematcher = lambda rev: match
2502
2504
2503 expr = []
2505 expr = []
2504 for op, val in sorted(opts.iteritems()):
2506 for op, val in sorted(opts.iteritems()):
2505 if not val:
2507 if not val:
2506 continue
2508 continue
2507 if op not in opt2revset:
2509 if op not in opt2revset:
2508 continue
2510 continue
2509 revop, andor = opt2revset[op]
2511 revop, andor = opt2revset[op]
2510 if '%(val)' not in revop:
2512 if '%(val)' not in revop:
2511 expr.append(revop)
2513 expr.append(revop)
2512 else:
2514 else:
2513 if not isinstance(val, list):
2515 if not isinstance(val, list):
2514 e = revop % {'val': val}
2516 e = revop % {'val': val}
2515 else:
2517 else:
2516 e = '(' + andor.join((revop % {'val': v}) for v in val) + ')'
2518 e = '(' + andor.join((revop % {'val': v}) for v in val) + ')'
2517 expr.append(e)
2519 expr.append(e)
2518
2520
2519 if expr:
2521 if expr:
2520 expr = '(' + ' and '.join(expr) + ')'
2522 expr = '(' + ' and '.join(expr) + ')'
2521 else:
2523 else:
2522 expr = None
2524 expr = None
2523 return expr, filematcher
2525 return expr, filematcher
2524
2526
2525 def _logrevs(repo, opts):
2527 def _logrevs(repo, opts):
2526 # Default --rev value depends on --follow but --follow behavior
2528 # Default --rev value depends on --follow but --follow behavior
2527 # depends on revisions resolved from --rev...
2529 # depends on revisions resolved from --rev...
2528 follow = opts.get('follow') or opts.get('follow_first')
2530 follow = opts.get('follow') or opts.get('follow_first')
2529 if opts.get('rev'):
2531 if opts.get('rev'):
2530 revs = scmutil.revrange(repo, opts['rev'])
2532 revs = scmutil.revrange(repo, opts['rev'])
2531 elif follow and repo.dirstate.p1() == nullid:
2533 elif follow and repo.dirstate.p1() == nullid:
2532 revs = smartset.baseset()
2534 revs = smartset.baseset()
2533 elif follow:
2535 elif follow:
2534 revs = repo.revs('reverse(:.)')
2536 revs = repo.revs('reverse(:.)')
2535 else:
2537 else:
2536 revs = smartset.spanset(repo)
2538 revs = smartset.spanset(repo)
2537 revs.reverse()
2539 revs.reverse()
2538 return revs
2540 return revs
2539
2541
2540 def getgraphlogrevs(repo, pats, opts):
2542 def getgraphlogrevs(repo, pats, opts):
2541 """Return (revs, expr, filematcher) where revs is an iterable of
2543 """Return (revs, expr, filematcher) where revs is an iterable of
2542 revision numbers, expr is a revset string built from log options
2544 revision numbers, expr is a revset string built from log options
2543 and file patterns or None, and used to filter 'revs'. If --stat or
2545 and file patterns or None, and used to filter 'revs'. If --stat or
2544 --patch are not passed filematcher is None. Otherwise it is a
2546 --patch are not passed filematcher is None. Otherwise it is a
2545 callable taking a revision number and returning a match objects
2547 callable taking a revision number and returning a match objects
2546 filtering the files to be detailed when displaying the revision.
2548 filtering the files to be detailed when displaying the revision.
2547 """
2549 """
2548 limit = loglimit(opts)
2550 limit = loglimit(opts)
2549 revs = _logrevs(repo, opts)
2551 revs = _logrevs(repo, opts)
2550 if not revs:
2552 if not revs:
2551 return smartset.baseset(), None, None
2553 return smartset.baseset(), None, None
2552 expr, filematcher = _makelogrevset(repo, pats, opts, revs)
2554 expr, filematcher = _makelogrevset(repo, pats, opts, revs)
2553 if opts.get('rev'):
2555 if opts.get('rev'):
2554 # User-specified revs might be unsorted, but don't sort before
2556 # User-specified revs might be unsorted, but don't sort before
2555 # _makelogrevset because it might depend on the order of revs
2557 # _makelogrevset because it might depend on the order of revs
2556 if not (revs.isdescending() or revs.istopo()):
2558 if not (revs.isdescending() or revs.istopo()):
2557 revs.sort(reverse=True)
2559 revs.sort(reverse=True)
2558 if expr:
2560 if expr:
2559 matcher = revset.match(repo.ui, expr, order=revset.followorder)
2561 matcher = revset.match(repo.ui, expr, order=revset.followorder)
2560 revs = matcher(repo, revs)
2562 revs = matcher(repo, revs)
2561 if limit is not None:
2563 if limit is not None:
2562 limitedrevs = []
2564 limitedrevs = []
2563 for idx, rev in enumerate(revs):
2565 for idx, rev in enumerate(revs):
2564 if idx >= limit:
2566 if idx >= limit:
2565 break
2567 break
2566 limitedrevs.append(rev)
2568 limitedrevs.append(rev)
2567 revs = smartset.baseset(limitedrevs)
2569 revs = smartset.baseset(limitedrevs)
2568
2570
2569 return revs, expr, filematcher
2571 return revs, expr, filematcher
2570
2572
2571 def getlogrevs(repo, pats, opts):
2573 def getlogrevs(repo, pats, opts):
2572 """Return (revs, expr, filematcher) where revs is an iterable of
2574 """Return (revs, expr, filematcher) where revs is an iterable of
2573 revision numbers, expr is a revset string built from log options
2575 revision numbers, expr is a revset string built from log options
2574 and file patterns or None, and used to filter 'revs'. If --stat or
2576 and file patterns or None, and used to filter 'revs'. If --stat or
2575 --patch are not passed filematcher is None. Otherwise it is a
2577 --patch are not passed filematcher is None. Otherwise it is a
2576 callable taking a revision number and returning a match objects
2578 callable taking a revision number and returning a match objects
2577 filtering the files to be detailed when displaying the revision.
2579 filtering the files to be detailed when displaying the revision.
2578 """
2580 """
2579 limit = loglimit(opts)
2581 limit = loglimit(opts)
2580 revs = _logrevs(repo, opts)
2582 revs = _logrevs(repo, opts)
2581 if not revs:
2583 if not revs:
2582 return smartset.baseset([]), None, None
2584 return smartset.baseset([]), None, None
2583 expr, filematcher = _makelogrevset(repo, pats, opts, revs)
2585 expr, filematcher = _makelogrevset(repo, pats, opts, revs)
2584 if expr:
2586 if expr:
2585 matcher = revset.match(repo.ui, expr, order=revset.followorder)
2587 matcher = revset.match(repo.ui, expr, order=revset.followorder)
2586 revs = matcher(repo, revs)
2588 revs = matcher(repo, revs)
2587 if limit is not None:
2589 if limit is not None:
2588 limitedrevs = []
2590 limitedrevs = []
2589 for idx, r in enumerate(revs):
2591 for idx, r in enumerate(revs):
2590 if limit <= idx:
2592 if limit <= idx:
2591 break
2593 break
2592 limitedrevs.append(r)
2594 limitedrevs.append(r)
2593 revs = smartset.baseset(limitedrevs)
2595 revs = smartset.baseset(limitedrevs)
2594
2596
2595 return revs, expr, filematcher
2597 return revs, expr, filematcher
2596
2598
2597 def _graphnodeformatter(ui, displayer):
2599 def _graphnodeformatter(ui, displayer):
2598 spec = ui.config('ui', 'graphnodetemplate')
2600 spec = ui.config('ui', 'graphnodetemplate')
2599 if not spec:
2601 if not spec:
2600 return templatekw.showgraphnode # fast path for "{graphnode}"
2602 return templatekw.showgraphnode # fast path for "{graphnode}"
2601
2603
2602 spec = templater.unquotestring(spec)
2604 spec = templater.unquotestring(spec)
2603 templ = formatter.maketemplater(ui, spec)
2605 templ = formatter.maketemplater(ui, spec)
2604 cache = {}
2606 cache = {}
2605 if isinstance(displayer, changeset_templater):
2607 if isinstance(displayer, changeset_templater):
2606 cache = displayer.cache # reuse cache of slow templates
2608 cache = displayer.cache # reuse cache of slow templates
2607 props = templatekw.keywords.copy()
2609 props = templatekw.keywords.copy()
2608 props['templ'] = templ
2610 props['templ'] = templ
2609 props['cache'] = cache
2611 props['cache'] = cache
2610 def formatnode(repo, ctx):
2612 def formatnode(repo, ctx):
2611 props['ctx'] = ctx
2613 props['ctx'] = ctx
2612 props['repo'] = repo
2614 props['repo'] = repo
2613 props['ui'] = repo.ui
2615 props['ui'] = repo.ui
2614 props['revcache'] = {}
2616 props['revcache'] = {}
2615 return templ.render(props)
2617 return templ.render(props)
2616 return formatnode
2618 return formatnode
2617
2619
2618 def displaygraph(ui, repo, dag, displayer, edgefn, getrenamed=None,
2620 def displaygraph(ui, repo, dag, displayer, edgefn, getrenamed=None,
2619 filematcher=None):
2621 filematcher=None):
2620 formatnode = _graphnodeformatter(ui, displayer)
2622 formatnode = _graphnodeformatter(ui, displayer)
2621 state = graphmod.asciistate()
2623 state = graphmod.asciistate()
2622 styles = state['styles']
2624 styles = state['styles']
2623
2625
2624 # only set graph styling if HGPLAIN is not set.
2626 # only set graph styling if HGPLAIN is not set.
2625 if ui.plain('graph'):
2627 if ui.plain('graph'):
2626 # set all edge styles to |, the default pre-3.8 behaviour
2628 # set all edge styles to |, the default pre-3.8 behaviour
2627 styles.update(dict.fromkeys(styles, '|'))
2629 styles.update(dict.fromkeys(styles, '|'))
2628 else:
2630 else:
2629 edgetypes = {
2631 edgetypes = {
2630 'parent': graphmod.PARENT,
2632 'parent': graphmod.PARENT,
2631 'grandparent': graphmod.GRANDPARENT,
2633 'grandparent': graphmod.GRANDPARENT,
2632 'missing': graphmod.MISSINGPARENT
2634 'missing': graphmod.MISSINGPARENT
2633 }
2635 }
2634 for name, key in edgetypes.items():
2636 for name, key in edgetypes.items():
2635 # experimental config: experimental.graphstyle.*
2637 # experimental config: experimental.graphstyle.*
2636 styles[key] = ui.config('experimental', 'graphstyle.%s' % name,
2638 styles[key] = ui.config('experimental', 'graphstyle.%s' % name,
2637 styles[key])
2639 styles[key])
2638 if not styles[key]:
2640 if not styles[key]:
2639 styles[key] = None
2641 styles[key] = None
2640
2642
2641 # experimental config: experimental.graphshorten
2643 # experimental config: experimental.graphshorten
2642 state['graphshorten'] = ui.configbool('experimental', 'graphshorten')
2644 state['graphshorten'] = ui.configbool('experimental', 'graphshorten')
2643
2645
2644 for rev, type, ctx, parents in dag:
2646 for rev, type, ctx, parents in dag:
2645 char = formatnode(repo, ctx)
2647 char = formatnode(repo, ctx)
2646 copies = None
2648 copies = None
2647 if getrenamed and ctx.rev():
2649 if getrenamed and ctx.rev():
2648 copies = []
2650 copies = []
2649 for fn in ctx.files():
2651 for fn in ctx.files():
2650 rename = getrenamed(fn, ctx.rev())
2652 rename = getrenamed(fn, ctx.rev())
2651 if rename:
2653 if rename:
2652 copies.append((fn, rename[0]))
2654 copies.append((fn, rename[0]))
2653 revmatchfn = None
2655 revmatchfn = None
2654 if filematcher is not None:
2656 if filematcher is not None:
2655 revmatchfn = filematcher(ctx.rev())
2657 revmatchfn = filematcher(ctx.rev())
2656 edges = edgefn(type, char, state, rev, parents)
2658 edges = edgefn(type, char, state, rev, parents)
2657 firstedge = next(edges)
2659 firstedge = next(edges)
2658 width = firstedge[2]
2660 width = firstedge[2]
2659 displayer.show(ctx, copies=copies, matchfn=revmatchfn,
2661 displayer.show(ctx, copies=copies, matchfn=revmatchfn,
2660 _graphwidth=width)
2662 _graphwidth=width)
2661 lines = displayer.hunk.pop(rev).split('\n')
2663 lines = displayer.hunk.pop(rev).split('\n')
2662 if not lines[-1]:
2664 if not lines[-1]:
2663 del lines[-1]
2665 del lines[-1]
2664 displayer.flush(ctx)
2666 displayer.flush(ctx)
2665 for type, char, width, coldata in itertools.chain([firstedge], edges):
2667 for type, char, width, coldata in itertools.chain([firstedge], edges):
2666 graphmod.ascii(ui, state, type, char, lines, coldata)
2668 graphmod.ascii(ui, state, type, char, lines, coldata)
2667 lines = []
2669 lines = []
2668 displayer.close()
2670 displayer.close()
2669
2671
2670 def graphlog(ui, repo, pats, opts):
2672 def graphlog(ui, repo, pats, opts):
2671 # Parameters are identical to log command ones
2673 # Parameters are identical to log command ones
2672 revs, expr, filematcher = getgraphlogrevs(repo, pats, opts)
2674 revs, expr, filematcher = getgraphlogrevs(repo, pats, opts)
2673 revdag = graphmod.dagwalker(repo, revs)
2675 revdag = graphmod.dagwalker(repo, revs)
2674
2676
2675 getrenamed = None
2677 getrenamed = None
2676 if opts.get('copies'):
2678 if opts.get('copies'):
2677 endrev = None
2679 endrev = None
2678 if opts.get('rev'):
2680 if opts.get('rev'):
2679 endrev = scmutil.revrange(repo, opts.get('rev')).max() + 1
2681 endrev = scmutil.revrange(repo, opts.get('rev')).max() + 1
2680 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
2682 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
2681
2683
2682 ui.pager('log')
2684 ui.pager('log')
2683 displayer = show_changeset(ui, repo, opts, buffered=True)
2685 displayer = show_changeset(ui, repo, opts, buffered=True)
2684 displaygraph(ui, repo, revdag, displayer, graphmod.asciiedges, getrenamed,
2686 displaygraph(ui, repo, revdag, displayer, graphmod.asciiedges, getrenamed,
2685 filematcher)
2687 filematcher)
2686
2688
2687 def checkunsupportedgraphflags(pats, opts):
2689 def checkunsupportedgraphflags(pats, opts):
2688 for op in ["newest_first"]:
2690 for op in ["newest_first"]:
2689 if op in opts and opts[op]:
2691 if op in opts and opts[op]:
2690 raise error.Abort(_("-G/--graph option is incompatible with --%s")
2692 raise error.Abort(_("-G/--graph option is incompatible with --%s")
2691 % op.replace("_", "-"))
2693 % op.replace("_", "-"))
2692
2694
2693 def graphrevs(repo, nodes, opts):
2695 def graphrevs(repo, nodes, opts):
2694 limit = loglimit(opts)
2696 limit = loglimit(opts)
2695 nodes.reverse()
2697 nodes.reverse()
2696 if limit is not None:
2698 if limit is not None:
2697 nodes = nodes[:limit]
2699 nodes = nodes[:limit]
2698 return graphmod.nodes(repo, nodes)
2700 return graphmod.nodes(repo, nodes)
2699
2701
2700 def add(ui, repo, match, prefix, explicitonly, **opts):
2702 def add(ui, repo, match, prefix, explicitonly, **opts):
2701 join = lambda f: os.path.join(prefix, f)
2703 join = lambda f: os.path.join(prefix, f)
2702 bad = []
2704 bad = []
2703
2705
2704 badfn = lambda x, y: bad.append(x) or match.bad(x, y)
2706 badfn = lambda x, y: bad.append(x) or match.bad(x, y)
2705 names = []
2707 names = []
2706 wctx = repo[None]
2708 wctx = repo[None]
2707 cca = None
2709 cca = None
2708 abort, warn = scmutil.checkportabilityalert(ui)
2710 abort, warn = scmutil.checkportabilityalert(ui)
2709 if abort or warn:
2711 if abort or warn:
2710 cca = scmutil.casecollisionauditor(ui, abort, repo.dirstate)
2712 cca = scmutil.casecollisionauditor(ui, abort, repo.dirstate)
2711
2713
2712 badmatch = matchmod.badmatch(match, badfn)
2714 badmatch = matchmod.badmatch(match, badfn)
2713 dirstate = repo.dirstate
2715 dirstate = repo.dirstate
2714 # We don't want to just call wctx.walk here, since it would return a lot of
2716 # We don't want to just call wctx.walk here, since it would return a lot of
2715 # clean files, which we aren't interested in and takes time.
2717 # clean files, which we aren't interested in and takes time.
2716 for f in sorted(dirstate.walk(badmatch, sorted(wctx.substate),
2718 for f in sorted(dirstate.walk(badmatch, sorted(wctx.substate),
2717 True, False, full=False)):
2719 True, False, full=False)):
2718 exact = match.exact(f)
2720 exact = match.exact(f)
2719 if exact or not explicitonly and f not in wctx and repo.wvfs.lexists(f):
2721 if exact or not explicitonly and f not in wctx and repo.wvfs.lexists(f):
2720 if cca:
2722 if cca:
2721 cca(f)
2723 cca(f)
2722 names.append(f)
2724 names.append(f)
2723 if ui.verbose or not exact:
2725 if ui.verbose or not exact:
2724 ui.status(_('adding %s\n') % match.rel(f))
2726 ui.status(_('adding %s\n') % match.rel(f))
2725
2727
2726 for subpath in sorted(wctx.substate):
2728 for subpath in sorted(wctx.substate):
2727 sub = wctx.sub(subpath)
2729 sub = wctx.sub(subpath)
2728 try:
2730 try:
2729 submatch = matchmod.subdirmatcher(subpath, match)
2731 submatch = matchmod.subdirmatcher(subpath, match)
2730 if opts.get(r'subrepos'):
2732 if opts.get(r'subrepos'):
2731 bad.extend(sub.add(ui, submatch, prefix, False, **opts))
2733 bad.extend(sub.add(ui, submatch, prefix, False, **opts))
2732 else:
2734 else:
2733 bad.extend(sub.add(ui, submatch, prefix, True, **opts))
2735 bad.extend(sub.add(ui, submatch, prefix, True, **opts))
2734 except error.LookupError:
2736 except error.LookupError:
2735 ui.status(_("skipping missing subrepository: %s\n")
2737 ui.status(_("skipping missing subrepository: %s\n")
2736 % join(subpath))
2738 % join(subpath))
2737
2739
2738 if not opts.get(r'dry_run'):
2740 if not opts.get(r'dry_run'):
2739 rejected = wctx.add(names, prefix)
2741 rejected = wctx.add(names, prefix)
2740 bad.extend(f for f in rejected if f in match.files())
2742 bad.extend(f for f in rejected if f in match.files())
2741 return bad
2743 return bad
2742
2744
2743 def addwebdirpath(repo, serverpath, webconf):
2745 def addwebdirpath(repo, serverpath, webconf):
2744 webconf[serverpath] = repo.root
2746 webconf[serverpath] = repo.root
2745 repo.ui.debug('adding %s = %s\n' % (serverpath, repo.root))
2747 repo.ui.debug('adding %s = %s\n' % (serverpath, repo.root))
2746
2748
2747 for r in repo.revs('filelog("path:.hgsub")'):
2749 for r in repo.revs('filelog("path:.hgsub")'):
2748 ctx = repo[r]
2750 ctx = repo[r]
2749 for subpath in ctx.substate:
2751 for subpath in ctx.substate:
2750 ctx.sub(subpath).addwebdirpath(serverpath, webconf)
2752 ctx.sub(subpath).addwebdirpath(serverpath, webconf)
2751
2753
2752 def forget(ui, repo, match, prefix, explicitonly):
2754 def forget(ui, repo, match, prefix, explicitonly):
2753 join = lambda f: os.path.join(prefix, f)
2755 join = lambda f: os.path.join(prefix, f)
2754 bad = []
2756 bad = []
2755 badfn = lambda x, y: bad.append(x) or match.bad(x, y)
2757 badfn = lambda x, y: bad.append(x) or match.bad(x, y)
2756 wctx = repo[None]
2758 wctx = repo[None]
2757 forgot = []
2759 forgot = []
2758
2760
2759 s = repo.status(match=matchmod.badmatch(match, badfn), clean=True)
2761 s = repo.status(match=matchmod.badmatch(match, badfn), clean=True)
2760 forget = sorted(s.modified + s.added + s.deleted + s.clean)
2762 forget = sorted(s.modified + s.added + s.deleted + s.clean)
2761 if explicitonly:
2763 if explicitonly:
2762 forget = [f for f in forget if match.exact(f)]
2764 forget = [f for f in forget if match.exact(f)]
2763
2765
2764 for subpath in sorted(wctx.substate):
2766 for subpath in sorted(wctx.substate):
2765 sub = wctx.sub(subpath)
2767 sub = wctx.sub(subpath)
2766 try:
2768 try:
2767 submatch = matchmod.subdirmatcher(subpath, match)
2769 submatch = matchmod.subdirmatcher(subpath, match)
2768 subbad, subforgot = sub.forget(submatch, prefix)
2770 subbad, subforgot = sub.forget(submatch, prefix)
2769 bad.extend([subpath + '/' + f for f in subbad])
2771 bad.extend([subpath + '/' + f for f in subbad])
2770 forgot.extend([subpath + '/' + f for f in subforgot])
2772 forgot.extend([subpath + '/' + f for f in subforgot])
2771 except error.LookupError:
2773 except error.LookupError:
2772 ui.status(_("skipping missing subrepository: %s\n")
2774 ui.status(_("skipping missing subrepository: %s\n")
2773 % join(subpath))
2775 % join(subpath))
2774
2776
2775 if not explicitonly:
2777 if not explicitonly:
2776 for f in match.files():
2778 for f in match.files():
2777 if f not in repo.dirstate and not repo.wvfs.isdir(f):
2779 if f not in repo.dirstate and not repo.wvfs.isdir(f):
2778 if f not in forgot:
2780 if f not in forgot:
2779 if repo.wvfs.exists(f):
2781 if repo.wvfs.exists(f):
2780 # Don't complain if the exact case match wasn't given.
2782 # Don't complain if the exact case match wasn't given.
2781 # But don't do this until after checking 'forgot', so
2783 # But don't do this until after checking 'forgot', so
2782 # that subrepo files aren't normalized, and this op is
2784 # that subrepo files aren't normalized, and this op is
2783 # purely from data cached by the status walk above.
2785 # purely from data cached by the status walk above.
2784 if repo.dirstate.normalize(f) in repo.dirstate:
2786 if repo.dirstate.normalize(f) in repo.dirstate:
2785 continue
2787 continue
2786 ui.warn(_('not removing %s: '
2788 ui.warn(_('not removing %s: '
2787 'file is already untracked\n')
2789 'file is already untracked\n')
2788 % match.rel(f))
2790 % match.rel(f))
2789 bad.append(f)
2791 bad.append(f)
2790
2792
2791 for f in forget:
2793 for f in forget:
2792 if ui.verbose or not match.exact(f):
2794 if ui.verbose or not match.exact(f):
2793 ui.status(_('removing %s\n') % match.rel(f))
2795 ui.status(_('removing %s\n') % match.rel(f))
2794
2796
2795 rejected = wctx.forget(forget, prefix)
2797 rejected = wctx.forget(forget, prefix)
2796 bad.extend(f for f in rejected if f in match.files())
2798 bad.extend(f for f in rejected if f in match.files())
2797 forgot.extend(f for f in forget if f not in rejected)
2799 forgot.extend(f for f in forget if f not in rejected)
2798 return bad, forgot
2800 return bad, forgot
2799
2801
2800 def files(ui, ctx, m, fm, fmt, subrepos):
2802 def files(ui, ctx, m, fm, fmt, subrepos):
2801 rev = ctx.rev()
2803 rev = ctx.rev()
2802 ret = 1
2804 ret = 1
2803 ds = ctx.repo().dirstate
2805 ds = ctx.repo().dirstate
2804
2806
2805 for f in ctx.matches(m):
2807 for f in ctx.matches(m):
2806 if rev is None and ds[f] == 'r':
2808 if rev is None and ds[f] == 'r':
2807 continue
2809 continue
2808 fm.startitem()
2810 fm.startitem()
2809 if ui.verbose:
2811 if ui.verbose:
2810 fc = ctx[f]
2812 fc = ctx[f]
2811 fm.write('size flags', '% 10d % 1s ', fc.size(), fc.flags())
2813 fm.write('size flags', '% 10d % 1s ', fc.size(), fc.flags())
2812 fm.data(abspath=f)
2814 fm.data(abspath=f)
2813 fm.write('path', fmt, m.rel(f))
2815 fm.write('path', fmt, m.rel(f))
2814 ret = 0
2816 ret = 0
2815
2817
2816 for subpath in sorted(ctx.substate):
2818 for subpath in sorted(ctx.substate):
2817 submatch = matchmod.subdirmatcher(subpath, m)
2819 submatch = matchmod.subdirmatcher(subpath, m)
2818 if (subrepos or m.exact(subpath) or any(submatch.files())):
2820 if (subrepos or m.exact(subpath) or any(submatch.files())):
2819 sub = ctx.sub(subpath)
2821 sub = ctx.sub(subpath)
2820 try:
2822 try:
2821 recurse = m.exact(subpath) or subrepos
2823 recurse = m.exact(subpath) or subrepos
2822 if sub.printfiles(ui, submatch, fm, fmt, recurse) == 0:
2824 if sub.printfiles(ui, submatch, fm, fmt, recurse) == 0:
2823 ret = 0
2825 ret = 0
2824 except error.LookupError:
2826 except error.LookupError:
2825 ui.status(_("skipping missing subrepository: %s\n")
2827 ui.status(_("skipping missing subrepository: %s\n")
2826 % m.abs(subpath))
2828 % m.abs(subpath))
2827
2829
2828 return ret
2830 return ret
2829
2831
2830 def remove(ui, repo, m, prefix, after, force, subrepos, warnings=None):
2832 def remove(ui, repo, m, prefix, after, force, subrepos, warnings=None):
2831 join = lambda f: os.path.join(prefix, f)
2833 join = lambda f: os.path.join(prefix, f)
2832 ret = 0
2834 ret = 0
2833 s = repo.status(match=m, clean=True)
2835 s = repo.status(match=m, clean=True)
2834 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
2836 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
2835
2837
2836 wctx = repo[None]
2838 wctx = repo[None]
2837
2839
2838 if warnings is None:
2840 if warnings is None:
2839 warnings = []
2841 warnings = []
2840 warn = True
2842 warn = True
2841 else:
2843 else:
2842 warn = False
2844 warn = False
2843
2845
2844 subs = sorted(wctx.substate)
2846 subs = sorted(wctx.substate)
2845 total = len(subs)
2847 total = len(subs)
2846 count = 0
2848 count = 0
2847 for subpath in subs:
2849 for subpath in subs:
2848 count += 1
2850 count += 1
2849 submatch = matchmod.subdirmatcher(subpath, m)
2851 submatch = matchmod.subdirmatcher(subpath, m)
2850 if subrepos or m.exact(subpath) or any(submatch.files()):
2852 if subrepos or m.exact(subpath) or any(submatch.files()):
2851 ui.progress(_('searching'), count, total=total, unit=_('subrepos'))
2853 ui.progress(_('searching'), count, total=total, unit=_('subrepos'))
2852 sub = wctx.sub(subpath)
2854 sub = wctx.sub(subpath)
2853 try:
2855 try:
2854 if sub.removefiles(submatch, prefix, after, force, subrepos,
2856 if sub.removefiles(submatch, prefix, after, force, subrepos,
2855 warnings):
2857 warnings):
2856 ret = 1
2858 ret = 1
2857 except error.LookupError:
2859 except error.LookupError:
2858 warnings.append(_("skipping missing subrepository: %s\n")
2860 warnings.append(_("skipping missing subrepository: %s\n")
2859 % join(subpath))
2861 % join(subpath))
2860 ui.progress(_('searching'), None)
2862 ui.progress(_('searching'), None)
2861
2863
2862 # warn about failure to delete explicit files/dirs
2864 # warn about failure to delete explicit files/dirs
2863 deleteddirs = util.dirs(deleted)
2865 deleteddirs = util.dirs(deleted)
2864 files = m.files()
2866 files = m.files()
2865 total = len(files)
2867 total = len(files)
2866 count = 0
2868 count = 0
2867 for f in files:
2869 for f in files:
2868 def insubrepo():
2870 def insubrepo():
2869 for subpath in wctx.substate:
2871 for subpath in wctx.substate:
2870 if f.startswith(subpath + '/'):
2872 if f.startswith(subpath + '/'):
2871 return True
2873 return True
2872 return False
2874 return False
2873
2875
2874 count += 1
2876 count += 1
2875 ui.progress(_('deleting'), count, total=total, unit=_('files'))
2877 ui.progress(_('deleting'), count, total=total, unit=_('files'))
2876 isdir = f in deleteddirs or wctx.hasdir(f)
2878 isdir = f in deleteddirs or wctx.hasdir(f)
2877 if (f in repo.dirstate or isdir or f == '.'
2879 if (f in repo.dirstate or isdir or f == '.'
2878 or insubrepo() or f in subs):
2880 or insubrepo() or f in subs):
2879 continue
2881 continue
2880
2882
2881 if repo.wvfs.exists(f):
2883 if repo.wvfs.exists(f):
2882 if repo.wvfs.isdir(f):
2884 if repo.wvfs.isdir(f):
2883 warnings.append(_('not removing %s: no tracked files\n')
2885 warnings.append(_('not removing %s: no tracked files\n')
2884 % m.rel(f))
2886 % m.rel(f))
2885 else:
2887 else:
2886 warnings.append(_('not removing %s: file is untracked\n')
2888 warnings.append(_('not removing %s: file is untracked\n')
2887 % m.rel(f))
2889 % m.rel(f))
2888 # missing files will generate a warning elsewhere
2890 # missing files will generate a warning elsewhere
2889 ret = 1
2891 ret = 1
2890 ui.progress(_('deleting'), None)
2892 ui.progress(_('deleting'), None)
2891
2893
2892 if force:
2894 if force:
2893 list = modified + deleted + clean + added
2895 list = modified + deleted + clean + added
2894 elif after:
2896 elif after:
2895 list = deleted
2897 list = deleted
2896 remaining = modified + added + clean
2898 remaining = modified + added + clean
2897 total = len(remaining)
2899 total = len(remaining)
2898 count = 0
2900 count = 0
2899 for f in remaining:
2901 for f in remaining:
2900 count += 1
2902 count += 1
2901 ui.progress(_('skipping'), count, total=total, unit=_('files'))
2903 ui.progress(_('skipping'), count, total=total, unit=_('files'))
2902 warnings.append(_('not removing %s: file still exists\n')
2904 warnings.append(_('not removing %s: file still exists\n')
2903 % m.rel(f))
2905 % m.rel(f))
2904 ret = 1
2906 ret = 1
2905 ui.progress(_('skipping'), None)
2907 ui.progress(_('skipping'), None)
2906 else:
2908 else:
2907 list = deleted + clean
2909 list = deleted + clean
2908 total = len(modified) + len(added)
2910 total = len(modified) + len(added)
2909 count = 0
2911 count = 0
2910 for f in modified:
2912 for f in modified:
2911 count += 1
2913 count += 1
2912 ui.progress(_('skipping'), count, total=total, unit=_('files'))
2914 ui.progress(_('skipping'), count, total=total, unit=_('files'))
2913 warnings.append(_('not removing %s: file is modified (use -f'
2915 warnings.append(_('not removing %s: file is modified (use -f'
2914 ' to force removal)\n') % m.rel(f))
2916 ' to force removal)\n') % m.rel(f))
2915 ret = 1
2917 ret = 1
2916 for f in added:
2918 for f in added:
2917 count += 1
2919 count += 1
2918 ui.progress(_('skipping'), count, total=total, unit=_('files'))
2920 ui.progress(_('skipping'), count, total=total, unit=_('files'))
2919 warnings.append(_("not removing %s: file has been marked for add"
2921 warnings.append(_("not removing %s: file has been marked for add"
2920 " (use 'hg forget' to undo add)\n") % m.rel(f))
2922 " (use 'hg forget' to undo add)\n") % m.rel(f))
2921 ret = 1
2923 ret = 1
2922 ui.progress(_('skipping'), None)
2924 ui.progress(_('skipping'), None)
2923
2925
2924 list = sorted(list)
2926 list = sorted(list)
2925 total = len(list)
2927 total = len(list)
2926 count = 0
2928 count = 0
2927 for f in list:
2929 for f in list:
2928 count += 1
2930 count += 1
2929 if ui.verbose or not m.exact(f):
2931 if ui.verbose or not m.exact(f):
2930 ui.progress(_('deleting'), count, total=total, unit=_('files'))
2932 ui.progress(_('deleting'), count, total=total, unit=_('files'))
2931 ui.status(_('removing %s\n') % m.rel(f))
2933 ui.status(_('removing %s\n') % m.rel(f))
2932 ui.progress(_('deleting'), None)
2934 ui.progress(_('deleting'), None)
2933
2935
2934 with repo.wlock():
2936 with repo.wlock():
2935 if not after:
2937 if not after:
2936 for f in list:
2938 for f in list:
2937 if f in added:
2939 if f in added:
2938 continue # we never unlink added files on remove
2940 continue # we never unlink added files on remove
2939 repo.wvfs.unlinkpath(f, ignoremissing=True)
2941 repo.wvfs.unlinkpath(f, ignoremissing=True)
2940 repo[None].forget(list)
2942 repo[None].forget(list)
2941
2943
2942 if warn:
2944 if warn:
2943 for warning in warnings:
2945 for warning in warnings:
2944 ui.warn(warning)
2946 ui.warn(warning)
2945
2947
2946 return ret
2948 return ret
2947
2949
2948 def cat(ui, repo, ctx, matcher, basefm, fntemplate, prefix, **opts):
2950 def cat(ui, repo, ctx, matcher, basefm, fntemplate, prefix, **opts):
2949 err = 1
2951 err = 1
2950
2952
2951 def write(path):
2953 def write(path):
2952 filename = None
2954 filename = None
2953 if fntemplate:
2955 if fntemplate:
2954 filename = makefilename(repo, fntemplate, ctx.node(),
2956 filename = makefilename(repo, fntemplate, ctx.node(),
2955 pathname=os.path.join(prefix, path))
2957 pathname=os.path.join(prefix, path))
2956 with formatter.maybereopen(basefm, filename, opts) as fm:
2958 with formatter.maybereopen(basefm, filename, opts) as fm:
2957 data = ctx[path].data()
2959 data = ctx[path].data()
2958 if opts.get('decode'):
2960 if opts.get('decode'):
2959 data = repo.wwritedata(path, data)
2961 data = repo.wwritedata(path, data)
2960 fm.startitem()
2962 fm.startitem()
2961 fm.write('data', '%s', data)
2963 fm.write('data', '%s', data)
2962 fm.data(abspath=path, path=matcher.rel(path))
2964 fm.data(abspath=path, path=matcher.rel(path))
2963
2965
2964 # Automation often uses hg cat on single files, so special case it
2966 # Automation often uses hg cat on single files, so special case it
2965 # for performance to avoid the cost of parsing the manifest.
2967 # for performance to avoid the cost of parsing the manifest.
2966 if len(matcher.files()) == 1 and not matcher.anypats():
2968 if len(matcher.files()) == 1 and not matcher.anypats():
2967 file = matcher.files()[0]
2969 file = matcher.files()[0]
2968 mfl = repo.manifestlog
2970 mfl = repo.manifestlog
2969 mfnode = ctx.manifestnode()
2971 mfnode = ctx.manifestnode()
2970 try:
2972 try:
2971 if mfnode and mfl[mfnode].find(file)[0]:
2973 if mfnode and mfl[mfnode].find(file)[0]:
2972 write(file)
2974 write(file)
2973 return 0
2975 return 0
2974 except KeyError:
2976 except KeyError:
2975 pass
2977 pass
2976
2978
2977 for abs in ctx.walk(matcher):
2979 for abs in ctx.walk(matcher):
2978 write(abs)
2980 write(abs)
2979 err = 0
2981 err = 0
2980
2982
2981 for subpath in sorted(ctx.substate):
2983 for subpath in sorted(ctx.substate):
2982 sub = ctx.sub(subpath)
2984 sub = ctx.sub(subpath)
2983 try:
2985 try:
2984 submatch = matchmod.subdirmatcher(subpath, matcher)
2986 submatch = matchmod.subdirmatcher(subpath, matcher)
2985
2987
2986 if not sub.cat(submatch, basefm, fntemplate,
2988 if not sub.cat(submatch, basefm, fntemplate,
2987 os.path.join(prefix, sub._path), **opts):
2989 os.path.join(prefix, sub._path), **opts):
2988 err = 0
2990 err = 0
2989 except error.RepoLookupError:
2991 except error.RepoLookupError:
2990 ui.status(_("skipping missing subrepository: %s\n")
2992 ui.status(_("skipping missing subrepository: %s\n")
2991 % os.path.join(prefix, subpath))
2993 % os.path.join(prefix, subpath))
2992
2994
2993 return err
2995 return err
2994
2996
2995 def commit(ui, repo, commitfunc, pats, opts):
2997 def commit(ui, repo, commitfunc, pats, opts):
2996 '''commit the specified files or all outstanding changes'''
2998 '''commit the specified files or all outstanding changes'''
2997 date = opts.get('date')
2999 date = opts.get('date')
2998 if date:
3000 if date:
2999 opts['date'] = util.parsedate(date)
3001 opts['date'] = util.parsedate(date)
3000 message = logmessage(ui, opts)
3002 message = logmessage(ui, opts)
3001 matcher = scmutil.match(repo[None], pats, opts)
3003 matcher = scmutil.match(repo[None], pats, opts)
3002
3004
3003 dsguard = None
3005 dsguard = None
3004 # extract addremove carefully -- this function can be called from a command
3006 # extract addremove carefully -- this function can be called from a command
3005 # that doesn't support addremove
3007 # that doesn't support addremove
3006 if opts.get('addremove'):
3008 if opts.get('addremove'):
3007 dsguard = dirstateguard.dirstateguard(repo, 'commit')
3009 dsguard = dirstateguard.dirstateguard(repo, 'commit')
3008 with dsguard or util.nullcontextmanager():
3010 with dsguard or util.nullcontextmanager():
3009 if dsguard:
3011 if dsguard:
3010 if scmutil.addremove(repo, matcher, "", opts) != 0:
3012 if scmutil.addremove(repo, matcher, "", opts) != 0:
3011 raise error.Abort(
3013 raise error.Abort(
3012 _("failed to mark all new/missing files as added/removed"))
3014 _("failed to mark all new/missing files as added/removed"))
3013
3015
3014 return commitfunc(ui, repo, message, matcher, opts)
3016 return commitfunc(ui, repo, message, matcher, opts)
3015
3017
3016 def samefile(f, ctx1, ctx2):
3018 def samefile(f, ctx1, ctx2):
3017 if f in ctx1.manifest():
3019 if f in ctx1.manifest():
3018 a = ctx1.filectx(f)
3020 a = ctx1.filectx(f)
3019 if f in ctx2.manifest():
3021 if f in ctx2.manifest():
3020 b = ctx2.filectx(f)
3022 b = ctx2.filectx(f)
3021 return (not a.cmp(b)
3023 return (not a.cmp(b)
3022 and a.flags() == b.flags())
3024 and a.flags() == b.flags())
3023 else:
3025 else:
3024 return False
3026 return False
3025 else:
3027 else:
3026 return f not in ctx2.manifest()
3028 return f not in ctx2.manifest()
3027
3029
3028 def amend(ui, repo, commitfunc, old, extra, pats, opts):
3030 def amend(ui, repo, commitfunc, old, extra, pats, opts):
3029 # avoid cycle context -> subrepo -> cmdutil
3031 # avoid cycle context -> subrepo -> cmdutil
3030 from . import context
3032 from . import context
3031
3033
3032 # amend will reuse the existing user if not specified, but the obsolete
3034 # amend will reuse the existing user if not specified, but the obsolete
3033 # marker creation requires that the current user's name is specified.
3035 # marker creation requires that the current user's name is specified.
3034 if obsolete.isenabled(repo, obsolete.createmarkersopt):
3036 if obsolete.isenabled(repo, obsolete.createmarkersopt):
3035 ui.username() # raise exception if username not set
3037 ui.username() # raise exception if username not set
3036
3038
3037 ui.note(_('amending changeset %s\n') % old)
3039 ui.note(_('amending changeset %s\n') % old)
3038 base = old.p1()
3040 base = old.p1()
3039
3041
3040 newid = None
3042 newid = None
3041 with repo.wlock(), repo.lock(), repo.transaction('amend'):
3043 with repo.wlock(), repo.lock(), repo.transaction('amend'):
3042 # See if we got a message from -m or -l, if not, open the editor
3044 # See if we got a message from -m or -l, if not, open the editor
3043 # with the message of the changeset to amend
3045 # with the message of the changeset to amend
3044 message = logmessage(ui, opts)
3046 message = logmessage(ui, opts)
3045 # ensure logfile does not conflict with later enforcement of the
3047 # ensure logfile does not conflict with later enforcement of the
3046 # message. potential logfile content has been processed by
3048 # message. potential logfile content has been processed by
3047 # `logmessage` anyway.
3049 # `logmessage` anyway.
3048 opts.pop('logfile')
3050 opts.pop('logfile')
3049 # First, do a regular commit to record all changes in the working
3051 # First, do a regular commit to record all changes in the working
3050 # directory (if there are any)
3052 # directory (if there are any)
3051 ui.callhooks = False
3053 ui.callhooks = False
3052 activebookmark = repo._bookmarks.active
3054 activebookmark = repo._bookmarks.active
3053 try:
3055 try:
3054 repo._bookmarks.active = None
3056 repo._bookmarks.active = None
3055 opts['message'] = 'temporary amend commit for %s' % old
3057 opts['message'] = 'temporary amend commit for %s' % old
3056 node = commit(ui, repo, commitfunc, pats, opts)
3058 node = commit(ui, repo, commitfunc, pats, opts)
3057 finally:
3059 finally:
3058 repo._bookmarks.active = activebookmark
3060 repo._bookmarks.active = activebookmark
3059 ui.callhooks = True
3061 ui.callhooks = True
3060 ctx = repo[node]
3062 ctx = repo[node]
3061
3063
3062 # Participating changesets:
3064 # Participating changesets:
3063 #
3065 #
3064 # node/ctx o - new (intermediate) commit that contains changes
3066 # node/ctx o - new (intermediate) commit that contains changes
3065 # | from working dir to go into amending commit
3067 # | from working dir to go into amending commit
3066 # | (or a workingctx if there were no changes)
3068 # | (or a workingctx if there were no changes)
3067 # |
3069 # |
3068 # old o - changeset to amend
3070 # old o - changeset to amend
3069 # |
3071 # |
3070 # base o - parent of amending changeset
3072 # base o - parent of amending changeset
3071
3073
3072 # Update extra dict from amended commit (e.g. to preserve graft
3074 # Update extra dict from amended commit (e.g. to preserve graft
3073 # source)
3075 # source)
3074 extra.update(old.extra())
3076 extra.update(old.extra())
3075
3077
3076 # Also update it from the intermediate commit or from the wctx
3078 # Also update it from the intermediate commit or from the wctx
3077 extra.update(ctx.extra())
3079 extra.update(ctx.extra())
3078
3080
3079 if len(old.parents()) > 1:
3081 if len(old.parents()) > 1:
3080 # ctx.files() isn't reliable for merges, so fall back to the
3082 # ctx.files() isn't reliable for merges, so fall back to the
3081 # slower repo.status() method
3083 # slower repo.status() method
3082 files = set([fn for st in repo.status(base, old)[:3]
3084 files = set([fn for st in repo.status(base, old)[:3]
3083 for fn in st])
3085 for fn in st])
3084 else:
3086 else:
3085 files = set(old.files())
3087 files = set(old.files())
3086
3088
3087 # Second, we use either the commit we just did, or if there were no
3089 # Second, we use either the commit we just did, or if there were no
3088 # changes the parent of the working directory as the version of the
3090 # changes the parent of the working directory as the version of the
3089 # files in the final amend commit
3091 # files in the final amend commit
3090 if node:
3092 if node:
3091 ui.note(_('copying changeset %s to %s\n') % (ctx, base))
3093 ui.note(_('copying changeset %s to %s\n') % (ctx, base))
3092
3094
3093 user = ctx.user()
3095 user = ctx.user()
3094 date = ctx.date()
3096 date = ctx.date()
3095 # Recompute copies (avoid recording a -> b -> a)
3097 # Recompute copies (avoid recording a -> b -> a)
3096 copied = copies.pathcopies(base, ctx)
3098 copied = copies.pathcopies(base, ctx)
3097 if old.p2:
3099 if old.p2:
3098 copied.update(copies.pathcopies(old.p2(), ctx))
3100 copied.update(copies.pathcopies(old.p2(), ctx))
3099
3101
3100 # Prune files which were reverted by the updates: if old
3102 # Prune files which were reverted by the updates: if old
3101 # introduced file X and our intermediate commit, node,
3103 # introduced file X and our intermediate commit, node,
3102 # renamed that file, then those two files are the same and
3104 # renamed that file, then those two files are the same and
3103 # we can discard X from our list of files. Likewise if X
3105 # we can discard X from our list of files. Likewise if X
3104 # was deleted, it's no longer relevant
3106 # was deleted, it's no longer relevant
3105 files.update(ctx.files())
3107 files.update(ctx.files())
3106 files = [f for f in files if not samefile(f, ctx, base)]
3108 files = [f for f in files if not samefile(f, ctx, base)]
3107
3109
3108 def filectxfn(repo, ctx_, path):
3110 def filectxfn(repo, ctx_, path):
3109 try:
3111 try:
3110 fctx = ctx[path]
3112 fctx = ctx[path]
3111 flags = fctx.flags()
3113 flags = fctx.flags()
3112 mctx = context.memfilectx(repo,
3114 mctx = context.memfilectx(repo,
3113 fctx.path(), fctx.data(),
3115 fctx.path(), fctx.data(),
3114 islink='l' in flags,
3116 islink='l' in flags,
3115 isexec='x' in flags,
3117 isexec='x' in flags,
3116 copied=copied.get(path))
3118 copied=copied.get(path))
3117 return mctx
3119 return mctx
3118 except KeyError:
3120 except KeyError:
3119 return None
3121 return None
3120 else:
3122 else:
3121 ui.note(_('copying changeset %s to %s\n') % (old, base))
3123 ui.note(_('copying changeset %s to %s\n') % (old, base))
3122
3124
3123 # Use version of files as in the old cset
3125 # Use version of files as in the old cset
3124 def filectxfn(repo, ctx_, path):
3126 def filectxfn(repo, ctx_, path):
3125 try:
3127 try:
3126 return old.filectx(path)
3128 return old.filectx(path)
3127 except KeyError:
3129 except KeyError:
3128 return None
3130 return None
3129
3131
3130 user = opts.get('user') or old.user()
3132 user = opts.get('user') or old.user()
3131 date = opts.get('date') or old.date()
3133 date = opts.get('date') or old.date()
3132 editform = mergeeditform(old, 'commit.amend')
3134 editform = mergeeditform(old, 'commit.amend')
3133 editor = getcommiteditor(editform=editform,
3135 editor = getcommiteditor(editform=editform,
3134 **pycompat.strkwargs(opts))
3136 **pycompat.strkwargs(opts))
3135 if not message:
3137 if not message:
3136 editor = getcommiteditor(edit=True, editform=editform)
3138 editor = getcommiteditor(edit=True, editform=editform)
3137 message = old.description()
3139 message = old.description()
3138
3140
3139 pureextra = extra.copy()
3141 pureextra = extra.copy()
3140 extra['amend_source'] = old.hex()
3142 extra['amend_source'] = old.hex()
3141
3143
3142 new = context.memctx(repo,
3144 new = context.memctx(repo,
3143 parents=[base.node(), old.p2().node()],
3145 parents=[base.node(), old.p2().node()],
3144 text=message,
3146 text=message,
3145 files=files,
3147 files=files,
3146 filectxfn=filectxfn,
3148 filectxfn=filectxfn,
3147 user=user,
3149 user=user,
3148 date=date,
3150 date=date,
3149 extra=extra,
3151 extra=extra,
3150 editor=editor)
3152 editor=editor)
3151
3153
3152 newdesc = changelog.stripdesc(new.description())
3154 newdesc = changelog.stripdesc(new.description())
3153 if ((not node)
3155 if ((not node)
3154 and newdesc == old.description()
3156 and newdesc == old.description()
3155 and user == old.user()
3157 and user == old.user()
3156 and date == old.date()
3158 and date == old.date()
3157 and pureextra == old.extra()):
3159 and pureextra == old.extra()):
3158 # nothing changed. continuing here would create a new node
3160 # nothing changed. continuing here would create a new node
3159 # anyway because of the amend_source noise.
3161 # anyway because of the amend_source noise.
3160 #
3162 #
3161 # This not what we expect from amend.
3163 # This not what we expect from amend.
3162 return old.node()
3164 return old.node()
3163
3165
3164 ph = repo.ui.config('phases', 'new-commit', phases.draft)
3166 ph = repo.ui.config('phases', 'new-commit', phases.draft)
3165 try:
3167 try:
3166 if opts.get('secret'):
3168 if opts.get('secret'):
3167 commitphase = 'secret'
3169 commitphase = 'secret'
3168 else:
3170 else:
3169 commitphase = old.phase()
3171 commitphase = old.phase()
3170 repo.ui.setconfig('phases', 'new-commit', commitphase, 'amend')
3172 repo.ui.setconfig('phases', 'new-commit', commitphase, 'amend')
3171 newid = repo.commitctx(new)
3173 newid = repo.commitctx(new)
3172 finally:
3174 finally:
3173 repo.ui.setconfig('phases', 'new-commit', ph, 'amend')
3175 repo.ui.setconfig('phases', 'new-commit', ph, 'amend')
3174 if newid != old.node():
3176 if newid != old.node():
3175 # Reroute the working copy parent to the new changeset
3177 # Reroute the working copy parent to the new changeset
3176 repo.setparents(newid, nullid)
3178 repo.setparents(newid, nullid)
3177 mapping = {old.node(): (newid,)}
3179 mapping = {old.node(): (newid,)}
3178 if node:
3180 if node:
3179 mapping[node] = ()
3181 mapping[node] = ()
3180 scmutil.cleanupnodes(repo, mapping, 'amend')
3182 scmutil.cleanupnodes(repo, mapping, 'amend')
3181 return newid
3183 return newid
3182
3184
3183 def commiteditor(repo, ctx, subs, editform=''):
3185 def commiteditor(repo, ctx, subs, editform=''):
3184 if ctx.description():
3186 if ctx.description():
3185 return ctx.description()
3187 return ctx.description()
3186 return commitforceeditor(repo, ctx, subs, editform=editform,
3188 return commitforceeditor(repo, ctx, subs, editform=editform,
3187 unchangedmessagedetection=True)
3189 unchangedmessagedetection=True)
3188
3190
3189 def commitforceeditor(repo, ctx, subs, finishdesc=None, extramsg=None,
3191 def commitforceeditor(repo, ctx, subs, finishdesc=None, extramsg=None,
3190 editform='', unchangedmessagedetection=False):
3192 editform='', unchangedmessagedetection=False):
3191 if not extramsg:
3193 if not extramsg:
3192 extramsg = _("Leave message empty to abort commit.")
3194 extramsg = _("Leave message empty to abort commit.")
3193
3195
3194 forms = [e for e in editform.split('.') if e]
3196 forms = [e for e in editform.split('.') if e]
3195 forms.insert(0, 'changeset')
3197 forms.insert(0, 'changeset')
3196 templatetext = None
3198 templatetext = None
3197 while forms:
3199 while forms:
3198 ref = '.'.join(forms)
3200 ref = '.'.join(forms)
3199 if repo.ui.config('committemplate', ref):
3201 if repo.ui.config('committemplate', ref):
3200 templatetext = committext = buildcommittemplate(
3202 templatetext = committext = buildcommittemplate(
3201 repo, ctx, subs, extramsg, ref)
3203 repo, ctx, subs, extramsg, ref)
3202 break
3204 break
3203 forms.pop()
3205 forms.pop()
3204 else:
3206 else:
3205 committext = buildcommittext(repo, ctx, subs, extramsg)
3207 committext = buildcommittext(repo, ctx, subs, extramsg)
3206
3208
3207 # run editor in the repository root
3209 # run editor in the repository root
3208 olddir = pycompat.getcwd()
3210 olddir = pycompat.getcwd()
3209 os.chdir(repo.root)
3211 os.chdir(repo.root)
3210
3212
3211 # make in-memory changes visible to external process
3213 # make in-memory changes visible to external process
3212 tr = repo.currenttransaction()
3214 tr = repo.currenttransaction()
3213 repo.dirstate.write(tr)
3215 repo.dirstate.write(tr)
3214 pending = tr and tr.writepending() and repo.root
3216 pending = tr and tr.writepending() and repo.root
3215
3217
3216 editortext = repo.ui.edit(committext, ctx.user(), ctx.extra(),
3218 editortext = repo.ui.edit(committext, ctx.user(), ctx.extra(),
3217 editform=editform, pending=pending,
3219 editform=editform, pending=pending,
3218 repopath=repo.path)
3220 repopath=repo.path)
3219 text = editortext
3221 text = editortext
3220
3222
3221 # strip away anything below this special string (used for editors that want
3223 # strip away anything below this special string (used for editors that want
3222 # to display the diff)
3224 # to display the diff)
3223 stripbelow = re.search(_linebelow, text, flags=re.MULTILINE)
3225 stripbelow = re.search(_linebelow, text, flags=re.MULTILINE)
3224 if stripbelow:
3226 if stripbelow:
3225 text = text[:stripbelow.start()]
3227 text = text[:stripbelow.start()]
3226
3228
3227 text = re.sub("(?m)^HG:.*(\n|$)", "", text)
3229 text = re.sub("(?m)^HG:.*(\n|$)", "", text)
3228 os.chdir(olddir)
3230 os.chdir(olddir)
3229
3231
3230 if finishdesc:
3232 if finishdesc:
3231 text = finishdesc(text)
3233 text = finishdesc(text)
3232 if not text.strip():
3234 if not text.strip():
3233 raise error.Abort(_("empty commit message"))
3235 raise error.Abort(_("empty commit message"))
3234 if unchangedmessagedetection and editortext == templatetext:
3236 if unchangedmessagedetection and editortext == templatetext:
3235 raise error.Abort(_("commit message unchanged"))
3237 raise error.Abort(_("commit message unchanged"))
3236
3238
3237 return text
3239 return text
3238
3240
3239 def buildcommittemplate(repo, ctx, subs, extramsg, ref):
3241 def buildcommittemplate(repo, ctx, subs, extramsg, ref):
3240 ui = repo.ui
3242 ui = repo.ui
3241 spec = formatter.templatespec(ref, None, None)
3243 spec = formatter.templatespec(ref, None, None)
3242 t = changeset_templater(ui, repo, spec, None, {}, False)
3244 t = changeset_templater(ui, repo, spec, None, {}, False)
3243 t.t.cache.update((k, templater.unquotestring(v))
3245 t.t.cache.update((k, templater.unquotestring(v))
3244 for k, v in repo.ui.configitems('committemplate'))
3246 for k, v in repo.ui.configitems('committemplate'))
3245
3247
3246 if not extramsg:
3248 if not extramsg:
3247 extramsg = '' # ensure that extramsg is string
3249 extramsg = '' # ensure that extramsg is string
3248
3250
3249 ui.pushbuffer()
3251 ui.pushbuffer()
3250 t.show(ctx, extramsg=extramsg)
3252 t.show(ctx, extramsg=extramsg)
3251 return ui.popbuffer()
3253 return ui.popbuffer()
3252
3254
3253 def hgprefix(msg):
3255 def hgprefix(msg):
3254 return "\n".join(["HG: %s" % a for a in msg.split("\n") if a])
3256 return "\n".join(["HG: %s" % a for a in msg.split("\n") if a])
3255
3257
3256 def buildcommittext(repo, ctx, subs, extramsg):
3258 def buildcommittext(repo, ctx, subs, extramsg):
3257 edittext = []
3259 edittext = []
3258 modified, added, removed = ctx.modified(), ctx.added(), ctx.removed()
3260 modified, added, removed = ctx.modified(), ctx.added(), ctx.removed()
3259 if ctx.description():
3261 if ctx.description():
3260 edittext.append(ctx.description())
3262 edittext.append(ctx.description())
3261 edittext.append("")
3263 edittext.append("")
3262 edittext.append("") # Empty line between message and comments.
3264 edittext.append("") # Empty line between message and comments.
3263 edittext.append(hgprefix(_("Enter commit message."
3265 edittext.append(hgprefix(_("Enter commit message."
3264 " Lines beginning with 'HG:' are removed.")))
3266 " Lines beginning with 'HG:' are removed.")))
3265 edittext.append(hgprefix(extramsg))
3267 edittext.append(hgprefix(extramsg))
3266 edittext.append("HG: --")
3268 edittext.append("HG: --")
3267 edittext.append(hgprefix(_("user: %s") % ctx.user()))
3269 edittext.append(hgprefix(_("user: %s") % ctx.user()))
3268 if ctx.p2():
3270 if ctx.p2():
3269 edittext.append(hgprefix(_("branch merge")))
3271 edittext.append(hgprefix(_("branch merge")))
3270 if ctx.branch():
3272 if ctx.branch():
3271 edittext.append(hgprefix(_("branch '%s'") % ctx.branch()))
3273 edittext.append(hgprefix(_("branch '%s'") % ctx.branch()))
3272 if bookmarks.isactivewdirparent(repo):
3274 if bookmarks.isactivewdirparent(repo):
3273 edittext.append(hgprefix(_("bookmark '%s'") % repo._activebookmark))
3275 edittext.append(hgprefix(_("bookmark '%s'") % repo._activebookmark))
3274 edittext.extend([hgprefix(_("subrepo %s") % s) for s in subs])
3276 edittext.extend([hgprefix(_("subrepo %s") % s) for s in subs])
3275 edittext.extend([hgprefix(_("added %s") % f) for f in added])
3277 edittext.extend([hgprefix(_("added %s") % f) for f in added])
3276 edittext.extend([hgprefix(_("changed %s") % f) for f in modified])
3278 edittext.extend([hgprefix(_("changed %s") % f) for f in modified])
3277 edittext.extend([hgprefix(_("removed %s") % f) for f in removed])
3279 edittext.extend([hgprefix(_("removed %s") % f) for f in removed])
3278 if not added and not modified and not removed:
3280 if not added and not modified and not removed:
3279 edittext.append(hgprefix(_("no files changed")))
3281 edittext.append(hgprefix(_("no files changed")))
3280 edittext.append("")
3282 edittext.append("")
3281
3283
3282 return "\n".join(edittext)
3284 return "\n".join(edittext)
3283
3285
3284 def commitstatus(repo, node, branch, bheads=None, opts=None):
3286 def commitstatus(repo, node, branch, bheads=None, opts=None):
3285 if opts is None:
3287 if opts is None:
3286 opts = {}
3288 opts = {}
3287 ctx = repo[node]
3289 ctx = repo[node]
3288 parents = ctx.parents()
3290 parents = ctx.parents()
3289
3291
3290 if (not opts.get('amend') and bheads and node not in bheads and not
3292 if (not opts.get('amend') and bheads and node not in bheads and not
3291 [x for x in parents if x.node() in bheads and x.branch() == branch]):
3293 [x for x in parents if x.node() in bheads and x.branch() == branch]):
3292 repo.ui.status(_('created new head\n'))
3294 repo.ui.status(_('created new head\n'))
3293 # The message is not printed for initial roots. For the other
3295 # The message is not printed for initial roots. For the other
3294 # changesets, it is printed in the following situations:
3296 # changesets, it is printed in the following situations:
3295 #
3297 #
3296 # Par column: for the 2 parents with ...
3298 # Par column: for the 2 parents with ...
3297 # N: null or no parent
3299 # N: null or no parent
3298 # B: parent is on another named branch
3300 # B: parent is on another named branch
3299 # C: parent is a regular non head changeset
3301 # C: parent is a regular non head changeset
3300 # H: parent was a branch head of the current branch
3302 # H: parent was a branch head of the current branch
3301 # Msg column: whether we print "created new head" message
3303 # Msg column: whether we print "created new head" message
3302 # In the following, it is assumed that there already exists some
3304 # In the following, it is assumed that there already exists some
3303 # initial branch heads of the current branch, otherwise nothing is
3305 # initial branch heads of the current branch, otherwise nothing is
3304 # printed anyway.
3306 # printed anyway.
3305 #
3307 #
3306 # Par Msg Comment
3308 # Par Msg Comment
3307 # N N y additional topo root
3309 # N N y additional topo root
3308 #
3310 #
3309 # B N y additional branch root
3311 # B N y additional branch root
3310 # C N y additional topo head
3312 # C N y additional topo head
3311 # H N n usual case
3313 # H N n usual case
3312 #
3314 #
3313 # B B y weird additional branch root
3315 # B B y weird additional branch root
3314 # C B y branch merge
3316 # C B y branch merge
3315 # H B n merge with named branch
3317 # H B n merge with named branch
3316 #
3318 #
3317 # C C y additional head from merge
3319 # C C y additional head from merge
3318 # C H n merge with a head
3320 # C H n merge with a head
3319 #
3321 #
3320 # H H n head merge: head count decreases
3322 # H H n head merge: head count decreases
3321
3323
3322 if not opts.get('close_branch'):
3324 if not opts.get('close_branch'):
3323 for r in parents:
3325 for r in parents:
3324 if r.closesbranch() and r.branch() == branch:
3326 if r.closesbranch() and r.branch() == branch:
3325 repo.ui.status(_('reopening closed branch head %d\n') % r)
3327 repo.ui.status(_('reopening closed branch head %d\n') % r)
3326
3328
3327 if repo.ui.debugflag:
3329 if repo.ui.debugflag:
3328 repo.ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx.hex()))
3330 repo.ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx.hex()))
3329 elif repo.ui.verbose:
3331 elif repo.ui.verbose:
3330 repo.ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx))
3332 repo.ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx))
3331
3333
3332 def postcommitstatus(repo, pats, opts):
3334 def postcommitstatus(repo, pats, opts):
3333 return repo.status(match=scmutil.match(repo[None], pats, opts))
3335 return repo.status(match=scmutil.match(repo[None], pats, opts))
3334
3336
3335 def revert(ui, repo, ctx, parents, *pats, **opts):
3337 def revert(ui, repo, ctx, parents, *pats, **opts):
3336 parent, p2 = parents
3338 parent, p2 = parents
3337 node = ctx.node()
3339 node = ctx.node()
3338
3340
3339 mf = ctx.manifest()
3341 mf = ctx.manifest()
3340 if node == p2:
3342 if node == p2:
3341 parent = p2
3343 parent = p2
3342
3344
3343 # need all matching names in dirstate and manifest of target rev,
3345 # need all matching names in dirstate and manifest of target rev,
3344 # so have to walk both. do not print errors if files exist in one
3346 # so have to walk both. do not print errors if files exist in one
3345 # but not other. in both cases, filesets should be evaluated against
3347 # but not other. in both cases, filesets should be evaluated against
3346 # workingctx to get consistent result (issue4497). this means 'set:**'
3348 # workingctx to get consistent result (issue4497). this means 'set:**'
3347 # cannot be used to select missing files from target rev.
3349 # cannot be used to select missing files from target rev.
3348
3350
3349 # `names` is a mapping for all elements in working copy and target revision
3351 # `names` is a mapping for all elements in working copy and target revision
3350 # The mapping is in the form:
3352 # The mapping is in the form:
3351 # <asb path in repo> -> (<path from CWD>, <exactly specified by matcher?>)
3353 # <asb path in repo> -> (<path from CWD>, <exactly specified by matcher?>)
3352 names = {}
3354 names = {}
3353
3355
3354 with repo.wlock():
3356 with repo.wlock():
3355 ## filling of the `names` mapping
3357 ## filling of the `names` mapping
3356 # walk dirstate to fill `names`
3358 # walk dirstate to fill `names`
3357
3359
3358 interactive = opts.get('interactive', False)
3360 interactive = opts.get('interactive', False)
3359 wctx = repo[None]
3361 wctx = repo[None]
3360 m = scmutil.match(wctx, pats, opts)
3362 m = scmutil.match(wctx, pats, opts)
3361
3363
3362 # we'll need this later
3364 # we'll need this later
3363 targetsubs = sorted(s for s in wctx.substate if m(s))
3365 targetsubs = sorted(s for s in wctx.substate if m(s))
3364
3366
3365 if not m.always():
3367 if not m.always():
3366 matcher = matchmod.badmatch(m, lambda x, y: False)
3368 matcher = matchmod.badmatch(m, lambda x, y: False)
3367 for abs in wctx.walk(matcher):
3369 for abs in wctx.walk(matcher):
3368 names[abs] = m.rel(abs), m.exact(abs)
3370 names[abs] = m.rel(abs), m.exact(abs)
3369
3371
3370 # walk target manifest to fill `names`
3372 # walk target manifest to fill `names`
3371
3373
3372 def badfn(path, msg):
3374 def badfn(path, msg):
3373 if path in names:
3375 if path in names:
3374 return
3376 return
3375 if path in ctx.substate:
3377 if path in ctx.substate:
3376 return
3378 return
3377 path_ = path + '/'
3379 path_ = path + '/'
3378 for f in names:
3380 for f in names:
3379 if f.startswith(path_):
3381 if f.startswith(path_):
3380 return
3382 return
3381 ui.warn("%s: %s\n" % (m.rel(path), msg))
3383 ui.warn("%s: %s\n" % (m.rel(path), msg))
3382
3384
3383 for abs in ctx.walk(matchmod.badmatch(m, badfn)):
3385 for abs in ctx.walk(matchmod.badmatch(m, badfn)):
3384 if abs not in names:
3386 if abs not in names:
3385 names[abs] = m.rel(abs), m.exact(abs)
3387 names[abs] = m.rel(abs), m.exact(abs)
3386
3388
3387 # Find status of all file in `names`.
3389 # Find status of all file in `names`.
3388 m = scmutil.matchfiles(repo, names)
3390 m = scmutil.matchfiles(repo, names)
3389
3391
3390 changes = repo.status(node1=node, match=m,
3392 changes = repo.status(node1=node, match=m,
3391 unknown=True, ignored=True, clean=True)
3393 unknown=True, ignored=True, clean=True)
3392 else:
3394 else:
3393 changes = repo.status(node1=node, match=m)
3395 changes = repo.status(node1=node, match=m)
3394 for kind in changes:
3396 for kind in changes:
3395 for abs in kind:
3397 for abs in kind:
3396 names[abs] = m.rel(abs), m.exact(abs)
3398 names[abs] = m.rel(abs), m.exact(abs)
3397
3399
3398 m = scmutil.matchfiles(repo, names)
3400 m = scmutil.matchfiles(repo, names)
3399
3401
3400 modified = set(changes.modified)
3402 modified = set(changes.modified)
3401 added = set(changes.added)
3403 added = set(changes.added)
3402 removed = set(changes.removed)
3404 removed = set(changes.removed)
3403 _deleted = set(changes.deleted)
3405 _deleted = set(changes.deleted)
3404 unknown = set(changes.unknown)
3406 unknown = set(changes.unknown)
3405 unknown.update(changes.ignored)
3407 unknown.update(changes.ignored)
3406 clean = set(changes.clean)
3408 clean = set(changes.clean)
3407 modadded = set()
3409 modadded = set()
3408
3410
3409 # We need to account for the state of the file in the dirstate,
3411 # We need to account for the state of the file in the dirstate,
3410 # even when we revert against something else than parent. This will
3412 # even when we revert against something else than parent. This will
3411 # slightly alter the behavior of revert (doing back up or not, delete
3413 # slightly alter the behavior of revert (doing back up or not, delete
3412 # or just forget etc).
3414 # or just forget etc).
3413 if parent == node:
3415 if parent == node:
3414 dsmodified = modified
3416 dsmodified = modified
3415 dsadded = added
3417 dsadded = added
3416 dsremoved = removed
3418 dsremoved = removed
3417 # store all local modifications, useful later for rename detection
3419 # store all local modifications, useful later for rename detection
3418 localchanges = dsmodified | dsadded
3420 localchanges = dsmodified | dsadded
3419 modified, added, removed = set(), set(), set()
3421 modified, added, removed = set(), set(), set()
3420 else:
3422 else:
3421 changes = repo.status(node1=parent, match=m)
3423 changes = repo.status(node1=parent, match=m)
3422 dsmodified = set(changes.modified)
3424 dsmodified = set(changes.modified)
3423 dsadded = set(changes.added)
3425 dsadded = set(changes.added)
3424 dsremoved = set(changes.removed)
3426 dsremoved = set(changes.removed)
3425 # store all local modifications, useful later for rename detection
3427 # store all local modifications, useful later for rename detection
3426 localchanges = dsmodified | dsadded
3428 localchanges = dsmodified | dsadded
3427
3429
3428 # only take into account for removes between wc and target
3430 # only take into account for removes between wc and target
3429 clean |= dsremoved - removed
3431 clean |= dsremoved - removed
3430 dsremoved &= removed
3432 dsremoved &= removed
3431 # distinct between dirstate remove and other
3433 # distinct between dirstate remove and other
3432 removed -= dsremoved
3434 removed -= dsremoved
3433
3435
3434 modadded = added & dsmodified
3436 modadded = added & dsmodified
3435 added -= modadded
3437 added -= modadded
3436
3438
3437 # tell newly modified apart.
3439 # tell newly modified apart.
3438 dsmodified &= modified
3440 dsmodified &= modified
3439 dsmodified |= modified & dsadded # dirstate added may need backup
3441 dsmodified |= modified & dsadded # dirstate added may need backup
3440 modified -= dsmodified
3442 modified -= dsmodified
3441
3443
3442 # We need to wait for some post-processing to update this set
3444 # We need to wait for some post-processing to update this set
3443 # before making the distinction. The dirstate will be used for
3445 # before making the distinction. The dirstate will be used for
3444 # that purpose.
3446 # that purpose.
3445 dsadded = added
3447 dsadded = added
3446
3448
3447 # in case of merge, files that are actually added can be reported as
3449 # in case of merge, files that are actually added can be reported as
3448 # modified, we need to post process the result
3450 # modified, we need to post process the result
3449 if p2 != nullid:
3451 if p2 != nullid:
3450 mergeadd = set(dsmodified)
3452 mergeadd = set(dsmodified)
3451 for path in dsmodified:
3453 for path in dsmodified:
3452 if path in mf:
3454 if path in mf:
3453 mergeadd.remove(path)
3455 mergeadd.remove(path)
3454 dsadded |= mergeadd
3456 dsadded |= mergeadd
3455 dsmodified -= mergeadd
3457 dsmodified -= mergeadd
3456
3458
3457 # if f is a rename, update `names` to also revert the source
3459 # if f is a rename, update `names` to also revert the source
3458 cwd = repo.getcwd()
3460 cwd = repo.getcwd()
3459 for f in localchanges:
3461 for f in localchanges:
3460 src = repo.dirstate.copied(f)
3462 src = repo.dirstate.copied(f)
3461 # XXX should we check for rename down to target node?
3463 # XXX should we check for rename down to target node?
3462 if src and src not in names and repo.dirstate[src] == 'r':
3464 if src and src not in names and repo.dirstate[src] == 'r':
3463 dsremoved.add(src)
3465 dsremoved.add(src)
3464 names[src] = (repo.pathto(src, cwd), True)
3466 names[src] = (repo.pathto(src, cwd), True)
3465
3467
3466 # determine the exact nature of the deleted changesets
3468 # determine the exact nature of the deleted changesets
3467 deladded = set(_deleted)
3469 deladded = set(_deleted)
3468 for path in _deleted:
3470 for path in _deleted:
3469 if path in mf:
3471 if path in mf:
3470 deladded.remove(path)
3472 deladded.remove(path)
3471 deleted = _deleted - deladded
3473 deleted = _deleted - deladded
3472
3474
3473 # distinguish between file to forget and the other
3475 # distinguish between file to forget and the other
3474 added = set()
3476 added = set()
3475 for abs in dsadded:
3477 for abs in dsadded:
3476 if repo.dirstate[abs] != 'a':
3478 if repo.dirstate[abs] != 'a':
3477 added.add(abs)
3479 added.add(abs)
3478 dsadded -= added
3480 dsadded -= added
3479
3481
3480 for abs in deladded:
3482 for abs in deladded:
3481 if repo.dirstate[abs] == 'a':
3483 if repo.dirstate[abs] == 'a':
3482 dsadded.add(abs)
3484 dsadded.add(abs)
3483 deladded -= dsadded
3485 deladded -= dsadded
3484
3486
3485 # For files marked as removed, we check if an unknown file is present at
3487 # For files marked as removed, we check if an unknown file is present at
3486 # the same path. If a such file exists it may need to be backed up.
3488 # the same path. If a such file exists it may need to be backed up.
3487 # Making the distinction at this stage helps have simpler backup
3489 # Making the distinction at this stage helps have simpler backup
3488 # logic.
3490 # logic.
3489 removunk = set()
3491 removunk = set()
3490 for abs in removed:
3492 for abs in removed:
3491 target = repo.wjoin(abs)
3493 target = repo.wjoin(abs)
3492 if os.path.lexists(target):
3494 if os.path.lexists(target):
3493 removunk.add(abs)
3495 removunk.add(abs)
3494 removed -= removunk
3496 removed -= removunk
3495
3497
3496 dsremovunk = set()
3498 dsremovunk = set()
3497 for abs in dsremoved:
3499 for abs in dsremoved:
3498 target = repo.wjoin(abs)
3500 target = repo.wjoin(abs)
3499 if os.path.lexists(target):
3501 if os.path.lexists(target):
3500 dsremovunk.add(abs)
3502 dsremovunk.add(abs)
3501 dsremoved -= dsremovunk
3503 dsremoved -= dsremovunk
3502
3504
3503 # action to be actually performed by revert
3505 # action to be actually performed by revert
3504 # (<list of file>, message>) tuple
3506 # (<list of file>, message>) tuple
3505 actions = {'revert': ([], _('reverting %s\n')),
3507 actions = {'revert': ([], _('reverting %s\n')),
3506 'add': ([], _('adding %s\n')),
3508 'add': ([], _('adding %s\n')),
3507 'remove': ([], _('removing %s\n')),
3509 'remove': ([], _('removing %s\n')),
3508 'drop': ([], _('removing %s\n')),
3510 'drop': ([], _('removing %s\n')),
3509 'forget': ([], _('forgetting %s\n')),
3511 'forget': ([], _('forgetting %s\n')),
3510 'undelete': ([], _('undeleting %s\n')),
3512 'undelete': ([], _('undeleting %s\n')),
3511 'noop': (None, _('no changes needed to %s\n')),
3513 'noop': (None, _('no changes needed to %s\n')),
3512 'unknown': (None, _('file not managed: %s\n')),
3514 'unknown': (None, _('file not managed: %s\n')),
3513 }
3515 }
3514
3516
3515 # "constant" that convey the backup strategy.
3517 # "constant" that convey the backup strategy.
3516 # All set to `discard` if `no-backup` is set do avoid checking
3518 # All set to `discard` if `no-backup` is set do avoid checking
3517 # no_backup lower in the code.
3519 # no_backup lower in the code.
3518 # These values are ordered for comparison purposes
3520 # These values are ordered for comparison purposes
3519 backupinteractive = 3 # do backup if interactively modified
3521 backupinteractive = 3 # do backup if interactively modified
3520 backup = 2 # unconditionally do backup
3522 backup = 2 # unconditionally do backup
3521 check = 1 # check if the existing file differs from target
3523 check = 1 # check if the existing file differs from target
3522 discard = 0 # never do backup
3524 discard = 0 # never do backup
3523 if opts.get('no_backup'):
3525 if opts.get('no_backup'):
3524 backupinteractive = backup = check = discard
3526 backupinteractive = backup = check = discard
3525 if interactive:
3527 if interactive:
3526 dsmodifiedbackup = backupinteractive
3528 dsmodifiedbackup = backupinteractive
3527 else:
3529 else:
3528 dsmodifiedbackup = backup
3530 dsmodifiedbackup = backup
3529 tobackup = set()
3531 tobackup = set()
3530
3532
3531 backupanddel = actions['remove']
3533 backupanddel = actions['remove']
3532 if not opts.get('no_backup'):
3534 if not opts.get('no_backup'):
3533 backupanddel = actions['drop']
3535 backupanddel = actions['drop']
3534
3536
3535 disptable = (
3537 disptable = (
3536 # dispatch table:
3538 # dispatch table:
3537 # file state
3539 # file state
3538 # action
3540 # action
3539 # make backup
3541 # make backup
3540
3542
3541 ## Sets that results that will change file on disk
3543 ## Sets that results that will change file on disk
3542 # Modified compared to target, no local change
3544 # Modified compared to target, no local change
3543 (modified, actions['revert'], discard),
3545 (modified, actions['revert'], discard),
3544 # Modified compared to target, but local file is deleted
3546 # Modified compared to target, but local file is deleted
3545 (deleted, actions['revert'], discard),
3547 (deleted, actions['revert'], discard),
3546 # Modified compared to target, local change
3548 # Modified compared to target, local change
3547 (dsmodified, actions['revert'], dsmodifiedbackup),
3549 (dsmodified, actions['revert'], dsmodifiedbackup),
3548 # Added since target
3550 # Added since target
3549 (added, actions['remove'], discard),
3551 (added, actions['remove'], discard),
3550 # Added in working directory
3552 # Added in working directory
3551 (dsadded, actions['forget'], discard),
3553 (dsadded, actions['forget'], discard),
3552 # Added since target, have local modification
3554 # Added since target, have local modification
3553 (modadded, backupanddel, backup),
3555 (modadded, backupanddel, backup),
3554 # Added since target but file is missing in working directory
3556 # Added since target but file is missing in working directory
3555 (deladded, actions['drop'], discard),
3557 (deladded, actions['drop'], discard),
3556 # Removed since target, before working copy parent
3558 # Removed since target, before working copy parent
3557 (removed, actions['add'], discard),
3559 (removed, actions['add'], discard),
3558 # Same as `removed` but an unknown file exists at the same path
3560 # Same as `removed` but an unknown file exists at the same path
3559 (removunk, actions['add'], check),
3561 (removunk, actions['add'], check),
3560 # Removed since targe, marked as such in working copy parent
3562 # Removed since targe, marked as such in working copy parent
3561 (dsremoved, actions['undelete'], discard),
3563 (dsremoved, actions['undelete'], discard),
3562 # Same as `dsremoved` but an unknown file exists at the same path
3564 # Same as `dsremoved` but an unknown file exists at the same path
3563 (dsremovunk, actions['undelete'], check),
3565 (dsremovunk, actions['undelete'], check),
3564 ## the following sets does not result in any file changes
3566 ## the following sets does not result in any file changes
3565 # File with no modification
3567 # File with no modification
3566 (clean, actions['noop'], discard),
3568 (clean, actions['noop'], discard),
3567 # Existing file, not tracked anywhere
3569 # Existing file, not tracked anywhere
3568 (unknown, actions['unknown'], discard),
3570 (unknown, actions['unknown'], discard),
3569 )
3571 )
3570
3572
3571 for abs, (rel, exact) in sorted(names.items()):
3573 for abs, (rel, exact) in sorted(names.items()):
3572 # target file to be touch on disk (relative to cwd)
3574 # target file to be touch on disk (relative to cwd)
3573 target = repo.wjoin(abs)
3575 target = repo.wjoin(abs)
3574 # search the entry in the dispatch table.
3576 # search the entry in the dispatch table.
3575 # if the file is in any of these sets, it was touched in the working
3577 # if the file is in any of these sets, it was touched in the working
3576 # directory parent and we are sure it needs to be reverted.
3578 # directory parent and we are sure it needs to be reverted.
3577 for table, (xlist, msg), dobackup in disptable:
3579 for table, (xlist, msg), dobackup in disptable:
3578 if abs not in table:
3580 if abs not in table:
3579 continue
3581 continue
3580 if xlist is not None:
3582 if xlist is not None:
3581 xlist.append(abs)
3583 xlist.append(abs)
3582 if dobackup:
3584 if dobackup:
3583 # If in interactive mode, don't automatically create
3585 # If in interactive mode, don't automatically create
3584 # .orig files (issue4793)
3586 # .orig files (issue4793)
3585 if dobackup == backupinteractive:
3587 if dobackup == backupinteractive:
3586 tobackup.add(abs)
3588 tobackup.add(abs)
3587 elif (backup <= dobackup or wctx[abs].cmp(ctx[abs])):
3589 elif (backup <= dobackup or wctx[abs].cmp(ctx[abs])):
3588 bakname = scmutil.origpath(ui, repo, rel)
3590 bakname = scmutil.origpath(ui, repo, rel)
3589 ui.note(_('saving current version of %s as %s\n') %
3591 ui.note(_('saving current version of %s as %s\n') %
3590 (rel, bakname))
3592 (rel, bakname))
3591 if not opts.get('dry_run'):
3593 if not opts.get('dry_run'):
3592 if interactive:
3594 if interactive:
3593 util.copyfile(target, bakname)
3595 util.copyfile(target, bakname)
3594 else:
3596 else:
3595 util.rename(target, bakname)
3597 util.rename(target, bakname)
3596 if ui.verbose or not exact:
3598 if ui.verbose or not exact:
3597 if not isinstance(msg, basestring):
3599 if not isinstance(msg, basestring):
3598 msg = msg(abs)
3600 msg = msg(abs)
3599 ui.status(msg % rel)
3601 ui.status(msg % rel)
3600 elif exact:
3602 elif exact:
3601 ui.warn(msg % rel)
3603 ui.warn(msg % rel)
3602 break
3604 break
3603
3605
3604 if not opts.get('dry_run'):
3606 if not opts.get('dry_run'):
3605 needdata = ('revert', 'add', 'undelete')
3607 needdata = ('revert', 'add', 'undelete')
3606 _revertprefetch(repo, ctx, *[actions[name][0] for name in needdata])
3608 _revertprefetch(repo, ctx, *[actions[name][0] for name in needdata])
3607 _performrevert(repo, parents, ctx, actions, interactive, tobackup)
3609 _performrevert(repo, parents, ctx, actions, interactive, tobackup)
3608
3610
3609 if targetsubs:
3611 if targetsubs:
3610 # Revert the subrepos on the revert list
3612 # Revert the subrepos on the revert list
3611 for sub in targetsubs:
3613 for sub in targetsubs:
3612 try:
3614 try:
3613 wctx.sub(sub).revert(ctx.substate[sub], *pats, **opts)
3615 wctx.sub(sub).revert(ctx.substate[sub], *pats, **opts)
3614 except KeyError:
3616 except KeyError:
3615 raise error.Abort("subrepository '%s' does not exist in %s!"
3617 raise error.Abort("subrepository '%s' does not exist in %s!"
3616 % (sub, short(ctx.node())))
3618 % (sub, short(ctx.node())))
3617
3619
3618 def _revertprefetch(repo, ctx, *files):
3620 def _revertprefetch(repo, ctx, *files):
3619 """Let extension changing the storage layer prefetch content"""
3621 """Let extension changing the storage layer prefetch content"""
3620 pass
3622 pass
3621
3623
3622 def _performrevert(repo, parents, ctx, actions, interactive=False,
3624 def _performrevert(repo, parents, ctx, actions, interactive=False,
3623 tobackup=None):
3625 tobackup=None):
3624 """function that actually perform all the actions computed for revert
3626 """function that actually perform all the actions computed for revert
3625
3627
3626 This is an independent function to let extension to plug in and react to
3628 This is an independent function to let extension to plug in and react to
3627 the imminent revert.
3629 the imminent revert.
3628
3630
3629 Make sure you have the working directory locked when calling this function.
3631 Make sure you have the working directory locked when calling this function.
3630 """
3632 """
3631 parent, p2 = parents
3633 parent, p2 = parents
3632 node = ctx.node()
3634 node = ctx.node()
3633 excluded_files = []
3635 excluded_files = []
3634 matcher_opts = {"exclude": excluded_files}
3636 matcher_opts = {"exclude": excluded_files}
3635
3637
3636 def checkout(f):
3638 def checkout(f):
3637 fc = ctx[f]
3639 fc = ctx[f]
3638 repo.wwrite(f, fc.data(), fc.flags())
3640 repo.wwrite(f, fc.data(), fc.flags())
3639
3641
3640 def doremove(f):
3642 def doremove(f):
3641 try:
3643 try:
3642 repo.wvfs.unlinkpath(f)
3644 repo.wvfs.unlinkpath(f)
3643 except OSError:
3645 except OSError:
3644 pass
3646 pass
3645 repo.dirstate.remove(f)
3647 repo.dirstate.remove(f)
3646
3648
3647 audit_path = pathutil.pathauditor(repo.root, cached=True)
3649 audit_path = pathutil.pathauditor(repo.root, cached=True)
3648 for f in actions['forget'][0]:
3650 for f in actions['forget'][0]:
3649 if interactive:
3651 if interactive:
3650 choice = repo.ui.promptchoice(
3652 choice = repo.ui.promptchoice(
3651 _("forget added file %s (Yn)?$$ &Yes $$ &No") % f)
3653 _("forget added file %s (Yn)?$$ &Yes $$ &No") % f)
3652 if choice == 0:
3654 if choice == 0:
3653 repo.dirstate.drop(f)
3655 repo.dirstate.drop(f)
3654 else:
3656 else:
3655 excluded_files.append(repo.wjoin(f))
3657 excluded_files.append(repo.wjoin(f))
3656 else:
3658 else:
3657 repo.dirstate.drop(f)
3659 repo.dirstate.drop(f)
3658 for f in actions['remove'][0]:
3660 for f in actions['remove'][0]:
3659 audit_path(f)
3661 audit_path(f)
3660 if interactive:
3662 if interactive:
3661 choice = repo.ui.promptchoice(
3663 choice = repo.ui.promptchoice(
3662 _("remove added file %s (Yn)?$$ &Yes $$ &No") % f)
3664 _("remove added file %s (Yn)?$$ &Yes $$ &No") % f)
3663 if choice == 0:
3665 if choice == 0:
3664 doremove(f)
3666 doremove(f)
3665 else:
3667 else:
3666 excluded_files.append(repo.wjoin(f))
3668 excluded_files.append(repo.wjoin(f))
3667 else:
3669 else:
3668 doremove(f)
3670 doremove(f)
3669 for f in actions['drop'][0]:
3671 for f in actions['drop'][0]:
3670 audit_path(f)
3672 audit_path(f)
3671 repo.dirstate.remove(f)
3673 repo.dirstate.remove(f)
3672
3674
3673 normal = None
3675 normal = None
3674 if node == parent:
3676 if node == parent:
3675 # We're reverting to our parent. If possible, we'd like status
3677 # We're reverting to our parent. If possible, we'd like status
3676 # to report the file as clean. We have to use normallookup for
3678 # to report the file as clean. We have to use normallookup for
3677 # merges to avoid losing information about merged/dirty files.
3679 # merges to avoid losing information about merged/dirty files.
3678 if p2 != nullid:
3680 if p2 != nullid:
3679 normal = repo.dirstate.normallookup
3681 normal = repo.dirstate.normallookup
3680 else:
3682 else:
3681 normal = repo.dirstate.normal
3683 normal = repo.dirstate.normal
3682
3684
3683 newlyaddedandmodifiedfiles = set()
3685 newlyaddedandmodifiedfiles = set()
3684 if interactive:
3686 if interactive:
3685 # Prompt the user for changes to revert
3687 # Prompt the user for changes to revert
3686 torevert = [repo.wjoin(f) for f in actions['revert'][0]]
3688 torevert = [repo.wjoin(f) for f in actions['revert'][0]]
3687 m = scmutil.match(ctx, torevert, matcher_opts)
3689 m = scmutil.match(ctx, torevert, matcher_opts)
3688 diffopts = patch.difffeatureopts(repo.ui, whitespace=True)
3690 diffopts = patch.difffeatureopts(repo.ui, whitespace=True)
3689 diffopts.nodates = True
3691 diffopts.nodates = True
3690 diffopts.git = True
3692 diffopts.git = True
3691 operation = 'discard'
3693 operation = 'discard'
3692 reversehunks = True
3694 reversehunks = True
3693 if node != parent:
3695 if node != parent:
3694 operation = 'revert'
3696 operation = 'revert'
3695 reversehunks = repo.ui.configbool('experimental',
3697 reversehunks = repo.ui.configbool('experimental',
3696 'revertalternateinteractivemode')
3698 'revertalternateinteractivemode')
3697 if reversehunks:
3699 if reversehunks:
3698 diff = patch.diff(repo, ctx.node(), None, m, opts=diffopts)
3700 diff = patch.diff(repo, ctx.node(), None, m, opts=diffopts)
3699 else:
3701 else:
3700 diff = patch.diff(repo, None, ctx.node(), m, opts=diffopts)
3702 diff = patch.diff(repo, None, ctx.node(), m, opts=diffopts)
3701 originalchunks = patch.parsepatch(diff)
3703 originalchunks = patch.parsepatch(diff)
3702
3704
3703 try:
3705 try:
3704
3706
3705 chunks, opts = recordfilter(repo.ui, originalchunks,
3707 chunks, opts = recordfilter(repo.ui, originalchunks,
3706 operation=operation)
3708 operation=operation)
3707 if reversehunks:
3709 if reversehunks:
3708 chunks = patch.reversehunks(chunks)
3710 chunks = patch.reversehunks(chunks)
3709
3711
3710 except patch.PatchError as err:
3712 except patch.PatchError as err:
3711 raise error.Abort(_('error parsing patch: %s') % err)
3713 raise error.Abort(_('error parsing patch: %s') % err)
3712
3714
3713 newlyaddedandmodifiedfiles = newandmodified(chunks, originalchunks)
3715 newlyaddedandmodifiedfiles = newandmodified(chunks, originalchunks)
3714 if tobackup is None:
3716 if tobackup is None:
3715 tobackup = set()
3717 tobackup = set()
3716 # Apply changes
3718 # Apply changes
3717 fp = stringio()
3719 fp = stringio()
3718 for c in chunks:
3720 for c in chunks:
3719 # Create a backup file only if this hunk should be backed up
3721 # Create a backup file only if this hunk should be backed up
3720 if ishunk(c) and c.header.filename() in tobackup:
3722 if ishunk(c) and c.header.filename() in tobackup:
3721 abs = c.header.filename()
3723 abs = c.header.filename()
3722 target = repo.wjoin(abs)
3724 target = repo.wjoin(abs)
3723 bakname = scmutil.origpath(repo.ui, repo, m.rel(abs))
3725 bakname = scmutil.origpath(repo.ui, repo, m.rel(abs))
3724 util.copyfile(target, bakname)
3726 util.copyfile(target, bakname)
3725 tobackup.remove(abs)
3727 tobackup.remove(abs)
3726 c.write(fp)
3728 c.write(fp)
3727 dopatch = fp.tell()
3729 dopatch = fp.tell()
3728 fp.seek(0)
3730 fp.seek(0)
3729 if dopatch:
3731 if dopatch:
3730 try:
3732 try:
3731 patch.internalpatch(repo.ui, repo, fp, 1, eolmode=None)
3733 patch.internalpatch(repo.ui, repo, fp, 1, eolmode=None)
3732 except patch.PatchError as err:
3734 except patch.PatchError as err:
3733 raise error.Abort(str(err))
3735 raise error.Abort(str(err))
3734 del fp
3736 del fp
3735 else:
3737 else:
3736 for f in actions['revert'][0]:
3738 for f in actions['revert'][0]:
3737 checkout(f)
3739 checkout(f)
3738 if normal:
3740 if normal:
3739 normal(f)
3741 normal(f)
3740
3742
3741 for f in actions['add'][0]:
3743 for f in actions['add'][0]:
3742 # Don't checkout modified files, they are already created by the diff
3744 # Don't checkout modified files, they are already created by the diff
3743 if f not in newlyaddedandmodifiedfiles:
3745 if f not in newlyaddedandmodifiedfiles:
3744 checkout(f)
3746 checkout(f)
3745 repo.dirstate.add(f)
3747 repo.dirstate.add(f)
3746
3748
3747 normal = repo.dirstate.normallookup
3749 normal = repo.dirstate.normallookup
3748 if node == parent and p2 == nullid:
3750 if node == parent and p2 == nullid:
3749 normal = repo.dirstate.normal
3751 normal = repo.dirstate.normal
3750 for f in actions['undelete'][0]:
3752 for f in actions['undelete'][0]:
3751 checkout(f)
3753 checkout(f)
3752 normal(f)
3754 normal(f)
3753
3755
3754 copied = copies.pathcopies(repo[parent], ctx)
3756 copied = copies.pathcopies(repo[parent], ctx)
3755
3757
3756 for f in actions['add'][0] + actions['undelete'][0] + actions['revert'][0]:
3758 for f in actions['add'][0] + actions['undelete'][0] + actions['revert'][0]:
3757 if f in copied:
3759 if f in copied:
3758 repo.dirstate.copy(copied[f], f)
3760 repo.dirstate.copy(copied[f], f)
3759
3761
3760 class command(registrar.command):
3762 class command(registrar.command):
3761 def _doregister(self, func, name, *args, **kwargs):
3763 def _doregister(self, func, name, *args, **kwargs):
3762 func._deprecatedregistrar = True # flag for deprecwarn in extensions.py
3764 func._deprecatedregistrar = True # flag for deprecwarn in extensions.py
3763 return super(command, self)._doregister(func, name, *args, **kwargs)
3765 return super(command, self)._doregister(func, name, *args, **kwargs)
3764
3766
3765 # a list of (ui, repo, otherpeer, opts, missing) functions called by
3767 # a list of (ui, repo, otherpeer, opts, missing) functions called by
3766 # commands.outgoing. "missing" is "missing" of the result of
3768 # commands.outgoing. "missing" is "missing" of the result of
3767 # "findcommonoutgoing()"
3769 # "findcommonoutgoing()"
3768 outgoinghooks = util.hooks()
3770 outgoinghooks = util.hooks()
3769
3771
3770 # a list of (ui, repo) functions called by commands.summary
3772 # a list of (ui, repo) functions called by commands.summary
3771 summaryhooks = util.hooks()
3773 summaryhooks = util.hooks()
3772
3774
3773 # a list of (ui, repo, opts, changes) functions called by commands.summary.
3775 # a list of (ui, repo, opts, changes) functions called by commands.summary.
3774 #
3776 #
3775 # functions should return tuple of booleans below, if 'changes' is None:
3777 # functions should return tuple of booleans below, if 'changes' is None:
3776 # (whether-incomings-are-needed, whether-outgoings-are-needed)
3778 # (whether-incomings-are-needed, whether-outgoings-are-needed)
3777 #
3779 #
3778 # otherwise, 'changes' is a tuple of tuples below:
3780 # otherwise, 'changes' is a tuple of tuples below:
3779 # - (sourceurl, sourcebranch, sourcepeer, incoming)
3781 # - (sourceurl, sourcebranch, sourcepeer, incoming)
3780 # - (desturl, destbranch, destpeer, outgoing)
3782 # - (desturl, destbranch, destpeer, outgoing)
3781 summaryremotehooks = util.hooks()
3783 summaryremotehooks = util.hooks()
3782
3784
3783 # A list of state files kept by multistep operations like graft.
3785 # A list of state files kept by multistep operations like graft.
3784 # Since graft cannot be aborted, it is considered 'clearable' by update.
3786 # Since graft cannot be aborted, it is considered 'clearable' by update.
3785 # note: bisect is intentionally excluded
3787 # note: bisect is intentionally excluded
3786 # (state file, clearable, allowcommit, error, hint)
3788 # (state file, clearable, allowcommit, error, hint)
3787 unfinishedstates = [
3789 unfinishedstates = [
3788 ('graftstate', True, False, _('graft in progress'),
3790 ('graftstate', True, False, _('graft in progress'),
3789 _("use 'hg graft --continue' or 'hg update' to abort")),
3791 _("use 'hg graft --continue' or 'hg update' to abort")),
3790 ('updatestate', True, False, _('last update was interrupted'),
3792 ('updatestate', True, False, _('last update was interrupted'),
3791 _("use 'hg update' to get a consistent checkout"))
3793 _("use 'hg update' to get a consistent checkout"))
3792 ]
3794 ]
3793
3795
3794 def checkunfinished(repo, commit=False):
3796 def checkunfinished(repo, commit=False):
3795 '''Look for an unfinished multistep operation, like graft, and abort
3797 '''Look for an unfinished multistep operation, like graft, and abort
3796 if found. It's probably good to check this right before
3798 if found. It's probably good to check this right before
3797 bailifchanged().
3799 bailifchanged().
3798 '''
3800 '''
3799 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3801 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3800 if commit and allowcommit:
3802 if commit and allowcommit:
3801 continue
3803 continue
3802 if repo.vfs.exists(f):
3804 if repo.vfs.exists(f):
3803 raise error.Abort(msg, hint=hint)
3805 raise error.Abort(msg, hint=hint)
3804
3806
3805 def clearunfinished(repo):
3807 def clearunfinished(repo):
3806 '''Check for unfinished operations (as above), and clear the ones
3808 '''Check for unfinished operations (as above), and clear the ones
3807 that are clearable.
3809 that are clearable.
3808 '''
3810 '''
3809 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3811 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3810 if not clearable and repo.vfs.exists(f):
3812 if not clearable and repo.vfs.exists(f):
3811 raise error.Abort(msg, hint=hint)
3813 raise error.Abort(msg, hint=hint)
3812 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3814 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3813 if clearable and repo.vfs.exists(f):
3815 if clearable and repo.vfs.exists(f):
3814 util.unlink(repo.vfs.join(f))
3816 util.unlink(repo.vfs.join(f))
3815
3817
3816 afterresolvedstates = [
3818 afterresolvedstates = [
3817 ('graftstate',
3819 ('graftstate',
3818 _('hg graft --continue')),
3820 _('hg graft --continue')),
3819 ]
3821 ]
3820
3822
3821 def howtocontinue(repo):
3823 def howtocontinue(repo):
3822 '''Check for an unfinished operation and return the command to finish
3824 '''Check for an unfinished operation and return the command to finish
3823 it.
3825 it.
3824
3826
3825 afterresolvedstates tuples define a .hg/{file} and the corresponding
3827 afterresolvedstates tuples define a .hg/{file} and the corresponding
3826 command needed to finish it.
3828 command needed to finish it.
3827
3829
3828 Returns a (msg, warning) tuple. 'msg' is a string and 'warning' is
3830 Returns a (msg, warning) tuple. 'msg' is a string and 'warning' is
3829 a boolean.
3831 a boolean.
3830 '''
3832 '''
3831 contmsg = _("continue: %s")
3833 contmsg = _("continue: %s")
3832 for f, msg in afterresolvedstates:
3834 for f, msg in afterresolvedstates:
3833 if repo.vfs.exists(f):
3835 if repo.vfs.exists(f):
3834 return contmsg % msg, True
3836 return contmsg % msg, True
3835 if repo[None].dirty(missing=True, merge=False, branch=False):
3837 if repo[None].dirty(missing=True, merge=False, branch=False):
3836 return contmsg % _("hg commit"), False
3838 return contmsg % _("hg commit"), False
3837 return None, None
3839 return None, None
3838
3840
3839 def checkafterresolved(repo):
3841 def checkafterresolved(repo):
3840 '''Inform the user about the next action after completing hg resolve
3842 '''Inform the user about the next action after completing hg resolve
3841
3843
3842 If there's a matching afterresolvedstates, howtocontinue will yield
3844 If there's a matching afterresolvedstates, howtocontinue will yield
3843 repo.ui.warn as the reporter.
3845 repo.ui.warn as the reporter.
3844
3846
3845 Otherwise, it will yield repo.ui.note.
3847 Otherwise, it will yield repo.ui.note.
3846 '''
3848 '''
3847 msg, warning = howtocontinue(repo)
3849 msg, warning = howtocontinue(repo)
3848 if msg is not None:
3850 if msg is not None:
3849 if warning:
3851 if warning:
3850 repo.ui.warn("%s\n" % msg)
3852 repo.ui.warn("%s\n" % msg)
3851 else:
3853 else:
3852 repo.ui.note("%s\n" % msg)
3854 repo.ui.note("%s\n" % msg)
3853
3855
3854 def wrongtooltocontinue(repo, task):
3856 def wrongtooltocontinue(repo, task):
3855 '''Raise an abort suggesting how to properly continue if there is an
3857 '''Raise an abort suggesting how to properly continue if there is an
3856 active task.
3858 active task.
3857
3859
3858 Uses howtocontinue() to find the active task.
3860 Uses howtocontinue() to find the active task.
3859
3861
3860 If there's no task (repo.ui.note for 'hg commit'), it does not offer
3862 If there's no task (repo.ui.note for 'hg commit'), it does not offer
3861 a hint.
3863 a hint.
3862 '''
3864 '''
3863 after = howtocontinue(repo)
3865 after = howtocontinue(repo)
3864 hint = None
3866 hint = None
3865 if after[1]:
3867 if after[1]:
3866 hint = after[0]
3868 hint = after[0]
3867 raise error.Abort(_('no %s in progress') % task, hint=hint)
3869 raise error.Abort(_('no %s in progress') % task, hint=hint)
@@ -1,2465 +1,2468 b''
1 The Mercurial system uses a set of configuration files to control
1 The Mercurial system uses a set of configuration files to control
2 aspects of its behavior.
2 aspects of its behavior.
3
3
4 Troubleshooting
4 Troubleshooting
5 ===============
5 ===============
6
6
7 If you're having problems with your configuration,
7 If you're having problems with your configuration,
8 :hg:`config --debug` can help you understand what is introducing
8 :hg:`config --debug` can help you understand what is introducing
9 a setting into your environment.
9 a setting into your environment.
10
10
11 See :hg:`help config.syntax` and :hg:`help config.files`
11 See :hg:`help config.syntax` and :hg:`help config.files`
12 for information about how and where to override things.
12 for information about how and where to override things.
13
13
14 Structure
14 Structure
15 =========
15 =========
16
16
17 The configuration files use a simple ini-file format. A configuration
17 The configuration files use a simple ini-file format. A configuration
18 file consists of sections, led by a ``[section]`` header and followed
18 file consists of sections, led by a ``[section]`` header and followed
19 by ``name = value`` entries::
19 by ``name = value`` entries::
20
20
21 [ui]
21 [ui]
22 username = Firstname Lastname <firstname.lastname@example.net>
22 username = Firstname Lastname <firstname.lastname@example.net>
23 verbose = True
23 verbose = True
24
24
25 The above entries will be referred to as ``ui.username`` and
25 The above entries will be referred to as ``ui.username`` and
26 ``ui.verbose``, respectively. See :hg:`help config.syntax`.
26 ``ui.verbose``, respectively. See :hg:`help config.syntax`.
27
27
28 Files
28 Files
29 =====
29 =====
30
30
31 Mercurial reads configuration data from several files, if they exist.
31 Mercurial reads configuration data from several files, if they exist.
32 These files do not exist by default and you will have to create the
32 These files do not exist by default and you will have to create the
33 appropriate configuration files yourself:
33 appropriate configuration files yourself:
34
34
35 Local configuration is put into the per-repository ``<repo>/.hg/hgrc`` file.
35 Local configuration is put into the per-repository ``<repo>/.hg/hgrc`` file.
36
36
37 Global configuration like the username setting is typically put into:
37 Global configuration like the username setting is typically put into:
38
38
39 .. container:: windows
39 .. container:: windows
40
40
41 - ``%USERPROFILE%\mercurial.ini`` (on Windows)
41 - ``%USERPROFILE%\mercurial.ini`` (on Windows)
42
42
43 .. container:: unix.plan9
43 .. container:: unix.plan9
44
44
45 - ``$HOME/.hgrc`` (on Unix, Plan9)
45 - ``$HOME/.hgrc`` (on Unix, Plan9)
46
46
47 The names of these files depend on the system on which Mercurial is
47 The names of these files depend on the system on which Mercurial is
48 installed. ``*.rc`` files from a single directory are read in
48 installed. ``*.rc`` files from a single directory are read in
49 alphabetical order, later ones overriding earlier ones. Where multiple
49 alphabetical order, later ones overriding earlier ones. Where multiple
50 paths are given below, settings from earlier paths override later
50 paths are given below, settings from earlier paths override later
51 ones.
51 ones.
52
52
53 .. container:: verbose.unix
53 .. container:: verbose.unix
54
54
55 On Unix, the following files are consulted:
55 On Unix, the following files are consulted:
56
56
57 - ``<repo>/.hg/hgrc`` (per-repository)
57 - ``<repo>/.hg/hgrc`` (per-repository)
58 - ``$HOME/.hgrc`` (per-user)
58 - ``$HOME/.hgrc`` (per-user)
59 - ``${XDG_CONFIG_HOME:-$HOME/.config}/hg/hgrc`` (per-user)
59 - ``${XDG_CONFIG_HOME:-$HOME/.config}/hg/hgrc`` (per-user)
60 - ``<install-root>/etc/mercurial/hgrc`` (per-installation)
60 - ``<install-root>/etc/mercurial/hgrc`` (per-installation)
61 - ``<install-root>/etc/mercurial/hgrc.d/*.rc`` (per-installation)
61 - ``<install-root>/etc/mercurial/hgrc.d/*.rc`` (per-installation)
62 - ``/etc/mercurial/hgrc`` (per-system)
62 - ``/etc/mercurial/hgrc`` (per-system)
63 - ``/etc/mercurial/hgrc.d/*.rc`` (per-system)
63 - ``/etc/mercurial/hgrc.d/*.rc`` (per-system)
64 - ``<internal>/default.d/*.rc`` (defaults)
64 - ``<internal>/default.d/*.rc`` (defaults)
65
65
66 .. container:: verbose.windows
66 .. container:: verbose.windows
67
67
68 On Windows, the following files are consulted:
68 On Windows, the following files are consulted:
69
69
70 - ``<repo>/.hg/hgrc`` (per-repository)
70 - ``<repo>/.hg/hgrc`` (per-repository)
71 - ``%USERPROFILE%\.hgrc`` (per-user)
71 - ``%USERPROFILE%\.hgrc`` (per-user)
72 - ``%USERPROFILE%\Mercurial.ini`` (per-user)
72 - ``%USERPROFILE%\Mercurial.ini`` (per-user)
73 - ``%HOME%\.hgrc`` (per-user)
73 - ``%HOME%\.hgrc`` (per-user)
74 - ``%HOME%\Mercurial.ini`` (per-user)
74 - ``%HOME%\Mercurial.ini`` (per-user)
75 - ``HKEY_LOCAL_MACHINE\SOFTWARE\Mercurial`` (per-installation)
75 - ``HKEY_LOCAL_MACHINE\SOFTWARE\Mercurial`` (per-installation)
76 - ``<install-dir>\hgrc.d\*.rc`` (per-installation)
76 - ``<install-dir>\hgrc.d\*.rc`` (per-installation)
77 - ``<install-dir>\Mercurial.ini`` (per-installation)
77 - ``<install-dir>\Mercurial.ini`` (per-installation)
78 - ``<internal>/default.d/*.rc`` (defaults)
78 - ``<internal>/default.d/*.rc`` (defaults)
79
79
80 .. note::
80 .. note::
81
81
82 The registry key ``HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Mercurial``
82 The registry key ``HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Mercurial``
83 is used when running 32-bit Python on 64-bit Windows.
83 is used when running 32-bit Python on 64-bit Windows.
84
84
85 .. container:: windows
85 .. container:: windows
86
86
87 On Windows 9x, ``%HOME%`` is replaced by ``%APPDATA%``.
87 On Windows 9x, ``%HOME%`` is replaced by ``%APPDATA%``.
88
88
89 .. container:: verbose.plan9
89 .. container:: verbose.plan9
90
90
91 On Plan9, the following files are consulted:
91 On Plan9, the following files are consulted:
92
92
93 - ``<repo>/.hg/hgrc`` (per-repository)
93 - ``<repo>/.hg/hgrc`` (per-repository)
94 - ``$home/lib/hgrc`` (per-user)
94 - ``$home/lib/hgrc`` (per-user)
95 - ``<install-root>/lib/mercurial/hgrc`` (per-installation)
95 - ``<install-root>/lib/mercurial/hgrc`` (per-installation)
96 - ``<install-root>/lib/mercurial/hgrc.d/*.rc`` (per-installation)
96 - ``<install-root>/lib/mercurial/hgrc.d/*.rc`` (per-installation)
97 - ``/lib/mercurial/hgrc`` (per-system)
97 - ``/lib/mercurial/hgrc`` (per-system)
98 - ``/lib/mercurial/hgrc.d/*.rc`` (per-system)
98 - ``/lib/mercurial/hgrc.d/*.rc`` (per-system)
99 - ``<internal>/default.d/*.rc`` (defaults)
99 - ``<internal>/default.d/*.rc`` (defaults)
100
100
101 Per-repository configuration options only apply in a
101 Per-repository configuration options only apply in a
102 particular repository. This file is not version-controlled, and
102 particular repository. This file is not version-controlled, and
103 will not get transferred during a "clone" operation. Options in
103 will not get transferred during a "clone" operation. Options in
104 this file override options in all other configuration files.
104 this file override options in all other configuration files.
105
105
106 .. container:: unix.plan9
106 .. container:: unix.plan9
107
107
108 On Plan 9 and Unix, most of this file will be ignored if it doesn't
108 On Plan 9 and Unix, most of this file will be ignored if it doesn't
109 belong to a trusted user or to a trusted group. See
109 belong to a trusted user or to a trusted group. See
110 :hg:`help config.trusted` for more details.
110 :hg:`help config.trusted` for more details.
111
111
112 Per-user configuration file(s) are for the user running Mercurial. Options
112 Per-user configuration file(s) are for the user running Mercurial. Options
113 in these files apply to all Mercurial commands executed by this user in any
113 in these files apply to all Mercurial commands executed by this user in any
114 directory. Options in these files override per-system and per-installation
114 directory. Options in these files override per-system and per-installation
115 options.
115 options.
116
116
117 Per-installation configuration files are searched for in the
117 Per-installation configuration files are searched for in the
118 directory where Mercurial is installed. ``<install-root>`` is the
118 directory where Mercurial is installed. ``<install-root>`` is the
119 parent directory of the **hg** executable (or symlink) being run.
119 parent directory of the **hg** executable (or symlink) being run.
120
120
121 .. container:: unix.plan9
121 .. container:: unix.plan9
122
122
123 For example, if installed in ``/shared/tools/bin/hg``, Mercurial
123 For example, if installed in ``/shared/tools/bin/hg``, Mercurial
124 will look in ``/shared/tools/etc/mercurial/hgrc``. Options in these
124 will look in ``/shared/tools/etc/mercurial/hgrc``. Options in these
125 files apply to all Mercurial commands executed by any user in any
125 files apply to all Mercurial commands executed by any user in any
126 directory.
126 directory.
127
127
128 Per-installation configuration files are for the system on
128 Per-installation configuration files are for the system on
129 which Mercurial is running. Options in these files apply to all
129 which Mercurial is running. Options in these files apply to all
130 Mercurial commands executed by any user in any directory. Registry
130 Mercurial commands executed by any user in any directory. Registry
131 keys contain PATH-like strings, every part of which must reference
131 keys contain PATH-like strings, every part of which must reference
132 a ``Mercurial.ini`` file or be a directory where ``*.rc`` files will
132 a ``Mercurial.ini`` file or be a directory where ``*.rc`` files will
133 be read. Mercurial checks each of these locations in the specified
133 be read. Mercurial checks each of these locations in the specified
134 order until one or more configuration files are detected.
134 order until one or more configuration files are detected.
135
135
136 Per-system configuration files are for the system on which Mercurial
136 Per-system configuration files are for the system on which Mercurial
137 is running. Options in these files apply to all Mercurial commands
137 is running. Options in these files apply to all Mercurial commands
138 executed by any user in any directory. Options in these files
138 executed by any user in any directory. Options in these files
139 override per-installation options.
139 override per-installation options.
140
140
141 Mercurial comes with some default configuration. The default configuration
141 Mercurial comes with some default configuration. The default configuration
142 files are installed with Mercurial and will be overwritten on upgrades. Default
142 files are installed with Mercurial and will be overwritten on upgrades. Default
143 configuration files should never be edited by users or administrators but can
143 configuration files should never be edited by users or administrators but can
144 be overridden in other configuration files. So far the directory only contains
144 be overridden in other configuration files. So far the directory only contains
145 merge tool configuration but packagers can also put other default configuration
145 merge tool configuration but packagers can also put other default configuration
146 there.
146 there.
147
147
148 Syntax
148 Syntax
149 ======
149 ======
150
150
151 A configuration file consists of sections, led by a ``[section]`` header
151 A configuration file consists of sections, led by a ``[section]`` header
152 and followed by ``name = value`` entries (sometimes called
152 and followed by ``name = value`` entries (sometimes called
153 ``configuration keys``)::
153 ``configuration keys``)::
154
154
155 [spam]
155 [spam]
156 eggs=ham
156 eggs=ham
157 green=
157 green=
158 eggs
158 eggs
159
159
160 Each line contains one entry. If the lines that follow are indented,
160 Each line contains one entry. If the lines that follow are indented,
161 they are treated as continuations of that entry. Leading whitespace is
161 they are treated as continuations of that entry. Leading whitespace is
162 removed from values. Empty lines are skipped. Lines beginning with
162 removed from values. Empty lines are skipped. Lines beginning with
163 ``#`` or ``;`` are ignored and may be used to provide comments.
163 ``#`` or ``;`` are ignored and may be used to provide comments.
164
164
165 Configuration keys can be set multiple times, in which case Mercurial
165 Configuration keys can be set multiple times, in which case Mercurial
166 will use the value that was configured last. As an example::
166 will use the value that was configured last. As an example::
167
167
168 [spam]
168 [spam]
169 eggs=large
169 eggs=large
170 ham=serrano
170 ham=serrano
171 eggs=small
171 eggs=small
172
172
173 This would set the configuration key named ``eggs`` to ``small``.
173 This would set the configuration key named ``eggs`` to ``small``.
174
174
175 It is also possible to define a section multiple times. A section can
175 It is also possible to define a section multiple times. A section can
176 be redefined on the same and/or on different configuration files. For
176 be redefined on the same and/or on different configuration files. For
177 example::
177 example::
178
178
179 [foo]
179 [foo]
180 eggs=large
180 eggs=large
181 ham=serrano
181 ham=serrano
182 eggs=small
182 eggs=small
183
183
184 [bar]
184 [bar]
185 eggs=ham
185 eggs=ham
186 green=
186 green=
187 eggs
187 eggs
188
188
189 [foo]
189 [foo]
190 ham=prosciutto
190 ham=prosciutto
191 eggs=medium
191 eggs=medium
192 bread=toasted
192 bread=toasted
193
193
194 This would set the ``eggs``, ``ham``, and ``bread`` configuration keys
194 This would set the ``eggs``, ``ham``, and ``bread`` configuration keys
195 of the ``foo`` section to ``medium``, ``prosciutto``, and ``toasted``,
195 of the ``foo`` section to ``medium``, ``prosciutto``, and ``toasted``,
196 respectively. As you can see there only thing that matters is the last
196 respectively. As you can see there only thing that matters is the last
197 value that was set for each of the configuration keys.
197 value that was set for each of the configuration keys.
198
198
199 If a configuration key is set multiple times in different
199 If a configuration key is set multiple times in different
200 configuration files the final value will depend on the order in which
200 configuration files the final value will depend on the order in which
201 the different configuration files are read, with settings from earlier
201 the different configuration files are read, with settings from earlier
202 paths overriding later ones as described on the ``Files`` section
202 paths overriding later ones as described on the ``Files`` section
203 above.
203 above.
204
204
205 A line of the form ``%include file`` will include ``file`` into the
205 A line of the form ``%include file`` will include ``file`` into the
206 current configuration file. The inclusion is recursive, which means
206 current configuration file. The inclusion is recursive, which means
207 that included files can include other files. Filenames are relative to
207 that included files can include other files. Filenames are relative to
208 the configuration file in which the ``%include`` directive is found.
208 the configuration file in which the ``%include`` directive is found.
209 Environment variables and ``~user`` constructs are expanded in
209 Environment variables and ``~user`` constructs are expanded in
210 ``file``. This lets you do something like::
210 ``file``. This lets you do something like::
211
211
212 %include ~/.hgrc.d/$HOST.rc
212 %include ~/.hgrc.d/$HOST.rc
213
213
214 to include a different configuration file on each computer you use.
214 to include a different configuration file on each computer you use.
215
215
216 A line with ``%unset name`` will remove ``name`` from the current
216 A line with ``%unset name`` will remove ``name`` from the current
217 section, if it has been set previously.
217 section, if it has been set previously.
218
218
219 The values are either free-form text strings, lists of text strings,
219 The values are either free-form text strings, lists of text strings,
220 or Boolean values. Boolean values can be set to true using any of "1",
220 or Boolean values. Boolean values can be set to true using any of "1",
221 "yes", "true", or "on" and to false using "0", "no", "false", or "off"
221 "yes", "true", or "on" and to false using "0", "no", "false", or "off"
222 (all case insensitive).
222 (all case insensitive).
223
223
224 List values are separated by whitespace or comma, except when values are
224 List values are separated by whitespace or comma, except when values are
225 placed in double quotation marks::
225 placed in double quotation marks::
226
226
227 allow_read = "John Doe, PhD", brian, betty
227 allow_read = "John Doe, PhD", brian, betty
228
228
229 Quotation marks can be escaped by prefixing them with a backslash. Only
229 Quotation marks can be escaped by prefixing them with a backslash. Only
230 quotation marks at the beginning of a word is counted as a quotation
230 quotation marks at the beginning of a word is counted as a quotation
231 (e.g., ``foo"bar baz`` is the list of ``foo"bar`` and ``baz``).
231 (e.g., ``foo"bar baz`` is the list of ``foo"bar`` and ``baz``).
232
232
233 Sections
233 Sections
234 ========
234 ========
235
235
236 This section describes the different sections that may appear in a
236 This section describes the different sections that may appear in a
237 Mercurial configuration file, the purpose of each section, its possible
237 Mercurial configuration file, the purpose of each section, its possible
238 keys, and their possible values.
238 keys, and their possible values.
239
239
240 ``alias``
240 ``alias``
241 ---------
241 ---------
242
242
243 Defines command aliases.
243 Defines command aliases.
244
244
245 Aliases allow you to define your own commands in terms of other
245 Aliases allow you to define your own commands in terms of other
246 commands (or aliases), optionally including arguments. Positional
246 commands (or aliases), optionally including arguments. Positional
247 arguments in the form of ``$1``, ``$2``, etc. in the alias definition
247 arguments in the form of ``$1``, ``$2``, etc. in the alias definition
248 are expanded by Mercurial before execution. Positional arguments not
248 are expanded by Mercurial before execution. Positional arguments not
249 already used by ``$N`` in the definition are put at the end of the
249 already used by ``$N`` in the definition are put at the end of the
250 command to be executed.
250 command to be executed.
251
251
252 Alias definitions consist of lines of the form::
252 Alias definitions consist of lines of the form::
253
253
254 <alias> = <command> [<argument>]...
254 <alias> = <command> [<argument>]...
255
255
256 For example, this definition::
256 For example, this definition::
257
257
258 latest = log --limit 5
258 latest = log --limit 5
259
259
260 creates a new command ``latest`` that shows only the five most recent
260 creates a new command ``latest`` that shows only the five most recent
261 changesets. You can define subsequent aliases using earlier ones::
261 changesets. You can define subsequent aliases using earlier ones::
262
262
263 stable5 = latest -b stable
263 stable5 = latest -b stable
264
264
265 .. note::
265 .. note::
266
266
267 It is possible to create aliases with the same names as
267 It is possible to create aliases with the same names as
268 existing commands, which will then override the original
268 existing commands, which will then override the original
269 definitions. This is almost always a bad idea!
269 definitions. This is almost always a bad idea!
270
270
271 An alias can start with an exclamation point (``!``) to make it a
271 An alias can start with an exclamation point (``!``) to make it a
272 shell alias. A shell alias is executed with the shell and will let you
272 shell alias. A shell alias is executed with the shell and will let you
273 run arbitrary commands. As an example, ::
273 run arbitrary commands. As an example, ::
274
274
275 echo = !echo $@
275 echo = !echo $@
276
276
277 will let you do ``hg echo foo`` to have ``foo`` printed in your
277 will let you do ``hg echo foo`` to have ``foo`` printed in your
278 terminal. A better example might be::
278 terminal. A better example might be::
279
279
280 purge = !$HG status --no-status --unknown -0 re: | xargs -0 rm -f
280 purge = !$HG status --no-status --unknown -0 re: | xargs -0 rm -f
281
281
282 which will make ``hg purge`` delete all unknown files in the
282 which will make ``hg purge`` delete all unknown files in the
283 repository in the same manner as the purge extension.
283 repository in the same manner as the purge extension.
284
284
285 Positional arguments like ``$1``, ``$2``, etc. in the alias definition
285 Positional arguments like ``$1``, ``$2``, etc. in the alias definition
286 expand to the command arguments. Unmatched arguments are
286 expand to the command arguments. Unmatched arguments are
287 removed. ``$0`` expands to the alias name and ``$@`` expands to all
287 removed. ``$0`` expands to the alias name and ``$@`` expands to all
288 arguments separated by a space. ``"$@"`` (with quotes) expands to all
288 arguments separated by a space. ``"$@"`` (with quotes) expands to all
289 arguments quoted individually and separated by a space. These expansions
289 arguments quoted individually and separated by a space. These expansions
290 happen before the command is passed to the shell.
290 happen before the command is passed to the shell.
291
291
292 Shell aliases are executed in an environment where ``$HG`` expands to
292 Shell aliases are executed in an environment where ``$HG`` expands to
293 the path of the Mercurial that was used to execute the alias. This is
293 the path of the Mercurial that was used to execute the alias. This is
294 useful when you want to call further Mercurial commands in a shell
294 useful when you want to call further Mercurial commands in a shell
295 alias, as was done above for the purge alias. In addition,
295 alias, as was done above for the purge alias. In addition,
296 ``$HG_ARGS`` expands to the arguments given to Mercurial. In the ``hg
296 ``$HG_ARGS`` expands to the arguments given to Mercurial. In the ``hg
297 echo foo`` call above, ``$HG_ARGS`` would expand to ``echo foo``.
297 echo foo`` call above, ``$HG_ARGS`` would expand to ``echo foo``.
298
298
299 .. note::
299 .. note::
300
300
301 Some global configuration options such as ``-R`` are
301 Some global configuration options such as ``-R`` are
302 processed before shell aliases and will thus not be passed to
302 processed before shell aliases and will thus not be passed to
303 aliases.
303 aliases.
304
304
305
305
306 ``annotate``
306 ``annotate``
307 ------------
307 ------------
308
308
309 Settings used when displaying file annotations. All values are
309 Settings used when displaying file annotations. All values are
310 Booleans and default to False. See :hg:`help config.diff` for
310 Booleans and default to False. See :hg:`help config.diff` for
311 related options for the diff command.
311 related options for the diff command.
312
312
313 ``ignorews``
313 ``ignorews``
314 Ignore white space when comparing lines.
314 Ignore white space when comparing lines.
315
315
316 ``ignorewseol``
317 Ignore white space at the end of a line when comparing lines.
318
316 ``ignorewsamount``
319 ``ignorewsamount``
317 Ignore changes in the amount of white space.
320 Ignore changes in the amount of white space.
318
321
319 ``ignoreblanklines``
322 ``ignoreblanklines``
320 Ignore changes whose lines are all blank.
323 Ignore changes whose lines are all blank.
321
324
322
325
323 ``auth``
326 ``auth``
324 --------
327 --------
325
328
326 Authentication credentials and other authentication-like configuration
329 Authentication credentials and other authentication-like configuration
327 for HTTP connections. This section allows you to store usernames and
330 for HTTP connections. This section allows you to store usernames and
328 passwords for use when logging *into* HTTP servers. See
331 passwords for use when logging *into* HTTP servers. See
329 :hg:`help config.web` if you want to configure *who* can login to
332 :hg:`help config.web` if you want to configure *who* can login to
330 your HTTP server.
333 your HTTP server.
331
334
332 The following options apply to all hosts.
335 The following options apply to all hosts.
333
336
334 ``cookiefile``
337 ``cookiefile``
335 Path to a file containing HTTP cookie lines. Cookies matching a
338 Path to a file containing HTTP cookie lines. Cookies matching a
336 host will be sent automatically.
339 host will be sent automatically.
337
340
338 The file format uses the Mozilla cookies.txt format, which defines cookies
341 The file format uses the Mozilla cookies.txt format, which defines cookies
339 on their own lines. Each line contains 7 fields delimited by the tab
342 on their own lines. Each line contains 7 fields delimited by the tab
340 character (domain, is_domain_cookie, path, is_secure, expires, name,
343 character (domain, is_domain_cookie, path, is_secure, expires, name,
341 value). For more info, do an Internet search for "Netscape cookies.txt
344 value). For more info, do an Internet search for "Netscape cookies.txt
342 format."
345 format."
343
346
344 Note: the cookies parser does not handle port numbers on domains. You
347 Note: the cookies parser does not handle port numbers on domains. You
345 will need to remove ports from the domain for the cookie to be recognized.
348 will need to remove ports from the domain for the cookie to be recognized.
346 This could result in a cookie being disclosed to an unwanted server.
349 This could result in a cookie being disclosed to an unwanted server.
347
350
348 The cookies file is read-only.
351 The cookies file is read-only.
349
352
350 Other options in this section are grouped by name and have the following
353 Other options in this section are grouped by name and have the following
351 format::
354 format::
352
355
353 <name>.<argument> = <value>
356 <name>.<argument> = <value>
354
357
355 where ``<name>`` is used to group arguments into authentication
358 where ``<name>`` is used to group arguments into authentication
356 entries. Example::
359 entries. Example::
357
360
358 foo.prefix = hg.intevation.de/mercurial
361 foo.prefix = hg.intevation.de/mercurial
359 foo.username = foo
362 foo.username = foo
360 foo.password = bar
363 foo.password = bar
361 foo.schemes = http https
364 foo.schemes = http https
362
365
363 bar.prefix = secure.example.org
366 bar.prefix = secure.example.org
364 bar.key = path/to/file.key
367 bar.key = path/to/file.key
365 bar.cert = path/to/file.cert
368 bar.cert = path/to/file.cert
366 bar.schemes = https
369 bar.schemes = https
367
370
368 Supported arguments:
371 Supported arguments:
369
372
370 ``prefix``
373 ``prefix``
371 Either ``*`` or a URI prefix with or without the scheme part.
374 Either ``*`` or a URI prefix with or without the scheme part.
372 The authentication entry with the longest matching prefix is used
375 The authentication entry with the longest matching prefix is used
373 (where ``*`` matches everything and counts as a match of length
376 (where ``*`` matches everything and counts as a match of length
374 1). If the prefix doesn't include a scheme, the match is performed
377 1). If the prefix doesn't include a scheme, the match is performed
375 against the URI with its scheme stripped as well, and the schemes
378 against the URI with its scheme stripped as well, and the schemes
376 argument, q.v., is then subsequently consulted.
379 argument, q.v., is then subsequently consulted.
377
380
378 ``username``
381 ``username``
379 Optional. Username to authenticate with. If not given, and the
382 Optional. Username to authenticate with. If not given, and the
380 remote site requires basic or digest authentication, the user will
383 remote site requires basic or digest authentication, the user will
381 be prompted for it. Environment variables are expanded in the
384 be prompted for it. Environment variables are expanded in the
382 username letting you do ``foo.username = $USER``. If the URI
385 username letting you do ``foo.username = $USER``. If the URI
383 includes a username, only ``[auth]`` entries with a matching
386 includes a username, only ``[auth]`` entries with a matching
384 username or without a username will be considered.
387 username or without a username will be considered.
385
388
386 ``password``
389 ``password``
387 Optional. Password to authenticate with. If not given, and the
390 Optional. Password to authenticate with. If not given, and the
388 remote site requires basic or digest authentication, the user
391 remote site requires basic or digest authentication, the user
389 will be prompted for it.
392 will be prompted for it.
390
393
391 ``key``
394 ``key``
392 Optional. PEM encoded client certificate key file. Environment
395 Optional. PEM encoded client certificate key file. Environment
393 variables are expanded in the filename.
396 variables are expanded in the filename.
394
397
395 ``cert``
398 ``cert``
396 Optional. PEM encoded client certificate chain file. Environment
399 Optional. PEM encoded client certificate chain file. Environment
397 variables are expanded in the filename.
400 variables are expanded in the filename.
398
401
399 ``schemes``
402 ``schemes``
400 Optional. Space separated list of URI schemes to use this
403 Optional. Space separated list of URI schemes to use this
401 authentication entry with. Only used if the prefix doesn't include
404 authentication entry with. Only used if the prefix doesn't include
402 a scheme. Supported schemes are http and https. They will match
405 a scheme. Supported schemes are http and https. They will match
403 static-http and static-https respectively, as well.
406 static-http and static-https respectively, as well.
404 (default: https)
407 (default: https)
405
408
406 If no suitable authentication entry is found, the user is prompted
409 If no suitable authentication entry is found, the user is prompted
407 for credentials as usual if required by the remote.
410 for credentials as usual if required by the remote.
408
411
409 ``color``
412 ``color``
410 ---------
413 ---------
411
414
412 Configure the Mercurial color mode. For details about how to define your custom
415 Configure the Mercurial color mode. For details about how to define your custom
413 effect and style see :hg:`help color`.
416 effect and style see :hg:`help color`.
414
417
415 ``mode``
418 ``mode``
416 String: control the method used to output color. One of ``auto``, ``ansi``,
419 String: control the method used to output color. One of ``auto``, ``ansi``,
417 ``win32``, ``terminfo`` or ``debug``. In auto mode, Mercurial will
420 ``win32``, ``terminfo`` or ``debug``. In auto mode, Mercurial will
418 use ANSI mode by default (or win32 mode prior to Windows 10) if it detects a
421 use ANSI mode by default (or win32 mode prior to Windows 10) if it detects a
419 terminal. Any invalid value will disable color.
422 terminal. Any invalid value will disable color.
420
423
421 ``pagermode``
424 ``pagermode``
422 String: optional override of ``color.mode`` used with pager.
425 String: optional override of ``color.mode`` used with pager.
423
426
424 On some systems, terminfo mode may cause problems when using
427 On some systems, terminfo mode may cause problems when using
425 color with ``less -R`` as a pager program. less with the -R option
428 color with ``less -R`` as a pager program. less with the -R option
426 will only display ECMA-48 color codes, and terminfo mode may sometimes
429 will only display ECMA-48 color codes, and terminfo mode may sometimes
427 emit codes that less doesn't understand. You can work around this by
430 emit codes that less doesn't understand. You can work around this by
428 either using ansi mode (or auto mode), or by using less -r (which will
431 either using ansi mode (or auto mode), or by using less -r (which will
429 pass through all terminal control codes, not just color control
432 pass through all terminal control codes, not just color control
430 codes).
433 codes).
431
434
432 On some systems (such as MSYS in Windows), the terminal may support
435 On some systems (such as MSYS in Windows), the terminal may support
433 a different color mode than the pager program.
436 a different color mode than the pager program.
434
437
435 ``commands``
438 ``commands``
436 ------------
439 ------------
437
440
438 ``status.relative``
441 ``status.relative``
439 Make paths in :hg:`status` output relative to the current directory.
442 Make paths in :hg:`status` output relative to the current directory.
440 (default: False)
443 (default: False)
441
444
442 ``update.requiredest``
445 ``update.requiredest``
443 Require that the user pass a destination when running :hg:`update`.
446 Require that the user pass a destination when running :hg:`update`.
444 For example, :hg:`update .::` will be allowed, but a plain :hg:`update`
447 For example, :hg:`update .::` will be allowed, but a plain :hg:`update`
445 will be disallowed.
448 will be disallowed.
446 (default: False)
449 (default: False)
447
450
448 ``committemplate``
451 ``committemplate``
449 ------------------
452 ------------------
450
453
451 ``changeset``
454 ``changeset``
452 String: configuration in this section is used as the template to
455 String: configuration in this section is used as the template to
453 customize the text shown in the editor when committing.
456 customize the text shown in the editor when committing.
454
457
455 In addition to pre-defined template keywords, commit log specific one
458 In addition to pre-defined template keywords, commit log specific one
456 below can be used for customization:
459 below can be used for customization:
457
460
458 ``extramsg``
461 ``extramsg``
459 String: Extra message (typically 'Leave message empty to abort
462 String: Extra message (typically 'Leave message empty to abort
460 commit.'). This may be changed by some commands or extensions.
463 commit.'). This may be changed by some commands or extensions.
461
464
462 For example, the template configuration below shows as same text as
465 For example, the template configuration below shows as same text as
463 one shown by default::
466 one shown by default::
464
467
465 [committemplate]
468 [committemplate]
466 changeset = {desc}\n\n
469 changeset = {desc}\n\n
467 HG: Enter commit message. Lines beginning with 'HG:' are removed.
470 HG: Enter commit message. Lines beginning with 'HG:' are removed.
468 HG: {extramsg}
471 HG: {extramsg}
469 HG: --
472 HG: --
470 HG: user: {author}\n{ifeq(p2rev, "-1", "",
473 HG: user: {author}\n{ifeq(p2rev, "-1", "",
471 "HG: branch merge\n")
474 "HG: branch merge\n")
472 }HG: branch '{branch}'\n{if(activebookmark,
475 }HG: branch '{branch}'\n{if(activebookmark,
473 "HG: bookmark '{activebookmark}'\n") }{subrepos %
476 "HG: bookmark '{activebookmark}'\n") }{subrepos %
474 "HG: subrepo {subrepo}\n" }{file_adds %
477 "HG: subrepo {subrepo}\n" }{file_adds %
475 "HG: added {file}\n" }{file_mods %
478 "HG: added {file}\n" }{file_mods %
476 "HG: changed {file}\n" }{file_dels %
479 "HG: changed {file}\n" }{file_dels %
477 "HG: removed {file}\n" }{if(files, "",
480 "HG: removed {file}\n" }{if(files, "",
478 "HG: no files changed\n")}
481 "HG: no files changed\n")}
479
482
480 ``diff()``
483 ``diff()``
481 String: show the diff (see :hg:`help templates` for detail)
484 String: show the diff (see :hg:`help templates` for detail)
482
485
483 Sometimes it is helpful to show the diff of the changeset in the editor without
486 Sometimes it is helpful to show the diff of the changeset in the editor without
484 having to prefix 'HG: ' to each line so that highlighting works correctly. For
487 having to prefix 'HG: ' to each line so that highlighting works correctly. For
485 this, Mercurial provides a special string which will ignore everything below
488 this, Mercurial provides a special string which will ignore everything below
486 it::
489 it::
487
490
488 HG: ------------------------ >8 ------------------------
491 HG: ------------------------ >8 ------------------------
489
492
490 For example, the template configuration below will show the diff below the
493 For example, the template configuration below will show the diff below the
491 extra message::
494 extra message::
492
495
493 [committemplate]
496 [committemplate]
494 changeset = {desc}\n\n
497 changeset = {desc}\n\n
495 HG: Enter commit message. Lines beginning with 'HG:' are removed.
498 HG: Enter commit message. Lines beginning with 'HG:' are removed.
496 HG: {extramsg}
499 HG: {extramsg}
497 HG: ------------------------ >8 ------------------------
500 HG: ------------------------ >8 ------------------------
498 HG: Do not touch the line above.
501 HG: Do not touch the line above.
499 HG: Everything below will be removed.
502 HG: Everything below will be removed.
500 {diff()}
503 {diff()}
501
504
502 .. note::
505 .. note::
503
506
504 For some problematic encodings (see :hg:`help win32mbcs` for
507 For some problematic encodings (see :hg:`help win32mbcs` for
505 detail), this customization should be configured carefully, to
508 detail), this customization should be configured carefully, to
506 avoid showing broken characters.
509 avoid showing broken characters.
507
510
508 For example, if a multibyte character ending with backslash (0x5c) is
511 For example, if a multibyte character ending with backslash (0x5c) is
509 followed by the ASCII character 'n' in the customized template,
512 followed by the ASCII character 'n' in the customized template,
510 the sequence of backslash and 'n' is treated as line-feed unexpectedly
513 the sequence of backslash and 'n' is treated as line-feed unexpectedly
511 (and the multibyte character is broken, too).
514 (and the multibyte character is broken, too).
512
515
513 Customized template is used for commands below (``--edit`` may be
516 Customized template is used for commands below (``--edit`` may be
514 required):
517 required):
515
518
516 - :hg:`backout`
519 - :hg:`backout`
517 - :hg:`commit`
520 - :hg:`commit`
518 - :hg:`fetch` (for merge commit only)
521 - :hg:`fetch` (for merge commit only)
519 - :hg:`graft`
522 - :hg:`graft`
520 - :hg:`histedit`
523 - :hg:`histedit`
521 - :hg:`import`
524 - :hg:`import`
522 - :hg:`qfold`, :hg:`qnew` and :hg:`qrefresh`
525 - :hg:`qfold`, :hg:`qnew` and :hg:`qrefresh`
523 - :hg:`rebase`
526 - :hg:`rebase`
524 - :hg:`shelve`
527 - :hg:`shelve`
525 - :hg:`sign`
528 - :hg:`sign`
526 - :hg:`tag`
529 - :hg:`tag`
527 - :hg:`transplant`
530 - :hg:`transplant`
528
531
529 Configuring items below instead of ``changeset`` allows showing
532 Configuring items below instead of ``changeset`` allows showing
530 customized message only for specific actions, or showing different
533 customized message only for specific actions, or showing different
531 messages for each action.
534 messages for each action.
532
535
533 - ``changeset.backout`` for :hg:`backout`
536 - ``changeset.backout`` for :hg:`backout`
534 - ``changeset.commit.amend.merge`` for :hg:`commit --amend` on merges
537 - ``changeset.commit.amend.merge`` for :hg:`commit --amend` on merges
535 - ``changeset.commit.amend.normal`` for :hg:`commit --amend` on other
538 - ``changeset.commit.amend.normal`` for :hg:`commit --amend` on other
536 - ``changeset.commit.normal.merge`` for :hg:`commit` on merges
539 - ``changeset.commit.normal.merge`` for :hg:`commit` on merges
537 - ``changeset.commit.normal.normal`` for :hg:`commit` on other
540 - ``changeset.commit.normal.normal`` for :hg:`commit` on other
538 - ``changeset.fetch`` for :hg:`fetch` (impling merge commit)
541 - ``changeset.fetch`` for :hg:`fetch` (impling merge commit)
539 - ``changeset.gpg.sign`` for :hg:`sign`
542 - ``changeset.gpg.sign`` for :hg:`sign`
540 - ``changeset.graft`` for :hg:`graft`
543 - ``changeset.graft`` for :hg:`graft`
541 - ``changeset.histedit.edit`` for ``edit`` of :hg:`histedit`
544 - ``changeset.histedit.edit`` for ``edit`` of :hg:`histedit`
542 - ``changeset.histedit.fold`` for ``fold`` of :hg:`histedit`
545 - ``changeset.histedit.fold`` for ``fold`` of :hg:`histedit`
543 - ``changeset.histedit.mess`` for ``mess`` of :hg:`histedit`
546 - ``changeset.histedit.mess`` for ``mess`` of :hg:`histedit`
544 - ``changeset.histedit.pick`` for ``pick`` of :hg:`histedit`
547 - ``changeset.histedit.pick`` for ``pick`` of :hg:`histedit`
545 - ``changeset.import.bypass`` for :hg:`import --bypass`
548 - ``changeset.import.bypass`` for :hg:`import --bypass`
546 - ``changeset.import.normal.merge`` for :hg:`import` on merges
549 - ``changeset.import.normal.merge`` for :hg:`import` on merges
547 - ``changeset.import.normal.normal`` for :hg:`import` on other
550 - ``changeset.import.normal.normal`` for :hg:`import` on other
548 - ``changeset.mq.qnew`` for :hg:`qnew`
551 - ``changeset.mq.qnew`` for :hg:`qnew`
549 - ``changeset.mq.qfold`` for :hg:`qfold`
552 - ``changeset.mq.qfold`` for :hg:`qfold`
550 - ``changeset.mq.qrefresh`` for :hg:`qrefresh`
553 - ``changeset.mq.qrefresh`` for :hg:`qrefresh`
551 - ``changeset.rebase.collapse`` for :hg:`rebase --collapse`
554 - ``changeset.rebase.collapse`` for :hg:`rebase --collapse`
552 - ``changeset.rebase.merge`` for :hg:`rebase` on merges
555 - ``changeset.rebase.merge`` for :hg:`rebase` on merges
553 - ``changeset.rebase.normal`` for :hg:`rebase` on other
556 - ``changeset.rebase.normal`` for :hg:`rebase` on other
554 - ``changeset.shelve.shelve`` for :hg:`shelve`
557 - ``changeset.shelve.shelve`` for :hg:`shelve`
555 - ``changeset.tag.add`` for :hg:`tag` without ``--remove``
558 - ``changeset.tag.add`` for :hg:`tag` without ``--remove``
556 - ``changeset.tag.remove`` for :hg:`tag --remove`
559 - ``changeset.tag.remove`` for :hg:`tag --remove`
557 - ``changeset.transplant.merge`` for :hg:`transplant` on merges
560 - ``changeset.transplant.merge`` for :hg:`transplant` on merges
558 - ``changeset.transplant.normal`` for :hg:`transplant` on other
561 - ``changeset.transplant.normal`` for :hg:`transplant` on other
559
562
560 These dot-separated lists of names are treated as hierarchical ones.
563 These dot-separated lists of names are treated as hierarchical ones.
561 For example, ``changeset.tag.remove`` customizes the commit message
564 For example, ``changeset.tag.remove`` customizes the commit message
562 only for :hg:`tag --remove`, but ``changeset.tag`` customizes the
565 only for :hg:`tag --remove`, but ``changeset.tag`` customizes the
563 commit message for :hg:`tag` regardless of ``--remove`` option.
566 commit message for :hg:`tag` regardless of ``--remove`` option.
564
567
565 When the external editor is invoked for a commit, the corresponding
568 When the external editor is invoked for a commit, the corresponding
566 dot-separated list of names without the ``changeset.`` prefix
569 dot-separated list of names without the ``changeset.`` prefix
567 (e.g. ``commit.normal.normal``) is in the ``HGEDITFORM`` environment
570 (e.g. ``commit.normal.normal``) is in the ``HGEDITFORM`` environment
568 variable.
571 variable.
569
572
570 In this section, items other than ``changeset`` can be referred from
573 In this section, items other than ``changeset`` can be referred from
571 others. For example, the configuration to list committed files up
574 others. For example, the configuration to list committed files up
572 below can be referred as ``{listupfiles}``::
575 below can be referred as ``{listupfiles}``::
573
576
574 [committemplate]
577 [committemplate]
575 listupfiles = {file_adds %
578 listupfiles = {file_adds %
576 "HG: added {file}\n" }{file_mods %
579 "HG: added {file}\n" }{file_mods %
577 "HG: changed {file}\n" }{file_dels %
580 "HG: changed {file}\n" }{file_dels %
578 "HG: removed {file}\n" }{if(files, "",
581 "HG: removed {file}\n" }{if(files, "",
579 "HG: no files changed\n")}
582 "HG: no files changed\n")}
580
583
581 ``decode/encode``
584 ``decode/encode``
582 -----------------
585 -----------------
583
586
584 Filters for transforming files on checkout/checkin. This would
587 Filters for transforming files on checkout/checkin. This would
585 typically be used for newline processing or other
588 typically be used for newline processing or other
586 localization/canonicalization of files.
589 localization/canonicalization of files.
587
590
588 Filters consist of a filter pattern followed by a filter command.
591 Filters consist of a filter pattern followed by a filter command.
589 Filter patterns are globs by default, rooted at the repository root.
592 Filter patterns are globs by default, rooted at the repository root.
590 For example, to match any file ending in ``.txt`` in the root
593 For example, to match any file ending in ``.txt`` in the root
591 directory only, use the pattern ``*.txt``. To match any file ending
594 directory only, use the pattern ``*.txt``. To match any file ending
592 in ``.c`` anywhere in the repository, use the pattern ``**.c``.
595 in ``.c`` anywhere in the repository, use the pattern ``**.c``.
593 For each file only the first matching filter applies.
596 For each file only the first matching filter applies.
594
597
595 The filter command can start with a specifier, either ``pipe:`` or
598 The filter command can start with a specifier, either ``pipe:`` or
596 ``tempfile:``. If no specifier is given, ``pipe:`` is used by default.
599 ``tempfile:``. If no specifier is given, ``pipe:`` is used by default.
597
600
598 A ``pipe:`` command must accept data on stdin and return the transformed
601 A ``pipe:`` command must accept data on stdin and return the transformed
599 data on stdout.
602 data on stdout.
600
603
601 Pipe example::
604 Pipe example::
602
605
603 [encode]
606 [encode]
604 # uncompress gzip files on checkin to improve delta compression
607 # uncompress gzip files on checkin to improve delta compression
605 # note: not necessarily a good idea, just an example
608 # note: not necessarily a good idea, just an example
606 *.gz = pipe: gunzip
609 *.gz = pipe: gunzip
607
610
608 [decode]
611 [decode]
609 # recompress gzip files when writing them to the working dir (we
612 # recompress gzip files when writing them to the working dir (we
610 # can safely omit "pipe:", because it's the default)
613 # can safely omit "pipe:", because it's the default)
611 *.gz = gzip
614 *.gz = gzip
612
615
613 A ``tempfile:`` command is a template. The string ``INFILE`` is replaced
616 A ``tempfile:`` command is a template. The string ``INFILE`` is replaced
614 with the name of a temporary file that contains the data to be
617 with the name of a temporary file that contains the data to be
615 filtered by the command. The string ``OUTFILE`` is replaced with the name
618 filtered by the command. The string ``OUTFILE`` is replaced with the name
616 of an empty temporary file, where the filtered data must be written by
619 of an empty temporary file, where the filtered data must be written by
617 the command.
620 the command.
618
621
619 .. container:: windows
622 .. container:: windows
620
623
621 .. note::
624 .. note::
622
625
623 The tempfile mechanism is recommended for Windows systems,
626 The tempfile mechanism is recommended for Windows systems,
624 where the standard shell I/O redirection operators often have
627 where the standard shell I/O redirection operators often have
625 strange effects and may corrupt the contents of your files.
628 strange effects and may corrupt the contents of your files.
626
629
627 This filter mechanism is used internally by the ``eol`` extension to
630 This filter mechanism is used internally by the ``eol`` extension to
628 translate line ending characters between Windows (CRLF) and Unix (LF)
631 translate line ending characters between Windows (CRLF) and Unix (LF)
629 format. We suggest you use the ``eol`` extension for convenience.
632 format. We suggest you use the ``eol`` extension for convenience.
630
633
631
634
632 ``defaults``
635 ``defaults``
633 ------------
636 ------------
634
637
635 (defaults are deprecated. Don't use them. Use aliases instead.)
638 (defaults are deprecated. Don't use them. Use aliases instead.)
636
639
637 Use the ``[defaults]`` section to define command defaults, i.e. the
640 Use the ``[defaults]`` section to define command defaults, i.e. the
638 default options/arguments to pass to the specified commands.
641 default options/arguments to pass to the specified commands.
639
642
640 The following example makes :hg:`log` run in verbose mode, and
643 The following example makes :hg:`log` run in verbose mode, and
641 :hg:`status` show only the modified files, by default::
644 :hg:`status` show only the modified files, by default::
642
645
643 [defaults]
646 [defaults]
644 log = -v
647 log = -v
645 status = -m
648 status = -m
646
649
647 The actual commands, instead of their aliases, must be used when
650 The actual commands, instead of their aliases, must be used when
648 defining command defaults. The command defaults will also be applied
651 defining command defaults. The command defaults will also be applied
649 to the aliases of the commands defined.
652 to the aliases of the commands defined.
650
653
651
654
652 ``diff``
655 ``diff``
653 --------
656 --------
654
657
655 Settings used when displaying diffs. Everything except for ``unified``
658 Settings used when displaying diffs. Everything except for ``unified``
656 is a Boolean and defaults to False. See :hg:`help config.annotate`
659 is a Boolean and defaults to False. See :hg:`help config.annotate`
657 for related options for the annotate command.
660 for related options for the annotate command.
658
661
659 ``git``
662 ``git``
660 Use git extended diff format.
663 Use git extended diff format.
661
664
662 ``nobinary``
665 ``nobinary``
663 Omit git binary patches.
666 Omit git binary patches.
664
667
665 ``nodates``
668 ``nodates``
666 Don't include dates in diff headers.
669 Don't include dates in diff headers.
667
670
668 ``noprefix``
671 ``noprefix``
669 Omit 'a/' and 'b/' prefixes from filenames. Ignored in plain mode.
672 Omit 'a/' and 'b/' prefixes from filenames. Ignored in plain mode.
670
673
671 ``showfunc``
674 ``showfunc``
672 Show which function each change is in.
675 Show which function each change is in.
673
676
674 ``ignorews``
677 ``ignorews``
675 Ignore white space when comparing lines.
678 Ignore white space when comparing lines.
676
679
677 ``ignorewsamount``
680 ``ignorewsamount``
678 Ignore changes in the amount of white space.
681 Ignore changes in the amount of white space.
679
682
680 ``ignoreblanklines``
683 ``ignoreblanklines``
681 Ignore changes whose lines are all blank.
684 Ignore changes whose lines are all blank.
682
685
683 ``unified``
686 ``unified``
684 Number of lines of context to show.
687 Number of lines of context to show.
685
688
686 ``email``
689 ``email``
687 ---------
690 ---------
688
691
689 Settings for extensions that send email messages.
692 Settings for extensions that send email messages.
690
693
691 ``from``
694 ``from``
692 Optional. Email address to use in "From" header and SMTP envelope
695 Optional. Email address to use in "From" header and SMTP envelope
693 of outgoing messages.
696 of outgoing messages.
694
697
695 ``to``
698 ``to``
696 Optional. Comma-separated list of recipients' email addresses.
699 Optional. Comma-separated list of recipients' email addresses.
697
700
698 ``cc``
701 ``cc``
699 Optional. Comma-separated list of carbon copy recipients'
702 Optional. Comma-separated list of carbon copy recipients'
700 email addresses.
703 email addresses.
701
704
702 ``bcc``
705 ``bcc``
703 Optional. Comma-separated list of blind carbon copy recipients'
706 Optional. Comma-separated list of blind carbon copy recipients'
704 email addresses.
707 email addresses.
705
708
706 ``method``
709 ``method``
707 Optional. Method to use to send email messages. If value is ``smtp``
710 Optional. Method to use to send email messages. If value is ``smtp``
708 (default), use SMTP (see the ``[smtp]`` section for configuration).
711 (default), use SMTP (see the ``[smtp]`` section for configuration).
709 Otherwise, use as name of program to run that acts like sendmail
712 Otherwise, use as name of program to run that acts like sendmail
710 (takes ``-f`` option for sender, list of recipients on command line,
713 (takes ``-f`` option for sender, list of recipients on command line,
711 message on stdin). Normally, setting this to ``sendmail`` or
714 message on stdin). Normally, setting this to ``sendmail`` or
712 ``/usr/sbin/sendmail`` is enough to use sendmail to send messages.
715 ``/usr/sbin/sendmail`` is enough to use sendmail to send messages.
713
716
714 ``charsets``
717 ``charsets``
715 Optional. Comma-separated list of character sets considered
718 Optional. Comma-separated list of character sets considered
716 convenient for recipients. Addresses, headers, and parts not
719 convenient for recipients. Addresses, headers, and parts not
717 containing patches of outgoing messages will be encoded in the
720 containing patches of outgoing messages will be encoded in the
718 first character set to which conversion from local encoding
721 first character set to which conversion from local encoding
719 (``$HGENCODING``, ``ui.fallbackencoding``) succeeds. If correct
722 (``$HGENCODING``, ``ui.fallbackencoding``) succeeds. If correct
720 conversion fails, the text in question is sent as is.
723 conversion fails, the text in question is sent as is.
721 (default: '')
724 (default: '')
722
725
723 Order of outgoing email character sets:
726 Order of outgoing email character sets:
724
727
725 1. ``us-ascii``: always first, regardless of settings
728 1. ``us-ascii``: always first, regardless of settings
726 2. ``email.charsets``: in order given by user
729 2. ``email.charsets``: in order given by user
727 3. ``ui.fallbackencoding``: if not in email.charsets
730 3. ``ui.fallbackencoding``: if not in email.charsets
728 4. ``$HGENCODING``: if not in email.charsets
731 4. ``$HGENCODING``: if not in email.charsets
729 5. ``utf-8``: always last, regardless of settings
732 5. ``utf-8``: always last, regardless of settings
730
733
731 Email example::
734 Email example::
732
735
733 [email]
736 [email]
734 from = Joseph User <joe.user@example.com>
737 from = Joseph User <joe.user@example.com>
735 method = /usr/sbin/sendmail
738 method = /usr/sbin/sendmail
736 # charsets for western Europeans
739 # charsets for western Europeans
737 # us-ascii, utf-8 omitted, as they are tried first and last
740 # us-ascii, utf-8 omitted, as they are tried first and last
738 charsets = iso-8859-1, iso-8859-15, windows-1252
741 charsets = iso-8859-1, iso-8859-15, windows-1252
739
742
740
743
741 ``extensions``
744 ``extensions``
742 --------------
745 --------------
743
746
744 Mercurial has an extension mechanism for adding new features. To
747 Mercurial has an extension mechanism for adding new features. To
745 enable an extension, create an entry for it in this section.
748 enable an extension, create an entry for it in this section.
746
749
747 If you know that the extension is already in Python's search path,
750 If you know that the extension is already in Python's search path,
748 you can give the name of the module, followed by ``=``, with nothing
751 you can give the name of the module, followed by ``=``, with nothing
749 after the ``=``.
752 after the ``=``.
750
753
751 Otherwise, give a name that you choose, followed by ``=``, followed by
754 Otherwise, give a name that you choose, followed by ``=``, followed by
752 the path to the ``.py`` file (including the file name extension) that
755 the path to the ``.py`` file (including the file name extension) that
753 defines the extension.
756 defines the extension.
754
757
755 To explicitly disable an extension that is enabled in an hgrc of
758 To explicitly disable an extension that is enabled in an hgrc of
756 broader scope, prepend its path with ``!``, as in ``foo = !/ext/path``
759 broader scope, prepend its path with ``!``, as in ``foo = !/ext/path``
757 or ``foo = !`` when path is not supplied.
760 or ``foo = !`` when path is not supplied.
758
761
759 Example for ``~/.hgrc``::
762 Example for ``~/.hgrc``::
760
763
761 [extensions]
764 [extensions]
762 # (the churn extension will get loaded from Mercurial's path)
765 # (the churn extension will get loaded from Mercurial's path)
763 churn =
766 churn =
764 # (this extension will get loaded from the file specified)
767 # (this extension will get loaded from the file specified)
765 myfeature = ~/.hgext/myfeature.py
768 myfeature = ~/.hgext/myfeature.py
766
769
767
770
768 ``format``
771 ``format``
769 ----------
772 ----------
770
773
771 ``usegeneraldelta``
774 ``usegeneraldelta``
772 Enable or disable the "generaldelta" repository format which improves
775 Enable or disable the "generaldelta" repository format which improves
773 repository compression by allowing "revlog" to store delta against arbitrary
776 repository compression by allowing "revlog" to store delta against arbitrary
774 revision instead of the previous stored one. This provides significant
777 revision instead of the previous stored one. This provides significant
775 improvement for repositories with branches.
778 improvement for repositories with branches.
776
779
777 Repositories with this on-disk format require Mercurial version 1.9.
780 Repositories with this on-disk format require Mercurial version 1.9.
778
781
779 Enabled by default.
782 Enabled by default.
780
783
781 ``dotencode``
784 ``dotencode``
782 Enable or disable the "dotencode" repository format which enhances
785 Enable or disable the "dotencode" repository format which enhances
783 the "fncache" repository format (which has to be enabled to use
786 the "fncache" repository format (which has to be enabled to use
784 dotencode) to avoid issues with filenames starting with ._ on
787 dotencode) to avoid issues with filenames starting with ._ on
785 Mac OS X and spaces on Windows.
788 Mac OS X and spaces on Windows.
786
789
787 Repositories with this on-disk format require Mercurial version 1.7.
790 Repositories with this on-disk format require Mercurial version 1.7.
788
791
789 Enabled by default.
792 Enabled by default.
790
793
791 ``usefncache``
794 ``usefncache``
792 Enable or disable the "fncache" repository format which enhances
795 Enable or disable the "fncache" repository format which enhances
793 the "store" repository format (which has to be enabled to use
796 the "store" repository format (which has to be enabled to use
794 fncache) to allow longer filenames and avoids using Windows
797 fncache) to allow longer filenames and avoids using Windows
795 reserved names, e.g. "nul".
798 reserved names, e.g. "nul".
796
799
797 Repositories with this on-disk format require Mercurial version 1.1.
800 Repositories with this on-disk format require Mercurial version 1.1.
798
801
799 Enabled by default.
802 Enabled by default.
800
803
801 ``usestore``
804 ``usestore``
802 Enable or disable the "store" repository format which improves
805 Enable or disable the "store" repository format which improves
803 compatibility with systems that fold case or otherwise mangle
806 compatibility with systems that fold case or otherwise mangle
804 filenames. Disabling this option will allow you to store longer filenames
807 filenames. Disabling this option will allow you to store longer filenames
805 in some situations at the expense of compatibility.
808 in some situations at the expense of compatibility.
806
809
807 Repositories with this on-disk format require Mercurial version 0.9.4.
810 Repositories with this on-disk format require Mercurial version 0.9.4.
808
811
809 Enabled by default.
812 Enabled by default.
810
813
811 ``graph``
814 ``graph``
812 ---------
815 ---------
813
816
814 Web graph view configuration. This section let you change graph
817 Web graph view configuration. This section let you change graph
815 elements display properties by branches, for instance to make the
818 elements display properties by branches, for instance to make the
816 ``default`` branch stand out.
819 ``default`` branch stand out.
817
820
818 Each line has the following format::
821 Each line has the following format::
819
822
820 <branch>.<argument> = <value>
823 <branch>.<argument> = <value>
821
824
822 where ``<branch>`` is the name of the branch being
825 where ``<branch>`` is the name of the branch being
823 customized. Example::
826 customized. Example::
824
827
825 [graph]
828 [graph]
826 # 2px width
829 # 2px width
827 default.width = 2
830 default.width = 2
828 # red color
831 # red color
829 default.color = FF0000
832 default.color = FF0000
830
833
831 Supported arguments:
834 Supported arguments:
832
835
833 ``width``
836 ``width``
834 Set branch edges width in pixels.
837 Set branch edges width in pixels.
835
838
836 ``color``
839 ``color``
837 Set branch edges color in hexadecimal RGB notation.
840 Set branch edges color in hexadecimal RGB notation.
838
841
839 ``hooks``
842 ``hooks``
840 ---------
843 ---------
841
844
842 Commands or Python functions that get automatically executed by
845 Commands or Python functions that get automatically executed by
843 various actions such as starting or finishing a commit. Multiple
846 various actions such as starting or finishing a commit. Multiple
844 hooks can be run for the same action by appending a suffix to the
847 hooks can be run for the same action by appending a suffix to the
845 action. Overriding a site-wide hook can be done by changing its
848 action. Overriding a site-wide hook can be done by changing its
846 value or setting it to an empty string. Hooks can be prioritized
849 value or setting it to an empty string. Hooks can be prioritized
847 by adding a prefix of ``priority.`` to the hook name on a new line
850 by adding a prefix of ``priority.`` to the hook name on a new line
848 and setting the priority. The default priority is 0.
851 and setting the priority. The default priority is 0.
849
852
850 Example ``.hg/hgrc``::
853 Example ``.hg/hgrc``::
851
854
852 [hooks]
855 [hooks]
853 # update working directory after adding changesets
856 # update working directory after adding changesets
854 changegroup.update = hg update
857 changegroup.update = hg update
855 # do not use the site-wide hook
858 # do not use the site-wide hook
856 incoming =
859 incoming =
857 incoming.email = /my/email/hook
860 incoming.email = /my/email/hook
858 incoming.autobuild = /my/build/hook
861 incoming.autobuild = /my/build/hook
859 # force autobuild hook to run before other incoming hooks
862 # force autobuild hook to run before other incoming hooks
860 priority.incoming.autobuild = 1
863 priority.incoming.autobuild = 1
861
864
862 Most hooks are run with environment variables set that give useful
865 Most hooks are run with environment variables set that give useful
863 additional information. For each hook below, the environment variables
866 additional information. For each hook below, the environment variables
864 it is passed are listed with names in the form ``$HG_foo``. The
867 it is passed are listed with names in the form ``$HG_foo``. The
865 ``$HG_HOOKTYPE`` and ``$HG_HOOKNAME`` variables are set for all hooks.
868 ``$HG_HOOKTYPE`` and ``$HG_HOOKNAME`` variables are set for all hooks.
866 They contain the type of hook which triggered the run and the full name
869 They contain the type of hook which triggered the run and the full name
867 of the hook in the config, respectively. In the example above, this will
870 of the hook in the config, respectively. In the example above, this will
868 be ``$HG_HOOKTYPE=incoming`` and ``$HG_HOOKNAME=incoming.email``.
871 be ``$HG_HOOKTYPE=incoming`` and ``$HG_HOOKNAME=incoming.email``.
869
872
870 ``changegroup``
873 ``changegroup``
871 Run after a changegroup has been added via push, pull or unbundle. The ID of
874 Run after a changegroup has been added via push, pull or unbundle. The ID of
872 the first new changeset is in ``$HG_NODE`` and last is in ``$HG_NODE_LAST``.
875 the first new changeset is in ``$HG_NODE`` and last is in ``$HG_NODE_LAST``.
873 The URL from which changes came is in ``$HG_URL``.
876 The URL from which changes came is in ``$HG_URL``.
874
877
875 ``commit``
878 ``commit``
876 Run after a changeset has been created in the local repository. The ID
879 Run after a changeset has been created in the local repository. The ID
877 of the newly created changeset is in ``$HG_NODE``. Parent changeset
880 of the newly created changeset is in ``$HG_NODE``. Parent changeset
878 IDs are in ``$HG_PARENT1`` and ``$HG_PARENT2``.
881 IDs are in ``$HG_PARENT1`` and ``$HG_PARENT2``.
879
882
880 ``incoming``
883 ``incoming``
881 Run after a changeset has been pulled, pushed, or unbundled into
884 Run after a changeset has been pulled, pushed, or unbundled into
882 the local repository. The ID of the newly arrived changeset is in
885 the local repository. The ID of the newly arrived changeset is in
883 ``$HG_NODE``. The URL that was source of the changes is in ``$HG_URL``.
886 ``$HG_NODE``. The URL that was source of the changes is in ``$HG_URL``.
884
887
885 ``outgoing``
888 ``outgoing``
886 Run after sending changes from the local repository to another. The ID of
889 Run after sending changes from the local repository to another. The ID of
887 first changeset sent is in ``$HG_NODE``. The source of operation is in
890 first changeset sent is in ``$HG_NODE``. The source of operation is in
888 ``$HG_SOURCE``. Also see :hg:`help config.hooks.preoutgoing`.
891 ``$HG_SOURCE``. Also see :hg:`help config.hooks.preoutgoing`.
889
892
890 ``post-<command>``
893 ``post-<command>``
891 Run after successful invocations of the associated command. The
894 Run after successful invocations of the associated command. The
892 contents of the command line are passed as ``$HG_ARGS`` and the result
895 contents of the command line are passed as ``$HG_ARGS`` and the result
893 code in ``$HG_RESULT``. Parsed command line arguments are passed as
896 code in ``$HG_RESULT``. Parsed command line arguments are passed as
894 ``$HG_PATS`` and ``$HG_OPTS``. These contain string representations of
897 ``$HG_PATS`` and ``$HG_OPTS``. These contain string representations of
895 the python data internally passed to <command>. ``$HG_OPTS`` is a
898 the python data internally passed to <command>. ``$HG_OPTS`` is a
896 dictionary of options (with unspecified options set to their defaults).
899 dictionary of options (with unspecified options set to their defaults).
897 ``$HG_PATS`` is a list of arguments. Hook failure is ignored.
900 ``$HG_PATS`` is a list of arguments. Hook failure is ignored.
898
901
899 ``fail-<command>``
902 ``fail-<command>``
900 Run after a failed invocation of an associated command. The contents
903 Run after a failed invocation of an associated command. The contents
901 of the command line are passed as ``$HG_ARGS``. Parsed command line
904 of the command line are passed as ``$HG_ARGS``. Parsed command line
902 arguments are passed as ``$HG_PATS`` and ``$HG_OPTS``. These contain
905 arguments are passed as ``$HG_PATS`` and ``$HG_OPTS``. These contain
903 string representations of the python data internally passed to
906 string representations of the python data internally passed to
904 <command>. ``$HG_OPTS`` is a dictionary of options (with unspecified
907 <command>. ``$HG_OPTS`` is a dictionary of options (with unspecified
905 options set to their defaults). ``$HG_PATS`` is a list of arguments.
908 options set to their defaults). ``$HG_PATS`` is a list of arguments.
906 Hook failure is ignored.
909 Hook failure is ignored.
907
910
908 ``pre-<command>``
911 ``pre-<command>``
909 Run before executing the associated command. The contents of the
912 Run before executing the associated command. The contents of the
910 command line are passed as ``$HG_ARGS``. Parsed command line arguments
913 command line are passed as ``$HG_ARGS``. Parsed command line arguments
911 are passed as ``$HG_PATS`` and ``$HG_OPTS``. These contain string
914 are passed as ``$HG_PATS`` and ``$HG_OPTS``. These contain string
912 representations of the data internally passed to <command>. ``$HG_OPTS``
915 representations of the data internally passed to <command>. ``$HG_OPTS``
913 is a dictionary of options (with unspecified options set to their
916 is a dictionary of options (with unspecified options set to their
914 defaults). ``$HG_PATS`` is a list of arguments. If the hook returns
917 defaults). ``$HG_PATS`` is a list of arguments. If the hook returns
915 failure, the command doesn't execute and Mercurial returns the failure
918 failure, the command doesn't execute and Mercurial returns the failure
916 code.
919 code.
917
920
918 ``prechangegroup``
921 ``prechangegroup``
919 Run before a changegroup is added via push, pull or unbundle. Exit
922 Run before a changegroup is added via push, pull or unbundle. Exit
920 status 0 allows the changegroup to proceed. A non-zero status will
923 status 0 allows the changegroup to proceed. A non-zero status will
921 cause the push, pull or unbundle to fail. The URL from which changes
924 cause the push, pull or unbundle to fail. The URL from which changes
922 will come is in ``$HG_URL``.
925 will come is in ``$HG_URL``.
923
926
924 ``precommit``
927 ``precommit``
925 Run before starting a local commit. Exit status 0 allows the
928 Run before starting a local commit. Exit status 0 allows the
926 commit to proceed. A non-zero status will cause the commit to fail.
929 commit to proceed. A non-zero status will cause the commit to fail.
927 Parent changeset IDs are in ``$HG_PARENT1`` and ``$HG_PARENT2``.
930 Parent changeset IDs are in ``$HG_PARENT1`` and ``$HG_PARENT2``.
928
931
929 ``prelistkeys``
932 ``prelistkeys``
930 Run before listing pushkeys (like bookmarks) in the
933 Run before listing pushkeys (like bookmarks) in the
931 repository. A non-zero status will cause failure. The key namespace is
934 repository. A non-zero status will cause failure. The key namespace is
932 in ``$HG_NAMESPACE``.
935 in ``$HG_NAMESPACE``.
933
936
934 ``preoutgoing``
937 ``preoutgoing``
935 Run before collecting changes to send from the local repository to
938 Run before collecting changes to send from the local repository to
936 another. A non-zero status will cause failure. This lets you prevent
939 another. A non-zero status will cause failure. This lets you prevent
937 pull over HTTP or SSH. It can also prevent propagating commits (via
940 pull over HTTP or SSH. It can also prevent propagating commits (via
938 local pull, push (outbound) or bundle commands), but not completely,
941 local pull, push (outbound) or bundle commands), but not completely,
939 since you can just copy files instead. The source of operation is in
942 since you can just copy files instead. The source of operation is in
940 ``$HG_SOURCE``. If "serve", the operation is happening on behalf of a remote
943 ``$HG_SOURCE``. If "serve", the operation is happening on behalf of a remote
941 SSH or HTTP repository. If "push", "pull" or "bundle", the operation
944 SSH or HTTP repository. If "push", "pull" or "bundle", the operation
942 is happening on behalf of a repository on same system.
945 is happening on behalf of a repository on same system.
943
946
944 ``prepushkey``
947 ``prepushkey``
945 Run before a pushkey (like a bookmark) is added to the
948 Run before a pushkey (like a bookmark) is added to the
946 repository. A non-zero status will cause the key to be rejected. The
949 repository. A non-zero status will cause the key to be rejected. The
947 key namespace is in ``$HG_NAMESPACE``, the key is in ``$HG_KEY``,
950 key namespace is in ``$HG_NAMESPACE``, the key is in ``$HG_KEY``,
948 the old value (if any) is in ``$HG_OLD``, and the new value is in
951 the old value (if any) is in ``$HG_OLD``, and the new value is in
949 ``$HG_NEW``.
952 ``$HG_NEW``.
950
953
951 ``pretag``
954 ``pretag``
952 Run before creating a tag. Exit status 0 allows the tag to be
955 Run before creating a tag. Exit status 0 allows the tag to be
953 created. A non-zero status will cause the tag to fail. The ID of the
956 created. A non-zero status will cause the tag to fail. The ID of the
954 changeset to tag is in ``$HG_NODE``. The name of tag is in ``$HG_TAG``. The
957 changeset to tag is in ``$HG_NODE``. The name of tag is in ``$HG_TAG``. The
955 tag is local if ``$HG_LOCAL=1``, or in the repository if ``$HG_LOCAL=0``.
958 tag is local if ``$HG_LOCAL=1``, or in the repository if ``$HG_LOCAL=0``.
956
959
957 ``pretxnopen``
960 ``pretxnopen``
958 Run before any new repository transaction is open. The reason for the
961 Run before any new repository transaction is open. The reason for the
959 transaction will be in ``$HG_TXNNAME``, and a unique identifier for the
962 transaction will be in ``$HG_TXNNAME``, and a unique identifier for the
960 transaction will be in ``HG_TXNID``. A non-zero status will prevent the
963 transaction will be in ``HG_TXNID``. A non-zero status will prevent the
961 transaction from being opened.
964 transaction from being opened.
962
965
963 ``pretxnclose``
966 ``pretxnclose``
964 Run right before the transaction is actually finalized. Any repository change
967 Run right before the transaction is actually finalized. Any repository change
965 will be visible to the hook program. This lets you validate the transaction
968 will be visible to the hook program. This lets you validate the transaction
966 content or change it. Exit status 0 allows the commit to proceed. A non-zero
969 content or change it. Exit status 0 allows the commit to proceed. A non-zero
967 status will cause the transaction to be rolled back. The reason for the
970 status will cause the transaction to be rolled back. The reason for the
968 transaction opening will be in ``$HG_TXNNAME``, and a unique identifier for
971 transaction opening will be in ``$HG_TXNNAME``, and a unique identifier for
969 the transaction will be in ``HG_TXNID``. The rest of the available data will
972 the transaction will be in ``HG_TXNID``. The rest of the available data will
970 vary according the transaction type. New changesets will add ``$HG_NODE``
973 vary according the transaction type. New changesets will add ``$HG_NODE``
971 (the ID of the first added changeset), ``$HG_NODE_LAST`` (the ID of the last
974 (the ID of the first added changeset), ``$HG_NODE_LAST`` (the ID of the last
972 added changeset), ``$HG_URL`` and ``$HG_SOURCE`` variables. Bookmark and
975 added changeset), ``$HG_URL`` and ``$HG_SOURCE`` variables. Bookmark and
973 phase changes will set ``HG_BOOKMARK_MOVED`` and ``HG_PHASES_MOVED`` to ``1``
976 phase changes will set ``HG_BOOKMARK_MOVED`` and ``HG_PHASES_MOVED`` to ``1``
974 respectively, etc.
977 respectively, etc.
975
978
976 ``txnclose``
979 ``txnclose``
977 Run after any repository transaction has been committed. At this
980 Run after any repository transaction has been committed. At this
978 point, the transaction can no longer be rolled back. The hook will run
981 point, the transaction can no longer be rolled back. The hook will run
979 after the lock is released. See :hg:`help config.hooks.pretxnclose` for
982 after the lock is released. See :hg:`help config.hooks.pretxnclose` for
980 details about available variables.
983 details about available variables.
981
984
982 ``txnabort``
985 ``txnabort``
983 Run when a transaction is aborted. See :hg:`help config.hooks.pretxnclose`
986 Run when a transaction is aborted. See :hg:`help config.hooks.pretxnclose`
984 for details about available variables.
987 for details about available variables.
985
988
986 ``pretxnchangegroup``
989 ``pretxnchangegroup``
987 Run after a changegroup has been added via push, pull or unbundle, but before
990 Run after a changegroup has been added via push, pull or unbundle, but before
988 the transaction has been committed. The changegroup is visible to the hook
991 the transaction has been committed. The changegroup is visible to the hook
989 program. This allows validation of incoming changes before accepting them.
992 program. This allows validation of incoming changes before accepting them.
990 The ID of the first new changeset is in ``$HG_NODE`` and last is in
993 The ID of the first new changeset is in ``$HG_NODE`` and last is in
991 ``$HG_NODE_LAST``. Exit status 0 allows the transaction to commit. A non-zero
994 ``$HG_NODE_LAST``. Exit status 0 allows the transaction to commit. A non-zero
992 status will cause the transaction to be rolled back, and the push, pull or
995 status will cause the transaction to be rolled back, and the push, pull or
993 unbundle will fail. The URL that was the source of changes is in ``$HG_URL``.
996 unbundle will fail. The URL that was the source of changes is in ``$HG_URL``.
994
997
995 ``pretxncommit``
998 ``pretxncommit``
996 Run after a changeset has been created, but before the transaction is
999 Run after a changeset has been created, but before the transaction is
997 committed. The changeset is visible to the hook program. This allows
1000 committed. The changeset is visible to the hook program. This allows
998 validation of the commit message and changes. Exit status 0 allows the
1001 validation of the commit message and changes. Exit status 0 allows the
999 commit to proceed. A non-zero status will cause the transaction to
1002 commit to proceed. A non-zero status will cause the transaction to
1000 be rolled back. The ID of the new changeset is in ``$HG_NODE``. The parent
1003 be rolled back. The ID of the new changeset is in ``$HG_NODE``. The parent
1001 changeset IDs are in ``$HG_PARENT1`` and ``$HG_PARENT2``.
1004 changeset IDs are in ``$HG_PARENT1`` and ``$HG_PARENT2``.
1002
1005
1003 ``preupdate``
1006 ``preupdate``
1004 Run before updating the working directory. Exit status 0 allows
1007 Run before updating the working directory. Exit status 0 allows
1005 the update to proceed. A non-zero status will prevent the update.
1008 the update to proceed. A non-zero status will prevent the update.
1006 The changeset ID of first new parent is in ``$HG_PARENT1``. If updating to a
1009 The changeset ID of first new parent is in ``$HG_PARENT1``. If updating to a
1007 merge, the ID of second new parent is in ``$HG_PARENT2``.
1010 merge, the ID of second new parent is in ``$HG_PARENT2``.
1008
1011
1009 ``listkeys``
1012 ``listkeys``
1010 Run after listing pushkeys (like bookmarks) in the repository. The
1013 Run after listing pushkeys (like bookmarks) in the repository. The
1011 key namespace is in ``$HG_NAMESPACE``. ``$HG_VALUES`` is a
1014 key namespace is in ``$HG_NAMESPACE``. ``$HG_VALUES`` is a
1012 dictionary containing the keys and values.
1015 dictionary containing the keys and values.
1013
1016
1014 ``pushkey``
1017 ``pushkey``
1015 Run after a pushkey (like a bookmark) is added to the
1018 Run after a pushkey (like a bookmark) is added to the
1016 repository. The key namespace is in ``$HG_NAMESPACE``, the key is in
1019 repository. The key namespace is in ``$HG_NAMESPACE``, the key is in
1017 ``$HG_KEY``, the old value (if any) is in ``$HG_OLD``, and the new
1020 ``$HG_KEY``, the old value (if any) is in ``$HG_OLD``, and the new
1018 value is in ``$HG_NEW``.
1021 value is in ``$HG_NEW``.
1019
1022
1020 ``tag``
1023 ``tag``
1021 Run after a tag is created. The ID of the tagged changeset is in ``$HG_NODE``.
1024 Run after a tag is created. The ID of the tagged changeset is in ``$HG_NODE``.
1022 The name of tag is in ``$HG_TAG``. The tag is local if ``$HG_LOCAL=1``, or in
1025 The name of tag is in ``$HG_TAG``. The tag is local if ``$HG_LOCAL=1``, or in
1023 the repository if ``$HG_LOCAL=0``.
1026 the repository if ``$HG_LOCAL=0``.
1024
1027
1025 ``update``
1028 ``update``
1026 Run after updating the working directory. The changeset ID of first
1029 Run after updating the working directory. The changeset ID of first
1027 new parent is in ``$HG_PARENT1``. If updating to a merge, the ID of second new
1030 new parent is in ``$HG_PARENT1``. If updating to a merge, the ID of second new
1028 parent is in ``$HG_PARENT2``. If the update succeeded, ``$HG_ERROR=0``. If the
1031 parent is in ``$HG_PARENT2``. If the update succeeded, ``$HG_ERROR=0``. If the
1029 update failed (e.g. because conflicts were not resolved), ``$HG_ERROR=1``.
1032 update failed (e.g. because conflicts were not resolved), ``$HG_ERROR=1``.
1030
1033
1031 .. note::
1034 .. note::
1032
1035
1033 It is generally better to use standard hooks rather than the
1036 It is generally better to use standard hooks rather than the
1034 generic pre- and post- command hooks, as they are guaranteed to be
1037 generic pre- and post- command hooks, as they are guaranteed to be
1035 called in the appropriate contexts for influencing transactions.
1038 called in the appropriate contexts for influencing transactions.
1036 Also, hooks like "commit" will be called in all contexts that
1039 Also, hooks like "commit" will be called in all contexts that
1037 generate a commit (e.g. tag) and not just the commit command.
1040 generate a commit (e.g. tag) and not just the commit command.
1038
1041
1039 .. note::
1042 .. note::
1040
1043
1041 Environment variables with empty values may not be passed to
1044 Environment variables with empty values may not be passed to
1042 hooks on platforms such as Windows. As an example, ``$HG_PARENT2``
1045 hooks on platforms such as Windows. As an example, ``$HG_PARENT2``
1043 will have an empty value under Unix-like platforms for non-merge
1046 will have an empty value under Unix-like platforms for non-merge
1044 changesets, while it will not be available at all under Windows.
1047 changesets, while it will not be available at all under Windows.
1045
1048
1046 The syntax for Python hooks is as follows::
1049 The syntax for Python hooks is as follows::
1047
1050
1048 hookname = python:modulename.submodule.callable
1051 hookname = python:modulename.submodule.callable
1049 hookname = python:/path/to/python/module.py:callable
1052 hookname = python:/path/to/python/module.py:callable
1050
1053
1051 Python hooks are run within the Mercurial process. Each hook is
1054 Python hooks are run within the Mercurial process. Each hook is
1052 called with at least three keyword arguments: a ui object (keyword
1055 called with at least three keyword arguments: a ui object (keyword
1053 ``ui``), a repository object (keyword ``repo``), and a ``hooktype``
1056 ``ui``), a repository object (keyword ``repo``), and a ``hooktype``
1054 keyword that tells what kind of hook is used. Arguments listed as
1057 keyword that tells what kind of hook is used. Arguments listed as
1055 environment variables above are passed as keyword arguments, with no
1058 environment variables above are passed as keyword arguments, with no
1056 ``HG_`` prefix, and names in lower case.
1059 ``HG_`` prefix, and names in lower case.
1057
1060
1058 If a Python hook returns a "true" value or raises an exception, this
1061 If a Python hook returns a "true" value or raises an exception, this
1059 is treated as a failure.
1062 is treated as a failure.
1060
1063
1061
1064
1062 ``hostfingerprints``
1065 ``hostfingerprints``
1063 --------------------
1066 --------------------
1064
1067
1065 (Deprecated. Use ``[hostsecurity]``'s ``fingerprints`` options instead.)
1068 (Deprecated. Use ``[hostsecurity]``'s ``fingerprints`` options instead.)
1066
1069
1067 Fingerprints of the certificates of known HTTPS servers.
1070 Fingerprints of the certificates of known HTTPS servers.
1068
1071
1069 A HTTPS connection to a server with a fingerprint configured here will
1072 A HTTPS connection to a server with a fingerprint configured here will
1070 only succeed if the servers certificate matches the fingerprint.
1073 only succeed if the servers certificate matches the fingerprint.
1071 This is very similar to how ssh known hosts works.
1074 This is very similar to how ssh known hosts works.
1072
1075
1073 The fingerprint is the SHA-1 hash value of the DER encoded certificate.
1076 The fingerprint is the SHA-1 hash value of the DER encoded certificate.
1074 Multiple values can be specified (separated by spaces or commas). This can
1077 Multiple values can be specified (separated by spaces or commas). This can
1075 be used to define both old and new fingerprints while a host transitions
1078 be used to define both old and new fingerprints while a host transitions
1076 to a new certificate.
1079 to a new certificate.
1077
1080
1078 The CA chain and web.cacerts is not used for servers with a fingerprint.
1081 The CA chain and web.cacerts is not used for servers with a fingerprint.
1079
1082
1080 For example::
1083 For example::
1081
1084
1082 [hostfingerprints]
1085 [hostfingerprints]
1083 hg.intevation.de = fc:e2:8d:d9:51:cd:cb:c1:4d:18:6b:b7:44:8d:49:72:57:e6:cd:33
1086 hg.intevation.de = fc:e2:8d:d9:51:cd:cb:c1:4d:18:6b:b7:44:8d:49:72:57:e6:cd:33
1084 hg.intevation.org = fc:e2:8d:d9:51:cd:cb:c1:4d:18:6b:b7:44:8d:49:72:57:e6:cd:33
1087 hg.intevation.org = fc:e2:8d:d9:51:cd:cb:c1:4d:18:6b:b7:44:8d:49:72:57:e6:cd:33
1085
1088
1086 ``hostsecurity``
1089 ``hostsecurity``
1087 ----------------
1090 ----------------
1088
1091
1089 Used to specify global and per-host security settings for connecting to
1092 Used to specify global and per-host security settings for connecting to
1090 other machines.
1093 other machines.
1091
1094
1092 The following options control default behavior for all hosts.
1095 The following options control default behavior for all hosts.
1093
1096
1094 ``ciphers``
1097 ``ciphers``
1095 Defines the cryptographic ciphers to use for connections.
1098 Defines the cryptographic ciphers to use for connections.
1096
1099
1097 Value must be a valid OpenSSL Cipher List Format as documented at
1100 Value must be a valid OpenSSL Cipher List Format as documented at
1098 https://www.openssl.org/docs/manmaster/apps/ciphers.html#CIPHER-LIST-FORMAT.
1101 https://www.openssl.org/docs/manmaster/apps/ciphers.html#CIPHER-LIST-FORMAT.
1099
1102
1100 This setting is for advanced users only. Setting to incorrect values
1103 This setting is for advanced users only. Setting to incorrect values
1101 can significantly lower connection security or decrease performance.
1104 can significantly lower connection security or decrease performance.
1102 You have been warned.
1105 You have been warned.
1103
1106
1104 This option requires Python 2.7.
1107 This option requires Python 2.7.
1105
1108
1106 ``minimumprotocol``
1109 ``minimumprotocol``
1107 Defines the minimum channel encryption protocol to use.
1110 Defines the minimum channel encryption protocol to use.
1108
1111
1109 By default, the highest version of TLS supported by both client and server
1112 By default, the highest version of TLS supported by both client and server
1110 is used.
1113 is used.
1111
1114
1112 Allowed values are: ``tls1.0``, ``tls1.1``, ``tls1.2``.
1115 Allowed values are: ``tls1.0``, ``tls1.1``, ``tls1.2``.
1113
1116
1114 When running on an old Python version, only ``tls1.0`` is allowed since
1117 When running on an old Python version, only ``tls1.0`` is allowed since
1115 old versions of Python only support up to TLS 1.0.
1118 old versions of Python only support up to TLS 1.0.
1116
1119
1117 When running a Python that supports modern TLS versions, the default is
1120 When running a Python that supports modern TLS versions, the default is
1118 ``tls1.1``. ``tls1.0`` can still be used to allow TLS 1.0. However, this
1121 ``tls1.1``. ``tls1.0`` can still be used to allow TLS 1.0. However, this
1119 weakens security and should only be used as a feature of last resort if
1122 weakens security and should only be used as a feature of last resort if
1120 a server does not support TLS 1.1+.
1123 a server does not support TLS 1.1+.
1121
1124
1122 Options in the ``[hostsecurity]`` section can have the form
1125 Options in the ``[hostsecurity]`` section can have the form
1123 ``hostname``:``setting``. This allows multiple settings to be defined on a
1126 ``hostname``:``setting``. This allows multiple settings to be defined on a
1124 per-host basis.
1127 per-host basis.
1125
1128
1126 The following per-host settings can be defined.
1129 The following per-host settings can be defined.
1127
1130
1128 ``ciphers``
1131 ``ciphers``
1129 This behaves like ``ciphers`` as described above except it only applies
1132 This behaves like ``ciphers`` as described above except it only applies
1130 to the host on which it is defined.
1133 to the host on which it is defined.
1131
1134
1132 ``fingerprints``
1135 ``fingerprints``
1133 A list of hashes of the DER encoded peer/remote certificate. Values have
1136 A list of hashes of the DER encoded peer/remote certificate. Values have
1134 the form ``algorithm``:``fingerprint``. e.g.
1137 the form ``algorithm``:``fingerprint``. e.g.
1135 ``sha256:c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2``.
1138 ``sha256:c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2``.
1136 In addition, colons (``:``) can appear in the fingerprint part.
1139 In addition, colons (``:``) can appear in the fingerprint part.
1137
1140
1138 The following algorithms/prefixes are supported: ``sha1``, ``sha256``,
1141 The following algorithms/prefixes are supported: ``sha1``, ``sha256``,
1139 ``sha512``.
1142 ``sha512``.
1140
1143
1141 Use of ``sha256`` or ``sha512`` is preferred.
1144 Use of ``sha256`` or ``sha512`` is preferred.
1142
1145
1143 If a fingerprint is specified, the CA chain is not validated for this
1146 If a fingerprint is specified, the CA chain is not validated for this
1144 host and Mercurial will require the remote certificate to match one
1147 host and Mercurial will require the remote certificate to match one
1145 of the fingerprints specified. This means if the server updates its
1148 of the fingerprints specified. This means if the server updates its
1146 certificate, Mercurial will abort until a new fingerprint is defined.
1149 certificate, Mercurial will abort until a new fingerprint is defined.
1147 This can provide stronger security than traditional CA-based validation
1150 This can provide stronger security than traditional CA-based validation
1148 at the expense of convenience.
1151 at the expense of convenience.
1149
1152
1150 This option takes precedence over ``verifycertsfile``.
1153 This option takes precedence over ``verifycertsfile``.
1151
1154
1152 ``minimumprotocol``
1155 ``minimumprotocol``
1153 This behaves like ``minimumprotocol`` as described above except it
1156 This behaves like ``minimumprotocol`` as described above except it
1154 only applies to the host on which it is defined.
1157 only applies to the host on which it is defined.
1155
1158
1156 ``verifycertsfile``
1159 ``verifycertsfile``
1157 Path to file a containing a list of PEM encoded certificates used to
1160 Path to file a containing a list of PEM encoded certificates used to
1158 verify the server certificate. Environment variables and ``~user``
1161 verify the server certificate. Environment variables and ``~user``
1159 constructs are expanded in the filename.
1162 constructs are expanded in the filename.
1160
1163
1161 The server certificate or the certificate's certificate authority (CA)
1164 The server certificate or the certificate's certificate authority (CA)
1162 must match a certificate from this file or certificate verification
1165 must match a certificate from this file or certificate verification
1163 will fail and connections to the server will be refused.
1166 will fail and connections to the server will be refused.
1164
1167
1165 If defined, only certificates provided by this file will be used:
1168 If defined, only certificates provided by this file will be used:
1166 ``web.cacerts`` and any system/default certificates will not be
1169 ``web.cacerts`` and any system/default certificates will not be
1167 used.
1170 used.
1168
1171
1169 This option has no effect if the per-host ``fingerprints`` option
1172 This option has no effect if the per-host ``fingerprints`` option
1170 is set.
1173 is set.
1171
1174
1172 The format of the file is as follows::
1175 The format of the file is as follows::
1173
1176
1174 -----BEGIN CERTIFICATE-----
1177 -----BEGIN CERTIFICATE-----
1175 ... (certificate in base64 PEM encoding) ...
1178 ... (certificate in base64 PEM encoding) ...
1176 -----END CERTIFICATE-----
1179 -----END CERTIFICATE-----
1177 -----BEGIN CERTIFICATE-----
1180 -----BEGIN CERTIFICATE-----
1178 ... (certificate in base64 PEM encoding) ...
1181 ... (certificate in base64 PEM encoding) ...
1179 -----END CERTIFICATE-----
1182 -----END CERTIFICATE-----
1180
1183
1181 For example::
1184 For example::
1182
1185
1183 [hostsecurity]
1186 [hostsecurity]
1184 hg.example.com:fingerprints = sha256:c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2
1187 hg.example.com:fingerprints = sha256:c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2
1185 hg2.example.com:fingerprints = sha1:914f1aff87249c09b6859b88b1906d30756491ca, sha1:fc:e2:8d:d9:51:cd:cb:c1:4d:18:6b:b7:44:8d:49:72:57:e6:cd:33
1188 hg2.example.com:fingerprints = sha1:914f1aff87249c09b6859b88b1906d30756491ca, sha1:fc:e2:8d:d9:51:cd:cb:c1:4d:18:6b:b7:44:8d:49:72:57:e6:cd:33
1186 hg3.example.com:fingerprints = sha256:9a:b0:dc:e2:75:ad:8a:b7:84:58:e5:1f:07:32:f1:87:e6:bd:24:22:af:b7:ce:8e:9c:b4:10:cf:b9:f4:0e:d2
1189 hg3.example.com:fingerprints = sha256:9a:b0:dc:e2:75:ad:8a:b7:84:58:e5:1f:07:32:f1:87:e6:bd:24:22:af:b7:ce:8e:9c:b4:10:cf:b9:f4:0e:d2
1187 foo.example.com:verifycertsfile = /etc/ssl/trusted-ca-certs.pem
1190 foo.example.com:verifycertsfile = /etc/ssl/trusted-ca-certs.pem
1188
1191
1189 To change the default minimum protocol version to TLS 1.2 but to allow TLS 1.1
1192 To change the default minimum protocol version to TLS 1.2 but to allow TLS 1.1
1190 when connecting to ``hg.example.com``::
1193 when connecting to ``hg.example.com``::
1191
1194
1192 [hostsecurity]
1195 [hostsecurity]
1193 minimumprotocol = tls1.2
1196 minimumprotocol = tls1.2
1194 hg.example.com:minimumprotocol = tls1.1
1197 hg.example.com:minimumprotocol = tls1.1
1195
1198
1196 ``http_proxy``
1199 ``http_proxy``
1197 --------------
1200 --------------
1198
1201
1199 Used to access web-based Mercurial repositories through a HTTP
1202 Used to access web-based Mercurial repositories through a HTTP
1200 proxy.
1203 proxy.
1201
1204
1202 ``host``
1205 ``host``
1203 Host name and (optional) port of the proxy server, for example
1206 Host name and (optional) port of the proxy server, for example
1204 "myproxy:8000".
1207 "myproxy:8000".
1205
1208
1206 ``no``
1209 ``no``
1207 Optional. Comma-separated list of host names that should bypass
1210 Optional. Comma-separated list of host names that should bypass
1208 the proxy.
1211 the proxy.
1209
1212
1210 ``passwd``
1213 ``passwd``
1211 Optional. Password to authenticate with at the proxy server.
1214 Optional. Password to authenticate with at the proxy server.
1212
1215
1213 ``user``
1216 ``user``
1214 Optional. User name to authenticate with at the proxy server.
1217 Optional. User name to authenticate with at the proxy server.
1215
1218
1216 ``always``
1219 ``always``
1217 Optional. Always use the proxy, even for localhost and any entries
1220 Optional. Always use the proxy, even for localhost and any entries
1218 in ``http_proxy.no``. (default: False)
1221 in ``http_proxy.no``. (default: False)
1219
1222
1220 ``merge``
1223 ``merge``
1221 ---------
1224 ---------
1222
1225
1223 This section specifies behavior during merges and updates.
1226 This section specifies behavior during merges and updates.
1224
1227
1225 ``checkignored``
1228 ``checkignored``
1226 Controls behavior when an ignored file on disk has the same name as a tracked
1229 Controls behavior when an ignored file on disk has the same name as a tracked
1227 file in the changeset being merged or updated to, and has different
1230 file in the changeset being merged or updated to, and has different
1228 contents. Options are ``abort``, ``warn`` and ``ignore``. With ``abort``,
1231 contents. Options are ``abort``, ``warn`` and ``ignore``. With ``abort``,
1229 abort on such files. With ``warn``, warn on such files and back them up as
1232 abort on such files. With ``warn``, warn on such files and back them up as
1230 ``.orig``. With ``ignore``, don't print a warning and back them up as
1233 ``.orig``. With ``ignore``, don't print a warning and back them up as
1231 ``.orig``. (default: ``abort``)
1234 ``.orig``. (default: ``abort``)
1232
1235
1233 ``checkunknown``
1236 ``checkunknown``
1234 Controls behavior when an unknown file that isn't ignored has the same name
1237 Controls behavior when an unknown file that isn't ignored has the same name
1235 as a tracked file in the changeset being merged or updated to, and has
1238 as a tracked file in the changeset being merged or updated to, and has
1236 different contents. Similar to ``merge.checkignored``, except for files that
1239 different contents. Similar to ``merge.checkignored``, except for files that
1237 are not ignored. (default: ``abort``)
1240 are not ignored. (default: ``abort``)
1238
1241
1239 ``merge-patterns``
1242 ``merge-patterns``
1240 ------------------
1243 ------------------
1241
1244
1242 This section specifies merge tools to associate with particular file
1245 This section specifies merge tools to associate with particular file
1243 patterns. Tools matched here will take precedence over the default
1246 patterns. Tools matched here will take precedence over the default
1244 merge tool. Patterns are globs by default, rooted at the repository
1247 merge tool. Patterns are globs by default, rooted at the repository
1245 root.
1248 root.
1246
1249
1247 Example::
1250 Example::
1248
1251
1249 [merge-patterns]
1252 [merge-patterns]
1250 **.c = kdiff3
1253 **.c = kdiff3
1251 **.jpg = myimgmerge
1254 **.jpg = myimgmerge
1252
1255
1253 ``merge-tools``
1256 ``merge-tools``
1254 ---------------
1257 ---------------
1255
1258
1256 This section configures external merge tools to use for file-level
1259 This section configures external merge tools to use for file-level
1257 merges. This section has likely been preconfigured at install time.
1260 merges. This section has likely been preconfigured at install time.
1258 Use :hg:`config merge-tools` to check the existing configuration.
1261 Use :hg:`config merge-tools` to check the existing configuration.
1259 Also see :hg:`help merge-tools` for more details.
1262 Also see :hg:`help merge-tools` for more details.
1260
1263
1261 Example ``~/.hgrc``::
1264 Example ``~/.hgrc``::
1262
1265
1263 [merge-tools]
1266 [merge-tools]
1264 # Override stock tool location
1267 # Override stock tool location
1265 kdiff3.executable = ~/bin/kdiff3
1268 kdiff3.executable = ~/bin/kdiff3
1266 # Specify command line
1269 # Specify command line
1267 kdiff3.args = $base $local $other -o $output
1270 kdiff3.args = $base $local $other -o $output
1268 # Give higher priority
1271 # Give higher priority
1269 kdiff3.priority = 1
1272 kdiff3.priority = 1
1270
1273
1271 # Changing the priority of preconfigured tool
1274 # Changing the priority of preconfigured tool
1272 meld.priority = 0
1275 meld.priority = 0
1273
1276
1274 # Disable a preconfigured tool
1277 # Disable a preconfigured tool
1275 vimdiff.disabled = yes
1278 vimdiff.disabled = yes
1276
1279
1277 # Define new tool
1280 # Define new tool
1278 myHtmlTool.args = -m $local $other $base $output
1281 myHtmlTool.args = -m $local $other $base $output
1279 myHtmlTool.regkey = Software\FooSoftware\HtmlMerge
1282 myHtmlTool.regkey = Software\FooSoftware\HtmlMerge
1280 myHtmlTool.priority = 1
1283 myHtmlTool.priority = 1
1281
1284
1282 Supported arguments:
1285 Supported arguments:
1283
1286
1284 ``priority``
1287 ``priority``
1285 The priority in which to evaluate this tool.
1288 The priority in which to evaluate this tool.
1286 (default: 0)
1289 (default: 0)
1287
1290
1288 ``executable``
1291 ``executable``
1289 Either just the name of the executable or its pathname.
1292 Either just the name of the executable or its pathname.
1290
1293
1291 .. container:: windows
1294 .. container:: windows
1292
1295
1293 On Windows, the path can use environment variables with ${ProgramFiles}
1296 On Windows, the path can use environment variables with ${ProgramFiles}
1294 syntax.
1297 syntax.
1295
1298
1296 (default: the tool name)
1299 (default: the tool name)
1297
1300
1298 ``args``
1301 ``args``
1299 The arguments to pass to the tool executable. You can refer to the
1302 The arguments to pass to the tool executable. You can refer to the
1300 files being merged as well as the output file through these
1303 files being merged as well as the output file through these
1301 variables: ``$base``, ``$local``, ``$other``, ``$output``. The meaning
1304 variables: ``$base``, ``$local``, ``$other``, ``$output``. The meaning
1302 of ``$local`` and ``$other`` can vary depending on which action is being
1305 of ``$local`` and ``$other`` can vary depending on which action is being
1303 performed. During and update or merge, ``$local`` represents the original
1306 performed. During and update or merge, ``$local`` represents the original
1304 state of the file, while ``$other`` represents the commit you are updating
1307 state of the file, while ``$other`` represents the commit you are updating
1305 to or the commit you are merging with. During a rebase ``$local``
1308 to or the commit you are merging with. During a rebase ``$local``
1306 represents the destination of the rebase, and ``$other`` represents the
1309 represents the destination of the rebase, and ``$other`` represents the
1307 commit being rebased.
1310 commit being rebased.
1308 (default: ``$local $base $other``)
1311 (default: ``$local $base $other``)
1309
1312
1310 ``premerge``
1313 ``premerge``
1311 Attempt to run internal non-interactive 3-way merge tool before
1314 Attempt to run internal non-interactive 3-way merge tool before
1312 launching external tool. Options are ``true``, ``false``, ``keep`` or
1315 launching external tool. Options are ``true``, ``false``, ``keep`` or
1313 ``keep-merge3``. The ``keep`` option will leave markers in the file if the
1316 ``keep-merge3``. The ``keep`` option will leave markers in the file if the
1314 premerge fails. The ``keep-merge3`` will do the same but include information
1317 premerge fails. The ``keep-merge3`` will do the same but include information
1315 about the base of the merge in the marker (see internal :merge3 in
1318 about the base of the merge in the marker (see internal :merge3 in
1316 :hg:`help merge-tools`).
1319 :hg:`help merge-tools`).
1317 (default: True)
1320 (default: True)
1318
1321
1319 ``binary``
1322 ``binary``
1320 This tool can merge binary files. (default: False, unless tool
1323 This tool can merge binary files. (default: False, unless tool
1321 was selected by file pattern match)
1324 was selected by file pattern match)
1322
1325
1323 ``symlink``
1326 ``symlink``
1324 This tool can merge symlinks. (default: False)
1327 This tool can merge symlinks. (default: False)
1325
1328
1326 ``check``
1329 ``check``
1327 A list of merge success-checking options:
1330 A list of merge success-checking options:
1328
1331
1329 ``changed``
1332 ``changed``
1330 Ask whether merge was successful when the merged file shows no changes.
1333 Ask whether merge was successful when the merged file shows no changes.
1331 ``conflicts``
1334 ``conflicts``
1332 Check whether there are conflicts even though the tool reported success.
1335 Check whether there are conflicts even though the tool reported success.
1333 ``prompt``
1336 ``prompt``
1334 Always prompt for merge success, regardless of success reported by tool.
1337 Always prompt for merge success, regardless of success reported by tool.
1335
1338
1336 ``fixeol``
1339 ``fixeol``
1337 Attempt to fix up EOL changes caused by the merge tool.
1340 Attempt to fix up EOL changes caused by the merge tool.
1338 (default: False)
1341 (default: False)
1339
1342
1340 ``gui``
1343 ``gui``
1341 This tool requires a graphical interface to run. (default: False)
1344 This tool requires a graphical interface to run. (default: False)
1342
1345
1343 .. container:: windows
1346 .. container:: windows
1344
1347
1345 ``regkey``
1348 ``regkey``
1346 Windows registry key which describes install location of this
1349 Windows registry key which describes install location of this
1347 tool. Mercurial will search for this key first under
1350 tool. Mercurial will search for this key first under
1348 ``HKEY_CURRENT_USER`` and then under ``HKEY_LOCAL_MACHINE``.
1351 ``HKEY_CURRENT_USER`` and then under ``HKEY_LOCAL_MACHINE``.
1349 (default: None)
1352 (default: None)
1350
1353
1351 ``regkeyalt``
1354 ``regkeyalt``
1352 An alternate Windows registry key to try if the first key is not
1355 An alternate Windows registry key to try if the first key is not
1353 found. The alternate key uses the same ``regname`` and ``regappend``
1356 found. The alternate key uses the same ``regname`` and ``regappend``
1354 semantics of the primary key. The most common use for this key
1357 semantics of the primary key. The most common use for this key
1355 is to search for 32bit applications on 64bit operating systems.
1358 is to search for 32bit applications on 64bit operating systems.
1356 (default: None)
1359 (default: None)
1357
1360
1358 ``regname``
1361 ``regname``
1359 Name of value to read from specified registry key.
1362 Name of value to read from specified registry key.
1360 (default: the unnamed (default) value)
1363 (default: the unnamed (default) value)
1361
1364
1362 ``regappend``
1365 ``regappend``
1363 String to append to the value read from the registry, typically
1366 String to append to the value read from the registry, typically
1364 the executable name of the tool.
1367 the executable name of the tool.
1365 (default: None)
1368 (default: None)
1366
1369
1367 ``pager``
1370 ``pager``
1368 ---------
1371 ---------
1369
1372
1370 Setting used to control when to paginate and with what external tool. See
1373 Setting used to control when to paginate and with what external tool. See
1371 :hg:`help pager` for details.
1374 :hg:`help pager` for details.
1372
1375
1373 ``pager``
1376 ``pager``
1374 Define the external tool used as pager.
1377 Define the external tool used as pager.
1375
1378
1376 If no pager is set, Mercurial uses the environment variable $PAGER.
1379 If no pager is set, Mercurial uses the environment variable $PAGER.
1377 If neither pager.pager, nor $PAGER is set, a default pager will be
1380 If neither pager.pager, nor $PAGER is set, a default pager will be
1378 used, typically `less` on Unix and `more` on Windows. Example::
1381 used, typically `less` on Unix and `more` on Windows. Example::
1379
1382
1380 [pager]
1383 [pager]
1381 pager = less -FRX
1384 pager = less -FRX
1382
1385
1383 ``ignore``
1386 ``ignore``
1384 List of commands to disable the pager for. Example::
1387 List of commands to disable the pager for. Example::
1385
1388
1386 [pager]
1389 [pager]
1387 ignore = version, help, update
1390 ignore = version, help, update
1388
1391
1389 ``patch``
1392 ``patch``
1390 ---------
1393 ---------
1391
1394
1392 Settings used when applying patches, for instance through the 'import'
1395 Settings used when applying patches, for instance through the 'import'
1393 command or with Mercurial Queues extension.
1396 command or with Mercurial Queues extension.
1394
1397
1395 ``eol``
1398 ``eol``
1396 When set to 'strict' patch content and patched files end of lines
1399 When set to 'strict' patch content and patched files end of lines
1397 are preserved. When set to ``lf`` or ``crlf``, both files end of
1400 are preserved. When set to ``lf`` or ``crlf``, both files end of
1398 lines are ignored when patching and the result line endings are
1401 lines are ignored when patching and the result line endings are
1399 normalized to either LF (Unix) or CRLF (Windows). When set to
1402 normalized to either LF (Unix) or CRLF (Windows). When set to
1400 ``auto``, end of lines are again ignored while patching but line
1403 ``auto``, end of lines are again ignored while patching but line
1401 endings in patched files are normalized to their original setting
1404 endings in patched files are normalized to their original setting
1402 on a per-file basis. If target file does not exist or has no end
1405 on a per-file basis. If target file does not exist or has no end
1403 of line, patch line endings are preserved.
1406 of line, patch line endings are preserved.
1404 (default: strict)
1407 (default: strict)
1405
1408
1406 ``fuzz``
1409 ``fuzz``
1407 The number of lines of 'fuzz' to allow when applying patches. This
1410 The number of lines of 'fuzz' to allow when applying patches. This
1408 controls how much context the patcher is allowed to ignore when
1411 controls how much context the patcher is allowed to ignore when
1409 trying to apply a patch.
1412 trying to apply a patch.
1410 (default: 2)
1413 (default: 2)
1411
1414
1412 ``paths``
1415 ``paths``
1413 ---------
1416 ---------
1414
1417
1415 Assigns symbolic names and behavior to repositories.
1418 Assigns symbolic names and behavior to repositories.
1416
1419
1417 Options are symbolic names defining the URL or directory that is the
1420 Options are symbolic names defining the URL or directory that is the
1418 location of the repository. Example::
1421 location of the repository. Example::
1419
1422
1420 [paths]
1423 [paths]
1421 my_server = https://example.com/my_repo
1424 my_server = https://example.com/my_repo
1422 local_path = /home/me/repo
1425 local_path = /home/me/repo
1423
1426
1424 These symbolic names can be used from the command line. To pull
1427 These symbolic names can be used from the command line. To pull
1425 from ``my_server``: :hg:`pull my_server`. To push to ``local_path``:
1428 from ``my_server``: :hg:`pull my_server`. To push to ``local_path``:
1426 :hg:`push local_path`.
1429 :hg:`push local_path`.
1427
1430
1428 Options containing colons (``:``) denote sub-options that can influence
1431 Options containing colons (``:``) denote sub-options that can influence
1429 behavior for that specific path. Example::
1432 behavior for that specific path. Example::
1430
1433
1431 [paths]
1434 [paths]
1432 my_server = https://example.com/my_path
1435 my_server = https://example.com/my_path
1433 my_server:pushurl = ssh://example.com/my_path
1436 my_server:pushurl = ssh://example.com/my_path
1434
1437
1435 The following sub-options can be defined:
1438 The following sub-options can be defined:
1436
1439
1437 ``pushurl``
1440 ``pushurl``
1438 The URL to use for push operations. If not defined, the location
1441 The URL to use for push operations. If not defined, the location
1439 defined by the path's main entry is used.
1442 defined by the path's main entry is used.
1440
1443
1441 ``pushrev``
1444 ``pushrev``
1442 A revset defining which revisions to push by default.
1445 A revset defining which revisions to push by default.
1443
1446
1444 When :hg:`push` is executed without a ``-r`` argument, the revset
1447 When :hg:`push` is executed without a ``-r`` argument, the revset
1445 defined by this sub-option is evaluated to determine what to push.
1448 defined by this sub-option is evaluated to determine what to push.
1446
1449
1447 For example, a value of ``.`` will push the working directory's
1450 For example, a value of ``.`` will push the working directory's
1448 revision by default.
1451 revision by default.
1449
1452
1450 Revsets specifying bookmarks will not result in the bookmark being
1453 Revsets specifying bookmarks will not result in the bookmark being
1451 pushed.
1454 pushed.
1452
1455
1453 The following special named paths exist:
1456 The following special named paths exist:
1454
1457
1455 ``default``
1458 ``default``
1456 The URL or directory to use when no source or remote is specified.
1459 The URL or directory to use when no source or remote is specified.
1457
1460
1458 :hg:`clone` will automatically define this path to the location the
1461 :hg:`clone` will automatically define this path to the location the
1459 repository was cloned from.
1462 repository was cloned from.
1460
1463
1461 ``default-push``
1464 ``default-push``
1462 (deprecated) The URL or directory for the default :hg:`push` location.
1465 (deprecated) The URL or directory for the default :hg:`push` location.
1463 ``default:pushurl`` should be used instead.
1466 ``default:pushurl`` should be used instead.
1464
1467
1465 ``phases``
1468 ``phases``
1466 ----------
1469 ----------
1467
1470
1468 Specifies default handling of phases. See :hg:`help phases` for more
1471 Specifies default handling of phases. See :hg:`help phases` for more
1469 information about working with phases.
1472 information about working with phases.
1470
1473
1471 ``publish``
1474 ``publish``
1472 Controls draft phase behavior when working as a server. When true,
1475 Controls draft phase behavior when working as a server. When true,
1473 pushed changesets are set to public in both client and server and
1476 pushed changesets are set to public in both client and server and
1474 pulled or cloned changesets are set to public in the client.
1477 pulled or cloned changesets are set to public in the client.
1475 (default: True)
1478 (default: True)
1476
1479
1477 ``new-commit``
1480 ``new-commit``
1478 Phase of newly-created commits.
1481 Phase of newly-created commits.
1479 (default: draft)
1482 (default: draft)
1480
1483
1481 ``checksubrepos``
1484 ``checksubrepos``
1482 Check the phase of the current revision of each subrepository. Allowed
1485 Check the phase of the current revision of each subrepository. Allowed
1483 values are "ignore", "follow" and "abort". For settings other than
1486 values are "ignore", "follow" and "abort". For settings other than
1484 "ignore", the phase of the current revision of each subrepository is
1487 "ignore", the phase of the current revision of each subrepository is
1485 checked before committing the parent repository. If any of those phases is
1488 checked before committing the parent repository. If any of those phases is
1486 greater than the phase of the parent repository (e.g. if a subrepo is in a
1489 greater than the phase of the parent repository (e.g. if a subrepo is in a
1487 "secret" phase while the parent repo is in "draft" phase), the commit is
1490 "secret" phase while the parent repo is in "draft" phase), the commit is
1488 either aborted (if checksubrepos is set to "abort") or the higher phase is
1491 either aborted (if checksubrepos is set to "abort") or the higher phase is
1489 used for the parent repository commit (if set to "follow").
1492 used for the parent repository commit (if set to "follow").
1490 (default: follow)
1493 (default: follow)
1491
1494
1492
1495
1493 ``profiling``
1496 ``profiling``
1494 -------------
1497 -------------
1495
1498
1496 Specifies profiling type, format, and file output. Two profilers are
1499 Specifies profiling type, format, and file output. Two profilers are
1497 supported: an instrumenting profiler (named ``ls``), and a sampling
1500 supported: an instrumenting profiler (named ``ls``), and a sampling
1498 profiler (named ``stat``).
1501 profiler (named ``stat``).
1499
1502
1500 In this section description, 'profiling data' stands for the raw data
1503 In this section description, 'profiling data' stands for the raw data
1501 collected during profiling, while 'profiling report' stands for a
1504 collected during profiling, while 'profiling report' stands for a
1502 statistical text report generated from the profiling data. The
1505 statistical text report generated from the profiling data. The
1503 profiling is done using lsprof.
1506 profiling is done using lsprof.
1504
1507
1505 ``enabled``
1508 ``enabled``
1506 Enable the profiler.
1509 Enable the profiler.
1507 (default: false)
1510 (default: false)
1508
1511
1509 This is equivalent to passing ``--profile`` on the command line.
1512 This is equivalent to passing ``--profile`` on the command line.
1510
1513
1511 ``type``
1514 ``type``
1512 The type of profiler to use.
1515 The type of profiler to use.
1513 (default: stat)
1516 (default: stat)
1514
1517
1515 ``ls``
1518 ``ls``
1516 Use Python's built-in instrumenting profiler. This profiler
1519 Use Python's built-in instrumenting profiler. This profiler
1517 works on all platforms, but each line number it reports is the
1520 works on all platforms, but each line number it reports is the
1518 first line of a function. This restriction makes it difficult to
1521 first line of a function. This restriction makes it difficult to
1519 identify the expensive parts of a non-trivial function.
1522 identify the expensive parts of a non-trivial function.
1520 ``stat``
1523 ``stat``
1521 Use a statistical profiler, statprof. This profiler is most
1524 Use a statistical profiler, statprof. This profiler is most
1522 useful for profiling commands that run for longer than about 0.1
1525 useful for profiling commands that run for longer than about 0.1
1523 seconds.
1526 seconds.
1524
1527
1525 ``format``
1528 ``format``
1526 Profiling format. Specific to the ``ls`` instrumenting profiler.
1529 Profiling format. Specific to the ``ls`` instrumenting profiler.
1527 (default: text)
1530 (default: text)
1528
1531
1529 ``text``
1532 ``text``
1530 Generate a profiling report. When saving to a file, it should be
1533 Generate a profiling report. When saving to a file, it should be
1531 noted that only the report is saved, and the profiling data is
1534 noted that only the report is saved, and the profiling data is
1532 not kept.
1535 not kept.
1533 ``kcachegrind``
1536 ``kcachegrind``
1534 Format profiling data for kcachegrind use: when saving to a
1537 Format profiling data for kcachegrind use: when saving to a
1535 file, the generated file can directly be loaded into
1538 file, the generated file can directly be loaded into
1536 kcachegrind.
1539 kcachegrind.
1537
1540
1538 ``statformat``
1541 ``statformat``
1539 Profiling format for the ``stat`` profiler.
1542 Profiling format for the ``stat`` profiler.
1540 (default: hotpath)
1543 (default: hotpath)
1541
1544
1542 ``hotpath``
1545 ``hotpath``
1543 Show a tree-based display containing the hot path of execution (where
1546 Show a tree-based display containing the hot path of execution (where
1544 most time was spent).
1547 most time was spent).
1545 ``bymethod``
1548 ``bymethod``
1546 Show a table of methods ordered by how frequently they are active.
1549 Show a table of methods ordered by how frequently they are active.
1547 ``byline``
1550 ``byline``
1548 Show a table of lines in files ordered by how frequently they are active.
1551 Show a table of lines in files ordered by how frequently they are active.
1549 ``json``
1552 ``json``
1550 Render profiling data as JSON.
1553 Render profiling data as JSON.
1551
1554
1552 ``frequency``
1555 ``frequency``
1553 Sampling frequency. Specific to the ``stat`` sampling profiler.
1556 Sampling frequency. Specific to the ``stat`` sampling profiler.
1554 (default: 1000)
1557 (default: 1000)
1555
1558
1556 ``output``
1559 ``output``
1557 File path where profiling data or report should be saved. If the
1560 File path where profiling data or report should be saved. If the
1558 file exists, it is replaced. (default: None, data is printed on
1561 file exists, it is replaced. (default: None, data is printed on
1559 stderr)
1562 stderr)
1560
1563
1561 ``sort``
1564 ``sort``
1562 Sort field. Specific to the ``ls`` instrumenting profiler.
1565 Sort field. Specific to the ``ls`` instrumenting profiler.
1563 One of ``callcount``, ``reccallcount``, ``totaltime`` and
1566 One of ``callcount``, ``reccallcount``, ``totaltime`` and
1564 ``inlinetime``.
1567 ``inlinetime``.
1565 (default: inlinetime)
1568 (default: inlinetime)
1566
1569
1567 ``limit``
1570 ``limit``
1568 Number of lines to show. Specific to the ``ls`` instrumenting profiler.
1571 Number of lines to show. Specific to the ``ls`` instrumenting profiler.
1569 (default: 30)
1572 (default: 30)
1570
1573
1571 ``nested``
1574 ``nested``
1572 Show at most this number of lines of drill-down info after each main entry.
1575 Show at most this number of lines of drill-down info after each main entry.
1573 This can help explain the difference between Total and Inline.
1576 This can help explain the difference between Total and Inline.
1574 Specific to the ``ls`` instrumenting profiler.
1577 Specific to the ``ls`` instrumenting profiler.
1575 (default: 5)
1578 (default: 5)
1576
1579
1577 ``showmin``
1580 ``showmin``
1578 Minimum fraction of samples an entry must have for it to be displayed.
1581 Minimum fraction of samples an entry must have for it to be displayed.
1579 Can be specified as a float between ``0.0`` and ``1.0`` or can have a
1582 Can be specified as a float between ``0.0`` and ``1.0`` or can have a
1580 ``%`` afterwards to allow values up to ``100``. e.g. ``5%``.
1583 ``%`` afterwards to allow values up to ``100``. e.g. ``5%``.
1581
1584
1582 Only used by the ``stat`` profiler.
1585 Only used by the ``stat`` profiler.
1583
1586
1584 For the ``hotpath`` format, default is ``0.05``.
1587 For the ``hotpath`` format, default is ``0.05``.
1585 For the ``chrome`` format, default is ``0.005``.
1588 For the ``chrome`` format, default is ``0.005``.
1586
1589
1587 The option is unused on other formats.
1590 The option is unused on other formats.
1588
1591
1589 ``showmax``
1592 ``showmax``
1590 Maximum fraction of samples an entry can have before it is ignored in
1593 Maximum fraction of samples an entry can have before it is ignored in
1591 display. Values format is the same as ``showmin``.
1594 display. Values format is the same as ``showmin``.
1592
1595
1593 Only used by the ``stat`` profiler.
1596 Only used by the ``stat`` profiler.
1594
1597
1595 For the ``chrome`` format, default is ``0.999``.
1598 For the ``chrome`` format, default is ``0.999``.
1596
1599
1597 The option is unused on other formats.
1600 The option is unused on other formats.
1598
1601
1599 ``progress``
1602 ``progress``
1600 ------------
1603 ------------
1601
1604
1602 Mercurial commands can draw progress bars that are as informative as
1605 Mercurial commands can draw progress bars that are as informative as
1603 possible. Some progress bars only offer indeterminate information, while others
1606 possible. Some progress bars only offer indeterminate information, while others
1604 have a definite end point.
1607 have a definite end point.
1605
1608
1606 ``delay``
1609 ``delay``
1607 Number of seconds (float) before showing the progress bar. (default: 3)
1610 Number of seconds (float) before showing the progress bar. (default: 3)
1608
1611
1609 ``changedelay``
1612 ``changedelay``
1610 Minimum delay before showing a new topic. When set to less than 3 * refresh,
1613 Minimum delay before showing a new topic. When set to less than 3 * refresh,
1611 that value will be used instead. (default: 1)
1614 that value will be used instead. (default: 1)
1612
1615
1613 ``refresh``
1616 ``refresh``
1614 Time in seconds between refreshes of the progress bar. (default: 0.1)
1617 Time in seconds between refreshes of the progress bar. (default: 0.1)
1615
1618
1616 ``format``
1619 ``format``
1617 Format of the progress bar.
1620 Format of the progress bar.
1618
1621
1619 Valid entries for the format field are ``topic``, ``bar``, ``number``,
1622 Valid entries for the format field are ``topic``, ``bar``, ``number``,
1620 ``unit``, ``estimate``, ``speed``, and ``item``. ``item`` defaults to the
1623 ``unit``, ``estimate``, ``speed``, and ``item``. ``item`` defaults to the
1621 last 20 characters of the item, but this can be changed by adding either
1624 last 20 characters of the item, but this can be changed by adding either
1622 ``-<num>`` which would take the last num characters, or ``+<num>`` for the
1625 ``-<num>`` which would take the last num characters, or ``+<num>`` for the
1623 first num characters.
1626 first num characters.
1624
1627
1625 (default: topic bar number estimate)
1628 (default: topic bar number estimate)
1626
1629
1627 ``width``
1630 ``width``
1628 If set, the maximum width of the progress information (that is, min(width,
1631 If set, the maximum width of the progress information (that is, min(width,
1629 term width) will be used).
1632 term width) will be used).
1630
1633
1631 ``clear-complete``
1634 ``clear-complete``
1632 Clear the progress bar after it's done. (default: True)
1635 Clear the progress bar after it's done. (default: True)
1633
1636
1634 ``disable``
1637 ``disable``
1635 If true, don't show a progress bar.
1638 If true, don't show a progress bar.
1636
1639
1637 ``assume-tty``
1640 ``assume-tty``
1638 If true, ALWAYS show a progress bar, unless disable is given.
1641 If true, ALWAYS show a progress bar, unless disable is given.
1639
1642
1640 ``rebase``
1643 ``rebase``
1641 ----------
1644 ----------
1642
1645
1643 ``allowdivergence``
1646 ``allowdivergence``
1644 Default to False, when True allow creating divergence when performing
1647 Default to False, when True allow creating divergence when performing
1645 rebase of obsolete changesets.
1648 rebase of obsolete changesets.
1646
1649
1647 ``revsetalias``
1650 ``revsetalias``
1648 ---------------
1651 ---------------
1649
1652
1650 Alias definitions for revsets. See :hg:`help revsets` for details.
1653 Alias definitions for revsets. See :hg:`help revsets` for details.
1651
1654
1652 ``server``
1655 ``server``
1653 ----------
1656 ----------
1654
1657
1655 Controls generic server settings.
1658 Controls generic server settings.
1656
1659
1657 ``compressionengines``
1660 ``compressionengines``
1658 List of compression engines and their relative priority to advertise
1661 List of compression engines and their relative priority to advertise
1659 to clients.
1662 to clients.
1660
1663
1661 The order of compression engines determines their priority, the first
1664 The order of compression engines determines their priority, the first
1662 having the highest priority. If a compression engine is not listed
1665 having the highest priority. If a compression engine is not listed
1663 here, it won't be advertised to clients.
1666 here, it won't be advertised to clients.
1664
1667
1665 If not set (the default), built-in defaults are used. Run
1668 If not set (the default), built-in defaults are used. Run
1666 :hg:`debuginstall` to list available compression engines and their
1669 :hg:`debuginstall` to list available compression engines and their
1667 default wire protocol priority.
1670 default wire protocol priority.
1668
1671
1669 Older Mercurial clients only support zlib compression and this setting
1672 Older Mercurial clients only support zlib compression and this setting
1670 has no effect for legacy clients.
1673 has no effect for legacy clients.
1671
1674
1672 ``uncompressed``
1675 ``uncompressed``
1673 Whether to allow clients to clone a repository using the
1676 Whether to allow clients to clone a repository using the
1674 uncompressed streaming protocol. This transfers about 40% more
1677 uncompressed streaming protocol. This transfers about 40% more
1675 data than a regular clone, but uses less memory and CPU on both
1678 data than a regular clone, but uses less memory and CPU on both
1676 server and client. Over a LAN (100 Mbps or better) or a very fast
1679 server and client. Over a LAN (100 Mbps or better) or a very fast
1677 WAN, an uncompressed streaming clone is a lot faster (~10x) than a
1680 WAN, an uncompressed streaming clone is a lot faster (~10x) than a
1678 regular clone. Over most WAN connections (anything slower than
1681 regular clone. Over most WAN connections (anything slower than
1679 about 6 Mbps), uncompressed streaming is slower, because of the
1682 about 6 Mbps), uncompressed streaming is slower, because of the
1680 extra data transfer overhead. This mode will also temporarily hold
1683 extra data transfer overhead. This mode will also temporarily hold
1681 the write lock while determining what data to transfer.
1684 the write lock while determining what data to transfer.
1682 (default: True)
1685 (default: True)
1683
1686
1684 ``uncompressedallowsecret``
1687 ``uncompressedallowsecret``
1685 Whether to allow stream clones when the repository contains secret
1688 Whether to allow stream clones when the repository contains secret
1686 changesets. (default: False)
1689 changesets. (default: False)
1687
1690
1688 ``preferuncompressed``
1691 ``preferuncompressed``
1689 When set, clients will try to use the uncompressed streaming
1692 When set, clients will try to use the uncompressed streaming
1690 protocol. (default: False)
1693 protocol. (default: False)
1691
1694
1692 ``disablefullbundle``
1695 ``disablefullbundle``
1693 When set, servers will refuse attempts to do pull-based clones.
1696 When set, servers will refuse attempts to do pull-based clones.
1694 If this option is set, ``preferuncompressed`` and/or clone bundles
1697 If this option is set, ``preferuncompressed`` and/or clone bundles
1695 are highly recommended. Partial clones will still be allowed.
1698 are highly recommended. Partial clones will still be allowed.
1696 (default: False)
1699 (default: False)
1697
1700
1698 ``concurrent-push-mode``
1701 ``concurrent-push-mode``
1699 Level of allowed race condition between two pushing clients.
1702 Level of allowed race condition between two pushing clients.
1700
1703
1701 - 'strict': push is abort if another client touched the repository
1704 - 'strict': push is abort if another client touched the repository
1702 while the push was preparing. (default)
1705 while the push was preparing. (default)
1703 - 'check-related': push is only aborted if it affects head that got also
1706 - 'check-related': push is only aborted if it affects head that got also
1704 affected while the push was preparing.
1707 affected while the push was preparing.
1705
1708
1706 This requires compatible client (version 4.3 and later). Old client will
1709 This requires compatible client (version 4.3 and later). Old client will
1707 use 'strict'.
1710 use 'strict'.
1708
1711
1709 ``validate``
1712 ``validate``
1710 Whether to validate the completeness of pushed changesets by
1713 Whether to validate the completeness of pushed changesets by
1711 checking that all new file revisions specified in manifests are
1714 checking that all new file revisions specified in manifests are
1712 present. (default: False)
1715 present. (default: False)
1713
1716
1714 ``maxhttpheaderlen``
1717 ``maxhttpheaderlen``
1715 Instruct HTTP clients not to send request headers longer than this
1718 Instruct HTTP clients not to send request headers longer than this
1716 many bytes. (default: 1024)
1719 many bytes. (default: 1024)
1717
1720
1718 ``bundle1``
1721 ``bundle1``
1719 Whether to allow clients to push and pull using the legacy bundle1
1722 Whether to allow clients to push and pull using the legacy bundle1
1720 exchange format. (default: True)
1723 exchange format. (default: True)
1721
1724
1722 ``bundle1gd``
1725 ``bundle1gd``
1723 Like ``bundle1`` but only used if the repository is using the
1726 Like ``bundle1`` but only used if the repository is using the
1724 *generaldelta* storage format. (default: True)
1727 *generaldelta* storage format. (default: True)
1725
1728
1726 ``bundle1.push``
1729 ``bundle1.push``
1727 Whether to allow clients to push using the legacy bundle1 exchange
1730 Whether to allow clients to push using the legacy bundle1 exchange
1728 format. (default: True)
1731 format. (default: True)
1729
1732
1730 ``bundle1gd.push``
1733 ``bundle1gd.push``
1731 Like ``bundle1.push`` but only used if the repository is using the
1734 Like ``bundle1.push`` but only used if the repository is using the
1732 *generaldelta* storage format. (default: True)
1735 *generaldelta* storage format. (default: True)
1733
1736
1734 ``bundle1.pull``
1737 ``bundle1.pull``
1735 Whether to allow clients to pull using the legacy bundle1 exchange
1738 Whether to allow clients to pull using the legacy bundle1 exchange
1736 format. (default: True)
1739 format. (default: True)
1737
1740
1738 ``bundle1gd.pull``
1741 ``bundle1gd.pull``
1739 Like ``bundle1.pull`` but only used if the repository is using the
1742 Like ``bundle1.pull`` but only used if the repository is using the
1740 *generaldelta* storage format. (default: True)
1743 *generaldelta* storage format. (default: True)
1741
1744
1742 Large repositories using the *generaldelta* storage format should
1745 Large repositories using the *generaldelta* storage format should
1743 consider setting this option because converting *generaldelta*
1746 consider setting this option because converting *generaldelta*
1744 repositories to the exchange format required by the bundle1 data
1747 repositories to the exchange format required by the bundle1 data
1745 format can consume a lot of CPU.
1748 format can consume a lot of CPU.
1746
1749
1747 ``zliblevel``
1750 ``zliblevel``
1748 Integer between ``-1`` and ``9`` that controls the zlib compression level
1751 Integer between ``-1`` and ``9`` that controls the zlib compression level
1749 for wire protocol commands that send zlib compressed output (notably the
1752 for wire protocol commands that send zlib compressed output (notably the
1750 commands that send repository history data).
1753 commands that send repository history data).
1751
1754
1752 The default (``-1``) uses the default zlib compression level, which is
1755 The default (``-1``) uses the default zlib compression level, which is
1753 likely equivalent to ``6``. ``0`` means no compression. ``9`` means
1756 likely equivalent to ``6``. ``0`` means no compression. ``9`` means
1754 maximum compression.
1757 maximum compression.
1755
1758
1756 Setting this option allows server operators to make trade-offs between
1759 Setting this option allows server operators to make trade-offs between
1757 bandwidth and CPU used. Lowering the compression lowers CPU utilization
1760 bandwidth and CPU used. Lowering the compression lowers CPU utilization
1758 but sends more bytes to clients.
1761 but sends more bytes to clients.
1759
1762
1760 This option only impacts the HTTP server.
1763 This option only impacts the HTTP server.
1761
1764
1762 ``zstdlevel``
1765 ``zstdlevel``
1763 Integer between ``1`` and ``22`` that controls the zstd compression level
1766 Integer between ``1`` and ``22`` that controls the zstd compression level
1764 for wire protocol commands. ``1`` is the minimal amount of compression and
1767 for wire protocol commands. ``1`` is the minimal amount of compression and
1765 ``22`` is the highest amount of compression.
1768 ``22`` is the highest amount of compression.
1766
1769
1767 The default (``3``) should be significantly faster than zlib while likely
1770 The default (``3``) should be significantly faster than zlib while likely
1768 delivering better compression ratios.
1771 delivering better compression ratios.
1769
1772
1770 This option only impacts the HTTP server.
1773 This option only impacts the HTTP server.
1771
1774
1772 See also ``server.zliblevel``.
1775 See also ``server.zliblevel``.
1773
1776
1774 ``smtp``
1777 ``smtp``
1775 --------
1778 --------
1776
1779
1777 Configuration for extensions that need to send email messages.
1780 Configuration for extensions that need to send email messages.
1778
1781
1779 ``host``
1782 ``host``
1780 Host name of mail server, e.g. "mail.example.com".
1783 Host name of mail server, e.g. "mail.example.com".
1781
1784
1782 ``port``
1785 ``port``
1783 Optional. Port to connect to on mail server. (default: 465 if
1786 Optional. Port to connect to on mail server. (default: 465 if
1784 ``tls`` is smtps; 25 otherwise)
1787 ``tls`` is smtps; 25 otherwise)
1785
1788
1786 ``tls``
1789 ``tls``
1787 Optional. Method to enable TLS when connecting to mail server: starttls,
1790 Optional. Method to enable TLS when connecting to mail server: starttls,
1788 smtps or none. (default: none)
1791 smtps or none. (default: none)
1789
1792
1790 ``username``
1793 ``username``
1791 Optional. User name for authenticating with the SMTP server.
1794 Optional. User name for authenticating with the SMTP server.
1792 (default: None)
1795 (default: None)
1793
1796
1794 ``password``
1797 ``password``
1795 Optional. Password for authenticating with the SMTP server. If not
1798 Optional. Password for authenticating with the SMTP server. If not
1796 specified, interactive sessions will prompt the user for a
1799 specified, interactive sessions will prompt the user for a
1797 password; non-interactive sessions will fail. (default: None)
1800 password; non-interactive sessions will fail. (default: None)
1798
1801
1799 ``local_hostname``
1802 ``local_hostname``
1800 Optional. The hostname that the sender can use to identify
1803 Optional. The hostname that the sender can use to identify
1801 itself to the MTA.
1804 itself to the MTA.
1802
1805
1803
1806
1804 ``subpaths``
1807 ``subpaths``
1805 ------------
1808 ------------
1806
1809
1807 Subrepository source URLs can go stale if a remote server changes name
1810 Subrepository source URLs can go stale if a remote server changes name
1808 or becomes temporarily unavailable. This section lets you define
1811 or becomes temporarily unavailable. This section lets you define
1809 rewrite rules of the form::
1812 rewrite rules of the form::
1810
1813
1811 <pattern> = <replacement>
1814 <pattern> = <replacement>
1812
1815
1813 where ``pattern`` is a regular expression matching a subrepository
1816 where ``pattern`` is a regular expression matching a subrepository
1814 source URL and ``replacement`` is the replacement string used to
1817 source URL and ``replacement`` is the replacement string used to
1815 rewrite it. Groups can be matched in ``pattern`` and referenced in
1818 rewrite it. Groups can be matched in ``pattern`` and referenced in
1816 ``replacements``. For instance::
1819 ``replacements``. For instance::
1817
1820
1818 http://server/(.*)-hg/ = http://hg.server/\1/
1821 http://server/(.*)-hg/ = http://hg.server/\1/
1819
1822
1820 rewrites ``http://server/foo-hg/`` into ``http://hg.server/foo/``.
1823 rewrites ``http://server/foo-hg/`` into ``http://hg.server/foo/``.
1821
1824
1822 Relative subrepository paths are first made absolute, and the
1825 Relative subrepository paths are first made absolute, and the
1823 rewrite rules are then applied on the full (absolute) path. If ``pattern``
1826 rewrite rules are then applied on the full (absolute) path. If ``pattern``
1824 doesn't match the full path, an attempt is made to apply it on the
1827 doesn't match the full path, an attempt is made to apply it on the
1825 relative path alone. The rules are applied in definition order.
1828 relative path alone. The rules are applied in definition order.
1826
1829
1827 ``templatealias``
1830 ``templatealias``
1828 -----------------
1831 -----------------
1829
1832
1830 Alias definitions for templates. See :hg:`help templates` for details.
1833 Alias definitions for templates. See :hg:`help templates` for details.
1831
1834
1832 ``templates``
1835 ``templates``
1833 -------------
1836 -------------
1834
1837
1835 Use the ``[templates]`` section to define template strings.
1838 Use the ``[templates]`` section to define template strings.
1836 See :hg:`help templates` for details.
1839 See :hg:`help templates` for details.
1837
1840
1838 ``trusted``
1841 ``trusted``
1839 -----------
1842 -----------
1840
1843
1841 Mercurial will not use the settings in the
1844 Mercurial will not use the settings in the
1842 ``.hg/hgrc`` file from a repository if it doesn't belong to a trusted
1845 ``.hg/hgrc`` file from a repository if it doesn't belong to a trusted
1843 user or to a trusted group, as various hgrc features allow arbitrary
1846 user or to a trusted group, as various hgrc features allow arbitrary
1844 commands to be run. This issue is often encountered when configuring
1847 commands to be run. This issue is often encountered when configuring
1845 hooks or extensions for shared repositories or servers. However,
1848 hooks or extensions for shared repositories or servers. However,
1846 the web interface will use some safe settings from the ``[web]``
1849 the web interface will use some safe settings from the ``[web]``
1847 section.
1850 section.
1848
1851
1849 This section specifies what users and groups are trusted. The
1852 This section specifies what users and groups are trusted. The
1850 current user is always trusted. To trust everybody, list a user or a
1853 current user is always trusted. To trust everybody, list a user or a
1851 group with name ``*``. These settings must be placed in an
1854 group with name ``*``. These settings must be placed in an
1852 *already-trusted file* to take effect, such as ``$HOME/.hgrc`` of the
1855 *already-trusted file* to take effect, such as ``$HOME/.hgrc`` of the
1853 user or service running Mercurial.
1856 user or service running Mercurial.
1854
1857
1855 ``users``
1858 ``users``
1856 Comma-separated list of trusted users.
1859 Comma-separated list of trusted users.
1857
1860
1858 ``groups``
1861 ``groups``
1859 Comma-separated list of trusted groups.
1862 Comma-separated list of trusted groups.
1860
1863
1861
1864
1862 ``ui``
1865 ``ui``
1863 ------
1866 ------
1864
1867
1865 User interface controls.
1868 User interface controls.
1866
1869
1867 ``archivemeta``
1870 ``archivemeta``
1868 Whether to include the .hg_archival.txt file containing meta data
1871 Whether to include the .hg_archival.txt file containing meta data
1869 (hashes for the repository base and for tip) in archives created
1872 (hashes for the repository base and for tip) in archives created
1870 by the :hg:`archive` command or downloaded via hgweb.
1873 by the :hg:`archive` command or downloaded via hgweb.
1871 (default: True)
1874 (default: True)
1872
1875
1873 ``askusername``
1876 ``askusername``
1874 Whether to prompt for a username when committing. If True, and
1877 Whether to prompt for a username when committing. If True, and
1875 neither ``$HGUSER`` nor ``$EMAIL`` has been specified, then the user will
1878 neither ``$HGUSER`` nor ``$EMAIL`` has been specified, then the user will
1876 be prompted to enter a username. If no username is entered, the
1879 be prompted to enter a username. If no username is entered, the
1877 default ``USER@HOST`` is used instead.
1880 default ``USER@HOST`` is used instead.
1878 (default: False)
1881 (default: False)
1879
1882
1880 ``clonebundles``
1883 ``clonebundles``
1881 Whether the "clone bundles" feature is enabled.
1884 Whether the "clone bundles" feature is enabled.
1882
1885
1883 When enabled, :hg:`clone` may download and apply a server-advertised
1886 When enabled, :hg:`clone` may download and apply a server-advertised
1884 bundle file from a URL instead of using the normal exchange mechanism.
1887 bundle file from a URL instead of using the normal exchange mechanism.
1885
1888
1886 This can likely result in faster and more reliable clones.
1889 This can likely result in faster and more reliable clones.
1887
1890
1888 (default: True)
1891 (default: True)
1889
1892
1890 ``clonebundlefallback``
1893 ``clonebundlefallback``
1891 Whether failure to apply an advertised "clone bundle" from a server
1894 Whether failure to apply an advertised "clone bundle" from a server
1892 should result in fallback to a regular clone.
1895 should result in fallback to a regular clone.
1893
1896
1894 This is disabled by default because servers advertising "clone
1897 This is disabled by default because servers advertising "clone
1895 bundles" often do so to reduce server load. If advertised bundles
1898 bundles" often do so to reduce server load. If advertised bundles
1896 start mass failing and clients automatically fall back to a regular
1899 start mass failing and clients automatically fall back to a regular
1897 clone, this would add significant and unexpected load to the server
1900 clone, this would add significant and unexpected load to the server
1898 since the server is expecting clone operations to be offloaded to
1901 since the server is expecting clone operations to be offloaded to
1899 pre-generated bundles. Failing fast (the default behavior) ensures
1902 pre-generated bundles. Failing fast (the default behavior) ensures
1900 clients don't overwhelm the server when "clone bundle" application
1903 clients don't overwhelm the server when "clone bundle" application
1901 fails.
1904 fails.
1902
1905
1903 (default: False)
1906 (default: False)
1904
1907
1905 ``clonebundleprefers``
1908 ``clonebundleprefers``
1906 Defines preferences for which "clone bundles" to use.
1909 Defines preferences for which "clone bundles" to use.
1907
1910
1908 Servers advertising "clone bundles" may advertise multiple available
1911 Servers advertising "clone bundles" may advertise multiple available
1909 bundles. Each bundle may have different attributes, such as the bundle
1912 bundles. Each bundle may have different attributes, such as the bundle
1910 type and compression format. This option is used to prefer a particular
1913 type and compression format. This option is used to prefer a particular
1911 bundle over another.
1914 bundle over another.
1912
1915
1913 The following keys are defined by Mercurial:
1916 The following keys are defined by Mercurial:
1914
1917
1915 BUNDLESPEC
1918 BUNDLESPEC
1916 A bundle type specifier. These are strings passed to :hg:`bundle -t`.
1919 A bundle type specifier. These are strings passed to :hg:`bundle -t`.
1917 e.g. ``gzip-v2`` or ``bzip2-v1``.
1920 e.g. ``gzip-v2`` or ``bzip2-v1``.
1918
1921
1919 COMPRESSION
1922 COMPRESSION
1920 The compression format of the bundle. e.g. ``gzip`` and ``bzip2``.
1923 The compression format of the bundle. e.g. ``gzip`` and ``bzip2``.
1921
1924
1922 Server operators may define custom keys.
1925 Server operators may define custom keys.
1923
1926
1924 Example values: ``COMPRESSION=bzip2``,
1927 Example values: ``COMPRESSION=bzip2``,
1925 ``BUNDLESPEC=gzip-v2, COMPRESSION=gzip``.
1928 ``BUNDLESPEC=gzip-v2, COMPRESSION=gzip``.
1926
1929
1927 By default, the first bundle advertised by the server is used.
1930 By default, the first bundle advertised by the server is used.
1928
1931
1929 ``color``
1932 ``color``
1930 When to colorize output. Possible value are Boolean ("yes" or "no"), or
1933 When to colorize output. Possible value are Boolean ("yes" or "no"), or
1931 "debug", or "always". (default: "yes"). "yes" will use color whenever it
1934 "debug", or "always". (default: "yes"). "yes" will use color whenever it
1932 seems possible. See :hg:`help color` for details.
1935 seems possible. See :hg:`help color` for details.
1933
1936
1934 ``commitsubrepos``
1937 ``commitsubrepos``
1935 Whether to commit modified subrepositories when committing the
1938 Whether to commit modified subrepositories when committing the
1936 parent repository. If False and one subrepository has uncommitted
1939 parent repository. If False and one subrepository has uncommitted
1937 changes, abort the commit.
1940 changes, abort the commit.
1938 (default: False)
1941 (default: False)
1939
1942
1940 ``debug``
1943 ``debug``
1941 Print debugging information. (default: False)
1944 Print debugging information. (default: False)
1942
1945
1943 ``editor``
1946 ``editor``
1944 The editor to use during a commit. (default: ``$EDITOR`` or ``vi``)
1947 The editor to use during a commit. (default: ``$EDITOR`` or ``vi``)
1945
1948
1946 ``fallbackencoding``
1949 ``fallbackencoding``
1947 Encoding to try if it's not possible to decode the changelog using
1950 Encoding to try if it's not possible to decode the changelog using
1948 UTF-8. (default: ISO-8859-1)
1951 UTF-8. (default: ISO-8859-1)
1949
1952
1950 ``graphnodetemplate``
1953 ``graphnodetemplate``
1951 The template used to print changeset nodes in an ASCII revision graph.
1954 The template used to print changeset nodes in an ASCII revision graph.
1952 (default: ``{graphnode}``)
1955 (default: ``{graphnode}``)
1953
1956
1954 ``ignore``
1957 ``ignore``
1955 A file to read per-user ignore patterns from. This file should be
1958 A file to read per-user ignore patterns from. This file should be
1956 in the same format as a repository-wide .hgignore file. Filenames
1959 in the same format as a repository-wide .hgignore file. Filenames
1957 are relative to the repository root. This option supports hook syntax,
1960 are relative to the repository root. This option supports hook syntax,
1958 so if you want to specify multiple ignore files, you can do so by
1961 so if you want to specify multiple ignore files, you can do so by
1959 setting something like ``ignore.other = ~/.hgignore2``. For details
1962 setting something like ``ignore.other = ~/.hgignore2``. For details
1960 of the ignore file format, see the ``hgignore(5)`` man page.
1963 of the ignore file format, see the ``hgignore(5)`` man page.
1961
1964
1962 ``interactive``
1965 ``interactive``
1963 Allow to prompt the user. (default: True)
1966 Allow to prompt the user. (default: True)
1964
1967
1965 ``interface``
1968 ``interface``
1966 Select the default interface for interactive features (default: text).
1969 Select the default interface for interactive features (default: text).
1967 Possible values are 'text' and 'curses'.
1970 Possible values are 'text' and 'curses'.
1968
1971
1969 ``interface.chunkselector``
1972 ``interface.chunkselector``
1970 Select the interface for change recording (e.g. :hg:`commit -i`).
1973 Select the interface for change recording (e.g. :hg:`commit -i`).
1971 Possible values are 'text' and 'curses'.
1974 Possible values are 'text' and 'curses'.
1972 This config overrides the interface specified by ui.interface.
1975 This config overrides the interface specified by ui.interface.
1973
1976
1974 ``logtemplate``
1977 ``logtemplate``
1975 Template string for commands that print changesets.
1978 Template string for commands that print changesets.
1976
1979
1977 ``merge``
1980 ``merge``
1978 The conflict resolution program to use during a manual merge.
1981 The conflict resolution program to use during a manual merge.
1979 For more information on merge tools see :hg:`help merge-tools`.
1982 For more information on merge tools see :hg:`help merge-tools`.
1980 For configuring merge tools see the ``[merge-tools]`` section.
1983 For configuring merge tools see the ``[merge-tools]`` section.
1981
1984
1982 ``mergemarkers``
1985 ``mergemarkers``
1983 Sets the merge conflict marker label styling. The ``detailed``
1986 Sets the merge conflict marker label styling. The ``detailed``
1984 style uses the ``mergemarkertemplate`` setting to style the labels.
1987 style uses the ``mergemarkertemplate`` setting to style the labels.
1985 The ``basic`` style just uses 'local' and 'other' as the marker label.
1988 The ``basic`` style just uses 'local' and 'other' as the marker label.
1986 One of ``basic`` or ``detailed``.
1989 One of ``basic`` or ``detailed``.
1987 (default: ``basic``)
1990 (default: ``basic``)
1988
1991
1989 ``mergemarkertemplate``
1992 ``mergemarkertemplate``
1990 The template used to print the commit description next to each conflict
1993 The template used to print the commit description next to each conflict
1991 marker during merge conflicts. See :hg:`help templates` for the template
1994 marker during merge conflicts. See :hg:`help templates` for the template
1992 format.
1995 format.
1993
1996
1994 Defaults to showing the hash, tags, branches, bookmarks, author, and
1997 Defaults to showing the hash, tags, branches, bookmarks, author, and
1995 the first line of the commit description.
1998 the first line of the commit description.
1996
1999
1997 If you use non-ASCII characters in names for tags, branches, bookmarks,
2000 If you use non-ASCII characters in names for tags, branches, bookmarks,
1998 authors, and/or commit descriptions, you must pay attention to encodings of
2001 authors, and/or commit descriptions, you must pay attention to encodings of
1999 managed files. At template expansion, non-ASCII characters use the encoding
2002 managed files. At template expansion, non-ASCII characters use the encoding
2000 specified by the ``--encoding`` global option, ``HGENCODING`` or other
2003 specified by the ``--encoding`` global option, ``HGENCODING`` or other
2001 environment variables that govern your locale. If the encoding of the merge
2004 environment variables that govern your locale. If the encoding of the merge
2002 markers is different from the encoding of the merged files,
2005 markers is different from the encoding of the merged files,
2003 serious problems may occur.
2006 serious problems may occur.
2004
2007
2005 ``origbackuppath``
2008 ``origbackuppath``
2006 The path to a directory used to store generated .orig files. If the path is
2009 The path to a directory used to store generated .orig files. If the path is
2007 not a directory, one will be created.
2010 not a directory, one will be created.
2008
2011
2009 ``paginate``
2012 ``paginate``
2010 Control the pagination of command output (default: True). See :hg:`help pager`
2013 Control the pagination of command output (default: True). See :hg:`help pager`
2011 for details.
2014 for details.
2012
2015
2013 ``patch``
2016 ``patch``
2014 An optional external tool that ``hg import`` and some extensions
2017 An optional external tool that ``hg import`` and some extensions
2015 will use for applying patches. By default Mercurial uses an
2018 will use for applying patches. By default Mercurial uses an
2016 internal patch utility. The external tool must work as the common
2019 internal patch utility. The external tool must work as the common
2017 Unix ``patch`` program. In particular, it must accept a ``-p``
2020 Unix ``patch`` program. In particular, it must accept a ``-p``
2018 argument to strip patch headers, a ``-d`` argument to specify the
2021 argument to strip patch headers, a ``-d`` argument to specify the
2019 current directory, a file name to patch, and a patch file to take
2022 current directory, a file name to patch, and a patch file to take
2020 from stdin.
2023 from stdin.
2021
2024
2022 It is possible to specify a patch tool together with extra
2025 It is possible to specify a patch tool together with extra
2023 arguments. For example, setting this option to ``patch --merge``
2026 arguments. For example, setting this option to ``patch --merge``
2024 will use the ``patch`` program with its 2-way merge option.
2027 will use the ``patch`` program with its 2-way merge option.
2025
2028
2026 ``portablefilenames``
2029 ``portablefilenames``
2027 Check for portable filenames. Can be ``warn``, ``ignore`` or ``abort``.
2030 Check for portable filenames. Can be ``warn``, ``ignore`` or ``abort``.
2028 (default: ``warn``)
2031 (default: ``warn``)
2029
2032
2030 ``warn``
2033 ``warn``
2031 Print a warning message on POSIX platforms, if a file with a non-portable
2034 Print a warning message on POSIX platforms, if a file with a non-portable
2032 filename is added (e.g. a file with a name that can't be created on
2035 filename is added (e.g. a file with a name that can't be created on
2033 Windows because it contains reserved parts like ``AUX``, reserved
2036 Windows because it contains reserved parts like ``AUX``, reserved
2034 characters like ``:``, or would cause a case collision with an existing
2037 characters like ``:``, or would cause a case collision with an existing
2035 file).
2038 file).
2036
2039
2037 ``ignore``
2040 ``ignore``
2038 Don't print a warning.
2041 Don't print a warning.
2039
2042
2040 ``abort``
2043 ``abort``
2041 The command is aborted.
2044 The command is aborted.
2042
2045
2043 ``true``
2046 ``true``
2044 Alias for ``warn``.
2047 Alias for ``warn``.
2045
2048
2046 ``false``
2049 ``false``
2047 Alias for ``ignore``.
2050 Alias for ``ignore``.
2048
2051
2049 .. container:: windows
2052 .. container:: windows
2050
2053
2051 On Windows, this configuration option is ignored and the command aborted.
2054 On Windows, this configuration option is ignored and the command aborted.
2052
2055
2053 ``quiet``
2056 ``quiet``
2054 Reduce the amount of output printed.
2057 Reduce the amount of output printed.
2055 (default: False)
2058 (default: False)
2056
2059
2057 ``remotecmd``
2060 ``remotecmd``
2058 Remote command to use for clone/push/pull operations.
2061 Remote command to use for clone/push/pull operations.
2059 (default: ``hg``)
2062 (default: ``hg``)
2060
2063
2061 ``report_untrusted``
2064 ``report_untrusted``
2062 Warn if a ``.hg/hgrc`` file is ignored due to not being owned by a
2065 Warn if a ``.hg/hgrc`` file is ignored due to not being owned by a
2063 trusted user or group.
2066 trusted user or group.
2064 (default: True)
2067 (default: True)
2065
2068
2066 ``slash``
2069 ``slash``
2067 Display paths using a slash (``/``) as the path separator. This
2070 Display paths using a slash (``/``) as the path separator. This
2068 only makes a difference on systems where the default path
2071 only makes a difference on systems where the default path
2069 separator is not the slash character (e.g. Windows uses the
2072 separator is not the slash character (e.g. Windows uses the
2070 backslash character (``\``)).
2073 backslash character (``\``)).
2071 (default: False)
2074 (default: False)
2072
2075
2073 ``statuscopies``
2076 ``statuscopies``
2074 Display copies in the status command.
2077 Display copies in the status command.
2075
2078
2076 ``ssh``
2079 ``ssh``
2077 Command to use for SSH connections. (default: ``ssh``)
2080 Command to use for SSH connections. (default: ``ssh``)
2078
2081
2079 ``strict``
2082 ``strict``
2080 Require exact command names, instead of allowing unambiguous
2083 Require exact command names, instead of allowing unambiguous
2081 abbreviations. (default: False)
2084 abbreviations. (default: False)
2082
2085
2083 ``style``
2086 ``style``
2084 Name of style to use for command output.
2087 Name of style to use for command output.
2085
2088
2086 ``supportcontact``
2089 ``supportcontact``
2087 A URL where users should report a Mercurial traceback. Use this if you are a
2090 A URL where users should report a Mercurial traceback. Use this if you are a
2088 large organisation with its own Mercurial deployment process and crash
2091 large organisation with its own Mercurial deployment process and crash
2089 reports should be addressed to your internal support.
2092 reports should be addressed to your internal support.
2090
2093
2091 ``textwidth``
2094 ``textwidth``
2092 Maximum width of help text. A longer line generated by ``hg help`` or
2095 Maximum width of help text. A longer line generated by ``hg help`` or
2093 ``hg subcommand --help`` will be broken after white space to get this
2096 ``hg subcommand --help`` will be broken after white space to get this
2094 width or the terminal width, whichever comes first.
2097 width or the terminal width, whichever comes first.
2095 A non-positive value will disable this and the terminal width will be
2098 A non-positive value will disable this and the terminal width will be
2096 used. (default: 78)
2099 used. (default: 78)
2097
2100
2098 ``timeout``
2101 ``timeout``
2099 The timeout used when a lock is held (in seconds), a negative value
2102 The timeout used when a lock is held (in seconds), a negative value
2100 means no timeout. (default: 600)
2103 means no timeout. (default: 600)
2101
2104
2102 ``traceback``
2105 ``traceback``
2103 Mercurial always prints a traceback when an unknown exception
2106 Mercurial always prints a traceback when an unknown exception
2104 occurs. Setting this to True will make Mercurial print a traceback
2107 occurs. Setting this to True will make Mercurial print a traceback
2105 on all exceptions, even those recognized by Mercurial (such as
2108 on all exceptions, even those recognized by Mercurial (such as
2106 IOError or MemoryError). (default: False)
2109 IOError or MemoryError). (default: False)
2107
2110
2108 ``tweakdefaults``
2111 ``tweakdefaults``
2109
2112
2110 By default Mercurial's behavior changes very little from release
2113 By default Mercurial's behavior changes very little from release
2111 to release, but over time the recommended config settings
2114 to release, but over time the recommended config settings
2112 shift. Enable this config to opt in to get automatic tweaks to
2115 shift. Enable this config to opt in to get automatic tweaks to
2113 Mercurial's behavior over time. This config setting will have no
2116 Mercurial's behavior over time. This config setting will have no
2114 effet if ``HGPLAIN` is set or ``HGPLAINEXCEPT`` is set and does
2117 effet if ``HGPLAIN` is set or ``HGPLAINEXCEPT`` is set and does
2115 not include ``tweakdefaults``. (default: False)
2118 not include ``tweakdefaults``. (default: False)
2116
2119
2117 ``username``
2120 ``username``
2118 The committer of a changeset created when running "commit".
2121 The committer of a changeset created when running "commit".
2119 Typically a person's name and email address, e.g. ``Fred Widget
2122 Typically a person's name and email address, e.g. ``Fred Widget
2120 <fred@example.com>``. Environment variables in the
2123 <fred@example.com>``. Environment variables in the
2121 username are expanded.
2124 username are expanded.
2122
2125
2123 (default: ``$EMAIL`` or ``username@hostname``. If the username in
2126 (default: ``$EMAIL`` or ``username@hostname``. If the username in
2124 hgrc is empty, e.g. if the system admin set ``username =`` in the
2127 hgrc is empty, e.g. if the system admin set ``username =`` in the
2125 system hgrc, it has to be specified manually or in a different
2128 system hgrc, it has to be specified manually or in a different
2126 hgrc file)
2129 hgrc file)
2127
2130
2128 ``verbose``
2131 ``verbose``
2129 Increase the amount of output printed. (default: False)
2132 Increase the amount of output printed. (default: False)
2130
2133
2131
2134
2132 ``web``
2135 ``web``
2133 -------
2136 -------
2134
2137
2135 Web interface configuration. The settings in this section apply to
2138 Web interface configuration. The settings in this section apply to
2136 both the builtin webserver (started by :hg:`serve`) and the script you
2139 both the builtin webserver (started by :hg:`serve`) and the script you
2137 run through a webserver (``hgweb.cgi`` and the derivatives for FastCGI
2140 run through a webserver (``hgweb.cgi`` and the derivatives for FastCGI
2138 and WSGI).
2141 and WSGI).
2139
2142
2140 The Mercurial webserver does no authentication (it does not prompt for
2143 The Mercurial webserver does no authentication (it does not prompt for
2141 usernames and passwords to validate *who* users are), but it does do
2144 usernames and passwords to validate *who* users are), but it does do
2142 authorization (it grants or denies access for *authenticated users*
2145 authorization (it grants or denies access for *authenticated users*
2143 based on settings in this section). You must either configure your
2146 based on settings in this section). You must either configure your
2144 webserver to do authentication for you, or disable the authorization
2147 webserver to do authentication for you, or disable the authorization
2145 checks.
2148 checks.
2146
2149
2147 For a quick setup in a trusted environment, e.g., a private LAN, where
2150 For a quick setup in a trusted environment, e.g., a private LAN, where
2148 you want it to accept pushes from anybody, you can use the following
2151 you want it to accept pushes from anybody, you can use the following
2149 command line::
2152 command line::
2150
2153
2151 $ hg --config web.allow_push=* --config web.push_ssl=False serve
2154 $ hg --config web.allow_push=* --config web.push_ssl=False serve
2152
2155
2153 Note that this will allow anybody to push anything to the server and
2156 Note that this will allow anybody to push anything to the server and
2154 that this should not be used for public servers.
2157 that this should not be used for public servers.
2155
2158
2156 The full set of options is:
2159 The full set of options is:
2157
2160
2158 ``accesslog``
2161 ``accesslog``
2159 Where to output the access log. (default: stdout)
2162 Where to output the access log. (default: stdout)
2160
2163
2161 ``address``
2164 ``address``
2162 Interface address to bind to. (default: all)
2165 Interface address to bind to. (default: all)
2163
2166
2164 ``allow_archive``
2167 ``allow_archive``
2165 List of archive format (bz2, gz, zip) allowed for downloading.
2168 List of archive format (bz2, gz, zip) allowed for downloading.
2166 (default: empty)
2169 (default: empty)
2167
2170
2168 ``allowbz2``
2171 ``allowbz2``
2169 (DEPRECATED) Whether to allow .tar.bz2 downloading of repository
2172 (DEPRECATED) Whether to allow .tar.bz2 downloading of repository
2170 revisions.
2173 revisions.
2171 (default: False)
2174 (default: False)
2172
2175
2173 ``allowgz``
2176 ``allowgz``
2174 (DEPRECATED) Whether to allow .tar.gz downloading of repository
2177 (DEPRECATED) Whether to allow .tar.gz downloading of repository
2175 revisions.
2178 revisions.
2176 (default: False)
2179 (default: False)
2177
2180
2178 ``allowpull``
2181 ``allowpull``
2179 Whether to allow pulling from the repository. (default: True)
2182 Whether to allow pulling from the repository. (default: True)
2180
2183
2181 ``allow_push``
2184 ``allow_push``
2182 Whether to allow pushing to the repository. If empty or not set,
2185 Whether to allow pushing to the repository. If empty or not set,
2183 pushing is not allowed. If the special value ``*``, any remote
2186 pushing is not allowed. If the special value ``*``, any remote
2184 user can push, including unauthenticated users. Otherwise, the
2187 user can push, including unauthenticated users. Otherwise, the
2185 remote user must have been authenticated, and the authenticated
2188 remote user must have been authenticated, and the authenticated
2186 user name must be present in this list. The contents of the
2189 user name must be present in this list. The contents of the
2187 allow_push list are examined after the deny_push list.
2190 allow_push list are examined after the deny_push list.
2188
2191
2189 ``allow_read``
2192 ``allow_read``
2190 If the user has not already been denied repository access due to
2193 If the user has not already been denied repository access due to
2191 the contents of deny_read, this list determines whether to grant
2194 the contents of deny_read, this list determines whether to grant
2192 repository access to the user. If this list is not empty, and the
2195 repository access to the user. If this list is not empty, and the
2193 user is unauthenticated or not present in the list, then access is
2196 user is unauthenticated or not present in the list, then access is
2194 denied for the user. If the list is empty or not set, then access
2197 denied for the user. If the list is empty or not set, then access
2195 is permitted to all users by default. Setting allow_read to the
2198 is permitted to all users by default. Setting allow_read to the
2196 special value ``*`` is equivalent to it not being set (i.e. access
2199 special value ``*`` is equivalent to it not being set (i.e. access
2197 is permitted to all users). The contents of the allow_read list are
2200 is permitted to all users). The contents of the allow_read list are
2198 examined after the deny_read list.
2201 examined after the deny_read list.
2199
2202
2200 ``allowzip``
2203 ``allowzip``
2201 (DEPRECATED) Whether to allow .zip downloading of repository
2204 (DEPRECATED) Whether to allow .zip downloading of repository
2202 revisions. This feature creates temporary files.
2205 revisions. This feature creates temporary files.
2203 (default: False)
2206 (default: False)
2204
2207
2205 ``archivesubrepos``
2208 ``archivesubrepos``
2206 Whether to recurse into subrepositories when archiving.
2209 Whether to recurse into subrepositories when archiving.
2207 (default: False)
2210 (default: False)
2208
2211
2209 ``baseurl``
2212 ``baseurl``
2210 Base URL to use when publishing URLs in other locations, so
2213 Base URL to use when publishing URLs in other locations, so
2211 third-party tools like email notification hooks can construct
2214 third-party tools like email notification hooks can construct
2212 URLs. Example: ``http://hgserver/repos/``.
2215 URLs. Example: ``http://hgserver/repos/``.
2213
2216
2214 ``cacerts``
2217 ``cacerts``
2215 Path to file containing a list of PEM encoded certificate
2218 Path to file containing a list of PEM encoded certificate
2216 authority certificates. Environment variables and ``~user``
2219 authority certificates. Environment variables and ``~user``
2217 constructs are expanded in the filename. If specified on the
2220 constructs are expanded in the filename. If specified on the
2218 client, then it will verify the identity of remote HTTPS servers
2221 client, then it will verify the identity of remote HTTPS servers
2219 with these certificates.
2222 with these certificates.
2220
2223
2221 To disable SSL verification temporarily, specify ``--insecure`` from
2224 To disable SSL verification temporarily, specify ``--insecure`` from
2222 command line.
2225 command line.
2223
2226
2224 You can use OpenSSL's CA certificate file if your platform has
2227 You can use OpenSSL's CA certificate file if your platform has
2225 one. On most Linux systems this will be
2228 one. On most Linux systems this will be
2226 ``/etc/ssl/certs/ca-certificates.crt``. Otherwise you will have to
2229 ``/etc/ssl/certs/ca-certificates.crt``. Otherwise you will have to
2227 generate this file manually. The form must be as follows::
2230 generate this file manually. The form must be as follows::
2228
2231
2229 -----BEGIN CERTIFICATE-----
2232 -----BEGIN CERTIFICATE-----
2230 ... (certificate in base64 PEM encoding) ...
2233 ... (certificate in base64 PEM encoding) ...
2231 -----END CERTIFICATE-----
2234 -----END CERTIFICATE-----
2232 -----BEGIN CERTIFICATE-----
2235 -----BEGIN CERTIFICATE-----
2233 ... (certificate in base64 PEM encoding) ...
2236 ... (certificate in base64 PEM encoding) ...
2234 -----END CERTIFICATE-----
2237 -----END CERTIFICATE-----
2235
2238
2236 ``cache``
2239 ``cache``
2237 Whether to support caching in hgweb. (default: True)
2240 Whether to support caching in hgweb. (default: True)
2238
2241
2239 ``certificate``
2242 ``certificate``
2240 Certificate to use when running :hg:`serve`.
2243 Certificate to use when running :hg:`serve`.
2241
2244
2242 ``collapse``
2245 ``collapse``
2243 With ``descend`` enabled, repositories in subdirectories are shown at
2246 With ``descend`` enabled, repositories in subdirectories are shown at
2244 a single level alongside repositories in the current path. With
2247 a single level alongside repositories in the current path. With
2245 ``collapse`` also enabled, repositories residing at a deeper level than
2248 ``collapse`` also enabled, repositories residing at a deeper level than
2246 the current path are grouped behind navigable directory entries that
2249 the current path are grouped behind navigable directory entries that
2247 lead to the locations of these repositories. In effect, this setting
2250 lead to the locations of these repositories. In effect, this setting
2248 collapses each collection of repositories found within a subdirectory
2251 collapses each collection of repositories found within a subdirectory
2249 into a single entry for that subdirectory. (default: False)
2252 into a single entry for that subdirectory. (default: False)
2250
2253
2251 ``comparisoncontext``
2254 ``comparisoncontext``
2252 Number of lines of context to show in side-by-side file comparison. If
2255 Number of lines of context to show in side-by-side file comparison. If
2253 negative or the value ``full``, whole files are shown. (default: 5)
2256 negative or the value ``full``, whole files are shown. (default: 5)
2254
2257
2255 This setting can be overridden by a ``context`` request parameter to the
2258 This setting can be overridden by a ``context`` request parameter to the
2256 ``comparison`` command, taking the same values.
2259 ``comparison`` command, taking the same values.
2257
2260
2258 ``contact``
2261 ``contact``
2259 Name or email address of the person in charge of the repository.
2262 Name or email address of the person in charge of the repository.
2260 (default: ui.username or ``$EMAIL`` or "unknown" if unset or empty)
2263 (default: ui.username or ``$EMAIL`` or "unknown" if unset or empty)
2261
2264
2262 ``csp``
2265 ``csp``
2263 Send a ``Content-Security-Policy`` HTTP header with this value.
2266 Send a ``Content-Security-Policy`` HTTP header with this value.
2264
2267
2265 The value may contain a special string ``%nonce%``, which will be replaced
2268 The value may contain a special string ``%nonce%``, which will be replaced
2266 by a randomly-generated one-time use value. If the value contains
2269 by a randomly-generated one-time use value. If the value contains
2267 ``%nonce%``, ``web.cache`` will be disabled, as caching undermines the
2270 ``%nonce%``, ``web.cache`` will be disabled, as caching undermines the
2268 one-time property of the nonce. This nonce will also be inserted into
2271 one-time property of the nonce. This nonce will also be inserted into
2269 ``<script>`` elements containing inline JavaScript.
2272 ``<script>`` elements containing inline JavaScript.
2270
2273
2271 Note: lots of HTML content sent by the server is derived from repository
2274 Note: lots of HTML content sent by the server is derived from repository
2272 data. Please consider the potential for malicious repository data to
2275 data. Please consider the potential for malicious repository data to
2273 "inject" itself into generated HTML content as part of your security
2276 "inject" itself into generated HTML content as part of your security
2274 threat model.
2277 threat model.
2275
2278
2276 ``deny_push``
2279 ``deny_push``
2277 Whether to deny pushing to the repository. If empty or not set,
2280 Whether to deny pushing to the repository. If empty or not set,
2278 push is not denied. If the special value ``*``, all remote users are
2281 push is not denied. If the special value ``*``, all remote users are
2279 denied push. Otherwise, unauthenticated users are all denied, and
2282 denied push. Otherwise, unauthenticated users are all denied, and
2280 any authenticated user name present in this list is also denied. The
2283 any authenticated user name present in this list is also denied. The
2281 contents of the deny_push list are examined before the allow_push list.
2284 contents of the deny_push list are examined before the allow_push list.
2282
2285
2283 ``deny_read``
2286 ``deny_read``
2284 Whether to deny reading/viewing of the repository. If this list is
2287 Whether to deny reading/viewing of the repository. If this list is
2285 not empty, unauthenticated users are all denied, and any
2288 not empty, unauthenticated users are all denied, and any
2286 authenticated user name present in this list is also denied access to
2289 authenticated user name present in this list is also denied access to
2287 the repository. If set to the special value ``*``, all remote users
2290 the repository. If set to the special value ``*``, all remote users
2288 are denied access (rarely needed ;). If deny_read is empty or not set,
2291 are denied access (rarely needed ;). If deny_read is empty or not set,
2289 the determination of repository access depends on the presence and
2292 the determination of repository access depends on the presence and
2290 content of the allow_read list (see description). If both
2293 content of the allow_read list (see description). If both
2291 deny_read and allow_read are empty or not set, then access is
2294 deny_read and allow_read are empty or not set, then access is
2292 permitted to all users by default. If the repository is being
2295 permitted to all users by default. If the repository is being
2293 served via hgwebdir, denied users will not be able to see it in
2296 served via hgwebdir, denied users will not be able to see it in
2294 the list of repositories. The contents of the deny_read list have
2297 the list of repositories. The contents of the deny_read list have
2295 priority over (are examined before) the contents of the allow_read
2298 priority over (are examined before) the contents of the allow_read
2296 list.
2299 list.
2297
2300
2298 ``descend``
2301 ``descend``
2299 hgwebdir indexes will not descend into subdirectories. Only repositories
2302 hgwebdir indexes will not descend into subdirectories. Only repositories
2300 directly in the current path will be shown (other repositories are still
2303 directly in the current path will be shown (other repositories are still
2301 available from the index corresponding to their containing path).
2304 available from the index corresponding to their containing path).
2302
2305
2303 ``description``
2306 ``description``
2304 Textual description of the repository's purpose or contents.
2307 Textual description of the repository's purpose or contents.
2305 (default: "unknown")
2308 (default: "unknown")
2306
2309
2307 ``encoding``
2310 ``encoding``
2308 Character encoding name. (default: the current locale charset)
2311 Character encoding name. (default: the current locale charset)
2309 Example: "UTF-8".
2312 Example: "UTF-8".
2310
2313
2311 ``errorlog``
2314 ``errorlog``
2312 Where to output the error log. (default: stderr)
2315 Where to output the error log. (default: stderr)
2313
2316
2314 ``guessmime``
2317 ``guessmime``
2315 Control MIME types for raw download of file content.
2318 Control MIME types for raw download of file content.
2316 Set to True to let hgweb guess the content type from the file
2319 Set to True to let hgweb guess the content type from the file
2317 extension. This will serve HTML files as ``text/html`` and might
2320 extension. This will serve HTML files as ``text/html`` and might
2318 allow cross-site scripting attacks when serving untrusted
2321 allow cross-site scripting attacks when serving untrusted
2319 repositories. (default: False)
2322 repositories. (default: False)
2320
2323
2321 ``hidden``
2324 ``hidden``
2322 Whether to hide the repository in the hgwebdir index.
2325 Whether to hide the repository in the hgwebdir index.
2323 (default: False)
2326 (default: False)
2324
2327
2325 ``ipv6``
2328 ``ipv6``
2326 Whether to use IPv6. (default: False)
2329 Whether to use IPv6. (default: False)
2327
2330
2328 ``labels``
2331 ``labels``
2329 List of string *labels* associated with the repository.
2332 List of string *labels* associated with the repository.
2330
2333
2331 Labels are exposed as a template keyword and can be used to customize
2334 Labels are exposed as a template keyword and can be used to customize
2332 output. e.g. the ``index`` template can group or filter repositories
2335 output. e.g. the ``index`` template can group or filter repositories
2333 by labels and the ``summary`` template can display additional content
2336 by labels and the ``summary`` template can display additional content
2334 if a specific label is present.
2337 if a specific label is present.
2335
2338
2336 ``logoimg``
2339 ``logoimg``
2337 File name of the logo image that some templates display on each page.
2340 File name of the logo image that some templates display on each page.
2338 The file name is relative to ``staticurl``. That is, the full path to
2341 The file name is relative to ``staticurl``. That is, the full path to
2339 the logo image is "staticurl/logoimg".
2342 the logo image is "staticurl/logoimg".
2340 If unset, ``hglogo.png`` will be used.
2343 If unset, ``hglogo.png`` will be used.
2341
2344
2342 ``logourl``
2345 ``logourl``
2343 Base URL to use for logos. If unset, ``https://mercurial-scm.org/``
2346 Base URL to use for logos. If unset, ``https://mercurial-scm.org/``
2344 will be used.
2347 will be used.
2345
2348
2346 ``maxchanges``
2349 ``maxchanges``
2347 Maximum number of changes to list on the changelog. (default: 10)
2350 Maximum number of changes to list on the changelog. (default: 10)
2348
2351
2349 ``maxfiles``
2352 ``maxfiles``
2350 Maximum number of files to list per changeset. (default: 10)
2353 Maximum number of files to list per changeset. (default: 10)
2351
2354
2352 ``maxshortchanges``
2355 ``maxshortchanges``
2353 Maximum number of changes to list on the shortlog, graph or filelog
2356 Maximum number of changes to list on the shortlog, graph or filelog
2354 pages. (default: 60)
2357 pages. (default: 60)
2355
2358
2356 ``name``
2359 ``name``
2357 Repository name to use in the web interface.
2360 Repository name to use in the web interface.
2358 (default: current working directory)
2361 (default: current working directory)
2359
2362
2360 ``port``
2363 ``port``
2361 Port to listen on. (default: 8000)
2364 Port to listen on. (default: 8000)
2362
2365
2363 ``prefix``
2366 ``prefix``
2364 Prefix path to serve from. (default: '' (server root))
2367 Prefix path to serve from. (default: '' (server root))
2365
2368
2366 ``push_ssl``
2369 ``push_ssl``
2367 Whether to require that inbound pushes be transported over SSL to
2370 Whether to require that inbound pushes be transported over SSL to
2368 prevent password sniffing. (default: True)
2371 prevent password sniffing. (default: True)
2369
2372
2370 ``refreshinterval``
2373 ``refreshinterval``
2371 How frequently directory listings re-scan the filesystem for new
2374 How frequently directory listings re-scan the filesystem for new
2372 repositories, in seconds. This is relevant when wildcards are used
2375 repositories, in seconds. This is relevant when wildcards are used
2373 to define paths. Depending on how much filesystem traversal is
2376 to define paths. Depending on how much filesystem traversal is
2374 required, refreshing may negatively impact performance.
2377 required, refreshing may negatively impact performance.
2375
2378
2376 Values less than or equal to 0 always refresh.
2379 Values less than or equal to 0 always refresh.
2377 (default: 20)
2380 (default: 20)
2378
2381
2379 ``staticurl``
2382 ``staticurl``
2380 Base URL to use for static files. If unset, static files (e.g. the
2383 Base URL to use for static files. If unset, static files (e.g. the
2381 hgicon.png favicon) will be served by the CGI script itself. Use
2384 hgicon.png favicon) will be served by the CGI script itself. Use
2382 this setting to serve them directly with the HTTP server.
2385 this setting to serve them directly with the HTTP server.
2383 Example: ``http://hgserver/static/``.
2386 Example: ``http://hgserver/static/``.
2384
2387
2385 ``stripes``
2388 ``stripes``
2386 How many lines a "zebra stripe" should span in multi-line output.
2389 How many lines a "zebra stripe" should span in multi-line output.
2387 Set to 0 to disable. (default: 1)
2390 Set to 0 to disable. (default: 1)
2388
2391
2389 ``style``
2392 ``style``
2390 Which template map style to use. The available options are the names of
2393 Which template map style to use. The available options are the names of
2391 subdirectories in the HTML templates path. (default: ``paper``)
2394 subdirectories in the HTML templates path. (default: ``paper``)
2392 Example: ``monoblue``.
2395 Example: ``monoblue``.
2393
2396
2394 ``templates``
2397 ``templates``
2395 Where to find the HTML templates. The default path to the HTML templates
2398 Where to find the HTML templates. The default path to the HTML templates
2396 can be obtained from ``hg debuginstall``.
2399 can be obtained from ``hg debuginstall``.
2397
2400
2398 ``websub``
2401 ``websub``
2399 ----------
2402 ----------
2400
2403
2401 Web substitution filter definition. You can use this section to
2404 Web substitution filter definition. You can use this section to
2402 define a set of regular expression substitution patterns which
2405 define a set of regular expression substitution patterns which
2403 let you automatically modify the hgweb server output.
2406 let you automatically modify the hgweb server output.
2404
2407
2405 The default hgweb templates only apply these substitution patterns
2408 The default hgweb templates only apply these substitution patterns
2406 on the revision description fields. You can apply them anywhere
2409 on the revision description fields. You can apply them anywhere
2407 you want when you create your own templates by adding calls to the
2410 you want when you create your own templates by adding calls to the
2408 "websub" filter (usually after calling the "escape" filter).
2411 "websub" filter (usually after calling the "escape" filter).
2409
2412
2410 This can be used, for example, to convert issue references to links
2413 This can be used, for example, to convert issue references to links
2411 to your issue tracker, or to convert "markdown-like" syntax into
2414 to your issue tracker, or to convert "markdown-like" syntax into
2412 HTML (see the examples below).
2415 HTML (see the examples below).
2413
2416
2414 Each entry in this section names a substitution filter.
2417 Each entry in this section names a substitution filter.
2415 The value of each entry defines the substitution expression itself.
2418 The value of each entry defines the substitution expression itself.
2416 The websub expressions follow the old interhg extension syntax,
2419 The websub expressions follow the old interhg extension syntax,
2417 which in turn imitates the Unix sed replacement syntax::
2420 which in turn imitates the Unix sed replacement syntax::
2418
2421
2419 patternname = s/SEARCH_REGEX/REPLACE_EXPRESSION/[i]
2422 patternname = s/SEARCH_REGEX/REPLACE_EXPRESSION/[i]
2420
2423
2421 You can use any separator other than "/". The final "i" is optional
2424 You can use any separator other than "/". The final "i" is optional
2422 and indicates that the search must be case insensitive.
2425 and indicates that the search must be case insensitive.
2423
2426
2424 Examples::
2427 Examples::
2425
2428
2426 [websub]
2429 [websub]
2427 issues = s|issue(\d+)|<a href="http://bts.example.org/issue\1">issue\1</a>|i
2430 issues = s|issue(\d+)|<a href="http://bts.example.org/issue\1">issue\1</a>|i
2428 italic = s/\b_(\S+)_\b/<i>\1<\/i>/
2431 italic = s/\b_(\S+)_\b/<i>\1<\/i>/
2429 bold = s/\*\b(\S+)\b\*/<b>\1<\/b>/
2432 bold = s/\*\b(\S+)\b\*/<b>\1<\/b>/
2430
2433
2431 ``worker``
2434 ``worker``
2432 ----------
2435 ----------
2433
2436
2434 Parallel master/worker configuration. We currently perform working
2437 Parallel master/worker configuration. We currently perform working
2435 directory updates in parallel on Unix-like systems, which greatly
2438 directory updates in parallel on Unix-like systems, which greatly
2436 helps performance.
2439 helps performance.
2437
2440
2438 ``numcpus``
2441 ``numcpus``
2439 Number of CPUs to use for parallel operations. A zero or
2442 Number of CPUs to use for parallel operations. A zero or
2440 negative value is treated as ``use the default``.
2443 negative value is treated as ``use the default``.
2441 (default: 4 or the number of CPUs on the system, whichever is larger)
2444 (default: 4 or the number of CPUs on the system, whichever is larger)
2442
2445
2443 ``backgroundclose``
2446 ``backgroundclose``
2444 Whether to enable closing file handles on background threads during certain
2447 Whether to enable closing file handles on background threads during certain
2445 operations. Some platforms aren't very efficient at closing file
2448 operations. Some platforms aren't very efficient at closing file
2446 handles that have been written or appended to. By performing file closing
2449 handles that have been written or appended to. By performing file closing
2447 on background threads, file write rate can increase substantially.
2450 on background threads, file write rate can increase substantially.
2448 (default: true on Windows, false elsewhere)
2451 (default: true on Windows, false elsewhere)
2449
2452
2450 ``backgroundcloseminfilecount``
2453 ``backgroundcloseminfilecount``
2451 Minimum number of files required to trigger background file closing.
2454 Minimum number of files required to trigger background file closing.
2452 Operations not writing this many files won't start background close
2455 Operations not writing this many files won't start background close
2453 threads.
2456 threads.
2454 (default: 2048)
2457 (default: 2048)
2455
2458
2456 ``backgroundclosemaxqueue``
2459 ``backgroundclosemaxqueue``
2457 The maximum number of opened file handles waiting to be closed in the
2460 The maximum number of opened file handles waiting to be closed in the
2458 background. This option only has an effect if ``backgroundclose`` is
2461 background. This option only has an effect if ``backgroundclose`` is
2459 enabled.
2462 enabled.
2460 (default: 384)
2463 (default: 384)
2461
2464
2462 ``backgroundclosethreadcount``
2465 ``backgroundclosethreadcount``
2463 Number of threads to process background file closes. Only relevant if
2466 Number of threads to process background file closes. Only relevant if
2464 ``backgroundclose`` is enabled.
2467 ``backgroundclose`` is enabled.
2465 (default: 4)
2468 (default: 4)
@@ -1,488 +1,491 b''
1 # mdiff.py - diff and patch routines for mercurial
1 # mdiff.py - diff and patch routines for mercurial
2 #
2 #
3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005, 2006 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 re
10 import re
11 import struct
11 import struct
12 import zlib
12 import zlib
13
13
14 from .i18n import _
14 from .i18n import _
15 from . import (
15 from . import (
16 error,
16 error,
17 policy,
17 policy,
18 pycompat,
18 pycompat,
19 util,
19 util,
20 )
20 )
21
21
22 bdiff = policy.importmod(r'bdiff')
22 bdiff = policy.importmod(r'bdiff')
23 mpatch = policy.importmod(r'mpatch')
23 mpatch = policy.importmod(r'mpatch')
24
24
25 blocks = bdiff.blocks
25 blocks = bdiff.blocks
26 fixws = bdiff.fixws
26 fixws = bdiff.fixws
27 patches = mpatch.patches
27 patches = mpatch.patches
28 patchedsize = mpatch.patchedsize
28 patchedsize = mpatch.patchedsize
29 textdiff = bdiff.bdiff
29 textdiff = bdiff.bdiff
30
30
31 def splitnewlines(text):
31 def splitnewlines(text):
32 '''like str.splitlines, but only split on newlines.'''
32 '''like str.splitlines, but only split on newlines.'''
33 lines = [l + '\n' for l in text.split('\n')]
33 lines = [l + '\n' for l in text.split('\n')]
34 if lines:
34 if lines:
35 if lines[-1] == '\n':
35 if lines[-1] == '\n':
36 lines.pop()
36 lines.pop()
37 else:
37 else:
38 lines[-1] = lines[-1][:-1]
38 lines[-1] = lines[-1][:-1]
39 return lines
39 return lines
40
40
41 class diffopts(object):
41 class diffopts(object):
42 '''context is the number of context lines
42 '''context is the number of context lines
43 text treats all files as text
43 text treats all files as text
44 showfunc enables diff -p output
44 showfunc enables diff -p output
45 git enables the git extended patch format
45 git enables the git extended patch format
46 nodates removes dates from diff headers
46 nodates removes dates from diff headers
47 nobinary ignores binary files
47 nobinary ignores binary files
48 noprefix disables the 'a/' and 'b/' prefixes (ignored in plain mode)
48 noprefix disables the 'a/' and 'b/' prefixes (ignored in plain mode)
49 ignorews ignores all whitespace changes in the diff
49 ignorews ignores all whitespace changes in the diff
50 ignorewsamount ignores changes in the amount of whitespace
50 ignorewsamount ignores changes in the amount of whitespace
51 ignoreblanklines ignores changes whose lines are all blank
51 ignoreblanklines ignores changes whose lines are all blank
52 upgrade generates git diffs to avoid data loss
52 upgrade generates git diffs to avoid data loss
53 '''
53 '''
54
54
55 defaults = {
55 defaults = {
56 'context': 3,
56 'context': 3,
57 'text': False,
57 'text': False,
58 'showfunc': False,
58 'showfunc': False,
59 'git': False,
59 'git': False,
60 'nodates': False,
60 'nodates': False,
61 'nobinary': False,
61 'nobinary': False,
62 'noprefix': False,
62 'noprefix': False,
63 'index': 0,
63 'index': 0,
64 'ignorews': False,
64 'ignorews': False,
65 'ignorewsamount': False,
65 'ignorewsamount': False,
66 'ignorewseol': False,
66 'ignoreblanklines': False,
67 'ignoreblanklines': False,
67 'upgrade': False,
68 'upgrade': False,
68 'showsimilarity': False,
69 'showsimilarity': False,
69 }
70 }
70
71
71 def __init__(self, **opts):
72 def __init__(self, **opts):
72 opts = pycompat.byteskwargs(opts)
73 opts = pycompat.byteskwargs(opts)
73 for k in self.defaults.keys():
74 for k in self.defaults.keys():
74 v = opts.get(k)
75 v = opts.get(k)
75 if v is None:
76 if v is None:
76 v = self.defaults[k]
77 v = self.defaults[k]
77 setattr(self, k, v)
78 setattr(self, k, v)
78
79
79 try:
80 try:
80 self.context = int(self.context)
81 self.context = int(self.context)
81 except ValueError:
82 except ValueError:
82 raise error.Abort(_('diff context lines count must be '
83 raise error.Abort(_('diff context lines count must be '
83 'an integer, not %r') % self.context)
84 'an integer, not %r') % self.context)
84
85
85 def copy(self, **kwargs):
86 def copy(self, **kwargs):
86 opts = dict((k, getattr(self, k)) for k in self.defaults)
87 opts = dict((k, getattr(self, k)) for k in self.defaults)
87 opts = pycompat.strkwargs(opts)
88 opts = pycompat.strkwargs(opts)
88 opts.update(kwargs)
89 opts.update(kwargs)
89 return diffopts(**opts)
90 return diffopts(**opts)
90
91
91 defaultopts = diffopts()
92 defaultopts = diffopts()
92
93
93 def wsclean(opts, text, blank=True):
94 def wsclean(opts, text, blank=True):
94 if opts.ignorews:
95 if opts.ignorews:
95 text = bdiff.fixws(text, 1)
96 text = bdiff.fixws(text, 1)
96 elif opts.ignorewsamount:
97 elif opts.ignorewsamount:
97 text = bdiff.fixws(text, 0)
98 text = bdiff.fixws(text, 0)
98 if blank and opts.ignoreblanklines:
99 if blank and opts.ignoreblanklines:
99 text = re.sub('\n+', '\n', text).strip('\n')
100 text = re.sub('\n+', '\n', text).strip('\n')
101 if opts.ignorewseol:
102 text = re.sub(r'[ \t\r\f]+\n', r'\n', text)
100 return text
103 return text
101
104
102 def splitblock(base1, lines1, base2, lines2, opts):
105 def splitblock(base1, lines1, base2, lines2, opts):
103 # The input lines matches except for interwoven blank lines. We
106 # The input lines matches except for interwoven blank lines. We
104 # transform it into a sequence of matching blocks and blank blocks.
107 # transform it into a sequence of matching blocks and blank blocks.
105 lines1 = [(wsclean(opts, l) and 1 or 0) for l in lines1]
108 lines1 = [(wsclean(opts, l) and 1 or 0) for l in lines1]
106 lines2 = [(wsclean(opts, l) and 1 or 0) for l in lines2]
109 lines2 = [(wsclean(opts, l) and 1 or 0) for l in lines2]
107 s1, e1 = 0, len(lines1)
110 s1, e1 = 0, len(lines1)
108 s2, e2 = 0, len(lines2)
111 s2, e2 = 0, len(lines2)
109 while s1 < e1 or s2 < e2:
112 while s1 < e1 or s2 < e2:
110 i1, i2, btype = s1, s2, '='
113 i1, i2, btype = s1, s2, '='
111 if (i1 >= e1 or lines1[i1] == 0
114 if (i1 >= e1 or lines1[i1] == 0
112 or i2 >= e2 or lines2[i2] == 0):
115 or i2 >= e2 or lines2[i2] == 0):
113 # Consume the block of blank lines
116 # Consume the block of blank lines
114 btype = '~'
117 btype = '~'
115 while i1 < e1 and lines1[i1] == 0:
118 while i1 < e1 and lines1[i1] == 0:
116 i1 += 1
119 i1 += 1
117 while i2 < e2 and lines2[i2] == 0:
120 while i2 < e2 and lines2[i2] == 0:
118 i2 += 1
121 i2 += 1
119 else:
122 else:
120 # Consume the matching lines
123 # Consume the matching lines
121 while i1 < e1 and lines1[i1] == 1 and lines2[i2] == 1:
124 while i1 < e1 and lines1[i1] == 1 and lines2[i2] == 1:
122 i1 += 1
125 i1 += 1
123 i2 += 1
126 i2 += 1
124 yield [base1 + s1, base1 + i1, base2 + s2, base2 + i2], btype
127 yield [base1 + s1, base1 + i1, base2 + s2, base2 + i2], btype
125 s1 = i1
128 s1 = i1
126 s2 = i2
129 s2 = i2
127
130
128 def hunkinrange(hunk, linerange):
131 def hunkinrange(hunk, linerange):
129 """Return True if `hunk` defined as (start, length) is in `linerange`
132 """Return True if `hunk` defined as (start, length) is in `linerange`
130 defined as (lowerbound, upperbound).
133 defined as (lowerbound, upperbound).
131
134
132 >>> hunkinrange((5, 10), (2, 7))
135 >>> hunkinrange((5, 10), (2, 7))
133 True
136 True
134 >>> hunkinrange((5, 10), (6, 12))
137 >>> hunkinrange((5, 10), (6, 12))
135 True
138 True
136 >>> hunkinrange((5, 10), (13, 17))
139 >>> hunkinrange((5, 10), (13, 17))
137 True
140 True
138 >>> hunkinrange((5, 10), (3, 17))
141 >>> hunkinrange((5, 10), (3, 17))
139 True
142 True
140 >>> hunkinrange((5, 10), (1, 3))
143 >>> hunkinrange((5, 10), (1, 3))
141 False
144 False
142 >>> hunkinrange((5, 10), (18, 20))
145 >>> hunkinrange((5, 10), (18, 20))
143 False
146 False
144 >>> hunkinrange((5, 10), (1, 5))
147 >>> hunkinrange((5, 10), (1, 5))
145 False
148 False
146 >>> hunkinrange((5, 10), (15, 27))
149 >>> hunkinrange((5, 10), (15, 27))
147 False
150 False
148 """
151 """
149 start, length = hunk
152 start, length = hunk
150 lowerbound, upperbound = linerange
153 lowerbound, upperbound = linerange
151 return lowerbound < start + length and start < upperbound
154 return lowerbound < start + length and start < upperbound
152
155
153 def blocksinrange(blocks, rangeb):
156 def blocksinrange(blocks, rangeb):
154 """filter `blocks` like (a1, a2, b1, b2) from items outside line range
157 """filter `blocks` like (a1, a2, b1, b2) from items outside line range
155 `rangeb` from ``(b1, b2)`` point of view.
158 `rangeb` from ``(b1, b2)`` point of view.
156
159
157 Return `filteredblocks, rangea` where:
160 Return `filteredblocks, rangea` where:
158
161
159 * `filteredblocks` is list of ``block = (a1, a2, b1, b2), stype`` items of
162 * `filteredblocks` is list of ``block = (a1, a2, b1, b2), stype`` items of
160 `blocks` that are inside `rangeb` from ``(b1, b2)`` point of view; a
163 `blocks` that are inside `rangeb` from ``(b1, b2)`` point of view; a
161 block ``(b1, b2)`` being inside `rangeb` if
164 block ``(b1, b2)`` being inside `rangeb` if
162 ``rangeb[0] < b2 and b1 < rangeb[1]``;
165 ``rangeb[0] < b2 and b1 < rangeb[1]``;
163 * `rangea` is the line range w.r.t. to ``(a1, a2)`` parts of `blocks`.
166 * `rangea` is the line range w.r.t. to ``(a1, a2)`` parts of `blocks`.
164 """
167 """
165 lbb, ubb = rangeb
168 lbb, ubb = rangeb
166 lba, uba = None, None
169 lba, uba = None, None
167 filteredblocks = []
170 filteredblocks = []
168 for block in blocks:
171 for block in blocks:
169 (a1, a2, b1, b2), stype = block
172 (a1, a2, b1, b2), stype = block
170 if lbb >= b1 and ubb <= b2 and stype == '=':
173 if lbb >= b1 and ubb <= b2 and stype == '=':
171 # rangeb is within a single "=" hunk, restrict back linerange1
174 # rangeb is within a single "=" hunk, restrict back linerange1
172 # by offsetting rangeb
175 # by offsetting rangeb
173 lba = lbb - b1 + a1
176 lba = lbb - b1 + a1
174 uba = ubb - b1 + a1
177 uba = ubb - b1 + a1
175 else:
178 else:
176 if b1 <= lbb < b2:
179 if b1 <= lbb < b2:
177 if stype == '=':
180 if stype == '=':
178 lba = a2 - (b2 - lbb)
181 lba = a2 - (b2 - lbb)
179 else:
182 else:
180 lba = a1
183 lba = a1
181 if b1 < ubb <= b2:
184 if b1 < ubb <= b2:
182 if stype == '=':
185 if stype == '=':
183 uba = a1 + (ubb - b1)
186 uba = a1 + (ubb - b1)
184 else:
187 else:
185 uba = a2
188 uba = a2
186 if hunkinrange((b1, (b2 - b1)), rangeb):
189 if hunkinrange((b1, (b2 - b1)), rangeb):
187 filteredblocks.append(block)
190 filteredblocks.append(block)
188 if lba is None or uba is None or uba < lba:
191 if lba is None or uba is None or uba < lba:
189 raise error.Abort(_('line range exceeds file size'))
192 raise error.Abort(_('line range exceeds file size'))
190 return filteredblocks, (lba, uba)
193 return filteredblocks, (lba, uba)
191
194
192 def allblocks(text1, text2, opts=None, lines1=None, lines2=None):
195 def allblocks(text1, text2, opts=None, lines1=None, lines2=None):
193 """Return (block, type) tuples, where block is an mdiff.blocks
196 """Return (block, type) tuples, where block is an mdiff.blocks
194 line entry. type is '=' for blocks matching exactly one another
197 line entry. type is '=' for blocks matching exactly one another
195 (bdiff blocks), '!' for non-matching blocks and '~' for blocks
198 (bdiff blocks), '!' for non-matching blocks and '~' for blocks
196 matching only after having filtered blank lines.
199 matching only after having filtered blank lines.
197 line1 and line2 are text1 and text2 split with splitnewlines() if
200 line1 and line2 are text1 and text2 split with splitnewlines() if
198 they are already available.
201 they are already available.
199 """
202 """
200 if opts is None:
203 if opts is None:
201 opts = defaultopts
204 opts = defaultopts
202 if opts.ignorews or opts.ignorewsamount:
205 if opts.ignorews or opts.ignorewsamount or opts.ignorewseol:
203 text1 = wsclean(opts, text1, False)
206 text1 = wsclean(opts, text1, False)
204 text2 = wsclean(opts, text2, False)
207 text2 = wsclean(opts, text2, False)
205 diff = bdiff.blocks(text1, text2)
208 diff = bdiff.blocks(text1, text2)
206 for i, s1 in enumerate(diff):
209 for i, s1 in enumerate(diff):
207 # The first match is special.
210 # The first match is special.
208 # we've either found a match starting at line 0 or a match later
211 # we've either found a match starting at line 0 or a match later
209 # in the file. If it starts later, old and new below will both be
212 # in the file. If it starts later, old and new below will both be
210 # empty and we'll continue to the next match.
213 # empty and we'll continue to the next match.
211 if i > 0:
214 if i > 0:
212 s = diff[i - 1]
215 s = diff[i - 1]
213 else:
216 else:
214 s = [0, 0, 0, 0]
217 s = [0, 0, 0, 0]
215 s = [s[1], s1[0], s[3], s1[2]]
218 s = [s[1], s1[0], s[3], s1[2]]
216
219
217 # bdiff sometimes gives huge matches past eof, this check eats them,
220 # bdiff sometimes gives huge matches past eof, this check eats them,
218 # and deals with the special first match case described above
221 # and deals with the special first match case described above
219 if s[0] != s[1] or s[2] != s[3]:
222 if s[0] != s[1] or s[2] != s[3]:
220 type = '!'
223 type = '!'
221 if opts.ignoreblanklines:
224 if opts.ignoreblanklines:
222 if lines1 is None:
225 if lines1 is None:
223 lines1 = splitnewlines(text1)
226 lines1 = splitnewlines(text1)
224 if lines2 is None:
227 if lines2 is None:
225 lines2 = splitnewlines(text2)
228 lines2 = splitnewlines(text2)
226 old = wsclean(opts, "".join(lines1[s[0]:s[1]]))
229 old = wsclean(opts, "".join(lines1[s[0]:s[1]]))
227 new = wsclean(opts, "".join(lines2[s[2]:s[3]]))
230 new = wsclean(opts, "".join(lines2[s[2]:s[3]]))
228 if old == new:
231 if old == new:
229 type = '~'
232 type = '~'
230 yield s, type
233 yield s, type
231 yield s1, '='
234 yield s1, '='
232
235
233 def unidiff(a, ad, b, bd, fn1, fn2, opts=defaultopts):
236 def unidiff(a, ad, b, bd, fn1, fn2, opts=defaultopts):
234 """Return a unified diff as a (headers, hunks) tuple.
237 """Return a unified diff as a (headers, hunks) tuple.
235
238
236 If the diff is not null, `headers` is a list with unified diff header
239 If the diff is not null, `headers` is a list with unified diff header
237 lines "--- <original>" and "+++ <new>" and `hunks` is a generator yielding
240 lines "--- <original>" and "+++ <new>" and `hunks` is a generator yielding
238 (hunkrange, hunklines) coming from _unidiff().
241 (hunkrange, hunklines) coming from _unidiff().
239 Otherwise, `headers` and `hunks` are empty.
242 Otherwise, `headers` and `hunks` are empty.
240 """
243 """
241 def datetag(date, fn=None):
244 def datetag(date, fn=None):
242 if not opts.git and not opts.nodates:
245 if not opts.git and not opts.nodates:
243 return '\t%s' % date
246 return '\t%s' % date
244 if fn and ' ' in fn:
247 if fn and ' ' in fn:
245 return '\t'
248 return '\t'
246 return ''
249 return ''
247
250
248 sentinel = [], ()
251 sentinel = [], ()
249 if not a and not b:
252 if not a and not b:
250 return sentinel
253 return sentinel
251
254
252 if opts.noprefix:
255 if opts.noprefix:
253 aprefix = bprefix = ''
256 aprefix = bprefix = ''
254 else:
257 else:
255 aprefix = 'a/'
258 aprefix = 'a/'
256 bprefix = 'b/'
259 bprefix = 'b/'
257
260
258 epoch = util.datestr((0, 0))
261 epoch = util.datestr((0, 0))
259
262
260 fn1 = util.pconvert(fn1)
263 fn1 = util.pconvert(fn1)
261 fn2 = util.pconvert(fn2)
264 fn2 = util.pconvert(fn2)
262
265
263 def checknonewline(lines):
266 def checknonewline(lines):
264 for text in lines:
267 for text in lines:
265 if text[-1:] != '\n':
268 if text[-1:] != '\n':
266 text += "\n\ No newline at end of file\n"
269 text += "\n\ No newline at end of file\n"
267 yield text
270 yield text
268
271
269 if not opts.text and (util.binary(a) or util.binary(b)):
272 if not opts.text and (util.binary(a) or util.binary(b)):
270 if a and b and len(a) == len(b) and a == b:
273 if a and b and len(a) == len(b) and a == b:
271 return sentinel
274 return sentinel
272 headerlines = []
275 headerlines = []
273 hunks = (None, ['Binary file %s has changed\n' % fn1]),
276 hunks = (None, ['Binary file %s has changed\n' % fn1]),
274 elif not a:
277 elif not a:
275 b = splitnewlines(b)
278 b = splitnewlines(b)
276 if a is None:
279 if a is None:
277 l1 = '--- /dev/null%s' % datetag(epoch)
280 l1 = '--- /dev/null%s' % datetag(epoch)
278 else:
281 else:
279 l1 = "--- %s%s%s" % (aprefix, fn1, datetag(ad, fn1))
282 l1 = "--- %s%s%s" % (aprefix, fn1, datetag(ad, fn1))
280 l2 = "+++ %s%s" % (bprefix + fn2, datetag(bd, fn2))
283 l2 = "+++ %s%s" % (bprefix + fn2, datetag(bd, fn2))
281 headerlines = [l1, l2]
284 headerlines = [l1, l2]
282 size = len(b)
285 size = len(b)
283 hunkrange = (0, 0, 1, size)
286 hunkrange = (0, 0, 1, size)
284 hunklines = ["@@ -0,0 +1,%d @@\n" % size] + ["+" + e for e in b]
287 hunklines = ["@@ -0,0 +1,%d @@\n" % size] + ["+" + e for e in b]
285 hunks = (hunkrange, checknonewline(hunklines)),
288 hunks = (hunkrange, checknonewline(hunklines)),
286 elif not b:
289 elif not b:
287 a = splitnewlines(a)
290 a = splitnewlines(a)
288 l1 = "--- %s%s%s" % (aprefix, fn1, datetag(ad, fn1))
291 l1 = "--- %s%s%s" % (aprefix, fn1, datetag(ad, fn1))
289 if b is None:
292 if b is None:
290 l2 = '+++ /dev/null%s' % datetag(epoch)
293 l2 = '+++ /dev/null%s' % datetag(epoch)
291 else:
294 else:
292 l2 = "+++ %s%s%s" % (bprefix, fn2, datetag(bd, fn2))
295 l2 = "+++ %s%s%s" % (bprefix, fn2, datetag(bd, fn2))
293 headerlines = [l1, l2]
296 headerlines = [l1, l2]
294 size = len(a)
297 size = len(a)
295 hunkrange = (1, size, 0, 0)
298 hunkrange = (1, size, 0, 0)
296 hunklines = ["@@ -1,%d +0,0 @@\n" % size] + ["-" + e for e in a]
299 hunklines = ["@@ -1,%d +0,0 @@\n" % size] + ["-" + e for e in a]
297 hunks = (hunkrange, checknonewline(hunklines)),
300 hunks = (hunkrange, checknonewline(hunklines)),
298 else:
301 else:
299 diffhunks = _unidiff(a, b, opts=opts)
302 diffhunks = _unidiff(a, b, opts=opts)
300 try:
303 try:
301 hunkrange, hunklines = next(diffhunks)
304 hunkrange, hunklines = next(diffhunks)
302 except StopIteration:
305 except StopIteration:
303 return sentinel
306 return sentinel
304
307
305 headerlines = [
308 headerlines = [
306 "--- %s%s%s" % (aprefix, fn1, datetag(ad, fn1)),
309 "--- %s%s%s" % (aprefix, fn1, datetag(ad, fn1)),
307 "+++ %s%s%s" % (bprefix, fn2, datetag(bd, fn2)),
310 "+++ %s%s%s" % (bprefix, fn2, datetag(bd, fn2)),
308 ]
311 ]
309 def rewindhunks():
312 def rewindhunks():
310 yield hunkrange, checknonewline(hunklines)
313 yield hunkrange, checknonewline(hunklines)
311 for hr, hl in diffhunks:
314 for hr, hl in diffhunks:
312 yield hr, checknonewline(hl)
315 yield hr, checknonewline(hl)
313
316
314 hunks = rewindhunks()
317 hunks = rewindhunks()
315
318
316 return headerlines, hunks
319 return headerlines, hunks
317
320
318 def _unidiff(t1, t2, opts=defaultopts):
321 def _unidiff(t1, t2, opts=defaultopts):
319 """Yield hunks of a headerless unified diff from t1 and t2 texts.
322 """Yield hunks of a headerless unified diff from t1 and t2 texts.
320
323
321 Each hunk consists of a (hunkrange, hunklines) tuple where `hunkrange` is a
324 Each hunk consists of a (hunkrange, hunklines) tuple where `hunkrange` is a
322 tuple (s1, l1, s2, l2) representing the range information of the hunk to
325 tuple (s1, l1, s2, l2) representing the range information of the hunk to
323 form the '@@ -s1,l1 +s2,l2 @@' header and `hunklines` is a list of lines
326 form the '@@ -s1,l1 +s2,l2 @@' header and `hunklines` is a list of lines
324 of the hunk combining said header followed by line additions and
327 of the hunk combining said header followed by line additions and
325 deletions.
328 deletions.
326 """
329 """
327 l1 = splitnewlines(t1)
330 l1 = splitnewlines(t1)
328 l2 = splitnewlines(t2)
331 l2 = splitnewlines(t2)
329 def contextend(l, len):
332 def contextend(l, len):
330 ret = l + opts.context
333 ret = l + opts.context
331 if ret > len:
334 if ret > len:
332 ret = len
335 ret = len
333 return ret
336 return ret
334
337
335 def contextstart(l):
338 def contextstart(l):
336 ret = l - opts.context
339 ret = l - opts.context
337 if ret < 0:
340 if ret < 0:
338 return 0
341 return 0
339 return ret
342 return ret
340
343
341 lastfunc = [0, '']
344 lastfunc = [0, '']
342 def yieldhunk(hunk):
345 def yieldhunk(hunk):
343 (astart, a2, bstart, b2, delta) = hunk
346 (astart, a2, bstart, b2, delta) = hunk
344 aend = contextend(a2, len(l1))
347 aend = contextend(a2, len(l1))
345 alen = aend - astart
348 alen = aend - astart
346 blen = b2 - bstart + aend - a2
349 blen = b2 - bstart + aend - a2
347
350
348 func = ""
351 func = ""
349 if opts.showfunc:
352 if opts.showfunc:
350 lastpos, func = lastfunc
353 lastpos, func = lastfunc
351 # walk backwards from the start of the context up to the start of
354 # walk backwards from the start of the context up to the start of
352 # the previous hunk context until we find a line starting with an
355 # the previous hunk context until we find a line starting with an
353 # alphanumeric char.
356 # alphanumeric char.
354 for i in xrange(astart - 1, lastpos - 1, -1):
357 for i in xrange(astart - 1, lastpos - 1, -1):
355 if l1[i][0].isalnum():
358 if l1[i][0].isalnum():
356 func = ' ' + l1[i].rstrip()[:40]
359 func = ' ' + l1[i].rstrip()[:40]
357 lastfunc[1] = func
360 lastfunc[1] = func
358 break
361 break
359 # by recording this hunk's starting point as the next place to
362 # by recording this hunk's starting point as the next place to
360 # start looking for function lines, we avoid reading any line in
363 # start looking for function lines, we avoid reading any line in
361 # the file more than once.
364 # the file more than once.
362 lastfunc[0] = astart
365 lastfunc[0] = astart
363
366
364 # zero-length hunk ranges report their start line as one less
367 # zero-length hunk ranges report their start line as one less
365 if alen:
368 if alen:
366 astart += 1
369 astart += 1
367 if blen:
370 if blen:
368 bstart += 1
371 bstart += 1
369
372
370 hunkrange = astart, alen, bstart, blen
373 hunkrange = astart, alen, bstart, blen
371 hunklines = (
374 hunklines = (
372 ["@@ -%d,%d +%d,%d @@%s\n" % (hunkrange + (func,))]
375 ["@@ -%d,%d +%d,%d @@%s\n" % (hunkrange + (func,))]
373 + delta
376 + delta
374 + [' ' + l1[x] for x in xrange(a2, aend)]
377 + [' ' + l1[x] for x in xrange(a2, aend)]
375 )
378 )
376 yield hunkrange, hunklines
379 yield hunkrange, hunklines
377
380
378 # bdiff.blocks gives us the matching sequences in the files. The loop
381 # bdiff.blocks gives us the matching sequences in the files. The loop
379 # below finds the spaces between those matching sequences and translates
382 # below finds the spaces between those matching sequences and translates
380 # them into diff output.
383 # them into diff output.
381 #
384 #
382 hunk = None
385 hunk = None
383 ignoredlines = 0
386 ignoredlines = 0
384 for s, stype in allblocks(t1, t2, opts, l1, l2):
387 for s, stype in allblocks(t1, t2, opts, l1, l2):
385 a1, a2, b1, b2 = s
388 a1, a2, b1, b2 = s
386 if stype != '!':
389 if stype != '!':
387 if stype == '~':
390 if stype == '~':
388 # The diff context lines are based on t1 content. When
391 # The diff context lines are based on t1 content. When
389 # blank lines are ignored, the new lines offsets must
392 # blank lines are ignored, the new lines offsets must
390 # be adjusted as if equivalent blocks ('~') had the
393 # be adjusted as if equivalent blocks ('~') had the
391 # same sizes on both sides.
394 # same sizes on both sides.
392 ignoredlines += (b2 - b1) - (a2 - a1)
395 ignoredlines += (b2 - b1) - (a2 - a1)
393 continue
396 continue
394 delta = []
397 delta = []
395 old = l1[a1:a2]
398 old = l1[a1:a2]
396 new = l2[b1:b2]
399 new = l2[b1:b2]
397
400
398 b1 -= ignoredlines
401 b1 -= ignoredlines
399 b2 -= ignoredlines
402 b2 -= ignoredlines
400 astart = contextstart(a1)
403 astart = contextstart(a1)
401 bstart = contextstart(b1)
404 bstart = contextstart(b1)
402 prev = None
405 prev = None
403 if hunk:
406 if hunk:
404 # join with the previous hunk if it falls inside the context
407 # join with the previous hunk if it falls inside the context
405 if astart < hunk[1] + opts.context + 1:
408 if astart < hunk[1] + opts.context + 1:
406 prev = hunk
409 prev = hunk
407 astart = hunk[1]
410 astart = hunk[1]
408 bstart = hunk[3]
411 bstart = hunk[3]
409 else:
412 else:
410 for x in yieldhunk(hunk):
413 for x in yieldhunk(hunk):
411 yield x
414 yield x
412 if prev:
415 if prev:
413 # we've joined the previous hunk, record the new ending points.
416 # we've joined the previous hunk, record the new ending points.
414 hunk[1] = a2
417 hunk[1] = a2
415 hunk[3] = b2
418 hunk[3] = b2
416 delta = hunk[4]
419 delta = hunk[4]
417 else:
420 else:
418 # create a new hunk
421 # create a new hunk
419 hunk = [astart, a2, bstart, b2, delta]
422 hunk = [astart, a2, bstart, b2, delta]
420
423
421 delta[len(delta):] = [' ' + x for x in l1[astart:a1]]
424 delta[len(delta):] = [' ' + x for x in l1[astart:a1]]
422 delta[len(delta):] = ['-' + x for x in old]
425 delta[len(delta):] = ['-' + x for x in old]
423 delta[len(delta):] = ['+' + x for x in new]
426 delta[len(delta):] = ['+' + x for x in new]
424
427
425 if hunk:
428 if hunk:
426 for x in yieldhunk(hunk):
429 for x in yieldhunk(hunk):
427 yield x
430 yield x
428
431
429 def b85diff(to, tn):
432 def b85diff(to, tn):
430 '''print base85-encoded binary diff'''
433 '''print base85-encoded binary diff'''
431 def fmtline(line):
434 def fmtline(line):
432 l = len(line)
435 l = len(line)
433 if l <= 26:
436 if l <= 26:
434 l = chr(ord('A') + l - 1)
437 l = chr(ord('A') + l - 1)
435 else:
438 else:
436 l = chr(l - 26 + ord('a') - 1)
439 l = chr(l - 26 + ord('a') - 1)
437 return '%c%s\n' % (l, util.b85encode(line, True))
440 return '%c%s\n' % (l, util.b85encode(line, True))
438
441
439 def chunk(text, csize=52):
442 def chunk(text, csize=52):
440 l = len(text)
443 l = len(text)
441 i = 0
444 i = 0
442 while i < l:
445 while i < l:
443 yield text[i:i + csize]
446 yield text[i:i + csize]
444 i += csize
447 i += csize
445
448
446 if to is None:
449 if to is None:
447 to = ''
450 to = ''
448 if tn is None:
451 if tn is None:
449 tn = ''
452 tn = ''
450
453
451 if to == tn:
454 if to == tn:
452 return ''
455 return ''
453
456
454 # TODO: deltas
457 # TODO: deltas
455 ret = []
458 ret = []
456 ret.append('GIT binary patch\n')
459 ret.append('GIT binary patch\n')
457 ret.append('literal %s\n' % len(tn))
460 ret.append('literal %s\n' % len(tn))
458 for l in chunk(zlib.compress(tn)):
461 for l in chunk(zlib.compress(tn)):
459 ret.append(fmtline(l))
462 ret.append(fmtline(l))
460 ret.append('\n')
463 ret.append('\n')
461
464
462 return ''.join(ret)
465 return ''.join(ret)
463
466
464 def patchtext(bin):
467 def patchtext(bin):
465 pos = 0
468 pos = 0
466 t = []
469 t = []
467 while pos < len(bin):
470 while pos < len(bin):
468 p1, p2, l = struct.unpack(">lll", bin[pos:pos + 12])
471 p1, p2, l = struct.unpack(">lll", bin[pos:pos + 12])
469 pos += 12
472 pos += 12
470 t.append(bin[pos:pos + l])
473 t.append(bin[pos:pos + l])
471 pos += l
474 pos += l
472 return "".join(t)
475 return "".join(t)
473
476
474 def patch(a, bin):
477 def patch(a, bin):
475 if len(a) == 0:
478 if len(a) == 0:
476 # skip over trivial delta header
479 # skip over trivial delta header
477 return util.buffer(bin, 12)
480 return util.buffer(bin, 12)
478 return mpatch.patches(a, [bin])
481 return mpatch.patches(a, [bin])
479
482
480 # similar to difflib.SequenceMatcher.get_matching_blocks
483 # similar to difflib.SequenceMatcher.get_matching_blocks
481 def get_matching_blocks(a, b):
484 def get_matching_blocks(a, b):
482 return [(d[0], d[2], d[1] - d[0]) for d in bdiff.blocks(a, b)]
485 return [(d[0], d[2], d[1] - d[0]) for d in bdiff.blocks(a, b)]
483
486
484 def trivialdiffheader(length):
487 def trivialdiffheader(length):
485 return struct.pack(">lll", 0, 0, length) if length else ''
488 return struct.pack(">lll", 0, 0, length) if length else ''
486
489
487 def replacediffheader(oldlen, newlen):
490 def replacediffheader(oldlen, newlen):
488 return struct.pack(">lll", 0, oldlen, newlen)
491 return struct.pack(">lll", 0, oldlen, newlen)
@@ -1,2793 +1,2794 b''
1 # patch.py - patch file parsing routines
1 # patch.py - patch file parsing routines
2 #
2 #
3 # Copyright 2006 Brendan Cully <brendan@kublai.com>
3 # Copyright 2006 Brendan Cully <brendan@kublai.com>
4 # Copyright 2007 Chris Mason <chris.mason@oracle.com>
4 # Copyright 2007 Chris Mason <chris.mason@oracle.com>
5 #
5 #
6 # This software may be used and distributed according to the terms of the
6 # This software may be used and distributed according to the terms of the
7 # GNU General Public License version 2 or any later version.
7 # GNU General Public License version 2 or any later version.
8
8
9 from __future__ import absolute_import
9 from __future__ import absolute_import
10
10
11 import collections
11 import collections
12 import copy
12 import copy
13 import email
13 import email
14 import errno
14 import errno
15 import hashlib
15 import hashlib
16 import os
16 import os
17 import posixpath
17 import posixpath
18 import re
18 import re
19 import shutil
19 import shutil
20 import tempfile
20 import tempfile
21 import zlib
21 import zlib
22
22
23 from .i18n import _
23 from .i18n import _
24 from .node import (
24 from .node import (
25 hex,
25 hex,
26 short,
26 short,
27 )
27 )
28 from . import (
28 from . import (
29 copies,
29 copies,
30 encoding,
30 encoding,
31 error,
31 error,
32 mail,
32 mail,
33 mdiff,
33 mdiff,
34 pathutil,
34 pathutil,
35 policy,
35 policy,
36 pycompat,
36 pycompat,
37 scmutil,
37 scmutil,
38 similar,
38 similar,
39 util,
39 util,
40 vfs as vfsmod,
40 vfs as vfsmod,
41 )
41 )
42
42
43 diffhelpers = policy.importmod(r'diffhelpers')
43 diffhelpers = policy.importmod(r'diffhelpers')
44 stringio = util.stringio
44 stringio = util.stringio
45
45
46 gitre = re.compile(br'diff --git a/(.*) b/(.*)')
46 gitre = re.compile(br'diff --git a/(.*) b/(.*)')
47 tabsplitter = re.compile(br'(\t+|[^\t]+)')
47 tabsplitter = re.compile(br'(\t+|[^\t]+)')
48
48
49 class PatchError(Exception):
49 class PatchError(Exception):
50 pass
50 pass
51
51
52
52
53 # public functions
53 # public functions
54
54
55 def split(stream):
55 def split(stream):
56 '''return an iterator of individual patches from a stream'''
56 '''return an iterator of individual patches from a stream'''
57 def isheader(line, inheader):
57 def isheader(line, inheader):
58 if inheader and line[0] in (' ', '\t'):
58 if inheader and line[0] in (' ', '\t'):
59 # continuation
59 # continuation
60 return True
60 return True
61 if line[0] in (' ', '-', '+'):
61 if line[0] in (' ', '-', '+'):
62 # diff line - don't check for header pattern in there
62 # diff line - don't check for header pattern in there
63 return False
63 return False
64 l = line.split(': ', 1)
64 l = line.split(': ', 1)
65 return len(l) == 2 and ' ' not in l[0]
65 return len(l) == 2 and ' ' not in l[0]
66
66
67 def chunk(lines):
67 def chunk(lines):
68 return stringio(''.join(lines))
68 return stringio(''.join(lines))
69
69
70 def hgsplit(stream, cur):
70 def hgsplit(stream, cur):
71 inheader = True
71 inheader = True
72
72
73 for line in stream:
73 for line in stream:
74 if not line.strip():
74 if not line.strip():
75 inheader = False
75 inheader = False
76 if not inheader and line.startswith('# HG changeset patch'):
76 if not inheader and line.startswith('# HG changeset patch'):
77 yield chunk(cur)
77 yield chunk(cur)
78 cur = []
78 cur = []
79 inheader = True
79 inheader = True
80
80
81 cur.append(line)
81 cur.append(line)
82
82
83 if cur:
83 if cur:
84 yield chunk(cur)
84 yield chunk(cur)
85
85
86 def mboxsplit(stream, cur):
86 def mboxsplit(stream, cur):
87 for line in stream:
87 for line in stream:
88 if line.startswith('From '):
88 if line.startswith('From '):
89 for c in split(chunk(cur[1:])):
89 for c in split(chunk(cur[1:])):
90 yield c
90 yield c
91 cur = []
91 cur = []
92
92
93 cur.append(line)
93 cur.append(line)
94
94
95 if cur:
95 if cur:
96 for c in split(chunk(cur[1:])):
96 for c in split(chunk(cur[1:])):
97 yield c
97 yield c
98
98
99 def mimesplit(stream, cur):
99 def mimesplit(stream, cur):
100 def msgfp(m):
100 def msgfp(m):
101 fp = stringio()
101 fp = stringio()
102 g = email.Generator.Generator(fp, mangle_from_=False)
102 g = email.Generator.Generator(fp, mangle_from_=False)
103 g.flatten(m)
103 g.flatten(m)
104 fp.seek(0)
104 fp.seek(0)
105 return fp
105 return fp
106
106
107 for line in stream:
107 for line in stream:
108 cur.append(line)
108 cur.append(line)
109 c = chunk(cur)
109 c = chunk(cur)
110
110
111 m = email.Parser.Parser().parse(c)
111 m = email.Parser.Parser().parse(c)
112 if not m.is_multipart():
112 if not m.is_multipart():
113 yield msgfp(m)
113 yield msgfp(m)
114 else:
114 else:
115 ok_types = ('text/plain', 'text/x-diff', 'text/x-patch')
115 ok_types = ('text/plain', 'text/x-diff', 'text/x-patch')
116 for part in m.walk():
116 for part in m.walk():
117 ct = part.get_content_type()
117 ct = part.get_content_type()
118 if ct not in ok_types:
118 if ct not in ok_types:
119 continue
119 continue
120 yield msgfp(part)
120 yield msgfp(part)
121
121
122 def headersplit(stream, cur):
122 def headersplit(stream, cur):
123 inheader = False
123 inheader = False
124
124
125 for line in stream:
125 for line in stream:
126 if not inheader and isheader(line, inheader):
126 if not inheader and isheader(line, inheader):
127 yield chunk(cur)
127 yield chunk(cur)
128 cur = []
128 cur = []
129 inheader = True
129 inheader = True
130 if inheader and not isheader(line, inheader):
130 if inheader and not isheader(line, inheader):
131 inheader = False
131 inheader = False
132
132
133 cur.append(line)
133 cur.append(line)
134
134
135 if cur:
135 if cur:
136 yield chunk(cur)
136 yield chunk(cur)
137
137
138 def remainder(cur):
138 def remainder(cur):
139 yield chunk(cur)
139 yield chunk(cur)
140
140
141 class fiter(object):
141 class fiter(object):
142 def __init__(self, fp):
142 def __init__(self, fp):
143 self.fp = fp
143 self.fp = fp
144
144
145 def __iter__(self):
145 def __iter__(self):
146 return self
146 return self
147
147
148 def next(self):
148 def next(self):
149 l = self.fp.readline()
149 l = self.fp.readline()
150 if not l:
150 if not l:
151 raise StopIteration
151 raise StopIteration
152 return l
152 return l
153
153
154 inheader = False
154 inheader = False
155 cur = []
155 cur = []
156
156
157 mimeheaders = ['content-type']
157 mimeheaders = ['content-type']
158
158
159 if not util.safehasattr(stream, 'next'):
159 if not util.safehasattr(stream, 'next'):
160 # http responses, for example, have readline but not next
160 # http responses, for example, have readline but not next
161 stream = fiter(stream)
161 stream = fiter(stream)
162
162
163 for line in stream:
163 for line in stream:
164 cur.append(line)
164 cur.append(line)
165 if line.startswith('# HG changeset patch'):
165 if line.startswith('# HG changeset patch'):
166 return hgsplit(stream, cur)
166 return hgsplit(stream, cur)
167 elif line.startswith('From '):
167 elif line.startswith('From '):
168 return mboxsplit(stream, cur)
168 return mboxsplit(stream, cur)
169 elif isheader(line, inheader):
169 elif isheader(line, inheader):
170 inheader = True
170 inheader = True
171 if line.split(':', 1)[0].lower() in mimeheaders:
171 if line.split(':', 1)[0].lower() in mimeheaders:
172 # let email parser handle this
172 # let email parser handle this
173 return mimesplit(stream, cur)
173 return mimesplit(stream, cur)
174 elif line.startswith('--- ') and inheader:
174 elif line.startswith('--- ') and inheader:
175 # No evil headers seen by diff start, split by hand
175 # No evil headers seen by diff start, split by hand
176 return headersplit(stream, cur)
176 return headersplit(stream, cur)
177 # Not enough info, keep reading
177 # Not enough info, keep reading
178
178
179 # if we are here, we have a very plain patch
179 # if we are here, we have a very plain patch
180 return remainder(cur)
180 return remainder(cur)
181
181
182 ## Some facility for extensible patch parsing:
182 ## Some facility for extensible patch parsing:
183 # list of pairs ("header to match", "data key")
183 # list of pairs ("header to match", "data key")
184 patchheadermap = [('Date', 'date'),
184 patchheadermap = [('Date', 'date'),
185 ('Branch', 'branch'),
185 ('Branch', 'branch'),
186 ('Node ID', 'nodeid'),
186 ('Node ID', 'nodeid'),
187 ]
187 ]
188
188
189 def extract(ui, fileobj):
189 def extract(ui, fileobj):
190 '''extract patch from data read from fileobj.
190 '''extract patch from data read from fileobj.
191
191
192 patch can be a normal patch or contained in an email message.
192 patch can be a normal patch or contained in an email message.
193
193
194 return a dictionary. Standard keys are:
194 return a dictionary. Standard keys are:
195 - filename,
195 - filename,
196 - message,
196 - message,
197 - user,
197 - user,
198 - date,
198 - date,
199 - branch,
199 - branch,
200 - node,
200 - node,
201 - p1,
201 - p1,
202 - p2.
202 - p2.
203 Any item can be missing from the dictionary. If filename is missing,
203 Any item can be missing from the dictionary. If filename is missing,
204 fileobj did not contain a patch. Caller must unlink filename when done.'''
204 fileobj did not contain a patch. Caller must unlink filename when done.'''
205
205
206 # attempt to detect the start of a patch
206 # attempt to detect the start of a patch
207 # (this heuristic is borrowed from quilt)
207 # (this heuristic is borrowed from quilt)
208 diffre = re.compile(r'^(?:Index:[ \t]|diff[ \t]|RCS file: |'
208 diffre = re.compile(r'^(?:Index:[ \t]|diff[ \t]|RCS file: |'
209 r'retrieving revision [0-9]+(\.[0-9]+)*$|'
209 r'retrieving revision [0-9]+(\.[0-9]+)*$|'
210 r'---[ \t].*?^\+\+\+[ \t]|'
210 r'---[ \t].*?^\+\+\+[ \t]|'
211 r'\*\*\*[ \t].*?^---[ \t])', re.MULTILINE|re.DOTALL)
211 r'\*\*\*[ \t].*?^---[ \t])', re.MULTILINE|re.DOTALL)
212
212
213 data = {}
213 data = {}
214 fd, tmpname = tempfile.mkstemp(prefix='hg-patch-')
214 fd, tmpname = tempfile.mkstemp(prefix='hg-patch-')
215 tmpfp = os.fdopen(fd, pycompat.sysstr('w'))
215 tmpfp = os.fdopen(fd, pycompat.sysstr('w'))
216 try:
216 try:
217 msg = email.Parser.Parser().parse(fileobj)
217 msg = email.Parser.Parser().parse(fileobj)
218
218
219 subject = msg['Subject'] and mail.headdecode(msg['Subject'])
219 subject = msg['Subject'] and mail.headdecode(msg['Subject'])
220 data['user'] = msg['From'] and mail.headdecode(msg['From'])
220 data['user'] = msg['From'] and mail.headdecode(msg['From'])
221 if not subject and not data['user']:
221 if not subject and not data['user']:
222 # Not an email, restore parsed headers if any
222 # Not an email, restore parsed headers if any
223 subject = '\n'.join(': '.join(h) for h in msg.items()) + '\n'
223 subject = '\n'.join(': '.join(h) for h in msg.items()) + '\n'
224
224
225 # should try to parse msg['Date']
225 # should try to parse msg['Date']
226 parents = []
226 parents = []
227
227
228 if subject:
228 if subject:
229 if subject.startswith('[PATCH'):
229 if subject.startswith('[PATCH'):
230 pend = subject.find(']')
230 pend = subject.find(']')
231 if pend >= 0:
231 if pend >= 0:
232 subject = subject[pend + 1:].lstrip()
232 subject = subject[pend + 1:].lstrip()
233 subject = re.sub(r'\n[ \t]+', ' ', subject)
233 subject = re.sub(r'\n[ \t]+', ' ', subject)
234 ui.debug('Subject: %s\n' % subject)
234 ui.debug('Subject: %s\n' % subject)
235 if data['user']:
235 if data['user']:
236 ui.debug('From: %s\n' % data['user'])
236 ui.debug('From: %s\n' % data['user'])
237 diffs_seen = 0
237 diffs_seen = 0
238 ok_types = ('text/plain', 'text/x-diff', 'text/x-patch')
238 ok_types = ('text/plain', 'text/x-diff', 'text/x-patch')
239 message = ''
239 message = ''
240 for part in msg.walk():
240 for part in msg.walk():
241 content_type = part.get_content_type()
241 content_type = part.get_content_type()
242 ui.debug('Content-Type: %s\n' % content_type)
242 ui.debug('Content-Type: %s\n' % content_type)
243 if content_type not in ok_types:
243 if content_type not in ok_types:
244 continue
244 continue
245 payload = part.get_payload(decode=True)
245 payload = part.get_payload(decode=True)
246 m = diffre.search(payload)
246 m = diffre.search(payload)
247 if m:
247 if m:
248 hgpatch = False
248 hgpatch = False
249 hgpatchheader = False
249 hgpatchheader = False
250 ignoretext = False
250 ignoretext = False
251
251
252 ui.debug('found patch at byte %d\n' % m.start(0))
252 ui.debug('found patch at byte %d\n' % m.start(0))
253 diffs_seen += 1
253 diffs_seen += 1
254 cfp = stringio()
254 cfp = stringio()
255 for line in payload[:m.start(0)].splitlines():
255 for line in payload[:m.start(0)].splitlines():
256 if line.startswith('# HG changeset patch') and not hgpatch:
256 if line.startswith('# HG changeset patch') and not hgpatch:
257 ui.debug('patch generated by hg export\n')
257 ui.debug('patch generated by hg export\n')
258 hgpatch = True
258 hgpatch = True
259 hgpatchheader = True
259 hgpatchheader = True
260 # drop earlier commit message content
260 # drop earlier commit message content
261 cfp.seek(0)
261 cfp.seek(0)
262 cfp.truncate()
262 cfp.truncate()
263 subject = None
263 subject = None
264 elif hgpatchheader:
264 elif hgpatchheader:
265 if line.startswith('# User '):
265 if line.startswith('# User '):
266 data['user'] = line[7:]
266 data['user'] = line[7:]
267 ui.debug('From: %s\n' % data['user'])
267 ui.debug('From: %s\n' % data['user'])
268 elif line.startswith("# Parent "):
268 elif line.startswith("# Parent "):
269 parents.append(line[9:].lstrip())
269 parents.append(line[9:].lstrip())
270 elif line.startswith("# "):
270 elif line.startswith("# "):
271 for header, key in patchheadermap:
271 for header, key in patchheadermap:
272 prefix = '# %s ' % header
272 prefix = '# %s ' % header
273 if line.startswith(prefix):
273 if line.startswith(prefix):
274 data[key] = line[len(prefix):]
274 data[key] = line[len(prefix):]
275 else:
275 else:
276 hgpatchheader = False
276 hgpatchheader = False
277 elif line == '---':
277 elif line == '---':
278 ignoretext = True
278 ignoretext = True
279 if not hgpatchheader and not ignoretext:
279 if not hgpatchheader and not ignoretext:
280 cfp.write(line)
280 cfp.write(line)
281 cfp.write('\n')
281 cfp.write('\n')
282 message = cfp.getvalue()
282 message = cfp.getvalue()
283 if tmpfp:
283 if tmpfp:
284 tmpfp.write(payload)
284 tmpfp.write(payload)
285 if not payload.endswith('\n'):
285 if not payload.endswith('\n'):
286 tmpfp.write('\n')
286 tmpfp.write('\n')
287 elif not diffs_seen and message and content_type == 'text/plain':
287 elif not diffs_seen and message and content_type == 'text/plain':
288 message += '\n' + payload
288 message += '\n' + payload
289 except: # re-raises
289 except: # re-raises
290 tmpfp.close()
290 tmpfp.close()
291 os.unlink(tmpname)
291 os.unlink(tmpname)
292 raise
292 raise
293
293
294 if subject and not message.startswith(subject):
294 if subject and not message.startswith(subject):
295 message = '%s\n%s' % (subject, message)
295 message = '%s\n%s' % (subject, message)
296 data['message'] = message
296 data['message'] = message
297 tmpfp.close()
297 tmpfp.close()
298 if parents:
298 if parents:
299 data['p1'] = parents.pop(0)
299 data['p1'] = parents.pop(0)
300 if parents:
300 if parents:
301 data['p2'] = parents.pop(0)
301 data['p2'] = parents.pop(0)
302
302
303 if diffs_seen:
303 if diffs_seen:
304 data['filename'] = tmpname
304 data['filename'] = tmpname
305 else:
305 else:
306 os.unlink(tmpname)
306 os.unlink(tmpname)
307 return data
307 return data
308
308
309 class patchmeta(object):
309 class patchmeta(object):
310 """Patched file metadata
310 """Patched file metadata
311
311
312 'op' is the performed operation within ADD, DELETE, RENAME, MODIFY
312 'op' is the performed operation within ADD, DELETE, RENAME, MODIFY
313 or COPY. 'path' is patched file path. 'oldpath' is set to the
313 or COPY. 'path' is patched file path. 'oldpath' is set to the
314 origin file when 'op' is either COPY or RENAME, None otherwise. If
314 origin file when 'op' is either COPY or RENAME, None otherwise. If
315 file mode is changed, 'mode' is a tuple (islink, isexec) where
315 file mode is changed, 'mode' is a tuple (islink, isexec) where
316 'islink' is True if the file is a symlink and 'isexec' is True if
316 'islink' is True if the file is a symlink and 'isexec' is True if
317 the file is executable. Otherwise, 'mode' is None.
317 the file is executable. Otherwise, 'mode' is None.
318 """
318 """
319 def __init__(self, path):
319 def __init__(self, path):
320 self.path = path
320 self.path = path
321 self.oldpath = None
321 self.oldpath = None
322 self.mode = None
322 self.mode = None
323 self.op = 'MODIFY'
323 self.op = 'MODIFY'
324 self.binary = False
324 self.binary = False
325
325
326 def setmode(self, mode):
326 def setmode(self, mode):
327 islink = mode & 0o20000
327 islink = mode & 0o20000
328 isexec = mode & 0o100
328 isexec = mode & 0o100
329 self.mode = (islink, isexec)
329 self.mode = (islink, isexec)
330
330
331 def copy(self):
331 def copy(self):
332 other = patchmeta(self.path)
332 other = patchmeta(self.path)
333 other.oldpath = self.oldpath
333 other.oldpath = self.oldpath
334 other.mode = self.mode
334 other.mode = self.mode
335 other.op = self.op
335 other.op = self.op
336 other.binary = self.binary
336 other.binary = self.binary
337 return other
337 return other
338
338
339 def _ispatchinga(self, afile):
339 def _ispatchinga(self, afile):
340 if afile == '/dev/null':
340 if afile == '/dev/null':
341 return self.op == 'ADD'
341 return self.op == 'ADD'
342 return afile == 'a/' + (self.oldpath or self.path)
342 return afile == 'a/' + (self.oldpath or self.path)
343
343
344 def _ispatchingb(self, bfile):
344 def _ispatchingb(self, bfile):
345 if bfile == '/dev/null':
345 if bfile == '/dev/null':
346 return self.op == 'DELETE'
346 return self.op == 'DELETE'
347 return bfile == 'b/' + self.path
347 return bfile == 'b/' + self.path
348
348
349 def ispatching(self, afile, bfile):
349 def ispatching(self, afile, bfile):
350 return self._ispatchinga(afile) and self._ispatchingb(bfile)
350 return self._ispatchinga(afile) and self._ispatchingb(bfile)
351
351
352 def __repr__(self):
352 def __repr__(self):
353 return "<patchmeta %s %r>" % (self.op, self.path)
353 return "<patchmeta %s %r>" % (self.op, self.path)
354
354
355 def readgitpatch(lr):
355 def readgitpatch(lr):
356 """extract git-style metadata about patches from <patchname>"""
356 """extract git-style metadata about patches from <patchname>"""
357
357
358 # Filter patch for git information
358 # Filter patch for git information
359 gp = None
359 gp = None
360 gitpatches = []
360 gitpatches = []
361 for line in lr:
361 for line in lr:
362 line = line.rstrip(' \r\n')
362 line = line.rstrip(' \r\n')
363 if line.startswith('diff --git a/'):
363 if line.startswith('diff --git a/'):
364 m = gitre.match(line)
364 m = gitre.match(line)
365 if m:
365 if m:
366 if gp:
366 if gp:
367 gitpatches.append(gp)
367 gitpatches.append(gp)
368 dst = m.group(2)
368 dst = m.group(2)
369 gp = patchmeta(dst)
369 gp = patchmeta(dst)
370 elif gp:
370 elif gp:
371 if line.startswith('--- '):
371 if line.startswith('--- '):
372 gitpatches.append(gp)
372 gitpatches.append(gp)
373 gp = None
373 gp = None
374 continue
374 continue
375 if line.startswith('rename from '):
375 if line.startswith('rename from '):
376 gp.op = 'RENAME'
376 gp.op = 'RENAME'
377 gp.oldpath = line[12:]
377 gp.oldpath = line[12:]
378 elif line.startswith('rename to '):
378 elif line.startswith('rename to '):
379 gp.path = line[10:]
379 gp.path = line[10:]
380 elif line.startswith('copy from '):
380 elif line.startswith('copy from '):
381 gp.op = 'COPY'
381 gp.op = 'COPY'
382 gp.oldpath = line[10:]
382 gp.oldpath = line[10:]
383 elif line.startswith('copy to '):
383 elif line.startswith('copy to '):
384 gp.path = line[8:]
384 gp.path = line[8:]
385 elif line.startswith('deleted file'):
385 elif line.startswith('deleted file'):
386 gp.op = 'DELETE'
386 gp.op = 'DELETE'
387 elif line.startswith('new file mode '):
387 elif line.startswith('new file mode '):
388 gp.op = 'ADD'
388 gp.op = 'ADD'
389 gp.setmode(int(line[-6:], 8))
389 gp.setmode(int(line[-6:], 8))
390 elif line.startswith('new mode '):
390 elif line.startswith('new mode '):
391 gp.setmode(int(line[-6:], 8))
391 gp.setmode(int(line[-6:], 8))
392 elif line.startswith('GIT binary patch'):
392 elif line.startswith('GIT binary patch'):
393 gp.binary = True
393 gp.binary = True
394 if gp:
394 if gp:
395 gitpatches.append(gp)
395 gitpatches.append(gp)
396
396
397 return gitpatches
397 return gitpatches
398
398
399 class linereader(object):
399 class linereader(object):
400 # simple class to allow pushing lines back into the input stream
400 # simple class to allow pushing lines back into the input stream
401 def __init__(self, fp):
401 def __init__(self, fp):
402 self.fp = fp
402 self.fp = fp
403 self.buf = []
403 self.buf = []
404
404
405 def push(self, line):
405 def push(self, line):
406 if line is not None:
406 if line is not None:
407 self.buf.append(line)
407 self.buf.append(line)
408
408
409 def readline(self):
409 def readline(self):
410 if self.buf:
410 if self.buf:
411 l = self.buf[0]
411 l = self.buf[0]
412 del self.buf[0]
412 del self.buf[0]
413 return l
413 return l
414 return self.fp.readline()
414 return self.fp.readline()
415
415
416 def __iter__(self):
416 def __iter__(self):
417 return iter(self.readline, '')
417 return iter(self.readline, '')
418
418
419 class abstractbackend(object):
419 class abstractbackend(object):
420 def __init__(self, ui):
420 def __init__(self, ui):
421 self.ui = ui
421 self.ui = ui
422
422
423 def getfile(self, fname):
423 def getfile(self, fname):
424 """Return target file data and flags as a (data, (islink,
424 """Return target file data and flags as a (data, (islink,
425 isexec)) tuple. Data is None if file is missing/deleted.
425 isexec)) tuple. Data is None if file is missing/deleted.
426 """
426 """
427 raise NotImplementedError
427 raise NotImplementedError
428
428
429 def setfile(self, fname, data, mode, copysource):
429 def setfile(self, fname, data, mode, copysource):
430 """Write data to target file fname and set its mode. mode is a
430 """Write data to target file fname and set its mode. mode is a
431 (islink, isexec) tuple. If data is None, the file content should
431 (islink, isexec) tuple. If data is None, the file content should
432 be left unchanged. If the file is modified after being copied,
432 be left unchanged. If the file is modified after being copied,
433 copysource is set to the original file name.
433 copysource is set to the original file name.
434 """
434 """
435 raise NotImplementedError
435 raise NotImplementedError
436
436
437 def unlink(self, fname):
437 def unlink(self, fname):
438 """Unlink target file."""
438 """Unlink target file."""
439 raise NotImplementedError
439 raise NotImplementedError
440
440
441 def writerej(self, fname, failed, total, lines):
441 def writerej(self, fname, failed, total, lines):
442 """Write rejected lines for fname. total is the number of hunks
442 """Write rejected lines for fname. total is the number of hunks
443 which failed to apply and total the total number of hunks for this
443 which failed to apply and total the total number of hunks for this
444 files.
444 files.
445 """
445 """
446 pass
446 pass
447
447
448 def exists(self, fname):
448 def exists(self, fname):
449 raise NotImplementedError
449 raise NotImplementedError
450
450
451 def close(self):
451 def close(self):
452 raise NotImplementedError
452 raise NotImplementedError
453
453
454 class fsbackend(abstractbackend):
454 class fsbackend(abstractbackend):
455 def __init__(self, ui, basedir):
455 def __init__(self, ui, basedir):
456 super(fsbackend, self).__init__(ui)
456 super(fsbackend, self).__init__(ui)
457 self.opener = vfsmod.vfs(basedir)
457 self.opener = vfsmod.vfs(basedir)
458
458
459 def getfile(self, fname):
459 def getfile(self, fname):
460 if self.opener.islink(fname):
460 if self.opener.islink(fname):
461 return (self.opener.readlink(fname), (True, False))
461 return (self.opener.readlink(fname), (True, False))
462
462
463 isexec = False
463 isexec = False
464 try:
464 try:
465 isexec = self.opener.lstat(fname).st_mode & 0o100 != 0
465 isexec = self.opener.lstat(fname).st_mode & 0o100 != 0
466 except OSError as e:
466 except OSError as e:
467 if e.errno != errno.ENOENT:
467 if e.errno != errno.ENOENT:
468 raise
468 raise
469 try:
469 try:
470 return (self.opener.read(fname), (False, isexec))
470 return (self.opener.read(fname), (False, isexec))
471 except IOError as e:
471 except IOError as e:
472 if e.errno != errno.ENOENT:
472 if e.errno != errno.ENOENT:
473 raise
473 raise
474 return None, None
474 return None, None
475
475
476 def setfile(self, fname, data, mode, copysource):
476 def setfile(self, fname, data, mode, copysource):
477 islink, isexec = mode
477 islink, isexec = mode
478 if data is None:
478 if data is None:
479 self.opener.setflags(fname, islink, isexec)
479 self.opener.setflags(fname, islink, isexec)
480 return
480 return
481 if islink:
481 if islink:
482 self.opener.symlink(data, fname)
482 self.opener.symlink(data, fname)
483 else:
483 else:
484 self.opener.write(fname, data)
484 self.opener.write(fname, data)
485 if isexec:
485 if isexec:
486 self.opener.setflags(fname, False, True)
486 self.opener.setflags(fname, False, True)
487
487
488 def unlink(self, fname):
488 def unlink(self, fname):
489 self.opener.unlinkpath(fname, ignoremissing=True)
489 self.opener.unlinkpath(fname, ignoremissing=True)
490
490
491 def writerej(self, fname, failed, total, lines):
491 def writerej(self, fname, failed, total, lines):
492 fname = fname + ".rej"
492 fname = fname + ".rej"
493 self.ui.warn(
493 self.ui.warn(
494 _("%d out of %d hunks FAILED -- saving rejects to file %s\n") %
494 _("%d out of %d hunks FAILED -- saving rejects to file %s\n") %
495 (failed, total, fname))
495 (failed, total, fname))
496 fp = self.opener(fname, 'w')
496 fp = self.opener(fname, 'w')
497 fp.writelines(lines)
497 fp.writelines(lines)
498 fp.close()
498 fp.close()
499
499
500 def exists(self, fname):
500 def exists(self, fname):
501 return self.opener.lexists(fname)
501 return self.opener.lexists(fname)
502
502
503 class workingbackend(fsbackend):
503 class workingbackend(fsbackend):
504 def __init__(self, ui, repo, similarity):
504 def __init__(self, ui, repo, similarity):
505 super(workingbackend, self).__init__(ui, repo.root)
505 super(workingbackend, self).__init__(ui, repo.root)
506 self.repo = repo
506 self.repo = repo
507 self.similarity = similarity
507 self.similarity = similarity
508 self.removed = set()
508 self.removed = set()
509 self.changed = set()
509 self.changed = set()
510 self.copied = []
510 self.copied = []
511
511
512 def _checkknown(self, fname):
512 def _checkknown(self, fname):
513 if self.repo.dirstate[fname] == '?' and self.exists(fname):
513 if self.repo.dirstate[fname] == '?' and self.exists(fname):
514 raise PatchError(_('cannot patch %s: file is not tracked') % fname)
514 raise PatchError(_('cannot patch %s: file is not tracked') % fname)
515
515
516 def setfile(self, fname, data, mode, copysource):
516 def setfile(self, fname, data, mode, copysource):
517 self._checkknown(fname)
517 self._checkknown(fname)
518 super(workingbackend, self).setfile(fname, data, mode, copysource)
518 super(workingbackend, self).setfile(fname, data, mode, copysource)
519 if copysource is not None:
519 if copysource is not None:
520 self.copied.append((copysource, fname))
520 self.copied.append((copysource, fname))
521 self.changed.add(fname)
521 self.changed.add(fname)
522
522
523 def unlink(self, fname):
523 def unlink(self, fname):
524 self._checkknown(fname)
524 self._checkknown(fname)
525 super(workingbackend, self).unlink(fname)
525 super(workingbackend, self).unlink(fname)
526 self.removed.add(fname)
526 self.removed.add(fname)
527 self.changed.add(fname)
527 self.changed.add(fname)
528
528
529 def close(self):
529 def close(self):
530 wctx = self.repo[None]
530 wctx = self.repo[None]
531 changed = set(self.changed)
531 changed = set(self.changed)
532 for src, dst in self.copied:
532 for src, dst in self.copied:
533 scmutil.dirstatecopy(self.ui, self.repo, wctx, src, dst)
533 scmutil.dirstatecopy(self.ui, self.repo, wctx, src, dst)
534 if self.removed:
534 if self.removed:
535 wctx.forget(sorted(self.removed))
535 wctx.forget(sorted(self.removed))
536 for f in self.removed:
536 for f in self.removed:
537 if f not in self.repo.dirstate:
537 if f not in self.repo.dirstate:
538 # File was deleted and no longer belongs to the
538 # File was deleted and no longer belongs to the
539 # dirstate, it was probably marked added then
539 # dirstate, it was probably marked added then
540 # deleted, and should not be considered by
540 # deleted, and should not be considered by
541 # marktouched().
541 # marktouched().
542 changed.discard(f)
542 changed.discard(f)
543 if changed:
543 if changed:
544 scmutil.marktouched(self.repo, changed, self.similarity)
544 scmutil.marktouched(self.repo, changed, self.similarity)
545 return sorted(self.changed)
545 return sorted(self.changed)
546
546
547 class filestore(object):
547 class filestore(object):
548 def __init__(self, maxsize=None):
548 def __init__(self, maxsize=None):
549 self.opener = None
549 self.opener = None
550 self.files = {}
550 self.files = {}
551 self.created = 0
551 self.created = 0
552 self.maxsize = maxsize
552 self.maxsize = maxsize
553 if self.maxsize is None:
553 if self.maxsize is None:
554 self.maxsize = 4*(2**20)
554 self.maxsize = 4*(2**20)
555 self.size = 0
555 self.size = 0
556 self.data = {}
556 self.data = {}
557
557
558 def setfile(self, fname, data, mode, copied=None):
558 def setfile(self, fname, data, mode, copied=None):
559 if self.maxsize < 0 or (len(data) + self.size) <= self.maxsize:
559 if self.maxsize < 0 or (len(data) + self.size) <= self.maxsize:
560 self.data[fname] = (data, mode, copied)
560 self.data[fname] = (data, mode, copied)
561 self.size += len(data)
561 self.size += len(data)
562 else:
562 else:
563 if self.opener is None:
563 if self.opener is None:
564 root = tempfile.mkdtemp(prefix='hg-patch-')
564 root = tempfile.mkdtemp(prefix='hg-patch-')
565 self.opener = vfsmod.vfs(root)
565 self.opener = vfsmod.vfs(root)
566 # Avoid filename issues with these simple names
566 # Avoid filename issues with these simple names
567 fn = str(self.created)
567 fn = str(self.created)
568 self.opener.write(fn, data)
568 self.opener.write(fn, data)
569 self.created += 1
569 self.created += 1
570 self.files[fname] = (fn, mode, copied)
570 self.files[fname] = (fn, mode, copied)
571
571
572 def getfile(self, fname):
572 def getfile(self, fname):
573 if fname in self.data:
573 if fname in self.data:
574 return self.data[fname]
574 return self.data[fname]
575 if not self.opener or fname not in self.files:
575 if not self.opener or fname not in self.files:
576 return None, None, None
576 return None, None, None
577 fn, mode, copied = self.files[fname]
577 fn, mode, copied = self.files[fname]
578 return self.opener.read(fn), mode, copied
578 return self.opener.read(fn), mode, copied
579
579
580 def close(self):
580 def close(self):
581 if self.opener:
581 if self.opener:
582 shutil.rmtree(self.opener.base)
582 shutil.rmtree(self.opener.base)
583
583
584 class repobackend(abstractbackend):
584 class repobackend(abstractbackend):
585 def __init__(self, ui, repo, ctx, store):
585 def __init__(self, ui, repo, ctx, store):
586 super(repobackend, self).__init__(ui)
586 super(repobackend, self).__init__(ui)
587 self.repo = repo
587 self.repo = repo
588 self.ctx = ctx
588 self.ctx = ctx
589 self.store = store
589 self.store = store
590 self.changed = set()
590 self.changed = set()
591 self.removed = set()
591 self.removed = set()
592 self.copied = {}
592 self.copied = {}
593
593
594 def _checkknown(self, fname):
594 def _checkknown(self, fname):
595 if fname not in self.ctx:
595 if fname not in self.ctx:
596 raise PatchError(_('cannot patch %s: file is not tracked') % fname)
596 raise PatchError(_('cannot patch %s: file is not tracked') % fname)
597
597
598 def getfile(self, fname):
598 def getfile(self, fname):
599 try:
599 try:
600 fctx = self.ctx[fname]
600 fctx = self.ctx[fname]
601 except error.LookupError:
601 except error.LookupError:
602 return None, None
602 return None, None
603 flags = fctx.flags()
603 flags = fctx.flags()
604 return fctx.data(), ('l' in flags, 'x' in flags)
604 return fctx.data(), ('l' in flags, 'x' in flags)
605
605
606 def setfile(self, fname, data, mode, copysource):
606 def setfile(self, fname, data, mode, copysource):
607 if copysource:
607 if copysource:
608 self._checkknown(copysource)
608 self._checkknown(copysource)
609 if data is None:
609 if data is None:
610 data = self.ctx[fname].data()
610 data = self.ctx[fname].data()
611 self.store.setfile(fname, data, mode, copysource)
611 self.store.setfile(fname, data, mode, copysource)
612 self.changed.add(fname)
612 self.changed.add(fname)
613 if copysource:
613 if copysource:
614 self.copied[fname] = copysource
614 self.copied[fname] = copysource
615
615
616 def unlink(self, fname):
616 def unlink(self, fname):
617 self._checkknown(fname)
617 self._checkknown(fname)
618 self.removed.add(fname)
618 self.removed.add(fname)
619
619
620 def exists(self, fname):
620 def exists(self, fname):
621 return fname in self.ctx
621 return fname in self.ctx
622
622
623 def close(self):
623 def close(self):
624 return self.changed | self.removed
624 return self.changed | self.removed
625
625
626 # @@ -start,len +start,len @@ or @@ -start +start @@ if len is 1
626 # @@ -start,len +start,len @@ or @@ -start +start @@ if len is 1
627 unidesc = re.compile('@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@')
627 unidesc = re.compile('@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@')
628 contextdesc = re.compile('(?:---|\*\*\*) (\d+)(?:,(\d+))? (?:---|\*\*\*)')
628 contextdesc = re.compile('(?:---|\*\*\*) (\d+)(?:,(\d+))? (?:---|\*\*\*)')
629 eolmodes = ['strict', 'crlf', 'lf', 'auto']
629 eolmodes = ['strict', 'crlf', 'lf', 'auto']
630
630
631 class patchfile(object):
631 class patchfile(object):
632 def __init__(self, ui, gp, backend, store, eolmode='strict'):
632 def __init__(self, ui, gp, backend, store, eolmode='strict'):
633 self.fname = gp.path
633 self.fname = gp.path
634 self.eolmode = eolmode
634 self.eolmode = eolmode
635 self.eol = None
635 self.eol = None
636 self.backend = backend
636 self.backend = backend
637 self.ui = ui
637 self.ui = ui
638 self.lines = []
638 self.lines = []
639 self.exists = False
639 self.exists = False
640 self.missing = True
640 self.missing = True
641 self.mode = gp.mode
641 self.mode = gp.mode
642 self.copysource = gp.oldpath
642 self.copysource = gp.oldpath
643 self.create = gp.op in ('ADD', 'COPY', 'RENAME')
643 self.create = gp.op in ('ADD', 'COPY', 'RENAME')
644 self.remove = gp.op == 'DELETE'
644 self.remove = gp.op == 'DELETE'
645 if self.copysource is None:
645 if self.copysource is None:
646 data, mode = backend.getfile(self.fname)
646 data, mode = backend.getfile(self.fname)
647 else:
647 else:
648 data, mode = store.getfile(self.copysource)[:2]
648 data, mode = store.getfile(self.copysource)[:2]
649 if data is not None:
649 if data is not None:
650 self.exists = self.copysource is None or backend.exists(self.fname)
650 self.exists = self.copysource is None or backend.exists(self.fname)
651 self.missing = False
651 self.missing = False
652 if data:
652 if data:
653 self.lines = mdiff.splitnewlines(data)
653 self.lines = mdiff.splitnewlines(data)
654 if self.mode is None:
654 if self.mode is None:
655 self.mode = mode
655 self.mode = mode
656 if self.lines:
656 if self.lines:
657 # Normalize line endings
657 # Normalize line endings
658 if self.lines[0].endswith('\r\n'):
658 if self.lines[0].endswith('\r\n'):
659 self.eol = '\r\n'
659 self.eol = '\r\n'
660 elif self.lines[0].endswith('\n'):
660 elif self.lines[0].endswith('\n'):
661 self.eol = '\n'
661 self.eol = '\n'
662 if eolmode != 'strict':
662 if eolmode != 'strict':
663 nlines = []
663 nlines = []
664 for l in self.lines:
664 for l in self.lines:
665 if l.endswith('\r\n'):
665 if l.endswith('\r\n'):
666 l = l[:-2] + '\n'
666 l = l[:-2] + '\n'
667 nlines.append(l)
667 nlines.append(l)
668 self.lines = nlines
668 self.lines = nlines
669 else:
669 else:
670 if self.create:
670 if self.create:
671 self.missing = False
671 self.missing = False
672 if self.mode is None:
672 if self.mode is None:
673 self.mode = (False, False)
673 self.mode = (False, False)
674 if self.missing:
674 if self.missing:
675 self.ui.warn(_("unable to find '%s' for patching\n") % self.fname)
675 self.ui.warn(_("unable to find '%s' for patching\n") % self.fname)
676 self.ui.warn(_("(use '--prefix' to apply patch relative to the "
676 self.ui.warn(_("(use '--prefix' to apply patch relative to the "
677 "current directory)\n"))
677 "current directory)\n"))
678
678
679 self.hash = {}
679 self.hash = {}
680 self.dirty = 0
680 self.dirty = 0
681 self.offset = 0
681 self.offset = 0
682 self.skew = 0
682 self.skew = 0
683 self.rej = []
683 self.rej = []
684 self.fileprinted = False
684 self.fileprinted = False
685 self.printfile(False)
685 self.printfile(False)
686 self.hunks = 0
686 self.hunks = 0
687
687
688 def writelines(self, fname, lines, mode):
688 def writelines(self, fname, lines, mode):
689 if self.eolmode == 'auto':
689 if self.eolmode == 'auto':
690 eol = self.eol
690 eol = self.eol
691 elif self.eolmode == 'crlf':
691 elif self.eolmode == 'crlf':
692 eol = '\r\n'
692 eol = '\r\n'
693 else:
693 else:
694 eol = '\n'
694 eol = '\n'
695
695
696 if self.eolmode != 'strict' and eol and eol != '\n':
696 if self.eolmode != 'strict' and eol and eol != '\n':
697 rawlines = []
697 rawlines = []
698 for l in lines:
698 for l in lines:
699 if l and l[-1] == '\n':
699 if l and l[-1] == '\n':
700 l = l[:-1] + eol
700 l = l[:-1] + eol
701 rawlines.append(l)
701 rawlines.append(l)
702 lines = rawlines
702 lines = rawlines
703
703
704 self.backend.setfile(fname, ''.join(lines), mode, self.copysource)
704 self.backend.setfile(fname, ''.join(lines), mode, self.copysource)
705
705
706 def printfile(self, warn):
706 def printfile(self, warn):
707 if self.fileprinted:
707 if self.fileprinted:
708 return
708 return
709 if warn or self.ui.verbose:
709 if warn or self.ui.verbose:
710 self.fileprinted = True
710 self.fileprinted = True
711 s = _("patching file %s\n") % self.fname
711 s = _("patching file %s\n") % self.fname
712 if warn:
712 if warn:
713 self.ui.warn(s)
713 self.ui.warn(s)
714 else:
714 else:
715 self.ui.note(s)
715 self.ui.note(s)
716
716
717
717
718 def findlines(self, l, linenum):
718 def findlines(self, l, linenum):
719 # looks through the hash and finds candidate lines. The
719 # looks through the hash and finds candidate lines. The
720 # result is a list of line numbers sorted based on distance
720 # result is a list of line numbers sorted based on distance
721 # from linenum
721 # from linenum
722
722
723 cand = self.hash.get(l, [])
723 cand = self.hash.get(l, [])
724 if len(cand) > 1:
724 if len(cand) > 1:
725 # resort our list of potentials forward then back.
725 # resort our list of potentials forward then back.
726 cand.sort(key=lambda x: abs(x - linenum))
726 cand.sort(key=lambda x: abs(x - linenum))
727 return cand
727 return cand
728
728
729 def write_rej(self):
729 def write_rej(self):
730 # our rejects are a little different from patch(1). This always
730 # our rejects are a little different from patch(1). This always
731 # creates rejects in the same form as the original patch. A file
731 # creates rejects in the same form as the original patch. A file
732 # header is inserted so that you can run the reject through patch again
732 # header is inserted so that you can run the reject through patch again
733 # without having to type the filename.
733 # without having to type the filename.
734 if not self.rej:
734 if not self.rej:
735 return
735 return
736 base = os.path.basename(self.fname)
736 base = os.path.basename(self.fname)
737 lines = ["--- %s\n+++ %s\n" % (base, base)]
737 lines = ["--- %s\n+++ %s\n" % (base, base)]
738 for x in self.rej:
738 for x in self.rej:
739 for l in x.hunk:
739 for l in x.hunk:
740 lines.append(l)
740 lines.append(l)
741 if l[-1:] != '\n':
741 if l[-1:] != '\n':
742 lines.append("\n\ No newline at end of file\n")
742 lines.append("\n\ No newline at end of file\n")
743 self.backend.writerej(self.fname, len(self.rej), self.hunks, lines)
743 self.backend.writerej(self.fname, len(self.rej), self.hunks, lines)
744
744
745 def apply(self, h):
745 def apply(self, h):
746 if not h.complete():
746 if not h.complete():
747 raise PatchError(_("bad hunk #%d %s (%d %d %d %d)") %
747 raise PatchError(_("bad hunk #%d %s (%d %d %d %d)") %
748 (h.number, h.desc, len(h.a), h.lena, len(h.b),
748 (h.number, h.desc, len(h.a), h.lena, len(h.b),
749 h.lenb))
749 h.lenb))
750
750
751 self.hunks += 1
751 self.hunks += 1
752
752
753 if self.missing:
753 if self.missing:
754 self.rej.append(h)
754 self.rej.append(h)
755 return -1
755 return -1
756
756
757 if self.exists and self.create:
757 if self.exists and self.create:
758 if self.copysource:
758 if self.copysource:
759 self.ui.warn(_("cannot create %s: destination already "
759 self.ui.warn(_("cannot create %s: destination already "
760 "exists\n") % self.fname)
760 "exists\n") % self.fname)
761 else:
761 else:
762 self.ui.warn(_("file %s already exists\n") % self.fname)
762 self.ui.warn(_("file %s already exists\n") % self.fname)
763 self.rej.append(h)
763 self.rej.append(h)
764 return -1
764 return -1
765
765
766 if isinstance(h, binhunk):
766 if isinstance(h, binhunk):
767 if self.remove:
767 if self.remove:
768 self.backend.unlink(self.fname)
768 self.backend.unlink(self.fname)
769 else:
769 else:
770 l = h.new(self.lines)
770 l = h.new(self.lines)
771 self.lines[:] = l
771 self.lines[:] = l
772 self.offset += len(l)
772 self.offset += len(l)
773 self.dirty = True
773 self.dirty = True
774 return 0
774 return 0
775
775
776 horig = h
776 horig = h
777 if (self.eolmode in ('crlf', 'lf')
777 if (self.eolmode in ('crlf', 'lf')
778 or self.eolmode == 'auto' and self.eol):
778 or self.eolmode == 'auto' and self.eol):
779 # If new eols are going to be normalized, then normalize
779 # If new eols are going to be normalized, then normalize
780 # hunk data before patching. Otherwise, preserve input
780 # hunk data before patching. Otherwise, preserve input
781 # line-endings.
781 # line-endings.
782 h = h.getnormalized()
782 h = h.getnormalized()
783
783
784 # fast case first, no offsets, no fuzz
784 # fast case first, no offsets, no fuzz
785 old, oldstart, new, newstart = h.fuzzit(0, False)
785 old, oldstart, new, newstart = h.fuzzit(0, False)
786 oldstart += self.offset
786 oldstart += self.offset
787 orig_start = oldstart
787 orig_start = oldstart
788 # if there's skew we want to emit the "(offset %d lines)" even
788 # if there's skew we want to emit the "(offset %d lines)" even
789 # when the hunk cleanly applies at start + skew, so skip the
789 # when the hunk cleanly applies at start + skew, so skip the
790 # fast case code
790 # fast case code
791 if (self.skew == 0 and
791 if (self.skew == 0 and
792 diffhelpers.testhunk(old, self.lines, oldstart) == 0):
792 diffhelpers.testhunk(old, self.lines, oldstart) == 0):
793 if self.remove:
793 if self.remove:
794 self.backend.unlink(self.fname)
794 self.backend.unlink(self.fname)
795 else:
795 else:
796 self.lines[oldstart:oldstart + len(old)] = new
796 self.lines[oldstart:oldstart + len(old)] = new
797 self.offset += len(new) - len(old)
797 self.offset += len(new) - len(old)
798 self.dirty = True
798 self.dirty = True
799 return 0
799 return 0
800
800
801 # ok, we couldn't match the hunk. Lets look for offsets and fuzz it
801 # ok, we couldn't match the hunk. Lets look for offsets and fuzz it
802 self.hash = {}
802 self.hash = {}
803 for x, s in enumerate(self.lines):
803 for x, s in enumerate(self.lines):
804 self.hash.setdefault(s, []).append(x)
804 self.hash.setdefault(s, []).append(x)
805
805
806 for fuzzlen in xrange(self.ui.configint("patch", "fuzz") + 1):
806 for fuzzlen in xrange(self.ui.configint("patch", "fuzz") + 1):
807 for toponly in [True, False]:
807 for toponly in [True, False]:
808 old, oldstart, new, newstart = h.fuzzit(fuzzlen, toponly)
808 old, oldstart, new, newstart = h.fuzzit(fuzzlen, toponly)
809 oldstart = oldstart + self.offset + self.skew
809 oldstart = oldstart + self.offset + self.skew
810 oldstart = min(oldstart, len(self.lines))
810 oldstart = min(oldstart, len(self.lines))
811 if old:
811 if old:
812 cand = self.findlines(old[0][1:], oldstart)
812 cand = self.findlines(old[0][1:], oldstart)
813 else:
813 else:
814 # Only adding lines with no or fuzzed context, just
814 # Only adding lines with no or fuzzed context, just
815 # take the skew in account
815 # take the skew in account
816 cand = [oldstart]
816 cand = [oldstart]
817
817
818 for l in cand:
818 for l in cand:
819 if not old or diffhelpers.testhunk(old, self.lines, l) == 0:
819 if not old or diffhelpers.testhunk(old, self.lines, l) == 0:
820 self.lines[l : l + len(old)] = new
820 self.lines[l : l + len(old)] = new
821 self.offset += len(new) - len(old)
821 self.offset += len(new) - len(old)
822 self.skew = l - orig_start
822 self.skew = l - orig_start
823 self.dirty = True
823 self.dirty = True
824 offset = l - orig_start - fuzzlen
824 offset = l - orig_start - fuzzlen
825 if fuzzlen:
825 if fuzzlen:
826 msg = _("Hunk #%d succeeded at %d "
826 msg = _("Hunk #%d succeeded at %d "
827 "with fuzz %d "
827 "with fuzz %d "
828 "(offset %d lines).\n")
828 "(offset %d lines).\n")
829 self.printfile(True)
829 self.printfile(True)
830 self.ui.warn(msg %
830 self.ui.warn(msg %
831 (h.number, l + 1, fuzzlen, offset))
831 (h.number, l + 1, fuzzlen, offset))
832 else:
832 else:
833 msg = _("Hunk #%d succeeded at %d "
833 msg = _("Hunk #%d succeeded at %d "
834 "(offset %d lines).\n")
834 "(offset %d lines).\n")
835 self.ui.note(msg % (h.number, l + 1, offset))
835 self.ui.note(msg % (h.number, l + 1, offset))
836 return fuzzlen
836 return fuzzlen
837 self.printfile(True)
837 self.printfile(True)
838 self.ui.warn(_("Hunk #%d FAILED at %d\n") % (h.number, orig_start))
838 self.ui.warn(_("Hunk #%d FAILED at %d\n") % (h.number, orig_start))
839 self.rej.append(horig)
839 self.rej.append(horig)
840 return -1
840 return -1
841
841
842 def close(self):
842 def close(self):
843 if self.dirty:
843 if self.dirty:
844 self.writelines(self.fname, self.lines, self.mode)
844 self.writelines(self.fname, self.lines, self.mode)
845 self.write_rej()
845 self.write_rej()
846 return len(self.rej)
846 return len(self.rej)
847
847
848 class header(object):
848 class header(object):
849 """patch header
849 """patch header
850 """
850 """
851 diffgit_re = re.compile('diff --git a/(.*) b/(.*)$')
851 diffgit_re = re.compile('diff --git a/(.*) b/(.*)$')
852 diff_re = re.compile('diff -r .* (.*)$')
852 diff_re = re.compile('diff -r .* (.*)$')
853 allhunks_re = re.compile('(?:index|deleted file) ')
853 allhunks_re = re.compile('(?:index|deleted file) ')
854 pretty_re = re.compile('(?:new file|deleted file) ')
854 pretty_re = re.compile('(?:new file|deleted file) ')
855 special_re = re.compile('(?:index|deleted|copy|rename) ')
855 special_re = re.compile('(?:index|deleted|copy|rename) ')
856 newfile_re = re.compile('(?:new file)')
856 newfile_re = re.compile('(?:new file)')
857
857
858 def __init__(self, header):
858 def __init__(self, header):
859 self.header = header
859 self.header = header
860 self.hunks = []
860 self.hunks = []
861
861
862 def binary(self):
862 def binary(self):
863 return any(h.startswith('index ') for h in self.header)
863 return any(h.startswith('index ') for h in self.header)
864
864
865 def pretty(self, fp):
865 def pretty(self, fp):
866 for h in self.header:
866 for h in self.header:
867 if h.startswith('index '):
867 if h.startswith('index '):
868 fp.write(_('this modifies a binary file (all or nothing)\n'))
868 fp.write(_('this modifies a binary file (all or nothing)\n'))
869 break
869 break
870 if self.pretty_re.match(h):
870 if self.pretty_re.match(h):
871 fp.write(h)
871 fp.write(h)
872 if self.binary():
872 if self.binary():
873 fp.write(_('this is a binary file\n'))
873 fp.write(_('this is a binary file\n'))
874 break
874 break
875 if h.startswith('---'):
875 if h.startswith('---'):
876 fp.write(_('%d hunks, %d lines changed\n') %
876 fp.write(_('%d hunks, %d lines changed\n') %
877 (len(self.hunks),
877 (len(self.hunks),
878 sum([max(h.added, h.removed) for h in self.hunks])))
878 sum([max(h.added, h.removed) for h in self.hunks])))
879 break
879 break
880 fp.write(h)
880 fp.write(h)
881
881
882 def write(self, fp):
882 def write(self, fp):
883 fp.write(''.join(self.header))
883 fp.write(''.join(self.header))
884
884
885 def allhunks(self):
885 def allhunks(self):
886 return any(self.allhunks_re.match(h) for h in self.header)
886 return any(self.allhunks_re.match(h) for h in self.header)
887
887
888 def files(self):
888 def files(self):
889 match = self.diffgit_re.match(self.header[0])
889 match = self.diffgit_re.match(self.header[0])
890 if match:
890 if match:
891 fromfile, tofile = match.groups()
891 fromfile, tofile = match.groups()
892 if fromfile == tofile:
892 if fromfile == tofile:
893 return [fromfile]
893 return [fromfile]
894 return [fromfile, tofile]
894 return [fromfile, tofile]
895 else:
895 else:
896 return self.diff_re.match(self.header[0]).groups()
896 return self.diff_re.match(self.header[0]).groups()
897
897
898 def filename(self):
898 def filename(self):
899 return self.files()[-1]
899 return self.files()[-1]
900
900
901 def __repr__(self):
901 def __repr__(self):
902 return '<header %s>' % (' '.join(map(repr, self.files())))
902 return '<header %s>' % (' '.join(map(repr, self.files())))
903
903
904 def isnewfile(self):
904 def isnewfile(self):
905 return any(self.newfile_re.match(h) for h in self.header)
905 return any(self.newfile_re.match(h) for h in self.header)
906
906
907 def special(self):
907 def special(self):
908 # Special files are shown only at the header level and not at the hunk
908 # Special files are shown only at the header level and not at the hunk
909 # level for example a file that has been deleted is a special file.
909 # level for example a file that has been deleted is a special file.
910 # The user cannot change the content of the operation, in the case of
910 # The user cannot change the content of the operation, in the case of
911 # the deleted file he has to take the deletion or not take it, he
911 # the deleted file he has to take the deletion or not take it, he
912 # cannot take some of it.
912 # cannot take some of it.
913 # Newly added files are special if they are empty, they are not special
913 # Newly added files are special if they are empty, they are not special
914 # if they have some content as we want to be able to change it
914 # if they have some content as we want to be able to change it
915 nocontent = len(self.header) == 2
915 nocontent = len(self.header) == 2
916 emptynewfile = self.isnewfile() and nocontent
916 emptynewfile = self.isnewfile() and nocontent
917 return emptynewfile or \
917 return emptynewfile or \
918 any(self.special_re.match(h) for h in self.header)
918 any(self.special_re.match(h) for h in self.header)
919
919
920 class recordhunk(object):
920 class recordhunk(object):
921 """patch hunk
921 """patch hunk
922
922
923 XXX shouldn't we merge this with the other hunk class?
923 XXX shouldn't we merge this with the other hunk class?
924 """
924 """
925
925
926 def __init__(self, header, fromline, toline, proc, before, hunk, after,
926 def __init__(self, header, fromline, toline, proc, before, hunk, after,
927 maxcontext=None):
927 maxcontext=None):
928 def trimcontext(lines, reverse=False):
928 def trimcontext(lines, reverse=False):
929 if maxcontext is not None:
929 if maxcontext is not None:
930 delta = len(lines) - maxcontext
930 delta = len(lines) - maxcontext
931 if delta > 0:
931 if delta > 0:
932 if reverse:
932 if reverse:
933 return delta, lines[delta:]
933 return delta, lines[delta:]
934 else:
934 else:
935 return delta, lines[:maxcontext]
935 return delta, lines[:maxcontext]
936 return 0, lines
936 return 0, lines
937
937
938 self.header = header
938 self.header = header
939 trimedbefore, self.before = trimcontext(before, True)
939 trimedbefore, self.before = trimcontext(before, True)
940 self.fromline = fromline + trimedbefore
940 self.fromline = fromline + trimedbefore
941 self.toline = toline + trimedbefore
941 self.toline = toline + trimedbefore
942 _trimedafter, self.after = trimcontext(after, False)
942 _trimedafter, self.after = trimcontext(after, False)
943 self.proc = proc
943 self.proc = proc
944 self.hunk = hunk
944 self.hunk = hunk
945 self.added, self.removed = self.countchanges(self.hunk)
945 self.added, self.removed = self.countchanges(self.hunk)
946
946
947 def __eq__(self, v):
947 def __eq__(self, v):
948 if not isinstance(v, recordhunk):
948 if not isinstance(v, recordhunk):
949 return False
949 return False
950
950
951 return ((v.hunk == self.hunk) and
951 return ((v.hunk == self.hunk) and
952 (v.proc == self.proc) and
952 (v.proc == self.proc) and
953 (self.fromline == v.fromline) and
953 (self.fromline == v.fromline) and
954 (self.header.files() == v.header.files()))
954 (self.header.files() == v.header.files()))
955
955
956 def __hash__(self):
956 def __hash__(self):
957 return hash((tuple(self.hunk),
957 return hash((tuple(self.hunk),
958 tuple(self.header.files()),
958 tuple(self.header.files()),
959 self.fromline,
959 self.fromline,
960 self.proc))
960 self.proc))
961
961
962 def countchanges(self, hunk):
962 def countchanges(self, hunk):
963 """hunk -> (n+,n-)"""
963 """hunk -> (n+,n-)"""
964 add = len([h for h in hunk if h[0] == '+'])
964 add = len([h for h in hunk if h[0] == '+'])
965 rem = len([h for h in hunk if h[0] == '-'])
965 rem = len([h for h in hunk if h[0] == '-'])
966 return add, rem
966 return add, rem
967
967
968 def reversehunk(self):
968 def reversehunk(self):
969 """return another recordhunk which is the reverse of the hunk
969 """return another recordhunk which is the reverse of the hunk
970
970
971 If this hunk is diff(A, B), the returned hunk is diff(B, A). To do
971 If this hunk is diff(A, B), the returned hunk is diff(B, A). To do
972 that, swap fromline/toline and +/- signs while keep other things
972 that, swap fromline/toline and +/- signs while keep other things
973 unchanged.
973 unchanged.
974 """
974 """
975 m = {'+': '-', '-': '+', '\\': '\\'}
975 m = {'+': '-', '-': '+', '\\': '\\'}
976 hunk = ['%s%s' % (m[l[0]], l[1:]) for l in self.hunk]
976 hunk = ['%s%s' % (m[l[0]], l[1:]) for l in self.hunk]
977 return recordhunk(self.header, self.toline, self.fromline, self.proc,
977 return recordhunk(self.header, self.toline, self.fromline, self.proc,
978 self.before, hunk, self.after)
978 self.before, hunk, self.after)
979
979
980 def write(self, fp):
980 def write(self, fp):
981 delta = len(self.before) + len(self.after)
981 delta = len(self.before) + len(self.after)
982 if self.after and self.after[-1] == '\\ No newline at end of file\n':
982 if self.after and self.after[-1] == '\\ No newline at end of file\n':
983 delta -= 1
983 delta -= 1
984 fromlen = delta + self.removed
984 fromlen = delta + self.removed
985 tolen = delta + self.added
985 tolen = delta + self.added
986 fp.write('@@ -%d,%d +%d,%d @@%s\n' %
986 fp.write('@@ -%d,%d +%d,%d @@%s\n' %
987 (self.fromline, fromlen, self.toline, tolen,
987 (self.fromline, fromlen, self.toline, tolen,
988 self.proc and (' ' + self.proc)))
988 self.proc and (' ' + self.proc)))
989 fp.write(''.join(self.before + self.hunk + self.after))
989 fp.write(''.join(self.before + self.hunk + self.after))
990
990
991 pretty = write
991 pretty = write
992
992
993 def filename(self):
993 def filename(self):
994 return self.header.filename()
994 return self.header.filename()
995
995
996 def __repr__(self):
996 def __repr__(self):
997 return '<hunk %r@%d>' % (self.filename(), self.fromline)
997 return '<hunk %r@%d>' % (self.filename(), self.fromline)
998
998
999 def filterpatch(ui, headers, operation=None):
999 def filterpatch(ui, headers, operation=None):
1000 """Interactively filter patch chunks into applied-only chunks"""
1000 """Interactively filter patch chunks into applied-only chunks"""
1001 if operation is None:
1001 if operation is None:
1002 operation = 'record'
1002 operation = 'record'
1003 messages = {
1003 messages = {
1004 'multiple': {
1004 'multiple': {
1005 'discard': _("discard change %d/%d to '%s'?"),
1005 'discard': _("discard change %d/%d to '%s'?"),
1006 'record': _("record change %d/%d to '%s'?"),
1006 'record': _("record change %d/%d to '%s'?"),
1007 'revert': _("revert change %d/%d to '%s'?"),
1007 'revert': _("revert change %d/%d to '%s'?"),
1008 }[operation],
1008 }[operation],
1009 'single': {
1009 'single': {
1010 'discard': _("discard this change to '%s'?"),
1010 'discard': _("discard this change to '%s'?"),
1011 'record': _("record this change to '%s'?"),
1011 'record': _("record this change to '%s'?"),
1012 'revert': _("revert this change to '%s'?"),
1012 'revert': _("revert this change to '%s'?"),
1013 }[operation],
1013 }[operation],
1014 'help': {
1014 'help': {
1015 'discard': _('[Ynesfdaq?]'
1015 'discard': _('[Ynesfdaq?]'
1016 '$$ &Yes, discard this change'
1016 '$$ &Yes, discard this change'
1017 '$$ &No, skip this change'
1017 '$$ &No, skip this change'
1018 '$$ &Edit this change manually'
1018 '$$ &Edit this change manually'
1019 '$$ &Skip remaining changes to this file'
1019 '$$ &Skip remaining changes to this file'
1020 '$$ Discard remaining changes to this &file'
1020 '$$ Discard remaining changes to this &file'
1021 '$$ &Done, skip remaining changes and files'
1021 '$$ &Done, skip remaining changes and files'
1022 '$$ Discard &all changes to all remaining files'
1022 '$$ Discard &all changes to all remaining files'
1023 '$$ &Quit, discarding no changes'
1023 '$$ &Quit, discarding no changes'
1024 '$$ &? (display help)'),
1024 '$$ &? (display help)'),
1025 'record': _('[Ynesfdaq?]'
1025 'record': _('[Ynesfdaq?]'
1026 '$$ &Yes, record this change'
1026 '$$ &Yes, record this change'
1027 '$$ &No, skip this change'
1027 '$$ &No, skip this change'
1028 '$$ &Edit this change manually'
1028 '$$ &Edit this change manually'
1029 '$$ &Skip remaining changes to this file'
1029 '$$ &Skip remaining changes to this file'
1030 '$$ Record remaining changes to this &file'
1030 '$$ Record remaining changes to this &file'
1031 '$$ &Done, skip remaining changes and files'
1031 '$$ &Done, skip remaining changes and files'
1032 '$$ Record &all changes to all remaining files'
1032 '$$ Record &all changes to all remaining files'
1033 '$$ &Quit, recording no changes'
1033 '$$ &Quit, recording no changes'
1034 '$$ &? (display help)'),
1034 '$$ &? (display help)'),
1035 'revert': _('[Ynesfdaq?]'
1035 'revert': _('[Ynesfdaq?]'
1036 '$$ &Yes, revert this change'
1036 '$$ &Yes, revert this change'
1037 '$$ &No, skip this change'
1037 '$$ &No, skip this change'
1038 '$$ &Edit this change manually'
1038 '$$ &Edit this change manually'
1039 '$$ &Skip remaining changes to this file'
1039 '$$ &Skip remaining changes to this file'
1040 '$$ Revert remaining changes to this &file'
1040 '$$ Revert remaining changes to this &file'
1041 '$$ &Done, skip remaining changes and files'
1041 '$$ &Done, skip remaining changes and files'
1042 '$$ Revert &all changes to all remaining files'
1042 '$$ Revert &all changes to all remaining files'
1043 '$$ &Quit, reverting no changes'
1043 '$$ &Quit, reverting no changes'
1044 '$$ &? (display help)')
1044 '$$ &? (display help)')
1045 }[operation]
1045 }[operation]
1046 }
1046 }
1047
1047
1048 def prompt(skipfile, skipall, query, chunk):
1048 def prompt(skipfile, skipall, query, chunk):
1049 """prompt query, and process base inputs
1049 """prompt query, and process base inputs
1050
1050
1051 - y/n for the rest of file
1051 - y/n for the rest of file
1052 - y/n for the rest
1052 - y/n for the rest
1053 - ? (help)
1053 - ? (help)
1054 - q (quit)
1054 - q (quit)
1055
1055
1056 Return True/False and possibly updated skipfile and skipall.
1056 Return True/False and possibly updated skipfile and skipall.
1057 """
1057 """
1058 newpatches = None
1058 newpatches = None
1059 if skipall is not None:
1059 if skipall is not None:
1060 return skipall, skipfile, skipall, newpatches
1060 return skipall, skipfile, skipall, newpatches
1061 if skipfile is not None:
1061 if skipfile is not None:
1062 return skipfile, skipfile, skipall, newpatches
1062 return skipfile, skipfile, skipall, newpatches
1063 while True:
1063 while True:
1064 resps = messages['help']
1064 resps = messages['help']
1065 r = ui.promptchoice("%s %s" % (query, resps))
1065 r = ui.promptchoice("%s %s" % (query, resps))
1066 ui.write("\n")
1066 ui.write("\n")
1067 if r == 8: # ?
1067 if r == 8: # ?
1068 for c, t in ui.extractchoices(resps)[1]:
1068 for c, t in ui.extractchoices(resps)[1]:
1069 ui.write('%s - %s\n' % (c, encoding.lower(t)))
1069 ui.write('%s - %s\n' % (c, encoding.lower(t)))
1070 continue
1070 continue
1071 elif r == 0: # yes
1071 elif r == 0: # yes
1072 ret = True
1072 ret = True
1073 elif r == 1: # no
1073 elif r == 1: # no
1074 ret = False
1074 ret = False
1075 elif r == 2: # Edit patch
1075 elif r == 2: # Edit patch
1076 if chunk is None:
1076 if chunk is None:
1077 ui.write(_('cannot edit patch for whole file'))
1077 ui.write(_('cannot edit patch for whole file'))
1078 ui.write("\n")
1078 ui.write("\n")
1079 continue
1079 continue
1080 if chunk.header.binary():
1080 if chunk.header.binary():
1081 ui.write(_('cannot edit patch for binary file'))
1081 ui.write(_('cannot edit patch for binary file'))
1082 ui.write("\n")
1082 ui.write("\n")
1083 continue
1083 continue
1084 # Patch comment based on the Git one (based on comment at end of
1084 # Patch comment based on the Git one (based on comment at end of
1085 # https://mercurial-scm.org/wiki/RecordExtension)
1085 # https://mercurial-scm.org/wiki/RecordExtension)
1086 phelp = '---' + _("""
1086 phelp = '---' + _("""
1087 To remove '-' lines, make them ' ' lines (context).
1087 To remove '-' lines, make them ' ' lines (context).
1088 To remove '+' lines, delete them.
1088 To remove '+' lines, delete them.
1089 Lines starting with # will be removed from the patch.
1089 Lines starting with # will be removed from the patch.
1090
1090
1091 If the patch applies cleanly, the edited hunk will immediately be
1091 If the patch applies cleanly, the edited hunk will immediately be
1092 added to the record list. If it does not apply cleanly, a rejects
1092 added to the record list. If it does not apply cleanly, a rejects
1093 file will be generated: you can use that when you try again. If
1093 file will be generated: you can use that when you try again. If
1094 all lines of the hunk are removed, then the edit is aborted and
1094 all lines of the hunk are removed, then the edit is aborted and
1095 the hunk is left unchanged.
1095 the hunk is left unchanged.
1096 """)
1096 """)
1097 (patchfd, patchfn) = tempfile.mkstemp(prefix="hg-editor-",
1097 (patchfd, patchfn) = tempfile.mkstemp(prefix="hg-editor-",
1098 suffix=".diff", text=True)
1098 suffix=".diff", text=True)
1099 ncpatchfp = None
1099 ncpatchfp = None
1100 try:
1100 try:
1101 # Write the initial patch
1101 # Write the initial patch
1102 f = os.fdopen(patchfd, pycompat.sysstr("w"))
1102 f = os.fdopen(patchfd, pycompat.sysstr("w"))
1103 chunk.header.write(f)
1103 chunk.header.write(f)
1104 chunk.write(f)
1104 chunk.write(f)
1105 f.write('\n'.join(['# ' + i for i in phelp.splitlines()]))
1105 f.write('\n'.join(['# ' + i for i in phelp.splitlines()]))
1106 f.close()
1106 f.close()
1107 # Start the editor and wait for it to complete
1107 # Start the editor and wait for it to complete
1108 editor = ui.geteditor()
1108 editor = ui.geteditor()
1109 ret = ui.system("%s \"%s\"" % (editor, patchfn),
1109 ret = ui.system("%s \"%s\"" % (editor, patchfn),
1110 environ={'HGUSER': ui.username()},
1110 environ={'HGUSER': ui.username()},
1111 blockedtag='filterpatch')
1111 blockedtag='filterpatch')
1112 if ret != 0:
1112 if ret != 0:
1113 ui.warn(_("editor exited with exit code %d\n") % ret)
1113 ui.warn(_("editor exited with exit code %d\n") % ret)
1114 continue
1114 continue
1115 # Remove comment lines
1115 # Remove comment lines
1116 patchfp = open(patchfn)
1116 patchfp = open(patchfn)
1117 ncpatchfp = stringio()
1117 ncpatchfp = stringio()
1118 for line in util.iterfile(patchfp):
1118 for line in util.iterfile(patchfp):
1119 if not line.startswith('#'):
1119 if not line.startswith('#'):
1120 ncpatchfp.write(line)
1120 ncpatchfp.write(line)
1121 patchfp.close()
1121 patchfp.close()
1122 ncpatchfp.seek(0)
1122 ncpatchfp.seek(0)
1123 newpatches = parsepatch(ncpatchfp)
1123 newpatches = parsepatch(ncpatchfp)
1124 finally:
1124 finally:
1125 os.unlink(patchfn)
1125 os.unlink(patchfn)
1126 del ncpatchfp
1126 del ncpatchfp
1127 # Signal that the chunk shouldn't be applied as-is, but
1127 # Signal that the chunk shouldn't be applied as-is, but
1128 # provide the new patch to be used instead.
1128 # provide the new patch to be used instead.
1129 ret = False
1129 ret = False
1130 elif r == 3: # Skip
1130 elif r == 3: # Skip
1131 ret = skipfile = False
1131 ret = skipfile = False
1132 elif r == 4: # file (Record remaining)
1132 elif r == 4: # file (Record remaining)
1133 ret = skipfile = True
1133 ret = skipfile = True
1134 elif r == 5: # done, skip remaining
1134 elif r == 5: # done, skip remaining
1135 ret = skipall = False
1135 ret = skipall = False
1136 elif r == 6: # all
1136 elif r == 6: # all
1137 ret = skipall = True
1137 ret = skipall = True
1138 elif r == 7: # quit
1138 elif r == 7: # quit
1139 raise error.Abort(_('user quit'))
1139 raise error.Abort(_('user quit'))
1140 return ret, skipfile, skipall, newpatches
1140 return ret, skipfile, skipall, newpatches
1141
1141
1142 seen = set()
1142 seen = set()
1143 applied = {} # 'filename' -> [] of chunks
1143 applied = {} # 'filename' -> [] of chunks
1144 skipfile, skipall = None, None
1144 skipfile, skipall = None, None
1145 pos, total = 1, sum(len(h.hunks) for h in headers)
1145 pos, total = 1, sum(len(h.hunks) for h in headers)
1146 for h in headers:
1146 for h in headers:
1147 pos += len(h.hunks)
1147 pos += len(h.hunks)
1148 skipfile = None
1148 skipfile = None
1149 fixoffset = 0
1149 fixoffset = 0
1150 hdr = ''.join(h.header)
1150 hdr = ''.join(h.header)
1151 if hdr in seen:
1151 if hdr in seen:
1152 continue
1152 continue
1153 seen.add(hdr)
1153 seen.add(hdr)
1154 if skipall is None:
1154 if skipall is None:
1155 h.pretty(ui)
1155 h.pretty(ui)
1156 msg = (_('examine changes to %s?') %
1156 msg = (_('examine changes to %s?') %
1157 _(' and ').join("'%s'" % f for f in h.files()))
1157 _(' and ').join("'%s'" % f for f in h.files()))
1158 r, skipfile, skipall, np = prompt(skipfile, skipall, msg, None)
1158 r, skipfile, skipall, np = prompt(skipfile, skipall, msg, None)
1159 if not r:
1159 if not r:
1160 continue
1160 continue
1161 applied[h.filename()] = [h]
1161 applied[h.filename()] = [h]
1162 if h.allhunks():
1162 if h.allhunks():
1163 applied[h.filename()] += h.hunks
1163 applied[h.filename()] += h.hunks
1164 continue
1164 continue
1165 for i, chunk in enumerate(h.hunks):
1165 for i, chunk in enumerate(h.hunks):
1166 if skipfile is None and skipall is None:
1166 if skipfile is None and skipall is None:
1167 chunk.pretty(ui)
1167 chunk.pretty(ui)
1168 if total == 1:
1168 if total == 1:
1169 msg = messages['single'] % chunk.filename()
1169 msg = messages['single'] % chunk.filename()
1170 else:
1170 else:
1171 idx = pos - len(h.hunks) + i
1171 idx = pos - len(h.hunks) + i
1172 msg = messages['multiple'] % (idx, total, chunk.filename())
1172 msg = messages['multiple'] % (idx, total, chunk.filename())
1173 r, skipfile, skipall, newpatches = prompt(skipfile,
1173 r, skipfile, skipall, newpatches = prompt(skipfile,
1174 skipall, msg, chunk)
1174 skipall, msg, chunk)
1175 if r:
1175 if r:
1176 if fixoffset:
1176 if fixoffset:
1177 chunk = copy.copy(chunk)
1177 chunk = copy.copy(chunk)
1178 chunk.toline += fixoffset
1178 chunk.toline += fixoffset
1179 applied[chunk.filename()].append(chunk)
1179 applied[chunk.filename()].append(chunk)
1180 elif newpatches is not None:
1180 elif newpatches is not None:
1181 for newpatch in newpatches:
1181 for newpatch in newpatches:
1182 for newhunk in newpatch.hunks:
1182 for newhunk in newpatch.hunks:
1183 if fixoffset:
1183 if fixoffset:
1184 newhunk.toline += fixoffset
1184 newhunk.toline += fixoffset
1185 applied[newhunk.filename()].append(newhunk)
1185 applied[newhunk.filename()].append(newhunk)
1186 else:
1186 else:
1187 fixoffset += chunk.removed - chunk.added
1187 fixoffset += chunk.removed - chunk.added
1188 return (sum([h for h in applied.itervalues()
1188 return (sum([h for h in applied.itervalues()
1189 if h[0].special() or len(h) > 1], []), {})
1189 if h[0].special() or len(h) > 1], []), {})
1190 class hunk(object):
1190 class hunk(object):
1191 def __init__(self, desc, num, lr, context):
1191 def __init__(self, desc, num, lr, context):
1192 self.number = num
1192 self.number = num
1193 self.desc = desc
1193 self.desc = desc
1194 self.hunk = [desc]
1194 self.hunk = [desc]
1195 self.a = []
1195 self.a = []
1196 self.b = []
1196 self.b = []
1197 self.starta = self.lena = None
1197 self.starta = self.lena = None
1198 self.startb = self.lenb = None
1198 self.startb = self.lenb = None
1199 if lr is not None:
1199 if lr is not None:
1200 if context:
1200 if context:
1201 self.read_context_hunk(lr)
1201 self.read_context_hunk(lr)
1202 else:
1202 else:
1203 self.read_unified_hunk(lr)
1203 self.read_unified_hunk(lr)
1204
1204
1205 def getnormalized(self):
1205 def getnormalized(self):
1206 """Return a copy with line endings normalized to LF."""
1206 """Return a copy with line endings normalized to LF."""
1207
1207
1208 def normalize(lines):
1208 def normalize(lines):
1209 nlines = []
1209 nlines = []
1210 for line in lines:
1210 for line in lines:
1211 if line.endswith('\r\n'):
1211 if line.endswith('\r\n'):
1212 line = line[:-2] + '\n'
1212 line = line[:-2] + '\n'
1213 nlines.append(line)
1213 nlines.append(line)
1214 return nlines
1214 return nlines
1215
1215
1216 # Dummy object, it is rebuilt manually
1216 # Dummy object, it is rebuilt manually
1217 nh = hunk(self.desc, self.number, None, None)
1217 nh = hunk(self.desc, self.number, None, None)
1218 nh.number = self.number
1218 nh.number = self.number
1219 nh.desc = self.desc
1219 nh.desc = self.desc
1220 nh.hunk = self.hunk
1220 nh.hunk = self.hunk
1221 nh.a = normalize(self.a)
1221 nh.a = normalize(self.a)
1222 nh.b = normalize(self.b)
1222 nh.b = normalize(self.b)
1223 nh.starta = self.starta
1223 nh.starta = self.starta
1224 nh.startb = self.startb
1224 nh.startb = self.startb
1225 nh.lena = self.lena
1225 nh.lena = self.lena
1226 nh.lenb = self.lenb
1226 nh.lenb = self.lenb
1227 return nh
1227 return nh
1228
1228
1229 def read_unified_hunk(self, lr):
1229 def read_unified_hunk(self, lr):
1230 m = unidesc.match(self.desc)
1230 m = unidesc.match(self.desc)
1231 if not m:
1231 if not m:
1232 raise PatchError(_("bad hunk #%d") % self.number)
1232 raise PatchError(_("bad hunk #%d") % self.number)
1233 self.starta, self.lena, self.startb, self.lenb = m.groups()
1233 self.starta, self.lena, self.startb, self.lenb = m.groups()
1234 if self.lena is None:
1234 if self.lena is None:
1235 self.lena = 1
1235 self.lena = 1
1236 else:
1236 else:
1237 self.lena = int(self.lena)
1237 self.lena = int(self.lena)
1238 if self.lenb is None:
1238 if self.lenb is None:
1239 self.lenb = 1
1239 self.lenb = 1
1240 else:
1240 else:
1241 self.lenb = int(self.lenb)
1241 self.lenb = int(self.lenb)
1242 self.starta = int(self.starta)
1242 self.starta = int(self.starta)
1243 self.startb = int(self.startb)
1243 self.startb = int(self.startb)
1244 diffhelpers.addlines(lr, self.hunk, self.lena, self.lenb, self.a,
1244 diffhelpers.addlines(lr, self.hunk, self.lena, self.lenb, self.a,
1245 self.b)
1245 self.b)
1246 # if we hit eof before finishing out the hunk, the last line will
1246 # if we hit eof before finishing out the hunk, the last line will
1247 # be zero length. Lets try to fix it up.
1247 # be zero length. Lets try to fix it up.
1248 while len(self.hunk[-1]) == 0:
1248 while len(self.hunk[-1]) == 0:
1249 del self.hunk[-1]
1249 del self.hunk[-1]
1250 del self.a[-1]
1250 del self.a[-1]
1251 del self.b[-1]
1251 del self.b[-1]
1252 self.lena -= 1
1252 self.lena -= 1
1253 self.lenb -= 1
1253 self.lenb -= 1
1254 self._fixnewline(lr)
1254 self._fixnewline(lr)
1255
1255
1256 def read_context_hunk(self, lr):
1256 def read_context_hunk(self, lr):
1257 self.desc = lr.readline()
1257 self.desc = lr.readline()
1258 m = contextdesc.match(self.desc)
1258 m = contextdesc.match(self.desc)
1259 if not m:
1259 if not m:
1260 raise PatchError(_("bad hunk #%d") % self.number)
1260 raise PatchError(_("bad hunk #%d") % self.number)
1261 self.starta, aend = m.groups()
1261 self.starta, aend = m.groups()
1262 self.starta = int(self.starta)
1262 self.starta = int(self.starta)
1263 if aend is None:
1263 if aend is None:
1264 aend = self.starta
1264 aend = self.starta
1265 self.lena = int(aend) - self.starta
1265 self.lena = int(aend) - self.starta
1266 if self.starta:
1266 if self.starta:
1267 self.lena += 1
1267 self.lena += 1
1268 for x in xrange(self.lena):
1268 for x in xrange(self.lena):
1269 l = lr.readline()
1269 l = lr.readline()
1270 if l.startswith('---'):
1270 if l.startswith('---'):
1271 # lines addition, old block is empty
1271 # lines addition, old block is empty
1272 lr.push(l)
1272 lr.push(l)
1273 break
1273 break
1274 s = l[2:]
1274 s = l[2:]
1275 if l.startswith('- ') or l.startswith('! '):
1275 if l.startswith('- ') or l.startswith('! '):
1276 u = '-' + s
1276 u = '-' + s
1277 elif l.startswith(' '):
1277 elif l.startswith(' '):
1278 u = ' ' + s
1278 u = ' ' + s
1279 else:
1279 else:
1280 raise PatchError(_("bad hunk #%d old text line %d") %
1280 raise PatchError(_("bad hunk #%d old text line %d") %
1281 (self.number, x))
1281 (self.number, x))
1282 self.a.append(u)
1282 self.a.append(u)
1283 self.hunk.append(u)
1283 self.hunk.append(u)
1284
1284
1285 l = lr.readline()
1285 l = lr.readline()
1286 if l.startswith('\ '):
1286 if l.startswith('\ '):
1287 s = self.a[-1][:-1]
1287 s = self.a[-1][:-1]
1288 self.a[-1] = s
1288 self.a[-1] = s
1289 self.hunk[-1] = s
1289 self.hunk[-1] = s
1290 l = lr.readline()
1290 l = lr.readline()
1291 m = contextdesc.match(l)
1291 m = contextdesc.match(l)
1292 if not m:
1292 if not m:
1293 raise PatchError(_("bad hunk #%d") % self.number)
1293 raise PatchError(_("bad hunk #%d") % self.number)
1294 self.startb, bend = m.groups()
1294 self.startb, bend = m.groups()
1295 self.startb = int(self.startb)
1295 self.startb = int(self.startb)
1296 if bend is None:
1296 if bend is None:
1297 bend = self.startb
1297 bend = self.startb
1298 self.lenb = int(bend) - self.startb
1298 self.lenb = int(bend) - self.startb
1299 if self.startb:
1299 if self.startb:
1300 self.lenb += 1
1300 self.lenb += 1
1301 hunki = 1
1301 hunki = 1
1302 for x in xrange(self.lenb):
1302 for x in xrange(self.lenb):
1303 l = lr.readline()
1303 l = lr.readline()
1304 if l.startswith('\ '):
1304 if l.startswith('\ '):
1305 # XXX: the only way to hit this is with an invalid line range.
1305 # XXX: the only way to hit this is with an invalid line range.
1306 # The no-eol marker is not counted in the line range, but I
1306 # The no-eol marker is not counted in the line range, but I
1307 # guess there are diff(1) out there which behave differently.
1307 # guess there are diff(1) out there which behave differently.
1308 s = self.b[-1][:-1]
1308 s = self.b[-1][:-1]
1309 self.b[-1] = s
1309 self.b[-1] = s
1310 self.hunk[hunki - 1] = s
1310 self.hunk[hunki - 1] = s
1311 continue
1311 continue
1312 if not l:
1312 if not l:
1313 # line deletions, new block is empty and we hit EOF
1313 # line deletions, new block is empty and we hit EOF
1314 lr.push(l)
1314 lr.push(l)
1315 break
1315 break
1316 s = l[2:]
1316 s = l[2:]
1317 if l.startswith('+ ') or l.startswith('! '):
1317 if l.startswith('+ ') or l.startswith('! '):
1318 u = '+' + s
1318 u = '+' + s
1319 elif l.startswith(' '):
1319 elif l.startswith(' '):
1320 u = ' ' + s
1320 u = ' ' + s
1321 elif len(self.b) == 0:
1321 elif len(self.b) == 0:
1322 # line deletions, new block is empty
1322 # line deletions, new block is empty
1323 lr.push(l)
1323 lr.push(l)
1324 break
1324 break
1325 else:
1325 else:
1326 raise PatchError(_("bad hunk #%d old text line %d") %
1326 raise PatchError(_("bad hunk #%d old text line %d") %
1327 (self.number, x))
1327 (self.number, x))
1328 self.b.append(s)
1328 self.b.append(s)
1329 while True:
1329 while True:
1330 if hunki >= len(self.hunk):
1330 if hunki >= len(self.hunk):
1331 h = ""
1331 h = ""
1332 else:
1332 else:
1333 h = self.hunk[hunki]
1333 h = self.hunk[hunki]
1334 hunki += 1
1334 hunki += 1
1335 if h == u:
1335 if h == u:
1336 break
1336 break
1337 elif h.startswith('-'):
1337 elif h.startswith('-'):
1338 continue
1338 continue
1339 else:
1339 else:
1340 self.hunk.insert(hunki - 1, u)
1340 self.hunk.insert(hunki - 1, u)
1341 break
1341 break
1342
1342
1343 if not self.a:
1343 if not self.a:
1344 # this happens when lines were only added to the hunk
1344 # this happens when lines were only added to the hunk
1345 for x in self.hunk:
1345 for x in self.hunk:
1346 if x.startswith('-') or x.startswith(' '):
1346 if x.startswith('-') or x.startswith(' '):
1347 self.a.append(x)
1347 self.a.append(x)
1348 if not self.b:
1348 if not self.b:
1349 # this happens when lines were only deleted from the hunk
1349 # this happens when lines were only deleted from the hunk
1350 for x in self.hunk:
1350 for x in self.hunk:
1351 if x.startswith('+') or x.startswith(' '):
1351 if x.startswith('+') or x.startswith(' '):
1352 self.b.append(x[1:])
1352 self.b.append(x[1:])
1353 # @@ -start,len +start,len @@
1353 # @@ -start,len +start,len @@
1354 self.desc = "@@ -%d,%d +%d,%d @@\n" % (self.starta, self.lena,
1354 self.desc = "@@ -%d,%d +%d,%d @@\n" % (self.starta, self.lena,
1355 self.startb, self.lenb)
1355 self.startb, self.lenb)
1356 self.hunk[0] = self.desc
1356 self.hunk[0] = self.desc
1357 self._fixnewline(lr)
1357 self._fixnewline(lr)
1358
1358
1359 def _fixnewline(self, lr):
1359 def _fixnewline(self, lr):
1360 l = lr.readline()
1360 l = lr.readline()
1361 if l.startswith('\ '):
1361 if l.startswith('\ '):
1362 diffhelpers.fix_newline(self.hunk, self.a, self.b)
1362 diffhelpers.fix_newline(self.hunk, self.a, self.b)
1363 else:
1363 else:
1364 lr.push(l)
1364 lr.push(l)
1365
1365
1366 def complete(self):
1366 def complete(self):
1367 return len(self.a) == self.lena and len(self.b) == self.lenb
1367 return len(self.a) == self.lena and len(self.b) == self.lenb
1368
1368
1369 def _fuzzit(self, old, new, fuzz, toponly):
1369 def _fuzzit(self, old, new, fuzz, toponly):
1370 # this removes context lines from the top and bottom of list 'l'. It
1370 # this removes context lines from the top and bottom of list 'l'. It
1371 # checks the hunk to make sure only context lines are removed, and then
1371 # checks the hunk to make sure only context lines are removed, and then
1372 # returns a new shortened list of lines.
1372 # returns a new shortened list of lines.
1373 fuzz = min(fuzz, len(old))
1373 fuzz = min(fuzz, len(old))
1374 if fuzz:
1374 if fuzz:
1375 top = 0
1375 top = 0
1376 bot = 0
1376 bot = 0
1377 hlen = len(self.hunk)
1377 hlen = len(self.hunk)
1378 for x in xrange(hlen - 1):
1378 for x in xrange(hlen - 1):
1379 # the hunk starts with the @@ line, so use x+1
1379 # the hunk starts with the @@ line, so use x+1
1380 if self.hunk[x + 1][0] == ' ':
1380 if self.hunk[x + 1][0] == ' ':
1381 top += 1
1381 top += 1
1382 else:
1382 else:
1383 break
1383 break
1384 if not toponly:
1384 if not toponly:
1385 for x in xrange(hlen - 1):
1385 for x in xrange(hlen - 1):
1386 if self.hunk[hlen - bot - 1][0] == ' ':
1386 if self.hunk[hlen - bot - 1][0] == ' ':
1387 bot += 1
1387 bot += 1
1388 else:
1388 else:
1389 break
1389 break
1390
1390
1391 bot = min(fuzz, bot)
1391 bot = min(fuzz, bot)
1392 top = min(fuzz, top)
1392 top = min(fuzz, top)
1393 return old[top:len(old) - bot], new[top:len(new) - bot], top
1393 return old[top:len(old) - bot], new[top:len(new) - bot], top
1394 return old, new, 0
1394 return old, new, 0
1395
1395
1396 def fuzzit(self, fuzz, toponly):
1396 def fuzzit(self, fuzz, toponly):
1397 old, new, top = self._fuzzit(self.a, self.b, fuzz, toponly)
1397 old, new, top = self._fuzzit(self.a, self.b, fuzz, toponly)
1398 oldstart = self.starta + top
1398 oldstart = self.starta + top
1399 newstart = self.startb + top
1399 newstart = self.startb + top
1400 # zero length hunk ranges already have their start decremented
1400 # zero length hunk ranges already have their start decremented
1401 if self.lena and oldstart > 0:
1401 if self.lena and oldstart > 0:
1402 oldstart -= 1
1402 oldstart -= 1
1403 if self.lenb and newstart > 0:
1403 if self.lenb and newstart > 0:
1404 newstart -= 1
1404 newstart -= 1
1405 return old, oldstart, new, newstart
1405 return old, oldstart, new, newstart
1406
1406
1407 class binhunk(object):
1407 class binhunk(object):
1408 'A binary patch file.'
1408 'A binary patch file.'
1409 def __init__(self, lr, fname):
1409 def __init__(self, lr, fname):
1410 self.text = None
1410 self.text = None
1411 self.delta = False
1411 self.delta = False
1412 self.hunk = ['GIT binary patch\n']
1412 self.hunk = ['GIT binary patch\n']
1413 self._fname = fname
1413 self._fname = fname
1414 self._read(lr)
1414 self._read(lr)
1415
1415
1416 def complete(self):
1416 def complete(self):
1417 return self.text is not None
1417 return self.text is not None
1418
1418
1419 def new(self, lines):
1419 def new(self, lines):
1420 if self.delta:
1420 if self.delta:
1421 return [applybindelta(self.text, ''.join(lines))]
1421 return [applybindelta(self.text, ''.join(lines))]
1422 return [self.text]
1422 return [self.text]
1423
1423
1424 def _read(self, lr):
1424 def _read(self, lr):
1425 def getline(lr, hunk):
1425 def getline(lr, hunk):
1426 l = lr.readline()
1426 l = lr.readline()
1427 hunk.append(l)
1427 hunk.append(l)
1428 return l.rstrip('\r\n')
1428 return l.rstrip('\r\n')
1429
1429
1430 size = 0
1430 size = 0
1431 while True:
1431 while True:
1432 line = getline(lr, self.hunk)
1432 line = getline(lr, self.hunk)
1433 if not line:
1433 if not line:
1434 raise PatchError(_('could not extract "%s" binary data')
1434 raise PatchError(_('could not extract "%s" binary data')
1435 % self._fname)
1435 % self._fname)
1436 if line.startswith('literal '):
1436 if line.startswith('literal '):
1437 size = int(line[8:].rstrip())
1437 size = int(line[8:].rstrip())
1438 break
1438 break
1439 if line.startswith('delta '):
1439 if line.startswith('delta '):
1440 size = int(line[6:].rstrip())
1440 size = int(line[6:].rstrip())
1441 self.delta = True
1441 self.delta = True
1442 break
1442 break
1443 dec = []
1443 dec = []
1444 line = getline(lr, self.hunk)
1444 line = getline(lr, self.hunk)
1445 while len(line) > 1:
1445 while len(line) > 1:
1446 l = line[0]
1446 l = line[0]
1447 if l <= 'Z' and l >= 'A':
1447 if l <= 'Z' and l >= 'A':
1448 l = ord(l) - ord('A') + 1
1448 l = ord(l) - ord('A') + 1
1449 else:
1449 else:
1450 l = ord(l) - ord('a') + 27
1450 l = ord(l) - ord('a') + 27
1451 try:
1451 try:
1452 dec.append(util.b85decode(line[1:])[:l])
1452 dec.append(util.b85decode(line[1:])[:l])
1453 except ValueError as e:
1453 except ValueError as e:
1454 raise PatchError(_('could not decode "%s" binary patch: %s')
1454 raise PatchError(_('could not decode "%s" binary patch: %s')
1455 % (self._fname, str(e)))
1455 % (self._fname, str(e)))
1456 line = getline(lr, self.hunk)
1456 line = getline(lr, self.hunk)
1457 text = zlib.decompress(''.join(dec))
1457 text = zlib.decompress(''.join(dec))
1458 if len(text) != size:
1458 if len(text) != size:
1459 raise PatchError(_('"%s" length is %d bytes, should be %d')
1459 raise PatchError(_('"%s" length is %d bytes, should be %d')
1460 % (self._fname, len(text), size))
1460 % (self._fname, len(text), size))
1461 self.text = text
1461 self.text = text
1462
1462
1463 def parsefilename(str):
1463 def parsefilename(str):
1464 # --- filename \t|space stuff
1464 # --- filename \t|space stuff
1465 s = str[4:].rstrip('\r\n')
1465 s = str[4:].rstrip('\r\n')
1466 i = s.find('\t')
1466 i = s.find('\t')
1467 if i < 0:
1467 if i < 0:
1468 i = s.find(' ')
1468 i = s.find(' ')
1469 if i < 0:
1469 if i < 0:
1470 return s
1470 return s
1471 return s[:i]
1471 return s[:i]
1472
1472
1473 def reversehunks(hunks):
1473 def reversehunks(hunks):
1474 '''reverse the signs in the hunks given as argument
1474 '''reverse the signs in the hunks given as argument
1475
1475
1476 This function operates on hunks coming out of patch.filterpatch, that is
1476 This function operates on hunks coming out of patch.filterpatch, that is
1477 a list of the form: [header1, hunk1, hunk2, header2...]. Example usage:
1477 a list of the form: [header1, hunk1, hunk2, header2...]. Example usage:
1478
1478
1479 >>> rawpatch = """diff --git a/folder1/g b/folder1/g
1479 >>> rawpatch = """diff --git a/folder1/g b/folder1/g
1480 ... --- a/folder1/g
1480 ... --- a/folder1/g
1481 ... +++ b/folder1/g
1481 ... +++ b/folder1/g
1482 ... @@ -1,7 +1,7 @@
1482 ... @@ -1,7 +1,7 @@
1483 ... +firstline
1483 ... +firstline
1484 ... c
1484 ... c
1485 ... 1
1485 ... 1
1486 ... 2
1486 ... 2
1487 ... + 3
1487 ... + 3
1488 ... -4
1488 ... -4
1489 ... 5
1489 ... 5
1490 ... d
1490 ... d
1491 ... +lastline"""
1491 ... +lastline"""
1492 >>> hunks = parsepatch(rawpatch)
1492 >>> hunks = parsepatch(rawpatch)
1493 >>> hunkscomingfromfilterpatch = []
1493 >>> hunkscomingfromfilterpatch = []
1494 >>> for h in hunks:
1494 >>> for h in hunks:
1495 ... hunkscomingfromfilterpatch.append(h)
1495 ... hunkscomingfromfilterpatch.append(h)
1496 ... hunkscomingfromfilterpatch.extend(h.hunks)
1496 ... hunkscomingfromfilterpatch.extend(h.hunks)
1497
1497
1498 >>> reversedhunks = reversehunks(hunkscomingfromfilterpatch)
1498 >>> reversedhunks = reversehunks(hunkscomingfromfilterpatch)
1499 >>> from . import util
1499 >>> from . import util
1500 >>> fp = util.stringio()
1500 >>> fp = util.stringio()
1501 >>> for c in reversedhunks:
1501 >>> for c in reversedhunks:
1502 ... c.write(fp)
1502 ... c.write(fp)
1503 >>> fp.seek(0)
1503 >>> fp.seek(0)
1504 >>> reversedpatch = fp.read()
1504 >>> reversedpatch = fp.read()
1505 >>> print reversedpatch
1505 >>> print reversedpatch
1506 diff --git a/folder1/g b/folder1/g
1506 diff --git a/folder1/g b/folder1/g
1507 --- a/folder1/g
1507 --- a/folder1/g
1508 +++ b/folder1/g
1508 +++ b/folder1/g
1509 @@ -1,4 +1,3 @@
1509 @@ -1,4 +1,3 @@
1510 -firstline
1510 -firstline
1511 c
1511 c
1512 1
1512 1
1513 2
1513 2
1514 @@ -2,6 +1,6 @@
1514 @@ -2,6 +1,6 @@
1515 c
1515 c
1516 1
1516 1
1517 2
1517 2
1518 - 3
1518 - 3
1519 +4
1519 +4
1520 5
1520 5
1521 d
1521 d
1522 @@ -6,3 +5,2 @@
1522 @@ -6,3 +5,2 @@
1523 5
1523 5
1524 d
1524 d
1525 -lastline
1525 -lastline
1526
1526
1527 '''
1527 '''
1528
1528
1529 newhunks = []
1529 newhunks = []
1530 for c in hunks:
1530 for c in hunks:
1531 if util.safehasattr(c, 'reversehunk'):
1531 if util.safehasattr(c, 'reversehunk'):
1532 c = c.reversehunk()
1532 c = c.reversehunk()
1533 newhunks.append(c)
1533 newhunks.append(c)
1534 return newhunks
1534 return newhunks
1535
1535
1536 def parsepatch(originalchunks, maxcontext=None):
1536 def parsepatch(originalchunks, maxcontext=None):
1537 """patch -> [] of headers -> [] of hunks
1537 """patch -> [] of headers -> [] of hunks
1538
1538
1539 If maxcontext is not None, trim context lines if necessary.
1539 If maxcontext is not None, trim context lines if necessary.
1540
1540
1541 >>> rawpatch = '''diff --git a/folder1/g b/folder1/g
1541 >>> rawpatch = '''diff --git a/folder1/g b/folder1/g
1542 ... --- a/folder1/g
1542 ... --- a/folder1/g
1543 ... +++ b/folder1/g
1543 ... +++ b/folder1/g
1544 ... @@ -1,8 +1,10 @@
1544 ... @@ -1,8 +1,10 @@
1545 ... 1
1545 ... 1
1546 ... 2
1546 ... 2
1547 ... -3
1547 ... -3
1548 ... 4
1548 ... 4
1549 ... 5
1549 ... 5
1550 ... 6
1550 ... 6
1551 ... +6.1
1551 ... +6.1
1552 ... +6.2
1552 ... +6.2
1553 ... 7
1553 ... 7
1554 ... 8
1554 ... 8
1555 ... +9'''
1555 ... +9'''
1556 >>> out = util.stringio()
1556 >>> out = util.stringio()
1557 >>> headers = parsepatch([rawpatch], maxcontext=1)
1557 >>> headers = parsepatch([rawpatch], maxcontext=1)
1558 >>> for header in headers:
1558 >>> for header in headers:
1559 ... header.write(out)
1559 ... header.write(out)
1560 ... for hunk in header.hunks:
1560 ... for hunk in header.hunks:
1561 ... hunk.write(out)
1561 ... hunk.write(out)
1562 >>> print(out.getvalue())
1562 >>> print(out.getvalue())
1563 diff --git a/folder1/g b/folder1/g
1563 diff --git a/folder1/g b/folder1/g
1564 --- a/folder1/g
1564 --- a/folder1/g
1565 +++ b/folder1/g
1565 +++ b/folder1/g
1566 @@ -2,3 +2,2 @@
1566 @@ -2,3 +2,2 @@
1567 2
1567 2
1568 -3
1568 -3
1569 4
1569 4
1570 @@ -6,2 +5,4 @@
1570 @@ -6,2 +5,4 @@
1571 6
1571 6
1572 +6.1
1572 +6.1
1573 +6.2
1573 +6.2
1574 7
1574 7
1575 @@ -8,1 +9,2 @@
1575 @@ -8,1 +9,2 @@
1576 8
1576 8
1577 +9
1577 +9
1578 """
1578 """
1579 class parser(object):
1579 class parser(object):
1580 """patch parsing state machine"""
1580 """patch parsing state machine"""
1581 def __init__(self):
1581 def __init__(self):
1582 self.fromline = 0
1582 self.fromline = 0
1583 self.toline = 0
1583 self.toline = 0
1584 self.proc = ''
1584 self.proc = ''
1585 self.header = None
1585 self.header = None
1586 self.context = []
1586 self.context = []
1587 self.before = []
1587 self.before = []
1588 self.hunk = []
1588 self.hunk = []
1589 self.headers = []
1589 self.headers = []
1590
1590
1591 def addrange(self, limits):
1591 def addrange(self, limits):
1592 fromstart, fromend, tostart, toend, proc = limits
1592 fromstart, fromend, tostart, toend, proc = limits
1593 self.fromline = int(fromstart)
1593 self.fromline = int(fromstart)
1594 self.toline = int(tostart)
1594 self.toline = int(tostart)
1595 self.proc = proc
1595 self.proc = proc
1596
1596
1597 def addcontext(self, context):
1597 def addcontext(self, context):
1598 if self.hunk:
1598 if self.hunk:
1599 h = recordhunk(self.header, self.fromline, self.toline,
1599 h = recordhunk(self.header, self.fromline, self.toline,
1600 self.proc, self.before, self.hunk, context, maxcontext)
1600 self.proc, self.before, self.hunk, context, maxcontext)
1601 self.header.hunks.append(h)
1601 self.header.hunks.append(h)
1602 self.fromline += len(self.before) + h.removed
1602 self.fromline += len(self.before) + h.removed
1603 self.toline += len(self.before) + h.added
1603 self.toline += len(self.before) + h.added
1604 self.before = []
1604 self.before = []
1605 self.hunk = []
1605 self.hunk = []
1606 self.context = context
1606 self.context = context
1607
1607
1608 def addhunk(self, hunk):
1608 def addhunk(self, hunk):
1609 if self.context:
1609 if self.context:
1610 self.before = self.context
1610 self.before = self.context
1611 self.context = []
1611 self.context = []
1612 self.hunk = hunk
1612 self.hunk = hunk
1613
1613
1614 def newfile(self, hdr):
1614 def newfile(self, hdr):
1615 self.addcontext([])
1615 self.addcontext([])
1616 h = header(hdr)
1616 h = header(hdr)
1617 self.headers.append(h)
1617 self.headers.append(h)
1618 self.header = h
1618 self.header = h
1619
1619
1620 def addother(self, line):
1620 def addother(self, line):
1621 pass # 'other' lines are ignored
1621 pass # 'other' lines are ignored
1622
1622
1623 def finished(self):
1623 def finished(self):
1624 self.addcontext([])
1624 self.addcontext([])
1625 return self.headers
1625 return self.headers
1626
1626
1627 transitions = {
1627 transitions = {
1628 'file': {'context': addcontext,
1628 'file': {'context': addcontext,
1629 'file': newfile,
1629 'file': newfile,
1630 'hunk': addhunk,
1630 'hunk': addhunk,
1631 'range': addrange},
1631 'range': addrange},
1632 'context': {'file': newfile,
1632 'context': {'file': newfile,
1633 'hunk': addhunk,
1633 'hunk': addhunk,
1634 'range': addrange,
1634 'range': addrange,
1635 'other': addother},
1635 'other': addother},
1636 'hunk': {'context': addcontext,
1636 'hunk': {'context': addcontext,
1637 'file': newfile,
1637 'file': newfile,
1638 'range': addrange},
1638 'range': addrange},
1639 'range': {'context': addcontext,
1639 'range': {'context': addcontext,
1640 'hunk': addhunk},
1640 'hunk': addhunk},
1641 'other': {'other': addother},
1641 'other': {'other': addother},
1642 }
1642 }
1643
1643
1644 p = parser()
1644 p = parser()
1645 fp = stringio()
1645 fp = stringio()
1646 fp.write(''.join(originalchunks))
1646 fp.write(''.join(originalchunks))
1647 fp.seek(0)
1647 fp.seek(0)
1648
1648
1649 state = 'context'
1649 state = 'context'
1650 for newstate, data in scanpatch(fp):
1650 for newstate, data in scanpatch(fp):
1651 try:
1651 try:
1652 p.transitions[state][newstate](p, data)
1652 p.transitions[state][newstate](p, data)
1653 except KeyError:
1653 except KeyError:
1654 raise PatchError('unhandled transition: %s -> %s' %
1654 raise PatchError('unhandled transition: %s -> %s' %
1655 (state, newstate))
1655 (state, newstate))
1656 state = newstate
1656 state = newstate
1657 del fp
1657 del fp
1658 return p.finished()
1658 return p.finished()
1659
1659
1660 def pathtransform(path, strip, prefix):
1660 def pathtransform(path, strip, prefix):
1661 '''turn a path from a patch into a path suitable for the repository
1661 '''turn a path from a patch into a path suitable for the repository
1662
1662
1663 prefix, if not empty, is expected to be normalized with a / at the end.
1663 prefix, if not empty, is expected to be normalized with a / at the end.
1664
1664
1665 Returns (stripped components, path in repository).
1665 Returns (stripped components, path in repository).
1666
1666
1667 >>> pathtransform('a/b/c', 0, '')
1667 >>> pathtransform('a/b/c', 0, '')
1668 ('', 'a/b/c')
1668 ('', 'a/b/c')
1669 >>> pathtransform(' a/b/c ', 0, '')
1669 >>> pathtransform(' a/b/c ', 0, '')
1670 ('', ' a/b/c')
1670 ('', ' a/b/c')
1671 >>> pathtransform(' a/b/c ', 2, '')
1671 >>> pathtransform(' a/b/c ', 2, '')
1672 ('a/b/', 'c')
1672 ('a/b/', 'c')
1673 >>> pathtransform('a/b/c', 0, 'd/e/')
1673 >>> pathtransform('a/b/c', 0, 'd/e/')
1674 ('', 'd/e/a/b/c')
1674 ('', 'd/e/a/b/c')
1675 >>> pathtransform(' a//b/c ', 2, 'd/e/')
1675 >>> pathtransform(' a//b/c ', 2, 'd/e/')
1676 ('a//b/', 'd/e/c')
1676 ('a//b/', 'd/e/c')
1677 >>> pathtransform('a/b/c', 3, '')
1677 >>> pathtransform('a/b/c', 3, '')
1678 Traceback (most recent call last):
1678 Traceback (most recent call last):
1679 PatchError: unable to strip away 1 of 3 dirs from a/b/c
1679 PatchError: unable to strip away 1 of 3 dirs from a/b/c
1680 '''
1680 '''
1681 pathlen = len(path)
1681 pathlen = len(path)
1682 i = 0
1682 i = 0
1683 if strip == 0:
1683 if strip == 0:
1684 return '', prefix + path.rstrip()
1684 return '', prefix + path.rstrip()
1685 count = strip
1685 count = strip
1686 while count > 0:
1686 while count > 0:
1687 i = path.find('/', i)
1687 i = path.find('/', i)
1688 if i == -1:
1688 if i == -1:
1689 raise PatchError(_("unable to strip away %d of %d dirs from %s") %
1689 raise PatchError(_("unable to strip away %d of %d dirs from %s") %
1690 (count, strip, path))
1690 (count, strip, path))
1691 i += 1
1691 i += 1
1692 # consume '//' in the path
1692 # consume '//' in the path
1693 while i < pathlen - 1 and path[i] == '/':
1693 while i < pathlen - 1 and path[i] == '/':
1694 i += 1
1694 i += 1
1695 count -= 1
1695 count -= 1
1696 return path[:i].lstrip(), prefix + path[i:].rstrip()
1696 return path[:i].lstrip(), prefix + path[i:].rstrip()
1697
1697
1698 def makepatchmeta(backend, afile_orig, bfile_orig, hunk, strip, prefix):
1698 def makepatchmeta(backend, afile_orig, bfile_orig, hunk, strip, prefix):
1699 nulla = afile_orig == "/dev/null"
1699 nulla = afile_orig == "/dev/null"
1700 nullb = bfile_orig == "/dev/null"
1700 nullb = bfile_orig == "/dev/null"
1701 create = nulla and hunk.starta == 0 and hunk.lena == 0
1701 create = nulla and hunk.starta == 0 and hunk.lena == 0
1702 remove = nullb and hunk.startb == 0 and hunk.lenb == 0
1702 remove = nullb and hunk.startb == 0 and hunk.lenb == 0
1703 abase, afile = pathtransform(afile_orig, strip, prefix)
1703 abase, afile = pathtransform(afile_orig, strip, prefix)
1704 gooda = not nulla and backend.exists(afile)
1704 gooda = not nulla and backend.exists(afile)
1705 bbase, bfile = pathtransform(bfile_orig, strip, prefix)
1705 bbase, bfile = pathtransform(bfile_orig, strip, prefix)
1706 if afile == bfile:
1706 if afile == bfile:
1707 goodb = gooda
1707 goodb = gooda
1708 else:
1708 else:
1709 goodb = not nullb and backend.exists(bfile)
1709 goodb = not nullb and backend.exists(bfile)
1710 missing = not goodb and not gooda and not create
1710 missing = not goodb and not gooda and not create
1711
1711
1712 # some diff programs apparently produce patches where the afile is
1712 # some diff programs apparently produce patches where the afile is
1713 # not /dev/null, but afile starts with bfile
1713 # not /dev/null, but afile starts with bfile
1714 abasedir = afile[:afile.rfind('/') + 1]
1714 abasedir = afile[:afile.rfind('/') + 1]
1715 bbasedir = bfile[:bfile.rfind('/') + 1]
1715 bbasedir = bfile[:bfile.rfind('/') + 1]
1716 if (missing and abasedir == bbasedir and afile.startswith(bfile)
1716 if (missing and abasedir == bbasedir and afile.startswith(bfile)
1717 and hunk.starta == 0 and hunk.lena == 0):
1717 and hunk.starta == 0 and hunk.lena == 0):
1718 create = True
1718 create = True
1719 missing = False
1719 missing = False
1720
1720
1721 # If afile is "a/b/foo" and bfile is "a/b/foo.orig" we assume the
1721 # If afile is "a/b/foo" and bfile is "a/b/foo.orig" we assume the
1722 # diff is between a file and its backup. In this case, the original
1722 # diff is between a file and its backup. In this case, the original
1723 # file should be patched (see original mpatch code).
1723 # file should be patched (see original mpatch code).
1724 isbackup = (abase == bbase and bfile.startswith(afile))
1724 isbackup = (abase == bbase and bfile.startswith(afile))
1725 fname = None
1725 fname = None
1726 if not missing:
1726 if not missing:
1727 if gooda and goodb:
1727 if gooda and goodb:
1728 if isbackup:
1728 if isbackup:
1729 fname = afile
1729 fname = afile
1730 else:
1730 else:
1731 fname = bfile
1731 fname = bfile
1732 elif gooda:
1732 elif gooda:
1733 fname = afile
1733 fname = afile
1734
1734
1735 if not fname:
1735 if not fname:
1736 if not nullb:
1736 if not nullb:
1737 if isbackup:
1737 if isbackup:
1738 fname = afile
1738 fname = afile
1739 else:
1739 else:
1740 fname = bfile
1740 fname = bfile
1741 elif not nulla:
1741 elif not nulla:
1742 fname = afile
1742 fname = afile
1743 else:
1743 else:
1744 raise PatchError(_("undefined source and destination files"))
1744 raise PatchError(_("undefined source and destination files"))
1745
1745
1746 gp = patchmeta(fname)
1746 gp = patchmeta(fname)
1747 if create:
1747 if create:
1748 gp.op = 'ADD'
1748 gp.op = 'ADD'
1749 elif remove:
1749 elif remove:
1750 gp.op = 'DELETE'
1750 gp.op = 'DELETE'
1751 return gp
1751 return gp
1752
1752
1753 def scanpatch(fp):
1753 def scanpatch(fp):
1754 """like patch.iterhunks, but yield different events
1754 """like patch.iterhunks, but yield different events
1755
1755
1756 - ('file', [header_lines + fromfile + tofile])
1756 - ('file', [header_lines + fromfile + tofile])
1757 - ('context', [context_lines])
1757 - ('context', [context_lines])
1758 - ('hunk', [hunk_lines])
1758 - ('hunk', [hunk_lines])
1759 - ('range', (-start,len, +start,len, proc))
1759 - ('range', (-start,len, +start,len, proc))
1760 """
1760 """
1761 lines_re = re.compile(r'@@ -(\d+),(\d+) \+(\d+),(\d+) @@\s*(.*)')
1761 lines_re = re.compile(r'@@ -(\d+),(\d+) \+(\d+),(\d+) @@\s*(.*)')
1762 lr = linereader(fp)
1762 lr = linereader(fp)
1763
1763
1764 def scanwhile(first, p):
1764 def scanwhile(first, p):
1765 """scan lr while predicate holds"""
1765 """scan lr while predicate holds"""
1766 lines = [first]
1766 lines = [first]
1767 for line in iter(lr.readline, ''):
1767 for line in iter(lr.readline, ''):
1768 if p(line):
1768 if p(line):
1769 lines.append(line)
1769 lines.append(line)
1770 else:
1770 else:
1771 lr.push(line)
1771 lr.push(line)
1772 break
1772 break
1773 return lines
1773 return lines
1774
1774
1775 for line in iter(lr.readline, ''):
1775 for line in iter(lr.readline, ''):
1776 if line.startswith('diff --git a/') or line.startswith('diff -r '):
1776 if line.startswith('diff --git a/') or line.startswith('diff -r '):
1777 def notheader(line):
1777 def notheader(line):
1778 s = line.split(None, 1)
1778 s = line.split(None, 1)
1779 return not s or s[0] not in ('---', 'diff')
1779 return not s or s[0] not in ('---', 'diff')
1780 header = scanwhile(line, notheader)
1780 header = scanwhile(line, notheader)
1781 fromfile = lr.readline()
1781 fromfile = lr.readline()
1782 if fromfile.startswith('---'):
1782 if fromfile.startswith('---'):
1783 tofile = lr.readline()
1783 tofile = lr.readline()
1784 header += [fromfile, tofile]
1784 header += [fromfile, tofile]
1785 else:
1785 else:
1786 lr.push(fromfile)
1786 lr.push(fromfile)
1787 yield 'file', header
1787 yield 'file', header
1788 elif line[0] == ' ':
1788 elif line[0] == ' ':
1789 yield 'context', scanwhile(line, lambda l: l[0] in ' \\')
1789 yield 'context', scanwhile(line, lambda l: l[0] in ' \\')
1790 elif line[0] in '-+':
1790 elif line[0] in '-+':
1791 yield 'hunk', scanwhile(line, lambda l: l[0] in '-+\\')
1791 yield 'hunk', scanwhile(line, lambda l: l[0] in '-+\\')
1792 else:
1792 else:
1793 m = lines_re.match(line)
1793 m = lines_re.match(line)
1794 if m:
1794 if m:
1795 yield 'range', m.groups()
1795 yield 'range', m.groups()
1796 else:
1796 else:
1797 yield 'other', line
1797 yield 'other', line
1798
1798
1799 def scangitpatch(lr, firstline):
1799 def scangitpatch(lr, firstline):
1800 """
1800 """
1801 Git patches can emit:
1801 Git patches can emit:
1802 - rename a to b
1802 - rename a to b
1803 - change b
1803 - change b
1804 - copy a to c
1804 - copy a to c
1805 - change c
1805 - change c
1806
1806
1807 We cannot apply this sequence as-is, the renamed 'a' could not be
1807 We cannot apply this sequence as-is, the renamed 'a' could not be
1808 found for it would have been renamed already. And we cannot copy
1808 found for it would have been renamed already. And we cannot copy
1809 from 'b' instead because 'b' would have been changed already. So
1809 from 'b' instead because 'b' would have been changed already. So
1810 we scan the git patch for copy and rename commands so we can
1810 we scan the git patch for copy and rename commands so we can
1811 perform the copies ahead of time.
1811 perform the copies ahead of time.
1812 """
1812 """
1813 pos = 0
1813 pos = 0
1814 try:
1814 try:
1815 pos = lr.fp.tell()
1815 pos = lr.fp.tell()
1816 fp = lr.fp
1816 fp = lr.fp
1817 except IOError:
1817 except IOError:
1818 fp = stringio(lr.fp.read())
1818 fp = stringio(lr.fp.read())
1819 gitlr = linereader(fp)
1819 gitlr = linereader(fp)
1820 gitlr.push(firstline)
1820 gitlr.push(firstline)
1821 gitpatches = readgitpatch(gitlr)
1821 gitpatches = readgitpatch(gitlr)
1822 fp.seek(pos)
1822 fp.seek(pos)
1823 return gitpatches
1823 return gitpatches
1824
1824
1825 def iterhunks(fp):
1825 def iterhunks(fp):
1826 """Read a patch and yield the following events:
1826 """Read a patch and yield the following events:
1827 - ("file", afile, bfile, firsthunk): select a new target file.
1827 - ("file", afile, bfile, firsthunk): select a new target file.
1828 - ("hunk", hunk): a new hunk is ready to be applied, follows a
1828 - ("hunk", hunk): a new hunk is ready to be applied, follows a
1829 "file" event.
1829 "file" event.
1830 - ("git", gitchanges): current diff is in git format, gitchanges
1830 - ("git", gitchanges): current diff is in git format, gitchanges
1831 maps filenames to gitpatch records. Unique event.
1831 maps filenames to gitpatch records. Unique event.
1832 """
1832 """
1833 afile = ""
1833 afile = ""
1834 bfile = ""
1834 bfile = ""
1835 state = None
1835 state = None
1836 hunknum = 0
1836 hunknum = 0
1837 emitfile = newfile = False
1837 emitfile = newfile = False
1838 gitpatches = None
1838 gitpatches = None
1839
1839
1840 # our states
1840 # our states
1841 BFILE = 1
1841 BFILE = 1
1842 context = None
1842 context = None
1843 lr = linereader(fp)
1843 lr = linereader(fp)
1844
1844
1845 for x in iter(lr.readline, ''):
1845 for x in iter(lr.readline, ''):
1846 if state == BFILE and (
1846 if state == BFILE and (
1847 (not context and x[0] == '@')
1847 (not context and x[0] == '@')
1848 or (context is not False and x.startswith('***************'))
1848 or (context is not False and x.startswith('***************'))
1849 or x.startswith('GIT binary patch')):
1849 or x.startswith('GIT binary patch')):
1850 gp = None
1850 gp = None
1851 if (gitpatches and
1851 if (gitpatches and
1852 gitpatches[-1].ispatching(afile, bfile)):
1852 gitpatches[-1].ispatching(afile, bfile)):
1853 gp = gitpatches.pop()
1853 gp = gitpatches.pop()
1854 if x.startswith('GIT binary patch'):
1854 if x.startswith('GIT binary patch'):
1855 h = binhunk(lr, gp.path)
1855 h = binhunk(lr, gp.path)
1856 else:
1856 else:
1857 if context is None and x.startswith('***************'):
1857 if context is None and x.startswith('***************'):
1858 context = True
1858 context = True
1859 h = hunk(x, hunknum + 1, lr, context)
1859 h = hunk(x, hunknum + 1, lr, context)
1860 hunknum += 1
1860 hunknum += 1
1861 if emitfile:
1861 if emitfile:
1862 emitfile = False
1862 emitfile = False
1863 yield 'file', (afile, bfile, h, gp and gp.copy() or None)
1863 yield 'file', (afile, bfile, h, gp and gp.copy() or None)
1864 yield 'hunk', h
1864 yield 'hunk', h
1865 elif x.startswith('diff --git a/'):
1865 elif x.startswith('diff --git a/'):
1866 m = gitre.match(x.rstrip(' \r\n'))
1866 m = gitre.match(x.rstrip(' \r\n'))
1867 if not m:
1867 if not m:
1868 continue
1868 continue
1869 if gitpatches is None:
1869 if gitpatches is None:
1870 # scan whole input for git metadata
1870 # scan whole input for git metadata
1871 gitpatches = scangitpatch(lr, x)
1871 gitpatches = scangitpatch(lr, x)
1872 yield 'git', [g.copy() for g in gitpatches
1872 yield 'git', [g.copy() for g in gitpatches
1873 if g.op in ('COPY', 'RENAME')]
1873 if g.op in ('COPY', 'RENAME')]
1874 gitpatches.reverse()
1874 gitpatches.reverse()
1875 afile = 'a/' + m.group(1)
1875 afile = 'a/' + m.group(1)
1876 bfile = 'b/' + m.group(2)
1876 bfile = 'b/' + m.group(2)
1877 while gitpatches and not gitpatches[-1].ispatching(afile, bfile):
1877 while gitpatches and not gitpatches[-1].ispatching(afile, bfile):
1878 gp = gitpatches.pop()
1878 gp = gitpatches.pop()
1879 yield 'file', ('a/' + gp.path, 'b/' + gp.path, None, gp.copy())
1879 yield 'file', ('a/' + gp.path, 'b/' + gp.path, None, gp.copy())
1880 if not gitpatches:
1880 if not gitpatches:
1881 raise PatchError(_('failed to synchronize metadata for "%s"')
1881 raise PatchError(_('failed to synchronize metadata for "%s"')
1882 % afile[2:])
1882 % afile[2:])
1883 gp = gitpatches[-1]
1883 gp = gitpatches[-1]
1884 newfile = True
1884 newfile = True
1885 elif x.startswith('---'):
1885 elif x.startswith('---'):
1886 # check for a unified diff
1886 # check for a unified diff
1887 l2 = lr.readline()
1887 l2 = lr.readline()
1888 if not l2.startswith('+++'):
1888 if not l2.startswith('+++'):
1889 lr.push(l2)
1889 lr.push(l2)
1890 continue
1890 continue
1891 newfile = True
1891 newfile = True
1892 context = False
1892 context = False
1893 afile = parsefilename(x)
1893 afile = parsefilename(x)
1894 bfile = parsefilename(l2)
1894 bfile = parsefilename(l2)
1895 elif x.startswith('***'):
1895 elif x.startswith('***'):
1896 # check for a context diff
1896 # check for a context diff
1897 l2 = lr.readline()
1897 l2 = lr.readline()
1898 if not l2.startswith('---'):
1898 if not l2.startswith('---'):
1899 lr.push(l2)
1899 lr.push(l2)
1900 continue
1900 continue
1901 l3 = lr.readline()
1901 l3 = lr.readline()
1902 lr.push(l3)
1902 lr.push(l3)
1903 if not l3.startswith("***************"):
1903 if not l3.startswith("***************"):
1904 lr.push(l2)
1904 lr.push(l2)
1905 continue
1905 continue
1906 newfile = True
1906 newfile = True
1907 context = True
1907 context = True
1908 afile = parsefilename(x)
1908 afile = parsefilename(x)
1909 bfile = parsefilename(l2)
1909 bfile = parsefilename(l2)
1910
1910
1911 if newfile:
1911 if newfile:
1912 newfile = False
1912 newfile = False
1913 emitfile = True
1913 emitfile = True
1914 state = BFILE
1914 state = BFILE
1915 hunknum = 0
1915 hunknum = 0
1916
1916
1917 while gitpatches:
1917 while gitpatches:
1918 gp = gitpatches.pop()
1918 gp = gitpatches.pop()
1919 yield 'file', ('a/' + gp.path, 'b/' + gp.path, None, gp.copy())
1919 yield 'file', ('a/' + gp.path, 'b/' + gp.path, None, gp.copy())
1920
1920
1921 def applybindelta(binchunk, data):
1921 def applybindelta(binchunk, data):
1922 """Apply a binary delta hunk
1922 """Apply a binary delta hunk
1923 The algorithm used is the algorithm from git's patch-delta.c
1923 The algorithm used is the algorithm from git's patch-delta.c
1924 """
1924 """
1925 def deltahead(binchunk):
1925 def deltahead(binchunk):
1926 i = 0
1926 i = 0
1927 for c in binchunk:
1927 for c in binchunk:
1928 i += 1
1928 i += 1
1929 if not (ord(c) & 0x80):
1929 if not (ord(c) & 0x80):
1930 return i
1930 return i
1931 return i
1931 return i
1932 out = ""
1932 out = ""
1933 s = deltahead(binchunk)
1933 s = deltahead(binchunk)
1934 binchunk = binchunk[s:]
1934 binchunk = binchunk[s:]
1935 s = deltahead(binchunk)
1935 s = deltahead(binchunk)
1936 binchunk = binchunk[s:]
1936 binchunk = binchunk[s:]
1937 i = 0
1937 i = 0
1938 while i < len(binchunk):
1938 while i < len(binchunk):
1939 cmd = ord(binchunk[i])
1939 cmd = ord(binchunk[i])
1940 i += 1
1940 i += 1
1941 if (cmd & 0x80):
1941 if (cmd & 0x80):
1942 offset = 0
1942 offset = 0
1943 size = 0
1943 size = 0
1944 if (cmd & 0x01):
1944 if (cmd & 0x01):
1945 offset = ord(binchunk[i])
1945 offset = ord(binchunk[i])
1946 i += 1
1946 i += 1
1947 if (cmd & 0x02):
1947 if (cmd & 0x02):
1948 offset |= ord(binchunk[i]) << 8
1948 offset |= ord(binchunk[i]) << 8
1949 i += 1
1949 i += 1
1950 if (cmd & 0x04):
1950 if (cmd & 0x04):
1951 offset |= ord(binchunk[i]) << 16
1951 offset |= ord(binchunk[i]) << 16
1952 i += 1
1952 i += 1
1953 if (cmd & 0x08):
1953 if (cmd & 0x08):
1954 offset |= ord(binchunk[i]) << 24
1954 offset |= ord(binchunk[i]) << 24
1955 i += 1
1955 i += 1
1956 if (cmd & 0x10):
1956 if (cmd & 0x10):
1957 size = ord(binchunk[i])
1957 size = ord(binchunk[i])
1958 i += 1
1958 i += 1
1959 if (cmd & 0x20):
1959 if (cmd & 0x20):
1960 size |= ord(binchunk[i]) << 8
1960 size |= ord(binchunk[i]) << 8
1961 i += 1
1961 i += 1
1962 if (cmd & 0x40):
1962 if (cmd & 0x40):
1963 size |= ord(binchunk[i]) << 16
1963 size |= ord(binchunk[i]) << 16
1964 i += 1
1964 i += 1
1965 if size == 0:
1965 if size == 0:
1966 size = 0x10000
1966 size = 0x10000
1967 offset_end = offset + size
1967 offset_end = offset + size
1968 out += data[offset:offset_end]
1968 out += data[offset:offset_end]
1969 elif cmd != 0:
1969 elif cmd != 0:
1970 offset_end = i + cmd
1970 offset_end = i + cmd
1971 out += binchunk[i:offset_end]
1971 out += binchunk[i:offset_end]
1972 i += cmd
1972 i += cmd
1973 else:
1973 else:
1974 raise PatchError(_('unexpected delta opcode 0'))
1974 raise PatchError(_('unexpected delta opcode 0'))
1975 return out
1975 return out
1976
1976
1977 def applydiff(ui, fp, backend, store, strip=1, prefix='', eolmode='strict'):
1977 def applydiff(ui, fp, backend, store, strip=1, prefix='', eolmode='strict'):
1978 """Reads a patch from fp and tries to apply it.
1978 """Reads a patch from fp and tries to apply it.
1979
1979
1980 Returns 0 for a clean patch, -1 if any rejects were found and 1 if
1980 Returns 0 for a clean patch, -1 if any rejects were found and 1 if
1981 there was any fuzz.
1981 there was any fuzz.
1982
1982
1983 If 'eolmode' is 'strict', the patch content and patched file are
1983 If 'eolmode' is 'strict', the patch content and patched file are
1984 read in binary mode. Otherwise, line endings are ignored when
1984 read in binary mode. Otherwise, line endings are ignored when
1985 patching then normalized according to 'eolmode'.
1985 patching then normalized according to 'eolmode'.
1986 """
1986 """
1987 return _applydiff(ui, fp, patchfile, backend, store, strip=strip,
1987 return _applydiff(ui, fp, patchfile, backend, store, strip=strip,
1988 prefix=prefix, eolmode=eolmode)
1988 prefix=prefix, eolmode=eolmode)
1989
1989
1990 def _applydiff(ui, fp, patcher, backend, store, strip=1, prefix='',
1990 def _applydiff(ui, fp, patcher, backend, store, strip=1, prefix='',
1991 eolmode='strict'):
1991 eolmode='strict'):
1992
1992
1993 if prefix:
1993 if prefix:
1994 prefix = pathutil.canonpath(backend.repo.root, backend.repo.getcwd(),
1994 prefix = pathutil.canonpath(backend.repo.root, backend.repo.getcwd(),
1995 prefix)
1995 prefix)
1996 if prefix != '':
1996 if prefix != '':
1997 prefix += '/'
1997 prefix += '/'
1998 def pstrip(p):
1998 def pstrip(p):
1999 return pathtransform(p, strip - 1, prefix)[1]
1999 return pathtransform(p, strip - 1, prefix)[1]
2000
2000
2001 rejects = 0
2001 rejects = 0
2002 err = 0
2002 err = 0
2003 current_file = None
2003 current_file = None
2004
2004
2005 for state, values in iterhunks(fp):
2005 for state, values in iterhunks(fp):
2006 if state == 'hunk':
2006 if state == 'hunk':
2007 if not current_file:
2007 if not current_file:
2008 continue
2008 continue
2009 ret = current_file.apply(values)
2009 ret = current_file.apply(values)
2010 if ret > 0:
2010 if ret > 0:
2011 err = 1
2011 err = 1
2012 elif state == 'file':
2012 elif state == 'file':
2013 if current_file:
2013 if current_file:
2014 rejects += current_file.close()
2014 rejects += current_file.close()
2015 current_file = None
2015 current_file = None
2016 afile, bfile, first_hunk, gp = values
2016 afile, bfile, first_hunk, gp = values
2017 if gp:
2017 if gp:
2018 gp.path = pstrip(gp.path)
2018 gp.path = pstrip(gp.path)
2019 if gp.oldpath:
2019 if gp.oldpath:
2020 gp.oldpath = pstrip(gp.oldpath)
2020 gp.oldpath = pstrip(gp.oldpath)
2021 else:
2021 else:
2022 gp = makepatchmeta(backend, afile, bfile, first_hunk, strip,
2022 gp = makepatchmeta(backend, afile, bfile, first_hunk, strip,
2023 prefix)
2023 prefix)
2024 if gp.op == 'RENAME':
2024 if gp.op == 'RENAME':
2025 backend.unlink(gp.oldpath)
2025 backend.unlink(gp.oldpath)
2026 if not first_hunk:
2026 if not first_hunk:
2027 if gp.op == 'DELETE':
2027 if gp.op == 'DELETE':
2028 backend.unlink(gp.path)
2028 backend.unlink(gp.path)
2029 continue
2029 continue
2030 data, mode = None, None
2030 data, mode = None, None
2031 if gp.op in ('RENAME', 'COPY'):
2031 if gp.op in ('RENAME', 'COPY'):
2032 data, mode = store.getfile(gp.oldpath)[:2]
2032 data, mode = store.getfile(gp.oldpath)[:2]
2033 if data is None:
2033 if data is None:
2034 # This means that the old path does not exist
2034 # This means that the old path does not exist
2035 raise PatchError(_("source file '%s' does not exist")
2035 raise PatchError(_("source file '%s' does not exist")
2036 % gp.oldpath)
2036 % gp.oldpath)
2037 if gp.mode:
2037 if gp.mode:
2038 mode = gp.mode
2038 mode = gp.mode
2039 if gp.op == 'ADD':
2039 if gp.op == 'ADD':
2040 # Added files without content have no hunk and
2040 # Added files without content have no hunk and
2041 # must be created
2041 # must be created
2042 data = ''
2042 data = ''
2043 if data or mode:
2043 if data or mode:
2044 if (gp.op in ('ADD', 'RENAME', 'COPY')
2044 if (gp.op in ('ADD', 'RENAME', 'COPY')
2045 and backend.exists(gp.path)):
2045 and backend.exists(gp.path)):
2046 raise PatchError(_("cannot create %s: destination "
2046 raise PatchError(_("cannot create %s: destination "
2047 "already exists") % gp.path)
2047 "already exists") % gp.path)
2048 backend.setfile(gp.path, data, mode, gp.oldpath)
2048 backend.setfile(gp.path, data, mode, gp.oldpath)
2049 continue
2049 continue
2050 try:
2050 try:
2051 current_file = patcher(ui, gp, backend, store,
2051 current_file = patcher(ui, gp, backend, store,
2052 eolmode=eolmode)
2052 eolmode=eolmode)
2053 except PatchError as inst:
2053 except PatchError as inst:
2054 ui.warn(str(inst) + '\n')
2054 ui.warn(str(inst) + '\n')
2055 current_file = None
2055 current_file = None
2056 rejects += 1
2056 rejects += 1
2057 continue
2057 continue
2058 elif state == 'git':
2058 elif state == 'git':
2059 for gp in values:
2059 for gp in values:
2060 path = pstrip(gp.oldpath)
2060 path = pstrip(gp.oldpath)
2061 data, mode = backend.getfile(path)
2061 data, mode = backend.getfile(path)
2062 if data is None:
2062 if data is None:
2063 # The error ignored here will trigger a getfile()
2063 # The error ignored here will trigger a getfile()
2064 # error in a place more appropriate for error
2064 # error in a place more appropriate for error
2065 # handling, and will not interrupt the patching
2065 # handling, and will not interrupt the patching
2066 # process.
2066 # process.
2067 pass
2067 pass
2068 else:
2068 else:
2069 store.setfile(path, data, mode)
2069 store.setfile(path, data, mode)
2070 else:
2070 else:
2071 raise error.Abort(_('unsupported parser state: %s') % state)
2071 raise error.Abort(_('unsupported parser state: %s') % state)
2072
2072
2073 if current_file:
2073 if current_file:
2074 rejects += current_file.close()
2074 rejects += current_file.close()
2075
2075
2076 if rejects:
2076 if rejects:
2077 return -1
2077 return -1
2078 return err
2078 return err
2079
2079
2080 def _externalpatch(ui, repo, patcher, patchname, strip, files,
2080 def _externalpatch(ui, repo, patcher, patchname, strip, files,
2081 similarity):
2081 similarity):
2082 """use <patcher> to apply <patchname> to the working directory.
2082 """use <patcher> to apply <patchname> to the working directory.
2083 returns whether patch was applied with fuzz factor."""
2083 returns whether patch was applied with fuzz factor."""
2084
2084
2085 fuzz = False
2085 fuzz = False
2086 args = []
2086 args = []
2087 cwd = repo.root
2087 cwd = repo.root
2088 if cwd:
2088 if cwd:
2089 args.append('-d %s' % util.shellquote(cwd))
2089 args.append('-d %s' % util.shellquote(cwd))
2090 fp = util.popen('%s %s -p%d < %s' % (patcher, ' '.join(args), strip,
2090 fp = util.popen('%s %s -p%d < %s' % (patcher, ' '.join(args), strip,
2091 util.shellquote(patchname)))
2091 util.shellquote(patchname)))
2092 try:
2092 try:
2093 for line in util.iterfile(fp):
2093 for line in util.iterfile(fp):
2094 line = line.rstrip()
2094 line = line.rstrip()
2095 ui.note(line + '\n')
2095 ui.note(line + '\n')
2096 if line.startswith('patching file '):
2096 if line.startswith('patching file '):
2097 pf = util.parsepatchoutput(line)
2097 pf = util.parsepatchoutput(line)
2098 printed_file = False
2098 printed_file = False
2099 files.add(pf)
2099 files.add(pf)
2100 elif line.find('with fuzz') >= 0:
2100 elif line.find('with fuzz') >= 0:
2101 fuzz = True
2101 fuzz = True
2102 if not printed_file:
2102 if not printed_file:
2103 ui.warn(pf + '\n')
2103 ui.warn(pf + '\n')
2104 printed_file = True
2104 printed_file = True
2105 ui.warn(line + '\n')
2105 ui.warn(line + '\n')
2106 elif line.find('saving rejects to file') >= 0:
2106 elif line.find('saving rejects to file') >= 0:
2107 ui.warn(line + '\n')
2107 ui.warn(line + '\n')
2108 elif line.find('FAILED') >= 0:
2108 elif line.find('FAILED') >= 0:
2109 if not printed_file:
2109 if not printed_file:
2110 ui.warn(pf + '\n')
2110 ui.warn(pf + '\n')
2111 printed_file = True
2111 printed_file = True
2112 ui.warn(line + '\n')
2112 ui.warn(line + '\n')
2113 finally:
2113 finally:
2114 if files:
2114 if files:
2115 scmutil.marktouched(repo, files, similarity)
2115 scmutil.marktouched(repo, files, similarity)
2116 code = fp.close()
2116 code = fp.close()
2117 if code:
2117 if code:
2118 raise PatchError(_("patch command failed: %s") %
2118 raise PatchError(_("patch command failed: %s") %
2119 util.explainexit(code)[0])
2119 util.explainexit(code)[0])
2120 return fuzz
2120 return fuzz
2121
2121
2122 def patchbackend(ui, backend, patchobj, strip, prefix, files=None,
2122 def patchbackend(ui, backend, patchobj, strip, prefix, files=None,
2123 eolmode='strict'):
2123 eolmode='strict'):
2124 if files is None:
2124 if files is None:
2125 files = set()
2125 files = set()
2126 if eolmode is None:
2126 if eolmode is None:
2127 eolmode = ui.config('patch', 'eol')
2127 eolmode = ui.config('patch', 'eol')
2128 if eolmode.lower() not in eolmodes:
2128 if eolmode.lower() not in eolmodes:
2129 raise error.Abort(_('unsupported line endings type: %s') % eolmode)
2129 raise error.Abort(_('unsupported line endings type: %s') % eolmode)
2130 eolmode = eolmode.lower()
2130 eolmode = eolmode.lower()
2131
2131
2132 store = filestore()
2132 store = filestore()
2133 try:
2133 try:
2134 fp = open(patchobj, 'rb')
2134 fp = open(patchobj, 'rb')
2135 except TypeError:
2135 except TypeError:
2136 fp = patchobj
2136 fp = patchobj
2137 try:
2137 try:
2138 ret = applydiff(ui, fp, backend, store, strip=strip, prefix=prefix,
2138 ret = applydiff(ui, fp, backend, store, strip=strip, prefix=prefix,
2139 eolmode=eolmode)
2139 eolmode=eolmode)
2140 finally:
2140 finally:
2141 if fp != patchobj:
2141 if fp != patchobj:
2142 fp.close()
2142 fp.close()
2143 files.update(backend.close())
2143 files.update(backend.close())
2144 store.close()
2144 store.close()
2145 if ret < 0:
2145 if ret < 0:
2146 raise PatchError(_('patch failed to apply'))
2146 raise PatchError(_('patch failed to apply'))
2147 return ret > 0
2147 return ret > 0
2148
2148
2149 def internalpatch(ui, repo, patchobj, strip, prefix='', files=None,
2149 def internalpatch(ui, repo, patchobj, strip, prefix='', files=None,
2150 eolmode='strict', similarity=0):
2150 eolmode='strict', similarity=0):
2151 """use builtin patch to apply <patchobj> to the working directory.
2151 """use builtin patch to apply <patchobj> to the working directory.
2152 returns whether patch was applied with fuzz factor."""
2152 returns whether patch was applied with fuzz factor."""
2153 backend = workingbackend(ui, repo, similarity)
2153 backend = workingbackend(ui, repo, similarity)
2154 return patchbackend(ui, backend, patchobj, strip, prefix, files, eolmode)
2154 return patchbackend(ui, backend, patchobj, strip, prefix, files, eolmode)
2155
2155
2156 def patchrepo(ui, repo, ctx, store, patchobj, strip, prefix, files=None,
2156 def patchrepo(ui, repo, ctx, store, patchobj, strip, prefix, files=None,
2157 eolmode='strict'):
2157 eolmode='strict'):
2158 backend = repobackend(ui, repo, ctx, store)
2158 backend = repobackend(ui, repo, ctx, store)
2159 return patchbackend(ui, backend, patchobj, strip, prefix, files, eolmode)
2159 return patchbackend(ui, backend, patchobj, strip, prefix, files, eolmode)
2160
2160
2161 def patch(ui, repo, patchname, strip=1, prefix='', files=None, eolmode='strict',
2161 def patch(ui, repo, patchname, strip=1, prefix='', files=None, eolmode='strict',
2162 similarity=0):
2162 similarity=0):
2163 """Apply <patchname> to the working directory.
2163 """Apply <patchname> to the working directory.
2164
2164
2165 'eolmode' specifies how end of lines should be handled. It can be:
2165 'eolmode' specifies how end of lines should be handled. It can be:
2166 - 'strict': inputs are read in binary mode, EOLs are preserved
2166 - 'strict': inputs are read in binary mode, EOLs are preserved
2167 - 'crlf': EOLs are ignored when patching and reset to CRLF
2167 - 'crlf': EOLs are ignored when patching and reset to CRLF
2168 - 'lf': EOLs are ignored when patching and reset to LF
2168 - 'lf': EOLs are ignored when patching and reset to LF
2169 - None: get it from user settings, default to 'strict'
2169 - None: get it from user settings, default to 'strict'
2170 'eolmode' is ignored when using an external patcher program.
2170 'eolmode' is ignored when using an external patcher program.
2171
2171
2172 Returns whether patch was applied with fuzz factor.
2172 Returns whether patch was applied with fuzz factor.
2173 """
2173 """
2174 patcher = ui.config('ui', 'patch')
2174 patcher = ui.config('ui', 'patch')
2175 if files is None:
2175 if files is None:
2176 files = set()
2176 files = set()
2177 if patcher:
2177 if patcher:
2178 return _externalpatch(ui, repo, patcher, patchname, strip,
2178 return _externalpatch(ui, repo, patcher, patchname, strip,
2179 files, similarity)
2179 files, similarity)
2180 return internalpatch(ui, repo, patchname, strip, prefix, files, eolmode,
2180 return internalpatch(ui, repo, patchname, strip, prefix, files, eolmode,
2181 similarity)
2181 similarity)
2182
2182
2183 def changedfiles(ui, repo, patchpath, strip=1):
2183 def changedfiles(ui, repo, patchpath, strip=1):
2184 backend = fsbackend(ui, repo.root)
2184 backend = fsbackend(ui, repo.root)
2185 with open(patchpath, 'rb') as fp:
2185 with open(patchpath, 'rb') as fp:
2186 changed = set()
2186 changed = set()
2187 for state, values in iterhunks(fp):
2187 for state, values in iterhunks(fp):
2188 if state == 'file':
2188 if state == 'file':
2189 afile, bfile, first_hunk, gp = values
2189 afile, bfile, first_hunk, gp = values
2190 if gp:
2190 if gp:
2191 gp.path = pathtransform(gp.path, strip - 1, '')[1]
2191 gp.path = pathtransform(gp.path, strip - 1, '')[1]
2192 if gp.oldpath:
2192 if gp.oldpath:
2193 gp.oldpath = pathtransform(gp.oldpath, strip - 1, '')[1]
2193 gp.oldpath = pathtransform(gp.oldpath, strip - 1, '')[1]
2194 else:
2194 else:
2195 gp = makepatchmeta(backend, afile, bfile, first_hunk, strip,
2195 gp = makepatchmeta(backend, afile, bfile, first_hunk, strip,
2196 '')
2196 '')
2197 changed.add(gp.path)
2197 changed.add(gp.path)
2198 if gp.op == 'RENAME':
2198 if gp.op == 'RENAME':
2199 changed.add(gp.oldpath)
2199 changed.add(gp.oldpath)
2200 elif state not in ('hunk', 'git'):
2200 elif state not in ('hunk', 'git'):
2201 raise error.Abort(_('unsupported parser state: %s') % state)
2201 raise error.Abort(_('unsupported parser state: %s') % state)
2202 return changed
2202 return changed
2203
2203
2204 class GitDiffRequired(Exception):
2204 class GitDiffRequired(Exception):
2205 pass
2205 pass
2206
2206
2207 def diffallopts(ui, opts=None, untrusted=False, section='diff'):
2207 def diffallopts(ui, opts=None, untrusted=False, section='diff'):
2208 '''return diffopts with all features supported and parsed'''
2208 '''return diffopts with all features supported and parsed'''
2209 return difffeatureopts(ui, opts=opts, untrusted=untrusted, section=section,
2209 return difffeatureopts(ui, opts=opts, untrusted=untrusted, section=section,
2210 git=True, whitespace=True, formatchanging=True)
2210 git=True, whitespace=True, formatchanging=True)
2211
2211
2212 diffopts = diffallopts
2212 diffopts = diffallopts
2213
2213
2214 def difffeatureopts(ui, opts=None, untrusted=False, section='diff', git=False,
2214 def difffeatureopts(ui, opts=None, untrusted=False, section='diff', git=False,
2215 whitespace=False, formatchanging=False):
2215 whitespace=False, formatchanging=False):
2216 '''return diffopts with only opted-in features parsed
2216 '''return diffopts with only opted-in features parsed
2217
2217
2218 Features:
2218 Features:
2219 - git: git-style diffs
2219 - git: git-style diffs
2220 - whitespace: whitespace options like ignoreblanklines and ignorews
2220 - whitespace: whitespace options like ignoreblanklines and ignorews
2221 - formatchanging: options that will likely break or cause correctness issues
2221 - formatchanging: options that will likely break or cause correctness issues
2222 with most diff parsers
2222 with most diff parsers
2223 '''
2223 '''
2224 def get(key, name=None, getter=ui.configbool, forceplain=None):
2224 def get(key, name=None, getter=ui.configbool, forceplain=None):
2225 if opts:
2225 if opts:
2226 v = opts.get(key)
2226 v = opts.get(key)
2227 # diffopts flags are either None-default (which is passed
2227 # diffopts flags are either None-default (which is passed
2228 # through unchanged, so we can identify unset values), or
2228 # through unchanged, so we can identify unset values), or
2229 # some other falsey default (eg --unified, which defaults
2229 # some other falsey default (eg --unified, which defaults
2230 # to an empty string). We only want to override the config
2230 # to an empty string). We only want to override the config
2231 # entries from hgrc with command line values if they
2231 # entries from hgrc with command line values if they
2232 # appear to have been set, which is any truthy value,
2232 # appear to have been set, which is any truthy value,
2233 # True, or False.
2233 # True, or False.
2234 if v or isinstance(v, bool):
2234 if v or isinstance(v, bool):
2235 return v
2235 return v
2236 if forceplain is not None and ui.plain():
2236 if forceplain is not None and ui.plain():
2237 return forceplain
2237 return forceplain
2238 return getter(section, name or key, None, untrusted=untrusted)
2238 return getter(section, name or key, None, untrusted=untrusted)
2239
2239
2240 # core options, expected to be understood by every diff parser
2240 # core options, expected to be understood by every diff parser
2241 buildopts = {
2241 buildopts = {
2242 'nodates': get('nodates'),
2242 'nodates': get('nodates'),
2243 'showfunc': get('show_function', 'showfunc'),
2243 'showfunc': get('show_function', 'showfunc'),
2244 'context': get('unified', getter=ui.config),
2244 'context': get('unified', getter=ui.config),
2245 }
2245 }
2246
2246
2247 if git:
2247 if git:
2248 buildopts['git'] = get('git')
2248 buildopts['git'] = get('git')
2249
2249
2250 # since this is in the experimental section, we need to call
2250 # since this is in the experimental section, we need to call
2251 # ui.configbool directory
2251 # ui.configbool directory
2252 buildopts['showsimilarity'] = ui.configbool('experimental',
2252 buildopts['showsimilarity'] = ui.configbool('experimental',
2253 'extendedheader.similarity')
2253 'extendedheader.similarity')
2254
2254
2255 # need to inspect the ui object instead of using get() since we want to
2255 # need to inspect the ui object instead of using get() since we want to
2256 # test for an int
2256 # test for an int
2257 hconf = ui.config('experimental', 'extendedheader.index')
2257 hconf = ui.config('experimental', 'extendedheader.index')
2258 if hconf is not None:
2258 if hconf is not None:
2259 hlen = None
2259 hlen = None
2260 try:
2260 try:
2261 # the hash config could be an integer (for length of hash) or a
2261 # the hash config could be an integer (for length of hash) or a
2262 # word (e.g. short, full, none)
2262 # word (e.g. short, full, none)
2263 hlen = int(hconf)
2263 hlen = int(hconf)
2264 if hlen < 0 or hlen > 40:
2264 if hlen < 0 or hlen > 40:
2265 msg = _("invalid length for extendedheader.index: '%d'\n")
2265 msg = _("invalid length for extendedheader.index: '%d'\n")
2266 ui.warn(msg % hlen)
2266 ui.warn(msg % hlen)
2267 except ValueError:
2267 except ValueError:
2268 # default value
2268 # default value
2269 if hconf == 'short' or hconf == '':
2269 if hconf == 'short' or hconf == '':
2270 hlen = 12
2270 hlen = 12
2271 elif hconf == 'full':
2271 elif hconf == 'full':
2272 hlen = 40
2272 hlen = 40
2273 elif hconf != 'none':
2273 elif hconf != 'none':
2274 msg = _("invalid value for extendedheader.index: '%s'\n")
2274 msg = _("invalid value for extendedheader.index: '%s'\n")
2275 ui.warn(msg % hconf)
2275 ui.warn(msg % hconf)
2276 finally:
2276 finally:
2277 buildopts['index'] = hlen
2277 buildopts['index'] = hlen
2278
2278
2279 if whitespace:
2279 if whitespace:
2280 buildopts['ignorews'] = get('ignore_all_space', 'ignorews')
2280 buildopts['ignorews'] = get('ignore_all_space', 'ignorews')
2281 buildopts['ignorewsamount'] = get('ignore_space_change',
2281 buildopts['ignorewsamount'] = get('ignore_space_change',
2282 'ignorewsamount')
2282 'ignorewsamount')
2283 buildopts['ignoreblanklines'] = get('ignore_blank_lines',
2283 buildopts['ignoreblanklines'] = get('ignore_blank_lines',
2284 'ignoreblanklines')
2284 'ignoreblanklines')
2285 buildopts['ignorewseol'] = get('ignore_space_at_eol', 'ignorewseol')
2285 if formatchanging:
2286 if formatchanging:
2286 buildopts['text'] = opts and opts.get('text')
2287 buildopts['text'] = opts and opts.get('text')
2287 binary = None if opts is None else opts.get('binary')
2288 binary = None if opts is None else opts.get('binary')
2288 buildopts['nobinary'] = (not binary if binary is not None
2289 buildopts['nobinary'] = (not binary if binary is not None
2289 else get('nobinary', forceplain=False))
2290 else get('nobinary', forceplain=False))
2290 buildopts['noprefix'] = get('noprefix', forceplain=False)
2291 buildopts['noprefix'] = get('noprefix', forceplain=False)
2291
2292
2292 return mdiff.diffopts(**pycompat.strkwargs(buildopts))
2293 return mdiff.diffopts(**pycompat.strkwargs(buildopts))
2293
2294
2294 def diff(repo, node1=None, node2=None, match=None, changes=None,
2295 def diff(repo, node1=None, node2=None, match=None, changes=None,
2295 opts=None, losedatafn=None, prefix='', relroot='', copy=None):
2296 opts=None, losedatafn=None, prefix='', relroot='', copy=None):
2296 '''yields diff of changes to files between two nodes, or node and
2297 '''yields diff of changes to files between two nodes, or node and
2297 working directory.
2298 working directory.
2298
2299
2299 if node1 is None, use first dirstate parent instead.
2300 if node1 is None, use first dirstate parent instead.
2300 if node2 is None, compare node1 with working directory.
2301 if node2 is None, compare node1 with working directory.
2301
2302
2302 losedatafn(**kwarg) is a callable run when opts.upgrade=True and
2303 losedatafn(**kwarg) is a callable run when opts.upgrade=True and
2303 every time some change cannot be represented with the current
2304 every time some change cannot be represented with the current
2304 patch format. Return False to upgrade to git patch format, True to
2305 patch format. Return False to upgrade to git patch format, True to
2305 accept the loss or raise an exception to abort the diff. It is
2306 accept the loss or raise an exception to abort the diff. It is
2306 called with the name of current file being diffed as 'fn'. If set
2307 called with the name of current file being diffed as 'fn'. If set
2307 to None, patches will always be upgraded to git format when
2308 to None, patches will always be upgraded to git format when
2308 necessary.
2309 necessary.
2309
2310
2310 prefix is a filename prefix that is prepended to all filenames on
2311 prefix is a filename prefix that is prepended to all filenames on
2311 display (used for subrepos).
2312 display (used for subrepos).
2312
2313
2313 relroot, if not empty, must be normalized with a trailing /. Any match
2314 relroot, if not empty, must be normalized with a trailing /. Any match
2314 patterns that fall outside it will be ignored.
2315 patterns that fall outside it will be ignored.
2315
2316
2316 copy, if not empty, should contain mappings {dst@y: src@x} of copy
2317 copy, if not empty, should contain mappings {dst@y: src@x} of copy
2317 information.'''
2318 information.'''
2318 for header, hunks in diffhunks(repo, node1=node1, node2=node2, match=match,
2319 for header, hunks in diffhunks(repo, node1=node1, node2=node2, match=match,
2319 changes=changes, opts=opts,
2320 changes=changes, opts=opts,
2320 losedatafn=losedatafn, prefix=prefix,
2321 losedatafn=losedatafn, prefix=prefix,
2321 relroot=relroot, copy=copy):
2322 relroot=relroot, copy=copy):
2322 text = ''.join(sum((list(hlines) for hrange, hlines in hunks), []))
2323 text = ''.join(sum((list(hlines) for hrange, hlines in hunks), []))
2323 if header and (text or len(header) > 1):
2324 if header and (text or len(header) > 1):
2324 yield '\n'.join(header) + '\n'
2325 yield '\n'.join(header) + '\n'
2325 if text:
2326 if text:
2326 yield text
2327 yield text
2327
2328
2328 def diffhunks(repo, node1=None, node2=None, match=None, changes=None,
2329 def diffhunks(repo, node1=None, node2=None, match=None, changes=None,
2329 opts=None, losedatafn=None, prefix='', relroot='', copy=None):
2330 opts=None, losedatafn=None, prefix='', relroot='', copy=None):
2330 """Yield diff of changes to files in the form of (`header`, `hunks`) tuples
2331 """Yield diff of changes to files in the form of (`header`, `hunks`) tuples
2331 where `header` is a list of diff headers and `hunks` is an iterable of
2332 where `header` is a list of diff headers and `hunks` is an iterable of
2332 (`hunkrange`, `hunklines`) tuples.
2333 (`hunkrange`, `hunklines`) tuples.
2333
2334
2334 See diff() for the meaning of parameters.
2335 See diff() for the meaning of parameters.
2335 """
2336 """
2336
2337
2337 if opts is None:
2338 if opts is None:
2338 opts = mdiff.defaultopts
2339 opts = mdiff.defaultopts
2339
2340
2340 if not node1 and not node2:
2341 if not node1 and not node2:
2341 node1 = repo.dirstate.p1()
2342 node1 = repo.dirstate.p1()
2342
2343
2343 def lrugetfilectx():
2344 def lrugetfilectx():
2344 cache = {}
2345 cache = {}
2345 order = collections.deque()
2346 order = collections.deque()
2346 def getfilectx(f, ctx):
2347 def getfilectx(f, ctx):
2347 fctx = ctx.filectx(f, filelog=cache.get(f))
2348 fctx = ctx.filectx(f, filelog=cache.get(f))
2348 if f not in cache:
2349 if f not in cache:
2349 if len(cache) > 20:
2350 if len(cache) > 20:
2350 del cache[order.popleft()]
2351 del cache[order.popleft()]
2351 cache[f] = fctx.filelog()
2352 cache[f] = fctx.filelog()
2352 else:
2353 else:
2353 order.remove(f)
2354 order.remove(f)
2354 order.append(f)
2355 order.append(f)
2355 return fctx
2356 return fctx
2356 return getfilectx
2357 return getfilectx
2357 getfilectx = lrugetfilectx()
2358 getfilectx = lrugetfilectx()
2358
2359
2359 ctx1 = repo[node1]
2360 ctx1 = repo[node1]
2360 ctx2 = repo[node2]
2361 ctx2 = repo[node2]
2361
2362
2362 relfiltered = False
2363 relfiltered = False
2363 if relroot != '' and match.always():
2364 if relroot != '' and match.always():
2364 # as a special case, create a new matcher with just the relroot
2365 # as a special case, create a new matcher with just the relroot
2365 pats = [relroot]
2366 pats = [relroot]
2366 match = scmutil.match(ctx2, pats, default='path')
2367 match = scmutil.match(ctx2, pats, default='path')
2367 relfiltered = True
2368 relfiltered = True
2368
2369
2369 if not changes:
2370 if not changes:
2370 changes = repo.status(ctx1, ctx2, match=match)
2371 changes = repo.status(ctx1, ctx2, match=match)
2371 modified, added, removed = changes[:3]
2372 modified, added, removed = changes[:3]
2372
2373
2373 if not modified and not added and not removed:
2374 if not modified and not added and not removed:
2374 return []
2375 return []
2375
2376
2376 if repo.ui.debugflag:
2377 if repo.ui.debugflag:
2377 hexfunc = hex
2378 hexfunc = hex
2378 else:
2379 else:
2379 hexfunc = short
2380 hexfunc = short
2380 revs = [hexfunc(node) for node in [ctx1.node(), ctx2.node()] if node]
2381 revs = [hexfunc(node) for node in [ctx1.node(), ctx2.node()] if node]
2381
2382
2382 if copy is None:
2383 if copy is None:
2383 copy = {}
2384 copy = {}
2384 if opts.git or opts.upgrade:
2385 if opts.git or opts.upgrade:
2385 copy = copies.pathcopies(ctx1, ctx2, match=match)
2386 copy = copies.pathcopies(ctx1, ctx2, match=match)
2386
2387
2387 if relroot is not None:
2388 if relroot is not None:
2388 if not relfiltered:
2389 if not relfiltered:
2389 # XXX this would ideally be done in the matcher, but that is
2390 # XXX this would ideally be done in the matcher, but that is
2390 # generally meant to 'or' patterns, not 'and' them. In this case we
2391 # generally meant to 'or' patterns, not 'and' them. In this case we
2391 # need to 'and' all the patterns from the matcher with relroot.
2392 # need to 'and' all the patterns from the matcher with relroot.
2392 def filterrel(l):
2393 def filterrel(l):
2393 return [f for f in l if f.startswith(relroot)]
2394 return [f for f in l if f.startswith(relroot)]
2394 modified = filterrel(modified)
2395 modified = filterrel(modified)
2395 added = filterrel(added)
2396 added = filterrel(added)
2396 removed = filterrel(removed)
2397 removed = filterrel(removed)
2397 relfiltered = True
2398 relfiltered = True
2398 # filter out copies where either side isn't inside the relative root
2399 # filter out copies where either side isn't inside the relative root
2399 copy = dict(((dst, src) for (dst, src) in copy.iteritems()
2400 copy = dict(((dst, src) for (dst, src) in copy.iteritems()
2400 if dst.startswith(relroot)
2401 if dst.startswith(relroot)
2401 and src.startswith(relroot)))
2402 and src.startswith(relroot)))
2402
2403
2403 modifiedset = set(modified)
2404 modifiedset = set(modified)
2404 addedset = set(added)
2405 addedset = set(added)
2405 removedset = set(removed)
2406 removedset = set(removed)
2406 for f in modified:
2407 for f in modified:
2407 if f not in ctx1:
2408 if f not in ctx1:
2408 # Fix up added, since merged-in additions appear as
2409 # Fix up added, since merged-in additions appear as
2409 # modifications during merges
2410 # modifications during merges
2410 modifiedset.remove(f)
2411 modifiedset.remove(f)
2411 addedset.add(f)
2412 addedset.add(f)
2412 for f in removed:
2413 for f in removed:
2413 if f not in ctx1:
2414 if f not in ctx1:
2414 # Merged-in additions that are then removed are reported as removed.
2415 # Merged-in additions that are then removed are reported as removed.
2415 # They are not in ctx1, so We don't want to show them in the diff.
2416 # They are not in ctx1, so We don't want to show them in the diff.
2416 removedset.remove(f)
2417 removedset.remove(f)
2417 modified = sorted(modifiedset)
2418 modified = sorted(modifiedset)
2418 added = sorted(addedset)
2419 added = sorted(addedset)
2419 removed = sorted(removedset)
2420 removed = sorted(removedset)
2420 for dst, src in copy.items():
2421 for dst, src in copy.items():
2421 if src not in ctx1:
2422 if src not in ctx1:
2422 # Files merged in during a merge and then copied/renamed are
2423 # Files merged in during a merge and then copied/renamed are
2423 # reported as copies. We want to show them in the diff as additions.
2424 # reported as copies. We want to show them in the diff as additions.
2424 del copy[dst]
2425 del copy[dst]
2425
2426
2426 def difffn(opts, losedata):
2427 def difffn(opts, losedata):
2427 return trydiff(repo, revs, ctx1, ctx2, modified, added, removed,
2428 return trydiff(repo, revs, ctx1, ctx2, modified, added, removed,
2428 copy, getfilectx, opts, losedata, prefix, relroot)
2429 copy, getfilectx, opts, losedata, prefix, relroot)
2429 if opts.upgrade and not opts.git:
2430 if opts.upgrade and not opts.git:
2430 try:
2431 try:
2431 def losedata(fn):
2432 def losedata(fn):
2432 if not losedatafn or not losedatafn(fn=fn):
2433 if not losedatafn or not losedatafn(fn=fn):
2433 raise GitDiffRequired
2434 raise GitDiffRequired
2434 # Buffer the whole output until we are sure it can be generated
2435 # Buffer the whole output until we are sure it can be generated
2435 return list(difffn(opts.copy(git=False), losedata))
2436 return list(difffn(opts.copy(git=False), losedata))
2436 except GitDiffRequired:
2437 except GitDiffRequired:
2437 return difffn(opts.copy(git=True), None)
2438 return difffn(opts.copy(git=True), None)
2438 else:
2439 else:
2439 return difffn(opts, None)
2440 return difffn(opts, None)
2440
2441
2441 def difflabel(func, *args, **kw):
2442 def difflabel(func, *args, **kw):
2442 '''yields 2-tuples of (output, label) based on the output of func()'''
2443 '''yields 2-tuples of (output, label) based on the output of func()'''
2443 headprefixes = [('diff', 'diff.diffline'),
2444 headprefixes = [('diff', 'diff.diffline'),
2444 ('copy', 'diff.extended'),
2445 ('copy', 'diff.extended'),
2445 ('rename', 'diff.extended'),
2446 ('rename', 'diff.extended'),
2446 ('old', 'diff.extended'),
2447 ('old', 'diff.extended'),
2447 ('new', 'diff.extended'),
2448 ('new', 'diff.extended'),
2448 ('deleted', 'diff.extended'),
2449 ('deleted', 'diff.extended'),
2449 ('index', 'diff.extended'),
2450 ('index', 'diff.extended'),
2450 ('similarity', 'diff.extended'),
2451 ('similarity', 'diff.extended'),
2451 ('---', 'diff.file_a'),
2452 ('---', 'diff.file_a'),
2452 ('+++', 'diff.file_b')]
2453 ('+++', 'diff.file_b')]
2453 textprefixes = [('@', 'diff.hunk'),
2454 textprefixes = [('@', 'diff.hunk'),
2454 ('-', 'diff.deleted'),
2455 ('-', 'diff.deleted'),
2455 ('+', 'diff.inserted')]
2456 ('+', 'diff.inserted')]
2456 head = False
2457 head = False
2457 for chunk in func(*args, **kw):
2458 for chunk in func(*args, **kw):
2458 lines = chunk.split('\n')
2459 lines = chunk.split('\n')
2459 for i, line in enumerate(lines):
2460 for i, line in enumerate(lines):
2460 if i != 0:
2461 if i != 0:
2461 yield ('\n', '')
2462 yield ('\n', '')
2462 if head:
2463 if head:
2463 if line.startswith('@'):
2464 if line.startswith('@'):
2464 head = False
2465 head = False
2465 else:
2466 else:
2466 if line and line[0] not in ' +-@\\':
2467 if line and line[0] not in ' +-@\\':
2467 head = True
2468 head = True
2468 stripline = line
2469 stripline = line
2469 diffline = False
2470 diffline = False
2470 if not head and line and line[0] in '+-':
2471 if not head and line and line[0] in '+-':
2471 # highlight tabs and trailing whitespace, but only in
2472 # highlight tabs and trailing whitespace, but only in
2472 # changed lines
2473 # changed lines
2473 stripline = line.rstrip()
2474 stripline = line.rstrip()
2474 diffline = True
2475 diffline = True
2475
2476
2476 prefixes = textprefixes
2477 prefixes = textprefixes
2477 if head:
2478 if head:
2478 prefixes = headprefixes
2479 prefixes = headprefixes
2479 for prefix, label in prefixes:
2480 for prefix, label in prefixes:
2480 if stripline.startswith(prefix):
2481 if stripline.startswith(prefix):
2481 if diffline:
2482 if diffline:
2482 for token in tabsplitter.findall(stripline):
2483 for token in tabsplitter.findall(stripline):
2483 if '\t' == token[0]:
2484 if '\t' == token[0]:
2484 yield (token, 'diff.tab')
2485 yield (token, 'diff.tab')
2485 else:
2486 else:
2486 yield (token, label)
2487 yield (token, label)
2487 else:
2488 else:
2488 yield (stripline, label)
2489 yield (stripline, label)
2489 break
2490 break
2490 else:
2491 else:
2491 yield (line, '')
2492 yield (line, '')
2492 if line != stripline:
2493 if line != stripline:
2493 yield (line[len(stripline):], 'diff.trailingwhitespace')
2494 yield (line[len(stripline):], 'diff.trailingwhitespace')
2494
2495
2495 def diffui(*args, **kw):
2496 def diffui(*args, **kw):
2496 '''like diff(), but yields 2-tuples of (output, label) for ui.write()'''
2497 '''like diff(), but yields 2-tuples of (output, label) for ui.write()'''
2497 return difflabel(diff, *args, **kw)
2498 return difflabel(diff, *args, **kw)
2498
2499
2499 def _filepairs(modified, added, removed, copy, opts):
2500 def _filepairs(modified, added, removed, copy, opts):
2500 '''generates tuples (f1, f2, copyop), where f1 is the name of the file
2501 '''generates tuples (f1, f2, copyop), where f1 is the name of the file
2501 before and f2 is the the name after. For added files, f1 will be None,
2502 before and f2 is the the name after. For added files, f1 will be None,
2502 and for removed files, f2 will be None. copyop may be set to None, 'copy'
2503 and for removed files, f2 will be None. copyop may be set to None, 'copy'
2503 or 'rename' (the latter two only if opts.git is set).'''
2504 or 'rename' (the latter two only if opts.git is set).'''
2504 gone = set()
2505 gone = set()
2505
2506
2506 copyto = dict([(v, k) for k, v in copy.items()])
2507 copyto = dict([(v, k) for k, v in copy.items()])
2507
2508
2508 addedset, removedset = set(added), set(removed)
2509 addedset, removedset = set(added), set(removed)
2509
2510
2510 for f in sorted(modified + added + removed):
2511 for f in sorted(modified + added + removed):
2511 copyop = None
2512 copyop = None
2512 f1, f2 = f, f
2513 f1, f2 = f, f
2513 if f in addedset:
2514 if f in addedset:
2514 f1 = None
2515 f1 = None
2515 if f in copy:
2516 if f in copy:
2516 if opts.git:
2517 if opts.git:
2517 f1 = copy[f]
2518 f1 = copy[f]
2518 if f1 in removedset and f1 not in gone:
2519 if f1 in removedset and f1 not in gone:
2519 copyop = 'rename'
2520 copyop = 'rename'
2520 gone.add(f1)
2521 gone.add(f1)
2521 else:
2522 else:
2522 copyop = 'copy'
2523 copyop = 'copy'
2523 elif f in removedset:
2524 elif f in removedset:
2524 f2 = None
2525 f2 = None
2525 if opts.git:
2526 if opts.git:
2526 # have we already reported a copy above?
2527 # have we already reported a copy above?
2527 if (f in copyto and copyto[f] in addedset
2528 if (f in copyto and copyto[f] in addedset
2528 and copy[copyto[f]] == f):
2529 and copy[copyto[f]] == f):
2529 continue
2530 continue
2530 yield f1, f2, copyop
2531 yield f1, f2, copyop
2531
2532
2532 def trydiff(repo, revs, ctx1, ctx2, modified, added, removed,
2533 def trydiff(repo, revs, ctx1, ctx2, modified, added, removed,
2533 copy, getfilectx, opts, losedatafn, prefix, relroot):
2534 copy, getfilectx, opts, losedatafn, prefix, relroot):
2534 '''given input data, generate a diff and yield it in blocks
2535 '''given input data, generate a diff and yield it in blocks
2535
2536
2536 If generating a diff would lose data like flags or binary data and
2537 If generating a diff would lose data like flags or binary data and
2537 losedatafn is not None, it will be called.
2538 losedatafn is not None, it will be called.
2538
2539
2539 relroot is removed and prefix is added to every path in the diff output.
2540 relroot is removed and prefix is added to every path in the diff output.
2540
2541
2541 If relroot is not empty, this function expects every path in modified,
2542 If relroot is not empty, this function expects every path in modified,
2542 added, removed and copy to start with it.'''
2543 added, removed and copy to start with it.'''
2543
2544
2544 def gitindex(text):
2545 def gitindex(text):
2545 if not text:
2546 if not text:
2546 text = ""
2547 text = ""
2547 l = len(text)
2548 l = len(text)
2548 s = hashlib.sha1('blob %d\0' % l)
2549 s = hashlib.sha1('blob %d\0' % l)
2549 s.update(text)
2550 s.update(text)
2550 return s.hexdigest()
2551 return s.hexdigest()
2551
2552
2552 if opts.noprefix:
2553 if opts.noprefix:
2553 aprefix = bprefix = ''
2554 aprefix = bprefix = ''
2554 else:
2555 else:
2555 aprefix = 'a/'
2556 aprefix = 'a/'
2556 bprefix = 'b/'
2557 bprefix = 'b/'
2557
2558
2558 def diffline(f, revs):
2559 def diffline(f, revs):
2559 revinfo = ' '.join(["-r %s" % rev for rev in revs])
2560 revinfo = ' '.join(["-r %s" % rev for rev in revs])
2560 return 'diff %s %s' % (revinfo, f)
2561 return 'diff %s %s' % (revinfo, f)
2561
2562
2562 def isempty(fctx):
2563 def isempty(fctx):
2563 return fctx is None or fctx.size() == 0
2564 return fctx is None or fctx.size() == 0
2564
2565
2565 date1 = util.datestr(ctx1.date())
2566 date1 = util.datestr(ctx1.date())
2566 date2 = util.datestr(ctx2.date())
2567 date2 = util.datestr(ctx2.date())
2567
2568
2568 gitmode = {'l': '120000', 'x': '100755', '': '100644'}
2569 gitmode = {'l': '120000', 'x': '100755', '': '100644'}
2569
2570
2570 if relroot != '' and (repo.ui.configbool('devel', 'all-warnings')
2571 if relroot != '' and (repo.ui.configbool('devel', 'all-warnings')
2571 or repo.ui.configbool('devel', 'check-relroot')):
2572 or repo.ui.configbool('devel', 'check-relroot')):
2572 for f in modified + added + removed + list(copy) + list(copy.values()):
2573 for f in modified + added + removed + list(copy) + list(copy.values()):
2573 if f is not None and not f.startswith(relroot):
2574 if f is not None and not f.startswith(relroot):
2574 raise AssertionError(
2575 raise AssertionError(
2575 "file %s doesn't start with relroot %s" % (f, relroot))
2576 "file %s doesn't start with relroot %s" % (f, relroot))
2576
2577
2577 for f1, f2, copyop in _filepairs(modified, added, removed, copy, opts):
2578 for f1, f2, copyop in _filepairs(modified, added, removed, copy, opts):
2578 content1 = None
2579 content1 = None
2579 content2 = None
2580 content2 = None
2580 fctx1 = None
2581 fctx1 = None
2581 fctx2 = None
2582 fctx2 = None
2582 flag1 = None
2583 flag1 = None
2583 flag2 = None
2584 flag2 = None
2584 if f1:
2585 if f1:
2585 fctx1 = getfilectx(f1, ctx1)
2586 fctx1 = getfilectx(f1, ctx1)
2586 if opts.git or losedatafn:
2587 if opts.git or losedatafn:
2587 flag1 = ctx1.flags(f1)
2588 flag1 = ctx1.flags(f1)
2588 if f2:
2589 if f2:
2589 fctx2 = getfilectx(f2, ctx2)
2590 fctx2 = getfilectx(f2, ctx2)
2590 if opts.git or losedatafn:
2591 if opts.git or losedatafn:
2591 flag2 = ctx2.flags(f2)
2592 flag2 = ctx2.flags(f2)
2592 # if binary is True, output "summary" or "base85", but not "text diff"
2593 # if binary is True, output "summary" or "base85", but not "text diff"
2593 binary = not opts.text and any(f.isbinary()
2594 binary = not opts.text and any(f.isbinary()
2594 for f in [fctx1, fctx2] if f is not None)
2595 for f in [fctx1, fctx2] if f is not None)
2595
2596
2596 if losedatafn and not opts.git:
2597 if losedatafn and not opts.git:
2597 if (binary or
2598 if (binary or
2598 # copy/rename
2599 # copy/rename
2599 f2 in copy or
2600 f2 in copy or
2600 # empty file creation
2601 # empty file creation
2601 (not f1 and isempty(fctx2)) or
2602 (not f1 and isempty(fctx2)) or
2602 # empty file deletion
2603 # empty file deletion
2603 (isempty(fctx1) and not f2) or
2604 (isempty(fctx1) and not f2) or
2604 # create with flags
2605 # create with flags
2605 (not f1 and flag2) or
2606 (not f1 and flag2) or
2606 # change flags
2607 # change flags
2607 (f1 and f2 and flag1 != flag2)):
2608 (f1 and f2 and flag1 != flag2)):
2608 losedatafn(f2 or f1)
2609 losedatafn(f2 or f1)
2609
2610
2610 path1 = f1 or f2
2611 path1 = f1 or f2
2611 path2 = f2 or f1
2612 path2 = f2 or f1
2612 path1 = posixpath.join(prefix, path1[len(relroot):])
2613 path1 = posixpath.join(prefix, path1[len(relroot):])
2613 path2 = posixpath.join(prefix, path2[len(relroot):])
2614 path2 = posixpath.join(prefix, path2[len(relroot):])
2614 header = []
2615 header = []
2615 if opts.git:
2616 if opts.git:
2616 header.append('diff --git %s%s %s%s' %
2617 header.append('diff --git %s%s %s%s' %
2617 (aprefix, path1, bprefix, path2))
2618 (aprefix, path1, bprefix, path2))
2618 if not f1: # added
2619 if not f1: # added
2619 header.append('new file mode %s' % gitmode[flag2])
2620 header.append('new file mode %s' % gitmode[flag2])
2620 elif not f2: # removed
2621 elif not f2: # removed
2621 header.append('deleted file mode %s' % gitmode[flag1])
2622 header.append('deleted file mode %s' % gitmode[flag1])
2622 else: # modified/copied/renamed
2623 else: # modified/copied/renamed
2623 mode1, mode2 = gitmode[flag1], gitmode[flag2]
2624 mode1, mode2 = gitmode[flag1], gitmode[flag2]
2624 if mode1 != mode2:
2625 if mode1 != mode2:
2625 header.append('old mode %s' % mode1)
2626 header.append('old mode %s' % mode1)
2626 header.append('new mode %s' % mode2)
2627 header.append('new mode %s' % mode2)
2627 if copyop is not None:
2628 if copyop is not None:
2628 if opts.showsimilarity:
2629 if opts.showsimilarity:
2629 sim = similar.score(ctx1[path1], ctx2[path2]) * 100
2630 sim = similar.score(ctx1[path1], ctx2[path2]) * 100
2630 header.append('similarity index %d%%' % sim)
2631 header.append('similarity index %d%%' % sim)
2631 header.append('%s from %s' % (copyop, path1))
2632 header.append('%s from %s' % (copyop, path1))
2632 header.append('%s to %s' % (copyop, path2))
2633 header.append('%s to %s' % (copyop, path2))
2633 elif revs and not repo.ui.quiet:
2634 elif revs and not repo.ui.quiet:
2634 header.append(diffline(path1, revs))
2635 header.append(diffline(path1, revs))
2635
2636
2636 # fctx.is | diffopts | what to | is fctx.data()
2637 # fctx.is | diffopts | what to | is fctx.data()
2637 # binary() | text nobinary git index | output? | outputted?
2638 # binary() | text nobinary git index | output? | outputted?
2638 # ------------------------------------|----------------------------
2639 # ------------------------------------|----------------------------
2639 # yes | no no no * | summary | no
2640 # yes | no no no * | summary | no
2640 # yes | no no yes * | base85 | yes
2641 # yes | no no yes * | base85 | yes
2641 # yes | no yes no * | summary | no
2642 # yes | no yes no * | summary | no
2642 # yes | no yes yes 0 | summary | no
2643 # yes | no yes yes 0 | summary | no
2643 # yes | no yes yes >0 | summary | semi [1]
2644 # yes | no yes yes >0 | summary | semi [1]
2644 # yes | yes * * * | text diff | yes
2645 # yes | yes * * * | text diff | yes
2645 # no | * * * * | text diff | yes
2646 # no | * * * * | text diff | yes
2646 # [1]: hash(fctx.data()) is outputted. so fctx.data() cannot be faked
2647 # [1]: hash(fctx.data()) is outputted. so fctx.data() cannot be faked
2647 if binary and (not opts.git or (opts.git and opts.nobinary and not
2648 if binary and (not opts.git or (opts.git and opts.nobinary and not
2648 opts.index)):
2649 opts.index)):
2649 # fast path: no binary content will be displayed, content1 and
2650 # fast path: no binary content will be displayed, content1 and
2650 # content2 are only used for equivalent test. cmp() could have a
2651 # content2 are only used for equivalent test. cmp() could have a
2651 # fast path.
2652 # fast path.
2652 if fctx1 is not None:
2653 if fctx1 is not None:
2653 content1 = b'\0'
2654 content1 = b'\0'
2654 if fctx2 is not None:
2655 if fctx2 is not None:
2655 if fctx1 is not None and not fctx1.cmp(fctx2):
2656 if fctx1 is not None and not fctx1.cmp(fctx2):
2656 content2 = b'\0' # not different
2657 content2 = b'\0' # not different
2657 else:
2658 else:
2658 content2 = b'\0\0'
2659 content2 = b'\0\0'
2659 else:
2660 else:
2660 # normal path: load contents
2661 # normal path: load contents
2661 if fctx1 is not None:
2662 if fctx1 is not None:
2662 content1 = fctx1.data()
2663 content1 = fctx1.data()
2663 if fctx2 is not None:
2664 if fctx2 is not None:
2664 content2 = fctx2.data()
2665 content2 = fctx2.data()
2665
2666
2666 if binary and opts.git and not opts.nobinary:
2667 if binary and opts.git and not opts.nobinary:
2667 text = mdiff.b85diff(content1, content2)
2668 text = mdiff.b85diff(content1, content2)
2668 if text:
2669 if text:
2669 header.append('index %s..%s' %
2670 header.append('index %s..%s' %
2670 (gitindex(content1), gitindex(content2)))
2671 (gitindex(content1), gitindex(content2)))
2671 hunks = (None, [text]),
2672 hunks = (None, [text]),
2672 else:
2673 else:
2673 if opts.git and opts.index > 0:
2674 if opts.git and opts.index > 0:
2674 flag = flag1
2675 flag = flag1
2675 if flag is None:
2676 if flag is None:
2676 flag = flag2
2677 flag = flag2
2677 header.append('index %s..%s %s' %
2678 header.append('index %s..%s %s' %
2678 (gitindex(content1)[0:opts.index],
2679 (gitindex(content1)[0:opts.index],
2679 gitindex(content2)[0:opts.index],
2680 gitindex(content2)[0:opts.index],
2680 gitmode[flag]))
2681 gitmode[flag]))
2681
2682
2682 uheaders, hunks = mdiff.unidiff(content1, date1,
2683 uheaders, hunks = mdiff.unidiff(content1, date1,
2683 content2, date2,
2684 content2, date2,
2684 path1, path2, opts=opts)
2685 path1, path2, opts=opts)
2685 header.extend(uheaders)
2686 header.extend(uheaders)
2686 yield header, hunks
2687 yield header, hunks
2687
2688
2688 def diffstatsum(stats):
2689 def diffstatsum(stats):
2689 maxfile, maxtotal, addtotal, removetotal, binary = 0, 0, 0, 0, False
2690 maxfile, maxtotal, addtotal, removetotal, binary = 0, 0, 0, 0, False
2690 for f, a, r, b in stats:
2691 for f, a, r, b in stats:
2691 maxfile = max(maxfile, encoding.colwidth(f))
2692 maxfile = max(maxfile, encoding.colwidth(f))
2692 maxtotal = max(maxtotal, a + r)
2693 maxtotal = max(maxtotal, a + r)
2693 addtotal += a
2694 addtotal += a
2694 removetotal += r
2695 removetotal += r
2695 binary = binary or b
2696 binary = binary or b
2696
2697
2697 return maxfile, maxtotal, addtotal, removetotal, binary
2698 return maxfile, maxtotal, addtotal, removetotal, binary
2698
2699
2699 def diffstatdata(lines):
2700 def diffstatdata(lines):
2700 diffre = re.compile('^diff .*-r [a-z0-9]+\s(.*)$')
2701 diffre = re.compile('^diff .*-r [a-z0-9]+\s(.*)$')
2701
2702
2702 results = []
2703 results = []
2703 filename, adds, removes, isbinary = None, 0, 0, False
2704 filename, adds, removes, isbinary = None, 0, 0, False
2704
2705
2705 def addresult():
2706 def addresult():
2706 if filename:
2707 if filename:
2707 results.append((filename, adds, removes, isbinary))
2708 results.append((filename, adds, removes, isbinary))
2708
2709
2709 # inheader is used to track if a line is in the
2710 # inheader is used to track if a line is in the
2710 # header portion of the diff. This helps properly account
2711 # header portion of the diff. This helps properly account
2711 # for lines that start with '--' or '++'
2712 # for lines that start with '--' or '++'
2712 inheader = False
2713 inheader = False
2713
2714
2714 for line in lines:
2715 for line in lines:
2715 if line.startswith('diff'):
2716 if line.startswith('diff'):
2716 addresult()
2717 addresult()
2717 # starting a new file diff
2718 # starting a new file diff
2718 # set numbers to 0 and reset inheader
2719 # set numbers to 0 and reset inheader
2719 inheader = True
2720 inheader = True
2720 adds, removes, isbinary = 0, 0, False
2721 adds, removes, isbinary = 0, 0, False
2721 if line.startswith('diff --git a/'):
2722 if line.startswith('diff --git a/'):
2722 filename = gitre.search(line).group(2)
2723 filename = gitre.search(line).group(2)
2723 elif line.startswith('diff -r'):
2724 elif line.startswith('diff -r'):
2724 # format: "diff -r ... -r ... filename"
2725 # format: "diff -r ... -r ... filename"
2725 filename = diffre.search(line).group(1)
2726 filename = diffre.search(line).group(1)
2726 elif line.startswith('@@'):
2727 elif line.startswith('@@'):
2727 inheader = False
2728 inheader = False
2728 elif line.startswith('+') and not inheader:
2729 elif line.startswith('+') and not inheader:
2729 adds += 1
2730 adds += 1
2730 elif line.startswith('-') and not inheader:
2731 elif line.startswith('-') and not inheader:
2731 removes += 1
2732 removes += 1
2732 elif (line.startswith('GIT binary patch') or
2733 elif (line.startswith('GIT binary patch') or
2733 line.startswith('Binary file')):
2734 line.startswith('Binary file')):
2734 isbinary = True
2735 isbinary = True
2735 addresult()
2736 addresult()
2736 return results
2737 return results
2737
2738
2738 def diffstat(lines, width=80):
2739 def diffstat(lines, width=80):
2739 output = []
2740 output = []
2740 stats = diffstatdata(lines)
2741 stats = diffstatdata(lines)
2741 maxname, maxtotal, totaladds, totalremoves, hasbinary = diffstatsum(stats)
2742 maxname, maxtotal, totaladds, totalremoves, hasbinary = diffstatsum(stats)
2742
2743
2743 countwidth = len(str(maxtotal))
2744 countwidth = len(str(maxtotal))
2744 if hasbinary and countwidth < 3:
2745 if hasbinary and countwidth < 3:
2745 countwidth = 3
2746 countwidth = 3
2746 graphwidth = width - countwidth - maxname - 6
2747 graphwidth = width - countwidth - maxname - 6
2747 if graphwidth < 10:
2748 if graphwidth < 10:
2748 graphwidth = 10
2749 graphwidth = 10
2749
2750
2750 def scale(i):
2751 def scale(i):
2751 if maxtotal <= graphwidth:
2752 if maxtotal <= graphwidth:
2752 return i
2753 return i
2753 # If diffstat runs out of room it doesn't print anything,
2754 # If diffstat runs out of room it doesn't print anything,
2754 # which isn't very useful, so always print at least one + or -
2755 # which isn't very useful, so always print at least one + or -
2755 # if there were at least some changes.
2756 # if there were at least some changes.
2756 return max(i * graphwidth // maxtotal, int(bool(i)))
2757 return max(i * graphwidth // maxtotal, int(bool(i)))
2757
2758
2758 for filename, adds, removes, isbinary in stats:
2759 for filename, adds, removes, isbinary in stats:
2759 if isbinary:
2760 if isbinary:
2760 count = 'Bin'
2761 count = 'Bin'
2761 else:
2762 else:
2762 count = '%d' % (adds + removes)
2763 count = '%d' % (adds + removes)
2763 pluses = '+' * scale(adds)
2764 pluses = '+' * scale(adds)
2764 minuses = '-' * scale(removes)
2765 minuses = '-' * scale(removes)
2765 output.append(' %s%s | %*s %s%s\n' %
2766 output.append(' %s%s | %*s %s%s\n' %
2766 (filename, ' ' * (maxname - encoding.colwidth(filename)),
2767 (filename, ' ' * (maxname - encoding.colwidth(filename)),
2767 countwidth, count, pluses, minuses))
2768 countwidth, count, pluses, minuses))
2768
2769
2769 if stats:
2770 if stats:
2770 output.append(_(' %d files changed, %d insertions(+), '
2771 output.append(_(' %d files changed, %d insertions(+), '
2771 '%d deletions(-)\n')
2772 '%d deletions(-)\n')
2772 % (len(stats), totaladds, totalremoves))
2773 % (len(stats), totaladds, totalremoves))
2773
2774
2774 return ''.join(output)
2775 return ''.join(output)
2775
2776
2776 def diffstatui(*args, **kw):
2777 def diffstatui(*args, **kw):
2777 '''like diffstat(), but yields 2-tuples of (output, label) for
2778 '''like diffstat(), but yields 2-tuples of (output, label) for
2778 ui.write()
2779 ui.write()
2779 '''
2780 '''
2780
2781
2781 for line in diffstat(*args, **kw).splitlines():
2782 for line in diffstat(*args, **kw).splitlines():
2782 if line and line[-1] in '+-':
2783 if line and line[-1] in '+-':
2783 name, graph = line.rsplit(' ', 1)
2784 name, graph = line.rsplit(' ', 1)
2784 yield (name + ' ', '')
2785 yield (name + ' ', '')
2785 m = re.search(br'\++', graph)
2786 m = re.search(br'\++', graph)
2786 if m:
2787 if m:
2787 yield (m.group(0), 'diffstat.inserted')
2788 yield (m.group(0), 'diffstat.inserted')
2788 m = re.search(br'-+', graph)
2789 m = re.search(br'-+', graph)
2789 if m:
2790 if m:
2790 yield (m.group(0), 'diffstat.deleted')
2791 yield (m.group(0), 'diffstat.deleted')
2791 else:
2792 else:
2792 yield (line, '')
2793 yield (line, '')
2793 yield ('\n', '')
2794 yield ('\n', '')
@@ -1,383 +1,383 b''
1 Show all commands except debug commands
1 Show all commands except debug commands
2 $ hg debugcomplete
2 $ hg debugcomplete
3 add
3 add
4 addremove
4 addremove
5 annotate
5 annotate
6 archive
6 archive
7 backout
7 backout
8 bisect
8 bisect
9 bookmarks
9 bookmarks
10 branch
10 branch
11 branches
11 branches
12 bundle
12 bundle
13 cat
13 cat
14 clone
14 clone
15 commit
15 commit
16 config
16 config
17 copy
17 copy
18 diff
18 diff
19 export
19 export
20 files
20 files
21 forget
21 forget
22 graft
22 graft
23 grep
23 grep
24 heads
24 heads
25 help
25 help
26 identify
26 identify
27 import
27 import
28 incoming
28 incoming
29 init
29 init
30 locate
30 locate
31 log
31 log
32 manifest
32 manifest
33 merge
33 merge
34 outgoing
34 outgoing
35 parents
35 parents
36 paths
36 paths
37 phase
37 phase
38 pull
38 pull
39 push
39 push
40 recover
40 recover
41 remove
41 remove
42 rename
42 rename
43 resolve
43 resolve
44 revert
44 revert
45 rollback
45 rollback
46 root
46 root
47 serve
47 serve
48 status
48 status
49 summary
49 summary
50 tag
50 tag
51 tags
51 tags
52 tip
52 tip
53 unbundle
53 unbundle
54 update
54 update
55 verify
55 verify
56 version
56 version
57
57
58 Show all commands that start with "a"
58 Show all commands that start with "a"
59 $ hg debugcomplete a
59 $ hg debugcomplete a
60 add
60 add
61 addremove
61 addremove
62 annotate
62 annotate
63 archive
63 archive
64
64
65 Do not show debug commands if there are other candidates
65 Do not show debug commands if there are other candidates
66 $ hg debugcomplete d
66 $ hg debugcomplete d
67 diff
67 diff
68
68
69 Show debug commands if there are no other candidates
69 Show debug commands if there are no other candidates
70 $ hg debugcomplete debug
70 $ hg debugcomplete debug
71 debugancestor
71 debugancestor
72 debugapplystreamclonebundle
72 debugapplystreamclonebundle
73 debugbuilddag
73 debugbuilddag
74 debugbundle
74 debugbundle
75 debugcheckstate
75 debugcheckstate
76 debugcolor
76 debugcolor
77 debugcommands
77 debugcommands
78 debugcomplete
78 debugcomplete
79 debugconfig
79 debugconfig
80 debugcreatestreamclonebundle
80 debugcreatestreamclonebundle
81 debugdag
81 debugdag
82 debugdata
82 debugdata
83 debugdate
83 debugdate
84 debugdeltachain
84 debugdeltachain
85 debugdirstate
85 debugdirstate
86 debugdiscovery
86 debugdiscovery
87 debugextensions
87 debugextensions
88 debugfileset
88 debugfileset
89 debugfsinfo
89 debugfsinfo
90 debuggetbundle
90 debuggetbundle
91 debugignore
91 debugignore
92 debugindex
92 debugindex
93 debugindexdot
93 debugindexdot
94 debuginstall
94 debuginstall
95 debugknown
95 debugknown
96 debuglabelcomplete
96 debuglabelcomplete
97 debuglocks
97 debuglocks
98 debugmergestate
98 debugmergestate
99 debugnamecomplete
99 debugnamecomplete
100 debugobsolete
100 debugobsolete
101 debugpathcomplete
101 debugpathcomplete
102 debugpickmergetool
102 debugpickmergetool
103 debugpushkey
103 debugpushkey
104 debugpvec
104 debugpvec
105 debugrebuilddirstate
105 debugrebuilddirstate
106 debugrebuildfncache
106 debugrebuildfncache
107 debugrename
107 debugrename
108 debugrevlog
108 debugrevlog
109 debugrevspec
109 debugrevspec
110 debugsetparents
110 debugsetparents
111 debugssl
111 debugssl
112 debugsub
112 debugsub
113 debugsuccessorssets
113 debugsuccessorssets
114 debugtemplate
114 debugtemplate
115 debugupdatecaches
115 debugupdatecaches
116 debugupgraderepo
116 debugupgraderepo
117 debugwalk
117 debugwalk
118 debugwireargs
118 debugwireargs
119
119
120 Do not show the alias of a debug command if there are other candidates
120 Do not show the alias of a debug command if there are other candidates
121 (this should hide rawcommit)
121 (this should hide rawcommit)
122 $ hg debugcomplete r
122 $ hg debugcomplete r
123 recover
123 recover
124 remove
124 remove
125 rename
125 rename
126 resolve
126 resolve
127 revert
127 revert
128 rollback
128 rollback
129 root
129 root
130 Show the alias of a debug command if there are no other candidates
130 Show the alias of a debug command if there are no other candidates
131 $ hg debugcomplete rawc
131 $ hg debugcomplete rawc
132
132
133
133
134 Show the global options
134 Show the global options
135 $ hg debugcomplete --options | sort
135 $ hg debugcomplete --options | sort
136 --color
136 --color
137 --config
137 --config
138 --cwd
138 --cwd
139 --debug
139 --debug
140 --debugger
140 --debugger
141 --encoding
141 --encoding
142 --encodingmode
142 --encodingmode
143 --help
143 --help
144 --hidden
144 --hidden
145 --noninteractive
145 --noninteractive
146 --pager
146 --pager
147 --profile
147 --profile
148 --quiet
148 --quiet
149 --repository
149 --repository
150 --time
150 --time
151 --traceback
151 --traceback
152 --verbose
152 --verbose
153 --version
153 --version
154 -R
154 -R
155 -h
155 -h
156 -q
156 -q
157 -v
157 -v
158 -y
158 -y
159
159
160 Show the options for the "serve" command
160 Show the options for the "serve" command
161 $ hg debugcomplete --options serve | sort
161 $ hg debugcomplete --options serve | sort
162 --accesslog
162 --accesslog
163 --address
163 --address
164 --certificate
164 --certificate
165 --cmdserver
165 --cmdserver
166 --color
166 --color
167 --config
167 --config
168 --cwd
168 --cwd
169 --daemon
169 --daemon
170 --daemon-postexec
170 --daemon-postexec
171 --debug
171 --debug
172 --debugger
172 --debugger
173 --encoding
173 --encoding
174 --encodingmode
174 --encodingmode
175 --errorlog
175 --errorlog
176 --help
176 --help
177 --hidden
177 --hidden
178 --ipv6
178 --ipv6
179 --name
179 --name
180 --noninteractive
180 --noninteractive
181 --pager
181 --pager
182 --pid-file
182 --pid-file
183 --port
183 --port
184 --prefix
184 --prefix
185 --profile
185 --profile
186 --quiet
186 --quiet
187 --repository
187 --repository
188 --stdio
188 --stdio
189 --style
189 --style
190 --subrepos
190 --subrepos
191 --templates
191 --templates
192 --time
192 --time
193 --traceback
193 --traceback
194 --verbose
194 --verbose
195 --version
195 --version
196 --web-conf
196 --web-conf
197 -6
197 -6
198 -A
198 -A
199 -E
199 -E
200 -R
200 -R
201 -S
201 -S
202 -a
202 -a
203 -d
203 -d
204 -h
204 -h
205 -n
205 -n
206 -p
206 -p
207 -q
207 -q
208 -t
208 -t
209 -v
209 -v
210 -y
210 -y
211
211
212 Show an error if we use --options with an ambiguous abbreviation
212 Show an error if we use --options with an ambiguous abbreviation
213 $ hg debugcomplete --options s
213 $ hg debugcomplete --options s
214 hg: command 's' is ambiguous:
214 hg: command 's' is ambiguous:
215 serve showconfig status summary
215 serve showconfig status summary
216 [255]
216 [255]
217
217
218 Show all commands + options
218 Show all commands + options
219 $ hg debugcommands
219 $ hg debugcommands
220 add: include, exclude, subrepos, dry-run
220 add: include, exclude, subrepos, dry-run
221 annotate: rev, follow, no-follow, text, user, file, date, number, changeset, line-number, skip, ignore-all-space, ignore-space-change, ignore-blank-lines, include, exclude, template
221 annotate: rev, follow, no-follow, text, user, file, date, number, changeset, line-number, skip, ignore-all-space, ignore-space-change, ignore-blank-lines, ignore-space-at-eol, include, exclude, template
222 clone: noupdate, updaterev, rev, branch, pull, uncompressed, ssh, remotecmd, insecure
222 clone: noupdate, updaterev, rev, branch, pull, uncompressed, ssh, remotecmd, insecure
223 commit: addremove, close-branch, amend, secret, edit, interactive, include, exclude, message, logfile, date, user, subrepos
223 commit: addremove, close-branch, amend, secret, edit, interactive, include, exclude, message, logfile, date, user, subrepos
224 diff: rev, change, text, git, binary, nodates, noprefix, show-function, reverse, ignore-all-space, ignore-space-change, ignore-blank-lines, unified, stat, root, include, exclude, subrepos
224 diff: rev, change, text, git, binary, nodates, noprefix, show-function, reverse, ignore-all-space, ignore-space-change, ignore-blank-lines, ignore-space-at-eol, unified, stat, root, include, exclude, subrepos
225 export: output, switch-parent, rev, text, git, binary, nodates
225 export: output, switch-parent, rev, text, git, binary, nodates
226 forget: include, exclude
226 forget: include, exclude
227 init: ssh, remotecmd, insecure
227 init: ssh, remotecmd, insecure
228 log: follow, follow-first, date, copies, keyword, rev, removed, only-merges, user, only-branch, branch, prune, patch, git, limit, no-merges, stat, graph, style, template, include, exclude
228 log: follow, follow-first, date, copies, keyword, rev, removed, only-merges, user, only-branch, branch, prune, patch, git, limit, no-merges, stat, graph, style, template, include, exclude
229 merge: force, rev, preview, tool
229 merge: force, rev, preview, tool
230 pull: update, force, rev, bookmark, branch, ssh, remotecmd, insecure
230 pull: update, force, rev, bookmark, branch, ssh, remotecmd, insecure
231 push: force, rev, bookmark, branch, new-branch, pushvars, ssh, remotecmd, insecure
231 push: force, rev, bookmark, branch, new-branch, pushvars, ssh, remotecmd, insecure
232 remove: after, force, subrepos, include, exclude
232 remove: after, force, subrepos, include, exclude
233 serve: accesslog, daemon, daemon-postexec, errorlog, port, address, prefix, name, web-conf, webdir-conf, pid-file, stdio, cmdserver, templates, style, ipv6, certificate, subrepos
233 serve: accesslog, daemon, daemon-postexec, errorlog, port, address, prefix, name, web-conf, webdir-conf, pid-file, stdio, cmdserver, templates, style, ipv6, certificate, subrepos
234 status: all, modified, added, removed, deleted, clean, unknown, ignored, no-status, terse, copies, print0, rev, change, include, exclude, subrepos, template
234 status: all, modified, added, removed, deleted, clean, unknown, ignored, no-status, terse, copies, print0, rev, change, include, exclude, subrepos, template
235 summary: remote
235 summary: remote
236 update: clean, check, merge, date, rev, tool
236 update: clean, check, merge, date, rev, tool
237 addremove: similarity, subrepos, include, exclude, dry-run
237 addremove: similarity, subrepos, include, exclude, dry-run
238 archive: no-decode, prefix, rev, type, subrepos, include, exclude
238 archive: no-decode, prefix, rev, type, subrepos, include, exclude
239 backout: merge, commit, no-commit, parent, rev, edit, tool, include, exclude, message, logfile, date, user
239 backout: merge, commit, no-commit, parent, rev, edit, tool, include, exclude, message, logfile, date, user
240 bisect: reset, good, bad, skip, extend, command, noupdate
240 bisect: reset, good, bad, skip, extend, command, noupdate
241 bookmarks: force, rev, delete, rename, inactive, template
241 bookmarks: force, rev, delete, rename, inactive, template
242 branch: force, clean
242 branch: force, clean
243 branches: active, closed, template
243 branches: active, closed, template
244 bundle: force, rev, branch, base, all, type, ssh, remotecmd, insecure
244 bundle: force, rev, branch, base, all, type, ssh, remotecmd, insecure
245 cat: output, rev, decode, include, exclude, template
245 cat: output, rev, decode, include, exclude, template
246 config: untrusted, edit, local, global, template
246 config: untrusted, edit, local, global, template
247 copy: after, force, include, exclude, dry-run
247 copy: after, force, include, exclude, dry-run
248 debugancestor:
248 debugancestor:
249 debugapplystreamclonebundle:
249 debugapplystreamclonebundle:
250 debugbuilddag: mergeable-file, overwritten-file, new-file
250 debugbuilddag: mergeable-file, overwritten-file, new-file
251 debugbundle: all, part-type, spec
251 debugbundle: all, part-type, spec
252 debugcheckstate:
252 debugcheckstate:
253 debugcolor: style
253 debugcolor: style
254 debugcommands:
254 debugcommands:
255 debugcomplete: options
255 debugcomplete: options
256 debugcreatestreamclonebundle:
256 debugcreatestreamclonebundle:
257 debugdag: tags, branches, dots, spaces
257 debugdag: tags, branches, dots, spaces
258 debugdata: changelog, manifest, dir
258 debugdata: changelog, manifest, dir
259 debugdate: extended
259 debugdate: extended
260 debugdeltachain: changelog, manifest, dir, template
260 debugdeltachain: changelog, manifest, dir, template
261 debugdirstate: nodates, datesort
261 debugdirstate: nodates, datesort
262 debugdiscovery: old, nonheads, ssh, remotecmd, insecure
262 debugdiscovery: old, nonheads, ssh, remotecmd, insecure
263 debugextensions: template
263 debugextensions: template
264 debugfileset: rev
264 debugfileset: rev
265 debugfsinfo:
265 debugfsinfo:
266 debuggetbundle: head, common, type
266 debuggetbundle: head, common, type
267 debugignore:
267 debugignore:
268 debugindex: changelog, manifest, dir, format
268 debugindex: changelog, manifest, dir, format
269 debugindexdot: changelog, manifest, dir
269 debugindexdot: changelog, manifest, dir
270 debuginstall: template
270 debuginstall: template
271 debugknown:
271 debugknown:
272 debuglabelcomplete:
272 debuglabelcomplete:
273 debuglocks: force-lock, force-wlock
273 debuglocks: force-lock, force-wlock
274 debugmergestate:
274 debugmergestate:
275 debugnamecomplete:
275 debugnamecomplete:
276 debugobsolete: flags, record-parents, rev, exclusive, index, delete, date, user, template
276 debugobsolete: flags, record-parents, rev, exclusive, index, delete, date, user, template
277 debugpathcomplete: full, normal, added, removed
277 debugpathcomplete: full, normal, added, removed
278 debugpickmergetool: rev, changedelete, include, exclude, tool
278 debugpickmergetool: rev, changedelete, include, exclude, tool
279 debugpushkey:
279 debugpushkey:
280 debugpvec:
280 debugpvec:
281 debugrebuilddirstate: rev, minimal
281 debugrebuilddirstate: rev, minimal
282 debugrebuildfncache:
282 debugrebuildfncache:
283 debugrename: rev
283 debugrename: rev
284 debugrevlog: changelog, manifest, dir, dump
284 debugrevlog: changelog, manifest, dir, dump
285 debugrevspec: optimize, show-revs, show-set, show-stage, no-optimized, verify-optimized
285 debugrevspec: optimize, show-revs, show-set, show-stage, no-optimized, verify-optimized
286 debugsetparents:
286 debugsetparents:
287 debugssl:
287 debugssl:
288 debugsub: rev
288 debugsub: rev
289 debugsuccessorssets: closest
289 debugsuccessorssets: closest
290 debugtemplate: rev, define
290 debugtemplate: rev, define
291 debugupdatecaches:
291 debugupdatecaches:
292 debugupgraderepo: optimize, run
292 debugupgraderepo: optimize, run
293 debugwalk: include, exclude
293 debugwalk: include, exclude
294 debugwireargs: three, four, five, ssh, remotecmd, insecure
294 debugwireargs: three, four, five, ssh, remotecmd, insecure
295 files: rev, print0, include, exclude, template, subrepos
295 files: rev, print0, include, exclude, template, subrepos
296 graft: rev, continue, edit, log, force, currentdate, currentuser, date, user, tool, dry-run
296 graft: rev, continue, edit, log, force, currentdate, currentuser, date, user, tool, dry-run
297 grep: print0, all, text, follow, ignore-case, files-with-matches, line-number, rev, user, date, template, include, exclude
297 grep: print0, all, text, follow, ignore-case, files-with-matches, line-number, rev, user, date, template, include, exclude
298 heads: rev, topo, active, closed, style, template
298 heads: rev, topo, active, closed, style, template
299 help: extension, command, keyword, system
299 help: extension, command, keyword, system
300 identify: rev, num, id, branch, tags, bookmarks, ssh, remotecmd, insecure, template
300 identify: rev, num, id, branch, tags, bookmarks, ssh, remotecmd, insecure, template
301 import: strip, base, edit, force, no-commit, bypass, partial, exact, prefix, import-branch, message, logfile, date, user, similarity
301 import: strip, base, edit, force, no-commit, bypass, partial, exact, prefix, import-branch, message, logfile, date, user, similarity
302 incoming: force, newest-first, bundle, rev, bookmarks, branch, patch, git, limit, no-merges, stat, graph, style, template, ssh, remotecmd, insecure, subrepos
302 incoming: force, newest-first, bundle, rev, bookmarks, branch, patch, git, limit, no-merges, stat, graph, style, template, ssh, remotecmd, insecure, subrepos
303 locate: rev, print0, fullpath, include, exclude
303 locate: rev, print0, fullpath, include, exclude
304 manifest: rev, all, template
304 manifest: rev, all, template
305 outgoing: force, rev, newest-first, bookmarks, branch, patch, git, limit, no-merges, stat, graph, style, template, ssh, remotecmd, insecure, subrepos
305 outgoing: force, rev, newest-first, bookmarks, branch, patch, git, limit, no-merges, stat, graph, style, template, ssh, remotecmd, insecure, subrepos
306 parents: rev, style, template
306 parents: rev, style, template
307 paths: template
307 paths: template
308 phase: public, draft, secret, force, rev
308 phase: public, draft, secret, force, rev
309 recover:
309 recover:
310 rename: after, force, include, exclude, dry-run
310 rename: after, force, include, exclude, dry-run
311 resolve: all, list, mark, unmark, no-status, tool, include, exclude, template
311 resolve: all, list, mark, unmark, no-status, tool, include, exclude, template
312 revert: all, date, rev, no-backup, interactive, include, exclude, dry-run
312 revert: all, date, rev, no-backup, interactive, include, exclude, dry-run
313 rollback: dry-run, force
313 rollback: dry-run, force
314 root:
314 root:
315 tag: force, local, rev, remove, edit, message, date, user
315 tag: force, local, rev, remove, edit, message, date, user
316 tags: template
316 tags: template
317 tip: patch, git, style, template
317 tip: patch, git, style, template
318 unbundle: update
318 unbundle: update
319 verify:
319 verify:
320 version: template
320 version: template
321
321
322 $ hg init a
322 $ hg init a
323 $ cd a
323 $ cd a
324 $ echo fee > fee
324 $ echo fee > fee
325 $ hg ci -q -Amfee
325 $ hg ci -q -Amfee
326 $ hg tag fee
326 $ hg tag fee
327 $ mkdir fie
327 $ mkdir fie
328 $ echo dead > fie/dead
328 $ echo dead > fie/dead
329 $ echo live > fie/live
329 $ echo live > fie/live
330 $ hg bookmark fo
330 $ hg bookmark fo
331 $ hg branch -q fie
331 $ hg branch -q fie
332 $ hg ci -q -Amfie
332 $ hg ci -q -Amfie
333 $ echo fo > fo
333 $ echo fo > fo
334 $ hg branch -qf default
334 $ hg branch -qf default
335 $ hg ci -q -Amfo
335 $ hg ci -q -Amfo
336 $ echo Fum > Fum
336 $ echo Fum > Fum
337 $ hg ci -q -AmFum
337 $ hg ci -q -AmFum
338 $ hg bookmark Fum
338 $ hg bookmark Fum
339
339
340 Test debugpathcomplete
340 Test debugpathcomplete
341
341
342 $ hg debugpathcomplete f
342 $ hg debugpathcomplete f
343 fee
343 fee
344 fie
344 fie
345 fo
345 fo
346 $ hg debugpathcomplete -f f
346 $ hg debugpathcomplete -f f
347 fee
347 fee
348 fie/dead
348 fie/dead
349 fie/live
349 fie/live
350 fo
350 fo
351
351
352 $ hg rm Fum
352 $ hg rm Fum
353 $ hg debugpathcomplete -r F
353 $ hg debugpathcomplete -r F
354 Fum
354 Fum
355
355
356 Test debugnamecomplete
356 Test debugnamecomplete
357
357
358 $ hg debugnamecomplete
358 $ hg debugnamecomplete
359 Fum
359 Fum
360 default
360 default
361 fee
361 fee
362 fie
362 fie
363 fo
363 fo
364 tip
364 tip
365 $ hg debugnamecomplete f
365 $ hg debugnamecomplete f
366 fee
366 fee
367 fie
367 fie
368 fo
368 fo
369
369
370 Test debuglabelcomplete, a deprecated name for debugnamecomplete that is still
370 Test debuglabelcomplete, a deprecated name for debugnamecomplete that is still
371 used for completions in some shells.
371 used for completions in some shells.
372
372
373 $ hg debuglabelcomplete
373 $ hg debuglabelcomplete
374 Fum
374 Fum
375 default
375 default
376 fee
376 fee
377 fie
377 fie
378 fo
378 fo
379 tip
379 tip
380 $ hg debuglabelcomplete f
380 $ hg debuglabelcomplete f
381 fee
381 fee
382 fie
382 fie
383 fo
383 fo
@@ -1,502 +1,517 b''
1 GNU diff is the reference for all of these results.
1 GNU diff is the reference for all of these results.
2
2
3 Prepare tests:
3 Prepare tests:
4
4
5 $ echo '[alias]' >> $HGRCPATH
5 $ echo '[alias]' >> $HGRCPATH
6 $ echo 'ndiff = diff --nodates' >> $HGRCPATH
6 $ echo 'ndiff = diff --nodates' >> $HGRCPATH
7
7
8 $ hg init
8 $ hg init
9 $ printf 'hello world\ngoodbye world\n' >foo
9 $ printf 'hello world\ngoodbye world\n' >foo
10 $ hg ci -Amfoo -ufoo
10 $ hg ci -Amfoo -ufoo
11 adding foo
11 adding foo
12
12
13
13
14 Test added blank lines:
14 Test added blank lines:
15
15
16 $ printf '\nhello world\n\ngoodbye world\n\n' >foo
16 $ printf '\nhello world\n\ngoodbye world\n\n' >foo
17
17
18 >>> two diffs showing three added lines <<<
18 >>> two diffs showing three added lines <<<
19
19
20 $ hg ndiff
20 $ hg ndiff
21 diff -r 540c40a65b78 foo
21 diff -r 540c40a65b78 foo
22 --- a/foo
22 --- a/foo
23 +++ b/foo
23 +++ b/foo
24 @@ -1,2 +1,5 @@
24 @@ -1,2 +1,5 @@
25 +
25 +
26 hello world
26 hello world
27 +
27 +
28 goodbye world
28 goodbye world
29 +
29 +
30 $ hg ndiff -b
30 $ hg ndiff -b
31 diff -r 540c40a65b78 foo
31 diff -r 540c40a65b78 foo
32 --- a/foo
32 --- a/foo
33 +++ b/foo
33 +++ b/foo
34 @@ -1,2 +1,5 @@
34 @@ -1,2 +1,5 @@
35 +
35 +
36 hello world
36 hello world
37 +
37 +
38 goodbye world
38 goodbye world
39 +
39 +
40
40
41 >>> no diffs <<<
41 >>> no diffs <<<
42
42
43 $ hg ndiff -B
43 $ hg ndiff -B
44 $ hg ndiff -Bb
44 $ hg ndiff -Bb
45
45
46
46
47 Test added horizontal space first on a line():
47 Test added horizontal space first on a line():
48
48
49 $ printf '\t hello world\ngoodbye world\n' >foo
49 $ printf '\t hello world\ngoodbye world\n' >foo
50
50
51 >>> four diffs showing added space first on the first line <<<
51 >>> four diffs showing added space first on the first line <<<
52
52
53 $ hg ndiff
53 $ hg ndiff
54 diff -r 540c40a65b78 foo
54 diff -r 540c40a65b78 foo
55 --- a/foo
55 --- a/foo
56 +++ b/foo
56 +++ b/foo
57 @@ -1,2 +1,2 @@
57 @@ -1,2 +1,2 @@
58 -hello world
58 -hello world
59 + hello world
59 + hello world
60 goodbye world
60 goodbye world
61
61
62 $ hg ndiff -b
62 $ hg ndiff -b
63 diff -r 540c40a65b78 foo
63 diff -r 540c40a65b78 foo
64 --- a/foo
64 --- a/foo
65 +++ b/foo
65 +++ b/foo
66 @@ -1,2 +1,2 @@
66 @@ -1,2 +1,2 @@
67 -hello world
67 -hello world
68 + hello world
68 + hello world
69 goodbye world
69 goodbye world
70
70
71 $ hg ndiff -B
71 $ hg ndiff -B
72 diff -r 540c40a65b78 foo
72 diff -r 540c40a65b78 foo
73 --- a/foo
73 --- a/foo
74 +++ b/foo
74 +++ b/foo
75 @@ -1,2 +1,2 @@
75 @@ -1,2 +1,2 @@
76 -hello world
76 -hello world
77 + hello world
77 + hello world
78 goodbye world
78 goodbye world
79
79
80 $ hg ndiff -Bb
80 $ hg ndiff -Bb
81 diff -r 540c40a65b78 foo
81 diff -r 540c40a65b78 foo
82 --- a/foo
82 --- a/foo
83 +++ b/foo
83 +++ b/foo
84 @@ -1,2 +1,2 @@
84 @@ -1,2 +1,2 @@
85 -hello world
85 -hello world
86 + hello world
86 + hello world
87 goodbye world
87 goodbye world
88
88
89
89
90 Test added horizontal space last on a line:
90 Test added horizontal space last on a line:
91
91
92 $ printf 'hello world\t \ngoodbye world\n' >foo
92 $ printf 'hello world\t \ngoodbye world\n' >foo
93
93
94 >>> two diffs showing space appended to the first line <<<
94 >>> two diffs showing space appended to the first line <<<
95
95
96 $ hg ndiff
96 $ hg ndiff
97 diff -r 540c40a65b78 foo
97 diff -r 540c40a65b78 foo
98 --- a/foo
98 --- a/foo
99 +++ b/foo
99 +++ b/foo
100 @@ -1,2 +1,2 @@
100 @@ -1,2 +1,2 @@
101 -hello world
101 -hello world
102 +hello world
102 +hello world
103 goodbye world
103 goodbye world
104
104
105 $ hg ndiff -B
105 $ hg ndiff -B
106 diff -r 540c40a65b78 foo
106 diff -r 540c40a65b78 foo
107 --- a/foo
107 --- a/foo
108 +++ b/foo
108 +++ b/foo
109 @@ -1,2 +1,2 @@
109 @@ -1,2 +1,2 @@
110 -hello world
110 -hello world
111 +hello world
111 +hello world
112 goodbye world
112 goodbye world
113
113
114 >>> no diffs <<<
114 >>> no diffs <<<
115
115
116 $ hg ndiff -b
116 $ hg ndiff -b
117 $ hg ndiff -Bb
117 $ hg ndiff -Bb
118
118
119
119
120 Test added horizontal space in the middle of a word:
120 Test added horizontal space in the middle of a word:
121
121
122 $ printf 'hello world\ngood bye world\n' >foo
122 $ printf 'hello world\ngood bye world\n' >foo
123
123
124 >>> four diffs showing space inserted into "goodbye" <<<
124 >>> four diffs showing space inserted into "goodbye" <<<
125
125
126 $ hg ndiff
126 $ hg ndiff
127 diff -r 540c40a65b78 foo
127 diff -r 540c40a65b78 foo
128 --- a/foo
128 --- a/foo
129 +++ b/foo
129 +++ b/foo
130 @@ -1,2 +1,2 @@
130 @@ -1,2 +1,2 @@
131 hello world
131 hello world
132 -goodbye world
132 -goodbye world
133 +good bye world
133 +good bye world
134
134
135 $ hg ndiff -B
135 $ hg ndiff -B
136 diff -r 540c40a65b78 foo
136 diff -r 540c40a65b78 foo
137 --- a/foo
137 --- a/foo
138 +++ b/foo
138 +++ b/foo
139 @@ -1,2 +1,2 @@
139 @@ -1,2 +1,2 @@
140 hello world
140 hello world
141 -goodbye world
141 -goodbye world
142 +good bye world
142 +good bye world
143
143
144 $ hg ndiff -b
144 $ hg ndiff -b
145 diff -r 540c40a65b78 foo
145 diff -r 540c40a65b78 foo
146 --- a/foo
146 --- a/foo
147 +++ b/foo
147 +++ b/foo
148 @@ -1,2 +1,2 @@
148 @@ -1,2 +1,2 @@
149 hello world
149 hello world
150 -goodbye world
150 -goodbye world
151 +good bye world
151 +good bye world
152
152
153 $ hg ndiff -Bb
153 $ hg ndiff -Bb
154 diff -r 540c40a65b78 foo
154 diff -r 540c40a65b78 foo
155 --- a/foo
155 --- a/foo
156 +++ b/foo
156 +++ b/foo
157 @@ -1,2 +1,2 @@
157 @@ -1,2 +1,2 @@
158 hello world
158 hello world
159 -goodbye world
159 -goodbye world
160 +good bye world
160 +good bye world
161
161
162
162
163 Test increased horizontal whitespace amount:
163 Test increased horizontal whitespace amount:
164
164
165 $ printf 'hello world\ngoodbye\t\t \tworld\n' >foo
165 $ printf 'hello world\ngoodbye\t\t \tworld\n' >foo
166
166
167 >>> two diffs showing changed whitespace amount in the last line <<<
167 >>> two diffs showing changed whitespace amount in the last line <<<
168
168
169 $ hg ndiff
169 $ hg ndiff
170 diff -r 540c40a65b78 foo
170 diff -r 540c40a65b78 foo
171 --- a/foo
171 --- a/foo
172 +++ b/foo
172 +++ b/foo
173 @@ -1,2 +1,2 @@
173 @@ -1,2 +1,2 @@
174 hello world
174 hello world
175 -goodbye world
175 -goodbye world
176 +goodbye world
176 +goodbye world
177
177
178 $ hg ndiff -B
178 $ hg ndiff -B
179 diff -r 540c40a65b78 foo
179 diff -r 540c40a65b78 foo
180 --- a/foo
180 --- a/foo
181 +++ b/foo
181 +++ b/foo
182 @@ -1,2 +1,2 @@
182 @@ -1,2 +1,2 @@
183 hello world
183 hello world
184 -goodbye world
184 -goodbye world
185 +goodbye world
185 +goodbye world
186
186
187 >>> no diffs <<<
187 >>> no diffs <<<
188
188
189 $ hg ndiff -b
189 $ hg ndiff -b
190 $ hg ndiff -Bb
190 $ hg ndiff -Bb
191
191
192
192
193 Test added blank line with horizontal whitespace:
193 Test added blank line with horizontal whitespace:
194
194
195 $ printf 'hello world\n \t\ngoodbye world\n' >foo
195 $ printf 'hello world\n \t\ngoodbye world\n' >foo
196
196
197 >>> three diffs showing added blank line with horizontal space <<<
197 >>> three diffs showing added blank line with horizontal space <<<
198
198
199 $ hg ndiff
199 $ hg ndiff
200 diff -r 540c40a65b78 foo
200 diff -r 540c40a65b78 foo
201 --- a/foo
201 --- a/foo
202 +++ b/foo
202 +++ b/foo
203 @@ -1,2 +1,3 @@
203 @@ -1,2 +1,3 @@
204 hello world
204 hello world
205 +
205 +
206 goodbye world
206 goodbye world
207
207
208 $ hg ndiff -B
208 $ hg ndiff -B
209 diff -r 540c40a65b78 foo
209 diff -r 540c40a65b78 foo
210 --- a/foo
210 --- a/foo
211 +++ b/foo
211 +++ b/foo
212 @@ -1,2 +1,3 @@
212 @@ -1,2 +1,3 @@
213 hello world
213 hello world
214 +
214 +
215 goodbye world
215 goodbye world
216
216
217 $ hg ndiff -b
217 $ hg ndiff -b
218 diff -r 540c40a65b78 foo
218 diff -r 540c40a65b78 foo
219 --- a/foo
219 --- a/foo
220 +++ b/foo
220 +++ b/foo
221 @@ -1,2 +1,3 @@
221 @@ -1,2 +1,3 @@
222 hello world
222 hello world
223 +
223 +
224 goodbye world
224 goodbye world
225
225
226 >>> no diffs <<<
226 >>> no diffs <<<
227
227
228 $ hg ndiff -Bb
228 $ hg ndiff -Bb
229
229
230
230
231 Test added blank line with other whitespace:
231 Test added blank line with other whitespace:
232
232
233 $ printf 'hello world\n \t\ngoodbye world \n' >foo
233 $ printf 'hello world\n \t\ngoodbye world \n' >foo
234
234
235 >>> three diffs showing added blank line with other space <<<
235 >>> three diffs showing added blank line with other space <<<
236
236
237 $ hg ndiff
237 $ hg ndiff
238 diff -r 540c40a65b78 foo
238 diff -r 540c40a65b78 foo
239 --- a/foo
239 --- a/foo
240 +++ b/foo
240 +++ b/foo
241 @@ -1,2 +1,3 @@
241 @@ -1,2 +1,3 @@
242 -hello world
242 -hello world
243 -goodbye world
243 -goodbye world
244 +hello world
244 +hello world
245 +
245 +
246 +goodbye world
246 +goodbye world
247
247
248 $ hg ndiff -B
248 $ hg ndiff -B
249 diff -r 540c40a65b78 foo
249 diff -r 540c40a65b78 foo
250 --- a/foo
250 --- a/foo
251 +++ b/foo
251 +++ b/foo
252 @@ -1,2 +1,3 @@
252 @@ -1,2 +1,3 @@
253 -hello world
253 -hello world
254 -goodbye world
254 -goodbye world
255 +hello world
255 +hello world
256 +
256 +
257 +goodbye world
257 +goodbye world
258
258
259 $ hg ndiff -b
259 $ hg ndiff -b
260 diff -r 540c40a65b78 foo
260 diff -r 540c40a65b78 foo
261 --- a/foo
261 --- a/foo
262 +++ b/foo
262 +++ b/foo
263 @@ -1,2 +1,3 @@
263 @@ -1,2 +1,3 @@
264 hello world
264 hello world
265 +
265 +
266 goodbye world
266 goodbye world
267
267
268 >>> no diffs <<<
268 >>> no diffs <<<
269
269
270 $ hg ndiff -Bb
270 $ hg ndiff -Bb
271
271
272
272
273 Test whitespace changes:
273 Test whitespace changes:
274
274
275 $ printf 'helloworld\ngoodbye\tworld \n' >foo
275 $ printf 'helloworld\ngoodbye\tworld \n' >foo
276
276
277 >>> four diffs showing changed whitespace <<<
277 >>> four diffs showing changed whitespace <<<
278
278
279 $ hg ndiff
279 $ hg ndiff
280 diff -r 540c40a65b78 foo
280 diff -r 540c40a65b78 foo
281 --- a/foo
281 --- a/foo
282 +++ b/foo
282 +++ b/foo
283 @@ -1,2 +1,2 @@
283 @@ -1,2 +1,2 @@
284 -hello world
284 -hello world
285 -goodbye world
285 -goodbye world
286 +helloworld
286 +helloworld
287 +goodbye world
287 +goodbye world
288
288
289 $ hg ndiff -B
289 $ hg ndiff -B
290 diff -r 540c40a65b78 foo
290 diff -r 540c40a65b78 foo
291 --- a/foo
291 --- a/foo
292 +++ b/foo
292 +++ b/foo
293 @@ -1,2 +1,2 @@
293 @@ -1,2 +1,2 @@
294 -hello world
294 -hello world
295 -goodbye world
295 -goodbye world
296 +helloworld
296 +helloworld
297 +goodbye world
297 +goodbye world
298
298
299 $ hg ndiff -b
299 $ hg ndiff -b
300 diff -r 540c40a65b78 foo
300 diff -r 540c40a65b78 foo
301 --- a/foo
301 --- a/foo
302 +++ b/foo
302 +++ b/foo
303 @@ -1,2 +1,2 @@
303 @@ -1,2 +1,2 @@
304 -hello world
304 -hello world
305 +helloworld
305 +helloworld
306 goodbye world
306 goodbye world
307
307
308 $ hg ndiff -Bb
308 $ hg ndiff -Bb
309 diff -r 540c40a65b78 foo
309 diff -r 540c40a65b78 foo
310 --- a/foo
310 --- a/foo
311 +++ b/foo
311 +++ b/foo
312 @@ -1,2 +1,2 @@
312 @@ -1,2 +1,2 @@
313 -hello world
313 -hello world
314 +helloworld
314 +helloworld
315 goodbye world
315 goodbye world
316
316
317 >>> no diffs <<<
317 >>> no diffs <<<
318
318
319 $ hg ndiff -w
319 $ hg ndiff -w
320
320
321
321
322 Test whitespace changes and blank lines:
322 Test whitespace changes and blank lines:
323
323
324 $ printf 'helloworld\n\n\n\ngoodbye\tworld \n' >foo
324 $ printf 'helloworld\n\n\n\ngoodbye\tworld \n' >foo
325
325
326 >>> five diffs showing changed whitespace <<<
326 >>> five diffs showing changed whitespace <<<
327
327
328 $ hg ndiff
328 $ hg ndiff
329 diff -r 540c40a65b78 foo
329 diff -r 540c40a65b78 foo
330 --- a/foo
330 --- a/foo
331 +++ b/foo
331 +++ b/foo
332 @@ -1,2 +1,5 @@
332 @@ -1,2 +1,5 @@
333 -hello world
333 -hello world
334 -goodbye world
334 -goodbye world
335 +helloworld
335 +helloworld
336 +
336 +
337 +
337 +
338 +
338 +
339 +goodbye world
339 +goodbye world
340
340
341 $ hg ndiff -B
341 $ hg ndiff -B
342 diff -r 540c40a65b78 foo
342 diff -r 540c40a65b78 foo
343 --- a/foo
343 --- a/foo
344 +++ b/foo
344 +++ b/foo
345 @@ -1,2 +1,5 @@
345 @@ -1,2 +1,5 @@
346 -hello world
346 -hello world
347 -goodbye world
347 -goodbye world
348 +helloworld
348 +helloworld
349 +
349 +
350 +
350 +
351 +
351 +
352 +goodbye world
352 +goodbye world
353
353
354 $ hg ndiff -b
354 $ hg ndiff -b
355 diff -r 540c40a65b78 foo
355 diff -r 540c40a65b78 foo
356 --- a/foo
356 --- a/foo
357 +++ b/foo
357 +++ b/foo
358 @@ -1,2 +1,5 @@
358 @@ -1,2 +1,5 @@
359 -hello world
359 -hello world
360 +helloworld
360 +helloworld
361 +
361 +
362 +
362 +
363 +
363 +
364 goodbye world
364 goodbye world
365
365
366 $ hg ndiff -Bb
366 $ hg ndiff -Bb
367 diff -r 540c40a65b78 foo
367 diff -r 540c40a65b78 foo
368 --- a/foo
368 --- a/foo
369 +++ b/foo
369 +++ b/foo
370 @@ -1,2 +1,5 @@
370 @@ -1,2 +1,5 @@
371 -hello world
371 -hello world
372 +helloworld
372 +helloworld
373 +
373 +
374 +
374 +
375 +
375 +
376 goodbye world
376 goodbye world
377
377
378 $ hg ndiff -w
378 $ hg ndiff -w
379 diff -r 540c40a65b78 foo
379 diff -r 540c40a65b78 foo
380 --- a/foo
380 --- a/foo
381 +++ b/foo
381 +++ b/foo
382 @@ -1,2 +1,5 @@
382 @@ -1,2 +1,5 @@
383 hello world
383 hello world
384 +
384 +
385 +
385 +
386 +
386 +
387 goodbye world
387 goodbye world
388
388
389 >>> no diffs <<<
389 >>> no diffs <<<
390
390
391 $ hg ndiff -wB
391 $ hg ndiff -wB
392
392
393
393
394 Test \r (carriage return) as used in "DOS" line endings:
394 Test \r (carriage return) as used in "DOS" line endings:
395
395
396 $ printf 'hello world\r\n\r\ngoodbye\rworld\n' >foo
396 $ printf 'hello world\r\n\r\ngoodbye\rworld\n' >foo
397
397
398 $ hg ndiff
398 $ hg ndiff
399 diff -r 540c40a65b78 foo
399 diff -r 540c40a65b78 foo
400 --- a/foo
400 --- a/foo
401 +++ b/foo
401 +++ b/foo
402 @@ -1,2 +1,3 @@
402 @@ -1,2 +1,3 @@
403 -hello world
403 -hello world
404 -goodbye world
404 -goodbye world
405 +hello world\r (esc)
405 +hello world\r (esc)
406 +\r (esc)
406 +\r (esc)
407 +goodbye\r (no-eol) (esc)
407 +goodbye\r (no-eol) (esc)
408 world
408 world
409
409
410 Test \r (carriage return) as used in "DOS" line endings:
411
412 $ printf 'hello world \r\n\t\ngoodbye world\n' >foo
413
414 $ hg ndiff --ignore-space-at-eol
415 diff -r 540c40a65b78 foo
416 --- a/foo
417 +++ b/foo
418 @@ -1,2 +1,3 @@
419 hello world
420 +\t (esc)
421 goodbye world
422
410 No completely blank lines to ignore:
423 No completely blank lines to ignore:
411
424
425 $ printf 'hello world\r\n\r\ngoodbye\rworld\n' >foo
426
412 $ hg ndiff --ignore-blank-lines
427 $ hg ndiff --ignore-blank-lines
413 diff -r 540c40a65b78 foo
428 diff -r 540c40a65b78 foo
414 --- a/foo
429 --- a/foo
415 +++ b/foo
430 +++ b/foo
416 @@ -1,2 +1,3 @@
431 @@ -1,2 +1,3 @@
417 -hello world
432 -hello world
418 -goodbye world
433 -goodbye world
419 +hello world\r (esc)
434 +hello world\r (esc)
420 +\r (esc)
435 +\r (esc)
421 +goodbye\r (no-eol) (esc)
436 +goodbye\r (no-eol) (esc)
422 world
437 world
423
438
424 Only new line noticed:
439 Only new line noticed:
425
440
426 $ hg ndiff --ignore-space-change
441 $ hg ndiff --ignore-space-change
427 diff -r 540c40a65b78 foo
442 diff -r 540c40a65b78 foo
428 --- a/foo
443 --- a/foo
429 +++ b/foo
444 +++ b/foo
430 @@ -1,2 +1,3 @@
445 @@ -1,2 +1,3 @@
431 hello world
446 hello world
432 +\r (esc)
447 +\r (esc)
433 goodbye world
448 goodbye world
434
449
435 $ hg ndiff --ignore-all-space
450 $ hg ndiff --ignore-all-space
436 diff -r 540c40a65b78 foo
451 diff -r 540c40a65b78 foo
437 --- a/foo
452 --- a/foo
438 +++ b/foo
453 +++ b/foo
439 @@ -1,2 +1,3 @@
454 @@ -1,2 +1,3 @@
440 hello world
455 hello world
441 +\r (esc)
456 +\r (esc)
442 goodbye world
457 goodbye world
443
458
444 New line not noticed when space change ignored:
459 New line not noticed when space change ignored:
445
460
446 $ hg ndiff --ignore-blank-lines --ignore-all-space
461 $ hg ndiff --ignore-blank-lines --ignore-all-space
447
462
448 Do not ignore all newlines, only blank lines
463 Do not ignore all newlines, only blank lines
449
464
450 $ printf 'hello \nworld\ngoodbye world\n' > foo
465 $ printf 'hello \nworld\ngoodbye world\n' > foo
451 $ hg ndiff --ignore-blank-lines
466 $ hg ndiff --ignore-blank-lines
452 diff -r 540c40a65b78 foo
467 diff -r 540c40a65b78 foo
453 --- a/foo
468 --- a/foo
454 +++ b/foo
469 +++ b/foo
455 @@ -1,2 +1,3 @@
470 @@ -1,2 +1,3 @@
456 -hello world
471 -hello world
457 +hello
472 +hello
458 +world
473 +world
459 goodbye world
474 goodbye world
460
475
461 Test hunk offsets adjustments with --ignore-blank-lines
476 Test hunk offsets adjustments with --ignore-blank-lines
462
477
463 $ hg revert -aC
478 $ hg revert -aC
464 reverting foo
479 reverting foo
465 $ printf '\nb\nx\nd\n' > a
480 $ printf '\nb\nx\nd\n' > a
466 $ printf 'b\ny\nd\n' > b
481 $ printf 'b\ny\nd\n' > b
467 $ hg add a b
482 $ hg add a b
468 $ hg ci -m add
483 $ hg ci -m add
469 $ hg cat -r . a > b
484 $ hg cat -r . a > b
470 $ hg cat -r . b > a
485 $ hg cat -r . b > a
471 $ hg diff -B --nodates a > ../diffa
486 $ hg diff -B --nodates a > ../diffa
472 $ cat ../diffa
487 $ cat ../diffa
473 diff -r 0e66aa54f318 a
488 diff -r 0e66aa54f318 a
474 --- a/a
489 --- a/a
475 +++ b/a
490 +++ b/a
476 @@ -1,4 +1,4 @@
491 @@ -1,4 +1,4 @@
477
492
478 b
493 b
479 -x
494 -x
480 +y
495 +y
481 d
496 d
482 $ hg diff -B --nodates b > ../diffb
497 $ hg diff -B --nodates b > ../diffb
483 $ cat ../diffb
498 $ cat ../diffb
484 diff -r 0e66aa54f318 b
499 diff -r 0e66aa54f318 b
485 --- a/b
500 --- a/b
486 +++ b/b
501 +++ b/b
487 @@ -1,3 +1,3 @@
502 @@ -1,3 +1,3 @@
488 b
503 b
489 -y
504 -y
490 +x
505 +x
491 d
506 d
492 $ hg revert -aC
507 $ hg revert -aC
493 reverting a
508 reverting a
494 reverting b
509 reverting b
495 $ hg import --no-commit ../diffa
510 $ hg import --no-commit ../diffa
496 applying ../diffa
511 applying ../diffa
497 $ hg revert -aC
512 $ hg revert -aC
498 reverting a
513 reverting a
499 $ hg import --no-commit ../diffb
514 $ hg import --no-commit ../diffb
500 applying ../diffb
515 applying ../diffb
501 $ hg revert -aC
516 $ hg revert -aC
502 reverting b
517 reverting b
@@ -1,3367 +1,3368 b''
1 Short help:
1 Short help:
2
2
3 $ hg
3 $ hg
4 Mercurial Distributed SCM
4 Mercurial Distributed SCM
5
5
6 basic commands:
6 basic commands:
7
7
8 add add the specified files on the next commit
8 add add the specified files on the next commit
9 annotate show changeset information by line for each file
9 annotate show changeset information by line for each file
10 clone make a copy of an existing repository
10 clone make a copy of an existing repository
11 commit commit the specified files or all outstanding changes
11 commit commit the specified files or all outstanding changes
12 diff diff repository (or selected files)
12 diff diff repository (or selected files)
13 export dump the header and diffs for one or more changesets
13 export dump the header and diffs for one or more changesets
14 forget forget the specified files on the next commit
14 forget forget the specified files on the next commit
15 init create a new repository in the given directory
15 init create a new repository in the given directory
16 log show revision history of entire repository or files
16 log show revision history of entire repository or files
17 merge merge another revision into working directory
17 merge merge another revision into working directory
18 pull pull changes from the specified source
18 pull pull changes from the specified source
19 push push changes to the specified destination
19 push push changes to the specified destination
20 remove remove the specified files on the next commit
20 remove remove the specified files on the next commit
21 serve start stand-alone webserver
21 serve start stand-alone webserver
22 status show changed files in the working directory
22 status show changed files in the working directory
23 summary summarize working directory state
23 summary summarize working directory state
24 update update working directory (or switch revisions)
24 update update working directory (or switch revisions)
25
25
26 (use 'hg help' for the full list of commands or 'hg -v' for details)
26 (use 'hg help' for the full list of commands or 'hg -v' for details)
27
27
28 $ hg -q
28 $ hg -q
29 add add the specified files on the next commit
29 add add the specified files on the next commit
30 annotate show changeset information by line for each file
30 annotate show changeset information by line for each file
31 clone make a copy of an existing repository
31 clone make a copy of an existing repository
32 commit commit the specified files or all outstanding changes
32 commit commit the specified files or all outstanding changes
33 diff diff repository (or selected files)
33 diff diff repository (or selected files)
34 export dump the header and diffs for one or more changesets
34 export dump the header and diffs for one or more changesets
35 forget forget the specified files on the next commit
35 forget forget the specified files on the next commit
36 init create a new repository in the given directory
36 init create a new repository in the given directory
37 log show revision history of entire repository or files
37 log show revision history of entire repository or files
38 merge merge another revision into working directory
38 merge merge another revision into working directory
39 pull pull changes from the specified source
39 pull pull changes from the specified source
40 push push changes to the specified destination
40 push push changes to the specified destination
41 remove remove the specified files on the next commit
41 remove remove the specified files on the next commit
42 serve start stand-alone webserver
42 serve start stand-alone webserver
43 status show changed files in the working directory
43 status show changed files in the working directory
44 summary summarize working directory state
44 summary summarize working directory state
45 update update working directory (or switch revisions)
45 update update working directory (or switch revisions)
46
46
47 $ hg help
47 $ hg help
48 Mercurial Distributed SCM
48 Mercurial Distributed SCM
49
49
50 list of commands:
50 list of commands:
51
51
52 add add the specified files on the next commit
52 add add the specified files on the next commit
53 addremove add all new files, delete all missing files
53 addremove add all new files, delete all missing files
54 annotate show changeset information by line for each file
54 annotate show changeset information by line for each file
55 archive create an unversioned archive of a repository revision
55 archive create an unversioned archive of a repository revision
56 backout reverse effect of earlier changeset
56 backout reverse effect of earlier changeset
57 bisect subdivision search of changesets
57 bisect subdivision search of changesets
58 bookmarks create a new bookmark or list existing bookmarks
58 bookmarks create a new bookmark or list existing bookmarks
59 branch set or show the current branch name
59 branch set or show the current branch name
60 branches list repository named branches
60 branches list repository named branches
61 bundle create a bundle file
61 bundle create a bundle file
62 cat output the current or given revision of files
62 cat output the current or given revision of files
63 clone make a copy of an existing repository
63 clone make a copy of an existing repository
64 commit commit the specified files or all outstanding changes
64 commit commit the specified files or all outstanding changes
65 config show combined config settings from all hgrc files
65 config show combined config settings from all hgrc files
66 copy mark files as copied for the next commit
66 copy mark files as copied for the next commit
67 diff diff repository (or selected files)
67 diff diff repository (or selected files)
68 export dump the header and diffs for one or more changesets
68 export dump the header and diffs for one or more changesets
69 files list tracked files
69 files list tracked files
70 forget forget the specified files on the next commit
70 forget forget the specified files on the next commit
71 graft copy changes from other branches onto the current branch
71 graft copy changes from other branches onto the current branch
72 grep search revision history for a pattern in specified files
72 grep search revision history for a pattern in specified files
73 heads show branch heads
73 heads show branch heads
74 help show help for a given topic or a help overview
74 help show help for a given topic or a help overview
75 identify identify the working directory or specified revision
75 identify identify the working directory or specified revision
76 import import an ordered set of patches
76 import import an ordered set of patches
77 incoming show new changesets found in source
77 incoming show new changesets found in source
78 init create a new repository in the given directory
78 init create a new repository in the given directory
79 log show revision history of entire repository or files
79 log show revision history of entire repository or files
80 manifest output the current or given revision of the project manifest
80 manifest output the current or given revision of the project manifest
81 merge merge another revision into working directory
81 merge merge another revision into working directory
82 outgoing show changesets not found in the destination
82 outgoing show changesets not found in the destination
83 paths show aliases for remote repositories
83 paths show aliases for remote repositories
84 phase set or show the current phase name
84 phase set or show the current phase name
85 pull pull changes from the specified source
85 pull pull changes from the specified source
86 push push changes to the specified destination
86 push push changes to the specified destination
87 recover roll back an interrupted transaction
87 recover roll back an interrupted transaction
88 remove remove the specified files on the next commit
88 remove remove the specified files on the next commit
89 rename rename files; equivalent of copy + remove
89 rename rename files; equivalent of copy + remove
90 resolve redo merges or set/view the merge status of files
90 resolve redo merges or set/view the merge status of files
91 revert restore files to their checkout state
91 revert restore files to their checkout state
92 root print the root (top) of the current working directory
92 root print the root (top) of the current working directory
93 serve start stand-alone webserver
93 serve start stand-alone webserver
94 status show changed files in the working directory
94 status show changed files in the working directory
95 summary summarize working directory state
95 summary summarize working directory state
96 tag add one or more tags for the current or given revision
96 tag add one or more tags for the current or given revision
97 tags list repository tags
97 tags list repository tags
98 unbundle apply one or more bundle files
98 unbundle apply one or more bundle files
99 update update working directory (or switch revisions)
99 update update working directory (or switch revisions)
100 verify verify the integrity of the repository
100 verify verify the integrity of the repository
101 version output version and copyright information
101 version output version and copyright information
102
102
103 additional help topics:
103 additional help topics:
104
104
105 bundlespec Bundle File Formats
105 bundlespec Bundle File Formats
106 color Colorizing Outputs
106 color Colorizing Outputs
107 config Configuration Files
107 config Configuration Files
108 dates Date Formats
108 dates Date Formats
109 diffs Diff Formats
109 diffs Diff Formats
110 environment Environment Variables
110 environment Environment Variables
111 extensions Using Additional Features
111 extensions Using Additional Features
112 filesets Specifying File Sets
112 filesets Specifying File Sets
113 glossary Glossary
113 glossary Glossary
114 hgignore Syntax for Mercurial Ignore Files
114 hgignore Syntax for Mercurial Ignore Files
115 hgweb Configuring hgweb
115 hgweb Configuring hgweb
116 internals Technical implementation topics
116 internals Technical implementation topics
117 merge-tools Merge Tools
117 merge-tools Merge Tools
118 pager Pager Support
118 pager Pager Support
119 patterns File Name Patterns
119 patterns File Name Patterns
120 phases Working with Phases
120 phases Working with Phases
121 revisions Specifying Revisions
121 revisions Specifying Revisions
122 scripting Using Mercurial from scripts and automation
122 scripting Using Mercurial from scripts and automation
123 subrepos Subrepositories
123 subrepos Subrepositories
124 templating Template Usage
124 templating Template Usage
125 urls URL Paths
125 urls URL Paths
126
126
127 (use 'hg help -v' to show built-in aliases and global options)
127 (use 'hg help -v' to show built-in aliases and global options)
128
128
129 $ hg -q help
129 $ hg -q help
130 add add the specified files on the next commit
130 add add the specified files on the next commit
131 addremove add all new files, delete all missing files
131 addremove add all new files, delete all missing files
132 annotate show changeset information by line for each file
132 annotate show changeset information by line for each file
133 archive create an unversioned archive of a repository revision
133 archive create an unversioned archive of a repository revision
134 backout reverse effect of earlier changeset
134 backout reverse effect of earlier changeset
135 bisect subdivision search of changesets
135 bisect subdivision search of changesets
136 bookmarks create a new bookmark or list existing bookmarks
136 bookmarks create a new bookmark or list existing bookmarks
137 branch set or show the current branch name
137 branch set or show the current branch name
138 branches list repository named branches
138 branches list repository named branches
139 bundle create a bundle file
139 bundle create a bundle file
140 cat output the current or given revision of files
140 cat output the current or given revision of files
141 clone make a copy of an existing repository
141 clone make a copy of an existing repository
142 commit commit the specified files or all outstanding changes
142 commit commit the specified files or all outstanding changes
143 config show combined config settings from all hgrc files
143 config show combined config settings from all hgrc files
144 copy mark files as copied for the next commit
144 copy mark files as copied for the next commit
145 diff diff repository (or selected files)
145 diff diff repository (or selected files)
146 export dump the header and diffs for one or more changesets
146 export dump the header and diffs for one or more changesets
147 files list tracked files
147 files list tracked files
148 forget forget the specified files on the next commit
148 forget forget the specified files on the next commit
149 graft copy changes from other branches onto the current branch
149 graft copy changes from other branches onto the current branch
150 grep search revision history for a pattern in specified files
150 grep search revision history for a pattern in specified files
151 heads show branch heads
151 heads show branch heads
152 help show help for a given topic or a help overview
152 help show help for a given topic or a help overview
153 identify identify the working directory or specified revision
153 identify identify the working directory or specified revision
154 import import an ordered set of patches
154 import import an ordered set of patches
155 incoming show new changesets found in source
155 incoming show new changesets found in source
156 init create a new repository in the given directory
156 init create a new repository in the given directory
157 log show revision history of entire repository or files
157 log show revision history of entire repository or files
158 manifest output the current or given revision of the project manifest
158 manifest output the current or given revision of the project manifest
159 merge merge another revision into working directory
159 merge merge another revision into working directory
160 outgoing show changesets not found in the destination
160 outgoing show changesets not found in the destination
161 paths show aliases for remote repositories
161 paths show aliases for remote repositories
162 phase set or show the current phase name
162 phase set or show the current phase name
163 pull pull changes from the specified source
163 pull pull changes from the specified source
164 push push changes to the specified destination
164 push push changes to the specified destination
165 recover roll back an interrupted transaction
165 recover roll back an interrupted transaction
166 remove remove the specified files on the next commit
166 remove remove the specified files on the next commit
167 rename rename files; equivalent of copy + remove
167 rename rename files; equivalent of copy + remove
168 resolve redo merges or set/view the merge status of files
168 resolve redo merges or set/view the merge status of files
169 revert restore files to their checkout state
169 revert restore files to their checkout state
170 root print the root (top) of the current working directory
170 root print the root (top) of the current working directory
171 serve start stand-alone webserver
171 serve start stand-alone webserver
172 status show changed files in the working directory
172 status show changed files in the working directory
173 summary summarize working directory state
173 summary summarize working directory state
174 tag add one or more tags for the current or given revision
174 tag add one or more tags for the current or given revision
175 tags list repository tags
175 tags list repository tags
176 unbundle apply one or more bundle files
176 unbundle apply one or more bundle files
177 update update working directory (or switch revisions)
177 update update working directory (or switch revisions)
178 verify verify the integrity of the repository
178 verify verify the integrity of the repository
179 version output version and copyright information
179 version output version and copyright information
180
180
181 additional help topics:
181 additional help topics:
182
182
183 bundlespec Bundle File Formats
183 bundlespec Bundle File Formats
184 color Colorizing Outputs
184 color Colorizing Outputs
185 config Configuration Files
185 config Configuration Files
186 dates Date Formats
186 dates Date Formats
187 diffs Diff Formats
187 diffs Diff Formats
188 environment Environment Variables
188 environment Environment Variables
189 extensions Using Additional Features
189 extensions Using Additional Features
190 filesets Specifying File Sets
190 filesets Specifying File Sets
191 glossary Glossary
191 glossary Glossary
192 hgignore Syntax for Mercurial Ignore Files
192 hgignore Syntax for Mercurial Ignore Files
193 hgweb Configuring hgweb
193 hgweb Configuring hgweb
194 internals Technical implementation topics
194 internals Technical implementation topics
195 merge-tools Merge Tools
195 merge-tools Merge Tools
196 pager Pager Support
196 pager Pager Support
197 patterns File Name Patterns
197 patterns File Name Patterns
198 phases Working with Phases
198 phases Working with Phases
199 revisions Specifying Revisions
199 revisions Specifying Revisions
200 scripting Using Mercurial from scripts and automation
200 scripting Using Mercurial from scripts and automation
201 subrepos Subrepositories
201 subrepos Subrepositories
202 templating Template Usage
202 templating Template Usage
203 urls URL Paths
203 urls URL Paths
204
204
205 Test extension help:
205 Test extension help:
206 $ hg help extensions --config extensions.rebase= --config extensions.children=
206 $ hg help extensions --config extensions.rebase= --config extensions.children=
207 Using Additional Features
207 Using Additional Features
208 """""""""""""""""""""""""
208 """""""""""""""""""""""""
209
209
210 Mercurial has the ability to add new features through the use of
210 Mercurial has the ability to add new features through the use of
211 extensions. Extensions may add new commands, add options to existing
211 extensions. Extensions may add new commands, add options to existing
212 commands, change the default behavior of commands, or implement hooks.
212 commands, change the default behavior of commands, or implement hooks.
213
213
214 To enable the "foo" extension, either shipped with Mercurial or in the
214 To enable the "foo" extension, either shipped with Mercurial or in the
215 Python search path, create an entry for it in your configuration file,
215 Python search path, create an entry for it in your configuration file,
216 like this:
216 like this:
217
217
218 [extensions]
218 [extensions]
219 foo =
219 foo =
220
220
221 You may also specify the full path to an extension:
221 You may also specify the full path to an extension:
222
222
223 [extensions]
223 [extensions]
224 myfeature = ~/.hgext/myfeature.py
224 myfeature = ~/.hgext/myfeature.py
225
225
226 See 'hg help config' for more information on configuration files.
226 See 'hg help config' for more information on configuration files.
227
227
228 Extensions are not loaded by default for a variety of reasons: they can
228 Extensions are not loaded by default for a variety of reasons: they can
229 increase startup overhead; they may be meant for advanced usage only; they
229 increase startup overhead; they may be meant for advanced usage only; they
230 may provide potentially dangerous abilities (such as letting you destroy
230 may provide potentially dangerous abilities (such as letting you destroy
231 or modify history); they might not be ready for prime time; or they may
231 or modify history); they might not be ready for prime time; or they may
232 alter some usual behaviors of stock Mercurial. It is thus up to the user
232 alter some usual behaviors of stock Mercurial. It is thus up to the user
233 to activate extensions as needed.
233 to activate extensions as needed.
234
234
235 To explicitly disable an extension enabled in a configuration file of
235 To explicitly disable an extension enabled in a configuration file of
236 broader scope, prepend its path with !:
236 broader scope, prepend its path with !:
237
237
238 [extensions]
238 [extensions]
239 # disabling extension bar residing in /path/to/extension/bar.py
239 # disabling extension bar residing in /path/to/extension/bar.py
240 bar = !/path/to/extension/bar.py
240 bar = !/path/to/extension/bar.py
241 # ditto, but no path was supplied for extension baz
241 # ditto, but no path was supplied for extension baz
242 baz = !
242 baz = !
243
243
244 enabled extensions:
244 enabled extensions:
245
245
246 children command to display child changesets (DEPRECATED)
246 children command to display child changesets (DEPRECATED)
247 rebase command to move sets of revisions to a different ancestor
247 rebase command to move sets of revisions to a different ancestor
248
248
249 disabled extensions:
249 disabled extensions:
250
250
251 acl hooks for controlling repository access
251 acl hooks for controlling repository access
252 blackbox log repository events to a blackbox for debugging
252 blackbox log repository events to a blackbox for debugging
253 bugzilla hooks for integrating with the Bugzilla bug tracker
253 bugzilla hooks for integrating with the Bugzilla bug tracker
254 censor erase file content at a given revision
254 censor erase file content at a given revision
255 churn command to display statistics about repository history
255 churn command to display statistics about repository history
256 clonebundles advertise pre-generated bundles to seed clones
256 clonebundles advertise pre-generated bundles to seed clones
257 convert import revisions from foreign VCS repositories into
257 convert import revisions from foreign VCS repositories into
258 Mercurial
258 Mercurial
259 eol automatically manage newlines in repository files
259 eol automatically manage newlines in repository files
260 extdiff command to allow external programs to compare revisions
260 extdiff command to allow external programs to compare revisions
261 factotum http authentication with factotum
261 factotum http authentication with factotum
262 gpg commands to sign and verify changesets
262 gpg commands to sign and verify changesets
263 hgk browse the repository in a graphical way
263 hgk browse the repository in a graphical way
264 highlight syntax highlighting for hgweb (requires Pygments)
264 highlight syntax highlighting for hgweb (requires Pygments)
265 histedit interactive history editing
265 histedit interactive history editing
266 keyword expand keywords in tracked files
266 keyword expand keywords in tracked files
267 largefiles track large binary files
267 largefiles track large binary files
268 mq manage a stack of patches
268 mq manage a stack of patches
269 notify hooks for sending email push notifications
269 notify hooks for sending email push notifications
270 patchbomb command to send changesets as (a series of) patch emails
270 patchbomb command to send changesets as (a series of) patch emails
271 purge command to delete untracked files from the working
271 purge command to delete untracked files from the working
272 directory
272 directory
273 relink recreates hardlinks between repository clones
273 relink recreates hardlinks between repository clones
274 schemes extend schemes with shortcuts to repository swarms
274 schemes extend schemes with shortcuts to repository swarms
275 share share a common history between several working directories
275 share share a common history between several working directories
276 shelve save and restore changes to the working directory
276 shelve save and restore changes to the working directory
277 strip strip changesets and their descendants from history
277 strip strip changesets and their descendants from history
278 transplant command to transplant changesets from another branch
278 transplant command to transplant changesets from another branch
279 win32mbcs allow the use of MBCS paths with problematic encodings
279 win32mbcs allow the use of MBCS paths with problematic encodings
280 zeroconf discover and advertise repositories on the local network
280 zeroconf discover and advertise repositories on the local network
281
281
282 Verify that extension keywords appear in help templates
282 Verify that extension keywords appear in help templates
283
283
284 $ hg help --config extensions.transplant= templating|grep transplant > /dev/null
284 $ hg help --config extensions.transplant= templating|grep transplant > /dev/null
285
285
286 Test short command list with verbose option
286 Test short command list with verbose option
287
287
288 $ hg -v help shortlist
288 $ hg -v help shortlist
289 Mercurial Distributed SCM
289 Mercurial Distributed SCM
290
290
291 basic commands:
291 basic commands:
292
292
293 add add the specified files on the next commit
293 add add the specified files on the next commit
294 annotate, blame
294 annotate, blame
295 show changeset information by line for each file
295 show changeset information by line for each file
296 clone make a copy of an existing repository
296 clone make a copy of an existing repository
297 commit, ci commit the specified files or all outstanding changes
297 commit, ci commit the specified files or all outstanding changes
298 diff diff repository (or selected files)
298 diff diff repository (or selected files)
299 export dump the header and diffs for one or more changesets
299 export dump the header and diffs for one or more changesets
300 forget forget the specified files on the next commit
300 forget forget the specified files on the next commit
301 init create a new repository in the given directory
301 init create a new repository in the given directory
302 log, history show revision history of entire repository or files
302 log, history show revision history of entire repository or files
303 merge merge another revision into working directory
303 merge merge another revision into working directory
304 pull pull changes from the specified source
304 pull pull changes from the specified source
305 push push changes to the specified destination
305 push push changes to the specified destination
306 remove, rm remove the specified files on the next commit
306 remove, rm remove the specified files on the next commit
307 serve start stand-alone webserver
307 serve start stand-alone webserver
308 status, st show changed files in the working directory
308 status, st show changed files in the working directory
309 summary, sum summarize working directory state
309 summary, sum summarize working directory state
310 update, up, checkout, co
310 update, up, checkout, co
311 update working directory (or switch revisions)
311 update working directory (or switch revisions)
312
312
313 global options ([+] can be repeated):
313 global options ([+] can be repeated):
314
314
315 -R --repository REPO repository root directory or name of overlay bundle
315 -R --repository REPO repository root directory or name of overlay bundle
316 file
316 file
317 --cwd DIR change working directory
317 --cwd DIR change working directory
318 -y --noninteractive do not prompt, automatically pick the first choice for
318 -y --noninteractive do not prompt, automatically pick the first choice for
319 all prompts
319 all prompts
320 -q --quiet suppress output
320 -q --quiet suppress output
321 -v --verbose enable additional output
321 -v --verbose enable additional output
322 --color TYPE when to colorize (boolean, always, auto, never, or
322 --color TYPE when to colorize (boolean, always, auto, never, or
323 debug)
323 debug)
324 --config CONFIG [+] set/override config option (use 'section.name=value')
324 --config CONFIG [+] set/override config option (use 'section.name=value')
325 --debug enable debugging output
325 --debug enable debugging output
326 --debugger start debugger
326 --debugger start debugger
327 --encoding ENCODE set the charset encoding (default: ascii)
327 --encoding ENCODE set the charset encoding (default: ascii)
328 --encodingmode MODE set the charset encoding mode (default: strict)
328 --encodingmode MODE set the charset encoding mode (default: strict)
329 --traceback always print a traceback on exception
329 --traceback always print a traceback on exception
330 --time time how long the command takes
330 --time time how long the command takes
331 --profile print command execution profile
331 --profile print command execution profile
332 --version output version information and exit
332 --version output version information and exit
333 -h --help display help and exit
333 -h --help display help and exit
334 --hidden consider hidden changesets
334 --hidden consider hidden changesets
335 --pager TYPE when to paginate (boolean, always, auto, or never)
335 --pager TYPE when to paginate (boolean, always, auto, or never)
336 (default: auto)
336 (default: auto)
337
337
338 (use 'hg help' for the full list of commands)
338 (use 'hg help' for the full list of commands)
339
339
340 $ hg add -h
340 $ hg add -h
341 hg add [OPTION]... [FILE]...
341 hg add [OPTION]... [FILE]...
342
342
343 add the specified files on the next commit
343 add the specified files on the next commit
344
344
345 Schedule files to be version controlled and added to the repository.
345 Schedule files to be version controlled and added to the repository.
346
346
347 The files will be added to the repository at the next commit. To undo an
347 The files will be added to the repository at the next commit. To undo an
348 add before that, see 'hg forget'.
348 add before that, see 'hg forget'.
349
349
350 If no names are given, add all files to the repository (except files
350 If no names are given, add all files to the repository (except files
351 matching ".hgignore").
351 matching ".hgignore").
352
352
353 Returns 0 if all files are successfully added.
353 Returns 0 if all files are successfully added.
354
354
355 options ([+] can be repeated):
355 options ([+] can be repeated):
356
356
357 -I --include PATTERN [+] include names matching the given patterns
357 -I --include PATTERN [+] include names matching the given patterns
358 -X --exclude PATTERN [+] exclude names matching the given patterns
358 -X --exclude PATTERN [+] exclude names matching the given patterns
359 -S --subrepos recurse into subrepositories
359 -S --subrepos recurse into subrepositories
360 -n --dry-run do not perform actions, just print output
360 -n --dry-run do not perform actions, just print output
361
361
362 (some details hidden, use --verbose to show complete help)
362 (some details hidden, use --verbose to show complete help)
363
363
364 Verbose help for add
364 Verbose help for add
365
365
366 $ hg add -hv
366 $ hg add -hv
367 hg add [OPTION]... [FILE]...
367 hg add [OPTION]... [FILE]...
368
368
369 add the specified files on the next commit
369 add the specified files on the next commit
370
370
371 Schedule files to be version controlled and added to the repository.
371 Schedule files to be version controlled and added to the repository.
372
372
373 The files will be added to the repository at the next commit. To undo an
373 The files will be added to the repository at the next commit. To undo an
374 add before that, see 'hg forget'.
374 add before that, see 'hg forget'.
375
375
376 If no names are given, add all files to the repository (except files
376 If no names are given, add all files to the repository (except files
377 matching ".hgignore").
377 matching ".hgignore").
378
378
379 Examples:
379 Examples:
380
380
381 - New (unknown) files are added automatically by 'hg add':
381 - New (unknown) files are added automatically by 'hg add':
382
382
383 $ ls
383 $ ls
384 foo.c
384 foo.c
385 $ hg status
385 $ hg status
386 ? foo.c
386 ? foo.c
387 $ hg add
387 $ hg add
388 adding foo.c
388 adding foo.c
389 $ hg status
389 $ hg status
390 A foo.c
390 A foo.c
391
391
392 - Specific files to be added can be specified:
392 - Specific files to be added can be specified:
393
393
394 $ ls
394 $ ls
395 bar.c foo.c
395 bar.c foo.c
396 $ hg status
396 $ hg status
397 ? bar.c
397 ? bar.c
398 ? foo.c
398 ? foo.c
399 $ hg add bar.c
399 $ hg add bar.c
400 $ hg status
400 $ hg status
401 A bar.c
401 A bar.c
402 ? foo.c
402 ? foo.c
403
403
404 Returns 0 if all files are successfully added.
404 Returns 0 if all files are successfully added.
405
405
406 options ([+] can be repeated):
406 options ([+] can be repeated):
407
407
408 -I --include PATTERN [+] include names matching the given patterns
408 -I --include PATTERN [+] include names matching the given patterns
409 -X --exclude PATTERN [+] exclude names matching the given patterns
409 -X --exclude PATTERN [+] exclude names matching the given patterns
410 -S --subrepos recurse into subrepositories
410 -S --subrepos recurse into subrepositories
411 -n --dry-run do not perform actions, just print output
411 -n --dry-run do not perform actions, just print output
412
412
413 global options ([+] can be repeated):
413 global options ([+] can be repeated):
414
414
415 -R --repository REPO repository root directory or name of overlay bundle
415 -R --repository REPO repository root directory or name of overlay bundle
416 file
416 file
417 --cwd DIR change working directory
417 --cwd DIR change working directory
418 -y --noninteractive do not prompt, automatically pick the first choice for
418 -y --noninteractive do not prompt, automatically pick the first choice for
419 all prompts
419 all prompts
420 -q --quiet suppress output
420 -q --quiet suppress output
421 -v --verbose enable additional output
421 -v --verbose enable additional output
422 --color TYPE when to colorize (boolean, always, auto, never, or
422 --color TYPE when to colorize (boolean, always, auto, never, or
423 debug)
423 debug)
424 --config CONFIG [+] set/override config option (use 'section.name=value')
424 --config CONFIG [+] set/override config option (use 'section.name=value')
425 --debug enable debugging output
425 --debug enable debugging output
426 --debugger start debugger
426 --debugger start debugger
427 --encoding ENCODE set the charset encoding (default: ascii)
427 --encoding ENCODE set the charset encoding (default: ascii)
428 --encodingmode MODE set the charset encoding mode (default: strict)
428 --encodingmode MODE set the charset encoding mode (default: strict)
429 --traceback always print a traceback on exception
429 --traceback always print a traceback on exception
430 --time time how long the command takes
430 --time time how long the command takes
431 --profile print command execution profile
431 --profile print command execution profile
432 --version output version information and exit
432 --version output version information and exit
433 -h --help display help and exit
433 -h --help display help and exit
434 --hidden consider hidden changesets
434 --hidden consider hidden changesets
435 --pager TYPE when to paginate (boolean, always, auto, or never)
435 --pager TYPE when to paginate (boolean, always, auto, or never)
436 (default: auto)
436 (default: auto)
437
437
438 Test the textwidth config option
438 Test the textwidth config option
439
439
440 $ hg root -h --config ui.textwidth=50
440 $ hg root -h --config ui.textwidth=50
441 hg root
441 hg root
442
442
443 print the root (top) of the current working
443 print the root (top) of the current working
444 directory
444 directory
445
445
446 Print the root directory of the current
446 Print the root directory of the current
447 repository.
447 repository.
448
448
449 Returns 0 on success.
449 Returns 0 on success.
450
450
451 (some details hidden, use --verbose to show
451 (some details hidden, use --verbose to show
452 complete help)
452 complete help)
453
453
454 Test help option with version option
454 Test help option with version option
455
455
456 $ hg add -h --version
456 $ hg add -h --version
457 Mercurial Distributed SCM (version *) (glob)
457 Mercurial Distributed SCM (version *) (glob)
458 (see https://mercurial-scm.org for more information)
458 (see https://mercurial-scm.org for more information)
459
459
460 Copyright (C) 2005-* Matt Mackall and others (glob)
460 Copyright (C) 2005-* Matt Mackall and others (glob)
461 This is free software; see the source for copying conditions. There is NO
461 This is free software; see the source for copying conditions. There is NO
462 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
462 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
463
463
464 $ hg add --skjdfks
464 $ hg add --skjdfks
465 hg add: option --skjdfks not recognized
465 hg add: option --skjdfks not recognized
466 hg add [OPTION]... [FILE]...
466 hg add [OPTION]... [FILE]...
467
467
468 add the specified files on the next commit
468 add the specified files on the next commit
469
469
470 options ([+] can be repeated):
470 options ([+] can be repeated):
471
471
472 -I --include PATTERN [+] include names matching the given patterns
472 -I --include PATTERN [+] include names matching the given patterns
473 -X --exclude PATTERN [+] exclude names matching the given patterns
473 -X --exclude PATTERN [+] exclude names matching the given patterns
474 -S --subrepos recurse into subrepositories
474 -S --subrepos recurse into subrepositories
475 -n --dry-run do not perform actions, just print output
475 -n --dry-run do not perform actions, just print output
476
476
477 (use 'hg add -h' to show more help)
477 (use 'hg add -h' to show more help)
478 [255]
478 [255]
479
479
480 Test ambiguous command help
480 Test ambiguous command help
481
481
482 $ hg help ad
482 $ hg help ad
483 list of commands:
483 list of commands:
484
484
485 add add the specified files on the next commit
485 add add the specified files on the next commit
486 addremove add all new files, delete all missing files
486 addremove add all new files, delete all missing files
487
487
488 (use 'hg help -v ad' to show built-in aliases and global options)
488 (use 'hg help -v ad' to show built-in aliases and global options)
489
489
490 Test command without options
490 Test command without options
491
491
492 $ hg help verify
492 $ hg help verify
493 hg verify
493 hg verify
494
494
495 verify the integrity of the repository
495 verify the integrity of the repository
496
496
497 Verify the integrity of the current repository.
497 Verify the integrity of the current repository.
498
498
499 This will perform an extensive check of the repository's integrity,
499 This will perform an extensive check of the repository's integrity,
500 validating the hashes and checksums of each entry in the changelog,
500 validating the hashes and checksums of each entry in the changelog,
501 manifest, and tracked files, as well as the integrity of their crosslinks
501 manifest, and tracked files, as well as the integrity of their crosslinks
502 and indices.
502 and indices.
503
503
504 Please see https://mercurial-scm.org/wiki/RepositoryCorruption for more
504 Please see https://mercurial-scm.org/wiki/RepositoryCorruption for more
505 information about recovery from corruption of the repository.
505 information about recovery from corruption of the repository.
506
506
507 Returns 0 on success, 1 if errors are encountered.
507 Returns 0 on success, 1 if errors are encountered.
508
508
509 (some details hidden, use --verbose to show complete help)
509 (some details hidden, use --verbose to show complete help)
510
510
511 $ hg help diff
511 $ hg help diff
512 hg diff [OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...
512 hg diff [OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...
513
513
514 diff repository (or selected files)
514 diff repository (or selected files)
515
515
516 Show differences between revisions for the specified files.
516 Show differences between revisions for the specified files.
517
517
518 Differences between files are shown using the unified diff format.
518 Differences between files are shown using the unified diff format.
519
519
520 Note:
520 Note:
521 'hg diff' may generate unexpected results for merges, as it will
521 'hg diff' may generate unexpected results for merges, as it will
522 default to comparing against the working directory's first parent
522 default to comparing against the working directory's first parent
523 changeset if no revisions are specified.
523 changeset if no revisions are specified.
524
524
525 When two revision arguments are given, then changes are shown between
525 When two revision arguments are given, then changes are shown between
526 those revisions. If only one revision is specified then that revision is
526 those revisions. If only one revision is specified then that revision is
527 compared to the working directory, and, when no revisions are specified,
527 compared to the working directory, and, when no revisions are specified,
528 the working directory files are compared to its first parent.
528 the working directory files are compared to its first parent.
529
529
530 Alternatively you can specify -c/--change with a revision to see the
530 Alternatively you can specify -c/--change with a revision to see the
531 changes in that changeset relative to its first parent.
531 changes in that changeset relative to its first parent.
532
532
533 Without the -a/--text option, diff will avoid generating diffs of files it
533 Without the -a/--text option, diff will avoid generating diffs of files it
534 detects as binary. With -a, diff will generate a diff anyway, probably
534 detects as binary. With -a, diff will generate a diff anyway, probably
535 with undesirable results.
535 with undesirable results.
536
536
537 Use the -g/--git option to generate diffs in the git extended diff format.
537 Use the -g/--git option to generate diffs in the git extended diff format.
538 For more information, read 'hg help diffs'.
538 For more information, read 'hg help diffs'.
539
539
540 Returns 0 on success.
540 Returns 0 on success.
541
541
542 options ([+] can be repeated):
542 options ([+] can be repeated):
543
543
544 -r --rev REV [+] revision
544 -r --rev REV [+] revision
545 -c --change REV change made by revision
545 -c --change REV change made by revision
546 -a --text treat all files as text
546 -a --text treat all files as text
547 -g --git use git extended diff format
547 -g --git use git extended diff format
548 --binary generate binary diffs in git mode (default)
548 --binary generate binary diffs in git mode (default)
549 --nodates omit dates from diff headers
549 --nodates omit dates from diff headers
550 --noprefix omit a/ and b/ prefixes from filenames
550 --noprefix omit a/ and b/ prefixes from filenames
551 -p --show-function show which function each change is in
551 -p --show-function show which function each change is in
552 --reverse produce a diff that undoes the changes
552 --reverse produce a diff that undoes the changes
553 -w --ignore-all-space ignore white space when comparing lines
553 -w --ignore-all-space ignore white space when comparing lines
554 -b --ignore-space-change ignore changes in the amount of white space
554 -b --ignore-space-change ignore changes in the amount of white space
555 -B --ignore-blank-lines ignore changes whose lines are all blank
555 -B --ignore-blank-lines ignore changes whose lines are all blank
556 -Z --ignore-space-at-eol ignore changes in whitespace at EOL
556 -U --unified NUM number of lines of context to show
557 -U --unified NUM number of lines of context to show
557 --stat output diffstat-style summary of changes
558 --stat output diffstat-style summary of changes
558 --root DIR produce diffs relative to subdirectory
559 --root DIR produce diffs relative to subdirectory
559 -I --include PATTERN [+] include names matching the given patterns
560 -I --include PATTERN [+] include names matching the given patterns
560 -X --exclude PATTERN [+] exclude names matching the given patterns
561 -X --exclude PATTERN [+] exclude names matching the given patterns
561 -S --subrepos recurse into subrepositories
562 -S --subrepos recurse into subrepositories
562
563
563 (some details hidden, use --verbose to show complete help)
564 (some details hidden, use --verbose to show complete help)
564
565
565 $ hg help status
566 $ hg help status
566 hg status [OPTION]... [FILE]...
567 hg status [OPTION]... [FILE]...
567
568
568 aliases: st
569 aliases: st
569
570
570 show changed files in the working directory
571 show changed files in the working directory
571
572
572 Show status of files in the repository. If names are given, only files
573 Show status of files in the repository. If names are given, only files
573 that match are shown. Files that are clean or ignored or the source of a
574 that match are shown. Files that are clean or ignored or the source of a
574 copy/move operation, are not listed unless -c/--clean, -i/--ignored,
575 copy/move operation, are not listed unless -c/--clean, -i/--ignored,
575 -C/--copies or -A/--all are given. Unless options described with "show
576 -C/--copies or -A/--all are given. Unless options described with "show
576 only ..." are given, the options -mardu are used.
577 only ..." are given, the options -mardu are used.
577
578
578 Option -q/--quiet hides untracked (unknown and ignored) files unless
579 Option -q/--quiet hides untracked (unknown and ignored) files unless
579 explicitly requested with -u/--unknown or -i/--ignored.
580 explicitly requested with -u/--unknown or -i/--ignored.
580
581
581 Note:
582 Note:
582 'hg status' may appear to disagree with diff if permissions have
583 'hg status' may appear to disagree with diff if permissions have
583 changed or a merge has occurred. The standard diff format does not
584 changed or a merge has occurred. The standard diff format does not
584 report permission changes and diff only reports changes relative to one
585 report permission changes and diff only reports changes relative to one
585 merge parent.
586 merge parent.
586
587
587 If one revision is given, it is used as the base revision. If two
588 If one revision is given, it is used as the base revision. If two
588 revisions are given, the differences between them are shown. The --change
589 revisions are given, the differences between them are shown. The --change
589 option can also be used as a shortcut to list the changed files of a
590 option can also be used as a shortcut to list the changed files of a
590 revision from its first parent.
591 revision from its first parent.
591
592
592 The codes used to show the status of files are:
593 The codes used to show the status of files are:
593
594
594 M = modified
595 M = modified
595 A = added
596 A = added
596 R = removed
597 R = removed
597 C = clean
598 C = clean
598 ! = missing (deleted by non-hg command, but still tracked)
599 ! = missing (deleted by non-hg command, but still tracked)
599 ? = not tracked
600 ? = not tracked
600 I = ignored
601 I = ignored
601 = origin of the previous file (with --copies)
602 = origin of the previous file (with --copies)
602
603
603 Returns 0 on success.
604 Returns 0 on success.
604
605
605 options ([+] can be repeated):
606 options ([+] can be repeated):
606
607
607 -A --all show status of all files
608 -A --all show status of all files
608 -m --modified show only modified files
609 -m --modified show only modified files
609 -a --added show only added files
610 -a --added show only added files
610 -r --removed show only removed files
611 -r --removed show only removed files
611 -d --deleted show only deleted (but tracked) files
612 -d --deleted show only deleted (but tracked) files
612 -c --clean show only files without changes
613 -c --clean show only files without changes
613 -u --unknown show only unknown (not tracked) files
614 -u --unknown show only unknown (not tracked) files
614 -i --ignored show only ignored files
615 -i --ignored show only ignored files
615 -n --no-status hide status prefix
616 -n --no-status hide status prefix
616 -C --copies show source of copied files
617 -C --copies show source of copied files
617 -0 --print0 end filenames with NUL, for use with xargs
618 -0 --print0 end filenames with NUL, for use with xargs
618 --rev REV [+] show difference from revision
619 --rev REV [+] show difference from revision
619 --change REV list the changed files of a revision
620 --change REV list the changed files of a revision
620 -I --include PATTERN [+] include names matching the given patterns
621 -I --include PATTERN [+] include names matching the given patterns
621 -X --exclude PATTERN [+] exclude names matching the given patterns
622 -X --exclude PATTERN [+] exclude names matching the given patterns
622 -S --subrepos recurse into subrepositories
623 -S --subrepos recurse into subrepositories
623
624
624 (some details hidden, use --verbose to show complete help)
625 (some details hidden, use --verbose to show complete help)
625
626
626 $ hg -q help status
627 $ hg -q help status
627 hg status [OPTION]... [FILE]...
628 hg status [OPTION]... [FILE]...
628
629
629 show changed files in the working directory
630 show changed files in the working directory
630
631
631 $ hg help foo
632 $ hg help foo
632 abort: no such help topic: foo
633 abort: no such help topic: foo
633 (try 'hg help --keyword foo')
634 (try 'hg help --keyword foo')
634 [255]
635 [255]
635
636
636 $ hg skjdfks
637 $ hg skjdfks
637 hg: unknown command 'skjdfks'
638 hg: unknown command 'skjdfks'
638 Mercurial Distributed SCM
639 Mercurial Distributed SCM
639
640
640 basic commands:
641 basic commands:
641
642
642 add add the specified files on the next commit
643 add add the specified files on the next commit
643 annotate show changeset information by line for each file
644 annotate show changeset information by line for each file
644 clone make a copy of an existing repository
645 clone make a copy of an existing repository
645 commit commit the specified files or all outstanding changes
646 commit commit the specified files or all outstanding changes
646 diff diff repository (or selected files)
647 diff diff repository (or selected files)
647 export dump the header and diffs for one or more changesets
648 export dump the header and diffs for one or more changesets
648 forget forget the specified files on the next commit
649 forget forget the specified files on the next commit
649 init create a new repository in the given directory
650 init create a new repository in the given directory
650 log show revision history of entire repository or files
651 log show revision history of entire repository or files
651 merge merge another revision into working directory
652 merge merge another revision into working directory
652 pull pull changes from the specified source
653 pull pull changes from the specified source
653 push push changes to the specified destination
654 push push changes to the specified destination
654 remove remove the specified files on the next commit
655 remove remove the specified files on the next commit
655 serve start stand-alone webserver
656 serve start stand-alone webserver
656 status show changed files in the working directory
657 status show changed files in the working directory
657 summary summarize working directory state
658 summary summarize working directory state
658 update update working directory (or switch revisions)
659 update update working directory (or switch revisions)
659
660
660 (use 'hg help' for the full list of commands or 'hg -v' for details)
661 (use 'hg help' for the full list of commands or 'hg -v' for details)
661 [255]
662 [255]
662
663
663 Typoed command gives suggestion
664 Typoed command gives suggestion
664 $ hg puls
665 $ hg puls
665 hg: unknown command 'puls'
666 hg: unknown command 'puls'
666 (did you mean one of pull, push?)
667 (did you mean one of pull, push?)
667 [255]
668 [255]
668
669
669 Not enabled extension gets suggested
670 Not enabled extension gets suggested
670
671
671 $ hg rebase
672 $ hg rebase
672 hg: unknown command 'rebase'
673 hg: unknown command 'rebase'
673 'rebase' is provided by the following extension:
674 'rebase' is provided by the following extension:
674
675
675 rebase command to move sets of revisions to a different ancestor
676 rebase command to move sets of revisions to a different ancestor
676
677
677 (use 'hg help extensions' for information on enabling extensions)
678 (use 'hg help extensions' for information on enabling extensions)
678 [255]
679 [255]
679
680
680 Disabled extension gets suggested
681 Disabled extension gets suggested
681 $ hg --config extensions.rebase=! rebase
682 $ hg --config extensions.rebase=! rebase
682 hg: unknown command 'rebase'
683 hg: unknown command 'rebase'
683 'rebase' is provided by the following extension:
684 'rebase' is provided by the following extension:
684
685
685 rebase command to move sets of revisions to a different ancestor
686 rebase command to move sets of revisions to a different ancestor
686
687
687 (use 'hg help extensions' for information on enabling extensions)
688 (use 'hg help extensions' for information on enabling extensions)
688 [255]
689 [255]
689
690
690 Make sure that we don't run afoul of the help system thinking that
691 Make sure that we don't run afoul of the help system thinking that
691 this is a section and erroring out weirdly.
692 this is a section and erroring out weirdly.
692
693
693 $ hg .log
694 $ hg .log
694 hg: unknown command '.log'
695 hg: unknown command '.log'
695 (did you mean log?)
696 (did you mean log?)
696 [255]
697 [255]
697
698
698 $ hg log.
699 $ hg log.
699 hg: unknown command 'log.'
700 hg: unknown command 'log.'
700 (did you mean log?)
701 (did you mean log?)
701 [255]
702 [255]
702 $ hg pu.lh
703 $ hg pu.lh
703 hg: unknown command 'pu.lh'
704 hg: unknown command 'pu.lh'
704 (did you mean one of pull, push?)
705 (did you mean one of pull, push?)
705 [255]
706 [255]
706
707
707 $ cat > helpext.py <<EOF
708 $ cat > helpext.py <<EOF
708 > import os
709 > import os
709 > from mercurial import commands, registrar
710 > from mercurial import commands, registrar
710 >
711 >
711 > cmdtable = {}
712 > cmdtable = {}
712 > command = registrar.command(cmdtable)
713 > command = registrar.command(cmdtable)
713 >
714 >
714 > @command(b'nohelp',
715 > @command(b'nohelp',
715 > [(b'', b'longdesc', 3, b'x'*90),
716 > [(b'', b'longdesc', 3, b'x'*90),
716 > (b'n', b'', None, b'normal desc'),
717 > (b'n', b'', None, b'normal desc'),
717 > (b'', b'newline', b'', b'line1\nline2')],
718 > (b'', b'newline', b'', b'line1\nline2')],
718 > b'hg nohelp',
719 > b'hg nohelp',
719 > norepo=True)
720 > norepo=True)
720 > @command(b'debugoptADV', [(b'', b'aopt', None, b'option is (ADVANCED)')])
721 > @command(b'debugoptADV', [(b'', b'aopt', None, b'option is (ADVANCED)')])
721 > @command(b'debugoptDEP', [(b'', b'dopt', None, b'option is (DEPRECATED)')])
722 > @command(b'debugoptDEP', [(b'', b'dopt', None, b'option is (DEPRECATED)')])
722 > @command(b'debugoptEXP', [(b'', b'eopt', None, b'option is (EXPERIMENTAL)')])
723 > @command(b'debugoptEXP', [(b'', b'eopt', None, b'option is (EXPERIMENTAL)')])
723 > def nohelp(ui, *args, **kwargs):
724 > def nohelp(ui, *args, **kwargs):
724 > pass
725 > pass
725 >
726 >
726 > def uisetup(ui):
727 > def uisetup(ui):
727 > ui.setconfig(b'alias', b'shellalias', b'!echo hi', b'helpext')
728 > ui.setconfig(b'alias', b'shellalias', b'!echo hi', b'helpext')
728 > ui.setconfig(b'alias', b'hgalias', b'summary', b'helpext')
729 > ui.setconfig(b'alias', b'hgalias', b'summary', b'helpext')
729 >
730 >
730 > EOF
731 > EOF
731 $ echo '[extensions]' >> $HGRCPATH
732 $ echo '[extensions]' >> $HGRCPATH
732 $ echo "helpext = `pwd`/helpext.py" >> $HGRCPATH
733 $ echo "helpext = `pwd`/helpext.py" >> $HGRCPATH
733
734
734 Test for aliases
735 Test for aliases
735
736
736 $ hg help hgalias
737 $ hg help hgalias
737 hg hgalias [--remote]
738 hg hgalias [--remote]
738
739
739 alias for: hg summary
740 alias for: hg summary
740
741
741 summarize working directory state
742 summarize working directory state
742
743
743 This generates a brief summary of the working directory state, including
744 This generates a brief summary of the working directory state, including
744 parents, branch, commit status, phase and available updates.
745 parents, branch, commit status, phase and available updates.
745
746
746 With the --remote option, this will check the default paths for incoming
747 With the --remote option, this will check the default paths for incoming
747 and outgoing changes. This can be time-consuming.
748 and outgoing changes. This can be time-consuming.
748
749
749 Returns 0 on success.
750 Returns 0 on success.
750
751
751 defined by: helpext
752 defined by: helpext
752
753
753 options:
754 options:
754
755
755 --remote check for push and pull
756 --remote check for push and pull
756
757
757 (some details hidden, use --verbose to show complete help)
758 (some details hidden, use --verbose to show complete help)
758
759
759 $ hg help shellalias
760 $ hg help shellalias
760 hg shellalias
761 hg shellalias
761
762
762 shell alias for:
763 shell alias for:
763
764
764 echo hi
765 echo hi
765
766
766 defined by: helpext
767 defined by: helpext
767
768
768 (some details hidden, use --verbose to show complete help)
769 (some details hidden, use --verbose to show complete help)
769
770
770 Test command with no help text
771 Test command with no help text
771
772
772 $ hg help nohelp
773 $ hg help nohelp
773 hg nohelp
774 hg nohelp
774
775
775 (no help text available)
776 (no help text available)
776
777
777 options:
778 options:
778
779
779 --longdesc VALUE xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
780 --longdesc VALUE xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
780 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx (default: 3)
781 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx (default: 3)
781 -n -- normal desc
782 -n -- normal desc
782 --newline VALUE line1 line2
783 --newline VALUE line1 line2
783
784
784 (some details hidden, use --verbose to show complete help)
785 (some details hidden, use --verbose to show complete help)
785
786
786 $ hg help -k nohelp
787 $ hg help -k nohelp
787 Commands:
788 Commands:
788
789
789 nohelp hg nohelp
790 nohelp hg nohelp
790
791
791 Extension Commands:
792 Extension Commands:
792
793
793 nohelp (no help text available)
794 nohelp (no help text available)
794
795
795 Test that default list of commands omits extension commands
796 Test that default list of commands omits extension commands
796
797
797 $ hg help
798 $ hg help
798 Mercurial Distributed SCM
799 Mercurial Distributed SCM
799
800
800 list of commands:
801 list of commands:
801
802
802 add add the specified files on the next commit
803 add add the specified files on the next commit
803 addremove add all new files, delete all missing files
804 addremove add all new files, delete all missing files
804 annotate show changeset information by line for each file
805 annotate show changeset information by line for each file
805 archive create an unversioned archive of a repository revision
806 archive create an unversioned archive of a repository revision
806 backout reverse effect of earlier changeset
807 backout reverse effect of earlier changeset
807 bisect subdivision search of changesets
808 bisect subdivision search of changesets
808 bookmarks create a new bookmark or list existing bookmarks
809 bookmarks create a new bookmark or list existing bookmarks
809 branch set or show the current branch name
810 branch set or show the current branch name
810 branches list repository named branches
811 branches list repository named branches
811 bundle create a bundle file
812 bundle create a bundle file
812 cat output the current or given revision of files
813 cat output the current or given revision of files
813 clone make a copy of an existing repository
814 clone make a copy of an existing repository
814 commit commit the specified files or all outstanding changes
815 commit commit the specified files or all outstanding changes
815 config show combined config settings from all hgrc files
816 config show combined config settings from all hgrc files
816 copy mark files as copied for the next commit
817 copy mark files as copied for the next commit
817 diff diff repository (or selected files)
818 diff diff repository (or selected files)
818 export dump the header and diffs for one or more changesets
819 export dump the header and diffs for one or more changesets
819 files list tracked files
820 files list tracked files
820 forget forget the specified files on the next commit
821 forget forget the specified files on the next commit
821 graft copy changes from other branches onto the current branch
822 graft copy changes from other branches onto the current branch
822 grep search revision history for a pattern in specified files
823 grep search revision history for a pattern in specified files
823 heads show branch heads
824 heads show branch heads
824 help show help for a given topic or a help overview
825 help show help for a given topic or a help overview
825 identify identify the working directory or specified revision
826 identify identify the working directory or specified revision
826 import import an ordered set of patches
827 import import an ordered set of patches
827 incoming show new changesets found in source
828 incoming show new changesets found in source
828 init create a new repository in the given directory
829 init create a new repository in the given directory
829 log show revision history of entire repository or files
830 log show revision history of entire repository or files
830 manifest output the current or given revision of the project manifest
831 manifest output the current or given revision of the project manifest
831 merge merge another revision into working directory
832 merge merge another revision into working directory
832 outgoing show changesets not found in the destination
833 outgoing show changesets not found in the destination
833 paths show aliases for remote repositories
834 paths show aliases for remote repositories
834 phase set or show the current phase name
835 phase set or show the current phase name
835 pull pull changes from the specified source
836 pull pull changes from the specified source
836 push push changes to the specified destination
837 push push changes to the specified destination
837 recover roll back an interrupted transaction
838 recover roll back an interrupted transaction
838 remove remove the specified files on the next commit
839 remove remove the specified files on the next commit
839 rename rename files; equivalent of copy + remove
840 rename rename files; equivalent of copy + remove
840 resolve redo merges or set/view the merge status of files
841 resolve redo merges or set/view the merge status of files
841 revert restore files to their checkout state
842 revert restore files to their checkout state
842 root print the root (top) of the current working directory
843 root print the root (top) of the current working directory
843 serve start stand-alone webserver
844 serve start stand-alone webserver
844 status show changed files in the working directory
845 status show changed files in the working directory
845 summary summarize working directory state
846 summary summarize working directory state
846 tag add one or more tags for the current or given revision
847 tag add one or more tags for the current or given revision
847 tags list repository tags
848 tags list repository tags
848 unbundle apply one or more bundle files
849 unbundle apply one or more bundle files
849 update update working directory (or switch revisions)
850 update update working directory (or switch revisions)
850 verify verify the integrity of the repository
851 verify verify the integrity of the repository
851 version output version and copyright information
852 version output version and copyright information
852
853
853 enabled extensions:
854 enabled extensions:
854
855
855 helpext (no help text available)
856 helpext (no help text available)
856
857
857 additional help topics:
858 additional help topics:
858
859
859 bundlespec Bundle File Formats
860 bundlespec Bundle File Formats
860 color Colorizing Outputs
861 color Colorizing Outputs
861 config Configuration Files
862 config Configuration Files
862 dates Date Formats
863 dates Date Formats
863 diffs Diff Formats
864 diffs Diff Formats
864 environment Environment Variables
865 environment Environment Variables
865 extensions Using Additional Features
866 extensions Using Additional Features
866 filesets Specifying File Sets
867 filesets Specifying File Sets
867 glossary Glossary
868 glossary Glossary
868 hgignore Syntax for Mercurial Ignore Files
869 hgignore Syntax for Mercurial Ignore Files
869 hgweb Configuring hgweb
870 hgweb Configuring hgweb
870 internals Technical implementation topics
871 internals Technical implementation topics
871 merge-tools Merge Tools
872 merge-tools Merge Tools
872 pager Pager Support
873 pager Pager Support
873 patterns File Name Patterns
874 patterns File Name Patterns
874 phases Working with Phases
875 phases Working with Phases
875 revisions Specifying Revisions
876 revisions Specifying Revisions
876 scripting Using Mercurial from scripts and automation
877 scripting Using Mercurial from scripts and automation
877 subrepos Subrepositories
878 subrepos Subrepositories
878 templating Template Usage
879 templating Template Usage
879 urls URL Paths
880 urls URL Paths
880
881
881 (use 'hg help -v' to show built-in aliases and global options)
882 (use 'hg help -v' to show built-in aliases and global options)
882
883
883
884
884 Test list of internal help commands
885 Test list of internal help commands
885
886
886 $ hg help debug
887 $ hg help debug
887 debug commands (internal and unsupported):
888 debug commands (internal and unsupported):
888
889
889 debugancestor
890 debugancestor
890 find the ancestor revision of two revisions in a given index
891 find the ancestor revision of two revisions in a given index
891 debugapplystreamclonebundle
892 debugapplystreamclonebundle
892 apply a stream clone bundle file
893 apply a stream clone bundle file
893 debugbuilddag
894 debugbuilddag
894 builds a repo with a given DAG from scratch in the current
895 builds a repo with a given DAG from scratch in the current
895 empty repo
896 empty repo
896 debugbundle lists the contents of a bundle
897 debugbundle lists the contents of a bundle
897 debugcheckstate
898 debugcheckstate
898 validate the correctness of the current dirstate
899 validate the correctness of the current dirstate
899 debugcolor show available color, effects or style
900 debugcolor show available color, effects or style
900 debugcommands
901 debugcommands
901 list all available commands and options
902 list all available commands and options
902 debugcomplete
903 debugcomplete
903 returns the completion list associated with the given command
904 returns the completion list associated with the given command
904 debugcreatestreamclonebundle
905 debugcreatestreamclonebundle
905 create a stream clone bundle file
906 create a stream clone bundle file
906 debugdag format the changelog or an index DAG as a concise textual
907 debugdag format the changelog or an index DAG as a concise textual
907 description
908 description
908 debugdata dump the contents of a data file revision
909 debugdata dump the contents of a data file revision
909 debugdate parse and display a date
910 debugdate parse and display a date
910 debugdeltachain
911 debugdeltachain
911 dump information about delta chains in a revlog
912 dump information about delta chains in a revlog
912 debugdirstate
913 debugdirstate
913 show the contents of the current dirstate
914 show the contents of the current dirstate
914 debugdiscovery
915 debugdiscovery
915 runs the changeset discovery protocol in isolation
916 runs the changeset discovery protocol in isolation
916 debugextensions
917 debugextensions
917 show information about active extensions
918 show information about active extensions
918 debugfileset parse and apply a fileset specification
919 debugfileset parse and apply a fileset specification
919 debugfsinfo show information detected about current filesystem
920 debugfsinfo show information detected about current filesystem
920 debuggetbundle
921 debuggetbundle
921 retrieves a bundle from a repo
922 retrieves a bundle from a repo
922 debugignore display the combined ignore pattern and information about
923 debugignore display the combined ignore pattern and information about
923 ignored files
924 ignored files
924 debugindex dump the contents of an index file
925 debugindex dump the contents of an index file
925 debugindexdot
926 debugindexdot
926 dump an index DAG as a graphviz dot file
927 dump an index DAG as a graphviz dot file
927 debuginstall test Mercurial installation
928 debuginstall test Mercurial installation
928 debugknown test whether node ids are known to a repo
929 debugknown test whether node ids are known to a repo
929 debuglocks show or modify state of locks
930 debuglocks show or modify state of locks
930 debugmergestate
931 debugmergestate
931 print merge state
932 print merge state
932 debugnamecomplete
933 debugnamecomplete
933 complete "names" - tags, open branch names, bookmark names
934 complete "names" - tags, open branch names, bookmark names
934 debugobsolete
935 debugobsolete
935 create arbitrary obsolete marker
936 create arbitrary obsolete marker
936 debugoptADV (no help text available)
937 debugoptADV (no help text available)
937 debugoptDEP (no help text available)
938 debugoptDEP (no help text available)
938 debugoptEXP (no help text available)
939 debugoptEXP (no help text available)
939 debugpathcomplete
940 debugpathcomplete
940 complete part or all of a tracked path
941 complete part or all of a tracked path
941 debugpickmergetool
942 debugpickmergetool
942 examine which merge tool is chosen for specified file
943 examine which merge tool is chosen for specified file
943 debugpushkey access the pushkey key/value protocol
944 debugpushkey access the pushkey key/value protocol
944 debugpvec (no help text available)
945 debugpvec (no help text available)
945 debugrebuilddirstate
946 debugrebuilddirstate
946 rebuild the dirstate as it would look like for the given
947 rebuild the dirstate as it would look like for the given
947 revision
948 revision
948 debugrebuildfncache
949 debugrebuildfncache
949 rebuild the fncache file
950 rebuild the fncache file
950 debugrename dump rename information
951 debugrename dump rename information
951 debugrevlog show data and statistics about a revlog
952 debugrevlog show data and statistics about a revlog
952 debugrevspec parse and apply a revision specification
953 debugrevspec parse and apply a revision specification
953 debugsetparents
954 debugsetparents
954 manually set the parents of the current working directory
955 manually set the parents of the current working directory
955 debugssl test a secure connection to a server
956 debugssl test a secure connection to a server
956 debugsub (no help text available)
957 debugsub (no help text available)
957 debugsuccessorssets
958 debugsuccessorssets
958 show set of successors for revision
959 show set of successors for revision
959 debugtemplate
960 debugtemplate
960 parse and apply a template
961 parse and apply a template
961 debugupdatecaches
962 debugupdatecaches
962 warm all known caches in the repository
963 warm all known caches in the repository
963 debugupgraderepo
964 debugupgraderepo
964 upgrade a repository to use different features
965 upgrade a repository to use different features
965 debugwalk show how files match on given patterns
966 debugwalk show how files match on given patterns
966 debugwireargs
967 debugwireargs
967 (no help text available)
968 (no help text available)
968
969
969 (use 'hg help -v debug' to show built-in aliases and global options)
970 (use 'hg help -v debug' to show built-in aliases and global options)
970
971
971 internals topic renders index of available sub-topics
972 internals topic renders index of available sub-topics
972
973
973 $ hg help internals
974 $ hg help internals
974 Technical implementation topics
975 Technical implementation topics
975 """""""""""""""""""""""""""""""
976 """""""""""""""""""""""""""""""
976
977
977 To access a subtopic, use "hg help internals.{subtopic-name}"
978 To access a subtopic, use "hg help internals.{subtopic-name}"
978
979
979 bundles Bundles
980 bundles Bundles
980 censor Censor
981 censor Censor
981 changegroups Changegroups
982 changegroups Changegroups
982 requirements Repository Requirements
983 requirements Repository Requirements
983 revlogs Revision Logs
984 revlogs Revision Logs
984 wireprotocol Wire Protocol
985 wireprotocol Wire Protocol
985
986
986 sub-topics can be accessed
987 sub-topics can be accessed
987
988
988 $ hg help internals.changegroups
989 $ hg help internals.changegroups
989 Changegroups
990 Changegroups
990 """"""""""""
991 """"""""""""
991
992
992 Changegroups are representations of repository revlog data, specifically
993 Changegroups are representations of repository revlog data, specifically
993 the changelog data, root/flat manifest data, treemanifest data, and
994 the changelog data, root/flat manifest data, treemanifest data, and
994 filelogs.
995 filelogs.
995
996
996 There are 3 versions of changegroups: "1", "2", and "3". From a high-
997 There are 3 versions of changegroups: "1", "2", and "3". From a high-
997 level, versions "1" and "2" are almost exactly the same, with the only
998 level, versions "1" and "2" are almost exactly the same, with the only
998 difference being an additional item in the *delta header*. Version "3"
999 difference being an additional item in the *delta header*. Version "3"
999 adds support for revlog flags in the *delta header* and optionally
1000 adds support for revlog flags in the *delta header* and optionally
1000 exchanging treemanifests (enabled by setting an option on the
1001 exchanging treemanifests (enabled by setting an option on the
1001 "changegroup" part in the bundle2).
1002 "changegroup" part in the bundle2).
1002
1003
1003 Changegroups when not exchanging treemanifests consist of 3 logical
1004 Changegroups when not exchanging treemanifests consist of 3 logical
1004 segments:
1005 segments:
1005
1006
1006 +---------------------------------+
1007 +---------------------------------+
1007 | | | |
1008 | | | |
1008 | changeset | manifest | filelogs |
1009 | changeset | manifest | filelogs |
1009 | | | |
1010 | | | |
1010 | | | |
1011 | | | |
1011 +---------------------------------+
1012 +---------------------------------+
1012
1013
1013 When exchanging treemanifests, there are 4 logical segments:
1014 When exchanging treemanifests, there are 4 logical segments:
1014
1015
1015 +-------------------------------------------------+
1016 +-------------------------------------------------+
1016 | | | | |
1017 | | | | |
1017 | changeset | root | treemanifests | filelogs |
1018 | changeset | root | treemanifests | filelogs |
1018 | | manifest | | |
1019 | | manifest | | |
1019 | | | | |
1020 | | | | |
1020 +-------------------------------------------------+
1021 +-------------------------------------------------+
1021
1022
1022 The principle building block of each segment is a *chunk*. A *chunk* is a
1023 The principle building block of each segment is a *chunk*. A *chunk* is a
1023 framed piece of data:
1024 framed piece of data:
1024
1025
1025 +---------------------------------------+
1026 +---------------------------------------+
1026 | | |
1027 | | |
1027 | length | data |
1028 | length | data |
1028 | (4 bytes) | (<length - 4> bytes) |
1029 | (4 bytes) | (<length - 4> bytes) |
1029 | | |
1030 | | |
1030 +---------------------------------------+
1031 +---------------------------------------+
1031
1032
1032 All integers are big-endian signed integers. Each chunk starts with a
1033 All integers are big-endian signed integers. Each chunk starts with a
1033 32-bit integer indicating the length of the entire chunk (including the
1034 32-bit integer indicating the length of the entire chunk (including the
1034 length field itself).
1035 length field itself).
1035
1036
1036 There is a special case chunk that has a value of 0 for the length
1037 There is a special case chunk that has a value of 0 for the length
1037 ("0x00000000"). We call this an *empty chunk*.
1038 ("0x00000000"). We call this an *empty chunk*.
1038
1039
1039 Delta Groups
1040 Delta Groups
1040 ============
1041 ============
1041
1042
1042 A *delta group* expresses the content of a revlog as a series of deltas,
1043 A *delta group* expresses the content of a revlog as a series of deltas,
1043 or patches against previous revisions.
1044 or patches against previous revisions.
1044
1045
1045 Delta groups consist of 0 or more *chunks* followed by the *empty chunk*
1046 Delta groups consist of 0 or more *chunks* followed by the *empty chunk*
1046 to signal the end of the delta group:
1047 to signal the end of the delta group:
1047
1048
1048 +------------------------------------------------------------------------+
1049 +------------------------------------------------------------------------+
1049 | | | | | |
1050 | | | | | |
1050 | chunk0 length | chunk0 data | chunk1 length | chunk1 data | 0x0 |
1051 | chunk0 length | chunk0 data | chunk1 length | chunk1 data | 0x0 |
1051 | (4 bytes) | (various) | (4 bytes) | (various) | (4 bytes) |
1052 | (4 bytes) | (various) | (4 bytes) | (various) | (4 bytes) |
1052 | | | | | |
1053 | | | | | |
1053 +------------------------------------------------------------------------+
1054 +------------------------------------------------------------------------+
1054
1055
1055 Each *chunk*'s data consists of the following:
1056 Each *chunk*'s data consists of the following:
1056
1057
1057 +---------------------------------------+
1058 +---------------------------------------+
1058 | | |
1059 | | |
1059 | delta header | delta data |
1060 | delta header | delta data |
1060 | (various by version) | (various) |
1061 | (various by version) | (various) |
1061 | | |
1062 | | |
1062 +---------------------------------------+
1063 +---------------------------------------+
1063
1064
1064 The *delta data* is a series of *delta*s that describe a diff from an
1065 The *delta data* is a series of *delta*s that describe a diff from an
1065 existing entry (either that the recipient already has, or previously
1066 existing entry (either that the recipient already has, or previously
1066 specified in the bundle/changegroup).
1067 specified in the bundle/changegroup).
1067
1068
1068 The *delta header* is different between versions "1", "2", and "3" of the
1069 The *delta header* is different between versions "1", "2", and "3" of the
1069 changegroup format.
1070 changegroup format.
1070
1071
1071 Version 1 (headerlen=80):
1072 Version 1 (headerlen=80):
1072
1073
1073 +------------------------------------------------------+
1074 +------------------------------------------------------+
1074 | | | | |
1075 | | | | |
1075 | node | p1 node | p2 node | link node |
1076 | node | p1 node | p2 node | link node |
1076 | (20 bytes) | (20 bytes) | (20 bytes) | (20 bytes) |
1077 | (20 bytes) | (20 bytes) | (20 bytes) | (20 bytes) |
1077 | | | | |
1078 | | | | |
1078 +------------------------------------------------------+
1079 +------------------------------------------------------+
1079
1080
1080 Version 2 (headerlen=100):
1081 Version 2 (headerlen=100):
1081
1082
1082 +------------------------------------------------------------------+
1083 +------------------------------------------------------------------+
1083 | | | | | |
1084 | | | | | |
1084 | node | p1 node | p2 node | base node | link node |
1085 | node | p1 node | p2 node | base node | link node |
1085 | (20 bytes) | (20 bytes) | (20 bytes) | (20 bytes) | (20 bytes) |
1086 | (20 bytes) | (20 bytes) | (20 bytes) | (20 bytes) | (20 bytes) |
1086 | | | | | |
1087 | | | | | |
1087 +------------------------------------------------------------------+
1088 +------------------------------------------------------------------+
1088
1089
1089 Version 3 (headerlen=102):
1090 Version 3 (headerlen=102):
1090
1091
1091 +------------------------------------------------------------------------------+
1092 +------------------------------------------------------------------------------+
1092 | | | | | | |
1093 | | | | | | |
1093 | node | p1 node | p2 node | base node | link node | flags |
1094 | node | p1 node | p2 node | base node | link node | flags |
1094 | (20 bytes) | (20 bytes) | (20 bytes) | (20 bytes) | (20 bytes) | (2 bytes) |
1095 | (20 bytes) | (20 bytes) | (20 bytes) | (20 bytes) | (20 bytes) | (2 bytes) |
1095 | | | | | | |
1096 | | | | | | |
1096 +------------------------------------------------------------------------------+
1097 +------------------------------------------------------------------------------+
1097
1098
1098 The *delta data* consists of "chunklen - 4 - headerlen" bytes, which
1099 The *delta data* consists of "chunklen - 4 - headerlen" bytes, which
1099 contain a series of *delta*s, densely packed (no separators). These deltas
1100 contain a series of *delta*s, densely packed (no separators). These deltas
1100 describe a diff from an existing entry (either that the recipient already
1101 describe a diff from an existing entry (either that the recipient already
1101 has, or previously specified in the bundle/changegroup). The format is
1102 has, or previously specified in the bundle/changegroup). The format is
1102 described more fully in "hg help internals.bdiff", but briefly:
1103 described more fully in "hg help internals.bdiff", but briefly:
1103
1104
1104 +---------------------------------------------------------------+
1105 +---------------------------------------------------------------+
1105 | | | | |
1106 | | | | |
1106 | start offset | end offset | new length | content |
1107 | start offset | end offset | new length | content |
1107 | (4 bytes) | (4 bytes) | (4 bytes) | (<new length> bytes) |
1108 | (4 bytes) | (4 bytes) | (4 bytes) | (<new length> bytes) |
1108 | | | | |
1109 | | | | |
1109 +---------------------------------------------------------------+
1110 +---------------------------------------------------------------+
1110
1111
1111 Please note that the length field in the delta data does *not* include
1112 Please note that the length field in the delta data does *not* include
1112 itself.
1113 itself.
1113
1114
1114 In version 1, the delta is always applied against the previous node from
1115 In version 1, the delta is always applied against the previous node from
1115 the changegroup or the first parent if this is the first entry in the
1116 the changegroup or the first parent if this is the first entry in the
1116 changegroup.
1117 changegroup.
1117
1118
1118 In version 2 and up, the delta base node is encoded in the entry in the
1119 In version 2 and up, the delta base node is encoded in the entry in the
1119 changegroup. This allows the delta to be expressed against any parent,
1120 changegroup. This allows the delta to be expressed against any parent,
1120 which can result in smaller deltas and more efficient encoding of data.
1121 which can result in smaller deltas and more efficient encoding of data.
1121
1122
1122 Changeset Segment
1123 Changeset Segment
1123 =================
1124 =================
1124
1125
1125 The *changeset segment* consists of a single *delta group* holding
1126 The *changeset segment* consists of a single *delta group* holding
1126 changelog data. The *empty chunk* at the end of the *delta group* denotes
1127 changelog data. The *empty chunk* at the end of the *delta group* denotes
1127 the boundary to the *manifest segment*.
1128 the boundary to the *manifest segment*.
1128
1129
1129 Manifest Segment
1130 Manifest Segment
1130 ================
1131 ================
1131
1132
1132 The *manifest segment* consists of a single *delta group* holding manifest
1133 The *manifest segment* consists of a single *delta group* holding manifest
1133 data. If treemanifests are in use, it contains only the manifest for the
1134 data. If treemanifests are in use, it contains only the manifest for the
1134 root directory of the repository. Otherwise, it contains the entire
1135 root directory of the repository. Otherwise, it contains the entire
1135 manifest data. The *empty chunk* at the end of the *delta group* denotes
1136 manifest data. The *empty chunk* at the end of the *delta group* denotes
1136 the boundary to the next segment (either the *treemanifests segment* or
1137 the boundary to the next segment (either the *treemanifests segment* or
1137 the *filelogs segment*, depending on version and the request options).
1138 the *filelogs segment*, depending on version and the request options).
1138
1139
1139 Treemanifests Segment
1140 Treemanifests Segment
1140 ---------------------
1141 ---------------------
1141
1142
1142 The *treemanifests segment* only exists in changegroup version "3", and
1143 The *treemanifests segment* only exists in changegroup version "3", and
1143 only if the 'treemanifest' param is part of the bundle2 changegroup part
1144 only if the 'treemanifest' param is part of the bundle2 changegroup part
1144 (it is not possible to use changegroup version 3 outside of bundle2).
1145 (it is not possible to use changegroup version 3 outside of bundle2).
1145 Aside from the filenames in the *treemanifests segment* containing a
1146 Aside from the filenames in the *treemanifests segment* containing a
1146 trailing "/" character, it behaves identically to the *filelogs segment*
1147 trailing "/" character, it behaves identically to the *filelogs segment*
1147 (see below). The final sub-segment is followed by an *empty chunk*
1148 (see below). The final sub-segment is followed by an *empty chunk*
1148 (logically, a sub-segment with filename size 0). This denotes the boundary
1149 (logically, a sub-segment with filename size 0). This denotes the boundary
1149 to the *filelogs segment*.
1150 to the *filelogs segment*.
1150
1151
1151 Filelogs Segment
1152 Filelogs Segment
1152 ================
1153 ================
1153
1154
1154 The *filelogs segment* consists of multiple sub-segments, each
1155 The *filelogs segment* consists of multiple sub-segments, each
1155 corresponding to an individual file whose data is being described:
1156 corresponding to an individual file whose data is being described:
1156
1157
1157 +--------------------------------------------------+
1158 +--------------------------------------------------+
1158 | | | | | |
1159 | | | | | |
1159 | filelog0 | filelog1 | filelog2 | ... | 0x0 |
1160 | filelog0 | filelog1 | filelog2 | ... | 0x0 |
1160 | | | | | (4 bytes) |
1161 | | | | | (4 bytes) |
1161 | | | | | |
1162 | | | | | |
1162 +--------------------------------------------------+
1163 +--------------------------------------------------+
1163
1164
1164 The final filelog sub-segment is followed by an *empty chunk* (logically,
1165 The final filelog sub-segment is followed by an *empty chunk* (logically,
1165 a sub-segment with filename size 0). This denotes the end of the segment
1166 a sub-segment with filename size 0). This denotes the end of the segment
1166 and of the overall changegroup.
1167 and of the overall changegroup.
1167
1168
1168 Each filelog sub-segment consists of the following:
1169 Each filelog sub-segment consists of the following:
1169
1170
1170 +------------------------------------------------------+
1171 +------------------------------------------------------+
1171 | | | |
1172 | | | |
1172 | filename length | filename | delta group |
1173 | filename length | filename | delta group |
1173 | (4 bytes) | (<length - 4> bytes) | (various) |
1174 | (4 bytes) | (<length - 4> bytes) | (various) |
1174 | | | |
1175 | | | |
1175 +------------------------------------------------------+
1176 +------------------------------------------------------+
1176
1177
1177 That is, a *chunk* consisting of the filename (not terminated or padded)
1178 That is, a *chunk* consisting of the filename (not terminated or padded)
1178 followed by N chunks constituting the *delta group* for this file. The
1179 followed by N chunks constituting the *delta group* for this file. The
1179 *empty chunk* at the end of each *delta group* denotes the boundary to the
1180 *empty chunk* at the end of each *delta group* denotes the boundary to the
1180 next filelog sub-segment.
1181 next filelog sub-segment.
1181
1182
1182 Test list of commands with command with no help text
1183 Test list of commands with command with no help text
1183
1184
1184 $ hg help helpext
1185 $ hg help helpext
1185 helpext extension - no help text available
1186 helpext extension - no help text available
1186
1187
1187 list of commands:
1188 list of commands:
1188
1189
1189 nohelp (no help text available)
1190 nohelp (no help text available)
1190
1191
1191 (use 'hg help -v helpext' to show built-in aliases and global options)
1192 (use 'hg help -v helpext' to show built-in aliases and global options)
1192
1193
1193
1194
1194 test advanced, deprecated and experimental options are hidden in command help
1195 test advanced, deprecated and experimental options are hidden in command help
1195 $ hg help debugoptADV
1196 $ hg help debugoptADV
1196 hg debugoptADV
1197 hg debugoptADV
1197
1198
1198 (no help text available)
1199 (no help text available)
1199
1200
1200 options:
1201 options:
1201
1202
1202 (some details hidden, use --verbose to show complete help)
1203 (some details hidden, use --verbose to show complete help)
1203 $ hg help debugoptDEP
1204 $ hg help debugoptDEP
1204 hg debugoptDEP
1205 hg debugoptDEP
1205
1206
1206 (no help text available)
1207 (no help text available)
1207
1208
1208 options:
1209 options:
1209
1210
1210 (some details hidden, use --verbose to show complete help)
1211 (some details hidden, use --verbose to show complete help)
1211
1212
1212 $ hg help debugoptEXP
1213 $ hg help debugoptEXP
1213 hg debugoptEXP
1214 hg debugoptEXP
1214
1215
1215 (no help text available)
1216 (no help text available)
1216
1217
1217 options:
1218 options:
1218
1219
1219 (some details hidden, use --verbose to show complete help)
1220 (some details hidden, use --verbose to show complete help)
1220
1221
1221 test advanced, deprecated and experimental options are shown with -v
1222 test advanced, deprecated and experimental options are shown with -v
1222 $ hg help -v debugoptADV | grep aopt
1223 $ hg help -v debugoptADV | grep aopt
1223 --aopt option is (ADVANCED)
1224 --aopt option is (ADVANCED)
1224 $ hg help -v debugoptDEP | grep dopt
1225 $ hg help -v debugoptDEP | grep dopt
1225 --dopt option is (DEPRECATED)
1226 --dopt option is (DEPRECATED)
1226 $ hg help -v debugoptEXP | grep eopt
1227 $ hg help -v debugoptEXP | grep eopt
1227 --eopt option is (EXPERIMENTAL)
1228 --eopt option is (EXPERIMENTAL)
1228
1229
1229 #if gettext
1230 #if gettext
1230 test deprecated option is hidden with translation with untranslated description
1231 test deprecated option is hidden with translation with untranslated description
1231 (use many globy for not failing on changed transaction)
1232 (use many globy for not failing on changed transaction)
1232 $ LANGUAGE=sv hg help debugoptDEP
1233 $ LANGUAGE=sv hg help debugoptDEP
1233 hg debugoptDEP
1234 hg debugoptDEP
1234
1235
1235 (*) (glob)
1236 (*) (glob)
1236
1237
1237 options:
1238 options:
1238
1239
1239 (some details hidden, use --verbose to show complete help)
1240 (some details hidden, use --verbose to show complete help)
1240 #endif
1241 #endif
1241
1242
1242 Test commands that collide with topics (issue4240)
1243 Test commands that collide with topics (issue4240)
1243
1244
1244 $ hg config -hq
1245 $ hg config -hq
1245 hg config [-u] [NAME]...
1246 hg config [-u] [NAME]...
1246
1247
1247 show combined config settings from all hgrc files
1248 show combined config settings from all hgrc files
1248 $ hg showconfig -hq
1249 $ hg showconfig -hq
1249 hg config [-u] [NAME]...
1250 hg config [-u] [NAME]...
1250
1251
1251 show combined config settings from all hgrc files
1252 show combined config settings from all hgrc files
1252
1253
1253 Test a help topic
1254 Test a help topic
1254
1255
1255 $ hg help dates
1256 $ hg help dates
1256 Date Formats
1257 Date Formats
1257 """"""""""""
1258 """"""""""""
1258
1259
1259 Some commands allow the user to specify a date, e.g.:
1260 Some commands allow the user to specify a date, e.g.:
1260
1261
1261 - backout, commit, import, tag: Specify the commit date.
1262 - backout, commit, import, tag: Specify the commit date.
1262 - log, revert, update: Select revision(s) by date.
1263 - log, revert, update: Select revision(s) by date.
1263
1264
1264 Many date formats are valid. Here are some examples:
1265 Many date formats are valid. Here are some examples:
1265
1266
1266 - "Wed Dec 6 13:18:29 2006" (local timezone assumed)
1267 - "Wed Dec 6 13:18:29 2006" (local timezone assumed)
1267 - "Dec 6 13:18 -0600" (year assumed, time offset provided)
1268 - "Dec 6 13:18 -0600" (year assumed, time offset provided)
1268 - "Dec 6 13:18 UTC" (UTC and GMT are aliases for +0000)
1269 - "Dec 6 13:18 UTC" (UTC and GMT are aliases for +0000)
1269 - "Dec 6" (midnight)
1270 - "Dec 6" (midnight)
1270 - "13:18" (today assumed)
1271 - "13:18" (today assumed)
1271 - "3:39" (3:39AM assumed)
1272 - "3:39" (3:39AM assumed)
1272 - "3:39pm" (15:39)
1273 - "3:39pm" (15:39)
1273 - "2006-12-06 13:18:29" (ISO 8601 format)
1274 - "2006-12-06 13:18:29" (ISO 8601 format)
1274 - "2006-12-6 13:18"
1275 - "2006-12-6 13:18"
1275 - "2006-12-6"
1276 - "2006-12-6"
1276 - "12-6"
1277 - "12-6"
1277 - "12/6"
1278 - "12/6"
1278 - "12/6/6" (Dec 6 2006)
1279 - "12/6/6" (Dec 6 2006)
1279 - "today" (midnight)
1280 - "today" (midnight)
1280 - "yesterday" (midnight)
1281 - "yesterday" (midnight)
1281 - "now" - right now
1282 - "now" - right now
1282
1283
1283 Lastly, there is Mercurial's internal format:
1284 Lastly, there is Mercurial's internal format:
1284
1285
1285 - "1165411109 0" (Wed Dec 6 13:18:29 2006 UTC)
1286 - "1165411109 0" (Wed Dec 6 13:18:29 2006 UTC)
1286
1287
1287 This is the internal representation format for dates. The first number is
1288 This is the internal representation format for dates. The first number is
1288 the number of seconds since the epoch (1970-01-01 00:00 UTC). The second
1289 the number of seconds since the epoch (1970-01-01 00:00 UTC). The second
1289 is the offset of the local timezone, in seconds west of UTC (negative if
1290 is the offset of the local timezone, in seconds west of UTC (negative if
1290 the timezone is east of UTC).
1291 the timezone is east of UTC).
1291
1292
1292 The log command also accepts date ranges:
1293 The log command also accepts date ranges:
1293
1294
1294 - "<DATE" - at or before a given date/time
1295 - "<DATE" - at or before a given date/time
1295 - ">DATE" - on or after a given date/time
1296 - ">DATE" - on or after a given date/time
1296 - "DATE to DATE" - a date range, inclusive
1297 - "DATE to DATE" - a date range, inclusive
1297 - "-DAYS" - within a given number of days of today
1298 - "-DAYS" - within a given number of days of today
1298
1299
1299 Test repeated config section name
1300 Test repeated config section name
1300
1301
1301 $ hg help config.host
1302 $ hg help config.host
1302 "http_proxy.host"
1303 "http_proxy.host"
1303 Host name and (optional) port of the proxy server, for example
1304 Host name and (optional) port of the proxy server, for example
1304 "myproxy:8000".
1305 "myproxy:8000".
1305
1306
1306 "smtp.host"
1307 "smtp.host"
1307 Host name of mail server, e.g. "mail.example.com".
1308 Host name of mail server, e.g. "mail.example.com".
1308
1309
1309 Unrelated trailing paragraphs shouldn't be included
1310 Unrelated trailing paragraphs shouldn't be included
1310
1311
1311 $ hg help config.extramsg | grep '^$'
1312 $ hg help config.extramsg | grep '^$'
1312
1313
1313
1314
1314 Test capitalized section name
1315 Test capitalized section name
1315
1316
1316 $ hg help scripting.HGPLAIN > /dev/null
1317 $ hg help scripting.HGPLAIN > /dev/null
1317
1318
1318 Help subsection:
1319 Help subsection:
1319
1320
1320 $ hg help config.charsets |grep "Email example:" > /dev/null
1321 $ hg help config.charsets |grep "Email example:" > /dev/null
1321 [1]
1322 [1]
1322
1323
1323 Show nested definitions
1324 Show nested definitions
1324 ("profiling.type"[break]"ls"[break]"stat"[break])
1325 ("profiling.type"[break]"ls"[break]"stat"[break])
1325
1326
1326 $ hg help config.type | egrep '^$'|wc -l
1327 $ hg help config.type | egrep '^$'|wc -l
1327 \s*3 (re)
1328 \s*3 (re)
1328
1329
1329 Separate sections from subsections
1330 Separate sections from subsections
1330
1331
1331 $ hg help config.format | egrep '^ ("|-)|^\s*$' | uniq
1332 $ hg help config.format | egrep '^ ("|-)|^\s*$' | uniq
1332 "format"
1333 "format"
1333 --------
1334 --------
1334
1335
1335 "usegeneraldelta"
1336 "usegeneraldelta"
1336
1337
1337 "dotencode"
1338 "dotencode"
1338
1339
1339 "usefncache"
1340 "usefncache"
1340
1341
1341 "usestore"
1342 "usestore"
1342
1343
1343 "profiling"
1344 "profiling"
1344 -----------
1345 -----------
1345
1346
1346 "format"
1347 "format"
1347
1348
1348 "progress"
1349 "progress"
1349 ----------
1350 ----------
1350
1351
1351 "format"
1352 "format"
1352
1353
1353
1354
1354 Last item in help config.*:
1355 Last item in help config.*:
1355
1356
1356 $ hg help config.`hg help config|grep '^ "'| \
1357 $ hg help config.`hg help config|grep '^ "'| \
1357 > tail -1|sed 's![ "]*!!g'`| \
1358 > tail -1|sed 's![ "]*!!g'`| \
1358 > grep 'hg help -c config' > /dev/null
1359 > grep 'hg help -c config' > /dev/null
1359 [1]
1360 [1]
1360
1361
1361 note to use help -c for general hg help config:
1362 note to use help -c for general hg help config:
1362
1363
1363 $ hg help config |grep 'hg help -c config' > /dev/null
1364 $ hg help config |grep 'hg help -c config' > /dev/null
1364
1365
1365 Test templating help
1366 Test templating help
1366
1367
1367 $ hg help templating | egrep '(desc|diffstat|firstline|nonempty) '
1368 $ hg help templating | egrep '(desc|diffstat|firstline|nonempty) '
1368 desc String. The text of the changeset description.
1369 desc String. The text of the changeset description.
1369 diffstat String. Statistics of changes with the following format:
1370 diffstat String. Statistics of changes with the following format:
1370 firstline Any text. Returns the first line of text.
1371 firstline Any text. Returns the first line of text.
1371 nonempty Any text. Returns '(none)' if the string is empty.
1372 nonempty Any text. Returns '(none)' if the string is empty.
1372
1373
1373 Test deprecated items
1374 Test deprecated items
1374
1375
1375 $ hg help -v templating | grep currentbookmark
1376 $ hg help -v templating | grep currentbookmark
1376 currentbookmark
1377 currentbookmark
1377 $ hg help templating | (grep currentbookmark || true)
1378 $ hg help templating | (grep currentbookmark || true)
1378
1379
1379 Test help hooks
1380 Test help hooks
1380
1381
1381 $ cat > helphook1.py <<EOF
1382 $ cat > helphook1.py <<EOF
1382 > from mercurial import help
1383 > from mercurial import help
1383 >
1384 >
1384 > def rewrite(ui, topic, doc):
1385 > def rewrite(ui, topic, doc):
1385 > return doc + '\nhelphook1\n'
1386 > return doc + '\nhelphook1\n'
1386 >
1387 >
1387 > def extsetup(ui):
1388 > def extsetup(ui):
1388 > help.addtopichook('revisions', rewrite)
1389 > help.addtopichook('revisions', rewrite)
1389 > EOF
1390 > EOF
1390 $ cat > helphook2.py <<EOF
1391 $ cat > helphook2.py <<EOF
1391 > from mercurial import help
1392 > from mercurial import help
1392 >
1393 >
1393 > def rewrite(ui, topic, doc):
1394 > def rewrite(ui, topic, doc):
1394 > return doc + '\nhelphook2\n'
1395 > return doc + '\nhelphook2\n'
1395 >
1396 >
1396 > def extsetup(ui):
1397 > def extsetup(ui):
1397 > help.addtopichook('revisions', rewrite)
1398 > help.addtopichook('revisions', rewrite)
1398 > EOF
1399 > EOF
1399 $ echo '[extensions]' >> $HGRCPATH
1400 $ echo '[extensions]' >> $HGRCPATH
1400 $ echo "helphook1 = `pwd`/helphook1.py" >> $HGRCPATH
1401 $ echo "helphook1 = `pwd`/helphook1.py" >> $HGRCPATH
1401 $ echo "helphook2 = `pwd`/helphook2.py" >> $HGRCPATH
1402 $ echo "helphook2 = `pwd`/helphook2.py" >> $HGRCPATH
1402 $ hg help revsets | grep helphook
1403 $ hg help revsets | grep helphook
1403 helphook1
1404 helphook1
1404 helphook2
1405 helphook2
1405
1406
1406 help -c should only show debug --debug
1407 help -c should only show debug --debug
1407
1408
1408 $ hg help -c --debug|egrep debug|wc -l|egrep '^\s*0\s*$'
1409 $ hg help -c --debug|egrep debug|wc -l|egrep '^\s*0\s*$'
1409 [1]
1410 [1]
1410
1411
1411 help -c should only show deprecated for -v
1412 help -c should only show deprecated for -v
1412
1413
1413 $ hg help -c -v|egrep DEPRECATED|wc -l|egrep '^\s*0\s*$'
1414 $ hg help -c -v|egrep DEPRECATED|wc -l|egrep '^\s*0\s*$'
1414 [1]
1415 [1]
1415
1416
1416 Test -s / --system
1417 Test -s / --system
1417
1418
1418 $ hg help config.files -s windows |grep 'etc/mercurial' | \
1419 $ hg help config.files -s windows |grep 'etc/mercurial' | \
1419 > wc -l | sed -e 's/ //g'
1420 > wc -l | sed -e 's/ //g'
1420 0
1421 0
1421 $ hg help config.files --system unix | grep 'USER' | \
1422 $ hg help config.files --system unix | grep 'USER' | \
1422 > wc -l | sed -e 's/ //g'
1423 > wc -l | sed -e 's/ //g'
1423 0
1424 0
1424
1425
1425 Test -e / -c / -k combinations
1426 Test -e / -c / -k combinations
1426
1427
1427 $ hg help -c|egrep '^[A-Z].*:|^ debug'
1428 $ hg help -c|egrep '^[A-Z].*:|^ debug'
1428 Commands:
1429 Commands:
1429 $ hg help -e|egrep '^[A-Z].*:|^ debug'
1430 $ hg help -e|egrep '^[A-Z].*:|^ debug'
1430 Extensions:
1431 Extensions:
1431 $ hg help -k|egrep '^[A-Z].*:|^ debug'
1432 $ hg help -k|egrep '^[A-Z].*:|^ debug'
1432 Topics:
1433 Topics:
1433 Commands:
1434 Commands:
1434 Extensions:
1435 Extensions:
1435 Extension Commands:
1436 Extension Commands:
1436 $ hg help -c schemes
1437 $ hg help -c schemes
1437 abort: no such help topic: schemes
1438 abort: no such help topic: schemes
1438 (try 'hg help --keyword schemes')
1439 (try 'hg help --keyword schemes')
1439 [255]
1440 [255]
1440 $ hg help -e schemes |head -1
1441 $ hg help -e schemes |head -1
1441 schemes extension - extend schemes with shortcuts to repository swarms
1442 schemes extension - extend schemes with shortcuts to repository swarms
1442 $ hg help -c -k dates |egrep '^(Topics|Extensions|Commands):'
1443 $ hg help -c -k dates |egrep '^(Topics|Extensions|Commands):'
1443 Commands:
1444 Commands:
1444 $ hg help -e -k a |egrep '^(Topics|Extensions|Commands):'
1445 $ hg help -e -k a |egrep '^(Topics|Extensions|Commands):'
1445 Extensions:
1446 Extensions:
1446 $ hg help -e -c -k date |egrep '^(Topics|Extensions|Commands):'
1447 $ hg help -e -c -k date |egrep '^(Topics|Extensions|Commands):'
1447 Extensions:
1448 Extensions:
1448 Commands:
1449 Commands:
1449 $ hg help -c commit > /dev/null
1450 $ hg help -c commit > /dev/null
1450 $ hg help -e -c commit > /dev/null
1451 $ hg help -e -c commit > /dev/null
1451 $ hg help -e commit > /dev/null
1452 $ hg help -e commit > /dev/null
1452 abort: no such help topic: commit
1453 abort: no such help topic: commit
1453 (try 'hg help --keyword commit')
1454 (try 'hg help --keyword commit')
1454 [255]
1455 [255]
1455
1456
1456 Test keyword search help
1457 Test keyword search help
1457
1458
1458 $ cat > prefixedname.py <<EOF
1459 $ cat > prefixedname.py <<EOF
1459 > '''matched against word "clone"
1460 > '''matched against word "clone"
1460 > '''
1461 > '''
1461 > EOF
1462 > EOF
1462 $ echo '[extensions]' >> $HGRCPATH
1463 $ echo '[extensions]' >> $HGRCPATH
1463 $ echo "dot.dot.prefixedname = `pwd`/prefixedname.py" >> $HGRCPATH
1464 $ echo "dot.dot.prefixedname = `pwd`/prefixedname.py" >> $HGRCPATH
1464 $ hg help -k clone
1465 $ hg help -k clone
1465 Topics:
1466 Topics:
1466
1467
1467 config Configuration Files
1468 config Configuration Files
1468 extensions Using Additional Features
1469 extensions Using Additional Features
1469 glossary Glossary
1470 glossary Glossary
1470 phases Working with Phases
1471 phases Working with Phases
1471 subrepos Subrepositories
1472 subrepos Subrepositories
1472 urls URL Paths
1473 urls URL Paths
1473
1474
1474 Commands:
1475 Commands:
1475
1476
1476 bookmarks create a new bookmark or list existing bookmarks
1477 bookmarks create a new bookmark or list existing bookmarks
1477 clone make a copy of an existing repository
1478 clone make a copy of an existing repository
1478 paths show aliases for remote repositories
1479 paths show aliases for remote repositories
1479 update update working directory (or switch revisions)
1480 update update working directory (or switch revisions)
1480
1481
1481 Extensions:
1482 Extensions:
1482
1483
1483 clonebundles advertise pre-generated bundles to seed clones
1484 clonebundles advertise pre-generated bundles to seed clones
1484 prefixedname matched against word "clone"
1485 prefixedname matched against word "clone"
1485 relink recreates hardlinks between repository clones
1486 relink recreates hardlinks between repository clones
1486
1487
1487 Extension Commands:
1488 Extension Commands:
1488
1489
1489 qclone clone main and patch repository at same time
1490 qclone clone main and patch repository at same time
1490
1491
1491 Test unfound topic
1492 Test unfound topic
1492
1493
1493 $ hg help nonexistingtopicthatwillneverexisteverever
1494 $ hg help nonexistingtopicthatwillneverexisteverever
1494 abort: no such help topic: nonexistingtopicthatwillneverexisteverever
1495 abort: no such help topic: nonexistingtopicthatwillneverexisteverever
1495 (try 'hg help --keyword nonexistingtopicthatwillneverexisteverever')
1496 (try 'hg help --keyword nonexistingtopicthatwillneverexisteverever')
1496 [255]
1497 [255]
1497
1498
1498 Test unfound keyword
1499 Test unfound keyword
1499
1500
1500 $ hg help --keyword nonexistingwordthatwillneverexisteverever
1501 $ hg help --keyword nonexistingwordthatwillneverexisteverever
1501 abort: no matches
1502 abort: no matches
1502 (try 'hg help' for a list of topics)
1503 (try 'hg help' for a list of topics)
1503 [255]
1504 [255]
1504
1505
1505 Test omit indicating for help
1506 Test omit indicating for help
1506
1507
1507 $ cat > addverboseitems.py <<EOF
1508 $ cat > addverboseitems.py <<EOF
1508 > '''extension to test omit indicating.
1509 > '''extension to test omit indicating.
1509 >
1510 >
1510 > This paragraph is never omitted (for extension)
1511 > This paragraph is never omitted (for extension)
1511 >
1512 >
1512 > .. container:: verbose
1513 > .. container:: verbose
1513 >
1514 >
1514 > This paragraph is omitted,
1515 > This paragraph is omitted,
1515 > if :hg:\`help\` is invoked without \`\`-v\`\` (for extension)
1516 > if :hg:\`help\` is invoked without \`\`-v\`\` (for extension)
1516 >
1517 >
1517 > This paragraph is never omitted, too (for extension)
1518 > This paragraph is never omitted, too (for extension)
1518 > '''
1519 > '''
1519 > from __future__ import absolute_import
1520 > from __future__ import absolute_import
1520 > from mercurial import commands, help
1521 > from mercurial import commands, help
1521 > testtopic = """This paragraph is never omitted (for topic).
1522 > testtopic = """This paragraph is never omitted (for topic).
1522 >
1523 >
1523 > .. container:: verbose
1524 > .. container:: verbose
1524 >
1525 >
1525 > This paragraph is omitted,
1526 > This paragraph is omitted,
1526 > if :hg:\`help\` is invoked without \`\`-v\`\` (for topic)
1527 > if :hg:\`help\` is invoked without \`\`-v\`\` (for topic)
1527 >
1528 >
1528 > This paragraph is never omitted, too (for topic)
1529 > This paragraph is never omitted, too (for topic)
1529 > """
1530 > """
1530 > def extsetup(ui):
1531 > def extsetup(ui):
1531 > help.helptable.append((["topic-containing-verbose"],
1532 > help.helptable.append((["topic-containing-verbose"],
1532 > "This is the topic to test omit indicating.",
1533 > "This is the topic to test omit indicating.",
1533 > lambda ui: testtopic))
1534 > lambda ui: testtopic))
1534 > EOF
1535 > EOF
1535 $ echo '[extensions]' >> $HGRCPATH
1536 $ echo '[extensions]' >> $HGRCPATH
1536 $ echo "addverboseitems = `pwd`/addverboseitems.py" >> $HGRCPATH
1537 $ echo "addverboseitems = `pwd`/addverboseitems.py" >> $HGRCPATH
1537 $ hg help addverboseitems
1538 $ hg help addverboseitems
1538 addverboseitems extension - extension to test omit indicating.
1539 addverboseitems extension - extension to test omit indicating.
1539
1540
1540 This paragraph is never omitted (for extension)
1541 This paragraph is never omitted (for extension)
1541
1542
1542 This paragraph is never omitted, too (for extension)
1543 This paragraph is never omitted, too (for extension)
1543
1544
1544 (some details hidden, use --verbose to show complete help)
1545 (some details hidden, use --verbose to show complete help)
1545
1546
1546 no commands defined
1547 no commands defined
1547 $ hg help -v addverboseitems
1548 $ hg help -v addverboseitems
1548 addverboseitems extension - extension to test omit indicating.
1549 addverboseitems extension - extension to test omit indicating.
1549
1550
1550 This paragraph is never omitted (for extension)
1551 This paragraph is never omitted (for extension)
1551
1552
1552 This paragraph is omitted, if 'hg help' is invoked without "-v" (for
1553 This paragraph is omitted, if 'hg help' is invoked without "-v" (for
1553 extension)
1554 extension)
1554
1555
1555 This paragraph is never omitted, too (for extension)
1556 This paragraph is never omitted, too (for extension)
1556
1557
1557 no commands defined
1558 no commands defined
1558 $ hg help topic-containing-verbose
1559 $ hg help topic-containing-verbose
1559 This is the topic to test omit indicating.
1560 This is the topic to test omit indicating.
1560 """"""""""""""""""""""""""""""""""""""""""
1561 """"""""""""""""""""""""""""""""""""""""""
1561
1562
1562 This paragraph is never omitted (for topic).
1563 This paragraph is never omitted (for topic).
1563
1564
1564 This paragraph is never omitted, too (for topic)
1565 This paragraph is never omitted, too (for topic)
1565
1566
1566 (some details hidden, use --verbose to show complete help)
1567 (some details hidden, use --verbose to show complete help)
1567 $ hg help -v topic-containing-verbose
1568 $ hg help -v topic-containing-verbose
1568 This is the topic to test omit indicating.
1569 This is the topic to test omit indicating.
1569 """"""""""""""""""""""""""""""""""""""""""
1570 """"""""""""""""""""""""""""""""""""""""""
1570
1571
1571 This paragraph is never omitted (for topic).
1572 This paragraph is never omitted (for topic).
1572
1573
1573 This paragraph is omitted, if 'hg help' is invoked without "-v" (for
1574 This paragraph is omitted, if 'hg help' is invoked without "-v" (for
1574 topic)
1575 topic)
1575
1576
1576 This paragraph is never omitted, too (for topic)
1577 This paragraph is never omitted, too (for topic)
1577
1578
1578 Test section lookup
1579 Test section lookup
1579
1580
1580 $ hg help revset.merge
1581 $ hg help revset.merge
1581 "merge()"
1582 "merge()"
1582 Changeset is a merge changeset.
1583 Changeset is a merge changeset.
1583
1584
1584 $ hg help glossary.dag
1585 $ hg help glossary.dag
1585 DAG
1586 DAG
1586 The repository of changesets of a distributed version control system
1587 The repository of changesets of a distributed version control system
1587 (DVCS) can be described as a directed acyclic graph (DAG), consisting
1588 (DVCS) can be described as a directed acyclic graph (DAG), consisting
1588 of nodes and edges, where nodes correspond to changesets and edges
1589 of nodes and edges, where nodes correspond to changesets and edges
1589 imply a parent -> child relation. This graph can be visualized by
1590 imply a parent -> child relation. This graph can be visualized by
1590 graphical tools such as 'hg log --graph'. In Mercurial, the DAG is
1591 graphical tools such as 'hg log --graph'. In Mercurial, the DAG is
1591 limited by the requirement for children to have at most two parents.
1592 limited by the requirement for children to have at most two parents.
1592
1593
1593
1594
1594 $ hg help hgrc.paths
1595 $ hg help hgrc.paths
1595 "paths"
1596 "paths"
1596 -------
1597 -------
1597
1598
1598 Assigns symbolic names and behavior to repositories.
1599 Assigns symbolic names and behavior to repositories.
1599
1600
1600 Options are symbolic names defining the URL or directory that is the
1601 Options are symbolic names defining the URL or directory that is the
1601 location of the repository. Example:
1602 location of the repository. Example:
1602
1603
1603 [paths]
1604 [paths]
1604 my_server = https://example.com/my_repo
1605 my_server = https://example.com/my_repo
1605 local_path = /home/me/repo
1606 local_path = /home/me/repo
1606
1607
1607 These symbolic names can be used from the command line. To pull from
1608 These symbolic names can be used from the command line. To pull from
1608 "my_server": 'hg pull my_server'. To push to "local_path": 'hg push
1609 "my_server": 'hg pull my_server'. To push to "local_path": 'hg push
1609 local_path'.
1610 local_path'.
1610
1611
1611 Options containing colons (":") denote sub-options that can influence
1612 Options containing colons (":") denote sub-options that can influence
1612 behavior for that specific path. Example:
1613 behavior for that specific path. Example:
1613
1614
1614 [paths]
1615 [paths]
1615 my_server = https://example.com/my_path
1616 my_server = https://example.com/my_path
1616 my_server:pushurl = ssh://example.com/my_path
1617 my_server:pushurl = ssh://example.com/my_path
1617
1618
1618 The following sub-options can be defined:
1619 The following sub-options can be defined:
1619
1620
1620 "pushurl"
1621 "pushurl"
1621 The URL to use for push operations. If not defined, the location
1622 The URL to use for push operations. If not defined, the location
1622 defined by the path's main entry is used.
1623 defined by the path's main entry is used.
1623
1624
1624 "pushrev"
1625 "pushrev"
1625 A revset defining which revisions to push by default.
1626 A revset defining which revisions to push by default.
1626
1627
1627 When 'hg push' is executed without a "-r" argument, the revset defined
1628 When 'hg push' is executed without a "-r" argument, the revset defined
1628 by this sub-option is evaluated to determine what to push.
1629 by this sub-option is evaluated to determine what to push.
1629
1630
1630 For example, a value of "." will push the working directory's revision
1631 For example, a value of "." will push the working directory's revision
1631 by default.
1632 by default.
1632
1633
1633 Revsets specifying bookmarks will not result in the bookmark being
1634 Revsets specifying bookmarks will not result in the bookmark being
1634 pushed.
1635 pushed.
1635
1636
1636 The following special named paths exist:
1637 The following special named paths exist:
1637
1638
1638 "default"
1639 "default"
1639 The URL or directory to use when no source or remote is specified.
1640 The URL or directory to use when no source or remote is specified.
1640
1641
1641 'hg clone' will automatically define this path to the location the
1642 'hg clone' will automatically define this path to the location the
1642 repository was cloned from.
1643 repository was cloned from.
1643
1644
1644 "default-push"
1645 "default-push"
1645 (deprecated) The URL or directory for the default 'hg push' location.
1646 (deprecated) The URL or directory for the default 'hg push' location.
1646 "default:pushurl" should be used instead.
1647 "default:pushurl" should be used instead.
1647
1648
1648 $ hg help glossary.mcguffin
1649 $ hg help glossary.mcguffin
1649 abort: help section not found: glossary.mcguffin
1650 abort: help section not found: glossary.mcguffin
1650 [255]
1651 [255]
1651
1652
1652 $ hg help glossary.mc.guffin
1653 $ hg help glossary.mc.guffin
1653 abort: help section not found: glossary.mc.guffin
1654 abort: help section not found: glossary.mc.guffin
1654 [255]
1655 [255]
1655
1656
1656 $ hg help template.files
1657 $ hg help template.files
1657 files List of strings. All files modified, added, or removed by
1658 files List of strings. All files modified, added, or removed by
1658 this changeset.
1659 this changeset.
1659 files(pattern)
1660 files(pattern)
1660 All files of the current changeset matching the pattern. See
1661 All files of the current changeset matching the pattern. See
1661 'hg help patterns'.
1662 'hg help patterns'.
1662
1663
1663 Test section lookup by translated message
1664 Test section lookup by translated message
1664
1665
1665 str.lower() instead of encoding.lower(str) on translated message might
1666 str.lower() instead of encoding.lower(str) on translated message might
1666 make message meaningless, because some encoding uses 0x41(A) - 0x5a(Z)
1667 make message meaningless, because some encoding uses 0x41(A) - 0x5a(Z)
1667 as the second or later byte of multi-byte character.
1668 as the second or later byte of multi-byte character.
1668
1669
1669 For example, "\x8bL\x98^" (translation of "record" in ja_JP.cp932)
1670 For example, "\x8bL\x98^" (translation of "record" in ja_JP.cp932)
1670 contains 0x4c (L). str.lower() replaces 0x4c(L) by 0x6c(l) and this
1671 contains 0x4c (L). str.lower() replaces 0x4c(L) by 0x6c(l) and this
1671 replacement makes message meaningless.
1672 replacement makes message meaningless.
1672
1673
1673 This tests that section lookup by translated string isn't broken by
1674 This tests that section lookup by translated string isn't broken by
1674 such str.lower().
1675 such str.lower().
1675
1676
1676 $ $PYTHON <<EOF
1677 $ $PYTHON <<EOF
1677 > def escape(s):
1678 > def escape(s):
1678 > return ''.join('\u%x' % ord(uc) for uc in s.decode('cp932'))
1679 > return ''.join('\u%x' % ord(uc) for uc in s.decode('cp932'))
1679 > # translation of "record" in ja_JP.cp932
1680 > # translation of "record" in ja_JP.cp932
1680 > upper = "\x8bL\x98^"
1681 > upper = "\x8bL\x98^"
1681 > # str.lower()-ed section name should be treated as different one
1682 > # str.lower()-ed section name should be treated as different one
1682 > lower = "\x8bl\x98^"
1683 > lower = "\x8bl\x98^"
1683 > with open('ambiguous.py', 'w') as fp:
1684 > with open('ambiguous.py', 'w') as fp:
1684 > fp.write("""# ambiguous section names in ja_JP.cp932
1685 > fp.write("""# ambiguous section names in ja_JP.cp932
1685 > u'''summary of extension
1686 > u'''summary of extension
1686 >
1687 >
1687 > %s
1688 > %s
1688 > ----
1689 > ----
1689 >
1690 >
1690 > Upper name should show only this message
1691 > Upper name should show only this message
1691 >
1692 >
1692 > %s
1693 > %s
1693 > ----
1694 > ----
1694 >
1695 >
1695 > Lower name should show only this message
1696 > Lower name should show only this message
1696 >
1697 >
1697 > subsequent section
1698 > subsequent section
1698 > ------------------
1699 > ------------------
1699 >
1700 >
1700 > This should be hidden at 'hg help ambiguous' with section name.
1701 > This should be hidden at 'hg help ambiguous' with section name.
1701 > '''
1702 > '''
1702 > """ % (escape(upper), escape(lower)))
1703 > """ % (escape(upper), escape(lower)))
1703 > EOF
1704 > EOF
1704
1705
1705 $ cat >> $HGRCPATH <<EOF
1706 $ cat >> $HGRCPATH <<EOF
1706 > [extensions]
1707 > [extensions]
1707 > ambiguous = ./ambiguous.py
1708 > ambiguous = ./ambiguous.py
1708 > EOF
1709 > EOF
1709
1710
1710 $ $PYTHON <<EOF | sh
1711 $ $PYTHON <<EOF | sh
1711 > upper = "\x8bL\x98^"
1712 > upper = "\x8bL\x98^"
1712 > print("hg --encoding cp932 help -e ambiguous.%s" % upper)
1713 > print("hg --encoding cp932 help -e ambiguous.%s" % upper)
1713 > EOF
1714 > EOF
1714 \x8bL\x98^ (esc)
1715 \x8bL\x98^ (esc)
1715 ----
1716 ----
1716
1717
1717 Upper name should show only this message
1718 Upper name should show only this message
1718
1719
1719
1720
1720 $ $PYTHON <<EOF | sh
1721 $ $PYTHON <<EOF | sh
1721 > lower = "\x8bl\x98^"
1722 > lower = "\x8bl\x98^"
1722 > print("hg --encoding cp932 help -e ambiguous.%s" % lower)
1723 > print("hg --encoding cp932 help -e ambiguous.%s" % lower)
1723 > EOF
1724 > EOF
1724 \x8bl\x98^ (esc)
1725 \x8bl\x98^ (esc)
1725 ----
1726 ----
1726
1727
1727 Lower name should show only this message
1728 Lower name should show only this message
1728
1729
1729
1730
1730 $ cat >> $HGRCPATH <<EOF
1731 $ cat >> $HGRCPATH <<EOF
1731 > [extensions]
1732 > [extensions]
1732 > ambiguous = !
1733 > ambiguous = !
1733 > EOF
1734 > EOF
1734
1735
1735 Show help content of disabled extensions
1736 Show help content of disabled extensions
1736
1737
1737 $ cat >> $HGRCPATH <<EOF
1738 $ cat >> $HGRCPATH <<EOF
1738 > [extensions]
1739 > [extensions]
1739 > ambiguous = !./ambiguous.py
1740 > ambiguous = !./ambiguous.py
1740 > EOF
1741 > EOF
1741 $ hg help -e ambiguous
1742 $ hg help -e ambiguous
1742 ambiguous extension - (no help text available)
1743 ambiguous extension - (no help text available)
1743
1744
1744 (use 'hg help extensions' for information on enabling extensions)
1745 (use 'hg help extensions' for information on enabling extensions)
1745
1746
1746 Test dynamic list of merge tools only shows up once
1747 Test dynamic list of merge tools only shows up once
1747 $ hg help merge-tools
1748 $ hg help merge-tools
1748 Merge Tools
1749 Merge Tools
1749 """""""""""
1750 """""""""""
1750
1751
1751 To merge files Mercurial uses merge tools.
1752 To merge files Mercurial uses merge tools.
1752
1753
1753 A merge tool combines two different versions of a file into a merged file.
1754 A merge tool combines two different versions of a file into a merged file.
1754 Merge tools are given the two files and the greatest common ancestor of
1755 Merge tools are given the two files and the greatest common ancestor of
1755 the two file versions, so they can determine the changes made on both
1756 the two file versions, so they can determine the changes made on both
1756 branches.
1757 branches.
1757
1758
1758 Merge tools are used both for 'hg resolve', 'hg merge', 'hg update', 'hg
1759 Merge tools are used both for 'hg resolve', 'hg merge', 'hg update', 'hg
1759 backout' and in several extensions.
1760 backout' and in several extensions.
1760
1761
1761 Usually, the merge tool tries to automatically reconcile the files by
1762 Usually, the merge tool tries to automatically reconcile the files by
1762 combining all non-overlapping changes that occurred separately in the two
1763 combining all non-overlapping changes that occurred separately in the two
1763 different evolutions of the same initial base file. Furthermore, some
1764 different evolutions of the same initial base file. Furthermore, some
1764 interactive merge programs make it easier to manually resolve conflicting
1765 interactive merge programs make it easier to manually resolve conflicting
1765 merges, either in a graphical way, or by inserting some conflict markers.
1766 merges, either in a graphical way, or by inserting some conflict markers.
1766 Mercurial does not include any interactive merge programs but relies on
1767 Mercurial does not include any interactive merge programs but relies on
1767 external tools for that.
1768 external tools for that.
1768
1769
1769 Available merge tools
1770 Available merge tools
1770 =====================
1771 =====================
1771
1772
1772 External merge tools and their properties are configured in the merge-
1773 External merge tools and their properties are configured in the merge-
1773 tools configuration section - see hgrc(5) - but they can often just be
1774 tools configuration section - see hgrc(5) - but they can often just be
1774 named by their executable.
1775 named by their executable.
1775
1776
1776 A merge tool is generally usable if its executable can be found on the
1777 A merge tool is generally usable if its executable can be found on the
1777 system and if it can handle the merge. The executable is found if it is an
1778 system and if it can handle the merge. The executable is found if it is an
1778 absolute or relative executable path or the name of an application in the
1779 absolute or relative executable path or the name of an application in the
1779 executable search path. The tool is assumed to be able to handle the merge
1780 executable search path. The tool is assumed to be able to handle the merge
1780 if it can handle symlinks if the file is a symlink, if it can handle
1781 if it can handle symlinks if the file is a symlink, if it can handle
1781 binary files if the file is binary, and if a GUI is available if the tool
1782 binary files if the file is binary, and if a GUI is available if the tool
1782 requires a GUI.
1783 requires a GUI.
1783
1784
1784 There are some internal merge tools which can be used. The internal merge
1785 There are some internal merge tools which can be used. The internal merge
1785 tools are:
1786 tools are:
1786
1787
1787 ":dump"
1788 ":dump"
1788 Creates three versions of the files to merge, containing the contents of
1789 Creates three versions of the files to merge, containing the contents of
1789 local, other and base. These files can then be used to perform a merge
1790 local, other and base. These files can then be used to perform a merge
1790 manually. If the file to be merged is named "a.txt", these files will
1791 manually. If the file to be merged is named "a.txt", these files will
1791 accordingly be named "a.txt.local", "a.txt.other" and "a.txt.base" and
1792 accordingly be named "a.txt.local", "a.txt.other" and "a.txt.base" and
1792 they will be placed in the same directory as "a.txt".
1793 they will be placed in the same directory as "a.txt".
1793
1794
1794 This implies permerge. Therefore, files aren't dumped, if premerge runs
1795 This implies permerge. Therefore, files aren't dumped, if premerge runs
1795 successfully. Use :forcedump to forcibly write files out.
1796 successfully. Use :forcedump to forcibly write files out.
1796
1797
1797 ":fail"
1798 ":fail"
1798 Rather than attempting to merge files that were modified on both
1799 Rather than attempting to merge files that were modified on both
1799 branches, it marks them as unresolved. The resolve command must be used
1800 branches, it marks them as unresolved. The resolve command must be used
1800 to resolve these conflicts.
1801 to resolve these conflicts.
1801
1802
1802 ":forcedump"
1803 ":forcedump"
1803 Creates three versions of the files as same as :dump, but omits
1804 Creates three versions of the files as same as :dump, but omits
1804 premerge.
1805 premerge.
1805
1806
1806 ":local"
1807 ":local"
1807 Uses the local 'p1()' version of files as the merged version.
1808 Uses the local 'p1()' version of files as the merged version.
1808
1809
1809 ":merge"
1810 ":merge"
1810 Uses the internal non-interactive simple merge algorithm for merging
1811 Uses the internal non-interactive simple merge algorithm for merging
1811 files. It will fail if there are any conflicts and leave markers in the
1812 files. It will fail if there are any conflicts and leave markers in the
1812 partially merged file. Markers will have two sections, one for each side
1813 partially merged file. Markers will have two sections, one for each side
1813 of merge.
1814 of merge.
1814
1815
1815 ":merge-local"
1816 ":merge-local"
1816 Like :merge, but resolve all conflicts non-interactively in favor of the
1817 Like :merge, but resolve all conflicts non-interactively in favor of the
1817 local 'p1()' changes.
1818 local 'p1()' changes.
1818
1819
1819 ":merge-other"
1820 ":merge-other"
1820 Like :merge, but resolve all conflicts non-interactively in favor of the
1821 Like :merge, but resolve all conflicts non-interactively in favor of the
1821 other 'p2()' changes.
1822 other 'p2()' changes.
1822
1823
1823 ":merge3"
1824 ":merge3"
1824 Uses the internal non-interactive simple merge algorithm for merging
1825 Uses the internal non-interactive simple merge algorithm for merging
1825 files. It will fail if there are any conflicts and leave markers in the
1826 files. It will fail if there are any conflicts and leave markers in the
1826 partially merged file. Marker will have three sections, one from each
1827 partially merged file. Marker will have three sections, one from each
1827 side of the merge and one for the base content.
1828 side of the merge and one for the base content.
1828
1829
1829 ":other"
1830 ":other"
1830 Uses the other 'p2()' version of files as the merged version.
1831 Uses the other 'p2()' version of files as the merged version.
1831
1832
1832 ":prompt"
1833 ":prompt"
1833 Asks the user which of the local 'p1()' or the other 'p2()' version to
1834 Asks the user which of the local 'p1()' or the other 'p2()' version to
1834 keep as the merged version.
1835 keep as the merged version.
1835
1836
1836 ":tagmerge"
1837 ":tagmerge"
1837 Uses the internal tag merge algorithm (experimental).
1838 Uses the internal tag merge algorithm (experimental).
1838
1839
1839 ":union"
1840 ":union"
1840 Uses the internal non-interactive simple merge algorithm for merging
1841 Uses the internal non-interactive simple merge algorithm for merging
1841 files. It will use both left and right sides for conflict regions. No
1842 files. It will use both left and right sides for conflict regions. No
1842 markers are inserted.
1843 markers are inserted.
1843
1844
1844 Internal tools are always available and do not require a GUI but will by
1845 Internal tools are always available and do not require a GUI but will by
1845 default not handle symlinks or binary files.
1846 default not handle symlinks or binary files.
1846
1847
1847 Choosing a merge tool
1848 Choosing a merge tool
1848 =====================
1849 =====================
1849
1850
1850 Mercurial uses these rules when deciding which merge tool to use:
1851 Mercurial uses these rules when deciding which merge tool to use:
1851
1852
1852 1. If a tool has been specified with the --tool option to merge or
1853 1. If a tool has been specified with the --tool option to merge or
1853 resolve, it is used. If it is the name of a tool in the merge-tools
1854 resolve, it is used. If it is the name of a tool in the merge-tools
1854 configuration, its configuration is used. Otherwise the specified tool
1855 configuration, its configuration is used. Otherwise the specified tool
1855 must be executable by the shell.
1856 must be executable by the shell.
1856 2. If the "HGMERGE" environment variable is present, its value is used and
1857 2. If the "HGMERGE" environment variable is present, its value is used and
1857 must be executable by the shell.
1858 must be executable by the shell.
1858 3. If the filename of the file to be merged matches any of the patterns in
1859 3. If the filename of the file to be merged matches any of the patterns in
1859 the merge-patterns configuration section, the first usable merge tool
1860 the merge-patterns configuration section, the first usable merge tool
1860 corresponding to a matching pattern is used. Here, binary capabilities
1861 corresponding to a matching pattern is used. Here, binary capabilities
1861 of the merge tool are not considered.
1862 of the merge tool are not considered.
1862 4. If ui.merge is set it will be considered next. If the value is not the
1863 4. If ui.merge is set it will be considered next. If the value is not the
1863 name of a configured tool, the specified value is used and must be
1864 name of a configured tool, the specified value is used and must be
1864 executable by the shell. Otherwise the named tool is used if it is
1865 executable by the shell. Otherwise the named tool is used if it is
1865 usable.
1866 usable.
1866 5. If any usable merge tools are present in the merge-tools configuration
1867 5. If any usable merge tools are present in the merge-tools configuration
1867 section, the one with the highest priority is used.
1868 section, the one with the highest priority is used.
1868 6. If a program named "hgmerge" can be found on the system, it is used -
1869 6. If a program named "hgmerge" can be found on the system, it is used -
1869 but it will by default not be used for symlinks and binary files.
1870 but it will by default not be used for symlinks and binary files.
1870 7. If the file to be merged is not binary and is not a symlink, then
1871 7. If the file to be merged is not binary and is not a symlink, then
1871 internal ":merge" is used.
1872 internal ":merge" is used.
1872 8. Otherwise, ":prompt" is used.
1873 8. Otherwise, ":prompt" is used.
1873
1874
1874 Note:
1875 Note:
1875 After selecting a merge program, Mercurial will by default attempt to
1876 After selecting a merge program, Mercurial will by default attempt to
1876 merge the files using a simple merge algorithm first. Only if it
1877 merge the files using a simple merge algorithm first. Only if it
1877 doesn't succeed because of conflicting changes Mercurial will actually
1878 doesn't succeed because of conflicting changes Mercurial will actually
1878 execute the merge program. Whether to use the simple merge algorithm
1879 execute the merge program. Whether to use the simple merge algorithm
1879 first can be controlled by the premerge setting of the merge tool.
1880 first can be controlled by the premerge setting of the merge tool.
1880 Premerge is enabled by default unless the file is binary or a symlink.
1881 Premerge is enabled by default unless the file is binary or a symlink.
1881
1882
1882 See the merge-tools and ui sections of hgrc(5) for details on the
1883 See the merge-tools and ui sections of hgrc(5) for details on the
1883 configuration of merge tools.
1884 configuration of merge tools.
1884
1885
1885 Compression engines listed in `hg help bundlespec`
1886 Compression engines listed in `hg help bundlespec`
1886
1887
1887 $ hg help bundlespec | grep gzip
1888 $ hg help bundlespec | grep gzip
1888 "v1" bundles can only use the "gzip", "bzip2", and "none" compression
1889 "v1" bundles can only use the "gzip", "bzip2", and "none" compression
1889 An algorithm that produces smaller bundles than "gzip".
1890 An algorithm that produces smaller bundles than "gzip".
1890 This engine will likely produce smaller bundles than "gzip" but will be
1891 This engine will likely produce smaller bundles than "gzip" but will be
1891 "gzip"
1892 "gzip"
1892 better compression than "gzip". It also frequently yields better (?)
1893 better compression than "gzip". It also frequently yields better (?)
1893
1894
1894 Test usage of section marks in help documents
1895 Test usage of section marks in help documents
1895
1896
1896 $ cd "$TESTDIR"/../doc
1897 $ cd "$TESTDIR"/../doc
1897 $ $PYTHON check-seclevel.py
1898 $ $PYTHON check-seclevel.py
1898 $ cd $TESTTMP
1899 $ cd $TESTTMP
1899
1900
1900 #if serve
1901 #if serve
1901
1902
1902 Test the help pages in hgweb.
1903 Test the help pages in hgweb.
1903
1904
1904 Dish up an empty repo; serve it cold.
1905 Dish up an empty repo; serve it cold.
1905
1906
1906 $ hg init "$TESTTMP/test"
1907 $ hg init "$TESTTMP/test"
1907 $ hg serve -R "$TESTTMP/test" -n test -p $HGPORT -d --pid-file=hg.pid
1908 $ hg serve -R "$TESTTMP/test" -n test -p $HGPORT -d --pid-file=hg.pid
1908 $ cat hg.pid >> $DAEMON_PIDS
1909 $ cat hg.pid >> $DAEMON_PIDS
1909
1910
1910 $ get-with-headers.py $LOCALIP:$HGPORT "help"
1911 $ get-with-headers.py $LOCALIP:$HGPORT "help"
1911 200 Script output follows
1912 200 Script output follows
1912
1913
1913 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
1914 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
1914 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
1915 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
1915 <head>
1916 <head>
1916 <link rel="icon" href="/static/hgicon.png" type="image/png" />
1917 <link rel="icon" href="/static/hgicon.png" type="image/png" />
1917 <meta name="robots" content="index, nofollow" />
1918 <meta name="robots" content="index, nofollow" />
1918 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
1919 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
1919 <script type="text/javascript" src="/static/mercurial.js"></script>
1920 <script type="text/javascript" src="/static/mercurial.js"></script>
1920
1921
1921 <title>Help: Index</title>
1922 <title>Help: Index</title>
1922 </head>
1923 </head>
1923 <body>
1924 <body>
1924
1925
1925 <div class="container">
1926 <div class="container">
1926 <div class="menu">
1927 <div class="menu">
1927 <div class="logo">
1928 <div class="logo">
1928 <a href="https://mercurial-scm.org/">
1929 <a href="https://mercurial-scm.org/">
1929 <img src="/static/hglogo.png" alt="mercurial" /></a>
1930 <img src="/static/hglogo.png" alt="mercurial" /></a>
1930 </div>
1931 </div>
1931 <ul>
1932 <ul>
1932 <li><a href="/shortlog">log</a></li>
1933 <li><a href="/shortlog">log</a></li>
1933 <li><a href="/graph">graph</a></li>
1934 <li><a href="/graph">graph</a></li>
1934 <li><a href="/tags">tags</a></li>
1935 <li><a href="/tags">tags</a></li>
1935 <li><a href="/bookmarks">bookmarks</a></li>
1936 <li><a href="/bookmarks">bookmarks</a></li>
1936 <li><a href="/branches">branches</a></li>
1937 <li><a href="/branches">branches</a></li>
1937 </ul>
1938 </ul>
1938 <ul>
1939 <ul>
1939 <li class="active">help</li>
1940 <li class="active">help</li>
1940 </ul>
1941 </ul>
1941 </div>
1942 </div>
1942
1943
1943 <div class="main">
1944 <div class="main">
1944 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
1945 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
1945
1946
1946 <form class="search" action="/log">
1947 <form class="search" action="/log">
1947
1948
1948 <p><input name="rev" id="search1" type="text" size="30" value="" /></p>
1949 <p><input name="rev" id="search1" type="text" size="30" value="" /></p>
1949 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
1950 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
1950 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
1951 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
1951 </form>
1952 </form>
1952 <table class="bigtable">
1953 <table class="bigtable">
1953 <tr><td colspan="2"><h2><a name="topics" href="#topics">Topics</a></h2></td></tr>
1954 <tr><td colspan="2"><h2><a name="topics" href="#topics">Topics</a></h2></td></tr>
1954
1955
1955 <tr><td>
1956 <tr><td>
1956 <a href="/help/bundlespec">
1957 <a href="/help/bundlespec">
1957 bundlespec
1958 bundlespec
1958 </a>
1959 </a>
1959 </td><td>
1960 </td><td>
1960 Bundle File Formats
1961 Bundle File Formats
1961 </td></tr>
1962 </td></tr>
1962 <tr><td>
1963 <tr><td>
1963 <a href="/help/color">
1964 <a href="/help/color">
1964 color
1965 color
1965 </a>
1966 </a>
1966 </td><td>
1967 </td><td>
1967 Colorizing Outputs
1968 Colorizing Outputs
1968 </td></tr>
1969 </td></tr>
1969 <tr><td>
1970 <tr><td>
1970 <a href="/help/config">
1971 <a href="/help/config">
1971 config
1972 config
1972 </a>
1973 </a>
1973 </td><td>
1974 </td><td>
1974 Configuration Files
1975 Configuration Files
1975 </td></tr>
1976 </td></tr>
1976 <tr><td>
1977 <tr><td>
1977 <a href="/help/dates">
1978 <a href="/help/dates">
1978 dates
1979 dates
1979 </a>
1980 </a>
1980 </td><td>
1981 </td><td>
1981 Date Formats
1982 Date Formats
1982 </td></tr>
1983 </td></tr>
1983 <tr><td>
1984 <tr><td>
1984 <a href="/help/diffs">
1985 <a href="/help/diffs">
1985 diffs
1986 diffs
1986 </a>
1987 </a>
1987 </td><td>
1988 </td><td>
1988 Diff Formats
1989 Diff Formats
1989 </td></tr>
1990 </td></tr>
1990 <tr><td>
1991 <tr><td>
1991 <a href="/help/environment">
1992 <a href="/help/environment">
1992 environment
1993 environment
1993 </a>
1994 </a>
1994 </td><td>
1995 </td><td>
1995 Environment Variables
1996 Environment Variables
1996 </td></tr>
1997 </td></tr>
1997 <tr><td>
1998 <tr><td>
1998 <a href="/help/extensions">
1999 <a href="/help/extensions">
1999 extensions
2000 extensions
2000 </a>
2001 </a>
2001 </td><td>
2002 </td><td>
2002 Using Additional Features
2003 Using Additional Features
2003 </td></tr>
2004 </td></tr>
2004 <tr><td>
2005 <tr><td>
2005 <a href="/help/filesets">
2006 <a href="/help/filesets">
2006 filesets
2007 filesets
2007 </a>
2008 </a>
2008 </td><td>
2009 </td><td>
2009 Specifying File Sets
2010 Specifying File Sets
2010 </td></tr>
2011 </td></tr>
2011 <tr><td>
2012 <tr><td>
2012 <a href="/help/glossary">
2013 <a href="/help/glossary">
2013 glossary
2014 glossary
2014 </a>
2015 </a>
2015 </td><td>
2016 </td><td>
2016 Glossary
2017 Glossary
2017 </td></tr>
2018 </td></tr>
2018 <tr><td>
2019 <tr><td>
2019 <a href="/help/hgignore">
2020 <a href="/help/hgignore">
2020 hgignore
2021 hgignore
2021 </a>
2022 </a>
2022 </td><td>
2023 </td><td>
2023 Syntax for Mercurial Ignore Files
2024 Syntax for Mercurial Ignore Files
2024 </td></tr>
2025 </td></tr>
2025 <tr><td>
2026 <tr><td>
2026 <a href="/help/hgweb">
2027 <a href="/help/hgweb">
2027 hgweb
2028 hgweb
2028 </a>
2029 </a>
2029 </td><td>
2030 </td><td>
2030 Configuring hgweb
2031 Configuring hgweb
2031 </td></tr>
2032 </td></tr>
2032 <tr><td>
2033 <tr><td>
2033 <a href="/help/internals">
2034 <a href="/help/internals">
2034 internals
2035 internals
2035 </a>
2036 </a>
2036 </td><td>
2037 </td><td>
2037 Technical implementation topics
2038 Technical implementation topics
2038 </td></tr>
2039 </td></tr>
2039 <tr><td>
2040 <tr><td>
2040 <a href="/help/merge-tools">
2041 <a href="/help/merge-tools">
2041 merge-tools
2042 merge-tools
2042 </a>
2043 </a>
2043 </td><td>
2044 </td><td>
2044 Merge Tools
2045 Merge Tools
2045 </td></tr>
2046 </td></tr>
2046 <tr><td>
2047 <tr><td>
2047 <a href="/help/pager">
2048 <a href="/help/pager">
2048 pager
2049 pager
2049 </a>
2050 </a>
2050 </td><td>
2051 </td><td>
2051 Pager Support
2052 Pager Support
2052 </td></tr>
2053 </td></tr>
2053 <tr><td>
2054 <tr><td>
2054 <a href="/help/patterns">
2055 <a href="/help/patterns">
2055 patterns
2056 patterns
2056 </a>
2057 </a>
2057 </td><td>
2058 </td><td>
2058 File Name Patterns
2059 File Name Patterns
2059 </td></tr>
2060 </td></tr>
2060 <tr><td>
2061 <tr><td>
2061 <a href="/help/phases">
2062 <a href="/help/phases">
2062 phases
2063 phases
2063 </a>
2064 </a>
2064 </td><td>
2065 </td><td>
2065 Working with Phases
2066 Working with Phases
2066 </td></tr>
2067 </td></tr>
2067 <tr><td>
2068 <tr><td>
2068 <a href="/help/revisions">
2069 <a href="/help/revisions">
2069 revisions
2070 revisions
2070 </a>
2071 </a>
2071 </td><td>
2072 </td><td>
2072 Specifying Revisions
2073 Specifying Revisions
2073 </td></tr>
2074 </td></tr>
2074 <tr><td>
2075 <tr><td>
2075 <a href="/help/scripting">
2076 <a href="/help/scripting">
2076 scripting
2077 scripting
2077 </a>
2078 </a>
2078 </td><td>
2079 </td><td>
2079 Using Mercurial from scripts and automation
2080 Using Mercurial from scripts and automation
2080 </td></tr>
2081 </td></tr>
2081 <tr><td>
2082 <tr><td>
2082 <a href="/help/subrepos">
2083 <a href="/help/subrepos">
2083 subrepos
2084 subrepos
2084 </a>
2085 </a>
2085 </td><td>
2086 </td><td>
2086 Subrepositories
2087 Subrepositories
2087 </td></tr>
2088 </td></tr>
2088 <tr><td>
2089 <tr><td>
2089 <a href="/help/templating">
2090 <a href="/help/templating">
2090 templating
2091 templating
2091 </a>
2092 </a>
2092 </td><td>
2093 </td><td>
2093 Template Usage
2094 Template Usage
2094 </td></tr>
2095 </td></tr>
2095 <tr><td>
2096 <tr><td>
2096 <a href="/help/urls">
2097 <a href="/help/urls">
2097 urls
2098 urls
2098 </a>
2099 </a>
2099 </td><td>
2100 </td><td>
2100 URL Paths
2101 URL Paths
2101 </td></tr>
2102 </td></tr>
2102 <tr><td>
2103 <tr><td>
2103 <a href="/help/topic-containing-verbose">
2104 <a href="/help/topic-containing-verbose">
2104 topic-containing-verbose
2105 topic-containing-verbose
2105 </a>
2106 </a>
2106 </td><td>
2107 </td><td>
2107 This is the topic to test omit indicating.
2108 This is the topic to test omit indicating.
2108 </td></tr>
2109 </td></tr>
2109
2110
2110
2111
2111 <tr><td colspan="2"><h2><a name="main" href="#main">Main Commands</a></h2></td></tr>
2112 <tr><td colspan="2"><h2><a name="main" href="#main">Main Commands</a></h2></td></tr>
2112
2113
2113 <tr><td>
2114 <tr><td>
2114 <a href="/help/add">
2115 <a href="/help/add">
2115 add
2116 add
2116 </a>
2117 </a>
2117 </td><td>
2118 </td><td>
2118 add the specified files on the next commit
2119 add the specified files on the next commit
2119 </td></tr>
2120 </td></tr>
2120 <tr><td>
2121 <tr><td>
2121 <a href="/help/annotate">
2122 <a href="/help/annotate">
2122 annotate
2123 annotate
2123 </a>
2124 </a>
2124 </td><td>
2125 </td><td>
2125 show changeset information by line for each file
2126 show changeset information by line for each file
2126 </td></tr>
2127 </td></tr>
2127 <tr><td>
2128 <tr><td>
2128 <a href="/help/clone">
2129 <a href="/help/clone">
2129 clone
2130 clone
2130 </a>
2131 </a>
2131 </td><td>
2132 </td><td>
2132 make a copy of an existing repository
2133 make a copy of an existing repository
2133 </td></tr>
2134 </td></tr>
2134 <tr><td>
2135 <tr><td>
2135 <a href="/help/commit">
2136 <a href="/help/commit">
2136 commit
2137 commit
2137 </a>
2138 </a>
2138 </td><td>
2139 </td><td>
2139 commit the specified files or all outstanding changes
2140 commit the specified files or all outstanding changes
2140 </td></tr>
2141 </td></tr>
2141 <tr><td>
2142 <tr><td>
2142 <a href="/help/diff">
2143 <a href="/help/diff">
2143 diff
2144 diff
2144 </a>
2145 </a>
2145 </td><td>
2146 </td><td>
2146 diff repository (or selected files)
2147 diff repository (or selected files)
2147 </td></tr>
2148 </td></tr>
2148 <tr><td>
2149 <tr><td>
2149 <a href="/help/export">
2150 <a href="/help/export">
2150 export
2151 export
2151 </a>
2152 </a>
2152 </td><td>
2153 </td><td>
2153 dump the header and diffs for one or more changesets
2154 dump the header and diffs for one or more changesets
2154 </td></tr>
2155 </td></tr>
2155 <tr><td>
2156 <tr><td>
2156 <a href="/help/forget">
2157 <a href="/help/forget">
2157 forget
2158 forget
2158 </a>
2159 </a>
2159 </td><td>
2160 </td><td>
2160 forget the specified files on the next commit
2161 forget the specified files on the next commit
2161 </td></tr>
2162 </td></tr>
2162 <tr><td>
2163 <tr><td>
2163 <a href="/help/init">
2164 <a href="/help/init">
2164 init
2165 init
2165 </a>
2166 </a>
2166 </td><td>
2167 </td><td>
2167 create a new repository in the given directory
2168 create a new repository in the given directory
2168 </td></tr>
2169 </td></tr>
2169 <tr><td>
2170 <tr><td>
2170 <a href="/help/log">
2171 <a href="/help/log">
2171 log
2172 log
2172 </a>
2173 </a>
2173 </td><td>
2174 </td><td>
2174 show revision history of entire repository or files
2175 show revision history of entire repository or files
2175 </td></tr>
2176 </td></tr>
2176 <tr><td>
2177 <tr><td>
2177 <a href="/help/merge">
2178 <a href="/help/merge">
2178 merge
2179 merge
2179 </a>
2180 </a>
2180 </td><td>
2181 </td><td>
2181 merge another revision into working directory
2182 merge another revision into working directory
2182 </td></tr>
2183 </td></tr>
2183 <tr><td>
2184 <tr><td>
2184 <a href="/help/pull">
2185 <a href="/help/pull">
2185 pull
2186 pull
2186 </a>
2187 </a>
2187 </td><td>
2188 </td><td>
2188 pull changes from the specified source
2189 pull changes from the specified source
2189 </td></tr>
2190 </td></tr>
2190 <tr><td>
2191 <tr><td>
2191 <a href="/help/push">
2192 <a href="/help/push">
2192 push
2193 push
2193 </a>
2194 </a>
2194 </td><td>
2195 </td><td>
2195 push changes to the specified destination
2196 push changes to the specified destination
2196 </td></tr>
2197 </td></tr>
2197 <tr><td>
2198 <tr><td>
2198 <a href="/help/remove">
2199 <a href="/help/remove">
2199 remove
2200 remove
2200 </a>
2201 </a>
2201 </td><td>
2202 </td><td>
2202 remove the specified files on the next commit
2203 remove the specified files on the next commit
2203 </td></tr>
2204 </td></tr>
2204 <tr><td>
2205 <tr><td>
2205 <a href="/help/serve">
2206 <a href="/help/serve">
2206 serve
2207 serve
2207 </a>
2208 </a>
2208 </td><td>
2209 </td><td>
2209 start stand-alone webserver
2210 start stand-alone webserver
2210 </td></tr>
2211 </td></tr>
2211 <tr><td>
2212 <tr><td>
2212 <a href="/help/status">
2213 <a href="/help/status">
2213 status
2214 status
2214 </a>
2215 </a>
2215 </td><td>
2216 </td><td>
2216 show changed files in the working directory
2217 show changed files in the working directory
2217 </td></tr>
2218 </td></tr>
2218 <tr><td>
2219 <tr><td>
2219 <a href="/help/summary">
2220 <a href="/help/summary">
2220 summary
2221 summary
2221 </a>
2222 </a>
2222 </td><td>
2223 </td><td>
2223 summarize working directory state
2224 summarize working directory state
2224 </td></tr>
2225 </td></tr>
2225 <tr><td>
2226 <tr><td>
2226 <a href="/help/update">
2227 <a href="/help/update">
2227 update
2228 update
2228 </a>
2229 </a>
2229 </td><td>
2230 </td><td>
2230 update working directory (or switch revisions)
2231 update working directory (or switch revisions)
2231 </td></tr>
2232 </td></tr>
2232
2233
2233
2234
2234
2235
2235 <tr><td colspan="2"><h2><a name="other" href="#other">Other Commands</a></h2></td></tr>
2236 <tr><td colspan="2"><h2><a name="other" href="#other">Other Commands</a></h2></td></tr>
2236
2237
2237 <tr><td>
2238 <tr><td>
2238 <a href="/help/addremove">
2239 <a href="/help/addremove">
2239 addremove
2240 addremove
2240 </a>
2241 </a>
2241 </td><td>
2242 </td><td>
2242 add all new files, delete all missing files
2243 add all new files, delete all missing files
2243 </td></tr>
2244 </td></tr>
2244 <tr><td>
2245 <tr><td>
2245 <a href="/help/archive">
2246 <a href="/help/archive">
2246 archive
2247 archive
2247 </a>
2248 </a>
2248 </td><td>
2249 </td><td>
2249 create an unversioned archive of a repository revision
2250 create an unversioned archive of a repository revision
2250 </td></tr>
2251 </td></tr>
2251 <tr><td>
2252 <tr><td>
2252 <a href="/help/backout">
2253 <a href="/help/backout">
2253 backout
2254 backout
2254 </a>
2255 </a>
2255 </td><td>
2256 </td><td>
2256 reverse effect of earlier changeset
2257 reverse effect of earlier changeset
2257 </td></tr>
2258 </td></tr>
2258 <tr><td>
2259 <tr><td>
2259 <a href="/help/bisect">
2260 <a href="/help/bisect">
2260 bisect
2261 bisect
2261 </a>
2262 </a>
2262 </td><td>
2263 </td><td>
2263 subdivision search of changesets
2264 subdivision search of changesets
2264 </td></tr>
2265 </td></tr>
2265 <tr><td>
2266 <tr><td>
2266 <a href="/help/bookmarks">
2267 <a href="/help/bookmarks">
2267 bookmarks
2268 bookmarks
2268 </a>
2269 </a>
2269 </td><td>
2270 </td><td>
2270 create a new bookmark or list existing bookmarks
2271 create a new bookmark or list existing bookmarks
2271 </td></tr>
2272 </td></tr>
2272 <tr><td>
2273 <tr><td>
2273 <a href="/help/branch">
2274 <a href="/help/branch">
2274 branch
2275 branch
2275 </a>
2276 </a>
2276 </td><td>
2277 </td><td>
2277 set or show the current branch name
2278 set or show the current branch name
2278 </td></tr>
2279 </td></tr>
2279 <tr><td>
2280 <tr><td>
2280 <a href="/help/branches">
2281 <a href="/help/branches">
2281 branches
2282 branches
2282 </a>
2283 </a>
2283 </td><td>
2284 </td><td>
2284 list repository named branches
2285 list repository named branches
2285 </td></tr>
2286 </td></tr>
2286 <tr><td>
2287 <tr><td>
2287 <a href="/help/bundle">
2288 <a href="/help/bundle">
2288 bundle
2289 bundle
2289 </a>
2290 </a>
2290 </td><td>
2291 </td><td>
2291 create a bundle file
2292 create a bundle file
2292 </td></tr>
2293 </td></tr>
2293 <tr><td>
2294 <tr><td>
2294 <a href="/help/cat">
2295 <a href="/help/cat">
2295 cat
2296 cat
2296 </a>
2297 </a>
2297 </td><td>
2298 </td><td>
2298 output the current or given revision of files
2299 output the current or given revision of files
2299 </td></tr>
2300 </td></tr>
2300 <tr><td>
2301 <tr><td>
2301 <a href="/help/config">
2302 <a href="/help/config">
2302 config
2303 config
2303 </a>
2304 </a>
2304 </td><td>
2305 </td><td>
2305 show combined config settings from all hgrc files
2306 show combined config settings from all hgrc files
2306 </td></tr>
2307 </td></tr>
2307 <tr><td>
2308 <tr><td>
2308 <a href="/help/copy">
2309 <a href="/help/copy">
2309 copy
2310 copy
2310 </a>
2311 </a>
2311 </td><td>
2312 </td><td>
2312 mark files as copied for the next commit
2313 mark files as copied for the next commit
2313 </td></tr>
2314 </td></tr>
2314 <tr><td>
2315 <tr><td>
2315 <a href="/help/files">
2316 <a href="/help/files">
2316 files
2317 files
2317 </a>
2318 </a>
2318 </td><td>
2319 </td><td>
2319 list tracked files
2320 list tracked files
2320 </td></tr>
2321 </td></tr>
2321 <tr><td>
2322 <tr><td>
2322 <a href="/help/graft">
2323 <a href="/help/graft">
2323 graft
2324 graft
2324 </a>
2325 </a>
2325 </td><td>
2326 </td><td>
2326 copy changes from other branches onto the current branch
2327 copy changes from other branches onto the current branch
2327 </td></tr>
2328 </td></tr>
2328 <tr><td>
2329 <tr><td>
2329 <a href="/help/grep">
2330 <a href="/help/grep">
2330 grep
2331 grep
2331 </a>
2332 </a>
2332 </td><td>
2333 </td><td>
2333 search revision history for a pattern in specified files
2334 search revision history for a pattern in specified files
2334 </td></tr>
2335 </td></tr>
2335 <tr><td>
2336 <tr><td>
2336 <a href="/help/heads">
2337 <a href="/help/heads">
2337 heads
2338 heads
2338 </a>
2339 </a>
2339 </td><td>
2340 </td><td>
2340 show branch heads
2341 show branch heads
2341 </td></tr>
2342 </td></tr>
2342 <tr><td>
2343 <tr><td>
2343 <a href="/help/help">
2344 <a href="/help/help">
2344 help
2345 help
2345 </a>
2346 </a>
2346 </td><td>
2347 </td><td>
2347 show help for a given topic or a help overview
2348 show help for a given topic or a help overview
2348 </td></tr>
2349 </td></tr>
2349 <tr><td>
2350 <tr><td>
2350 <a href="/help/hgalias">
2351 <a href="/help/hgalias">
2351 hgalias
2352 hgalias
2352 </a>
2353 </a>
2353 </td><td>
2354 </td><td>
2354 summarize working directory state
2355 summarize working directory state
2355 </td></tr>
2356 </td></tr>
2356 <tr><td>
2357 <tr><td>
2357 <a href="/help/identify">
2358 <a href="/help/identify">
2358 identify
2359 identify
2359 </a>
2360 </a>
2360 </td><td>
2361 </td><td>
2361 identify the working directory or specified revision
2362 identify the working directory or specified revision
2362 </td></tr>
2363 </td></tr>
2363 <tr><td>
2364 <tr><td>
2364 <a href="/help/import">
2365 <a href="/help/import">
2365 import
2366 import
2366 </a>
2367 </a>
2367 </td><td>
2368 </td><td>
2368 import an ordered set of patches
2369 import an ordered set of patches
2369 </td></tr>
2370 </td></tr>
2370 <tr><td>
2371 <tr><td>
2371 <a href="/help/incoming">
2372 <a href="/help/incoming">
2372 incoming
2373 incoming
2373 </a>
2374 </a>
2374 </td><td>
2375 </td><td>
2375 show new changesets found in source
2376 show new changesets found in source
2376 </td></tr>
2377 </td></tr>
2377 <tr><td>
2378 <tr><td>
2378 <a href="/help/manifest">
2379 <a href="/help/manifest">
2379 manifest
2380 manifest
2380 </a>
2381 </a>
2381 </td><td>
2382 </td><td>
2382 output the current or given revision of the project manifest
2383 output the current or given revision of the project manifest
2383 </td></tr>
2384 </td></tr>
2384 <tr><td>
2385 <tr><td>
2385 <a href="/help/nohelp">
2386 <a href="/help/nohelp">
2386 nohelp
2387 nohelp
2387 </a>
2388 </a>
2388 </td><td>
2389 </td><td>
2389 (no help text available)
2390 (no help text available)
2390 </td></tr>
2391 </td></tr>
2391 <tr><td>
2392 <tr><td>
2392 <a href="/help/outgoing">
2393 <a href="/help/outgoing">
2393 outgoing
2394 outgoing
2394 </a>
2395 </a>
2395 </td><td>
2396 </td><td>
2396 show changesets not found in the destination
2397 show changesets not found in the destination
2397 </td></tr>
2398 </td></tr>
2398 <tr><td>
2399 <tr><td>
2399 <a href="/help/paths">
2400 <a href="/help/paths">
2400 paths
2401 paths
2401 </a>
2402 </a>
2402 </td><td>
2403 </td><td>
2403 show aliases for remote repositories
2404 show aliases for remote repositories
2404 </td></tr>
2405 </td></tr>
2405 <tr><td>
2406 <tr><td>
2406 <a href="/help/phase">
2407 <a href="/help/phase">
2407 phase
2408 phase
2408 </a>
2409 </a>
2409 </td><td>
2410 </td><td>
2410 set or show the current phase name
2411 set or show the current phase name
2411 </td></tr>
2412 </td></tr>
2412 <tr><td>
2413 <tr><td>
2413 <a href="/help/recover">
2414 <a href="/help/recover">
2414 recover
2415 recover
2415 </a>
2416 </a>
2416 </td><td>
2417 </td><td>
2417 roll back an interrupted transaction
2418 roll back an interrupted transaction
2418 </td></tr>
2419 </td></tr>
2419 <tr><td>
2420 <tr><td>
2420 <a href="/help/rename">
2421 <a href="/help/rename">
2421 rename
2422 rename
2422 </a>
2423 </a>
2423 </td><td>
2424 </td><td>
2424 rename files; equivalent of copy + remove
2425 rename files; equivalent of copy + remove
2425 </td></tr>
2426 </td></tr>
2426 <tr><td>
2427 <tr><td>
2427 <a href="/help/resolve">
2428 <a href="/help/resolve">
2428 resolve
2429 resolve
2429 </a>
2430 </a>
2430 </td><td>
2431 </td><td>
2431 redo merges or set/view the merge status of files
2432 redo merges or set/view the merge status of files
2432 </td></tr>
2433 </td></tr>
2433 <tr><td>
2434 <tr><td>
2434 <a href="/help/revert">
2435 <a href="/help/revert">
2435 revert
2436 revert
2436 </a>
2437 </a>
2437 </td><td>
2438 </td><td>
2438 restore files to their checkout state
2439 restore files to their checkout state
2439 </td></tr>
2440 </td></tr>
2440 <tr><td>
2441 <tr><td>
2441 <a href="/help/root">
2442 <a href="/help/root">
2442 root
2443 root
2443 </a>
2444 </a>
2444 </td><td>
2445 </td><td>
2445 print the root (top) of the current working directory
2446 print the root (top) of the current working directory
2446 </td></tr>
2447 </td></tr>
2447 <tr><td>
2448 <tr><td>
2448 <a href="/help/shellalias">
2449 <a href="/help/shellalias">
2449 shellalias
2450 shellalias
2450 </a>
2451 </a>
2451 </td><td>
2452 </td><td>
2452 (no help text available)
2453 (no help text available)
2453 </td></tr>
2454 </td></tr>
2454 <tr><td>
2455 <tr><td>
2455 <a href="/help/tag">
2456 <a href="/help/tag">
2456 tag
2457 tag
2457 </a>
2458 </a>
2458 </td><td>
2459 </td><td>
2459 add one or more tags for the current or given revision
2460 add one or more tags for the current or given revision
2460 </td></tr>
2461 </td></tr>
2461 <tr><td>
2462 <tr><td>
2462 <a href="/help/tags">
2463 <a href="/help/tags">
2463 tags
2464 tags
2464 </a>
2465 </a>
2465 </td><td>
2466 </td><td>
2466 list repository tags
2467 list repository tags
2467 </td></tr>
2468 </td></tr>
2468 <tr><td>
2469 <tr><td>
2469 <a href="/help/unbundle">
2470 <a href="/help/unbundle">
2470 unbundle
2471 unbundle
2471 </a>
2472 </a>
2472 </td><td>
2473 </td><td>
2473 apply one or more bundle files
2474 apply one or more bundle files
2474 </td></tr>
2475 </td></tr>
2475 <tr><td>
2476 <tr><td>
2476 <a href="/help/verify">
2477 <a href="/help/verify">
2477 verify
2478 verify
2478 </a>
2479 </a>
2479 </td><td>
2480 </td><td>
2480 verify the integrity of the repository
2481 verify the integrity of the repository
2481 </td></tr>
2482 </td></tr>
2482 <tr><td>
2483 <tr><td>
2483 <a href="/help/version">
2484 <a href="/help/version">
2484 version
2485 version
2485 </a>
2486 </a>
2486 </td><td>
2487 </td><td>
2487 output version and copyright information
2488 output version and copyright information
2488 </td></tr>
2489 </td></tr>
2489
2490
2490
2491
2491 </table>
2492 </table>
2492 </div>
2493 </div>
2493 </div>
2494 </div>
2494
2495
2495
2496
2496
2497
2497 </body>
2498 </body>
2498 </html>
2499 </html>
2499
2500
2500
2501
2501 $ get-with-headers.py $LOCALIP:$HGPORT "help/add"
2502 $ get-with-headers.py $LOCALIP:$HGPORT "help/add"
2502 200 Script output follows
2503 200 Script output follows
2503
2504
2504 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
2505 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
2505 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
2506 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
2506 <head>
2507 <head>
2507 <link rel="icon" href="/static/hgicon.png" type="image/png" />
2508 <link rel="icon" href="/static/hgicon.png" type="image/png" />
2508 <meta name="robots" content="index, nofollow" />
2509 <meta name="robots" content="index, nofollow" />
2509 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
2510 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
2510 <script type="text/javascript" src="/static/mercurial.js"></script>
2511 <script type="text/javascript" src="/static/mercurial.js"></script>
2511
2512
2512 <title>Help: add</title>
2513 <title>Help: add</title>
2513 </head>
2514 </head>
2514 <body>
2515 <body>
2515
2516
2516 <div class="container">
2517 <div class="container">
2517 <div class="menu">
2518 <div class="menu">
2518 <div class="logo">
2519 <div class="logo">
2519 <a href="https://mercurial-scm.org/">
2520 <a href="https://mercurial-scm.org/">
2520 <img src="/static/hglogo.png" alt="mercurial" /></a>
2521 <img src="/static/hglogo.png" alt="mercurial" /></a>
2521 </div>
2522 </div>
2522 <ul>
2523 <ul>
2523 <li><a href="/shortlog">log</a></li>
2524 <li><a href="/shortlog">log</a></li>
2524 <li><a href="/graph">graph</a></li>
2525 <li><a href="/graph">graph</a></li>
2525 <li><a href="/tags">tags</a></li>
2526 <li><a href="/tags">tags</a></li>
2526 <li><a href="/bookmarks">bookmarks</a></li>
2527 <li><a href="/bookmarks">bookmarks</a></li>
2527 <li><a href="/branches">branches</a></li>
2528 <li><a href="/branches">branches</a></li>
2528 </ul>
2529 </ul>
2529 <ul>
2530 <ul>
2530 <li class="active"><a href="/help">help</a></li>
2531 <li class="active"><a href="/help">help</a></li>
2531 </ul>
2532 </ul>
2532 </div>
2533 </div>
2533
2534
2534 <div class="main">
2535 <div class="main">
2535 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
2536 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
2536 <h3>Help: add</h3>
2537 <h3>Help: add</h3>
2537
2538
2538 <form class="search" action="/log">
2539 <form class="search" action="/log">
2539
2540
2540 <p><input name="rev" id="search1" type="text" size="30" value="" /></p>
2541 <p><input name="rev" id="search1" type="text" size="30" value="" /></p>
2541 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
2542 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
2542 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
2543 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
2543 </form>
2544 </form>
2544 <div id="doc">
2545 <div id="doc">
2545 <p>
2546 <p>
2546 hg add [OPTION]... [FILE]...
2547 hg add [OPTION]... [FILE]...
2547 </p>
2548 </p>
2548 <p>
2549 <p>
2549 add the specified files on the next commit
2550 add the specified files on the next commit
2550 </p>
2551 </p>
2551 <p>
2552 <p>
2552 Schedule files to be version controlled and added to the
2553 Schedule files to be version controlled and added to the
2553 repository.
2554 repository.
2554 </p>
2555 </p>
2555 <p>
2556 <p>
2556 The files will be added to the repository at the next commit. To
2557 The files will be added to the repository at the next commit. To
2557 undo an add before that, see 'hg forget'.
2558 undo an add before that, see 'hg forget'.
2558 </p>
2559 </p>
2559 <p>
2560 <p>
2560 If no names are given, add all files to the repository (except
2561 If no names are given, add all files to the repository (except
2561 files matching &quot;.hgignore&quot;).
2562 files matching &quot;.hgignore&quot;).
2562 </p>
2563 </p>
2563 <p>
2564 <p>
2564 Examples:
2565 Examples:
2565 </p>
2566 </p>
2566 <ul>
2567 <ul>
2567 <li> New (unknown) files are added automatically by 'hg add':
2568 <li> New (unknown) files are added automatically by 'hg add':
2568 <pre>
2569 <pre>
2569 \$ ls (re)
2570 \$ ls (re)
2570 foo.c
2571 foo.c
2571 \$ hg status (re)
2572 \$ hg status (re)
2572 ? foo.c
2573 ? foo.c
2573 \$ hg add (re)
2574 \$ hg add (re)
2574 adding foo.c
2575 adding foo.c
2575 \$ hg status (re)
2576 \$ hg status (re)
2576 A foo.c
2577 A foo.c
2577 </pre>
2578 </pre>
2578 <li> Specific files to be added can be specified:
2579 <li> Specific files to be added can be specified:
2579 <pre>
2580 <pre>
2580 \$ ls (re)
2581 \$ ls (re)
2581 bar.c foo.c
2582 bar.c foo.c
2582 \$ hg status (re)
2583 \$ hg status (re)
2583 ? bar.c
2584 ? bar.c
2584 ? foo.c
2585 ? foo.c
2585 \$ hg add bar.c (re)
2586 \$ hg add bar.c (re)
2586 \$ hg status (re)
2587 \$ hg status (re)
2587 A bar.c
2588 A bar.c
2588 ? foo.c
2589 ? foo.c
2589 </pre>
2590 </pre>
2590 </ul>
2591 </ul>
2591 <p>
2592 <p>
2592 Returns 0 if all files are successfully added.
2593 Returns 0 if all files are successfully added.
2593 </p>
2594 </p>
2594 <p>
2595 <p>
2595 options ([+] can be repeated):
2596 options ([+] can be repeated):
2596 </p>
2597 </p>
2597 <table>
2598 <table>
2598 <tr><td>-I</td>
2599 <tr><td>-I</td>
2599 <td>--include PATTERN [+]</td>
2600 <td>--include PATTERN [+]</td>
2600 <td>include names matching the given patterns</td></tr>
2601 <td>include names matching the given patterns</td></tr>
2601 <tr><td>-X</td>
2602 <tr><td>-X</td>
2602 <td>--exclude PATTERN [+]</td>
2603 <td>--exclude PATTERN [+]</td>
2603 <td>exclude names matching the given patterns</td></tr>
2604 <td>exclude names matching the given patterns</td></tr>
2604 <tr><td>-S</td>
2605 <tr><td>-S</td>
2605 <td>--subrepos</td>
2606 <td>--subrepos</td>
2606 <td>recurse into subrepositories</td></tr>
2607 <td>recurse into subrepositories</td></tr>
2607 <tr><td>-n</td>
2608 <tr><td>-n</td>
2608 <td>--dry-run</td>
2609 <td>--dry-run</td>
2609 <td>do not perform actions, just print output</td></tr>
2610 <td>do not perform actions, just print output</td></tr>
2610 </table>
2611 </table>
2611 <p>
2612 <p>
2612 global options ([+] can be repeated):
2613 global options ([+] can be repeated):
2613 </p>
2614 </p>
2614 <table>
2615 <table>
2615 <tr><td>-R</td>
2616 <tr><td>-R</td>
2616 <td>--repository REPO</td>
2617 <td>--repository REPO</td>
2617 <td>repository root directory or name of overlay bundle file</td></tr>
2618 <td>repository root directory or name of overlay bundle file</td></tr>
2618 <tr><td></td>
2619 <tr><td></td>
2619 <td>--cwd DIR</td>
2620 <td>--cwd DIR</td>
2620 <td>change working directory</td></tr>
2621 <td>change working directory</td></tr>
2621 <tr><td>-y</td>
2622 <tr><td>-y</td>
2622 <td>--noninteractive</td>
2623 <td>--noninteractive</td>
2623 <td>do not prompt, automatically pick the first choice for all prompts</td></tr>
2624 <td>do not prompt, automatically pick the first choice for all prompts</td></tr>
2624 <tr><td>-q</td>
2625 <tr><td>-q</td>
2625 <td>--quiet</td>
2626 <td>--quiet</td>
2626 <td>suppress output</td></tr>
2627 <td>suppress output</td></tr>
2627 <tr><td>-v</td>
2628 <tr><td>-v</td>
2628 <td>--verbose</td>
2629 <td>--verbose</td>
2629 <td>enable additional output</td></tr>
2630 <td>enable additional output</td></tr>
2630 <tr><td></td>
2631 <tr><td></td>
2631 <td>--color TYPE</td>
2632 <td>--color TYPE</td>
2632 <td>when to colorize (boolean, always, auto, never, or debug)</td></tr>
2633 <td>when to colorize (boolean, always, auto, never, or debug)</td></tr>
2633 <tr><td></td>
2634 <tr><td></td>
2634 <td>--config CONFIG [+]</td>
2635 <td>--config CONFIG [+]</td>
2635 <td>set/override config option (use 'section.name=value')</td></tr>
2636 <td>set/override config option (use 'section.name=value')</td></tr>
2636 <tr><td></td>
2637 <tr><td></td>
2637 <td>--debug</td>
2638 <td>--debug</td>
2638 <td>enable debugging output</td></tr>
2639 <td>enable debugging output</td></tr>
2639 <tr><td></td>
2640 <tr><td></td>
2640 <td>--debugger</td>
2641 <td>--debugger</td>
2641 <td>start debugger</td></tr>
2642 <td>start debugger</td></tr>
2642 <tr><td></td>
2643 <tr><td></td>
2643 <td>--encoding ENCODE</td>
2644 <td>--encoding ENCODE</td>
2644 <td>set the charset encoding (default: ascii)</td></tr>
2645 <td>set the charset encoding (default: ascii)</td></tr>
2645 <tr><td></td>
2646 <tr><td></td>
2646 <td>--encodingmode MODE</td>
2647 <td>--encodingmode MODE</td>
2647 <td>set the charset encoding mode (default: strict)</td></tr>
2648 <td>set the charset encoding mode (default: strict)</td></tr>
2648 <tr><td></td>
2649 <tr><td></td>
2649 <td>--traceback</td>
2650 <td>--traceback</td>
2650 <td>always print a traceback on exception</td></tr>
2651 <td>always print a traceback on exception</td></tr>
2651 <tr><td></td>
2652 <tr><td></td>
2652 <td>--time</td>
2653 <td>--time</td>
2653 <td>time how long the command takes</td></tr>
2654 <td>time how long the command takes</td></tr>
2654 <tr><td></td>
2655 <tr><td></td>
2655 <td>--profile</td>
2656 <td>--profile</td>
2656 <td>print command execution profile</td></tr>
2657 <td>print command execution profile</td></tr>
2657 <tr><td></td>
2658 <tr><td></td>
2658 <td>--version</td>
2659 <td>--version</td>
2659 <td>output version information and exit</td></tr>
2660 <td>output version information and exit</td></tr>
2660 <tr><td>-h</td>
2661 <tr><td>-h</td>
2661 <td>--help</td>
2662 <td>--help</td>
2662 <td>display help and exit</td></tr>
2663 <td>display help and exit</td></tr>
2663 <tr><td></td>
2664 <tr><td></td>
2664 <td>--hidden</td>
2665 <td>--hidden</td>
2665 <td>consider hidden changesets</td></tr>
2666 <td>consider hidden changesets</td></tr>
2666 <tr><td></td>
2667 <tr><td></td>
2667 <td>--pager TYPE</td>
2668 <td>--pager TYPE</td>
2668 <td>when to paginate (boolean, always, auto, or never) (default: auto)</td></tr>
2669 <td>when to paginate (boolean, always, auto, or never) (default: auto)</td></tr>
2669 </table>
2670 </table>
2670
2671
2671 </div>
2672 </div>
2672 </div>
2673 </div>
2673 </div>
2674 </div>
2674
2675
2675
2676
2676
2677
2677 </body>
2678 </body>
2678 </html>
2679 </html>
2679
2680
2680
2681
2681 $ get-with-headers.py $LOCALIP:$HGPORT "help/remove"
2682 $ get-with-headers.py $LOCALIP:$HGPORT "help/remove"
2682 200 Script output follows
2683 200 Script output follows
2683
2684
2684 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
2685 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
2685 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
2686 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
2686 <head>
2687 <head>
2687 <link rel="icon" href="/static/hgicon.png" type="image/png" />
2688 <link rel="icon" href="/static/hgicon.png" type="image/png" />
2688 <meta name="robots" content="index, nofollow" />
2689 <meta name="robots" content="index, nofollow" />
2689 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
2690 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
2690 <script type="text/javascript" src="/static/mercurial.js"></script>
2691 <script type="text/javascript" src="/static/mercurial.js"></script>
2691
2692
2692 <title>Help: remove</title>
2693 <title>Help: remove</title>
2693 </head>
2694 </head>
2694 <body>
2695 <body>
2695
2696
2696 <div class="container">
2697 <div class="container">
2697 <div class="menu">
2698 <div class="menu">
2698 <div class="logo">
2699 <div class="logo">
2699 <a href="https://mercurial-scm.org/">
2700 <a href="https://mercurial-scm.org/">
2700 <img src="/static/hglogo.png" alt="mercurial" /></a>
2701 <img src="/static/hglogo.png" alt="mercurial" /></a>
2701 </div>
2702 </div>
2702 <ul>
2703 <ul>
2703 <li><a href="/shortlog">log</a></li>
2704 <li><a href="/shortlog">log</a></li>
2704 <li><a href="/graph">graph</a></li>
2705 <li><a href="/graph">graph</a></li>
2705 <li><a href="/tags">tags</a></li>
2706 <li><a href="/tags">tags</a></li>
2706 <li><a href="/bookmarks">bookmarks</a></li>
2707 <li><a href="/bookmarks">bookmarks</a></li>
2707 <li><a href="/branches">branches</a></li>
2708 <li><a href="/branches">branches</a></li>
2708 </ul>
2709 </ul>
2709 <ul>
2710 <ul>
2710 <li class="active"><a href="/help">help</a></li>
2711 <li class="active"><a href="/help">help</a></li>
2711 </ul>
2712 </ul>
2712 </div>
2713 </div>
2713
2714
2714 <div class="main">
2715 <div class="main">
2715 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
2716 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
2716 <h3>Help: remove</h3>
2717 <h3>Help: remove</h3>
2717
2718
2718 <form class="search" action="/log">
2719 <form class="search" action="/log">
2719
2720
2720 <p><input name="rev" id="search1" type="text" size="30" value="" /></p>
2721 <p><input name="rev" id="search1" type="text" size="30" value="" /></p>
2721 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
2722 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
2722 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
2723 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
2723 </form>
2724 </form>
2724 <div id="doc">
2725 <div id="doc">
2725 <p>
2726 <p>
2726 hg remove [OPTION]... FILE...
2727 hg remove [OPTION]... FILE...
2727 </p>
2728 </p>
2728 <p>
2729 <p>
2729 aliases: rm
2730 aliases: rm
2730 </p>
2731 </p>
2731 <p>
2732 <p>
2732 remove the specified files on the next commit
2733 remove the specified files on the next commit
2733 </p>
2734 </p>
2734 <p>
2735 <p>
2735 Schedule the indicated files for removal from the current branch.
2736 Schedule the indicated files for removal from the current branch.
2736 </p>
2737 </p>
2737 <p>
2738 <p>
2738 This command schedules the files to be removed at the next commit.
2739 This command schedules the files to be removed at the next commit.
2739 To undo a remove before that, see 'hg revert'. To undo added
2740 To undo a remove before that, see 'hg revert'. To undo added
2740 files, see 'hg forget'.
2741 files, see 'hg forget'.
2741 </p>
2742 </p>
2742 <p>
2743 <p>
2743 -A/--after can be used to remove only files that have already
2744 -A/--after can be used to remove only files that have already
2744 been deleted, -f/--force can be used to force deletion, and -Af
2745 been deleted, -f/--force can be used to force deletion, and -Af
2745 can be used to remove files from the next revision without
2746 can be used to remove files from the next revision without
2746 deleting them from the working directory.
2747 deleting them from the working directory.
2747 </p>
2748 </p>
2748 <p>
2749 <p>
2749 The following table details the behavior of remove for different
2750 The following table details the behavior of remove for different
2750 file states (columns) and option combinations (rows). The file
2751 file states (columns) and option combinations (rows). The file
2751 states are Added [A], Clean [C], Modified [M] and Missing [!]
2752 states are Added [A], Clean [C], Modified [M] and Missing [!]
2752 (as reported by 'hg status'). The actions are Warn, Remove
2753 (as reported by 'hg status'). The actions are Warn, Remove
2753 (from branch) and Delete (from disk):
2754 (from branch) and Delete (from disk):
2754 </p>
2755 </p>
2755 <table>
2756 <table>
2756 <tr><td>opt/state</td>
2757 <tr><td>opt/state</td>
2757 <td>A</td>
2758 <td>A</td>
2758 <td>C</td>
2759 <td>C</td>
2759 <td>M</td>
2760 <td>M</td>
2760 <td>!</td></tr>
2761 <td>!</td></tr>
2761 <tr><td>none</td>
2762 <tr><td>none</td>
2762 <td>W</td>
2763 <td>W</td>
2763 <td>RD</td>
2764 <td>RD</td>
2764 <td>W</td>
2765 <td>W</td>
2765 <td>R</td></tr>
2766 <td>R</td></tr>
2766 <tr><td>-f</td>
2767 <tr><td>-f</td>
2767 <td>R</td>
2768 <td>R</td>
2768 <td>RD</td>
2769 <td>RD</td>
2769 <td>RD</td>
2770 <td>RD</td>
2770 <td>R</td></tr>
2771 <td>R</td></tr>
2771 <tr><td>-A</td>
2772 <tr><td>-A</td>
2772 <td>W</td>
2773 <td>W</td>
2773 <td>W</td>
2774 <td>W</td>
2774 <td>W</td>
2775 <td>W</td>
2775 <td>R</td></tr>
2776 <td>R</td></tr>
2776 <tr><td>-Af</td>
2777 <tr><td>-Af</td>
2777 <td>R</td>
2778 <td>R</td>
2778 <td>R</td>
2779 <td>R</td>
2779 <td>R</td>
2780 <td>R</td>
2780 <td>R</td></tr>
2781 <td>R</td></tr>
2781 </table>
2782 </table>
2782 <p>
2783 <p>
2783 <b>Note:</b>
2784 <b>Note:</b>
2784 </p>
2785 </p>
2785 <p>
2786 <p>
2786 'hg remove' never deletes files in Added [A] state from the
2787 'hg remove' never deletes files in Added [A] state from the
2787 working directory, not even if &quot;--force&quot; is specified.
2788 working directory, not even if &quot;--force&quot; is specified.
2788 </p>
2789 </p>
2789 <p>
2790 <p>
2790 Returns 0 on success, 1 if any warnings encountered.
2791 Returns 0 on success, 1 if any warnings encountered.
2791 </p>
2792 </p>
2792 <p>
2793 <p>
2793 options ([+] can be repeated):
2794 options ([+] can be repeated):
2794 </p>
2795 </p>
2795 <table>
2796 <table>
2796 <tr><td>-A</td>
2797 <tr><td>-A</td>
2797 <td>--after</td>
2798 <td>--after</td>
2798 <td>record delete for missing files</td></tr>
2799 <td>record delete for missing files</td></tr>
2799 <tr><td>-f</td>
2800 <tr><td>-f</td>
2800 <td>--force</td>
2801 <td>--force</td>
2801 <td>forget added files, delete modified files</td></tr>
2802 <td>forget added files, delete modified files</td></tr>
2802 <tr><td>-S</td>
2803 <tr><td>-S</td>
2803 <td>--subrepos</td>
2804 <td>--subrepos</td>
2804 <td>recurse into subrepositories</td></tr>
2805 <td>recurse into subrepositories</td></tr>
2805 <tr><td>-I</td>
2806 <tr><td>-I</td>
2806 <td>--include PATTERN [+]</td>
2807 <td>--include PATTERN [+]</td>
2807 <td>include names matching the given patterns</td></tr>
2808 <td>include names matching the given patterns</td></tr>
2808 <tr><td>-X</td>
2809 <tr><td>-X</td>
2809 <td>--exclude PATTERN [+]</td>
2810 <td>--exclude PATTERN [+]</td>
2810 <td>exclude names matching the given patterns</td></tr>
2811 <td>exclude names matching the given patterns</td></tr>
2811 </table>
2812 </table>
2812 <p>
2813 <p>
2813 global options ([+] can be repeated):
2814 global options ([+] can be repeated):
2814 </p>
2815 </p>
2815 <table>
2816 <table>
2816 <tr><td>-R</td>
2817 <tr><td>-R</td>
2817 <td>--repository REPO</td>
2818 <td>--repository REPO</td>
2818 <td>repository root directory or name of overlay bundle file</td></tr>
2819 <td>repository root directory or name of overlay bundle file</td></tr>
2819 <tr><td></td>
2820 <tr><td></td>
2820 <td>--cwd DIR</td>
2821 <td>--cwd DIR</td>
2821 <td>change working directory</td></tr>
2822 <td>change working directory</td></tr>
2822 <tr><td>-y</td>
2823 <tr><td>-y</td>
2823 <td>--noninteractive</td>
2824 <td>--noninteractive</td>
2824 <td>do not prompt, automatically pick the first choice for all prompts</td></tr>
2825 <td>do not prompt, automatically pick the first choice for all prompts</td></tr>
2825 <tr><td>-q</td>
2826 <tr><td>-q</td>
2826 <td>--quiet</td>
2827 <td>--quiet</td>
2827 <td>suppress output</td></tr>
2828 <td>suppress output</td></tr>
2828 <tr><td>-v</td>
2829 <tr><td>-v</td>
2829 <td>--verbose</td>
2830 <td>--verbose</td>
2830 <td>enable additional output</td></tr>
2831 <td>enable additional output</td></tr>
2831 <tr><td></td>
2832 <tr><td></td>
2832 <td>--color TYPE</td>
2833 <td>--color TYPE</td>
2833 <td>when to colorize (boolean, always, auto, never, or debug)</td></tr>
2834 <td>when to colorize (boolean, always, auto, never, or debug)</td></tr>
2834 <tr><td></td>
2835 <tr><td></td>
2835 <td>--config CONFIG [+]</td>
2836 <td>--config CONFIG [+]</td>
2836 <td>set/override config option (use 'section.name=value')</td></tr>
2837 <td>set/override config option (use 'section.name=value')</td></tr>
2837 <tr><td></td>
2838 <tr><td></td>
2838 <td>--debug</td>
2839 <td>--debug</td>
2839 <td>enable debugging output</td></tr>
2840 <td>enable debugging output</td></tr>
2840 <tr><td></td>
2841 <tr><td></td>
2841 <td>--debugger</td>
2842 <td>--debugger</td>
2842 <td>start debugger</td></tr>
2843 <td>start debugger</td></tr>
2843 <tr><td></td>
2844 <tr><td></td>
2844 <td>--encoding ENCODE</td>
2845 <td>--encoding ENCODE</td>
2845 <td>set the charset encoding (default: ascii)</td></tr>
2846 <td>set the charset encoding (default: ascii)</td></tr>
2846 <tr><td></td>
2847 <tr><td></td>
2847 <td>--encodingmode MODE</td>
2848 <td>--encodingmode MODE</td>
2848 <td>set the charset encoding mode (default: strict)</td></tr>
2849 <td>set the charset encoding mode (default: strict)</td></tr>
2849 <tr><td></td>
2850 <tr><td></td>
2850 <td>--traceback</td>
2851 <td>--traceback</td>
2851 <td>always print a traceback on exception</td></tr>
2852 <td>always print a traceback on exception</td></tr>
2852 <tr><td></td>
2853 <tr><td></td>
2853 <td>--time</td>
2854 <td>--time</td>
2854 <td>time how long the command takes</td></tr>
2855 <td>time how long the command takes</td></tr>
2855 <tr><td></td>
2856 <tr><td></td>
2856 <td>--profile</td>
2857 <td>--profile</td>
2857 <td>print command execution profile</td></tr>
2858 <td>print command execution profile</td></tr>
2858 <tr><td></td>
2859 <tr><td></td>
2859 <td>--version</td>
2860 <td>--version</td>
2860 <td>output version information and exit</td></tr>
2861 <td>output version information and exit</td></tr>
2861 <tr><td>-h</td>
2862 <tr><td>-h</td>
2862 <td>--help</td>
2863 <td>--help</td>
2863 <td>display help and exit</td></tr>
2864 <td>display help and exit</td></tr>
2864 <tr><td></td>
2865 <tr><td></td>
2865 <td>--hidden</td>
2866 <td>--hidden</td>
2866 <td>consider hidden changesets</td></tr>
2867 <td>consider hidden changesets</td></tr>
2867 <tr><td></td>
2868 <tr><td></td>
2868 <td>--pager TYPE</td>
2869 <td>--pager TYPE</td>
2869 <td>when to paginate (boolean, always, auto, or never) (default: auto)</td></tr>
2870 <td>when to paginate (boolean, always, auto, or never) (default: auto)</td></tr>
2870 </table>
2871 </table>
2871
2872
2872 </div>
2873 </div>
2873 </div>
2874 </div>
2874 </div>
2875 </div>
2875
2876
2876
2877
2877
2878
2878 </body>
2879 </body>
2879 </html>
2880 </html>
2880
2881
2881
2882
2882 $ get-with-headers.py $LOCALIP:$HGPORT "help/dates"
2883 $ get-with-headers.py $LOCALIP:$HGPORT "help/dates"
2883 200 Script output follows
2884 200 Script output follows
2884
2885
2885 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
2886 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
2886 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
2887 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
2887 <head>
2888 <head>
2888 <link rel="icon" href="/static/hgicon.png" type="image/png" />
2889 <link rel="icon" href="/static/hgicon.png" type="image/png" />
2889 <meta name="robots" content="index, nofollow" />
2890 <meta name="robots" content="index, nofollow" />
2890 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
2891 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
2891 <script type="text/javascript" src="/static/mercurial.js"></script>
2892 <script type="text/javascript" src="/static/mercurial.js"></script>
2892
2893
2893 <title>Help: dates</title>
2894 <title>Help: dates</title>
2894 </head>
2895 </head>
2895 <body>
2896 <body>
2896
2897
2897 <div class="container">
2898 <div class="container">
2898 <div class="menu">
2899 <div class="menu">
2899 <div class="logo">
2900 <div class="logo">
2900 <a href="https://mercurial-scm.org/">
2901 <a href="https://mercurial-scm.org/">
2901 <img src="/static/hglogo.png" alt="mercurial" /></a>
2902 <img src="/static/hglogo.png" alt="mercurial" /></a>
2902 </div>
2903 </div>
2903 <ul>
2904 <ul>
2904 <li><a href="/shortlog">log</a></li>
2905 <li><a href="/shortlog">log</a></li>
2905 <li><a href="/graph">graph</a></li>
2906 <li><a href="/graph">graph</a></li>
2906 <li><a href="/tags">tags</a></li>
2907 <li><a href="/tags">tags</a></li>
2907 <li><a href="/bookmarks">bookmarks</a></li>
2908 <li><a href="/bookmarks">bookmarks</a></li>
2908 <li><a href="/branches">branches</a></li>
2909 <li><a href="/branches">branches</a></li>
2909 </ul>
2910 </ul>
2910 <ul>
2911 <ul>
2911 <li class="active"><a href="/help">help</a></li>
2912 <li class="active"><a href="/help">help</a></li>
2912 </ul>
2913 </ul>
2913 </div>
2914 </div>
2914
2915
2915 <div class="main">
2916 <div class="main">
2916 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
2917 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
2917 <h3>Help: dates</h3>
2918 <h3>Help: dates</h3>
2918
2919
2919 <form class="search" action="/log">
2920 <form class="search" action="/log">
2920
2921
2921 <p><input name="rev" id="search1" type="text" size="30" value="" /></p>
2922 <p><input name="rev" id="search1" type="text" size="30" value="" /></p>
2922 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
2923 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
2923 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
2924 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
2924 </form>
2925 </form>
2925 <div id="doc">
2926 <div id="doc">
2926 <h1>Date Formats</h1>
2927 <h1>Date Formats</h1>
2927 <p>
2928 <p>
2928 Some commands allow the user to specify a date, e.g.:
2929 Some commands allow the user to specify a date, e.g.:
2929 </p>
2930 </p>
2930 <ul>
2931 <ul>
2931 <li> backout, commit, import, tag: Specify the commit date.
2932 <li> backout, commit, import, tag: Specify the commit date.
2932 <li> log, revert, update: Select revision(s) by date.
2933 <li> log, revert, update: Select revision(s) by date.
2933 </ul>
2934 </ul>
2934 <p>
2935 <p>
2935 Many date formats are valid. Here are some examples:
2936 Many date formats are valid. Here are some examples:
2936 </p>
2937 </p>
2937 <ul>
2938 <ul>
2938 <li> &quot;Wed Dec 6 13:18:29 2006&quot; (local timezone assumed)
2939 <li> &quot;Wed Dec 6 13:18:29 2006&quot; (local timezone assumed)
2939 <li> &quot;Dec 6 13:18 -0600&quot; (year assumed, time offset provided)
2940 <li> &quot;Dec 6 13:18 -0600&quot; (year assumed, time offset provided)
2940 <li> &quot;Dec 6 13:18 UTC&quot; (UTC and GMT are aliases for +0000)
2941 <li> &quot;Dec 6 13:18 UTC&quot; (UTC and GMT are aliases for +0000)
2941 <li> &quot;Dec 6&quot; (midnight)
2942 <li> &quot;Dec 6&quot; (midnight)
2942 <li> &quot;13:18&quot; (today assumed)
2943 <li> &quot;13:18&quot; (today assumed)
2943 <li> &quot;3:39&quot; (3:39AM assumed)
2944 <li> &quot;3:39&quot; (3:39AM assumed)
2944 <li> &quot;3:39pm&quot; (15:39)
2945 <li> &quot;3:39pm&quot; (15:39)
2945 <li> &quot;2006-12-06 13:18:29&quot; (ISO 8601 format)
2946 <li> &quot;2006-12-06 13:18:29&quot; (ISO 8601 format)
2946 <li> &quot;2006-12-6 13:18&quot;
2947 <li> &quot;2006-12-6 13:18&quot;
2947 <li> &quot;2006-12-6&quot;
2948 <li> &quot;2006-12-6&quot;
2948 <li> &quot;12-6&quot;
2949 <li> &quot;12-6&quot;
2949 <li> &quot;12/6&quot;
2950 <li> &quot;12/6&quot;
2950 <li> &quot;12/6/6&quot; (Dec 6 2006)
2951 <li> &quot;12/6/6&quot; (Dec 6 2006)
2951 <li> &quot;today&quot; (midnight)
2952 <li> &quot;today&quot; (midnight)
2952 <li> &quot;yesterday&quot; (midnight)
2953 <li> &quot;yesterday&quot; (midnight)
2953 <li> &quot;now&quot; - right now
2954 <li> &quot;now&quot; - right now
2954 </ul>
2955 </ul>
2955 <p>
2956 <p>
2956 Lastly, there is Mercurial's internal format:
2957 Lastly, there is Mercurial's internal format:
2957 </p>
2958 </p>
2958 <ul>
2959 <ul>
2959 <li> &quot;1165411109 0&quot; (Wed Dec 6 13:18:29 2006 UTC)
2960 <li> &quot;1165411109 0&quot; (Wed Dec 6 13:18:29 2006 UTC)
2960 </ul>
2961 </ul>
2961 <p>
2962 <p>
2962 This is the internal representation format for dates. The first number
2963 This is the internal representation format for dates. The first number
2963 is the number of seconds since the epoch (1970-01-01 00:00 UTC). The
2964 is the number of seconds since the epoch (1970-01-01 00:00 UTC). The
2964 second is the offset of the local timezone, in seconds west of UTC
2965 second is the offset of the local timezone, in seconds west of UTC
2965 (negative if the timezone is east of UTC).
2966 (negative if the timezone is east of UTC).
2966 </p>
2967 </p>
2967 <p>
2968 <p>
2968 The log command also accepts date ranges:
2969 The log command also accepts date ranges:
2969 </p>
2970 </p>
2970 <ul>
2971 <ul>
2971 <li> &quot;&lt;DATE&quot; - at or before a given date/time
2972 <li> &quot;&lt;DATE&quot; - at or before a given date/time
2972 <li> &quot;&gt;DATE&quot; - on or after a given date/time
2973 <li> &quot;&gt;DATE&quot; - on or after a given date/time
2973 <li> &quot;DATE to DATE&quot; - a date range, inclusive
2974 <li> &quot;DATE to DATE&quot; - a date range, inclusive
2974 <li> &quot;-DAYS&quot; - within a given number of days of today
2975 <li> &quot;-DAYS&quot; - within a given number of days of today
2975 </ul>
2976 </ul>
2976
2977
2977 </div>
2978 </div>
2978 </div>
2979 </div>
2979 </div>
2980 </div>
2980
2981
2981
2982
2982
2983
2983 </body>
2984 </body>
2984 </html>
2985 </html>
2985
2986
2986
2987
2987 Sub-topic indexes rendered properly
2988 Sub-topic indexes rendered properly
2988
2989
2989 $ get-with-headers.py $LOCALIP:$HGPORT "help/internals"
2990 $ get-with-headers.py $LOCALIP:$HGPORT "help/internals"
2990 200 Script output follows
2991 200 Script output follows
2991
2992
2992 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
2993 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
2993 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
2994 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
2994 <head>
2995 <head>
2995 <link rel="icon" href="/static/hgicon.png" type="image/png" />
2996 <link rel="icon" href="/static/hgicon.png" type="image/png" />
2996 <meta name="robots" content="index, nofollow" />
2997 <meta name="robots" content="index, nofollow" />
2997 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
2998 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
2998 <script type="text/javascript" src="/static/mercurial.js"></script>
2999 <script type="text/javascript" src="/static/mercurial.js"></script>
2999
3000
3000 <title>Help: internals</title>
3001 <title>Help: internals</title>
3001 </head>
3002 </head>
3002 <body>
3003 <body>
3003
3004
3004 <div class="container">
3005 <div class="container">
3005 <div class="menu">
3006 <div class="menu">
3006 <div class="logo">
3007 <div class="logo">
3007 <a href="https://mercurial-scm.org/">
3008 <a href="https://mercurial-scm.org/">
3008 <img src="/static/hglogo.png" alt="mercurial" /></a>
3009 <img src="/static/hglogo.png" alt="mercurial" /></a>
3009 </div>
3010 </div>
3010 <ul>
3011 <ul>
3011 <li><a href="/shortlog">log</a></li>
3012 <li><a href="/shortlog">log</a></li>
3012 <li><a href="/graph">graph</a></li>
3013 <li><a href="/graph">graph</a></li>
3013 <li><a href="/tags">tags</a></li>
3014 <li><a href="/tags">tags</a></li>
3014 <li><a href="/bookmarks">bookmarks</a></li>
3015 <li><a href="/bookmarks">bookmarks</a></li>
3015 <li><a href="/branches">branches</a></li>
3016 <li><a href="/branches">branches</a></li>
3016 </ul>
3017 </ul>
3017 <ul>
3018 <ul>
3018 <li><a href="/help">help</a></li>
3019 <li><a href="/help">help</a></li>
3019 </ul>
3020 </ul>
3020 </div>
3021 </div>
3021
3022
3022 <div class="main">
3023 <div class="main">
3023 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
3024 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
3024
3025
3025 <form class="search" action="/log">
3026 <form class="search" action="/log">
3026
3027
3027 <p><input name="rev" id="search1" type="text" size="30" value="" /></p>
3028 <p><input name="rev" id="search1" type="text" size="30" value="" /></p>
3028 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
3029 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
3029 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
3030 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
3030 </form>
3031 </form>
3031 <table class="bigtable">
3032 <table class="bigtable">
3032 <tr><td colspan="2"><h2><a name="topics" href="#topics">Topics</a></h2></td></tr>
3033 <tr><td colspan="2"><h2><a name="topics" href="#topics">Topics</a></h2></td></tr>
3033
3034
3034 <tr><td>
3035 <tr><td>
3035 <a href="/help/internals.bundles">
3036 <a href="/help/internals.bundles">
3036 bundles
3037 bundles
3037 </a>
3038 </a>
3038 </td><td>
3039 </td><td>
3039 Bundles
3040 Bundles
3040 </td></tr>
3041 </td></tr>
3041 <tr><td>
3042 <tr><td>
3042 <a href="/help/internals.censor">
3043 <a href="/help/internals.censor">
3043 censor
3044 censor
3044 </a>
3045 </a>
3045 </td><td>
3046 </td><td>
3046 Censor
3047 Censor
3047 </td></tr>
3048 </td></tr>
3048 <tr><td>
3049 <tr><td>
3049 <a href="/help/internals.changegroups">
3050 <a href="/help/internals.changegroups">
3050 changegroups
3051 changegroups
3051 </a>
3052 </a>
3052 </td><td>
3053 </td><td>
3053 Changegroups
3054 Changegroups
3054 </td></tr>
3055 </td></tr>
3055 <tr><td>
3056 <tr><td>
3056 <a href="/help/internals.requirements">
3057 <a href="/help/internals.requirements">
3057 requirements
3058 requirements
3058 </a>
3059 </a>
3059 </td><td>
3060 </td><td>
3060 Repository Requirements
3061 Repository Requirements
3061 </td></tr>
3062 </td></tr>
3062 <tr><td>
3063 <tr><td>
3063 <a href="/help/internals.revlogs">
3064 <a href="/help/internals.revlogs">
3064 revlogs
3065 revlogs
3065 </a>
3066 </a>
3066 </td><td>
3067 </td><td>
3067 Revision Logs
3068 Revision Logs
3068 </td></tr>
3069 </td></tr>
3069 <tr><td>
3070 <tr><td>
3070 <a href="/help/internals.wireprotocol">
3071 <a href="/help/internals.wireprotocol">
3071 wireprotocol
3072 wireprotocol
3072 </a>
3073 </a>
3073 </td><td>
3074 </td><td>
3074 Wire Protocol
3075 Wire Protocol
3075 </td></tr>
3076 </td></tr>
3076
3077
3077
3078
3078
3079
3079
3080
3080
3081
3081 </table>
3082 </table>
3082 </div>
3083 </div>
3083 </div>
3084 </div>
3084
3085
3085
3086
3086
3087
3087 </body>
3088 </body>
3088 </html>
3089 </html>
3089
3090
3090
3091
3091 Sub-topic topics rendered properly
3092 Sub-topic topics rendered properly
3092
3093
3093 $ get-with-headers.py $LOCALIP:$HGPORT "help/internals.changegroups"
3094 $ get-with-headers.py $LOCALIP:$HGPORT "help/internals.changegroups"
3094 200 Script output follows
3095 200 Script output follows
3095
3096
3096 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
3097 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
3097 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
3098 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
3098 <head>
3099 <head>
3099 <link rel="icon" href="/static/hgicon.png" type="image/png" />
3100 <link rel="icon" href="/static/hgicon.png" type="image/png" />
3100 <meta name="robots" content="index, nofollow" />
3101 <meta name="robots" content="index, nofollow" />
3101 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
3102 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
3102 <script type="text/javascript" src="/static/mercurial.js"></script>
3103 <script type="text/javascript" src="/static/mercurial.js"></script>
3103
3104
3104 <title>Help: internals.changegroups</title>
3105 <title>Help: internals.changegroups</title>
3105 </head>
3106 </head>
3106 <body>
3107 <body>
3107
3108
3108 <div class="container">
3109 <div class="container">
3109 <div class="menu">
3110 <div class="menu">
3110 <div class="logo">
3111 <div class="logo">
3111 <a href="https://mercurial-scm.org/">
3112 <a href="https://mercurial-scm.org/">
3112 <img src="/static/hglogo.png" alt="mercurial" /></a>
3113 <img src="/static/hglogo.png" alt="mercurial" /></a>
3113 </div>
3114 </div>
3114 <ul>
3115 <ul>
3115 <li><a href="/shortlog">log</a></li>
3116 <li><a href="/shortlog">log</a></li>
3116 <li><a href="/graph">graph</a></li>
3117 <li><a href="/graph">graph</a></li>
3117 <li><a href="/tags">tags</a></li>
3118 <li><a href="/tags">tags</a></li>
3118 <li><a href="/bookmarks">bookmarks</a></li>
3119 <li><a href="/bookmarks">bookmarks</a></li>
3119 <li><a href="/branches">branches</a></li>
3120 <li><a href="/branches">branches</a></li>
3120 </ul>
3121 </ul>
3121 <ul>
3122 <ul>
3122 <li class="active"><a href="/help">help</a></li>
3123 <li class="active"><a href="/help">help</a></li>
3123 </ul>
3124 </ul>
3124 </div>
3125 </div>
3125
3126
3126 <div class="main">
3127 <div class="main">
3127 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
3128 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
3128 <h3>Help: internals.changegroups</h3>
3129 <h3>Help: internals.changegroups</h3>
3129
3130
3130 <form class="search" action="/log">
3131 <form class="search" action="/log">
3131
3132
3132 <p><input name="rev" id="search1" type="text" size="30" value="" /></p>
3133 <p><input name="rev" id="search1" type="text" size="30" value="" /></p>
3133 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
3134 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
3134 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
3135 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
3135 </form>
3136 </form>
3136 <div id="doc">
3137 <div id="doc">
3137 <h1>Changegroups</h1>
3138 <h1>Changegroups</h1>
3138 <p>
3139 <p>
3139 Changegroups are representations of repository revlog data, specifically
3140 Changegroups are representations of repository revlog data, specifically
3140 the changelog data, root/flat manifest data, treemanifest data, and
3141 the changelog data, root/flat manifest data, treemanifest data, and
3141 filelogs.
3142 filelogs.
3142 </p>
3143 </p>
3143 <p>
3144 <p>
3144 There are 3 versions of changegroups: &quot;1&quot;, &quot;2&quot;, and &quot;3&quot;. From a
3145 There are 3 versions of changegroups: &quot;1&quot;, &quot;2&quot;, and &quot;3&quot;. From a
3145 high-level, versions &quot;1&quot; and &quot;2&quot; are almost exactly the same, with the
3146 high-level, versions &quot;1&quot; and &quot;2&quot; are almost exactly the same, with the
3146 only difference being an additional item in the *delta header*. Version
3147 only difference being an additional item in the *delta header*. Version
3147 &quot;3&quot; adds support for revlog flags in the *delta header* and optionally
3148 &quot;3&quot; adds support for revlog flags in the *delta header* and optionally
3148 exchanging treemanifests (enabled by setting an option on the
3149 exchanging treemanifests (enabled by setting an option on the
3149 &quot;changegroup&quot; part in the bundle2).
3150 &quot;changegroup&quot; part in the bundle2).
3150 </p>
3151 </p>
3151 <p>
3152 <p>
3152 Changegroups when not exchanging treemanifests consist of 3 logical
3153 Changegroups when not exchanging treemanifests consist of 3 logical
3153 segments:
3154 segments:
3154 </p>
3155 </p>
3155 <pre>
3156 <pre>
3156 +---------------------------------+
3157 +---------------------------------+
3157 | | | |
3158 | | | |
3158 | changeset | manifest | filelogs |
3159 | changeset | manifest | filelogs |
3159 | | | |
3160 | | | |
3160 | | | |
3161 | | | |
3161 +---------------------------------+
3162 +---------------------------------+
3162 </pre>
3163 </pre>
3163 <p>
3164 <p>
3164 When exchanging treemanifests, there are 4 logical segments:
3165 When exchanging treemanifests, there are 4 logical segments:
3165 </p>
3166 </p>
3166 <pre>
3167 <pre>
3167 +-------------------------------------------------+
3168 +-------------------------------------------------+
3168 | | | | |
3169 | | | | |
3169 | changeset | root | treemanifests | filelogs |
3170 | changeset | root | treemanifests | filelogs |
3170 | | manifest | | |
3171 | | manifest | | |
3171 | | | | |
3172 | | | | |
3172 +-------------------------------------------------+
3173 +-------------------------------------------------+
3173 </pre>
3174 </pre>
3174 <p>
3175 <p>
3175 The principle building block of each segment is a *chunk*. A *chunk*
3176 The principle building block of each segment is a *chunk*. A *chunk*
3176 is a framed piece of data:
3177 is a framed piece of data:
3177 </p>
3178 </p>
3178 <pre>
3179 <pre>
3179 +---------------------------------------+
3180 +---------------------------------------+
3180 | | |
3181 | | |
3181 | length | data |
3182 | length | data |
3182 | (4 bytes) | (&lt;length - 4&gt; bytes) |
3183 | (4 bytes) | (&lt;length - 4&gt; bytes) |
3183 | | |
3184 | | |
3184 +---------------------------------------+
3185 +---------------------------------------+
3185 </pre>
3186 </pre>
3186 <p>
3187 <p>
3187 All integers are big-endian signed integers. Each chunk starts with a 32-bit
3188 All integers are big-endian signed integers. Each chunk starts with a 32-bit
3188 integer indicating the length of the entire chunk (including the length field
3189 integer indicating the length of the entire chunk (including the length field
3189 itself).
3190 itself).
3190 </p>
3191 </p>
3191 <p>
3192 <p>
3192 There is a special case chunk that has a value of 0 for the length
3193 There is a special case chunk that has a value of 0 for the length
3193 (&quot;0x00000000&quot;). We call this an *empty chunk*.
3194 (&quot;0x00000000&quot;). We call this an *empty chunk*.
3194 </p>
3195 </p>
3195 <h2>Delta Groups</h2>
3196 <h2>Delta Groups</h2>
3196 <p>
3197 <p>
3197 A *delta group* expresses the content of a revlog as a series of deltas,
3198 A *delta group* expresses the content of a revlog as a series of deltas,
3198 or patches against previous revisions.
3199 or patches against previous revisions.
3199 </p>
3200 </p>
3200 <p>
3201 <p>
3201 Delta groups consist of 0 or more *chunks* followed by the *empty chunk*
3202 Delta groups consist of 0 or more *chunks* followed by the *empty chunk*
3202 to signal the end of the delta group:
3203 to signal the end of the delta group:
3203 </p>
3204 </p>
3204 <pre>
3205 <pre>
3205 +------------------------------------------------------------------------+
3206 +------------------------------------------------------------------------+
3206 | | | | | |
3207 | | | | | |
3207 | chunk0 length | chunk0 data | chunk1 length | chunk1 data | 0x0 |
3208 | chunk0 length | chunk0 data | chunk1 length | chunk1 data | 0x0 |
3208 | (4 bytes) | (various) | (4 bytes) | (various) | (4 bytes) |
3209 | (4 bytes) | (various) | (4 bytes) | (various) | (4 bytes) |
3209 | | | | | |
3210 | | | | | |
3210 +------------------------------------------------------------------------+
3211 +------------------------------------------------------------------------+
3211 </pre>
3212 </pre>
3212 <p>
3213 <p>
3213 Each *chunk*'s data consists of the following:
3214 Each *chunk*'s data consists of the following:
3214 </p>
3215 </p>
3215 <pre>
3216 <pre>
3216 +---------------------------------------+
3217 +---------------------------------------+
3217 | | |
3218 | | |
3218 | delta header | delta data |
3219 | delta header | delta data |
3219 | (various by version) | (various) |
3220 | (various by version) | (various) |
3220 | | |
3221 | | |
3221 +---------------------------------------+
3222 +---------------------------------------+
3222 </pre>
3223 </pre>
3223 <p>
3224 <p>
3224 The *delta data* is a series of *delta*s that describe a diff from an existing
3225 The *delta data* is a series of *delta*s that describe a diff from an existing
3225 entry (either that the recipient already has, or previously specified in the
3226 entry (either that the recipient already has, or previously specified in the
3226 bundle/changegroup).
3227 bundle/changegroup).
3227 </p>
3228 </p>
3228 <p>
3229 <p>
3229 The *delta header* is different between versions &quot;1&quot;, &quot;2&quot;, and
3230 The *delta header* is different between versions &quot;1&quot;, &quot;2&quot;, and
3230 &quot;3&quot; of the changegroup format.
3231 &quot;3&quot; of the changegroup format.
3231 </p>
3232 </p>
3232 <p>
3233 <p>
3233 Version 1 (headerlen=80):
3234 Version 1 (headerlen=80):
3234 </p>
3235 </p>
3235 <pre>
3236 <pre>
3236 +------------------------------------------------------+
3237 +------------------------------------------------------+
3237 | | | | |
3238 | | | | |
3238 | node | p1 node | p2 node | link node |
3239 | node | p1 node | p2 node | link node |
3239 | (20 bytes) | (20 bytes) | (20 bytes) | (20 bytes) |
3240 | (20 bytes) | (20 bytes) | (20 bytes) | (20 bytes) |
3240 | | | | |
3241 | | | | |
3241 +------------------------------------------------------+
3242 +------------------------------------------------------+
3242 </pre>
3243 </pre>
3243 <p>
3244 <p>
3244 Version 2 (headerlen=100):
3245 Version 2 (headerlen=100):
3245 </p>
3246 </p>
3246 <pre>
3247 <pre>
3247 +------------------------------------------------------------------+
3248 +------------------------------------------------------------------+
3248 | | | | | |
3249 | | | | | |
3249 | node | p1 node | p2 node | base node | link node |
3250 | node | p1 node | p2 node | base node | link node |
3250 | (20 bytes) | (20 bytes) | (20 bytes) | (20 bytes) | (20 bytes) |
3251 | (20 bytes) | (20 bytes) | (20 bytes) | (20 bytes) | (20 bytes) |
3251 | | | | | |
3252 | | | | | |
3252 +------------------------------------------------------------------+
3253 +------------------------------------------------------------------+
3253 </pre>
3254 </pre>
3254 <p>
3255 <p>
3255 Version 3 (headerlen=102):
3256 Version 3 (headerlen=102):
3256 </p>
3257 </p>
3257 <pre>
3258 <pre>
3258 +------------------------------------------------------------------------------+
3259 +------------------------------------------------------------------------------+
3259 | | | | | | |
3260 | | | | | | |
3260 | node | p1 node | p2 node | base node | link node | flags |
3261 | node | p1 node | p2 node | base node | link node | flags |
3261 | (20 bytes) | (20 bytes) | (20 bytes) | (20 bytes) | (20 bytes) | (2 bytes) |
3262 | (20 bytes) | (20 bytes) | (20 bytes) | (20 bytes) | (20 bytes) | (2 bytes) |
3262 | | | | | | |
3263 | | | | | | |
3263 +------------------------------------------------------------------------------+
3264 +------------------------------------------------------------------------------+
3264 </pre>
3265 </pre>
3265 <p>
3266 <p>
3266 The *delta data* consists of &quot;chunklen - 4 - headerlen&quot; bytes, which contain a
3267 The *delta data* consists of &quot;chunklen - 4 - headerlen&quot; bytes, which contain a
3267 series of *delta*s, densely packed (no separators). These deltas describe a diff
3268 series of *delta*s, densely packed (no separators). These deltas describe a diff
3268 from an existing entry (either that the recipient already has, or previously
3269 from an existing entry (either that the recipient already has, or previously
3269 specified in the bundle/changegroup). The format is described more fully in
3270 specified in the bundle/changegroup). The format is described more fully in
3270 &quot;hg help internals.bdiff&quot;, but briefly:
3271 &quot;hg help internals.bdiff&quot;, but briefly:
3271 </p>
3272 </p>
3272 <pre>
3273 <pre>
3273 +---------------------------------------------------------------+
3274 +---------------------------------------------------------------+
3274 | | | | |
3275 | | | | |
3275 | start offset | end offset | new length | content |
3276 | start offset | end offset | new length | content |
3276 | (4 bytes) | (4 bytes) | (4 bytes) | (&lt;new length&gt; bytes) |
3277 | (4 bytes) | (4 bytes) | (4 bytes) | (&lt;new length&gt; bytes) |
3277 | | | | |
3278 | | | | |
3278 +---------------------------------------------------------------+
3279 +---------------------------------------------------------------+
3279 </pre>
3280 </pre>
3280 <p>
3281 <p>
3281 Please note that the length field in the delta data does *not* include itself.
3282 Please note that the length field in the delta data does *not* include itself.
3282 </p>
3283 </p>
3283 <p>
3284 <p>
3284 In version 1, the delta is always applied against the previous node from
3285 In version 1, the delta is always applied against the previous node from
3285 the changegroup or the first parent if this is the first entry in the
3286 the changegroup or the first parent if this is the first entry in the
3286 changegroup.
3287 changegroup.
3287 </p>
3288 </p>
3288 <p>
3289 <p>
3289 In version 2 and up, the delta base node is encoded in the entry in the
3290 In version 2 and up, the delta base node is encoded in the entry in the
3290 changegroup. This allows the delta to be expressed against any parent,
3291 changegroup. This allows the delta to be expressed against any parent,
3291 which can result in smaller deltas and more efficient encoding of data.
3292 which can result in smaller deltas and more efficient encoding of data.
3292 </p>
3293 </p>
3293 <h2>Changeset Segment</h2>
3294 <h2>Changeset Segment</h2>
3294 <p>
3295 <p>
3295 The *changeset segment* consists of a single *delta group* holding
3296 The *changeset segment* consists of a single *delta group* holding
3296 changelog data. The *empty chunk* at the end of the *delta group* denotes
3297 changelog data. The *empty chunk* at the end of the *delta group* denotes
3297 the boundary to the *manifest segment*.
3298 the boundary to the *manifest segment*.
3298 </p>
3299 </p>
3299 <h2>Manifest Segment</h2>
3300 <h2>Manifest Segment</h2>
3300 <p>
3301 <p>
3301 The *manifest segment* consists of a single *delta group* holding manifest
3302 The *manifest segment* consists of a single *delta group* holding manifest
3302 data. If treemanifests are in use, it contains only the manifest for the
3303 data. If treemanifests are in use, it contains only the manifest for the
3303 root directory of the repository. Otherwise, it contains the entire
3304 root directory of the repository. Otherwise, it contains the entire
3304 manifest data. The *empty chunk* at the end of the *delta group* denotes
3305 manifest data. The *empty chunk* at the end of the *delta group* denotes
3305 the boundary to the next segment (either the *treemanifests segment* or the
3306 the boundary to the next segment (either the *treemanifests segment* or the
3306 *filelogs segment*, depending on version and the request options).
3307 *filelogs segment*, depending on version and the request options).
3307 </p>
3308 </p>
3308 <h3>Treemanifests Segment</h3>
3309 <h3>Treemanifests Segment</h3>
3309 <p>
3310 <p>
3310 The *treemanifests segment* only exists in changegroup version &quot;3&quot;, and
3311 The *treemanifests segment* only exists in changegroup version &quot;3&quot;, and
3311 only if the 'treemanifest' param is part of the bundle2 changegroup part
3312 only if the 'treemanifest' param is part of the bundle2 changegroup part
3312 (it is not possible to use changegroup version 3 outside of bundle2).
3313 (it is not possible to use changegroup version 3 outside of bundle2).
3313 Aside from the filenames in the *treemanifests segment* containing a
3314 Aside from the filenames in the *treemanifests segment* containing a
3314 trailing &quot;/&quot; character, it behaves identically to the *filelogs segment*
3315 trailing &quot;/&quot; character, it behaves identically to the *filelogs segment*
3315 (see below). The final sub-segment is followed by an *empty chunk* (logically,
3316 (see below). The final sub-segment is followed by an *empty chunk* (logically,
3316 a sub-segment with filename size 0). This denotes the boundary to the
3317 a sub-segment with filename size 0). This denotes the boundary to the
3317 *filelogs segment*.
3318 *filelogs segment*.
3318 </p>
3319 </p>
3319 <h2>Filelogs Segment</h2>
3320 <h2>Filelogs Segment</h2>
3320 <p>
3321 <p>
3321 The *filelogs segment* consists of multiple sub-segments, each
3322 The *filelogs segment* consists of multiple sub-segments, each
3322 corresponding to an individual file whose data is being described:
3323 corresponding to an individual file whose data is being described:
3323 </p>
3324 </p>
3324 <pre>
3325 <pre>
3325 +--------------------------------------------------+
3326 +--------------------------------------------------+
3326 | | | | | |
3327 | | | | | |
3327 | filelog0 | filelog1 | filelog2 | ... | 0x0 |
3328 | filelog0 | filelog1 | filelog2 | ... | 0x0 |
3328 | | | | | (4 bytes) |
3329 | | | | | (4 bytes) |
3329 | | | | | |
3330 | | | | | |
3330 +--------------------------------------------------+
3331 +--------------------------------------------------+
3331 </pre>
3332 </pre>
3332 <p>
3333 <p>
3333 The final filelog sub-segment is followed by an *empty chunk* (logically,
3334 The final filelog sub-segment is followed by an *empty chunk* (logically,
3334 a sub-segment with filename size 0). This denotes the end of the segment
3335 a sub-segment with filename size 0). This denotes the end of the segment
3335 and of the overall changegroup.
3336 and of the overall changegroup.
3336 </p>
3337 </p>
3337 <p>
3338 <p>
3338 Each filelog sub-segment consists of the following:
3339 Each filelog sub-segment consists of the following:
3339 </p>
3340 </p>
3340 <pre>
3341 <pre>
3341 +------------------------------------------------------+
3342 +------------------------------------------------------+
3342 | | | |
3343 | | | |
3343 | filename length | filename | delta group |
3344 | filename length | filename | delta group |
3344 | (4 bytes) | (&lt;length - 4&gt; bytes) | (various) |
3345 | (4 bytes) | (&lt;length - 4&gt; bytes) | (various) |
3345 | | | |
3346 | | | |
3346 +------------------------------------------------------+
3347 +------------------------------------------------------+
3347 </pre>
3348 </pre>
3348 <p>
3349 <p>
3349 That is, a *chunk* consisting of the filename (not terminated or padded)
3350 That is, a *chunk* consisting of the filename (not terminated or padded)
3350 followed by N chunks constituting the *delta group* for this file. The
3351 followed by N chunks constituting the *delta group* for this file. The
3351 *empty chunk* at the end of each *delta group* denotes the boundary to the
3352 *empty chunk* at the end of each *delta group* denotes the boundary to the
3352 next filelog sub-segment.
3353 next filelog sub-segment.
3353 </p>
3354 </p>
3354
3355
3355 </div>
3356 </div>
3356 </div>
3357 </div>
3357 </div>
3358 </div>
3358
3359
3359
3360
3360
3361
3361 </body>
3362 </body>
3362 </html>
3363 </html>
3363
3364
3364
3365
3365 $ killdaemons.py
3366 $ killdaemons.py
3366
3367
3367 #endif
3368 #endif
@@ -1,422 +1,424 b''
1 Create configuration
1 Create configuration
2
2
3 $ echo "[ui]" >> $HGRCPATH
3 $ echo "[ui]" >> $HGRCPATH
4 $ echo "interactive=true" >> $HGRCPATH
4 $ echo "interactive=true" >> $HGRCPATH
5
5
6 help record (no record)
6 help record (no record)
7
7
8 $ hg help record
8 $ hg help record
9 record extension - commands to interactively select changes for
9 record extension - commands to interactively select changes for
10 commit/qrefresh (DEPRECATED)
10 commit/qrefresh (DEPRECATED)
11
11
12 The feature provided by this extension has been moved into core Mercurial as
12 The feature provided by this extension has been moved into core Mercurial as
13 'hg commit --interactive'.
13 'hg commit --interactive'.
14
14
15 (use 'hg help extensions' for information on enabling extensions)
15 (use 'hg help extensions' for information on enabling extensions)
16
16
17 help qrecord (no record)
17 help qrecord (no record)
18
18
19 $ hg help qrecord
19 $ hg help qrecord
20 'qrecord' is provided by the following extension:
20 'qrecord' is provided by the following extension:
21
21
22 record commands to interactively select changes for commit/qrefresh
22 record commands to interactively select changes for commit/qrefresh
23 (DEPRECATED)
23 (DEPRECATED)
24
24
25 (use 'hg help extensions' for information on enabling extensions)
25 (use 'hg help extensions' for information on enabling extensions)
26
26
27 $ echo "[extensions]" >> $HGRCPATH
27 $ echo "[extensions]" >> $HGRCPATH
28 $ echo "record=" >> $HGRCPATH
28 $ echo "record=" >> $HGRCPATH
29
29
30 help record (record)
30 help record (record)
31
31
32 $ hg help record
32 $ hg help record
33 hg record [OPTION]... [FILE]...
33 hg record [OPTION]... [FILE]...
34
34
35 interactively select changes to commit
35 interactively select changes to commit
36
36
37 If a list of files is omitted, all changes reported by 'hg status' will be
37 If a list of files is omitted, all changes reported by 'hg status' will be
38 candidates for recording.
38 candidates for recording.
39
39
40 See 'hg help dates' for a list of formats valid for -d/--date.
40 See 'hg help dates' for a list of formats valid for -d/--date.
41
41
42 If using the text interface (see 'hg help config'), you will be prompted
42 If using the text interface (see 'hg help config'), you will be prompted
43 for whether to record changes to each modified file, and for files with
43 for whether to record changes to each modified file, and for files with
44 multiple changes, for each change to use. For each query, the following
44 multiple changes, for each change to use. For each query, the following
45 responses are possible:
45 responses are possible:
46
46
47 y - record this change
47 y - record this change
48 n - skip this change
48 n - skip this change
49 e - edit this change manually
49 e - edit this change manually
50
50
51 s - skip remaining changes to this file
51 s - skip remaining changes to this file
52 f - record remaining changes to this file
52 f - record remaining changes to this file
53
53
54 d - done, skip remaining changes and files
54 d - done, skip remaining changes and files
55 a - record all changes to all remaining files
55 a - record all changes to all remaining files
56 q - quit, recording no changes
56 q - quit, recording no changes
57
57
58 ? - display help
58 ? - display help
59
59
60 This command is not available when committing a merge.
60 This command is not available when committing a merge.
61
61
62 (use 'hg help -e record' to show help for the record extension)
62 (use 'hg help -e record' to show help for the record extension)
63
63
64 options ([+] can be repeated):
64 options ([+] can be repeated):
65
65
66 -A --addremove mark new/missing files as added/removed before
66 -A --addremove mark new/missing files as added/removed before
67 committing
67 committing
68 --close-branch mark a branch head as closed
68 --close-branch mark a branch head as closed
69 --amend amend the parent of the working directory
69 --amend amend the parent of the working directory
70 -s --secret use the secret phase for committing
70 -s --secret use the secret phase for committing
71 -e --edit invoke editor on commit messages
71 -e --edit invoke editor on commit messages
72 -I --include PATTERN [+] include names matching the given patterns
72 -I --include PATTERN [+] include names matching the given patterns
73 -X --exclude PATTERN [+] exclude names matching the given patterns
73 -X --exclude PATTERN [+] exclude names matching the given patterns
74 -m --message TEXT use text as commit message
74 -m --message TEXT use text as commit message
75 -l --logfile FILE read commit message from file
75 -l --logfile FILE read commit message from file
76 -d --date DATE record the specified date as commit date
76 -d --date DATE record the specified date as commit date
77 -u --user USER record the specified user as committer
77 -u --user USER record the specified user as committer
78 -S --subrepos recurse into subrepositories
78 -S --subrepos recurse into subrepositories
79 -w --ignore-all-space ignore white space when comparing lines
79 -w --ignore-all-space ignore white space when comparing lines
80 -b --ignore-space-change ignore changes in the amount of white space
80 -b --ignore-space-change ignore changes in the amount of white space
81 -B --ignore-blank-lines ignore changes whose lines are all blank
81 -B --ignore-blank-lines ignore changes whose lines are all blank
82 -Z --ignore-space-at-eol ignore changes in whitespace at EOL
82
83
83 (some details hidden, use --verbose to show complete help)
84 (some details hidden, use --verbose to show complete help)
84
85
85 help (no mq, so no qrecord)
86 help (no mq, so no qrecord)
86
87
87 $ hg help qrecord
88 $ hg help qrecord
88 hg qrecord [OPTION]... PATCH [FILE]...
89 hg qrecord [OPTION]... PATCH [FILE]...
89
90
90 interactively record a new patch
91 interactively record a new patch
91
92
92 See 'hg help qnew' & 'hg help record' for more information and usage.
93 See 'hg help qnew' & 'hg help record' for more information and usage.
93
94
94 (some details hidden, use --verbose to show complete help)
95 (some details hidden, use --verbose to show complete help)
95
96
96 $ hg init a
97 $ hg init a
97
98
98 qrecord (mq not present)
99 qrecord (mq not present)
99
100
100 $ hg -R a qrecord
101 $ hg -R a qrecord
101 hg qrecord: invalid arguments
102 hg qrecord: invalid arguments
102 hg qrecord [OPTION]... PATCH [FILE]...
103 hg qrecord [OPTION]... PATCH [FILE]...
103
104
104 interactively record a new patch
105 interactively record a new patch
105
106
106 (use 'hg qrecord -h' to show more help)
107 (use 'hg qrecord -h' to show more help)
107 [255]
108 [255]
108
109
109 qrecord patch (mq not present)
110 qrecord patch (mq not present)
110
111
111 $ hg -R a qrecord patch
112 $ hg -R a qrecord patch
112 abort: 'mq' extension not loaded
113 abort: 'mq' extension not loaded
113 [255]
114 [255]
114
115
115 help (bad mq)
116 help (bad mq)
116
117
117 $ echo "mq=nonexistent" >> $HGRCPATH
118 $ echo "mq=nonexistent" >> $HGRCPATH
118 $ hg help qrecord
119 $ hg help qrecord
119 *** failed to import extension mq from nonexistent: [Errno *] * (glob)
120 *** failed to import extension mq from nonexistent: [Errno *] * (glob)
120 hg qrecord [OPTION]... PATCH [FILE]...
121 hg qrecord [OPTION]... PATCH [FILE]...
121
122
122 interactively record a new patch
123 interactively record a new patch
123
124
124 See 'hg help qnew' & 'hg help record' for more information and usage.
125 See 'hg help qnew' & 'hg help record' for more information and usage.
125
126
126 (some details hidden, use --verbose to show complete help)
127 (some details hidden, use --verbose to show complete help)
127
128
128 help (mq present)
129 help (mq present)
129
130
130 $ sed 's/mq=nonexistent/mq=/' $HGRCPATH > hgrc.tmp
131 $ sed 's/mq=nonexistent/mq=/' $HGRCPATH > hgrc.tmp
131 $ mv hgrc.tmp $HGRCPATH
132 $ mv hgrc.tmp $HGRCPATH
132
133
133 $ hg help qrecord
134 $ hg help qrecord
134 hg qrecord [OPTION]... PATCH [FILE]...
135 hg qrecord [OPTION]... PATCH [FILE]...
135
136
136 interactively record a new patch
137 interactively record a new patch
137
138
138 See 'hg help qnew' & 'hg help record' for more information and usage.
139 See 'hg help qnew' & 'hg help record' for more information and usage.
139
140
140 options ([+] can be repeated):
141 options ([+] can be repeated):
141
142
142 -e --edit invoke editor on commit messages
143 -e --edit invoke editor on commit messages
143 -g --git use git extended diff format
144 -g --git use git extended diff format
144 -U --currentuser add "From: <current user>" to patch
145 -U --currentuser add "From: <current user>" to patch
145 -u --user USER add "From: <USER>" to patch
146 -u --user USER add "From: <USER>" to patch
146 -D --currentdate add "Date: <current date>" to patch
147 -D --currentdate add "Date: <current date>" to patch
147 -d --date DATE add "Date: <DATE>" to patch
148 -d --date DATE add "Date: <DATE>" to patch
148 -I --include PATTERN [+] include names matching the given patterns
149 -I --include PATTERN [+] include names matching the given patterns
149 -X --exclude PATTERN [+] exclude names matching the given patterns
150 -X --exclude PATTERN [+] exclude names matching the given patterns
150 -m --message TEXT use text as commit message
151 -m --message TEXT use text as commit message
151 -l --logfile FILE read commit message from file
152 -l --logfile FILE read commit message from file
152 -w --ignore-all-space ignore white space when comparing lines
153 -w --ignore-all-space ignore white space when comparing lines
153 -b --ignore-space-change ignore changes in the amount of white space
154 -b --ignore-space-change ignore changes in the amount of white space
154 -B --ignore-blank-lines ignore changes whose lines are all blank
155 -B --ignore-blank-lines ignore changes whose lines are all blank
156 -Z --ignore-space-at-eol ignore changes in whitespace at EOL
155 --mq operate on patch repository
157 --mq operate on patch repository
156
158
157 (some details hidden, use --verbose to show complete help)
159 (some details hidden, use --verbose to show complete help)
158
160
159 $ cd a
161 $ cd a
160
162
161 Base commit
163 Base commit
162
164
163 $ cat > 1.txt <<EOF
165 $ cat > 1.txt <<EOF
164 > 1
166 > 1
165 > 2
167 > 2
166 > 3
168 > 3
167 > 4
169 > 4
168 > 5
170 > 5
169 > EOF
171 > EOF
170 $ cat > 2.txt <<EOF
172 $ cat > 2.txt <<EOF
171 > a
173 > a
172 > b
174 > b
173 > c
175 > c
174 > d
176 > d
175 > e
177 > e
176 > f
178 > f
177 > EOF
179 > EOF
178
180
179 $ mkdir dir
181 $ mkdir dir
180 $ cat > dir/a.txt <<EOF
182 $ cat > dir/a.txt <<EOF
181 > hello world
183 > hello world
182 >
184 >
183 > someone
185 > someone
184 > up
186 > up
185 > there
187 > there
186 > loves
188 > loves
187 > me
189 > me
188 > EOF
190 > EOF
189
191
190 $ hg add 1.txt 2.txt dir/a.txt
192 $ hg add 1.txt 2.txt dir/a.txt
191 $ hg commit -m 'initial checkin'
193 $ hg commit -m 'initial checkin'
192
194
193 Changing files
195 Changing files
194
196
195 $ sed -e 's/2/2 2/;s/4/4 4/' 1.txt > 1.txt.new
197 $ sed -e 's/2/2 2/;s/4/4 4/' 1.txt > 1.txt.new
196 $ sed -e 's/b/b b/' 2.txt > 2.txt.new
198 $ sed -e 's/b/b b/' 2.txt > 2.txt.new
197 $ sed -e 's/hello world/hello world!/' dir/a.txt > dir/a.txt.new
199 $ sed -e 's/hello world/hello world!/' dir/a.txt > dir/a.txt.new
198
200
199 $ mv -f 1.txt.new 1.txt
201 $ mv -f 1.txt.new 1.txt
200 $ mv -f 2.txt.new 2.txt
202 $ mv -f 2.txt.new 2.txt
201 $ mv -f dir/a.txt.new dir/a.txt
203 $ mv -f dir/a.txt.new dir/a.txt
202
204
203 Whole diff
205 Whole diff
204
206
205 $ hg diff --nodates
207 $ hg diff --nodates
206 diff -r 1057167b20ef 1.txt
208 diff -r 1057167b20ef 1.txt
207 --- a/1.txt
209 --- a/1.txt
208 +++ b/1.txt
210 +++ b/1.txt
209 @@ -1,5 +1,5 @@
211 @@ -1,5 +1,5 @@
210 1
212 1
211 -2
213 -2
212 +2 2
214 +2 2
213 3
215 3
214 -4
216 -4
215 +4 4
217 +4 4
216 5
218 5
217 diff -r 1057167b20ef 2.txt
219 diff -r 1057167b20ef 2.txt
218 --- a/2.txt
220 --- a/2.txt
219 +++ b/2.txt
221 +++ b/2.txt
220 @@ -1,5 +1,5 @@
222 @@ -1,5 +1,5 @@
221 a
223 a
222 -b
224 -b
223 +b b
225 +b b
224 c
226 c
225 d
227 d
226 e
228 e
227 diff -r 1057167b20ef dir/a.txt
229 diff -r 1057167b20ef dir/a.txt
228 --- a/dir/a.txt
230 --- a/dir/a.txt
229 +++ b/dir/a.txt
231 +++ b/dir/a.txt
230 @@ -1,4 +1,4 @@
232 @@ -1,4 +1,4 @@
231 -hello world
233 -hello world
232 +hello world!
234 +hello world!
233
235
234 someone
236 someone
235 up
237 up
236
238
237 qrecord with bad patch name, should abort before prompting
239 qrecord with bad patch name, should abort before prompting
238
240
239 $ hg qrecord .hg
241 $ hg qrecord .hg
240 abort: patch name cannot begin with ".hg"
242 abort: patch name cannot begin with ".hg"
241 [255]
243 [255]
242 $ hg qrecord ' foo'
244 $ hg qrecord ' foo'
243 abort: patch name cannot begin or end with whitespace
245 abort: patch name cannot begin or end with whitespace
244 [255]
246 [255]
245 $ hg qrecord 'foo '
247 $ hg qrecord 'foo '
246 abort: patch name cannot begin or end with whitespace
248 abort: patch name cannot begin or end with whitespace
247 [255]
249 [255]
248
250
249 qrecord a.patch
251 qrecord a.patch
250
252
251 $ hg qrecord -d '0 0' -m aaa a.patch <<EOF
253 $ hg qrecord -d '0 0' -m aaa a.patch <<EOF
252 > y
254 > y
253 > y
255 > y
254 > n
256 > n
255 > y
257 > y
256 > y
258 > y
257 > n
259 > n
258 > EOF
260 > EOF
259 diff --git a/1.txt b/1.txt
261 diff --git a/1.txt b/1.txt
260 2 hunks, 2 lines changed
262 2 hunks, 2 lines changed
261 examine changes to '1.txt'? [Ynesfdaq?] y
263 examine changes to '1.txt'? [Ynesfdaq?] y
262
264
263 @@ -1,3 +1,3 @@
265 @@ -1,3 +1,3 @@
264 1
266 1
265 -2
267 -2
266 +2 2
268 +2 2
267 3
269 3
268 record change 1/4 to '1.txt'? [Ynesfdaq?] y
270 record change 1/4 to '1.txt'? [Ynesfdaq?] y
269
271
270 @@ -3,3 +3,3 @@
272 @@ -3,3 +3,3 @@
271 3
273 3
272 -4
274 -4
273 +4 4
275 +4 4
274 5
276 5
275 record change 2/4 to '1.txt'? [Ynesfdaq?] n
277 record change 2/4 to '1.txt'? [Ynesfdaq?] n
276
278
277 diff --git a/2.txt b/2.txt
279 diff --git a/2.txt b/2.txt
278 1 hunks, 1 lines changed
280 1 hunks, 1 lines changed
279 examine changes to '2.txt'? [Ynesfdaq?] y
281 examine changes to '2.txt'? [Ynesfdaq?] y
280
282
281 @@ -1,5 +1,5 @@
283 @@ -1,5 +1,5 @@
282 a
284 a
283 -b
285 -b
284 +b b
286 +b b
285 c
287 c
286 d
288 d
287 e
289 e
288 record change 3/4 to '2.txt'? [Ynesfdaq?] y
290 record change 3/4 to '2.txt'? [Ynesfdaq?] y
289
291
290 diff --git a/dir/a.txt b/dir/a.txt
292 diff --git a/dir/a.txt b/dir/a.txt
291 1 hunks, 1 lines changed
293 1 hunks, 1 lines changed
292 examine changes to 'dir/a.txt'? [Ynesfdaq?] n
294 examine changes to 'dir/a.txt'? [Ynesfdaq?] n
293
295
294
296
295 After qrecord a.patch 'tip'"
297 After qrecord a.patch 'tip'"
296
298
297 $ hg tip -p
299 $ hg tip -p
298 changeset: 1:5d1ca63427ee
300 changeset: 1:5d1ca63427ee
299 tag: a.patch
301 tag: a.patch
300 tag: qbase
302 tag: qbase
301 tag: qtip
303 tag: qtip
302 tag: tip
304 tag: tip
303 user: test
305 user: test
304 date: Thu Jan 01 00:00:00 1970 +0000
306 date: Thu Jan 01 00:00:00 1970 +0000
305 summary: aaa
307 summary: aaa
306
308
307 diff -r 1057167b20ef -r 5d1ca63427ee 1.txt
309 diff -r 1057167b20ef -r 5d1ca63427ee 1.txt
308 --- a/1.txt Thu Jan 01 00:00:00 1970 +0000
310 --- a/1.txt Thu Jan 01 00:00:00 1970 +0000
309 +++ b/1.txt Thu Jan 01 00:00:00 1970 +0000
311 +++ b/1.txt Thu Jan 01 00:00:00 1970 +0000
310 @@ -1,5 +1,5 @@
312 @@ -1,5 +1,5 @@
311 1
313 1
312 -2
314 -2
313 +2 2
315 +2 2
314 3
316 3
315 4
317 4
316 5
318 5
317 diff -r 1057167b20ef -r 5d1ca63427ee 2.txt
319 diff -r 1057167b20ef -r 5d1ca63427ee 2.txt
318 --- a/2.txt Thu Jan 01 00:00:00 1970 +0000
320 --- a/2.txt Thu Jan 01 00:00:00 1970 +0000
319 +++ b/2.txt Thu Jan 01 00:00:00 1970 +0000
321 +++ b/2.txt Thu Jan 01 00:00:00 1970 +0000
320 @@ -1,5 +1,5 @@
322 @@ -1,5 +1,5 @@
321 a
323 a
322 -b
324 -b
323 +b b
325 +b b
324 c
326 c
325 d
327 d
326 e
328 e
327
329
328
330
329 After qrecord a.patch 'diff'"
331 After qrecord a.patch 'diff'"
330
332
331 $ hg diff --nodates
333 $ hg diff --nodates
332 diff -r 5d1ca63427ee 1.txt
334 diff -r 5d1ca63427ee 1.txt
333 --- a/1.txt
335 --- a/1.txt
334 +++ b/1.txt
336 +++ b/1.txt
335 @@ -1,5 +1,5 @@
337 @@ -1,5 +1,5 @@
336 1
338 1
337 2 2
339 2 2
338 3
340 3
339 -4
341 -4
340 +4 4
342 +4 4
341 5
343 5
342 diff -r 5d1ca63427ee dir/a.txt
344 diff -r 5d1ca63427ee dir/a.txt
343 --- a/dir/a.txt
345 --- a/dir/a.txt
344 +++ b/dir/a.txt
346 +++ b/dir/a.txt
345 @@ -1,4 +1,4 @@
347 @@ -1,4 +1,4 @@
346 -hello world
348 -hello world
347 +hello world!
349 +hello world!
348
350
349 someone
351 someone
350 up
352 up
351
353
352 qrecord b.patch
354 qrecord b.patch
353
355
354 $ hg qrecord -d '0 0' -m bbb b.patch <<EOF
356 $ hg qrecord -d '0 0' -m bbb b.patch <<EOF
355 > y
357 > y
356 > y
358 > y
357 > y
359 > y
358 > y
360 > y
359 > EOF
361 > EOF
360 diff --git a/1.txt b/1.txt
362 diff --git a/1.txt b/1.txt
361 1 hunks, 1 lines changed
363 1 hunks, 1 lines changed
362 examine changes to '1.txt'? [Ynesfdaq?] y
364 examine changes to '1.txt'? [Ynesfdaq?] y
363
365
364 @@ -1,5 +1,5 @@
366 @@ -1,5 +1,5 @@
365 1
367 1
366 2 2
368 2 2
367 3
369 3
368 -4
370 -4
369 +4 4
371 +4 4
370 5
372 5
371 record change 1/2 to '1.txt'? [Ynesfdaq?] y
373 record change 1/2 to '1.txt'? [Ynesfdaq?] y
372
374
373 diff --git a/dir/a.txt b/dir/a.txt
375 diff --git a/dir/a.txt b/dir/a.txt
374 1 hunks, 1 lines changed
376 1 hunks, 1 lines changed
375 examine changes to 'dir/a.txt'? [Ynesfdaq?] y
377 examine changes to 'dir/a.txt'? [Ynesfdaq?] y
376
378
377 @@ -1,4 +1,4 @@
379 @@ -1,4 +1,4 @@
378 -hello world
380 -hello world
379 +hello world!
381 +hello world!
380
382
381 someone
383 someone
382 up
384 up
383 record change 2/2 to 'dir/a.txt'? [Ynesfdaq?] y
385 record change 2/2 to 'dir/a.txt'? [Ynesfdaq?] y
384
386
385
387
386 After qrecord b.patch 'tip'
388 After qrecord b.patch 'tip'
387
389
388 $ hg tip -p
390 $ hg tip -p
389 changeset: 2:b056198bf878
391 changeset: 2:b056198bf878
390 tag: b.patch
392 tag: b.patch
391 tag: qtip
393 tag: qtip
392 tag: tip
394 tag: tip
393 user: test
395 user: test
394 date: Thu Jan 01 00:00:00 1970 +0000
396 date: Thu Jan 01 00:00:00 1970 +0000
395 summary: bbb
397 summary: bbb
396
398
397 diff -r 5d1ca63427ee -r b056198bf878 1.txt
399 diff -r 5d1ca63427ee -r b056198bf878 1.txt
398 --- a/1.txt Thu Jan 01 00:00:00 1970 +0000
400 --- a/1.txt Thu Jan 01 00:00:00 1970 +0000
399 +++ b/1.txt Thu Jan 01 00:00:00 1970 +0000
401 +++ b/1.txt Thu Jan 01 00:00:00 1970 +0000
400 @@ -1,5 +1,5 @@
402 @@ -1,5 +1,5 @@
401 1
403 1
402 2 2
404 2 2
403 3
405 3
404 -4
406 -4
405 +4 4
407 +4 4
406 5
408 5
407 diff -r 5d1ca63427ee -r b056198bf878 dir/a.txt
409 diff -r 5d1ca63427ee -r b056198bf878 dir/a.txt
408 --- a/dir/a.txt Thu Jan 01 00:00:00 1970 +0000
410 --- a/dir/a.txt Thu Jan 01 00:00:00 1970 +0000
409 +++ b/dir/a.txt Thu Jan 01 00:00:00 1970 +0000
411 +++ b/dir/a.txt Thu Jan 01 00:00:00 1970 +0000
410 @@ -1,4 +1,4 @@
412 @@ -1,4 +1,4 @@
411 -hello world
413 -hello world
412 +hello world!
414 +hello world!
413
415
414 someone
416 someone
415 up
417 up
416
418
417
419
418 After qrecord b.patch 'diff'
420 After qrecord b.patch 'diff'
419
421
420 $ hg diff --nodates
422 $ hg diff --nodates
421
423
422 $ cd ..
424 $ cd ..
@@ -1,91 +1,92 b''
1 Set up a repo
1 Set up a repo
2
2
3 $ cat <<EOF >> $HGRCPATH
3 $ cat <<EOF >> $HGRCPATH
4 > [ui]
4 > [ui]
5 > interactive = true
5 > interactive = true
6 > [extensions]
6 > [extensions]
7 > record =
7 > record =
8 > EOF
8 > EOF
9
9
10 $ hg init a
10 $ hg init a
11 $ cd a
11 $ cd a
12
12
13 Record help
13 Record help
14
14
15 $ hg record -h
15 $ hg record -h
16 hg record [OPTION]... [FILE]...
16 hg record [OPTION]... [FILE]...
17
17
18 interactively select changes to commit
18 interactively select changes to commit
19
19
20 If a list of files is omitted, all changes reported by 'hg status' will be
20 If a list of files is omitted, all changes reported by 'hg status' will be
21 candidates for recording.
21 candidates for recording.
22
22
23 See 'hg help dates' for a list of formats valid for -d/--date.
23 See 'hg help dates' for a list of formats valid for -d/--date.
24
24
25 If using the text interface (see 'hg help config'), you will be prompted
25 If using the text interface (see 'hg help config'), you will be prompted
26 for whether to record changes to each modified file, and for files with
26 for whether to record changes to each modified file, and for files with
27 multiple changes, for each change to use. For each query, the following
27 multiple changes, for each change to use. For each query, the following
28 responses are possible:
28 responses are possible:
29
29
30 y - record this change
30 y - record this change
31 n - skip this change
31 n - skip this change
32 e - edit this change manually
32 e - edit this change manually
33
33
34 s - skip remaining changes to this file
34 s - skip remaining changes to this file
35 f - record remaining changes to this file
35 f - record remaining changes to this file
36
36
37 d - done, skip remaining changes and files
37 d - done, skip remaining changes and files
38 a - record all changes to all remaining files
38 a - record all changes to all remaining files
39 q - quit, recording no changes
39 q - quit, recording no changes
40
40
41 ? - display help
41 ? - display help
42
42
43 This command is not available when committing a merge.
43 This command is not available when committing a merge.
44
44
45 (use 'hg help -e record' to show help for the record extension)
45 (use 'hg help -e record' to show help for the record extension)
46
46
47 options ([+] can be repeated):
47 options ([+] can be repeated):
48
48
49 -A --addremove mark new/missing files as added/removed before
49 -A --addremove mark new/missing files as added/removed before
50 committing
50 committing
51 --close-branch mark a branch head as closed
51 --close-branch mark a branch head as closed
52 --amend amend the parent of the working directory
52 --amend amend the parent of the working directory
53 -s --secret use the secret phase for committing
53 -s --secret use the secret phase for committing
54 -e --edit invoke editor on commit messages
54 -e --edit invoke editor on commit messages
55 -I --include PATTERN [+] include names matching the given patterns
55 -I --include PATTERN [+] include names matching the given patterns
56 -X --exclude PATTERN [+] exclude names matching the given patterns
56 -X --exclude PATTERN [+] exclude names matching the given patterns
57 -m --message TEXT use text as commit message
57 -m --message TEXT use text as commit message
58 -l --logfile FILE read commit message from file
58 -l --logfile FILE read commit message from file
59 -d --date DATE record the specified date as commit date
59 -d --date DATE record the specified date as commit date
60 -u --user USER record the specified user as committer
60 -u --user USER record the specified user as committer
61 -S --subrepos recurse into subrepositories
61 -S --subrepos recurse into subrepositories
62 -w --ignore-all-space ignore white space when comparing lines
62 -w --ignore-all-space ignore white space when comparing lines
63 -b --ignore-space-change ignore changes in the amount of white space
63 -b --ignore-space-change ignore changes in the amount of white space
64 -B --ignore-blank-lines ignore changes whose lines are all blank
64 -B --ignore-blank-lines ignore changes whose lines are all blank
65 -Z --ignore-space-at-eol ignore changes in whitespace at EOL
65
66
66 (some details hidden, use --verbose to show complete help)
67 (some details hidden, use --verbose to show complete help)
67
68
68 Select no files
69 Select no files
69
70
70 $ touch empty-rw
71 $ touch empty-rw
71 $ hg add empty-rw
72 $ hg add empty-rw
72
73
73 $ hg record empty-rw<<EOF
74 $ hg record empty-rw<<EOF
74 > n
75 > n
75 > EOF
76 > EOF
76 diff --git a/empty-rw b/empty-rw
77 diff --git a/empty-rw b/empty-rw
77 new file mode 100644
78 new file mode 100644
78 examine changes to 'empty-rw'? [Ynesfdaq?] n
79 examine changes to 'empty-rw'? [Ynesfdaq?] n
79
80
80 no changes to record
81 no changes to record
81 [1]
82 [1]
82
83
83 $ hg tip -p
84 $ hg tip -p
84 changeset: -1:000000000000
85 changeset: -1:000000000000
85 tag: tip
86 tag: tip
86 user:
87 user:
87 date: Thu Jan 01 00:00:00 1970 +0000
88 date: Thu Jan 01 00:00:00 1970 +0000
88
89
89
90
90
91
91
92
General Comments 0
You need to be logged in to leave comments. Login now