##// END OF EJS Templates
branch: allow changing branch name to existing name if possible...
Pulkit Goyal -
r35764:e5b6ba78 default
parent child Browse files
Show More
@@ -1,4022 +1,4027 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 dagop,
29 dagop,
30 dirstateguard,
30 dirstateguard,
31 encoding,
31 encoding,
32 error,
32 error,
33 formatter,
33 formatter,
34 graphmod,
34 graphmod,
35 match as matchmod,
35 match as matchmod,
36 mdiff,
36 mdiff,
37 obsolete,
37 obsolete,
38 patch,
38 patch,
39 pathutil,
39 pathutil,
40 pycompat,
40 pycompat,
41 registrar,
41 registrar,
42 revlog,
42 revlog,
43 revset,
43 revset,
44 revsetlang,
44 revsetlang,
45 rewriteutil,
45 rewriteutil,
46 scmutil,
46 scmutil,
47 smartset,
47 smartset,
48 templatekw,
48 templatekw,
49 templater,
49 templater,
50 util,
50 util,
51 vfs as vfsmod,
51 vfs as vfsmod,
52 )
52 )
53 stringio = util.stringio
53 stringio = util.stringio
54
54
55 # templates of common command options
55 # templates of common command options
56
56
57 dryrunopts = [
57 dryrunopts = [
58 ('n', 'dry-run', None,
58 ('n', 'dry-run', None,
59 _('do not perform actions, just print output')),
59 _('do not perform actions, just print output')),
60 ]
60 ]
61
61
62 remoteopts = [
62 remoteopts = [
63 ('e', 'ssh', '',
63 ('e', 'ssh', '',
64 _('specify ssh command to use'), _('CMD')),
64 _('specify ssh command to use'), _('CMD')),
65 ('', 'remotecmd', '',
65 ('', 'remotecmd', '',
66 _('specify hg command to run on the remote side'), _('CMD')),
66 _('specify hg command to run on the remote side'), _('CMD')),
67 ('', 'insecure', None,
67 ('', 'insecure', None,
68 _('do not verify server certificate (ignoring web.cacerts config)')),
68 _('do not verify server certificate (ignoring web.cacerts config)')),
69 ]
69 ]
70
70
71 walkopts = [
71 walkopts = [
72 ('I', 'include', [],
72 ('I', 'include', [],
73 _('include names matching the given patterns'), _('PATTERN')),
73 _('include names matching the given patterns'), _('PATTERN')),
74 ('X', 'exclude', [],
74 ('X', 'exclude', [],
75 _('exclude names matching the given patterns'), _('PATTERN')),
75 _('exclude names matching the given patterns'), _('PATTERN')),
76 ]
76 ]
77
77
78 commitopts = [
78 commitopts = [
79 ('m', 'message', '',
79 ('m', 'message', '',
80 _('use text as commit message'), _('TEXT')),
80 _('use text as commit message'), _('TEXT')),
81 ('l', 'logfile', '',
81 ('l', 'logfile', '',
82 _('read commit message from file'), _('FILE')),
82 _('read commit message from file'), _('FILE')),
83 ]
83 ]
84
84
85 commitopts2 = [
85 commitopts2 = [
86 ('d', 'date', '',
86 ('d', 'date', '',
87 _('record the specified date as commit date'), _('DATE')),
87 _('record the specified date as commit date'), _('DATE')),
88 ('u', 'user', '',
88 ('u', 'user', '',
89 _('record the specified user as committer'), _('USER')),
89 _('record the specified user as committer'), _('USER')),
90 ]
90 ]
91
91
92 # hidden for now
92 # hidden for now
93 formatteropts = [
93 formatteropts = [
94 ('T', 'template', '',
94 ('T', 'template', '',
95 _('display with template (EXPERIMENTAL)'), _('TEMPLATE')),
95 _('display with template (EXPERIMENTAL)'), _('TEMPLATE')),
96 ]
96 ]
97
97
98 templateopts = [
98 templateopts = [
99 ('', 'style', '',
99 ('', 'style', '',
100 _('display using template map file (DEPRECATED)'), _('STYLE')),
100 _('display using template map file (DEPRECATED)'), _('STYLE')),
101 ('T', 'template', '',
101 ('T', 'template', '',
102 _('display with template'), _('TEMPLATE')),
102 _('display with template'), _('TEMPLATE')),
103 ]
103 ]
104
104
105 logopts = [
105 logopts = [
106 ('p', 'patch', None, _('show patch')),
106 ('p', 'patch', None, _('show patch')),
107 ('g', 'git', None, _('use git extended diff format')),
107 ('g', 'git', None, _('use git extended diff format')),
108 ('l', 'limit', '',
108 ('l', 'limit', '',
109 _('limit number of changes displayed'), _('NUM')),
109 _('limit number of changes displayed'), _('NUM')),
110 ('M', 'no-merges', None, _('do not show merges')),
110 ('M', 'no-merges', None, _('do not show merges')),
111 ('', 'stat', None, _('output diffstat-style summary of changes')),
111 ('', 'stat', None, _('output diffstat-style summary of changes')),
112 ('G', 'graph', None, _("show the revision DAG")),
112 ('G', 'graph', None, _("show the revision DAG")),
113 ] + templateopts
113 ] + templateopts
114
114
115 diffopts = [
115 diffopts = [
116 ('a', 'text', None, _('treat all files as text')),
116 ('a', 'text', None, _('treat all files as text')),
117 ('g', 'git', None, _('use git extended diff format')),
117 ('g', 'git', None, _('use git extended diff format')),
118 ('', 'binary', None, _('generate binary diffs in git mode (default)')),
118 ('', 'binary', None, _('generate binary diffs in git mode (default)')),
119 ('', 'nodates', None, _('omit dates from diff headers'))
119 ('', 'nodates', None, _('omit dates from diff headers'))
120 ]
120 ]
121
121
122 diffwsopts = [
122 diffwsopts = [
123 ('w', 'ignore-all-space', None,
123 ('w', 'ignore-all-space', None,
124 _('ignore white space when comparing lines')),
124 _('ignore white space when comparing lines')),
125 ('b', 'ignore-space-change', None,
125 ('b', 'ignore-space-change', None,
126 _('ignore changes in the amount of white space')),
126 _('ignore changes in the amount of white space')),
127 ('B', 'ignore-blank-lines', None,
127 ('B', 'ignore-blank-lines', None,
128 _('ignore changes whose lines are all blank')),
128 _('ignore changes whose lines are all blank')),
129 ('Z', 'ignore-space-at-eol', None,
129 ('Z', 'ignore-space-at-eol', None,
130 _('ignore changes in whitespace at EOL')),
130 _('ignore changes in whitespace at EOL')),
131 ]
131 ]
132
132
133 diffopts2 = [
133 diffopts2 = [
134 ('', 'noprefix', None, _('omit a/ and b/ prefixes from filenames')),
134 ('', 'noprefix', None, _('omit a/ and b/ prefixes from filenames')),
135 ('p', 'show-function', None, _('show which function each change is in')),
135 ('p', 'show-function', None, _('show which function each change is in')),
136 ('', 'reverse', None, _('produce a diff that undoes the changes')),
136 ('', 'reverse', None, _('produce a diff that undoes the changes')),
137 ] + diffwsopts + [
137 ] + diffwsopts + [
138 ('U', 'unified', '',
138 ('U', 'unified', '',
139 _('number of lines of context to show'), _('NUM')),
139 _('number of lines of context to show'), _('NUM')),
140 ('', 'stat', None, _('output diffstat-style summary of changes')),
140 ('', 'stat', None, _('output diffstat-style summary of changes')),
141 ('', 'root', '', _('produce diffs relative to subdirectory'), _('DIR')),
141 ('', 'root', '', _('produce diffs relative to subdirectory'), _('DIR')),
142 ]
142 ]
143
143
144 mergetoolopts = [
144 mergetoolopts = [
145 ('t', 'tool', '', _('specify merge tool')),
145 ('t', 'tool', '', _('specify merge tool')),
146 ]
146 ]
147
147
148 similarityopts = [
148 similarityopts = [
149 ('s', 'similarity', '',
149 ('s', 'similarity', '',
150 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
150 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
151 ]
151 ]
152
152
153 subrepoopts = [
153 subrepoopts = [
154 ('S', 'subrepos', None,
154 ('S', 'subrepos', None,
155 _('recurse into subrepositories'))
155 _('recurse into subrepositories'))
156 ]
156 ]
157
157
158 debugrevlogopts = [
158 debugrevlogopts = [
159 ('c', 'changelog', False, _('open changelog')),
159 ('c', 'changelog', False, _('open changelog')),
160 ('m', 'manifest', False, _('open manifest')),
160 ('m', 'manifest', False, _('open manifest')),
161 ('', 'dir', '', _('open directory manifest')),
161 ('', 'dir', '', _('open directory manifest')),
162 ]
162 ]
163
163
164 # special string such that everything below this line will be ingored in the
164 # special string such that everything below this line will be ingored in the
165 # editor text
165 # editor text
166 _linebelow = "^HG: ------------------------ >8 ------------------------$"
166 _linebelow = "^HG: ------------------------ >8 ------------------------$"
167
167
168 def ishunk(x):
168 def ishunk(x):
169 hunkclasses = (crecordmod.uihunk, patch.recordhunk)
169 hunkclasses = (crecordmod.uihunk, patch.recordhunk)
170 return isinstance(x, hunkclasses)
170 return isinstance(x, hunkclasses)
171
171
172 def newandmodified(chunks, originalchunks):
172 def newandmodified(chunks, originalchunks):
173 newlyaddedandmodifiedfiles = set()
173 newlyaddedandmodifiedfiles = set()
174 for chunk in chunks:
174 for chunk in chunks:
175 if ishunk(chunk) and chunk.header.isnewfile() and chunk not in \
175 if ishunk(chunk) and chunk.header.isnewfile() and chunk not in \
176 originalchunks:
176 originalchunks:
177 newlyaddedandmodifiedfiles.add(chunk.header.filename())
177 newlyaddedandmodifiedfiles.add(chunk.header.filename())
178 return newlyaddedandmodifiedfiles
178 return newlyaddedandmodifiedfiles
179
179
180 def parsealiases(cmd):
180 def parsealiases(cmd):
181 return cmd.lstrip("^").split("|")
181 return cmd.lstrip("^").split("|")
182
182
183 def setupwrapcolorwrite(ui):
183 def setupwrapcolorwrite(ui):
184 # wrap ui.write so diff output can be labeled/colorized
184 # wrap ui.write so diff output can be labeled/colorized
185 def wrapwrite(orig, *args, **kw):
185 def wrapwrite(orig, *args, **kw):
186 label = kw.pop(r'label', '')
186 label = kw.pop(r'label', '')
187 for chunk, l in patch.difflabel(lambda: args):
187 for chunk, l in patch.difflabel(lambda: args):
188 orig(chunk, label=label + l)
188 orig(chunk, label=label + l)
189
189
190 oldwrite = ui.write
190 oldwrite = ui.write
191 def wrap(*args, **kwargs):
191 def wrap(*args, **kwargs):
192 return wrapwrite(oldwrite, *args, **kwargs)
192 return wrapwrite(oldwrite, *args, **kwargs)
193 setattr(ui, 'write', wrap)
193 setattr(ui, 'write', wrap)
194 return oldwrite
194 return oldwrite
195
195
196 def filterchunks(ui, originalhunks, usecurses, testfile, operation=None):
196 def filterchunks(ui, originalhunks, usecurses, testfile, operation=None):
197 if usecurses:
197 if usecurses:
198 if testfile:
198 if testfile:
199 recordfn = crecordmod.testdecorator(testfile,
199 recordfn = crecordmod.testdecorator(testfile,
200 crecordmod.testchunkselector)
200 crecordmod.testchunkselector)
201 else:
201 else:
202 recordfn = crecordmod.chunkselector
202 recordfn = crecordmod.chunkselector
203
203
204 return crecordmod.filterpatch(ui, originalhunks, recordfn, operation)
204 return crecordmod.filterpatch(ui, originalhunks, recordfn, operation)
205
205
206 else:
206 else:
207 return patch.filterpatch(ui, originalhunks, operation)
207 return patch.filterpatch(ui, originalhunks, operation)
208
208
209 def recordfilter(ui, originalhunks, operation=None):
209 def recordfilter(ui, originalhunks, operation=None):
210 """ Prompts the user to filter the originalhunks and return a list of
210 """ Prompts the user to filter the originalhunks and return a list of
211 selected hunks.
211 selected hunks.
212 *operation* is used for to build ui messages to indicate the user what
212 *operation* is used for to build ui messages to indicate the user what
213 kind of filtering they are doing: reverting, committing, shelving, etc.
213 kind of filtering they are doing: reverting, committing, shelving, etc.
214 (see patch.filterpatch).
214 (see patch.filterpatch).
215 """
215 """
216 usecurses = crecordmod.checkcurses(ui)
216 usecurses = crecordmod.checkcurses(ui)
217 testfile = ui.config('experimental', 'crecordtest')
217 testfile = ui.config('experimental', 'crecordtest')
218 oldwrite = setupwrapcolorwrite(ui)
218 oldwrite = setupwrapcolorwrite(ui)
219 try:
219 try:
220 newchunks, newopts = filterchunks(ui, originalhunks, usecurses,
220 newchunks, newopts = filterchunks(ui, originalhunks, usecurses,
221 testfile, operation)
221 testfile, operation)
222 finally:
222 finally:
223 ui.write = oldwrite
223 ui.write = oldwrite
224 return newchunks, newopts
224 return newchunks, newopts
225
225
226 def dorecord(ui, repo, commitfunc, cmdsuggest, backupall,
226 def dorecord(ui, repo, commitfunc, cmdsuggest, backupall,
227 filterfn, *pats, **opts):
227 filterfn, *pats, **opts):
228 from . import merge as mergemod
228 from . import merge as mergemod
229 opts = pycompat.byteskwargs(opts)
229 opts = pycompat.byteskwargs(opts)
230 if not ui.interactive():
230 if not ui.interactive():
231 if cmdsuggest:
231 if cmdsuggest:
232 msg = _('running non-interactively, use %s instead') % cmdsuggest
232 msg = _('running non-interactively, use %s instead') % cmdsuggest
233 else:
233 else:
234 msg = _('running non-interactively')
234 msg = _('running non-interactively')
235 raise error.Abort(msg)
235 raise error.Abort(msg)
236
236
237 # make sure username is set before going interactive
237 # make sure username is set before going interactive
238 if not opts.get('user'):
238 if not opts.get('user'):
239 ui.username() # raise exception, username not provided
239 ui.username() # raise exception, username not provided
240
240
241 def recordfunc(ui, repo, message, match, opts):
241 def recordfunc(ui, repo, message, match, opts):
242 """This is generic record driver.
242 """This is generic record driver.
243
243
244 Its job is to interactively filter local changes, and
244 Its job is to interactively filter local changes, and
245 accordingly prepare working directory into a state in which the
245 accordingly prepare working directory into a state in which the
246 job can be delegated to a non-interactive commit command such as
246 job can be delegated to a non-interactive commit command such as
247 'commit' or 'qrefresh'.
247 'commit' or 'qrefresh'.
248
248
249 After the actual job is done by non-interactive command, the
249 After the actual job is done by non-interactive command, the
250 working directory is restored to its original state.
250 working directory is restored to its original state.
251
251
252 In the end we'll record interesting changes, and everything else
252 In the end we'll record interesting changes, and everything else
253 will be left in place, so the user can continue working.
253 will be left in place, so the user can continue working.
254 """
254 """
255
255
256 checkunfinished(repo, commit=True)
256 checkunfinished(repo, commit=True)
257 wctx = repo[None]
257 wctx = repo[None]
258 merge = len(wctx.parents()) > 1
258 merge = len(wctx.parents()) > 1
259 if merge:
259 if merge:
260 raise error.Abort(_('cannot partially commit a merge '
260 raise error.Abort(_('cannot partially commit a merge '
261 '(use "hg commit" instead)'))
261 '(use "hg commit" instead)'))
262
262
263 def fail(f, msg):
263 def fail(f, msg):
264 raise error.Abort('%s: %s' % (f, msg))
264 raise error.Abort('%s: %s' % (f, msg))
265
265
266 force = opts.get('force')
266 force = opts.get('force')
267 if not force:
267 if not force:
268 vdirs = []
268 vdirs = []
269 match.explicitdir = vdirs.append
269 match.explicitdir = vdirs.append
270 match.bad = fail
270 match.bad = fail
271
271
272 status = repo.status(match=match)
272 status = repo.status(match=match)
273 if not force:
273 if not force:
274 repo.checkcommitpatterns(wctx, vdirs, match, status, fail)
274 repo.checkcommitpatterns(wctx, vdirs, match, status, fail)
275 diffopts = patch.difffeatureopts(ui, opts=opts, whitespace=True)
275 diffopts = patch.difffeatureopts(ui, opts=opts, whitespace=True)
276 diffopts.nodates = True
276 diffopts.nodates = True
277 diffopts.git = True
277 diffopts.git = True
278 diffopts.showfunc = True
278 diffopts.showfunc = True
279 originaldiff = patch.diff(repo, changes=status, opts=diffopts)
279 originaldiff = patch.diff(repo, changes=status, opts=diffopts)
280 originalchunks = patch.parsepatch(originaldiff)
280 originalchunks = patch.parsepatch(originaldiff)
281
281
282 # 1. filter patch, since we are intending to apply subset of it
282 # 1. filter patch, since we are intending to apply subset of it
283 try:
283 try:
284 chunks, newopts = filterfn(ui, originalchunks)
284 chunks, newopts = filterfn(ui, originalchunks)
285 except error.PatchError as err:
285 except error.PatchError as err:
286 raise error.Abort(_('error parsing patch: %s') % err)
286 raise error.Abort(_('error parsing patch: %s') % err)
287 opts.update(newopts)
287 opts.update(newopts)
288
288
289 # We need to keep a backup of files that have been newly added and
289 # We need to keep a backup of files that have been newly added and
290 # modified during the recording process because there is a previous
290 # modified during the recording process because there is a previous
291 # version without the edit in the workdir
291 # version without the edit in the workdir
292 newlyaddedandmodifiedfiles = newandmodified(chunks, originalchunks)
292 newlyaddedandmodifiedfiles = newandmodified(chunks, originalchunks)
293 contenders = set()
293 contenders = set()
294 for h in chunks:
294 for h in chunks:
295 try:
295 try:
296 contenders.update(set(h.files()))
296 contenders.update(set(h.files()))
297 except AttributeError:
297 except AttributeError:
298 pass
298 pass
299
299
300 changed = status.modified + status.added + status.removed
300 changed = status.modified + status.added + status.removed
301 newfiles = [f for f in changed if f in contenders]
301 newfiles = [f for f in changed if f in contenders]
302 if not newfiles:
302 if not newfiles:
303 ui.status(_('no changes to record\n'))
303 ui.status(_('no changes to record\n'))
304 return 0
304 return 0
305
305
306 modified = set(status.modified)
306 modified = set(status.modified)
307
307
308 # 2. backup changed files, so we can restore them in the end
308 # 2. backup changed files, so we can restore them in the end
309
309
310 if backupall:
310 if backupall:
311 tobackup = changed
311 tobackup = changed
312 else:
312 else:
313 tobackup = [f for f in newfiles if f in modified or f in \
313 tobackup = [f for f in newfiles if f in modified or f in \
314 newlyaddedandmodifiedfiles]
314 newlyaddedandmodifiedfiles]
315 backups = {}
315 backups = {}
316 if tobackup:
316 if tobackup:
317 backupdir = repo.vfs.join('record-backups')
317 backupdir = repo.vfs.join('record-backups')
318 try:
318 try:
319 os.mkdir(backupdir)
319 os.mkdir(backupdir)
320 except OSError as err:
320 except OSError as err:
321 if err.errno != errno.EEXIST:
321 if err.errno != errno.EEXIST:
322 raise
322 raise
323 try:
323 try:
324 # backup continues
324 # backup continues
325 for f in tobackup:
325 for f in tobackup:
326 fd, tmpname = tempfile.mkstemp(prefix=f.replace('/', '_')+'.',
326 fd, tmpname = tempfile.mkstemp(prefix=f.replace('/', '_')+'.',
327 dir=backupdir)
327 dir=backupdir)
328 os.close(fd)
328 os.close(fd)
329 ui.debug('backup %r as %r\n' % (f, tmpname))
329 ui.debug('backup %r as %r\n' % (f, tmpname))
330 util.copyfile(repo.wjoin(f), tmpname, copystat=True)
330 util.copyfile(repo.wjoin(f), tmpname, copystat=True)
331 backups[f] = tmpname
331 backups[f] = tmpname
332
332
333 fp = stringio()
333 fp = stringio()
334 for c in chunks:
334 for c in chunks:
335 fname = c.filename()
335 fname = c.filename()
336 if fname in backups:
336 if fname in backups:
337 c.write(fp)
337 c.write(fp)
338 dopatch = fp.tell()
338 dopatch = fp.tell()
339 fp.seek(0)
339 fp.seek(0)
340
340
341 # 2.5 optionally review / modify patch in text editor
341 # 2.5 optionally review / modify patch in text editor
342 if opts.get('review', False):
342 if opts.get('review', False):
343 patchtext = (crecordmod.diffhelptext
343 patchtext = (crecordmod.diffhelptext
344 + crecordmod.patchhelptext
344 + crecordmod.patchhelptext
345 + fp.read())
345 + fp.read())
346 reviewedpatch = ui.edit(patchtext, "",
346 reviewedpatch = ui.edit(patchtext, "",
347 action="diff",
347 action="diff",
348 repopath=repo.path)
348 repopath=repo.path)
349 fp.truncate(0)
349 fp.truncate(0)
350 fp.write(reviewedpatch)
350 fp.write(reviewedpatch)
351 fp.seek(0)
351 fp.seek(0)
352
352
353 [os.unlink(repo.wjoin(c)) for c in newlyaddedandmodifiedfiles]
353 [os.unlink(repo.wjoin(c)) for c in newlyaddedandmodifiedfiles]
354 # 3a. apply filtered patch to clean repo (clean)
354 # 3a. apply filtered patch to clean repo (clean)
355 if backups:
355 if backups:
356 # Equivalent to hg.revert
356 # Equivalent to hg.revert
357 m = scmutil.matchfiles(repo, backups.keys())
357 m = scmutil.matchfiles(repo, backups.keys())
358 mergemod.update(repo, repo.dirstate.p1(),
358 mergemod.update(repo, repo.dirstate.p1(),
359 False, True, matcher=m)
359 False, True, matcher=m)
360
360
361 # 3b. (apply)
361 # 3b. (apply)
362 if dopatch:
362 if dopatch:
363 try:
363 try:
364 ui.debug('applying patch\n')
364 ui.debug('applying patch\n')
365 ui.debug(fp.getvalue())
365 ui.debug(fp.getvalue())
366 patch.internalpatch(ui, repo, fp, 1, eolmode=None)
366 patch.internalpatch(ui, repo, fp, 1, eolmode=None)
367 except error.PatchError as err:
367 except error.PatchError as err:
368 raise error.Abort(str(err))
368 raise error.Abort(str(err))
369 del fp
369 del fp
370
370
371 # 4. We prepared working directory according to filtered
371 # 4. We prepared working directory according to filtered
372 # patch. Now is the time to delegate the job to
372 # patch. Now is the time to delegate the job to
373 # commit/qrefresh or the like!
373 # commit/qrefresh or the like!
374
374
375 # Make all of the pathnames absolute.
375 # Make all of the pathnames absolute.
376 newfiles = [repo.wjoin(nf) for nf in newfiles]
376 newfiles = [repo.wjoin(nf) for nf in newfiles]
377 return commitfunc(ui, repo, *newfiles, **pycompat.strkwargs(opts))
377 return commitfunc(ui, repo, *newfiles, **pycompat.strkwargs(opts))
378 finally:
378 finally:
379 # 5. finally restore backed-up files
379 # 5. finally restore backed-up files
380 try:
380 try:
381 dirstate = repo.dirstate
381 dirstate = repo.dirstate
382 for realname, tmpname in backups.iteritems():
382 for realname, tmpname in backups.iteritems():
383 ui.debug('restoring %r to %r\n' % (tmpname, realname))
383 ui.debug('restoring %r to %r\n' % (tmpname, realname))
384
384
385 if dirstate[realname] == 'n':
385 if dirstate[realname] == 'n':
386 # without normallookup, restoring timestamp
386 # without normallookup, restoring timestamp
387 # may cause partially committed files
387 # may cause partially committed files
388 # to be treated as unmodified
388 # to be treated as unmodified
389 dirstate.normallookup(realname)
389 dirstate.normallookup(realname)
390
390
391 # copystat=True here and above are a hack to trick any
391 # copystat=True here and above are a hack to trick any
392 # editors that have f open that we haven't modified them.
392 # editors that have f open that we haven't modified them.
393 #
393 #
394 # Also note that this racy as an editor could notice the
394 # Also note that this racy as an editor could notice the
395 # file's mtime before we've finished writing it.
395 # file's mtime before we've finished writing it.
396 util.copyfile(tmpname, repo.wjoin(realname), copystat=True)
396 util.copyfile(tmpname, repo.wjoin(realname), copystat=True)
397 os.unlink(tmpname)
397 os.unlink(tmpname)
398 if tobackup:
398 if tobackup:
399 os.rmdir(backupdir)
399 os.rmdir(backupdir)
400 except OSError:
400 except OSError:
401 pass
401 pass
402
402
403 def recordinwlock(ui, repo, message, match, opts):
403 def recordinwlock(ui, repo, message, match, opts):
404 with repo.wlock():
404 with repo.wlock():
405 return recordfunc(ui, repo, message, match, opts)
405 return recordfunc(ui, repo, message, match, opts)
406
406
407 return commit(ui, repo, recordinwlock, pats, opts)
407 return commit(ui, repo, recordinwlock, pats, opts)
408
408
409 class dirnode(object):
409 class dirnode(object):
410 """
410 """
411 Represent a directory in user working copy with information required for
411 Represent a directory in user working copy with information required for
412 the purpose of tersing its status.
412 the purpose of tersing its status.
413
413
414 path is the path to the directory
414 path is the path to the directory
415
415
416 statuses is a set of statuses of all files in this directory (this includes
416 statuses is a set of statuses of all files in this directory (this includes
417 all the files in all the subdirectories too)
417 all the files in all the subdirectories too)
418
418
419 files is a list of files which are direct child of this directory
419 files is a list of files which are direct child of this directory
420
420
421 subdirs is a dictionary of sub-directory name as the key and it's own
421 subdirs is a dictionary of sub-directory name as the key and it's own
422 dirnode object as the value
422 dirnode object as the value
423 """
423 """
424
424
425 def __init__(self, dirpath):
425 def __init__(self, dirpath):
426 self.path = dirpath
426 self.path = dirpath
427 self.statuses = set([])
427 self.statuses = set([])
428 self.files = []
428 self.files = []
429 self.subdirs = {}
429 self.subdirs = {}
430
430
431 def _addfileindir(self, filename, status):
431 def _addfileindir(self, filename, status):
432 """Add a file in this directory as a direct child."""
432 """Add a file in this directory as a direct child."""
433 self.files.append((filename, status))
433 self.files.append((filename, status))
434
434
435 def addfile(self, filename, status):
435 def addfile(self, filename, status):
436 """
436 """
437 Add a file to this directory or to its direct parent directory.
437 Add a file to this directory or to its direct parent directory.
438
438
439 If the file is not direct child of this directory, we traverse to the
439 If the file is not direct child of this directory, we traverse to the
440 directory of which this file is a direct child of and add the file
440 directory of which this file is a direct child of and add the file
441 there.
441 there.
442 """
442 """
443
443
444 # the filename contains a path separator, it means it's not the direct
444 # the filename contains a path separator, it means it's not the direct
445 # child of this directory
445 # child of this directory
446 if '/' in filename:
446 if '/' in filename:
447 subdir, filep = filename.split('/', 1)
447 subdir, filep = filename.split('/', 1)
448
448
449 # does the dirnode object for subdir exists
449 # does the dirnode object for subdir exists
450 if subdir not in self.subdirs:
450 if subdir not in self.subdirs:
451 subdirpath = os.path.join(self.path, subdir)
451 subdirpath = os.path.join(self.path, subdir)
452 self.subdirs[subdir] = dirnode(subdirpath)
452 self.subdirs[subdir] = dirnode(subdirpath)
453
453
454 # try adding the file in subdir
454 # try adding the file in subdir
455 self.subdirs[subdir].addfile(filep, status)
455 self.subdirs[subdir].addfile(filep, status)
456
456
457 else:
457 else:
458 self._addfileindir(filename, status)
458 self._addfileindir(filename, status)
459
459
460 if status not in self.statuses:
460 if status not in self.statuses:
461 self.statuses.add(status)
461 self.statuses.add(status)
462
462
463 def iterfilepaths(self):
463 def iterfilepaths(self):
464 """Yield (status, path) for files directly under this directory."""
464 """Yield (status, path) for files directly under this directory."""
465 for f, st in self.files:
465 for f, st in self.files:
466 yield st, os.path.join(self.path, f)
466 yield st, os.path.join(self.path, f)
467
467
468 def tersewalk(self, terseargs):
468 def tersewalk(self, terseargs):
469 """
469 """
470 Yield (status, path) obtained by processing the status of this
470 Yield (status, path) obtained by processing the status of this
471 dirnode.
471 dirnode.
472
472
473 terseargs is the string of arguments passed by the user with `--terse`
473 terseargs is the string of arguments passed by the user with `--terse`
474 flag.
474 flag.
475
475
476 Following are the cases which can happen:
476 Following are the cases which can happen:
477
477
478 1) All the files in the directory (including all the files in its
478 1) All the files in the directory (including all the files in its
479 subdirectories) share the same status and the user has asked us to terse
479 subdirectories) share the same status and the user has asked us to terse
480 that status. -> yield (status, dirpath)
480 that status. -> yield (status, dirpath)
481
481
482 2) Otherwise, we do following:
482 2) Otherwise, we do following:
483
483
484 a) Yield (status, filepath) for all the files which are in this
484 a) Yield (status, filepath) for all the files which are in this
485 directory (only the ones in this directory, not the subdirs)
485 directory (only the ones in this directory, not the subdirs)
486
486
487 b) Recurse the function on all the subdirectories of this
487 b) Recurse the function on all the subdirectories of this
488 directory
488 directory
489 """
489 """
490
490
491 if len(self.statuses) == 1:
491 if len(self.statuses) == 1:
492 onlyst = self.statuses.pop()
492 onlyst = self.statuses.pop()
493
493
494 # Making sure we terse only when the status abbreviation is
494 # Making sure we terse only when the status abbreviation is
495 # passed as terse argument
495 # passed as terse argument
496 if onlyst in terseargs:
496 if onlyst in terseargs:
497 yield onlyst, self.path + pycompat.ossep
497 yield onlyst, self.path + pycompat.ossep
498 return
498 return
499
499
500 # add the files to status list
500 # add the files to status list
501 for st, fpath in self.iterfilepaths():
501 for st, fpath in self.iterfilepaths():
502 yield st, fpath
502 yield st, fpath
503
503
504 #recurse on the subdirs
504 #recurse on the subdirs
505 for dirobj in self.subdirs.values():
505 for dirobj in self.subdirs.values():
506 for st, fpath in dirobj.tersewalk(terseargs):
506 for st, fpath in dirobj.tersewalk(terseargs):
507 yield st, fpath
507 yield st, fpath
508
508
509 def tersedir(statuslist, terseargs):
509 def tersedir(statuslist, terseargs):
510 """
510 """
511 Terse the status if all the files in a directory shares the same status.
511 Terse the status if all the files in a directory shares the same status.
512
512
513 statuslist is scmutil.status() object which contains a list of files for
513 statuslist is scmutil.status() object which contains a list of files for
514 each status.
514 each status.
515 terseargs is string which is passed by the user as the argument to `--terse`
515 terseargs is string which is passed by the user as the argument to `--terse`
516 flag.
516 flag.
517
517
518 The function makes a tree of objects of dirnode class, and at each node it
518 The function makes a tree of objects of dirnode class, and at each node it
519 stores the information required to know whether we can terse a certain
519 stores the information required to know whether we can terse a certain
520 directory or not.
520 directory or not.
521 """
521 """
522 # the order matters here as that is used to produce final list
522 # the order matters here as that is used to produce final list
523 allst = ('m', 'a', 'r', 'd', 'u', 'i', 'c')
523 allst = ('m', 'a', 'r', 'd', 'u', 'i', 'c')
524
524
525 # checking the argument validity
525 # checking the argument validity
526 for s in pycompat.bytestr(terseargs):
526 for s in pycompat.bytestr(terseargs):
527 if s not in allst:
527 if s not in allst:
528 raise error.Abort(_("'%s' not recognized") % s)
528 raise error.Abort(_("'%s' not recognized") % s)
529
529
530 # creating a dirnode object for the root of the repo
530 # creating a dirnode object for the root of the repo
531 rootobj = dirnode('')
531 rootobj = dirnode('')
532 pstatus = ('modified', 'added', 'deleted', 'clean', 'unknown',
532 pstatus = ('modified', 'added', 'deleted', 'clean', 'unknown',
533 'ignored', 'removed')
533 'ignored', 'removed')
534
534
535 tersedict = {}
535 tersedict = {}
536 for attrname in pstatus:
536 for attrname in pstatus:
537 statuschar = attrname[0:1]
537 statuschar = attrname[0:1]
538 for f in getattr(statuslist, attrname):
538 for f in getattr(statuslist, attrname):
539 rootobj.addfile(f, statuschar)
539 rootobj.addfile(f, statuschar)
540 tersedict[statuschar] = []
540 tersedict[statuschar] = []
541
541
542 # we won't be tersing the root dir, so add files in it
542 # we won't be tersing the root dir, so add files in it
543 for st, fpath in rootobj.iterfilepaths():
543 for st, fpath in rootobj.iterfilepaths():
544 tersedict[st].append(fpath)
544 tersedict[st].append(fpath)
545
545
546 # process each sub-directory and build tersedict
546 # process each sub-directory and build tersedict
547 for subdir in rootobj.subdirs.values():
547 for subdir in rootobj.subdirs.values():
548 for st, f in subdir.tersewalk(terseargs):
548 for st, f in subdir.tersewalk(terseargs):
549 tersedict[st].append(f)
549 tersedict[st].append(f)
550
550
551 tersedlist = []
551 tersedlist = []
552 for st in allst:
552 for st in allst:
553 tersedict[st].sort()
553 tersedict[st].sort()
554 tersedlist.append(tersedict[st])
554 tersedlist.append(tersedict[st])
555
555
556 return tersedlist
556 return tersedlist
557
557
558 def _commentlines(raw):
558 def _commentlines(raw):
559 '''Surround lineswith a comment char and a new line'''
559 '''Surround lineswith a comment char and a new line'''
560 lines = raw.splitlines()
560 lines = raw.splitlines()
561 commentedlines = ['# %s' % line for line in lines]
561 commentedlines = ['# %s' % line for line in lines]
562 return '\n'.join(commentedlines) + '\n'
562 return '\n'.join(commentedlines) + '\n'
563
563
564 def _conflictsmsg(repo):
564 def _conflictsmsg(repo):
565 # avoid merge cycle
565 # avoid merge cycle
566 from . import merge as mergemod
566 from . import merge as mergemod
567 mergestate = mergemod.mergestate.read(repo)
567 mergestate = mergemod.mergestate.read(repo)
568 if not mergestate.active():
568 if not mergestate.active():
569 return
569 return
570
570
571 m = scmutil.match(repo[None])
571 m = scmutil.match(repo[None])
572 unresolvedlist = [f for f in mergestate.unresolved() if m(f)]
572 unresolvedlist = [f for f in mergestate.unresolved() if m(f)]
573 if unresolvedlist:
573 if unresolvedlist:
574 mergeliststr = '\n'.join(
574 mergeliststr = '\n'.join(
575 [' %s' % util.pathto(repo.root, pycompat.getcwd(), path)
575 [' %s' % util.pathto(repo.root, pycompat.getcwd(), path)
576 for path in unresolvedlist])
576 for path in unresolvedlist])
577 msg = _('''Unresolved merge conflicts:
577 msg = _('''Unresolved merge conflicts:
578
578
579 %s
579 %s
580
580
581 To mark files as resolved: hg resolve --mark FILE''') % mergeliststr
581 To mark files as resolved: hg resolve --mark FILE''') % mergeliststr
582 else:
582 else:
583 msg = _('No unresolved merge conflicts.')
583 msg = _('No unresolved merge conflicts.')
584
584
585 return _commentlines(msg)
585 return _commentlines(msg)
586
586
587 def _helpmessage(continuecmd, abortcmd):
587 def _helpmessage(continuecmd, abortcmd):
588 msg = _('To continue: %s\n'
588 msg = _('To continue: %s\n'
589 'To abort: %s') % (continuecmd, abortcmd)
589 'To abort: %s') % (continuecmd, abortcmd)
590 return _commentlines(msg)
590 return _commentlines(msg)
591
591
592 def _rebasemsg():
592 def _rebasemsg():
593 return _helpmessage('hg rebase --continue', 'hg rebase --abort')
593 return _helpmessage('hg rebase --continue', 'hg rebase --abort')
594
594
595 def _histeditmsg():
595 def _histeditmsg():
596 return _helpmessage('hg histedit --continue', 'hg histedit --abort')
596 return _helpmessage('hg histedit --continue', 'hg histedit --abort')
597
597
598 def _unshelvemsg():
598 def _unshelvemsg():
599 return _helpmessage('hg unshelve --continue', 'hg unshelve --abort')
599 return _helpmessage('hg unshelve --continue', 'hg unshelve --abort')
600
600
601 def _updatecleanmsg(dest=None):
601 def _updatecleanmsg(dest=None):
602 warning = _('warning: this will discard uncommitted changes')
602 warning = _('warning: this will discard uncommitted changes')
603 return 'hg update --clean %s (%s)' % (dest or '.', warning)
603 return 'hg update --clean %s (%s)' % (dest or '.', warning)
604
604
605 def _graftmsg():
605 def _graftmsg():
606 # tweakdefaults requires `update` to have a rev hence the `.`
606 # tweakdefaults requires `update` to have a rev hence the `.`
607 return _helpmessage('hg graft --continue', _updatecleanmsg())
607 return _helpmessage('hg graft --continue', _updatecleanmsg())
608
608
609 def _mergemsg():
609 def _mergemsg():
610 # tweakdefaults requires `update` to have a rev hence the `.`
610 # tweakdefaults requires `update` to have a rev hence the `.`
611 return _helpmessage('hg commit', _updatecleanmsg())
611 return _helpmessage('hg commit', _updatecleanmsg())
612
612
613 def _bisectmsg():
613 def _bisectmsg():
614 msg = _('To mark the changeset good: hg bisect --good\n'
614 msg = _('To mark the changeset good: hg bisect --good\n'
615 'To mark the changeset bad: hg bisect --bad\n'
615 'To mark the changeset bad: hg bisect --bad\n'
616 'To abort: hg bisect --reset\n')
616 'To abort: hg bisect --reset\n')
617 return _commentlines(msg)
617 return _commentlines(msg)
618
618
619 def fileexistspredicate(filename):
619 def fileexistspredicate(filename):
620 return lambda repo: repo.vfs.exists(filename)
620 return lambda repo: repo.vfs.exists(filename)
621
621
622 def _mergepredicate(repo):
622 def _mergepredicate(repo):
623 return len(repo[None].parents()) > 1
623 return len(repo[None].parents()) > 1
624
624
625 STATES = (
625 STATES = (
626 # (state, predicate to detect states, helpful message function)
626 # (state, predicate to detect states, helpful message function)
627 ('histedit', fileexistspredicate('histedit-state'), _histeditmsg),
627 ('histedit', fileexistspredicate('histedit-state'), _histeditmsg),
628 ('bisect', fileexistspredicate('bisect.state'), _bisectmsg),
628 ('bisect', fileexistspredicate('bisect.state'), _bisectmsg),
629 ('graft', fileexistspredicate('graftstate'), _graftmsg),
629 ('graft', fileexistspredicate('graftstate'), _graftmsg),
630 ('unshelve', fileexistspredicate('unshelverebasestate'), _unshelvemsg),
630 ('unshelve', fileexistspredicate('unshelverebasestate'), _unshelvemsg),
631 ('rebase', fileexistspredicate('rebasestate'), _rebasemsg),
631 ('rebase', fileexistspredicate('rebasestate'), _rebasemsg),
632 # The merge state is part of a list that will be iterated over.
632 # The merge state is part of a list that will be iterated over.
633 # They need to be last because some of the other unfinished states may also
633 # They need to be last because some of the other unfinished states may also
634 # be in a merge or update state (eg. rebase, histedit, graft, etc).
634 # be in a merge or update state (eg. rebase, histedit, graft, etc).
635 # We want those to have priority.
635 # We want those to have priority.
636 ('merge', _mergepredicate, _mergemsg),
636 ('merge', _mergepredicate, _mergemsg),
637 )
637 )
638
638
639 def _getrepostate(repo):
639 def _getrepostate(repo):
640 # experimental config: commands.status.skipstates
640 # experimental config: commands.status.skipstates
641 skip = set(repo.ui.configlist('commands', 'status.skipstates'))
641 skip = set(repo.ui.configlist('commands', 'status.skipstates'))
642 for state, statedetectionpredicate, msgfn in STATES:
642 for state, statedetectionpredicate, msgfn in STATES:
643 if state in skip:
643 if state in skip:
644 continue
644 continue
645 if statedetectionpredicate(repo):
645 if statedetectionpredicate(repo):
646 return (state, statedetectionpredicate, msgfn)
646 return (state, statedetectionpredicate, msgfn)
647
647
648 def morestatus(repo, fm):
648 def morestatus(repo, fm):
649 statetuple = _getrepostate(repo)
649 statetuple = _getrepostate(repo)
650 label = 'status.morestatus'
650 label = 'status.morestatus'
651 if statetuple:
651 if statetuple:
652 fm.startitem()
652 fm.startitem()
653 state, statedetectionpredicate, helpfulmsg = statetuple
653 state, statedetectionpredicate, helpfulmsg = statetuple
654 statemsg = _('The repository is in an unfinished *%s* state.') % state
654 statemsg = _('The repository is in an unfinished *%s* state.') % state
655 fm.write('statemsg', '%s\n', _commentlines(statemsg), label=label)
655 fm.write('statemsg', '%s\n', _commentlines(statemsg), label=label)
656 conmsg = _conflictsmsg(repo)
656 conmsg = _conflictsmsg(repo)
657 if conmsg:
657 if conmsg:
658 fm.write('conflictsmsg', '%s\n', conmsg, label=label)
658 fm.write('conflictsmsg', '%s\n', conmsg, label=label)
659 if helpfulmsg:
659 if helpfulmsg:
660 helpmsg = helpfulmsg()
660 helpmsg = helpfulmsg()
661 fm.write('helpmsg', '%s\n', helpmsg, label=label)
661 fm.write('helpmsg', '%s\n', helpmsg, label=label)
662
662
663 def findpossible(cmd, table, strict=False):
663 def findpossible(cmd, table, strict=False):
664 """
664 """
665 Return cmd -> (aliases, command table entry)
665 Return cmd -> (aliases, command table entry)
666 for each matching command.
666 for each matching command.
667 Return debug commands (or their aliases) only if no normal command matches.
667 Return debug commands (or their aliases) only if no normal command matches.
668 """
668 """
669 choice = {}
669 choice = {}
670 debugchoice = {}
670 debugchoice = {}
671
671
672 if cmd in table:
672 if cmd in table:
673 # short-circuit exact matches, "log" alias beats "^log|history"
673 # short-circuit exact matches, "log" alias beats "^log|history"
674 keys = [cmd]
674 keys = [cmd]
675 else:
675 else:
676 keys = table.keys()
676 keys = table.keys()
677
677
678 allcmds = []
678 allcmds = []
679 for e in keys:
679 for e in keys:
680 aliases = parsealiases(e)
680 aliases = parsealiases(e)
681 allcmds.extend(aliases)
681 allcmds.extend(aliases)
682 found = None
682 found = None
683 if cmd in aliases:
683 if cmd in aliases:
684 found = cmd
684 found = cmd
685 elif not strict:
685 elif not strict:
686 for a in aliases:
686 for a in aliases:
687 if a.startswith(cmd):
687 if a.startswith(cmd):
688 found = a
688 found = a
689 break
689 break
690 if found is not None:
690 if found is not None:
691 if aliases[0].startswith("debug") or found.startswith("debug"):
691 if aliases[0].startswith("debug") or found.startswith("debug"):
692 debugchoice[found] = (aliases, table[e])
692 debugchoice[found] = (aliases, table[e])
693 else:
693 else:
694 choice[found] = (aliases, table[e])
694 choice[found] = (aliases, table[e])
695
695
696 if not choice and debugchoice:
696 if not choice and debugchoice:
697 choice = debugchoice
697 choice = debugchoice
698
698
699 return choice, allcmds
699 return choice, allcmds
700
700
701 def findcmd(cmd, table, strict=True):
701 def findcmd(cmd, table, strict=True):
702 """Return (aliases, command table entry) for command string."""
702 """Return (aliases, command table entry) for command string."""
703 choice, allcmds = findpossible(cmd, table, strict)
703 choice, allcmds = findpossible(cmd, table, strict)
704
704
705 if cmd in choice:
705 if cmd in choice:
706 return choice[cmd]
706 return choice[cmd]
707
707
708 if len(choice) > 1:
708 if len(choice) > 1:
709 clist = sorted(choice)
709 clist = sorted(choice)
710 raise error.AmbiguousCommand(cmd, clist)
710 raise error.AmbiguousCommand(cmd, clist)
711
711
712 if choice:
712 if choice:
713 return list(choice.values())[0]
713 return list(choice.values())[0]
714
714
715 raise error.UnknownCommand(cmd, allcmds)
715 raise error.UnknownCommand(cmd, allcmds)
716
716
717 def changebranch(ui, repo, revs, label):
717 def changebranch(ui, repo, revs, label):
718 """ Change the branch name of given revs to label """
718 """ Change the branch name of given revs to label """
719
719
720 with repo.wlock(), repo.lock(), repo.transaction('branches'):
720 with repo.wlock(), repo.lock(), repo.transaction('branches'):
721 # abort in case of uncommitted merge or dirty wdir
721 # abort in case of uncommitted merge or dirty wdir
722 bailifchanged(repo)
722 bailifchanged(repo)
723 revs = scmutil.revrange(repo, revs)
723 revs = scmutil.revrange(repo, revs)
724 if not revs:
724 if not revs:
725 raise error.Abort("empty revision set")
725 raise error.Abort("empty revision set")
726 roots = repo.revs('roots(%ld)', revs)
726 roots = repo.revs('roots(%ld)', revs)
727 if len(roots) > 1:
727 if len(roots) > 1:
728 raise error.Abort(_("cannot change branch of non-linear revisions"))
728 raise error.Abort(_("cannot change branch of non-linear revisions"))
729 rewriteutil.precheck(repo, revs, 'change branch of')
729 rewriteutil.precheck(repo, revs, 'change branch of')
730
731 root = repo[roots.first()]
732 if not root.p1().branch() == label and label in repo.branchmap():
733 raise error.Abort(_("a branch of the same name already exists"))
734
730 if repo.revs('merge() and %ld', revs):
735 if repo.revs('merge() and %ld', revs):
731 raise error.Abort(_("cannot change branch of a merge commit"))
736 raise error.Abort(_("cannot change branch of a merge commit"))
732 if repo.revs('obsolete() and %ld', revs):
737 if repo.revs('obsolete() and %ld', revs):
733 raise error.Abort(_("cannot change branch of a obsolete changeset"))
738 raise error.Abort(_("cannot change branch of a obsolete changeset"))
734
739
735 # make sure only topological heads
740 # make sure only topological heads
736 if repo.revs('heads(%ld) - head()', revs):
741 if repo.revs('heads(%ld) - head()', revs):
737 raise error.Abort(_("cannot change branch in middle of a stack"))
742 raise error.Abort(_("cannot change branch in middle of a stack"))
738
743
739 replacements = {}
744 replacements = {}
740 # avoid import cycle mercurial.cmdutil -> mercurial.context ->
745 # avoid import cycle mercurial.cmdutil -> mercurial.context ->
741 # mercurial.subrepo -> mercurial.cmdutil
746 # mercurial.subrepo -> mercurial.cmdutil
742 from . import context
747 from . import context
743 for rev in revs:
748 for rev in revs:
744 ctx = repo[rev]
749 ctx = repo[rev]
745 oldbranch = ctx.branch()
750 oldbranch = ctx.branch()
746 # check if ctx has same branch
751 # check if ctx has same branch
747 if oldbranch == label:
752 if oldbranch == label:
748 continue
753 continue
749
754
750 def filectxfn(repo, newctx, path):
755 def filectxfn(repo, newctx, path):
751 try:
756 try:
752 return ctx[path]
757 return ctx[path]
753 except error.ManifestLookupError:
758 except error.ManifestLookupError:
754 return None
759 return None
755
760
756 ui.debug("changing branch of '%s' from '%s' to '%s'\n"
761 ui.debug("changing branch of '%s' from '%s' to '%s'\n"
757 % (hex(ctx.node()), oldbranch, label))
762 % (hex(ctx.node()), oldbranch, label))
758 extra = ctx.extra()
763 extra = ctx.extra()
759 extra['branch_change'] = hex(ctx.node())
764 extra['branch_change'] = hex(ctx.node())
760 # While changing branch of set of linear commits, make sure that
765 # While changing branch of set of linear commits, make sure that
761 # we base our commits on new parent rather than old parent which
766 # we base our commits on new parent rather than old parent which
762 # was obsoleted while changing the branch
767 # was obsoleted while changing the branch
763 p1 = ctx.p1().node()
768 p1 = ctx.p1().node()
764 p2 = ctx.p2().node()
769 p2 = ctx.p2().node()
765 if p1 in replacements:
770 if p1 in replacements:
766 p1 = replacements[p1][0]
771 p1 = replacements[p1][0]
767 if p2 in replacements:
772 if p2 in replacements:
768 p2 = replacements[p2][0]
773 p2 = replacements[p2][0]
769
774
770 mc = context.memctx(repo, (p1, p2),
775 mc = context.memctx(repo, (p1, p2),
771 ctx.description(),
776 ctx.description(),
772 ctx.files(),
777 ctx.files(),
773 filectxfn,
778 filectxfn,
774 user=ctx.user(),
779 user=ctx.user(),
775 date=ctx.date(),
780 date=ctx.date(),
776 extra=extra,
781 extra=extra,
777 branch=label)
782 branch=label)
778
783
779 commitphase = ctx.phase()
784 commitphase = ctx.phase()
780 overrides = {('phases', 'new-commit'): commitphase}
785 overrides = {('phases', 'new-commit'): commitphase}
781 with repo.ui.configoverride(overrides, 'branch-change'):
786 with repo.ui.configoverride(overrides, 'branch-change'):
782 newnode = repo.commitctx(mc)
787 newnode = repo.commitctx(mc)
783
788
784 replacements[ctx.node()] = (newnode,)
789 replacements[ctx.node()] = (newnode,)
785 ui.debug('new node id is %s\n' % hex(newnode))
790 ui.debug('new node id is %s\n' % hex(newnode))
786
791
787 # create obsmarkers and move bookmarks
792 # create obsmarkers and move bookmarks
788 scmutil.cleanupnodes(repo, replacements, 'branch-change')
793 scmutil.cleanupnodes(repo, replacements, 'branch-change')
789
794
790 # move the working copy too
795 # move the working copy too
791 wctx = repo[None]
796 wctx = repo[None]
792 # in-progress merge is a bit too complex for now.
797 # in-progress merge is a bit too complex for now.
793 if len(wctx.parents()) == 1:
798 if len(wctx.parents()) == 1:
794 newid = replacements.get(wctx.p1().node())
799 newid = replacements.get(wctx.p1().node())
795 if newid is not None:
800 if newid is not None:
796 # avoid import cycle mercurial.cmdutil -> mercurial.hg ->
801 # avoid import cycle mercurial.cmdutil -> mercurial.hg ->
797 # mercurial.cmdutil
802 # mercurial.cmdutil
798 from . import hg
803 from . import hg
799 hg.update(repo, newid[0], quietempty=True)
804 hg.update(repo, newid[0], quietempty=True)
800
805
801 ui.status(_("changed branch on %d changesets\n") % len(replacements))
806 ui.status(_("changed branch on %d changesets\n") % len(replacements))
802
807
803 def findrepo(p):
808 def findrepo(p):
804 while not os.path.isdir(os.path.join(p, ".hg")):
809 while not os.path.isdir(os.path.join(p, ".hg")):
805 oldp, p = p, os.path.dirname(p)
810 oldp, p = p, os.path.dirname(p)
806 if p == oldp:
811 if p == oldp:
807 return None
812 return None
808
813
809 return p
814 return p
810
815
811 def bailifchanged(repo, merge=True, hint=None):
816 def bailifchanged(repo, merge=True, hint=None):
812 """ enforce the precondition that working directory must be clean.
817 """ enforce the precondition that working directory must be clean.
813
818
814 'merge' can be set to false if a pending uncommitted merge should be
819 'merge' can be set to false if a pending uncommitted merge should be
815 ignored (such as when 'update --check' runs).
820 ignored (such as when 'update --check' runs).
816
821
817 'hint' is the usual hint given to Abort exception.
822 'hint' is the usual hint given to Abort exception.
818 """
823 """
819
824
820 if merge and repo.dirstate.p2() != nullid:
825 if merge and repo.dirstate.p2() != nullid:
821 raise error.Abort(_('outstanding uncommitted merge'), hint=hint)
826 raise error.Abort(_('outstanding uncommitted merge'), hint=hint)
822 modified, added, removed, deleted = repo.status()[:4]
827 modified, added, removed, deleted = repo.status()[:4]
823 if modified or added or removed or deleted:
828 if modified or added or removed or deleted:
824 raise error.Abort(_('uncommitted changes'), hint=hint)
829 raise error.Abort(_('uncommitted changes'), hint=hint)
825 ctx = repo[None]
830 ctx = repo[None]
826 for s in sorted(ctx.substate):
831 for s in sorted(ctx.substate):
827 ctx.sub(s).bailifchanged(hint=hint)
832 ctx.sub(s).bailifchanged(hint=hint)
828
833
829 def logmessage(ui, opts):
834 def logmessage(ui, opts):
830 """ get the log message according to -m and -l option """
835 """ get the log message according to -m and -l option """
831 message = opts.get('message')
836 message = opts.get('message')
832 logfile = opts.get('logfile')
837 logfile = opts.get('logfile')
833
838
834 if message and logfile:
839 if message and logfile:
835 raise error.Abort(_('options --message and --logfile are mutually '
840 raise error.Abort(_('options --message and --logfile are mutually '
836 'exclusive'))
841 'exclusive'))
837 if not message and logfile:
842 if not message and logfile:
838 try:
843 try:
839 if isstdiofilename(logfile):
844 if isstdiofilename(logfile):
840 message = ui.fin.read()
845 message = ui.fin.read()
841 else:
846 else:
842 message = '\n'.join(util.readfile(logfile).splitlines())
847 message = '\n'.join(util.readfile(logfile).splitlines())
843 except IOError as inst:
848 except IOError as inst:
844 raise error.Abort(_("can't read commit message '%s': %s") %
849 raise error.Abort(_("can't read commit message '%s': %s") %
845 (logfile, encoding.strtolocal(inst.strerror)))
850 (logfile, encoding.strtolocal(inst.strerror)))
846 return message
851 return message
847
852
848 def mergeeditform(ctxorbool, baseformname):
853 def mergeeditform(ctxorbool, baseformname):
849 """return appropriate editform name (referencing a committemplate)
854 """return appropriate editform name (referencing a committemplate)
850
855
851 'ctxorbool' is either a ctx to be committed, or a bool indicating whether
856 'ctxorbool' is either a ctx to be committed, or a bool indicating whether
852 merging is committed.
857 merging is committed.
853
858
854 This returns baseformname with '.merge' appended if it is a merge,
859 This returns baseformname with '.merge' appended if it is a merge,
855 otherwise '.normal' is appended.
860 otherwise '.normal' is appended.
856 """
861 """
857 if isinstance(ctxorbool, bool):
862 if isinstance(ctxorbool, bool):
858 if ctxorbool:
863 if ctxorbool:
859 return baseformname + ".merge"
864 return baseformname + ".merge"
860 elif 1 < len(ctxorbool.parents()):
865 elif 1 < len(ctxorbool.parents()):
861 return baseformname + ".merge"
866 return baseformname + ".merge"
862
867
863 return baseformname + ".normal"
868 return baseformname + ".normal"
864
869
865 def getcommiteditor(edit=False, finishdesc=None, extramsg=None,
870 def getcommiteditor(edit=False, finishdesc=None, extramsg=None,
866 editform='', **opts):
871 editform='', **opts):
867 """get appropriate commit message editor according to '--edit' option
872 """get appropriate commit message editor according to '--edit' option
868
873
869 'finishdesc' is a function to be called with edited commit message
874 'finishdesc' is a function to be called with edited commit message
870 (= 'description' of the new changeset) just after editing, but
875 (= 'description' of the new changeset) just after editing, but
871 before checking empty-ness. It should return actual text to be
876 before checking empty-ness. It should return actual text to be
872 stored into history. This allows to change description before
877 stored into history. This allows to change description before
873 storing.
878 storing.
874
879
875 'extramsg' is a extra message to be shown in the editor instead of
880 'extramsg' is a extra message to be shown in the editor instead of
876 'Leave message empty to abort commit' line. 'HG: ' prefix and EOL
881 'Leave message empty to abort commit' line. 'HG: ' prefix and EOL
877 is automatically added.
882 is automatically added.
878
883
879 'editform' is a dot-separated list of names, to distinguish
884 'editform' is a dot-separated list of names, to distinguish
880 the purpose of commit text editing.
885 the purpose of commit text editing.
881
886
882 'getcommiteditor' returns 'commitforceeditor' regardless of
887 'getcommiteditor' returns 'commitforceeditor' regardless of
883 'edit', if one of 'finishdesc' or 'extramsg' is specified, because
888 'edit', if one of 'finishdesc' or 'extramsg' is specified, because
884 they are specific for usage in MQ.
889 they are specific for usage in MQ.
885 """
890 """
886 if edit or finishdesc or extramsg:
891 if edit or finishdesc or extramsg:
887 return lambda r, c, s: commitforceeditor(r, c, s,
892 return lambda r, c, s: commitforceeditor(r, c, s,
888 finishdesc=finishdesc,
893 finishdesc=finishdesc,
889 extramsg=extramsg,
894 extramsg=extramsg,
890 editform=editform)
895 editform=editform)
891 elif editform:
896 elif editform:
892 return lambda r, c, s: commiteditor(r, c, s, editform=editform)
897 return lambda r, c, s: commiteditor(r, c, s, editform=editform)
893 else:
898 else:
894 return commiteditor
899 return commiteditor
895
900
896 def loglimit(opts):
901 def loglimit(opts):
897 """get the log limit according to option -l/--limit"""
902 """get the log limit according to option -l/--limit"""
898 limit = opts.get('limit')
903 limit = opts.get('limit')
899 if limit:
904 if limit:
900 try:
905 try:
901 limit = int(limit)
906 limit = int(limit)
902 except ValueError:
907 except ValueError:
903 raise error.Abort(_('limit must be a positive integer'))
908 raise error.Abort(_('limit must be a positive integer'))
904 if limit <= 0:
909 if limit <= 0:
905 raise error.Abort(_('limit must be positive'))
910 raise error.Abort(_('limit must be positive'))
906 else:
911 else:
907 limit = None
912 limit = None
908 return limit
913 return limit
909
914
910 def makefilename(repo, pat, node, desc=None,
915 def makefilename(repo, pat, node, desc=None,
911 total=None, seqno=None, revwidth=None, pathname=None):
916 total=None, seqno=None, revwidth=None, pathname=None):
912 node_expander = {
917 node_expander = {
913 'H': lambda: hex(node),
918 'H': lambda: hex(node),
914 'R': lambda: '%d' % repo.changelog.rev(node),
919 'R': lambda: '%d' % repo.changelog.rev(node),
915 'h': lambda: short(node),
920 'h': lambda: short(node),
916 'm': lambda: re.sub('[^\w]', '_', desc or '')
921 'm': lambda: re.sub('[^\w]', '_', desc or '')
917 }
922 }
918 expander = {
923 expander = {
919 '%': lambda: '%',
924 '%': lambda: '%',
920 'b': lambda: os.path.basename(repo.root),
925 'b': lambda: os.path.basename(repo.root),
921 }
926 }
922
927
923 try:
928 try:
924 if node:
929 if node:
925 expander.update(node_expander)
930 expander.update(node_expander)
926 if node:
931 if node:
927 expander['r'] = (lambda:
932 expander['r'] = (lambda:
928 ('%d' % repo.changelog.rev(node)).zfill(revwidth or 0))
933 ('%d' % repo.changelog.rev(node)).zfill(revwidth or 0))
929 if total is not None:
934 if total is not None:
930 expander['N'] = lambda: '%d' % total
935 expander['N'] = lambda: '%d' % total
931 if seqno is not None:
936 if seqno is not None:
932 expander['n'] = lambda: '%d' % seqno
937 expander['n'] = lambda: '%d' % seqno
933 if total is not None and seqno is not None:
938 if total is not None and seqno is not None:
934 expander['n'] = (lambda: ('%d' % seqno).zfill(len('%d' % total)))
939 expander['n'] = (lambda: ('%d' % seqno).zfill(len('%d' % total)))
935 if pathname is not None:
940 if pathname is not None:
936 expander['s'] = lambda: os.path.basename(pathname)
941 expander['s'] = lambda: os.path.basename(pathname)
937 expander['d'] = lambda: os.path.dirname(pathname) or '.'
942 expander['d'] = lambda: os.path.dirname(pathname) or '.'
938 expander['p'] = lambda: pathname
943 expander['p'] = lambda: pathname
939
944
940 newname = []
945 newname = []
941 patlen = len(pat)
946 patlen = len(pat)
942 i = 0
947 i = 0
943 while i < patlen:
948 while i < patlen:
944 c = pat[i:i + 1]
949 c = pat[i:i + 1]
945 if c == '%':
950 if c == '%':
946 i += 1
951 i += 1
947 c = pat[i:i + 1]
952 c = pat[i:i + 1]
948 c = expander[c]()
953 c = expander[c]()
949 newname.append(c)
954 newname.append(c)
950 i += 1
955 i += 1
951 return ''.join(newname)
956 return ''.join(newname)
952 except KeyError as inst:
957 except KeyError as inst:
953 raise error.Abort(_("invalid format spec '%%%s' in output filename") %
958 raise error.Abort(_("invalid format spec '%%%s' in output filename") %
954 inst.args[0])
959 inst.args[0])
955
960
956 def isstdiofilename(pat):
961 def isstdiofilename(pat):
957 """True if the given pat looks like a filename denoting stdin/stdout"""
962 """True if the given pat looks like a filename denoting stdin/stdout"""
958 return not pat or pat == '-'
963 return not pat or pat == '-'
959
964
960 class _unclosablefile(object):
965 class _unclosablefile(object):
961 def __init__(self, fp):
966 def __init__(self, fp):
962 self._fp = fp
967 self._fp = fp
963
968
964 def close(self):
969 def close(self):
965 pass
970 pass
966
971
967 def __iter__(self):
972 def __iter__(self):
968 return iter(self._fp)
973 return iter(self._fp)
969
974
970 def __getattr__(self, attr):
975 def __getattr__(self, attr):
971 return getattr(self._fp, attr)
976 return getattr(self._fp, attr)
972
977
973 def __enter__(self):
978 def __enter__(self):
974 return self
979 return self
975
980
976 def __exit__(self, exc_type, exc_value, exc_tb):
981 def __exit__(self, exc_type, exc_value, exc_tb):
977 pass
982 pass
978
983
979 def makefileobj(repo, pat, node=None, desc=None, total=None,
984 def makefileobj(repo, pat, node=None, desc=None, total=None,
980 seqno=None, revwidth=None, mode='wb', modemap=None,
985 seqno=None, revwidth=None, mode='wb', modemap=None,
981 pathname=None):
986 pathname=None):
982
987
983 writable = mode not in ('r', 'rb')
988 writable = mode not in ('r', 'rb')
984
989
985 if isstdiofilename(pat):
990 if isstdiofilename(pat):
986 if writable:
991 if writable:
987 fp = repo.ui.fout
992 fp = repo.ui.fout
988 else:
993 else:
989 fp = repo.ui.fin
994 fp = repo.ui.fin
990 return _unclosablefile(fp)
995 return _unclosablefile(fp)
991 fn = makefilename(repo, pat, node, desc, total, seqno, revwidth, pathname)
996 fn = makefilename(repo, pat, node, desc, total, seqno, revwidth, pathname)
992 if modemap is not None:
997 if modemap is not None:
993 mode = modemap.get(fn, mode)
998 mode = modemap.get(fn, mode)
994 if mode == 'wb':
999 if mode == 'wb':
995 modemap[fn] = 'ab'
1000 modemap[fn] = 'ab'
996 return open(fn, mode)
1001 return open(fn, mode)
997
1002
998 def openrevlog(repo, cmd, file_, opts):
1003 def openrevlog(repo, cmd, file_, opts):
999 """opens the changelog, manifest, a filelog or a given revlog"""
1004 """opens the changelog, manifest, a filelog or a given revlog"""
1000 cl = opts['changelog']
1005 cl = opts['changelog']
1001 mf = opts['manifest']
1006 mf = opts['manifest']
1002 dir = opts['dir']
1007 dir = opts['dir']
1003 msg = None
1008 msg = None
1004 if cl and mf:
1009 if cl and mf:
1005 msg = _('cannot specify --changelog and --manifest at the same time')
1010 msg = _('cannot specify --changelog and --manifest at the same time')
1006 elif cl and dir:
1011 elif cl and dir:
1007 msg = _('cannot specify --changelog and --dir at the same time')
1012 msg = _('cannot specify --changelog and --dir at the same time')
1008 elif cl or mf or dir:
1013 elif cl or mf or dir:
1009 if file_:
1014 if file_:
1010 msg = _('cannot specify filename with --changelog or --manifest')
1015 msg = _('cannot specify filename with --changelog or --manifest')
1011 elif not repo:
1016 elif not repo:
1012 msg = _('cannot specify --changelog or --manifest or --dir '
1017 msg = _('cannot specify --changelog or --manifest or --dir '
1013 'without a repository')
1018 'without a repository')
1014 if msg:
1019 if msg:
1015 raise error.Abort(msg)
1020 raise error.Abort(msg)
1016
1021
1017 r = None
1022 r = None
1018 if repo:
1023 if repo:
1019 if cl:
1024 if cl:
1020 r = repo.unfiltered().changelog
1025 r = repo.unfiltered().changelog
1021 elif dir:
1026 elif dir:
1022 if 'treemanifest' not in repo.requirements:
1027 if 'treemanifest' not in repo.requirements:
1023 raise error.Abort(_("--dir can only be used on repos with "
1028 raise error.Abort(_("--dir can only be used on repos with "
1024 "treemanifest enabled"))
1029 "treemanifest enabled"))
1025 dirlog = repo.manifestlog._revlog.dirlog(dir)
1030 dirlog = repo.manifestlog._revlog.dirlog(dir)
1026 if len(dirlog):
1031 if len(dirlog):
1027 r = dirlog
1032 r = dirlog
1028 elif mf:
1033 elif mf:
1029 r = repo.manifestlog._revlog
1034 r = repo.manifestlog._revlog
1030 elif file_:
1035 elif file_:
1031 filelog = repo.file(file_)
1036 filelog = repo.file(file_)
1032 if len(filelog):
1037 if len(filelog):
1033 r = filelog
1038 r = filelog
1034 if not r:
1039 if not r:
1035 if not file_:
1040 if not file_:
1036 raise error.CommandError(cmd, _('invalid arguments'))
1041 raise error.CommandError(cmd, _('invalid arguments'))
1037 if not os.path.isfile(file_):
1042 if not os.path.isfile(file_):
1038 raise error.Abort(_("revlog '%s' not found") % file_)
1043 raise error.Abort(_("revlog '%s' not found") % file_)
1039 r = revlog.revlog(vfsmod.vfs(pycompat.getcwd(), audit=False),
1044 r = revlog.revlog(vfsmod.vfs(pycompat.getcwd(), audit=False),
1040 file_[:-2] + ".i")
1045 file_[:-2] + ".i")
1041 return r
1046 return r
1042
1047
1043 def copy(ui, repo, pats, opts, rename=False):
1048 def copy(ui, repo, pats, opts, rename=False):
1044 # called with the repo lock held
1049 # called with the repo lock held
1045 #
1050 #
1046 # hgsep => pathname that uses "/" to separate directories
1051 # hgsep => pathname that uses "/" to separate directories
1047 # ossep => pathname that uses os.sep to separate directories
1052 # ossep => pathname that uses os.sep to separate directories
1048 cwd = repo.getcwd()
1053 cwd = repo.getcwd()
1049 targets = {}
1054 targets = {}
1050 after = opts.get("after")
1055 after = opts.get("after")
1051 dryrun = opts.get("dry_run")
1056 dryrun = opts.get("dry_run")
1052 wctx = repo[None]
1057 wctx = repo[None]
1053
1058
1054 def walkpat(pat):
1059 def walkpat(pat):
1055 srcs = []
1060 srcs = []
1056 if after:
1061 if after:
1057 badstates = '?'
1062 badstates = '?'
1058 else:
1063 else:
1059 badstates = '?r'
1064 badstates = '?r'
1060 m = scmutil.match(wctx, [pat], opts, globbed=True)
1065 m = scmutil.match(wctx, [pat], opts, globbed=True)
1061 for abs in wctx.walk(m):
1066 for abs in wctx.walk(m):
1062 state = repo.dirstate[abs]
1067 state = repo.dirstate[abs]
1063 rel = m.rel(abs)
1068 rel = m.rel(abs)
1064 exact = m.exact(abs)
1069 exact = m.exact(abs)
1065 if state in badstates:
1070 if state in badstates:
1066 if exact and state == '?':
1071 if exact and state == '?':
1067 ui.warn(_('%s: not copying - file is not managed\n') % rel)
1072 ui.warn(_('%s: not copying - file is not managed\n') % rel)
1068 if exact and state == 'r':
1073 if exact and state == 'r':
1069 ui.warn(_('%s: not copying - file has been marked for'
1074 ui.warn(_('%s: not copying - file has been marked for'
1070 ' remove\n') % rel)
1075 ' remove\n') % rel)
1071 continue
1076 continue
1072 # abs: hgsep
1077 # abs: hgsep
1073 # rel: ossep
1078 # rel: ossep
1074 srcs.append((abs, rel, exact))
1079 srcs.append((abs, rel, exact))
1075 return srcs
1080 return srcs
1076
1081
1077 # abssrc: hgsep
1082 # abssrc: hgsep
1078 # relsrc: ossep
1083 # relsrc: ossep
1079 # otarget: ossep
1084 # otarget: ossep
1080 def copyfile(abssrc, relsrc, otarget, exact):
1085 def copyfile(abssrc, relsrc, otarget, exact):
1081 abstarget = pathutil.canonpath(repo.root, cwd, otarget)
1086 abstarget = pathutil.canonpath(repo.root, cwd, otarget)
1082 if '/' in abstarget:
1087 if '/' in abstarget:
1083 # We cannot normalize abstarget itself, this would prevent
1088 # We cannot normalize abstarget itself, this would prevent
1084 # case only renames, like a => A.
1089 # case only renames, like a => A.
1085 abspath, absname = abstarget.rsplit('/', 1)
1090 abspath, absname = abstarget.rsplit('/', 1)
1086 abstarget = repo.dirstate.normalize(abspath) + '/' + absname
1091 abstarget = repo.dirstate.normalize(abspath) + '/' + absname
1087 reltarget = repo.pathto(abstarget, cwd)
1092 reltarget = repo.pathto(abstarget, cwd)
1088 target = repo.wjoin(abstarget)
1093 target = repo.wjoin(abstarget)
1089 src = repo.wjoin(abssrc)
1094 src = repo.wjoin(abssrc)
1090 state = repo.dirstate[abstarget]
1095 state = repo.dirstate[abstarget]
1091
1096
1092 scmutil.checkportable(ui, abstarget)
1097 scmutil.checkportable(ui, abstarget)
1093
1098
1094 # check for collisions
1099 # check for collisions
1095 prevsrc = targets.get(abstarget)
1100 prevsrc = targets.get(abstarget)
1096 if prevsrc is not None:
1101 if prevsrc is not None:
1097 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
1102 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
1098 (reltarget, repo.pathto(abssrc, cwd),
1103 (reltarget, repo.pathto(abssrc, cwd),
1099 repo.pathto(prevsrc, cwd)))
1104 repo.pathto(prevsrc, cwd)))
1100 return
1105 return
1101
1106
1102 # check for overwrites
1107 # check for overwrites
1103 exists = os.path.lexists(target)
1108 exists = os.path.lexists(target)
1104 samefile = False
1109 samefile = False
1105 if exists and abssrc != abstarget:
1110 if exists and abssrc != abstarget:
1106 if (repo.dirstate.normalize(abssrc) ==
1111 if (repo.dirstate.normalize(abssrc) ==
1107 repo.dirstate.normalize(abstarget)):
1112 repo.dirstate.normalize(abstarget)):
1108 if not rename:
1113 if not rename:
1109 ui.warn(_("%s: can't copy - same file\n") % reltarget)
1114 ui.warn(_("%s: can't copy - same file\n") % reltarget)
1110 return
1115 return
1111 exists = False
1116 exists = False
1112 samefile = True
1117 samefile = True
1113
1118
1114 if not after and exists or after and state in 'mn':
1119 if not after and exists or after and state in 'mn':
1115 if not opts['force']:
1120 if not opts['force']:
1116 if state in 'mn':
1121 if state in 'mn':
1117 msg = _('%s: not overwriting - file already committed\n')
1122 msg = _('%s: not overwriting - file already committed\n')
1118 if after:
1123 if after:
1119 flags = '--after --force'
1124 flags = '--after --force'
1120 else:
1125 else:
1121 flags = '--force'
1126 flags = '--force'
1122 if rename:
1127 if rename:
1123 hint = _('(hg rename %s to replace the file by '
1128 hint = _('(hg rename %s to replace the file by '
1124 'recording a rename)\n') % flags
1129 'recording a rename)\n') % flags
1125 else:
1130 else:
1126 hint = _('(hg copy %s to replace the file by '
1131 hint = _('(hg copy %s to replace the file by '
1127 'recording a copy)\n') % flags
1132 'recording a copy)\n') % flags
1128 else:
1133 else:
1129 msg = _('%s: not overwriting - file exists\n')
1134 msg = _('%s: not overwriting - file exists\n')
1130 if rename:
1135 if rename:
1131 hint = _('(hg rename --after to record the rename)\n')
1136 hint = _('(hg rename --after to record the rename)\n')
1132 else:
1137 else:
1133 hint = _('(hg copy --after to record the copy)\n')
1138 hint = _('(hg copy --after to record the copy)\n')
1134 ui.warn(msg % reltarget)
1139 ui.warn(msg % reltarget)
1135 ui.warn(hint)
1140 ui.warn(hint)
1136 return
1141 return
1137
1142
1138 if after:
1143 if after:
1139 if not exists:
1144 if not exists:
1140 if rename:
1145 if rename:
1141 ui.warn(_('%s: not recording move - %s does not exist\n') %
1146 ui.warn(_('%s: not recording move - %s does not exist\n') %
1142 (relsrc, reltarget))
1147 (relsrc, reltarget))
1143 else:
1148 else:
1144 ui.warn(_('%s: not recording copy - %s does not exist\n') %
1149 ui.warn(_('%s: not recording copy - %s does not exist\n') %
1145 (relsrc, reltarget))
1150 (relsrc, reltarget))
1146 return
1151 return
1147 elif not dryrun:
1152 elif not dryrun:
1148 try:
1153 try:
1149 if exists:
1154 if exists:
1150 os.unlink(target)
1155 os.unlink(target)
1151 targetdir = os.path.dirname(target) or '.'
1156 targetdir = os.path.dirname(target) or '.'
1152 if not os.path.isdir(targetdir):
1157 if not os.path.isdir(targetdir):
1153 os.makedirs(targetdir)
1158 os.makedirs(targetdir)
1154 if samefile:
1159 if samefile:
1155 tmp = target + "~hgrename"
1160 tmp = target + "~hgrename"
1156 os.rename(src, tmp)
1161 os.rename(src, tmp)
1157 os.rename(tmp, target)
1162 os.rename(tmp, target)
1158 else:
1163 else:
1159 util.copyfile(src, target)
1164 util.copyfile(src, target)
1160 srcexists = True
1165 srcexists = True
1161 except IOError as inst:
1166 except IOError as inst:
1162 if inst.errno == errno.ENOENT:
1167 if inst.errno == errno.ENOENT:
1163 ui.warn(_('%s: deleted in working directory\n') % relsrc)
1168 ui.warn(_('%s: deleted in working directory\n') % relsrc)
1164 srcexists = False
1169 srcexists = False
1165 else:
1170 else:
1166 ui.warn(_('%s: cannot copy - %s\n') %
1171 ui.warn(_('%s: cannot copy - %s\n') %
1167 (relsrc, encoding.strtolocal(inst.strerror)))
1172 (relsrc, encoding.strtolocal(inst.strerror)))
1168 return True # report a failure
1173 return True # report a failure
1169
1174
1170 if ui.verbose or not exact:
1175 if ui.verbose or not exact:
1171 if rename:
1176 if rename:
1172 ui.status(_('moving %s to %s\n') % (relsrc, reltarget))
1177 ui.status(_('moving %s to %s\n') % (relsrc, reltarget))
1173 else:
1178 else:
1174 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
1179 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
1175
1180
1176 targets[abstarget] = abssrc
1181 targets[abstarget] = abssrc
1177
1182
1178 # fix up dirstate
1183 # fix up dirstate
1179 scmutil.dirstatecopy(ui, repo, wctx, abssrc, abstarget,
1184 scmutil.dirstatecopy(ui, repo, wctx, abssrc, abstarget,
1180 dryrun=dryrun, cwd=cwd)
1185 dryrun=dryrun, cwd=cwd)
1181 if rename and not dryrun:
1186 if rename and not dryrun:
1182 if not after and srcexists and not samefile:
1187 if not after and srcexists and not samefile:
1183 repo.wvfs.unlinkpath(abssrc)
1188 repo.wvfs.unlinkpath(abssrc)
1184 wctx.forget([abssrc])
1189 wctx.forget([abssrc])
1185
1190
1186 # pat: ossep
1191 # pat: ossep
1187 # dest ossep
1192 # dest ossep
1188 # srcs: list of (hgsep, hgsep, ossep, bool)
1193 # srcs: list of (hgsep, hgsep, ossep, bool)
1189 # return: function that takes hgsep and returns ossep
1194 # return: function that takes hgsep and returns ossep
1190 def targetpathfn(pat, dest, srcs):
1195 def targetpathfn(pat, dest, srcs):
1191 if os.path.isdir(pat):
1196 if os.path.isdir(pat):
1192 abspfx = pathutil.canonpath(repo.root, cwd, pat)
1197 abspfx = pathutil.canonpath(repo.root, cwd, pat)
1193 abspfx = util.localpath(abspfx)
1198 abspfx = util.localpath(abspfx)
1194 if destdirexists:
1199 if destdirexists:
1195 striplen = len(os.path.split(abspfx)[0])
1200 striplen = len(os.path.split(abspfx)[0])
1196 else:
1201 else:
1197 striplen = len(abspfx)
1202 striplen = len(abspfx)
1198 if striplen:
1203 if striplen:
1199 striplen += len(pycompat.ossep)
1204 striplen += len(pycompat.ossep)
1200 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
1205 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
1201 elif destdirexists:
1206 elif destdirexists:
1202 res = lambda p: os.path.join(dest,
1207 res = lambda p: os.path.join(dest,
1203 os.path.basename(util.localpath(p)))
1208 os.path.basename(util.localpath(p)))
1204 else:
1209 else:
1205 res = lambda p: dest
1210 res = lambda p: dest
1206 return res
1211 return res
1207
1212
1208 # pat: ossep
1213 # pat: ossep
1209 # dest ossep
1214 # dest ossep
1210 # srcs: list of (hgsep, hgsep, ossep, bool)
1215 # srcs: list of (hgsep, hgsep, ossep, bool)
1211 # return: function that takes hgsep and returns ossep
1216 # return: function that takes hgsep and returns ossep
1212 def targetpathafterfn(pat, dest, srcs):
1217 def targetpathafterfn(pat, dest, srcs):
1213 if matchmod.patkind(pat):
1218 if matchmod.patkind(pat):
1214 # a mercurial pattern
1219 # a mercurial pattern
1215 res = lambda p: os.path.join(dest,
1220 res = lambda p: os.path.join(dest,
1216 os.path.basename(util.localpath(p)))
1221 os.path.basename(util.localpath(p)))
1217 else:
1222 else:
1218 abspfx = pathutil.canonpath(repo.root, cwd, pat)
1223 abspfx = pathutil.canonpath(repo.root, cwd, pat)
1219 if len(abspfx) < len(srcs[0][0]):
1224 if len(abspfx) < len(srcs[0][0]):
1220 # A directory. Either the target path contains the last
1225 # A directory. Either the target path contains the last
1221 # component of the source path or it does not.
1226 # component of the source path or it does not.
1222 def evalpath(striplen):
1227 def evalpath(striplen):
1223 score = 0
1228 score = 0
1224 for s in srcs:
1229 for s in srcs:
1225 t = os.path.join(dest, util.localpath(s[0])[striplen:])
1230 t = os.path.join(dest, util.localpath(s[0])[striplen:])
1226 if os.path.lexists(t):
1231 if os.path.lexists(t):
1227 score += 1
1232 score += 1
1228 return score
1233 return score
1229
1234
1230 abspfx = util.localpath(abspfx)
1235 abspfx = util.localpath(abspfx)
1231 striplen = len(abspfx)
1236 striplen = len(abspfx)
1232 if striplen:
1237 if striplen:
1233 striplen += len(pycompat.ossep)
1238 striplen += len(pycompat.ossep)
1234 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
1239 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
1235 score = evalpath(striplen)
1240 score = evalpath(striplen)
1236 striplen1 = len(os.path.split(abspfx)[0])
1241 striplen1 = len(os.path.split(abspfx)[0])
1237 if striplen1:
1242 if striplen1:
1238 striplen1 += len(pycompat.ossep)
1243 striplen1 += len(pycompat.ossep)
1239 if evalpath(striplen1) > score:
1244 if evalpath(striplen1) > score:
1240 striplen = striplen1
1245 striplen = striplen1
1241 res = lambda p: os.path.join(dest,
1246 res = lambda p: os.path.join(dest,
1242 util.localpath(p)[striplen:])
1247 util.localpath(p)[striplen:])
1243 else:
1248 else:
1244 # a file
1249 # a file
1245 if destdirexists:
1250 if destdirexists:
1246 res = lambda p: os.path.join(dest,
1251 res = lambda p: os.path.join(dest,
1247 os.path.basename(util.localpath(p)))
1252 os.path.basename(util.localpath(p)))
1248 else:
1253 else:
1249 res = lambda p: dest
1254 res = lambda p: dest
1250 return res
1255 return res
1251
1256
1252 pats = scmutil.expandpats(pats)
1257 pats = scmutil.expandpats(pats)
1253 if not pats:
1258 if not pats:
1254 raise error.Abort(_('no source or destination specified'))
1259 raise error.Abort(_('no source or destination specified'))
1255 if len(pats) == 1:
1260 if len(pats) == 1:
1256 raise error.Abort(_('no destination specified'))
1261 raise error.Abort(_('no destination specified'))
1257 dest = pats.pop()
1262 dest = pats.pop()
1258 destdirexists = os.path.isdir(dest) and not os.path.islink(dest)
1263 destdirexists = os.path.isdir(dest) and not os.path.islink(dest)
1259 if not destdirexists:
1264 if not destdirexists:
1260 if len(pats) > 1 or matchmod.patkind(pats[0]):
1265 if len(pats) > 1 or matchmod.patkind(pats[0]):
1261 raise error.Abort(_('with multiple sources, destination must be an '
1266 raise error.Abort(_('with multiple sources, destination must be an '
1262 'existing directory'))
1267 'existing directory'))
1263 if util.endswithsep(dest):
1268 if util.endswithsep(dest):
1264 raise error.Abort(_('destination %s is not a directory') % dest)
1269 raise error.Abort(_('destination %s is not a directory') % dest)
1265
1270
1266 tfn = targetpathfn
1271 tfn = targetpathfn
1267 if after:
1272 if after:
1268 tfn = targetpathafterfn
1273 tfn = targetpathafterfn
1269 copylist = []
1274 copylist = []
1270 for pat in pats:
1275 for pat in pats:
1271 srcs = walkpat(pat)
1276 srcs = walkpat(pat)
1272 if not srcs:
1277 if not srcs:
1273 continue
1278 continue
1274 copylist.append((tfn(pat, dest, srcs), srcs))
1279 copylist.append((tfn(pat, dest, srcs), srcs))
1275 if not copylist:
1280 if not copylist:
1276 raise error.Abort(_('no files to copy'))
1281 raise error.Abort(_('no files to copy'))
1277
1282
1278 errors = 0
1283 errors = 0
1279 for targetpath, srcs in copylist:
1284 for targetpath, srcs in copylist:
1280 for abssrc, relsrc, exact in srcs:
1285 for abssrc, relsrc, exact in srcs:
1281 if copyfile(abssrc, relsrc, targetpath(abssrc), exact):
1286 if copyfile(abssrc, relsrc, targetpath(abssrc), exact):
1282 errors += 1
1287 errors += 1
1283
1288
1284 if errors:
1289 if errors:
1285 ui.warn(_('(consider using --after)\n'))
1290 ui.warn(_('(consider using --after)\n'))
1286
1291
1287 return errors != 0
1292 return errors != 0
1288
1293
1289 ## facility to let extension process additional data into an import patch
1294 ## facility to let extension process additional data into an import patch
1290 # list of identifier to be executed in order
1295 # list of identifier to be executed in order
1291 extrapreimport = [] # run before commit
1296 extrapreimport = [] # run before commit
1292 extrapostimport = [] # run after commit
1297 extrapostimport = [] # run after commit
1293 # mapping from identifier to actual import function
1298 # mapping from identifier to actual import function
1294 #
1299 #
1295 # 'preimport' are run before the commit is made and are provided the following
1300 # 'preimport' are run before the commit is made and are provided the following
1296 # arguments:
1301 # arguments:
1297 # - repo: the localrepository instance,
1302 # - repo: the localrepository instance,
1298 # - patchdata: data extracted from patch header (cf m.patch.patchheadermap),
1303 # - patchdata: data extracted from patch header (cf m.patch.patchheadermap),
1299 # - extra: the future extra dictionary of the changeset, please mutate it,
1304 # - extra: the future extra dictionary of the changeset, please mutate it,
1300 # - opts: the import options.
1305 # - opts: the import options.
1301 # XXX ideally, we would just pass an ctx ready to be computed, that would allow
1306 # XXX ideally, we would just pass an ctx ready to be computed, that would allow
1302 # mutation of in memory commit and more. Feel free to rework the code to get
1307 # mutation of in memory commit and more. Feel free to rework the code to get
1303 # there.
1308 # there.
1304 extrapreimportmap = {}
1309 extrapreimportmap = {}
1305 # 'postimport' are run after the commit is made and are provided the following
1310 # 'postimport' are run after the commit is made and are provided the following
1306 # argument:
1311 # argument:
1307 # - ctx: the changectx created by import.
1312 # - ctx: the changectx created by import.
1308 extrapostimportmap = {}
1313 extrapostimportmap = {}
1309
1314
1310 def tryimportone(ui, repo, hunk, parents, opts, msgs, updatefunc):
1315 def tryimportone(ui, repo, hunk, parents, opts, msgs, updatefunc):
1311 """Utility function used by commands.import to import a single patch
1316 """Utility function used by commands.import to import a single patch
1312
1317
1313 This function is explicitly defined here to help the evolve extension to
1318 This function is explicitly defined here to help the evolve extension to
1314 wrap this part of the import logic.
1319 wrap this part of the import logic.
1315
1320
1316 The API is currently a bit ugly because it a simple code translation from
1321 The API is currently a bit ugly because it a simple code translation from
1317 the import command. Feel free to make it better.
1322 the import command. Feel free to make it better.
1318
1323
1319 :hunk: a patch (as a binary string)
1324 :hunk: a patch (as a binary string)
1320 :parents: nodes that will be parent of the created commit
1325 :parents: nodes that will be parent of the created commit
1321 :opts: the full dict of option passed to the import command
1326 :opts: the full dict of option passed to the import command
1322 :msgs: list to save commit message to.
1327 :msgs: list to save commit message to.
1323 (used in case we need to save it when failing)
1328 (used in case we need to save it when failing)
1324 :updatefunc: a function that update a repo to a given node
1329 :updatefunc: a function that update a repo to a given node
1325 updatefunc(<repo>, <node>)
1330 updatefunc(<repo>, <node>)
1326 """
1331 """
1327 # avoid cycle context -> subrepo -> cmdutil
1332 # avoid cycle context -> subrepo -> cmdutil
1328 from . import context
1333 from . import context
1329 extractdata = patch.extract(ui, hunk)
1334 extractdata = patch.extract(ui, hunk)
1330 tmpname = extractdata.get('filename')
1335 tmpname = extractdata.get('filename')
1331 message = extractdata.get('message')
1336 message = extractdata.get('message')
1332 user = opts.get('user') or extractdata.get('user')
1337 user = opts.get('user') or extractdata.get('user')
1333 date = opts.get('date') or extractdata.get('date')
1338 date = opts.get('date') or extractdata.get('date')
1334 branch = extractdata.get('branch')
1339 branch = extractdata.get('branch')
1335 nodeid = extractdata.get('nodeid')
1340 nodeid = extractdata.get('nodeid')
1336 p1 = extractdata.get('p1')
1341 p1 = extractdata.get('p1')
1337 p2 = extractdata.get('p2')
1342 p2 = extractdata.get('p2')
1338
1343
1339 nocommit = opts.get('no_commit')
1344 nocommit = opts.get('no_commit')
1340 importbranch = opts.get('import_branch')
1345 importbranch = opts.get('import_branch')
1341 update = not opts.get('bypass')
1346 update = not opts.get('bypass')
1342 strip = opts["strip"]
1347 strip = opts["strip"]
1343 prefix = opts["prefix"]
1348 prefix = opts["prefix"]
1344 sim = float(opts.get('similarity') or 0)
1349 sim = float(opts.get('similarity') or 0)
1345 if not tmpname:
1350 if not tmpname:
1346 return (None, None, False)
1351 return (None, None, False)
1347
1352
1348 rejects = False
1353 rejects = False
1349
1354
1350 try:
1355 try:
1351 cmdline_message = logmessage(ui, opts)
1356 cmdline_message = logmessage(ui, opts)
1352 if cmdline_message:
1357 if cmdline_message:
1353 # pickup the cmdline msg
1358 # pickup the cmdline msg
1354 message = cmdline_message
1359 message = cmdline_message
1355 elif message:
1360 elif message:
1356 # pickup the patch msg
1361 # pickup the patch msg
1357 message = message.strip()
1362 message = message.strip()
1358 else:
1363 else:
1359 # launch the editor
1364 # launch the editor
1360 message = None
1365 message = None
1361 ui.debug('message:\n%s\n' % message)
1366 ui.debug('message:\n%s\n' % message)
1362
1367
1363 if len(parents) == 1:
1368 if len(parents) == 1:
1364 parents.append(repo[nullid])
1369 parents.append(repo[nullid])
1365 if opts.get('exact'):
1370 if opts.get('exact'):
1366 if not nodeid or not p1:
1371 if not nodeid or not p1:
1367 raise error.Abort(_('not a Mercurial patch'))
1372 raise error.Abort(_('not a Mercurial patch'))
1368 p1 = repo[p1]
1373 p1 = repo[p1]
1369 p2 = repo[p2 or nullid]
1374 p2 = repo[p2 or nullid]
1370 elif p2:
1375 elif p2:
1371 try:
1376 try:
1372 p1 = repo[p1]
1377 p1 = repo[p1]
1373 p2 = repo[p2]
1378 p2 = repo[p2]
1374 # Without any options, consider p2 only if the
1379 # Without any options, consider p2 only if the
1375 # patch is being applied on top of the recorded
1380 # patch is being applied on top of the recorded
1376 # first parent.
1381 # first parent.
1377 if p1 != parents[0]:
1382 if p1 != parents[0]:
1378 p1 = parents[0]
1383 p1 = parents[0]
1379 p2 = repo[nullid]
1384 p2 = repo[nullid]
1380 except error.RepoError:
1385 except error.RepoError:
1381 p1, p2 = parents
1386 p1, p2 = parents
1382 if p2.node() == nullid:
1387 if p2.node() == nullid:
1383 ui.warn(_("warning: import the patch as a normal revision\n"
1388 ui.warn(_("warning: import the patch as a normal revision\n"
1384 "(use --exact to import the patch as a merge)\n"))
1389 "(use --exact to import the patch as a merge)\n"))
1385 else:
1390 else:
1386 p1, p2 = parents
1391 p1, p2 = parents
1387
1392
1388 n = None
1393 n = None
1389 if update:
1394 if update:
1390 if p1 != parents[0]:
1395 if p1 != parents[0]:
1391 updatefunc(repo, p1.node())
1396 updatefunc(repo, p1.node())
1392 if p2 != parents[1]:
1397 if p2 != parents[1]:
1393 repo.setparents(p1.node(), p2.node())
1398 repo.setparents(p1.node(), p2.node())
1394
1399
1395 if opts.get('exact') or importbranch:
1400 if opts.get('exact') or importbranch:
1396 repo.dirstate.setbranch(branch or 'default')
1401 repo.dirstate.setbranch(branch or 'default')
1397
1402
1398 partial = opts.get('partial', False)
1403 partial = opts.get('partial', False)
1399 files = set()
1404 files = set()
1400 try:
1405 try:
1401 patch.patch(ui, repo, tmpname, strip=strip, prefix=prefix,
1406 patch.patch(ui, repo, tmpname, strip=strip, prefix=prefix,
1402 files=files, eolmode=None, similarity=sim / 100.0)
1407 files=files, eolmode=None, similarity=sim / 100.0)
1403 except error.PatchError as e:
1408 except error.PatchError as e:
1404 if not partial:
1409 if not partial:
1405 raise error.Abort(str(e))
1410 raise error.Abort(str(e))
1406 if partial:
1411 if partial:
1407 rejects = True
1412 rejects = True
1408
1413
1409 files = list(files)
1414 files = list(files)
1410 if nocommit:
1415 if nocommit:
1411 if message:
1416 if message:
1412 msgs.append(message)
1417 msgs.append(message)
1413 else:
1418 else:
1414 if opts.get('exact') or p2:
1419 if opts.get('exact') or p2:
1415 # If you got here, you either use --force and know what
1420 # If you got here, you either use --force and know what
1416 # you are doing or used --exact or a merge patch while
1421 # you are doing or used --exact or a merge patch while
1417 # being updated to its first parent.
1422 # being updated to its first parent.
1418 m = None
1423 m = None
1419 else:
1424 else:
1420 m = scmutil.matchfiles(repo, files or [])
1425 m = scmutil.matchfiles(repo, files or [])
1421 editform = mergeeditform(repo[None], 'import.normal')
1426 editform = mergeeditform(repo[None], 'import.normal')
1422 if opts.get('exact'):
1427 if opts.get('exact'):
1423 editor = None
1428 editor = None
1424 else:
1429 else:
1425 editor = getcommiteditor(editform=editform,
1430 editor = getcommiteditor(editform=editform,
1426 **pycompat.strkwargs(opts))
1431 **pycompat.strkwargs(opts))
1427 extra = {}
1432 extra = {}
1428 for idfunc in extrapreimport:
1433 for idfunc in extrapreimport:
1429 extrapreimportmap[idfunc](repo, extractdata, extra, opts)
1434 extrapreimportmap[idfunc](repo, extractdata, extra, opts)
1430 overrides = {}
1435 overrides = {}
1431 if partial:
1436 if partial:
1432 overrides[('ui', 'allowemptycommit')] = True
1437 overrides[('ui', 'allowemptycommit')] = True
1433 with repo.ui.configoverride(overrides, 'import'):
1438 with repo.ui.configoverride(overrides, 'import'):
1434 n = repo.commit(message, user,
1439 n = repo.commit(message, user,
1435 date, match=m,
1440 date, match=m,
1436 editor=editor, extra=extra)
1441 editor=editor, extra=extra)
1437 for idfunc in extrapostimport:
1442 for idfunc in extrapostimport:
1438 extrapostimportmap[idfunc](repo[n])
1443 extrapostimportmap[idfunc](repo[n])
1439 else:
1444 else:
1440 if opts.get('exact') or importbranch:
1445 if opts.get('exact') or importbranch:
1441 branch = branch or 'default'
1446 branch = branch or 'default'
1442 else:
1447 else:
1443 branch = p1.branch()
1448 branch = p1.branch()
1444 store = patch.filestore()
1449 store = patch.filestore()
1445 try:
1450 try:
1446 files = set()
1451 files = set()
1447 try:
1452 try:
1448 patch.patchrepo(ui, repo, p1, store, tmpname, strip, prefix,
1453 patch.patchrepo(ui, repo, p1, store, tmpname, strip, prefix,
1449 files, eolmode=None)
1454 files, eolmode=None)
1450 except error.PatchError as e:
1455 except error.PatchError as e:
1451 raise error.Abort(str(e))
1456 raise error.Abort(str(e))
1452 if opts.get('exact'):
1457 if opts.get('exact'):
1453 editor = None
1458 editor = None
1454 else:
1459 else:
1455 editor = getcommiteditor(editform='import.bypass')
1460 editor = getcommiteditor(editform='import.bypass')
1456 memctx = context.memctx(repo, (p1.node(), p2.node()),
1461 memctx = context.memctx(repo, (p1.node(), p2.node()),
1457 message,
1462 message,
1458 files=files,
1463 files=files,
1459 filectxfn=store,
1464 filectxfn=store,
1460 user=user,
1465 user=user,
1461 date=date,
1466 date=date,
1462 branch=branch,
1467 branch=branch,
1463 editor=editor)
1468 editor=editor)
1464 n = memctx.commit()
1469 n = memctx.commit()
1465 finally:
1470 finally:
1466 store.close()
1471 store.close()
1467 if opts.get('exact') and nocommit:
1472 if opts.get('exact') and nocommit:
1468 # --exact with --no-commit is still useful in that it does merge
1473 # --exact with --no-commit is still useful in that it does merge
1469 # and branch bits
1474 # and branch bits
1470 ui.warn(_("warning: can't check exact import with --no-commit\n"))
1475 ui.warn(_("warning: can't check exact import with --no-commit\n"))
1471 elif opts.get('exact') and hex(n) != nodeid:
1476 elif opts.get('exact') and hex(n) != nodeid:
1472 raise error.Abort(_('patch is damaged or loses information'))
1477 raise error.Abort(_('patch is damaged or loses information'))
1473 msg = _('applied to working directory')
1478 msg = _('applied to working directory')
1474 if n:
1479 if n:
1475 # i18n: refers to a short changeset id
1480 # i18n: refers to a short changeset id
1476 msg = _('created %s') % short(n)
1481 msg = _('created %s') % short(n)
1477 return (msg, n, rejects)
1482 return (msg, n, rejects)
1478 finally:
1483 finally:
1479 os.unlink(tmpname)
1484 os.unlink(tmpname)
1480
1485
1481 # facility to let extensions include additional data in an exported patch
1486 # facility to let extensions include additional data in an exported patch
1482 # list of identifiers to be executed in order
1487 # list of identifiers to be executed in order
1483 extraexport = []
1488 extraexport = []
1484 # mapping from identifier to actual export function
1489 # mapping from identifier to actual export function
1485 # function as to return a string to be added to the header or None
1490 # function as to return a string to be added to the header or None
1486 # it is given two arguments (sequencenumber, changectx)
1491 # it is given two arguments (sequencenumber, changectx)
1487 extraexportmap = {}
1492 extraexportmap = {}
1488
1493
1489 def _exportsingle(repo, ctx, match, switch_parent, rev, seqno, write, diffopts):
1494 def _exportsingle(repo, ctx, match, switch_parent, rev, seqno, write, diffopts):
1490 node = scmutil.binnode(ctx)
1495 node = scmutil.binnode(ctx)
1491 parents = [p.node() for p in ctx.parents() if p]
1496 parents = [p.node() for p in ctx.parents() if p]
1492 branch = ctx.branch()
1497 branch = ctx.branch()
1493 if switch_parent:
1498 if switch_parent:
1494 parents.reverse()
1499 parents.reverse()
1495
1500
1496 if parents:
1501 if parents:
1497 prev = parents[0]
1502 prev = parents[0]
1498 else:
1503 else:
1499 prev = nullid
1504 prev = nullid
1500
1505
1501 write("# HG changeset patch\n")
1506 write("# HG changeset patch\n")
1502 write("# User %s\n" % ctx.user())
1507 write("# User %s\n" % ctx.user())
1503 write("# Date %d %d\n" % ctx.date())
1508 write("# Date %d %d\n" % ctx.date())
1504 write("# %s\n" % util.datestr(ctx.date()))
1509 write("# %s\n" % util.datestr(ctx.date()))
1505 if branch and branch != 'default':
1510 if branch and branch != 'default':
1506 write("# Branch %s\n" % branch)
1511 write("# Branch %s\n" % branch)
1507 write("# Node ID %s\n" % hex(node))
1512 write("# Node ID %s\n" % hex(node))
1508 write("# Parent %s\n" % hex(prev))
1513 write("# Parent %s\n" % hex(prev))
1509 if len(parents) > 1:
1514 if len(parents) > 1:
1510 write("# Parent %s\n" % hex(parents[1]))
1515 write("# Parent %s\n" % hex(parents[1]))
1511
1516
1512 for headerid in extraexport:
1517 for headerid in extraexport:
1513 header = extraexportmap[headerid](seqno, ctx)
1518 header = extraexportmap[headerid](seqno, ctx)
1514 if header is not None:
1519 if header is not None:
1515 write('# %s\n' % header)
1520 write('# %s\n' % header)
1516 write(ctx.description().rstrip())
1521 write(ctx.description().rstrip())
1517 write("\n\n")
1522 write("\n\n")
1518
1523
1519 for chunk, label in patch.diffui(repo, prev, node, match, opts=diffopts):
1524 for chunk, label in patch.diffui(repo, prev, node, match, opts=diffopts):
1520 write(chunk, label=label)
1525 write(chunk, label=label)
1521
1526
1522 def export(repo, revs, fntemplate='hg-%h.patch', fp=None, switch_parent=False,
1527 def export(repo, revs, fntemplate='hg-%h.patch', fp=None, switch_parent=False,
1523 opts=None, match=None):
1528 opts=None, match=None):
1524 '''export changesets as hg patches
1529 '''export changesets as hg patches
1525
1530
1526 Args:
1531 Args:
1527 repo: The repository from which we're exporting revisions.
1532 repo: The repository from which we're exporting revisions.
1528 revs: A list of revisions to export as revision numbers.
1533 revs: A list of revisions to export as revision numbers.
1529 fntemplate: An optional string to use for generating patch file names.
1534 fntemplate: An optional string to use for generating patch file names.
1530 fp: An optional file-like object to which patches should be written.
1535 fp: An optional file-like object to which patches should be written.
1531 switch_parent: If True, show diffs against second parent when not nullid.
1536 switch_parent: If True, show diffs against second parent when not nullid.
1532 Default is false, which always shows diff against p1.
1537 Default is false, which always shows diff against p1.
1533 opts: diff options to use for generating the patch.
1538 opts: diff options to use for generating the patch.
1534 match: If specified, only export changes to files matching this matcher.
1539 match: If specified, only export changes to files matching this matcher.
1535
1540
1536 Returns:
1541 Returns:
1537 Nothing.
1542 Nothing.
1538
1543
1539 Side Effect:
1544 Side Effect:
1540 "HG Changeset Patch" data is emitted to one of the following
1545 "HG Changeset Patch" data is emitted to one of the following
1541 destinations:
1546 destinations:
1542 fp is specified: All revs are written to the specified
1547 fp is specified: All revs are written to the specified
1543 file-like object.
1548 file-like object.
1544 fntemplate specified: Each rev is written to a unique file named using
1549 fntemplate specified: Each rev is written to a unique file named using
1545 the given template.
1550 the given template.
1546 Neither fp nor template specified: All revs written to repo.ui.write()
1551 Neither fp nor template specified: All revs written to repo.ui.write()
1547 '''
1552 '''
1548
1553
1549 total = len(revs)
1554 total = len(revs)
1550 revwidth = max(len(str(rev)) for rev in revs)
1555 revwidth = max(len(str(rev)) for rev in revs)
1551 filemode = {}
1556 filemode = {}
1552
1557
1553 write = None
1558 write = None
1554 dest = '<unnamed>'
1559 dest = '<unnamed>'
1555 if fp:
1560 if fp:
1556 dest = getattr(fp, 'name', dest)
1561 dest = getattr(fp, 'name', dest)
1557 def write(s, **kw):
1562 def write(s, **kw):
1558 fp.write(s)
1563 fp.write(s)
1559 elif not fntemplate:
1564 elif not fntemplate:
1560 write = repo.ui.write
1565 write = repo.ui.write
1561
1566
1562 for seqno, rev in enumerate(revs, 1):
1567 for seqno, rev in enumerate(revs, 1):
1563 ctx = repo[rev]
1568 ctx = repo[rev]
1564 fo = None
1569 fo = None
1565 if not fp and fntemplate:
1570 if not fp and fntemplate:
1566 desc_lines = ctx.description().rstrip().split('\n')
1571 desc_lines = ctx.description().rstrip().split('\n')
1567 desc = desc_lines[0] #Commit always has a first line.
1572 desc = desc_lines[0] #Commit always has a first line.
1568 fo = makefileobj(repo, fntemplate, ctx.node(), desc=desc,
1573 fo = makefileobj(repo, fntemplate, ctx.node(), desc=desc,
1569 total=total, seqno=seqno, revwidth=revwidth,
1574 total=total, seqno=seqno, revwidth=revwidth,
1570 mode='wb', modemap=filemode)
1575 mode='wb', modemap=filemode)
1571 dest = fo.name
1576 dest = fo.name
1572 def write(s, **kw):
1577 def write(s, **kw):
1573 fo.write(s)
1578 fo.write(s)
1574 if not dest.startswith('<'):
1579 if not dest.startswith('<'):
1575 repo.ui.note("%s\n" % dest)
1580 repo.ui.note("%s\n" % dest)
1576 _exportsingle(
1581 _exportsingle(
1577 repo, ctx, match, switch_parent, rev, seqno, write, opts)
1582 repo, ctx, match, switch_parent, rev, seqno, write, opts)
1578 if fo is not None:
1583 if fo is not None:
1579 fo.close()
1584 fo.close()
1580
1585
1581 def diffordiffstat(ui, repo, diffopts, node1, node2, match,
1586 def diffordiffstat(ui, repo, diffopts, node1, node2, match,
1582 changes=None, stat=False, fp=None, prefix='',
1587 changes=None, stat=False, fp=None, prefix='',
1583 root='', listsubrepos=False, hunksfilterfn=None):
1588 root='', listsubrepos=False, hunksfilterfn=None):
1584 '''show diff or diffstat.'''
1589 '''show diff or diffstat.'''
1585 if fp is None:
1590 if fp is None:
1586 write = ui.write
1591 write = ui.write
1587 else:
1592 else:
1588 def write(s, **kw):
1593 def write(s, **kw):
1589 fp.write(s)
1594 fp.write(s)
1590
1595
1591 if root:
1596 if root:
1592 relroot = pathutil.canonpath(repo.root, repo.getcwd(), root)
1597 relroot = pathutil.canonpath(repo.root, repo.getcwd(), root)
1593 else:
1598 else:
1594 relroot = ''
1599 relroot = ''
1595 if relroot != '':
1600 if relroot != '':
1596 # XXX relative roots currently don't work if the root is within a
1601 # XXX relative roots currently don't work if the root is within a
1597 # subrepo
1602 # subrepo
1598 uirelroot = match.uipath(relroot)
1603 uirelroot = match.uipath(relroot)
1599 relroot += '/'
1604 relroot += '/'
1600 for matchroot in match.files():
1605 for matchroot in match.files():
1601 if not matchroot.startswith(relroot):
1606 if not matchroot.startswith(relroot):
1602 ui.warn(_('warning: %s not inside relative root %s\n') % (
1607 ui.warn(_('warning: %s not inside relative root %s\n') % (
1603 match.uipath(matchroot), uirelroot))
1608 match.uipath(matchroot), uirelroot))
1604
1609
1605 if stat:
1610 if stat:
1606 diffopts = diffopts.copy(context=0, noprefix=False)
1611 diffopts = diffopts.copy(context=0, noprefix=False)
1607 width = 80
1612 width = 80
1608 if not ui.plain():
1613 if not ui.plain():
1609 width = ui.termwidth()
1614 width = ui.termwidth()
1610 chunks = patch.diff(repo, node1, node2, match, changes, opts=diffopts,
1615 chunks = patch.diff(repo, node1, node2, match, changes, opts=diffopts,
1611 prefix=prefix, relroot=relroot,
1616 prefix=prefix, relroot=relroot,
1612 hunksfilterfn=hunksfilterfn)
1617 hunksfilterfn=hunksfilterfn)
1613 for chunk, label in patch.diffstatui(util.iterlines(chunks),
1618 for chunk, label in patch.diffstatui(util.iterlines(chunks),
1614 width=width):
1619 width=width):
1615 write(chunk, label=label)
1620 write(chunk, label=label)
1616 else:
1621 else:
1617 for chunk, label in patch.diffui(repo, node1, node2, match,
1622 for chunk, label in patch.diffui(repo, node1, node2, match,
1618 changes, opts=diffopts, prefix=prefix,
1623 changes, opts=diffopts, prefix=prefix,
1619 relroot=relroot,
1624 relroot=relroot,
1620 hunksfilterfn=hunksfilterfn):
1625 hunksfilterfn=hunksfilterfn):
1621 write(chunk, label=label)
1626 write(chunk, label=label)
1622
1627
1623 if listsubrepos:
1628 if listsubrepos:
1624 ctx1 = repo[node1]
1629 ctx1 = repo[node1]
1625 ctx2 = repo[node2]
1630 ctx2 = repo[node2]
1626 for subpath, sub in scmutil.itersubrepos(ctx1, ctx2):
1631 for subpath, sub in scmutil.itersubrepos(ctx1, ctx2):
1627 tempnode2 = node2
1632 tempnode2 = node2
1628 try:
1633 try:
1629 if node2 is not None:
1634 if node2 is not None:
1630 tempnode2 = ctx2.substate[subpath][1]
1635 tempnode2 = ctx2.substate[subpath][1]
1631 except KeyError:
1636 except KeyError:
1632 # A subrepo that existed in node1 was deleted between node1 and
1637 # A subrepo that existed in node1 was deleted between node1 and
1633 # node2 (inclusive). Thus, ctx2's substate won't contain that
1638 # node2 (inclusive). Thus, ctx2's substate won't contain that
1634 # subpath. The best we can do is to ignore it.
1639 # subpath. The best we can do is to ignore it.
1635 tempnode2 = None
1640 tempnode2 = None
1636 submatch = matchmod.subdirmatcher(subpath, match)
1641 submatch = matchmod.subdirmatcher(subpath, match)
1637 sub.diff(ui, diffopts, tempnode2, submatch, changes=changes,
1642 sub.diff(ui, diffopts, tempnode2, submatch, changes=changes,
1638 stat=stat, fp=fp, prefix=prefix)
1643 stat=stat, fp=fp, prefix=prefix)
1639
1644
1640 def _changesetlabels(ctx):
1645 def _changesetlabels(ctx):
1641 labels = ['log.changeset', 'changeset.%s' % ctx.phasestr()]
1646 labels = ['log.changeset', 'changeset.%s' % ctx.phasestr()]
1642 if ctx.obsolete():
1647 if ctx.obsolete():
1643 labels.append('changeset.obsolete')
1648 labels.append('changeset.obsolete')
1644 if ctx.isunstable():
1649 if ctx.isunstable():
1645 labels.append('changeset.unstable')
1650 labels.append('changeset.unstable')
1646 for instability in ctx.instabilities():
1651 for instability in ctx.instabilities():
1647 labels.append('instability.%s' % instability)
1652 labels.append('instability.%s' % instability)
1648 return ' '.join(labels)
1653 return ' '.join(labels)
1649
1654
1650 class changeset_printer(object):
1655 class changeset_printer(object):
1651 '''show changeset information when templating not requested.'''
1656 '''show changeset information when templating not requested.'''
1652
1657
1653 def __init__(self, ui, repo, matchfn, diffopts, buffered):
1658 def __init__(self, ui, repo, matchfn, diffopts, buffered):
1654 self.ui = ui
1659 self.ui = ui
1655 self.repo = repo
1660 self.repo = repo
1656 self.buffered = buffered
1661 self.buffered = buffered
1657 self.matchfn = matchfn
1662 self.matchfn = matchfn
1658 self.diffopts = diffopts
1663 self.diffopts = diffopts
1659 self.header = {}
1664 self.header = {}
1660 self.hunk = {}
1665 self.hunk = {}
1661 self.lastheader = None
1666 self.lastheader = None
1662 self.footer = None
1667 self.footer = None
1663 self._columns = templatekw.getlogcolumns()
1668 self._columns = templatekw.getlogcolumns()
1664
1669
1665 def flush(self, ctx):
1670 def flush(self, ctx):
1666 rev = ctx.rev()
1671 rev = ctx.rev()
1667 if rev in self.header:
1672 if rev in self.header:
1668 h = self.header[rev]
1673 h = self.header[rev]
1669 if h != self.lastheader:
1674 if h != self.lastheader:
1670 self.lastheader = h
1675 self.lastheader = h
1671 self.ui.write(h)
1676 self.ui.write(h)
1672 del self.header[rev]
1677 del self.header[rev]
1673 if rev in self.hunk:
1678 if rev in self.hunk:
1674 self.ui.write(self.hunk[rev])
1679 self.ui.write(self.hunk[rev])
1675 del self.hunk[rev]
1680 del self.hunk[rev]
1676
1681
1677 def close(self):
1682 def close(self):
1678 if self.footer:
1683 if self.footer:
1679 self.ui.write(self.footer)
1684 self.ui.write(self.footer)
1680
1685
1681 def show(self, ctx, copies=None, matchfn=None, hunksfilterfn=None,
1686 def show(self, ctx, copies=None, matchfn=None, hunksfilterfn=None,
1682 **props):
1687 **props):
1683 props = pycompat.byteskwargs(props)
1688 props = pycompat.byteskwargs(props)
1684 if self.buffered:
1689 if self.buffered:
1685 self.ui.pushbuffer(labeled=True)
1690 self.ui.pushbuffer(labeled=True)
1686 self._show(ctx, copies, matchfn, hunksfilterfn, props)
1691 self._show(ctx, copies, matchfn, hunksfilterfn, props)
1687 self.hunk[ctx.rev()] = self.ui.popbuffer()
1692 self.hunk[ctx.rev()] = self.ui.popbuffer()
1688 else:
1693 else:
1689 self._show(ctx, copies, matchfn, hunksfilterfn, props)
1694 self._show(ctx, copies, matchfn, hunksfilterfn, props)
1690
1695
1691 def _show(self, ctx, copies, matchfn, hunksfilterfn, props):
1696 def _show(self, ctx, copies, matchfn, hunksfilterfn, props):
1692 '''show a single changeset or file revision'''
1697 '''show a single changeset or file revision'''
1693 changenode = ctx.node()
1698 changenode = ctx.node()
1694 rev = ctx.rev()
1699 rev = ctx.rev()
1695
1700
1696 if self.ui.quiet:
1701 if self.ui.quiet:
1697 self.ui.write("%s\n" % scmutil.formatchangeid(ctx),
1702 self.ui.write("%s\n" % scmutil.formatchangeid(ctx),
1698 label='log.node')
1703 label='log.node')
1699 return
1704 return
1700
1705
1701 columns = self._columns
1706 columns = self._columns
1702 self.ui.write(columns['changeset'] % scmutil.formatchangeid(ctx),
1707 self.ui.write(columns['changeset'] % scmutil.formatchangeid(ctx),
1703 label=_changesetlabels(ctx))
1708 label=_changesetlabels(ctx))
1704
1709
1705 # branches are shown first before any other names due to backwards
1710 # branches are shown first before any other names due to backwards
1706 # compatibility
1711 # compatibility
1707 branch = ctx.branch()
1712 branch = ctx.branch()
1708 # don't show the default branch name
1713 # don't show the default branch name
1709 if branch != 'default':
1714 if branch != 'default':
1710 self.ui.write(columns['branch'] % branch, label='log.branch')
1715 self.ui.write(columns['branch'] % branch, label='log.branch')
1711
1716
1712 for nsname, ns in self.repo.names.iteritems():
1717 for nsname, ns in self.repo.names.iteritems():
1713 # branches has special logic already handled above, so here we just
1718 # branches has special logic already handled above, so here we just
1714 # skip it
1719 # skip it
1715 if nsname == 'branches':
1720 if nsname == 'branches':
1716 continue
1721 continue
1717 # we will use the templatename as the color name since those two
1722 # we will use the templatename as the color name since those two
1718 # should be the same
1723 # should be the same
1719 for name in ns.names(self.repo, changenode):
1724 for name in ns.names(self.repo, changenode):
1720 self.ui.write(ns.logfmt % name,
1725 self.ui.write(ns.logfmt % name,
1721 label='log.%s' % ns.colorname)
1726 label='log.%s' % ns.colorname)
1722 if self.ui.debugflag:
1727 if self.ui.debugflag:
1723 self.ui.write(columns['phase'] % ctx.phasestr(), label='log.phase')
1728 self.ui.write(columns['phase'] % ctx.phasestr(), label='log.phase')
1724 for pctx in scmutil.meaningfulparents(self.repo, ctx):
1729 for pctx in scmutil.meaningfulparents(self.repo, ctx):
1725 label = 'log.parent changeset.%s' % pctx.phasestr()
1730 label = 'log.parent changeset.%s' % pctx.phasestr()
1726 self.ui.write(columns['parent'] % scmutil.formatchangeid(pctx),
1731 self.ui.write(columns['parent'] % scmutil.formatchangeid(pctx),
1727 label=label)
1732 label=label)
1728
1733
1729 if self.ui.debugflag and rev is not None:
1734 if self.ui.debugflag and rev is not None:
1730 mnode = ctx.manifestnode()
1735 mnode = ctx.manifestnode()
1731 mrev = self.repo.manifestlog._revlog.rev(mnode)
1736 mrev = self.repo.manifestlog._revlog.rev(mnode)
1732 self.ui.write(columns['manifest']
1737 self.ui.write(columns['manifest']
1733 % scmutil.formatrevnode(self.ui, mrev, mnode),
1738 % scmutil.formatrevnode(self.ui, mrev, mnode),
1734 label='ui.debug log.manifest')
1739 label='ui.debug log.manifest')
1735 self.ui.write(columns['user'] % ctx.user(), label='log.user')
1740 self.ui.write(columns['user'] % ctx.user(), label='log.user')
1736 self.ui.write(columns['date'] % util.datestr(ctx.date()),
1741 self.ui.write(columns['date'] % util.datestr(ctx.date()),
1737 label='log.date')
1742 label='log.date')
1738
1743
1739 if ctx.isunstable():
1744 if ctx.isunstable():
1740 instabilities = ctx.instabilities()
1745 instabilities = ctx.instabilities()
1741 self.ui.write(columns['instability'] % ', '.join(instabilities),
1746 self.ui.write(columns['instability'] % ', '.join(instabilities),
1742 label='log.instability')
1747 label='log.instability')
1743
1748
1744 elif ctx.obsolete():
1749 elif ctx.obsolete():
1745 self._showobsfate(ctx)
1750 self._showobsfate(ctx)
1746
1751
1747 self._exthook(ctx)
1752 self._exthook(ctx)
1748
1753
1749 if self.ui.debugflag:
1754 if self.ui.debugflag:
1750 files = ctx.p1().status(ctx)[:3]
1755 files = ctx.p1().status(ctx)[:3]
1751 for key, value in zip(['files', 'files+', 'files-'], files):
1756 for key, value in zip(['files', 'files+', 'files-'], files):
1752 if value:
1757 if value:
1753 self.ui.write(columns[key] % " ".join(value),
1758 self.ui.write(columns[key] % " ".join(value),
1754 label='ui.debug log.files')
1759 label='ui.debug log.files')
1755 elif ctx.files() and self.ui.verbose:
1760 elif ctx.files() and self.ui.verbose:
1756 self.ui.write(columns['files'] % " ".join(ctx.files()),
1761 self.ui.write(columns['files'] % " ".join(ctx.files()),
1757 label='ui.note log.files')
1762 label='ui.note log.files')
1758 if copies and self.ui.verbose:
1763 if copies and self.ui.verbose:
1759 copies = ['%s (%s)' % c for c in copies]
1764 copies = ['%s (%s)' % c for c in copies]
1760 self.ui.write(columns['copies'] % ' '.join(copies),
1765 self.ui.write(columns['copies'] % ' '.join(copies),
1761 label='ui.note log.copies')
1766 label='ui.note log.copies')
1762
1767
1763 extra = ctx.extra()
1768 extra = ctx.extra()
1764 if extra and self.ui.debugflag:
1769 if extra and self.ui.debugflag:
1765 for key, value in sorted(extra.items()):
1770 for key, value in sorted(extra.items()):
1766 self.ui.write(columns['extra'] % (key, util.escapestr(value)),
1771 self.ui.write(columns['extra'] % (key, util.escapestr(value)),
1767 label='ui.debug log.extra')
1772 label='ui.debug log.extra')
1768
1773
1769 description = ctx.description().strip()
1774 description = ctx.description().strip()
1770 if description:
1775 if description:
1771 if self.ui.verbose:
1776 if self.ui.verbose:
1772 self.ui.write(_("description:\n"),
1777 self.ui.write(_("description:\n"),
1773 label='ui.note log.description')
1778 label='ui.note log.description')
1774 self.ui.write(description,
1779 self.ui.write(description,
1775 label='ui.note log.description')
1780 label='ui.note log.description')
1776 self.ui.write("\n\n")
1781 self.ui.write("\n\n")
1777 else:
1782 else:
1778 self.ui.write(columns['summary'] % description.splitlines()[0],
1783 self.ui.write(columns['summary'] % description.splitlines()[0],
1779 label='log.summary')
1784 label='log.summary')
1780 self.ui.write("\n")
1785 self.ui.write("\n")
1781
1786
1782 self.showpatch(ctx, matchfn, hunksfilterfn=hunksfilterfn)
1787 self.showpatch(ctx, matchfn, hunksfilterfn=hunksfilterfn)
1783
1788
1784 def _showobsfate(self, ctx):
1789 def _showobsfate(self, ctx):
1785 obsfate = templatekw.showobsfate(repo=self.repo, ctx=ctx, ui=self.ui)
1790 obsfate = templatekw.showobsfate(repo=self.repo, ctx=ctx, ui=self.ui)
1786
1791
1787 if obsfate:
1792 if obsfate:
1788 for obsfateline in obsfate:
1793 for obsfateline in obsfate:
1789 self.ui.write(self._columns['obsolete'] % obsfateline,
1794 self.ui.write(self._columns['obsolete'] % obsfateline,
1790 label='log.obsfate')
1795 label='log.obsfate')
1791
1796
1792 def _exthook(self, ctx):
1797 def _exthook(self, ctx):
1793 '''empty method used by extension as a hook point
1798 '''empty method used by extension as a hook point
1794 '''
1799 '''
1795
1800
1796 def showpatch(self, ctx, matchfn, hunksfilterfn=None):
1801 def showpatch(self, ctx, matchfn, hunksfilterfn=None):
1797 if not matchfn:
1802 if not matchfn:
1798 matchfn = self.matchfn
1803 matchfn = self.matchfn
1799 if matchfn:
1804 if matchfn:
1800 stat = self.diffopts.get('stat')
1805 stat = self.diffopts.get('stat')
1801 diff = self.diffopts.get('patch')
1806 diff = self.diffopts.get('patch')
1802 diffopts = patch.diffallopts(self.ui, self.diffopts)
1807 diffopts = patch.diffallopts(self.ui, self.diffopts)
1803 node = ctx.node()
1808 node = ctx.node()
1804 prev = ctx.p1().node()
1809 prev = ctx.p1().node()
1805 if stat:
1810 if stat:
1806 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1811 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1807 match=matchfn, stat=True,
1812 match=matchfn, stat=True,
1808 hunksfilterfn=hunksfilterfn)
1813 hunksfilterfn=hunksfilterfn)
1809 if diff:
1814 if diff:
1810 if stat:
1815 if stat:
1811 self.ui.write("\n")
1816 self.ui.write("\n")
1812 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1817 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1813 match=matchfn, stat=False,
1818 match=matchfn, stat=False,
1814 hunksfilterfn=hunksfilterfn)
1819 hunksfilterfn=hunksfilterfn)
1815 if stat or diff:
1820 if stat or diff:
1816 self.ui.write("\n")
1821 self.ui.write("\n")
1817
1822
1818 class jsonchangeset(changeset_printer):
1823 class jsonchangeset(changeset_printer):
1819 '''format changeset information.'''
1824 '''format changeset information.'''
1820
1825
1821 def __init__(self, ui, repo, matchfn, diffopts, buffered):
1826 def __init__(self, ui, repo, matchfn, diffopts, buffered):
1822 changeset_printer.__init__(self, ui, repo, matchfn, diffopts, buffered)
1827 changeset_printer.__init__(self, ui, repo, matchfn, diffopts, buffered)
1823 self.cache = {}
1828 self.cache = {}
1824 self._first = True
1829 self._first = True
1825
1830
1826 def close(self):
1831 def close(self):
1827 if not self._first:
1832 if not self._first:
1828 self.ui.write("\n]\n")
1833 self.ui.write("\n]\n")
1829 else:
1834 else:
1830 self.ui.write("[]\n")
1835 self.ui.write("[]\n")
1831
1836
1832 def _show(self, ctx, copies, matchfn, hunksfilterfn, props):
1837 def _show(self, ctx, copies, matchfn, hunksfilterfn, props):
1833 '''show a single changeset or file revision'''
1838 '''show a single changeset or file revision'''
1834 rev = ctx.rev()
1839 rev = ctx.rev()
1835 if rev is None:
1840 if rev is None:
1836 jrev = jnode = 'null'
1841 jrev = jnode = 'null'
1837 else:
1842 else:
1838 jrev = '%d' % rev
1843 jrev = '%d' % rev
1839 jnode = '"%s"' % hex(ctx.node())
1844 jnode = '"%s"' % hex(ctx.node())
1840 j = encoding.jsonescape
1845 j = encoding.jsonescape
1841
1846
1842 if self._first:
1847 if self._first:
1843 self.ui.write("[\n {")
1848 self.ui.write("[\n {")
1844 self._first = False
1849 self._first = False
1845 else:
1850 else:
1846 self.ui.write(",\n {")
1851 self.ui.write(",\n {")
1847
1852
1848 if self.ui.quiet:
1853 if self.ui.quiet:
1849 self.ui.write(('\n "rev": %s') % jrev)
1854 self.ui.write(('\n "rev": %s') % jrev)
1850 self.ui.write((',\n "node": %s') % jnode)
1855 self.ui.write((',\n "node": %s') % jnode)
1851 self.ui.write('\n }')
1856 self.ui.write('\n }')
1852 return
1857 return
1853
1858
1854 self.ui.write(('\n "rev": %s') % jrev)
1859 self.ui.write(('\n "rev": %s') % jrev)
1855 self.ui.write((',\n "node": %s') % jnode)
1860 self.ui.write((',\n "node": %s') % jnode)
1856 self.ui.write((',\n "branch": "%s"') % j(ctx.branch()))
1861 self.ui.write((',\n "branch": "%s"') % j(ctx.branch()))
1857 self.ui.write((',\n "phase": "%s"') % ctx.phasestr())
1862 self.ui.write((',\n "phase": "%s"') % ctx.phasestr())
1858 self.ui.write((',\n "user": "%s"') % j(ctx.user()))
1863 self.ui.write((',\n "user": "%s"') % j(ctx.user()))
1859 self.ui.write((',\n "date": [%d, %d]') % ctx.date())
1864 self.ui.write((',\n "date": [%d, %d]') % ctx.date())
1860 self.ui.write((',\n "desc": "%s"') % j(ctx.description()))
1865 self.ui.write((',\n "desc": "%s"') % j(ctx.description()))
1861
1866
1862 self.ui.write((',\n "bookmarks": [%s]') %
1867 self.ui.write((',\n "bookmarks": [%s]') %
1863 ", ".join('"%s"' % j(b) for b in ctx.bookmarks()))
1868 ", ".join('"%s"' % j(b) for b in ctx.bookmarks()))
1864 self.ui.write((',\n "tags": [%s]') %
1869 self.ui.write((',\n "tags": [%s]') %
1865 ", ".join('"%s"' % j(t) for t in ctx.tags()))
1870 ", ".join('"%s"' % j(t) for t in ctx.tags()))
1866 self.ui.write((',\n "parents": [%s]') %
1871 self.ui.write((',\n "parents": [%s]') %
1867 ", ".join('"%s"' % c.hex() for c in ctx.parents()))
1872 ", ".join('"%s"' % c.hex() for c in ctx.parents()))
1868
1873
1869 if self.ui.debugflag:
1874 if self.ui.debugflag:
1870 if rev is None:
1875 if rev is None:
1871 jmanifestnode = 'null'
1876 jmanifestnode = 'null'
1872 else:
1877 else:
1873 jmanifestnode = '"%s"' % hex(ctx.manifestnode())
1878 jmanifestnode = '"%s"' % hex(ctx.manifestnode())
1874 self.ui.write((',\n "manifest": %s') % jmanifestnode)
1879 self.ui.write((',\n "manifest": %s') % jmanifestnode)
1875
1880
1876 self.ui.write((',\n "extra": {%s}') %
1881 self.ui.write((',\n "extra": {%s}') %
1877 ", ".join('"%s": "%s"' % (j(k), j(v))
1882 ", ".join('"%s": "%s"' % (j(k), j(v))
1878 for k, v in ctx.extra().items()))
1883 for k, v in ctx.extra().items()))
1879
1884
1880 files = ctx.p1().status(ctx)
1885 files = ctx.p1().status(ctx)
1881 self.ui.write((',\n "modified": [%s]') %
1886 self.ui.write((',\n "modified": [%s]') %
1882 ", ".join('"%s"' % j(f) for f in files[0]))
1887 ", ".join('"%s"' % j(f) for f in files[0]))
1883 self.ui.write((',\n "added": [%s]') %
1888 self.ui.write((',\n "added": [%s]') %
1884 ", ".join('"%s"' % j(f) for f in files[1]))
1889 ", ".join('"%s"' % j(f) for f in files[1]))
1885 self.ui.write((',\n "removed": [%s]') %
1890 self.ui.write((',\n "removed": [%s]') %
1886 ", ".join('"%s"' % j(f) for f in files[2]))
1891 ", ".join('"%s"' % j(f) for f in files[2]))
1887
1892
1888 elif self.ui.verbose:
1893 elif self.ui.verbose:
1889 self.ui.write((',\n "files": [%s]') %
1894 self.ui.write((',\n "files": [%s]') %
1890 ", ".join('"%s"' % j(f) for f in ctx.files()))
1895 ", ".join('"%s"' % j(f) for f in ctx.files()))
1891
1896
1892 if copies:
1897 if copies:
1893 self.ui.write((',\n "copies": {%s}') %
1898 self.ui.write((',\n "copies": {%s}') %
1894 ", ".join('"%s": "%s"' % (j(k), j(v))
1899 ", ".join('"%s": "%s"' % (j(k), j(v))
1895 for k, v in copies))
1900 for k, v in copies))
1896
1901
1897 matchfn = self.matchfn
1902 matchfn = self.matchfn
1898 if matchfn:
1903 if matchfn:
1899 stat = self.diffopts.get('stat')
1904 stat = self.diffopts.get('stat')
1900 diff = self.diffopts.get('patch')
1905 diff = self.diffopts.get('patch')
1901 diffopts = patch.difffeatureopts(self.ui, self.diffopts, git=True)
1906 diffopts = patch.difffeatureopts(self.ui, self.diffopts, git=True)
1902 node, prev = ctx.node(), ctx.p1().node()
1907 node, prev = ctx.node(), ctx.p1().node()
1903 if stat:
1908 if stat:
1904 self.ui.pushbuffer()
1909 self.ui.pushbuffer()
1905 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1910 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1906 match=matchfn, stat=True)
1911 match=matchfn, stat=True)
1907 self.ui.write((',\n "diffstat": "%s"')
1912 self.ui.write((',\n "diffstat": "%s"')
1908 % j(self.ui.popbuffer()))
1913 % j(self.ui.popbuffer()))
1909 if diff:
1914 if diff:
1910 self.ui.pushbuffer()
1915 self.ui.pushbuffer()
1911 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1916 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1912 match=matchfn, stat=False)
1917 match=matchfn, stat=False)
1913 self.ui.write((',\n "diff": "%s"') % j(self.ui.popbuffer()))
1918 self.ui.write((',\n "diff": "%s"') % j(self.ui.popbuffer()))
1914
1919
1915 self.ui.write("\n }")
1920 self.ui.write("\n }")
1916
1921
1917 class changeset_templater(changeset_printer):
1922 class changeset_templater(changeset_printer):
1918 '''format changeset information.
1923 '''format changeset information.
1919
1924
1920 Note: there are a variety of convenience functions to build a
1925 Note: there are a variety of convenience functions to build a
1921 changeset_templater for common cases. See functions such as:
1926 changeset_templater for common cases. See functions such as:
1922 makelogtemplater, show_changeset, buildcommittemplate, or other
1927 makelogtemplater, show_changeset, buildcommittemplate, or other
1923 functions that use changesest_templater.
1928 functions that use changesest_templater.
1924 '''
1929 '''
1925
1930
1926 # Arguments before "buffered" used to be positional. Consider not
1931 # Arguments before "buffered" used to be positional. Consider not
1927 # adding/removing arguments before "buffered" to not break callers.
1932 # adding/removing arguments before "buffered" to not break callers.
1928 def __init__(self, ui, repo, tmplspec, matchfn=None, diffopts=None,
1933 def __init__(self, ui, repo, tmplspec, matchfn=None, diffopts=None,
1929 buffered=False):
1934 buffered=False):
1930 diffopts = diffopts or {}
1935 diffopts = diffopts or {}
1931
1936
1932 changeset_printer.__init__(self, ui, repo, matchfn, diffopts, buffered)
1937 changeset_printer.__init__(self, ui, repo, matchfn, diffopts, buffered)
1933 tres = formatter.templateresources(ui, repo)
1938 tres = formatter.templateresources(ui, repo)
1934 self.t = formatter.loadtemplater(ui, tmplspec,
1939 self.t = formatter.loadtemplater(ui, tmplspec,
1935 defaults=templatekw.keywords,
1940 defaults=templatekw.keywords,
1936 resources=tres,
1941 resources=tres,
1937 cache=templatekw.defaulttempl)
1942 cache=templatekw.defaulttempl)
1938 self._counter = itertools.count()
1943 self._counter = itertools.count()
1939 self.cache = tres['cache'] # shared with _graphnodeformatter()
1944 self.cache = tres['cache'] # shared with _graphnodeformatter()
1940
1945
1941 self._tref = tmplspec.ref
1946 self._tref = tmplspec.ref
1942 self._parts = {'header': '', 'footer': '',
1947 self._parts = {'header': '', 'footer': '',
1943 tmplspec.ref: tmplspec.ref,
1948 tmplspec.ref: tmplspec.ref,
1944 'docheader': '', 'docfooter': '',
1949 'docheader': '', 'docfooter': '',
1945 'separator': ''}
1950 'separator': ''}
1946 if tmplspec.mapfile:
1951 if tmplspec.mapfile:
1947 # find correct templates for current mode, for backward
1952 # find correct templates for current mode, for backward
1948 # compatibility with 'log -v/-q/--debug' using a mapfile
1953 # compatibility with 'log -v/-q/--debug' using a mapfile
1949 tmplmodes = [
1954 tmplmodes = [
1950 (True, ''),
1955 (True, ''),
1951 (self.ui.verbose, '_verbose'),
1956 (self.ui.verbose, '_verbose'),
1952 (self.ui.quiet, '_quiet'),
1957 (self.ui.quiet, '_quiet'),
1953 (self.ui.debugflag, '_debug'),
1958 (self.ui.debugflag, '_debug'),
1954 ]
1959 ]
1955 for mode, postfix in tmplmodes:
1960 for mode, postfix in tmplmodes:
1956 for t in self._parts:
1961 for t in self._parts:
1957 cur = t + postfix
1962 cur = t + postfix
1958 if mode and cur in self.t:
1963 if mode and cur in self.t:
1959 self._parts[t] = cur
1964 self._parts[t] = cur
1960 else:
1965 else:
1961 partnames = [p for p in self._parts.keys() if p != tmplspec.ref]
1966 partnames = [p for p in self._parts.keys() if p != tmplspec.ref]
1962 m = formatter.templatepartsmap(tmplspec, self.t, partnames)
1967 m = formatter.templatepartsmap(tmplspec, self.t, partnames)
1963 self._parts.update(m)
1968 self._parts.update(m)
1964
1969
1965 if self._parts['docheader']:
1970 if self._parts['docheader']:
1966 self.ui.write(templater.stringify(self.t(self._parts['docheader'])))
1971 self.ui.write(templater.stringify(self.t(self._parts['docheader'])))
1967
1972
1968 def close(self):
1973 def close(self):
1969 if self._parts['docfooter']:
1974 if self._parts['docfooter']:
1970 if not self.footer:
1975 if not self.footer:
1971 self.footer = ""
1976 self.footer = ""
1972 self.footer += templater.stringify(self.t(self._parts['docfooter']))
1977 self.footer += templater.stringify(self.t(self._parts['docfooter']))
1973 return super(changeset_templater, self).close()
1978 return super(changeset_templater, self).close()
1974
1979
1975 def _show(self, ctx, copies, matchfn, hunksfilterfn, props):
1980 def _show(self, ctx, copies, matchfn, hunksfilterfn, props):
1976 '''show a single changeset or file revision'''
1981 '''show a single changeset or file revision'''
1977 props = props.copy()
1982 props = props.copy()
1978 props['ctx'] = ctx
1983 props['ctx'] = ctx
1979 props['index'] = index = next(self._counter)
1984 props['index'] = index = next(self._counter)
1980 props['revcache'] = {'copies': copies}
1985 props['revcache'] = {'copies': copies}
1981 props = pycompat.strkwargs(props)
1986 props = pycompat.strkwargs(props)
1982
1987
1983 # write separator, which wouldn't work well with the header part below
1988 # write separator, which wouldn't work well with the header part below
1984 # since there's inherently a conflict between header (across items) and
1989 # since there's inherently a conflict between header (across items) and
1985 # separator (per item)
1990 # separator (per item)
1986 if self._parts['separator'] and index > 0:
1991 if self._parts['separator'] and index > 0:
1987 self.ui.write(templater.stringify(self.t(self._parts['separator'])))
1992 self.ui.write(templater.stringify(self.t(self._parts['separator'])))
1988
1993
1989 # write header
1994 # write header
1990 if self._parts['header']:
1995 if self._parts['header']:
1991 h = templater.stringify(self.t(self._parts['header'], **props))
1996 h = templater.stringify(self.t(self._parts['header'], **props))
1992 if self.buffered:
1997 if self.buffered:
1993 self.header[ctx.rev()] = h
1998 self.header[ctx.rev()] = h
1994 else:
1999 else:
1995 if self.lastheader != h:
2000 if self.lastheader != h:
1996 self.lastheader = h
2001 self.lastheader = h
1997 self.ui.write(h)
2002 self.ui.write(h)
1998
2003
1999 # write changeset metadata, then patch if requested
2004 # write changeset metadata, then patch if requested
2000 key = self._parts[self._tref]
2005 key = self._parts[self._tref]
2001 self.ui.write(templater.stringify(self.t(key, **props)))
2006 self.ui.write(templater.stringify(self.t(key, **props)))
2002 self.showpatch(ctx, matchfn, hunksfilterfn=hunksfilterfn)
2007 self.showpatch(ctx, matchfn, hunksfilterfn=hunksfilterfn)
2003
2008
2004 if self._parts['footer']:
2009 if self._parts['footer']:
2005 if not self.footer:
2010 if not self.footer:
2006 self.footer = templater.stringify(
2011 self.footer = templater.stringify(
2007 self.t(self._parts['footer'], **props))
2012 self.t(self._parts['footer'], **props))
2008
2013
2009 def logtemplatespec(tmpl, mapfile):
2014 def logtemplatespec(tmpl, mapfile):
2010 if mapfile:
2015 if mapfile:
2011 return formatter.templatespec('changeset', tmpl, mapfile)
2016 return formatter.templatespec('changeset', tmpl, mapfile)
2012 else:
2017 else:
2013 return formatter.templatespec('', tmpl, None)
2018 return formatter.templatespec('', tmpl, None)
2014
2019
2015 def _lookuplogtemplate(ui, tmpl, style):
2020 def _lookuplogtemplate(ui, tmpl, style):
2016 """Find the template matching the given template spec or style
2021 """Find the template matching the given template spec or style
2017
2022
2018 See formatter.lookuptemplate() for details.
2023 See formatter.lookuptemplate() for details.
2019 """
2024 """
2020
2025
2021 # ui settings
2026 # ui settings
2022 if not tmpl and not style: # template are stronger than style
2027 if not tmpl and not style: # template are stronger than style
2023 tmpl = ui.config('ui', 'logtemplate')
2028 tmpl = ui.config('ui', 'logtemplate')
2024 if tmpl:
2029 if tmpl:
2025 return logtemplatespec(templater.unquotestring(tmpl), None)
2030 return logtemplatespec(templater.unquotestring(tmpl), None)
2026 else:
2031 else:
2027 style = util.expandpath(ui.config('ui', 'style'))
2032 style = util.expandpath(ui.config('ui', 'style'))
2028
2033
2029 if not tmpl and style:
2034 if not tmpl and style:
2030 mapfile = style
2035 mapfile = style
2031 if not os.path.split(mapfile)[0]:
2036 if not os.path.split(mapfile)[0]:
2032 mapname = (templater.templatepath('map-cmdline.' + mapfile)
2037 mapname = (templater.templatepath('map-cmdline.' + mapfile)
2033 or templater.templatepath(mapfile))
2038 or templater.templatepath(mapfile))
2034 if mapname:
2039 if mapname:
2035 mapfile = mapname
2040 mapfile = mapname
2036 return logtemplatespec(None, mapfile)
2041 return logtemplatespec(None, mapfile)
2037
2042
2038 if not tmpl:
2043 if not tmpl:
2039 return logtemplatespec(None, None)
2044 return logtemplatespec(None, None)
2040
2045
2041 return formatter.lookuptemplate(ui, 'changeset', tmpl)
2046 return formatter.lookuptemplate(ui, 'changeset', tmpl)
2042
2047
2043 def makelogtemplater(ui, repo, tmpl, buffered=False):
2048 def makelogtemplater(ui, repo, tmpl, buffered=False):
2044 """Create a changeset_templater from a literal template 'tmpl'
2049 """Create a changeset_templater from a literal template 'tmpl'
2045 byte-string."""
2050 byte-string."""
2046 spec = logtemplatespec(tmpl, None)
2051 spec = logtemplatespec(tmpl, None)
2047 return changeset_templater(ui, repo, spec, buffered=buffered)
2052 return changeset_templater(ui, repo, spec, buffered=buffered)
2048
2053
2049 def show_changeset(ui, repo, opts, buffered=False):
2054 def show_changeset(ui, repo, opts, buffered=False):
2050 """show one changeset using template or regular display.
2055 """show one changeset using template or regular display.
2051
2056
2052 Display format will be the first non-empty hit of:
2057 Display format will be the first non-empty hit of:
2053 1. option 'template'
2058 1. option 'template'
2054 2. option 'style'
2059 2. option 'style'
2055 3. [ui] setting 'logtemplate'
2060 3. [ui] setting 'logtemplate'
2056 4. [ui] setting 'style'
2061 4. [ui] setting 'style'
2057 If all of these values are either the unset or the empty string,
2062 If all of these values are either the unset or the empty string,
2058 regular display via changeset_printer() is done.
2063 regular display via changeset_printer() is done.
2059 """
2064 """
2060 # options
2065 # options
2061 match = None
2066 match = None
2062 if opts.get('patch') or opts.get('stat'):
2067 if opts.get('patch') or opts.get('stat'):
2063 match = scmutil.matchall(repo)
2068 match = scmutil.matchall(repo)
2064
2069
2065 if opts.get('template') == 'json':
2070 if opts.get('template') == 'json':
2066 return jsonchangeset(ui, repo, match, opts, buffered)
2071 return jsonchangeset(ui, repo, match, opts, buffered)
2067
2072
2068 spec = _lookuplogtemplate(ui, opts.get('template'), opts.get('style'))
2073 spec = _lookuplogtemplate(ui, opts.get('template'), opts.get('style'))
2069
2074
2070 if not spec.ref and not spec.tmpl and not spec.mapfile:
2075 if not spec.ref and not spec.tmpl and not spec.mapfile:
2071 return changeset_printer(ui, repo, match, opts, buffered)
2076 return changeset_printer(ui, repo, match, opts, buffered)
2072
2077
2073 return changeset_templater(ui, repo, spec, match, opts, buffered)
2078 return changeset_templater(ui, repo, spec, match, opts, buffered)
2074
2079
2075 def showmarker(fm, marker, index=None):
2080 def showmarker(fm, marker, index=None):
2076 """utility function to display obsolescence marker in a readable way
2081 """utility function to display obsolescence marker in a readable way
2077
2082
2078 To be used by debug function."""
2083 To be used by debug function."""
2079 if index is not None:
2084 if index is not None:
2080 fm.write('index', '%i ', index)
2085 fm.write('index', '%i ', index)
2081 fm.write('prednode', '%s ', hex(marker.prednode()))
2086 fm.write('prednode', '%s ', hex(marker.prednode()))
2082 succs = marker.succnodes()
2087 succs = marker.succnodes()
2083 fm.condwrite(succs, 'succnodes', '%s ',
2088 fm.condwrite(succs, 'succnodes', '%s ',
2084 fm.formatlist(map(hex, succs), name='node'))
2089 fm.formatlist(map(hex, succs), name='node'))
2085 fm.write('flag', '%X ', marker.flags())
2090 fm.write('flag', '%X ', marker.flags())
2086 parents = marker.parentnodes()
2091 parents = marker.parentnodes()
2087 if parents is not None:
2092 if parents is not None:
2088 fm.write('parentnodes', '{%s} ',
2093 fm.write('parentnodes', '{%s} ',
2089 fm.formatlist(map(hex, parents), name='node', sep=', '))
2094 fm.formatlist(map(hex, parents), name='node', sep=', '))
2090 fm.write('date', '(%s) ', fm.formatdate(marker.date()))
2095 fm.write('date', '(%s) ', fm.formatdate(marker.date()))
2091 meta = marker.metadata().copy()
2096 meta = marker.metadata().copy()
2092 meta.pop('date', None)
2097 meta.pop('date', None)
2093 fm.write('metadata', '{%s}', fm.formatdict(meta, fmt='%r: %r', sep=', '))
2098 fm.write('metadata', '{%s}', fm.formatdict(meta, fmt='%r: %r', sep=', '))
2094 fm.plain('\n')
2099 fm.plain('\n')
2095
2100
2096 def finddate(ui, repo, date):
2101 def finddate(ui, repo, date):
2097 """Find the tipmost changeset that matches the given date spec"""
2102 """Find the tipmost changeset that matches the given date spec"""
2098
2103
2099 df = util.matchdate(date)
2104 df = util.matchdate(date)
2100 m = scmutil.matchall(repo)
2105 m = scmutil.matchall(repo)
2101 results = {}
2106 results = {}
2102
2107
2103 def prep(ctx, fns):
2108 def prep(ctx, fns):
2104 d = ctx.date()
2109 d = ctx.date()
2105 if df(d[0]):
2110 if df(d[0]):
2106 results[ctx.rev()] = d
2111 results[ctx.rev()] = d
2107
2112
2108 for ctx in walkchangerevs(repo, m, {'rev': None}, prep):
2113 for ctx in walkchangerevs(repo, m, {'rev': None}, prep):
2109 rev = ctx.rev()
2114 rev = ctx.rev()
2110 if rev in results:
2115 if rev in results:
2111 ui.status(_("found revision %s from %s\n") %
2116 ui.status(_("found revision %s from %s\n") %
2112 (rev, util.datestr(results[rev])))
2117 (rev, util.datestr(results[rev])))
2113 return '%d' % rev
2118 return '%d' % rev
2114
2119
2115 raise error.Abort(_("revision matching date not found"))
2120 raise error.Abort(_("revision matching date not found"))
2116
2121
2117 def increasingwindows(windowsize=8, sizelimit=512):
2122 def increasingwindows(windowsize=8, sizelimit=512):
2118 while True:
2123 while True:
2119 yield windowsize
2124 yield windowsize
2120 if windowsize < sizelimit:
2125 if windowsize < sizelimit:
2121 windowsize *= 2
2126 windowsize *= 2
2122
2127
2123 def _walkrevs(repo, opts):
2128 def _walkrevs(repo, opts):
2124 # Default --rev value depends on --follow but --follow behavior
2129 # Default --rev value depends on --follow but --follow behavior
2125 # depends on revisions resolved from --rev...
2130 # depends on revisions resolved from --rev...
2126 follow = opts.get('follow') or opts.get('follow_first')
2131 follow = opts.get('follow') or opts.get('follow_first')
2127 if opts.get('rev'):
2132 if opts.get('rev'):
2128 revs = scmutil.revrange(repo, opts['rev'])
2133 revs = scmutil.revrange(repo, opts['rev'])
2129 elif follow and repo.dirstate.p1() == nullid:
2134 elif follow and repo.dirstate.p1() == nullid:
2130 revs = smartset.baseset()
2135 revs = smartset.baseset()
2131 elif follow:
2136 elif follow:
2132 revs = repo.revs('reverse(:.)')
2137 revs = repo.revs('reverse(:.)')
2133 else:
2138 else:
2134 revs = smartset.spanset(repo)
2139 revs = smartset.spanset(repo)
2135 revs.reverse()
2140 revs.reverse()
2136 return revs
2141 return revs
2137
2142
2138 class FileWalkError(Exception):
2143 class FileWalkError(Exception):
2139 pass
2144 pass
2140
2145
2141 def walkfilerevs(repo, match, follow, revs, fncache):
2146 def walkfilerevs(repo, match, follow, revs, fncache):
2142 '''Walks the file history for the matched files.
2147 '''Walks the file history for the matched files.
2143
2148
2144 Returns the changeset revs that are involved in the file history.
2149 Returns the changeset revs that are involved in the file history.
2145
2150
2146 Throws FileWalkError if the file history can't be walked using
2151 Throws FileWalkError if the file history can't be walked using
2147 filelogs alone.
2152 filelogs alone.
2148 '''
2153 '''
2149 wanted = set()
2154 wanted = set()
2150 copies = []
2155 copies = []
2151 minrev, maxrev = min(revs), max(revs)
2156 minrev, maxrev = min(revs), max(revs)
2152 def filerevgen(filelog, last):
2157 def filerevgen(filelog, last):
2153 """
2158 """
2154 Only files, no patterns. Check the history of each file.
2159 Only files, no patterns. Check the history of each file.
2155
2160
2156 Examines filelog entries within minrev, maxrev linkrev range
2161 Examines filelog entries within minrev, maxrev linkrev range
2157 Returns an iterator yielding (linkrev, parentlinkrevs, copied)
2162 Returns an iterator yielding (linkrev, parentlinkrevs, copied)
2158 tuples in backwards order
2163 tuples in backwards order
2159 """
2164 """
2160 cl_count = len(repo)
2165 cl_count = len(repo)
2161 revs = []
2166 revs = []
2162 for j in xrange(0, last + 1):
2167 for j in xrange(0, last + 1):
2163 linkrev = filelog.linkrev(j)
2168 linkrev = filelog.linkrev(j)
2164 if linkrev < minrev:
2169 if linkrev < minrev:
2165 continue
2170 continue
2166 # only yield rev for which we have the changelog, it can
2171 # only yield rev for which we have the changelog, it can
2167 # happen while doing "hg log" during a pull or commit
2172 # happen while doing "hg log" during a pull or commit
2168 if linkrev >= cl_count:
2173 if linkrev >= cl_count:
2169 break
2174 break
2170
2175
2171 parentlinkrevs = []
2176 parentlinkrevs = []
2172 for p in filelog.parentrevs(j):
2177 for p in filelog.parentrevs(j):
2173 if p != nullrev:
2178 if p != nullrev:
2174 parentlinkrevs.append(filelog.linkrev(p))
2179 parentlinkrevs.append(filelog.linkrev(p))
2175 n = filelog.node(j)
2180 n = filelog.node(j)
2176 revs.append((linkrev, parentlinkrevs,
2181 revs.append((linkrev, parentlinkrevs,
2177 follow and filelog.renamed(n)))
2182 follow and filelog.renamed(n)))
2178
2183
2179 return reversed(revs)
2184 return reversed(revs)
2180 def iterfiles():
2185 def iterfiles():
2181 pctx = repo['.']
2186 pctx = repo['.']
2182 for filename in match.files():
2187 for filename in match.files():
2183 if follow:
2188 if follow:
2184 if filename not in pctx:
2189 if filename not in pctx:
2185 raise error.Abort(_('cannot follow file not in parent '
2190 raise error.Abort(_('cannot follow file not in parent '
2186 'revision: "%s"') % filename)
2191 'revision: "%s"') % filename)
2187 yield filename, pctx[filename].filenode()
2192 yield filename, pctx[filename].filenode()
2188 else:
2193 else:
2189 yield filename, None
2194 yield filename, None
2190 for filename_node in copies:
2195 for filename_node in copies:
2191 yield filename_node
2196 yield filename_node
2192
2197
2193 for file_, node in iterfiles():
2198 for file_, node in iterfiles():
2194 filelog = repo.file(file_)
2199 filelog = repo.file(file_)
2195 if not len(filelog):
2200 if not len(filelog):
2196 if node is None:
2201 if node is None:
2197 # A zero count may be a directory or deleted file, so
2202 # A zero count may be a directory or deleted file, so
2198 # try to find matching entries on the slow path.
2203 # try to find matching entries on the slow path.
2199 if follow:
2204 if follow:
2200 raise error.Abort(
2205 raise error.Abort(
2201 _('cannot follow nonexistent file: "%s"') % file_)
2206 _('cannot follow nonexistent file: "%s"') % file_)
2202 raise FileWalkError("Cannot walk via filelog")
2207 raise FileWalkError("Cannot walk via filelog")
2203 else:
2208 else:
2204 continue
2209 continue
2205
2210
2206 if node is None:
2211 if node is None:
2207 last = len(filelog) - 1
2212 last = len(filelog) - 1
2208 else:
2213 else:
2209 last = filelog.rev(node)
2214 last = filelog.rev(node)
2210
2215
2211 # keep track of all ancestors of the file
2216 # keep track of all ancestors of the file
2212 ancestors = {filelog.linkrev(last)}
2217 ancestors = {filelog.linkrev(last)}
2213
2218
2214 # iterate from latest to oldest revision
2219 # iterate from latest to oldest revision
2215 for rev, flparentlinkrevs, copied in filerevgen(filelog, last):
2220 for rev, flparentlinkrevs, copied in filerevgen(filelog, last):
2216 if not follow:
2221 if not follow:
2217 if rev > maxrev:
2222 if rev > maxrev:
2218 continue
2223 continue
2219 else:
2224 else:
2220 # Note that last might not be the first interesting
2225 # Note that last might not be the first interesting
2221 # rev to us:
2226 # rev to us:
2222 # if the file has been changed after maxrev, we'll
2227 # if the file has been changed after maxrev, we'll
2223 # have linkrev(last) > maxrev, and we still need
2228 # have linkrev(last) > maxrev, and we still need
2224 # to explore the file graph
2229 # to explore the file graph
2225 if rev not in ancestors:
2230 if rev not in ancestors:
2226 continue
2231 continue
2227 # XXX insert 1327 fix here
2232 # XXX insert 1327 fix here
2228 if flparentlinkrevs:
2233 if flparentlinkrevs:
2229 ancestors.update(flparentlinkrevs)
2234 ancestors.update(flparentlinkrevs)
2230
2235
2231 fncache.setdefault(rev, []).append(file_)
2236 fncache.setdefault(rev, []).append(file_)
2232 wanted.add(rev)
2237 wanted.add(rev)
2233 if copied:
2238 if copied:
2234 copies.append(copied)
2239 copies.append(copied)
2235
2240
2236 return wanted
2241 return wanted
2237
2242
2238 class _followfilter(object):
2243 class _followfilter(object):
2239 def __init__(self, repo, onlyfirst=False):
2244 def __init__(self, repo, onlyfirst=False):
2240 self.repo = repo
2245 self.repo = repo
2241 self.startrev = nullrev
2246 self.startrev = nullrev
2242 self.roots = set()
2247 self.roots = set()
2243 self.onlyfirst = onlyfirst
2248 self.onlyfirst = onlyfirst
2244
2249
2245 def match(self, rev):
2250 def match(self, rev):
2246 def realparents(rev):
2251 def realparents(rev):
2247 if self.onlyfirst:
2252 if self.onlyfirst:
2248 return self.repo.changelog.parentrevs(rev)[0:1]
2253 return self.repo.changelog.parentrevs(rev)[0:1]
2249 else:
2254 else:
2250 return filter(lambda x: x != nullrev,
2255 return filter(lambda x: x != nullrev,
2251 self.repo.changelog.parentrevs(rev))
2256 self.repo.changelog.parentrevs(rev))
2252
2257
2253 if self.startrev == nullrev:
2258 if self.startrev == nullrev:
2254 self.startrev = rev
2259 self.startrev = rev
2255 return True
2260 return True
2256
2261
2257 if rev > self.startrev:
2262 if rev > self.startrev:
2258 # forward: all descendants
2263 # forward: all descendants
2259 if not self.roots:
2264 if not self.roots:
2260 self.roots.add(self.startrev)
2265 self.roots.add(self.startrev)
2261 for parent in realparents(rev):
2266 for parent in realparents(rev):
2262 if parent in self.roots:
2267 if parent in self.roots:
2263 self.roots.add(rev)
2268 self.roots.add(rev)
2264 return True
2269 return True
2265 else:
2270 else:
2266 # backwards: all parents
2271 # backwards: all parents
2267 if not self.roots:
2272 if not self.roots:
2268 self.roots.update(realparents(self.startrev))
2273 self.roots.update(realparents(self.startrev))
2269 if rev in self.roots:
2274 if rev in self.roots:
2270 self.roots.remove(rev)
2275 self.roots.remove(rev)
2271 self.roots.update(realparents(rev))
2276 self.roots.update(realparents(rev))
2272 return True
2277 return True
2273
2278
2274 return False
2279 return False
2275
2280
2276 def walkchangerevs(repo, match, opts, prepare):
2281 def walkchangerevs(repo, match, opts, prepare):
2277 '''Iterate over files and the revs in which they changed.
2282 '''Iterate over files and the revs in which they changed.
2278
2283
2279 Callers most commonly need to iterate backwards over the history
2284 Callers most commonly need to iterate backwards over the history
2280 in which they are interested. Doing so has awful (quadratic-looking)
2285 in which they are interested. Doing so has awful (quadratic-looking)
2281 performance, so we use iterators in a "windowed" way.
2286 performance, so we use iterators in a "windowed" way.
2282
2287
2283 We walk a window of revisions in the desired order. Within the
2288 We walk a window of revisions in the desired order. Within the
2284 window, we first walk forwards to gather data, then in the desired
2289 window, we first walk forwards to gather data, then in the desired
2285 order (usually backwards) to display it.
2290 order (usually backwards) to display it.
2286
2291
2287 This function returns an iterator yielding contexts. Before
2292 This function returns an iterator yielding contexts. Before
2288 yielding each context, the iterator will first call the prepare
2293 yielding each context, the iterator will first call the prepare
2289 function on each context in the window in forward order.'''
2294 function on each context in the window in forward order.'''
2290
2295
2291 follow = opts.get('follow') or opts.get('follow_first')
2296 follow = opts.get('follow') or opts.get('follow_first')
2292 revs = _walkrevs(repo, opts)
2297 revs = _walkrevs(repo, opts)
2293 if not revs:
2298 if not revs:
2294 return []
2299 return []
2295 wanted = set()
2300 wanted = set()
2296 slowpath = match.anypats() or (not match.always() and opts.get('removed'))
2301 slowpath = match.anypats() or (not match.always() and opts.get('removed'))
2297 fncache = {}
2302 fncache = {}
2298 change = repo.changectx
2303 change = repo.changectx
2299
2304
2300 # First step is to fill wanted, the set of revisions that we want to yield.
2305 # First step is to fill wanted, the set of revisions that we want to yield.
2301 # When it does not induce extra cost, we also fill fncache for revisions in
2306 # When it does not induce extra cost, we also fill fncache for revisions in
2302 # wanted: a cache of filenames that were changed (ctx.files()) and that
2307 # wanted: a cache of filenames that were changed (ctx.files()) and that
2303 # match the file filtering conditions.
2308 # match the file filtering conditions.
2304
2309
2305 if match.always():
2310 if match.always():
2306 # No files, no patterns. Display all revs.
2311 # No files, no patterns. Display all revs.
2307 wanted = revs
2312 wanted = revs
2308 elif not slowpath:
2313 elif not slowpath:
2309 # We only have to read through the filelog to find wanted revisions
2314 # We only have to read through the filelog to find wanted revisions
2310
2315
2311 try:
2316 try:
2312 wanted = walkfilerevs(repo, match, follow, revs, fncache)
2317 wanted = walkfilerevs(repo, match, follow, revs, fncache)
2313 except FileWalkError:
2318 except FileWalkError:
2314 slowpath = True
2319 slowpath = True
2315
2320
2316 # We decided to fall back to the slowpath because at least one
2321 # We decided to fall back to the slowpath because at least one
2317 # of the paths was not a file. Check to see if at least one of them
2322 # of the paths was not a file. Check to see if at least one of them
2318 # existed in history, otherwise simply return
2323 # existed in history, otherwise simply return
2319 for path in match.files():
2324 for path in match.files():
2320 if path == '.' or path in repo.store:
2325 if path == '.' or path in repo.store:
2321 break
2326 break
2322 else:
2327 else:
2323 return []
2328 return []
2324
2329
2325 if slowpath:
2330 if slowpath:
2326 # We have to read the changelog to match filenames against
2331 # We have to read the changelog to match filenames against
2327 # changed files
2332 # changed files
2328
2333
2329 if follow:
2334 if follow:
2330 raise error.Abort(_('can only follow copies/renames for explicit '
2335 raise error.Abort(_('can only follow copies/renames for explicit '
2331 'filenames'))
2336 'filenames'))
2332
2337
2333 # The slow path checks files modified in every changeset.
2338 # The slow path checks files modified in every changeset.
2334 # This is really slow on large repos, so compute the set lazily.
2339 # This is really slow on large repos, so compute the set lazily.
2335 class lazywantedset(object):
2340 class lazywantedset(object):
2336 def __init__(self):
2341 def __init__(self):
2337 self.set = set()
2342 self.set = set()
2338 self.revs = set(revs)
2343 self.revs = set(revs)
2339
2344
2340 # No need to worry about locality here because it will be accessed
2345 # No need to worry about locality here because it will be accessed
2341 # in the same order as the increasing window below.
2346 # in the same order as the increasing window below.
2342 def __contains__(self, value):
2347 def __contains__(self, value):
2343 if value in self.set:
2348 if value in self.set:
2344 return True
2349 return True
2345 elif not value in self.revs:
2350 elif not value in self.revs:
2346 return False
2351 return False
2347 else:
2352 else:
2348 self.revs.discard(value)
2353 self.revs.discard(value)
2349 ctx = change(value)
2354 ctx = change(value)
2350 matches = filter(match, ctx.files())
2355 matches = filter(match, ctx.files())
2351 if matches:
2356 if matches:
2352 fncache[value] = matches
2357 fncache[value] = matches
2353 self.set.add(value)
2358 self.set.add(value)
2354 return True
2359 return True
2355 return False
2360 return False
2356
2361
2357 def discard(self, value):
2362 def discard(self, value):
2358 self.revs.discard(value)
2363 self.revs.discard(value)
2359 self.set.discard(value)
2364 self.set.discard(value)
2360
2365
2361 wanted = lazywantedset()
2366 wanted = lazywantedset()
2362
2367
2363 # it might be worthwhile to do this in the iterator if the rev range
2368 # it might be worthwhile to do this in the iterator if the rev range
2364 # is descending and the prune args are all within that range
2369 # is descending and the prune args are all within that range
2365 for rev in opts.get('prune', ()):
2370 for rev in opts.get('prune', ()):
2366 rev = repo[rev].rev()
2371 rev = repo[rev].rev()
2367 ff = _followfilter(repo)
2372 ff = _followfilter(repo)
2368 stop = min(revs[0], revs[-1])
2373 stop = min(revs[0], revs[-1])
2369 for x in xrange(rev, stop - 1, -1):
2374 for x in xrange(rev, stop - 1, -1):
2370 if ff.match(x):
2375 if ff.match(x):
2371 wanted = wanted - [x]
2376 wanted = wanted - [x]
2372
2377
2373 # Now that wanted is correctly initialized, we can iterate over the
2378 # Now that wanted is correctly initialized, we can iterate over the
2374 # revision range, yielding only revisions in wanted.
2379 # revision range, yielding only revisions in wanted.
2375 def iterate():
2380 def iterate():
2376 if follow and match.always():
2381 if follow and match.always():
2377 ff = _followfilter(repo, onlyfirst=opts.get('follow_first'))
2382 ff = _followfilter(repo, onlyfirst=opts.get('follow_first'))
2378 def want(rev):
2383 def want(rev):
2379 return ff.match(rev) and rev in wanted
2384 return ff.match(rev) and rev in wanted
2380 else:
2385 else:
2381 def want(rev):
2386 def want(rev):
2382 return rev in wanted
2387 return rev in wanted
2383
2388
2384 it = iter(revs)
2389 it = iter(revs)
2385 stopiteration = False
2390 stopiteration = False
2386 for windowsize in increasingwindows():
2391 for windowsize in increasingwindows():
2387 nrevs = []
2392 nrevs = []
2388 for i in xrange(windowsize):
2393 for i in xrange(windowsize):
2389 rev = next(it, None)
2394 rev = next(it, None)
2390 if rev is None:
2395 if rev is None:
2391 stopiteration = True
2396 stopiteration = True
2392 break
2397 break
2393 elif want(rev):
2398 elif want(rev):
2394 nrevs.append(rev)
2399 nrevs.append(rev)
2395 for rev in sorted(nrevs):
2400 for rev in sorted(nrevs):
2396 fns = fncache.get(rev)
2401 fns = fncache.get(rev)
2397 ctx = change(rev)
2402 ctx = change(rev)
2398 if not fns:
2403 if not fns:
2399 def fns_generator():
2404 def fns_generator():
2400 for f in ctx.files():
2405 for f in ctx.files():
2401 if match(f):
2406 if match(f):
2402 yield f
2407 yield f
2403 fns = fns_generator()
2408 fns = fns_generator()
2404 prepare(ctx, fns)
2409 prepare(ctx, fns)
2405 for rev in nrevs:
2410 for rev in nrevs:
2406 yield change(rev)
2411 yield change(rev)
2407
2412
2408 if stopiteration:
2413 if stopiteration:
2409 break
2414 break
2410
2415
2411 return iterate()
2416 return iterate()
2412
2417
2413 def _makelogmatcher(repo, revs, pats, opts):
2418 def _makelogmatcher(repo, revs, pats, opts):
2414 """Build matcher and expanded patterns from log options
2419 """Build matcher and expanded patterns from log options
2415
2420
2416 If --follow, revs are the revisions to follow from.
2421 If --follow, revs are the revisions to follow from.
2417
2422
2418 Returns (match, pats, slowpath) where
2423 Returns (match, pats, slowpath) where
2419 - match: a matcher built from the given pats and -I/-X opts
2424 - match: a matcher built from the given pats and -I/-X opts
2420 - pats: patterns used (globs are expanded on Windows)
2425 - pats: patterns used (globs are expanded on Windows)
2421 - slowpath: True if patterns aren't as simple as scanning filelogs
2426 - slowpath: True if patterns aren't as simple as scanning filelogs
2422 """
2427 """
2423 # pats/include/exclude are passed to match.match() directly in
2428 # pats/include/exclude are passed to match.match() directly in
2424 # _matchfiles() revset but walkchangerevs() builds its matcher with
2429 # _matchfiles() revset but walkchangerevs() builds its matcher with
2425 # scmutil.match(). The difference is input pats are globbed on
2430 # scmutil.match(). The difference is input pats are globbed on
2426 # platforms without shell expansion (windows).
2431 # platforms without shell expansion (windows).
2427 wctx = repo[None]
2432 wctx = repo[None]
2428 match, pats = scmutil.matchandpats(wctx, pats, opts)
2433 match, pats = scmutil.matchandpats(wctx, pats, opts)
2429 slowpath = match.anypats() or (not match.always() and opts.get('removed'))
2434 slowpath = match.anypats() or (not match.always() and opts.get('removed'))
2430 if not slowpath:
2435 if not slowpath:
2431 follow = opts.get('follow') or opts.get('follow_first')
2436 follow = opts.get('follow') or opts.get('follow_first')
2432 startctxs = []
2437 startctxs = []
2433 if follow and opts.get('rev'):
2438 if follow and opts.get('rev'):
2434 startctxs = [repo[r] for r in revs]
2439 startctxs = [repo[r] for r in revs]
2435 for f in match.files():
2440 for f in match.files():
2436 if follow and startctxs:
2441 if follow and startctxs:
2437 # No idea if the path was a directory at that revision, so
2442 # No idea if the path was a directory at that revision, so
2438 # take the slow path.
2443 # take the slow path.
2439 if any(f not in c for c in startctxs):
2444 if any(f not in c for c in startctxs):
2440 slowpath = True
2445 slowpath = True
2441 continue
2446 continue
2442 elif follow and f not in wctx:
2447 elif follow and f not in wctx:
2443 # If the file exists, it may be a directory, so let it
2448 # If the file exists, it may be a directory, so let it
2444 # take the slow path.
2449 # take the slow path.
2445 if os.path.exists(repo.wjoin(f)):
2450 if os.path.exists(repo.wjoin(f)):
2446 slowpath = True
2451 slowpath = True
2447 continue
2452 continue
2448 else:
2453 else:
2449 raise error.Abort(_('cannot follow file not in parent '
2454 raise error.Abort(_('cannot follow file not in parent '
2450 'revision: "%s"') % f)
2455 'revision: "%s"') % f)
2451 filelog = repo.file(f)
2456 filelog = repo.file(f)
2452 if not filelog:
2457 if not filelog:
2453 # A zero count may be a directory or deleted file, so
2458 # A zero count may be a directory or deleted file, so
2454 # try to find matching entries on the slow path.
2459 # try to find matching entries on the slow path.
2455 if follow:
2460 if follow:
2456 raise error.Abort(
2461 raise error.Abort(
2457 _('cannot follow nonexistent file: "%s"') % f)
2462 _('cannot follow nonexistent file: "%s"') % f)
2458 slowpath = True
2463 slowpath = True
2459
2464
2460 # We decided to fall back to the slowpath because at least one
2465 # We decided to fall back to the slowpath because at least one
2461 # of the paths was not a file. Check to see if at least one of them
2466 # of the paths was not a file. Check to see if at least one of them
2462 # existed in history - in that case, we'll continue down the
2467 # existed in history - in that case, we'll continue down the
2463 # slowpath; otherwise, we can turn off the slowpath
2468 # slowpath; otherwise, we can turn off the slowpath
2464 if slowpath:
2469 if slowpath:
2465 for path in match.files():
2470 for path in match.files():
2466 if path == '.' or path in repo.store:
2471 if path == '.' or path in repo.store:
2467 break
2472 break
2468 else:
2473 else:
2469 slowpath = False
2474 slowpath = False
2470
2475
2471 return match, pats, slowpath
2476 return match, pats, slowpath
2472
2477
2473 def _fileancestors(repo, revs, match, followfirst):
2478 def _fileancestors(repo, revs, match, followfirst):
2474 fctxs = []
2479 fctxs = []
2475 for r in revs:
2480 for r in revs:
2476 ctx = repo[r]
2481 ctx = repo[r]
2477 fctxs.extend(ctx[f].introfilectx() for f in ctx.walk(match))
2482 fctxs.extend(ctx[f].introfilectx() for f in ctx.walk(match))
2478
2483
2479 # When displaying a revision with --patch --follow FILE, we have
2484 # When displaying a revision with --patch --follow FILE, we have
2480 # to know which file of the revision must be diffed. With
2485 # to know which file of the revision must be diffed. With
2481 # --follow, we want the names of the ancestors of FILE in the
2486 # --follow, we want the names of the ancestors of FILE in the
2482 # revision, stored in "fcache". "fcache" is populated as a side effect
2487 # revision, stored in "fcache". "fcache" is populated as a side effect
2483 # of the graph traversal.
2488 # of the graph traversal.
2484 fcache = {}
2489 fcache = {}
2485 def filematcher(rev):
2490 def filematcher(rev):
2486 return scmutil.matchfiles(repo, fcache.get(rev, []))
2491 return scmutil.matchfiles(repo, fcache.get(rev, []))
2487
2492
2488 def revgen():
2493 def revgen():
2489 for rev, cs in dagop.filectxancestors(fctxs, followfirst=followfirst):
2494 for rev, cs in dagop.filectxancestors(fctxs, followfirst=followfirst):
2490 fcache[rev] = [c.path() for c in cs]
2495 fcache[rev] = [c.path() for c in cs]
2491 yield rev
2496 yield rev
2492 return smartset.generatorset(revgen(), iterasc=False), filematcher
2497 return smartset.generatorset(revgen(), iterasc=False), filematcher
2493
2498
2494 def _makenofollowlogfilematcher(repo, pats, opts):
2499 def _makenofollowlogfilematcher(repo, pats, opts):
2495 '''hook for extensions to override the filematcher for non-follow cases'''
2500 '''hook for extensions to override the filematcher for non-follow cases'''
2496 return None
2501 return None
2497
2502
2498 _opt2logrevset = {
2503 _opt2logrevset = {
2499 'no_merges': ('not merge()', None),
2504 'no_merges': ('not merge()', None),
2500 'only_merges': ('merge()', None),
2505 'only_merges': ('merge()', None),
2501 '_matchfiles': (None, '_matchfiles(%ps)'),
2506 '_matchfiles': (None, '_matchfiles(%ps)'),
2502 'date': ('date(%s)', None),
2507 'date': ('date(%s)', None),
2503 'branch': ('branch(%s)', '%lr'),
2508 'branch': ('branch(%s)', '%lr'),
2504 '_patslog': ('filelog(%s)', '%lr'),
2509 '_patslog': ('filelog(%s)', '%lr'),
2505 'keyword': ('keyword(%s)', '%lr'),
2510 'keyword': ('keyword(%s)', '%lr'),
2506 'prune': ('ancestors(%s)', 'not %lr'),
2511 'prune': ('ancestors(%s)', 'not %lr'),
2507 'user': ('user(%s)', '%lr'),
2512 'user': ('user(%s)', '%lr'),
2508 }
2513 }
2509
2514
2510 def _makelogrevset(repo, match, pats, slowpath, opts):
2515 def _makelogrevset(repo, match, pats, slowpath, opts):
2511 """Return a revset string built from log options and file patterns"""
2516 """Return a revset string built from log options and file patterns"""
2512 opts = dict(opts)
2517 opts = dict(opts)
2513 # follow or not follow?
2518 # follow or not follow?
2514 follow = opts.get('follow') or opts.get('follow_first')
2519 follow = opts.get('follow') or opts.get('follow_first')
2515
2520
2516 # branch and only_branch are really aliases and must be handled at
2521 # branch and only_branch are really aliases and must be handled at
2517 # the same time
2522 # the same time
2518 opts['branch'] = opts.get('branch', []) + opts.get('only_branch', [])
2523 opts['branch'] = opts.get('branch', []) + opts.get('only_branch', [])
2519 opts['branch'] = [repo.lookupbranch(b) for b in opts['branch']]
2524 opts['branch'] = [repo.lookupbranch(b) for b in opts['branch']]
2520
2525
2521 if slowpath:
2526 if slowpath:
2522 # See walkchangerevs() slow path.
2527 # See walkchangerevs() slow path.
2523 #
2528 #
2524 # pats/include/exclude cannot be represented as separate
2529 # pats/include/exclude cannot be represented as separate
2525 # revset expressions as their filtering logic applies at file
2530 # revset expressions as their filtering logic applies at file
2526 # level. For instance "-I a -X b" matches a revision touching
2531 # level. For instance "-I a -X b" matches a revision touching
2527 # "a" and "b" while "file(a) and not file(b)" does
2532 # "a" and "b" while "file(a) and not file(b)" does
2528 # not. Besides, filesets are evaluated against the working
2533 # not. Besides, filesets are evaluated against the working
2529 # directory.
2534 # directory.
2530 matchargs = ['r:', 'd:relpath']
2535 matchargs = ['r:', 'd:relpath']
2531 for p in pats:
2536 for p in pats:
2532 matchargs.append('p:' + p)
2537 matchargs.append('p:' + p)
2533 for p in opts.get('include', []):
2538 for p in opts.get('include', []):
2534 matchargs.append('i:' + p)
2539 matchargs.append('i:' + p)
2535 for p in opts.get('exclude', []):
2540 for p in opts.get('exclude', []):
2536 matchargs.append('x:' + p)
2541 matchargs.append('x:' + p)
2537 opts['_matchfiles'] = matchargs
2542 opts['_matchfiles'] = matchargs
2538 elif not follow:
2543 elif not follow:
2539 opts['_patslog'] = list(pats)
2544 opts['_patslog'] = list(pats)
2540
2545
2541 expr = []
2546 expr = []
2542 for op, val in sorted(opts.iteritems()):
2547 for op, val in sorted(opts.iteritems()):
2543 if not val:
2548 if not val:
2544 continue
2549 continue
2545 if op not in _opt2logrevset:
2550 if op not in _opt2logrevset:
2546 continue
2551 continue
2547 revop, listop = _opt2logrevset[op]
2552 revop, listop = _opt2logrevset[op]
2548 if revop and '%' not in revop:
2553 if revop and '%' not in revop:
2549 expr.append(revop)
2554 expr.append(revop)
2550 elif not listop:
2555 elif not listop:
2551 expr.append(revsetlang.formatspec(revop, val))
2556 expr.append(revsetlang.formatspec(revop, val))
2552 else:
2557 else:
2553 if revop:
2558 if revop:
2554 val = [revsetlang.formatspec(revop, v) for v in val]
2559 val = [revsetlang.formatspec(revop, v) for v in val]
2555 expr.append(revsetlang.formatspec(listop, val))
2560 expr.append(revsetlang.formatspec(listop, val))
2556
2561
2557 if expr:
2562 if expr:
2558 expr = '(' + ' and '.join(expr) + ')'
2563 expr = '(' + ' and '.join(expr) + ')'
2559 else:
2564 else:
2560 expr = None
2565 expr = None
2561 return expr
2566 return expr
2562
2567
2563 def _logrevs(repo, opts):
2568 def _logrevs(repo, opts):
2564 """Return the initial set of revisions to be filtered or followed"""
2569 """Return the initial set of revisions to be filtered or followed"""
2565 follow = opts.get('follow') or opts.get('follow_first')
2570 follow = opts.get('follow') or opts.get('follow_first')
2566 if opts.get('rev'):
2571 if opts.get('rev'):
2567 revs = scmutil.revrange(repo, opts['rev'])
2572 revs = scmutil.revrange(repo, opts['rev'])
2568 elif follow and repo.dirstate.p1() == nullid:
2573 elif follow and repo.dirstate.p1() == nullid:
2569 revs = smartset.baseset()
2574 revs = smartset.baseset()
2570 elif follow:
2575 elif follow:
2571 revs = repo.revs('.')
2576 revs = repo.revs('.')
2572 else:
2577 else:
2573 revs = smartset.spanset(repo)
2578 revs = smartset.spanset(repo)
2574 revs.reverse()
2579 revs.reverse()
2575 return revs
2580 return revs
2576
2581
2577 def getlogrevs(repo, pats, opts):
2582 def getlogrevs(repo, pats, opts):
2578 """Return (revs, filematcher) where revs is a smartset
2583 """Return (revs, filematcher) where revs is a smartset
2579
2584
2580 filematcher is a callable taking a revision number and returning a match
2585 filematcher is a callable taking a revision number and returning a match
2581 objects filtering the files to be detailed when displaying the revision.
2586 objects filtering the files to be detailed when displaying the revision.
2582 """
2587 """
2583 follow = opts.get('follow') or opts.get('follow_first')
2588 follow = opts.get('follow') or opts.get('follow_first')
2584 followfirst = opts.get('follow_first')
2589 followfirst = opts.get('follow_first')
2585 limit = loglimit(opts)
2590 limit = loglimit(opts)
2586 revs = _logrevs(repo, opts)
2591 revs = _logrevs(repo, opts)
2587 if not revs:
2592 if not revs:
2588 return smartset.baseset(), None
2593 return smartset.baseset(), None
2589 match, pats, slowpath = _makelogmatcher(repo, revs, pats, opts)
2594 match, pats, slowpath = _makelogmatcher(repo, revs, pats, opts)
2590 filematcher = None
2595 filematcher = None
2591 if follow:
2596 if follow:
2592 if slowpath or match.always():
2597 if slowpath or match.always():
2593 revs = dagop.revancestors(repo, revs, followfirst=followfirst)
2598 revs = dagop.revancestors(repo, revs, followfirst=followfirst)
2594 else:
2599 else:
2595 revs, filematcher = _fileancestors(repo, revs, match, followfirst)
2600 revs, filematcher = _fileancestors(repo, revs, match, followfirst)
2596 revs.reverse()
2601 revs.reverse()
2597 if filematcher is None:
2602 if filematcher is None:
2598 filematcher = _makenofollowlogfilematcher(repo, pats, opts)
2603 filematcher = _makenofollowlogfilematcher(repo, pats, opts)
2599 if filematcher is None:
2604 if filematcher is None:
2600 def filematcher(rev):
2605 def filematcher(rev):
2601 return match
2606 return match
2602
2607
2603 expr = _makelogrevset(repo, match, pats, slowpath, opts)
2608 expr = _makelogrevset(repo, match, pats, slowpath, opts)
2604 if opts.get('graph') and opts.get('rev'):
2609 if opts.get('graph') and opts.get('rev'):
2605 # User-specified revs might be unsorted, but don't sort before
2610 # User-specified revs might be unsorted, but don't sort before
2606 # _makelogrevset because it might depend on the order of revs
2611 # _makelogrevset because it might depend on the order of revs
2607 if not (revs.isdescending() or revs.istopo()):
2612 if not (revs.isdescending() or revs.istopo()):
2608 revs.sort(reverse=True)
2613 revs.sort(reverse=True)
2609 if expr:
2614 if expr:
2610 matcher = revset.match(None, expr)
2615 matcher = revset.match(None, expr)
2611 revs = matcher(repo, revs)
2616 revs = matcher(repo, revs)
2612 if limit is not None:
2617 if limit is not None:
2613 revs = revs.slice(0, limit)
2618 revs = revs.slice(0, limit)
2614 return revs, filematcher
2619 return revs, filematcher
2615
2620
2616 def _parselinerangelogopt(repo, opts):
2621 def _parselinerangelogopt(repo, opts):
2617 """Parse --line-range log option and return a list of tuples (filename,
2622 """Parse --line-range log option and return a list of tuples (filename,
2618 (fromline, toline)).
2623 (fromline, toline)).
2619 """
2624 """
2620 linerangebyfname = []
2625 linerangebyfname = []
2621 for pat in opts.get('line_range', []):
2626 for pat in opts.get('line_range', []):
2622 try:
2627 try:
2623 pat, linerange = pat.rsplit(',', 1)
2628 pat, linerange = pat.rsplit(',', 1)
2624 except ValueError:
2629 except ValueError:
2625 raise error.Abort(_('malformatted line-range pattern %s') % pat)
2630 raise error.Abort(_('malformatted line-range pattern %s') % pat)
2626 try:
2631 try:
2627 fromline, toline = map(int, linerange.split(':'))
2632 fromline, toline = map(int, linerange.split(':'))
2628 except ValueError:
2633 except ValueError:
2629 raise error.Abort(_("invalid line range for %s") % pat)
2634 raise error.Abort(_("invalid line range for %s") % pat)
2630 msg = _("line range pattern '%s' must match exactly one file") % pat
2635 msg = _("line range pattern '%s' must match exactly one file") % pat
2631 fname = scmutil.parsefollowlinespattern(repo, None, pat, msg)
2636 fname = scmutil.parsefollowlinespattern(repo, None, pat, msg)
2632 linerangebyfname.append(
2637 linerangebyfname.append(
2633 (fname, util.processlinerange(fromline, toline)))
2638 (fname, util.processlinerange(fromline, toline)))
2634 return linerangebyfname
2639 return linerangebyfname
2635
2640
2636 def getloglinerangerevs(repo, userrevs, opts):
2641 def getloglinerangerevs(repo, userrevs, opts):
2637 """Return (revs, filematcher, hunksfilter).
2642 """Return (revs, filematcher, hunksfilter).
2638
2643
2639 "revs" are revisions obtained by processing "line-range" log options and
2644 "revs" are revisions obtained by processing "line-range" log options and
2640 walking block ancestors of each specified file/line-range.
2645 walking block ancestors of each specified file/line-range.
2641
2646
2642 "filematcher(rev) -> match" is a factory function returning a match object
2647 "filematcher(rev) -> match" is a factory function returning a match object
2643 for a given revision for file patterns specified in --line-range option.
2648 for a given revision for file patterns specified in --line-range option.
2644 If neither --stat nor --patch options are passed, "filematcher" is None.
2649 If neither --stat nor --patch options are passed, "filematcher" is None.
2645
2650
2646 "hunksfilter(rev) -> filterfn(fctx, hunks)" is a factory function
2651 "hunksfilter(rev) -> filterfn(fctx, hunks)" is a factory function
2647 returning a hunks filtering function.
2652 returning a hunks filtering function.
2648 If neither --stat nor --patch options are passed, "filterhunks" is None.
2653 If neither --stat nor --patch options are passed, "filterhunks" is None.
2649 """
2654 """
2650 wctx = repo[None]
2655 wctx = repo[None]
2651
2656
2652 # Two-levels map of "rev -> file ctx -> [line range]".
2657 # Two-levels map of "rev -> file ctx -> [line range]".
2653 linerangesbyrev = {}
2658 linerangesbyrev = {}
2654 for fname, (fromline, toline) in _parselinerangelogopt(repo, opts):
2659 for fname, (fromline, toline) in _parselinerangelogopt(repo, opts):
2655 if fname not in wctx:
2660 if fname not in wctx:
2656 raise error.Abort(_('cannot follow file not in parent '
2661 raise error.Abort(_('cannot follow file not in parent '
2657 'revision: "%s"') % fname)
2662 'revision: "%s"') % fname)
2658 fctx = wctx.filectx(fname)
2663 fctx = wctx.filectx(fname)
2659 for fctx, linerange in dagop.blockancestors(fctx, fromline, toline):
2664 for fctx, linerange in dagop.blockancestors(fctx, fromline, toline):
2660 rev = fctx.introrev()
2665 rev = fctx.introrev()
2661 if rev not in userrevs:
2666 if rev not in userrevs:
2662 continue
2667 continue
2663 linerangesbyrev.setdefault(
2668 linerangesbyrev.setdefault(
2664 rev, {}).setdefault(
2669 rev, {}).setdefault(
2665 fctx.path(), []).append(linerange)
2670 fctx.path(), []).append(linerange)
2666
2671
2667 filematcher = None
2672 filematcher = None
2668 hunksfilter = None
2673 hunksfilter = None
2669 if opts.get('patch') or opts.get('stat'):
2674 if opts.get('patch') or opts.get('stat'):
2670
2675
2671 def nofilterhunksfn(fctx, hunks):
2676 def nofilterhunksfn(fctx, hunks):
2672 return hunks
2677 return hunks
2673
2678
2674 def hunksfilter(rev):
2679 def hunksfilter(rev):
2675 fctxlineranges = linerangesbyrev.get(rev)
2680 fctxlineranges = linerangesbyrev.get(rev)
2676 if fctxlineranges is None:
2681 if fctxlineranges is None:
2677 return nofilterhunksfn
2682 return nofilterhunksfn
2678
2683
2679 def filterfn(fctx, hunks):
2684 def filterfn(fctx, hunks):
2680 lineranges = fctxlineranges.get(fctx.path())
2685 lineranges = fctxlineranges.get(fctx.path())
2681 if lineranges is not None:
2686 if lineranges is not None:
2682 for hr, lines in hunks:
2687 for hr, lines in hunks:
2683 if hr is None: # binary
2688 if hr is None: # binary
2684 yield hr, lines
2689 yield hr, lines
2685 continue
2690 continue
2686 if any(mdiff.hunkinrange(hr[2:], lr)
2691 if any(mdiff.hunkinrange(hr[2:], lr)
2687 for lr in lineranges):
2692 for lr in lineranges):
2688 yield hr, lines
2693 yield hr, lines
2689 else:
2694 else:
2690 for hunk in hunks:
2695 for hunk in hunks:
2691 yield hunk
2696 yield hunk
2692
2697
2693 return filterfn
2698 return filterfn
2694
2699
2695 def filematcher(rev):
2700 def filematcher(rev):
2696 files = list(linerangesbyrev.get(rev, []))
2701 files = list(linerangesbyrev.get(rev, []))
2697 return scmutil.matchfiles(repo, files)
2702 return scmutil.matchfiles(repo, files)
2698
2703
2699 revs = sorted(linerangesbyrev, reverse=True)
2704 revs = sorted(linerangesbyrev, reverse=True)
2700
2705
2701 return revs, filematcher, hunksfilter
2706 return revs, filematcher, hunksfilter
2702
2707
2703 def _graphnodeformatter(ui, displayer):
2708 def _graphnodeformatter(ui, displayer):
2704 spec = ui.config('ui', 'graphnodetemplate')
2709 spec = ui.config('ui', 'graphnodetemplate')
2705 if not spec:
2710 if not spec:
2706 return templatekw.showgraphnode # fast path for "{graphnode}"
2711 return templatekw.showgraphnode # fast path for "{graphnode}"
2707
2712
2708 spec = templater.unquotestring(spec)
2713 spec = templater.unquotestring(spec)
2709 tres = formatter.templateresources(ui)
2714 tres = formatter.templateresources(ui)
2710 if isinstance(displayer, changeset_templater):
2715 if isinstance(displayer, changeset_templater):
2711 tres['cache'] = displayer.cache # reuse cache of slow templates
2716 tres['cache'] = displayer.cache # reuse cache of slow templates
2712 templ = formatter.maketemplater(ui, spec, defaults=templatekw.keywords,
2717 templ = formatter.maketemplater(ui, spec, defaults=templatekw.keywords,
2713 resources=tres)
2718 resources=tres)
2714 def formatnode(repo, ctx):
2719 def formatnode(repo, ctx):
2715 props = {'ctx': ctx, 'repo': repo, 'revcache': {}}
2720 props = {'ctx': ctx, 'repo': repo, 'revcache': {}}
2716 return templ.render(props)
2721 return templ.render(props)
2717 return formatnode
2722 return formatnode
2718
2723
2719 def displaygraph(ui, repo, dag, displayer, edgefn, getrenamed=None,
2724 def displaygraph(ui, repo, dag, displayer, edgefn, getrenamed=None,
2720 filematcher=None, props=None):
2725 filematcher=None, props=None):
2721 props = props or {}
2726 props = props or {}
2722 formatnode = _graphnodeformatter(ui, displayer)
2727 formatnode = _graphnodeformatter(ui, displayer)
2723 state = graphmod.asciistate()
2728 state = graphmod.asciistate()
2724 styles = state['styles']
2729 styles = state['styles']
2725
2730
2726 # only set graph styling if HGPLAIN is not set.
2731 # only set graph styling if HGPLAIN is not set.
2727 if ui.plain('graph'):
2732 if ui.plain('graph'):
2728 # set all edge styles to |, the default pre-3.8 behaviour
2733 # set all edge styles to |, the default pre-3.8 behaviour
2729 styles.update(dict.fromkeys(styles, '|'))
2734 styles.update(dict.fromkeys(styles, '|'))
2730 else:
2735 else:
2731 edgetypes = {
2736 edgetypes = {
2732 'parent': graphmod.PARENT,
2737 'parent': graphmod.PARENT,
2733 'grandparent': graphmod.GRANDPARENT,
2738 'grandparent': graphmod.GRANDPARENT,
2734 'missing': graphmod.MISSINGPARENT
2739 'missing': graphmod.MISSINGPARENT
2735 }
2740 }
2736 for name, key in edgetypes.items():
2741 for name, key in edgetypes.items():
2737 # experimental config: experimental.graphstyle.*
2742 # experimental config: experimental.graphstyle.*
2738 styles[key] = ui.config('experimental', 'graphstyle.%s' % name,
2743 styles[key] = ui.config('experimental', 'graphstyle.%s' % name,
2739 styles[key])
2744 styles[key])
2740 if not styles[key]:
2745 if not styles[key]:
2741 styles[key] = None
2746 styles[key] = None
2742
2747
2743 # experimental config: experimental.graphshorten
2748 # experimental config: experimental.graphshorten
2744 state['graphshorten'] = ui.configbool('experimental', 'graphshorten')
2749 state['graphshorten'] = ui.configbool('experimental', 'graphshorten')
2745
2750
2746 for rev, type, ctx, parents in dag:
2751 for rev, type, ctx, parents in dag:
2747 char = formatnode(repo, ctx)
2752 char = formatnode(repo, ctx)
2748 copies = None
2753 copies = None
2749 if getrenamed and ctx.rev():
2754 if getrenamed and ctx.rev():
2750 copies = []
2755 copies = []
2751 for fn in ctx.files():
2756 for fn in ctx.files():
2752 rename = getrenamed(fn, ctx.rev())
2757 rename = getrenamed(fn, ctx.rev())
2753 if rename:
2758 if rename:
2754 copies.append((fn, rename[0]))
2759 copies.append((fn, rename[0]))
2755 revmatchfn = None
2760 revmatchfn = None
2756 if filematcher is not None:
2761 if filematcher is not None:
2757 revmatchfn = filematcher(ctx.rev())
2762 revmatchfn = filematcher(ctx.rev())
2758 edges = edgefn(type, char, state, rev, parents)
2763 edges = edgefn(type, char, state, rev, parents)
2759 firstedge = next(edges)
2764 firstedge = next(edges)
2760 width = firstedge[2]
2765 width = firstedge[2]
2761 displayer.show(ctx, copies=copies, matchfn=revmatchfn,
2766 displayer.show(ctx, copies=copies, matchfn=revmatchfn,
2762 _graphwidth=width, **pycompat.strkwargs(props))
2767 _graphwidth=width, **pycompat.strkwargs(props))
2763 lines = displayer.hunk.pop(rev).split('\n')
2768 lines = displayer.hunk.pop(rev).split('\n')
2764 if not lines[-1]:
2769 if not lines[-1]:
2765 del lines[-1]
2770 del lines[-1]
2766 displayer.flush(ctx)
2771 displayer.flush(ctx)
2767 for type, char, width, coldata in itertools.chain([firstedge], edges):
2772 for type, char, width, coldata in itertools.chain([firstedge], edges):
2768 graphmod.ascii(ui, state, type, char, lines, coldata)
2773 graphmod.ascii(ui, state, type, char, lines, coldata)
2769 lines = []
2774 lines = []
2770 displayer.close()
2775 displayer.close()
2771
2776
2772 def graphlog(ui, repo, revs, filematcher, opts):
2777 def graphlog(ui, repo, revs, filematcher, opts):
2773 # Parameters are identical to log command ones
2778 # Parameters are identical to log command ones
2774 revdag = graphmod.dagwalker(repo, revs)
2779 revdag = graphmod.dagwalker(repo, revs)
2775
2780
2776 getrenamed = None
2781 getrenamed = None
2777 if opts.get('copies'):
2782 if opts.get('copies'):
2778 endrev = None
2783 endrev = None
2779 if opts.get('rev'):
2784 if opts.get('rev'):
2780 endrev = scmutil.revrange(repo, opts.get('rev')).max() + 1
2785 endrev = scmutil.revrange(repo, opts.get('rev')).max() + 1
2781 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
2786 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
2782
2787
2783 ui.pager('log')
2788 ui.pager('log')
2784 displayer = show_changeset(ui, repo, opts, buffered=True)
2789 displayer = show_changeset(ui, repo, opts, buffered=True)
2785 displaygraph(ui, repo, revdag, displayer, graphmod.asciiedges, getrenamed,
2790 displaygraph(ui, repo, revdag, displayer, graphmod.asciiedges, getrenamed,
2786 filematcher)
2791 filematcher)
2787
2792
2788 def checkunsupportedgraphflags(pats, opts):
2793 def checkunsupportedgraphflags(pats, opts):
2789 for op in ["newest_first"]:
2794 for op in ["newest_first"]:
2790 if op in opts and opts[op]:
2795 if op in opts and opts[op]:
2791 raise error.Abort(_("-G/--graph option is incompatible with --%s")
2796 raise error.Abort(_("-G/--graph option is incompatible with --%s")
2792 % op.replace("_", "-"))
2797 % op.replace("_", "-"))
2793
2798
2794 def graphrevs(repo, nodes, opts):
2799 def graphrevs(repo, nodes, opts):
2795 limit = loglimit(opts)
2800 limit = loglimit(opts)
2796 nodes.reverse()
2801 nodes.reverse()
2797 if limit is not None:
2802 if limit is not None:
2798 nodes = nodes[:limit]
2803 nodes = nodes[:limit]
2799 return graphmod.nodes(repo, nodes)
2804 return graphmod.nodes(repo, nodes)
2800
2805
2801 def add(ui, repo, match, prefix, explicitonly, **opts):
2806 def add(ui, repo, match, prefix, explicitonly, **opts):
2802 join = lambda f: os.path.join(prefix, f)
2807 join = lambda f: os.path.join(prefix, f)
2803 bad = []
2808 bad = []
2804
2809
2805 badfn = lambda x, y: bad.append(x) or match.bad(x, y)
2810 badfn = lambda x, y: bad.append(x) or match.bad(x, y)
2806 names = []
2811 names = []
2807 wctx = repo[None]
2812 wctx = repo[None]
2808 cca = None
2813 cca = None
2809 abort, warn = scmutil.checkportabilityalert(ui)
2814 abort, warn = scmutil.checkportabilityalert(ui)
2810 if abort or warn:
2815 if abort or warn:
2811 cca = scmutil.casecollisionauditor(ui, abort, repo.dirstate)
2816 cca = scmutil.casecollisionauditor(ui, abort, repo.dirstate)
2812
2817
2813 badmatch = matchmod.badmatch(match, badfn)
2818 badmatch = matchmod.badmatch(match, badfn)
2814 dirstate = repo.dirstate
2819 dirstate = repo.dirstate
2815 # We don't want to just call wctx.walk here, since it would return a lot of
2820 # We don't want to just call wctx.walk here, since it would return a lot of
2816 # clean files, which we aren't interested in and takes time.
2821 # clean files, which we aren't interested in and takes time.
2817 for f in sorted(dirstate.walk(badmatch, subrepos=sorted(wctx.substate),
2822 for f in sorted(dirstate.walk(badmatch, subrepos=sorted(wctx.substate),
2818 unknown=True, ignored=False, full=False)):
2823 unknown=True, ignored=False, full=False)):
2819 exact = match.exact(f)
2824 exact = match.exact(f)
2820 if exact or not explicitonly and f not in wctx and repo.wvfs.lexists(f):
2825 if exact or not explicitonly and f not in wctx and repo.wvfs.lexists(f):
2821 if cca:
2826 if cca:
2822 cca(f)
2827 cca(f)
2823 names.append(f)
2828 names.append(f)
2824 if ui.verbose or not exact:
2829 if ui.verbose or not exact:
2825 ui.status(_('adding %s\n') % match.rel(f))
2830 ui.status(_('adding %s\n') % match.rel(f))
2826
2831
2827 for subpath in sorted(wctx.substate):
2832 for subpath in sorted(wctx.substate):
2828 sub = wctx.sub(subpath)
2833 sub = wctx.sub(subpath)
2829 try:
2834 try:
2830 submatch = matchmod.subdirmatcher(subpath, match)
2835 submatch = matchmod.subdirmatcher(subpath, match)
2831 if opts.get(r'subrepos'):
2836 if opts.get(r'subrepos'):
2832 bad.extend(sub.add(ui, submatch, prefix, False, **opts))
2837 bad.extend(sub.add(ui, submatch, prefix, False, **opts))
2833 else:
2838 else:
2834 bad.extend(sub.add(ui, submatch, prefix, True, **opts))
2839 bad.extend(sub.add(ui, submatch, prefix, True, **opts))
2835 except error.LookupError:
2840 except error.LookupError:
2836 ui.status(_("skipping missing subrepository: %s\n")
2841 ui.status(_("skipping missing subrepository: %s\n")
2837 % join(subpath))
2842 % join(subpath))
2838
2843
2839 if not opts.get(r'dry_run'):
2844 if not opts.get(r'dry_run'):
2840 rejected = wctx.add(names, prefix)
2845 rejected = wctx.add(names, prefix)
2841 bad.extend(f for f in rejected if f in match.files())
2846 bad.extend(f for f in rejected if f in match.files())
2842 return bad
2847 return bad
2843
2848
2844 def addwebdirpath(repo, serverpath, webconf):
2849 def addwebdirpath(repo, serverpath, webconf):
2845 webconf[serverpath] = repo.root
2850 webconf[serverpath] = repo.root
2846 repo.ui.debug('adding %s = %s\n' % (serverpath, repo.root))
2851 repo.ui.debug('adding %s = %s\n' % (serverpath, repo.root))
2847
2852
2848 for r in repo.revs('filelog("path:.hgsub")'):
2853 for r in repo.revs('filelog("path:.hgsub")'):
2849 ctx = repo[r]
2854 ctx = repo[r]
2850 for subpath in ctx.substate:
2855 for subpath in ctx.substate:
2851 ctx.sub(subpath).addwebdirpath(serverpath, webconf)
2856 ctx.sub(subpath).addwebdirpath(serverpath, webconf)
2852
2857
2853 def forget(ui, repo, match, prefix, explicitonly):
2858 def forget(ui, repo, match, prefix, explicitonly):
2854 join = lambda f: os.path.join(prefix, f)
2859 join = lambda f: os.path.join(prefix, f)
2855 bad = []
2860 bad = []
2856 badfn = lambda x, y: bad.append(x) or match.bad(x, y)
2861 badfn = lambda x, y: bad.append(x) or match.bad(x, y)
2857 wctx = repo[None]
2862 wctx = repo[None]
2858 forgot = []
2863 forgot = []
2859
2864
2860 s = repo.status(match=matchmod.badmatch(match, badfn), clean=True)
2865 s = repo.status(match=matchmod.badmatch(match, badfn), clean=True)
2861 forget = sorted(s.modified + s.added + s.deleted + s.clean)
2866 forget = sorted(s.modified + s.added + s.deleted + s.clean)
2862 if explicitonly:
2867 if explicitonly:
2863 forget = [f for f in forget if match.exact(f)]
2868 forget = [f for f in forget if match.exact(f)]
2864
2869
2865 for subpath in sorted(wctx.substate):
2870 for subpath in sorted(wctx.substate):
2866 sub = wctx.sub(subpath)
2871 sub = wctx.sub(subpath)
2867 try:
2872 try:
2868 submatch = matchmod.subdirmatcher(subpath, match)
2873 submatch = matchmod.subdirmatcher(subpath, match)
2869 subbad, subforgot = sub.forget(submatch, prefix)
2874 subbad, subforgot = sub.forget(submatch, prefix)
2870 bad.extend([subpath + '/' + f for f in subbad])
2875 bad.extend([subpath + '/' + f for f in subbad])
2871 forgot.extend([subpath + '/' + f for f in subforgot])
2876 forgot.extend([subpath + '/' + f for f in subforgot])
2872 except error.LookupError:
2877 except error.LookupError:
2873 ui.status(_("skipping missing subrepository: %s\n")
2878 ui.status(_("skipping missing subrepository: %s\n")
2874 % join(subpath))
2879 % join(subpath))
2875
2880
2876 if not explicitonly:
2881 if not explicitonly:
2877 for f in match.files():
2882 for f in match.files():
2878 if f not in repo.dirstate and not repo.wvfs.isdir(f):
2883 if f not in repo.dirstate and not repo.wvfs.isdir(f):
2879 if f not in forgot:
2884 if f not in forgot:
2880 if repo.wvfs.exists(f):
2885 if repo.wvfs.exists(f):
2881 # Don't complain if the exact case match wasn't given.
2886 # Don't complain if the exact case match wasn't given.
2882 # But don't do this until after checking 'forgot', so
2887 # But don't do this until after checking 'forgot', so
2883 # that subrepo files aren't normalized, and this op is
2888 # that subrepo files aren't normalized, and this op is
2884 # purely from data cached by the status walk above.
2889 # purely from data cached by the status walk above.
2885 if repo.dirstate.normalize(f) in repo.dirstate:
2890 if repo.dirstate.normalize(f) in repo.dirstate:
2886 continue
2891 continue
2887 ui.warn(_('not removing %s: '
2892 ui.warn(_('not removing %s: '
2888 'file is already untracked\n')
2893 'file is already untracked\n')
2889 % match.rel(f))
2894 % match.rel(f))
2890 bad.append(f)
2895 bad.append(f)
2891
2896
2892 for f in forget:
2897 for f in forget:
2893 if ui.verbose or not match.exact(f):
2898 if ui.verbose or not match.exact(f):
2894 ui.status(_('removing %s\n') % match.rel(f))
2899 ui.status(_('removing %s\n') % match.rel(f))
2895
2900
2896 rejected = wctx.forget(forget, prefix)
2901 rejected = wctx.forget(forget, prefix)
2897 bad.extend(f for f in rejected if f in match.files())
2902 bad.extend(f for f in rejected if f in match.files())
2898 forgot.extend(f for f in forget if f not in rejected)
2903 forgot.extend(f for f in forget if f not in rejected)
2899 return bad, forgot
2904 return bad, forgot
2900
2905
2901 def files(ui, ctx, m, fm, fmt, subrepos):
2906 def files(ui, ctx, m, fm, fmt, subrepos):
2902 rev = ctx.rev()
2907 rev = ctx.rev()
2903 ret = 1
2908 ret = 1
2904 ds = ctx.repo().dirstate
2909 ds = ctx.repo().dirstate
2905
2910
2906 for f in ctx.matches(m):
2911 for f in ctx.matches(m):
2907 if rev is None and ds[f] == 'r':
2912 if rev is None and ds[f] == 'r':
2908 continue
2913 continue
2909 fm.startitem()
2914 fm.startitem()
2910 if ui.verbose:
2915 if ui.verbose:
2911 fc = ctx[f]
2916 fc = ctx[f]
2912 fm.write('size flags', '% 10d % 1s ', fc.size(), fc.flags())
2917 fm.write('size flags', '% 10d % 1s ', fc.size(), fc.flags())
2913 fm.data(abspath=f)
2918 fm.data(abspath=f)
2914 fm.write('path', fmt, m.rel(f))
2919 fm.write('path', fmt, m.rel(f))
2915 ret = 0
2920 ret = 0
2916
2921
2917 for subpath in sorted(ctx.substate):
2922 for subpath in sorted(ctx.substate):
2918 submatch = matchmod.subdirmatcher(subpath, m)
2923 submatch = matchmod.subdirmatcher(subpath, m)
2919 if (subrepos or m.exact(subpath) or any(submatch.files())):
2924 if (subrepos or m.exact(subpath) or any(submatch.files())):
2920 sub = ctx.sub(subpath)
2925 sub = ctx.sub(subpath)
2921 try:
2926 try:
2922 recurse = m.exact(subpath) or subrepos
2927 recurse = m.exact(subpath) or subrepos
2923 if sub.printfiles(ui, submatch, fm, fmt, recurse) == 0:
2928 if sub.printfiles(ui, submatch, fm, fmt, recurse) == 0:
2924 ret = 0
2929 ret = 0
2925 except error.LookupError:
2930 except error.LookupError:
2926 ui.status(_("skipping missing subrepository: %s\n")
2931 ui.status(_("skipping missing subrepository: %s\n")
2927 % m.abs(subpath))
2932 % m.abs(subpath))
2928
2933
2929 return ret
2934 return ret
2930
2935
2931 def remove(ui, repo, m, prefix, after, force, subrepos, warnings=None):
2936 def remove(ui, repo, m, prefix, after, force, subrepos, warnings=None):
2932 join = lambda f: os.path.join(prefix, f)
2937 join = lambda f: os.path.join(prefix, f)
2933 ret = 0
2938 ret = 0
2934 s = repo.status(match=m, clean=True)
2939 s = repo.status(match=m, clean=True)
2935 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
2940 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
2936
2941
2937 wctx = repo[None]
2942 wctx = repo[None]
2938
2943
2939 if warnings is None:
2944 if warnings is None:
2940 warnings = []
2945 warnings = []
2941 warn = True
2946 warn = True
2942 else:
2947 else:
2943 warn = False
2948 warn = False
2944
2949
2945 subs = sorted(wctx.substate)
2950 subs = sorted(wctx.substate)
2946 total = len(subs)
2951 total = len(subs)
2947 count = 0
2952 count = 0
2948 for subpath in subs:
2953 for subpath in subs:
2949 count += 1
2954 count += 1
2950 submatch = matchmod.subdirmatcher(subpath, m)
2955 submatch = matchmod.subdirmatcher(subpath, m)
2951 if subrepos or m.exact(subpath) or any(submatch.files()):
2956 if subrepos or m.exact(subpath) or any(submatch.files()):
2952 ui.progress(_('searching'), count, total=total, unit=_('subrepos'))
2957 ui.progress(_('searching'), count, total=total, unit=_('subrepos'))
2953 sub = wctx.sub(subpath)
2958 sub = wctx.sub(subpath)
2954 try:
2959 try:
2955 if sub.removefiles(submatch, prefix, after, force, subrepos,
2960 if sub.removefiles(submatch, prefix, after, force, subrepos,
2956 warnings):
2961 warnings):
2957 ret = 1
2962 ret = 1
2958 except error.LookupError:
2963 except error.LookupError:
2959 warnings.append(_("skipping missing subrepository: %s\n")
2964 warnings.append(_("skipping missing subrepository: %s\n")
2960 % join(subpath))
2965 % join(subpath))
2961 ui.progress(_('searching'), None)
2966 ui.progress(_('searching'), None)
2962
2967
2963 # warn about failure to delete explicit files/dirs
2968 # warn about failure to delete explicit files/dirs
2964 deleteddirs = util.dirs(deleted)
2969 deleteddirs = util.dirs(deleted)
2965 files = m.files()
2970 files = m.files()
2966 total = len(files)
2971 total = len(files)
2967 count = 0
2972 count = 0
2968 for f in files:
2973 for f in files:
2969 def insubrepo():
2974 def insubrepo():
2970 for subpath in wctx.substate:
2975 for subpath in wctx.substate:
2971 if f.startswith(subpath + '/'):
2976 if f.startswith(subpath + '/'):
2972 return True
2977 return True
2973 return False
2978 return False
2974
2979
2975 count += 1
2980 count += 1
2976 ui.progress(_('deleting'), count, total=total, unit=_('files'))
2981 ui.progress(_('deleting'), count, total=total, unit=_('files'))
2977 isdir = f in deleteddirs or wctx.hasdir(f)
2982 isdir = f in deleteddirs or wctx.hasdir(f)
2978 if (f in repo.dirstate or isdir or f == '.'
2983 if (f in repo.dirstate or isdir or f == '.'
2979 or insubrepo() or f in subs):
2984 or insubrepo() or f in subs):
2980 continue
2985 continue
2981
2986
2982 if repo.wvfs.exists(f):
2987 if repo.wvfs.exists(f):
2983 if repo.wvfs.isdir(f):
2988 if repo.wvfs.isdir(f):
2984 warnings.append(_('not removing %s: no tracked files\n')
2989 warnings.append(_('not removing %s: no tracked files\n')
2985 % m.rel(f))
2990 % m.rel(f))
2986 else:
2991 else:
2987 warnings.append(_('not removing %s: file is untracked\n')
2992 warnings.append(_('not removing %s: file is untracked\n')
2988 % m.rel(f))
2993 % m.rel(f))
2989 # missing files will generate a warning elsewhere
2994 # missing files will generate a warning elsewhere
2990 ret = 1
2995 ret = 1
2991 ui.progress(_('deleting'), None)
2996 ui.progress(_('deleting'), None)
2992
2997
2993 if force:
2998 if force:
2994 list = modified + deleted + clean + added
2999 list = modified + deleted + clean + added
2995 elif after:
3000 elif after:
2996 list = deleted
3001 list = deleted
2997 remaining = modified + added + clean
3002 remaining = modified + added + clean
2998 total = len(remaining)
3003 total = len(remaining)
2999 count = 0
3004 count = 0
3000 for f in remaining:
3005 for f in remaining:
3001 count += 1
3006 count += 1
3002 ui.progress(_('skipping'), count, total=total, unit=_('files'))
3007 ui.progress(_('skipping'), count, total=total, unit=_('files'))
3003 if ui.verbose or (f in files):
3008 if ui.verbose or (f in files):
3004 warnings.append(_('not removing %s: file still exists\n')
3009 warnings.append(_('not removing %s: file still exists\n')
3005 % m.rel(f))
3010 % m.rel(f))
3006 ret = 1
3011 ret = 1
3007 ui.progress(_('skipping'), None)
3012 ui.progress(_('skipping'), None)
3008 else:
3013 else:
3009 list = deleted + clean
3014 list = deleted + clean
3010 total = len(modified) + len(added)
3015 total = len(modified) + len(added)
3011 count = 0
3016 count = 0
3012 for f in modified:
3017 for f in modified:
3013 count += 1
3018 count += 1
3014 ui.progress(_('skipping'), count, total=total, unit=_('files'))
3019 ui.progress(_('skipping'), count, total=total, unit=_('files'))
3015 warnings.append(_('not removing %s: file is modified (use -f'
3020 warnings.append(_('not removing %s: file is modified (use -f'
3016 ' to force removal)\n') % m.rel(f))
3021 ' to force removal)\n') % m.rel(f))
3017 ret = 1
3022 ret = 1
3018 for f in added:
3023 for f in added:
3019 count += 1
3024 count += 1
3020 ui.progress(_('skipping'), count, total=total, unit=_('files'))
3025 ui.progress(_('skipping'), count, total=total, unit=_('files'))
3021 warnings.append(_("not removing %s: file has been marked for add"
3026 warnings.append(_("not removing %s: file has been marked for add"
3022 " (use 'hg forget' to undo add)\n") % m.rel(f))
3027 " (use 'hg forget' to undo add)\n") % m.rel(f))
3023 ret = 1
3028 ret = 1
3024 ui.progress(_('skipping'), None)
3029 ui.progress(_('skipping'), None)
3025
3030
3026 list = sorted(list)
3031 list = sorted(list)
3027 total = len(list)
3032 total = len(list)
3028 count = 0
3033 count = 0
3029 for f in list:
3034 for f in list:
3030 count += 1
3035 count += 1
3031 if ui.verbose or not m.exact(f):
3036 if ui.verbose or not m.exact(f):
3032 ui.progress(_('deleting'), count, total=total, unit=_('files'))
3037 ui.progress(_('deleting'), count, total=total, unit=_('files'))
3033 ui.status(_('removing %s\n') % m.rel(f))
3038 ui.status(_('removing %s\n') % m.rel(f))
3034 ui.progress(_('deleting'), None)
3039 ui.progress(_('deleting'), None)
3035
3040
3036 with repo.wlock():
3041 with repo.wlock():
3037 if not after:
3042 if not after:
3038 for f in list:
3043 for f in list:
3039 if f in added:
3044 if f in added:
3040 continue # we never unlink added files on remove
3045 continue # we never unlink added files on remove
3041 repo.wvfs.unlinkpath(f, ignoremissing=True)
3046 repo.wvfs.unlinkpath(f, ignoremissing=True)
3042 repo[None].forget(list)
3047 repo[None].forget(list)
3043
3048
3044 if warn:
3049 if warn:
3045 for warning in warnings:
3050 for warning in warnings:
3046 ui.warn(warning)
3051 ui.warn(warning)
3047
3052
3048 return ret
3053 return ret
3049
3054
3050 def _updatecatformatter(fm, ctx, matcher, path, decode):
3055 def _updatecatformatter(fm, ctx, matcher, path, decode):
3051 """Hook for adding data to the formatter used by ``hg cat``.
3056 """Hook for adding data to the formatter used by ``hg cat``.
3052
3057
3053 Extensions (e.g., lfs) can wrap this to inject keywords/data, but must call
3058 Extensions (e.g., lfs) can wrap this to inject keywords/data, but must call
3054 this method first."""
3059 this method first."""
3055 data = ctx[path].data()
3060 data = ctx[path].data()
3056 if decode:
3061 if decode:
3057 data = ctx.repo().wwritedata(path, data)
3062 data = ctx.repo().wwritedata(path, data)
3058 fm.startitem()
3063 fm.startitem()
3059 fm.write('data', '%s', data)
3064 fm.write('data', '%s', data)
3060 fm.data(abspath=path, path=matcher.rel(path))
3065 fm.data(abspath=path, path=matcher.rel(path))
3061
3066
3062 def cat(ui, repo, ctx, matcher, basefm, fntemplate, prefix, **opts):
3067 def cat(ui, repo, ctx, matcher, basefm, fntemplate, prefix, **opts):
3063 err = 1
3068 err = 1
3064 opts = pycompat.byteskwargs(opts)
3069 opts = pycompat.byteskwargs(opts)
3065
3070
3066 def write(path):
3071 def write(path):
3067 filename = None
3072 filename = None
3068 if fntemplate:
3073 if fntemplate:
3069 filename = makefilename(repo, fntemplate, ctx.node(),
3074 filename = makefilename(repo, fntemplate, ctx.node(),
3070 pathname=os.path.join(prefix, path))
3075 pathname=os.path.join(prefix, path))
3071 # attempt to create the directory if it does not already exist
3076 # attempt to create the directory if it does not already exist
3072 try:
3077 try:
3073 os.makedirs(os.path.dirname(filename))
3078 os.makedirs(os.path.dirname(filename))
3074 except OSError:
3079 except OSError:
3075 pass
3080 pass
3076 with formatter.maybereopen(basefm, filename, opts) as fm:
3081 with formatter.maybereopen(basefm, filename, opts) as fm:
3077 _updatecatformatter(fm, ctx, matcher, path, opts.get('decode'))
3082 _updatecatformatter(fm, ctx, matcher, path, opts.get('decode'))
3078
3083
3079 # Automation often uses hg cat on single files, so special case it
3084 # Automation often uses hg cat on single files, so special case it
3080 # for performance to avoid the cost of parsing the manifest.
3085 # for performance to avoid the cost of parsing the manifest.
3081 if len(matcher.files()) == 1 and not matcher.anypats():
3086 if len(matcher.files()) == 1 and not matcher.anypats():
3082 file = matcher.files()[0]
3087 file = matcher.files()[0]
3083 mfl = repo.manifestlog
3088 mfl = repo.manifestlog
3084 mfnode = ctx.manifestnode()
3089 mfnode = ctx.manifestnode()
3085 try:
3090 try:
3086 if mfnode and mfl[mfnode].find(file)[0]:
3091 if mfnode and mfl[mfnode].find(file)[0]:
3087 write(file)
3092 write(file)
3088 return 0
3093 return 0
3089 except KeyError:
3094 except KeyError:
3090 pass
3095 pass
3091
3096
3092 for abs in ctx.walk(matcher):
3097 for abs in ctx.walk(matcher):
3093 write(abs)
3098 write(abs)
3094 err = 0
3099 err = 0
3095
3100
3096 for subpath in sorted(ctx.substate):
3101 for subpath in sorted(ctx.substate):
3097 sub = ctx.sub(subpath)
3102 sub = ctx.sub(subpath)
3098 try:
3103 try:
3099 submatch = matchmod.subdirmatcher(subpath, matcher)
3104 submatch = matchmod.subdirmatcher(subpath, matcher)
3100
3105
3101 if not sub.cat(submatch, basefm, fntemplate,
3106 if not sub.cat(submatch, basefm, fntemplate,
3102 os.path.join(prefix, sub._path),
3107 os.path.join(prefix, sub._path),
3103 **pycompat.strkwargs(opts)):
3108 **pycompat.strkwargs(opts)):
3104 err = 0
3109 err = 0
3105 except error.RepoLookupError:
3110 except error.RepoLookupError:
3106 ui.status(_("skipping missing subrepository: %s\n")
3111 ui.status(_("skipping missing subrepository: %s\n")
3107 % os.path.join(prefix, subpath))
3112 % os.path.join(prefix, subpath))
3108
3113
3109 return err
3114 return err
3110
3115
3111 def commit(ui, repo, commitfunc, pats, opts):
3116 def commit(ui, repo, commitfunc, pats, opts):
3112 '''commit the specified files or all outstanding changes'''
3117 '''commit the specified files or all outstanding changes'''
3113 date = opts.get('date')
3118 date = opts.get('date')
3114 if date:
3119 if date:
3115 opts['date'] = util.parsedate(date)
3120 opts['date'] = util.parsedate(date)
3116 message = logmessage(ui, opts)
3121 message = logmessage(ui, opts)
3117 matcher = scmutil.match(repo[None], pats, opts)
3122 matcher = scmutil.match(repo[None], pats, opts)
3118
3123
3119 dsguard = None
3124 dsguard = None
3120 # extract addremove carefully -- this function can be called from a command
3125 # extract addremove carefully -- this function can be called from a command
3121 # that doesn't support addremove
3126 # that doesn't support addremove
3122 if opts.get('addremove'):
3127 if opts.get('addremove'):
3123 dsguard = dirstateguard.dirstateguard(repo, 'commit')
3128 dsguard = dirstateguard.dirstateguard(repo, 'commit')
3124 with dsguard or util.nullcontextmanager():
3129 with dsguard or util.nullcontextmanager():
3125 if dsguard:
3130 if dsguard:
3126 if scmutil.addremove(repo, matcher, "", opts) != 0:
3131 if scmutil.addremove(repo, matcher, "", opts) != 0:
3127 raise error.Abort(
3132 raise error.Abort(
3128 _("failed to mark all new/missing files as added/removed"))
3133 _("failed to mark all new/missing files as added/removed"))
3129
3134
3130 return commitfunc(ui, repo, message, matcher, opts)
3135 return commitfunc(ui, repo, message, matcher, opts)
3131
3136
3132 def samefile(f, ctx1, ctx2):
3137 def samefile(f, ctx1, ctx2):
3133 if f in ctx1.manifest():
3138 if f in ctx1.manifest():
3134 a = ctx1.filectx(f)
3139 a = ctx1.filectx(f)
3135 if f in ctx2.manifest():
3140 if f in ctx2.manifest():
3136 b = ctx2.filectx(f)
3141 b = ctx2.filectx(f)
3137 return (not a.cmp(b)
3142 return (not a.cmp(b)
3138 and a.flags() == b.flags())
3143 and a.flags() == b.flags())
3139 else:
3144 else:
3140 return False
3145 return False
3141 else:
3146 else:
3142 return f not in ctx2.manifest()
3147 return f not in ctx2.manifest()
3143
3148
3144 def amend(ui, repo, old, extra, pats, opts):
3149 def amend(ui, repo, old, extra, pats, opts):
3145 # avoid cycle context -> subrepo -> cmdutil
3150 # avoid cycle context -> subrepo -> cmdutil
3146 from . import context
3151 from . import context
3147
3152
3148 # amend will reuse the existing user if not specified, but the obsolete
3153 # amend will reuse the existing user if not specified, but the obsolete
3149 # marker creation requires that the current user's name is specified.
3154 # marker creation requires that the current user's name is specified.
3150 if obsolete.isenabled(repo, obsolete.createmarkersopt):
3155 if obsolete.isenabled(repo, obsolete.createmarkersopt):
3151 ui.username() # raise exception if username not set
3156 ui.username() # raise exception if username not set
3152
3157
3153 ui.note(_('amending changeset %s\n') % old)
3158 ui.note(_('amending changeset %s\n') % old)
3154 base = old.p1()
3159 base = old.p1()
3155
3160
3156 with repo.wlock(), repo.lock(), repo.transaction('amend'):
3161 with repo.wlock(), repo.lock(), repo.transaction('amend'):
3157 # Participating changesets:
3162 # Participating changesets:
3158 #
3163 #
3159 # wctx o - workingctx that contains changes from working copy
3164 # wctx o - workingctx that contains changes from working copy
3160 # | to go into amending commit
3165 # | to go into amending commit
3161 # |
3166 # |
3162 # old o - changeset to amend
3167 # old o - changeset to amend
3163 # |
3168 # |
3164 # base o - first parent of the changeset to amend
3169 # base o - first parent of the changeset to amend
3165 wctx = repo[None]
3170 wctx = repo[None]
3166
3171
3167 # Copy to avoid mutating input
3172 # Copy to avoid mutating input
3168 extra = extra.copy()
3173 extra = extra.copy()
3169 # Update extra dict from amended commit (e.g. to preserve graft
3174 # Update extra dict from amended commit (e.g. to preserve graft
3170 # source)
3175 # source)
3171 extra.update(old.extra())
3176 extra.update(old.extra())
3172
3177
3173 # Also update it from the from the wctx
3178 # Also update it from the from the wctx
3174 extra.update(wctx.extra())
3179 extra.update(wctx.extra())
3175
3180
3176 user = opts.get('user') or old.user()
3181 user = opts.get('user') or old.user()
3177 date = opts.get('date') or old.date()
3182 date = opts.get('date') or old.date()
3178
3183
3179 # Parse the date to allow comparison between date and old.date()
3184 # Parse the date to allow comparison between date and old.date()
3180 date = util.parsedate(date)
3185 date = util.parsedate(date)
3181
3186
3182 if len(old.parents()) > 1:
3187 if len(old.parents()) > 1:
3183 # ctx.files() isn't reliable for merges, so fall back to the
3188 # ctx.files() isn't reliable for merges, so fall back to the
3184 # slower repo.status() method
3189 # slower repo.status() method
3185 files = set([fn for st in repo.status(base, old)[:3]
3190 files = set([fn for st in repo.status(base, old)[:3]
3186 for fn in st])
3191 for fn in st])
3187 else:
3192 else:
3188 files = set(old.files())
3193 files = set(old.files())
3189
3194
3190 # add/remove the files to the working copy if the "addremove" option
3195 # add/remove the files to the working copy if the "addremove" option
3191 # was specified.
3196 # was specified.
3192 matcher = scmutil.match(wctx, pats, opts)
3197 matcher = scmutil.match(wctx, pats, opts)
3193 if (opts.get('addremove')
3198 if (opts.get('addremove')
3194 and scmutil.addremove(repo, matcher, "", opts)):
3199 and scmutil.addremove(repo, matcher, "", opts)):
3195 raise error.Abort(
3200 raise error.Abort(
3196 _("failed to mark all new/missing files as added/removed"))
3201 _("failed to mark all new/missing files as added/removed"))
3197
3202
3198 # Check subrepos. This depends on in-place wctx._status update in
3203 # Check subrepos. This depends on in-place wctx._status update in
3199 # subrepo.precommit(). To minimize the risk of this hack, we do
3204 # subrepo.precommit(). To minimize the risk of this hack, we do
3200 # nothing if .hgsub does not exist.
3205 # nothing if .hgsub does not exist.
3201 if '.hgsub' in wctx or '.hgsub' in old:
3206 if '.hgsub' in wctx or '.hgsub' in old:
3202 from . import subrepo # avoid cycle: cmdutil -> subrepo -> cmdutil
3207 from . import subrepo # avoid cycle: cmdutil -> subrepo -> cmdutil
3203 subs, commitsubs, newsubstate = subrepo.precommit(
3208 subs, commitsubs, newsubstate = subrepo.precommit(
3204 ui, wctx, wctx._status, matcher)
3209 ui, wctx, wctx._status, matcher)
3205 # amend should abort if commitsubrepos is enabled
3210 # amend should abort if commitsubrepos is enabled
3206 assert not commitsubs
3211 assert not commitsubs
3207 if subs:
3212 if subs:
3208 subrepo.writestate(repo, newsubstate)
3213 subrepo.writestate(repo, newsubstate)
3209
3214
3210 filestoamend = set(f for f in wctx.files() if matcher(f))
3215 filestoamend = set(f for f in wctx.files() if matcher(f))
3211
3216
3212 changes = (len(filestoamend) > 0)
3217 changes = (len(filestoamend) > 0)
3213 if changes:
3218 if changes:
3214 # Recompute copies (avoid recording a -> b -> a)
3219 # Recompute copies (avoid recording a -> b -> a)
3215 copied = copies.pathcopies(base, wctx, matcher)
3220 copied = copies.pathcopies(base, wctx, matcher)
3216 if old.p2:
3221 if old.p2:
3217 copied.update(copies.pathcopies(old.p2(), wctx, matcher))
3222 copied.update(copies.pathcopies(old.p2(), wctx, matcher))
3218
3223
3219 # Prune files which were reverted by the updates: if old
3224 # Prune files which were reverted by the updates: if old
3220 # introduced file X and the file was renamed in the working
3225 # introduced file X and the file was renamed in the working
3221 # copy, then those two files are the same and
3226 # copy, then those two files are the same and
3222 # we can discard X from our list of files. Likewise if X
3227 # we can discard X from our list of files. Likewise if X
3223 # was removed, it's no longer relevant. If X is missing (aka
3228 # was removed, it's no longer relevant. If X is missing (aka
3224 # deleted), old X must be preserved.
3229 # deleted), old X must be preserved.
3225 files.update(filestoamend)
3230 files.update(filestoamend)
3226 files = [f for f in files if (not samefile(f, wctx, base)
3231 files = [f for f in files if (not samefile(f, wctx, base)
3227 or f in wctx.deleted())]
3232 or f in wctx.deleted())]
3228
3233
3229 def filectxfn(repo, ctx_, path):
3234 def filectxfn(repo, ctx_, path):
3230 try:
3235 try:
3231 # If the file being considered is not amongst the files
3236 # If the file being considered is not amongst the files
3232 # to be amended, we should return the file context from the
3237 # to be amended, we should return the file context from the
3233 # old changeset. This avoids issues when only some files in
3238 # old changeset. This avoids issues when only some files in
3234 # the working copy are being amended but there are also
3239 # the working copy are being amended but there are also
3235 # changes to other files from the old changeset.
3240 # changes to other files from the old changeset.
3236 if path not in filestoamend:
3241 if path not in filestoamend:
3237 return old.filectx(path)
3242 return old.filectx(path)
3238
3243
3239 # Return None for removed files.
3244 # Return None for removed files.
3240 if path in wctx.removed():
3245 if path in wctx.removed():
3241 return None
3246 return None
3242
3247
3243 fctx = wctx[path]
3248 fctx = wctx[path]
3244 flags = fctx.flags()
3249 flags = fctx.flags()
3245 mctx = context.memfilectx(repo, ctx_,
3250 mctx = context.memfilectx(repo, ctx_,
3246 fctx.path(), fctx.data(),
3251 fctx.path(), fctx.data(),
3247 islink='l' in flags,
3252 islink='l' in flags,
3248 isexec='x' in flags,
3253 isexec='x' in flags,
3249 copied=copied.get(path))
3254 copied=copied.get(path))
3250 return mctx
3255 return mctx
3251 except KeyError:
3256 except KeyError:
3252 return None
3257 return None
3253 else:
3258 else:
3254 ui.note(_('copying changeset %s to %s\n') % (old, base))
3259 ui.note(_('copying changeset %s to %s\n') % (old, base))
3255
3260
3256 # Use version of files as in the old cset
3261 # Use version of files as in the old cset
3257 def filectxfn(repo, ctx_, path):
3262 def filectxfn(repo, ctx_, path):
3258 try:
3263 try:
3259 return old.filectx(path)
3264 return old.filectx(path)
3260 except KeyError:
3265 except KeyError:
3261 return None
3266 return None
3262
3267
3263 # See if we got a message from -m or -l, if not, open the editor with
3268 # See if we got a message from -m or -l, if not, open the editor with
3264 # the message of the changeset to amend.
3269 # the message of the changeset to amend.
3265 message = logmessage(ui, opts)
3270 message = logmessage(ui, opts)
3266
3271
3267 editform = mergeeditform(old, 'commit.amend')
3272 editform = mergeeditform(old, 'commit.amend')
3268 editor = getcommiteditor(editform=editform,
3273 editor = getcommiteditor(editform=editform,
3269 **pycompat.strkwargs(opts))
3274 **pycompat.strkwargs(opts))
3270
3275
3271 if not message:
3276 if not message:
3272 editor = getcommiteditor(edit=True, editform=editform)
3277 editor = getcommiteditor(edit=True, editform=editform)
3273 message = old.description()
3278 message = old.description()
3274
3279
3275 pureextra = extra.copy()
3280 pureextra = extra.copy()
3276 extra['amend_source'] = old.hex()
3281 extra['amend_source'] = old.hex()
3277
3282
3278 new = context.memctx(repo,
3283 new = context.memctx(repo,
3279 parents=[base.node(), old.p2().node()],
3284 parents=[base.node(), old.p2().node()],
3280 text=message,
3285 text=message,
3281 files=files,
3286 files=files,
3282 filectxfn=filectxfn,
3287 filectxfn=filectxfn,
3283 user=user,
3288 user=user,
3284 date=date,
3289 date=date,
3285 extra=extra,
3290 extra=extra,
3286 editor=editor)
3291 editor=editor)
3287
3292
3288 newdesc = changelog.stripdesc(new.description())
3293 newdesc = changelog.stripdesc(new.description())
3289 if ((not changes)
3294 if ((not changes)
3290 and newdesc == old.description()
3295 and newdesc == old.description()
3291 and user == old.user()
3296 and user == old.user()
3292 and date == old.date()
3297 and date == old.date()
3293 and pureextra == old.extra()):
3298 and pureextra == old.extra()):
3294 # nothing changed. continuing here would create a new node
3299 # nothing changed. continuing here would create a new node
3295 # anyway because of the amend_source noise.
3300 # anyway because of the amend_source noise.
3296 #
3301 #
3297 # This not what we expect from amend.
3302 # This not what we expect from amend.
3298 return old.node()
3303 return old.node()
3299
3304
3300 if opts.get('secret'):
3305 if opts.get('secret'):
3301 commitphase = 'secret'
3306 commitphase = 'secret'
3302 else:
3307 else:
3303 commitphase = old.phase()
3308 commitphase = old.phase()
3304 overrides = {('phases', 'new-commit'): commitphase}
3309 overrides = {('phases', 'new-commit'): commitphase}
3305 with ui.configoverride(overrides, 'amend'):
3310 with ui.configoverride(overrides, 'amend'):
3306 newid = repo.commitctx(new)
3311 newid = repo.commitctx(new)
3307
3312
3308 # Reroute the working copy parent to the new changeset
3313 # Reroute the working copy parent to the new changeset
3309 repo.setparents(newid, nullid)
3314 repo.setparents(newid, nullid)
3310 mapping = {old.node(): (newid,)}
3315 mapping = {old.node(): (newid,)}
3311 obsmetadata = None
3316 obsmetadata = None
3312 if opts.get('note'):
3317 if opts.get('note'):
3313 obsmetadata = {'note': opts['note']}
3318 obsmetadata = {'note': opts['note']}
3314 scmutil.cleanupnodes(repo, mapping, 'amend', metadata=obsmetadata)
3319 scmutil.cleanupnodes(repo, mapping, 'amend', metadata=obsmetadata)
3315
3320
3316 # Fixing the dirstate because localrepo.commitctx does not update
3321 # Fixing the dirstate because localrepo.commitctx does not update
3317 # it. This is rather convenient because we did not need to update
3322 # it. This is rather convenient because we did not need to update
3318 # the dirstate for all the files in the new commit which commitctx
3323 # the dirstate for all the files in the new commit which commitctx
3319 # could have done if it updated the dirstate. Now, we can
3324 # could have done if it updated the dirstate. Now, we can
3320 # selectively update the dirstate only for the amended files.
3325 # selectively update the dirstate only for the amended files.
3321 dirstate = repo.dirstate
3326 dirstate = repo.dirstate
3322
3327
3323 # Update the state of the files which were added and
3328 # Update the state of the files which were added and
3324 # and modified in the amend to "normal" in the dirstate.
3329 # and modified in the amend to "normal" in the dirstate.
3325 normalfiles = set(wctx.modified() + wctx.added()) & filestoamend
3330 normalfiles = set(wctx.modified() + wctx.added()) & filestoamend
3326 for f in normalfiles:
3331 for f in normalfiles:
3327 dirstate.normal(f)
3332 dirstate.normal(f)
3328
3333
3329 # Update the state of files which were removed in the amend
3334 # Update the state of files which were removed in the amend
3330 # to "removed" in the dirstate.
3335 # to "removed" in the dirstate.
3331 removedfiles = set(wctx.removed()) & filestoamend
3336 removedfiles = set(wctx.removed()) & filestoamend
3332 for f in removedfiles:
3337 for f in removedfiles:
3333 dirstate.drop(f)
3338 dirstate.drop(f)
3334
3339
3335 return newid
3340 return newid
3336
3341
3337 def commiteditor(repo, ctx, subs, editform=''):
3342 def commiteditor(repo, ctx, subs, editform=''):
3338 if ctx.description():
3343 if ctx.description():
3339 return ctx.description()
3344 return ctx.description()
3340 return commitforceeditor(repo, ctx, subs, editform=editform,
3345 return commitforceeditor(repo, ctx, subs, editform=editform,
3341 unchangedmessagedetection=True)
3346 unchangedmessagedetection=True)
3342
3347
3343 def commitforceeditor(repo, ctx, subs, finishdesc=None, extramsg=None,
3348 def commitforceeditor(repo, ctx, subs, finishdesc=None, extramsg=None,
3344 editform='', unchangedmessagedetection=False):
3349 editform='', unchangedmessagedetection=False):
3345 if not extramsg:
3350 if not extramsg:
3346 extramsg = _("Leave message empty to abort commit.")
3351 extramsg = _("Leave message empty to abort commit.")
3347
3352
3348 forms = [e for e in editform.split('.') if e]
3353 forms = [e for e in editform.split('.') if e]
3349 forms.insert(0, 'changeset')
3354 forms.insert(0, 'changeset')
3350 templatetext = None
3355 templatetext = None
3351 while forms:
3356 while forms:
3352 ref = '.'.join(forms)
3357 ref = '.'.join(forms)
3353 if repo.ui.config('committemplate', ref):
3358 if repo.ui.config('committemplate', ref):
3354 templatetext = committext = buildcommittemplate(
3359 templatetext = committext = buildcommittemplate(
3355 repo, ctx, subs, extramsg, ref)
3360 repo, ctx, subs, extramsg, ref)
3356 break
3361 break
3357 forms.pop()
3362 forms.pop()
3358 else:
3363 else:
3359 committext = buildcommittext(repo, ctx, subs, extramsg)
3364 committext = buildcommittext(repo, ctx, subs, extramsg)
3360
3365
3361 # run editor in the repository root
3366 # run editor in the repository root
3362 olddir = pycompat.getcwd()
3367 olddir = pycompat.getcwd()
3363 os.chdir(repo.root)
3368 os.chdir(repo.root)
3364
3369
3365 # make in-memory changes visible to external process
3370 # make in-memory changes visible to external process
3366 tr = repo.currenttransaction()
3371 tr = repo.currenttransaction()
3367 repo.dirstate.write(tr)
3372 repo.dirstate.write(tr)
3368 pending = tr and tr.writepending() and repo.root
3373 pending = tr and tr.writepending() and repo.root
3369
3374
3370 editortext = repo.ui.edit(committext, ctx.user(), ctx.extra(),
3375 editortext = repo.ui.edit(committext, ctx.user(), ctx.extra(),
3371 editform=editform, pending=pending,
3376 editform=editform, pending=pending,
3372 repopath=repo.path, action='commit')
3377 repopath=repo.path, action='commit')
3373 text = editortext
3378 text = editortext
3374
3379
3375 # strip away anything below this special string (used for editors that want
3380 # strip away anything below this special string (used for editors that want
3376 # to display the diff)
3381 # to display the diff)
3377 stripbelow = re.search(_linebelow, text, flags=re.MULTILINE)
3382 stripbelow = re.search(_linebelow, text, flags=re.MULTILINE)
3378 if stripbelow:
3383 if stripbelow:
3379 text = text[:stripbelow.start()]
3384 text = text[:stripbelow.start()]
3380
3385
3381 text = re.sub("(?m)^HG:.*(\n|$)", "", text)
3386 text = re.sub("(?m)^HG:.*(\n|$)", "", text)
3382 os.chdir(olddir)
3387 os.chdir(olddir)
3383
3388
3384 if finishdesc:
3389 if finishdesc:
3385 text = finishdesc(text)
3390 text = finishdesc(text)
3386 if not text.strip():
3391 if not text.strip():
3387 raise error.Abort(_("empty commit message"))
3392 raise error.Abort(_("empty commit message"))
3388 if unchangedmessagedetection and editortext == templatetext:
3393 if unchangedmessagedetection and editortext == templatetext:
3389 raise error.Abort(_("commit message unchanged"))
3394 raise error.Abort(_("commit message unchanged"))
3390
3395
3391 return text
3396 return text
3392
3397
3393 def buildcommittemplate(repo, ctx, subs, extramsg, ref):
3398 def buildcommittemplate(repo, ctx, subs, extramsg, ref):
3394 ui = repo.ui
3399 ui = repo.ui
3395 spec = formatter.templatespec(ref, None, None)
3400 spec = formatter.templatespec(ref, None, None)
3396 t = changeset_templater(ui, repo, spec, None, {}, False)
3401 t = changeset_templater(ui, repo, spec, None, {}, False)
3397 t.t.cache.update((k, templater.unquotestring(v))
3402 t.t.cache.update((k, templater.unquotestring(v))
3398 for k, v in repo.ui.configitems('committemplate'))
3403 for k, v in repo.ui.configitems('committemplate'))
3399
3404
3400 if not extramsg:
3405 if not extramsg:
3401 extramsg = '' # ensure that extramsg is string
3406 extramsg = '' # ensure that extramsg is string
3402
3407
3403 ui.pushbuffer()
3408 ui.pushbuffer()
3404 t.show(ctx, extramsg=extramsg)
3409 t.show(ctx, extramsg=extramsg)
3405 return ui.popbuffer()
3410 return ui.popbuffer()
3406
3411
3407 def hgprefix(msg):
3412 def hgprefix(msg):
3408 return "\n".join(["HG: %s" % a for a in msg.split("\n") if a])
3413 return "\n".join(["HG: %s" % a for a in msg.split("\n") if a])
3409
3414
3410 def buildcommittext(repo, ctx, subs, extramsg):
3415 def buildcommittext(repo, ctx, subs, extramsg):
3411 edittext = []
3416 edittext = []
3412 modified, added, removed = ctx.modified(), ctx.added(), ctx.removed()
3417 modified, added, removed = ctx.modified(), ctx.added(), ctx.removed()
3413 if ctx.description():
3418 if ctx.description():
3414 edittext.append(ctx.description())
3419 edittext.append(ctx.description())
3415 edittext.append("")
3420 edittext.append("")
3416 edittext.append("") # Empty line between message and comments.
3421 edittext.append("") # Empty line between message and comments.
3417 edittext.append(hgprefix(_("Enter commit message."
3422 edittext.append(hgprefix(_("Enter commit message."
3418 " Lines beginning with 'HG:' are removed.")))
3423 " Lines beginning with 'HG:' are removed.")))
3419 edittext.append(hgprefix(extramsg))
3424 edittext.append(hgprefix(extramsg))
3420 edittext.append("HG: --")
3425 edittext.append("HG: --")
3421 edittext.append(hgprefix(_("user: %s") % ctx.user()))
3426 edittext.append(hgprefix(_("user: %s") % ctx.user()))
3422 if ctx.p2():
3427 if ctx.p2():
3423 edittext.append(hgprefix(_("branch merge")))
3428 edittext.append(hgprefix(_("branch merge")))
3424 if ctx.branch():
3429 if ctx.branch():
3425 edittext.append(hgprefix(_("branch '%s'") % ctx.branch()))
3430 edittext.append(hgprefix(_("branch '%s'") % ctx.branch()))
3426 if bookmarks.isactivewdirparent(repo):
3431 if bookmarks.isactivewdirparent(repo):
3427 edittext.append(hgprefix(_("bookmark '%s'") % repo._activebookmark))
3432 edittext.append(hgprefix(_("bookmark '%s'") % repo._activebookmark))
3428 edittext.extend([hgprefix(_("subrepo %s") % s) for s in subs])
3433 edittext.extend([hgprefix(_("subrepo %s") % s) for s in subs])
3429 edittext.extend([hgprefix(_("added %s") % f) for f in added])
3434 edittext.extend([hgprefix(_("added %s") % f) for f in added])
3430 edittext.extend([hgprefix(_("changed %s") % f) for f in modified])
3435 edittext.extend([hgprefix(_("changed %s") % f) for f in modified])
3431 edittext.extend([hgprefix(_("removed %s") % f) for f in removed])
3436 edittext.extend([hgprefix(_("removed %s") % f) for f in removed])
3432 if not added and not modified and not removed:
3437 if not added and not modified and not removed:
3433 edittext.append(hgprefix(_("no files changed")))
3438 edittext.append(hgprefix(_("no files changed")))
3434 edittext.append("")
3439 edittext.append("")
3435
3440
3436 return "\n".join(edittext)
3441 return "\n".join(edittext)
3437
3442
3438 def commitstatus(repo, node, branch, bheads=None, opts=None):
3443 def commitstatus(repo, node, branch, bheads=None, opts=None):
3439 if opts is None:
3444 if opts is None:
3440 opts = {}
3445 opts = {}
3441 ctx = repo[node]
3446 ctx = repo[node]
3442 parents = ctx.parents()
3447 parents = ctx.parents()
3443
3448
3444 if (not opts.get('amend') and bheads and node not in bheads and not
3449 if (not opts.get('amend') and bheads and node not in bheads and not
3445 [x for x in parents if x.node() in bheads and x.branch() == branch]):
3450 [x for x in parents if x.node() in bheads and x.branch() == branch]):
3446 repo.ui.status(_('created new head\n'))
3451 repo.ui.status(_('created new head\n'))
3447 # The message is not printed for initial roots. For the other
3452 # The message is not printed for initial roots. For the other
3448 # changesets, it is printed in the following situations:
3453 # changesets, it is printed in the following situations:
3449 #
3454 #
3450 # Par column: for the 2 parents with ...
3455 # Par column: for the 2 parents with ...
3451 # N: null or no parent
3456 # N: null or no parent
3452 # B: parent is on another named branch
3457 # B: parent is on another named branch
3453 # C: parent is a regular non head changeset
3458 # C: parent is a regular non head changeset
3454 # H: parent was a branch head of the current branch
3459 # H: parent was a branch head of the current branch
3455 # Msg column: whether we print "created new head" message
3460 # Msg column: whether we print "created new head" message
3456 # In the following, it is assumed that there already exists some
3461 # In the following, it is assumed that there already exists some
3457 # initial branch heads of the current branch, otherwise nothing is
3462 # initial branch heads of the current branch, otherwise nothing is
3458 # printed anyway.
3463 # printed anyway.
3459 #
3464 #
3460 # Par Msg Comment
3465 # Par Msg Comment
3461 # N N y additional topo root
3466 # N N y additional topo root
3462 #
3467 #
3463 # B N y additional branch root
3468 # B N y additional branch root
3464 # C N y additional topo head
3469 # C N y additional topo head
3465 # H N n usual case
3470 # H N n usual case
3466 #
3471 #
3467 # B B y weird additional branch root
3472 # B B y weird additional branch root
3468 # C B y branch merge
3473 # C B y branch merge
3469 # H B n merge with named branch
3474 # H B n merge with named branch
3470 #
3475 #
3471 # C C y additional head from merge
3476 # C C y additional head from merge
3472 # C H n merge with a head
3477 # C H n merge with a head
3473 #
3478 #
3474 # H H n head merge: head count decreases
3479 # H H n head merge: head count decreases
3475
3480
3476 if not opts.get('close_branch'):
3481 if not opts.get('close_branch'):
3477 for r in parents:
3482 for r in parents:
3478 if r.closesbranch() and r.branch() == branch:
3483 if r.closesbranch() and r.branch() == branch:
3479 repo.ui.status(_('reopening closed branch head %d\n') % r)
3484 repo.ui.status(_('reopening closed branch head %d\n') % r)
3480
3485
3481 if repo.ui.debugflag:
3486 if repo.ui.debugflag:
3482 repo.ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx.hex()))
3487 repo.ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx.hex()))
3483 elif repo.ui.verbose:
3488 elif repo.ui.verbose:
3484 repo.ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx))
3489 repo.ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx))
3485
3490
3486 def postcommitstatus(repo, pats, opts):
3491 def postcommitstatus(repo, pats, opts):
3487 return repo.status(match=scmutil.match(repo[None], pats, opts))
3492 return repo.status(match=scmutil.match(repo[None], pats, opts))
3488
3493
3489 def revert(ui, repo, ctx, parents, *pats, **opts):
3494 def revert(ui, repo, ctx, parents, *pats, **opts):
3490 opts = pycompat.byteskwargs(opts)
3495 opts = pycompat.byteskwargs(opts)
3491 parent, p2 = parents
3496 parent, p2 = parents
3492 node = ctx.node()
3497 node = ctx.node()
3493
3498
3494 mf = ctx.manifest()
3499 mf = ctx.manifest()
3495 if node == p2:
3500 if node == p2:
3496 parent = p2
3501 parent = p2
3497
3502
3498 # need all matching names in dirstate and manifest of target rev,
3503 # need all matching names in dirstate and manifest of target rev,
3499 # so have to walk both. do not print errors if files exist in one
3504 # so have to walk both. do not print errors if files exist in one
3500 # but not other. in both cases, filesets should be evaluated against
3505 # but not other. in both cases, filesets should be evaluated against
3501 # workingctx to get consistent result (issue4497). this means 'set:**'
3506 # workingctx to get consistent result (issue4497). this means 'set:**'
3502 # cannot be used to select missing files from target rev.
3507 # cannot be used to select missing files from target rev.
3503
3508
3504 # `names` is a mapping for all elements in working copy and target revision
3509 # `names` is a mapping for all elements in working copy and target revision
3505 # The mapping is in the form:
3510 # The mapping is in the form:
3506 # <asb path in repo> -> (<path from CWD>, <exactly specified by matcher?>)
3511 # <asb path in repo> -> (<path from CWD>, <exactly specified by matcher?>)
3507 names = {}
3512 names = {}
3508
3513
3509 with repo.wlock():
3514 with repo.wlock():
3510 ## filling of the `names` mapping
3515 ## filling of the `names` mapping
3511 # walk dirstate to fill `names`
3516 # walk dirstate to fill `names`
3512
3517
3513 interactive = opts.get('interactive', False)
3518 interactive = opts.get('interactive', False)
3514 wctx = repo[None]
3519 wctx = repo[None]
3515 m = scmutil.match(wctx, pats, opts)
3520 m = scmutil.match(wctx, pats, opts)
3516
3521
3517 # we'll need this later
3522 # we'll need this later
3518 targetsubs = sorted(s for s in wctx.substate if m(s))
3523 targetsubs = sorted(s for s in wctx.substate if m(s))
3519
3524
3520 if not m.always():
3525 if not m.always():
3521 matcher = matchmod.badmatch(m, lambda x, y: False)
3526 matcher = matchmod.badmatch(m, lambda x, y: False)
3522 for abs in wctx.walk(matcher):
3527 for abs in wctx.walk(matcher):
3523 names[abs] = m.rel(abs), m.exact(abs)
3528 names[abs] = m.rel(abs), m.exact(abs)
3524
3529
3525 # walk target manifest to fill `names`
3530 # walk target manifest to fill `names`
3526
3531
3527 def badfn(path, msg):
3532 def badfn(path, msg):
3528 if path in names:
3533 if path in names:
3529 return
3534 return
3530 if path in ctx.substate:
3535 if path in ctx.substate:
3531 return
3536 return
3532 path_ = path + '/'
3537 path_ = path + '/'
3533 for f in names:
3538 for f in names:
3534 if f.startswith(path_):
3539 if f.startswith(path_):
3535 return
3540 return
3536 ui.warn("%s: %s\n" % (m.rel(path), msg))
3541 ui.warn("%s: %s\n" % (m.rel(path), msg))
3537
3542
3538 for abs in ctx.walk(matchmod.badmatch(m, badfn)):
3543 for abs in ctx.walk(matchmod.badmatch(m, badfn)):
3539 if abs not in names:
3544 if abs not in names:
3540 names[abs] = m.rel(abs), m.exact(abs)
3545 names[abs] = m.rel(abs), m.exact(abs)
3541
3546
3542 # Find status of all file in `names`.
3547 # Find status of all file in `names`.
3543 m = scmutil.matchfiles(repo, names)
3548 m = scmutil.matchfiles(repo, names)
3544
3549
3545 changes = repo.status(node1=node, match=m,
3550 changes = repo.status(node1=node, match=m,
3546 unknown=True, ignored=True, clean=True)
3551 unknown=True, ignored=True, clean=True)
3547 else:
3552 else:
3548 changes = repo.status(node1=node, match=m)
3553 changes = repo.status(node1=node, match=m)
3549 for kind in changes:
3554 for kind in changes:
3550 for abs in kind:
3555 for abs in kind:
3551 names[abs] = m.rel(abs), m.exact(abs)
3556 names[abs] = m.rel(abs), m.exact(abs)
3552
3557
3553 m = scmutil.matchfiles(repo, names)
3558 m = scmutil.matchfiles(repo, names)
3554
3559
3555 modified = set(changes.modified)
3560 modified = set(changes.modified)
3556 added = set(changes.added)
3561 added = set(changes.added)
3557 removed = set(changes.removed)
3562 removed = set(changes.removed)
3558 _deleted = set(changes.deleted)
3563 _deleted = set(changes.deleted)
3559 unknown = set(changes.unknown)
3564 unknown = set(changes.unknown)
3560 unknown.update(changes.ignored)
3565 unknown.update(changes.ignored)
3561 clean = set(changes.clean)
3566 clean = set(changes.clean)
3562 modadded = set()
3567 modadded = set()
3563
3568
3564 # We need to account for the state of the file in the dirstate,
3569 # We need to account for the state of the file in the dirstate,
3565 # even when we revert against something else than parent. This will
3570 # even when we revert against something else than parent. This will
3566 # slightly alter the behavior of revert (doing back up or not, delete
3571 # slightly alter the behavior of revert (doing back up or not, delete
3567 # or just forget etc).
3572 # or just forget etc).
3568 if parent == node:
3573 if parent == node:
3569 dsmodified = modified
3574 dsmodified = modified
3570 dsadded = added
3575 dsadded = added
3571 dsremoved = removed
3576 dsremoved = removed
3572 # store all local modifications, useful later for rename detection
3577 # store all local modifications, useful later for rename detection
3573 localchanges = dsmodified | dsadded
3578 localchanges = dsmodified | dsadded
3574 modified, added, removed = set(), set(), set()
3579 modified, added, removed = set(), set(), set()
3575 else:
3580 else:
3576 changes = repo.status(node1=parent, match=m)
3581 changes = repo.status(node1=parent, match=m)
3577 dsmodified = set(changes.modified)
3582 dsmodified = set(changes.modified)
3578 dsadded = set(changes.added)
3583 dsadded = set(changes.added)
3579 dsremoved = set(changes.removed)
3584 dsremoved = set(changes.removed)
3580 # store all local modifications, useful later for rename detection
3585 # store all local modifications, useful later for rename detection
3581 localchanges = dsmodified | dsadded
3586 localchanges = dsmodified | dsadded
3582
3587
3583 # only take into account for removes between wc and target
3588 # only take into account for removes between wc and target
3584 clean |= dsremoved - removed
3589 clean |= dsremoved - removed
3585 dsremoved &= removed
3590 dsremoved &= removed
3586 # distinct between dirstate remove and other
3591 # distinct between dirstate remove and other
3587 removed -= dsremoved
3592 removed -= dsremoved
3588
3593
3589 modadded = added & dsmodified
3594 modadded = added & dsmodified
3590 added -= modadded
3595 added -= modadded
3591
3596
3592 # tell newly modified apart.
3597 # tell newly modified apart.
3593 dsmodified &= modified
3598 dsmodified &= modified
3594 dsmodified |= modified & dsadded # dirstate added may need backup
3599 dsmodified |= modified & dsadded # dirstate added may need backup
3595 modified -= dsmodified
3600 modified -= dsmodified
3596
3601
3597 # We need to wait for some post-processing to update this set
3602 # We need to wait for some post-processing to update this set
3598 # before making the distinction. The dirstate will be used for
3603 # before making the distinction. The dirstate will be used for
3599 # that purpose.
3604 # that purpose.
3600 dsadded = added
3605 dsadded = added
3601
3606
3602 # in case of merge, files that are actually added can be reported as
3607 # in case of merge, files that are actually added can be reported as
3603 # modified, we need to post process the result
3608 # modified, we need to post process the result
3604 if p2 != nullid:
3609 if p2 != nullid:
3605 mergeadd = set(dsmodified)
3610 mergeadd = set(dsmodified)
3606 for path in dsmodified:
3611 for path in dsmodified:
3607 if path in mf:
3612 if path in mf:
3608 mergeadd.remove(path)
3613 mergeadd.remove(path)
3609 dsadded |= mergeadd
3614 dsadded |= mergeadd
3610 dsmodified -= mergeadd
3615 dsmodified -= mergeadd
3611
3616
3612 # if f is a rename, update `names` to also revert the source
3617 # if f is a rename, update `names` to also revert the source
3613 cwd = repo.getcwd()
3618 cwd = repo.getcwd()
3614 for f in localchanges:
3619 for f in localchanges:
3615 src = repo.dirstate.copied(f)
3620 src = repo.dirstate.copied(f)
3616 # XXX should we check for rename down to target node?
3621 # XXX should we check for rename down to target node?
3617 if src and src not in names and repo.dirstate[src] == 'r':
3622 if src and src not in names and repo.dirstate[src] == 'r':
3618 dsremoved.add(src)
3623 dsremoved.add(src)
3619 names[src] = (repo.pathto(src, cwd), True)
3624 names[src] = (repo.pathto(src, cwd), True)
3620
3625
3621 # determine the exact nature of the deleted changesets
3626 # determine the exact nature of the deleted changesets
3622 deladded = set(_deleted)
3627 deladded = set(_deleted)
3623 for path in _deleted:
3628 for path in _deleted:
3624 if path in mf:
3629 if path in mf:
3625 deladded.remove(path)
3630 deladded.remove(path)
3626 deleted = _deleted - deladded
3631 deleted = _deleted - deladded
3627
3632
3628 # distinguish between file to forget and the other
3633 # distinguish between file to forget and the other
3629 added = set()
3634 added = set()
3630 for abs in dsadded:
3635 for abs in dsadded:
3631 if repo.dirstate[abs] != 'a':
3636 if repo.dirstate[abs] != 'a':
3632 added.add(abs)
3637 added.add(abs)
3633 dsadded -= added
3638 dsadded -= added
3634
3639
3635 for abs in deladded:
3640 for abs in deladded:
3636 if repo.dirstate[abs] == 'a':
3641 if repo.dirstate[abs] == 'a':
3637 dsadded.add(abs)
3642 dsadded.add(abs)
3638 deladded -= dsadded
3643 deladded -= dsadded
3639
3644
3640 # For files marked as removed, we check if an unknown file is present at
3645 # For files marked as removed, we check if an unknown file is present at
3641 # the same path. If a such file exists it may need to be backed up.
3646 # the same path. If a such file exists it may need to be backed up.
3642 # Making the distinction at this stage helps have simpler backup
3647 # Making the distinction at this stage helps have simpler backup
3643 # logic.
3648 # logic.
3644 removunk = set()
3649 removunk = set()
3645 for abs in removed:
3650 for abs in removed:
3646 target = repo.wjoin(abs)
3651 target = repo.wjoin(abs)
3647 if os.path.lexists(target):
3652 if os.path.lexists(target):
3648 removunk.add(abs)
3653 removunk.add(abs)
3649 removed -= removunk
3654 removed -= removunk
3650
3655
3651 dsremovunk = set()
3656 dsremovunk = set()
3652 for abs in dsremoved:
3657 for abs in dsremoved:
3653 target = repo.wjoin(abs)
3658 target = repo.wjoin(abs)
3654 if os.path.lexists(target):
3659 if os.path.lexists(target):
3655 dsremovunk.add(abs)
3660 dsremovunk.add(abs)
3656 dsremoved -= dsremovunk
3661 dsremoved -= dsremovunk
3657
3662
3658 # action to be actually performed by revert
3663 # action to be actually performed by revert
3659 # (<list of file>, message>) tuple
3664 # (<list of file>, message>) tuple
3660 actions = {'revert': ([], _('reverting %s\n')),
3665 actions = {'revert': ([], _('reverting %s\n')),
3661 'add': ([], _('adding %s\n')),
3666 'add': ([], _('adding %s\n')),
3662 'remove': ([], _('removing %s\n')),
3667 'remove': ([], _('removing %s\n')),
3663 'drop': ([], _('removing %s\n')),
3668 'drop': ([], _('removing %s\n')),
3664 'forget': ([], _('forgetting %s\n')),
3669 'forget': ([], _('forgetting %s\n')),
3665 'undelete': ([], _('undeleting %s\n')),
3670 'undelete': ([], _('undeleting %s\n')),
3666 'noop': (None, _('no changes needed to %s\n')),
3671 'noop': (None, _('no changes needed to %s\n')),
3667 'unknown': (None, _('file not managed: %s\n')),
3672 'unknown': (None, _('file not managed: %s\n')),
3668 }
3673 }
3669
3674
3670 # "constant" that convey the backup strategy.
3675 # "constant" that convey the backup strategy.
3671 # All set to `discard` if `no-backup` is set do avoid checking
3676 # All set to `discard` if `no-backup` is set do avoid checking
3672 # no_backup lower in the code.
3677 # no_backup lower in the code.
3673 # These values are ordered for comparison purposes
3678 # These values are ordered for comparison purposes
3674 backupinteractive = 3 # do backup if interactively modified
3679 backupinteractive = 3 # do backup if interactively modified
3675 backup = 2 # unconditionally do backup
3680 backup = 2 # unconditionally do backup
3676 check = 1 # check if the existing file differs from target
3681 check = 1 # check if the existing file differs from target
3677 discard = 0 # never do backup
3682 discard = 0 # never do backup
3678 if opts.get('no_backup'):
3683 if opts.get('no_backup'):
3679 backupinteractive = backup = check = discard
3684 backupinteractive = backup = check = discard
3680 if interactive:
3685 if interactive:
3681 dsmodifiedbackup = backupinteractive
3686 dsmodifiedbackup = backupinteractive
3682 else:
3687 else:
3683 dsmodifiedbackup = backup
3688 dsmodifiedbackup = backup
3684 tobackup = set()
3689 tobackup = set()
3685
3690
3686 backupanddel = actions['remove']
3691 backupanddel = actions['remove']
3687 if not opts.get('no_backup'):
3692 if not opts.get('no_backup'):
3688 backupanddel = actions['drop']
3693 backupanddel = actions['drop']
3689
3694
3690 disptable = (
3695 disptable = (
3691 # dispatch table:
3696 # dispatch table:
3692 # file state
3697 # file state
3693 # action
3698 # action
3694 # make backup
3699 # make backup
3695
3700
3696 ## Sets that results that will change file on disk
3701 ## Sets that results that will change file on disk
3697 # Modified compared to target, no local change
3702 # Modified compared to target, no local change
3698 (modified, actions['revert'], discard),
3703 (modified, actions['revert'], discard),
3699 # Modified compared to target, but local file is deleted
3704 # Modified compared to target, but local file is deleted
3700 (deleted, actions['revert'], discard),
3705 (deleted, actions['revert'], discard),
3701 # Modified compared to target, local change
3706 # Modified compared to target, local change
3702 (dsmodified, actions['revert'], dsmodifiedbackup),
3707 (dsmodified, actions['revert'], dsmodifiedbackup),
3703 # Added since target
3708 # Added since target
3704 (added, actions['remove'], discard),
3709 (added, actions['remove'], discard),
3705 # Added in working directory
3710 # Added in working directory
3706 (dsadded, actions['forget'], discard),
3711 (dsadded, actions['forget'], discard),
3707 # Added since target, have local modification
3712 # Added since target, have local modification
3708 (modadded, backupanddel, backup),
3713 (modadded, backupanddel, backup),
3709 # Added since target but file is missing in working directory
3714 # Added since target but file is missing in working directory
3710 (deladded, actions['drop'], discard),
3715 (deladded, actions['drop'], discard),
3711 # Removed since target, before working copy parent
3716 # Removed since target, before working copy parent
3712 (removed, actions['add'], discard),
3717 (removed, actions['add'], discard),
3713 # Same as `removed` but an unknown file exists at the same path
3718 # Same as `removed` but an unknown file exists at the same path
3714 (removunk, actions['add'], check),
3719 (removunk, actions['add'], check),
3715 # Removed since targe, marked as such in working copy parent
3720 # Removed since targe, marked as such in working copy parent
3716 (dsremoved, actions['undelete'], discard),
3721 (dsremoved, actions['undelete'], discard),
3717 # Same as `dsremoved` but an unknown file exists at the same path
3722 # Same as `dsremoved` but an unknown file exists at the same path
3718 (dsremovunk, actions['undelete'], check),
3723 (dsremovunk, actions['undelete'], check),
3719 ## the following sets does not result in any file changes
3724 ## the following sets does not result in any file changes
3720 # File with no modification
3725 # File with no modification
3721 (clean, actions['noop'], discard),
3726 (clean, actions['noop'], discard),
3722 # Existing file, not tracked anywhere
3727 # Existing file, not tracked anywhere
3723 (unknown, actions['unknown'], discard),
3728 (unknown, actions['unknown'], discard),
3724 )
3729 )
3725
3730
3726 for abs, (rel, exact) in sorted(names.items()):
3731 for abs, (rel, exact) in sorted(names.items()):
3727 # target file to be touch on disk (relative to cwd)
3732 # target file to be touch on disk (relative to cwd)
3728 target = repo.wjoin(abs)
3733 target = repo.wjoin(abs)
3729 # search the entry in the dispatch table.
3734 # search the entry in the dispatch table.
3730 # if the file is in any of these sets, it was touched in the working
3735 # if the file is in any of these sets, it was touched in the working
3731 # directory parent and we are sure it needs to be reverted.
3736 # directory parent and we are sure it needs to be reverted.
3732 for table, (xlist, msg), dobackup in disptable:
3737 for table, (xlist, msg), dobackup in disptable:
3733 if abs not in table:
3738 if abs not in table:
3734 continue
3739 continue
3735 if xlist is not None:
3740 if xlist is not None:
3736 xlist.append(abs)
3741 xlist.append(abs)
3737 if dobackup:
3742 if dobackup:
3738 # If in interactive mode, don't automatically create
3743 # If in interactive mode, don't automatically create
3739 # .orig files (issue4793)
3744 # .orig files (issue4793)
3740 if dobackup == backupinteractive:
3745 if dobackup == backupinteractive:
3741 tobackup.add(abs)
3746 tobackup.add(abs)
3742 elif (backup <= dobackup or wctx[abs].cmp(ctx[abs])):
3747 elif (backup <= dobackup or wctx[abs].cmp(ctx[abs])):
3743 bakname = scmutil.origpath(ui, repo, rel)
3748 bakname = scmutil.origpath(ui, repo, rel)
3744 ui.note(_('saving current version of %s as %s\n') %
3749 ui.note(_('saving current version of %s as %s\n') %
3745 (rel, bakname))
3750 (rel, bakname))
3746 if not opts.get('dry_run'):
3751 if not opts.get('dry_run'):
3747 if interactive:
3752 if interactive:
3748 util.copyfile(target, bakname)
3753 util.copyfile(target, bakname)
3749 else:
3754 else:
3750 util.rename(target, bakname)
3755 util.rename(target, bakname)
3751 if ui.verbose or not exact:
3756 if ui.verbose or not exact:
3752 if not isinstance(msg, bytes):
3757 if not isinstance(msg, bytes):
3753 msg = msg(abs)
3758 msg = msg(abs)
3754 ui.status(msg % rel)
3759 ui.status(msg % rel)
3755 elif exact:
3760 elif exact:
3756 ui.warn(msg % rel)
3761 ui.warn(msg % rel)
3757 break
3762 break
3758
3763
3759 if not opts.get('dry_run'):
3764 if not opts.get('dry_run'):
3760 needdata = ('revert', 'add', 'undelete')
3765 needdata = ('revert', 'add', 'undelete')
3761 _revertprefetch(repo, ctx, *[actions[name][0] for name in needdata])
3766 _revertprefetch(repo, ctx, *[actions[name][0] for name in needdata])
3762 _performrevert(repo, parents, ctx, actions, interactive, tobackup)
3767 _performrevert(repo, parents, ctx, actions, interactive, tobackup)
3763
3768
3764 if targetsubs:
3769 if targetsubs:
3765 # Revert the subrepos on the revert list
3770 # Revert the subrepos on the revert list
3766 for sub in targetsubs:
3771 for sub in targetsubs:
3767 try:
3772 try:
3768 wctx.sub(sub).revert(ctx.substate[sub], *pats,
3773 wctx.sub(sub).revert(ctx.substate[sub], *pats,
3769 **pycompat.strkwargs(opts))
3774 **pycompat.strkwargs(opts))
3770 except KeyError:
3775 except KeyError:
3771 raise error.Abort("subrepository '%s' does not exist in %s!"
3776 raise error.Abort("subrepository '%s' does not exist in %s!"
3772 % (sub, short(ctx.node())))
3777 % (sub, short(ctx.node())))
3773
3778
3774 def _revertprefetch(repo, ctx, *files):
3779 def _revertprefetch(repo, ctx, *files):
3775 """Let extension changing the storage layer prefetch content"""
3780 """Let extension changing the storage layer prefetch content"""
3776
3781
3777 def _performrevert(repo, parents, ctx, actions, interactive=False,
3782 def _performrevert(repo, parents, ctx, actions, interactive=False,
3778 tobackup=None):
3783 tobackup=None):
3779 """function that actually perform all the actions computed for revert
3784 """function that actually perform all the actions computed for revert
3780
3785
3781 This is an independent function to let extension to plug in and react to
3786 This is an independent function to let extension to plug in and react to
3782 the imminent revert.
3787 the imminent revert.
3783
3788
3784 Make sure you have the working directory locked when calling this function.
3789 Make sure you have the working directory locked when calling this function.
3785 """
3790 """
3786 parent, p2 = parents
3791 parent, p2 = parents
3787 node = ctx.node()
3792 node = ctx.node()
3788 excluded_files = []
3793 excluded_files = []
3789 matcher_opts = {"exclude": excluded_files}
3794 matcher_opts = {"exclude": excluded_files}
3790
3795
3791 def checkout(f):
3796 def checkout(f):
3792 fc = ctx[f]
3797 fc = ctx[f]
3793 repo.wwrite(f, fc.data(), fc.flags())
3798 repo.wwrite(f, fc.data(), fc.flags())
3794
3799
3795 def doremove(f):
3800 def doremove(f):
3796 try:
3801 try:
3797 repo.wvfs.unlinkpath(f)
3802 repo.wvfs.unlinkpath(f)
3798 except OSError:
3803 except OSError:
3799 pass
3804 pass
3800 repo.dirstate.remove(f)
3805 repo.dirstate.remove(f)
3801
3806
3802 audit_path = pathutil.pathauditor(repo.root, cached=True)
3807 audit_path = pathutil.pathauditor(repo.root, cached=True)
3803 for f in actions['forget'][0]:
3808 for f in actions['forget'][0]:
3804 if interactive:
3809 if interactive:
3805 choice = repo.ui.promptchoice(
3810 choice = repo.ui.promptchoice(
3806 _("forget added file %s (Yn)?$$ &Yes $$ &No") % f)
3811 _("forget added file %s (Yn)?$$ &Yes $$ &No") % f)
3807 if choice == 0:
3812 if choice == 0:
3808 repo.dirstate.drop(f)
3813 repo.dirstate.drop(f)
3809 else:
3814 else:
3810 excluded_files.append(repo.wjoin(f))
3815 excluded_files.append(repo.wjoin(f))
3811 else:
3816 else:
3812 repo.dirstate.drop(f)
3817 repo.dirstate.drop(f)
3813 for f in actions['remove'][0]:
3818 for f in actions['remove'][0]:
3814 audit_path(f)
3819 audit_path(f)
3815 if interactive:
3820 if interactive:
3816 choice = repo.ui.promptchoice(
3821 choice = repo.ui.promptchoice(
3817 _("remove added file %s (Yn)?$$ &Yes $$ &No") % f)
3822 _("remove added file %s (Yn)?$$ &Yes $$ &No") % f)
3818 if choice == 0:
3823 if choice == 0:
3819 doremove(f)
3824 doremove(f)
3820 else:
3825 else:
3821 excluded_files.append(repo.wjoin(f))
3826 excluded_files.append(repo.wjoin(f))
3822 else:
3827 else:
3823 doremove(f)
3828 doremove(f)
3824 for f in actions['drop'][0]:
3829 for f in actions['drop'][0]:
3825 audit_path(f)
3830 audit_path(f)
3826 repo.dirstate.remove(f)
3831 repo.dirstate.remove(f)
3827
3832
3828 normal = None
3833 normal = None
3829 if node == parent:
3834 if node == parent:
3830 # We're reverting to our parent. If possible, we'd like status
3835 # We're reverting to our parent. If possible, we'd like status
3831 # to report the file as clean. We have to use normallookup for
3836 # to report the file as clean. We have to use normallookup for
3832 # merges to avoid losing information about merged/dirty files.
3837 # merges to avoid losing information about merged/dirty files.
3833 if p2 != nullid:
3838 if p2 != nullid:
3834 normal = repo.dirstate.normallookup
3839 normal = repo.dirstate.normallookup
3835 else:
3840 else:
3836 normal = repo.dirstate.normal
3841 normal = repo.dirstate.normal
3837
3842
3838 newlyaddedandmodifiedfiles = set()
3843 newlyaddedandmodifiedfiles = set()
3839 if interactive:
3844 if interactive:
3840 # Prompt the user for changes to revert
3845 # Prompt the user for changes to revert
3841 torevert = [repo.wjoin(f) for f in actions['revert'][0]]
3846 torevert = [repo.wjoin(f) for f in actions['revert'][0]]
3842 m = scmutil.match(ctx, torevert, matcher_opts)
3847 m = scmutil.match(ctx, torevert, matcher_opts)
3843 diffopts = patch.difffeatureopts(repo.ui, whitespace=True)
3848 diffopts = patch.difffeatureopts(repo.ui, whitespace=True)
3844 diffopts.nodates = True
3849 diffopts.nodates = True
3845 diffopts.git = True
3850 diffopts.git = True
3846 operation = 'discard'
3851 operation = 'discard'
3847 reversehunks = True
3852 reversehunks = True
3848 if node != parent:
3853 if node != parent:
3849 operation = 'apply'
3854 operation = 'apply'
3850 reversehunks = False
3855 reversehunks = False
3851 if reversehunks:
3856 if reversehunks:
3852 diff = patch.diff(repo, ctx.node(), None, m, opts=diffopts)
3857 diff = patch.diff(repo, ctx.node(), None, m, opts=diffopts)
3853 else:
3858 else:
3854 diff = patch.diff(repo, None, ctx.node(), m, opts=diffopts)
3859 diff = patch.diff(repo, None, ctx.node(), m, opts=diffopts)
3855 originalchunks = patch.parsepatch(diff)
3860 originalchunks = patch.parsepatch(diff)
3856
3861
3857 try:
3862 try:
3858
3863
3859 chunks, opts = recordfilter(repo.ui, originalchunks,
3864 chunks, opts = recordfilter(repo.ui, originalchunks,
3860 operation=operation)
3865 operation=operation)
3861 if reversehunks:
3866 if reversehunks:
3862 chunks = patch.reversehunks(chunks)
3867 chunks = patch.reversehunks(chunks)
3863
3868
3864 except error.PatchError as err:
3869 except error.PatchError as err:
3865 raise error.Abort(_('error parsing patch: %s') % err)
3870 raise error.Abort(_('error parsing patch: %s') % err)
3866
3871
3867 newlyaddedandmodifiedfiles = newandmodified(chunks, originalchunks)
3872 newlyaddedandmodifiedfiles = newandmodified(chunks, originalchunks)
3868 if tobackup is None:
3873 if tobackup is None:
3869 tobackup = set()
3874 tobackup = set()
3870 # Apply changes
3875 # Apply changes
3871 fp = stringio()
3876 fp = stringio()
3872 for c in chunks:
3877 for c in chunks:
3873 # Create a backup file only if this hunk should be backed up
3878 # Create a backup file only if this hunk should be backed up
3874 if ishunk(c) and c.header.filename() in tobackup:
3879 if ishunk(c) and c.header.filename() in tobackup:
3875 abs = c.header.filename()
3880 abs = c.header.filename()
3876 target = repo.wjoin(abs)
3881 target = repo.wjoin(abs)
3877 bakname = scmutil.origpath(repo.ui, repo, m.rel(abs))
3882 bakname = scmutil.origpath(repo.ui, repo, m.rel(abs))
3878 util.copyfile(target, bakname)
3883 util.copyfile(target, bakname)
3879 tobackup.remove(abs)
3884 tobackup.remove(abs)
3880 c.write(fp)
3885 c.write(fp)
3881 dopatch = fp.tell()
3886 dopatch = fp.tell()
3882 fp.seek(0)
3887 fp.seek(0)
3883 if dopatch:
3888 if dopatch:
3884 try:
3889 try:
3885 patch.internalpatch(repo.ui, repo, fp, 1, eolmode=None)
3890 patch.internalpatch(repo.ui, repo, fp, 1, eolmode=None)
3886 except error.PatchError as err:
3891 except error.PatchError as err:
3887 raise error.Abort(str(err))
3892 raise error.Abort(str(err))
3888 del fp
3893 del fp
3889 else:
3894 else:
3890 for f in actions['revert'][0]:
3895 for f in actions['revert'][0]:
3891 checkout(f)
3896 checkout(f)
3892 if normal:
3897 if normal:
3893 normal(f)
3898 normal(f)
3894
3899
3895 for f in actions['add'][0]:
3900 for f in actions['add'][0]:
3896 # Don't checkout modified files, they are already created by the diff
3901 # Don't checkout modified files, they are already created by the diff
3897 if f not in newlyaddedandmodifiedfiles:
3902 if f not in newlyaddedandmodifiedfiles:
3898 checkout(f)
3903 checkout(f)
3899 repo.dirstate.add(f)
3904 repo.dirstate.add(f)
3900
3905
3901 normal = repo.dirstate.normallookup
3906 normal = repo.dirstate.normallookup
3902 if node == parent and p2 == nullid:
3907 if node == parent and p2 == nullid:
3903 normal = repo.dirstate.normal
3908 normal = repo.dirstate.normal
3904 for f in actions['undelete'][0]:
3909 for f in actions['undelete'][0]:
3905 checkout(f)
3910 checkout(f)
3906 normal(f)
3911 normal(f)
3907
3912
3908 copied = copies.pathcopies(repo[parent], ctx)
3913 copied = copies.pathcopies(repo[parent], ctx)
3909
3914
3910 for f in actions['add'][0] + actions['undelete'][0] + actions['revert'][0]:
3915 for f in actions['add'][0] + actions['undelete'][0] + actions['revert'][0]:
3911 if f in copied:
3916 if f in copied:
3912 repo.dirstate.copy(copied[f], f)
3917 repo.dirstate.copy(copied[f], f)
3913
3918
3914 class command(registrar.command):
3919 class command(registrar.command):
3915 """deprecated: used registrar.command instead"""
3920 """deprecated: used registrar.command instead"""
3916 def _doregister(self, func, name, *args, **kwargs):
3921 def _doregister(self, func, name, *args, **kwargs):
3917 func._deprecatedregistrar = True # flag for deprecwarn in extensions.py
3922 func._deprecatedregistrar = True # flag for deprecwarn in extensions.py
3918 return super(command, self)._doregister(func, name, *args, **kwargs)
3923 return super(command, self)._doregister(func, name, *args, **kwargs)
3919
3924
3920 # a list of (ui, repo, otherpeer, opts, missing) functions called by
3925 # a list of (ui, repo, otherpeer, opts, missing) functions called by
3921 # commands.outgoing. "missing" is "missing" of the result of
3926 # commands.outgoing. "missing" is "missing" of the result of
3922 # "findcommonoutgoing()"
3927 # "findcommonoutgoing()"
3923 outgoinghooks = util.hooks()
3928 outgoinghooks = util.hooks()
3924
3929
3925 # a list of (ui, repo) functions called by commands.summary
3930 # a list of (ui, repo) functions called by commands.summary
3926 summaryhooks = util.hooks()
3931 summaryhooks = util.hooks()
3927
3932
3928 # a list of (ui, repo, opts, changes) functions called by commands.summary.
3933 # a list of (ui, repo, opts, changes) functions called by commands.summary.
3929 #
3934 #
3930 # functions should return tuple of booleans below, if 'changes' is None:
3935 # functions should return tuple of booleans below, if 'changes' is None:
3931 # (whether-incomings-are-needed, whether-outgoings-are-needed)
3936 # (whether-incomings-are-needed, whether-outgoings-are-needed)
3932 #
3937 #
3933 # otherwise, 'changes' is a tuple of tuples below:
3938 # otherwise, 'changes' is a tuple of tuples below:
3934 # - (sourceurl, sourcebranch, sourcepeer, incoming)
3939 # - (sourceurl, sourcebranch, sourcepeer, incoming)
3935 # - (desturl, destbranch, destpeer, outgoing)
3940 # - (desturl, destbranch, destpeer, outgoing)
3936 summaryremotehooks = util.hooks()
3941 summaryremotehooks = util.hooks()
3937
3942
3938 # A list of state files kept by multistep operations like graft.
3943 # A list of state files kept by multistep operations like graft.
3939 # Since graft cannot be aborted, it is considered 'clearable' by update.
3944 # Since graft cannot be aborted, it is considered 'clearable' by update.
3940 # note: bisect is intentionally excluded
3945 # note: bisect is intentionally excluded
3941 # (state file, clearable, allowcommit, error, hint)
3946 # (state file, clearable, allowcommit, error, hint)
3942 unfinishedstates = [
3947 unfinishedstates = [
3943 ('graftstate', True, False, _('graft in progress'),
3948 ('graftstate', True, False, _('graft in progress'),
3944 _("use 'hg graft --continue' or 'hg update' to abort")),
3949 _("use 'hg graft --continue' or 'hg update' to abort")),
3945 ('updatestate', True, False, _('last update was interrupted'),
3950 ('updatestate', True, False, _('last update was interrupted'),
3946 _("use 'hg update' to get a consistent checkout"))
3951 _("use 'hg update' to get a consistent checkout"))
3947 ]
3952 ]
3948
3953
3949 def checkunfinished(repo, commit=False):
3954 def checkunfinished(repo, commit=False):
3950 '''Look for an unfinished multistep operation, like graft, and abort
3955 '''Look for an unfinished multistep operation, like graft, and abort
3951 if found. It's probably good to check this right before
3956 if found. It's probably good to check this right before
3952 bailifchanged().
3957 bailifchanged().
3953 '''
3958 '''
3954 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3959 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3955 if commit and allowcommit:
3960 if commit and allowcommit:
3956 continue
3961 continue
3957 if repo.vfs.exists(f):
3962 if repo.vfs.exists(f):
3958 raise error.Abort(msg, hint=hint)
3963 raise error.Abort(msg, hint=hint)
3959
3964
3960 def clearunfinished(repo):
3965 def clearunfinished(repo):
3961 '''Check for unfinished operations (as above), and clear the ones
3966 '''Check for unfinished operations (as above), and clear the ones
3962 that are clearable.
3967 that are clearable.
3963 '''
3968 '''
3964 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3969 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3965 if not clearable and repo.vfs.exists(f):
3970 if not clearable and repo.vfs.exists(f):
3966 raise error.Abort(msg, hint=hint)
3971 raise error.Abort(msg, hint=hint)
3967 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3972 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3968 if clearable and repo.vfs.exists(f):
3973 if clearable and repo.vfs.exists(f):
3969 util.unlink(repo.vfs.join(f))
3974 util.unlink(repo.vfs.join(f))
3970
3975
3971 afterresolvedstates = [
3976 afterresolvedstates = [
3972 ('graftstate',
3977 ('graftstate',
3973 _('hg graft --continue')),
3978 _('hg graft --continue')),
3974 ]
3979 ]
3975
3980
3976 def howtocontinue(repo):
3981 def howtocontinue(repo):
3977 '''Check for an unfinished operation and return the command to finish
3982 '''Check for an unfinished operation and return the command to finish
3978 it.
3983 it.
3979
3984
3980 afterresolvedstates tuples define a .hg/{file} and the corresponding
3985 afterresolvedstates tuples define a .hg/{file} and the corresponding
3981 command needed to finish it.
3986 command needed to finish it.
3982
3987
3983 Returns a (msg, warning) tuple. 'msg' is a string and 'warning' is
3988 Returns a (msg, warning) tuple. 'msg' is a string and 'warning' is
3984 a boolean.
3989 a boolean.
3985 '''
3990 '''
3986 contmsg = _("continue: %s")
3991 contmsg = _("continue: %s")
3987 for f, msg in afterresolvedstates:
3992 for f, msg in afterresolvedstates:
3988 if repo.vfs.exists(f):
3993 if repo.vfs.exists(f):
3989 return contmsg % msg, True
3994 return contmsg % msg, True
3990 if repo[None].dirty(missing=True, merge=False, branch=False):
3995 if repo[None].dirty(missing=True, merge=False, branch=False):
3991 return contmsg % _("hg commit"), False
3996 return contmsg % _("hg commit"), False
3992 return None, None
3997 return None, None
3993
3998
3994 def checkafterresolved(repo):
3999 def checkafterresolved(repo):
3995 '''Inform the user about the next action after completing hg resolve
4000 '''Inform the user about the next action after completing hg resolve
3996
4001
3997 If there's a matching afterresolvedstates, howtocontinue will yield
4002 If there's a matching afterresolvedstates, howtocontinue will yield
3998 repo.ui.warn as the reporter.
4003 repo.ui.warn as the reporter.
3999
4004
4000 Otherwise, it will yield repo.ui.note.
4005 Otherwise, it will yield repo.ui.note.
4001 '''
4006 '''
4002 msg, warning = howtocontinue(repo)
4007 msg, warning = howtocontinue(repo)
4003 if msg is not None:
4008 if msg is not None:
4004 if warning:
4009 if warning:
4005 repo.ui.warn("%s\n" % msg)
4010 repo.ui.warn("%s\n" % msg)
4006 else:
4011 else:
4007 repo.ui.note("%s\n" % msg)
4012 repo.ui.note("%s\n" % msg)
4008
4013
4009 def wrongtooltocontinue(repo, task):
4014 def wrongtooltocontinue(repo, task):
4010 '''Raise an abort suggesting how to properly continue if there is an
4015 '''Raise an abort suggesting how to properly continue if there is an
4011 active task.
4016 active task.
4012
4017
4013 Uses howtocontinue() to find the active task.
4018 Uses howtocontinue() to find the active task.
4014
4019
4015 If there's no task (repo.ui.note for 'hg commit'), it does not offer
4020 If there's no task (repo.ui.note for 'hg commit'), it does not offer
4016 a hint.
4021 a hint.
4017 '''
4022 '''
4018 after = howtocontinue(repo)
4023 after = howtocontinue(repo)
4019 hint = None
4024 hint = None
4020 if after[1]:
4025 if after[1]:
4021 hint = after[0]
4026 hint = after[0]
4022 raise error.Abort(_('no %s in progress') % task, hint=hint)
4027 raise error.Abort(_('no %s in progress') % task, hint=hint)
@@ -1,5641 +1,5636 b''
1 # commands.py - command processing for mercurial
1 # commands.py - command processing for 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 difflib
10 import difflib
11 import errno
11 import errno
12 import os
12 import os
13 import re
13 import re
14 import sys
14 import sys
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 from . import (
23 from . import (
24 archival,
24 archival,
25 bookmarks,
25 bookmarks,
26 bundle2,
26 bundle2,
27 changegroup,
27 changegroup,
28 cmdutil,
28 cmdutil,
29 copies,
29 copies,
30 debugcommands as debugcommandsmod,
30 debugcommands as debugcommandsmod,
31 destutil,
31 destutil,
32 dirstateguard,
32 dirstateguard,
33 discovery,
33 discovery,
34 encoding,
34 encoding,
35 error,
35 error,
36 exchange,
36 exchange,
37 extensions,
37 extensions,
38 formatter,
38 formatter,
39 graphmod,
39 graphmod,
40 hbisect,
40 hbisect,
41 help,
41 help,
42 hg,
42 hg,
43 lock as lockmod,
43 lock as lockmod,
44 merge as mergemod,
44 merge as mergemod,
45 obsolete,
45 obsolete,
46 obsutil,
46 obsutil,
47 patch,
47 patch,
48 phases,
48 phases,
49 pycompat,
49 pycompat,
50 rcutil,
50 rcutil,
51 registrar,
51 registrar,
52 revsetlang,
52 revsetlang,
53 rewriteutil,
53 rewriteutil,
54 scmutil,
54 scmutil,
55 server,
55 server,
56 sshserver,
56 sshserver,
57 streamclone,
57 streamclone,
58 tags as tagsmod,
58 tags as tagsmod,
59 templatekw,
59 templatekw,
60 ui as uimod,
60 ui as uimod,
61 util,
61 util,
62 )
62 )
63
63
64 release = lockmod.release
64 release = lockmod.release
65
65
66 table = {}
66 table = {}
67 table.update(debugcommandsmod.command._table)
67 table.update(debugcommandsmod.command._table)
68
68
69 command = registrar.command(table)
69 command = registrar.command(table)
70 readonly = registrar.command.readonly
70 readonly = registrar.command.readonly
71
71
72 # common command options
72 # common command options
73
73
74 globalopts = [
74 globalopts = [
75 ('R', 'repository', '',
75 ('R', 'repository', '',
76 _('repository root directory or name of overlay bundle file'),
76 _('repository root directory or name of overlay bundle file'),
77 _('REPO')),
77 _('REPO')),
78 ('', 'cwd', '',
78 ('', 'cwd', '',
79 _('change working directory'), _('DIR')),
79 _('change working directory'), _('DIR')),
80 ('y', 'noninteractive', None,
80 ('y', 'noninteractive', None,
81 _('do not prompt, automatically pick the first choice for all prompts')),
81 _('do not prompt, automatically pick the first choice for all prompts')),
82 ('q', 'quiet', None, _('suppress output')),
82 ('q', 'quiet', None, _('suppress output')),
83 ('v', 'verbose', None, _('enable additional output')),
83 ('v', 'verbose', None, _('enable additional output')),
84 ('', 'color', '',
84 ('', 'color', '',
85 # i18n: 'always', 'auto', 'never', and 'debug' are keywords
85 # i18n: 'always', 'auto', 'never', and 'debug' are keywords
86 # and should not be translated
86 # and should not be translated
87 _("when to colorize (boolean, always, auto, never, or debug)"),
87 _("when to colorize (boolean, always, auto, never, or debug)"),
88 _('TYPE')),
88 _('TYPE')),
89 ('', 'config', [],
89 ('', 'config', [],
90 _('set/override config option (use \'section.name=value\')'),
90 _('set/override config option (use \'section.name=value\')'),
91 _('CONFIG')),
91 _('CONFIG')),
92 ('', 'debug', None, _('enable debugging output')),
92 ('', 'debug', None, _('enable debugging output')),
93 ('', 'debugger', None, _('start debugger')),
93 ('', 'debugger', None, _('start debugger')),
94 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
94 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
95 _('ENCODE')),
95 _('ENCODE')),
96 ('', 'encodingmode', encoding.encodingmode,
96 ('', 'encodingmode', encoding.encodingmode,
97 _('set the charset encoding mode'), _('MODE')),
97 _('set the charset encoding mode'), _('MODE')),
98 ('', 'traceback', None, _('always print a traceback on exception')),
98 ('', 'traceback', None, _('always print a traceback on exception')),
99 ('', 'time', None, _('time how long the command takes')),
99 ('', 'time', None, _('time how long the command takes')),
100 ('', 'profile', None, _('print command execution profile')),
100 ('', 'profile', None, _('print command execution profile')),
101 ('', 'version', None, _('output version information and exit')),
101 ('', 'version', None, _('output version information and exit')),
102 ('h', 'help', None, _('display help and exit')),
102 ('h', 'help', None, _('display help and exit')),
103 ('', 'hidden', False, _('consider hidden changesets')),
103 ('', 'hidden', False, _('consider hidden changesets')),
104 ('', 'pager', 'auto',
104 ('', 'pager', 'auto',
105 _("when to paginate (boolean, always, auto, or never)"), _('TYPE')),
105 _("when to paginate (boolean, always, auto, or never)"), _('TYPE')),
106 ]
106 ]
107
107
108 dryrunopts = cmdutil.dryrunopts
108 dryrunopts = cmdutil.dryrunopts
109 remoteopts = cmdutil.remoteopts
109 remoteopts = cmdutil.remoteopts
110 walkopts = cmdutil.walkopts
110 walkopts = cmdutil.walkopts
111 commitopts = cmdutil.commitopts
111 commitopts = cmdutil.commitopts
112 commitopts2 = cmdutil.commitopts2
112 commitopts2 = cmdutil.commitopts2
113 formatteropts = cmdutil.formatteropts
113 formatteropts = cmdutil.formatteropts
114 templateopts = cmdutil.templateopts
114 templateopts = cmdutil.templateopts
115 logopts = cmdutil.logopts
115 logopts = cmdutil.logopts
116 diffopts = cmdutil.diffopts
116 diffopts = cmdutil.diffopts
117 diffwsopts = cmdutil.diffwsopts
117 diffwsopts = cmdutil.diffwsopts
118 diffopts2 = cmdutil.diffopts2
118 diffopts2 = cmdutil.diffopts2
119 mergetoolopts = cmdutil.mergetoolopts
119 mergetoolopts = cmdutil.mergetoolopts
120 similarityopts = cmdutil.similarityopts
120 similarityopts = cmdutil.similarityopts
121 subrepoopts = cmdutil.subrepoopts
121 subrepoopts = cmdutil.subrepoopts
122 debugrevlogopts = cmdutil.debugrevlogopts
122 debugrevlogopts = cmdutil.debugrevlogopts
123
123
124 # Commands start here, listed alphabetically
124 # Commands start here, listed alphabetically
125
125
126 @command('^add',
126 @command('^add',
127 walkopts + subrepoopts + dryrunopts,
127 walkopts + subrepoopts + dryrunopts,
128 _('[OPTION]... [FILE]...'),
128 _('[OPTION]... [FILE]...'),
129 inferrepo=True)
129 inferrepo=True)
130 def add(ui, repo, *pats, **opts):
130 def add(ui, repo, *pats, **opts):
131 """add the specified files on the next commit
131 """add the specified files on the next commit
132
132
133 Schedule files to be version controlled and added to the
133 Schedule files to be version controlled and added to the
134 repository.
134 repository.
135
135
136 The files will be added to the repository at the next commit. To
136 The files will be added to the repository at the next commit. To
137 undo an add before that, see :hg:`forget`.
137 undo an add before that, see :hg:`forget`.
138
138
139 If no names are given, add all files to the repository (except
139 If no names are given, add all files to the repository (except
140 files matching ``.hgignore``).
140 files matching ``.hgignore``).
141
141
142 .. container:: verbose
142 .. container:: verbose
143
143
144 Examples:
144 Examples:
145
145
146 - New (unknown) files are added
146 - New (unknown) files are added
147 automatically by :hg:`add`::
147 automatically by :hg:`add`::
148
148
149 $ ls
149 $ ls
150 foo.c
150 foo.c
151 $ hg status
151 $ hg status
152 ? foo.c
152 ? foo.c
153 $ hg add
153 $ hg add
154 adding foo.c
154 adding foo.c
155 $ hg status
155 $ hg status
156 A foo.c
156 A foo.c
157
157
158 - Specific files to be added can be specified::
158 - Specific files to be added can be specified::
159
159
160 $ ls
160 $ ls
161 bar.c foo.c
161 bar.c foo.c
162 $ hg status
162 $ hg status
163 ? bar.c
163 ? bar.c
164 ? foo.c
164 ? foo.c
165 $ hg add bar.c
165 $ hg add bar.c
166 $ hg status
166 $ hg status
167 A bar.c
167 A bar.c
168 ? foo.c
168 ? foo.c
169
169
170 Returns 0 if all files are successfully added.
170 Returns 0 if all files are successfully added.
171 """
171 """
172
172
173 m = scmutil.match(repo[None], pats, pycompat.byteskwargs(opts))
173 m = scmutil.match(repo[None], pats, pycompat.byteskwargs(opts))
174 rejected = cmdutil.add(ui, repo, m, "", False, **opts)
174 rejected = cmdutil.add(ui, repo, m, "", False, **opts)
175 return rejected and 1 or 0
175 return rejected and 1 or 0
176
176
177 @command('addremove',
177 @command('addremove',
178 similarityopts + subrepoopts + walkopts + dryrunopts,
178 similarityopts + subrepoopts + walkopts + dryrunopts,
179 _('[OPTION]... [FILE]...'),
179 _('[OPTION]... [FILE]...'),
180 inferrepo=True)
180 inferrepo=True)
181 def addremove(ui, repo, *pats, **opts):
181 def addremove(ui, repo, *pats, **opts):
182 """add all new files, delete all missing files
182 """add all new files, delete all missing files
183
183
184 Add all new files and remove all missing files from the
184 Add all new files and remove all missing files from the
185 repository.
185 repository.
186
186
187 Unless names are given, new files are ignored if they match any of
187 Unless names are given, new files are ignored if they match any of
188 the patterns in ``.hgignore``. As with add, these changes take
188 the patterns in ``.hgignore``. As with add, these changes take
189 effect at the next commit.
189 effect at the next commit.
190
190
191 Use the -s/--similarity option to detect renamed files. This
191 Use the -s/--similarity option to detect renamed files. This
192 option takes a percentage between 0 (disabled) and 100 (files must
192 option takes a percentage between 0 (disabled) and 100 (files must
193 be identical) as its parameter. With a parameter greater than 0,
193 be identical) as its parameter. With a parameter greater than 0,
194 this compares every removed file with every added file and records
194 this compares every removed file with every added file and records
195 those similar enough as renames. Detecting renamed files this way
195 those similar enough as renames. Detecting renamed files this way
196 can be expensive. After using this option, :hg:`status -C` can be
196 can be expensive. After using this option, :hg:`status -C` can be
197 used to check which files were identified as moved or renamed. If
197 used to check which files were identified as moved or renamed. If
198 not specified, -s/--similarity defaults to 100 and only renames of
198 not specified, -s/--similarity defaults to 100 and only renames of
199 identical files are detected.
199 identical files are detected.
200
200
201 .. container:: verbose
201 .. container:: verbose
202
202
203 Examples:
203 Examples:
204
204
205 - A number of files (bar.c and foo.c) are new,
205 - A number of files (bar.c and foo.c) are new,
206 while foobar.c has been removed (without using :hg:`remove`)
206 while foobar.c has been removed (without using :hg:`remove`)
207 from the repository::
207 from the repository::
208
208
209 $ ls
209 $ ls
210 bar.c foo.c
210 bar.c foo.c
211 $ hg status
211 $ hg status
212 ! foobar.c
212 ! foobar.c
213 ? bar.c
213 ? bar.c
214 ? foo.c
214 ? foo.c
215 $ hg addremove
215 $ hg addremove
216 adding bar.c
216 adding bar.c
217 adding foo.c
217 adding foo.c
218 removing foobar.c
218 removing foobar.c
219 $ hg status
219 $ hg status
220 A bar.c
220 A bar.c
221 A foo.c
221 A foo.c
222 R foobar.c
222 R foobar.c
223
223
224 - A file foobar.c was moved to foo.c without using :hg:`rename`.
224 - A file foobar.c was moved to foo.c without using :hg:`rename`.
225 Afterwards, it was edited slightly::
225 Afterwards, it was edited slightly::
226
226
227 $ ls
227 $ ls
228 foo.c
228 foo.c
229 $ hg status
229 $ hg status
230 ! foobar.c
230 ! foobar.c
231 ? foo.c
231 ? foo.c
232 $ hg addremove --similarity 90
232 $ hg addremove --similarity 90
233 removing foobar.c
233 removing foobar.c
234 adding foo.c
234 adding foo.c
235 recording removal of foobar.c as rename to foo.c (94% similar)
235 recording removal of foobar.c as rename to foo.c (94% similar)
236 $ hg status -C
236 $ hg status -C
237 A foo.c
237 A foo.c
238 foobar.c
238 foobar.c
239 R foobar.c
239 R foobar.c
240
240
241 Returns 0 if all files are successfully added.
241 Returns 0 if all files are successfully added.
242 """
242 """
243 opts = pycompat.byteskwargs(opts)
243 opts = pycompat.byteskwargs(opts)
244 try:
244 try:
245 sim = float(opts.get('similarity') or 100)
245 sim = float(opts.get('similarity') or 100)
246 except ValueError:
246 except ValueError:
247 raise error.Abort(_('similarity must be a number'))
247 raise error.Abort(_('similarity must be a number'))
248 if sim < 0 or sim > 100:
248 if sim < 0 or sim > 100:
249 raise error.Abort(_('similarity must be between 0 and 100'))
249 raise error.Abort(_('similarity must be between 0 and 100'))
250 matcher = scmutil.match(repo[None], pats, opts)
250 matcher = scmutil.match(repo[None], pats, opts)
251 return scmutil.addremove(repo, matcher, "", opts, similarity=sim / 100.0)
251 return scmutil.addremove(repo, matcher, "", opts, similarity=sim / 100.0)
252
252
253 @command('^annotate|blame',
253 @command('^annotate|blame',
254 [('r', 'rev', '', _('annotate the specified revision'), _('REV')),
254 [('r', 'rev', '', _('annotate the specified revision'), _('REV')),
255 ('', 'follow', None,
255 ('', 'follow', None,
256 _('follow copies/renames and list the filename (DEPRECATED)')),
256 _('follow copies/renames and list the filename (DEPRECATED)')),
257 ('', 'no-follow', None, _("don't follow copies and renames")),
257 ('', 'no-follow', None, _("don't follow copies and renames")),
258 ('a', 'text', None, _('treat all files as text')),
258 ('a', 'text', None, _('treat all files as text')),
259 ('u', 'user', None, _('list the author (long with -v)')),
259 ('u', 'user', None, _('list the author (long with -v)')),
260 ('f', 'file', None, _('list the filename')),
260 ('f', 'file', None, _('list the filename')),
261 ('d', 'date', None, _('list the date (short with -q)')),
261 ('d', 'date', None, _('list the date (short with -q)')),
262 ('n', 'number', None, _('list the revision number (default)')),
262 ('n', 'number', None, _('list the revision number (default)')),
263 ('c', 'changeset', None, _('list the changeset')),
263 ('c', 'changeset', None, _('list the changeset')),
264 ('l', 'line-number', None, _('show line number at the first appearance')),
264 ('l', 'line-number', None, _('show line number at the first appearance')),
265 ('', 'skip', [], _('revision to not display (EXPERIMENTAL)'), _('REV')),
265 ('', 'skip', [], _('revision to not display (EXPERIMENTAL)'), _('REV')),
266 ] + diffwsopts + walkopts + formatteropts,
266 ] + diffwsopts + walkopts + formatteropts,
267 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
267 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
268 inferrepo=True)
268 inferrepo=True)
269 def annotate(ui, repo, *pats, **opts):
269 def annotate(ui, repo, *pats, **opts):
270 """show changeset information by line for each file
270 """show changeset information by line for each file
271
271
272 List changes in files, showing the revision id responsible for
272 List changes in files, showing the revision id responsible for
273 each line.
273 each line.
274
274
275 This command is useful for discovering when a change was made and
275 This command is useful for discovering when a change was made and
276 by whom.
276 by whom.
277
277
278 If you include --file, --user, or --date, the revision number is
278 If you include --file, --user, or --date, the revision number is
279 suppressed unless you also include --number.
279 suppressed unless you also include --number.
280
280
281 Without the -a/--text option, annotate will avoid processing files
281 Without the -a/--text option, annotate will avoid processing files
282 it detects as binary. With -a, annotate will annotate the file
282 it detects as binary. With -a, annotate will annotate the file
283 anyway, although the results will probably be neither useful
283 anyway, although the results will probably be neither useful
284 nor desirable.
284 nor desirable.
285
285
286 Returns 0 on success.
286 Returns 0 on success.
287 """
287 """
288 opts = pycompat.byteskwargs(opts)
288 opts = pycompat.byteskwargs(opts)
289 if not pats:
289 if not pats:
290 raise error.Abort(_('at least one filename or pattern is required'))
290 raise error.Abort(_('at least one filename or pattern is required'))
291
291
292 if opts.get('follow'):
292 if opts.get('follow'):
293 # --follow is deprecated and now just an alias for -f/--file
293 # --follow is deprecated and now just an alias for -f/--file
294 # to mimic the behavior of Mercurial before version 1.5
294 # to mimic the behavior of Mercurial before version 1.5
295 opts['file'] = True
295 opts['file'] = True
296
296
297 rev = opts.get('rev')
297 rev = opts.get('rev')
298 if rev:
298 if rev:
299 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
299 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
300 ctx = scmutil.revsingle(repo, rev)
300 ctx = scmutil.revsingle(repo, rev)
301
301
302 rootfm = ui.formatter('annotate', opts)
302 rootfm = ui.formatter('annotate', opts)
303 if ui.quiet:
303 if ui.quiet:
304 datefunc = util.shortdate
304 datefunc = util.shortdate
305 else:
305 else:
306 datefunc = util.datestr
306 datefunc = util.datestr
307 if ctx.rev() is None:
307 if ctx.rev() is None:
308 def hexfn(node):
308 def hexfn(node):
309 if node is None:
309 if node is None:
310 return None
310 return None
311 else:
311 else:
312 return rootfm.hexfunc(node)
312 return rootfm.hexfunc(node)
313 if opts.get('changeset'):
313 if opts.get('changeset'):
314 # omit "+" suffix which is appended to node hex
314 # omit "+" suffix which is appended to node hex
315 def formatrev(rev):
315 def formatrev(rev):
316 if rev is None:
316 if rev is None:
317 return '%d' % ctx.p1().rev()
317 return '%d' % ctx.p1().rev()
318 else:
318 else:
319 return '%d' % rev
319 return '%d' % rev
320 else:
320 else:
321 def formatrev(rev):
321 def formatrev(rev):
322 if rev is None:
322 if rev is None:
323 return '%d+' % ctx.p1().rev()
323 return '%d+' % ctx.p1().rev()
324 else:
324 else:
325 return '%d ' % rev
325 return '%d ' % rev
326 def formathex(hex):
326 def formathex(hex):
327 if hex is None:
327 if hex is None:
328 return '%s+' % rootfm.hexfunc(ctx.p1().node())
328 return '%s+' % rootfm.hexfunc(ctx.p1().node())
329 else:
329 else:
330 return '%s ' % hex
330 return '%s ' % hex
331 else:
331 else:
332 hexfn = rootfm.hexfunc
332 hexfn = rootfm.hexfunc
333 formatrev = formathex = pycompat.bytestr
333 formatrev = formathex = pycompat.bytestr
334
334
335 opmap = [('user', ' ', lambda x: x.fctx.user(), ui.shortuser),
335 opmap = [('user', ' ', lambda x: x.fctx.user(), ui.shortuser),
336 ('number', ' ', lambda x: x.fctx.rev(), formatrev),
336 ('number', ' ', lambda x: x.fctx.rev(), formatrev),
337 ('changeset', ' ', lambda x: hexfn(x.fctx.node()), formathex),
337 ('changeset', ' ', lambda x: hexfn(x.fctx.node()), formathex),
338 ('date', ' ', lambda x: x.fctx.date(), util.cachefunc(datefunc)),
338 ('date', ' ', lambda x: x.fctx.date(), util.cachefunc(datefunc)),
339 ('file', ' ', lambda x: x.fctx.path(), str),
339 ('file', ' ', lambda x: x.fctx.path(), str),
340 ('line_number', ':', lambda x: x.lineno, str),
340 ('line_number', ':', lambda x: x.lineno, str),
341 ]
341 ]
342 fieldnamemap = {'number': 'rev', 'changeset': 'node'}
342 fieldnamemap = {'number': 'rev', 'changeset': 'node'}
343
343
344 if (not opts.get('user') and not opts.get('changeset')
344 if (not opts.get('user') and not opts.get('changeset')
345 and not opts.get('date') and not opts.get('file')):
345 and not opts.get('date') and not opts.get('file')):
346 opts['number'] = True
346 opts['number'] = True
347
347
348 linenumber = opts.get('line_number') is not None
348 linenumber = opts.get('line_number') is not None
349 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
349 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
350 raise error.Abort(_('at least one of -n/-c is required for -l'))
350 raise error.Abort(_('at least one of -n/-c is required for -l'))
351
351
352 ui.pager('annotate')
352 ui.pager('annotate')
353
353
354 if rootfm.isplain():
354 if rootfm.isplain():
355 def makefunc(get, fmt):
355 def makefunc(get, fmt):
356 return lambda x: fmt(get(x))
356 return lambda x: fmt(get(x))
357 else:
357 else:
358 def makefunc(get, fmt):
358 def makefunc(get, fmt):
359 return get
359 return get
360 funcmap = [(makefunc(get, fmt), sep) for op, sep, get, fmt in opmap
360 funcmap = [(makefunc(get, fmt), sep) for op, sep, get, fmt in opmap
361 if opts.get(op)]
361 if opts.get(op)]
362 funcmap[0] = (funcmap[0][0], '') # no separator in front of first column
362 funcmap[0] = (funcmap[0][0], '') # no separator in front of first column
363 fields = ' '.join(fieldnamemap.get(op, op) for op, sep, get, fmt in opmap
363 fields = ' '.join(fieldnamemap.get(op, op) for op, sep, get, fmt in opmap
364 if opts.get(op))
364 if opts.get(op))
365
365
366 def bad(x, y):
366 def bad(x, y):
367 raise error.Abort("%s: %s" % (x, y))
367 raise error.Abort("%s: %s" % (x, y))
368
368
369 m = scmutil.match(ctx, pats, opts, badfn=bad)
369 m = scmutil.match(ctx, pats, opts, badfn=bad)
370
370
371 follow = not opts.get('no_follow')
371 follow = not opts.get('no_follow')
372 diffopts = patch.difffeatureopts(ui, opts, section='annotate',
372 diffopts = patch.difffeatureopts(ui, opts, section='annotate',
373 whitespace=True)
373 whitespace=True)
374 skiprevs = opts.get('skip')
374 skiprevs = opts.get('skip')
375 if skiprevs:
375 if skiprevs:
376 skiprevs = scmutil.revrange(repo, skiprevs)
376 skiprevs = scmutil.revrange(repo, skiprevs)
377
377
378 for abs in ctx.walk(m):
378 for abs in ctx.walk(m):
379 fctx = ctx[abs]
379 fctx = ctx[abs]
380 rootfm.startitem()
380 rootfm.startitem()
381 rootfm.data(abspath=abs, path=m.rel(abs))
381 rootfm.data(abspath=abs, path=m.rel(abs))
382 if not opts.get('text') and fctx.isbinary():
382 if not opts.get('text') and fctx.isbinary():
383 rootfm.plain(_("%s: binary file\n")
383 rootfm.plain(_("%s: binary file\n")
384 % ((pats and m.rel(abs)) or abs))
384 % ((pats and m.rel(abs)) or abs))
385 continue
385 continue
386
386
387 fm = rootfm.nested('lines')
387 fm = rootfm.nested('lines')
388 lines = fctx.annotate(follow=follow, linenumber=linenumber,
388 lines = fctx.annotate(follow=follow, linenumber=linenumber,
389 skiprevs=skiprevs, diffopts=diffopts)
389 skiprevs=skiprevs, diffopts=diffopts)
390 if not lines:
390 if not lines:
391 fm.end()
391 fm.end()
392 continue
392 continue
393 formats = []
393 formats = []
394 pieces = []
394 pieces = []
395
395
396 for f, sep in funcmap:
396 for f, sep in funcmap:
397 l = [f(n) for n, dummy in lines]
397 l = [f(n) for n, dummy in lines]
398 if fm.isplain():
398 if fm.isplain():
399 sizes = [encoding.colwidth(x) for x in l]
399 sizes = [encoding.colwidth(x) for x in l]
400 ml = max(sizes)
400 ml = max(sizes)
401 formats.append([sep + ' ' * (ml - w) + '%s' for w in sizes])
401 formats.append([sep + ' ' * (ml - w) + '%s' for w in sizes])
402 else:
402 else:
403 formats.append(['%s' for x in l])
403 formats.append(['%s' for x in l])
404 pieces.append(l)
404 pieces.append(l)
405
405
406 for f, p, l in zip(zip(*formats), zip(*pieces), lines):
406 for f, p, l in zip(zip(*formats), zip(*pieces), lines):
407 fm.startitem()
407 fm.startitem()
408 fm.write(fields, "".join(f), *p)
408 fm.write(fields, "".join(f), *p)
409 if l[0].skip:
409 if l[0].skip:
410 fmt = "* %s"
410 fmt = "* %s"
411 else:
411 else:
412 fmt = ": %s"
412 fmt = ": %s"
413 fm.write('line', fmt, l[1])
413 fm.write('line', fmt, l[1])
414
414
415 if not lines[-1][1].endswith('\n'):
415 if not lines[-1][1].endswith('\n'):
416 fm.plain('\n')
416 fm.plain('\n')
417 fm.end()
417 fm.end()
418
418
419 rootfm.end()
419 rootfm.end()
420
420
421 @command('archive',
421 @command('archive',
422 [('', 'no-decode', None, _('do not pass files through decoders')),
422 [('', 'no-decode', None, _('do not pass files through decoders')),
423 ('p', 'prefix', '', _('directory prefix for files in archive'),
423 ('p', 'prefix', '', _('directory prefix for files in archive'),
424 _('PREFIX')),
424 _('PREFIX')),
425 ('r', 'rev', '', _('revision to distribute'), _('REV')),
425 ('r', 'rev', '', _('revision to distribute'), _('REV')),
426 ('t', 'type', '', _('type of distribution to create'), _('TYPE')),
426 ('t', 'type', '', _('type of distribution to create'), _('TYPE')),
427 ] + subrepoopts + walkopts,
427 ] + subrepoopts + walkopts,
428 _('[OPTION]... DEST'))
428 _('[OPTION]... DEST'))
429 def archive(ui, repo, dest, **opts):
429 def archive(ui, repo, dest, **opts):
430 '''create an unversioned archive of a repository revision
430 '''create an unversioned archive of a repository revision
431
431
432 By default, the revision used is the parent of the working
432 By default, the revision used is the parent of the working
433 directory; use -r/--rev to specify a different revision.
433 directory; use -r/--rev to specify a different revision.
434
434
435 The archive type is automatically detected based on file
435 The archive type is automatically detected based on file
436 extension (to override, use -t/--type).
436 extension (to override, use -t/--type).
437
437
438 .. container:: verbose
438 .. container:: verbose
439
439
440 Examples:
440 Examples:
441
441
442 - create a zip file containing the 1.0 release::
442 - create a zip file containing the 1.0 release::
443
443
444 hg archive -r 1.0 project-1.0.zip
444 hg archive -r 1.0 project-1.0.zip
445
445
446 - create a tarball excluding .hg files::
446 - create a tarball excluding .hg files::
447
447
448 hg archive project.tar.gz -X ".hg*"
448 hg archive project.tar.gz -X ".hg*"
449
449
450 Valid types are:
450 Valid types are:
451
451
452 :``files``: a directory full of files (default)
452 :``files``: a directory full of files (default)
453 :``tar``: tar archive, uncompressed
453 :``tar``: tar archive, uncompressed
454 :``tbz2``: tar archive, compressed using bzip2
454 :``tbz2``: tar archive, compressed using bzip2
455 :``tgz``: tar archive, compressed using gzip
455 :``tgz``: tar archive, compressed using gzip
456 :``uzip``: zip archive, uncompressed
456 :``uzip``: zip archive, uncompressed
457 :``zip``: zip archive, compressed using deflate
457 :``zip``: zip archive, compressed using deflate
458
458
459 The exact name of the destination archive or directory is given
459 The exact name of the destination archive or directory is given
460 using a format string; see :hg:`help export` for details.
460 using a format string; see :hg:`help export` for details.
461
461
462 Each member added to an archive file has a directory prefix
462 Each member added to an archive file has a directory prefix
463 prepended. Use -p/--prefix to specify a format string for the
463 prepended. Use -p/--prefix to specify a format string for the
464 prefix. The default is the basename of the archive, with suffixes
464 prefix. The default is the basename of the archive, with suffixes
465 removed.
465 removed.
466
466
467 Returns 0 on success.
467 Returns 0 on success.
468 '''
468 '''
469
469
470 opts = pycompat.byteskwargs(opts)
470 opts = pycompat.byteskwargs(opts)
471 rev = opts.get('rev')
471 rev = opts.get('rev')
472 if rev:
472 if rev:
473 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
473 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
474 ctx = scmutil.revsingle(repo, rev)
474 ctx = scmutil.revsingle(repo, rev)
475 if not ctx:
475 if not ctx:
476 raise error.Abort(_('no working directory: please specify a revision'))
476 raise error.Abort(_('no working directory: please specify a revision'))
477 node = ctx.node()
477 node = ctx.node()
478 dest = cmdutil.makefilename(repo, dest, node)
478 dest = cmdutil.makefilename(repo, dest, node)
479 if os.path.realpath(dest) == repo.root:
479 if os.path.realpath(dest) == repo.root:
480 raise error.Abort(_('repository root cannot be destination'))
480 raise error.Abort(_('repository root cannot be destination'))
481
481
482 kind = opts.get('type') or archival.guesskind(dest) or 'files'
482 kind = opts.get('type') or archival.guesskind(dest) or 'files'
483 prefix = opts.get('prefix')
483 prefix = opts.get('prefix')
484
484
485 if dest == '-':
485 if dest == '-':
486 if kind == 'files':
486 if kind == 'files':
487 raise error.Abort(_('cannot archive plain files to stdout'))
487 raise error.Abort(_('cannot archive plain files to stdout'))
488 dest = cmdutil.makefileobj(repo, dest)
488 dest = cmdutil.makefileobj(repo, dest)
489 if not prefix:
489 if not prefix:
490 prefix = os.path.basename(repo.root) + '-%h'
490 prefix = os.path.basename(repo.root) + '-%h'
491
491
492 prefix = cmdutil.makefilename(repo, prefix, node)
492 prefix = cmdutil.makefilename(repo, prefix, node)
493 match = scmutil.match(ctx, [], opts)
493 match = scmutil.match(ctx, [], opts)
494 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
494 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
495 match, prefix, subrepos=opts.get('subrepos'))
495 match, prefix, subrepos=opts.get('subrepos'))
496
496
497 @command('backout',
497 @command('backout',
498 [('', 'merge', None, _('merge with old dirstate parent after backout')),
498 [('', 'merge', None, _('merge with old dirstate parent after backout')),
499 ('', 'commit', None,
499 ('', 'commit', None,
500 _('commit if no conflicts were encountered (DEPRECATED)')),
500 _('commit if no conflicts were encountered (DEPRECATED)')),
501 ('', 'no-commit', None, _('do not commit')),
501 ('', 'no-commit', None, _('do not commit')),
502 ('', 'parent', '',
502 ('', 'parent', '',
503 _('parent to choose when backing out merge (DEPRECATED)'), _('REV')),
503 _('parent to choose when backing out merge (DEPRECATED)'), _('REV')),
504 ('r', 'rev', '', _('revision to backout'), _('REV')),
504 ('r', 'rev', '', _('revision to backout'), _('REV')),
505 ('e', 'edit', False, _('invoke editor on commit messages')),
505 ('e', 'edit', False, _('invoke editor on commit messages')),
506 ] + mergetoolopts + walkopts + commitopts + commitopts2,
506 ] + mergetoolopts + walkopts + commitopts + commitopts2,
507 _('[OPTION]... [-r] REV'))
507 _('[OPTION]... [-r] REV'))
508 def backout(ui, repo, node=None, rev=None, **opts):
508 def backout(ui, repo, node=None, rev=None, **opts):
509 '''reverse effect of earlier changeset
509 '''reverse effect of earlier changeset
510
510
511 Prepare a new changeset with the effect of REV undone in the
511 Prepare a new changeset with the effect of REV undone in the
512 current working directory. If no conflicts were encountered,
512 current working directory. If no conflicts were encountered,
513 it will be committed immediately.
513 it will be committed immediately.
514
514
515 If REV is the parent of the working directory, then this new changeset
515 If REV is the parent of the working directory, then this new changeset
516 is committed automatically (unless --no-commit is specified).
516 is committed automatically (unless --no-commit is specified).
517
517
518 .. note::
518 .. note::
519
519
520 :hg:`backout` cannot be used to fix either an unwanted or
520 :hg:`backout` cannot be used to fix either an unwanted or
521 incorrect merge.
521 incorrect merge.
522
522
523 .. container:: verbose
523 .. container:: verbose
524
524
525 Examples:
525 Examples:
526
526
527 - Reverse the effect of the parent of the working directory.
527 - Reverse the effect of the parent of the working directory.
528 This backout will be committed immediately::
528 This backout will be committed immediately::
529
529
530 hg backout -r .
530 hg backout -r .
531
531
532 - Reverse the effect of previous bad revision 23::
532 - Reverse the effect of previous bad revision 23::
533
533
534 hg backout -r 23
534 hg backout -r 23
535
535
536 - Reverse the effect of previous bad revision 23 and
536 - Reverse the effect of previous bad revision 23 and
537 leave changes uncommitted::
537 leave changes uncommitted::
538
538
539 hg backout -r 23 --no-commit
539 hg backout -r 23 --no-commit
540 hg commit -m "Backout revision 23"
540 hg commit -m "Backout revision 23"
541
541
542 By default, the pending changeset will have one parent,
542 By default, the pending changeset will have one parent,
543 maintaining a linear history. With --merge, the pending
543 maintaining a linear history. With --merge, the pending
544 changeset will instead have two parents: the old parent of the
544 changeset will instead have two parents: the old parent of the
545 working directory and a new child of REV that simply undoes REV.
545 working directory and a new child of REV that simply undoes REV.
546
546
547 Before version 1.7, the behavior without --merge was equivalent
547 Before version 1.7, the behavior without --merge was equivalent
548 to specifying --merge followed by :hg:`update --clean .` to
548 to specifying --merge followed by :hg:`update --clean .` to
549 cancel the merge and leave the child of REV as a head to be
549 cancel the merge and leave the child of REV as a head to be
550 merged separately.
550 merged separately.
551
551
552 See :hg:`help dates` for a list of formats valid for -d/--date.
552 See :hg:`help dates` for a list of formats valid for -d/--date.
553
553
554 See :hg:`help revert` for a way to restore files to the state
554 See :hg:`help revert` for a way to restore files to the state
555 of another revision.
555 of another revision.
556
556
557 Returns 0 on success, 1 if nothing to backout or there are unresolved
557 Returns 0 on success, 1 if nothing to backout or there are unresolved
558 files.
558 files.
559 '''
559 '''
560 wlock = lock = None
560 wlock = lock = None
561 try:
561 try:
562 wlock = repo.wlock()
562 wlock = repo.wlock()
563 lock = repo.lock()
563 lock = repo.lock()
564 return _dobackout(ui, repo, node, rev, **opts)
564 return _dobackout(ui, repo, node, rev, **opts)
565 finally:
565 finally:
566 release(lock, wlock)
566 release(lock, wlock)
567
567
568 def _dobackout(ui, repo, node=None, rev=None, **opts):
568 def _dobackout(ui, repo, node=None, rev=None, **opts):
569 opts = pycompat.byteskwargs(opts)
569 opts = pycompat.byteskwargs(opts)
570 if opts.get('commit') and opts.get('no_commit'):
570 if opts.get('commit') and opts.get('no_commit'):
571 raise error.Abort(_("cannot use --commit with --no-commit"))
571 raise error.Abort(_("cannot use --commit with --no-commit"))
572 if opts.get('merge') and opts.get('no_commit'):
572 if opts.get('merge') and opts.get('no_commit'):
573 raise error.Abort(_("cannot use --merge with --no-commit"))
573 raise error.Abort(_("cannot use --merge with --no-commit"))
574
574
575 if rev and node:
575 if rev and node:
576 raise error.Abort(_("please specify just one revision"))
576 raise error.Abort(_("please specify just one revision"))
577
577
578 if not rev:
578 if not rev:
579 rev = node
579 rev = node
580
580
581 if not rev:
581 if not rev:
582 raise error.Abort(_("please specify a revision to backout"))
582 raise error.Abort(_("please specify a revision to backout"))
583
583
584 date = opts.get('date')
584 date = opts.get('date')
585 if date:
585 if date:
586 opts['date'] = util.parsedate(date)
586 opts['date'] = util.parsedate(date)
587
587
588 cmdutil.checkunfinished(repo)
588 cmdutil.checkunfinished(repo)
589 cmdutil.bailifchanged(repo)
589 cmdutil.bailifchanged(repo)
590 node = scmutil.revsingle(repo, rev).node()
590 node = scmutil.revsingle(repo, rev).node()
591
591
592 op1, op2 = repo.dirstate.parents()
592 op1, op2 = repo.dirstate.parents()
593 if not repo.changelog.isancestor(node, op1):
593 if not repo.changelog.isancestor(node, op1):
594 raise error.Abort(_('cannot backout change that is not an ancestor'))
594 raise error.Abort(_('cannot backout change that is not an ancestor'))
595
595
596 p1, p2 = repo.changelog.parents(node)
596 p1, p2 = repo.changelog.parents(node)
597 if p1 == nullid:
597 if p1 == nullid:
598 raise error.Abort(_('cannot backout a change with no parents'))
598 raise error.Abort(_('cannot backout a change with no parents'))
599 if p2 != nullid:
599 if p2 != nullid:
600 if not opts.get('parent'):
600 if not opts.get('parent'):
601 raise error.Abort(_('cannot backout a merge changeset'))
601 raise error.Abort(_('cannot backout a merge changeset'))
602 p = repo.lookup(opts['parent'])
602 p = repo.lookup(opts['parent'])
603 if p not in (p1, p2):
603 if p not in (p1, p2):
604 raise error.Abort(_('%s is not a parent of %s') %
604 raise error.Abort(_('%s is not a parent of %s') %
605 (short(p), short(node)))
605 (short(p), short(node)))
606 parent = p
606 parent = p
607 else:
607 else:
608 if opts.get('parent'):
608 if opts.get('parent'):
609 raise error.Abort(_('cannot use --parent on non-merge changeset'))
609 raise error.Abort(_('cannot use --parent on non-merge changeset'))
610 parent = p1
610 parent = p1
611
611
612 # the backout should appear on the same branch
612 # the backout should appear on the same branch
613 branch = repo.dirstate.branch()
613 branch = repo.dirstate.branch()
614 bheads = repo.branchheads(branch)
614 bheads = repo.branchheads(branch)
615 rctx = scmutil.revsingle(repo, hex(parent))
615 rctx = scmutil.revsingle(repo, hex(parent))
616 if not opts.get('merge') and op1 != node:
616 if not opts.get('merge') and op1 != node:
617 dsguard = dirstateguard.dirstateguard(repo, 'backout')
617 dsguard = dirstateguard.dirstateguard(repo, 'backout')
618 try:
618 try:
619 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
619 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
620 'backout')
620 'backout')
621 stats = mergemod.update(repo, parent, True, True, node, False)
621 stats = mergemod.update(repo, parent, True, True, node, False)
622 repo.setparents(op1, op2)
622 repo.setparents(op1, op2)
623 dsguard.close()
623 dsguard.close()
624 hg._showstats(repo, stats)
624 hg._showstats(repo, stats)
625 if stats[3]:
625 if stats[3]:
626 repo.ui.status(_("use 'hg resolve' to retry unresolved "
626 repo.ui.status(_("use 'hg resolve' to retry unresolved "
627 "file merges\n"))
627 "file merges\n"))
628 return 1
628 return 1
629 finally:
629 finally:
630 ui.setconfig('ui', 'forcemerge', '', '')
630 ui.setconfig('ui', 'forcemerge', '', '')
631 lockmod.release(dsguard)
631 lockmod.release(dsguard)
632 else:
632 else:
633 hg.clean(repo, node, show_stats=False)
633 hg.clean(repo, node, show_stats=False)
634 repo.dirstate.setbranch(branch)
634 repo.dirstate.setbranch(branch)
635 cmdutil.revert(ui, repo, rctx, repo.dirstate.parents())
635 cmdutil.revert(ui, repo, rctx, repo.dirstate.parents())
636
636
637 if opts.get('no_commit'):
637 if opts.get('no_commit'):
638 msg = _("changeset %s backed out, "
638 msg = _("changeset %s backed out, "
639 "don't forget to commit.\n")
639 "don't forget to commit.\n")
640 ui.status(msg % short(node))
640 ui.status(msg % short(node))
641 return 0
641 return 0
642
642
643 def commitfunc(ui, repo, message, match, opts):
643 def commitfunc(ui, repo, message, match, opts):
644 editform = 'backout'
644 editform = 'backout'
645 e = cmdutil.getcommiteditor(editform=editform,
645 e = cmdutil.getcommiteditor(editform=editform,
646 **pycompat.strkwargs(opts))
646 **pycompat.strkwargs(opts))
647 if not message:
647 if not message:
648 # we don't translate commit messages
648 # we don't translate commit messages
649 message = "Backed out changeset %s" % short(node)
649 message = "Backed out changeset %s" % short(node)
650 e = cmdutil.getcommiteditor(edit=True, editform=editform)
650 e = cmdutil.getcommiteditor(edit=True, editform=editform)
651 return repo.commit(message, opts.get('user'), opts.get('date'),
651 return repo.commit(message, opts.get('user'), opts.get('date'),
652 match, editor=e)
652 match, editor=e)
653 newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
653 newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
654 if not newnode:
654 if not newnode:
655 ui.status(_("nothing changed\n"))
655 ui.status(_("nothing changed\n"))
656 return 1
656 return 1
657 cmdutil.commitstatus(repo, newnode, branch, bheads)
657 cmdutil.commitstatus(repo, newnode, branch, bheads)
658
658
659 def nice(node):
659 def nice(node):
660 return '%d:%s' % (repo.changelog.rev(node), short(node))
660 return '%d:%s' % (repo.changelog.rev(node), short(node))
661 ui.status(_('changeset %s backs out changeset %s\n') %
661 ui.status(_('changeset %s backs out changeset %s\n') %
662 (nice(repo.changelog.tip()), nice(node)))
662 (nice(repo.changelog.tip()), nice(node)))
663 if opts.get('merge') and op1 != node:
663 if opts.get('merge') and op1 != node:
664 hg.clean(repo, op1, show_stats=False)
664 hg.clean(repo, op1, show_stats=False)
665 ui.status(_('merging with changeset %s\n')
665 ui.status(_('merging with changeset %s\n')
666 % nice(repo.changelog.tip()))
666 % nice(repo.changelog.tip()))
667 try:
667 try:
668 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
668 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
669 'backout')
669 'backout')
670 return hg.merge(repo, hex(repo.changelog.tip()))
670 return hg.merge(repo, hex(repo.changelog.tip()))
671 finally:
671 finally:
672 ui.setconfig('ui', 'forcemerge', '', '')
672 ui.setconfig('ui', 'forcemerge', '', '')
673 return 0
673 return 0
674
674
675 @command('bisect',
675 @command('bisect',
676 [('r', 'reset', False, _('reset bisect state')),
676 [('r', 'reset', False, _('reset bisect state')),
677 ('g', 'good', False, _('mark changeset good')),
677 ('g', 'good', False, _('mark changeset good')),
678 ('b', 'bad', False, _('mark changeset bad')),
678 ('b', 'bad', False, _('mark changeset bad')),
679 ('s', 'skip', False, _('skip testing changeset')),
679 ('s', 'skip', False, _('skip testing changeset')),
680 ('e', 'extend', False, _('extend the bisect range')),
680 ('e', 'extend', False, _('extend the bisect range')),
681 ('c', 'command', '', _('use command to check changeset state'), _('CMD')),
681 ('c', 'command', '', _('use command to check changeset state'), _('CMD')),
682 ('U', 'noupdate', False, _('do not update to target'))],
682 ('U', 'noupdate', False, _('do not update to target'))],
683 _("[-gbsr] [-U] [-c CMD] [REV]"))
683 _("[-gbsr] [-U] [-c CMD] [REV]"))
684 def bisect(ui, repo, rev=None, extra=None, command=None,
684 def bisect(ui, repo, rev=None, extra=None, command=None,
685 reset=None, good=None, bad=None, skip=None, extend=None,
685 reset=None, good=None, bad=None, skip=None, extend=None,
686 noupdate=None):
686 noupdate=None):
687 """subdivision search of changesets
687 """subdivision search of changesets
688
688
689 This command helps to find changesets which introduce problems. To
689 This command helps to find changesets which introduce problems. To
690 use, mark the earliest changeset you know exhibits the problem as
690 use, mark the earliest changeset you know exhibits the problem as
691 bad, then mark the latest changeset which is free from the problem
691 bad, then mark the latest changeset which is free from the problem
692 as good. Bisect will update your working directory to a revision
692 as good. Bisect will update your working directory to a revision
693 for testing (unless the -U/--noupdate option is specified). Once
693 for testing (unless the -U/--noupdate option is specified). Once
694 you have performed tests, mark the working directory as good or
694 you have performed tests, mark the working directory as good or
695 bad, and bisect will either update to another candidate changeset
695 bad, and bisect will either update to another candidate changeset
696 or announce that it has found the bad revision.
696 or announce that it has found the bad revision.
697
697
698 As a shortcut, you can also use the revision argument to mark a
698 As a shortcut, you can also use the revision argument to mark a
699 revision as good or bad without checking it out first.
699 revision as good or bad without checking it out first.
700
700
701 If you supply a command, it will be used for automatic bisection.
701 If you supply a command, it will be used for automatic bisection.
702 The environment variable HG_NODE will contain the ID of the
702 The environment variable HG_NODE will contain the ID of the
703 changeset being tested. The exit status of the command will be
703 changeset being tested. The exit status of the command will be
704 used to mark revisions as good or bad: status 0 means good, 125
704 used to mark revisions as good or bad: status 0 means good, 125
705 means to skip the revision, 127 (command not found) will abort the
705 means to skip the revision, 127 (command not found) will abort the
706 bisection, and any other non-zero exit status means the revision
706 bisection, and any other non-zero exit status means the revision
707 is bad.
707 is bad.
708
708
709 .. container:: verbose
709 .. container:: verbose
710
710
711 Some examples:
711 Some examples:
712
712
713 - start a bisection with known bad revision 34, and good revision 12::
713 - start a bisection with known bad revision 34, and good revision 12::
714
714
715 hg bisect --bad 34
715 hg bisect --bad 34
716 hg bisect --good 12
716 hg bisect --good 12
717
717
718 - advance the current bisection by marking current revision as good or
718 - advance the current bisection by marking current revision as good or
719 bad::
719 bad::
720
720
721 hg bisect --good
721 hg bisect --good
722 hg bisect --bad
722 hg bisect --bad
723
723
724 - mark the current revision, or a known revision, to be skipped (e.g. if
724 - mark the current revision, or a known revision, to be skipped (e.g. if
725 that revision is not usable because of another issue)::
725 that revision is not usable because of another issue)::
726
726
727 hg bisect --skip
727 hg bisect --skip
728 hg bisect --skip 23
728 hg bisect --skip 23
729
729
730 - skip all revisions that do not touch directories ``foo`` or ``bar``::
730 - skip all revisions that do not touch directories ``foo`` or ``bar``::
731
731
732 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
732 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
733
733
734 - forget the current bisection::
734 - forget the current bisection::
735
735
736 hg bisect --reset
736 hg bisect --reset
737
737
738 - use 'make && make tests' to automatically find the first broken
738 - use 'make && make tests' to automatically find the first broken
739 revision::
739 revision::
740
740
741 hg bisect --reset
741 hg bisect --reset
742 hg bisect --bad 34
742 hg bisect --bad 34
743 hg bisect --good 12
743 hg bisect --good 12
744 hg bisect --command "make && make tests"
744 hg bisect --command "make && make tests"
745
745
746 - see all changesets whose states are already known in the current
746 - see all changesets whose states are already known in the current
747 bisection::
747 bisection::
748
748
749 hg log -r "bisect(pruned)"
749 hg log -r "bisect(pruned)"
750
750
751 - see the changeset currently being bisected (especially useful
751 - see the changeset currently being bisected (especially useful
752 if running with -U/--noupdate)::
752 if running with -U/--noupdate)::
753
753
754 hg log -r "bisect(current)"
754 hg log -r "bisect(current)"
755
755
756 - see all changesets that took part in the current bisection::
756 - see all changesets that took part in the current bisection::
757
757
758 hg log -r "bisect(range)"
758 hg log -r "bisect(range)"
759
759
760 - you can even get a nice graph::
760 - you can even get a nice graph::
761
761
762 hg log --graph -r "bisect(range)"
762 hg log --graph -r "bisect(range)"
763
763
764 See :hg:`help revisions.bisect` for more about the `bisect()` predicate.
764 See :hg:`help revisions.bisect` for more about the `bisect()` predicate.
765
765
766 Returns 0 on success.
766 Returns 0 on success.
767 """
767 """
768 # backward compatibility
768 # backward compatibility
769 if rev in "good bad reset init".split():
769 if rev in "good bad reset init".split():
770 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
770 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
771 cmd, rev, extra = rev, extra, None
771 cmd, rev, extra = rev, extra, None
772 if cmd == "good":
772 if cmd == "good":
773 good = True
773 good = True
774 elif cmd == "bad":
774 elif cmd == "bad":
775 bad = True
775 bad = True
776 else:
776 else:
777 reset = True
777 reset = True
778 elif extra:
778 elif extra:
779 raise error.Abort(_('incompatible arguments'))
779 raise error.Abort(_('incompatible arguments'))
780
780
781 incompatibles = {
781 incompatibles = {
782 '--bad': bad,
782 '--bad': bad,
783 '--command': bool(command),
783 '--command': bool(command),
784 '--extend': extend,
784 '--extend': extend,
785 '--good': good,
785 '--good': good,
786 '--reset': reset,
786 '--reset': reset,
787 '--skip': skip,
787 '--skip': skip,
788 }
788 }
789
789
790 enabled = [x for x in incompatibles if incompatibles[x]]
790 enabled = [x for x in incompatibles if incompatibles[x]]
791
791
792 if len(enabled) > 1:
792 if len(enabled) > 1:
793 raise error.Abort(_('%s and %s are incompatible') %
793 raise error.Abort(_('%s and %s are incompatible') %
794 tuple(sorted(enabled)[0:2]))
794 tuple(sorted(enabled)[0:2]))
795
795
796 if reset:
796 if reset:
797 hbisect.resetstate(repo)
797 hbisect.resetstate(repo)
798 return
798 return
799
799
800 state = hbisect.load_state(repo)
800 state = hbisect.load_state(repo)
801
801
802 # update state
802 # update state
803 if good or bad or skip:
803 if good or bad or skip:
804 if rev:
804 if rev:
805 nodes = [repo.lookup(i) for i in scmutil.revrange(repo, [rev])]
805 nodes = [repo.lookup(i) for i in scmutil.revrange(repo, [rev])]
806 else:
806 else:
807 nodes = [repo.lookup('.')]
807 nodes = [repo.lookup('.')]
808 if good:
808 if good:
809 state['good'] += nodes
809 state['good'] += nodes
810 elif bad:
810 elif bad:
811 state['bad'] += nodes
811 state['bad'] += nodes
812 elif skip:
812 elif skip:
813 state['skip'] += nodes
813 state['skip'] += nodes
814 hbisect.save_state(repo, state)
814 hbisect.save_state(repo, state)
815 if not (state['good'] and state['bad']):
815 if not (state['good'] and state['bad']):
816 return
816 return
817
817
818 def mayupdate(repo, node, show_stats=True):
818 def mayupdate(repo, node, show_stats=True):
819 """common used update sequence"""
819 """common used update sequence"""
820 if noupdate:
820 if noupdate:
821 return
821 return
822 cmdutil.checkunfinished(repo)
822 cmdutil.checkunfinished(repo)
823 cmdutil.bailifchanged(repo)
823 cmdutil.bailifchanged(repo)
824 return hg.clean(repo, node, show_stats=show_stats)
824 return hg.clean(repo, node, show_stats=show_stats)
825
825
826 displayer = cmdutil.show_changeset(ui, repo, {})
826 displayer = cmdutil.show_changeset(ui, repo, {})
827
827
828 if command:
828 if command:
829 changesets = 1
829 changesets = 1
830 if noupdate:
830 if noupdate:
831 try:
831 try:
832 node = state['current'][0]
832 node = state['current'][0]
833 except LookupError:
833 except LookupError:
834 raise error.Abort(_('current bisect revision is unknown - '
834 raise error.Abort(_('current bisect revision is unknown - '
835 'start a new bisect to fix'))
835 'start a new bisect to fix'))
836 else:
836 else:
837 node, p2 = repo.dirstate.parents()
837 node, p2 = repo.dirstate.parents()
838 if p2 != nullid:
838 if p2 != nullid:
839 raise error.Abort(_('current bisect revision is a merge'))
839 raise error.Abort(_('current bisect revision is a merge'))
840 if rev:
840 if rev:
841 node = repo[scmutil.revsingle(repo, rev, node)].node()
841 node = repo[scmutil.revsingle(repo, rev, node)].node()
842 try:
842 try:
843 while changesets:
843 while changesets:
844 # update state
844 # update state
845 state['current'] = [node]
845 state['current'] = [node]
846 hbisect.save_state(repo, state)
846 hbisect.save_state(repo, state)
847 status = ui.system(command, environ={'HG_NODE': hex(node)},
847 status = ui.system(command, environ={'HG_NODE': hex(node)},
848 blockedtag='bisect_check')
848 blockedtag='bisect_check')
849 if status == 125:
849 if status == 125:
850 transition = "skip"
850 transition = "skip"
851 elif status == 0:
851 elif status == 0:
852 transition = "good"
852 transition = "good"
853 # status < 0 means process was killed
853 # status < 0 means process was killed
854 elif status == 127:
854 elif status == 127:
855 raise error.Abort(_("failed to execute %s") % command)
855 raise error.Abort(_("failed to execute %s") % command)
856 elif status < 0:
856 elif status < 0:
857 raise error.Abort(_("%s killed") % command)
857 raise error.Abort(_("%s killed") % command)
858 else:
858 else:
859 transition = "bad"
859 transition = "bad"
860 state[transition].append(node)
860 state[transition].append(node)
861 ctx = repo[node]
861 ctx = repo[node]
862 ui.status(_('changeset %d:%s: %s\n') % (ctx, ctx, transition))
862 ui.status(_('changeset %d:%s: %s\n') % (ctx, ctx, transition))
863 hbisect.checkstate(state)
863 hbisect.checkstate(state)
864 # bisect
864 # bisect
865 nodes, changesets, bgood = hbisect.bisect(repo, state)
865 nodes, changesets, bgood = hbisect.bisect(repo, state)
866 # update to next check
866 # update to next check
867 node = nodes[0]
867 node = nodes[0]
868 mayupdate(repo, node, show_stats=False)
868 mayupdate(repo, node, show_stats=False)
869 finally:
869 finally:
870 state['current'] = [node]
870 state['current'] = [node]
871 hbisect.save_state(repo, state)
871 hbisect.save_state(repo, state)
872 hbisect.printresult(ui, repo, state, displayer, nodes, bgood)
872 hbisect.printresult(ui, repo, state, displayer, nodes, bgood)
873 return
873 return
874
874
875 hbisect.checkstate(state)
875 hbisect.checkstate(state)
876
876
877 # actually bisect
877 # actually bisect
878 nodes, changesets, good = hbisect.bisect(repo, state)
878 nodes, changesets, good = hbisect.bisect(repo, state)
879 if extend:
879 if extend:
880 if not changesets:
880 if not changesets:
881 extendnode = hbisect.extendrange(repo, state, nodes, good)
881 extendnode = hbisect.extendrange(repo, state, nodes, good)
882 if extendnode is not None:
882 if extendnode is not None:
883 ui.write(_("Extending search to changeset %d:%s\n")
883 ui.write(_("Extending search to changeset %d:%s\n")
884 % (extendnode.rev(), extendnode))
884 % (extendnode.rev(), extendnode))
885 state['current'] = [extendnode.node()]
885 state['current'] = [extendnode.node()]
886 hbisect.save_state(repo, state)
886 hbisect.save_state(repo, state)
887 return mayupdate(repo, extendnode.node())
887 return mayupdate(repo, extendnode.node())
888 raise error.Abort(_("nothing to extend"))
888 raise error.Abort(_("nothing to extend"))
889
889
890 if changesets == 0:
890 if changesets == 0:
891 hbisect.printresult(ui, repo, state, displayer, nodes, good)
891 hbisect.printresult(ui, repo, state, displayer, nodes, good)
892 else:
892 else:
893 assert len(nodes) == 1 # only a single node can be tested next
893 assert len(nodes) == 1 # only a single node can be tested next
894 node = nodes[0]
894 node = nodes[0]
895 # compute the approximate number of remaining tests
895 # compute the approximate number of remaining tests
896 tests, size = 0, 2
896 tests, size = 0, 2
897 while size <= changesets:
897 while size <= changesets:
898 tests, size = tests + 1, size * 2
898 tests, size = tests + 1, size * 2
899 rev = repo.changelog.rev(node)
899 rev = repo.changelog.rev(node)
900 ui.write(_("Testing changeset %d:%s "
900 ui.write(_("Testing changeset %d:%s "
901 "(%d changesets remaining, ~%d tests)\n")
901 "(%d changesets remaining, ~%d tests)\n")
902 % (rev, short(node), changesets, tests))
902 % (rev, short(node), changesets, tests))
903 state['current'] = [node]
903 state['current'] = [node]
904 hbisect.save_state(repo, state)
904 hbisect.save_state(repo, state)
905 return mayupdate(repo, node)
905 return mayupdate(repo, node)
906
906
907 @command('bookmarks|bookmark',
907 @command('bookmarks|bookmark',
908 [('f', 'force', False, _('force')),
908 [('f', 'force', False, _('force')),
909 ('r', 'rev', '', _('revision for bookmark action'), _('REV')),
909 ('r', 'rev', '', _('revision for bookmark action'), _('REV')),
910 ('d', 'delete', False, _('delete a given bookmark')),
910 ('d', 'delete', False, _('delete a given bookmark')),
911 ('m', 'rename', '', _('rename a given bookmark'), _('OLD')),
911 ('m', 'rename', '', _('rename a given bookmark'), _('OLD')),
912 ('i', 'inactive', False, _('mark a bookmark inactive')),
912 ('i', 'inactive', False, _('mark a bookmark inactive')),
913 ] + formatteropts,
913 ] + formatteropts,
914 _('hg bookmarks [OPTIONS]... [NAME]...'))
914 _('hg bookmarks [OPTIONS]... [NAME]...'))
915 def bookmark(ui, repo, *names, **opts):
915 def bookmark(ui, repo, *names, **opts):
916 '''create a new bookmark or list existing bookmarks
916 '''create a new bookmark or list existing bookmarks
917
917
918 Bookmarks are labels on changesets to help track lines of development.
918 Bookmarks are labels on changesets to help track lines of development.
919 Bookmarks are unversioned and can be moved, renamed and deleted.
919 Bookmarks are unversioned and can be moved, renamed and deleted.
920 Deleting or moving a bookmark has no effect on the associated changesets.
920 Deleting or moving a bookmark has no effect on the associated changesets.
921
921
922 Creating or updating to a bookmark causes it to be marked as 'active'.
922 Creating or updating to a bookmark causes it to be marked as 'active'.
923 The active bookmark is indicated with a '*'.
923 The active bookmark is indicated with a '*'.
924 When a commit is made, the active bookmark will advance to the new commit.
924 When a commit is made, the active bookmark will advance to the new commit.
925 A plain :hg:`update` will also advance an active bookmark, if possible.
925 A plain :hg:`update` will also advance an active bookmark, if possible.
926 Updating away from a bookmark will cause it to be deactivated.
926 Updating away from a bookmark will cause it to be deactivated.
927
927
928 Bookmarks can be pushed and pulled between repositories (see
928 Bookmarks can be pushed and pulled between repositories (see
929 :hg:`help push` and :hg:`help pull`). If a shared bookmark has
929 :hg:`help push` and :hg:`help pull`). If a shared bookmark has
930 diverged, a new 'divergent bookmark' of the form 'name@path' will
930 diverged, a new 'divergent bookmark' of the form 'name@path' will
931 be created. Using :hg:`merge` will resolve the divergence.
931 be created. Using :hg:`merge` will resolve the divergence.
932
932
933 Specifying bookmark as '.' to -m or -d options is equivalent to specifying
933 Specifying bookmark as '.' to -m or -d options is equivalent to specifying
934 the active bookmark's name.
934 the active bookmark's name.
935
935
936 A bookmark named '@' has the special property that :hg:`clone` will
936 A bookmark named '@' has the special property that :hg:`clone` will
937 check it out by default if it exists.
937 check it out by default if it exists.
938
938
939 .. container:: verbose
939 .. container:: verbose
940
940
941 Examples:
941 Examples:
942
942
943 - create an active bookmark for a new line of development::
943 - create an active bookmark for a new line of development::
944
944
945 hg book new-feature
945 hg book new-feature
946
946
947 - create an inactive bookmark as a place marker::
947 - create an inactive bookmark as a place marker::
948
948
949 hg book -i reviewed
949 hg book -i reviewed
950
950
951 - create an inactive bookmark on another changeset::
951 - create an inactive bookmark on another changeset::
952
952
953 hg book -r .^ tested
953 hg book -r .^ tested
954
954
955 - rename bookmark turkey to dinner::
955 - rename bookmark turkey to dinner::
956
956
957 hg book -m turkey dinner
957 hg book -m turkey dinner
958
958
959 - move the '@' bookmark from another branch::
959 - move the '@' bookmark from another branch::
960
960
961 hg book -f @
961 hg book -f @
962 '''
962 '''
963 force = opts.get(r'force')
963 force = opts.get(r'force')
964 rev = opts.get(r'rev')
964 rev = opts.get(r'rev')
965 delete = opts.get(r'delete')
965 delete = opts.get(r'delete')
966 rename = opts.get(r'rename')
966 rename = opts.get(r'rename')
967 inactive = opts.get(r'inactive')
967 inactive = opts.get(r'inactive')
968
968
969 if delete and rename:
969 if delete and rename:
970 raise error.Abort(_("--delete and --rename are incompatible"))
970 raise error.Abort(_("--delete and --rename are incompatible"))
971 if delete and rev:
971 if delete and rev:
972 raise error.Abort(_("--rev is incompatible with --delete"))
972 raise error.Abort(_("--rev is incompatible with --delete"))
973 if rename and rev:
973 if rename and rev:
974 raise error.Abort(_("--rev is incompatible with --rename"))
974 raise error.Abort(_("--rev is incompatible with --rename"))
975 if not names and (delete or rev):
975 if not names and (delete or rev):
976 raise error.Abort(_("bookmark name required"))
976 raise error.Abort(_("bookmark name required"))
977
977
978 if delete or rename or names or inactive:
978 if delete or rename or names or inactive:
979 with repo.wlock(), repo.lock(), repo.transaction('bookmark') as tr:
979 with repo.wlock(), repo.lock(), repo.transaction('bookmark') as tr:
980 if delete:
980 if delete:
981 names = pycompat.maplist(repo._bookmarks.expandname, names)
981 names = pycompat.maplist(repo._bookmarks.expandname, names)
982 bookmarks.delete(repo, tr, names)
982 bookmarks.delete(repo, tr, names)
983 elif rename:
983 elif rename:
984 if not names:
984 if not names:
985 raise error.Abort(_("new bookmark name required"))
985 raise error.Abort(_("new bookmark name required"))
986 elif len(names) > 1:
986 elif len(names) > 1:
987 raise error.Abort(_("only one new bookmark name allowed"))
987 raise error.Abort(_("only one new bookmark name allowed"))
988 rename = repo._bookmarks.expandname(rename)
988 rename = repo._bookmarks.expandname(rename)
989 bookmarks.rename(repo, tr, rename, names[0], force, inactive)
989 bookmarks.rename(repo, tr, rename, names[0], force, inactive)
990 elif names:
990 elif names:
991 bookmarks.addbookmarks(repo, tr, names, rev, force, inactive)
991 bookmarks.addbookmarks(repo, tr, names, rev, force, inactive)
992 elif inactive:
992 elif inactive:
993 if len(repo._bookmarks) == 0:
993 if len(repo._bookmarks) == 0:
994 ui.status(_("no bookmarks set\n"))
994 ui.status(_("no bookmarks set\n"))
995 elif not repo._activebookmark:
995 elif not repo._activebookmark:
996 ui.status(_("no active bookmark\n"))
996 ui.status(_("no active bookmark\n"))
997 else:
997 else:
998 bookmarks.deactivate(repo)
998 bookmarks.deactivate(repo)
999 else: # show bookmarks
999 else: # show bookmarks
1000 bookmarks.printbookmarks(ui, repo, **opts)
1000 bookmarks.printbookmarks(ui, repo, **opts)
1001
1001
1002 @command('branch',
1002 @command('branch',
1003 [('f', 'force', None,
1003 [('f', 'force', None,
1004 _('set branch name even if it shadows an existing branch')),
1004 _('set branch name even if it shadows an existing branch')),
1005 ('C', 'clean', None, _('reset branch name to parent branch name')),
1005 ('C', 'clean', None, _('reset branch name to parent branch name')),
1006 ('r', 'rev', [], _('change branches of the given revs (EXPERIMENTAL)')),
1006 ('r', 'rev', [], _('change branches of the given revs (EXPERIMENTAL)')),
1007 ],
1007 ],
1008 _('[-fC] [NAME]'))
1008 _('[-fC] [NAME]'))
1009 def branch(ui, repo, label=None, **opts):
1009 def branch(ui, repo, label=None, **opts):
1010 """set or show the current branch name
1010 """set or show the current branch name
1011
1011
1012 .. note::
1012 .. note::
1013
1013
1014 Branch names are permanent and global. Use :hg:`bookmark` to create a
1014 Branch names are permanent and global. Use :hg:`bookmark` to create a
1015 light-weight bookmark instead. See :hg:`help glossary` for more
1015 light-weight bookmark instead. See :hg:`help glossary` for more
1016 information about named branches and bookmarks.
1016 information about named branches and bookmarks.
1017
1017
1018 With no argument, show the current branch name. With one argument,
1018 With no argument, show the current branch name. With one argument,
1019 set the working directory branch name (the branch will not exist
1019 set the working directory branch name (the branch will not exist
1020 in the repository until the next commit). Standard practice
1020 in the repository until the next commit). Standard practice
1021 recommends that primary development take place on the 'default'
1021 recommends that primary development take place on the 'default'
1022 branch.
1022 branch.
1023
1023
1024 Unless -f/--force is specified, branch will not let you set a
1024 Unless -f/--force is specified, branch will not let you set a
1025 branch name that already exists.
1025 branch name that already exists.
1026
1026
1027 Use -C/--clean to reset the working directory branch to that of
1027 Use -C/--clean to reset the working directory branch to that of
1028 the parent of the working directory, negating a previous branch
1028 the parent of the working directory, negating a previous branch
1029 change.
1029 change.
1030
1030
1031 Use the command :hg:`update` to switch to an existing branch. Use
1031 Use the command :hg:`update` to switch to an existing branch. Use
1032 :hg:`commit --close-branch` to mark this branch head as closed.
1032 :hg:`commit --close-branch` to mark this branch head as closed.
1033 When all heads of a branch are closed, the branch will be
1033 When all heads of a branch are closed, the branch will be
1034 considered closed.
1034 considered closed.
1035
1035
1036 Returns 0 on success.
1036 Returns 0 on success.
1037 """
1037 """
1038 opts = pycompat.byteskwargs(opts)
1038 opts = pycompat.byteskwargs(opts)
1039 revs = opts.get('rev')
1039 revs = opts.get('rev')
1040 if label:
1040 if label:
1041 label = label.strip()
1041 label = label.strip()
1042
1042
1043 if not opts.get('clean') and not label:
1043 if not opts.get('clean') and not label:
1044 if revs:
1044 if revs:
1045 raise error.Abort(_("no branch name specified for the revisions"))
1045 raise error.Abort(_("no branch name specified for the revisions"))
1046 ui.write("%s\n" % repo.dirstate.branch())
1046 ui.write("%s\n" % repo.dirstate.branch())
1047 return
1047 return
1048
1048
1049 with repo.wlock():
1049 with repo.wlock():
1050 if opts.get('clean'):
1050 if opts.get('clean'):
1051 label = repo[None].p1().branch()
1051 label = repo[None].p1().branch()
1052 repo.dirstate.setbranch(label)
1052 repo.dirstate.setbranch(label)
1053 ui.status(_('reset working directory to branch %s\n') % label)
1053 ui.status(_('reset working directory to branch %s\n') % label)
1054 elif label:
1054 elif label:
1055
1055
1056 scmutil.checknewlabel(repo, label, 'branch')
1056 scmutil.checknewlabel(repo, label, 'branch')
1057 if revs:
1057 if revs:
1058 # XXX: we should allow setting name to existing branch if the
1059 # branch of root of the revs is same as the new branch name
1060 if label in repo.branchmap():
1061 raise error.Abort(_('a branch of the same'
1062 ' name already exists'))
1063 return cmdutil.changebranch(ui, repo, revs, label)
1058 return cmdutil.changebranch(ui, repo, revs, label)
1064
1059
1065 if not opts.get('force') and label in repo.branchmap():
1060 if not opts.get('force') and label in repo.branchmap():
1066 if label not in [p.branch() for p in repo[None].parents()]:
1061 if label not in [p.branch() for p in repo[None].parents()]:
1067 raise error.Abort(_('a branch of the same name already'
1062 raise error.Abort(_('a branch of the same name already'
1068 ' exists'),
1063 ' exists'),
1069 # i18n: "it" refers to an existing branch
1064 # i18n: "it" refers to an existing branch
1070 hint=_("use 'hg update' to switch to it"))
1065 hint=_("use 'hg update' to switch to it"))
1071
1066
1072 repo.dirstate.setbranch(label)
1067 repo.dirstate.setbranch(label)
1073 ui.status(_('marked working directory as branch %s\n') % label)
1068 ui.status(_('marked working directory as branch %s\n') % label)
1074
1069
1075 # find any open named branches aside from default
1070 # find any open named branches aside from default
1076 others = [n for n, h, t, c in repo.branchmap().iterbranches()
1071 others = [n for n, h, t, c in repo.branchmap().iterbranches()
1077 if n != "default" and not c]
1072 if n != "default" and not c]
1078 if not others:
1073 if not others:
1079 ui.status(_('(branches are permanent and global, '
1074 ui.status(_('(branches are permanent and global, '
1080 'did you want a bookmark?)\n'))
1075 'did you want a bookmark?)\n'))
1081
1076
1082 @command('branches',
1077 @command('branches',
1083 [('a', 'active', False,
1078 [('a', 'active', False,
1084 _('show only branches that have unmerged heads (DEPRECATED)')),
1079 _('show only branches that have unmerged heads (DEPRECATED)')),
1085 ('c', 'closed', False, _('show normal and closed branches')),
1080 ('c', 'closed', False, _('show normal and closed branches')),
1086 ] + formatteropts,
1081 ] + formatteropts,
1087 _('[-c]'), cmdtype=readonly)
1082 _('[-c]'), cmdtype=readonly)
1088 def branches(ui, repo, active=False, closed=False, **opts):
1083 def branches(ui, repo, active=False, closed=False, **opts):
1089 """list repository named branches
1084 """list repository named branches
1090
1085
1091 List the repository's named branches, indicating which ones are
1086 List the repository's named branches, indicating which ones are
1092 inactive. If -c/--closed is specified, also list branches which have
1087 inactive. If -c/--closed is specified, also list branches which have
1093 been marked closed (see :hg:`commit --close-branch`).
1088 been marked closed (see :hg:`commit --close-branch`).
1094
1089
1095 Use the command :hg:`update` to switch to an existing branch.
1090 Use the command :hg:`update` to switch to an existing branch.
1096
1091
1097 Returns 0.
1092 Returns 0.
1098 """
1093 """
1099
1094
1100 opts = pycompat.byteskwargs(opts)
1095 opts = pycompat.byteskwargs(opts)
1101 ui.pager('branches')
1096 ui.pager('branches')
1102 fm = ui.formatter('branches', opts)
1097 fm = ui.formatter('branches', opts)
1103 hexfunc = fm.hexfunc
1098 hexfunc = fm.hexfunc
1104
1099
1105 allheads = set(repo.heads())
1100 allheads = set(repo.heads())
1106 branches = []
1101 branches = []
1107 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1102 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1108 isactive = False
1103 isactive = False
1109 if not isclosed:
1104 if not isclosed:
1110 openheads = set(repo.branchmap().iteropen(heads))
1105 openheads = set(repo.branchmap().iteropen(heads))
1111 isactive = bool(openheads & allheads)
1106 isactive = bool(openheads & allheads)
1112 branches.append((tag, repo[tip], isactive, not isclosed))
1107 branches.append((tag, repo[tip], isactive, not isclosed))
1113 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]),
1108 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]),
1114 reverse=True)
1109 reverse=True)
1115
1110
1116 for tag, ctx, isactive, isopen in branches:
1111 for tag, ctx, isactive, isopen in branches:
1117 if active and not isactive:
1112 if active and not isactive:
1118 continue
1113 continue
1119 if isactive:
1114 if isactive:
1120 label = 'branches.active'
1115 label = 'branches.active'
1121 notice = ''
1116 notice = ''
1122 elif not isopen:
1117 elif not isopen:
1123 if not closed:
1118 if not closed:
1124 continue
1119 continue
1125 label = 'branches.closed'
1120 label = 'branches.closed'
1126 notice = _(' (closed)')
1121 notice = _(' (closed)')
1127 else:
1122 else:
1128 label = 'branches.inactive'
1123 label = 'branches.inactive'
1129 notice = _(' (inactive)')
1124 notice = _(' (inactive)')
1130 current = (tag == repo.dirstate.branch())
1125 current = (tag == repo.dirstate.branch())
1131 if current:
1126 if current:
1132 label = 'branches.current'
1127 label = 'branches.current'
1133
1128
1134 fm.startitem()
1129 fm.startitem()
1135 fm.write('branch', '%s', tag, label=label)
1130 fm.write('branch', '%s', tag, label=label)
1136 rev = ctx.rev()
1131 rev = ctx.rev()
1137 padsize = max(31 - len(str(rev)) - encoding.colwidth(tag), 0)
1132 padsize = max(31 - len(str(rev)) - encoding.colwidth(tag), 0)
1138 fmt = ' ' * padsize + ' %d:%s'
1133 fmt = ' ' * padsize + ' %d:%s'
1139 fm.condwrite(not ui.quiet, 'rev node', fmt, rev, hexfunc(ctx.node()),
1134 fm.condwrite(not ui.quiet, 'rev node', fmt, rev, hexfunc(ctx.node()),
1140 label='log.changeset changeset.%s' % ctx.phasestr())
1135 label='log.changeset changeset.%s' % ctx.phasestr())
1141 fm.context(ctx=ctx)
1136 fm.context(ctx=ctx)
1142 fm.data(active=isactive, closed=not isopen, current=current)
1137 fm.data(active=isactive, closed=not isopen, current=current)
1143 if not ui.quiet:
1138 if not ui.quiet:
1144 fm.plain(notice)
1139 fm.plain(notice)
1145 fm.plain('\n')
1140 fm.plain('\n')
1146 fm.end()
1141 fm.end()
1147
1142
1148 @command('bundle',
1143 @command('bundle',
1149 [('f', 'force', None, _('run even when the destination is unrelated')),
1144 [('f', 'force', None, _('run even when the destination is unrelated')),
1150 ('r', 'rev', [], _('a changeset intended to be added to the destination'),
1145 ('r', 'rev', [], _('a changeset intended to be added to the destination'),
1151 _('REV')),
1146 _('REV')),
1152 ('b', 'branch', [], _('a specific branch you would like to bundle'),
1147 ('b', 'branch', [], _('a specific branch you would like to bundle'),
1153 _('BRANCH')),
1148 _('BRANCH')),
1154 ('', 'base', [],
1149 ('', 'base', [],
1155 _('a base changeset assumed to be available at the destination'),
1150 _('a base changeset assumed to be available at the destination'),
1156 _('REV')),
1151 _('REV')),
1157 ('a', 'all', None, _('bundle all changesets in the repository')),
1152 ('a', 'all', None, _('bundle all changesets in the repository')),
1158 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
1153 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
1159 ] + remoteopts,
1154 ] + remoteopts,
1160 _('[-f] [-t BUNDLESPEC] [-a] [-r REV]... [--base REV]... FILE [DEST]'))
1155 _('[-f] [-t BUNDLESPEC] [-a] [-r REV]... [--base REV]... FILE [DEST]'))
1161 def bundle(ui, repo, fname, dest=None, **opts):
1156 def bundle(ui, repo, fname, dest=None, **opts):
1162 """create a bundle file
1157 """create a bundle file
1163
1158
1164 Generate a bundle file containing data to be added to a repository.
1159 Generate a bundle file containing data to be added to a repository.
1165
1160
1166 To create a bundle containing all changesets, use -a/--all
1161 To create a bundle containing all changesets, use -a/--all
1167 (or --base null). Otherwise, hg assumes the destination will have
1162 (or --base null). Otherwise, hg assumes the destination will have
1168 all the nodes you specify with --base parameters. Otherwise, hg
1163 all the nodes you specify with --base parameters. Otherwise, hg
1169 will assume the repository has all the nodes in destination, or
1164 will assume the repository has all the nodes in destination, or
1170 default-push/default if no destination is specified.
1165 default-push/default if no destination is specified.
1171
1166
1172 You can change bundle format with the -t/--type option. See
1167 You can change bundle format with the -t/--type option. See
1173 :hg:`help bundlespec` for documentation on this format. By default,
1168 :hg:`help bundlespec` for documentation on this format. By default,
1174 the most appropriate format is used and compression defaults to
1169 the most appropriate format is used and compression defaults to
1175 bzip2.
1170 bzip2.
1176
1171
1177 The bundle file can then be transferred using conventional means
1172 The bundle file can then be transferred using conventional means
1178 and applied to another repository with the unbundle or pull
1173 and applied to another repository with the unbundle or pull
1179 command. This is useful when direct push and pull are not
1174 command. This is useful when direct push and pull are not
1180 available or when exporting an entire repository is undesirable.
1175 available or when exporting an entire repository is undesirable.
1181
1176
1182 Applying bundles preserves all changeset contents including
1177 Applying bundles preserves all changeset contents including
1183 permissions, copy/rename information, and revision history.
1178 permissions, copy/rename information, and revision history.
1184
1179
1185 Returns 0 on success, 1 if no changes found.
1180 Returns 0 on success, 1 if no changes found.
1186 """
1181 """
1187 opts = pycompat.byteskwargs(opts)
1182 opts = pycompat.byteskwargs(opts)
1188 revs = None
1183 revs = None
1189 if 'rev' in opts:
1184 if 'rev' in opts:
1190 revstrings = opts['rev']
1185 revstrings = opts['rev']
1191 revs = scmutil.revrange(repo, revstrings)
1186 revs = scmutil.revrange(repo, revstrings)
1192 if revstrings and not revs:
1187 if revstrings and not revs:
1193 raise error.Abort(_('no commits to bundle'))
1188 raise error.Abort(_('no commits to bundle'))
1194
1189
1195 bundletype = opts.get('type', 'bzip2').lower()
1190 bundletype = opts.get('type', 'bzip2').lower()
1196 try:
1191 try:
1197 bcompression, cgversion, params = exchange.parsebundlespec(
1192 bcompression, cgversion, params = exchange.parsebundlespec(
1198 repo, bundletype, strict=False)
1193 repo, bundletype, strict=False)
1199 except error.UnsupportedBundleSpecification as e:
1194 except error.UnsupportedBundleSpecification as e:
1200 raise error.Abort(str(e),
1195 raise error.Abort(str(e),
1201 hint=_("see 'hg help bundlespec' for supported "
1196 hint=_("see 'hg help bundlespec' for supported "
1202 "values for --type"))
1197 "values for --type"))
1203
1198
1204 # Packed bundles are a pseudo bundle format for now.
1199 # Packed bundles are a pseudo bundle format for now.
1205 if cgversion == 's1':
1200 if cgversion == 's1':
1206 raise error.Abort(_('packed bundles cannot be produced by "hg bundle"'),
1201 raise error.Abort(_('packed bundles cannot be produced by "hg bundle"'),
1207 hint=_("use 'hg debugcreatestreamclonebundle'"))
1202 hint=_("use 'hg debugcreatestreamclonebundle'"))
1208
1203
1209 if opts.get('all'):
1204 if opts.get('all'):
1210 if dest:
1205 if dest:
1211 raise error.Abort(_("--all is incompatible with specifying "
1206 raise error.Abort(_("--all is incompatible with specifying "
1212 "a destination"))
1207 "a destination"))
1213 if opts.get('base'):
1208 if opts.get('base'):
1214 ui.warn(_("ignoring --base because --all was specified\n"))
1209 ui.warn(_("ignoring --base because --all was specified\n"))
1215 base = ['null']
1210 base = ['null']
1216 else:
1211 else:
1217 base = scmutil.revrange(repo, opts.get('base'))
1212 base = scmutil.revrange(repo, opts.get('base'))
1218 if cgversion not in changegroup.supportedoutgoingversions(repo):
1213 if cgversion not in changegroup.supportedoutgoingversions(repo):
1219 raise error.Abort(_("repository does not support bundle version %s") %
1214 raise error.Abort(_("repository does not support bundle version %s") %
1220 cgversion)
1215 cgversion)
1221
1216
1222 if base:
1217 if base:
1223 if dest:
1218 if dest:
1224 raise error.Abort(_("--base is incompatible with specifying "
1219 raise error.Abort(_("--base is incompatible with specifying "
1225 "a destination"))
1220 "a destination"))
1226 common = [repo.lookup(rev) for rev in base]
1221 common = [repo.lookup(rev) for rev in base]
1227 heads = revs and map(repo.lookup, revs) or None
1222 heads = revs and map(repo.lookup, revs) or None
1228 outgoing = discovery.outgoing(repo, common, heads)
1223 outgoing = discovery.outgoing(repo, common, heads)
1229 else:
1224 else:
1230 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1225 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1231 dest, branches = hg.parseurl(dest, opts.get('branch'))
1226 dest, branches = hg.parseurl(dest, opts.get('branch'))
1232 other = hg.peer(repo, opts, dest)
1227 other = hg.peer(repo, opts, dest)
1233 revs, checkout = hg.addbranchrevs(repo, repo, branches, revs)
1228 revs, checkout = hg.addbranchrevs(repo, repo, branches, revs)
1234 heads = revs and map(repo.lookup, revs) or revs
1229 heads = revs and map(repo.lookup, revs) or revs
1235 outgoing = discovery.findcommonoutgoing(repo, other,
1230 outgoing = discovery.findcommonoutgoing(repo, other,
1236 onlyheads=heads,
1231 onlyheads=heads,
1237 force=opts.get('force'),
1232 force=opts.get('force'),
1238 portable=True)
1233 portable=True)
1239
1234
1240 if not outgoing.missing:
1235 if not outgoing.missing:
1241 scmutil.nochangesfound(ui, repo, not base and outgoing.excluded)
1236 scmutil.nochangesfound(ui, repo, not base and outgoing.excluded)
1242 return 1
1237 return 1
1243
1238
1244 if cgversion == '01': #bundle1
1239 if cgversion == '01': #bundle1
1245 if bcompression is None:
1240 if bcompression is None:
1246 bcompression = 'UN'
1241 bcompression = 'UN'
1247 bversion = 'HG10' + bcompression
1242 bversion = 'HG10' + bcompression
1248 bcompression = None
1243 bcompression = None
1249 elif cgversion in ('02', '03'):
1244 elif cgversion in ('02', '03'):
1250 bversion = 'HG20'
1245 bversion = 'HG20'
1251 else:
1246 else:
1252 raise error.ProgrammingError(
1247 raise error.ProgrammingError(
1253 'bundle: unexpected changegroup version %s' % cgversion)
1248 'bundle: unexpected changegroup version %s' % cgversion)
1254
1249
1255 # TODO compression options should be derived from bundlespec parsing.
1250 # TODO compression options should be derived from bundlespec parsing.
1256 # This is a temporary hack to allow adjusting bundle compression
1251 # This is a temporary hack to allow adjusting bundle compression
1257 # level without a) formalizing the bundlespec changes to declare it
1252 # level without a) formalizing the bundlespec changes to declare it
1258 # b) introducing a command flag.
1253 # b) introducing a command flag.
1259 compopts = {}
1254 compopts = {}
1260 complevel = ui.configint('experimental', 'bundlecomplevel')
1255 complevel = ui.configint('experimental', 'bundlecomplevel')
1261 if complevel is not None:
1256 if complevel is not None:
1262 compopts['level'] = complevel
1257 compopts['level'] = complevel
1263
1258
1264
1259
1265 contentopts = {'cg.version': cgversion}
1260 contentopts = {'cg.version': cgversion}
1266 if repo.ui.configbool('experimental', 'evolution.bundle-obsmarker'):
1261 if repo.ui.configbool('experimental', 'evolution.bundle-obsmarker'):
1267 contentopts['obsolescence'] = True
1262 contentopts['obsolescence'] = True
1268 if repo.ui.configbool('experimental', 'bundle-phases'):
1263 if repo.ui.configbool('experimental', 'bundle-phases'):
1269 contentopts['phases'] = True
1264 contentopts['phases'] = True
1270 bundle2.writenewbundle(ui, repo, 'bundle', fname, bversion, outgoing,
1265 bundle2.writenewbundle(ui, repo, 'bundle', fname, bversion, outgoing,
1271 contentopts, compression=bcompression,
1266 contentopts, compression=bcompression,
1272 compopts=compopts)
1267 compopts=compopts)
1273
1268
1274 @command('cat',
1269 @command('cat',
1275 [('o', 'output', '',
1270 [('o', 'output', '',
1276 _('print output to file with formatted name'), _('FORMAT')),
1271 _('print output to file with formatted name'), _('FORMAT')),
1277 ('r', 'rev', '', _('print the given revision'), _('REV')),
1272 ('r', 'rev', '', _('print the given revision'), _('REV')),
1278 ('', 'decode', None, _('apply any matching decode filter')),
1273 ('', 'decode', None, _('apply any matching decode filter')),
1279 ] + walkopts + formatteropts,
1274 ] + walkopts + formatteropts,
1280 _('[OPTION]... FILE...'),
1275 _('[OPTION]... FILE...'),
1281 inferrepo=True, cmdtype=readonly)
1276 inferrepo=True, cmdtype=readonly)
1282 def cat(ui, repo, file1, *pats, **opts):
1277 def cat(ui, repo, file1, *pats, **opts):
1283 """output the current or given revision of files
1278 """output the current or given revision of files
1284
1279
1285 Print the specified files as they were at the given revision. If
1280 Print the specified files as they were at the given revision. If
1286 no revision is given, the parent of the working directory is used.
1281 no revision is given, the parent of the working directory is used.
1287
1282
1288 Output may be to a file, in which case the name of the file is
1283 Output may be to a file, in which case the name of the file is
1289 given using a format string. The formatting rules as follows:
1284 given using a format string. The formatting rules as follows:
1290
1285
1291 :``%%``: literal "%" character
1286 :``%%``: literal "%" character
1292 :``%s``: basename of file being printed
1287 :``%s``: basename of file being printed
1293 :``%d``: dirname of file being printed, or '.' if in repository root
1288 :``%d``: dirname of file being printed, or '.' if in repository root
1294 :``%p``: root-relative path name of file being printed
1289 :``%p``: root-relative path name of file being printed
1295 :``%H``: changeset hash (40 hexadecimal digits)
1290 :``%H``: changeset hash (40 hexadecimal digits)
1296 :``%R``: changeset revision number
1291 :``%R``: changeset revision number
1297 :``%h``: short-form changeset hash (12 hexadecimal digits)
1292 :``%h``: short-form changeset hash (12 hexadecimal digits)
1298 :``%r``: zero-padded changeset revision number
1293 :``%r``: zero-padded changeset revision number
1299 :``%b``: basename of the exporting repository
1294 :``%b``: basename of the exporting repository
1300
1295
1301 Returns 0 on success.
1296 Returns 0 on success.
1302 """
1297 """
1303 opts = pycompat.byteskwargs(opts)
1298 opts = pycompat.byteskwargs(opts)
1304 rev = opts.get('rev')
1299 rev = opts.get('rev')
1305 if rev:
1300 if rev:
1306 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
1301 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
1307 ctx = scmutil.revsingle(repo, rev)
1302 ctx = scmutil.revsingle(repo, rev)
1308 m = scmutil.match(ctx, (file1,) + pats, opts)
1303 m = scmutil.match(ctx, (file1,) + pats, opts)
1309 fntemplate = opts.pop('output', '')
1304 fntemplate = opts.pop('output', '')
1310 if cmdutil.isstdiofilename(fntemplate):
1305 if cmdutil.isstdiofilename(fntemplate):
1311 fntemplate = ''
1306 fntemplate = ''
1312
1307
1313 if fntemplate:
1308 if fntemplate:
1314 fm = formatter.nullformatter(ui, 'cat')
1309 fm = formatter.nullformatter(ui, 'cat')
1315 else:
1310 else:
1316 ui.pager('cat')
1311 ui.pager('cat')
1317 fm = ui.formatter('cat', opts)
1312 fm = ui.formatter('cat', opts)
1318 with fm:
1313 with fm:
1319 return cmdutil.cat(ui, repo, ctx, m, fm, fntemplate, '',
1314 return cmdutil.cat(ui, repo, ctx, m, fm, fntemplate, '',
1320 **pycompat.strkwargs(opts))
1315 **pycompat.strkwargs(opts))
1321
1316
1322 @command('^clone',
1317 @command('^clone',
1323 [('U', 'noupdate', None, _('the clone will include an empty working '
1318 [('U', 'noupdate', None, _('the clone will include an empty working '
1324 'directory (only a repository)')),
1319 'directory (only a repository)')),
1325 ('u', 'updaterev', '', _('revision, tag, or branch to check out'),
1320 ('u', 'updaterev', '', _('revision, tag, or branch to check out'),
1326 _('REV')),
1321 _('REV')),
1327 ('r', 'rev', [], _('include the specified changeset'), _('REV')),
1322 ('r', 'rev', [], _('include the specified changeset'), _('REV')),
1328 ('b', 'branch', [], _('clone only the specified branch'), _('BRANCH')),
1323 ('b', 'branch', [], _('clone only the specified branch'), _('BRANCH')),
1329 ('', 'pull', None, _('use pull protocol to copy metadata')),
1324 ('', 'pull', None, _('use pull protocol to copy metadata')),
1330 ('', 'uncompressed', None,
1325 ('', 'uncompressed', None,
1331 _('an alias to --stream (DEPRECATED)')),
1326 _('an alias to --stream (DEPRECATED)')),
1332 ('', 'stream', None,
1327 ('', 'stream', None,
1333 _('clone with minimal data processing')),
1328 _('clone with minimal data processing')),
1334 ] + remoteopts,
1329 ] + remoteopts,
1335 _('[OPTION]... SOURCE [DEST]'),
1330 _('[OPTION]... SOURCE [DEST]'),
1336 norepo=True)
1331 norepo=True)
1337 def clone(ui, source, dest=None, **opts):
1332 def clone(ui, source, dest=None, **opts):
1338 """make a copy of an existing repository
1333 """make a copy of an existing repository
1339
1334
1340 Create a copy of an existing repository in a new directory.
1335 Create a copy of an existing repository in a new directory.
1341
1336
1342 If no destination directory name is specified, it defaults to the
1337 If no destination directory name is specified, it defaults to the
1343 basename of the source.
1338 basename of the source.
1344
1339
1345 The location of the source is added to the new repository's
1340 The location of the source is added to the new repository's
1346 ``.hg/hgrc`` file, as the default to be used for future pulls.
1341 ``.hg/hgrc`` file, as the default to be used for future pulls.
1347
1342
1348 Only local paths and ``ssh://`` URLs are supported as
1343 Only local paths and ``ssh://`` URLs are supported as
1349 destinations. For ``ssh://`` destinations, no working directory or
1344 destinations. For ``ssh://`` destinations, no working directory or
1350 ``.hg/hgrc`` will be created on the remote side.
1345 ``.hg/hgrc`` will be created on the remote side.
1351
1346
1352 If the source repository has a bookmark called '@' set, that
1347 If the source repository has a bookmark called '@' set, that
1353 revision will be checked out in the new repository by default.
1348 revision will be checked out in the new repository by default.
1354
1349
1355 To check out a particular version, use -u/--update, or
1350 To check out a particular version, use -u/--update, or
1356 -U/--noupdate to create a clone with no working directory.
1351 -U/--noupdate to create a clone with no working directory.
1357
1352
1358 To pull only a subset of changesets, specify one or more revisions
1353 To pull only a subset of changesets, specify one or more revisions
1359 identifiers with -r/--rev or branches with -b/--branch. The
1354 identifiers with -r/--rev or branches with -b/--branch. The
1360 resulting clone will contain only the specified changesets and
1355 resulting clone will contain only the specified changesets and
1361 their ancestors. These options (or 'clone src#rev dest') imply
1356 their ancestors. These options (or 'clone src#rev dest') imply
1362 --pull, even for local source repositories.
1357 --pull, even for local source repositories.
1363
1358
1364 In normal clone mode, the remote normalizes repository data into a common
1359 In normal clone mode, the remote normalizes repository data into a common
1365 exchange format and the receiving end translates this data into its local
1360 exchange format and the receiving end translates this data into its local
1366 storage format. --stream activates a different clone mode that essentially
1361 storage format. --stream activates a different clone mode that essentially
1367 copies repository files from the remote with minimal data processing. This
1362 copies repository files from the remote with minimal data processing. This
1368 significantly reduces the CPU cost of a clone both remotely and locally.
1363 significantly reduces the CPU cost of a clone both remotely and locally.
1369 However, it often increases the transferred data size by 30-40%. This can
1364 However, it often increases the transferred data size by 30-40%. This can
1370 result in substantially faster clones where I/O throughput is plentiful,
1365 result in substantially faster clones where I/O throughput is plentiful,
1371 especially for larger repositories. A side-effect of --stream clones is
1366 especially for larger repositories. A side-effect of --stream clones is
1372 that storage settings and requirements on the remote are applied locally:
1367 that storage settings and requirements on the remote are applied locally:
1373 a modern client may inherit legacy or inefficient storage used by the
1368 a modern client may inherit legacy or inefficient storage used by the
1374 remote or a legacy Mercurial client may not be able to clone from a
1369 remote or a legacy Mercurial client may not be able to clone from a
1375 modern Mercurial remote.
1370 modern Mercurial remote.
1376
1371
1377 .. note::
1372 .. note::
1378
1373
1379 Specifying a tag will include the tagged changeset but not the
1374 Specifying a tag will include the tagged changeset but not the
1380 changeset containing the tag.
1375 changeset containing the tag.
1381
1376
1382 .. container:: verbose
1377 .. container:: verbose
1383
1378
1384 For efficiency, hardlinks are used for cloning whenever the
1379 For efficiency, hardlinks are used for cloning whenever the
1385 source and destination are on the same filesystem (note this
1380 source and destination are on the same filesystem (note this
1386 applies only to the repository data, not to the working
1381 applies only to the repository data, not to the working
1387 directory). Some filesystems, such as AFS, implement hardlinking
1382 directory). Some filesystems, such as AFS, implement hardlinking
1388 incorrectly, but do not report errors. In these cases, use the
1383 incorrectly, but do not report errors. In these cases, use the
1389 --pull option to avoid hardlinking.
1384 --pull option to avoid hardlinking.
1390
1385
1391 Mercurial will update the working directory to the first applicable
1386 Mercurial will update the working directory to the first applicable
1392 revision from this list:
1387 revision from this list:
1393
1388
1394 a) null if -U or the source repository has no changesets
1389 a) null if -U or the source repository has no changesets
1395 b) if -u . and the source repository is local, the first parent of
1390 b) if -u . and the source repository is local, the first parent of
1396 the source repository's working directory
1391 the source repository's working directory
1397 c) the changeset specified with -u (if a branch name, this means the
1392 c) the changeset specified with -u (if a branch name, this means the
1398 latest head of that branch)
1393 latest head of that branch)
1399 d) the changeset specified with -r
1394 d) the changeset specified with -r
1400 e) the tipmost head specified with -b
1395 e) the tipmost head specified with -b
1401 f) the tipmost head specified with the url#branch source syntax
1396 f) the tipmost head specified with the url#branch source syntax
1402 g) the revision marked with the '@' bookmark, if present
1397 g) the revision marked with the '@' bookmark, if present
1403 h) the tipmost head of the default branch
1398 h) the tipmost head of the default branch
1404 i) tip
1399 i) tip
1405
1400
1406 When cloning from servers that support it, Mercurial may fetch
1401 When cloning from servers that support it, Mercurial may fetch
1407 pre-generated data from a server-advertised URL. When this is done,
1402 pre-generated data from a server-advertised URL. When this is done,
1408 hooks operating on incoming changesets and changegroups may fire twice,
1403 hooks operating on incoming changesets and changegroups may fire twice,
1409 once for the bundle fetched from the URL and another for any additional
1404 once for the bundle fetched from the URL and another for any additional
1410 data not fetched from this URL. In addition, if an error occurs, the
1405 data not fetched from this URL. In addition, if an error occurs, the
1411 repository may be rolled back to a partial clone. This behavior may
1406 repository may be rolled back to a partial clone. This behavior may
1412 change in future releases. See :hg:`help -e clonebundles` for more.
1407 change in future releases. See :hg:`help -e clonebundles` for more.
1413
1408
1414 Examples:
1409 Examples:
1415
1410
1416 - clone a remote repository to a new directory named hg/::
1411 - clone a remote repository to a new directory named hg/::
1417
1412
1418 hg clone https://www.mercurial-scm.org/repo/hg/
1413 hg clone https://www.mercurial-scm.org/repo/hg/
1419
1414
1420 - create a lightweight local clone::
1415 - create a lightweight local clone::
1421
1416
1422 hg clone project/ project-feature/
1417 hg clone project/ project-feature/
1423
1418
1424 - clone from an absolute path on an ssh server (note double-slash)::
1419 - clone from an absolute path on an ssh server (note double-slash)::
1425
1420
1426 hg clone ssh://user@server//home/projects/alpha/
1421 hg clone ssh://user@server//home/projects/alpha/
1427
1422
1428 - do a streaming clone while checking out a specified version::
1423 - do a streaming clone while checking out a specified version::
1429
1424
1430 hg clone --stream http://server/repo -u 1.5
1425 hg clone --stream http://server/repo -u 1.5
1431
1426
1432 - create a repository without changesets after a particular revision::
1427 - create a repository without changesets after a particular revision::
1433
1428
1434 hg clone -r 04e544 experimental/ good/
1429 hg clone -r 04e544 experimental/ good/
1435
1430
1436 - clone (and track) a particular named branch::
1431 - clone (and track) a particular named branch::
1437
1432
1438 hg clone https://www.mercurial-scm.org/repo/hg/#stable
1433 hg clone https://www.mercurial-scm.org/repo/hg/#stable
1439
1434
1440 See :hg:`help urls` for details on specifying URLs.
1435 See :hg:`help urls` for details on specifying URLs.
1441
1436
1442 Returns 0 on success.
1437 Returns 0 on success.
1443 """
1438 """
1444 opts = pycompat.byteskwargs(opts)
1439 opts = pycompat.byteskwargs(opts)
1445 if opts.get('noupdate') and opts.get('updaterev'):
1440 if opts.get('noupdate') and opts.get('updaterev'):
1446 raise error.Abort(_("cannot specify both --noupdate and --updaterev"))
1441 raise error.Abort(_("cannot specify both --noupdate and --updaterev"))
1447
1442
1448 r = hg.clone(ui, opts, source, dest,
1443 r = hg.clone(ui, opts, source, dest,
1449 pull=opts.get('pull'),
1444 pull=opts.get('pull'),
1450 stream=opts.get('stream') or opts.get('uncompressed'),
1445 stream=opts.get('stream') or opts.get('uncompressed'),
1451 rev=opts.get('rev'),
1446 rev=opts.get('rev'),
1452 update=opts.get('updaterev') or not opts.get('noupdate'),
1447 update=opts.get('updaterev') or not opts.get('noupdate'),
1453 branch=opts.get('branch'),
1448 branch=opts.get('branch'),
1454 shareopts=opts.get('shareopts'))
1449 shareopts=opts.get('shareopts'))
1455
1450
1456 return r is None
1451 return r is None
1457
1452
1458 @command('^commit|ci',
1453 @command('^commit|ci',
1459 [('A', 'addremove', None,
1454 [('A', 'addremove', None,
1460 _('mark new/missing files as added/removed before committing')),
1455 _('mark new/missing files as added/removed before committing')),
1461 ('', 'close-branch', None,
1456 ('', 'close-branch', None,
1462 _('mark a branch head as closed')),
1457 _('mark a branch head as closed')),
1463 ('', 'amend', None, _('amend the parent of the working directory')),
1458 ('', 'amend', None, _('amend the parent of the working directory')),
1464 ('s', 'secret', None, _('use the secret phase for committing')),
1459 ('s', 'secret', None, _('use the secret phase for committing')),
1465 ('e', 'edit', None, _('invoke editor on commit messages')),
1460 ('e', 'edit', None, _('invoke editor on commit messages')),
1466 ('i', 'interactive', None, _('use interactive mode')),
1461 ('i', 'interactive', None, _('use interactive mode')),
1467 ] + walkopts + commitopts + commitopts2 + subrepoopts,
1462 ] + walkopts + commitopts + commitopts2 + subrepoopts,
1468 _('[OPTION]... [FILE]...'),
1463 _('[OPTION]... [FILE]...'),
1469 inferrepo=True)
1464 inferrepo=True)
1470 def commit(ui, repo, *pats, **opts):
1465 def commit(ui, repo, *pats, **opts):
1471 """commit the specified files or all outstanding changes
1466 """commit the specified files or all outstanding changes
1472
1467
1473 Commit changes to the given files into the repository. Unlike a
1468 Commit changes to the given files into the repository. Unlike a
1474 centralized SCM, this operation is a local operation. See
1469 centralized SCM, this operation is a local operation. See
1475 :hg:`push` for a way to actively distribute your changes.
1470 :hg:`push` for a way to actively distribute your changes.
1476
1471
1477 If a list of files is omitted, all changes reported by :hg:`status`
1472 If a list of files is omitted, all changes reported by :hg:`status`
1478 will be committed.
1473 will be committed.
1479
1474
1480 If you are committing the result of a merge, do not provide any
1475 If you are committing the result of a merge, do not provide any
1481 filenames or -I/-X filters.
1476 filenames or -I/-X filters.
1482
1477
1483 If no commit message is specified, Mercurial starts your
1478 If no commit message is specified, Mercurial starts your
1484 configured editor where you can enter a message. In case your
1479 configured editor where you can enter a message. In case your
1485 commit fails, you will find a backup of your message in
1480 commit fails, you will find a backup of your message in
1486 ``.hg/last-message.txt``.
1481 ``.hg/last-message.txt``.
1487
1482
1488 The --close-branch flag can be used to mark the current branch
1483 The --close-branch flag can be used to mark the current branch
1489 head closed. When all heads of a branch are closed, the branch
1484 head closed. When all heads of a branch are closed, the branch
1490 will be considered closed and no longer listed.
1485 will be considered closed and no longer listed.
1491
1486
1492 The --amend flag can be used to amend the parent of the
1487 The --amend flag can be used to amend the parent of the
1493 working directory with a new commit that contains the changes
1488 working directory with a new commit that contains the changes
1494 in the parent in addition to those currently reported by :hg:`status`,
1489 in the parent in addition to those currently reported by :hg:`status`,
1495 if there are any. The old commit is stored in a backup bundle in
1490 if there are any. The old commit is stored in a backup bundle in
1496 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
1491 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
1497 on how to restore it).
1492 on how to restore it).
1498
1493
1499 Message, user and date are taken from the amended commit unless
1494 Message, user and date are taken from the amended commit unless
1500 specified. When a message isn't specified on the command line,
1495 specified. When a message isn't specified on the command line,
1501 the editor will open with the message of the amended commit.
1496 the editor will open with the message of the amended commit.
1502
1497
1503 It is not possible to amend public changesets (see :hg:`help phases`)
1498 It is not possible to amend public changesets (see :hg:`help phases`)
1504 or changesets that have children.
1499 or changesets that have children.
1505
1500
1506 See :hg:`help dates` for a list of formats valid for -d/--date.
1501 See :hg:`help dates` for a list of formats valid for -d/--date.
1507
1502
1508 Returns 0 on success, 1 if nothing changed.
1503 Returns 0 on success, 1 if nothing changed.
1509
1504
1510 .. container:: verbose
1505 .. container:: verbose
1511
1506
1512 Examples:
1507 Examples:
1513
1508
1514 - commit all files ending in .py::
1509 - commit all files ending in .py::
1515
1510
1516 hg commit --include "set:**.py"
1511 hg commit --include "set:**.py"
1517
1512
1518 - commit all non-binary files::
1513 - commit all non-binary files::
1519
1514
1520 hg commit --exclude "set:binary()"
1515 hg commit --exclude "set:binary()"
1521
1516
1522 - amend the current commit and set the date to now::
1517 - amend the current commit and set the date to now::
1523
1518
1524 hg commit --amend --date now
1519 hg commit --amend --date now
1525 """
1520 """
1526 wlock = lock = None
1521 wlock = lock = None
1527 try:
1522 try:
1528 wlock = repo.wlock()
1523 wlock = repo.wlock()
1529 lock = repo.lock()
1524 lock = repo.lock()
1530 return _docommit(ui, repo, *pats, **opts)
1525 return _docommit(ui, repo, *pats, **opts)
1531 finally:
1526 finally:
1532 release(lock, wlock)
1527 release(lock, wlock)
1533
1528
1534 def _docommit(ui, repo, *pats, **opts):
1529 def _docommit(ui, repo, *pats, **opts):
1535 if opts.get(r'interactive'):
1530 if opts.get(r'interactive'):
1536 opts.pop(r'interactive')
1531 opts.pop(r'interactive')
1537 ret = cmdutil.dorecord(ui, repo, commit, None, False,
1532 ret = cmdutil.dorecord(ui, repo, commit, None, False,
1538 cmdutil.recordfilter, *pats,
1533 cmdutil.recordfilter, *pats,
1539 **opts)
1534 **opts)
1540 # ret can be 0 (no changes to record) or the value returned by
1535 # ret can be 0 (no changes to record) or the value returned by
1541 # commit(), 1 if nothing changed or None on success.
1536 # commit(), 1 if nothing changed or None on success.
1542 return 1 if ret == 0 else ret
1537 return 1 if ret == 0 else ret
1543
1538
1544 opts = pycompat.byteskwargs(opts)
1539 opts = pycompat.byteskwargs(opts)
1545 if opts.get('subrepos'):
1540 if opts.get('subrepos'):
1546 if opts.get('amend'):
1541 if opts.get('amend'):
1547 raise error.Abort(_('cannot amend with --subrepos'))
1542 raise error.Abort(_('cannot amend with --subrepos'))
1548 # Let --subrepos on the command line override config setting.
1543 # Let --subrepos on the command line override config setting.
1549 ui.setconfig('ui', 'commitsubrepos', True, 'commit')
1544 ui.setconfig('ui', 'commitsubrepos', True, 'commit')
1550
1545
1551 cmdutil.checkunfinished(repo, commit=True)
1546 cmdutil.checkunfinished(repo, commit=True)
1552
1547
1553 branch = repo[None].branch()
1548 branch = repo[None].branch()
1554 bheads = repo.branchheads(branch)
1549 bheads = repo.branchheads(branch)
1555
1550
1556 extra = {}
1551 extra = {}
1557 if opts.get('close_branch'):
1552 if opts.get('close_branch'):
1558 extra['close'] = 1
1553 extra['close'] = 1
1559
1554
1560 if not bheads:
1555 if not bheads:
1561 raise error.Abort(_('can only close branch heads'))
1556 raise error.Abort(_('can only close branch heads'))
1562 elif opts.get('amend'):
1557 elif opts.get('amend'):
1563 if repo[None].parents()[0].p1().branch() != branch and \
1558 if repo[None].parents()[0].p1().branch() != branch and \
1564 repo[None].parents()[0].p2().branch() != branch:
1559 repo[None].parents()[0].p2().branch() != branch:
1565 raise error.Abort(_('can only close branch heads'))
1560 raise error.Abort(_('can only close branch heads'))
1566
1561
1567 if opts.get('amend'):
1562 if opts.get('amend'):
1568 if ui.configbool('ui', 'commitsubrepos'):
1563 if ui.configbool('ui', 'commitsubrepos'):
1569 raise error.Abort(_('cannot amend with ui.commitsubrepos enabled'))
1564 raise error.Abort(_('cannot amend with ui.commitsubrepos enabled'))
1570
1565
1571 old = repo['.']
1566 old = repo['.']
1572 rewriteutil.precheck(repo, [old.rev()], 'amend')
1567 rewriteutil.precheck(repo, [old.rev()], 'amend')
1573
1568
1574 # Currently histedit gets confused if an amend happens while histedit
1569 # Currently histedit gets confused if an amend happens while histedit
1575 # is in progress. Since we have a checkunfinished command, we are
1570 # is in progress. Since we have a checkunfinished command, we are
1576 # temporarily honoring it.
1571 # temporarily honoring it.
1577 #
1572 #
1578 # Note: eventually this guard will be removed. Please do not expect
1573 # Note: eventually this guard will be removed. Please do not expect
1579 # this behavior to remain.
1574 # this behavior to remain.
1580 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
1575 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
1581 cmdutil.checkunfinished(repo)
1576 cmdutil.checkunfinished(repo)
1582
1577
1583 node = cmdutil.amend(ui, repo, old, extra, pats, opts)
1578 node = cmdutil.amend(ui, repo, old, extra, pats, opts)
1584 if node == old.node():
1579 if node == old.node():
1585 ui.status(_("nothing changed\n"))
1580 ui.status(_("nothing changed\n"))
1586 return 1
1581 return 1
1587 else:
1582 else:
1588 def commitfunc(ui, repo, message, match, opts):
1583 def commitfunc(ui, repo, message, match, opts):
1589 overrides = {}
1584 overrides = {}
1590 if opts.get('secret'):
1585 if opts.get('secret'):
1591 overrides[('phases', 'new-commit')] = 'secret'
1586 overrides[('phases', 'new-commit')] = 'secret'
1592
1587
1593 baseui = repo.baseui
1588 baseui = repo.baseui
1594 with baseui.configoverride(overrides, 'commit'):
1589 with baseui.configoverride(overrides, 'commit'):
1595 with ui.configoverride(overrides, 'commit'):
1590 with ui.configoverride(overrides, 'commit'):
1596 editform = cmdutil.mergeeditform(repo[None],
1591 editform = cmdutil.mergeeditform(repo[None],
1597 'commit.normal')
1592 'commit.normal')
1598 editor = cmdutil.getcommiteditor(
1593 editor = cmdutil.getcommiteditor(
1599 editform=editform, **pycompat.strkwargs(opts))
1594 editform=editform, **pycompat.strkwargs(opts))
1600 return repo.commit(message,
1595 return repo.commit(message,
1601 opts.get('user'),
1596 opts.get('user'),
1602 opts.get('date'),
1597 opts.get('date'),
1603 match,
1598 match,
1604 editor=editor,
1599 editor=editor,
1605 extra=extra)
1600 extra=extra)
1606
1601
1607 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1602 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1608
1603
1609 if not node:
1604 if not node:
1610 stat = cmdutil.postcommitstatus(repo, pats, opts)
1605 stat = cmdutil.postcommitstatus(repo, pats, opts)
1611 if stat[3]:
1606 if stat[3]:
1612 ui.status(_("nothing changed (%d missing files, see "
1607 ui.status(_("nothing changed (%d missing files, see "
1613 "'hg status')\n") % len(stat[3]))
1608 "'hg status')\n") % len(stat[3]))
1614 else:
1609 else:
1615 ui.status(_("nothing changed\n"))
1610 ui.status(_("nothing changed\n"))
1616 return 1
1611 return 1
1617
1612
1618 cmdutil.commitstatus(repo, node, branch, bheads, opts)
1613 cmdutil.commitstatus(repo, node, branch, bheads, opts)
1619
1614
1620 @command('config|showconfig|debugconfig',
1615 @command('config|showconfig|debugconfig',
1621 [('u', 'untrusted', None, _('show untrusted configuration options')),
1616 [('u', 'untrusted', None, _('show untrusted configuration options')),
1622 ('e', 'edit', None, _('edit user config')),
1617 ('e', 'edit', None, _('edit user config')),
1623 ('l', 'local', None, _('edit repository config')),
1618 ('l', 'local', None, _('edit repository config')),
1624 ('g', 'global', None, _('edit global config'))] + formatteropts,
1619 ('g', 'global', None, _('edit global config'))] + formatteropts,
1625 _('[-u] [NAME]...'),
1620 _('[-u] [NAME]...'),
1626 optionalrepo=True, cmdtype=readonly)
1621 optionalrepo=True, cmdtype=readonly)
1627 def config(ui, repo, *values, **opts):
1622 def config(ui, repo, *values, **opts):
1628 """show combined config settings from all hgrc files
1623 """show combined config settings from all hgrc files
1629
1624
1630 With no arguments, print names and values of all config items.
1625 With no arguments, print names and values of all config items.
1631
1626
1632 With one argument of the form section.name, print just the value
1627 With one argument of the form section.name, print just the value
1633 of that config item.
1628 of that config item.
1634
1629
1635 With multiple arguments, print names and values of all config
1630 With multiple arguments, print names and values of all config
1636 items with matching section names.
1631 items with matching section names.
1637
1632
1638 With --edit, start an editor on the user-level config file. With
1633 With --edit, start an editor on the user-level config file. With
1639 --global, edit the system-wide config file. With --local, edit the
1634 --global, edit the system-wide config file. With --local, edit the
1640 repository-level config file.
1635 repository-level config file.
1641
1636
1642 With --debug, the source (filename and line number) is printed
1637 With --debug, the source (filename and line number) is printed
1643 for each config item.
1638 for each config item.
1644
1639
1645 See :hg:`help config` for more information about config files.
1640 See :hg:`help config` for more information about config files.
1646
1641
1647 Returns 0 on success, 1 if NAME does not exist.
1642 Returns 0 on success, 1 if NAME does not exist.
1648
1643
1649 """
1644 """
1650
1645
1651 opts = pycompat.byteskwargs(opts)
1646 opts = pycompat.byteskwargs(opts)
1652 if opts.get('edit') or opts.get('local') or opts.get('global'):
1647 if opts.get('edit') or opts.get('local') or opts.get('global'):
1653 if opts.get('local') and opts.get('global'):
1648 if opts.get('local') and opts.get('global'):
1654 raise error.Abort(_("can't use --local and --global together"))
1649 raise error.Abort(_("can't use --local and --global together"))
1655
1650
1656 if opts.get('local'):
1651 if opts.get('local'):
1657 if not repo:
1652 if not repo:
1658 raise error.Abort(_("can't use --local outside a repository"))
1653 raise error.Abort(_("can't use --local outside a repository"))
1659 paths = [repo.vfs.join('hgrc')]
1654 paths = [repo.vfs.join('hgrc')]
1660 elif opts.get('global'):
1655 elif opts.get('global'):
1661 paths = rcutil.systemrcpath()
1656 paths = rcutil.systemrcpath()
1662 else:
1657 else:
1663 paths = rcutil.userrcpath()
1658 paths = rcutil.userrcpath()
1664
1659
1665 for f in paths:
1660 for f in paths:
1666 if os.path.exists(f):
1661 if os.path.exists(f):
1667 break
1662 break
1668 else:
1663 else:
1669 if opts.get('global'):
1664 if opts.get('global'):
1670 samplehgrc = uimod.samplehgrcs['global']
1665 samplehgrc = uimod.samplehgrcs['global']
1671 elif opts.get('local'):
1666 elif opts.get('local'):
1672 samplehgrc = uimod.samplehgrcs['local']
1667 samplehgrc = uimod.samplehgrcs['local']
1673 else:
1668 else:
1674 samplehgrc = uimod.samplehgrcs['user']
1669 samplehgrc = uimod.samplehgrcs['user']
1675
1670
1676 f = paths[0]
1671 f = paths[0]
1677 fp = open(f, "wb")
1672 fp = open(f, "wb")
1678 fp.write(util.tonativeeol(samplehgrc))
1673 fp.write(util.tonativeeol(samplehgrc))
1679 fp.close()
1674 fp.close()
1680
1675
1681 editor = ui.geteditor()
1676 editor = ui.geteditor()
1682 ui.system("%s \"%s\"" % (editor, f),
1677 ui.system("%s \"%s\"" % (editor, f),
1683 onerr=error.Abort, errprefix=_("edit failed"),
1678 onerr=error.Abort, errprefix=_("edit failed"),
1684 blockedtag='config_edit')
1679 blockedtag='config_edit')
1685 return
1680 return
1686 ui.pager('config')
1681 ui.pager('config')
1687 fm = ui.formatter('config', opts)
1682 fm = ui.formatter('config', opts)
1688 for t, f in rcutil.rccomponents():
1683 for t, f in rcutil.rccomponents():
1689 if t == 'path':
1684 if t == 'path':
1690 ui.debug('read config from: %s\n' % f)
1685 ui.debug('read config from: %s\n' % f)
1691 elif t == 'items':
1686 elif t == 'items':
1692 for section, name, value, source in f:
1687 for section, name, value, source in f:
1693 ui.debug('set config by: %s\n' % source)
1688 ui.debug('set config by: %s\n' % source)
1694 else:
1689 else:
1695 raise error.ProgrammingError('unknown rctype: %s' % t)
1690 raise error.ProgrammingError('unknown rctype: %s' % t)
1696 untrusted = bool(opts.get('untrusted'))
1691 untrusted = bool(opts.get('untrusted'))
1697 if values:
1692 if values:
1698 sections = [v for v in values if '.' not in v]
1693 sections = [v for v in values if '.' not in v]
1699 items = [v for v in values if '.' in v]
1694 items = [v for v in values if '.' in v]
1700 if len(items) > 1 or items and sections:
1695 if len(items) > 1 or items and sections:
1701 raise error.Abort(_('only one config item permitted'))
1696 raise error.Abort(_('only one config item permitted'))
1702 matched = False
1697 matched = False
1703 for section, name, value in ui.walkconfig(untrusted=untrusted):
1698 for section, name, value in ui.walkconfig(untrusted=untrusted):
1704 source = ui.configsource(section, name, untrusted)
1699 source = ui.configsource(section, name, untrusted)
1705 value = pycompat.bytestr(value)
1700 value = pycompat.bytestr(value)
1706 if fm.isplain():
1701 if fm.isplain():
1707 source = source or 'none'
1702 source = source or 'none'
1708 value = value.replace('\n', '\\n')
1703 value = value.replace('\n', '\\n')
1709 entryname = section + '.' + name
1704 entryname = section + '.' + name
1710 if values:
1705 if values:
1711 for v in values:
1706 for v in values:
1712 if v == section:
1707 if v == section:
1713 fm.startitem()
1708 fm.startitem()
1714 fm.condwrite(ui.debugflag, 'source', '%s: ', source)
1709 fm.condwrite(ui.debugflag, 'source', '%s: ', source)
1715 fm.write('name value', '%s=%s\n', entryname, value)
1710 fm.write('name value', '%s=%s\n', entryname, value)
1716 matched = True
1711 matched = True
1717 elif v == entryname:
1712 elif v == entryname:
1718 fm.startitem()
1713 fm.startitem()
1719 fm.condwrite(ui.debugflag, 'source', '%s: ', source)
1714 fm.condwrite(ui.debugflag, 'source', '%s: ', source)
1720 fm.write('value', '%s\n', value)
1715 fm.write('value', '%s\n', value)
1721 fm.data(name=entryname)
1716 fm.data(name=entryname)
1722 matched = True
1717 matched = True
1723 else:
1718 else:
1724 fm.startitem()
1719 fm.startitem()
1725 fm.condwrite(ui.debugflag, 'source', '%s: ', source)
1720 fm.condwrite(ui.debugflag, 'source', '%s: ', source)
1726 fm.write('name value', '%s=%s\n', entryname, value)
1721 fm.write('name value', '%s=%s\n', entryname, value)
1727 matched = True
1722 matched = True
1728 fm.end()
1723 fm.end()
1729 if matched:
1724 if matched:
1730 return 0
1725 return 0
1731 return 1
1726 return 1
1732
1727
1733 @command('copy|cp',
1728 @command('copy|cp',
1734 [('A', 'after', None, _('record a copy that has already occurred')),
1729 [('A', 'after', None, _('record a copy that has already occurred')),
1735 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1730 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1736 ] + walkopts + dryrunopts,
1731 ] + walkopts + dryrunopts,
1737 _('[OPTION]... [SOURCE]... DEST'))
1732 _('[OPTION]... [SOURCE]... DEST'))
1738 def copy(ui, repo, *pats, **opts):
1733 def copy(ui, repo, *pats, **opts):
1739 """mark files as copied for the next commit
1734 """mark files as copied for the next commit
1740
1735
1741 Mark dest as having copies of source files. If dest is a
1736 Mark dest as having copies of source files. If dest is a
1742 directory, copies are put in that directory. If dest is a file,
1737 directory, copies are put in that directory. If dest is a file,
1743 the source must be a single file.
1738 the source must be a single file.
1744
1739
1745 By default, this command copies the contents of files as they
1740 By default, this command copies the contents of files as they
1746 exist in the working directory. If invoked with -A/--after, the
1741 exist in the working directory. If invoked with -A/--after, the
1747 operation is recorded, but no copying is performed.
1742 operation is recorded, but no copying is performed.
1748
1743
1749 This command takes effect with the next commit. To undo a copy
1744 This command takes effect with the next commit. To undo a copy
1750 before that, see :hg:`revert`.
1745 before that, see :hg:`revert`.
1751
1746
1752 Returns 0 on success, 1 if errors are encountered.
1747 Returns 0 on success, 1 if errors are encountered.
1753 """
1748 """
1754 opts = pycompat.byteskwargs(opts)
1749 opts = pycompat.byteskwargs(opts)
1755 with repo.wlock(False):
1750 with repo.wlock(False):
1756 return cmdutil.copy(ui, repo, pats, opts)
1751 return cmdutil.copy(ui, repo, pats, opts)
1757
1752
1758 @command('debugcommands', [], _('[COMMAND]'), norepo=True)
1753 @command('debugcommands', [], _('[COMMAND]'), norepo=True)
1759 def debugcommands(ui, cmd='', *args):
1754 def debugcommands(ui, cmd='', *args):
1760 """list all available commands and options"""
1755 """list all available commands and options"""
1761 for cmd, vals in sorted(table.iteritems()):
1756 for cmd, vals in sorted(table.iteritems()):
1762 cmd = cmd.split('|')[0].strip('^')
1757 cmd = cmd.split('|')[0].strip('^')
1763 opts = ', '.join([i[1] for i in vals[1]])
1758 opts = ', '.join([i[1] for i in vals[1]])
1764 ui.write('%s: %s\n' % (cmd, opts))
1759 ui.write('%s: %s\n' % (cmd, opts))
1765
1760
1766 @command('debugcomplete',
1761 @command('debugcomplete',
1767 [('o', 'options', None, _('show the command options'))],
1762 [('o', 'options', None, _('show the command options'))],
1768 _('[-o] CMD'),
1763 _('[-o] CMD'),
1769 norepo=True)
1764 norepo=True)
1770 def debugcomplete(ui, cmd='', **opts):
1765 def debugcomplete(ui, cmd='', **opts):
1771 """returns the completion list associated with the given command"""
1766 """returns the completion list associated with the given command"""
1772
1767
1773 if opts.get(r'options'):
1768 if opts.get(r'options'):
1774 options = []
1769 options = []
1775 otables = [globalopts]
1770 otables = [globalopts]
1776 if cmd:
1771 if cmd:
1777 aliases, entry = cmdutil.findcmd(cmd, table, False)
1772 aliases, entry = cmdutil.findcmd(cmd, table, False)
1778 otables.append(entry[1])
1773 otables.append(entry[1])
1779 for t in otables:
1774 for t in otables:
1780 for o in t:
1775 for o in t:
1781 if "(DEPRECATED)" in o[3]:
1776 if "(DEPRECATED)" in o[3]:
1782 continue
1777 continue
1783 if o[0]:
1778 if o[0]:
1784 options.append('-%s' % o[0])
1779 options.append('-%s' % o[0])
1785 options.append('--%s' % o[1])
1780 options.append('--%s' % o[1])
1786 ui.write("%s\n" % "\n".join(options))
1781 ui.write("%s\n" % "\n".join(options))
1787 return
1782 return
1788
1783
1789 cmdlist, unused_allcmds = cmdutil.findpossible(cmd, table)
1784 cmdlist, unused_allcmds = cmdutil.findpossible(cmd, table)
1790 if ui.verbose:
1785 if ui.verbose:
1791 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
1786 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
1792 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
1787 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
1793
1788
1794 @command('^diff',
1789 @command('^diff',
1795 [('r', 'rev', [], _('revision'), _('REV')),
1790 [('r', 'rev', [], _('revision'), _('REV')),
1796 ('c', 'change', '', _('change made by revision'), _('REV'))
1791 ('c', 'change', '', _('change made by revision'), _('REV'))
1797 ] + diffopts + diffopts2 + walkopts + subrepoopts,
1792 ] + diffopts + diffopts2 + walkopts + subrepoopts,
1798 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'),
1793 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'),
1799 inferrepo=True, cmdtype=readonly)
1794 inferrepo=True, cmdtype=readonly)
1800 def diff(ui, repo, *pats, **opts):
1795 def diff(ui, repo, *pats, **opts):
1801 """diff repository (or selected files)
1796 """diff repository (or selected files)
1802
1797
1803 Show differences between revisions for the specified files.
1798 Show differences between revisions for the specified files.
1804
1799
1805 Differences between files are shown using the unified diff format.
1800 Differences between files are shown using the unified diff format.
1806
1801
1807 .. note::
1802 .. note::
1808
1803
1809 :hg:`diff` may generate unexpected results for merges, as it will
1804 :hg:`diff` may generate unexpected results for merges, as it will
1810 default to comparing against the working directory's first
1805 default to comparing against the working directory's first
1811 parent changeset if no revisions are specified.
1806 parent changeset if no revisions are specified.
1812
1807
1813 When two revision arguments are given, then changes are shown
1808 When two revision arguments are given, then changes are shown
1814 between those revisions. If only one revision is specified then
1809 between those revisions. If only one revision is specified then
1815 that revision is compared to the working directory, and, when no
1810 that revision is compared to the working directory, and, when no
1816 revisions are specified, the working directory files are compared
1811 revisions are specified, the working directory files are compared
1817 to its first parent.
1812 to its first parent.
1818
1813
1819 Alternatively you can specify -c/--change with a revision to see
1814 Alternatively you can specify -c/--change with a revision to see
1820 the changes in that changeset relative to its first parent.
1815 the changes in that changeset relative to its first parent.
1821
1816
1822 Without the -a/--text option, diff will avoid generating diffs of
1817 Without the -a/--text option, diff will avoid generating diffs of
1823 files it detects as binary. With -a, diff will generate a diff
1818 files it detects as binary. With -a, diff will generate a diff
1824 anyway, probably with undesirable results.
1819 anyway, probably with undesirable results.
1825
1820
1826 Use the -g/--git option to generate diffs in the git extended diff
1821 Use the -g/--git option to generate diffs in the git extended diff
1827 format. For more information, read :hg:`help diffs`.
1822 format. For more information, read :hg:`help diffs`.
1828
1823
1829 .. container:: verbose
1824 .. container:: verbose
1830
1825
1831 Examples:
1826 Examples:
1832
1827
1833 - compare a file in the current working directory to its parent::
1828 - compare a file in the current working directory to its parent::
1834
1829
1835 hg diff foo.c
1830 hg diff foo.c
1836
1831
1837 - compare two historical versions of a directory, with rename info::
1832 - compare two historical versions of a directory, with rename info::
1838
1833
1839 hg diff --git -r 1.0:1.2 lib/
1834 hg diff --git -r 1.0:1.2 lib/
1840
1835
1841 - get change stats relative to the last change on some date::
1836 - get change stats relative to the last change on some date::
1842
1837
1843 hg diff --stat -r "date('may 2')"
1838 hg diff --stat -r "date('may 2')"
1844
1839
1845 - diff all newly-added files that contain a keyword::
1840 - diff all newly-added files that contain a keyword::
1846
1841
1847 hg diff "set:added() and grep(GNU)"
1842 hg diff "set:added() and grep(GNU)"
1848
1843
1849 - compare a revision and its parents::
1844 - compare a revision and its parents::
1850
1845
1851 hg diff -c 9353 # compare against first parent
1846 hg diff -c 9353 # compare against first parent
1852 hg diff -r 9353^:9353 # same using revset syntax
1847 hg diff -r 9353^:9353 # same using revset syntax
1853 hg diff -r 9353^2:9353 # compare against the second parent
1848 hg diff -r 9353^2:9353 # compare against the second parent
1854
1849
1855 Returns 0 on success.
1850 Returns 0 on success.
1856 """
1851 """
1857
1852
1858 opts = pycompat.byteskwargs(opts)
1853 opts = pycompat.byteskwargs(opts)
1859 revs = opts.get('rev')
1854 revs = opts.get('rev')
1860 change = opts.get('change')
1855 change = opts.get('change')
1861 stat = opts.get('stat')
1856 stat = opts.get('stat')
1862 reverse = opts.get('reverse')
1857 reverse = opts.get('reverse')
1863
1858
1864 if revs and change:
1859 if revs and change:
1865 msg = _('cannot specify --rev and --change at the same time')
1860 msg = _('cannot specify --rev and --change at the same time')
1866 raise error.Abort(msg)
1861 raise error.Abort(msg)
1867 elif change:
1862 elif change:
1868 repo = scmutil.unhidehashlikerevs(repo, [change], 'nowarn')
1863 repo = scmutil.unhidehashlikerevs(repo, [change], 'nowarn')
1869 node2 = scmutil.revsingle(repo, change, None).node()
1864 node2 = scmutil.revsingle(repo, change, None).node()
1870 node1 = repo[node2].p1().node()
1865 node1 = repo[node2].p1().node()
1871 else:
1866 else:
1872 repo = scmutil.unhidehashlikerevs(repo, revs, 'nowarn')
1867 repo = scmutil.unhidehashlikerevs(repo, revs, 'nowarn')
1873 node1, node2 = scmutil.revpair(repo, revs)
1868 node1, node2 = scmutil.revpair(repo, revs)
1874
1869
1875 if reverse:
1870 if reverse:
1876 node1, node2 = node2, node1
1871 node1, node2 = node2, node1
1877
1872
1878 diffopts = patch.diffallopts(ui, opts)
1873 diffopts = patch.diffallopts(ui, opts)
1879 m = scmutil.match(repo[node2], pats, opts)
1874 m = scmutil.match(repo[node2], pats, opts)
1880 ui.pager('diff')
1875 ui.pager('diff')
1881 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
1876 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
1882 listsubrepos=opts.get('subrepos'),
1877 listsubrepos=opts.get('subrepos'),
1883 root=opts.get('root'))
1878 root=opts.get('root'))
1884
1879
1885 @command('^export',
1880 @command('^export',
1886 [('o', 'output', '',
1881 [('o', 'output', '',
1887 _('print output to file with formatted name'), _('FORMAT')),
1882 _('print output to file with formatted name'), _('FORMAT')),
1888 ('', 'switch-parent', None, _('diff against the second parent')),
1883 ('', 'switch-parent', None, _('diff against the second parent')),
1889 ('r', 'rev', [], _('revisions to export'), _('REV')),
1884 ('r', 'rev', [], _('revisions to export'), _('REV')),
1890 ] + diffopts,
1885 ] + diffopts,
1891 _('[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'), cmdtype=readonly)
1886 _('[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'), cmdtype=readonly)
1892 def export(ui, repo, *changesets, **opts):
1887 def export(ui, repo, *changesets, **opts):
1893 """dump the header and diffs for one or more changesets
1888 """dump the header and diffs for one or more changesets
1894
1889
1895 Print the changeset header and diffs for one or more revisions.
1890 Print the changeset header and diffs for one or more revisions.
1896 If no revision is given, the parent of the working directory is used.
1891 If no revision is given, the parent of the working directory is used.
1897
1892
1898 The information shown in the changeset header is: author, date,
1893 The information shown in the changeset header is: author, date,
1899 branch name (if non-default), changeset hash, parent(s) and commit
1894 branch name (if non-default), changeset hash, parent(s) and commit
1900 comment.
1895 comment.
1901
1896
1902 .. note::
1897 .. note::
1903
1898
1904 :hg:`export` may generate unexpected diff output for merge
1899 :hg:`export` may generate unexpected diff output for merge
1905 changesets, as it will compare the merge changeset against its
1900 changesets, as it will compare the merge changeset against its
1906 first parent only.
1901 first parent only.
1907
1902
1908 Output may be to a file, in which case the name of the file is
1903 Output may be to a file, in which case the name of the file is
1909 given using a format string. The formatting rules are as follows:
1904 given using a format string. The formatting rules are as follows:
1910
1905
1911 :``%%``: literal "%" character
1906 :``%%``: literal "%" character
1912 :``%H``: changeset hash (40 hexadecimal digits)
1907 :``%H``: changeset hash (40 hexadecimal digits)
1913 :``%N``: number of patches being generated
1908 :``%N``: number of patches being generated
1914 :``%R``: changeset revision number
1909 :``%R``: changeset revision number
1915 :``%b``: basename of the exporting repository
1910 :``%b``: basename of the exporting repository
1916 :``%h``: short-form changeset hash (12 hexadecimal digits)
1911 :``%h``: short-form changeset hash (12 hexadecimal digits)
1917 :``%m``: first line of the commit message (only alphanumeric characters)
1912 :``%m``: first line of the commit message (only alphanumeric characters)
1918 :``%n``: zero-padded sequence number, starting at 1
1913 :``%n``: zero-padded sequence number, starting at 1
1919 :``%r``: zero-padded changeset revision number
1914 :``%r``: zero-padded changeset revision number
1920
1915
1921 Without the -a/--text option, export will avoid generating diffs
1916 Without the -a/--text option, export will avoid generating diffs
1922 of files it detects as binary. With -a, export will generate a
1917 of files it detects as binary. With -a, export will generate a
1923 diff anyway, probably with undesirable results.
1918 diff anyway, probably with undesirable results.
1924
1919
1925 Use the -g/--git option to generate diffs in the git extended diff
1920 Use the -g/--git option to generate diffs in the git extended diff
1926 format. See :hg:`help diffs` for more information.
1921 format. See :hg:`help diffs` for more information.
1927
1922
1928 With the --switch-parent option, the diff will be against the
1923 With the --switch-parent option, the diff will be against the
1929 second parent. It can be useful to review a merge.
1924 second parent. It can be useful to review a merge.
1930
1925
1931 .. container:: verbose
1926 .. container:: verbose
1932
1927
1933 Examples:
1928 Examples:
1934
1929
1935 - use export and import to transplant a bugfix to the current
1930 - use export and import to transplant a bugfix to the current
1936 branch::
1931 branch::
1937
1932
1938 hg export -r 9353 | hg import -
1933 hg export -r 9353 | hg import -
1939
1934
1940 - export all the changesets between two revisions to a file with
1935 - export all the changesets between two revisions to a file with
1941 rename information::
1936 rename information::
1942
1937
1943 hg export --git -r 123:150 > changes.txt
1938 hg export --git -r 123:150 > changes.txt
1944
1939
1945 - split outgoing changes into a series of patches with
1940 - split outgoing changes into a series of patches with
1946 descriptive names::
1941 descriptive names::
1947
1942
1948 hg export -r "outgoing()" -o "%n-%m.patch"
1943 hg export -r "outgoing()" -o "%n-%m.patch"
1949
1944
1950 Returns 0 on success.
1945 Returns 0 on success.
1951 """
1946 """
1952 opts = pycompat.byteskwargs(opts)
1947 opts = pycompat.byteskwargs(opts)
1953 changesets += tuple(opts.get('rev', []))
1948 changesets += tuple(opts.get('rev', []))
1954 if not changesets:
1949 if not changesets:
1955 changesets = ['.']
1950 changesets = ['.']
1956 repo = scmutil.unhidehashlikerevs(repo, changesets, 'nowarn')
1951 repo = scmutil.unhidehashlikerevs(repo, changesets, 'nowarn')
1957 revs = scmutil.revrange(repo, changesets)
1952 revs = scmutil.revrange(repo, changesets)
1958 if not revs:
1953 if not revs:
1959 raise error.Abort(_("export requires at least one changeset"))
1954 raise error.Abort(_("export requires at least one changeset"))
1960 if len(revs) > 1:
1955 if len(revs) > 1:
1961 ui.note(_('exporting patches:\n'))
1956 ui.note(_('exporting patches:\n'))
1962 else:
1957 else:
1963 ui.note(_('exporting patch:\n'))
1958 ui.note(_('exporting patch:\n'))
1964 ui.pager('export')
1959 ui.pager('export')
1965 cmdutil.export(repo, revs, fntemplate=opts.get('output'),
1960 cmdutil.export(repo, revs, fntemplate=opts.get('output'),
1966 switch_parent=opts.get('switch_parent'),
1961 switch_parent=opts.get('switch_parent'),
1967 opts=patch.diffallopts(ui, opts))
1962 opts=patch.diffallopts(ui, opts))
1968
1963
1969 @command('files',
1964 @command('files',
1970 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
1965 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
1971 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
1966 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
1972 ] + walkopts + formatteropts + subrepoopts,
1967 ] + walkopts + formatteropts + subrepoopts,
1973 _('[OPTION]... [FILE]...'), cmdtype=readonly)
1968 _('[OPTION]... [FILE]...'), cmdtype=readonly)
1974 def files(ui, repo, *pats, **opts):
1969 def files(ui, repo, *pats, **opts):
1975 """list tracked files
1970 """list tracked files
1976
1971
1977 Print files under Mercurial control in the working directory or
1972 Print files under Mercurial control in the working directory or
1978 specified revision for given files (excluding removed files).
1973 specified revision for given files (excluding removed files).
1979 Files can be specified as filenames or filesets.
1974 Files can be specified as filenames or filesets.
1980
1975
1981 If no files are given to match, this command prints the names
1976 If no files are given to match, this command prints the names
1982 of all files under Mercurial control.
1977 of all files under Mercurial control.
1983
1978
1984 .. container:: verbose
1979 .. container:: verbose
1985
1980
1986 Examples:
1981 Examples:
1987
1982
1988 - list all files under the current directory::
1983 - list all files under the current directory::
1989
1984
1990 hg files .
1985 hg files .
1991
1986
1992 - shows sizes and flags for current revision::
1987 - shows sizes and flags for current revision::
1993
1988
1994 hg files -vr .
1989 hg files -vr .
1995
1990
1996 - list all files named README::
1991 - list all files named README::
1997
1992
1998 hg files -I "**/README"
1993 hg files -I "**/README"
1999
1994
2000 - list all binary files::
1995 - list all binary files::
2001
1996
2002 hg files "set:binary()"
1997 hg files "set:binary()"
2003
1998
2004 - find files containing a regular expression::
1999 - find files containing a regular expression::
2005
2000
2006 hg files "set:grep('bob')"
2001 hg files "set:grep('bob')"
2007
2002
2008 - search tracked file contents with xargs and grep::
2003 - search tracked file contents with xargs and grep::
2009
2004
2010 hg files -0 | xargs -0 grep foo
2005 hg files -0 | xargs -0 grep foo
2011
2006
2012 See :hg:`help patterns` and :hg:`help filesets` for more information
2007 See :hg:`help patterns` and :hg:`help filesets` for more information
2013 on specifying file patterns.
2008 on specifying file patterns.
2014
2009
2015 Returns 0 if a match is found, 1 otherwise.
2010 Returns 0 if a match is found, 1 otherwise.
2016
2011
2017 """
2012 """
2018
2013
2019 opts = pycompat.byteskwargs(opts)
2014 opts = pycompat.byteskwargs(opts)
2020 rev = opts.get('rev')
2015 rev = opts.get('rev')
2021 if rev:
2016 if rev:
2022 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
2017 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
2023 ctx = scmutil.revsingle(repo, rev, None)
2018 ctx = scmutil.revsingle(repo, rev, None)
2024
2019
2025 end = '\n'
2020 end = '\n'
2026 if opts.get('print0'):
2021 if opts.get('print0'):
2027 end = '\0'
2022 end = '\0'
2028 fmt = '%s' + end
2023 fmt = '%s' + end
2029
2024
2030 m = scmutil.match(ctx, pats, opts)
2025 m = scmutil.match(ctx, pats, opts)
2031 ui.pager('files')
2026 ui.pager('files')
2032 with ui.formatter('files', opts) as fm:
2027 with ui.formatter('files', opts) as fm:
2033 return cmdutil.files(ui, ctx, m, fm, fmt, opts.get('subrepos'))
2028 return cmdutil.files(ui, ctx, m, fm, fmt, opts.get('subrepos'))
2034
2029
2035 @command('^forget', walkopts, _('[OPTION]... FILE...'), inferrepo=True)
2030 @command('^forget', walkopts, _('[OPTION]... FILE...'), inferrepo=True)
2036 def forget(ui, repo, *pats, **opts):
2031 def forget(ui, repo, *pats, **opts):
2037 """forget the specified files on the next commit
2032 """forget the specified files on the next commit
2038
2033
2039 Mark the specified files so they will no longer be tracked
2034 Mark the specified files so they will no longer be tracked
2040 after the next commit.
2035 after the next commit.
2041
2036
2042 This only removes files from the current branch, not from the
2037 This only removes files from the current branch, not from the
2043 entire project history, and it does not delete them from the
2038 entire project history, and it does not delete them from the
2044 working directory.
2039 working directory.
2045
2040
2046 To delete the file from the working directory, see :hg:`remove`.
2041 To delete the file from the working directory, see :hg:`remove`.
2047
2042
2048 To undo a forget before the next commit, see :hg:`add`.
2043 To undo a forget before the next commit, see :hg:`add`.
2049
2044
2050 .. container:: verbose
2045 .. container:: verbose
2051
2046
2052 Examples:
2047 Examples:
2053
2048
2054 - forget newly-added binary files::
2049 - forget newly-added binary files::
2055
2050
2056 hg forget "set:added() and binary()"
2051 hg forget "set:added() and binary()"
2057
2052
2058 - forget files that would be excluded by .hgignore::
2053 - forget files that would be excluded by .hgignore::
2059
2054
2060 hg forget "set:hgignore()"
2055 hg forget "set:hgignore()"
2061
2056
2062 Returns 0 on success.
2057 Returns 0 on success.
2063 """
2058 """
2064
2059
2065 opts = pycompat.byteskwargs(opts)
2060 opts = pycompat.byteskwargs(opts)
2066 if not pats:
2061 if not pats:
2067 raise error.Abort(_('no files specified'))
2062 raise error.Abort(_('no files specified'))
2068
2063
2069 m = scmutil.match(repo[None], pats, opts)
2064 m = scmutil.match(repo[None], pats, opts)
2070 rejected = cmdutil.forget(ui, repo, m, prefix="", explicitonly=False)[0]
2065 rejected = cmdutil.forget(ui, repo, m, prefix="", explicitonly=False)[0]
2071 return rejected and 1 or 0
2066 return rejected and 1 or 0
2072
2067
2073 @command(
2068 @command(
2074 'graft',
2069 'graft',
2075 [('r', 'rev', [], _('revisions to graft'), _('REV')),
2070 [('r', 'rev', [], _('revisions to graft'), _('REV')),
2076 ('c', 'continue', False, _('resume interrupted graft')),
2071 ('c', 'continue', False, _('resume interrupted graft')),
2077 ('e', 'edit', False, _('invoke editor on commit messages')),
2072 ('e', 'edit', False, _('invoke editor on commit messages')),
2078 ('', 'log', None, _('append graft info to log message')),
2073 ('', 'log', None, _('append graft info to log message')),
2079 ('f', 'force', False, _('force graft')),
2074 ('f', 'force', False, _('force graft')),
2080 ('D', 'currentdate', False,
2075 ('D', 'currentdate', False,
2081 _('record the current date as commit date')),
2076 _('record the current date as commit date')),
2082 ('U', 'currentuser', False,
2077 ('U', 'currentuser', False,
2083 _('record the current user as committer'), _('DATE'))]
2078 _('record the current user as committer'), _('DATE'))]
2084 + commitopts2 + mergetoolopts + dryrunopts,
2079 + commitopts2 + mergetoolopts + dryrunopts,
2085 _('[OPTION]... [-r REV]... REV...'))
2080 _('[OPTION]... [-r REV]... REV...'))
2086 def graft(ui, repo, *revs, **opts):
2081 def graft(ui, repo, *revs, **opts):
2087 '''copy changes from other branches onto the current branch
2082 '''copy changes from other branches onto the current branch
2088
2083
2089 This command uses Mercurial's merge logic to copy individual
2084 This command uses Mercurial's merge logic to copy individual
2090 changes from other branches without merging branches in the
2085 changes from other branches without merging branches in the
2091 history graph. This is sometimes known as 'backporting' or
2086 history graph. This is sometimes known as 'backporting' or
2092 'cherry-picking'. By default, graft will copy user, date, and
2087 'cherry-picking'. By default, graft will copy user, date, and
2093 description from the source changesets.
2088 description from the source changesets.
2094
2089
2095 Changesets that are ancestors of the current revision, that have
2090 Changesets that are ancestors of the current revision, that have
2096 already been grafted, or that are merges will be skipped.
2091 already been grafted, or that are merges will be skipped.
2097
2092
2098 If --log is specified, log messages will have a comment appended
2093 If --log is specified, log messages will have a comment appended
2099 of the form::
2094 of the form::
2100
2095
2101 (grafted from CHANGESETHASH)
2096 (grafted from CHANGESETHASH)
2102
2097
2103 If --force is specified, revisions will be grafted even if they
2098 If --force is specified, revisions will be grafted even if they
2104 are already ancestors of, or have been grafted to, the destination.
2099 are already ancestors of, or have been grafted to, the destination.
2105 This is useful when the revisions have since been backed out.
2100 This is useful when the revisions have since been backed out.
2106
2101
2107 If a graft merge results in conflicts, the graft process is
2102 If a graft merge results in conflicts, the graft process is
2108 interrupted so that the current merge can be manually resolved.
2103 interrupted so that the current merge can be manually resolved.
2109 Once all conflicts are addressed, the graft process can be
2104 Once all conflicts are addressed, the graft process can be
2110 continued with the -c/--continue option.
2105 continued with the -c/--continue option.
2111
2106
2112 .. note::
2107 .. note::
2113
2108
2114 The -c/--continue option does not reapply earlier options, except
2109 The -c/--continue option does not reapply earlier options, except
2115 for --force.
2110 for --force.
2116
2111
2117 .. container:: verbose
2112 .. container:: verbose
2118
2113
2119 Examples:
2114 Examples:
2120
2115
2121 - copy a single change to the stable branch and edit its description::
2116 - copy a single change to the stable branch and edit its description::
2122
2117
2123 hg update stable
2118 hg update stable
2124 hg graft --edit 9393
2119 hg graft --edit 9393
2125
2120
2126 - graft a range of changesets with one exception, updating dates::
2121 - graft a range of changesets with one exception, updating dates::
2127
2122
2128 hg graft -D "2085::2093 and not 2091"
2123 hg graft -D "2085::2093 and not 2091"
2129
2124
2130 - continue a graft after resolving conflicts::
2125 - continue a graft after resolving conflicts::
2131
2126
2132 hg graft -c
2127 hg graft -c
2133
2128
2134 - show the source of a grafted changeset::
2129 - show the source of a grafted changeset::
2135
2130
2136 hg log --debug -r .
2131 hg log --debug -r .
2137
2132
2138 - show revisions sorted by date::
2133 - show revisions sorted by date::
2139
2134
2140 hg log -r "sort(all(), date)"
2135 hg log -r "sort(all(), date)"
2141
2136
2142 See :hg:`help revisions` for more about specifying revisions.
2137 See :hg:`help revisions` for more about specifying revisions.
2143
2138
2144 Returns 0 on successful completion.
2139 Returns 0 on successful completion.
2145 '''
2140 '''
2146 with repo.wlock():
2141 with repo.wlock():
2147 return _dograft(ui, repo, *revs, **opts)
2142 return _dograft(ui, repo, *revs, **opts)
2148
2143
2149 def _dograft(ui, repo, *revs, **opts):
2144 def _dograft(ui, repo, *revs, **opts):
2150 opts = pycompat.byteskwargs(opts)
2145 opts = pycompat.byteskwargs(opts)
2151 if revs and opts.get('rev'):
2146 if revs and opts.get('rev'):
2152 ui.warn(_('warning: inconsistent use of --rev might give unexpected '
2147 ui.warn(_('warning: inconsistent use of --rev might give unexpected '
2153 'revision ordering!\n'))
2148 'revision ordering!\n'))
2154
2149
2155 revs = list(revs)
2150 revs = list(revs)
2156 revs.extend(opts.get('rev'))
2151 revs.extend(opts.get('rev'))
2157
2152
2158 if not opts.get('user') and opts.get('currentuser'):
2153 if not opts.get('user') and opts.get('currentuser'):
2159 opts['user'] = ui.username()
2154 opts['user'] = ui.username()
2160 if not opts.get('date') and opts.get('currentdate'):
2155 if not opts.get('date') and opts.get('currentdate'):
2161 opts['date'] = "%d %d" % util.makedate()
2156 opts['date'] = "%d %d" % util.makedate()
2162
2157
2163 editor = cmdutil.getcommiteditor(editform='graft',
2158 editor = cmdutil.getcommiteditor(editform='graft',
2164 **pycompat.strkwargs(opts))
2159 **pycompat.strkwargs(opts))
2165
2160
2166 cont = False
2161 cont = False
2167 if opts.get('continue'):
2162 if opts.get('continue'):
2168 cont = True
2163 cont = True
2169 if revs:
2164 if revs:
2170 raise error.Abort(_("can't specify --continue and revisions"))
2165 raise error.Abort(_("can't specify --continue and revisions"))
2171 # read in unfinished revisions
2166 # read in unfinished revisions
2172 try:
2167 try:
2173 nodes = repo.vfs.read('graftstate').splitlines()
2168 nodes = repo.vfs.read('graftstate').splitlines()
2174 revs = [repo[node].rev() for node in nodes]
2169 revs = [repo[node].rev() for node in nodes]
2175 except IOError as inst:
2170 except IOError as inst:
2176 if inst.errno != errno.ENOENT:
2171 if inst.errno != errno.ENOENT:
2177 raise
2172 raise
2178 cmdutil.wrongtooltocontinue(repo, _('graft'))
2173 cmdutil.wrongtooltocontinue(repo, _('graft'))
2179 else:
2174 else:
2180 cmdutil.checkunfinished(repo)
2175 cmdutil.checkunfinished(repo)
2181 cmdutil.bailifchanged(repo)
2176 cmdutil.bailifchanged(repo)
2182 if not revs:
2177 if not revs:
2183 raise error.Abort(_('no revisions specified'))
2178 raise error.Abort(_('no revisions specified'))
2184 revs = scmutil.revrange(repo, revs)
2179 revs = scmutil.revrange(repo, revs)
2185
2180
2186 skipped = set()
2181 skipped = set()
2187 # check for merges
2182 # check for merges
2188 for rev in repo.revs('%ld and merge()', revs):
2183 for rev in repo.revs('%ld and merge()', revs):
2189 ui.warn(_('skipping ungraftable merge revision %d\n') % rev)
2184 ui.warn(_('skipping ungraftable merge revision %d\n') % rev)
2190 skipped.add(rev)
2185 skipped.add(rev)
2191 revs = [r for r in revs if r not in skipped]
2186 revs = [r for r in revs if r not in skipped]
2192 if not revs:
2187 if not revs:
2193 return -1
2188 return -1
2194
2189
2195 # Don't check in the --continue case, in effect retaining --force across
2190 # Don't check in the --continue case, in effect retaining --force across
2196 # --continues. That's because without --force, any revisions we decided to
2191 # --continues. That's because without --force, any revisions we decided to
2197 # skip would have been filtered out here, so they wouldn't have made their
2192 # skip would have been filtered out here, so they wouldn't have made their
2198 # way to the graftstate. With --force, any revisions we would have otherwise
2193 # way to the graftstate. With --force, any revisions we would have otherwise
2199 # skipped would not have been filtered out, and if they hadn't been applied
2194 # skipped would not have been filtered out, and if they hadn't been applied
2200 # already, they'd have been in the graftstate.
2195 # already, they'd have been in the graftstate.
2201 if not (cont or opts.get('force')):
2196 if not (cont or opts.get('force')):
2202 # check for ancestors of dest branch
2197 # check for ancestors of dest branch
2203 crev = repo['.'].rev()
2198 crev = repo['.'].rev()
2204 ancestors = repo.changelog.ancestors([crev], inclusive=True)
2199 ancestors = repo.changelog.ancestors([crev], inclusive=True)
2205 # XXX make this lazy in the future
2200 # XXX make this lazy in the future
2206 # don't mutate while iterating, create a copy
2201 # don't mutate while iterating, create a copy
2207 for rev in list(revs):
2202 for rev in list(revs):
2208 if rev in ancestors:
2203 if rev in ancestors:
2209 ui.warn(_('skipping ancestor revision %d:%s\n') %
2204 ui.warn(_('skipping ancestor revision %d:%s\n') %
2210 (rev, repo[rev]))
2205 (rev, repo[rev]))
2211 # XXX remove on list is slow
2206 # XXX remove on list is slow
2212 revs.remove(rev)
2207 revs.remove(rev)
2213 if not revs:
2208 if not revs:
2214 return -1
2209 return -1
2215
2210
2216 # analyze revs for earlier grafts
2211 # analyze revs for earlier grafts
2217 ids = {}
2212 ids = {}
2218 for ctx in repo.set("%ld", revs):
2213 for ctx in repo.set("%ld", revs):
2219 ids[ctx.hex()] = ctx.rev()
2214 ids[ctx.hex()] = ctx.rev()
2220 n = ctx.extra().get('source')
2215 n = ctx.extra().get('source')
2221 if n:
2216 if n:
2222 ids[n] = ctx.rev()
2217 ids[n] = ctx.rev()
2223
2218
2224 # check ancestors for earlier grafts
2219 # check ancestors for earlier grafts
2225 ui.debug('scanning for duplicate grafts\n')
2220 ui.debug('scanning for duplicate grafts\n')
2226
2221
2227 # The only changesets we can be sure doesn't contain grafts of any
2222 # The only changesets we can be sure doesn't contain grafts of any
2228 # revs, are the ones that are common ancestors of *all* revs:
2223 # revs, are the ones that are common ancestors of *all* revs:
2229 for rev in repo.revs('only(%d,ancestor(%ld))', crev, revs):
2224 for rev in repo.revs('only(%d,ancestor(%ld))', crev, revs):
2230 ctx = repo[rev]
2225 ctx = repo[rev]
2231 n = ctx.extra().get('source')
2226 n = ctx.extra().get('source')
2232 if n in ids:
2227 if n in ids:
2233 try:
2228 try:
2234 r = repo[n].rev()
2229 r = repo[n].rev()
2235 except error.RepoLookupError:
2230 except error.RepoLookupError:
2236 r = None
2231 r = None
2237 if r in revs:
2232 if r in revs:
2238 ui.warn(_('skipping revision %d:%s '
2233 ui.warn(_('skipping revision %d:%s '
2239 '(already grafted to %d:%s)\n')
2234 '(already grafted to %d:%s)\n')
2240 % (r, repo[r], rev, ctx))
2235 % (r, repo[r], rev, ctx))
2241 revs.remove(r)
2236 revs.remove(r)
2242 elif ids[n] in revs:
2237 elif ids[n] in revs:
2243 if r is None:
2238 if r is None:
2244 ui.warn(_('skipping already grafted revision %d:%s '
2239 ui.warn(_('skipping already grafted revision %d:%s '
2245 '(%d:%s also has unknown origin %s)\n')
2240 '(%d:%s also has unknown origin %s)\n')
2246 % (ids[n], repo[ids[n]], rev, ctx, n[:12]))
2241 % (ids[n], repo[ids[n]], rev, ctx, n[:12]))
2247 else:
2242 else:
2248 ui.warn(_('skipping already grafted revision %d:%s '
2243 ui.warn(_('skipping already grafted revision %d:%s '
2249 '(%d:%s also has origin %d:%s)\n')
2244 '(%d:%s also has origin %d:%s)\n')
2250 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12]))
2245 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12]))
2251 revs.remove(ids[n])
2246 revs.remove(ids[n])
2252 elif ctx.hex() in ids:
2247 elif ctx.hex() in ids:
2253 r = ids[ctx.hex()]
2248 r = ids[ctx.hex()]
2254 ui.warn(_('skipping already grafted revision %d:%s '
2249 ui.warn(_('skipping already grafted revision %d:%s '
2255 '(was grafted from %d:%s)\n') %
2250 '(was grafted from %d:%s)\n') %
2256 (r, repo[r], rev, ctx))
2251 (r, repo[r], rev, ctx))
2257 revs.remove(r)
2252 revs.remove(r)
2258 if not revs:
2253 if not revs:
2259 return -1
2254 return -1
2260
2255
2261 for pos, ctx in enumerate(repo.set("%ld", revs)):
2256 for pos, ctx in enumerate(repo.set("%ld", revs)):
2262 desc = '%d:%s "%s"' % (ctx.rev(), ctx,
2257 desc = '%d:%s "%s"' % (ctx.rev(), ctx,
2263 ctx.description().split('\n', 1)[0])
2258 ctx.description().split('\n', 1)[0])
2264 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
2259 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
2265 if names:
2260 if names:
2266 desc += ' (%s)' % ' '.join(names)
2261 desc += ' (%s)' % ' '.join(names)
2267 ui.status(_('grafting %s\n') % desc)
2262 ui.status(_('grafting %s\n') % desc)
2268 if opts.get('dry_run'):
2263 if opts.get('dry_run'):
2269 continue
2264 continue
2270
2265
2271 source = ctx.extra().get('source')
2266 source = ctx.extra().get('source')
2272 extra = {}
2267 extra = {}
2273 if source:
2268 if source:
2274 extra['source'] = source
2269 extra['source'] = source
2275 extra['intermediate-source'] = ctx.hex()
2270 extra['intermediate-source'] = ctx.hex()
2276 else:
2271 else:
2277 extra['source'] = ctx.hex()
2272 extra['source'] = ctx.hex()
2278 user = ctx.user()
2273 user = ctx.user()
2279 if opts.get('user'):
2274 if opts.get('user'):
2280 user = opts['user']
2275 user = opts['user']
2281 date = ctx.date()
2276 date = ctx.date()
2282 if opts.get('date'):
2277 if opts.get('date'):
2283 date = opts['date']
2278 date = opts['date']
2284 message = ctx.description()
2279 message = ctx.description()
2285 if opts.get('log'):
2280 if opts.get('log'):
2286 message += '\n(grafted from %s)' % ctx.hex()
2281 message += '\n(grafted from %s)' % ctx.hex()
2287
2282
2288 # we don't merge the first commit when continuing
2283 # we don't merge the first commit when continuing
2289 if not cont:
2284 if not cont:
2290 # perform the graft merge with p1(rev) as 'ancestor'
2285 # perform the graft merge with p1(rev) as 'ancestor'
2291 try:
2286 try:
2292 # ui.forcemerge is an internal variable, do not document
2287 # ui.forcemerge is an internal variable, do not document
2293 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
2288 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
2294 'graft')
2289 'graft')
2295 stats = mergemod.graft(repo, ctx, ctx.p1(),
2290 stats = mergemod.graft(repo, ctx, ctx.p1(),
2296 ['local', 'graft'])
2291 ['local', 'graft'])
2297 finally:
2292 finally:
2298 repo.ui.setconfig('ui', 'forcemerge', '', 'graft')
2293 repo.ui.setconfig('ui', 'forcemerge', '', 'graft')
2299 # report any conflicts
2294 # report any conflicts
2300 if stats and stats[3] > 0:
2295 if stats and stats[3] > 0:
2301 # write out state for --continue
2296 # write out state for --continue
2302 nodelines = [repo[rev].hex() + "\n" for rev in revs[pos:]]
2297 nodelines = [repo[rev].hex() + "\n" for rev in revs[pos:]]
2303 repo.vfs.write('graftstate', ''.join(nodelines))
2298 repo.vfs.write('graftstate', ''.join(nodelines))
2304 extra = ''
2299 extra = ''
2305 if opts.get('user'):
2300 if opts.get('user'):
2306 extra += ' --user %s' % util.shellquote(opts['user'])
2301 extra += ' --user %s' % util.shellquote(opts['user'])
2307 if opts.get('date'):
2302 if opts.get('date'):
2308 extra += ' --date %s' % util.shellquote(opts['date'])
2303 extra += ' --date %s' % util.shellquote(opts['date'])
2309 if opts.get('log'):
2304 if opts.get('log'):
2310 extra += ' --log'
2305 extra += ' --log'
2311 hint=_("use 'hg resolve' and 'hg graft --continue%s'") % extra
2306 hint=_("use 'hg resolve' and 'hg graft --continue%s'") % extra
2312 raise error.Abort(
2307 raise error.Abort(
2313 _("unresolved conflicts, can't continue"),
2308 _("unresolved conflicts, can't continue"),
2314 hint=hint)
2309 hint=hint)
2315 else:
2310 else:
2316 cont = False
2311 cont = False
2317
2312
2318 # commit
2313 # commit
2319 node = repo.commit(text=message, user=user,
2314 node = repo.commit(text=message, user=user,
2320 date=date, extra=extra, editor=editor)
2315 date=date, extra=extra, editor=editor)
2321 if node is None:
2316 if node is None:
2322 ui.warn(
2317 ui.warn(
2323 _('note: graft of %d:%s created no changes to commit\n') %
2318 _('note: graft of %d:%s created no changes to commit\n') %
2324 (ctx.rev(), ctx))
2319 (ctx.rev(), ctx))
2325
2320
2326 # remove state when we complete successfully
2321 # remove state when we complete successfully
2327 if not opts.get('dry_run'):
2322 if not opts.get('dry_run'):
2328 repo.vfs.unlinkpath('graftstate', ignoremissing=True)
2323 repo.vfs.unlinkpath('graftstate', ignoremissing=True)
2329
2324
2330 return 0
2325 return 0
2331
2326
2332 @command('grep',
2327 @command('grep',
2333 [('0', 'print0', None, _('end fields with NUL')),
2328 [('0', 'print0', None, _('end fields with NUL')),
2334 ('', 'all', None, _('print all revisions that match')),
2329 ('', 'all', None, _('print all revisions that match')),
2335 ('a', 'text', None, _('treat all files as text')),
2330 ('a', 'text', None, _('treat all files as text')),
2336 ('f', 'follow', None,
2331 ('f', 'follow', None,
2337 _('follow changeset history,'
2332 _('follow changeset history,'
2338 ' or file history across copies and renames')),
2333 ' or file history across copies and renames')),
2339 ('i', 'ignore-case', None, _('ignore case when matching')),
2334 ('i', 'ignore-case', None, _('ignore case when matching')),
2340 ('l', 'files-with-matches', None,
2335 ('l', 'files-with-matches', None,
2341 _('print only filenames and revisions that match')),
2336 _('print only filenames and revisions that match')),
2342 ('n', 'line-number', None, _('print matching line numbers')),
2337 ('n', 'line-number', None, _('print matching line numbers')),
2343 ('r', 'rev', [],
2338 ('r', 'rev', [],
2344 _('only search files changed within revision range'), _('REV')),
2339 _('only search files changed within revision range'), _('REV')),
2345 ('u', 'user', None, _('list the author (long with -v)')),
2340 ('u', 'user', None, _('list the author (long with -v)')),
2346 ('d', 'date', None, _('list the date (short with -q)')),
2341 ('d', 'date', None, _('list the date (short with -q)')),
2347 ] + formatteropts + walkopts,
2342 ] + formatteropts + walkopts,
2348 _('[OPTION]... PATTERN [FILE]...'),
2343 _('[OPTION]... PATTERN [FILE]...'),
2349 inferrepo=True, cmdtype=readonly)
2344 inferrepo=True, cmdtype=readonly)
2350 def grep(ui, repo, pattern, *pats, **opts):
2345 def grep(ui, repo, pattern, *pats, **opts):
2351 """search revision history for a pattern in specified files
2346 """search revision history for a pattern in specified files
2352
2347
2353 Search revision history for a regular expression in the specified
2348 Search revision history for a regular expression in the specified
2354 files or the entire project.
2349 files or the entire project.
2355
2350
2356 By default, grep prints the most recent revision number for each
2351 By default, grep prints the most recent revision number for each
2357 file in which it finds a match. To get it to print every revision
2352 file in which it finds a match. To get it to print every revision
2358 that contains a change in match status ("-" for a match that becomes
2353 that contains a change in match status ("-" for a match that becomes
2359 a non-match, or "+" for a non-match that becomes a match), use the
2354 a non-match, or "+" for a non-match that becomes a match), use the
2360 --all flag.
2355 --all flag.
2361
2356
2362 PATTERN can be any Python (roughly Perl-compatible) regular
2357 PATTERN can be any Python (roughly Perl-compatible) regular
2363 expression.
2358 expression.
2364
2359
2365 If no FILEs are specified (and -f/--follow isn't set), all files in
2360 If no FILEs are specified (and -f/--follow isn't set), all files in
2366 the repository are searched, including those that don't exist in the
2361 the repository are searched, including those that don't exist in the
2367 current branch or have been deleted in a prior changeset.
2362 current branch or have been deleted in a prior changeset.
2368
2363
2369 Returns 0 if a match is found, 1 otherwise.
2364 Returns 0 if a match is found, 1 otherwise.
2370 """
2365 """
2371 opts = pycompat.byteskwargs(opts)
2366 opts = pycompat.byteskwargs(opts)
2372 reflags = re.M
2367 reflags = re.M
2373 if opts.get('ignore_case'):
2368 if opts.get('ignore_case'):
2374 reflags |= re.I
2369 reflags |= re.I
2375 try:
2370 try:
2376 regexp = util.re.compile(pattern, reflags)
2371 regexp = util.re.compile(pattern, reflags)
2377 except re.error as inst:
2372 except re.error as inst:
2378 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
2373 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
2379 return 1
2374 return 1
2380 sep, eol = ':', '\n'
2375 sep, eol = ':', '\n'
2381 if opts.get('print0'):
2376 if opts.get('print0'):
2382 sep = eol = '\0'
2377 sep = eol = '\0'
2383
2378
2384 getfile = util.lrucachefunc(repo.file)
2379 getfile = util.lrucachefunc(repo.file)
2385
2380
2386 def matchlines(body):
2381 def matchlines(body):
2387 begin = 0
2382 begin = 0
2388 linenum = 0
2383 linenum = 0
2389 while begin < len(body):
2384 while begin < len(body):
2390 match = regexp.search(body, begin)
2385 match = regexp.search(body, begin)
2391 if not match:
2386 if not match:
2392 break
2387 break
2393 mstart, mend = match.span()
2388 mstart, mend = match.span()
2394 linenum += body.count('\n', begin, mstart) + 1
2389 linenum += body.count('\n', begin, mstart) + 1
2395 lstart = body.rfind('\n', begin, mstart) + 1 or begin
2390 lstart = body.rfind('\n', begin, mstart) + 1 or begin
2396 begin = body.find('\n', mend) + 1 or len(body) + 1
2391 begin = body.find('\n', mend) + 1 or len(body) + 1
2397 lend = begin - 1
2392 lend = begin - 1
2398 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
2393 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
2399
2394
2400 class linestate(object):
2395 class linestate(object):
2401 def __init__(self, line, linenum, colstart, colend):
2396 def __init__(self, line, linenum, colstart, colend):
2402 self.line = line
2397 self.line = line
2403 self.linenum = linenum
2398 self.linenum = linenum
2404 self.colstart = colstart
2399 self.colstart = colstart
2405 self.colend = colend
2400 self.colend = colend
2406
2401
2407 def __hash__(self):
2402 def __hash__(self):
2408 return hash((self.linenum, self.line))
2403 return hash((self.linenum, self.line))
2409
2404
2410 def __eq__(self, other):
2405 def __eq__(self, other):
2411 return self.line == other.line
2406 return self.line == other.line
2412
2407
2413 def findpos(self):
2408 def findpos(self):
2414 """Iterate all (start, end) indices of matches"""
2409 """Iterate all (start, end) indices of matches"""
2415 yield self.colstart, self.colend
2410 yield self.colstart, self.colend
2416 p = self.colend
2411 p = self.colend
2417 while p < len(self.line):
2412 while p < len(self.line):
2418 m = regexp.search(self.line, p)
2413 m = regexp.search(self.line, p)
2419 if not m:
2414 if not m:
2420 break
2415 break
2421 yield m.span()
2416 yield m.span()
2422 p = m.end()
2417 p = m.end()
2423
2418
2424 matches = {}
2419 matches = {}
2425 copies = {}
2420 copies = {}
2426 def grepbody(fn, rev, body):
2421 def grepbody(fn, rev, body):
2427 matches[rev].setdefault(fn, [])
2422 matches[rev].setdefault(fn, [])
2428 m = matches[rev][fn]
2423 m = matches[rev][fn]
2429 for lnum, cstart, cend, line in matchlines(body):
2424 for lnum, cstart, cend, line in matchlines(body):
2430 s = linestate(line, lnum, cstart, cend)
2425 s = linestate(line, lnum, cstart, cend)
2431 m.append(s)
2426 m.append(s)
2432
2427
2433 def difflinestates(a, b):
2428 def difflinestates(a, b):
2434 sm = difflib.SequenceMatcher(None, a, b)
2429 sm = difflib.SequenceMatcher(None, a, b)
2435 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
2430 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
2436 if tag == 'insert':
2431 if tag == 'insert':
2437 for i in xrange(blo, bhi):
2432 for i in xrange(blo, bhi):
2438 yield ('+', b[i])
2433 yield ('+', b[i])
2439 elif tag == 'delete':
2434 elif tag == 'delete':
2440 for i in xrange(alo, ahi):
2435 for i in xrange(alo, ahi):
2441 yield ('-', a[i])
2436 yield ('-', a[i])
2442 elif tag == 'replace':
2437 elif tag == 'replace':
2443 for i in xrange(alo, ahi):
2438 for i in xrange(alo, ahi):
2444 yield ('-', a[i])
2439 yield ('-', a[i])
2445 for i in xrange(blo, bhi):
2440 for i in xrange(blo, bhi):
2446 yield ('+', b[i])
2441 yield ('+', b[i])
2447
2442
2448 def display(fm, fn, ctx, pstates, states):
2443 def display(fm, fn, ctx, pstates, states):
2449 rev = ctx.rev()
2444 rev = ctx.rev()
2450 if fm.isplain():
2445 if fm.isplain():
2451 formatuser = ui.shortuser
2446 formatuser = ui.shortuser
2452 else:
2447 else:
2453 formatuser = str
2448 formatuser = str
2454 if ui.quiet:
2449 if ui.quiet:
2455 datefmt = '%Y-%m-%d'
2450 datefmt = '%Y-%m-%d'
2456 else:
2451 else:
2457 datefmt = '%a %b %d %H:%M:%S %Y %1%2'
2452 datefmt = '%a %b %d %H:%M:%S %Y %1%2'
2458 found = False
2453 found = False
2459 @util.cachefunc
2454 @util.cachefunc
2460 def binary():
2455 def binary():
2461 flog = getfile(fn)
2456 flog = getfile(fn)
2462 return util.binary(flog.read(ctx.filenode(fn)))
2457 return util.binary(flog.read(ctx.filenode(fn)))
2463
2458
2464 fieldnamemap = {'filename': 'file', 'linenumber': 'line_number'}
2459 fieldnamemap = {'filename': 'file', 'linenumber': 'line_number'}
2465 if opts.get('all'):
2460 if opts.get('all'):
2466 iter = difflinestates(pstates, states)
2461 iter = difflinestates(pstates, states)
2467 else:
2462 else:
2468 iter = [('', l) for l in states]
2463 iter = [('', l) for l in states]
2469 for change, l in iter:
2464 for change, l in iter:
2470 fm.startitem()
2465 fm.startitem()
2471 fm.data(node=fm.hexfunc(ctx.node()))
2466 fm.data(node=fm.hexfunc(ctx.node()))
2472 cols = [
2467 cols = [
2473 ('filename', fn, True),
2468 ('filename', fn, True),
2474 ('rev', rev, True),
2469 ('rev', rev, True),
2475 ('linenumber', l.linenum, opts.get('line_number')),
2470 ('linenumber', l.linenum, opts.get('line_number')),
2476 ]
2471 ]
2477 if opts.get('all'):
2472 if opts.get('all'):
2478 cols.append(('change', change, True))
2473 cols.append(('change', change, True))
2479 cols.extend([
2474 cols.extend([
2480 ('user', formatuser(ctx.user()), opts.get('user')),
2475 ('user', formatuser(ctx.user()), opts.get('user')),
2481 ('date', fm.formatdate(ctx.date(), datefmt), opts.get('date')),
2476 ('date', fm.formatdate(ctx.date(), datefmt), opts.get('date')),
2482 ])
2477 ])
2483 lastcol = next(name for name, data, cond in reversed(cols) if cond)
2478 lastcol = next(name for name, data, cond in reversed(cols) if cond)
2484 for name, data, cond in cols:
2479 for name, data, cond in cols:
2485 field = fieldnamemap.get(name, name)
2480 field = fieldnamemap.get(name, name)
2486 fm.condwrite(cond, field, '%s', data, label='grep.%s' % name)
2481 fm.condwrite(cond, field, '%s', data, label='grep.%s' % name)
2487 if cond and name != lastcol:
2482 if cond and name != lastcol:
2488 fm.plain(sep, label='grep.sep')
2483 fm.plain(sep, label='grep.sep')
2489 if not opts.get('files_with_matches'):
2484 if not opts.get('files_with_matches'):
2490 fm.plain(sep, label='grep.sep')
2485 fm.plain(sep, label='grep.sep')
2491 if not opts.get('text') and binary():
2486 if not opts.get('text') and binary():
2492 fm.plain(_(" Binary file matches"))
2487 fm.plain(_(" Binary file matches"))
2493 else:
2488 else:
2494 displaymatches(fm.nested('texts'), l)
2489 displaymatches(fm.nested('texts'), l)
2495 fm.plain(eol)
2490 fm.plain(eol)
2496 found = True
2491 found = True
2497 if opts.get('files_with_matches'):
2492 if opts.get('files_with_matches'):
2498 break
2493 break
2499 return found
2494 return found
2500
2495
2501 def displaymatches(fm, l):
2496 def displaymatches(fm, l):
2502 p = 0
2497 p = 0
2503 for s, e in l.findpos():
2498 for s, e in l.findpos():
2504 if p < s:
2499 if p < s:
2505 fm.startitem()
2500 fm.startitem()
2506 fm.write('text', '%s', l.line[p:s])
2501 fm.write('text', '%s', l.line[p:s])
2507 fm.data(matched=False)
2502 fm.data(matched=False)
2508 fm.startitem()
2503 fm.startitem()
2509 fm.write('text', '%s', l.line[s:e], label='grep.match')
2504 fm.write('text', '%s', l.line[s:e], label='grep.match')
2510 fm.data(matched=True)
2505 fm.data(matched=True)
2511 p = e
2506 p = e
2512 if p < len(l.line):
2507 if p < len(l.line):
2513 fm.startitem()
2508 fm.startitem()
2514 fm.write('text', '%s', l.line[p:])
2509 fm.write('text', '%s', l.line[p:])
2515 fm.data(matched=False)
2510 fm.data(matched=False)
2516 fm.end()
2511 fm.end()
2517
2512
2518 skip = {}
2513 skip = {}
2519 revfiles = {}
2514 revfiles = {}
2520 match = scmutil.match(repo[None], pats, opts)
2515 match = scmutil.match(repo[None], pats, opts)
2521 found = False
2516 found = False
2522 follow = opts.get('follow')
2517 follow = opts.get('follow')
2523
2518
2524 def prep(ctx, fns):
2519 def prep(ctx, fns):
2525 rev = ctx.rev()
2520 rev = ctx.rev()
2526 pctx = ctx.p1()
2521 pctx = ctx.p1()
2527 parent = pctx.rev()
2522 parent = pctx.rev()
2528 matches.setdefault(rev, {})
2523 matches.setdefault(rev, {})
2529 matches.setdefault(parent, {})
2524 matches.setdefault(parent, {})
2530 files = revfiles.setdefault(rev, [])
2525 files = revfiles.setdefault(rev, [])
2531 for fn in fns:
2526 for fn in fns:
2532 flog = getfile(fn)
2527 flog = getfile(fn)
2533 try:
2528 try:
2534 fnode = ctx.filenode(fn)
2529 fnode = ctx.filenode(fn)
2535 except error.LookupError:
2530 except error.LookupError:
2536 continue
2531 continue
2537
2532
2538 copied = flog.renamed(fnode)
2533 copied = flog.renamed(fnode)
2539 copy = follow and copied and copied[0]
2534 copy = follow and copied and copied[0]
2540 if copy:
2535 if copy:
2541 copies.setdefault(rev, {})[fn] = copy
2536 copies.setdefault(rev, {})[fn] = copy
2542 if fn in skip:
2537 if fn in skip:
2543 if copy:
2538 if copy:
2544 skip[copy] = True
2539 skip[copy] = True
2545 continue
2540 continue
2546 files.append(fn)
2541 files.append(fn)
2547
2542
2548 if fn not in matches[rev]:
2543 if fn not in matches[rev]:
2549 grepbody(fn, rev, flog.read(fnode))
2544 grepbody(fn, rev, flog.read(fnode))
2550
2545
2551 pfn = copy or fn
2546 pfn = copy or fn
2552 if pfn not in matches[parent]:
2547 if pfn not in matches[parent]:
2553 try:
2548 try:
2554 fnode = pctx.filenode(pfn)
2549 fnode = pctx.filenode(pfn)
2555 grepbody(pfn, parent, flog.read(fnode))
2550 grepbody(pfn, parent, flog.read(fnode))
2556 except error.LookupError:
2551 except error.LookupError:
2557 pass
2552 pass
2558
2553
2559 ui.pager('grep')
2554 ui.pager('grep')
2560 fm = ui.formatter('grep', opts)
2555 fm = ui.formatter('grep', opts)
2561 for ctx in cmdutil.walkchangerevs(repo, match, opts, prep):
2556 for ctx in cmdutil.walkchangerevs(repo, match, opts, prep):
2562 rev = ctx.rev()
2557 rev = ctx.rev()
2563 parent = ctx.p1().rev()
2558 parent = ctx.p1().rev()
2564 for fn in sorted(revfiles.get(rev, [])):
2559 for fn in sorted(revfiles.get(rev, [])):
2565 states = matches[rev][fn]
2560 states = matches[rev][fn]
2566 copy = copies.get(rev, {}).get(fn)
2561 copy = copies.get(rev, {}).get(fn)
2567 if fn in skip:
2562 if fn in skip:
2568 if copy:
2563 if copy:
2569 skip[copy] = True
2564 skip[copy] = True
2570 continue
2565 continue
2571 pstates = matches.get(parent, {}).get(copy or fn, [])
2566 pstates = matches.get(parent, {}).get(copy or fn, [])
2572 if pstates or states:
2567 if pstates or states:
2573 r = display(fm, fn, ctx, pstates, states)
2568 r = display(fm, fn, ctx, pstates, states)
2574 found = found or r
2569 found = found or r
2575 if r and not opts.get('all'):
2570 if r and not opts.get('all'):
2576 skip[fn] = True
2571 skip[fn] = True
2577 if copy:
2572 if copy:
2578 skip[copy] = True
2573 skip[copy] = True
2579 del matches[rev]
2574 del matches[rev]
2580 del revfiles[rev]
2575 del revfiles[rev]
2581 fm.end()
2576 fm.end()
2582
2577
2583 return not found
2578 return not found
2584
2579
2585 @command('heads',
2580 @command('heads',
2586 [('r', 'rev', '',
2581 [('r', 'rev', '',
2587 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
2582 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
2588 ('t', 'topo', False, _('show topological heads only')),
2583 ('t', 'topo', False, _('show topological heads only')),
2589 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
2584 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
2590 ('c', 'closed', False, _('show normal and closed branch heads')),
2585 ('c', 'closed', False, _('show normal and closed branch heads')),
2591 ] + templateopts,
2586 ] + templateopts,
2592 _('[-ct] [-r STARTREV] [REV]...'), cmdtype=readonly)
2587 _('[-ct] [-r STARTREV] [REV]...'), cmdtype=readonly)
2593 def heads(ui, repo, *branchrevs, **opts):
2588 def heads(ui, repo, *branchrevs, **opts):
2594 """show branch heads
2589 """show branch heads
2595
2590
2596 With no arguments, show all open branch heads in the repository.
2591 With no arguments, show all open branch heads in the repository.
2597 Branch heads are changesets that have no descendants on the
2592 Branch heads are changesets that have no descendants on the
2598 same branch. They are where development generally takes place and
2593 same branch. They are where development generally takes place and
2599 are the usual targets for update and merge operations.
2594 are the usual targets for update and merge operations.
2600
2595
2601 If one or more REVs are given, only open branch heads on the
2596 If one or more REVs are given, only open branch heads on the
2602 branches associated with the specified changesets are shown. This
2597 branches associated with the specified changesets are shown. This
2603 means that you can use :hg:`heads .` to see the heads on the
2598 means that you can use :hg:`heads .` to see the heads on the
2604 currently checked-out branch.
2599 currently checked-out branch.
2605
2600
2606 If -c/--closed is specified, also show branch heads marked closed
2601 If -c/--closed is specified, also show branch heads marked closed
2607 (see :hg:`commit --close-branch`).
2602 (see :hg:`commit --close-branch`).
2608
2603
2609 If STARTREV is specified, only those heads that are descendants of
2604 If STARTREV is specified, only those heads that are descendants of
2610 STARTREV will be displayed.
2605 STARTREV will be displayed.
2611
2606
2612 If -t/--topo is specified, named branch mechanics will be ignored and only
2607 If -t/--topo is specified, named branch mechanics will be ignored and only
2613 topological heads (changesets with no children) will be shown.
2608 topological heads (changesets with no children) will be shown.
2614
2609
2615 Returns 0 if matching heads are found, 1 if not.
2610 Returns 0 if matching heads are found, 1 if not.
2616 """
2611 """
2617
2612
2618 opts = pycompat.byteskwargs(opts)
2613 opts = pycompat.byteskwargs(opts)
2619 start = None
2614 start = None
2620 rev = opts.get('rev')
2615 rev = opts.get('rev')
2621 if rev:
2616 if rev:
2622 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
2617 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
2623 start = scmutil.revsingle(repo, rev, None).node()
2618 start = scmutil.revsingle(repo, rev, None).node()
2624
2619
2625 if opts.get('topo'):
2620 if opts.get('topo'):
2626 heads = [repo[h] for h in repo.heads(start)]
2621 heads = [repo[h] for h in repo.heads(start)]
2627 else:
2622 else:
2628 heads = []
2623 heads = []
2629 for branch in repo.branchmap():
2624 for branch in repo.branchmap():
2630 heads += repo.branchheads(branch, start, opts.get('closed'))
2625 heads += repo.branchheads(branch, start, opts.get('closed'))
2631 heads = [repo[h] for h in heads]
2626 heads = [repo[h] for h in heads]
2632
2627
2633 if branchrevs:
2628 if branchrevs:
2634 branches = set(repo[br].branch() for br in branchrevs)
2629 branches = set(repo[br].branch() for br in branchrevs)
2635 heads = [h for h in heads if h.branch() in branches]
2630 heads = [h for h in heads if h.branch() in branches]
2636
2631
2637 if opts.get('active') and branchrevs:
2632 if opts.get('active') and branchrevs:
2638 dagheads = repo.heads(start)
2633 dagheads = repo.heads(start)
2639 heads = [h for h in heads if h.node() in dagheads]
2634 heads = [h for h in heads if h.node() in dagheads]
2640
2635
2641 if branchrevs:
2636 if branchrevs:
2642 haveheads = set(h.branch() for h in heads)
2637 haveheads = set(h.branch() for h in heads)
2643 if branches - haveheads:
2638 if branches - haveheads:
2644 headless = ', '.join(b for b in branches - haveheads)
2639 headless = ', '.join(b for b in branches - haveheads)
2645 msg = _('no open branch heads found on branches %s')
2640 msg = _('no open branch heads found on branches %s')
2646 if opts.get('rev'):
2641 if opts.get('rev'):
2647 msg += _(' (started at %s)') % opts['rev']
2642 msg += _(' (started at %s)') % opts['rev']
2648 ui.warn((msg + '\n') % headless)
2643 ui.warn((msg + '\n') % headless)
2649
2644
2650 if not heads:
2645 if not heads:
2651 return 1
2646 return 1
2652
2647
2653 ui.pager('heads')
2648 ui.pager('heads')
2654 heads = sorted(heads, key=lambda x: -x.rev())
2649 heads = sorted(heads, key=lambda x: -x.rev())
2655 displayer = cmdutil.show_changeset(ui, repo, opts)
2650 displayer = cmdutil.show_changeset(ui, repo, opts)
2656 for ctx in heads:
2651 for ctx in heads:
2657 displayer.show(ctx)
2652 displayer.show(ctx)
2658 displayer.close()
2653 displayer.close()
2659
2654
2660 @command('help',
2655 @command('help',
2661 [('e', 'extension', None, _('show only help for extensions')),
2656 [('e', 'extension', None, _('show only help for extensions')),
2662 ('c', 'command', None, _('show only help for commands')),
2657 ('c', 'command', None, _('show only help for commands')),
2663 ('k', 'keyword', None, _('show topics matching keyword')),
2658 ('k', 'keyword', None, _('show topics matching keyword')),
2664 ('s', 'system', [], _('show help for specific platform(s)')),
2659 ('s', 'system', [], _('show help for specific platform(s)')),
2665 ],
2660 ],
2666 _('[-ecks] [TOPIC]'),
2661 _('[-ecks] [TOPIC]'),
2667 norepo=True, cmdtype=readonly)
2662 norepo=True, cmdtype=readonly)
2668 def help_(ui, name=None, **opts):
2663 def help_(ui, name=None, **opts):
2669 """show help for a given topic or a help overview
2664 """show help for a given topic or a help overview
2670
2665
2671 With no arguments, print a list of commands with short help messages.
2666 With no arguments, print a list of commands with short help messages.
2672
2667
2673 Given a topic, extension, or command name, print help for that
2668 Given a topic, extension, or command name, print help for that
2674 topic.
2669 topic.
2675
2670
2676 Returns 0 if successful.
2671 Returns 0 if successful.
2677 """
2672 """
2678
2673
2679 keep = opts.get(r'system') or []
2674 keep = opts.get(r'system') or []
2680 if len(keep) == 0:
2675 if len(keep) == 0:
2681 if pycompat.sysplatform.startswith('win'):
2676 if pycompat.sysplatform.startswith('win'):
2682 keep.append('windows')
2677 keep.append('windows')
2683 elif pycompat.sysplatform == 'OpenVMS':
2678 elif pycompat.sysplatform == 'OpenVMS':
2684 keep.append('vms')
2679 keep.append('vms')
2685 elif pycompat.sysplatform == 'plan9':
2680 elif pycompat.sysplatform == 'plan9':
2686 keep.append('plan9')
2681 keep.append('plan9')
2687 else:
2682 else:
2688 keep.append('unix')
2683 keep.append('unix')
2689 keep.append(pycompat.sysplatform.lower())
2684 keep.append(pycompat.sysplatform.lower())
2690 if ui.verbose:
2685 if ui.verbose:
2691 keep.append('verbose')
2686 keep.append('verbose')
2692
2687
2693 commands = sys.modules[__name__]
2688 commands = sys.modules[__name__]
2694 formatted = help.formattedhelp(ui, commands, name, keep=keep, **opts)
2689 formatted = help.formattedhelp(ui, commands, name, keep=keep, **opts)
2695 ui.pager('help')
2690 ui.pager('help')
2696 ui.write(formatted)
2691 ui.write(formatted)
2697
2692
2698
2693
2699 @command('identify|id',
2694 @command('identify|id',
2700 [('r', 'rev', '',
2695 [('r', 'rev', '',
2701 _('identify the specified revision'), _('REV')),
2696 _('identify the specified revision'), _('REV')),
2702 ('n', 'num', None, _('show local revision number')),
2697 ('n', 'num', None, _('show local revision number')),
2703 ('i', 'id', None, _('show global revision id')),
2698 ('i', 'id', None, _('show global revision id')),
2704 ('b', 'branch', None, _('show branch')),
2699 ('b', 'branch', None, _('show branch')),
2705 ('t', 'tags', None, _('show tags')),
2700 ('t', 'tags', None, _('show tags')),
2706 ('B', 'bookmarks', None, _('show bookmarks')),
2701 ('B', 'bookmarks', None, _('show bookmarks')),
2707 ] + remoteopts + formatteropts,
2702 ] + remoteopts + formatteropts,
2708 _('[-nibtB] [-r REV] [SOURCE]'),
2703 _('[-nibtB] [-r REV] [SOURCE]'),
2709 optionalrepo=True, cmdtype=readonly)
2704 optionalrepo=True, cmdtype=readonly)
2710 def identify(ui, repo, source=None, rev=None,
2705 def identify(ui, repo, source=None, rev=None,
2711 num=None, id=None, branch=None, tags=None, bookmarks=None, **opts):
2706 num=None, id=None, branch=None, tags=None, bookmarks=None, **opts):
2712 """identify the working directory or specified revision
2707 """identify the working directory or specified revision
2713
2708
2714 Print a summary identifying the repository state at REV using one or
2709 Print a summary identifying the repository state at REV using one or
2715 two parent hash identifiers, followed by a "+" if the working
2710 two parent hash identifiers, followed by a "+" if the working
2716 directory has uncommitted changes, the branch name (if not default),
2711 directory has uncommitted changes, the branch name (if not default),
2717 a list of tags, and a list of bookmarks.
2712 a list of tags, and a list of bookmarks.
2718
2713
2719 When REV is not given, print a summary of the current state of the
2714 When REV is not given, print a summary of the current state of the
2720 repository.
2715 repository.
2721
2716
2722 Specifying a path to a repository root or Mercurial bundle will
2717 Specifying a path to a repository root or Mercurial bundle will
2723 cause lookup to operate on that repository/bundle.
2718 cause lookup to operate on that repository/bundle.
2724
2719
2725 .. container:: verbose
2720 .. container:: verbose
2726
2721
2727 Examples:
2722 Examples:
2728
2723
2729 - generate a build identifier for the working directory::
2724 - generate a build identifier for the working directory::
2730
2725
2731 hg id --id > build-id.dat
2726 hg id --id > build-id.dat
2732
2727
2733 - find the revision corresponding to a tag::
2728 - find the revision corresponding to a tag::
2734
2729
2735 hg id -n -r 1.3
2730 hg id -n -r 1.3
2736
2731
2737 - check the most recent revision of a remote repository::
2732 - check the most recent revision of a remote repository::
2738
2733
2739 hg id -r tip https://www.mercurial-scm.org/repo/hg/
2734 hg id -r tip https://www.mercurial-scm.org/repo/hg/
2740
2735
2741 See :hg:`log` for generating more information about specific revisions,
2736 See :hg:`log` for generating more information about specific revisions,
2742 including full hash identifiers.
2737 including full hash identifiers.
2743
2738
2744 Returns 0 if successful.
2739 Returns 0 if successful.
2745 """
2740 """
2746
2741
2747 opts = pycompat.byteskwargs(opts)
2742 opts = pycompat.byteskwargs(opts)
2748 if not repo and not source:
2743 if not repo and not source:
2749 raise error.Abort(_("there is no Mercurial repository here "
2744 raise error.Abort(_("there is no Mercurial repository here "
2750 "(.hg not found)"))
2745 "(.hg not found)"))
2751
2746
2752 if ui.debugflag:
2747 if ui.debugflag:
2753 hexfunc = hex
2748 hexfunc = hex
2754 else:
2749 else:
2755 hexfunc = short
2750 hexfunc = short
2756 default = not (num or id or branch or tags or bookmarks)
2751 default = not (num or id or branch or tags or bookmarks)
2757 output = []
2752 output = []
2758 revs = []
2753 revs = []
2759
2754
2760 if source:
2755 if source:
2761 source, branches = hg.parseurl(ui.expandpath(source))
2756 source, branches = hg.parseurl(ui.expandpath(source))
2762 peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo
2757 peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo
2763 repo = peer.local()
2758 repo = peer.local()
2764 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
2759 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
2765
2760
2766 fm = ui.formatter('identify', opts)
2761 fm = ui.formatter('identify', opts)
2767 fm.startitem()
2762 fm.startitem()
2768
2763
2769 if not repo:
2764 if not repo:
2770 if num or branch or tags:
2765 if num or branch or tags:
2771 raise error.Abort(
2766 raise error.Abort(
2772 _("can't query remote revision number, branch, or tags"))
2767 _("can't query remote revision number, branch, or tags"))
2773 if not rev and revs:
2768 if not rev and revs:
2774 rev = revs[0]
2769 rev = revs[0]
2775 if not rev:
2770 if not rev:
2776 rev = "tip"
2771 rev = "tip"
2777
2772
2778 remoterev = peer.lookup(rev)
2773 remoterev = peer.lookup(rev)
2779 hexrev = hexfunc(remoterev)
2774 hexrev = hexfunc(remoterev)
2780 if default or id:
2775 if default or id:
2781 output = [hexrev]
2776 output = [hexrev]
2782 fm.data(id=hexrev)
2777 fm.data(id=hexrev)
2783
2778
2784 def getbms():
2779 def getbms():
2785 bms = []
2780 bms = []
2786
2781
2787 if 'bookmarks' in peer.listkeys('namespaces'):
2782 if 'bookmarks' in peer.listkeys('namespaces'):
2788 hexremoterev = hex(remoterev)
2783 hexremoterev = hex(remoterev)
2789 bms = [bm for bm, bmr in peer.listkeys('bookmarks').iteritems()
2784 bms = [bm for bm, bmr in peer.listkeys('bookmarks').iteritems()
2790 if bmr == hexremoterev]
2785 if bmr == hexremoterev]
2791
2786
2792 return sorted(bms)
2787 return sorted(bms)
2793
2788
2794 bms = getbms()
2789 bms = getbms()
2795 if bookmarks:
2790 if bookmarks:
2796 output.extend(bms)
2791 output.extend(bms)
2797 elif default and not ui.quiet:
2792 elif default and not ui.quiet:
2798 # multiple bookmarks for a single parent separated by '/'
2793 # multiple bookmarks for a single parent separated by '/'
2799 bm = '/'.join(bms)
2794 bm = '/'.join(bms)
2800 if bm:
2795 if bm:
2801 output.append(bm)
2796 output.append(bm)
2802
2797
2803 fm.data(node=hex(remoterev))
2798 fm.data(node=hex(remoterev))
2804 fm.data(bookmarks=fm.formatlist(bms, name='bookmark'))
2799 fm.data(bookmarks=fm.formatlist(bms, name='bookmark'))
2805 else:
2800 else:
2806 if rev:
2801 if rev:
2807 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
2802 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
2808 ctx = scmutil.revsingle(repo, rev, None)
2803 ctx = scmutil.revsingle(repo, rev, None)
2809
2804
2810 if ctx.rev() is None:
2805 if ctx.rev() is None:
2811 ctx = repo[None]
2806 ctx = repo[None]
2812 parents = ctx.parents()
2807 parents = ctx.parents()
2813 taglist = []
2808 taglist = []
2814 for p in parents:
2809 for p in parents:
2815 taglist.extend(p.tags())
2810 taglist.extend(p.tags())
2816
2811
2817 dirty = ""
2812 dirty = ""
2818 if ctx.dirty(missing=True, merge=False, branch=False):
2813 if ctx.dirty(missing=True, merge=False, branch=False):
2819 dirty = '+'
2814 dirty = '+'
2820 fm.data(dirty=dirty)
2815 fm.data(dirty=dirty)
2821
2816
2822 hexoutput = [hexfunc(p.node()) for p in parents]
2817 hexoutput = [hexfunc(p.node()) for p in parents]
2823 if default or id:
2818 if default or id:
2824 output = ["%s%s" % ('+'.join(hexoutput), dirty)]
2819 output = ["%s%s" % ('+'.join(hexoutput), dirty)]
2825 fm.data(id="%s%s" % ('+'.join(hexoutput), dirty))
2820 fm.data(id="%s%s" % ('+'.join(hexoutput), dirty))
2826
2821
2827 if num:
2822 if num:
2828 numoutput = ["%d" % p.rev() for p in parents]
2823 numoutput = ["%d" % p.rev() for p in parents]
2829 output.append("%s%s" % ('+'.join(numoutput), dirty))
2824 output.append("%s%s" % ('+'.join(numoutput), dirty))
2830
2825
2831 fn = fm.nested('parents')
2826 fn = fm.nested('parents')
2832 for p in parents:
2827 for p in parents:
2833 fn.startitem()
2828 fn.startitem()
2834 fn.data(rev=p.rev())
2829 fn.data(rev=p.rev())
2835 fn.data(node=p.hex())
2830 fn.data(node=p.hex())
2836 fn.context(ctx=p)
2831 fn.context(ctx=p)
2837 fn.end()
2832 fn.end()
2838 else:
2833 else:
2839 hexoutput = hexfunc(ctx.node())
2834 hexoutput = hexfunc(ctx.node())
2840 if default or id:
2835 if default or id:
2841 output = [hexoutput]
2836 output = [hexoutput]
2842 fm.data(id=hexoutput)
2837 fm.data(id=hexoutput)
2843
2838
2844 if num:
2839 if num:
2845 output.append(pycompat.bytestr(ctx.rev()))
2840 output.append(pycompat.bytestr(ctx.rev()))
2846 taglist = ctx.tags()
2841 taglist = ctx.tags()
2847
2842
2848 if default and not ui.quiet:
2843 if default and not ui.quiet:
2849 b = ctx.branch()
2844 b = ctx.branch()
2850 if b != 'default':
2845 if b != 'default':
2851 output.append("(%s)" % b)
2846 output.append("(%s)" % b)
2852
2847
2853 # multiple tags for a single parent separated by '/'
2848 # multiple tags for a single parent separated by '/'
2854 t = '/'.join(taglist)
2849 t = '/'.join(taglist)
2855 if t:
2850 if t:
2856 output.append(t)
2851 output.append(t)
2857
2852
2858 # multiple bookmarks for a single parent separated by '/'
2853 # multiple bookmarks for a single parent separated by '/'
2859 bm = '/'.join(ctx.bookmarks())
2854 bm = '/'.join(ctx.bookmarks())
2860 if bm:
2855 if bm:
2861 output.append(bm)
2856 output.append(bm)
2862 else:
2857 else:
2863 if branch:
2858 if branch:
2864 output.append(ctx.branch())
2859 output.append(ctx.branch())
2865
2860
2866 if tags:
2861 if tags:
2867 output.extend(taglist)
2862 output.extend(taglist)
2868
2863
2869 if bookmarks:
2864 if bookmarks:
2870 output.extend(ctx.bookmarks())
2865 output.extend(ctx.bookmarks())
2871
2866
2872 fm.data(node=ctx.hex())
2867 fm.data(node=ctx.hex())
2873 fm.data(branch=ctx.branch())
2868 fm.data(branch=ctx.branch())
2874 fm.data(tags=fm.formatlist(taglist, name='tag', sep=':'))
2869 fm.data(tags=fm.formatlist(taglist, name='tag', sep=':'))
2875 fm.data(bookmarks=fm.formatlist(ctx.bookmarks(), name='bookmark'))
2870 fm.data(bookmarks=fm.formatlist(ctx.bookmarks(), name='bookmark'))
2876 fm.context(ctx=ctx)
2871 fm.context(ctx=ctx)
2877
2872
2878 fm.plain("%s\n" % ' '.join(output))
2873 fm.plain("%s\n" % ' '.join(output))
2879 fm.end()
2874 fm.end()
2880
2875
2881 @command('import|patch',
2876 @command('import|patch',
2882 [('p', 'strip', 1,
2877 [('p', 'strip', 1,
2883 _('directory strip option for patch. This has the same '
2878 _('directory strip option for patch. This has the same '
2884 'meaning as the corresponding patch option'), _('NUM')),
2879 'meaning as the corresponding patch option'), _('NUM')),
2885 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
2880 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
2886 ('e', 'edit', False, _('invoke editor on commit messages')),
2881 ('e', 'edit', False, _('invoke editor on commit messages')),
2887 ('f', 'force', None,
2882 ('f', 'force', None,
2888 _('skip check for outstanding uncommitted changes (DEPRECATED)')),
2883 _('skip check for outstanding uncommitted changes (DEPRECATED)')),
2889 ('', 'no-commit', None,
2884 ('', 'no-commit', None,
2890 _("don't commit, just update the working directory")),
2885 _("don't commit, just update the working directory")),
2891 ('', 'bypass', None,
2886 ('', 'bypass', None,
2892 _("apply patch without touching the working directory")),
2887 _("apply patch without touching the working directory")),
2893 ('', 'partial', None,
2888 ('', 'partial', None,
2894 _('commit even if some hunks fail')),
2889 _('commit even if some hunks fail')),
2895 ('', 'exact', None,
2890 ('', 'exact', None,
2896 _('abort if patch would apply lossily')),
2891 _('abort if patch would apply lossily')),
2897 ('', 'prefix', '',
2892 ('', 'prefix', '',
2898 _('apply patch to subdirectory'), _('DIR')),
2893 _('apply patch to subdirectory'), _('DIR')),
2899 ('', 'import-branch', None,
2894 ('', 'import-branch', None,
2900 _('use any branch information in patch (implied by --exact)'))] +
2895 _('use any branch information in patch (implied by --exact)'))] +
2901 commitopts + commitopts2 + similarityopts,
2896 commitopts + commitopts2 + similarityopts,
2902 _('[OPTION]... PATCH...'))
2897 _('[OPTION]... PATCH...'))
2903 def import_(ui, repo, patch1=None, *patches, **opts):
2898 def import_(ui, repo, patch1=None, *patches, **opts):
2904 """import an ordered set of patches
2899 """import an ordered set of patches
2905
2900
2906 Import a list of patches and commit them individually (unless
2901 Import a list of patches and commit them individually (unless
2907 --no-commit is specified).
2902 --no-commit is specified).
2908
2903
2909 To read a patch from standard input (stdin), use "-" as the patch
2904 To read a patch from standard input (stdin), use "-" as the patch
2910 name. If a URL is specified, the patch will be downloaded from
2905 name. If a URL is specified, the patch will be downloaded from
2911 there.
2906 there.
2912
2907
2913 Import first applies changes to the working directory (unless
2908 Import first applies changes to the working directory (unless
2914 --bypass is specified), import will abort if there are outstanding
2909 --bypass is specified), import will abort if there are outstanding
2915 changes.
2910 changes.
2916
2911
2917 Use --bypass to apply and commit patches directly to the
2912 Use --bypass to apply and commit patches directly to the
2918 repository, without affecting the working directory. Without
2913 repository, without affecting the working directory. Without
2919 --exact, patches will be applied on top of the working directory
2914 --exact, patches will be applied on top of the working directory
2920 parent revision.
2915 parent revision.
2921
2916
2922 You can import a patch straight from a mail message. Even patches
2917 You can import a patch straight from a mail message. Even patches
2923 as attachments work (to use the body part, it must have type
2918 as attachments work (to use the body part, it must have type
2924 text/plain or text/x-patch). From and Subject headers of email
2919 text/plain or text/x-patch). From and Subject headers of email
2925 message are used as default committer and commit message. All
2920 message are used as default committer and commit message. All
2926 text/plain body parts before first diff are added to the commit
2921 text/plain body parts before first diff are added to the commit
2927 message.
2922 message.
2928
2923
2929 If the imported patch was generated by :hg:`export`, user and
2924 If the imported patch was generated by :hg:`export`, user and
2930 description from patch override values from message headers and
2925 description from patch override values from message headers and
2931 body. Values given on command line with -m/--message and -u/--user
2926 body. Values given on command line with -m/--message and -u/--user
2932 override these.
2927 override these.
2933
2928
2934 If --exact is specified, import will set the working directory to
2929 If --exact is specified, import will set the working directory to
2935 the parent of each patch before applying it, and will abort if the
2930 the parent of each patch before applying it, and will abort if the
2936 resulting changeset has a different ID than the one recorded in
2931 resulting changeset has a different ID than the one recorded in
2937 the patch. This will guard against various ways that portable
2932 the patch. This will guard against various ways that portable
2938 patch formats and mail systems might fail to transfer Mercurial
2933 patch formats and mail systems might fail to transfer Mercurial
2939 data or metadata. See :hg:`bundle` for lossless transmission.
2934 data or metadata. See :hg:`bundle` for lossless transmission.
2940
2935
2941 Use --partial to ensure a changeset will be created from the patch
2936 Use --partial to ensure a changeset will be created from the patch
2942 even if some hunks fail to apply. Hunks that fail to apply will be
2937 even if some hunks fail to apply. Hunks that fail to apply will be
2943 written to a <target-file>.rej file. Conflicts can then be resolved
2938 written to a <target-file>.rej file. Conflicts can then be resolved
2944 by hand before :hg:`commit --amend` is run to update the created
2939 by hand before :hg:`commit --amend` is run to update the created
2945 changeset. This flag exists to let people import patches that
2940 changeset. This flag exists to let people import patches that
2946 partially apply without losing the associated metadata (author,
2941 partially apply without losing the associated metadata (author,
2947 date, description, ...).
2942 date, description, ...).
2948
2943
2949 .. note::
2944 .. note::
2950
2945
2951 When no hunks apply cleanly, :hg:`import --partial` will create
2946 When no hunks apply cleanly, :hg:`import --partial` will create
2952 an empty changeset, importing only the patch metadata.
2947 an empty changeset, importing only the patch metadata.
2953
2948
2954 With -s/--similarity, hg will attempt to discover renames and
2949 With -s/--similarity, hg will attempt to discover renames and
2955 copies in the patch in the same way as :hg:`addremove`.
2950 copies in the patch in the same way as :hg:`addremove`.
2956
2951
2957 It is possible to use external patch programs to perform the patch
2952 It is possible to use external patch programs to perform the patch
2958 by setting the ``ui.patch`` configuration option. For the default
2953 by setting the ``ui.patch`` configuration option. For the default
2959 internal tool, the fuzz can also be configured via ``patch.fuzz``.
2954 internal tool, the fuzz can also be configured via ``patch.fuzz``.
2960 See :hg:`help config` for more information about configuration
2955 See :hg:`help config` for more information about configuration
2961 files and how to use these options.
2956 files and how to use these options.
2962
2957
2963 See :hg:`help dates` for a list of formats valid for -d/--date.
2958 See :hg:`help dates` for a list of formats valid for -d/--date.
2964
2959
2965 .. container:: verbose
2960 .. container:: verbose
2966
2961
2967 Examples:
2962 Examples:
2968
2963
2969 - import a traditional patch from a website and detect renames::
2964 - import a traditional patch from a website and detect renames::
2970
2965
2971 hg import -s 80 http://example.com/bugfix.patch
2966 hg import -s 80 http://example.com/bugfix.patch
2972
2967
2973 - import a changeset from an hgweb server::
2968 - import a changeset from an hgweb server::
2974
2969
2975 hg import https://www.mercurial-scm.org/repo/hg/rev/5ca8c111e9aa
2970 hg import https://www.mercurial-scm.org/repo/hg/rev/5ca8c111e9aa
2976
2971
2977 - import all the patches in an Unix-style mbox::
2972 - import all the patches in an Unix-style mbox::
2978
2973
2979 hg import incoming-patches.mbox
2974 hg import incoming-patches.mbox
2980
2975
2981 - import patches from stdin::
2976 - import patches from stdin::
2982
2977
2983 hg import -
2978 hg import -
2984
2979
2985 - attempt to exactly restore an exported changeset (not always
2980 - attempt to exactly restore an exported changeset (not always
2986 possible)::
2981 possible)::
2987
2982
2988 hg import --exact proposed-fix.patch
2983 hg import --exact proposed-fix.patch
2989
2984
2990 - use an external tool to apply a patch which is too fuzzy for
2985 - use an external tool to apply a patch which is too fuzzy for
2991 the default internal tool.
2986 the default internal tool.
2992
2987
2993 hg import --config ui.patch="patch --merge" fuzzy.patch
2988 hg import --config ui.patch="patch --merge" fuzzy.patch
2994
2989
2995 - change the default fuzzing from 2 to a less strict 7
2990 - change the default fuzzing from 2 to a less strict 7
2996
2991
2997 hg import --config ui.fuzz=7 fuzz.patch
2992 hg import --config ui.fuzz=7 fuzz.patch
2998
2993
2999 Returns 0 on success, 1 on partial success (see --partial).
2994 Returns 0 on success, 1 on partial success (see --partial).
3000 """
2995 """
3001
2996
3002 opts = pycompat.byteskwargs(opts)
2997 opts = pycompat.byteskwargs(opts)
3003 if not patch1:
2998 if not patch1:
3004 raise error.Abort(_('need at least one patch to import'))
2999 raise error.Abort(_('need at least one patch to import'))
3005
3000
3006 patches = (patch1,) + patches
3001 patches = (patch1,) + patches
3007
3002
3008 date = opts.get('date')
3003 date = opts.get('date')
3009 if date:
3004 if date:
3010 opts['date'] = util.parsedate(date)
3005 opts['date'] = util.parsedate(date)
3011
3006
3012 exact = opts.get('exact')
3007 exact = opts.get('exact')
3013 update = not opts.get('bypass')
3008 update = not opts.get('bypass')
3014 if not update and opts.get('no_commit'):
3009 if not update and opts.get('no_commit'):
3015 raise error.Abort(_('cannot use --no-commit with --bypass'))
3010 raise error.Abort(_('cannot use --no-commit with --bypass'))
3016 try:
3011 try:
3017 sim = float(opts.get('similarity') or 0)
3012 sim = float(opts.get('similarity') or 0)
3018 except ValueError:
3013 except ValueError:
3019 raise error.Abort(_('similarity must be a number'))
3014 raise error.Abort(_('similarity must be a number'))
3020 if sim < 0 or sim > 100:
3015 if sim < 0 or sim > 100:
3021 raise error.Abort(_('similarity must be between 0 and 100'))
3016 raise error.Abort(_('similarity must be between 0 and 100'))
3022 if sim and not update:
3017 if sim and not update:
3023 raise error.Abort(_('cannot use --similarity with --bypass'))
3018 raise error.Abort(_('cannot use --similarity with --bypass'))
3024 if exact:
3019 if exact:
3025 if opts.get('edit'):
3020 if opts.get('edit'):
3026 raise error.Abort(_('cannot use --exact with --edit'))
3021 raise error.Abort(_('cannot use --exact with --edit'))
3027 if opts.get('prefix'):
3022 if opts.get('prefix'):
3028 raise error.Abort(_('cannot use --exact with --prefix'))
3023 raise error.Abort(_('cannot use --exact with --prefix'))
3029
3024
3030 base = opts["base"]
3025 base = opts["base"]
3031 wlock = dsguard = lock = tr = None
3026 wlock = dsguard = lock = tr = None
3032 msgs = []
3027 msgs = []
3033 ret = 0
3028 ret = 0
3034
3029
3035
3030
3036 try:
3031 try:
3037 wlock = repo.wlock()
3032 wlock = repo.wlock()
3038
3033
3039 if update:
3034 if update:
3040 cmdutil.checkunfinished(repo)
3035 cmdutil.checkunfinished(repo)
3041 if (exact or not opts.get('force')):
3036 if (exact or not opts.get('force')):
3042 cmdutil.bailifchanged(repo)
3037 cmdutil.bailifchanged(repo)
3043
3038
3044 if not opts.get('no_commit'):
3039 if not opts.get('no_commit'):
3045 lock = repo.lock()
3040 lock = repo.lock()
3046 tr = repo.transaction('import')
3041 tr = repo.transaction('import')
3047 else:
3042 else:
3048 dsguard = dirstateguard.dirstateguard(repo, 'import')
3043 dsguard = dirstateguard.dirstateguard(repo, 'import')
3049 parents = repo[None].parents()
3044 parents = repo[None].parents()
3050 for patchurl in patches:
3045 for patchurl in patches:
3051 if patchurl == '-':
3046 if patchurl == '-':
3052 ui.status(_('applying patch from stdin\n'))
3047 ui.status(_('applying patch from stdin\n'))
3053 patchfile = ui.fin
3048 patchfile = ui.fin
3054 patchurl = 'stdin' # for error message
3049 patchurl = 'stdin' # for error message
3055 else:
3050 else:
3056 patchurl = os.path.join(base, patchurl)
3051 patchurl = os.path.join(base, patchurl)
3057 ui.status(_('applying %s\n') % patchurl)
3052 ui.status(_('applying %s\n') % patchurl)
3058 patchfile = hg.openpath(ui, patchurl)
3053 patchfile = hg.openpath(ui, patchurl)
3059
3054
3060 haspatch = False
3055 haspatch = False
3061 for hunk in patch.split(patchfile):
3056 for hunk in patch.split(patchfile):
3062 (msg, node, rej) = cmdutil.tryimportone(ui, repo, hunk,
3057 (msg, node, rej) = cmdutil.tryimportone(ui, repo, hunk,
3063 parents, opts,
3058 parents, opts,
3064 msgs, hg.clean)
3059 msgs, hg.clean)
3065 if msg:
3060 if msg:
3066 haspatch = True
3061 haspatch = True
3067 ui.note(msg + '\n')
3062 ui.note(msg + '\n')
3068 if update or exact:
3063 if update or exact:
3069 parents = repo[None].parents()
3064 parents = repo[None].parents()
3070 else:
3065 else:
3071 parents = [repo[node]]
3066 parents = [repo[node]]
3072 if rej:
3067 if rej:
3073 ui.write_err(_("patch applied partially\n"))
3068 ui.write_err(_("patch applied partially\n"))
3074 ui.write_err(_("(fix the .rej files and run "
3069 ui.write_err(_("(fix the .rej files and run "
3075 "`hg commit --amend`)\n"))
3070 "`hg commit --amend`)\n"))
3076 ret = 1
3071 ret = 1
3077 break
3072 break
3078
3073
3079 if not haspatch:
3074 if not haspatch:
3080 raise error.Abort(_('%s: no diffs found') % patchurl)
3075 raise error.Abort(_('%s: no diffs found') % patchurl)
3081
3076
3082 if tr:
3077 if tr:
3083 tr.close()
3078 tr.close()
3084 if msgs:
3079 if msgs:
3085 repo.savecommitmessage('\n* * *\n'.join(msgs))
3080 repo.savecommitmessage('\n* * *\n'.join(msgs))
3086 if dsguard:
3081 if dsguard:
3087 dsguard.close()
3082 dsguard.close()
3088 return ret
3083 return ret
3089 finally:
3084 finally:
3090 if tr:
3085 if tr:
3091 tr.release()
3086 tr.release()
3092 release(lock, dsguard, wlock)
3087 release(lock, dsguard, wlock)
3093
3088
3094 @command('incoming|in',
3089 @command('incoming|in',
3095 [('f', 'force', None,
3090 [('f', 'force', None,
3096 _('run even if remote repository is unrelated')),
3091 _('run even if remote repository is unrelated')),
3097 ('n', 'newest-first', None, _('show newest record first')),
3092 ('n', 'newest-first', None, _('show newest record first')),
3098 ('', 'bundle', '',
3093 ('', 'bundle', '',
3099 _('file to store the bundles into'), _('FILE')),
3094 _('file to store the bundles into'), _('FILE')),
3100 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3095 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3101 ('B', 'bookmarks', False, _("compare bookmarks")),
3096 ('B', 'bookmarks', False, _("compare bookmarks")),
3102 ('b', 'branch', [],
3097 ('b', 'branch', [],
3103 _('a specific branch you would like to pull'), _('BRANCH')),
3098 _('a specific branch you would like to pull'), _('BRANCH')),
3104 ] + logopts + remoteopts + subrepoopts,
3099 ] + logopts + remoteopts + subrepoopts,
3105 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'))
3100 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'))
3106 def incoming(ui, repo, source="default", **opts):
3101 def incoming(ui, repo, source="default", **opts):
3107 """show new changesets found in source
3102 """show new changesets found in source
3108
3103
3109 Show new changesets found in the specified path/URL or the default
3104 Show new changesets found in the specified path/URL or the default
3110 pull location. These are the changesets that would have been pulled
3105 pull location. These are the changesets that would have been pulled
3111 by :hg:`pull` at the time you issued this command.
3106 by :hg:`pull` at the time you issued this command.
3112
3107
3113 See pull for valid source format details.
3108 See pull for valid source format details.
3114
3109
3115 .. container:: verbose
3110 .. container:: verbose
3116
3111
3117 With -B/--bookmarks, the result of bookmark comparison between
3112 With -B/--bookmarks, the result of bookmark comparison between
3118 local and remote repositories is displayed. With -v/--verbose,
3113 local and remote repositories is displayed. With -v/--verbose,
3119 status is also displayed for each bookmark like below::
3114 status is also displayed for each bookmark like below::
3120
3115
3121 BM1 01234567890a added
3116 BM1 01234567890a added
3122 BM2 1234567890ab advanced
3117 BM2 1234567890ab advanced
3123 BM3 234567890abc diverged
3118 BM3 234567890abc diverged
3124 BM4 34567890abcd changed
3119 BM4 34567890abcd changed
3125
3120
3126 The action taken locally when pulling depends on the
3121 The action taken locally when pulling depends on the
3127 status of each bookmark:
3122 status of each bookmark:
3128
3123
3129 :``added``: pull will create it
3124 :``added``: pull will create it
3130 :``advanced``: pull will update it
3125 :``advanced``: pull will update it
3131 :``diverged``: pull will create a divergent bookmark
3126 :``diverged``: pull will create a divergent bookmark
3132 :``changed``: result depends on remote changesets
3127 :``changed``: result depends on remote changesets
3133
3128
3134 From the point of view of pulling behavior, bookmark
3129 From the point of view of pulling behavior, bookmark
3135 existing only in the remote repository are treated as ``added``,
3130 existing only in the remote repository are treated as ``added``,
3136 even if it is in fact locally deleted.
3131 even if it is in fact locally deleted.
3137
3132
3138 .. container:: verbose
3133 .. container:: verbose
3139
3134
3140 For remote repository, using --bundle avoids downloading the
3135 For remote repository, using --bundle avoids downloading the
3141 changesets twice if the incoming is followed by a pull.
3136 changesets twice if the incoming is followed by a pull.
3142
3137
3143 Examples:
3138 Examples:
3144
3139
3145 - show incoming changes with patches and full description::
3140 - show incoming changes with patches and full description::
3146
3141
3147 hg incoming -vp
3142 hg incoming -vp
3148
3143
3149 - show incoming changes excluding merges, store a bundle::
3144 - show incoming changes excluding merges, store a bundle::
3150
3145
3151 hg in -vpM --bundle incoming.hg
3146 hg in -vpM --bundle incoming.hg
3152 hg pull incoming.hg
3147 hg pull incoming.hg
3153
3148
3154 - briefly list changes inside a bundle::
3149 - briefly list changes inside a bundle::
3155
3150
3156 hg in changes.hg -T "{desc|firstline}\\n"
3151 hg in changes.hg -T "{desc|firstline}\\n"
3157
3152
3158 Returns 0 if there are incoming changes, 1 otherwise.
3153 Returns 0 if there are incoming changes, 1 otherwise.
3159 """
3154 """
3160 opts = pycompat.byteskwargs(opts)
3155 opts = pycompat.byteskwargs(opts)
3161 if opts.get('graph'):
3156 if opts.get('graph'):
3162 cmdutil.checkunsupportedgraphflags([], opts)
3157 cmdutil.checkunsupportedgraphflags([], opts)
3163 def display(other, chlist, displayer):
3158 def display(other, chlist, displayer):
3164 revdag = cmdutil.graphrevs(other, chlist, opts)
3159 revdag = cmdutil.graphrevs(other, chlist, opts)
3165 cmdutil.displaygraph(ui, repo, revdag, displayer,
3160 cmdutil.displaygraph(ui, repo, revdag, displayer,
3166 graphmod.asciiedges)
3161 graphmod.asciiedges)
3167
3162
3168 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
3163 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
3169 return 0
3164 return 0
3170
3165
3171 if opts.get('bundle') and opts.get('subrepos'):
3166 if opts.get('bundle') and opts.get('subrepos'):
3172 raise error.Abort(_('cannot combine --bundle and --subrepos'))
3167 raise error.Abort(_('cannot combine --bundle and --subrepos'))
3173
3168
3174 if opts.get('bookmarks'):
3169 if opts.get('bookmarks'):
3175 source, branches = hg.parseurl(ui.expandpath(source),
3170 source, branches = hg.parseurl(ui.expandpath(source),
3176 opts.get('branch'))
3171 opts.get('branch'))
3177 other = hg.peer(repo, opts, source)
3172 other = hg.peer(repo, opts, source)
3178 if 'bookmarks' not in other.listkeys('namespaces'):
3173 if 'bookmarks' not in other.listkeys('namespaces'):
3179 ui.warn(_("remote doesn't support bookmarks\n"))
3174 ui.warn(_("remote doesn't support bookmarks\n"))
3180 return 0
3175 return 0
3181 ui.pager('incoming')
3176 ui.pager('incoming')
3182 ui.status(_('comparing with %s\n') % util.hidepassword(source))
3177 ui.status(_('comparing with %s\n') % util.hidepassword(source))
3183 return bookmarks.incoming(ui, repo, other)
3178 return bookmarks.incoming(ui, repo, other)
3184
3179
3185 repo._subtoppath = ui.expandpath(source)
3180 repo._subtoppath = ui.expandpath(source)
3186 try:
3181 try:
3187 return hg.incoming(ui, repo, source, opts)
3182 return hg.incoming(ui, repo, source, opts)
3188 finally:
3183 finally:
3189 del repo._subtoppath
3184 del repo._subtoppath
3190
3185
3191
3186
3192 @command('^init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'),
3187 @command('^init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'),
3193 norepo=True)
3188 norepo=True)
3194 def init(ui, dest=".", **opts):
3189 def init(ui, dest=".", **opts):
3195 """create a new repository in the given directory
3190 """create a new repository in the given directory
3196
3191
3197 Initialize a new repository in the given directory. If the given
3192 Initialize a new repository in the given directory. If the given
3198 directory does not exist, it will be created.
3193 directory does not exist, it will be created.
3199
3194
3200 If no directory is given, the current directory is used.
3195 If no directory is given, the current directory is used.
3201
3196
3202 It is possible to specify an ``ssh://`` URL as the destination.
3197 It is possible to specify an ``ssh://`` URL as the destination.
3203 See :hg:`help urls` for more information.
3198 See :hg:`help urls` for more information.
3204
3199
3205 Returns 0 on success.
3200 Returns 0 on success.
3206 """
3201 """
3207 opts = pycompat.byteskwargs(opts)
3202 opts = pycompat.byteskwargs(opts)
3208 hg.peer(ui, opts, ui.expandpath(dest), create=True)
3203 hg.peer(ui, opts, ui.expandpath(dest), create=True)
3209
3204
3210 @command('locate',
3205 @command('locate',
3211 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
3206 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
3212 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
3207 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
3213 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
3208 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
3214 ] + walkopts,
3209 ] + walkopts,
3215 _('[OPTION]... [PATTERN]...'))
3210 _('[OPTION]... [PATTERN]...'))
3216 def locate(ui, repo, *pats, **opts):
3211 def locate(ui, repo, *pats, **opts):
3217 """locate files matching specific patterns (DEPRECATED)
3212 """locate files matching specific patterns (DEPRECATED)
3218
3213
3219 Print files under Mercurial control in the working directory whose
3214 Print files under Mercurial control in the working directory whose
3220 names match the given patterns.
3215 names match the given patterns.
3221
3216
3222 By default, this command searches all directories in the working
3217 By default, this command searches all directories in the working
3223 directory. To search just the current directory and its
3218 directory. To search just the current directory and its
3224 subdirectories, use "--include .".
3219 subdirectories, use "--include .".
3225
3220
3226 If no patterns are given to match, this command prints the names
3221 If no patterns are given to match, this command prints the names
3227 of all files under Mercurial control in the working directory.
3222 of all files under Mercurial control in the working directory.
3228
3223
3229 If you want to feed the output of this command into the "xargs"
3224 If you want to feed the output of this command into the "xargs"
3230 command, use the -0 option to both this command and "xargs". This
3225 command, use the -0 option to both this command and "xargs". This
3231 will avoid the problem of "xargs" treating single filenames that
3226 will avoid the problem of "xargs" treating single filenames that
3232 contain whitespace as multiple filenames.
3227 contain whitespace as multiple filenames.
3233
3228
3234 See :hg:`help files` for a more versatile command.
3229 See :hg:`help files` for a more versatile command.
3235
3230
3236 Returns 0 if a match is found, 1 otherwise.
3231 Returns 0 if a match is found, 1 otherwise.
3237 """
3232 """
3238 opts = pycompat.byteskwargs(opts)
3233 opts = pycompat.byteskwargs(opts)
3239 if opts.get('print0'):
3234 if opts.get('print0'):
3240 end = '\0'
3235 end = '\0'
3241 else:
3236 else:
3242 end = '\n'
3237 end = '\n'
3243 rev = scmutil.revsingle(repo, opts.get('rev'), None).node()
3238 rev = scmutil.revsingle(repo, opts.get('rev'), None).node()
3244
3239
3245 ret = 1
3240 ret = 1
3246 ctx = repo[rev]
3241 ctx = repo[rev]
3247 m = scmutil.match(ctx, pats, opts, default='relglob',
3242 m = scmutil.match(ctx, pats, opts, default='relglob',
3248 badfn=lambda x, y: False)
3243 badfn=lambda x, y: False)
3249
3244
3250 ui.pager('locate')
3245 ui.pager('locate')
3251 for abs in ctx.matches(m):
3246 for abs in ctx.matches(m):
3252 if opts.get('fullpath'):
3247 if opts.get('fullpath'):
3253 ui.write(repo.wjoin(abs), end)
3248 ui.write(repo.wjoin(abs), end)
3254 else:
3249 else:
3255 ui.write(((pats and m.rel(abs)) or abs), end)
3250 ui.write(((pats and m.rel(abs)) or abs), end)
3256 ret = 0
3251 ret = 0
3257
3252
3258 return ret
3253 return ret
3259
3254
3260 @command('^log|history',
3255 @command('^log|history',
3261 [('f', 'follow', None,
3256 [('f', 'follow', None,
3262 _('follow changeset history, or file history across copies and renames')),
3257 _('follow changeset history, or file history across copies and renames')),
3263 ('', 'follow-first', None,
3258 ('', 'follow-first', None,
3264 _('only follow the first parent of merge changesets (DEPRECATED)')),
3259 _('only follow the first parent of merge changesets (DEPRECATED)')),
3265 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
3260 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
3266 ('C', 'copies', None, _('show copied files')),
3261 ('C', 'copies', None, _('show copied files')),
3267 ('k', 'keyword', [],
3262 ('k', 'keyword', [],
3268 _('do case-insensitive search for a given text'), _('TEXT')),
3263 _('do case-insensitive search for a given text'), _('TEXT')),
3269 ('r', 'rev', [], _('show the specified revision or revset'), _('REV')),
3264 ('r', 'rev', [], _('show the specified revision or revset'), _('REV')),
3270 ('L', 'line-range', [],
3265 ('L', 'line-range', [],
3271 _('follow line range of specified file (EXPERIMENTAL)'),
3266 _('follow line range of specified file (EXPERIMENTAL)'),
3272 _('FILE,RANGE')),
3267 _('FILE,RANGE')),
3273 ('', 'removed', None, _('include revisions where files were removed')),
3268 ('', 'removed', None, _('include revisions where files were removed')),
3274 ('m', 'only-merges', None, _('show only merges (DEPRECATED)')),
3269 ('m', 'only-merges', None, _('show only merges (DEPRECATED)')),
3275 ('u', 'user', [], _('revisions committed by user'), _('USER')),
3270 ('u', 'user', [], _('revisions committed by user'), _('USER')),
3276 ('', 'only-branch', [],
3271 ('', 'only-branch', [],
3277 _('show only changesets within the given named branch (DEPRECATED)'),
3272 _('show only changesets within the given named branch (DEPRECATED)'),
3278 _('BRANCH')),
3273 _('BRANCH')),
3279 ('b', 'branch', [],
3274 ('b', 'branch', [],
3280 _('show changesets within the given named branch'), _('BRANCH')),
3275 _('show changesets within the given named branch'), _('BRANCH')),
3281 ('P', 'prune', [],
3276 ('P', 'prune', [],
3282 _('do not display revision or any of its ancestors'), _('REV')),
3277 _('do not display revision or any of its ancestors'), _('REV')),
3283 ] + logopts + walkopts,
3278 ] + logopts + walkopts,
3284 _('[OPTION]... [FILE]'),
3279 _('[OPTION]... [FILE]'),
3285 inferrepo=True, cmdtype=readonly)
3280 inferrepo=True, cmdtype=readonly)
3286 def log(ui, repo, *pats, **opts):
3281 def log(ui, repo, *pats, **opts):
3287 """show revision history of entire repository or files
3282 """show revision history of entire repository or files
3288
3283
3289 Print the revision history of the specified files or the entire
3284 Print the revision history of the specified files or the entire
3290 project.
3285 project.
3291
3286
3292 If no revision range is specified, the default is ``tip:0`` unless
3287 If no revision range is specified, the default is ``tip:0`` unless
3293 --follow is set, in which case the working directory parent is
3288 --follow is set, in which case the working directory parent is
3294 used as the starting revision.
3289 used as the starting revision.
3295
3290
3296 File history is shown without following rename or copy history of
3291 File history is shown without following rename or copy history of
3297 files. Use -f/--follow with a filename to follow history across
3292 files. Use -f/--follow with a filename to follow history across
3298 renames and copies. --follow without a filename will only show
3293 renames and copies. --follow without a filename will only show
3299 ancestors of the starting revision.
3294 ancestors of the starting revision.
3300
3295
3301 By default this command prints revision number and changeset id,
3296 By default this command prints revision number and changeset id,
3302 tags, non-trivial parents, user, date and time, and a summary for
3297 tags, non-trivial parents, user, date and time, and a summary for
3303 each commit. When the -v/--verbose switch is used, the list of
3298 each commit. When the -v/--verbose switch is used, the list of
3304 changed files and full commit message are shown.
3299 changed files and full commit message are shown.
3305
3300
3306 With --graph the revisions are shown as an ASCII art DAG with the most
3301 With --graph the revisions are shown as an ASCII art DAG with the most
3307 recent changeset at the top.
3302 recent changeset at the top.
3308 'o' is a changeset, '@' is a working directory parent, 'x' is obsolete,
3303 'o' is a changeset, '@' is a working directory parent, 'x' is obsolete,
3309 and '+' represents a fork where the changeset from the lines below is a
3304 and '+' represents a fork where the changeset from the lines below is a
3310 parent of the 'o' merge on the same line.
3305 parent of the 'o' merge on the same line.
3311 Paths in the DAG are represented with '|', '/' and so forth. ':' in place
3306 Paths in the DAG are represented with '|', '/' and so forth. ':' in place
3312 of a '|' indicates one or more revisions in a path are omitted.
3307 of a '|' indicates one or more revisions in a path are omitted.
3313
3308
3314 .. container:: verbose
3309 .. container:: verbose
3315
3310
3316 Use -L/--line-range FILE,M:N options to follow the history of lines
3311 Use -L/--line-range FILE,M:N options to follow the history of lines
3317 from M to N in FILE. With -p/--patch only diff hunks affecting
3312 from M to N in FILE. With -p/--patch only diff hunks affecting
3318 specified line range will be shown. This option requires --follow;
3313 specified line range will be shown. This option requires --follow;
3319 it can be specified multiple times. Currently, this option is not
3314 it can be specified multiple times. Currently, this option is not
3320 compatible with --graph. This option is experimental.
3315 compatible with --graph. This option is experimental.
3321
3316
3322 .. note::
3317 .. note::
3323
3318
3324 :hg:`log --patch` may generate unexpected diff output for merge
3319 :hg:`log --patch` may generate unexpected diff output for merge
3325 changesets, as it will only compare the merge changeset against
3320 changesets, as it will only compare the merge changeset against
3326 its first parent. Also, only files different from BOTH parents
3321 its first parent. Also, only files different from BOTH parents
3327 will appear in files:.
3322 will appear in files:.
3328
3323
3329 .. note::
3324 .. note::
3330
3325
3331 For performance reasons, :hg:`log FILE` may omit duplicate changes
3326 For performance reasons, :hg:`log FILE` may omit duplicate changes
3332 made on branches and will not show removals or mode changes. To
3327 made on branches and will not show removals or mode changes. To
3333 see all such changes, use the --removed switch.
3328 see all such changes, use the --removed switch.
3334
3329
3335 .. container:: verbose
3330 .. container:: verbose
3336
3331
3337 .. note::
3332 .. note::
3338
3333
3339 The history resulting from -L/--line-range options depends on diff
3334 The history resulting from -L/--line-range options depends on diff
3340 options; for instance if white-spaces are ignored, respective changes
3335 options; for instance if white-spaces are ignored, respective changes
3341 with only white-spaces in specified line range will not be listed.
3336 with only white-spaces in specified line range will not be listed.
3342
3337
3343 .. container:: verbose
3338 .. container:: verbose
3344
3339
3345 Some examples:
3340 Some examples:
3346
3341
3347 - changesets with full descriptions and file lists::
3342 - changesets with full descriptions and file lists::
3348
3343
3349 hg log -v
3344 hg log -v
3350
3345
3351 - changesets ancestral to the working directory::
3346 - changesets ancestral to the working directory::
3352
3347
3353 hg log -f
3348 hg log -f
3354
3349
3355 - last 10 commits on the current branch::
3350 - last 10 commits on the current branch::
3356
3351
3357 hg log -l 10 -b .
3352 hg log -l 10 -b .
3358
3353
3359 - changesets showing all modifications of a file, including removals::
3354 - changesets showing all modifications of a file, including removals::
3360
3355
3361 hg log --removed file.c
3356 hg log --removed file.c
3362
3357
3363 - all changesets that touch a directory, with diffs, excluding merges::
3358 - all changesets that touch a directory, with diffs, excluding merges::
3364
3359
3365 hg log -Mp lib/
3360 hg log -Mp lib/
3366
3361
3367 - all revision numbers that match a keyword::
3362 - all revision numbers that match a keyword::
3368
3363
3369 hg log -k bug --template "{rev}\\n"
3364 hg log -k bug --template "{rev}\\n"
3370
3365
3371 - the full hash identifier of the working directory parent::
3366 - the full hash identifier of the working directory parent::
3372
3367
3373 hg log -r . --template "{node}\\n"
3368 hg log -r . --template "{node}\\n"
3374
3369
3375 - list available log templates::
3370 - list available log templates::
3376
3371
3377 hg log -T list
3372 hg log -T list
3378
3373
3379 - check if a given changeset is included in a tagged release::
3374 - check if a given changeset is included in a tagged release::
3380
3375
3381 hg log -r "a21ccf and ancestor(1.9)"
3376 hg log -r "a21ccf and ancestor(1.9)"
3382
3377
3383 - find all changesets by some user in a date range::
3378 - find all changesets by some user in a date range::
3384
3379
3385 hg log -k alice -d "may 2008 to jul 2008"
3380 hg log -k alice -d "may 2008 to jul 2008"
3386
3381
3387 - summary of all changesets after the last tag::
3382 - summary of all changesets after the last tag::
3388
3383
3389 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
3384 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
3390
3385
3391 - changesets touching lines 13 to 23 for file.c::
3386 - changesets touching lines 13 to 23 for file.c::
3392
3387
3393 hg log -L file.c,13:23
3388 hg log -L file.c,13:23
3394
3389
3395 - changesets touching lines 13 to 23 for file.c and lines 2 to 6 of
3390 - changesets touching lines 13 to 23 for file.c and lines 2 to 6 of
3396 main.c with patch::
3391 main.c with patch::
3397
3392
3398 hg log -L file.c,13:23 -L main.c,2:6 -p
3393 hg log -L file.c,13:23 -L main.c,2:6 -p
3399
3394
3400 See :hg:`help dates` for a list of formats valid for -d/--date.
3395 See :hg:`help dates` for a list of formats valid for -d/--date.
3401
3396
3402 See :hg:`help revisions` for more about specifying and ordering
3397 See :hg:`help revisions` for more about specifying and ordering
3403 revisions.
3398 revisions.
3404
3399
3405 See :hg:`help templates` for more about pre-packaged styles and
3400 See :hg:`help templates` for more about pre-packaged styles and
3406 specifying custom templates. The default template used by the log
3401 specifying custom templates. The default template used by the log
3407 command can be customized via the ``ui.logtemplate`` configuration
3402 command can be customized via the ``ui.logtemplate`` configuration
3408 setting.
3403 setting.
3409
3404
3410 Returns 0 on success.
3405 Returns 0 on success.
3411
3406
3412 """
3407 """
3413 opts = pycompat.byteskwargs(opts)
3408 opts = pycompat.byteskwargs(opts)
3414 linerange = opts.get('line_range')
3409 linerange = opts.get('line_range')
3415
3410
3416 if linerange and not opts.get('follow'):
3411 if linerange and not opts.get('follow'):
3417 raise error.Abort(_('--line-range requires --follow'))
3412 raise error.Abort(_('--line-range requires --follow'))
3418
3413
3419 if linerange and pats:
3414 if linerange and pats:
3420 raise error.Abort(
3415 raise error.Abort(
3421 _('FILE arguments are not compatible with --line-range option')
3416 _('FILE arguments are not compatible with --line-range option')
3422 )
3417 )
3423
3418
3424 repo = scmutil.unhidehashlikerevs(repo, opts.get('rev'), 'nowarn')
3419 repo = scmutil.unhidehashlikerevs(repo, opts.get('rev'), 'nowarn')
3425 revs, filematcher = cmdutil.getlogrevs(repo, pats, opts)
3420 revs, filematcher = cmdutil.getlogrevs(repo, pats, opts)
3426 hunksfilter = None
3421 hunksfilter = None
3427
3422
3428 if opts.get('graph'):
3423 if opts.get('graph'):
3429 if linerange:
3424 if linerange:
3430 raise error.Abort(_('graph not supported with line range patterns'))
3425 raise error.Abort(_('graph not supported with line range patterns'))
3431 return cmdutil.graphlog(ui, repo, revs, filematcher, opts)
3426 return cmdutil.graphlog(ui, repo, revs, filematcher, opts)
3432
3427
3433 if linerange:
3428 if linerange:
3434 revs, lrfilematcher, hunksfilter = cmdutil.getloglinerangerevs(
3429 revs, lrfilematcher, hunksfilter = cmdutil.getloglinerangerevs(
3435 repo, revs, opts)
3430 repo, revs, opts)
3436
3431
3437 if filematcher is not None and lrfilematcher is not None:
3432 if filematcher is not None and lrfilematcher is not None:
3438 basefilematcher = filematcher
3433 basefilematcher = filematcher
3439
3434
3440 def filematcher(rev):
3435 def filematcher(rev):
3441 files = (basefilematcher(rev).files()
3436 files = (basefilematcher(rev).files()
3442 + lrfilematcher(rev).files())
3437 + lrfilematcher(rev).files())
3443 return scmutil.matchfiles(repo, files)
3438 return scmutil.matchfiles(repo, files)
3444
3439
3445 elif filematcher is None:
3440 elif filematcher is None:
3446 filematcher = lrfilematcher
3441 filematcher = lrfilematcher
3447
3442
3448 getrenamed = None
3443 getrenamed = None
3449 if opts.get('copies'):
3444 if opts.get('copies'):
3450 endrev = None
3445 endrev = None
3451 if opts.get('rev'):
3446 if opts.get('rev'):
3452 endrev = scmutil.revrange(repo, opts.get('rev')).max() + 1
3447 endrev = scmutil.revrange(repo, opts.get('rev')).max() + 1
3453 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
3448 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
3454
3449
3455 ui.pager('log')
3450 ui.pager('log')
3456 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
3451 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
3457 for rev in revs:
3452 for rev in revs:
3458 ctx = repo[rev]
3453 ctx = repo[rev]
3459 copies = None
3454 copies = None
3460 if getrenamed is not None and rev:
3455 if getrenamed is not None and rev:
3461 copies = []
3456 copies = []
3462 for fn in ctx.files():
3457 for fn in ctx.files():
3463 rename = getrenamed(fn, rev)
3458 rename = getrenamed(fn, rev)
3464 if rename:
3459 if rename:
3465 copies.append((fn, rename[0]))
3460 copies.append((fn, rename[0]))
3466 if filematcher:
3461 if filematcher:
3467 revmatchfn = filematcher(ctx.rev())
3462 revmatchfn = filematcher(ctx.rev())
3468 else:
3463 else:
3469 revmatchfn = None
3464 revmatchfn = None
3470 if hunksfilter:
3465 if hunksfilter:
3471 revhunksfilter = hunksfilter(rev)
3466 revhunksfilter = hunksfilter(rev)
3472 else:
3467 else:
3473 revhunksfilter = None
3468 revhunksfilter = None
3474 displayer.show(ctx, copies=copies, matchfn=revmatchfn,
3469 displayer.show(ctx, copies=copies, matchfn=revmatchfn,
3475 hunksfilterfn=revhunksfilter)
3470 hunksfilterfn=revhunksfilter)
3476 displayer.flush(ctx)
3471 displayer.flush(ctx)
3477
3472
3478 displayer.close()
3473 displayer.close()
3479
3474
3480 @command('manifest',
3475 @command('manifest',
3481 [('r', 'rev', '', _('revision to display'), _('REV')),
3476 [('r', 'rev', '', _('revision to display'), _('REV')),
3482 ('', 'all', False, _("list files from all revisions"))]
3477 ('', 'all', False, _("list files from all revisions"))]
3483 + formatteropts,
3478 + formatteropts,
3484 _('[-r REV]'), cmdtype=readonly)
3479 _('[-r REV]'), cmdtype=readonly)
3485 def manifest(ui, repo, node=None, rev=None, **opts):
3480 def manifest(ui, repo, node=None, rev=None, **opts):
3486 """output the current or given revision of the project manifest
3481 """output the current or given revision of the project manifest
3487
3482
3488 Print a list of version controlled files for the given revision.
3483 Print a list of version controlled files for the given revision.
3489 If no revision is given, the first parent of the working directory
3484 If no revision is given, the first parent of the working directory
3490 is used, or the null revision if no revision is checked out.
3485 is used, or the null revision if no revision is checked out.
3491
3486
3492 With -v, print file permissions, symlink and executable bits.
3487 With -v, print file permissions, symlink and executable bits.
3493 With --debug, print file revision hashes.
3488 With --debug, print file revision hashes.
3494
3489
3495 If option --all is specified, the list of all files from all revisions
3490 If option --all is specified, the list of all files from all revisions
3496 is printed. This includes deleted and renamed files.
3491 is printed. This includes deleted and renamed files.
3497
3492
3498 Returns 0 on success.
3493 Returns 0 on success.
3499 """
3494 """
3500 opts = pycompat.byteskwargs(opts)
3495 opts = pycompat.byteskwargs(opts)
3501 fm = ui.formatter('manifest', opts)
3496 fm = ui.formatter('manifest', opts)
3502
3497
3503 if opts.get('all'):
3498 if opts.get('all'):
3504 if rev or node:
3499 if rev or node:
3505 raise error.Abort(_("can't specify a revision with --all"))
3500 raise error.Abort(_("can't specify a revision with --all"))
3506
3501
3507 res = []
3502 res = []
3508 prefix = "data/"
3503 prefix = "data/"
3509 suffix = ".i"
3504 suffix = ".i"
3510 plen = len(prefix)
3505 plen = len(prefix)
3511 slen = len(suffix)
3506 slen = len(suffix)
3512 with repo.lock():
3507 with repo.lock():
3513 for fn, b, size in repo.store.datafiles():
3508 for fn, b, size in repo.store.datafiles():
3514 if size != 0 and fn[-slen:] == suffix and fn[:plen] == prefix:
3509 if size != 0 and fn[-slen:] == suffix and fn[:plen] == prefix:
3515 res.append(fn[plen:-slen])
3510 res.append(fn[plen:-slen])
3516 ui.pager('manifest')
3511 ui.pager('manifest')
3517 for f in res:
3512 for f in res:
3518 fm.startitem()
3513 fm.startitem()
3519 fm.write("path", '%s\n', f)
3514 fm.write("path", '%s\n', f)
3520 fm.end()
3515 fm.end()
3521 return
3516 return
3522
3517
3523 if rev and node:
3518 if rev and node:
3524 raise error.Abort(_("please specify just one revision"))
3519 raise error.Abort(_("please specify just one revision"))
3525
3520
3526 if not node:
3521 if not node:
3527 node = rev
3522 node = rev
3528
3523
3529 char = {'l': '@', 'x': '*', '': ''}
3524 char = {'l': '@', 'x': '*', '': ''}
3530 mode = {'l': '644', 'x': '755', '': '644'}
3525 mode = {'l': '644', 'x': '755', '': '644'}
3531 if node:
3526 if node:
3532 repo = scmutil.unhidehashlikerevs(repo, [node], 'nowarn')
3527 repo = scmutil.unhidehashlikerevs(repo, [node], 'nowarn')
3533 ctx = scmutil.revsingle(repo, node)
3528 ctx = scmutil.revsingle(repo, node)
3534 mf = ctx.manifest()
3529 mf = ctx.manifest()
3535 ui.pager('manifest')
3530 ui.pager('manifest')
3536 for f in ctx:
3531 for f in ctx:
3537 fm.startitem()
3532 fm.startitem()
3538 fl = ctx[f].flags()
3533 fl = ctx[f].flags()
3539 fm.condwrite(ui.debugflag, 'hash', '%s ', hex(mf[f]))
3534 fm.condwrite(ui.debugflag, 'hash', '%s ', hex(mf[f]))
3540 fm.condwrite(ui.verbose, 'mode type', '%s %1s ', mode[fl], char[fl])
3535 fm.condwrite(ui.verbose, 'mode type', '%s %1s ', mode[fl], char[fl])
3541 fm.write('path', '%s\n', f)
3536 fm.write('path', '%s\n', f)
3542 fm.end()
3537 fm.end()
3543
3538
3544 @command('^merge',
3539 @command('^merge',
3545 [('f', 'force', None,
3540 [('f', 'force', None,
3546 _('force a merge including outstanding changes (DEPRECATED)')),
3541 _('force a merge including outstanding changes (DEPRECATED)')),
3547 ('r', 'rev', '', _('revision to merge'), _('REV')),
3542 ('r', 'rev', '', _('revision to merge'), _('REV')),
3548 ('P', 'preview', None,
3543 ('P', 'preview', None,
3549 _('review revisions to merge (no merge is performed)')),
3544 _('review revisions to merge (no merge is performed)')),
3550 ('', 'abort', None, _('abort the ongoing merge')),
3545 ('', 'abort', None, _('abort the ongoing merge')),
3551 ] + mergetoolopts,
3546 ] + mergetoolopts,
3552 _('[-P] [[-r] REV]'))
3547 _('[-P] [[-r] REV]'))
3553 def merge(ui, repo, node=None, **opts):
3548 def merge(ui, repo, node=None, **opts):
3554 """merge another revision into working directory
3549 """merge another revision into working directory
3555
3550
3556 The current working directory is updated with all changes made in
3551 The current working directory is updated with all changes made in
3557 the requested revision since the last common predecessor revision.
3552 the requested revision since the last common predecessor revision.
3558
3553
3559 Files that changed between either parent are marked as changed for
3554 Files that changed between either parent are marked as changed for
3560 the next commit and a commit must be performed before any further
3555 the next commit and a commit must be performed before any further
3561 updates to the repository are allowed. The next commit will have
3556 updates to the repository are allowed. The next commit will have
3562 two parents.
3557 two parents.
3563
3558
3564 ``--tool`` can be used to specify the merge tool used for file
3559 ``--tool`` can be used to specify the merge tool used for file
3565 merges. It overrides the HGMERGE environment variable and your
3560 merges. It overrides the HGMERGE environment variable and your
3566 configuration files. See :hg:`help merge-tools` for options.
3561 configuration files. See :hg:`help merge-tools` for options.
3567
3562
3568 If no revision is specified, the working directory's parent is a
3563 If no revision is specified, the working directory's parent is a
3569 head revision, and the current branch contains exactly one other
3564 head revision, and the current branch contains exactly one other
3570 head, the other head is merged with by default. Otherwise, an
3565 head, the other head is merged with by default. Otherwise, an
3571 explicit revision with which to merge with must be provided.
3566 explicit revision with which to merge with must be provided.
3572
3567
3573 See :hg:`help resolve` for information on handling file conflicts.
3568 See :hg:`help resolve` for information on handling file conflicts.
3574
3569
3575 To undo an uncommitted merge, use :hg:`merge --abort` which
3570 To undo an uncommitted merge, use :hg:`merge --abort` which
3576 will check out a clean copy of the original merge parent, losing
3571 will check out a clean copy of the original merge parent, losing
3577 all changes.
3572 all changes.
3578
3573
3579 Returns 0 on success, 1 if there are unresolved files.
3574 Returns 0 on success, 1 if there are unresolved files.
3580 """
3575 """
3581
3576
3582 opts = pycompat.byteskwargs(opts)
3577 opts = pycompat.byteskwargs(opts)
3583 abort = opts.get('abort')
3578 abort = opts.get('abort')
3584 if abort and repo.dirstate.p2() == nullid:
3579 if abort and repo.dirstate.p2() == nullid:
3585 cmdutil.wrongtooltocontinue(repo, _('merge'))
3580 cmdutil.wrongtooltocontinue(repo, _('merge'))
3586 if abort:
3581 if abort:
3587 if node:
3582 if node:
3588 raise error.Abort(_("cannot specify a node with --abort"))
3583 raise error.Abort(_("cannot specify a node with --abort"))
3589 if opts.get('rev'):
3584 if opts.get('rev'):
3590 raise error.Abort(_("cannot specify both --rev and --abort"))
3585 raise error.Abort(_("cannot specify both --rev and --abort"))
3591 if opts.get('preview'):
3586 if opts.get('preview'):
3592 raise error.Abort(_("cannot specify --preview with --abort"))
3587 raise error.Abort(_("cannot specify --preview with --abort"))
3593 if opts.get('rev') and node:
3588 if opts.get('rev') and node:
3594 raise error.Abort(_("please specify just one revision"))
3589 raise error.Abort(_("please specify just one revision"))
3595 if not node:
3590 if not node:
3596 node = opts.get('rev')
3591 node = opts.get('rev')
3597
3592
3598 if node:
3593 if node:
3599 node = scmutil.revsingle(repo, node).node()
3594 node = scmutil.revsingle(repo, node).node()
3600
3595
3601 if not node and not abort:
3596 if not node and not abort:
3602 node = repo[destutil.destmerge(repo)].node()
3597 node = repo[destutil.destmerge(repo)].node()
3603
3598
3604 if opts.get('preview'):
3599 if opts.get('preview'):
3605 # find nodes that are ancestors of p2 but not of p1
3600 # find nodes that are ancestors of p2 but not of p1
3606 p1 = repo.lookup('.')
3601 p1 = repo.lookup('.')
3607 p2 = repo.lookup(node)
3602 p2 = repo.lookup(node)
3608 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
3603 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
3609
3604
3610 displayer = cmdutil.show_changeset(ui, repo, opts)
3605 displayer = cmdutil.show_changeset(ui, repo, opts)
3611 for node in nodes:
3606 for node in nodes:
3612 displayer.show(repo[node])
3607 displayer.show(repo[node])
3613 displayer.close()
3608 displayer.close()
3614 return 0
3609 return 0
3615
3610
3616 try:
3611 try:
3617 # ui.forcemerge is an internal variable, do not document
3612 # ui.forcemerge is an internal variable, do not document
3618 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''), 'merge')
3613 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''), 'merge')
3619 force = opts.get('force')
3614 force = opts.get('force')
3620 labels = ['working copy', 'merge rev']
3615 labels = ['working copy', 'merge rev']
3621 return hg.merge(repo, node, force=force, mergeforce=force,
3616 return hg.merge(repo, node, force=force, mergeforce=force,
3622 labels=labels, abort=abort)
3617 labels=labels, abort=abort)
3623 finally:
3618 finally:
3624 ui.setconfig('ui', 'forcemerge', '', 'merge')
3619 ui.setconfig('ui', 'forcemerge', '', 'merge')
3625
3620
3626 @command('outgoing|out',
3621 @command('outgoing|out',
3627 [('f', 'force', None, _('run even when the destination is unrelated')),
3622 [('f', 'force', None, _('run even when the destination is unrelated')),
3628 ('r', 'rev', [],
3623 ('r', 'rev', [],
3629 _('a changeset intended to be included in the destination'), _('REV')),
3624 _('a changeset intended to be included in the destination'), _('REV')),
3630 ('n', 'newest-first', None, _('show newest record first')),
3625 ('n', 'newest-first', None, _('show newest record first')),
3631 ('B', 'bookmarks', False, _('compare bookmarks')),
3626 ('B', 'bookmarks', False, _('compare bookmarks')),
3632 ('b', 'branch', [], _('a specific branch you would like to push'),
3627 ('b', 'branch', [], _('a specific branch you would like to push'),
3633 _('BRANCH')),
3628 _('BRANCH')),
3634 ] + logopts + remoteopts + subrepoopts,
3629 ] + logopts + remoteopts + subrepoopts,
3635 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'))
3630 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'))
3636 def outgoing(ui, repo, dest=None, **opts):
3631 def outgoing(ui, repo, dest=None, **opts):
3637 """show changesets not found in the destination
3632 """show changesets not found in the destination
3638
3633
3639 Show changesets not found in the specified destination repository
3634 Show changesets not found in the specified destination repository
3640 or the default push location. These are the changesets that would
3635 or the default push location. These are the changesets that would
3641 be pushed if a push was requested.
3636 be pushed if a push was requested.
3642
3637
3643 See pull for details of valid destination formats.
3638 See pull for details of valid destination formats.
3644
3639
3645 .. container:: verbose
3640 .. container:: verbose
3646
3641
3647 With -B/--bookmarks, the result of bookmark comparison between
3642 With -B/--bookmarks, the result of bookmark comparison between
3648 local and remote repositories is displayed. With -v/--verbose,
3643 local and remote repositories is displayed. With -v/--verbose,
3649 status is also displayed for each bookmark like below::
3644 status is also displayed for each bookmark like below::
3650
3645
3651 BM1 01234567890a added
3646 BM1 01234567890a added
3652 BM2 deleted
3647 BM2 deleted
3653 BM3 234567890abc advanced
3648 BM3 234567890abc advanced
3654 BM4 34567890abcd diverged
3649 BM4 34567890abcd diverged
3655 BM5 4567890abcde changed
3650 BM5 4567890abcde changed
3656
3651
3657 The action taken when pushing depends on the
3652 The action taken when pushing depends on the
3658 status of each bookmark:
3653 status of each bookmark:
3659
3654
3660 :``added``: push with ``-B`` will create it
3655 :``added``: push with ``-B`` will create it
3661 :``deleted``: push with ``-B`` will delete it
3656 :``deleted``: push with ``-B`` will delete it
3662 :``advanced``: push will update it
3657 :``advanced``: push will update it
3663 :``diverged``: push with ``-B`` will update it
3658 :``diverged``: push with ``-B`` will update it
3664 :``changed``: push with ``-B`` will update it
3659 :``changed``: push with ``-B`` will update it
3665
3660
3666 From the point of view of pushing behavior, bookmarks
3661 From the point of view of pushing behavior, bookmarks
3667 existing only in the remote repository are treated as
3662 existing only in the remote repository are treated as
3668 ``deleted``, even if it is in fact added remotely.
3663 ``deleted``, even if it is in fact added remotely.
3669
3664
3670 Returns 0 if there are outgoing changes, 1 otherwise.
3665 Returns 0 if there are outgoing changes, 1 otherwise.
3671 """
3666 """
3672 opts = pycompat.byteskwargs(opts)
3667 opts = pycompat.byteskwargs(opts)
3673 if opts.get('graph'):
3668 if opts.get('graph'):
3674 cmdutil.checkunsupportedgraphflags([], opts)
3669 cmdutil.checkunsupportedgraphflags([], opts)
3675 o, other = hg._outgoing(ui, repo, dest, opts)
3670 o, other = hg._outgoing(ui, repo, dest, opts)
3676 if not o:
3671 if not o:
3677 cmdutil.outgoinghooks(ui, repo, other, opts, o)
3672 cmdutil.outgoinghooks(ui, repo, other, opts, o)
3678 return
3673 return
3679
3674
3680 revdag = cmdutil.graphrevs(repo, o, opts)
3675 revdag = cmdutil.graphrevs(repo, o, opts)
3681 ui.pager('outgoing')
3676 ui.pager('outgoing')
3682 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
3677 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
3683 cmdutil.displaygraph(ui, repo, revdag, displayer, graphmod.asciiedges)
3678 cmdutil.displaygraph(ui, repo, revdag, displayer, graphmod.asciiedges)
3684 cmdutil.outgoinghooks(ui, repo, other, opts, o)
3679 cmdutil.outgoinghooks(ui, repo, other, opts, o)
3685 return 0
3680 return 0
3686
3681
3687 if opts.get('bookmarks'):
3682 if opts.get('bookmarks'):
3688 dest = ui.expandpath(dest or 'default-push', dest or 'default')
3683 dest = ui.expandpath(dest or 'default-push', dest or 'default')
3689 dest, branches = hg.parseurl(dest, opts.get('branch'))
3684 dest, branches = hg.parseurl(dest, opts.get('branch'))
3690 other = hg.peer(repo, opts, dest)
3685 other = hg.peer(repo, opts, dest)
3691 if 'bookmarks' not in other.listkeys('namespaces'):
3686 if 'bookmarks' not in other.listkeys('namespaces'):
3692 ui.warn(_("remote doesn't support bookmarks\n"))
3687 ui.warn(_("remote doesn't support bookmarks\n"))
3693 return 0
3688 return 0
3694 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
3689 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
3695 ui.pager('outgoing')
3690 ui.pager('outgoing')
3696 return bookmarks.outgoing(ui, repo, other)
3691 return bookmarks.outgoing(ui, repo, other)
3697
3692
3698 repo._subtoppath = ui.expandpath(dest or 'default-push', dest or 'default')
3693 repo._subtoppath = ui.expandpath(dest or 'default-push', dest or 'default')
3699 try:
3694 try:
3700 return hg.outgoing(ui, repo, dest, opts)
3695 return hg.outgoing(ui, repo, dest, opts)
3701 finally:
3696 finally:
3702 del repo._subtoppath
3697 del repo._subtoppath
3703
3698
3704 @command('parents',
3699 @command('parents',
3705 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
3700 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
3706 ] + templateopts,
3701 ] + templateopts,
3707 _('[-r REV] [FILE]'),
3702 _('[-r REV] [FILE]'),
3708 inferrepo=True)
3703 inferrepo=True)
3709 def parents(ui, repo, file_=None, **opts):
3704 def parents(ui, repo, file_=None, **opts):
3710 """show the parents of the working directory or revision (DEPRECATED)
3705 """show the parents of the working directory or revision (DEPRECATED)
3711
3706
3712 Print the working directory's parent revisions. If a revision is
3707 Print the working directory's parent revisions. If a revision is
3713 given via -r/--rev, the parent of that revision will be printed.
3708 given via -r/--rev, the parent of that revision will be printed.
3714 If a file argument is given, the revision in which the file was
3709 If a file argument is given, the revision in which the file was
3715 last changed (before the working directory revision or the
3710 last changed (before the working directory revision or the
3716 argument to --rev if given) is printed.
3711 argument to --rev if given) is printed.
3717
3712
3718 This command is equivalent to::
3713 This command is equivalent to::
3719
3714
3720 hg log -r "p1()+p2()" or
3715 hg log -r "p1()+p2()" or
3721 hg log -r "p1(REV)+p2(REV)" or
3716 hg log -r "p1(REV)+p2(REV)" or
3722 hg log -r "max(::p1() and file(FILE))+max(::p2() and file(FILE))" or
3717 hg log -r "max(::p1() and file(FILE))+max(::p2() and file(FILE))" or
3723 hg log -r "max(::p1(REV) and file(FILE))+max(::p2(REV) and file(FILE))"
3718 hg log -r "max(::p1(REV) and file(FILE))+max(::p2(REV) and file(FILE))"
3724
3719
3725 See :hg:`summary` and :hg:`help revsets` for related information.
3720 See :hg:`summary` and :hg:`help revsets` for related information.
3726
3721
3727 Returns 0 on success.
3722 Returns 0 on success.
3728 """
3723 """
3729
3724
3730 opts = pycompat.byteskwargs(opts)
3725 opts = pycompat.byteskwargs(opts)
3731 rev = opts.get('rev')
3726 rev = opts.get('rev')
3732 if rev:
3727 if rev:
3733 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
3728 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
3734 ctx = scmutil.revsingle(repo, rev, None)
3729 ctx = scmutil.revsingle(repo, rev, None)
3735
3730
3736 if file_:
3731 if file_:
3737 m = scmutil.match(ctx, (file_,), opts)
3732 m = scmutil.match(ctx, (file_,), opts)
3738 if m.anypats() or len(m.files()) != 1:
3733 if m.anypats() or len(m.files()) != 1:
3739 raise error.Abort(_('can only specify an explicit filename'))
3734 raise error.Abort(_('can only specify an explicit filename'))
3740 file_ = m.files()[0]
3735 file_ = m.files()[0]
3741 filenodes = []
3736 filenodes = []
3742 for cp in ctx.parents():
3737 for cp in ctx.parents():
3743 if not cp:
3738 if not cp:
3744 continue
3739 continue
3745 try:
3740 try:
3746 filenodes.append(cp.filenode(file_))
3741 filenodes.append(cp.filenode(file_))
3747 except error.LookupError:
3742 except error.LookupError:
3748 pass
3743 pass
3749 if not filenodes:
3744 if not filenodes:
3750 raise error.Abort(_("'%s' not found in manifest!") % file_)
3745 raise error.Abort(_("'%s' not found in manifest!") % file_)
3751 p = []
3746 p = []
3752 for fn in filenodes:
3747 for fn in filenodes:
3753 fctx = repo.filectx(file_, fileid=fn)
3748 fctx = repo.filectx(file_, fileid=fn)
3754 p.append(fctx.node())
3749 p.append(fctx.node())
3755 else:
3750 else:
3756 p = [cp.node() for cp in ctx.parents()]
3751 p = [cp.node() for cp in ctx.parents()]
3757
3752
3758 displayer = cmdutil.show_changeset(ui, repo, opts)
3753 displayer = cmdutil.show_changeset(ui, repo, opts)
3759 for n in p:
3754 for n in p:
3760 if n != nullid:
3755 if n != nullid:
3761 displayer.show(repo[n])
3756 displayer.show(repo[n])
3762 displayer.close()
3757 displayer.close()
3763
3758
3764 @command('paths', formatteropts, _('[NAME]'), optionalrepo=True,
3759 @command('paths', formatteropts, _('[NAME]'), optionalrepo=True,
3765 cmdtype=readonly)
3760 cmdtype=readonly)
3766 def paths(ui, repo, search=None, **opts):
3761 def paths(ui, repo, search=None, **opts):
3767 """show aliases for remote repositories
3762 """show aliases for remote repositories
3768
3763
3769 Show definition of symbolic path name NAME. If no name is given,
3764 Show definition of symbolic path name NAME. If no name is given,
3770 show definition of all available names.
3765 show definition of all available names.
3771
3766
3772 Option -q/--quiet suppresses all output when searching for NAME
3767 Option -q/--quiet suppresses all output when searching for NAME
3773 and shows only the path names when listing all definitions.
3768 and shows only the path names when listing all definitions.
3774
3769
3775 Path names are defined in the [paths] section of your
3770 Path names are defined in the [paths] section of your
3776 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
3771 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
3777 repository, ``.hg/hgrc`` is used, too.
3772 repository, ``.hg/hgrc`` is used, too.
3778
3773
3779 The path names ``default`` and ``default-push`` have a special
3774 The path names ``default`` and ``default-push`` have a special
3780 meaning. When performing a push or pull operation, they are used
3775 meaning. When performing a push or pull operation, they are used
3781 as fallbacks if no location is specified on the command-line.
3776 as fallbacks if no location is specified on the command-line.
3782 When ``default-push`` is set, it will be used for push and
3777 When ``default-push`` is set, it will be used for push and
3783 ``default`` will be used for pull; otherwise ``default`` is used
3778 ``default`` will be used for pull; otherwise ``default`` is used
3784 as the fallback for both. When cloning a repository, the clone
3779 as the fallback for both. When cloning a repository, the clone
3785 source is written as ``default`` in ``.hg/hgrc``.
3780 source is written as ``default`` in ``.hg/hgrc``.
3786
3781
3787 .. note::
3782 .. note::
3788
3783
3789 ``default`` and ``default-push`` apply to all inbound (e.g.
3784 ``default`` and ``default-push`` apply to all inbound (e.g.
3790 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email`
3785 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email`
3791 and :hg:`bundle`) operations.
3786 and :hg:`bundle`) operations.
3792
3787
3793 See :hg:`help urls` for more information.
3788 See :hg:`help urls` for more information.
3794
3789
3795 Returns 0 on success.
3790 Returns 0 on success.
3796 """
3791 """
3797
3792
3798 opts = pycompat.byteskwargs(opts)
3793 opts = pycompat.byteskwargs(opts)
3799 ui.pager('paths')
3794 ui.pager('paths')
3800 if search:
3795 if search:
3801 pathitems = [(name, path) for name, path in ui.paths.iteritems()
3796 pathitems = [(name, path) for name, path in ui.paths.iteritems()
3802 if name == search]
3797 if name == search]
3803 else:
3798 else:
3804 pathitems = sorted(ui.paths.iteritems())
3799 pathitems = sorted(ui.paths.iteritems())
3805
3800
3806 fm = ui.formatter('paths', opts)
3801 fm = ui.formatter('paths', opts)
3807 if fm.isplain():
3802 if fm.isplain():
3808 hidepassword = util.hidepassword
3803 hidepassword = util.hidepassword
3809 else:
3804 else:
3810 hidepassword = str
3805 hidepassword = str
3811 if ui.quiet:
3806 if ui.quiet:
3812 namefmt = '%s\n'
3807 namefmt = '%s\n'
3813 else:
3808 else:
3814 namefmt = '%s = '
3809 namefmt = '%s = '
3815 showsubopts = not search and not ui.quiet
3810 showsubopts = not search and not ui.quiet
3816
3811
3817 for name, path in pathitems:
3812 for name, path in pathitems:
3818 fm.startitem()
3813 fm.startitem()
3819 fm.condwrite(not search, 'name', namefmt, name)
3814 fm.condwrite(not search, 'name', namefmt, name)
3820 fm.condwrite(not ui.quiet, 'url', '%s\n', hidepassword(path.rawloc))
3815 fm.condwrite(not ui.quiet, 'url', '%s\n', hidepassword(path.rawloc))
3821 for subopt, value in sorted(path.suboptions.items()):
3816 for subopt, value in sorted(path.suboptions.items()):
3822 assert subopt not in ('name', 'url')
3817 assert subopt not in ('name', 'url')
3823 if showsubopts:
3818 if showsubopts:
3824 fm.plain('%s:%s = ' % (name, subopt))
3819 fm.plain('%s:%s = ' % (name, subopt))
3825 fm.condwrite(showsubopts, subopt, '%s\n', value)
3820 fm.condwrite(showsubopts, subopt, '%s\n', value)
3826
3821
3827 fm.end()
3822 fm.end()
3828
3823
3829 if search and not pathitems:
3824 if search and not pathitems:
3830 if not ui.quiet:
3825 if not ui.quiet:
3831 ui.warn(_("not found!\n"))
3826 ui.warn(_("not found!\n"))
3832 return 1
3827 return 1
3833 else:
3828 else:
3834 return 0
3829 return 0
3835
3830
3836 @command('phase',
3831 @command('phase',
3837 [('p', 'public', False, _('set changeset phase to public')),
3832 [('p', 'public', False, _('set changeset phase to public')),
3838 ('d', 'draft', False, _('set changeset phase to draft')),
3833 ('d', 'draft', False, _('set changeset phase to draft')),
3839 ('s', 'secret', False, _('set changeset phase to secret')),
3834 ('s', 'secret', False, _('set changeset phase to secret')),
3840 ('f', 'force', False, _('allow to move boundary backward')),
3835 ('f', 'force', False, _('allow to move boundary backward')),
3841 ('r', 'rev', [], _('target revision'), _('REV')),
3836 ('r', 'rev', [], _('target revision'), _('REV')),
3842 ],
3837 ],
3843 _('[-p|-d|-s] [-f] [-r] [REV...]'))
3838 _('[-p|-d|-s] [-f] [-r] [REV...]'))
3844 def phase(ui, repo, *revs, **opts):
3839 def phase(ui, repo, *revs, **opts):
3845 """set or show the current phase name
3840 """set or show the current phase name
3846
3841
3847 With no argument, show the phase name of the current revision(s).
3842 With no argument, show the phase name of the current revision(s).
3848
3843
3849 With one of -p/--public, -d/--draft or -s/--secret, change the
3844 With one of -p/--public, -d/--draft or -s/--secret, change the
3850 phase value of the specified revisions.
3845 phase value of the specified revisions.
3851
3846
3852 Unless -f/--force is specified, :hg:`phase` won't move changesets from a
3847 Unless -f/--force is specified, :hg:`phase` won't move changesets from a
3853 lower phase to a higher phase. Phases are ordered as follows::
3848 lower phase to a higher phase. Phases are ordered as follows::
3854
3849
3855 public < draft < secret
3850 public < draft < secret
3856
3851
3857 Returns 0 on success, 1 if some phases could not be changed.
3852 Returns 0 on success, 1 if some phases could not be changed.
3858
3853
3859 (For more information about the phases concept, see :hg:`help phases`.)
3854 (For more information about the phases concept, see :hg:`help phases`.)
3860 """
3855 """
3861 opts = pycompat.byteskwargs(opts)
3856 opts = pycompat.byteskwargs(opts)
3862 # search for a unique phase argument
3857 # search for a unique phase argument
3863 targetphase = None
3858 targetphase = None
3864 for idx, name in enumerate(phases.phasenames):
3859 for idx, name in enumerate(phases.phasenames):
3865 if opts[name]:
3860 if opts[name]:
3866 if targetphase is not None:
3861 if targetphase is not None:
3867 raise error.Abort(_('only one phase can be specified'))
3862 raise error.Abort(_('only one phase can be specified'))
3868 targetphase = idx
3863 targetphase = idx
3869
3864
3870 # look for specified revision
3865 # look for specified revision
3871 revs = list(revs)
3866 revs = list(revs)
3872 revs.extend(opts['rev'])
3867 revs.extend(opts['rev'])
3873 if not revs:
3868 if not revs:
3874 # display both parents as the second parent phase can influence
3869 # display both parents as the second parent phase can influence
3875 # the phase of a merge commit
3870 # the phase of a merge commit
3876 revs = [c.rev() for c in repo[None].parents()]
3871 revs = [c.rev() for c in repo[None].parents()]
3877
3872
3878 revs = scmutil.revrange(repo, revs)
3873 revs = scmutil.revrange(repo, revs)
3879
3874
3880 ret = 0
3875 ret = 0
3881 if targetphase is None:
3876 if targetphase is None:
3882 # display
3877 # display
3883 for r in revs:
3878 for r in revs:
3884 ctx = repo[r]
3879 ctx = repo[r]
3885 ui.write('%i: %s\n' % (ctx.rev(), ctx.phasestr()))
3880 ui.write('%i: %s\n' % (ctx.rev(), ctx.phasestr()))
3886 else:
3881 else:
3887 with repo.lock(), repo.transaction("phase") as tr:
3882 with repo.lock(), repo.transaction("phase") as tr:
3888 # set phase
3883 # set phase
3889 if not revs:
3884 if not revs:
3890 raise error.Abort(_('empty revision set'))
3885 raise error.Abort(_('empty revision set'))
3891 nodes = [repo[r].node() for r in revs]
3886 nodes = [repo[r].node() for r in revs]
3892 # moving revision from public to draft may hide them
3887 # moving revision from public to draft may hide them
3893 # We have to check result on an unfiltered repository
3888 # We have to check result on an unfiltered repository
3894 unfi = repo.unfiltered()
3889 unfi = repo.unfiltered()
3895 getphase = unfi._phasecache.phase
3890 getphase = unfi._phasecache.phase
3896 olddata = [getphase(unfi, r) for r in unfi]
3891 olddata = [getphase(unfi, r) for r in unfi]
3897 phases.advanceboundary(repo, tr, targetphase, nodes)
3892 phases.advanceboundary(repo, tr, targetphase, nodes)
3898 if opts['force']:
3893 if opts['force']:
3899 phases.retractboundary(repo, tr, targetphase, nodes)
3894 phases.retractboundary(repo, tr, targetphase, nodes)
3900 getphase = unfi._phasecache.phase
3895 getphase = unfi._phasecache.phase
3901 newdata = [getphase(unfi, r) for r in unfi]
3896 newdata = [getphase(unfi, r) for r in unfi]
3902 changes = sum(newdata[r] != olddata[r] for r in unfi)
3897 changes = sum(newdata[r] != olddata[r] for r in unfi)
3903 cl = unfi.changelog
3898 cl = unfi.changelog
3904 rejected = [n for n in nodes
3899 rejected = [n for n in nodes
3905 if newdata[cl.rev(n)] < targetphase]
3900 if newdata[cl.rev(n)] < targetphase]
3906 if rejected:
3901 if rejected:
3907 ui.warn(_('cannot move %i changesets to a higher '
3902 ui.warn(_('cannot move %i changesets to a higher '
3908 'phase, use --force\n') % len(rejected))
3903 'phase, use --force\n') % len(rejected))
3909 ret = 1
3904 ret = 1
3910 if changes:
3905 if changes:
3911 msg = _('phase changed for %i changesets\n') % changes
3906 msg = _('phase changed for %i changesets\n') % changes
3912 if ret:
3907 if ret:
3913 ui.status(msg)
3908 ui.status(msg)
3914 else:
3909 else:
3915 ui.note(msg)
3910 ui.note(msg)
3916 else:
3911 else:
3917 ui.warn(_('no phases changed\n'))
3912 ui.warn(_('no phases changed\n'))
3918 return ret
3913 return ret
3919
3914
3920 def postincoming(ui, repo, modheads, optupdate, checkout, brev):
3915 def postincoming(ui, repo, modheads, optupdate, checkout, brev):
3921 """Run after a changegroup has been added via pull/unbundle
3916 """Run after a changegroup has been added via pull/unbundle
3922
3917
3923 This takes arguments below:
3918 This takes arguments below:
3924
3919
3925 :modheads: change of heads by pull/unbundle
3920 :modheads: change of heads by pull/unbundle
3926 :optupdate: updating working directory is needed or not
3921 :optupdate: updating working directory is needed or not
3927 :checkout: update destination revision (or None to default destination)
3922 :checkout: update destination revision (or None to default destination)
3928 :brev: a name, which might be a bookmark to be activated after updating
3923 :brev: a name, which might be a bookmark to be activated after updating
3929 """
3924 """
3930 if modheads == 0:
3925 if modheads == 0:
3931 return
3926 return
3932 if optupdate:
3927 if optupdate:
3933 try:
3928 try:
3934 return hg.updatetotally(ui, repo, checkout, brev)
3929 return hg.updatetotally(ui, repo, checkout, brev)
3935 except error.UpdateAbort as inst:
3930 except error.UpdateAbort as inst:
3936 msg = _("not updating: %s") % str(inst)
3931 msg = _("not updating: %s") % str(inst)
3937 hint = inst.hint
3932 hint = inst.hint
3938 raise error.UpdateAbort(msg, hint=hint)
3933 raise error.UpdateAbort(msg, hint=hint)
3939 if modheads > 1:
3934 if modheads > 1:
3940 currentbranchheads = len(repo.branchheads())
3935 currentbranchheads = len(repo.branchheads())
3941 if currentbranchheads == modheads:
3936 if currentbranchheads == modheads:
3942 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
3937 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
3943 elif currentbranchheads > 1:
3938 elif currentbranchheads > 1:
3944 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to "
3939 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to "
3945 "merge)\n"))
3940 "merge)\n"))
3946 else:
3941 else:
3947 ui.status(_("(run 'hg heads' to see heads)\n"))
3942 ui.status(_("(run 'hg heads' to see heads)\n"))
3948 elif not ui.configbool('commands', 'update.requiredest'):
3943 elif not ui.configbool('commands', 'update.requiredest'):
3949 ui.status(_("(run 'hg update' to get a working copy)\n"))
3944 ui.status(_("(run 'hg update' to get a working copy)\n"))
3950
3945
3951 @command('^pull',
3946 @command('^pull',
3952 [('u', 'update', None,
3947 [('u', 'update', None,
3953 _('update to new branch head if new descendants were pulled')),
3948 _('update to new branch head if new descendants were pulled')),
3954 ('f', 'force', None, _('run even when remote repository is unrelated')),
3949 ('f', 'force', None, _('run even when remote repository is unrelated')),
3955 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3950 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3956 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
3951 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
3957 ('b', 'branch', [], _('a specific branch you would like to pull'),
3952 ('b', 'branch', [], _('a specific branch you would like to pull'),
3958 _('BRANCH')),
3953 _('BRANCH')),
3959 ] + remoteopts,
3954 ] + remoteopts,
3960 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'))
3955 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'))
3961 def pull(ui, repo, source="default", **opts):
3956 def pull(ui, repo, source="default", **opts):
3962 """pull changes from the specified source
3957 """pull changes from the specified source
3963
3958
3964 Pull changes from a remote repository to a local one.
3959 Pull changes from a remote repository to a local one.
3965
3960
3966 This finds all changes from the repository at the specified path
3961 This finds all changes from the repository at the specified path
3967 or URL and adds them to a local repository (the current one unless
3962 or URL and adds them to a local repository (the current one unless
3968 -R is specified). By default, this does not update the copy of the
3963 -R is specified). By default, this does not update the copy of the
3969 project in the working directory.
3964 project in the working directory.
3970
3965
3971 Use :hg:`incoming` if you want to see what would have been added
3966 Use :hg:`incoming` if you want to see what would have been added
3972 by a pull at the time you issued this command. If you then decide
3967 by a pull at the time you issued this command. If you then decide
3973 to add those changes to the repository, you should use :hg:`pull
3968 to add those changes to the repository, you should use :hg:`pull
3974 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
3969 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
3975
3970
3976 If SOURCE is omitted, the 'default' path will be used.
3971 If SOURCE is omitted, the 'default' path will be used.
3977 See :hg:`help urls` for more information.
3972 See :hg:`help urls` for more information.
3978
3973
3979 Specifying bookmark as ``.`` is equivalent to specifying the active
3974 Specifying bookmark as ``.`` is equivalent to specifying the active
3980 bookmark's name.
3975 bookmark's name.
3981
3976
3982 Returns 0 on success, 1 if an update had unresolved files.
3977 Returns 0 on success, 1 if an update had unresolved files.
3983 """
3978 """
3984
3979
3985 opts = pycompat.byteskwargs(opts)
3980 opts = pycompat.byteskwargs(opts)
3986 if ui.configbool('commands', 'update.requiredest') and opts.get('update'):
3981 if ui.configbool('commands', 'update.requiredest') and opts.get('update'):
3987 msg = _('update destination required by configuration')
3982 msg = _('update destination required by configuration')
3988 hint = _('use hg pull followed by hg update DEST')
3983 hint = _('use hg pull followed by hg update DEST')
3989 raise error.Abort(msg, hint=hint)
3984 raise error.Abort(msg, hint=hint)
3990
3985
3991 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
3986 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
3992 ui.status(_('pulling from %s\n') % util.hidepassword(source))
3987 ui.status(_('pulling from %s\n') % util.hidepassword(source))
3993 other = hg.peer(repo, opts, source)
3988 other = hg.peer(repo, opts, source)
3994 try:
3989 try:
3995 revs, checkout = hg.addbranchrevs(repo, other, branches,
3990 revs, checkout = hg.addbranchrevs(repo, other, branches,
3996 opts.get('rev'))
3991 opts.get('rev'))
3997
3992
3998
3993
3999 pullopargs = {}
3994 pullopargs = {}
4000 if opts.get('bookmark'):
3995 if opts.get('bookmark'):
4001 if not revs:
3996 if not revs:
4002 revs = []
3997 revs = []
4003 # The list of bookmark used here is not the one used to actually
3998 # The list of bookmark used here is not the one used to actually
4004 # update the bookmark name. This can result in the revision pulled
3999 # update the bookmark name. This can result in the revision pulled
4005 # not ending up with the name of the bookmark because of a race
4000 # not ending up with the name of the bookmark because of a race
4006 # condition on the server. (See issue 4689 for details)
4001 # condition on the server. (See issue 4689 for details)
4007 remotebookmarks = other.listkeys('bookmarks')
4002 remotebookmarks = other.listkeys('bookmarks')
4008 remotebookmarks = bookmarks.unhexlifybookmarks(remotebookmarks)
4003 remotebookmarks = bookmarks.unhexlifybookmarks(remotebookmarks)
4009 pullopargs['remotebookmarks'] = remotebookmarks
4004 pullopargs['remotebookmarks'] = remotebookmarks
4010 for b in opts['bookmark']:
4005 for b in opts['bookmark']:
4011 b = repo._bookmarks.expandname(b)
4006 b = repo._bookmarks.expandname(b)
4012 if b not in remotebookmarks:
4007 if b not in remotebookmarks:
4013 raise error.Abort(_('remote bookmark %s not found!') % b)
4008 raise error.Abort(_('remote bookmark %s not found!') % b)
4014 revs.append(hex(remotebookmarks[b]))
4009 revs.append(hex(remotebookmarks[b]))
4015
4010
4016 if revs:
4011 if revs:
4017 try:
4012 try:
4018 # When 'rev' is a bookmark name, we cannot guarantee that it
4013 # When 'rev' is a bookmark name, we cannot guarantee that it
4019 # will be updated with that name because of a race condition
4014 # will be updated with that name because of a race condition
4020 # server side. (See issue 4689 for details)
4015 # server side. (See issue 4689 for details)
4021 oldrevs = revs
4016 oldrevs = revs
4022 revs = [] # actually, nodes
4017 revs = [] # actually, nodes
4023 for r in oldrevs:
4018 for r in oldrevs:
4024 node = other.lookup(r)
4019 node = other.lookup(r)
4025 revs.append(node)
4020 revs.append(node)
4026 if r == checkout:
4021 if r == checkout:
4027 checkout = node
4022 checkout = node
4028 except error.CapabilityError:
4023 except error.CapabilityError:
4029 err = _("other repository doesn't support revision lookup, "
4024 err = _("other repository doesn't support revision lookup, "
4030 "so a rev cannot be specified.")
4025 "so a rev cannot be specified.")
4031 raise error.Abort(err)
4026 raise error.Abort(err)
4032
4027
4033 wlock = util.nullcontextmanager()
4028 wlock = util.nullcontextmanager()
4034 if opts.get('update'):
4029 if opts.get('update'):
4035 wlock = repo.wlock()
4030 wlock = repo.wlock()
4036 with wlock:
4031 with wlock:
4037 pullopargs.update(opts.get('opargs', {}))
4032 pullopargs.update(opts.get('opargs', {}))
4038 modheads = exchange.pull(repo, other, heads=revs,
4033 modheads = exchange.pull(repo, other, heads=revs,
4039 force=opts.get('force'),
4034 force=opts.get('force'),
4040 bookmarks=opts.get('bookmark', ()),
4035 bookmarks=opts.get('bookmark', ()),
4041 opargs=pullopargs).cgresult
4036 opargs=pullopargs).cgresult
4042
4037
4043 # brev is a name, which might be a bookmark to be activated at
4038 # brev is a name, which might be a bookmark to be activated at
4044 # the end of the update. In other words, it is an explicit
4039 # the end of the update. In other words, it is an explicit
4045 # destination of the update
4040 # destination of the update
4046 brev = None
4041 brev = None
4047
4042
4048 if checkout:
4043 if checkout:
4049 checkout = str(repo.changelog.rev(checkout))
4044 checkout = str(repo.changelog.rev(checkout))
4050
4045
4051 # order below depends on implementation of
4046 # order below depends on implementation of
4052 # hg.addbranchrevs(). opts['bookmark'] is ignored,
4047 # hg.addbranchrevs(). opts['bookmark'] is ignored,
4053 # because 'checkout' is determined without it.
4048 # because 'checkout' is determined without it.
4054 if opts.get('rev'):
4049 if opts.get('rev'):
4055 brev = opts['rev'][0]
4050 brev = opts['rev'][0]
4056 elif opts.get('branch'):
4051 elif opts.get('branch'):
4057 brev = opts['branch'][0]
4052 brev = opts['branch'][0]
4058 else:
4053 else:
4059 brev = branches[0]
4054 brev = branches[0]
4060 repo._subtoppath = source
4055 repo._subtoppath = source
4061 try:
4056 try:
4062 ret = postincoming(ui, repo, modheads, opts.get('update'),
4057 ret = postincoming(ui, repo, modheads, opts.get('update'),
4063 checkout, brev)
4058 checkout, brev)
4064
4059
4065 finally:
4060 finally:
4066 del repo._subtoppath
4061 del repo._subtoppath
4067
4062
4068 finally:
4063 finally:
4069 other.close()
4064 other.close()
4070 return ret
4065 return ret
4071
4066
4072 @command('^push',
4067 @command('^push',
4073 [('f', 'force', None, _('force push')),
4068 [('f', 'force', None, _('force push')),
4074 ('r', 'rev', [],
4069 ('r', 'rev', [],
4075 _('a changeset intended to be included in the destination'),
4070 _('a changeset intended to be included in the destination'),
4076 _('REV')),
4071 _('REV')),
4077 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
4072 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
4078 ('b', 'branch', [],
4073 ('b', 'branch', [],
4079 _('a specific branch you would like to push'), _('BRANCH')),
4074 _('a specific branch you would like to push'), _('BRANCH')),
4080 ('', 'new-branch', False, _('allow pushing a new branch')),
4075 ('', 'new-branch', False, _('allow pushing a new branch')),
4081 ('', 'pushvars', [], _('variables that can be sent to server (ADVANCED)')),
4076 ('', 'pushvars', [], _('variables that can be sent to server (ADVANCED)')),
4082 ] + remoteopts,
4077 ] + remoteopts,
4083 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'))
4078 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'))
4084 def push(ui, repo, dest=None, **opts):
4079 def push(ui, repo, dest=None, **opts):
4085 """push changes to the specified destination
4080 """push changes to the specified destination
4086
4081
4087 Push changesets from the local repository to the specified
4082 Push changesets from the local repository to the specified
4088 destination.
4083 destination.
4089
4084
4090 This operation is symmetrical to pull: it is identical to a pull
4085 This operation is symmetrical to pull: it is identical to a pull
4091 in the destination repository from the current one.
4086 in the destination repository from the current one.
4092
4087
4093 By default, push will not allow creation of new heads at the
4088 By default, push will not allow creation of new heads at the
4094 destination, since multiple heads would make it unclear which head
4089 destination, since multiple heads would make it unclear which head
4095 to use. In this situation, it is recommended to pull and merge
4090 to use. In this situation, it is recommended to pull and merge
4096 before pushing.
4091 before pushing.
4097
4092
4098 Use --new-branch if you want to allow push to create a new named
4093 Use --new-branch if you want to allow push to create a new named
4099 branch that is not present at the destination. This allows you to
4094 branch that is not present at the destination. This allows you to
4100 only create a new branch without forcing other changes.
4095 only create a new branch without forcing other changes.
4101
4096
4102 .. note::
4097 .. note::
4103
4098
4104 Extra care should be taken with the -f/--force option,
4099 Extra care should be taken with the -f/--force option,
4105 which will push all new heads on all branches, an action which will
4100 which will push all new heads on all branches, an action which will
4106 almost always cause confusion for collaborators.
4101 almost always cause confusion for collaborators.
4107
4102
4108 If -r/--rev is used, the specified revision and all its ancestors
4103 If -r/--rev is used, the specified revision and all its ancestors
4109 will be pushed to the remote repository.
4104 will be pushed to the remote repository.
4110
4105
4111 If -B/--bookmark is used, the specified bookmarked revision, its
4106 If -B/--bookmark is used, the specified bookmarked revision, its
4112 ancestors, and the bookmark will be pushed to the remote
4107 ancestors, and the bookmark will be pushed to the remote
4113 repository. Specifying ``.`` is equivalent to specifying the active
4108 repository. Specifying ``.`` is equivalent to specifying the active
4114 bookmark's name.
4109 bookmark's name.
4115
4110
4116 Please see :hg:`help urls` for important details about ``ssh://``
4111 Please see :hg:`help urls` for important details about ``ssh://``
4117 URLs. If DESTINATION is omitted, a default path will be used.
4112 URLs. If DESTINATION is omitted, a default path will be used.
4118
4113
4119 .. container:: verbose
4114 .. container:: verbose
4120
4115
4121 The --pushvars option sends strings to the server that become
4116 The --pushvars option sends strings to the server that become
4122 environment variables prepended with ``HG_USERVAR_``. For example,
4117 environment variables prepended with ``HG_USERVAR_``. For example,
4123 ``--pushvars ENABLE_FEATURE=true``, provides the server side hooks with
4118 ``--pushvars ENABLE_FEATURE=true``, provides the server side hooks with
4124 ``HG_USERVAR_ENABLE_FEATURE=true`` as part of their environment.
4119 ``HG_USERVAR_ENABLE_FEATURE=true`` as part of their environment.
4125
4120
4126 pushvars can provide for user-overridable hooks as well as set debug
4121 pushvars can provide for user-overridable hooks as well as set debug
4127 levels. One example is having a hook that blocks commits containing
4122 levels. One example is having a hook that blocks commits containing
4128 conflict markers, but enables the user to override the hook if the file
4123 conflict markers, but enables the user to override the hook if the file
4129 is using conflict markers for testing purposes or the file format has
4124 is using conflict markers for testing purposes or the file format has
4130 strings that look like conflict markers.
4125 strings that look like conflict markers.
4131
4126
4132 By default, servers will ignore `--pushvars`. To enable it add the
4127 By default, servers will ignore `--pushvars`. To enable it add the
4133 following to your configuration file::
4128 following to your configuration file::
4134
4129
4135 [push]
4130 [push]
4136 pushvars.server = true
4131 pushvars.server = true
4137
4132
4138 Returns 0 if push was successful, 1 if nothing to push.
4133 Returns 0 if push was successful, 1 if nothing to push.
4139 """
4134 """
4140
4135
4141 opts = pycompat.byteskwargs(opts)
4136 opts = pycompat.byteskwargs(opts)
4142 if opts.get('bookmark'):
4137 if opts.get('bookmark'):
4143 ui.setconfig('bookmarks', 'pushing', opts['bookmark'], 'push')
4138 ui.setconfig('bookmarks', 'pushing', opts['bookmark'], 'push')
4144 for b in opts['bookmark']:
4139 for b in opts['bookmark']:
4145 # translate -B options to -r so changesets get pushed
4140 # translate -B options to -r so changesets get pushed
4146 b = repo._bookmarks.expandname(b)
4141 b = repo._bookmarks.expandname(b)
4147 if b in repo._bookmarks:
4142 if b in repo._bookmarks:
4148 opts.setdefault('rev', []).append(b)
4143 opts.setdefault('rev', []).append(b)
4149 else:
4144 else:
4150 # if we try to push a deleted bookmark, translate it to null
4145 # if we try to push a deleted bookmark, translate it to null
4151 # this lets simultaneous -r, -b options continue working
4146 # this lets simultaneous -r, -b options continue working
4152 opts.setdefault('rev', []).append("null")
4147 opts.setdefault('rev', []).append("null")
4153
4148
4154 path = ui.paths.getpath(dest, default=('default-push', 'default'))
4149 path = ui.paths.getpath(dest, default=('default-push', 'default'))
4155 if not path:
4150 if not path:
4156 raise error.Abort(_('default repository not configured!'),
4151 raise error.Abort(_('default repository not configured!'),
4157 hint=_("see 'hg help config.paths'"))
4152 hint=_("see 'hg help config.paths'"))
4158 dest = path.pushloc or path.loc
4153 dest = path.pushloc or path.loc
4159 branches = (path.branch, opts.get('branch') or [])
4154 branches = (path.branch, opts.get('branch') or [])
4160 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
4155 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
4161 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
4156 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
4162 other = hg.peer(repo, opts, dest)
4157 other = hg.peer(repo, opts, dest)
4163
4158
4164 if revs:
4159 if revs:
4165 revs = [repo.lookup(r) for r in scmutil.revrange(repo, revs)]
4160 revs = [repo.lookup(r) for r in scmutil.revrange(repo, revs)]
4166 if not revs:
4161 if not revs:
4167 raise error.Abort(_("specified revisions evaluate to an empty set"),
4162 raise error.Abort(_("specified revisions evaluate to an empty set"),
4168 hint=_("use different revision arguments"))
4163 hint=_("use different revision arguments"))
4169 elif path.pushrev:
4164 elif path.pushrev:
4170 # It doesn't make any sense to specify ancestor revisions. So limit
4165 # It doesn't make any sense to specify ancestor revisions. So limit
4171 # to DAG heads to make discovery simpler.
4166 # to DAG heads to make discovery simpler.
4172 expr = revsetlang.formatspec('heads(%r)', path.pushrev)
4167 expr = revsetlang.formatspec('heads(%r)', path.pushrev)
4173 revs = scmutil.revrange(repo, [expr])
4168 revs = scmutil.revrange(repo, [expr])
4174 revs = [repo[rev].node() for rev in revs]
4169 revs = [repo[rev].node() for rev in revs]
4175 if not revs:
4170 if not revs:
4176 raise error.Abort(_('default push revset for path evaluates to an '
4171 raise error.Abort(_('default push revset for path evaluates to an '
4177 'empty set'))
4172 'empty set'))
4178
4173
4179 repo._subtoppath = dest
4174 repo._subtoppath = dest
4180 try:
4175 try:
4181 # push subrepos depth-first for coherent ordering
4176 # push subrepos depth-first for coherent ordering
4182 c = repo['']
4177 c = repo['']
4183 subs = c.substate # only repos that are committed
4178 subs = c.substate # only repos that are committed
4184 for s in sorted(subs):
4179 for s in sorted(subs):
4185 result = c.sub(s).push(opts)
4180 result = c.sub(s).push(opts)
4186 if result == 0:
4181 if result == 0:
4187 return not result
4182 return not result
4188 finally:
4183 finally:
4189 del repo._subtoppath
4184 del repo._subtoppath
4190
4185
4191 opargs = dict(opts.get('opargs', {})) # copy opargs since we may mutate it
4186 opargs = dict(opts.get('opargs', {})) # copy opargs since we may mutate it
4192 opargs.setdefault('pushvars', []).extend(opts.get('pushvars', []))
4187 opargs.setdefault('pushvars', []).extend(opts.get('pushvars', []))
4193
4188
4194 pushop = exchange.push(repo, other, opts.get('force'), revs=revs,
4189 pushop = exchange.push(repo, other, opts.get('force'), revs=revs,
4195 newbranch=opts.get('new_branch'),
4190 newbranch=opts.get('new_branch'),
4196 bookmarks=opts.get('bookmark', ()),
4191 bookmarks=opts.get('bookmark', ()),
4197 opargs=opargs)
4192 opargs=opargs)
4198
4193
4199 result = not pushop.cgresult
4194 result = not pushop.cgresult
4200
4195
4201 if pushop.bkresult is not None:
4196 if pushop.bkresult is not None:
4202 if pushop.bkresult == 2:
4197 if pushop.bkresult == 2:
4203 result = 2
4198 result = 2
4204 elif not result and pushop.bkresult:
4199 elif not result and pushop.bkresult:
4205 result = 2
4200 result = 2
4206
4201
4207 return result
4202 return result
4208
4203
4209 @command('recover', [])
4204 @command('recover', [])
4210 def recover(ui, repo):
4205 def recover(ui, repo):
4211 """roll back an interrupted transaction
4206 """roll back an interrupted transaction
4212
4207
4213 Recover from an interrupted commit or pull.
4208 Recover from an interrupted commit or pull.
4214
4209
4215 This command tries to fix the repository status after an
4210 This command tries to fix the repository status after an
4216 interrupted operation. It should only be necessary when Mercurial
4211 interrupted operation. It should only be necessary when Mercurial
4217 suggests it.
4212 suggests it.
4218
4213
4219 Returns 0 if successful, 1 if nothing to recover or verify fails.
4214 Returns 0 if successful, 1 if nothing to recover or verify fails.
4220 """
4215 """
4221 if repo.recover():
4216 if repo.recover():
4222 return hg.verify(repo)
4217 return hg.verify(repo)
4223 return 1
4218 return 1
4224
4219
4225 @command('^remove|rm',
4220 @command('^remove|rm',
4226 [('A', 'after', None, _('record delete for missing files')),
4221 [('A', 'after', None, _('record delete for missing files')),
4227 ('f', 'force', None,
4222 ('f', 'force', None,
4228 _('forget added files, delete modified files')),
4223 _('forget added files, delete modified files')),
4229 ] + subrepoopts + walkopts,
4224 ] + subrepoopts + walkopts,
4230 _('[OPTION]... FILE...'),
4225 _('[OPTION]... FILE...'),
4231 inferrepo=True)
4226 inferrepo=True)
4232 def remove(ui, repo, *pats, **opts):
4227 def remove(ui, repo, *pats, **opts):
4233 """remove the specified files on the next commit
4228 """remove the specified files on the next commit
4234
4229
4235 Schedule the indicated files for removal from the current branch.
4230 Schedule the indicated files for removal from the current branch.
4236
4231
4237 This command schedules the files to be removed at the next commit.
4232 This command schedules the files to be removed at the next commit.
4238 To undo a remove before that, see :hg:`revert`. To undo added
4233 To undo a remove before that, see :hg:`revert`. To undo added
4239 files, see :hg:`forget`.
4234 files, see :hg:`forget`.
4240
4235
4241 .. container:: verbose
4236 .. container:: verbose
4242
4237
4243 -A/--after can be used to remove only files that have already
4238 -A/--after can be used to remove only files that have already
4244 been deleted, -f/--force can be used to force deletion, and -Af
4239 been deleted, -f/--force can be used to force deletion, and -Af
4245 can be used to remove files from the next revision without
4240 can be used to remove files from the next revision without
4246 deleting them from the working directory.
4241 deleting them from the working directory.
4247
4242
4248 The following table details the behavior of remove for different
4243 The following table details the behavior of remove for different
4249 file states (columns) and option combinations (rows). The file
4244 file states (columns) and option combinations (rows). The file
4250 states are Added [A], Clean [C], Modified [M] and Missing [!]
4245 states are Added [A], Clean [C], Modified [M] and Missing [!]
4251 (as reported by :hg:`status`). The actions are Warn, Remove
4246 (as reported by :hg:`status`). The actions are Warn, Remove
4252 (from branch) and Delete (from disk):
4247 (from branch) and Delete (from disk):
4253
4248
4254 ========= == == == ==
4249 ========= == == == ==
4255 opt/state A C M !
4250 opt/state A C M !
4256 ========= == == == ==
4251 ========= == == == ==
4257 none W RD W R
4252 none W RD W R
4258 -f R RD RD R
4253 -f R RD RD R
4259 -A W W W R
4254 -A W W W R
4260 -Af R R R R
4255 -Af R R R R
4261 ========= == == == ==
4256 ========= == == == ==
4262
4257
4263 .. note::
4258 .. note::
4264
4259
4265 :hg:`remove` never deletes files in Added [A] state from the
4260 :hg:`remove` never deletes files in Added [A] state from the
4266 working directory, not even if ``--force`` is specified.
4261 working directory, not even if ``--force`` is specified.
4267
4262
4268 Returns 0 on success, 1 if any warnings encountered.
4263 Returns 0 on success, 1 if any warnings encountered.
4269 """
4264 """
4270
4265
4271 opts = pycompat.byteskwargs(opts)
4266 opts = pycompat.byteskwargs(opts)
4272 after, force = opts.get('after'), opts.get('force')
4267 after, force = opts.get('after'), opts.get('force')
4273 if not pats and not after:
4268 if not pats and not after:
4274 raise error.Abort(_('no files specified'))
4269 raise error.Abort(_('no files specified'))
4275
4270
4276 m = scmutil.match(repo[None], pats, opts)
4271 m = scmutil.match(repo[None], pats, opts)
4277 subrepos = opts.get('subrepos')
4272 subrepos = opts.get('subrepos')
4278 return cmdutil.remove(ui, repo, m, "", after, force, subrepos)
4273 return cmdutil.remove(ui, repo, m, "", after, force, subrepos)
4279
4274
4280 @command('rename|move|mv',
4275 @command('rename|move|mv',
4281 [('A', 'after', None, _('record a rename that has already occurred')),
4276 [('A', 'after', None, _('record a rename that has already occurred')),
4282 ('f', 'force', None, _('forcibly copy over an existing managed file')),
4277 ('f', 'force', None, _('forcibly copy over an existing managed file')),
4283 ] + walkopts + dryrunopts,
4278 ] + walkopts + dryrunopts,
4284 _('[OPTION]... SOURCE... DEST'))
4279 _('[OPTION]... SOURCE... DEST'))
4285 def rename(ui, repo, *pats, **opts):
4280 def rename(ui, repo, *pats, **opts):
4286 """rename files; equivalent of copy + remove
4281 """rename files; equivalent of copy + remove
4287
4282
4288 Mark dest as copies of sources; mark sources for deletion. If dest
4283 Mark dest as copies of sources; mark sources for deletion. If dest
4289 is a directory, copies are put in that directory. If dest is a
4284 is a directory, copies are put in that directory. If dest is a
4290 file, there can only be one source.
4285 file, there can only be one source.
4291
4286
4292 By default, this command copies the contents of files as they
4287 By default, this command copies the contents of files as they
4293 exist in the working directory. If invoked with -A/--after, the
4288 exist in the working directory. If invoked with -A/--after, the
4294 operation is recorded, but no copying is performed.
4289 operation is recorded, but no copying is performed.
4295
4290
4296 This command takes effect at the next commit. To undo a rename
4291 This command takes effect at the next commit. To undo a rename
4297 before that, see :hg:`revert`.
4292 before that, see :hg:`revert`.
4298
4293
4299 Returns 0 on success, 1 if errors are encountered.
4294 Returns 0 on success, 1 if errors are encountered.
4300 """
4295 """
4301 opts = pycompat.byteskwargs(opts)
4296 opts = pycompat.byteskwargs(opts)
4302 with repo.wlock(False):
4297 with repo.wlock(False):
4303 return cmdutil.copy(ui, repo, pats, opts, rename=True)
4298 return cmdutil.copy(ui, repo, pats, opts, rename=True)
4304
4299
4305 @command('resolve',
4300 @command('resolve',
4306 [('a', 'all', None, _('select all unresolved files')),
4301 [('a', 'all', None, _('select all unresolved files')),
4307 ('l', 'list', None, _('list state of files needing merge')),
4302 ('l', 'list', None, _('list state of files needing merge')),
4308 ('m', 'mark', None, _('mark files as resolved')),
4303 ('m', 'mark', None, _('mark files as resolved')),
4309 ('u', 'unmark', None, _('mark files as unresolved')),
4304 ('u', 'unmark', None, _('mark files as unresolved')),
4310 ('n', 'no-status', None, _('hide status prefix'))]
4305 ('n', 'no-status', None, _('hide status prefix'))]
4311 + mergetoolopts + walkopts + formatteropts,
4306 + mergetoolopts + walkopts + formatteropts,
4312 _('[OPTION]... [FILE]...'),
4307 _('[OPTION]... [FILE]...'),
4313 inferrepo=True)
4308 inferrepo=True)
4314 def resolve(ui, repo, *pats, **opts):
4309 def resolve(ui, repo, *pats, **opts):
4315 """redo merges or set/view the merge status of files
4310 """redo merges or set/view the merge status of files
4316
4311
4317 Merges with unresolved conflicts are often the result of
4312 Merges with unresolved conflicts are often the result of
4318 non-interactive merging using the ``internal:merge`` configuration
4313 non-interactive merging using the ``internal:merge`` configuration
4319 setting, or a command-line merge tool like ``diff3``. The resolve
4314 setting, or a command-line merge tool like ``diff3``. The resolve
4320 command is used to manage the files involved in a merge, after
4315 command is used to manage the files involved in a merge, after
4321 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
4316 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
4322 working directory must have two parents). See :hg:`help
4317 working directory must have two parents). See :hg:`help
4323 merge-tools` for information on configuring merge tools.
4318 merge-tools` for information on configuring merge tools.
4324
4319
4325 The resolve command can be used in the following ways:
4320 The resolve command can be used in the following ways:
4326
4321
4327 - :hg:`resolve [--tool TOOL] FILE...`: attempt to re-merge the specified
4322 - :hg:`resolve [--tool TOOL] FILE...`: attempt to re-merge the specified
4328 files, discarding any previous merge attempts. Re-merging is not
4323 files, discarding any previous merge attempts. Re-merging is not
4329 performed for files already marked as resolved. Use ``--all/-a``
4324 performed for files already marked as resolved. Use ``--all/-a``
4330 to select all unresolved files. ``--tool`` can be used to specify
4325 to select all unresolved files. ``--tool`` can be used to specify
4331 the merge tool used for the given files. It overrides the HGMERGE
4326 the merge tool used for the given files. It overrides the HGMERGE
4332 environment variable and your configuration files. Previous file
4327 environment variable and your configuration files. Previous file
4333 contents are saved with a ``.orig`` suffix.
4328 contents are saved with a ``.orig`` suffix.
4334
4329
4335 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
4330 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
4336 (e.g. after having manually fixed-up the files). The default is
4331 (e.g. after having manually fixed-up the files). The default is
4337 to mark all unresolved files.
4332 to mark all unresolved files.
4338
4333
4339 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
4334 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
4340 default is to mark all resolved files.
4335 default is to mark all resolved files.
4341
4336
4342 - :hg:`resolve -l`: list files which had or still have conflicts.
4337 - :hg:`resolve -l`: list files which had or still have conflicts.
4343 In the printed list, ``U`` = unresolved and ``R`` = resolved.
4338 In the printed list, ``U`` = unresolved and ``R`` = resolved.
4344 You can use ``set:unresolved()`` or ``set:resolved()`` to filter
4339 You can use ``set:unresolved()`` or ``set:resolved()`` to filter
4345 the list. See :hg:`help filesets` for details.
4340 the list. See :hg:`help filesets` for details.
4346
4341
4347 .. note::
4342 .. note::
4348
4343
4349 Mercurial will not let you commit files with unresolved merge
4344 Mercurial will not let you commit files with unresolved merge
4350 conflicts. You must use :hg:`resolve -m ...` before you can
4345 conflicts. You must use :hg:`resolve -m ...` before you can
4351 commit after a conflicting merge.
4346 commit after a conflicting merge.
4352
4347
4353 Returns 0 on success, 1 if any files fail a resolve attempt.
4348 Returns 0 on success, 1 if any files fail a resolve attempt.
4354 """
4349 """
4355
4350
4356 opts = pycompat.byteskwargs(opts)
4351 opts = pycompat.byteskwargs(opts)
4357 flaglist = 'all mark unmark list no_status'.split()
4352 flaglist = 'all mark unmark list no_status'.split()
4358 all, mark, unmark, show, nostatus = \
4353 all, mark, unmark, show, nostatus = \
4359 [opts.get(o) for o in flaglist]
4354 [opts.get(o) for o in flaglist]
4360
4355
4361 if (show and (mark or unmark)) or (mark and unmark):
4356 if (show and (mark or unmark)) or (mark and unmark):
4362 raise error.Abort(_("too many options specified"))
4357 raise error.Abort(_("too many options specified"))
4363 if pats and all:
4358 if pats and all:
4364 raise error.Abort(_("can't specify --all and patterns"))
4359 raise error.Abort(_("can't specify --all and patterns"))
4365 if not (all or pats or show or mark or unmark):
4360 if not (all or pats or show or mark or unmark):
4366 raise error.Abort(_('no files or directories specified'),
4361 raise error.Abort(_('no files or directories specified'),
4367 hint=('use --all to re-merge all unresolved files'))
4362 hint=('use --all to re-merge all unresolved files'))
4368
4363
4369 if show:
4364 if show:
4370 ui.pager('resolve')
4365 ui.pager('resolve')
4371 fm = ui.formatter('resolve', opts)
4366 fm = ui.formatter('resolve', opts)
4372 ms = mergemod.mergestate.read(repo)
4367 ms = mergemod.mergestate.read(repo)
4373 m = scmutil.match(repo[None], pats, opts)
4368 m = scmutil.match(repo[None], pats, opts)
4374
4369
4375 # Labels and keys based on merge state. Unresolved path conflicts show
4370 # Labels and keys based on merge state. Unresolved path conflicts show
4376 # as 'P'. Resolved path conflicts show as 'R', the same as normal
4371 # as 'P'. Resolved path conflicts show as 'R', the same as normal
4377 # resolved conflicts.
4372 # resolved conflicts.
4378 mergestateinfo = {
4373 mergestateinfo = {
4379 'u': ('resolve.unresolved', 'U'),
4374 'u': ('resolve.unresolved', 'U'),
4380 'r': ('resolve.resolved', 'R'),
4375 'r': ('resolve.resolved', 'R'),
4381 'pu': ('resolve.unresolved', 'P'),
4376 'pu': ('resolve.unresolved', 'P'),
4382 'pr': ('resolve.resolved', 'R'),
4377 'pr': ('resolve.resolved', 'R'),
4383 'd': ('resolve.driverresolved', 'D'),
4378 'd': ('resolve.driverresolved', 'D'),
4384 }
4379 }
4385
4380
4386 for f in ms:
4381 for f in ms:
4387 if not m(f):
4382 if not m(f):
4388 continue
4383 continue
4389
4384
4390 label, key = mergestateinfo[ms[f]]
4385 label, key = mergestateinfo[ms[f]]
4391 fm.startitem()
4386 fm.startitem()
4392 fm.condwrite(not nostatus, 'status', '%s ', key, label=label)
4387 fm.condwrite(not nostatus, 'status', '%s ', key, label=label)
4393 fm.write('path', '%s\n', f, label=label)
4388 fm.write('path', '%s\n', f, label=label)
4394 fm.end()
4389 fm.end()
4395 return 0
4390 return 0
4396
4391
4397 with repo.wlock():
4392 with repo.wlock():
4398 ms = mergemod.mergestate.read(repo)
4393 ms = mergemod.mergestate.read(repo)
4399
4394
4400 if not (ms.active() or repo.dirstate.p2() != nullid):
4395 if not (ms.active() or repo.dirstate.p2() != nullid):
4401 raise error.Abort(
4396 raise error.Abort(
4402 _('resolve command not applicable when not merging'))
4397 _('resolve command not applicable when not merging'))
4403
4398
4404 wctx = repo[None]
4399 wctx = repo[None]
4405
4400
4406 if ms.mergedriver and ms.mdstate() == 'u':
4401 if ms.mergedriver and ms.mdstate() == 'u':
4407 proceed = mergemod.driverpreprocess(repo, ms, wctx)
4402 proceed = mergemod.driverpreprocess(repo, ms, wctx)
4408 ms.commit()
4403 ms.commit()
4409 # allow mark and unmark to go through
4404 # allow mark and unmark to go through
4410 if not mark and not unmark and not proceed:
4405 if not mark and not unmark and not proceed:
4411 return 1
4406 return 1
4412
4407
4413 m = scmutil.match(wctx, pats, opts)
4408 m = scmutil.match(wctx, pats, opts)
4414 ret = 0
4409 ret = 0
4415 didwork = False
4410 didwork = False
4416 runconclude = False
4411 runconclude = False
4417
4412
4418 tocomplete = []
4413 tocomplete = []
4419 for f in ms:
4414 for f in ms:
4420 if not m(f):
4415 if not m(f):
4421 continue
4416 continue
4422
4417
4423 didwork = True
4418 didwork = True
4424
4419
4425 # don't let driver-resolved files be marked, and run the conclude
4420 # don't let driver-resolved files be marked, and run the conclude
4426 # step if asked to resolve
4421 # step if asked to resolve
4427 if ms[f] == "d":
4422 if ms[f] == "d":
4428 exact = m.exact(f)
4423 exact = m.exact(f)
4429 if mark:
4424 if mark:
4430 if exact:
4425 if exact:
4431 ui.warn(_('not marking %s as it is driver-resolved\n')
4426 ui.warn(_('not marking %s as it is driver-resolved\n')
4432 % f)
4427 % f)
4433 elif unmark:
4428 elif unmark:
4434 if exact:
4429 if exact:
4435 ui.warn(_('not unmarking %s as it is driver-resolved\n')
4430 ui.warn(_('not unmarking %s as it is driver-resolved\n')
4436 % f)
4431 % f)
4437 else:
4432 else:
4438 runconclude = True
4433 runconclude = True
4439 continue
4434 continue
4440
4435
4441 # path conflicts must be resolved manually
4436 # path conflicts must be resolved manually
4442 if ms[f] in ("pu", "pr"):
4437 if ms[f] in ("pu", "pr"):
4443 if mark:
4438 if mark:
4444 ms.mark(f, "pr")
4439 ms.mark(f, "pr")
4445 elif unmark:
4440 elif unmark:
4446 ms.mark(f, "pu")
4441 ms.mark(f, "pu")
4447 elif ms[f] == "pu":
4442 elif ms[f] == "pu":
4448 ui.warn(_('%s: path conflict must be resolved manually\n')
4443 ui.warn(_('%s: path conflict must be resolved manually\n')
4449 % f)
4444 % f)
4450 continue
4445 continue
4451
4446
4452 if mark:
4447 if mark:
4453 ms.mark(f, "r")
4448 ms.mark(f, "r")
4454 elif unmark:
4449 elif unmark:
4455 ms.mark(f, "u")
4450 ms.mark(f, "u")
4456 else:
4451 else:
4457 # backup pre-resolve (merge uses .orig for its own purposes)
4452 # backup pre-resolve (merge uses .orig for its own purposes)
4458 a = repo.wjoin(f)
4453 a = repo.wjoin(f)
4459 try:
4454 try:
4460 util.copyfile(a, a + ".resolve")
4455 util.copyfile(a, a + ".resolve")
4461 except (IOError, OSError) as inst:
4456 except (IOError, OSError) as inst:
4462 if inst.errno != errno.ENOENT:
4457 if inst.errno != errno.ENOENT:
4463 raise
4458 raise
4464
4459
4465 try:
4460 try:
4466 # preresolve file
4461 # preresolve file
4467 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
4462 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
4468 'resolve')
4463 'resolve')
4469 complete, r = ms.preresolve(f, wctx)
4464 complete, r = ms.preresolve(f, wctx)
4470 if not complete:
4465 if not complete:
4471 tocomplete.append(f)
4466 tocomplete.append(f)
4472 elif r:
4467 elif r:
4473 ret = 1
4468 ret = 1
4474 finally:
4469 finally:
4475 ui.setconfig('ui', 'forcemerge', '', 'resolve')
4470 ui.setconfig('ui', 'forcemerge', '', 'resolve')
4476 ms.commit()
4471 ms.commit()
4477
4472
4478 # replace filemerge's .orig file with our resolve file, but only
4473 # replace filemerge's .orig file with our resolve file, but only
4479 # for merges that are complete
4474 # for merges that are complete
4480 if complete:
4475 if complete:
4481 try:
4476 try:
4482 util.rename(a + ".resolve",
4477 util.rename(a + ".resolve",
4483 scmutil.origpath(ui, repo, a))
4478 scmutil.origpath(ui, repo, a))
4484 except OSError as inst:
4479 except OSError as inst:
4485 if inst.errno != errno.ENOENT:
4480 if inst.errno != errno.ENOENT:
4486 raise
4481 raise
4487
4482
4488 for f in tocomplete:
4483 for f in tocomplete:
4489 try:
4484 try:
4490 # resolve file
4485 # resolve file
4491 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
4486 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
4492 'resolve')
4487 'resolve')
4493 r = ms.resolve(f, wctx)
4488 r = ms.resolve(f, wctx)
4494 if r:
4489 if r:
4495 ret = 1
4490 ret = 1
4496 finally:
4491 finally:
4497 ui.setconfig('ui', 'forcemerge', '', 'resolve')
4492 ui.setconfig('ui', 'forcemerge', '', 'resolve')
4498 ms.commit()
4493 ms.commit()
4499
4494
4500 # replace filemerge's .orig file with our resolve file
4495 # replace filemerge's .orig file with our resolve file
4501 a = repo.wjoin(f)
4496 a = repo.wjoin(f)
4502 try:
4497 try:
4503 util.rename(a + ".resolve", scmutil.origpath(ui, repo, a))
4498 util.rename(a + ".resolve", scmutil.origpath(ui, repo, a))
4504 except OSError as inst:
4499 except OSError as inst:
4505 if inst.errno != errno.ENOENT:
4500 if inst.errno != errno.ENOENT:
4506 raise
4501 raise
4507
4502
4508 ms.commit()
4503 ms.commit()
4509 ms.recordactions()
4504 ms.recordactions()
4510
4505
4511 if not didwork and pats:
4506 if not didwork and pats:
4512 hint = None
4507 hint = None
4513 if not any([p for p in pats if p.find(':') >= 0]):
4508 if not any([p for p in pats if p.find(':') >= 0]):
4514 pats = ['path:%s' % p for p in pats]
4509 pats = ['path:%s' % p for p in pats]
4515 m = scmutil.match(wctx, pats, opts)
4510 m = scmutil.match(wctx, pats, opts)
4516 for f in ms:
4511 for f in ms:
4517 if not m(f):
4512 if not m(f):
4518 continue
4513 continue
4519 flags = ''.join(['-%s ' % o[0] for o in flaglist
4514 flags = ''.join(['-%s ' % o[0] for o in flaglist
4520 if opts.get(o)])
4515 if opts.get(o)])
4521 hint = _("(try: hg resolve %s%s)\n") % (
4516 hint = _("(try: hg resolve %s%s)\n") % (
4522 flags,
4517 flags,
4523 ' '.join(pats))
4518 ' '.join(pats))
4524 break
4519 break
4525 ui.warn(_("arguments do not match paths that need resolving\n"))
4520 ui.warn(_("arguments do not match paths that need resolving\n"))
4526 if hint:
4521 if hint:
4527 ui.warn(hint)
4522 ui.warn(hint)
4528 elif ms.mergedriver and ms.mdstate() != 's':
4523 elif ms.mergedriver and ms.mdstate() != 's':
4529 # run conclude step when either a driver-resolved file is requested
4524 # run conclude step when either a driver-resolved file is requested
4530 # or there are no driver-resolved files
4525 # or there are no driver-resolved files
4531 # we can't use 'ret' to determine whether any files are unresolved
4526 # we can't use 'ret' to determine whether any files are unresolved
4532 # because we might not have tried to resolve some
4527 # because we might not have tried to resolve some
4533 if ((runconclude or not list(ms.driverresolved()))
4528 if ((runconclude or not list(ms.driverresolved()))
4534 and not list(ms.unresolved())):
4529 and not list(ms.unresolved())):
4535 proceed = mergemod.driverconclude(repo, ms, wctx)
4530 proceed = mergemod.driverconclude(repo, ms, wctx)
4536 ms.commit()
4531 ms.commit()
4537 if not proceed:
4532 if not proceed:
4538 return 1
4533 return 1
4539
4534
4540 # Nudge users into finishing an unfinished operation
4535 # Nudge users into finishing an unfinished operation
4541 unresolvedf = list(ms.unresolved())
4536 unresolvedf = list(ms.unresolved())
4542 driverresolvedf = list(ms.driverresolved())
4537 driverresolvedf = list(ms.driverresolved())
4543 if not unresolvedf and not driverresolvedf:
4538 if not unresolvedf and not driverresolvedf:
4544 ui.status(_('(no more unresolved files)\n'))
4539 ui.status(_('(no more unresolved files)\n'))
4545 cmdutil.checkafterresolved(repo)
4540 cmdutil.checkafterresolved(repo)
4546 elif not unresolvedf:
4541 elif not unresolvedf:
4547 ui.status(_('(no more unresolved files -- '
4542 ui.status(_('(no more unresolved files -- '
4548 'run "hg resolve --all" to conclude)\n'))
4543 'run "hg resolve --all" to conclude)\n'))
4549
4544
4550 return ret
4545 return ret
4551
4546
4552 @command('revert',
4547 @command('revert',
4553 [('a', 'all', None, _('revert all changes when no arguments given')),
4548 [('a', 'all', None, _('revert all changes when no arguments given')),
4554 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
4549 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
4555 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
4550 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
4556 ('C', 'no-backup', None, _('do not save backup copies of files')),
4551 ('C', 'no-backup', None, _('do not save backup copies of files')),
4557 ('i', 'interactive', None, _('interactively select the changes')),
4552 ('i', 'interactive', None, _('interactively select the changes')),
4558 ] + walkopts + dryrunopts,
4553 ] + walkopts + dryrunopts,
4559 _('[OPTION]... [-r REV] [NAME]...'))
4554 _('[OPTION]... [-r REV] [NAME]...'))
4560 def revert(ui, repo, *pats, **opts):
4555 def revert(ui, repo, *pats, **opts):
4561 """restore files to their checkout state
4556 """restore files to their checkout state
4562
4557
4563 .. note::
4558 .. note::
4564
4559
4565 To check out earlier revisions, you should use :hg:`update REV`.
4560 To check out earlier revisions, you should use :hg:`update REV`.
4566 To cancel an uncommitted merge (and lose your changes),
4561 To cancel an uncommitted merge (and lose your changes),
4567 use :hg:`update --clean .`.
4562 use :hg:`update --clean .`.
4568
4563
4569 With no revision specified, revert the specified files or directories
4564 With no revision specified, revert the specified files or directories
4570 to the contents they had in the parent of the working directory.
4565 to the contents they had in the parent of the working directory.
4571 This restores the contents of files to an unmodified
4566 This restores the contents of files to an unmodified
4572 state and unschedules adds, removes, copies, and renames. If the
4567 state and unschedules adds, removes, copies, and renames. If the
4573 working directory has two parents, you must explicitly specify a
4568 working directory has two parents, you must explicitly specify a
4574 revision.
4569 revision.
4575
4570
4576 Using the -r/--rev or -d/--date options, revert the given files or
4571 Using the -r/--rev or -d/--date options, revert the given files or
4577 directories to their states as of a specific revision. Because
4572 directories to their states as of a specific revision. Because
4578 revert does not change the working directory parents, this will
4573 revert does not change the working directory parents, this will
4579 cause these files to appear modified. This can be helpful to "back
4574 cause these files to appear modified. This can be helpful to "back
4580 out" some or all of an earlier change. See :hg:`backout` for a
4575 out" some or all of an earlier change. See :hg:`backout` for a
4581 related method.
4576 related method.
4582
4577
4583 Modified files are saved with a .orig suffix before reverting.
4578 Modified files are saved with a .orig suffix before reverting.
4584 To disable these backups, use --no-backup. It is possible to store
4579 To disable these backups, use --no-backup. It is possible to store
4585 the backup files in a custom directory relative to the root of the
4580 the backup files in a custom directory relative to the root of the
4586 repository by setting the ``ui.origbackuppath`` configuration
4581 repository by setting the ``ui.origbackuppath`` configuration
4587 option.
4582 option.
4588
4583
4589 See :hg:`help dates` for a list of formats valid for -d/--date.
4584 See :hg:`help dates` for a list of formats valid for -d/--date.
4590
4585
4591 See :hg:`help backout` for a way to reverse the effect of an
4586 See :hg:`help backout` for a way to reverse the effect of an
4592 earlier changeset.
4587 earlier changeset.
4593
4588
4594 Returns 0 on success.
4589 Returns 0 on success.
4595 """
4590 """
4596
4591
4597 opts = pycompat.byteskwargs(opts)
4592 opts = pycompat.byteskwargs(opts)
4598 if opts.get("date"):
4593 if opts.get("date"):
4599 if opts.get("rev"):
4594 if opts.get("rev"):
4600 raise error.Abort(_("you can't specify a revision and a date"))
4595 raise error.Abort(_("you can't specify a revision and a date"))
4601 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
4596 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
4602
4597
4603 parent, p2 = repo.dirstate.parents()
4598 parent, p2 = repo.dirstate.parents()
4604 if not opts.get('rev') and p2 != nullid:
4599 if not opts.get('rev') and p2 != nullid:
4605 # revert after merge is a trap for new users (issue2915)
4600 # revert after merge is a trap for new users (issue2915)
4606 raise error.Abort(_('uncommitted merge with no revision specified'),
4601 raise error.Abort(_('uncommitted merge with no revision specified'),
4607 hint=_("use 'hg update' or see 'hg help revert'"))
4602 hint=_("use 'hg update' or see 'hg help revert'"))
4608
4603
4609 rev = opts.get('rev')
4604 rev = opts.get('rev')
4610 if rev:
4605 if rev:
4611 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
4606 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
4612 ctx = scmutil.revsingle(repo, rev)
4607 ctx = scmutil.revsingle(repo, rev)
4613
4608
4614 if (not (pats or opts.get('include') or opts.get('exclude') or
4609 if (not (pats or opts.get('include') or opts.get('exclude') or
4615 opts.get('all') or opts.get('interactive'))):
4610 opts.get('all') or opts.get('interactive'))):
4616 msg = _("no files or directories specified")
4611 msg = _("no files or directories specified")
4617 if p2 != nullid:
4612 if p2 != nullid:
4618 hint = _("uncommitted merge, use --all to discard all changes,"
4613 hint = _("uncommitted merge, use --all to discard all changes,"
4619 " or 'hg update -C .' to abort the merge")
4614 " or 'hg update -C .' to abort the merge")
4620 raise error.Abort(msg, hint=hint)
4615 raise error.Abort(msg, hint=hint)
4621 dirty = any(repo.status())
4616 dirty = any(repo.status())
4622 node = ctx.node()
4617 node = ctx.node()
4623 if node != parent:
4618 if node != parent:
4624 if dirty:
4619 if dirty:
4625 hint = _("uncommitted changes, use --all to discard all"
4620 hint = _("uncommitted changes, use --all to discard all"
4626 " changes, or 'hg update %s' to update") % ctx.rev()
4621 " changes, or 'hg update %s' to update") % ctx.rev()
4627 else:
4622 else:
4628 hint = _("use --all to revert all files,"
4623 hint = _("use --all to revert all files,"
4629 " or 'hg update %s' to update") % ctx.rev()
4624 " or 'hg update %s' to update") % ctx.rev()
4630 elif dirty:
4625 elif dirty:
4631 hint = _("uncommitted changes, use --all to discard all changes")
4626 hint = _("uncommitted changes, use --all to discard all changes")
4632 else:
4627 else:
4633 hint = _("use --all to revert all files")
4628 hint = _("use --all to revert all files")
4634 raise error.Abort(msg, hint=hint)
4629 raise error.Abort(msg, hint=hint)
4635
4630
4636 return cmdutil.revert(ui, repo, ctx, (parent, p2), *pats,
4631 return cmdutil.revert(ui, repo, ctx, (parent, p2), *pats,
4637 **pycompat.strkwargs(opts))
4632 **pycompat.strkwargs(opts))
4638
4633
4639 @command('rollback', dryrunopts +
4634 @command('rollback', dryrunopts +
4640 [('f', 'force', False, _('ignore safety measures'))])
4635 [('f', 'force', False, _('ignore safety measures'))])
4641 def rollback(ui, repo, **opts):
4636 def rollback(ui, repo, **opts):
4642 """roll back the last transaction (DANGEROUS) (DEPRECATED)
4637 """roll back the last transaction (DANGEROUS) (DEPRECATED)
4643
4638
4644 Please use :hg:`commit --amend` instead of rollback to correct
4639 Please use :hg:`commit --amend` instead of rollback to correct
4645 mistakes in the last commit.
4640 mistakes in the last commit.
4646
4641
4647 This command should be used with care. There is only one level of
4642 This command should be used with care. There is only one level of
4648 rollback, and there is no way to undo a rollback. It will also
4643 rollback, and there is no way to undo a rollback. It will also
4649 restore the dirstate at the time of the last transaction, losing
4644 restore the dirstate at the time of the last transaction, losing
4650 any dirstate changes since that time. This command does not alter
4645 any dirstate changes since that time. This command does not alter
4651 the working directory.
4646 the working directory.
4652
4647
4653 Transactions are used to encapsulate the effects of all commands
4648 Transactions are used to encapsulate the effects of all commands
4654 that create new changesets or propagate existing changesets into a
4649 that create new changesets or propagate existing changesets into a
4655 repository.
4650 repository.
4656
4651
4657 .. container:: verbose
4652 .. container:: verbose
4658
4653
4659 For example, the following commands are transactional, and their
4654 For example, the following commands are transactional, and their
4660 effects can be rolled back:
4655 effects can be rolled back:
4661
4656
4662 - commit
4657 - commit
4663 - import
4658 - import
4664 - pull
4659 - pull
4665 - push (with this repository as the destination)
4660 - push (with this repository as the destination)
4666 - unbundle
4661 - unbundle
4667
4662
4668 To avoid permanent data loss, rollback will refuse to rollback a
4663 To avoid permanent data loss, rollback will refuse to rollback a
4669 commit transaction if it isn't checked out. Use --force to
4664 commit transaction if it isn't checked out. Use --force to
4670 override this protection.
4665 override this protection.
4671
4666
4672 The rollback command can be entirely disabled by setting the
4667 The rollback command can be entirely disabled by setting the
4673 ``ui.rollback`` configuration setting to false. If you're here
4668 ``ui.rollback`` configuration setting to false. If you're here
4674 because you want to use rollback and it's disabled, you can
4669 because you want to use rollback and it's disabled, you can
4675 re-enable the command by setting ``ui.rollback`` to true.
4670 re-enable the command by setting ``ui.rollback`` to true.
4676
4671
4677 This command is not intended for use on public repositories. Once
4672 This command is not intended for use on public repositories. Once
4678 changes are visible for pull by other users, rolling a transaction
4673 changes are visible for pull by other users, rolling a transaction
4679 back locally is ineffective (someone else may already have pulled
4674 back locally is ineffective (someone else may already have pulled
4680 the changes). Furthermore, a race is possible with readers of the
4675 the changes). Furthermore, a race is possible with readers of the
4681 repository; for example an in-progress pull from the repository
4676 repository; for example an in-progress pull from the repository
4682 may fail if a rollback is performed.
4677 may fail if a rollback is performed.
4683
4678
4684 Returns 0 on success, 1 if no rollback data is available.
4679 Returns 0 on success, 1 if no rollback data is available.
4685 """
4680 """
4686 if not ui.configbool('ui', 'rollback'):
4681 if not ui.configbool('ui', 'rollback'):
4687 raise error.Abort(_('rollback is disabled because it is unsafe'),
4682 raise error.Abort(_('rollback is disabled because it is unsafe'),
4688 hint=('see `hg help -v rollback` for information'))
4683 hint=('see `hg help -v rollback` for information'))
4689 return repo.rollback(dryrun=opts.get(r'dry_run'),
4684 return repo.rollback(dryrun=opts.get(r'dry_run'),
4690 force=opts.get(r'force'))
4685 force=opts.get(r'force'))
4691
4686
4692 @command('root', [], cmdtype=readonly)
4687 @command('root', [], cmdtype=readonly)
4693 def root(ui, repo):
4688 def root(ui, repo):
4694 """print the root (top) of the current working directory
4689 """print the root (top) of the current working directory
4695
4690
4696 Print the root directory of the current repository.
4691 Print the root directory of the current repository.
4697
4692
4698 Returns 0 on success.
4693 Returns 0 on success.
4699 """
4694 """
4700 ui.write(repo.root + "\n")
4695 ui.write(repo.root + "\n")
4701
4696
4702 @command('^serve',
4697 @command('^serve',
4703 [('A', 'accesslog', '', _('name of access log file to write to'),
4698 [('A', 'accesslog', '', _('name of access log file to write to'),
4704 _('FILE')),
4699 _('FILE')),
4705 ('d', 'daemon', None, _('run server in background')),
4700 ('d', 'daemon', None, _('run server in background')),
4706 ('', 'daemon-postexec', [], _('used internally by daemon mode')),
4701 ('', 'daemon-postexec', [], _('used internally by daemon mode')),
4707 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
4702 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
4708 # use string type, then we can check if something was passed
4703 # use string type, then we can check if something was passed
4709 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
4704 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
4710 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
4705 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
4711 _('ADDR')),
4706 _('ADDR')),
4712 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
4707 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
4713 _('PREFIX')),
4708 _('PREFIX')),
4714 ('n', 'name', '',
4709 ('n', 'name', '',
4715 _('name to show in web pages (default: working directory)'), _('NAME')),
4710 _('name to show in web pages (default: working directory)'), _('NAME')),
4716 ('', 'web-conf', '',
4711 ('', 'web-conf', '',
4717 _("name of the hgweb config file (see 'hg help hgweb')"), _('FILE')),
4712 _("name of the hgweb config file (see 'hg help hgweb')"), _('FILE')),
4718 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
4713 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
4719 _('FILE')),
4714 _('FILE')),
4720 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
4715 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
4721 ('', 'stdio', None, _('for remote clients (ADVANCED)')),
4716 ('', 'stdio', None, _('for remote clients (ADVANCED)')),
4722 ('', 'cmdserver', '', _('for remote clients (ADVANCED)'), _('MODE')),
4717 ('', 'cmdserver', '', _('for remote clients (ADVANCED)'), _('MODE')),
4723 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
4718 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
4724 ('', 'style', '', _('template style to use'), _('STYLE')),
4719 ('', 'style', '', _('template style to use'), _('STYLE')),
4725 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
4720 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
4726 ('', 'certificate', '', _('SSL certificate file'), _('FILE'))]
4721 ('', 'certificate', '', _('SSL certificate file'), _('FILE'))]
4727 + subrepoopts,
4722 + subrepoopts,
4728 _('[OPTION]...'),
4723 _('[OPTION]...'),
4729 optionalrepo=True)
4724 optionalrepo=True)
4730 def serve(ui, repo, **opts):
4725 def serve(ui, repo, **opts):
4731 """start stand-alone webserver
4726 """start stand-alone webserver
4732
4727
4733 Start a local HTTP repository browser and pull server. You can use
4728 Start a local HTTP repository browser and pull server. You can use
4734 this for ad-hoc sharing and browsing of repositories. It is
4729 this for ad-hoc sharing and browsing of repositories. It is
4735 recommended to use a real web server to serve a repository for
4730 recommended to use a real web server to serve a repository for
4736 longer periods of time.
4731 longer periods of time.
4737
4732
4738 Please note that the server does not implement access control.
4733 Please note that the server does not implement access control.
4739 This means that, by default, anybody can read from the server and
4734 This means that, by default, anybody can read from the server and
4740 nobody can write to it by default. Set the ``web.allow-push``
4735 nobody can write to it by default. Set the ``web.allow-push``
4741 option to ``*`` to allow everybody to push to the server. You
4736 option to ``*`` to allow everybody to push to the server. You
4742 should use a real web server if you need to authenticate users.
4737 should use a real web server if you need to authenticate users.
4743
4738
4744 By default, the server logs accesses to stdout and errors to
4739 By default, the server logs accesses to stdout and errors to
4745 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
4740 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
4746 files.
4741 files.
4747
4742
4748 To have the server choose a free port number to listen on, specify
4743 To have the server choose a free port number to listen on, specify
4749 a port number of 0; in this case, the server will print the port
4744 a port number of 0; in this case, the server will print the port
4750 number it uses.
4745 number it uses.
4751
4746
4752 Returns 0 on success.
4747 Returns 0 on success.
4753 """
4748 """
4754
4749
4755 opts = pycompat.byteskwargs(opts)
4750 opts = pycompat.byteskwargs(opts)
4756 if opts["stdio"] and opts["cmdserver"]:
4751 if opts["stdio"] and opts["cmdserver"]:
4757 raise error.Abort(_("cannot use --stdio with --cmdserver"))
4752 raise error.Abort(_("cannot use --stdio with --cmdserver"))
4758
4753
4759 if opts["stdio"]:
4754 if opts["stdio"]:
4760 if repo is None:
4755 if repo is None:
4761 raise error.RepoError(_("there is no Mercurial repository here"
4756 raise error.RepoError(_("there is no Mercurial repository here"
4762 " (.hg not found)"))
4757 " (.hg not found)"))
4763 s = sshserver.sshserver(ui, repo)
4758 s = sshserver.sshserver(ui, repo)
4764 s.serve_forever()
4759 s.serve_forever()
4765
4760
4766 service = server.createservice(ui, repo, opts)
4761 service = server.createservice(ui, repo, opts)
4767 return server.runservice(opts, initfn=service.init, runfn=service.run)
4762 return server.runservice(opts, initfn=service.init, runfn=service.run)
4768
4763
4769 @command('^status|st',
4764 @command('^status|st',
4770 [('A', 'all', None, _('show status of all files')),
4765 [('A', 'all', None, _('show status of all files')),
4771 ('m', 'modified', None, _('show only modified files')),
4766 ('m', 'modified', None, _('show only modified files')),
4772 ('a', 'added', None, _('show only added files')),
4767 ('a', 'added', None, _('show only added files')),
4773 ('r', 'removed', None, _('show only removed files')),
4768 ('r', 'removed', None, _('show only removed files')),
4774 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
4769 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
4775 ('c', 'clean', None, _('show only files without changes')),
4770 ('c', 'clean', None, _('show only files without changes')),
4776 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
4771 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
4777 ('i', 'ignored', None, _('show only ignored files')),
4772 ('i', 'ignored', None, _('show only ignored files')),
4778 ('n', 'no-status', None, _('hide status prefix')),
4773 ('n', 'no-status', None, _('hide status prefix')),
4779 ('t', 'terse', '', _('show the terse output (EXPERIMENTAL)')),
4774 ('t', 'terse', '', _('show the terse output (EXPERIMENTAL)')),
4780 ('C', 'copies', None, _('show source of copied files')),
4775 ('C', 'copies', None, _('show source of copied files')),
4781 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
4776 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
4782 ('', 'rev', [], _('show difference from revision'), _('REV')),
4777 ('', 'rev', [], _('show difference from revision'), _('REV')),
4783 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
4778 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
4784 ] + walkopts + subrepoopts + formatteropts,
4779 ] + walkopts + subrepoopts + formatteropts,
4785 _('[OPTION]... [FILE]...'),
4780 _('[OPTION]... [FILE]...'),
4786 inferrepo=True, cmdtype=readonly)
4781 inferrepo=True, cmdtype=readonly)
4787 def status(ui, repo, *pats, **opts):
4782 def status(ui, repo, *pats, **opts):
4788 """show changed files in the working directory
4783 """show changed files in the working directory
4789
4784
4790 Show status of files in the repository. If names are given, only
4785 Show status of files in the repository. If names are given, only
4791 files that match are shown. Files that are clean or ignored or
4786 files that match are shown. Files that are clean or ignored or
4792 the source of a copy/move operation, are not listed unless
4787 the source of a copy/move operation, are not listed unless
4793 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
4788 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
4794 Unless options described with "show only ..." are given, the
4789 Unless options described with "show only ..." are given, the
4795 options -mardu are used.
4790 options -mardu are used.
4796
4791
4797 Option -q/--quiet hides untracked (unknown and ignored) files
4792 Option -q/--quiet hides untracked (unknown and ignored) files
4798 unless explicitly requested with -u/--unknown or -i/--ignored.
4793 unless explicitly requested with -u/--unknown or -i/--ignored.
4799
4794
4800 .. note::
4795 .. note::
4801
4796
4802 :hg:`status` may appear to disagree with diff if permissions have
4797 :hg:`status` may appear to disagree with diff if permissions have
4803 changed or a merge has occurred. The standard diff format does
4798 changed or a merge has occurred. The standard diff format does
4804 not report permission changes and diff only reports changes
4799 not report permission changes and diff only reports changes
4805 relative to one merge parent.
4800 relative to one merge parent.
4806
4801
4807 If one revision is given, it is used as the base revision.
4802 If one revision is given, it is used as the base revision.
4808 If two revisions are given, the differences between them are
4803 If two revisions are given, the differences between them are
4809 shown. The --change option can also be used as a shortcut to list
4804 shown. The --change option can also be used as a shortcut to list
4810 the changed files of a revision from its first parent.
4805 the changed files of a revision from its first parent.
4811
4806
4812 The codes used to show the status of files are::
4807 The codes used to show the status of files are::
4813
4808
4814 M = modified
4809 M = modified
4815 A = added
4810 A = added
4816 R = removed
4811 R = removed
4817 C = clean
4812 C = clean
4818 ! = missing (deleted by non-hg command, but still tracked)
4813 ! = missing (deleted by non-hg command, but still tracked)
4819 ? = not tracked
4814 ? = not tracked
4820 I = ignored
4815 I = ignored
4821 = origin of the previous file (with --copies)
4816 = origin of the previous file (with --copies)
4822
4817
4823 .. container:: verbose
4818 .. container:: verbose
4824
4819
4825 The -t/--terse option abbreviates the output by showing only the directory
4820 The -t/--terse option abbreviates the output by showing only the directory
4826 name if all the files in it share the same status. The option takes an
4821 name if all the files in it share the same status. The option takes an
4827 argument indicating the statuses to abbreviate: 'm' for 'modified', 'a'
4822 argument indicating the statuses to abbreviate: 'm' for 'modified', 'a'
4828 for 'added', 'r' for 'removed', 'd' for 'deleted', 'u' for 'unknown', 'i'
4823 for 'added', 'r' for 'removed', 'd' for 'deleted', 'u' for 'unknown', 'i'
4829 for 'ignored' and 'c' for clean.
4824 for 'ignored' and 'c' for clean.
4830
4825
4831 It abbreviates only those statuses which are passed. Note that clean and
4826 It abbreviates only those statuses which are passed. Note that clean and
4832 ignored files are not displayed with '--terse ic' unless the -c/--clean
4827 ignored files are not displayed with '--terse ic' unless the -c/--clean
4833 and -i/--ignored options are also used.
4828 and -i/--ignored options are also used.
4834
4829
4835 The -v/--verbose option shows information when the repository is in an
4830 The -v/--verbose option shows information when the repository is in an
4836 unfinished merge, shelve, rebase state etc. You can have this behavior
4831 unfinished merge, shelve, rebase state etc. You can have this behavior
4837 turned on by default by enabling the ``commands.status.verbose`` option.
4832 turned on by default by enabling the ``commands.status.verbose`` option.
4838
4833
4839 You can skip displaying some of these states by setting
4834 You can skip displaying some of these states by setting
4840 ``commands.status.skipstates`` to one or more of: 'bisect', 'graft',
4835 ``commands.status.skipstates`` to one or more of: 'bisect', 'graft',
4841 'histedit', 'merge', 'rebase', or 'unshelve'.
4836 'histedit', 'merge', 'rebase', or 'unshelve'.
4842
4837
4843 Examples:
4838 Examples:
4844
4839
4845 - show changes in the working directory relative to a
4840 - show changes in the working directory relative to a
4846 changeset::
4841 changeset::
4847
4842
4848 hg status --rev 9353
4843 hg status --rev 9353
4849
4844
4850 - show changes in the working directory relative to the
4845 - show changes in the working directory relative to the
4851 current directory (see :hg:`help patterns` for more information)::
4846 current directory (see :hg:`help patterns` for more information)::
4852
4847
4853 hg status re:
4848 hg status re:
4854
4849
4855 - show all changes including copies in an existing changeset::
4850 - show all changes including copies in an existing changeset::
4856
4851
4857 hg status --copies --change 9353
4852 hg status --copies --change 9353
4858
4853
4859 - get a NUL separated list of added files, suitable for xargs::
4854 - get a NUL separated list of added files, suitable for xargs::
4860
4855
4861 hg status -an0
4856 hg status -an0
4862
4857
4863 - show more information about the repository status, abbreviating
4858 - show more information about the repository status, abbreviating
4864 added, removed, modified, deleted, and untracked paths::
4859 added, removed, modified, deleted, and untracked paths::
4865
4860
4866 hg status -v -t mardu
4861 hg status -v -t mardu
4867
4862
4868 Returns 0 on success.
4863 Returns 0 on success.
4869
4864
4870 """
4865 """
4871
4866
4872 opts = pycompat.byteskwargs(opts)
4867 opts = pycompat.byteskwargs(opts)
4873 revs = opts.get('rev')
4868 revs = opts.get('rev')
4874 change = opts.get('change')
4869 change = opts.get('change')
4875 terse = opts.get('terse')
4870 terse = opts.get('terse')
4876
4871
4877 if revs and change:
4872 if revs and change:
4878 msg = _('cannot specify --rev and --change at the same time')
4873 msg = _('cannot specify --rev and --change at the same time')
4879 raise error.Abort(msg)
4874 raise error.Abort(msg)
4880 elif revs and terse:
4875 elif revs and terse:
4881 msg = _('cannot use --terse with --rev')
4876 msg = _('cannot use --terse with --rev')
4882 raise error.Abort(msg)
4877 raise error.Abort(msg)
4883 elif change:
4878 elif change:
4884 repo = scmutil.unhidehashlikerevs(repo, [change], 'nowarn')
4879 repo = scmutil.unhidehashlikerevs(repo, [change], 'nowarn')
4885 node2 = scmutil.revsingle(repo, change, None).node()
4880 node2 = scmutil.revsingle(repo, change, None).node()
4886 node1 = repo[node2].p1().node()
4881 node1 = repo[node2].p1().node()
4887 else:
4882 else:
4888 repo = scmutil.unhidehashlikerevs(repo, revs, 'nowarn')
4883 repo = scmutil.unhidehashlikerevs(repo, revs, 'nowarn')
4889 node1, node2 = scmutil.revpair(repo, revs)
4884 node1, node2 = scmutil.revpair(repo, revs)
4890
4885
4891 if pats or ui.configbool('commands', 'status.relative'):
4886 if pats or ui.configbool('commands', 'status.relative'):
4892 cwd = repo.getcwd()
4887 cwd = repo.getcwd()
4893 else:
4888 else:
4894 cwd = ''
4889 cwd = ''
4895
4890
4896 if opts.get('print0'):
4891 if opts.get('print0'):
4897 end = '\0'
4892 end = '\0'
4898 else:
4893 else:
4899 end = '\n'
4894 end = '\n'
4900 copy = {}
4895 copy = {}
4901 states = 'modified added removed deleted unknown ignored clean'.split()
4896 states = 'modified added removed deleted unknown ignored clean'.split()
4902 show = [k for k in states if opts.get(k)]
4897 show = [k for k in states if opts.get(k)]
4903 if opts.get('all'):
4898 if opts.get('all'):
4904 show += ui.quiet and (states[:4] + ['clean']) or states
4899 show += ui.quiet and (states[:4] + ['clean']) or states
4905
4900
4906 if not show:
4901 if not show:
4907 if ui.quiet:
4902 if ui.quiet:
4908 show = states[:4]
4903 show = states[:4]
4909 else:
4904 else:
4910 show = states[:5]
4905 show = states[:5]
4911
4906
4912 m = scmutil.match(repo[node2], pats, opts)
4907 m = scmutil.match(repo[node2], pats, opts)
4913 if terse:
4908 if terse:
4914 # we need to compute clean and unknown to terse
4909 # we need to compute clean and unknown to terse
4915 stat = repo.status(node1, node2, m,
4910 stat = repo.status(node1, node2, m,
4916 'ignored' in show or 'i' in terse,
4911 'ignored' in show or 'i' in terse,
4917 True, True, opts.get('subrepos'))
4912 True, True, opts.get('subrepos'))
4918
4913
4919 stat = cmdutil.tersedir(stat, terse)
4914 stat = cmdutil.tersedir(stat, terse)
4920 else:
4915 else:
4921 stat = repo.status(node1, node2, m,
4916 stat = repo.status(node1, node2, m,
4922 'ignored' in show, 'clean' in show,
4917 'ignored' in show, 'clean' in show,
4923 'unknown' in show, opts.get('subrepos'))
4918 'unknown' in show, opts.get('subrepos'))
4924
4919
4925 changestates = zip(states, pycompat.iterbytestr('MAR!?IC'), stat)
4920 changestates = zip(states, pycompat.iterbytestr('MAR!?IC'), stat)
4926
4921
4927 if (opts.get('all') or opts.get('copies')
4922 if (opts.get('all') or opts.get('copies')
4928 or ui.configbool('ui', 'statuscopies')) and not opts.get('no_status'):
4923 or ui.configbool('ui', 'statuscopies')) and not opts.get('no_status'):
4929 copy = copies.pathcopies(repo[node1], repo[node2], m)
4924 copy = copies.pathcopies(repo[node1], repo[node2], m)
4930
4925
4931 ui.pager('status')
4926 ui.pager('status')
4932 fm = ui.formatter('status', opts)
4927 fm = ui.formatter('status', opts)
4933 fmt = '%s' + end
4928 fmt = '%s' + end
4934 showchar = not opts.get('no_status')
4929 showchar = not opts.get('no_status')
4935
4930
4936 for state, char, files in changestates:
4931 for state, char, files in changestates:
4937 if state in show:
4932 if state in show:
4938 label = 'status.' + state
4933 label = 'status.' + state
4939 for f in files:
4934 for f in files:
4940 fm.startitem()
4935 fm.startitem()
4941 fm.condwrite(showchar, 'status', '%s ', char, label=label)
4936 fm.condwrite(showchar, 'status', '%s ', char, label=label)
4942 fm.write('path', fmt, repo.pathto(f, cwd), label=label)
4937 fm.write('path', fmt, repo.pathto(f, cwd), label=label)
4943 if f in copy:
4938 if f in copy:
4944 fm.write("copy", ' %s' + end, repo.pathto(copy[f], cwd),
4939 fm.write("copy", ' %s' + end, repo.pathto(copy[f], cwd),
4945 label='status.copied')
4940 label='status.copied')
4946
4941
4947 if ((ui.verbose or ui.configbool('commands', 'status.verbose'))
4942 if ((ui.verbose or ui.configbool('commands', 'status.verbose'))
4948 and not ui.plain()):
4943 and not ui.plain()):
4949 cmdutil.morestatus(repo, fm)
4944 cmdutil.morestatus(repo, fm)
4950 fm.end()
4945 fm.end()
4951
4946
4952 @command('^summary|sum',
4947 @command('^summary|sum',
4953 [('', 'remote', None, _('check for push and pull'))],
4948 [('', 'remote', None, _('check for push and pull'))],
4954 '[--remote]', cmdtype=readonly)
4949 '[--remote]', cmdtype=readonly)
4955 def summary(ui, repo, **opts):
4950 def summary(ui, repo, **opts):
4956 """summarize working directory state
4951 """summarize working directory state
4957
4952
4958 This generates a brief summary of the working directory state,
4953 This generates a brief summary of the working directory state,
4959 including parents, branch, commit status, phase and available updates.
4954 including parents, branch, commit status, phase and available updates.
4960
4955
4961 With the --remote option, this will check the default paths for
4956 With the --remote option, this will check the default paths for
4962 incoming and outgoing changes. This can be time-consuming.
4957 incoming and outgoing changes. This can be time-consuming.
4963
4958
4964 Returns 0 on success.
4959 Returns 0 on success.
4965 """
4960 """
4966
4961
4967 opts = pycompat.byteskwargs(opts)
4962 opts = pycompat.byteskwargs(opts)
4968 ui.pager('summary')
4963 ui.pager('summary')
4969 ctx = repo[None]
4964 ctx = repo[None]
4970 parents = ctx.parents()
4965 parents = ctx.parents()
4971 pnode = parents[0].node()
4966 pnode = parents[0].node()
4972 marks = []
4967 marks = []
4973
4968
4974 ms = None
4969 ms = None
4975 try:
4970 try:
4976 ms = mergemod.mergestate.read(repo)
4971 ms = mergemod.mergestate.read(repo)
4977 except error.UnsupportedMergeRecords as e:
4972 except error.UnsupportedMergeRecords as e:
4978 s = ' '.join(e.recordtypes)
4973 s = ' '.join(e.recordtypes)
4979 ui.warn(
4974 ui.warn(
4980 _('warning: merge state has unsupported record types: %s\n') % s)
4975 _('warning: merge state has unsupported record types: %s\n') % s)
4981 unresolved = []
4976 unresolved = []
4982 else:
4977 else:
4983 unresolved = list(ms.unresolved())
4978 unresolved = list(ms.unresolved())
4984
4979
4985 for p in parents:
4980 for p in parents:
4986 # label with log.changeset (instead of log.parent) since this
4981 # label with log.changeset (instead of log.parent) since this
4987 # shows a working directory parent *changeset*:
4982 # shows a working directory parent *changeset*:
4988 # i18n: column positioning for "hg summary"
4983 # i18n: column positioning for "hg summary"
4989 ui.write(_('parent: %d:%s ') % (p.rev(), p),
4984 ui.write(_('parent: %d:%s ') % (p.rev(), p),
4990 label=cmdutil._changesetlabels(p))
4985 label=cmdutil._changesetlabels(p))
4991 ui.write(' '.join(p.tags()), label='log.tag')
4986 ui.write(' '.join(p.tags()), label='log.tag')
4992 if p.bookmarks():
4987 if p.bookmarks():
4993 marks.extend(p.bookmarks())
4988 marks.extend(p.bookmarks())
4994 if p.rev() == -1:
4989 if p.rev() == -1:
4995 if not len(repo):
4990 if not len(repo):
4996 ui.write(_(' (empty repository)'))
4991 ui.write(_(' (empty repository)'))
4997 else:
4992 else:
4998 ui.write(_(' (no revision checked out)'))
4993 ui.write(_(' (no revision checked out)'))
4999 if p.obsolete():
4994 if p.obsolete():
5000 ui.write(_(' (obsolete)'))
4995 ui.write(_(' (obsolete)'))
5001 if p.isunstable():
4996 if p.isunstable():
5002 instabilities = (ui.label(instability, 'trouble.%s' % instability)
4997 instabilities = (ui.label(instability, 'trouble.%s' % instability)
5003 for instability in p.instabilities())
4998 for instability in p.instabilities())
5004 ui.write(' ('
4999 ui.write(' ('
5005 + ', '.join(instabilities)
5000 + ', '.join(instabilities)
5006 + ')')
5001 + ')')
5007 ui.write('\n')
5002 ui.write('\n')
5008 if p.description():
5003 if p.description():
5009 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
5004 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
5010 label='log.summary')
5005 label='log.summary')
5011
5006
5012 branch = ctx.branch()
5007 branch = ctx.branch()
5013 bheads = repo.branchheads(branch)
5008 bheads = repo.branchheads(branch)
5014 # i18n: column positioning for "hg summary"
5009 # i18n: column positioning for "hg summary"
5015 m = _('branch: %s\n') % branch
5010 m = _('branch: %s\n') % branch
5016 if branch != 'default':
5011 if branch != 'default':
5017 ui.write(m, label='log.branch')
5012 ui.write(m, label='log.branch')
5018 else:
5013 else:
5019 ui.status(m, label='log.branch')
5014 ui.status(m, label='log.branch')
5020
5015
5021 if marks:
5016 if marks:
5022 active = repo._activebookmark
5017 active = repo._activebookmark
5023 # i18n: column positioning for "hg summary"
5018 # i18n: column positioning for "hg summary"
5024 ui.write(_('bookmarks:'), label='log.bookmark')
5019 ui.write(_('bookmarks:'), label='log.bookmark')
5025 if active is not None:
5020 if active is not None:
5026 if active in marks:
5021 if active in marks:
5027 ui.write(' *' + active, label=bookmarks.activebookmarklabel)
5022 ui.write(' *' + active, label=bookmarks.activebookmarklabel)
5028 marks.remove(active)
5023 marks.remove(active)
5029 else:
5024 else:
5030 ui.write(' [%s]' % active, label=bookmarks.activebookmarklabel)
5025 ui.write(' [%s]' % active, label=bookmarks.activebookmarklabel)
5031 for m in marks:
5026 for m in marks:
5032 ui.write(' ' + m, label='log.bookmark')
5027 ui.write(' ' + m, label='log.bookmark')
5033 ui.write('\n', label='log.bookmark')
5028 ui.write('\n', label='log.bookmark')
5034
5029
5035 status = repo.status(unknown=True)
5030 status = repo.status(unknown=True)
5036
5031
5037 c = repo.dirstate.copies()
5032 c = repo.dirstate.copies()
5038 copied, renamed = [], []
5033 copied, renamed = [], []
5039 for d, s in c.iteritems():
5034 for d, s in c.iteritems():
5040 if s in status.removed:
5035 if s in status.removed:
5041 status.removed.remove(s)
5036 status.removed.remove(s)
5042 renamed.append(d)
5037 renamed.append(d)
5043 else:
5038 else:
5044 copied.append(d)
5039 copied.append(d)
5045 if d in status.added:
5040 if d in status.added:
5046 status.added.remove(d)
5041 status.added.remove(d)
5047
5042
5048 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
5043 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
5049
5044
5050 labels = [(ui.label(_('%d modified'), 'status.modified'), status.modified),
5045 labels = [(ui.label(_('%d modified'), 'status.modified'), status.modified),
5051 (ui.label(_('%d added'), 'status.added'), status.added),
5046 (ui.label(_('%d added'), 'status.added'), status.added),
5052 (ui.label(_('%d removed'), 'status.removed'), status.removed),
5047 (ui.label(_('%d removed'), 'status.removed'), status.removed),
5053 (ui.label(_('%d renamed'), 'status.copied'), renamed),
5048 (ui.label(_('%d renamed'), 'status.copied'), renamed),
5054 (ui.label(_('%d copied'), 'status.copied'), copied),
5049 (ui.label(_('%d copied'), 'status.copied'), copied),
5055 (ui.label(_('%d deleted'), 'status.deleted'), status.deleted),
5050 (ui.label(_('%d deleted'), 'status.deleted'), status.deleted),
5056 (ui.label(_('%d unknown'), 'status.unknown'), status.unknown),
5051 (ui.label(_('%d unknown'), 'status.unknown'), status.unknown),
5057 (ui.label(_('%d unresolved'), 'resolve.unresolved'), unresolved),
5052 (ui.label(_('%d unresolved'), 'resolve.unresolved'), unresolved),
5058 (ui.label(_('%d subrepos'), 'status.modified'), subs)]
5053 (ui.label(_('%d subrepos'), 'status.modified'), subs)]
5059 t = []
5054 t = []
5060 for l, s in labels:
5055 for l, s in labels:
5061 if s:
5056 if s:
5062 t.append(l % len(s))
5057 t.append(l % len(s))
5063
5058
5064 t = ', '.join(t)
5059 t = ', '.join(t)
5065 cleanworkdir = False
5060 cleanworkdir = False
5066
5061
5067 if repo.vfs.exists('graftstate'):
5062 if repo.vfs.exists('graftstate'):
5068 t += _(' (graft in progress)')
5063 t += _(' (graft in progress)')
5069 if repo.vfs.exists('updatestate'):
5064 if repo.vfs.exists('updatestate'):
5070 t += _(' (interrupted update)')
5065 t += _(' (interrupted update)')
5071 elif len(parents) > 1:
5066 elif len(parents) > 1:
5072 t += _(' (merge)')
5067 t += _(' (merge)')
5073 elif branch != parents[0].branch():
5068 elif branch != parents[0].branch():
5074 t += _(' (new branch)')
5069 t += _(' (new branch)')
5075 elif (parents[0].closesbranch() and
5070 elif (parents[0].closesbranch() and
5076 pnode in repo.branchheads(branch, closed=True)):
5071 pnode in repo.branchheads(branch, closed=True)):
5077 t += _(' (head closed)')
5072 t += _(' (head closed)')
5078 elif not (status.modified or status.added or status.removed or renamed or
5073 elif not (status.modified or status.added or status.removed or renamed or
5079 copied or subs):
5074 copied or subs):
5080 t += _(' (clean)')
5075 t += _(' (clean)')
5081 cleanworkdir = True
5076 cleanworkdir = True
5082 elif pnode not in bheads:
5077 elif pnode not in bheads:
5083 t += _(' (new branch head)')
5078 t += _(' (new branch head)')
5084
5079
5085 if parents:
5080 if parents:
5086 pendingphase = max(p.phase() for p in parents)
5081 pendingphase = max(p.phase() for p in parents)
5087 else:
5082 else:
5088 pendingphase = phases.public
5083 pendingphase = phases.public
5089
5084
5090 if pendingphase > phases.newcommitphase(ui):
5085 if pendingphase > phases.newcommitphase(ui):
5091 t += ' (%s)' % phases.phasenames[pendingphase]
5086 t += ' (%s)' % phases.phasenames[pendingphase]
5092
5087
5093 if cleanworkdir:
5088 if cleanworkdir:
5094 # i18n: column positioning for "hg summary"
5089 # i18n: column positioning for "hg summary"
5095 ui.status(_('commit: %s\n') % t.strip())
5090 ui.status(_('commit: %s\n') % t.strip())
5096 else:
5091 else:
5097 # i18n: column positioning for "hg summary"
5092 # i18n: column positioning for "hg summary"
5098 ui.write(_('commit: %s\n') % t.strip())
5093 ui.write(_('commit: %s\n') % t.strip())
5099
5094
5100 # all ancestors of branch heads - all ancestors of parent = new csets
5095 # all ancestors of branch heads - all ancestors of parent = new csets
5101 new = len(repo.changelog.findmissing([pctx.node() for pctx in parents],
5096 new = len(repo.changelog.findmissing([pctx.node() for pctx in parents],
5102 bheads))
5097 bheads))
5103
5098
5104 if new == 0:
5099 if new == 0:
5105 # i18n: column positioning for "hg summary"
5100 # i18n: column positioning for "hg summary"
5106 ui.status(_('update: (current)\n'))
5101 ui.status(_('update: (current)\n'))
5107 elif pnode not in bheads:
5102 elif pnode not in bheads:
5108 # i18n: column positioning for "hg summary"
5103 # i18n: column positioning for "hg summary"
5109 ui.write(_('update: %d new changesets (update)\n') % new)
5104 ui.write(_('update: %d new changesets (update)\n') % new)
5110 else:
5105 else:
5111 # i18n: column positioning for "hg summary"
5106 # i18n: column positioning for "hg summary"
5112 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
5107 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
5113 (new, len(bheads)))
5108 (new, len(bheads)))
5114
5109
5115 t = []
5110 t = []
5116 draft = len(repo.revs('draft()'))
5111 draft = len(repo.revs('draft()'))
5117 if draft:
5112 if draft:
5118 t.append(_('%d draft') % draft)
5113 t.append(_('%d draft') % draft)
5119 secret = len(repo.revs('secret()'))
5114 secret = len(repo.revs('secret()'))
5120 if secret:
5115 if secret:
5121 t.append(_('%d secret') % secret)
5116 t.append(_('%d secret') % secret)
5122
5117
5123 if draft or secret:
5118 if draft or secret:
5124 ui.status(_('phases: %s\n') % ', '.join(t))
5119 ui.status(_('phases: %s\n') % ', '.join(t))
5125
5120
5126 if obsolete.isenabled(repo, obsolete.createmarkersopt):
5121 if obsolete.isenabled(repo, obsolete.createmarkersopt):
5127 for trouble in ("orphan", "contentdivergent", "phasedivergent"):
5122 for trouble in ("orphan", "contentdivergent", "phasedivergent"):
5128 numtrouble = len(repo.revs(trouble + "()"))
5123 numtrouble = len(repo.revs(trouble + "()"))
5129 # We write all the possibilities to ease translation
5124 # We write all the possibilities to ease translation
5130 troublemsg = {
5125 troublemsg = {
5131 "orphan": _("orphan: %d changesets"),
5126 "orphan": _("orphan: %d changesets"),
5132 "contentdivergent": _("content-divergent: %d changesets"),
5127 "contentdivergent": _("content-divergent: %d changesets"),
5133 "phasedivergent": _("phase-divergent: %d changesets"),
5128 "phasedivergent": _("phase-divergent: %d changesets"),
5134 }
5129 }
5135 if numtrouble > 0:
5130 if numtrouble > 0:
5136 ui.status(troublemsg[trouble] % numtrouble + "\n")
5131 ui.status(troublemsg[trouble] % numtrouble + "\n")
5137
5132
5138 cmdutil.summaryhooks(ui, repo)
5133 cmdutil.summaryhooks(ui, repo)
5139
5134
5140 if opts.get('remote'):
5135 if opts.get('remote'):
5141 needsincoming, needsoutgoing = True, True
5136 needsincoming, needsoutgoing = True, True
5142 else:
5137 else:
5143 needsincoming, needsoutgoing = False, False
5138 needsincoming, needsoutgoing = False, False
5144 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
5139 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
5145 if i:
5140 if i:
5146 needsincoming = True
5141 needsincoming = True
5147 if o:
5142 if o:
5148 needsoutgoing = True
5143 needsoutgoing = True
5149 if not needsincoming and not needsoutgoing:
5144 if not needsincoming and not needsoutgoing:
5150 return
5145 return
5151
5146
5152 def getincoming():
5147 def getincoming():
5153 source, branches = hg.parseurl(ui.expandpath('default'))
5148 source, branches = hg.parseurl(ui.expandpath('default'))
5154 sbranch = branches[0]
5149 sbranch = branches[0]
5155 try:
5150 try:
5156 other = hg.peer(repo, {}, source)
5151 other = hg.peer(repo, {}, source)
5157 except error.RepoError:
5152 except error.RepoError:
5158 if opts.get('remote'):
5153 if opts.get('remote'):
5159 raise
5154 raise
5160 return source, sbranch, None, None, None
5155 return source, sbranch, None, None, None
5161 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
5156 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
5162 if revs:
5157 if revs:
5163 revs = [other.lookup(rev) for rev in revs]
5158 revs = [other.lookup(rev) for rev in revs]
5164 ui.debug('comparing with %s\n' % util.hidepassword(source))
5159 ui.debug('comparing with %s\n' % util.hidepassword(source))
5165 repo.ui.pushbuffer()
5160 repo.ui.pushbuffer()
5166 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
5161 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
5167 repo.ui.popbuffer()
5162 repo.ui.popbuffer()
5168 return source, sbranch, other, commoninc, commoninc[1]
5163 return source, sbranch, other, commoninc, commoninc[1]
5169
5164
5170 if needsincoming:
5165 if needsincoming:
5171 source, sbranch, sother, commoninc, incoming = getincoming()
5166 source, sbranch, sother, commoninc, incoming = getincoming()
5172 else:
5167 else:
5173 source = sbranch = sother = commoninc = incoming = None
5168 source = sbranch = sother = commoninc = incoming = None
5174
5169
5175 def getoutgoing():
5170 def getoutgoing():
5176 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
5171 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
5177 dbranch = branches[0]
5172 dbranch = branches[0]
5178 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
5173 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
5179 if source != dest:
5174 if source != dest:
5180 try:
5175 try:
5181 dother = hg.peer(repo, {}, dest)
5176 dother = hg.peer(repo, {}, dest)
5182 except error.RepoError:
5177 except error.RepoError:
5183 if opts.get('remote'):
5178 if opts.get('remote'):
5184 raise
5179 raise
5185 return dest, dbranch, None, None
5180 return dest, dbranch, None, None
5186 ui.debug('comparing with %s\n' % util.hidepassword(dest))
5181 ui.debug('comparing with %s\n' % util.hidepassword(dest))
5187 elif sother is None:
5182 elif sother is None:
5188 # there is no explicit destination peer, but source one is invalid
5183 # there is no explicit destination peer, but source one is invalid
5189 return dest, dbranch, None, None
5184 return dest, dbranch, None, None
5190 else:
5185 else:
5191 dother = sother
5186 dother = sother
5192 if (source != dest or (sbranch is not None and sbranch != dbranch)):
5187 if (source != dest or (sbranch is not None and sbranch != dbranch)):
5193 common = None
5188 common = None
5194 else:
5189 else:
5195 common = commoninc
5190 common = commoninc
5196 if revs:
5191 if revs:
5197 revs = [repo.lookup(rev) for rev in revs]
5192 revs = [repo.lookup(rev) for rev in revs]
5198 repo.ui.pushbuffer()
5193 repo.ui.pushbuffer()
5199 outgoing = discovery.findcommonoutgoing(repo, dother, onlyheads=revs,
5194 outgoing = discovery.findcommonoutgoing(repo, dother, onlyheads=revs,
5200 commoninc=common)
5195 commoninc=common)
5201 repo.ui.popbuffer()
5196 repo.ui.popbuffer()
5202 return dest, dbranch, dother, outgoing
5197 return dest, dbranch, dother, outgoing
5203
5198
5204 if needsoutgoing:
5199 if needsoutgoing:
5205 dest, dbranch, dother, outgoing = getoutgoing()
5200 dest, dbranch, dother, outgoing = getoutgoing()
5206 else:
5201 else:
5207 dest = dbranch = dother = outgoing = None
5202 dest = dbranch = dother = outgoing = None
5208
5203
5209 if opts.get('remote'):
5204 if opts.get('remote'):
5210 t = []
5205 t = []
5211 if incoming:
5206 if incoming:
5212 t.append(_('1 or more incoming'))
5207 t.append(_('1 or more incoming'))
5213 o = outgoing.missing
5208 o = outgoing.missing
5214 if o:
5209 if o:
5215 t.append(_('%d outgoing') % len(o))
5210 t.append(_('%d outgoing') % len(o))
5216 other = dother or sother
5211 other = dother or sother
5217 if 'bookmarks' in other.listkeys('namespaces'):
5212 if 'bookmarks' in other.listkeys('namespaces'):
5218 counts = bookmarks.summary(repo, other)
5213 counts = bookmarks.summary(repo, other)
5219 if counts[0] > 0:
5214 if counts[0] > 0:
5220 t.append(_('%d incoming bookmarks') % counts[0])
5215 t.append(_('%d incoming bookmarks') % counts[0])
5221 if counts[1] > 0:
5216 if counts[1] > 0:
5222 t.append(_('%d outgoing bookmarks') % counts[1])
5217 t.append(_('%d outgoing bookmarks') % counts[1])
5223
5218
5224 if t:
5219 if t:
5225 # i18n: column positioning for "hg summary"
5220 # i18n: column positioning for "hg summary"
5226 ui.write(_('remote: %s\n') % (', '.join(t)))
5221 ui.write(_('remote: %s\n') % (', '.join(t)))
5227 else:
5222 else:
5228 # i18n: column positioning for "hg summary"
5223 # i18n: column positioning for "hg summary"
5229 ui.status(_('remote: (synced)\n'))
5224 ui.status(_('remote: (synced)\n'))
5230
5225
5231 cmdutil.summaryremotehooks(ui, repo, opts,
5226 cmdutil.summaryremotehooks(ui, repo, opts,
5232 ((source, sbranch, sother, commoninc),
5227 ((source, sbranch, sother, commoninc),
5233 (dest, dbranch, dother, outgoing)))
5228 (dest, dbranch, dother, outgoing)))
5234
5229
5235 @command('tag',
5230 @command('tag',
5236 [('f', 'force', None, _('force tag')),
5231 [('f', 'force', None, _('force tag')),
5237 ('l', 'local', None, _('make the tag local')),
5232 ('l', 'local', None, _('make the tag local')),
5238 ('r', 'rev', '', _('revision to tag'), _('REV')),
5233 ('r', 'rev', '', _('revision to tag'), _('REV')),
5239 ('', 'remove', None, _('remove a tag')),
5234 ('', 'remove', None, _('remove a tag')),
5240 # -l/--local is already there, commitopts cannot be used
5235 # -l/--local is already there, commitopts cannot be used
5241 ('e', 'edit', None, _('invoke editor on commit messages')),
5236 ('e', 'edit', None, _('invoke editor on commit messages')),
5242 ('m', 'message', '', _('use text as commit message'), _('TEXT')),
5237 ('m', 'message', '', _('use text as commit message'), _('TEXT')),
5243 ] + commitopts2,
5238 ] + commitopts2,
5244 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'))
5239 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'))
5245 def tag(ui, repo, name1, *names, **opts):
5240 def tag(ui, repo, name1, *names, **opts):
5246 """add one or more tags for the current or given revision
5241 """add one or more tags for the current or given revision
5247
5242
5248 Name a particular revision using <name>.
5243 Name a particular revision using <name>.
5249
5244
5250 Tags are used to name particular revisions of the repository and are
5245 Tags are used to name particular revisions of the repository and are
5251 very useful to compare different revisions, to go back to significant
5246 very useful to compare different revisions, to go back to significant
5252 earlier versions or to mark branch points as releases, etc. Changing
5247 earlier versions or to mark branch points as releases, etc. Changing
5253 an existing tag is normally disallowed; use -f/--force to override.
5248 an existing tag is normally disallowed; use -f/--force to override.
5254
5249
5255 If no revision is given, the parent of the working directory is
5250 If no revision is given, the parent of the working directory is
5256 used.
5251 used.
5257
5252
5258 To facilitate version control, distribution, and merging of tags,
5253 To facilitate version control, distribution, and merging of tags,
5259 they are stored as a file named ".hgtags" which is managed similarly
5254 they are stored as a file named ".hgtags" which is managed similarly
5260 to other project files and can be hand-edited if necessary. This
5255 to other project files and can be hand-edited if necessary. This
5261 also means that tagging creates a new commit. The file
5256 also means that tagging creates a new commit. The file
5262 ".hg/localtags" is used for local tags (not shared among
5257 ".hg/localtags" is used for local tags (not shared among
5263 repositories).
5258 repositories).
5264
5259
5265 Tag commits are usually made at the head of a branch. If the parent
5260 Tag commits are usually made at the head of a branch. If the parent
5266 of the working directory is not a branch head, :hg:`tag` aborts; use
5261 of the working directory is not a branch head, :hg:`tag` aborts; use
5267 -f/--force to force the tag commit to be based on a non-head
5262 -f/--force to force the tag commit to be based on a non-head
5268 changeset.
5263 changeset.
5269
5264
5270 See :hg:`help dates` for a list of formats valid for -d/--date.
5265 See :hg:`help dates` for a list of formats valid for -d/--date.
5271
5266
5272 Since tag names have priority over branch names during revision
5267 Since tag names have priority over branch names during revision
5273 lookup, using an existing branch name as a tag name is discouraged.
5268 lookup, using an existing branch name as a tag name is discouraged.
5274
5269
5275 Returns 0 on success.
5270 Returns 0 on success.
5276 """
5271 """
5277 opts = pycompat.byteskwargs(opts)
5272 opts = pycompat.byteskwargs(opts)
5278 wlock = lock = None
5273 wlock = lock = None
5279 try:
5274 try:
5280 wlock = repo.wlock()
5275 wlock = repo.wlock()
5281 lock = repo.lock()
5276 lock = repo.lock()
5282 rev_ = "."
5277 rev_ = "."
5283 names = [t.strip() for t in (name1,) + names]
5278 names = [t.strip() for t in (name1,) + names]
5284 if len(names) != len(set(names)):
5279 if len(names) != len(set(names)):
5285 raise error.Abort(_('tag names must be unique'))
5280 raise error.Abort(_('tag names must be unique'))
5286 for n in names:
5281 for n in names:
5287 scmutil.checknewlabel(repo, n, 'tag')
5282 scmutil.checknewlabel(repo, n, 'tag')
5288 if not n:
5283 if not n:
5289 raise error.Abort(_('tag names cannot consist entirely of '
5284 raise error.Abort(_('tag names cannot consist entirely of '
5290 'whitespace'))
5285 'whitespace'))
5291 if opts.get('rev') and opts.get('remove'):
5286 if opts.get('rev') and opts.get('remove'):
5292 raise error.Abort(_("--rev and --remove are incompatible"))
5287 raise error.Abort(_("--rev and --remove are incompatible"))
5293 if opts.get('rev'):
5288 if opts.get('rev'):
5294 rev_ = opts['rev']
5289 rev_ = opts['rev']
5295 message = opts.get('message')
5290 message = opts.get('message')
5296 if opts.get('remove'):
5291 if opts.get('remove'):
5297 if opts.get('local'):
5292 if opts.get('local'):
5298 expectedtype = 'local'
5293 expectedtype = 'local'
5299 else:
5294 else:
5300 expectedtype = 'global'
5295 expectedtype = 'global'
5301
5296
5302 for n in names:
5297 for n in names:
5303 if not repo.tagtype(n):
5298 if not repo.tagtype(n):
5304 raise error.Abort(_("tag '%s' does not exist") % n)
5299 raise error.Abort(_("tag '%s' does not exist") % n)
5305 if repo.tagtype(n) != expectedtype:
5300 if repo.tagtype(n) != expectedtype:
5306 if expectedtype == 'global':
5301 if expectedtype == 'global':
5307 raise error.Abort(_("tag '%s' is not a global tag") % n)
5302 raise error.Abort(_("tag '%s' is not a global tag") % n)
5308 else:
5303 else:
5309 raise error.Abort(_("tag '%s' is not a local tag") % n)
5304 raise error.Abort(_("tag '%s' is not a local tag") % n)
5310 rev_ = 'null'
5305 rev_ = 'null'
5311 if not message:
5306 if not message:
5312 # we don't translate commit messages
5307 # we don't translate commit messages
5313 message = 'Removed tag %s' % ', '.join(names)
5308 message = 'Removed tag %s' % ', '.join(names)
5314 elif not opts.get('force'):
5309 elif not opts.get('force'):
5315 for n in names:
5310 for n in names:
5316 if n in repo.tags():
5311 if n in repo.tags():
5317 raise error.Abort(_("tag '%s' already exists "
5312 raise error.Abort(_("tag '%s' already exists "
5318 "(use -f to force)") % n)
5313 "(use -f to force)") % n)
5319 if not opts.get('local'):
5314 if not opts.get('local'):
5320 p1, p2 = repo.dirstate.parents()
5315 p1, p2 = repo.dirstate.parents()
5321 if p2 != nullid:
5316 if p2 != nullid:
5322 raise error.Abort(_('uncommitted merge'))
5317 raise error.Abort(_('uncommitted merge'))
5323 bheads = repo.branchheads()
5318 bheads = repo.branchheads()
5324 if not opts.get('force') and bheads and p1 not in bheads:
5319 if not opts.get('force') and bheads and p1 not in bheads:
5325 raise error.Abort(_('working directory is not at a branch head '
5320 raise error.Abort(_('working directory is not at a branch head '
5326 '(use -f to force)'))
5321 '(use -f to force)'))
5327 r = scmutil.revsingle(repo, rev_).node()
5322 r = scmutil.revsingle(repo, rev_).node()
5328
5323
5329 if not message:
5324 if not message:
5330 # we don't translate commit messages
5325 # we don't translate commit messages
5331 message = ('Added tag %s for changeset %s' %
5326 message = ('Added tag %s for changeset %s' %
5332 (', '.join(names), short(r)))
5327 (', '.join(names), short(r)))
5333
5328
5334 date = opts.get('date')
5329 date = opts.get('date')
5335 if date:
5330 if date:
5336 date = util.parsedate(date)
5331 date = util.parsedate(date)
5337
5332
5338 if opts.get('remove'):
5333 if opts.get('remove'):
5339 editform = 'tag.remove'
5334 editform = 'tag.remove'
5340 else:
5335 else:
5341 editform = 'tag.add'
5336 editform = 'tag.add'
5342 editor = cmdutil.getcommiteditor(editform=editform,
5337 editor = cmdutil.getcommiteditor(editform=editform,
5343 **pycompat.strkwargs(opts))
5338 **pycompat.strkwargs(opts))
5344
5339
5345 # don't allow tagging the null rev
5340 # don't allow tagging the null rev
5346 if (not opts.get('remove') and
5341 if (not opts.get('remove') and
5347 scmutil.revsingle(repo, rev_).rev() == nullrev):
5342 scmutil.revsingle(repo, rev_).rev() == nullrev):
5348 raise error.Abort(_("cannot tag null revision"))
5343 raise error.Abort(_("cannot tag null revision"))
5349
5344
5350 tagsmod.tag(repo, names, r, message, opts.get('local'),
5345 tagsmod.tag(repo, names, r, message, opts.get('local'),
5351 opts.get('user'), date, editor=editor)
5346 opts.get('user'), date, editor=editor)
5352 finally:
5347 finally:
5353 release(lock, wlock)
5348 release(lock, wlock)
5354
5349
5355 @command('tags', formatteropts, '', cmdtype=readonly)
5350 @command('tags', formatteropts, '', cmdtype=readonly)
5356 def tags(ui, repo, **opts):
5351 def tags(ui, repo, **opts):
5357 """list repository tags
5352 """list repository tags
5358
5353
5359 This lists both regular and local tags. When the -v/--verbose
5354 This lists both regular and local tags. When the -v/--verbose
5360 switch is used, a third column "local" is printed for local tags.
5355 switch is used, a third column "local" is printed for local tags.
5361 When the -q/--quiet switch is used, only the tag name is printed.
5356 When the -q/--quiet switch is used, only the tag name is printed.
5362
5357
5363 Returns 0 on success.
5358 Returns 0 on success.
5364 """
5359 """
5365
5360
5366 opts = pycompat.byteskwargs(opts)
5361 opts = pycompat.byteskwargs(opts)
5367 ui.pager('tags')
5362 ui.pager('tags')
5368 fm = ui.formatter('tags', opts)
5363 fm = ui.formatter('tags', opts)
5369 hexfunc = fm.hexfunc
5364 hexfunc = fm.hexfunc
5370 tagtype = ""
5365 tagtype = ""
5371
5366
5372 for t, n in reversed(repo.tagslist()):
5367 for t, n in reversed(repo.tagslist()):
5373 hn = hexfunc(n)
5368 hn = hexfunc(n)
5374 label = 'tags.normal'
5369 label = 'tags.normal'
5375 tagtype = ''
5370 tagtype = ''
5376 if repo.tagtype(t) == 'local':
5371 if repo.tagtype(t) == 'local':
5377 label = 'tags.local'
5372 label = 'tags.local'
5378 tagtype = 'local'
5373 tagtype = 'local'
5379
5374
5380 fm.startitem()
5375 fm.startitem()
5381 fm.write('tag', '%s', t, label=label)
5376 fm.write('tag', '%s', t, label=label)
5382 fmt = " " * (30 - encoding.colwidth(t)) + ' %5d:%s'
5377 fmt = " " * (30 - encoding.colwidth(t)) + ' %5d:%s'
5383 fm.condwrite(not ui.quiet, 'rev node', fmt,
5378 fm.condwrite(not ui.quiet, 'rev node', fmt,
5384 repo.changelog.rev(n), hn, label=label)
5379 repo.changelog.rev(n), hn, label=label)
5385 fm.condwrite(ui.verbose and tagtype, 'type', ' %s',
5380 fm.condwrite(ui.verbose and tagtype, 'type', ' %s',
5386 tagtype, label=label)
5381 tagtype, label=label)
5387 fm.plain('\n')
5382 fm.plain('\n')
5388 fm.end()
5383 fm.end()
5389
5384
5390 @command('tip',
5385 @command('tip',
5391 [('p', 'patch', None, _('show patch')),
5386 [('p', 'patch', None, _('show patch')),
5392 ('g', 'git', None, _('use git extended diff format')),
5387 ('g', 'git', None, _('use git extended diff format')),
5393 ] + templateopts,
5388 ] + templateopts,
5394 _('[-p] [-g]'))
5389 _('[-p] [-g]'))
5395 def tip(ui, repo, **opts):
5390 def tip(ui, repo, **opts):
5396 """show the tip revision (DEPRECATED)
5391 """show the tip revision (DEPRECATED)
5397
5392
5398 The tip revision (usually just called the tip) is the changeset
5393 The tip revision (usually just called the tip) is the changeset
5399 most recently added to the repository (and therefore the most
5394 most recently added to the repository (and therefore the most
5400 recently changed head).
5395 recently changed head).
5401
5396
5402 If you have just made a commit, that commit will be the tip. If
5397 If you have just made a commit, that commit will be the tip. If
5403 you have just pulled changes from another repository, the tip of
5398 you have just pulled changes from another repository, the tip of
5404 that repository becomes the current tip. The "tip" tag is special
5399 that repository becomes the current tip. The "tip" tag is special
5405 and cannot be renamed or assigned to a different changeset.
5400 and cannot be renamed or assigned to a different changeset.
5406
5401
5407 This command is deprecated, please use :hg:`heads` instead.
5402 This command is deprecated, please use :hg:`heads` instead.
5408
5403
5409 Returns 0 on success.
5404 Returns 0 on success.
5410 """
5405 """
5411 opts = pycompat.byteskwargs(opts)
5406 opts = pycompat.byteskwargs(opts)
5412 displayer = cmdutil.show_changeset(ui, repo, opts)
5407 displayer = cmdutil.show_changeset(ui, repo, opts)
5413 displayer.show(repo['tip'])
5408 displayer.show(repo['tip'])
5414 displayer.close()
5409 displayer.close()
5415
5410
5416 @command('unbundle',
5411 @command('unbundle',
5417 [('u', 'update', None,
5412 [('u', 'update', None,
5418 _('update to new branch head if changesets were unbundled'))],
5413 _('update to new branch head if changesets were unbundled'))],
5419 _('[-u] FILE...'))
5414 _('[-u] FILE...'))
5420 def unbundle(ui, repo, fname1, *fnames, **opts):
5415 def unbundle(ui, repo, fname1, *fnames, **opts):
5421 """apply one or more bundle files
5416 """apply one or more bundle files
5422
5417
5423 Apply one or more bundle files generated by :hg:`bundle`.
5418 Apply one or more bundle files generated by :hg:`bundle`.
5424
5419
5425 Returns 0 on success, 1 if an update has unresolved files.
5420 Returns 0 on success, 1 if an update has unresolved files.
5426 """
5421 """
5427 fnames = (fname1,) + fnames
5422 fnames = (fname1,) + fnames
5428
5423
5429 with repo.lock():
5424 with repo.lock():
5430 for fname in fnames:
5425 for fname in fnames:
5431 f = hg.openpath(ui, fname)
5426 f = hg.openpath(ui, fname)
5432 gen = exchange.readbundle(ui, f, fname)
5427 gen = exchange.readbundle(ui, f, fname)
5433 if isinstance(gen, streamclone.streamcloneapplier):
5428 if isinstance(gen, streamclone.streamcloneapplier):
5434 raise error.Abort(
5429 raise error.Abort(
5435 _('packed bundles cannot be applied with '
5430 _('packed bundles cannot be applied with '
5436 '"hg unbundle"'),
5431 '"hg unbundle"'),
5437 hint=_('use "hg debugapplystreamclonebundle"'))
5432 hint=_('use "hg debugapplystreamclonebundle"'))
5438 url = 'bundle:' + fname
5433 url = 'bundle:' + fname
5439 try:
5434 try:
5440 txnname = 'unbundle'
5435 txnname = 'unbundle'
5441 if not isinstance(gen, bundle2.unbundle20):
5436 if not isinstance(gen, bundle2.unbundle20):
5442 txnname = 'unbundle\n%s' % util.hidepassword(url)
5437 txnname = 'unbundle\n%s' % util.hidepassword(url)
5443 with repo.transaction(txnname) as tr:
5438 with repo.transaction(txnname) as tr:
5444 op = bundle2.applybundle(repo, gen, tr, source='unbundle',
5439 op = bundle2.applybundle(repo, gen, tr, source='unbundle',
5445 url=url)
5440 url=url)
5446 except error.BundleUnknownFeatureError as exc:
5441 except error.BundleUnknownFeatureError as exc:
5447 raise error.Abort(
5442 raise error.Abort(
5448 _('%s: unknown bundle feature, %s') % (fname, exc),
5443 _('%s: unknown bundle feature, %s') % (fname, exc),
5449 hint=_("see https://mercurial-scm.org/"
5444 hint=_("see https://mercurial-scm.org/"
5450 "wiki/BundleFeature for more "
5445 "wiki/BundleFeature for more "
5451 "information"))
5446 "information"))
5452 modheads = bundle2.combinechangegroupresults(op)
5447 modheads = bundle2.combinechangegroupresults(op)
5453
5448
5454 return postincoming(ui, repo, modheads, opts.get(r'update'), None, None)
5449 return postincoming(ui, repo, modheads, opts.get(r'update'), None, None)
5455
5450
5456 @command('^update|up|checkout|co',
5451 @command('^update|up|checkout|co',
5457 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
5452 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
5458 ('c', 'check', None, _('require clean working directory')),
5453 ('c', 'check', None, _('require clean working directory')),
5459 ('m', 'merge', None, _('merge uncommitted changes')),
5454 ('m', 'merge', None, _('merge uncommitted changes')),
5460 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5455 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5461 ('r', 'rev', '', _('revision'), _('REV'))
5456 ('r', 'rev', '', _('revision'), _('REV'))
5462 ] + mergetoolopts,
5457 ] + mergetoolopts,
5463 _('[-C|-c|-m] [-d DATE] [[-r] REV]'))
5458 _('[-C|-c|-m] [-d DATE] [[-r] REV]'))
5464 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False,
5459 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False,
5465 merge=None, tool=None):
5460 merge=None, tool=None):
5466 """update working directory (or switch revisions)
5461 """update working directory (or switch revisions)
5467
5462
5468 Update the repository's working directory to the specified
5463 Update the repository's working directory to the specified
5469 changeset. If no changeset is specified, update to the tip of the
5464 changeset. If no changeset is specified, update to the tip of the
5470 current named branch and move the active bookmark (see :hg:`help
5465 current named branch and move the active bookmark (see :hg:`help
5471 bookmarks`).
5466 bookmarks`).
5472
5467
5473 Update sets the working directory's parent revision to the specified
5468 Update sets the working directory's parent revision to the specified
5474 changeset (see :hg:`help parents`).
5469 changeset (see :hg:`help parents`).
5475
5470
5476 If the changeset is not a descendant or ancestor of the working
5471 If the changeset is not a descendant or ancestor of the working
5477 directory's parent and there are uncommitted changes, the update is
5472 directory's parent and there are uncommitted changes, the update is
5478 aborted. With the -c/--check option, the working directory is checked
5473 aborted. With the -c/--check option, the working directory is checked
5479 for uncommitted changes; if none are found, the working directory is
5474 for uncommitted changes; if none are found, the working directory is
5480 updated to the specified changeset.
5475 updated to the specified changeset.
5481
5476
5482 .. container:: verbose
5477 .. container:: verbose
5483
5478
5484 The -C/--clean, -c/--check, and -m/--merge options control what
5479 The -C/--clean, -c/--check, and -m/--merge options control what
5485 happens if the working directory contains uncommitted changes.
5480 happens if the working directory contains uncommitted changes.
5486 At most of one of them can be specified.
5481 At most of one of them can be specified.
5487
5482
5488 1. If no option is specified, and if
5483 1. If no option is specified, and if
5489 the requested changeset is an ancestor or descendant of
5484 the requested changeset is an ancestor or descendant of
5490 the working directory's parent, the uncommitted changes
5485 the working directory's parent, the uncommitted changes
5491 are merged into the requested changeset and the merged
5486 are merged into the requested changeset and the merged
5492 result is left uncommitted. If the requested changeset is
5487 result is left uncommitted. If the requested changeset is
5493 not an ancestor or descendant (that is, it is on another
5488 not an ancestor or descendant (that is, it is on another
5494 branch), the update is aborted and the uncommitted changes
5489 branch), the update is aborted and the uncommitted changes
5495 are preserved.
5490 are preserved.
5496
5491
5497 2. With the -m/--merge option, the update is allowed even if the
5492 2. With the -m/--merge option, the update is allowed even if the
5498 requested changeset is not an ancestor or descendant of
5493 requested changeset is not an ancestor or descendant of
5499 the working directory's parent.
5494 the working directory's parent.
5500
5495
5501 3. With the -c/--check option, the update is aborted and the
5496 3. With the -c/--check option, the update is aborted and the
5502 uncommitted changes are preserved.
5497 uncommitted changes are preserved.
5503
5498
5504 4. With the -C/--clean option, uncommitted changes are discarded and
5499 4. With the -C/--clean option, uncommitted changes are discarded and
5505 the working directory is updated to the requested changeset.
5500 the working directory is updated to the requested changeset.
5506
5501
5507 To cancel an uncommitted merge (and lose your changes), use
5502 To cancel an uncommitted merge (and lose your changes), use
5508 :hg:`update --clean .`.
5503 :hg:`update --clean .`.
5509
5504
5510 Use null as the changeset to remove the working directory (like
5505 Use null as the changeset to remove the working directory (like
5511 :hg:`clone -U`).
5506 :hg:`clone -U`).
5512
5507
5513 If you want to revert just one file to an older revision, use
5508 If you want to revert just one file to an older revision, use
5514 :hg:`revert [-r REV] NAME`.
5509 :hg:`revert [-r REV] NAME`.
5515
5510
5516 See :hg:`help dates` for a list of formats valid for -d/--date.
5511 See :hg:`help dates` for a list of formats valid for -d/--date.
5517
5512
5518 Returns 0 on success, 1 if there are unresolved files.
5513 Returns 0 on success, 1 if there are unresolved files.
5519 """
5514 """
5520 if rev and node:
5515 if rev and node:
5521 raise error.Abort(_("please specify just one revision"))
5516 raise error.Abort(_("please specify just one revision"))
5522
5517
5523 if ui.configbool('commands', 'update.requiredest'):
5518 if ui.configbool('commands', 'update.requiredest'):
5524 if not node and not rev and not date:
5519 if not node and not rev and not date:
5525 raise error.Abort(_('you must specify a destination'),
5520 raise error.Abort(_('you must specify a destination'),
5526 hint=_('for example: hg update ".::"'))
5521 hint=_('for example: hg update ".::"'))
5527
5522
5528 if rev is None or rev == '':
5523 if rev is None or rev == '':
5529 rev = node
5524 rev = node
5530
5525
5531 if date and rev is not None:
5526 if date and rev is not None:
5532 raise error.Abort(_("you can't specify a revision and a date"))
5527 raise error.Abort(_("you can't specify a revision and a date"))
5533
5528
5534 if len([x for x in (clean, check, merge) if x]) > 1:
5529 if len([x for x in (clean, check, merge) if x]) > 1:
5535 raise error.Abort(_("can only specify one of -C/--clean, -c/--check, "
5530 raise error.Abort(_("can only specify one of -C/--clean, -c/--check, "
5536 "or -m/--merge"))
5531 "or -m/--merge"))
5537
5532
5538 updatecheck = None
5533 updatecheck = None
5539 if check:
5534 if check:
5540 updatecheck = 'abort'
5535 updatecheck = 'abort'
5541 elif merge:
5536 elif merge:
5542 updatecheck = 'none'
5537 updatecheck = 'none'
5543
5538
5544 with repo.wlock():
5539 with repo.wlock():
5545 cmdutil.clearunfinished(repo)
5540 cmdutil.clearunfinished(repo)
5546
5541
5547 if date:
5542 if date:
5548 rev = cmdutil.finddate(ui, repo, date)
5543 rev = cmdutil.finddate(ui, repo, date)
5549
5544
5550 # if we defined a bookmark, we have to remember the original name
5545 # if we defined a bookmark, we have to remember the original name
5551 brev = rev
5546 brev = rev
5552 if rev:
5547 if rev:
5553 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
5548 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
5554 ctx = scmutil.revsingle(repo, rev, rev)
5549 ctx = scmutil.revsingle(repo, rev, rev)
5555 rev = ctx.rev()
5550 rev = ctx.rev()
5556 if ctx.hidden():
5551 if ctx.hidden():
5557 ctxstr = ctx.hex()[:12]
5552 ctxstr = ctx.hex()[:12]
5558 ui.warn(_("updating to a hidden changeset %s\n") % ctxstr)
5553 ui.warn(_("updating to a hidden changeset %s\n") % ctxstr)
5559
5554
5560 if ctx.obsolete():
5555 if ctx.obsolete():
5561 obsfatemsg = obsutil._getfilteredreason(repo, ctxstr, ctx)
5556 obsfatemsg = obsutil._getfilteredreason(repo, ctxstr, ctx)
5562 ui.warn("(%s)\n" % obsfatemsg)
5557 ui.warn("(%s)\n" % obsfatemsg)
5563
5558
5564 repo.ui.setconfig('ui', 'forcemerge', tool, 'update')
5559 repo.ui.setconfig('ui', 'forcemerge', tool, 'update')
5565
5560
5566 return hg.updatetotally(ui, repo, rev, brev, clean=clean,
5561 return hg.updatetotally(ui, repo, rev, brev, clean=clean,
5567 updatecheck=updatecheck)
5562 updatecheck=updatecheck)
5568
5563
5569 @command('verify', [])
5564 @command('verify', [])
5570 def verify(ui, repo):
5565 def verify(ui, repo):
5571 """verify the integrity of the repository
5566 """verify the integrity of the repository
5572
5567
5573 Verify the integrity of the current repository.
5568 Verify the integrity of the current repository.
5574
5569
5575 This will perform an extensive check of the repository's
5570 This will perform an extensive check of the repository's
5576 integrity, validating the hashes and checksums of each entry in
5571 integrity, validating the hashes and checksums of each entry in
5577 the changelog, manifest, and tracked files, as well as the
5572 the changelog, manifest, and tracked files, as well as the
5578 integrity of their crosslinks and indices.
5573 integrity of their crosslinks and indices.
5579
5574
5580 Please see https://mercurial-scm.org/wiki/RepositoryCorruption
5575 Please see https://mercurial-scm.org/wiki/RepositoryCorruption
5581 for more information about recovery from corruption of the
5576 for more information about recovery from corruption of the
5582 repository.
5577 repository.
5583
5578
5584 Returns 0 on success, 1 if errors are encountered.
5579 Returns 0 on success, 1 if errors are encountered.
5585 """
5580 """
5586 return hg.verify(repo)
5581 return hg.verify(repo)
5587
5582
5588 @command('version', [] + formatteropts, norepo=True, cmdtype=readonly)
5583 @command('version', [] + formatteropts, norepo=True, cmdtype=readonly)
5589 def version_(ui, **opts):
5584 def version_(ui, **opts):
5590 """output version and copyright information"""
5585 """output version and copyright information"""
5591 opts = pycompat.byteskwargs(opts)
5586 opts = pycompat.byteskwargs(opts)
5592 if ui.verbose:
5587 if ui.verbose:
5593 ui.pager('version')
5588 ui.pager('version')
5594 fm = ui.formatter("version", opts)
5589 fm = ui.formatter("version", opts)
5595 fm.startitem()
5590 fm.startitem()
5596 fm.write("ver", _("Mercurial Distributed SCM (version %s)\n"),
5591 fm.write("ver", _("Mercurial Distributed SCM (version %s)\n"),
5597 util.version())
5592 util.version())
5598 license = _(
5593 license = _(
5599 "(see https://mercurial-scm.org for more information)\n"
5594 "(see https://mercurial-scm.org for more information)\n"
5600 "\nCopyright (C) 2005-2017 Matt Mackall and others\n"
5595 "\nCopyright (C) 2005-2017 Matt Mackall and others\n"
5601 "This is free software; see the source for copying conditions. "
5596 "This is free software; see the source for copying conditions. "
5602 "There is NO\nwarranty; "
5597 "There is NO\nwarranty; "
5603 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
5598 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
5604 )
5599 )
5605 if not ui.quiet:
5600 if not ui.quiet:
5606 fm.plain(license)
5601 fm.plain(license)
5607
5602
5608 if ui.verbose:
5603 if ui.verbose:
5609 fm.plain(_("\nEnabled extensions:\n\n"))
5604 fm.plain(_("\nEnabled extensions:\n\n"))
5610 # format names and versions into columns
5605 # format names and versions into columns
5611 names = []
5606 names = []
5612 vers = []
5607 vers = []
5613 isinternals = []
5608 isinternals = []
5614 for name, module in extensions.extensions():
5609 for name, module in extensions.extensions():
5615 names.append(name)
5610 names.append(name)
5616 vers.append(extensions.moduleversion(module) or None)
5611 vers.append(extensions.moduleversion(module) or None)
5617 isinternals.append(extensions.ismoduleinternal(module))
5612 isinternals.append(extensions.ismoduleinternal(module))
5618 fn = fm.nested("extensions")
5613 fn = fm.nested("extensions")
5619 if names:
5614 if names:
5620 namefmt = " %%-%ds " % max(len(n) for n in names)
5615 namefmt = " %%-%ds " % max(len(n) for n in names)
5621 places = [_("external"), _("internal")]
5616 places = [_("external"), _("internal")]
5622 for n, v, p in zip(names, vers, isinternals):
5617 for n, v, p in zip(names, vers, isinternals):
5623 fn.startitem()
5618 fn.startitem()
5624 fn.condwrite(ui.verbose, "name", namefmt, n)
5619 fn.condwrite(ui.verbose, "name", namefmt, n)
5625 if ui.verbose:
5620 if ui.verbose:
5626 fn.plain("%s " % places[p])
5621 fn.plain("%s " % places[p])
5627 fn.data(bundled=p)
5622 fn.data(bundled=p)
5628 fn.condwrite(ui.verbose and v, "ver", "%s", v)
5623 fn.condwrite(ui.verbose and v, "ver", "%s", v)
5629 if ui.verbose:
5624 if ui.verbose:
5630 fn.plain("\n")
5625 fn.plain("\n")
5631 fn.end()
5626 fn.end()
5632 fm.end()
5627 fm.end()
5633
5628
5634 def loadcmdtable(ui, name, cmdtable):
5629 def loadcmdtable(ui, name, cmdtable):
5635 """Load command functions from specified cmdtable
5630 """Load command functions from specified cmdtable
5636 """
5631 """
5637 overrides = [cmd for cmd in cmdtable if cmd in table]
5632 overrides = [cmd for cmd in cmdtable if cmd in table]
5638 if overrides:
5633 if overrides:
5639 ui.warn(_("extension '%s' overrides commands: %s\n")
5634 ui.warn(_("extension '%s' overrides commands: %s\n")
5640 % (name, " ".join(overrides)))
5635 % (name, " ".join(overrides)))
5641 table.update(cmdtable)
5636 table.update(cmdtable)
@@ -1,296 +1,330 b''
1 Testing changing branch on commits
1 Testing changing branch on commits
2 ==================================
2 ==================================
3
3
4 Setup
4 Setup
5
5
6 $ cat >> $HGRCPATH << EOF
6 $ cat >> $HGRCPATH << EOF
7 > [alias]
7 > [alias]
8 > glog = log -G -T "{rev}:{node|short} {desc}\n{branch} ({bookmarks})"
8 > glog = log -G -T "{rev}:{node|short} {desc}\n{branch} ({bookmarks})"
9 > [experimental]
9 > [experimental]
10 > evolution = createmarkers
10 > evolution = createmarkers
11 > [extensions]
11 > [extensions]
12 > rebase=
12 > rebase=
13 > EOF
13 > EOF
14
14
15 $ hg init repo
15 $ hg init repo
16 $ cd repo
16 $ cd repo
17 $ for ch in a b c d e; do echo foo >> $ch; hg ci -Aqm "Added "$ch; done
17 $ for ch in a b c d e; do echo foo >> $ch; hg ci -Aqm "Added "$ch; done
18 $ hg glog
18 $ hg glog
19 @ 4:aa98ab95a928 Added e
19 @ 4:aa98ab95a928 Added e
20 | default ()
20 | default ()
21 o 3:62615734edd5 Added d
21 o 3:62615734edd5 Added d
22 | default ()
22 | default ()
23 o 2:28ad74487de9 Added c
23 o 2:28ad74487de9 Added c
24 | default ()
24 | default ()
25 o 1:29becc82797a Added b
25 o 1:29becc82797a Added b
26 | default ()
26 | default ()
27 o 0:18d04c59bb5d Added a
27 o 0:18d04c59bb5d Added a
28 default ()
28 default ()
29
29
30 $ hg branches
30 $ hg branches
31 default 4:aa98ab95a928
31 default 4:aa98ab95a928
32
32
33 Try without passing a new branch name
33 Try without passing a new branch name
34
34
35 $ hg branch -r .
35 $ hg branch -r .
36 abort: no branch name specified for the revisions
36 abort: no branch name specified for the revisions
37 [255]
37 [255]
38
38
39 Setting an invalid branch name
39 Setting an invalid branch name
40
40
41 $ hg branch -r . a:b
41 $ hg branch -r . a:b
42 abort: ':' cannot be used in a name
42 abort: ':' cannot be used in a name
43 [255]
43 [255]
44 $ hg branch -r . tip
44 $ hg branch -r . tip
45 abort: the name 'tip' is reserved
45 abort: the name 'tip' is reserved
46 [255]
46 [255]
47 $ hg branch -r . 1234
47 $ hg branch -r . 1234
48 abort: cannot use an integer as a name
48 abort: cannot use an integer as a name
49 [255]
49 [255]
50
50
51 Change on non-linear set of commits
51 Change on non-linear set of commits
52
52
53 $ hg branch -r 2 -r 4 foo
53 $ hg branch -r 2 -r 4 foo
54 abort: cannot change branch of non-linear revisions
54 abort: cannot change branch of non-linear revisions
55 [255]
55 [255]
56
56
57 Change in middle of the stack (linear commits)
57 Change in middle of the stack (linear commits)
58
58
59 $ hg branch -r 1::3 foo
59 $ hg branch -r 1::3 foo
60 abort: cannot change branch of changeset with children
60 abort: cannot change branch of changeset with children
61 [255]
61 [255]
62
62
63 Change with dirty working directory
63 Change with dirty working directory
64
64
65 $ echo bar > a
65 $ echo bar > a
66 $ hg branch -r . foo
66 $ hg branch -r . foo
67 abort: uncommitted changes
67 abort: uncommitted changes
68 [255]
68 [255]
69
69
70 $ hg revert --all
70 $ hg revert --all
71 reverting a
71 reverting a
72
72
73 Change on empty revision set
73 Change on empty revision set
74
74
75 $ hg branch -r 'draft() - all()' foo
75 $ hg branch -r 'draft() - all()' foo
76 abort: empty revision set
76 abort: empty revision set
77 [255]
77 [255]
78
78
79 Changing branch on linear set of commits from head
79 Changing branch on linear set of commits from head
80
80
81 Without obsmarkers
81 Without obsmarkers
82
82
83 $ hg branch -r 3:4 foo --config experimental.evolution=!
83 $ hg branch -r 3:4 foo --config experimental.evolution=!
84 changed branch on 2 changesets
84 changed branch on 2 changesets
85 saved backup bundle to $TESTTMP/repo/.hg/strip-backup/62615734edd5-e86bd13a-branch-change.hg (glob)
85 saved backup bundle to $TESTTMP/repo/.hg/strip-backup/62615734edd5-e86bd13a-branch-change.hg (glob)
86 $ hg glog
86 $ hg glog
87 @ 4:3938acfb5c0f Added e
87 @ 4:3938acfb5c0f Added e
88 | foo ()
88 | foo ()
89 o 3:9435da006bdc Added d
89 o 3:9435da006bdc Added d
90 | foo ()
90 | foo ()
91 o 2:28ad74487de9 Added c
91 o 2:28ad74487de9 Added c
92 | default ()
92 | default ()
93 o 1:29becc82797a Added b
93 o 1:29becc82797a Added b
94 | default ()
94 | default ()
95 o 0:18d04c59bb5d Added a
95 o 0:18d04c59bb5d Added a
96 default ()
96 default ()
97
97
98 $ hg branches
98 $ hg branches
99 foo 4:3938acfb5c0f
99 foo 4:3938acfb5c0f
100 default 2:28ad74487de9 (inactive)
100 default 2:28ad74487de9 (inactive)
101
101
102 With obsmarkers
102 With obsmarkers
103
103
104 $ hg branch -r 3::4 bar
104 $ hg branch -r 3::4 bar
105 changed branch on 2 changesets
105 changed branch on 2 changesets
106 $ hg glog
106 $ hg glog
107 @ 6:7c1991464886 Added e
107 @ 6:7c1991464886 Added e
108 | bar ()
108 | bar ()
109 o 5:1ea05e93925f Added d
109 o 5:1ea05e93925f Added d
110 | bar ()
110 | bar ()
111 o 2:28ad74487de9 Added c
111 o 2:28ad74487de9 Added c
112 | default ()
112 | default ()
113 o 1:29becc82797a Added b
113 o 1:29becc82797a Added b
114 | default ()
114 | default ()
115 o 0:18d04c59bb5d Added a
115 o 0:18d04c59bb5d Added a
116 default ()
116 default ()
117
117
118 $ hg branches
118 $ hg branches
119 bar 6:7c1991464886
119 bar 6:7c1991464886
120 default 2:28ad74487de9 (inactive)
120 default 2:28ad74487de9 (inactive)
121
121
122 Change branch name to an existing branch
122 Change branch name to an existing branch
123
123
124 $ hg branch -r . default
124 $ hg branch -r . default
125 abort: a branch of the same name already exists
125 abort: a branch of the same name already exists
126 [255]
126 [255]
127
127
128 Changing on a branch head which is not topological head
128 Changing on a branch head which is not topological head
129
129
130 $ hg branch -r 2 stable
130 $ hg branch -r 2 stable
131 abort: cannot change branch of changeset with children
131 abort: cannot change branch of changeset with children
132 [255]
132 [255]
133
133
134 Enabling the allowunstable config and trying to change branch on a branch head
134 Enabling the allowunstable config and trying to change branch on a branch head
135 which is not a topological head
135 which is not a topological head
136
136
137 $ echo "[experimental]" >> .hg/hgrc
137 $ echo "[experimental]" >> .hg/hgrc
138 $ echo "evolution.allowunstable=yes" >> .hg/hgrc
138 $ echo "evolution.allowunstable=yes" >> .hg/hgrc
139 $ hg branch -r 2 foo
139 $ hg branch -r 2 foo
140 changed branch on 1 changesets
140 changed branch on 1 changesets
141 2 new orphan changesets
141 2 new orphan changesets
142
142
143 Changing branch of an obsoleted changeset
143 Changing branch of an obsoleted changeset
144
144
145 $ hg branch -r 4 foobar
145 $ hg branch -r 4 foobar
146 abort: hidden revision '4' was rewritten as: 7c1991464886!
146 abort: hidden revision '4' was rewritten as: 7c1991464886!
147 (use --hidden to access hidden revisions)
147 (use --hidden to access hidden revisions)
148 [255]
148 [255]
149
149
150 $ hg branch -r 4 --hidden foobar
150 $ hg branch -r 4 --hidden foobar
151 abort: cannot change branch of a obsolete changeset
151 abort: cannot change branch of a obsolete changeset
152 [255]
152 [255]
153
153
154 Make sure bookmark movement is correct
154 Make sure bookmark movement is correct
155
155
156 $ hg bookmark b1
156 $ hg bookmark b1
157 $ hg glog -r '.^::'
157 $ hg glog -r '.^::'
158 @ 6:7c1991464886 Added e
158 @ 6:7c1991464886 Added e
159 | bar (b1)
159 | bar (b1)
160 * 5:1ea05e93925f Added d
160 * 5:1ea05e93925f Added d
161 | bar ()
161 | bar ()
162 ~
162 ~
163
163
164 $ hg branch -r '(.^)::' wat --debug
164 $ hg branch -r '(.^)::' wat --debug
165 changing branch of '1ea05e93925f806d875a2163f9b76764be644636' from 'bar' to 'wat'
165 changing branch of '1ea05e93925f806d875a2163f9b76764be644636' from 'bar' to 'wat'
166 committing files:
166 committing files:
167 d
167 d
168 committing manifest
168 committing manifest
169 committing changelog
169 committing changelog
170 new node id is 343660ccab7400da637bd6a211d07f413536d718
170 new node id is 343660ccab7400da637bd6a211d07f413536d718
171 changing branch of '7c19914648869f5b02fc7fed31ddee9783fdd680' from 'bar' to 'wat'
171 changing branch of '7c19914648869f5b02fc7fed31ddee9783fdd680' from 'bar' to 'wat'
172 committing files:
172 committing files:
173 e
173 e
174 committing manifest
174 committing manifest
175 committing changelog
175 committing changelog
176 new node id is de1404b45a69f8cc6437d7679033ee33e9efb4ba
176 new node id is de1404b45a69f8cc6437d7679033ee33e9efb4ba
177 moving bookmarks ['b1'] from 7c19914648869f5b02fc7fed31ddee9783fdd680 to de1404b45a69f8cc6437d7679033ee33e9efb4ba
177 moving bookmarks ['b1'] from 7c19914648869f5b02fc7fed31ddee9783fdd680 to de1404b45a69f8cc6437d7679033ee33e9efb4ba
178 resolving manifests
178 resolving manifests
179 branchmerge: False, force: False, partial: False
179 branchmerge: False, force: False, partial: False
180 ancestor: 7c1991464886, local: 7c1991464886+, remote: de1404b45a69
180 ancestor: 7c1991464886, local: 7c1991464886+, remote: de1404b45a69
181 changed branch on 2 changesets
181 changed branch on 2 changesets
182 updating the branch cache
182 updating the branch cache
183 invalid branchheads cache (served): tip differs
183 invalid branchheads cache (served): tip differs
184
184
185 $ hg glog -r '(.^)::'
185 $ hg glog -r '(.^)::'
186 @ 9:de1404b45a69 Added e
186 @ 9:de1404b45a69 Added e
187 | wat (b1)
187 | wat (b1)
188 * 8:343660ccab74 Added d
188 * 8:343660ccab74 Added d
189 | wat ()
189 | wat ()
190 ~
190 ~
191
191
192 Make sure phase handling is correct
192 Make sure phase handling is correct
193
193
194 $ echo foo >> bar
194 $ echo foo >> bar
195 $ hg ci -Aqm "added bar" --secret
195 $ hg ci -Aqm "added bar" --secret
196 1 new orphan changesets
196 1 new orphan changesets
197 $ hg glog -r .
197 $ hg glog -r .
198 @ 10:8ad1294c1660 added bar
198 @ 10:8ad1294c1660 added bar
199 | wat (b1)
199 | wat (b1)
200 ~
200 ~
201 $ hg branch -r . secret
201 $ hg branch -r . secret
202 changed branch on 1 changesets
202 changed branch on 1 changesets
203 $ hg phase -r .
203 $ hg phase -r .
204 11: secret
204 11: secret
205
205
206 $ hg branches
206 $ hg branches
207 secret 11:38a9b2d53f98
207 secret 11:38a9b2d53f98
208 foo 7:8a4729a5e2b8
208 foo 7:8a4729a5e2b8
209 wat 9:de1404b45a69 (inactive)
209 wat 9:de1404b45a69 (inactive)
210 default 2:28ad74487de9 (inactive)
210 default 2:28ad74487de9 (inactive)
211 $ hg branch
211 $ hg branch
212 secret
212 secret
213
213
214 Changing branch of another head, different from one on which we are
214 Changing branch of another head, different from one on which we are
215
215
216 $ hg glog
216 $ hg glog
217 @ 11:38a9b2d53f98 added bar
217 @ 11:38a9b2d53f98 added bar
218 | secret (b1)
218 | secret (b1)
219 * 9:de1404b45a69 Added e
219 * 9:de1404b45a69 Added e
220 | wat ()
220 | wat ()
221 * 8:343660ccab74 Added d
221 * 8:343660ccab74 Added d
222 | wat ()
222 | wat ()
223 | o 7:8a4729a5e2b8 Added c
223 | o 7:8a4729a5e2b8 Added c
224 | | foo ()
224 | | foo ()
225 x | 2:28ad74487de9 Added c
225 x | 2:28ad74487de9 Added c
226 |/ default ()
226 |/ default ()
227 o 1:29becc82797a Added b
227 o 1:29becc82797a Added b
228 | default ()
228 | default ()
229 o 0:18d04c59bb5d Added a
229 o 0:18d04c59bb5d Added a
230 default ()
230 default ()
231
231
232 $ hg branch
232 $ hg branch
233 secret
233 secret
234
234
235 $ hg branch -r 7 foobar
235 $ hg branch -r 7 foobar
236 changed branch on 1 changesets
236 changed branch on 1 changesets
237
237
238 The current branch must be preserved
238 The current branch must be preserved
239 $ hg branch
239 $ hg branch
240 secret
240 secret
241
241
242 Changing branch on multiple heads at once
242 Changing branch on multiple heads at once
243
243
244 $ hg rebase -s 8 -d 12 --keepbranches -q
244 $ hg rebase -s 8 -d 12 --keepbranches -q
245
245
246 $ hg rebase -s 14 -d 1 --keepbranches -q
246 $ hg rebase -s 14 -d 1 --keepbranches -q
247
247
248 $ hg branch -r 0: stable
248 $ hg branch -r 0: stable
249 changed branch on 6 changesets
249 changed branch on 6 changesets
250 $ hg glog
250 $ hg glog
251 @ 23:6a5ddbcfb870 added bar
251 @ 23:6a5ddbcfb870 added bar
252 | stable (b1)
252 | stable (b1)
253 o 22:baedc6e98a67 Added e
253 o 22:baedc6e98a67 Added e
254 | stable ()
254 | stable ()
255 | o 21:99ac7bf8aad1 Added d
255 | o 21:99ac7bf8aad1 Added d
256 | | stable ()
256 | | stable ()
257 | o 20:0ecb4d39c4bd Added c
257 | o 20:0ecb4d39c4bd Added c
258 |/ stable ()
258 |/ stable ()
259 o 19:fd45b986b109 Added b
259 o 19:fd45b986b109 Added b
260 | stable ()
260 | stable ()
261 o 18:204d2769eca2 Added a
261 o 18:204d2769eca2 Added a
262 stable ()
262 stable ()
263
263
264 $ hg branches
264 $ hg branches
265 stable 23:6a5ddbcfb870
265 stable 23:6a5ddbcfb870
266
266
267 $ hg branch
267 $ hg branch
268 stable
268 stable
269
269
270 Changing to same branch name does not work
270 Changing to same branch is no-op
271
271
272 $ hg branch -r 19::21 stable
272 $ hg branch -r 19::21 stable
273 abort: a branch of the same name already exists
273 changed branch on 0 changesets
274 [255]
274
275 Changing branch name to existing branch name if the branch of parent of root of
276 revs is same as the new branch name
277
278 $ hg branch -r 20::21 bugfix
279 changed branch on 2 changesets
280 $ hg glog
281 o 25:714defe1cf34 Added d
282 | bugfix ()
283 o 24:98394def28fc Added c
284 | bugfix ()
285 | @ 23:6a5ddbcfb870 added bar
286 | | stable (b1)
287 | o 22:baedc6e98a67 Added e
288 |/ stable ()
289 o 19:fd45b986b109 Added b
290 | stable ()
291 o 18:204d2769eca2 Added a
292 stable ()
293
294 $ hg branch -r 24:25 stable
295 changed branch on 2 changesets
296 $ hg glog
297 o 27:4ec342341562 Added d
298 | stable ()
299 o 26:83f48859c2de Added c
300 | stable ()
301 | @ 23:6a5ddbcfb870 added bar
302 | | stable (b1)
303 | o 22:baedc6e98a67 Added e
304 |/ stable ()
305 o 19:fd45b986b109 Added b
306 | stable ()
307 o 18:204d2769eca2 Added a
308 stable ()
275
309
276 Testing on merge
310 Testing on merge
277
311
278 $ hg merge -r 20
312 $ hg merge -r 26
279 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
313 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
280 (branch merge, don't forget to commit)
314 (branch merge, don't forget to commit)
281
315
282 $ hg branch -r . abcd
316 $ hg branch -r . abcd
283 abort: outstanding uncommitted merge
317 abort: outstanding uncommitted merge
284 [255]
318 [255]
285 $ hg ci -m "Merge commit"
319 $ hg ci -m "Merge commit"
286 $ hg branch -r '(.^)::' def
320 $ hg branch -r '(.^)::' def
287 abort: cannot change branch of a merge commit
321 abort: cannot change branch of a merge commit
288 [255]
322 [255]
289
323
290 Changing branch on public changeset
324 Changing branch on public changeset
291
325
292 $ hg phase -r 21 -p
326 $ hg phase -r 27 -p
293 $ hg branch -r 21 def
327 $ hg branch -r 27 def
294 abort: cannot change branch of public changesets
328 abort: cannot change branch of public changesets
295 (see 'hg help phases' for details)
329 (see 'hg help phases' for details)
296 [255]
330 [255]
General Comments 0
You need to be logged in to leave comments. Login now