##// END OF EJS Templates
revset: move validation of incomplete parsing to parse() function...
Yuya Nishihara -
r25251:235f6490 default
parent child Browse files
Show More
@@ -1,6484 +1,6484 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 node import hex, bin, nullid, nullrev, short
8 from node import hex, bin, nullid, nullrev, short
9 from lock import release
9 from lock import release
10 from i18n import _
10 from i18n import _
11 import os, re, difflib, time, tempfile, errno, shlex
11 import os, re, difflib, time, tempfile, errno, shlex
12 import sys, socket
12 import sys, socket
13 import hg, scmutil, util, revlog, copies, error, bookmarks
13 import hg, scmutil, util, revlog, copies, error, bookmarks
14 import patch, help, encoding, templatekw, discovery
14 import patch, help, encoding, templatekw, discovery
15 import archival, changegroup, cmdutil, hbisect
15 import archival, changegroup, cmdutil, hbisect
16 import sshserver, hgweb, commandserver
16 import sshserver, hgweb, commandserver
17 import extensions
17 import extensions
18 from hgweb import server as hgweb_server
18 from hgweb import server as hgweb_server
19 import merge as mergemod
19 import merge as mergemod
20 import minirst, revset, fileset
20 import minirst, revset, fileset
21 import dagparser, context, simplemerge, graphmod, copies
21 import dagparser, context, simplemerge, graphmod, copies
22 import random
22 import random
23 import setdiscovery, treediscovery, dagutil, pvec, localrepo
23 import setdiscovery, treediscovery, dagutil, pvec, localrepo
24 import phases, obsolete, exchange, bundle2
24 import phases, obsolete, exchange, bundle2
25 import ui as uimod
25 import ui as uimod
26
26
27 table = {}
27 table = {}
28
28
29 command = cmdutil.command(table)
29 command = cmdutil.command(table)
30
30
31 # Space delimited list of commands that don't require local repositories.
31 # Space delimited list of commands that don't require local repositories.
32 # This should be populated by passing norepo=True into the @command decorator.
32 # This should be populated by passing norepo=True into the @command decorator.
33 norepo = ''
33 norepo = ''
34 # Space delimited list of commands that optionally require local repositories.
34 # Space delimited list of commands that optionally require local repositories.
35 # This should be populated by passing optionalrepo=True into the @command
35 # This should be populated by passing optionalrepo=True into the @command
36 # decorator.
36 # decorator.
37 optionalrepo = ''
37 optionalrepo = ''
38 # Space delimited list of commands that will examine arguments looking for
38 # Space delimited list of commands that will examine arguments looking for
39 # a repository. This should be populated by passing inferrepo=True into the
39 # a repository. This should be populated by passing inferrepo=True into the
40 # @command decorator.
40 # @command decorator.
41 inferrepo = ''
41 inferrepo = ''
42
42
43 # common command options
43 # common command options
44
44
45 globalopts = [
45 globalopts = [
46 ('R', 'repository', '',
46 ('R', 'repository', '',
47 _('repository root directory or name of overlay bundle file'),
47 _('repository root directory or name of overlay bundle file'),
48 _('REPO')),
48 _('REPO')),
49 ('', 'cwd', '',
49 ('', 'cwd', '',
50 _('change working directory'), _('DIR')),
50 _('change working directory'), _('DIR')),
51 ('y', 'noninteractive', None,
51 ('y', 'noninteractive', None,
52 _('do not prompt, automatically pick the first choice for all prompts')),
52 _('do not prompt, automatically pick the first choice for all prompts')),
53 ('q', 'quiet', None, _('suppress output')),
53 ('q', 'quiet', None, _('suppress output')),
54 ('v', 'verbose', None, _('enable additional output')),
54 ('v', 'verbose', None, _('enable additional output')),
55 ('', 'config', [],
55 ('', 'config', [],
56 _('set/override config option (use \'section.name=value\')'),
56 _('set/override config option (use \'section.name=value\')'),
57 _('CONFIG')),
57 _('CONFIG')),
58 ('', 'debug', None, _('enable debugging output')),
58 ('', 'debug', None, _('enable debugging output')),
59 ('', 'debugger', None, _('start debugger')),
59 ('', 'debugger', None, _('start debugger')),
60 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
60 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
61 _('ENCODE')),
61 _('ENCODE')),
62 ('', 'encodingmode', encoding.encodingmode,
62 ('', 'encodingmode', encoding.encodingmode,
63 _('set the charset encoding mode'), _('MODE')),
63 _('set the charset encoding mode'), _('MODE')),
64 ('', 'traceback', None, _('always print a traceback on exception')),
64 ('', 'traceback', None, _('always print a traceback on exception')),
65 ('', 'time', None, _('time how long the command takes')),
65 ('', 'time', None, _('time how long the command takes')),
66 ('', 'profile', None, _('print command execution profile')),
66 ('', 'profile', None, _('print command execution profile')),
67 ('', 'version', None, _('output version information and exit')),
67 ('', 'version', None, _('output version information and exit')),
68 ('h', 'help', None, _('display help and exit')),
68 ('h', 'help', None, _('display help and exit')),
69 ('', 'hidden', False, _('consider hidden changesets')),
69 ('', 'hidden', False, _('consider hidden changesets')),
70 ]
70 ]
71
71
72 dryrunopts = [('n', 'dry-run', None,
72 dryrunopts = [('n', 'dry-run', None,
73 _('do not perform actions, just print output'))]
73 _('do not perform actions, just print output'))]
74
74
75 remoteopts = [
75 remoteopts = [
76 ('e', 'ssh', '',
76 ('e', 'ssh', '',
77 _('specify ssh command to use'), _('CMD')),
77 _('specify ssh command to use'), _('CMD')),
78 ('', 'remotecmd', '',
78 ('', 'remotecmd', '',
79 _('specify hg command to run on the remote side'), _('CMD')),
79 _('specify hg command to run on the remote side'), _('CMD')),
80 ('', 'insecure', None,
80 ('', 'insecure', None,
81 _('do not verify server certificate (ignoring web.cacerts config)')),
81 _('do not verify server certificate (ignoring web.cacerts config)')),
82 ]
82 ]
83
83
84 walkopts = [
84 walkopts = [
85 ('I', 'include', [],
85 ('I', 'include', [],
86 _('include names matching the given patterns'), _('PATTERN')),
86 _('include names matching the given patterns'), _('PATTERN')),
87 ('X', 'exclude', [],
87 ('X', 'exclude', [],
88 _('exclude names matching the given patterns'), _('PATTERN')),
88 _('exclude names matching the given patterns'), _('PATTERN')),
89 ]
89 ]
90
90
91 commitopts = [
91 commitopts = [
92 ('m', 'message', '',
92 ('m', 'message', '',
93 _('use text as commit message'), _('TEXT')),
93 _('use text as commit message'), _('TEXT')),
94 ('l', 'logfile', '',
94 ('l', 'logfile', '',
95 _('read commit message from file'), _('FILE')),
95 _('read commit message from file'), _('FILE')),
96 ]
96 ]
97
97
98 commitopts2 = [
98 commitopts2 = [
99 ('d', 'date', '',
99 ('d', 'date', '',
100 _('record the specified date as commit date'), _('DATE')),
100 _('record the specified date as commit date'), _('DATE')),
101 ('u', 'user', '',
101 ('u', 'user', '',
102 _('record the specified user as committer'), _('USER')),
102 _('record the specified user as committer'), _('USER')),
103 ]
103 ]
104
104
105 # hidden for now
105 # hidden for now
106 formatteropts = [
106 formatteropts = [
107 ('T', 'template', '',
107 ('T', 'template', '',
108 _('display with template (EXPERIMENTAL)'), _('TEMPLATE')),
108 _('display with template (EXPERIMENTAL)'), _('TEMPLATE')),
109 ]
109 ]
110
110
111 templateopts = [
111 templateopts = [
112 ('', 'style', '',
112 ('', 'style', '',
113 _('display using template map file (DEPRECATED)'), _('STYLE')),
113 _('display using template map file (DEPRECATED)'), _('STYLE')),
114 ('T', 'template', '',
114 ('T', 'template', '',
115 _('display with template'), _('TEMPLATE')),
115 _('display with template'), _('TEMPLATE')),
116 ]
116 ]
117
117
118 logopts = [
118 logopts = [
119 ('p', 'patch', None, _('show patch')),
119 ('p', 'patch', None, _('show patch')),
120 ('g', 'git', None, _('use git extended diff format')),
120 ('g', 'git', None, _('use git extended diff format')),
121 ('l', 'limit', '',
121 ('l', 'limit', '',
122 _('limit number of changes displayed'), _('NUM')),
122 _('limit number of changes displayed'), _('NUM')),
123 ('M', 'no-merges', None, _('do not show merges')),
123 ('M', 'no-merges', None, _('do not show merges')),
124 ('', 'stat', None, _('output diffstat-style summary of changes')),
124 ('', 'stat', None, _('output diffstat-style summary of changes')),
125 ('G', 'graph', None, _("show the revision DAG")),
125 ('G', 'graph', None, _("show the revision DAG")),
126 ] + templateopts
126 ] + templateopts
127
127
128 diffopts = [
128 diffopts = [
129 ('a', 'text', None, _('treat all files as text')),
129 ('a', 'text', None, _('treat all files as text')),
130 ('g', 'git', None, _('use git extended diff format')),
130 ('g', 'git', None, _('use git extended diff format')),
131 ('', 'nodates', None, _('omit dates from diff headers'))
131 ('', 'nodates', None, _('omit dates from diff headers'))
132 ]
132 ]
133
133
134 diffwsopts = [
134 diffwsopts = [
135 ('w', 'ignore-all-space', None,
135 ('w', 'ignore-all-space', None,
136 _('ignore white space when comparing lines')),
136 _('ignore white space when comparing lines')),
137 ('b', 'ignore-space-change', None,
137 ('b', 'ignore-space-change', None,
138 _('ignore changes in the amount of white space')),
138 _('ignore changes in the amount of white space')),
139 ('B', 'ignore-blank-lines', None,
139 ('B', 'ignore-blank-lines', None,
140 _('ignore changes whose lines are all blank')),
140 _('ignore changes whose lines are all blank')),
141 ]
141 ]
142
142
143 diffopts2 = [
143 diffopts2 = [
144 ('', 'noprefix', None, _('omit a/ and b/ prefixes from filenames')),
144 ('', 'noprefix', None, _('omit a/ and b/ prefixes from filenames')),
145 ('p', 'show-function', None, _('show which function each change is in')),
145 ('p', 'show-function', None, _('show which function each change is in')),
146 ('', 'reverse', None, _('produce a diff that undoes the changes')),
146 ('', 'reverse', None, _('produce a diff that undoes the changes')),
147 ] + diffwsopts + [
147 ] + diffwsopts + [
148 ('U', 'unified', '',
148 ('U', 'unified', '',
149 _('number of lines of context to show'), _('NUM')),
149 _('number of lines of context to show'), _('NUM')),
150 ('', 'stat', None, _('output diffstat-style summary of changes')),
150 ('', 'stat', None, _('output diffstat-style summary of changes')),
151 ('', 'root', '', _('produce diffs relative to subdirectory'), _('DIR')),
151 ('', 'root', '', _('produce diffs relative to subdirectory'), _('DIR')),
152 ]
152 ]
153
153
154 mergetoolopts = [
154 mergetoolopts = [
155 ('t', 'tool', '', _('specify merge tool')),
155 ('t', 'tool', '', _('specify merge tool')),
156 ]
156 ]
157
157
158 similarityopts = [
158 similarityopts = [
159 ('s', 'similarity', '',
159 ('s', 'similarity', '',
160 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
160 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
161 ]
161 ]
162
162
163 subrepoopts = [
163 subrepoopts = [
164 ('S', 'subrepos', None,
164 ('S', 'subrepos', None,
165 _('recurse into subrepositories'))
165 _('recurse into subrepositories'))
166 ]
166 ]
167
167
168 # Commands start here, listed alphabetically
168 # Commands start here, listed alphabetically
169
169
170 @command('^add',
170 @command('^add',
171 walkopts + subrepoopts + dryrunopts,
171 walkopts + subrepoopts + dryrunopts,
172 _('[OPTION]... [FILE]...'),
172 _('[OPTION]... [FILE]...'),
173 inferrepo=True)
173 inferrepo=True)
174 def add(ui, repo, *pats, **opts):
174 def add(ui, repo, *pats, **opts):
175 """add the specified files on the next commit
175 """add the specified files on the next commit
176
176
177 Schedule files to be version controlled and added to the
177 Schedule files to be version controlled and added to the
178 repository.
178 repository.
179
179
180 The files will be added to the repository at the next commit. To
180 The files will be added to the repository at the next commit. To
181 undo an add before that, see :hg:`forget`.
181 undo an add before that, see :hg:`forget`.
182
182
183 If no names are given, add all files to the repository.
183 If no names are given, add all files to the repository.
184
184
185 .. container:: verbose
185 .. container:: verbose
186
186
187 An example showing how new (unknown) files are added
187 An example showing how new (unknown) files are added
188 automatically by :hg:`add`::
188 automatically by :hg:`add`::
189
189
190 $ ls
190 $ ls
191 foo.c
191 foo.c
192 $ hg status
192 $ hg status
193 ? foo.c
193 ? foo.c
194 $ hg add
194 $ hg add
195 adding foo.c
195 adding foo.c
196 $ hg status
196 $ hg status
197 A foo.c
197 A foo.c
198
198
199 Returns 0 if all files are successfully added.
199 Returns 0 if all files are successfully added.
200 """
200 """
201
201
202 m = scmutil.match(repo[None], pats, opts)
202 m = scmutil.match(repo[None], pats, opts)
203 rejected = cmdutil.add(ui, repo, m, "", False, **opts)
203 rejected = cmdutil.add(ui, repo, m, "", False, **opts)
204 return rejected and 1 or 0
204 return rejected and 1 or 0
205
205
206 @command('addremove',
206 @command('addremove',
207 similarityopts + subrepoopts + walkopts + dryrunopts,
207 similarityopts + subrepoopts + walkopts + dryrunopts,
208 _('[OPTION]... [FILE]...'),
208 _('[OPTION]... [FILE]...'),
209 inferrepo=True)
209 inferrepo=True)
210 def addremove(ui, repo, *pats, **opts):
210 def addremove(ui, repo, *pats, **opts):
211 """add all new files, delete all missing files
211 """add all new files, delete all missing files
212
212
213 Add all new files and remove all missing files from the
213 Add all new files and remove all missing files from the
214 repository.
214 repository.
215
215
216 New files are ignored if they match any of the patterns in
216 New files are ignored if they match any of the patterns in
217 ``.hgignore``. As with add, these changes take effect at the next
217 ``.hgignore``. As with add, these changes take effect at the next
218 commit.
218 commit.
219
219
220 Use the -s/--similarity option to detect renamed files. This
220 Use the -s/--similarity option to detect renamed files. This
221 option takes a percentage between 0 (disabled) and 100 (files must
221 option takes a percentage between 0 (disabled) and 100 (files must
222 be identical) as its parameter. With a parameter greater than 0,
222 be identical) as its parameter. With a parameter greater than 0,
223 this compares every removed file with every added file and records
223 this compares every removed file with every added file and records
224 those similar enough as renames. Detecting renamed files this way
224 those similar enough as renames. Detecting renamed files this way
225 can be expensive. After using this option, :hg:`status -C` can be
225 can be expensive. After using this option, :hg:`status -C` can be
226 used to check which files were identified as moved or renamed. If
226 used to check which files were identified as moved or renamed. If
227 not specified, -s/--similarity defaults to 100 and only renames of
227 not specified, -s/--similarity defaults to 100 and only renames of
228 identical files are detected.
228 identical files are detected.
229
229
230 Returns 0 if all files are successfully added.
230 Returns 0 if all files are successfully added.
231 """
231 """
232 try:
232 try:
233 sim = float(opts.get('similarity') or 100)
233 sim = float(opts.get('similarity') or 100)
234 except ValueError:
234 except ValueError:
235 raise util.Abort(_('similarity must be a number'))
235 raise util.Abort(_('similarity must be a number'))
236 if sim < 0 or sim > 100:
236 if sim < 0 or sim > 100:
237 raise util.Abort(_('similarity must be between 0 and 100'))
237 raise util.Abort(_('similarity must be between 0 and 100'))
238 matcher = scmutil.match(repo[None], pats, opts)
238 matcher = scmutil.match(repo[None], pats, opts)
239 return scmutil.addremove(repo, matcher, "", opts, similarity=sim / 100.0)
239 return scmutil.addremove(repo, matcher, "", opts, similarity=sim / 100.0)
240
240
241 @command('^annotate|blame',
241 @command('^annotate|blame',
242 [('r', 'rev', '', _('annotate the specified revision'), _('REV')),
242 [('r', 'rev', '', _('annotate the specified revision'), _('REV')),
243 ('', 'follow', None,
243 ('', 'follow', None,
244 _('follow copies/renames and list the filename (DEPRECATED)')),
244 _('follow copies/renames and list the filename (DEPRECATED)')),
245 ('', 'no-follow', None, _("don't follow copies and renames")),
245 ('', 'no-follow', None, _("don't follow copies and renames")),
246 ('a', 'text', None, _('treat all files as text')),
246 ('a', 'text', None, _('treat all files as text')),
247 ('u', 'user', None, _('list the author (long with -v)')),
247 ('u', 'user', None, _('list the author (long with -v)')),
248 ('f', 'file', None, _('list the filename')),
248 ('f', 'file', None, _('list the filename')),
249 ('d', 'date', None, _('list the date (short with -q)')),
249 ('d', 'date', None, _('list the date (short with -q)')),
250 ('n', 'number', None, _('list the revision number (default)')),
250 ('n', 'number', None, _('list the revision number (default)')),
251 ('c', 'changeset', None, _('list the changeset')),
251 ('c', 'changeset', None, _('list the changeset')),
252 ('l', 'line-number', None, _('show line number at the first appearance'))
252 ('l', 'line-number', None, _('show line number at the first appearance'))
253 ] + diffwsopts + walkopts + formatteropts,
253 ] + diffwsopts + walkopts + formatteropts,
254 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
254 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
255 inferrepo=True)
255 inferrepo=True)
256 def annotate(ui, repo, *pats, **opts):
256 def annotate(ui, repo, *pats, **opts):
257 """show changeset information by line for each file
257 """show changeset information by line for each file
258
258
259 List changes in files, showing the revision id responsible for
259 List changes in files, showing the revision id responsible for
260 each line
260 each line
261
261
262 This command is useful for discovering when a change was made and
262 This command is useful for discovering when a change was made and
263 by whom.
263 by whom.
264
264
265 Without the -a/--text option, annotate will avoid processing files
265 Without the -a/--text option, annotate will avoid processing files
266 it detects as binary. With -a, annotate will annotate the file
266 it detects as binary. With -a, annotate will annotate the file
267 anyway, although the results will probably be neither useful
267 anyway, although the results will probably be neither useful
268 nor desirable.
268 nor desirable.
269
269
270 Returns 0 on success.
270 Returns 0 on success.
271 """
271 """
272 if not pats:
272 if not pats:
273 raise util.Abort(_('at least one filename or pattern is required'))
273 raise util.Abort(_('at least one filename or pattern is required'))
274
274
275 if opts.get('follow'):
275 if opts.get('follow'):
276 # --follow is deprecated and now just an alias for -f/--file
276 # --follow is deprecated and now just an alias for -f/--file
277 # to mimic the behavior of Mercurial before version 1.5
277 # to mimic the behavior of Mercurial before version 1.5
278 opts['file'] = True
278 opts['file'] = True
279
279
280 ctx = scmutil.revsingle(repo, opts.get('rev'))
280 ctx = scmutil.revsingle(repo, opts.get('rev'))
281
281
282 fm = ui.formatter('annotate', opts)
282 fm = ui.formatter('annotate', opts)
283 if ui.quiet:
283 if ui.quiet:
284 datefunc = util.shortdate
284 datefunc = util.shortdate
285 else:
285 else:
286 datefunc = util.datestr
286 datefunc = util.datestr
287 if ctx.rev() is None:
287 if ctx.rev() is None:
288 def hexfn(node):
288 def hexfn(node):
289 if node is None:
289 if node is None:
290 return None
290 return None
291 else:
291 else:
292 return fm.hexfunc(node)
292 return fm.hexfunc(node)
293 if opts.get('changeset'):
293 if opts.get('changeset'):
294 # omit "+" suffix which is appended to node hex
294 # omit "+" suffix which is appended to node hex
295 def formatrev(rev):
295 def formatrev(rev):
296 if rev is None:
296 if rev is None:
297 return '%d' % ctx.p1().rev()
297 return '%d' % ctx.p1().rev()
298 else:
298 else:
299 return '%d' % rev
299 return '%d' % rev
300 else:
300 else:
301 def formatrev(rev):
301 def formatrev(rev):
302 if rev is None:
302 if rev is None:
303 return '%d+' % ctx.p1().rev()
303 return '%d+' % ctx.p1().rev()
304 else:
304 else:
305 return '%d ' % rev
305 return '%d ' % rev
306 def formathex(hex):
306 def formathex(hex):
307 if hex is None:
307 if hex is None:
308 return '%s+' % fm.hexfunc(ctx.p1().node())
308 return '%s+' % fm.hexfunc(ctx.p1().node())
309 else:
309 else:
310 return '%s ' % hex
310 return '%s ' % hex
311 else:
311 else:
312 hexfn = fm.hexfunc
312 hexfn = fm.hexfunc
313 formatrev = formathex = str
313 formatrev = formathex = str
314
314
315 opmap = [('user', ' ', lambda x: x[0].user(), ui.shortuser),
315 opmap = [('user', ' ', lambda x: x[0].user(), ui.shortuser),
316 ('number', ' ', lambda x: x[0].rev(), formatrev),
316 ('number', ' ', lambda x: x[0].rev(), formatrev),
317 ('changeset', ' ', lambda x: hexfn(x[0].node()), formathex),
317 ('changeset', ' ', lambda x: hexfn(x[0].node()), formathex),
318 ('date', ' ', lambda x: x[0].date(), util.cachefunc(datefunc)),
318 ('date', ' ', lambda x: x[0].date(), util.cachefunc(datefunc)),
319 ('file', ' ', lambda x: x[0].path(), str),
319 ('file', ' ', lambda x: x[0].path(), str),
320 ('line_number', ':', lambda x: x[1], str),
320 ('line_number', ':', lambda x: x[1], str),
321 ]
321 ]
322 fieldnamemap = {'number': 'rev', 'changeset': 'node'}
322 fieldnamemap = {'number': 'rev', 'changeset': 'node'}
323
323
324 if (not opts.get('user') and not opts.get('changeset')
324 if (not opts.get('user') and not opts.get('changeset')
325 and not opts.get('date') and not opts.get('file')):
325 and not opts.get('date') and not opts.get('file')):
326 opts['number'] = True
326 opts['number'] = True
327
327
328 linenumber = opts.get('line_number') is not None
328 linenumber = opts.get('line_number') is not None
329 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
329 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
330 raise util.Abort(_('at least one of -n/-c is required for -l'))
330 raise util.Abort(_('at least one of -n/-c is required for -l'))
331
331
332 if fm:
332 if fm:
333 def makefunc(get, fmt):
333 def makefunc(get, fmt):
334 return get
334 return get
335 else:
335 else:
336 def makefunc(get, fmt):
336 def makefunc(get, fmt):
337 return lambda x: fmt(get(x))
337 return lambda x: fmt(get(x))
338 funcmap = [(makefunc(get, fmt), sep) for op, sep, get, fmt in opmap
338 funcmap = [(makefunc(get, fmt), sep) for op, sep, get, fmt in opmap
339 if opts.get(op)]
339 if opts.get(op)]
340 funcmap[0] = (funcmap[0][0], '') # no separator in front of first column
340 funcmap[0] = (funcmap[0][0], '') # no separator in front of first column
341 fields = ' '.join(fieldnamemap.get(op, op) for op, sep, get, fmt in opmap
341 fields = ' '.join(fieldnamemap.get(op, op) for op, sep, get, fmt in opmap
342 if opts.get(op))
342 if opts.get(op))
343
343
344 def bad(x, y):
344 def bad(x, y):
345 raise util.Abort("%s: %s" % (x, y))
345 raise util.Abort("%s: %s" % (x, y))
346
346
347 m = scmutil.match(ctx, pats, opts)
347 m = scmutil.match(ctx, pats, opts)
348 m.bad = bad
348 m.bad = bad
349 follow = not opts.get('no_follow')
349 follow = not opts.get('no_follow')
350 diffopts = patch.difffeatureopts(ui, opts, section='annotate',
350 diffopts = patch.difffeatureopts(ui, opts, section='annotate',
351 whitespace=True)
351 whitespace=True)
352 for abs in ctx.walk(m):
352 for abs in ctx.walk(m):
353 fctx = ctx[abs]
353 fctx = ctx[abs]
354 if not opts.get('text') and util.binary(fctx.data()):
354 if not opts.get('text') and util.binary(fctx.data()):
355 fm.plain(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
355 fm.plain(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
356 continue
356 continue
357
357
358 lines = fctx.annotate(follow=follow, linenumber=linenumber,
358 lines = fctx.annotate(follow=follow, linenumber=linenumber,
359 diffopts=diffopts)
359 diffopts=diffopts)
360 formats = []
360 formats = []
361 pieces = []
361 pieces = []
362
362
363 for f, sep in funcmap:
363 for f, sep in funcmap:
364 l = [f(n) for n, dummy in lines]
364 l = [f(n) for n, dummy in lines]
365 if l:
365 if l:
366 if fm:
366 if fm:
367 formats.append(['%s' for x in l])
367 formats.append(['%s' for x in l])
368 else:
368 else:
369 sizes = [encoding.colwidth(x) for x in l]
369 sizes = [encoding.colwidth(x) for x in l]
370 ml = max(sizes)
370 ml = max(sizes)
371 formats.append([sep + ' ' * (ml - w) + '%s' for w in sizes])
371 formats.append([sep + ' ' * (ml - w) + '%s' for w in sizes])
372 pieces.append(l)
372 pieces.append(l)
373
373
374 for f, p, l in zip(zip(*formats), zip(*pieces), lines):
374 for f, p, l in zip(zip(*formats), zip(*pieces), lines):
375 fm.startitem()
375 fm.startitem()
376 fm.write(fields, "".join(f), *p)
376 fm.write(fields, "".join(f), *p)
377 fm.write('line', ": %s", l[1])
377 fm.write('line', ": %s", l[1])
378
378
379 if lines and not lines[-1][1].endswith('\n'):
379 if lines and not lines[-1][1].endswith('\n'):
380 fm.plain('\n')
380 fm.plain('\n')
381
381
382 fm.end()
382 fm.end()
383
383
384 @command('archive',
384 @command('archive',
385 [('', 'no-decode', None, _('do not pass files through decoders')),
385 [('', 'no-decode', None, _('do not pass files through decoders')),
386 ('p', 'prefix', '', _('directory prefix for files in archive'),
386 ('p', 'prefix', '', _('directory prefix for files in archive'),
387 _('PREFIX')),
387 _('PREFIX')),
388 ('r', 'rev', '', _('revision to distribute'), _('REV')),
388 ('r', 'rev', '', _('revision to distribute'), _('REV')),
389 ('t', 'type', '', _('type of distribution to create'), _('TYPE')),
389 ('t', 'type', '', _('type of distribution to create'), _('TYPE')),
390 ] + subrepoopts + walkopts,
390 ] + subrepoopts + walkopts,
391 _('[OPTION]... DEST'))
391 _('[OPTION]... DEST'))
392 def archive(ui, repo, dest, **opts):
392 def archive(ui, repo, dest, **opts):
393 '''create an unversioned archive of a repository revision
393 '''create an unversioned archive of a repository revision
394
394
395 By default, the revision used is the parent of the working
395 By default, the revision used is the parent of the working
396 directory; use -r/--rev to specify a different revision.
396 directory; use -r/--rev to specify a different revision.
397
397
398 The archive type is automatically detected based on file
398 The archive type is automatically detected based on file
399 extension (or override using -t/--type).
399 extension (or override using -t/--type).
400
400
401 .. container:: verbose
401 .. container:: verbose
402
402
403 Examples:
403 Examples:
404
404
405 - create a zip file containing the 1.0 release::
405 - create a zip file containing the 1.0 release::
406
406
407 hg archive -r 1.0 project-1.0.zip
407 hg archive -r 1.0 project-1.0.zip
408
408
409 - create a tarball excluding .hg files::
409 - create a tarball excluding .hg files::
410
410
411 hg archive project.tar.gz -X ".hg*"
411 hg archive project.tar.gz -X ".hg*"
412
412
413 Valid types are:
413 Valid types are:
414
414
415 :``files``: a directory full of files (default)
415 :``files``: a directory full of files (default)
416 :``tar``: tar archive, uncompressed
416 :``tar``: tar archive, uncompressed
417 :``tbz2``: tar archive, compressed using bzip2
417 :``tbz2``: tar archive, compressed using bzip2
418 :``tgz``: tar archive, compressed using gzip
418 :``tgz``: tar archive, compressed using gzip
419 :``uzip``: zip archive, uncompressed
419 :``uzip``: zip archive, uncompressed
420 :``zip``: zip archive, compressed using deflate
420 :``zip``: zip archive, compressed using deflate
421
421
422 The exact name of the destination archive or directory is given
422 The exact name of the destination archive or directory is given
423 using a format string; see :hg:`help export` for details.
423 using a format string; see :hg:`help export` for details.
424
424
425 Each member added to an archive file has a directory prefix
425 Each member added to an archive file has a directory prefix
426 prepended. Use -p/--prefix to specify a format string for the
426 prepended. Use -p/--prefix to specify a format string for the
427 prefix. The default is the basename of the archive, with suffixes
427 prefix. The default is the basename of the archive, with suffixes
428 removed.
428 removed.
429
429
430 Returns 0 on success.
430 Returns 0 on success.
431 '''
431 '''
432
432
433 ctx = scmutil.revsingle(repo, opts.get('rev'))
433 ctx = scmutil.revsingle(repo, opts.get('rev'))
434 if not ctx:
434 if not ctx:
435 raise util.Abort(_('no working directory: please specify a revision'))
435 raise util.Abort(_('no working directory: please specify a revision'))
436 node = ctx.node()
436 node = ctx.node()
437 dest = cmdutil.makefilename(repo, dest, node)
437 dest = cmdutil.makefilename(repo, dest, node)
438 if os.path.realpath(dest) == repo.root:
438 if os.path.realpath(dest) == repo.root:
439 raise util.Abort(_('repository root cannot be destination'))
439 raise util.Abort(_('repository root cannot be destination'))
440
440
441 kind = opts.get('type') or archival.guesskind(dest) or 'files'
441 kind = opts.get('type') or archival.guesskind(dest) or 'files'
442 prefix = opts.get('prefix')
442 prefix = opts.get('prefix')
443
443
444 if dest == '-':
444 if dest == '-':
445 if kind == 'files':
445 if kind == 'files':
446 raise util.Abort(_('cannot archive plain files to stdout'))
446 raise util.Abort(_('cannot archive plain files to stdout'))
447 dest = cmdutil.makefileobj(repo, dest)
447 dest = cmdutil.makefileobj(repo, dest)
448 if not prefix:
448 if not prefix:
449 prefix = os.path.basename(repo.root) + '-%h'
449 prefix = os.path.basename(repo.root) + '-%h'
450
450
451 prefix = cmdutil.makefilename(repo, prefix, node)
451 prefix = cmdutil.makefilename(repo, prefix, node)
452 matchfn = scmutil.match(ctx, [], opts)
452 matchfn = scmutil.match(ctx, [], opts)
453 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
453 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
454 matchfn, prefix, subrepos=opts.get('subrepos'))
454 matchfn, prefix, subrepos=opts.get('subrepos'))
455
455
456 @command('backout',
456 @command('backout',
457 [('', 'merge', None, _('merge with old dirstate parent after backout')),
457 [('', 'merge', None, _('merge with old dirstate parent after backout')),
458 ('', 'commit', None, _('commit if no conflicts were encountered')),
458 ('', 'commit', None, _('commit if no conflicts were encountered')),
459 ('', 'parent', '',
459 ('', 'parent', '',
460 _('parent to choose when backing out merge (DEPRECATED)'), _('REV')),
460 _('parent to choose when backing out merge (DEPRECATED)'), _('REV')),
461 ('r', 'rev', '', _('revision to backout'), _('REV')),
461 ('r', 'rev', '', _('revision to backout'), _('REV')),
462 ('e', 'edit', False, _('invoke editor on commit messages')),
462 ('e', 'edit', False, _('invoke editor on commit messages')),
463 ] + mergetoolopts + walkopts + commitopts + commitopts2,
463 ] + mergetoolopts + walkopts + commitopts + commitopts2,
464 _('[OPTION]... [-r] REV'))
464 _('[OPTION]... [-r] REV'))
465 def backout(ui, repo, node=None, rev=None, commit=False, **opts):
465 def backout(ui, repo, node=None, rev=None, commit=False, **opts):
466 '''reverse effect of earlier changeset
466 '''reverse effect of earlier changeset
467
467
468 Prepare a new changeset with the effect of REV undone in the
468 Prepare a new changeset with the effect of REV undone in the
469 current working directory.
469 current working directory.
470
470
471 If REV is the parent of the working directory, then this new changeset
471 If REV is the parent of the working directory, then this new changeset
472 is committed automatically. Otherwise, hg needs to merge the
472 is committed automatically. Otherwise, hg needs to merge the
473 changes and the merged result is left uncommitted.
473 changes and the merged result is left uncommitted.
474
474
475 .. note::
475 .. note::
476
476
477 backout cannot be used to fix either an unwanted or
477 backout cannot be used to fix either an unwanted or
478 incorrect merge.
478 incorrect merge.
479
479
480 .. container:: verbose
480 .. container:: verbose
481
481
482 By default, the pending changeset will have one parent,
482 By default, the pending changeset will have one parent,
483 maintaining a linear history. With --merge, the pending
483 maintaining a linear history. With --merge, the pending
484 changeset will instead have two parents: the old parent of the
484 changeset will instead have two parents: the old parent of the
485 working directory and a new child of REV that simply undoes REV.
485 working directory and a new child of REV that simply undoes REV.
486
486
487 Before version 1.7, the behavior without --merge was equivalent
487 Before version 1.7, the behavior without --merge was equivalent
488 to specifying --merge followed by :hg:`update --clean .` to
488 to specifying --merge followed by :hg:`update --clean .` to
489 cancel the merge and leave the child of REV as a head to be
489 cancel the merge and leave the child of REV as a head to be
490 merged separately.
490 merged separately.
491
491
492 See :hg:`help dates` for a list of formats valid for -d/--date.
492 See :hg:`help dates` for a list of formats valid for -d/--date.
493
493
494 Returns 0 on success, 1 if nothing to backout or there are unresolved
494 Returns 0 on success, 1 if nothing to backout or there are unresolved
495 files.
495 files.
496 '''
496 '''
497 if rev and node:
497 if rev and node:
498 raise util.Abort(_("please specify just one revision"))
498 raise util.Abort(_("please specify just one revision"))
499
499
500 if not rev:
500 if not rev:
501 rev = node
501 rev = node
502
502
503 if not rev:
503 if not rev:
504 raise util.Abort(_("please specify a revision to backout"))
504 raise util.Abort(_("please specify a revision to backout"))
505
505
506 date = opts.get('date')
506 date = opts.get('date')
507 if date:
507 if date:
508 opts['date'] = util.parsedate(date)
508 opts['date'] = util.parsedate(date)
509
509
510 cmdutil.checkunfinished(repo)
510 cmdutil.checkunfinished(repo)
511 cmdutil.bailifchanged(repo)
511 cmdutil.bailifchanged(repo)
512 node = scmutil.revsingle(repo, rev).node()
512 node = scmutil.revsingle(repo, rev).node()
513
513
514 op1, op2 = repo.dirstate.parents()
514 op1, op2 = repo.dirstate.parents()
515 if not repo.changelog.isancestor(node, op1):
515 if not repo.changelog.isancestor(node, op1):
516 raise util.Abort(_('cannot backout change that is not an ancestor'))
516 raise util.Abort(_('cannot backout change that is not an ancestor'))
517
517
518 p1, p2 = repo.changelog.parents(node)
518 p1, p2 = repo.changelog.parents(node)
519 if p1 == nullid:
519 if p1 == nullid:
520 raise util.Abort(_('cannot backout a change with no parents'))
520 raise util.Abort(_('cannot backout a change with no parents'))
521 if p2 != nullid:
521 if p2 != nullid:
522 if not opts.get('parent'):
522 if not opts.get('parent'):
523 raise util.Abort(_('cannot backout a merge changeset'))
523 raise util.Abort(_('cannot backout a merge changeset'))
524 p = repo.lookup(opts['parent'])
524 p = repo.lookup(opts['parent'])
525 if p not in (p1, p2):
525 if p not in (p1, p2):
526 raise util.Abort(_('%s is not a parent of %s') %
526 raise util.Abort(_('%s is not a parent of %s') %
527 (short(p), short(node)))
527 (short(p), short(node)))
528 parent = p
528 parent = p
529 else:
529 else:
530 if opts.get('parent'):
530 if opts.get('parent'):
531 raise util.Abort(_('cannot use --parent on non-merge changeset'))
531 raise util.Abort(_('cannot use --parent on non-merge changeset'))
532 parent = p1
532 parent = p1
533
533
534 # the backout should appear on the same branch
534 # the backout should appear on the same branch
535 wlock = repo.wlock()
535 wlock = repo.wlock()
536 try:
536 try:
537 branch = repo.dirstate.branch()
537 branch = repo.dirstate.branch()
538 bheads = repo.branchheads(branch)
538 bheads = repo.branchheads(branch)
539 rctx = scmutil.revsingle(repo, hex(parent))
539 rctx = scmutil.revsingle(repo, hex(parent))
540 if not opts.get('merge') and op1 != node:
540 if not opts.get('merge') and op1 != node:
541 try:
541 try:
542 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
542 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
543 'backout')
543 'backout')
544 repo.dirstate.beginparentchange()
544 repo.dirstate.beginparentchange()
545 stats = mergemod.update(repo, parent, True, True, False,
545 stats = mergemod.update(repo, parent, True, True, False,
546 node, False)
546 node, False)
547 repo.setparents(op1, op2)
547 repo.setparents(op1, op2)
548 repo.dirstate.endparentchange()
548 repo.dirstate.endparentchange()
549 hg._showstats(repo, stats)
549 hg._showstats(repo, stats)
550 if stats[3]:
550 if stats[3]:
551 repo.ui.status(_("use 'hg resolve' to retry unresolved "
551 repo.ui.status(_("use 'hg resolve' to retry unresolved "
552 "file merges\n"))
552 "file merges\n"))
553 return 1
553 return 1
554 elif not commit:
554 elif not commit:
555 msg = _("changeset %s backed out, "
555 msg = _("changeset %s backed out, "
556 "don't forget to commit.\n")
556 "don't forget to commit.\n")
557 ui.status(msg % short(node))
557 ui.status(msg % short(node))
558 return 0
558 return 0
559 finally:
559 finally:
560 ui.setconfig('ui', 'forcemerge', '', '')
560 ui.setconfig('ui', 'forcemerge', '', '')
561 else:
561 else:
562 hg.clean(repo, node, show_stats=False)
562 hg.clean(repo, node, show_stats=False)
563 repo.dirstate.setbranch(branch)
563 repo.dirstate.setbranch(branch)
564 cmdutil.revert(ui, repo, rctx, repo.dirstate.parents())
564 cmdutil.revert(ui, repo, rctx, repo.dirstate.parents())
565
565
566
566
567 def commitfunc(ui, repo, message, match, opts):
567 def commitfunc(ui, repo, message, match, opts):
568 editform = 'backout'
568 editform = 'backout'
569 e = cmdutil.getcommiteditor(editform=editform, **opts)
569 e = cmdutil.getcommiteditor(editform=editform, **opts)
570 if not message:
570 if not message:
571 # we don't translate commit messages
571 # we don't translate commit messages
572 message = "Backed out changeset %s" % short(node)
572 message = "Backed out changeset %s" % short(node)
573 e = cmdutil.getcommiteditor(edit=True, editform=editform)
573 e = cmdutil.getcommiteditor(edit=True, editform=editform)
574 return repo.commit(message, opts.get('user'), opts.get('date'),
574 return repo.commit(message, opts.get('user'), opts.get('date'),
575 match, editor=e)
575 match, editor=e)
576 newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
576 newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
577 if not newnode:
577 if not newnode:
578 ui.status(_("nothing changed\n"))
578 ui.status(_("nothing changed\n"))
579 return 1
579 return 1
580 cmdutil.commitstatus(repo, newnode, branch, bheads)
580 cmdutil.commitstatus(repo, newnode, branch, bheads)
581
581
582 def nice(node):
582 def nice(node):
583 return '%d:%s' % (repo.changelog.rev(node), short(node))
583 return '%d:%s' % (repo.changelog.rev(node), short(node))
584 ui.status(_('changeset %s backs out changeset %s\n') %
584 ui.status(_('changeset %s backs out changeset %s\n') %
585 (nice(repo.changelog.tip()), nice(node)))
585 (nice(repo.changelog.tip()), nice(node)))
586 if opts.get('merge') and op1 != node:
586 if opts.get('merge') and op1 != node:
587 hg.clean(repo, op1, show_stats=False)
587 hg.clean(repo, op1, show_stats=False)
588 ui.status(_('merging with changeset %s\n')
588 ui.status(_('merging with changeset %s\n')
589 % nice(repo.changelog.tip()))
589 % nice(repo.changelog.tip()))
590 try:
590 try:
591 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
591 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
592 'backout')
592 'backout')
593 return hg.merge(repo, hex(repo.changelog.tip()))
593 return hg.merge(repo, hex(repo.changelog.tip()))
594 finally:
594 finally:
595 ui.setconfig('ui', 'forcemerge', '', '')
595 ui.setconfig('ui', 'forcemerge', '', '')
596 finally:
596 finally:
597 wlock.release()
597 wlock.release()
598 return 0
598 return 0
599
599
600 @command('bisect',
600 @command('bisect',
601 [('r', 'reset', False, _('reset bisect state')),
601 [('r', 'reset', False, _('reset bisect state')),
602 ('g', 'good', False, _('mark changeset good')),
602 ('g', 'good', False, _('mark changeset good')),
603 ('b', 'bad', False, _('mark changeset bad')),
603 ('b', 'bad', False, _('mark changeset bad')),
604 ('s', 'skip', False, _('skip testing changeset')),
604 ('s', 'skip', False, _('skip testing changeset')),
605 ('e', 'extend', False, _('extend the bisect range')),
605 ('e', 'extend', False, _('extend the bisect range')),
606 ('c', 'command', '', _('use command to check changeset state'), _('CMD')),
606 ('c', 'command', '', _('use command to check changeset state'), _('CMD')),
607 ('U', 'noupdate', False, _('do not update to target'))],
607 ('U', 'noupdate', False, _('do not update to target'))],
608 _("[-gbsr] [-U] [-c CMD] [REV]"))
608 _("[-gbsr] [-U] [-c CMD] [REV]"))
609 def bisect(ui, repo, rev=None, extra=None, command=None,
609 def bisect(ui, repo, rev=None, extra=None, command=None,
610 reset=None, good=None, bad=None, skip=None, extend=None,
610 reset=None, good=None, bad=None, skip=None, extend=None,
611 noupdate=None):
611 noupdate=None):
612 """subdivision search of changesets
612 """subdivision search of changesets
613
613
614 This command helps to find changesets which introduce problems. To
614 This command helps to find changesets which introduce problems. To
615 use, mark the earliest changeset you know exhibits the problem as
615 use, mark the earliest changeset you know exhibits the problem as
616 bad, then mark the latest changeset which is free from the problem
616 bad, then mark the latest changeset which is free from the problem
617 as good. Bisect will update your working directory to a revision
617 as good. Bisect will update your working directory to a revision
618 for testing (unless the -U/--noupdate option is specified). Once
618 for testing (unless the -U/--noupdate option is specified). Once
619 you have performed tests, mark the working directory as good or
619 you have performed tests, mark the working directory as good or
620 bad, and bisect will either update to another candidate changeset
620 bad, and bisect will either update to another candidate changeset
621 or announce that it has found the bad revision.
621 or announce that it has found the bad revision.
622
622
623 As a shortcut, you can also use the revision argument to mark a
623 As a shortcut, you can also use the revision argument to mark a
624 revision as good or bad without checking it out first.
624 revision as good or bad without checking it out first.
625
625
626 If you supply a command, it will be used for automatic bisection.
626 If you supply a command, it will be used for automatic bisection.
627 The environment variable HG_NODE will contain the ID of the
627 The environment variable HG_NODE will contain the ID of the
628 changeset being tested. The exit status of the command will be
628 changeset being tested. The exit status of the command will be
629 used to mark revisions as good or bad: status 0 means good, 125
629 used to mark revisions as good or bad: status 0 means good, 125
630 means to skip the revision, 127 (command not found) will abort the
630 means to skip the revision, 127 (command not found) will abort the
631 bisection, and any other non-zero exit status means the revision
631 bisection, and any other non-zero exit status means the revision
632 is bad.
632 is bad.
633
633
634 .. container:: verbose
634 .. container:: verbose
635
635
636 Some examples:
636 Some examples:
637
637
638 - start a bisection with known bad revision 34, and good revision 12::
638 - start a bisection with known bad revision 34, and good revision 12::
639
639
640 hg bisect --bad 34
640 hg bisect --bad 34
641 hg bisect --good 12
641 hg bisect --good 12
642
642
643 - advance the current bisection by marking current revision as good or
643 - advance the current bisection by marking current revision as good or
644 bad::
644 bad::
645
645
646 hg bisect --good
646 hg bisect --good
647 hg bisect --bad
647 hg bisect --bad
648
648
649 - mark the current revision, or a known revision, to be skipped (e.g. if
649 - mark the current revision, or a known revision, to be skipped (e.g. if
650 that revision is not usable because of another issue)::
650 that revision is not usable because of another issue)::
651
651
652 hg bisect --skip
652 hg bisect --skip
653 hg bisect --skip 23
653 hg bisect --skip 23
654
654
655 - skip all revisions that do not touch directories ``foo`` or ``bar``::
655 - skip all revisions that do not touch directories ``foo`` or ``bar``::
656
656
657 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
657 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
658
658
659 - forget the current bisection::
659 - forget the current bisection::
660
660
661 hg bisect --reset
661 hg bisect --reset
662
662
663 - use 'make && make tests' to automatically find the first broken
663 - use 'make && make tests' to automatically find the first broken
664 revision::
664 revision::
665
665
666 hg bisect --reset
666 hg bisect --reset
667 hg bisect --bad 34
667 hg bisect --bad 34
668 hg bisect --good 12
668 hg bisect --good 12
669 hg bisect --command "make && make tests"
669 hg bisect --command "make && make tests"
670
670
671 - see all changesets whose states are already known in the current
671 - see all changesets whose states are already known in the current
672 bisection::
672 bisection::
673
673
674 hg log -r "bisect(pruned)"
674 hg log -r "bisect(pruned)"
675
675
676 - see the changeset currently being bisected (especially useful
676 - see the changeset currently being bisected (especially useful
677 if running with -U/--noupdate)::
677 if running with -U/--noupdate)::
678
678
679 hg log -r "bisect(current)"
679 hg log -r "bisect(current)"
680
680
681 - see all changesets that took part in the current bisection::
681 - see all changesets that took part in the current bisection::
682
682
683 hg log -r "bisect(range)"
683 hg log -r "bisect(range)"
684
684
685 - you can even get a nice graph::
685 - you can even get a nice graph::
686
686
687 hg log --graph -r "bisect(range)"
687 hg log --graph -r "bisect(range)"
688
688
689 See :hg:`help revsets` for more about the `bisect()` keyword.
689 See :hg:`help revsets` for more about the `bisect()` keyword.
690
690
691 Returns 0 on success.
691 Returns 0 on success.
692 """
692 """
693 def extendbisectrange(nodes, good):
693 def extendbisectrange(nodes, good):
694 # bisect is incomplete when it ends on a merge node and
694 # bisect is incomplete when it ends on a merge node and
695 # one of the parent was not checked.
695 # one of the parent was not checked.
696 parents = repo[nodes[0]].parents()
696 parents = repo[nodes[0]].parents()
697 if len(parents) > 1:
697 if len(parents) > 1:
698 if good:
698 if good:
699 side = state['bad']
699 side = state['bad']
700 else:
700 else:
701 side = state['good']
701 side = state['good']
702 num = len(set(i.node() for i in parents) & set(side))
702 num = len(set(i.node() for i in parents) & set(side))
703 if num == 1:
703 if num == 1:
704 return parents[0].ancestor(parents[1])
704 return parents[0].ancestor(parents[1])
705 return None
705 return None
706
706
707 def print_result(nodes, good):
707 def print_result(nodes, good):
708 displayer = cmdutil.show_changeset(ui, repo, {})
708 displayer = cmdutil.show_changeset(ui, repo, {})
709 if len(nodes) == 1:
709 if len(nodes) == 1:
710 # narrowed it down to a single revision
710 # narrowed it down to a single revision
711 if good:
711 if good:
712 ui.write(_("The first good revision is:\n"))
712 ui.write(_("The first good revision is:\n"))
713 else:
713 else:
714 ui.write(_("The first bad revision is:\n"))
714 ui.write(_("The first bad revision is:\n"))
715 displayer.show(repo[nodes[0]])
715 displayer.show(repo[nodes[0]])
716 extendnode = extendbisectrange(nodes, good)
716 extendnode = extendbisectrange(nodes, good)
717 if extendnode is not None:
717 if extendnode is not None:
718 ui.write(_('Not all ancestors of this changeset have been'
718 ui.write(_('Not all ancestors of this changeset have been'
719 ' checked.\nUse bisect --extend to continue the '
719 ' checked.\nUse bisect --extend to continue the '
720 'bisection from\nthe common ancestor, %s.\n')
720 'bisection from\nthe common ancestor, %s.\n')
721 % extendnode)
721 % extendnode)
722 else:
722 else:
723 # multiple possible revisions
723 # multiple possible revisions
724 if good:
724 if good:
725 ui.write(_("Due to skipped revisions, the first "
725 ui.write(_("Due to skipped revisions, the first "
726 "good revision could be any of:\n"))
726 "good revision could be any of:\n"))
727 else:
727 else:
728 ui.write(_("Due to skipped revisions, the first "
728 ui.write(_("Due to skipped revisions, the first "
729 "bad revision could be any of:\n"))
729 "bad revision could be any of:\n"))
730 for n in nodes:
730 for n in nodes:
731 displayer.show(repo[n])
731 displayer.show(repo[n])
732 displayer.close()
732 displayer.close()
733
733
734 def check_state(state, interactive=True):
734 def check_state(state, interactive=True):
735 if not state['good'] or not state['bad']:
735 if not state['good'] or not state['bad']:
736 if (good or bad or skip or reset) and interactive:
736 if (good or bad or skip or reset) and interactive:
737 return
737 return
738 if not state['good']:
738 if not state['good']:
739 raise util.Abort(_('cannot bisect (no known good revisions)'))
739 raise util.Abort(_('cannot bisect (no known good revisions)'))
740 else:
740 else:
741 raise util.Abort(_('cannot bisect (no known bad revisions)'))
741 raise util.Abort(_('cannot bisect (no known bad revisions)'))
742 return True
742 return True
743
743
744 # backward compatibility
744 # backward compatibility
745 if rev in "good bad reset init".split():
745 if rev in "good bad reset init".split():
746 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
746 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
747 cmd, rev, extra = rev, extra, None
747 cmd, rev, extra = rev, extra, None
748 if cmd == "good":
748 if cmd == "good":
749 good = True
749 good = True
750 elif cmd == "bad":
750 elif cmd == "bad":
751 bad = True
751 bad = True
752 else:
752 else:
753 reset = True
753 reset = True
754 elif extra or good + bad + skip + reset + extend + bool(command) > 1:
754 elif extra or good + bad + skip + reset + extend + bool(command) > 1:
755 raise util.Abort(_('incompatible arguments'))
755 raise util.Abort(_('incompatible arguments'))
756
756
757 cmdutil.checkunfinished(repo)
757 cmdutil.checkunfinished(repo)
758
758
759 if reset:
759 if reset:
760 p = repo.join("bisect.state")
760 p = repo.join("bisect.state")
761 if os.path.exists(p):
761 if os.path.exists(p):
762 os.unlink(p)
762 os.unlink(p)
763 return
763 return
764
764
765 state = hbisect.load_state(repo)
765 state = hbisect.load_state(repo)
766
766
767 if command:
767 if command:
768 changesets = 1
768 changesets = 1
769 if noupdate:
769 if noupdate:
770 try:
770 try:
771 node = state['current'][0]
771 node = state['current'][0]
772 except LookupError:
772 except LookupError:
773 raise util.Abort(_('current bisect revision is unknown - '
773 raise util.Abort(_('current bisect revision is unknown - '
774 'start a new bisect to fix'))
774 'start a new bisect to fix'))
775 else:
775 else:
776 node, p2 = repo.dirstate.parents()
776 node, p2 = repo.dirstate.parents()
777 if p2 != nullid:
777 if p2 != nullid:
778 raise util.Abort(_('current bisect revision is a merge'))
778 raise util.Abort(_('current bisect revision is a merge'))
779 try:
779 try:
780 while changesets:
780 while changesets:
781 # update state
781 # update state
782 state['current'] = [node]
782 state['current'] = [node]
783 hbisect.save_state(repo, state)
783 hbisect.save_state(repo, state)
784 status = ui.system(command, environ={'HG_NODE': hex(node)})
784 status = ui.system(command, environ={'HG_NODE': hex(node)})
785 if status == 125:
785 if status == 125:
786 transition = "skip"
786 transition = "skip"
787 elif status == 0:
787 elif status == 0:
788 transition = "good"
788 transition = "good"
789 # status < 0 means process was killed
789 # status < 0 means process was killed
790 elif status == 127:
790 elif status == 127:
791 raise util.Abort(_("failed to execute %s") % command)
791 raise util.Abort(_("failed to execute %s") % command)
792 elif status < 0:
792 elif status < 0:
793 raise util.Abort(_("%s killed") % command)
793 raise util.Abort(_("%s killed") % command)
794 else:
794 else:
795 transition = "bad"
795 transition = "bad"
796 ctx = scmutil.revsingle(repo, rev, node)
796 ctx = scmutil.revsingle(repo, rev, node)
797 rev = None # clear for future iterations
797 rev = None # clear for future iterations
798 state[transition].append(ctx.node())
798 state[transition].append(ctx.node())
799 ui.status(_('changeset %d:%s: %s\n') % (ctx, ctx, transition))
799 ui.status(_('changeset %d:%s: %s\n') % (ctx, ctx, transition))
800 check_state(state, interactive=False)
800 check_state(state, interactive=False)
801 # bisect
801 # bisect
802 nodes, changesets, bgood = hbisect.bisect(repo.changelog, state)
802 nodes, changesets, bgood = hbisect.bisect(repo.changelog, state)
803 # update to next check
803 # update to next check
804 node = nodes[0]
804 node = nodes[0]
805 if not noupdate:
805 if not noupdate:
806 cmdutil.bailifchanged(repo)
806 cmdutil.bailifchanged(repo)
807 hg.clean(repo, node, show_stats=False)
807 hg.clean(repo, node, show_stats=False)
808 finally:
808 finally:
809 state['current'] = [node]
809 state['current'] = [node]
810 hbisect.save_state(repo, state)
810 hbisect.save_state(repo, state)
811 print_result(nodes, bgood)
811 print_result(nodes, bgood)
812 return
812 return
813
813
814 # update state
814 # update state
815
815
816 if rev:
816 if rev:
817 nodes = [repo.lookup(i) for i in scmutil.revrange(repo, [rev])]
817 nodes = [repo.lookup(i) for i in scmutil.revrange(repo, [rev])]
818 else:
818 else:
819 nodes = [repo.lookup('.')]
819 nodes = [repo.lookup('.')]
820
820
821 if good or bad or skip:
821 if good or bad or skip:
822 if good:
822 if good:
823 state['good'] += nodes
823 state['good'] += nodes
824 elif bad:
824 elif bad:
825 state['bad'] += nodes
825 state['bad'] += nodes
826 elif skip:
826 elif skip:
827 state['skip'] += nodes
827 state['skip'] += nodes
828 hbisect.save_state(repo, state)
828 hbisect.save_state(repo, state)
829
829
830 if not check_state(state):
830 if not check_state(state):
831 return
831 return
832
832
833 # actually bisect
833 # actually bisect
834 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
834 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
835 if extend:
835 if extend:
836 if not changesets:
836 if not changesets:
837 extendnode = extendbisectrange(nodes, good)
837 extendnode = extendbisectrange(nodes, good)
838 if extendnode is not None:
838 if extendnode is not None:
839 ui.write(_("Extending search to changeset %d:%s\n")
839 ui.write(_("Extending search to changeset %d:%s\n")
840 % (extendnode.rev(), extendnode))
840 % (extendnode.rev(), extendnode))
841 state['current'] = [extendnode.node()]
841 state['current'] = [extendnode.node()]
842 hbisect.save_state(repo, state)
842 hbisect.save_state(repo, state)
843 if noupdate:
843 if noupdate:
844 return
844 return
845 cmdutil.bailifchanged(repo)
845 cmdutil.bailifchanged(repo)
846 return hg.clean(repo, extendnode.node())
846 return hg.clean(repo, extendnode.node())
847 raise util.Abort(_("nothing to extend"))
847 raise util.Abort(_("nothing to extend"))
848
848
849 if changesets == 0:
849 if changesets == 0:
850 print_result(nodes, good)
850 print_result(nodes, good)
851 else:
851 else:
852 assert len(nodes) == 1 # only a single node can be tested next
852 assert len(nodes) == 1 # only a single node can be tested next
853 node = nodes[0]
853 node = nodes[0]
854 # compute the approximate number of remaining tests
854 # compute the approximate number of remaining tests
855 tests, size = 0, 2
855 tests, size = 0, 2
856 while size <= changesets:
856 while size <= changesets:
857 tests, size = tests + 1, size * 2
857 tests, size = tests + 1, size * 2
858 rev = repo.changelog.rev(node)
858 rev = repo.changelog.rev(node)
859 ui.write(_("Testing changeset %d:%s "
859 ui.write(_("Testing changeset %d:%s "
860 "(%d changesets remaining, ~%d tests)\n")
860 "(%d changesets remaining, ~%d tests)\n")
861 % (rev, short(node), changesets, tests))
861 % (rev, short(node), changesets, tests))
862 state['current'] = [node]
862 state['current'] = [node]
863 hbisect.save_state(repo, state)
863 hbisect.save_state(repo, state)
864 if not noupdate:
864 if not noupdate:
865 cmdutil.bailifchanged(repo)
865 cmdutil.bailifchanged(repo)
866 return hg.clean(repo, node)
866 return hg.clean(repo, node)
867
867
868 @command('bookmarks|bookmark',
868 @command('bookmarks|bookmark',
869 [('f', 'force', False, _('force')),
869 [('f', 'force', False, _('force')),
870 ('r', 'rev', '', _('revision'), _('REV')),
870 ('r', 'rev', '', _('revision'), _('REV')),
871 ('d', 'delete', False, _('delete a given bookmark')),
871 ('d', 'delete', False, _('delete a given bookmark')),
872 ('m', 'rename', '', _('rename a given bookmark'), _('NAME')),
872 ('m', 'rename', '', _('rename a given bookmark'), _('NAME')),
873 ('i', 'inactive', False, _('mark a bookmark inactive')),
873 ('i', 'inactive', False, _('mark a bookmark inactive')),
874 ] + formatteropts,
874 ] + formatteropts,
875 _('hg bookmarks [OPTIONS]... [NAME]...'))
875 _('hg bookmarks [OPTIONS]... [NAME]...'))
876 def bookmark(ui, repo, *names, **opts):
876 def bookmark(ui, repo, *names, **opts):
877 '''create a new bookmark or list existing bookmarks
877 '''create a new bookmark or list existing bookmarks
878
878
879 Bookmarks are labels on changesets to help track lines of development.
879 Bookmarks are labels on changesets to help track lines of development.
880 Bookmarks are unversioned and can be moved, renamed and deleted.
880 Bookmarks are unversioned and can be moved, renamed and deleted.
881 Deleting or moving a bookmark has no effect on the associated changesets.
881 Deleting or moving a bookmark has no effect on the associated changesets.
882
882
883 Creating or updating to a bookmark causes it to be marked as 'active'.
883 Creating or updating to a bookmark causes it to be marked as 'active'.
884 The active bookmark is indicated with a '*'.
884 The active bookmark is indicated with a '*'.
885 When a commit is made, the active bookmark will advance to the new commit.
885 When a commit is made, the active bookmark will advance to the new commit.
886 A plain :hg:`update` will also advance an active bookmark, if possible.
886 A plain :hg:`update` will also advance an active bookmark, if possible.
887 Updating away from a bookmark will cause it to be deactivated.
887 Updating away from a bookmark will cause it to be deactivated.
888
888
889 Bookmarks can be pushed and pulled between repositories (see
889 Bookmarks can be pushed and pulled between repositories (see
890 :hg:`help push` and :hg:`help pull`). If a shared bookmark has
890 :hg:`help push` and :hg:`help pull`). If a shared bookmark has
891 diverged, a new 'divergent bookmark' of the form 'name@path' will
891 diverged, a new 'divergent bookmark' of the form 'name@path' will
892 be created. Using :hg:`merge` will resolve the divergence.
892 be created. Using :hg:`merge` will resolve the divergence.
893
893
894 A bookmark named '@' has the special property that :hg:`clone` will
894 A bookmark named '@' has the special property that :hg:`clone` will
895 check it out by default if it exists.
895 check it out by default if it exists.
896
896
897 .. container:: verbose
897 .. container:: verbose
898
898
899 Examples:
899 Examples:
900
900
901 - create an active bookmark for a new line of development::
901 - create an active bookmark for a new line of development::
902
902
903 hg book new-feature
903 hg book new-feature
904
904
905 - create an inactive bookmark as a place marker::
905 - create an inactive bookmark as a place marker::
906
906
907 hg book -i reviewed
907 hg book -i reviewed
908
908
909 - create an inactive bookmark on another changeset::
909 - create an inactive bookmark on another changeset::
910
910
911 hg book -r .^ tested
911 hg book -r .^ tested
912
912
913 - move the '@' bookmark from another branch::
913 - move the '@' bookmark from another branch::
914
914
915 hg book -f @
915 hg book -f @
916 '''
916 '''
917 force = opts.get('force')
917 force = opts.get('force')
918 rev = opts.get('rev')
918 rev = opts.get('rev')
919 delete = opts.get('delete')
919 delete = opts.get('delete')
920 rename = opts.get('rename')
920 rename = opts.get('rename')
921 inactive = opts.get('inactive')
921 inactive = opts.get('inactive')
922
922
923 def checkformat(mark):
923 def checkformat(mark):
924 mark = mark.strip()
924 mark = mark.strip()
925 if not mark:
925 if not mark:
926 raise util.Abort(_("bookmark names cannot consist entirely of "
926 raise util.Abort(_("bookmark names cannot consist entirely of "
927 "whitespace"))
927 "whitespace"))
928 scmutil.checknewlabel(repo, mark, 'bookmark')
928 scmutil.checknewlabel(repo, mark, 'bookmark')
929 return mark
929 return mark
930
930
931 def checkconflict(repo, mark, cur, force=False, target=None):
931 def checkconflict(repo, mark, cur, force=False, target=None):
932 if mark in marks and not force:
932 if mark in marks and not force:
933 if target:
933 if target:
934 if marks[mark] == target and target == cur:
934 if marks[mark] == target and target == cur:
935 # re-activating a bookmark
935 # re-activating a bookmark
936 return
936 return
937 anc = repo.changelog.ancestors([repo[target].rev()])
937 anc = repo.changelog.ancestors([repo[target].rev()])
938 bmctx = repo[marks[mark]]
938 bmctx = repo[marks[mark]]
939 divs = [repo[b].node() for b in marks
939 divs = [repo[b].node() for b in marks
940 if b.split('@', 1)[0] == mark.split('@', 1)[0]]
940 if b.split('@', 1)[0] == mark.split('@', 1)[0]]
941
941
942 # allow resolving a single divergent bookmark even if moving
942 # allow resolving a single divergent bookmark even if moving
943 # the bookmark across branches when a revision is specified
943 # the bookmark across branches when a revision is specified
944 # that contains a divergent bookmark
944 # that contains a divergent bookmark
945 if bmctx.rev() not in anc and target in divs:
945 if bmctx.rev() not in anc and target in divs:
946 bookmarks.deletedivergent(repo, [target], mark)
946 bookmarks.deletedivergent(repo, [target], mark)
947 return
947 return
948
948
949 deletefrom = [b for b in divs
949 deletefrom = [b for b in divs
950 if repo[b].rev() in anc or b == target]
950 if repo[b].rev() in anc or b == target]
951 bookmarks.deletedivergent(repo, deletefrom, mark)
951 bookmarks.deletedivergent(repo, deletefrom, mark)
952 if bookmarks.validdest(repo, bmctx, repo[target]):
952 if bookmarks.validdest(repo, bmctx, repo[target]):
953 ui.status(_("moving bookmark '%s' forward from %s\n") %
953 ui.status(_("moving bookmark '%s' forward from %s\n") %
954 (mark, short(bmctx.node())))
954 (mark, short(bmctx.node())))
955 return
955 return
956 raise util.Abort(_("bookmark '%s' already exists "
956 raise util.Abort(_("bookmark '%s' already exists "
957 "(use -f to force)") % mark)
957 "(use -f to force)") % mark)
958 if ((mark in repo.branchmap() or mark == repo.dirstate.branch())
958 if ((mark in repo.branchmap() or mark == repo.dirstate.branch())
959 and not force):
959 and not force):
960 raise util.Abort(
960 raise util.Abort(
961 _("a bookmark cannot have the name of an existing branch"))
961 _("a bookmark cannot have the name of an existing branch"))
962
962
963 if delete and rename:
963 if delete and rename:
964 raise util.Abort(_("--delete and --rename are incompatible"))
964 raise util.Abort(_("--delete and --rename are incompatible"))
965 if delete and rev:
965 if delete and rev:
966 raise util.Abort(_("--rev is incompatible with --delete"))
966 raise util.Abort(_("--rev is incompatible with --delete"))
967 if rename and rev:
967 if rename and rev:
968 raise util.Abort(_("--rev is incompatible with --rename"))
968 raise util.Abort(_("--rev is incompatible with --rename"))
969 if not names and (delete or rev):
969 if not names and (delete or rev):
970 raise util.Abort(_("bookmark name required"))
970 raise util.Abort(_("bookmark name required"))
971
971
972 if delete or rename or names or inactive:
972 if delete or rename or names or inactive:
973 wlock = repo.wlock()
973 wlock = repo.wlock()
974 try:
974 try:
975 cur = repo.changectx('.').node()
975 cur = repo.changectx('.').node()
976 marks = repo._bookmarks
976 marks = repo._bookmarks
977 if delete:
977 if delete:
978 for mark in names:
978 for mark in names:
979 if mark not in marks:
979 if mark not in marks:
980 raise util.Abort(_("bookmark '%s' does not exist") %
980 raise util.Abort(_("bookmark '%s' does not exist") %
981 mark)
981 mark)
982 if mark == repo._activebookmark:
982 if mark == repo._activebookmark:
983 bookmarks.deactivate(repo)
983 bookmarks.deactivate(repo)
984 del marks[mark]
984 del marks[mark]
985 marks.write()
985 marks.write()
986
986
987 elif rename:
987 elif rename:
988 if not names:
988 if not names:
989 raise util.Abort(_("new bookmark name required"))
989 raise util.Abort(_("new bookmark name required"))
990 elif len(names) > 1:
990 elif len(names) > 1:
991 raise util.Abort(_("only one new bookmark name allowed"))
991 raise util.Abort(_("only one new bookmark name allowed"))
992 mark = checkformat(names[0])
992 mark = checkformat(names[0])
993 if rename not in marks:
993 if rename not in marks:
994 raise util.Abort(_("bookmark '%s' does not exist") % rename)
994 raise util.Abort(_("bookmark '%s' does not exist") % rename)
995 checkconflict(repo, mark, cur, force)
995 checkconflict(repo, mark, cur, force)
996 marks[mark] = marks[rename]
996 marks[mark] = marks[rename]
997 if repo._activebookmark == rename and not inactive:
997 if repo._activebookmark == rename and not inactive:
998 bookmarks.activate(repo, mark)
998 bookmarks.activate(repo, mark)
999 del marks[rename]
999 del marks[rename]
1000 marks.write()
1000 marks.write()
1001
1001
1002 elif names:
1002 elif names:
1003 newact = None
1003 newact = None
1004 for mark in names:
1004 for mark in names:
1005 mark = checkformat(mark)
1005 mark = checkformat(mark)
1006 if newact is None:
1006 if newact is None:
1007 newact = mark
1007 newact = mark
1008 if inactive and mark == repo._activebookmark:
1008 if inactive and mark == repo._activebookmark:
1009 bookmarks.deactivate(repo)
1009 bookmarks.deactivate(repo)
1010 return
1010 return
1011 tgt = cur
1011 tgt = cur
1012 if rev:
1012 if rev:
1013 tgt = scmutil.revsingle(repo, rev).node()
1013 tgt = scmutil.revsingle(repo, rev).node()
1014 checkconflict(repo, mark, cur, force, tgt)
1014 checkconflict(repo, mark, cur, force, tgt)
1015 marks[mark] = tgt
1015 marks[mark] = tgt
1016 if not inactive and cur == marks[newact] and not rev:
1016 if not inactive and cur == marks[newact] and not rev:
1017 bookmarks.activate(repo, newact)
1017 bookmarks.activate(repo, newact)
1018 elif cur != tgt and newact == repo._activebookmark:
1018 elif cur != tgt and newact == repo._activebookmark:
1019 bookmarks.deactivate(repo)
1019 bookmarks.deactivate(repo)
1020 marks.write()
1020 marks.write()
1021
1021
1022 elif inactive:
1022 elif inactive:
1023 if len(marks) == 0:
1023 if len(marks) == 0:
1024 ui.status(_("no bookmarks set\n"))
1024 ui.status(_("no bookmarks set\n"))
1025 elif not repo._activebookmark:
1025 elif not repo._activebookmark:
1026 ui.status(_("no active bookmark\n"))
1026 ui.status(_("no active bookmark\n"))
1027 else:
1027 else:
1028 bookmarks.deactivate(repo)
1028 bookmarks.deactivate(repo)
1029 finally:
1029 finally:
1030 wlock.release()
1030 wlock.release()
1031 else: # show bookmarks
1031 else: # show bookmarks
1032 fm = ui.formatter('bookmarks', opts)
1032 fm = ui.formatter('bookmarks', opts)
1033 hexfn = fm.hexfunc
1033 hexfn = fm.hexfunc
1034 marks = repo._bookmarks
1034 marks = repo._bookmarks
1035 if len(marks) == 0 and not fm:
1035 if len(marks) == 0 and not fm:
1036 ui.status(_("no bookmarks set\n"))
1036 ui.status(_("no bookmarks set\n"))
1037 for bmark, n in sorted(marks.iteritems()):
1037 for bmark, n in sorted(marks.iteritems()):
1038 current = repo._activebookmark
1038 current = repo._activebookmark
1039 if bmark == current:
1039 if bmark == current:
1040 prefix, label = '*', 'bookmarks.current'
1040 prefix, label = '*', 'bookmarks.current'
1041 else:
1041 else:
1042 prefix, label = ' ', ''
1042 prefix, label = ' ', ''
1043
1043
1044 fm.startitem()
1044 fm.startitem()
1045 if not ui.quiet:
1045 if not ui.quiet:
1046 fm.plain(' %s ' % prefix, label=label)
1046 fm.plain(' %s ' % prefix, label=label)
1047 fm.write('bookmark', '%s', bmark, label=label)
1047 fm.write('bookmark', '%s', bmark, label=label)
1048 pad = " " * (25 - encoding.colwidth(bmark))
1048 pad = " " * (25 - encoding.colwidth(bmark))
1049 fm.condwrite(not ui.quiet, 'rev node', pad + ' %d:%s',
1049 fm.condwrite(not ui.quiet, 'rev node', pad + ' %d:%s',
1050 repo.changelog.rev(n), hexfn(n), label=label)
1050 repo.changelog.rev(n), hexfn(n), label=label)
1051 fm.data(active=(bmark == current))
1051 fm.data(active=(bmark == current))
1052 fm.plain('\n')
1052 fm.plain('\n')
1053 fm.end()
1053 fm.end()
1054
1054
1055 @command('branch',
1055 @command('branch',
1056 [('f', 'force', None,
1056 [('f', 'force', None,
1057 _('set branch name even if it shadows an existing branch')),
1057 _('set branch name even if it shadows an existing branch')),
1058 ('C', 'clean', None, _('reset branch name to parent branch name'))],
1058 ('C', 'clean', None, _('reset branch name to parent branch name'))],
1059 _('[-fC] [NAME]'))
1059 _('[-fC] [NAME]'))
1060 def branch(ui, repo, label=None, **opts):
1060 def branch(ui, repo, label=None, **opts):
1061 """set or show the current branch name
1061 """set or show the current branch name
1062
1062
1063 .. note::
1063 .. note::
1064
1064
1065 Branch names are permanent and global. Use :hg:`bookmark` to create a
1065 Branch names are permanent and global. Use :hg:`bookmark` to create a
1066 light-weight bookmark instead. See :hg:`help glossary` for more
1066 light-weight bookmark instead. See :hg:`help glossary` for more
1067 information about named branches and bookmarks.
1067 information about named branches and bookmarks.
1068
1068
1069 With no argument, show the current branch name. With one argument,
1069 With no argument, show the current branch name. With one argument,
1070 set the working directory branch name (the branch will not exist
1070 set the working directory branch name (the branch will not exist
1071 in the repository until the next commit). Standard practice
1071 in the repository until the next commit). Standard practice
1072 recommends that primary development take place on the 'default'
1072 recommends that primary development take place on the 'default'
1073 branch.
1073 branch.
1074
1074
1075 Unless -f/--force is specified, branch will not let you set a
1075 Unless -f/--force is specified, branch will not let you set a
1076 branch name that already exists.
1076 branch name that already exists.
1077
1077
1078 Use -C/--clean to reset the working directory branch to that of
1078 Use -C/--clean to reset the working directory branch to that of
1079 the parent of the working directory, negating a previous branch
1079 the parent of the working directory, negating a previous branch
1080 change.
1080 change.
1081
1081
1082 Use the command :hg:`update` to switch to an existing branch. Use
1082 Use the command :hg:`update` to switch to an existing branch. Use
1083 :hg:`commit --close-branch` to mark this branch as closed.
1083 :hg:`commit --close-branch` to mark this branch as closed.
1084
1084
1085 Returns 0 on success.
1085 Returns 0 on success.
1086 """
1086 """
1087 if label:
1087 if label:
1088 label = label.strip()
1088 label = label.strip()
1089
1089
1090 if not opts.get('clean') and not label:
1090 if not opts.get('clean') and not label:
1091 ui.write("%s\n" % repo.dirstate.branch())
1091 ui.write("%s\n" % repo.dirstate.branch())
1092 return
1092 return
1093
1093
1094 wlock = repo.wlock()
1094 wlock = repo.wlock()
1095 try:
1095 try:
1096 if opts.get('clean'):
1096 if opts.get('clean'):
1097 label = repo[None].p1().branch()
1097 label = repo[None].p1().branch()
1098 repo.dirstate.setbranch(label)
1098 repo.dirstate.setbranch(label)
1099 ui.status(_('reset working directory to branch %s\n') % label)
1099 ui.status(_('reset working directory to branch %s\n') % label)
1100 elif label:
1100 elif label:
1101 if not opts.get('force') and label in repo.branchmap():
1101 if not opts.get('force') and label in repo.branchmap():
1102 if label not in [p.branch() for p in repo.parents()]:
1102 if label not in [p.branch() for p in repo.parents()]:
1103 raise util.Abort(_('a branch of the same name already'
1103 raise util.Abort(_('a branch of the same name already'
1104 ' exists'),
1104 ' exists'),
1105 # i18n: "it" refers to an existing branch
1105 # i18n: "it" refers to an existing branch
1106 hint=_("use 'hg update' to switch to it"))
1106 hint=_("use 'hg update' to switch to it"))
1107 scmutil.checknewlabel(repo, label, 'branch')
1107 scmutil.checknewlabel(repo, label, 'branch')
1108 repo.dirstate.setbranch(label)
1108 repo.dirstate.setbranch(label)
1109 ui.status(_('marked working directory as branch %s\n') % label)
1109 ui.status(_('marked working directory as branch %s\n') % label)
1110 ui.status(_('(branches are permanent and global, '
1110 ui.status(_('(branches are permanent and global, '
1111 'did you want a bookmark?)\n'))
1111 'did you want a bookmark?)\n'))
1112 finally:
1112 finally:
1113 wlock.release()
1113 wlock.release()
1114
1114
1115 @command('branches',
1115 @command('branches',
1116 [('a', 'active', False,
1116 [('a', 'active', False,
1117 _('show only branches that have unmerged heads (DEPRECATED)')),
1117 _('show only branches that have unmerged heads (DEPRECATED)')),
1118 ('c', 'closed', False, _('show normal and closed branches')),
1118 ('c', 'closed', False, _('show normal and closed branches')),
1119 ] + formatteropts,
1119 ] + formatteropts,
1120 _('[-ac]'))
1120 _('[-ac]'))
1121 def branches(ui, repo, active=False, closed=False, **opts):
1121 def branches(ui, repo, active=False, closed=False, **opts):
1122 """list repository named branches
1122 """list repository named branches
1123
1123
1124 List the repository's named branches, indicating which ones are
1124 List the repository's named branches, indicating which ones are
1125 inactive. If -c/--closed is specified, also list branches which have
1125 inactive. If -c/--closed is specified, also list branches which have
1126 been marked closed (see :hg:`commit --close-branch`).
1126 been marked closed (see :hg:`commit --close-branch`).
1127
1127
1128 Use the command :hg:`update` to switch to an existing branch.
1128 Use the command :hg:`update` to switch to an existing branch.
1129
1129
1130 Returns 0.
1130 Returns 0.
1131 """
1131 """
1132
1132
1133 fm = ui.formatter('branches', opts)
1133 fm = ui.formatter('branches', opts)
1134 hexfunc = fm.hexfunc
1134 hexfunc = fm.hexfunc
1135
1135
1136 allheads = set(repo.heads())
1136 allheads = set(repo.heads())
1137 branches = []
1137 branches = []
1138 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1138 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1139 isactive = not isclosed and bool(set(heads) & allheads)
1139 isactive = not isclosed and bool(set(heads) & allheads)
1140 branches.append((tag, repo[tip], isactive, not isclosed))
1140 branches.append((tag, repo[tip], isactive, not isclosed))
1141 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]),
1141 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]),
1142 reverse=True)
1142 reverse=True)
1143
1143
1144 for tag, ctx, isactive, isopen in branches:
1144 for tag, ctx, isactive, isopen in branches:
1145 if active and not isactive:
1145 if active and not isactive:
1146 continue
1146 continue
1147 if isactive:
1147 if isactive:
1148 label = 'branches.active'
1148 label = 'branches.active'
1149 notice = ''
1149 notice = ''
1150 elif not isopen:
1150 elif not isopen:
1151 if not closed:
1151 if not closed:
1152 continue
1152 continue
1153 label = 'branches.closed'
1153 label = 'branches.closed'
1154 notice = _(' (closed)')
1154 notice = _(' (closed)')
1155 else:
1155 else:
1156 label = 'branches.inactive'
1156 label = 'branches.inactive'
1157 notice = _(' (inactive)')
1157 notice = _(' (inactive)')
1158 current = (tag == repo.dirstate.branch())
1158 current = (tag == repo.dirstate.branch())
1159 if current:
1159 if current:
1160 label = 'branches.current'
1160 label = 'branches.current'
1161
1161
1162 fm.startitem()
1162 fm.startitem()
1163 fm.write('branch', '%s', tag, label=label)
1163 fm.write('branch', '%s', tag, label=label)
1164 rev = ctx.rev()
1164 rev = ctx.rev()
1165 padsize = max(31 - len(str(rev)) - encoding.colwidth(tag), 0)
1165 padsize = max(31 - len(str(rev)) - encoding.colwidth(tag), 0)
1166 fmt = ' ' * padsize + ' %d:%s'
1166 fmt = ' ' * padsize + ' %d:%s'
1167 fm.condwrite(not ui.quiet, 'rev node', fmt, rev, hexfunc(ctx.node()),
1167 fm.condwrite(not ui.quiet, 'rev node', fmt, rev, hexfunc(ctx.node()),
1168 label='log.changeset changeset.%s' % ctx.phasestr())
1168 label='log.changeset changeset.%s' % ctx.phasestr())
1169 fm.data(active=isactive, closed=not isopen, current=current)
1169 fm.data(active=isactive, closed=not isopen, current=current)
1170 if not ui.quiet:
1170 if not ui.quiet:
1171 fm.plain(notice)
1171 fm.plain(notice)
1172 fm.plain('\n')
1172 fm.plain('\n')
1173 fm.end()
1173 fm.end()
1174
1174
1175 @command('bundle',
1175 @command('bundle',
1176 [('f', 'force', None, _('run even when the destination is unrelated')),
1176 [('f', 'force', None, _('run even when the destination is unrelated')),
1177 ('r', 'rev', [], _('a changeset intended to be added to the destination'),
1177 ('r', 'rev', [], _('a changeset intended to be added to the destination'),
1178 _('REV')),
1178 _('REV')),
1179 ('b', 'branch', [], _('a specific branch you would like to bundle'),
1179 ('b', 'branch', [], _('a specific branch you would like to bundle'),
1180 _('BRANCH')),
1180 _('BRANCH')),
1181 ('', 'base', [],
1181 ('', 'base', [],
1182 _('a base changeset assumed to be available at the destination'),
1182 _('a base changeset assumed to be available at the destination'),
1183 _('REV')),
1183 _('REV')),
1184 ('a', 'all', None, _('bundle all changesets in the repository')),
1184 ('a', 'all', None, _('bundle all changesets in the repository')),
1185 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
1185 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
1186 ] + remoteopts,
1186 ] + remoteopts,
1187 _('[-f] [-t TYPE] [-a] [-r REV]... [--base REV]... FILE [DEST]'))
1187 _('[-f] [-t TYPE] [-a] [-r REV]... [--base REV]... FILE [DEST]'))
1188 def bundle(ui, repo, fname, dest=None, **opts):
1188 def bundle(ui, repo, fname, dest=None, **opts):
1189 """create a changegroup file
1189 """create a changegroup file
1190
1190
1191 Generate a compressed changegroup file collecting changesets not
1191 Generate a compressed changegroup file collecting changesets not
1192 known to be in another repository.
1192 known to be in another repository.
1193
1193
1194 If you omit the destination repository, then hg assumes the
1194 If you omit the destination repository, then hg assumes the
1195 destination will have all the nodes you specify with --base
1195 destination will have all the nodes you specify with --base
1196 parameters. To create a bundle containing all changesets, use
1196 parameters. To create a bundle containing all changesets, use
1197 -a/--all (or --base null).
1197 -a/--all (or --base null).
1198
1198
1199 You can change compression method with the -t/--type option.
1199 You can change compression method with the -t/--type option.
1200 The available compression methods are: none, bzip2, and
1200 The available compression methods are: none, bzip2, and
1201 gzip (by default, bundles are compressed using bzip2).
1201 gzip (by default, bundles are compressed using bzip2).
1202
1202
1203 The bundle file can then be transferred using conventional means
1203 The bundle file can then be transferred using conventional means
1204 and applied to another repository with the unbundle or pull
1204 and applied to another repository with the unbundle or pull
1205 command. This is useful when direct push and pull are not
1205 command. This is useful when direct push and pull are not
1206 available or when exporting an entire repository is undesirable.
1206 available or when exporting an entire repository is undesirable.
1207
1207
1208 Applying bundles preserves all changeset contents including
1208 Applying bundles preserves all changeset contents including
1209 permissions, copy/rename information, and revision history.
1209 permissions, copy/rename information, and revision history.
1210
1210
1211 Returns 0 on success, 1 if no changes found.
1211 Returns 0 on success, 1 if no changes found.
1212 """
1212 """
1213 revs = None
1213 revs = None
1214 if 'rev' in opts:
1214 if 'rev' in opts:
1215 revs = scmutil.revrange(repo, opts['rev'])
1215 revs = scmutil.revrange(repo, opts['rev'])
1216
1216
1217 bundletype = opts.get('type', 'bzip2').lower()
1217 bundletype = opts.get('type', 'bzip2').lower()
1218 btypes = {'none': 'HG10UN',
1218 btypes = {'none': 'HG10UN',
1219 'bzip2': 'HG10BZ',
1219 'bzip2': 'HG10BZ',
1220 'gzip': 'HG10GZ',
1220 'gzip': 'HG10GZ',
1221 'bundle2': 'HG20'}
1221 'bundle2': 'HG20'}
1222 bundletype = btypes.get(bundletype)
1222 bundletype = btypes.get(bundletype)
1223 if bundletype not in changegroup.bundletypes:
1223 if bundletype not in changegroup.bundletypes:
1224 raise util.Abort(_('unknown bundle type specified with --type'))
1224 raise util.Abort(_('unknown bundle type specified with --type'))
1225
1225
1226 if opts.get('all'):
1226 if opts.get('all'):
1227 base = ['null']
1227 base = ['null']
1228 else:
1228 else:
1229 base = scmutil.revrange(repo, opts.get('base'))
1229 base = scmutil.revrange(repo, opts.get('base'))
1230 # TODO: get desired bundlecaps from command line.
1230 # TODO: get desired bundlecaps from command line.
1231 bundlecaps = None
1231 bundlecaps = None
1232 if base:
1232 if base:
1233 if dest:
1233 if dest:
1234 raise util.Abort(_("--base is incompatible with specifying "
1234 raise util.Abort(_("--base is incompatible with specifying "
1235 "a destination"))
1235 "a destination"))
1236 common = [repo.lookup(rev) for rev in base]
1236 common = [repo.lookup(rev) for rev in base]
1237 heads = revs and map(repo.lookup, revs) or revs
1237 heads = revs and map(repo.lookup, revs) or revs
1238 cg = changegroup.getchangegroup(repo, 'bundle', heads=heads,
1238 cg = changegroup.getchangegroup(repo, 'bundle', heads=heads,
1239 common=common, bundlecaps=bundlecaps)
1239 common=common, bundlecaps=bundlecaps)
1240 outgoing = None
1240 outgoing = None
1241 else:
1241 else:
1242 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1242 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1243 dest, branches = hg.parseurl(dest, opts.get('branch'))
1243 dest, branches = hg.parseurl(dest, opts.get('branch'))
1244 other = hg.peer(repo, opts, dest)
1244 other = hg.peer(repo, opts, dest)
1245 revs, checkout = hg.addbranchrevs(repo, repo, branches, revs)
1245 revs, checkout = hg.addbranchrevs(repo, repo, branches, revs)
1246 heads = revs and map(repo.lookup, revs) or revs
1246 heads = revs and map(repo.lookup, revs) or revs
1247 outgoing = discovery.findcommonoutgoing(repo, other,
1247 outgoing = discovery.findcommonoutgoing(repo, other,
1248 onlyheads=heads,
1248 onlyheads=heads,
1249 force=opts.get('force'),
1249 force=opts.get('force'),
1250 portable=True)
1250 portable=True)
1251 cg = changegroup.getlocalchangegroup(repo, 'bundle', outgoing,
1251 cg = changegroup.getlocalchangegroup(repo, 'bundle', outgoing,
1252 bundlecaps)
1252 bundlecaps)
1253 if not cg:
1253 if not cg:
1254 scmutil.nochangesfound(ui, repo, outgoing and outgoing.excluded)
1254 scmutil.nochangesfound(ui, repo, outgoing and outgoing.excluded)
1255 return 1
1255 return 1
1256
1256
1257 changegroup.writebundle(ui, cg, fname, bundletype)
1257 changegroup.writebundle(ui, cg, fname, bundletype)
1258
1258
1259 @command('cat',
1259 @command('cat',
1260 [('o', 'output', '',
1260 [('o', 'output', '',
1261 _('print output to file with formatted name'), _('FORMAT')),
1261 _('print output to file with formatted name'), _('FORMAT')),
1262 ('r', 'rev', '', _('print the given revision'), _('REV')),
1262 ('r', 'rev', '', _('print the given revision'), _('REV')),
1263 ('', 'decode', None, _('apply any matching decode filter')),
1263 ('', 'decode', None, _('apply any matching decode filter')),
1264 ] + walkopts,
1264 ] + walkopts,
1265 _('[OPTION]... FILE...'),
1265 _('[OPTION]... FILE...'),
1266 inferrepo=True)
1266 inferrepo=True)
1267 def cat(ui, repo, file1, *pats, **opts):
1267 def cat(ui, repo, file1, *pats, **opts):
1268 """output the current or given revision of files
1268 """output the current or given revision of files
1269
1269
1270 Print the specified files as they were at the given revision. If
1270 Print the specified files as they were at the given revision. If
1271 no revision is given, the parent of the working directory is used.
1271 no revision is given, the parent of the working directory is used.
1272
1272
1273 Output may be to a file, in which case the name of the file is
1273 Output may be to a file, in which case the name of the file is
1274 given using a format string. The formatting rules as follows:
1274 given using a format string. The formatting rules as follows:
1275
1275
1276 :``%%``: literal "%" character
1276 :``%%``: literal "%" character
1277 :``%s``: basename of file being printed
1277 :``%s``: basename of file being printed
1278 :``%d``: dirname of file being printed, or '.' if in repository root
1278 :``%d``: dirname of file being printed, or '.' if in repository root
1279 :``%p``: root-relative path name of file being printed
1279 :``%p``: root-relative path name of file being printed
1280 :``%H``: changeset hash (40 hexadecimal digits)
1280 :``%H``: changeset hash (40 hexadecimal digits)
1281 :``%R``: changeset revision number
1281 :``%R``: changeset revision number
1282 :``%h``: short-form changeset hash (12 hexadecimal digits)
1282 :``%h``: short-form changeset hash (12 hexadecimal digits)
1283 :``%r``: zero-padded changeset revision number
1283 :``%r``: zero-padded changeset revision number
1284 :``%b``: basename of the exporting repository
1284 :``%b``: basename of the exporting repository
1285
1285
1286 Returns 0 on success.
1286 Returns 0 on success.
1287 """
1287 """
1288 ctx = scmutil.revsingle(repo, opts.get('rev'))
1288 ctx = scmutil.revsingle(repo, opts.get('rev'))
1289 m = scmutil.match(ctx, (file1,) + pats, opts)
1289 m = scmutil.match(ctx, (file1,) + pats, opts)
1290
1290
1291 return cmdutil.cat(ui, repo, ctx, m, '', **opts)
1291 return cmdutil.cat(ui, repo, ctx, m, '', **opts)
1292
1292
1293 @command('^clone',
1293 @command('^clone',
1294 [('U', 'noupdate', None, _('the clone will include an empty working '
1294 [('U', 'noupdate', None, _('the clone will include an empty working '
1295 'directory (only a repository)')),
1295 'directory (only a repository)')),
1296 ('u', 'updaterev', '', _('revision, tag or branch to check out'), _('REV')),
1296 ('u', 'updaterev', '', _('revision, tag or branch to check out'), _('REV')),
1297 ('r', 'rev', [], _('include the specified changeset'), _('REV')),
1297 ('r', 'rev', [], _('include the specified changeset'), _('REV')),
1298 ('b', 'branch', [], _('clone only the specified branch'), _('BRANCH')),
1298 ('b', 'branch', [], _('clone only the specified branch'), _('BRANCH')),
1299 ('', 'pull', None, _('use pull protocol to copy metadata')),
1299 ('', 'pull', None, _('use pull protocol to copy metadata')),
1300 ('', 'uncompressed', None, _('use uncompressed transfer (fast over LAN)')),
1300 ('', 'uncompressed', None, _('use uncompressed transfer (fast over LAN)')),
1301 ] + remoteopts,
1301 ] + remoteopts,
1302 _('[OPTION]... SOURCE [DEST]'),
1302 _('[OPTION]... SOURCE [DEST]'),
1303 norepo=True)
1303 norepo=True)
1304 def clone(ui, source, dest=None, **opts):
1304 def clone(ui, source, dest=None, **opts):
1305 """make a copy of an existing repository
1305 """make a copy of an existing repository
1306
1306
1307 Create a copy of an existing repository in a new directory.
1307 Create a copy of an existing repository in a new directory.
1308
1308
1309 If no destination directory name is specified, it defaults to the
1309 If no destination directory name is specified, it defaults to the
1310 basename of the source.
1310 basename of the source.
1311
1311
1312 The location of the source is added to the new repository's
1312 The location of the source is added to the new repository's
1313 ``.hg/hgrc`` file, as the default to be used for future pulls.
1313 ``.hg/hgrc`` file, as the default to be used for future pulls.
1314
1314
1315 Only local paths and ``ssh://`` URLs are supported as
1315 Only local paths and ``ssh://`` URLs are supported as
1316 destinations. For ``ssh://`` destinations, no working directory or
1316 destinations. For ``ssh://`` destinations, no working directory or
1317 ``.hg/hgrc`` will be created on the remote side.
1317 ``.hg/hgrc`` will be created on the remote side.
1318
1318
1319 To pull only a subset of changesets, specify one or more revisions
1319 To pull only a subset of changesets, specify one or more revisions
1320 identifiers with -r/--rev or branches with -b/--branch. The
1320 identifiers with -r/--rev or branches with -b/--branch. The
1321 resulting clone will contain only the specified changesets and
1321 resulting clone will contain only the specified changesets and
1322 their ancestors. These options (or 'clone src#rev dest') imply
1322 their ancestors. These options (or 'clone src#rev dest') imply
1323 --pull, even for local source repositories. Note that specifying a
1323 --pull, even for local source repositories. Note that specifying a
1324 tag will include the tagged changeset but not the changeset
1324 tag will include the tagged changeset but not the changeset
1325 containing the tag.
1325 containing the tag.
1326
1326
1327 If the source repository has a bookmark called '@' set, that
1327 If the source repository has a bookmark called '@' set, that
1328 revision will be checked out in the new repository by default.
1328 revision will be checked out in the new repository by default.
1329
1329
1330 To check out a particular version, use -u/--update, or
1330 To check out a particular version, use -u/--update, or
1331 -U/--noupdate to create a clone with no working directory.
1331 -U/--noupdate to create a clone with no working directory.
1332
1332
1333 .. container:: verbose
1333 .. container:: verbose
1334
1334
1335 For efficiency, hardlinks are used for cloning whenever the
1335 For efficiency, hardlinks are used for cloning whenever the
1336 source and destination are on the same filesystem (note this
1336 source and destination are on the same filesystem (note this
1337 applies only to the repository data, not to the working
1337 applies only to the repository data, not to the working
1338 directory). Some filesystems, such as AFS, implement hardlinking
1338 directory). Some filesystems, such as AFS, implement hardlinking
1339 incorrectly, but do not report errors. In these cases, use the
1339 incorrectly, but do not report errors. In these cases, use the
1340 --pull option to avoid hardlinking.
1340 --pull option to avoid hardlinking.
1341
1341
1342 In some cases, you can clone repositories and the working
1342 In some cases, you can clone repositories and the working
1343 directory using full hardlinks with ::
1343 directory using full hardlinks with ::
1344
1344
1345 $ cp -al REPO REPOCLONE
1345 $ cp -al REPO REPOCLONE
1346
1346
1347 This is the fastest way to clone, but it is not always safe. The
1347 This is the fastest way to clone, but it is not always safe. The
1348 operation is not atomic (making sure REPO is not modified during
1348 operation is not atomic (making sure REPO is not modified during
1349 the operation is up to you) and you have to make sure your
1349 the operation is up to you) and you have to make sure your
1350 editor breaks hardlinks (Emacs and most Linux Kernel tools do
1350 editor breaks hardlinks (Emacs and most Linux Kernel tools do
1351 so). Also, this is not compatible with certain extensions that
1351 so). Also, this is not compatible with certain extensions that
1352 place their metadata under the .hg directory, such as mq.
1352 place their metadata under the .hg directory, such as mq.
1353
1353
1354 Mercurial will update the working directory to the first applicable
1354 Mercurial will update the working directory to the first applicable
1355 revision from this list:
1355 revision from this list:
1356
1356
1357 a) null if -U or the source repository has no changesets
1357 a) null if -U or the source repository has no changesets
1358 b) if -u . and the source repository is local, the first parent of
1358 b) if -u . and the source repository is local, the first parent of
1359 the source repository's working directory
1359 the source repository's working directory
1360 c) the changeset specified with -u (if a branch name, this means the
1360 c) the changeset specified with -u (if a branch name, this means the
1361 latest head of that branch)
1361 latest head of that branch)
1362 d) the changeset specified with -r
1362 d) the changeset specified with -r
1363 e) the tipmost head specified with -b
1363 e) the tipmost head specified with -b
1364 f) the tipmost head specified with the url#branch source syntax
1364 f) the tipmost head specified with the url#branch source syntax
1365 g) the revision marked with the '@' bookmark, if present
1365 g) the revision marked with the '@' bookmark, if present
1366 h) the tipmost head of the default branch
1366 h) the tipmost head of the default branch
1367 i) tip
1367 i) tip
1368
1368
1369 Examples:
1369 Examples:
1370
1370
1371 - clone a remote repository to a new directory named hg/::
1371 - clone a remote repository to a new directory named hg/::
1372
1372
1373 hg clone http://selenic.com/hg
1373 hg clone http://selenic.com/hg
1374
1374
1375 - create a lightweight local clone::
1375 - create a lightweight local clone::
1376
1376
1377 hg clone project/ project-feature/
1377 hg clone project/ project-feature/
1378
1378
1379 - clone from an absolute path on an ssh server (note double-slash)::
1379 - clone from an absolute path on an ssh server (note double-slash)::
1380
1380
1381 hg clone ssh://user@server//home/projects/alpha/
1381 hg clone ssh://user@server//home/projects/alpha/
1382
1382
1383 - do a high-speed clone over a LAN while checking out a
1383 - do a high-speed clone over a LAN while checking out a
1384 specified version::
1384 specified version::
1385
1385
1386 hg clone --uncompressed http://server/repo -u 1.5
1386 hg clone --uncompressed http://server/repo -u 1.5
1387
1387
1388 - create a repository without changesets after a particular revision::
1388 - create a repository without changesets after a particular revision::
1389
1389
1390 hg clone -r 04e544 experimental/ good/
1390 hg clone -r 04e544 experimental/ good/
1391
1391
1392 - clone (and track) a particular named branch::
1392 - clone (and track) a particular named branch::
1393
1393
1394 hg clone http://selenic.com/hg#stable
1394 hg clone http://selenic.com/hg#stable
1395
1395
1396 See :hg:`help urls` for details on specifying URLs.
1396 See :hg:`help urls` for details on specifying URLs.
1397
1397
1398 Returns 0 on success.
1398 Returns 0 on success.
1399 """
1399 """
1400 if opts.get('noupdate') and opts.get('updaterev'):
1400 if opts.get('noupdate') and opts.get('updaterev'):
1401 raise util.Abort(_("cannot specify both --noupdate and --updaterev"))
1401 raise util.Abort(_("cannot specify both --noupdate and --updaterev"))
1402
1402
1403 r = hg.clone(ui, opts, source, dest,
1403 r = hg.clone(ui, opts, source, dest,
1404 pull=opts.get('pull'),
1404 pull=opts.get('pull'),
1405 stream=opts.get('uncompressed'),
1405 stream=opts.get('uncompressed'),
1406 rev=opts.get('rev'),
1406 rev=opts.get('rev'),
1407 update=opts.get('updaterev') or not opts.get('noupdate'),
1407 update=opts.get('updaterev') or not opts.get('noupdate'),
1408 branch=opts.get('branch'))
1408 branch=opts.get('branch'))
1409
1409
1410 return r is None
1410 return r is None
1411
1411
1412 @command('^commit|ci',
1412 @command('^commit|ci',
1413 [('A', 'addremove', None,
1413 [('A', 'addremove', None,
1414 _('mark new/missing files as added/removed before committing')),
1414 _('mark new/missing files as added/removed before committing')),
1415 ('', 'close-branch', None,
1415 ('', 'close-branch', None,
1416 _('mark a branch as closed, hiding it from the branch list')),
1416 _('mark a branch as closed, hiding it from the branch list')),
1417 ('', 'amend', None, _('amend the parent of the working directory')),
1417 ('', 'amend', None, _('amend the parent of the working directory')),
1418 ('s', 'secret', None, _('use the secret phase for committing')),
1418 ('s', 'secret', None, _('use the secret phase for committing')),
1419 ('e', 'edit', None, _('invoke editor on commit messages')),
1419 ('e', 'edit', None, _('invoke editor on commit messages')),
1420 ('i', 'interactive', None, _('use interactive mode')),
1420 ('i', 'interactive', None, _('use interactive mode')),
1421 ] + walkopts + commitopts + commitopts2 + subrepoopts,
1421 ] + walkopts + commitopts + commitopts2 + subrepoopts,
1422 _('[OPTION]... [FILE]...'),
1422 _('[OPTION]... [FILE]...'),
1423 inferrepo=True)
1423 inferrepo=True)
1424 def commit(ui, repo, *pats, **opts):
1424 def commit(ui, repo, *pats, **opts):
1425 """commit the specified files or all outstanding changes
1425 """commit the specified files or all outstanding changes
1426
1426
1427 Commit changes to the given files into the repository. Unlike a
1427 Commit changes to the given files into the repository. Unlike a
1428 centralized SCM, this operation is a local operation. See
1428 centralized SCM, this operation is a local operation. See
1429 :hg:`push` for a way to actively distribute your changes.
1429 :hg:`push` for a way to actively distribute your changes.
1430
1430
1431 If a list of files is omitted, all changes reported by :hg:`status`
1431 If a list of files is omitted, all changes reported by :hg:`status`
1432 will be committed.
1432 will be committed.
1433
1433
1434 If you are committing the result of a merge, do not provide any
1434 If you are committing the result of a merge, do not provide any
1435 filenames or -I/-X filters.
1435 filenames or -I/-X filters.
1436
1436
1437 If no commit message is specified, Mercurial starts your
1437 If no commit message is specified, Mercurial starts your
1438 configured editor where you can enter a message. In case your
1438 configured editor where you can enter a message. In case your
1439 commit fails, you will find a backup of your message in
1439 commit fails, you will find a backup of your message in
1440 ``.hg/last-message.txt``.
1440 ``.hg/last-message.txt``.
1441
1441
1442 The --amend flag can be used to amend the parent of the
1442 The --amend flag can be used to amend the parent of the
1443 working directory with a new commit that contains the changes
1443 working directory with a new commit that contains the changes
1444 in the parent in addition to those currently reported by :hg:`status`,
1444 in the parent in addition to those currently reported by :hg:`status`,
1445 if there are any. The old commit is stored in a backup bundle in
1445 if there are any. The old commit is stored in a backup bundle in
1446 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
1446 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
1447 on how to restore it).
1447 on how to restore it).
1448
1448
1449 Message, user and date are taken from the amended commit unless
1449 Message, user and date are taken from the amended commit unless
1450 specified. When a message isn't specified on the command line,
1450 specified. When a message isn't specified on the command line,
1451 the editor will open with the message of the amended commit.
1451 the editor will open with the message of the amended commit.
1452
1452
1453 It is not possible to amend public changesets (see :hg:`help phases`)
1453 It is not possible to amend public changesets (see :hg:`help phases`)
1454 or changesets that have children.
1454 or changesets that have children.
1455
1455
1456 See :hg:`help dates` for a list of formats valid for -d/--date.
1456 See :hg:`help dates` for a list of formats valid for -d/--date.
1457
1457
1458 Returns 0 on success, 1 if nothing changed.
1458 Returns 0 on success, 1 if nothing changed.
1459 """
1459 """
1460 if opts.get('interactive'):
1460 if opts.get('interactive'):
1461 opts.pop('interactive')
1461 opts.pop('interactive')
1462 cmdutil.dorecord(ui, repo, commit, 'commit', False,
1462 cmdutil.dorecord(ui, repo, commit, 'commit', False,
1463 cmdutil.recordfilter, *pats, **opts)
1463 cmdutil.recordfilter, *pats, **opts)
1464 return
1464 return
1465
1465
1466 if opts.get('subrepos'):
1466 if opts.get('subrepos'):
1467 if opts.get('amend'):
1467 if opts.get('amend'):
1468 raise util.Abort(_('cannot amend with --subrepos'))
1468 raise util.Abort(_('cannot amend with --subrepos'))
1469 # Let --subrepos on the command line override config setting.
1469 # Let --subrepos on the command line override config setting.
1470 ui.setconfig('ui', 'commitsubrepos', True, 'commit')
1470 ui.setconfig('ui', 'commitsubrepos', True, 'commit')
1471
1471
1472 cmdutil.checkunfinished(repo, commit=True)
1472 cmdutil.checkunfinished(repo, commit=True)
1473
1473
1474 branch = repo[None].branch()
1474 branch = repo[None].branch()
1475 bheads = repo.branchheads(branch)
1475 bheads = repo.branchheads(branch)
1476
1476
1477 extra = {}
1477 extra = {}
1478 if opts.get('close_branch'):
1478 if opts.get('close_branch'):
1479 extra['close'] = 1
1479 extra['close'] = 1
1480
1480
1481 if not bheads:
1481 if not bheads:
1482 raise util.Abort(_('can only close branch heads'))
1482 raise util.Abort(_('can only close branch heads'))
1483 elif opts.get('amend'):
1483 elif opts.get('amend'):
1484 if repo.parents()[0].p1().branch() != branch and \
1484 if repo.parents()[0].p1().branch() != branch and \
1485 repo.parents()[0].p2().branch() != branch:
1485 repo.parents()[0].p2().branch() != branch:
1486 raise util.Abort(_('can only close branch heads'))
1486 raise util.Abort(_('can only close branch heads'))
1487
1487
1488 if opts.get('amend'):
1488 if opts.get('amend'):
1489 if ui.configbool('ui', 'commitsubrepos'):
1489 if ui.configbool('ui', 'commitsubrepos'):
1490 raise util.Abort(_('cannot amend with ui.commitsubrepos enabled'))
1490 raise util.Abort(_('cannot amend with ui.commitsubrepos enabled'))
1491
1491
1492 old = repo['.']
1492 old = repo['.']
1493 if not old.mutable():
1493 if not old.mutable():
1494 raise util.Abort(_('cannot amend public changesets'))
1494 raise util.Abort(_('cannot amend public changesets'))
1495 if len(repo[None].parents()) > 1:
1495 if len(repo[None].parents()) > 1:
1496 raise util.Abort(_('cannot amend while merging'))
1496 raise util.Abort(_('cannot amend while merging'))
1497 allowunstable = obsolete.isenabled(repo, obsolete.allowunstableopt)
1497 allowunstable = obsolete.isenabled(repo, obsolete.allowunstableopt)
1498 if not allowunstable and old.children():
1498 if not allowunstable and old.children():
1499 raise util.Abort(_('cannot amend changeset with children'))
1499 raise util.Abort(_('cannot amend changeset with children'))
1500
1500
1501 # commitfunc is used only for temporary amend commit by cmdutil.amend
1501 # commitfunc is used only for temporary amend commit by cmdutil.amend
1502 def commitfunc(ui, repo, message, match, opts):
1502 def commitfunc(ui, repo, message, match, opts):
1503 return repo.commit(message,
1503 return repo.commit(message,
1504 opts.get('user') or old.user(),
1504 opts.get('user') or old.user(),
1505 opts.get('date') or old.date(),
1505 opts.get('date') or old.date(),
1506 match,
1506 match,
1507 extra=extra)
1507 extra=extra)
1508
1508
1509 current = repo._activebookmark
1509 current = repo._activebookmark
1510 marks = old.bookmarks()
1510 marks = old.bookmarks()
1511 node = cmdutil.amend(ui, repo, commitfunc, old, extra, pats, opts)
1511 node = cmdutil.amend(ui, repo, commitfunc, old, extra, pats, opts)
1512 if node == old.node():
1512 if node == old.node():
1513 ui.status(_("nothing changed\n"))
1513 ui.status(_("nothing changed\n"))
1514 return 1
1514 return 1
1515 elif marks:
1515 elif marks:
1516 ui.debug('moving bookmarks %r from %s to %s\n' %
1516 ui.debug('moving bookmarks %r from %s to %s\n' %
1517 (marks, old.hex(), hex(node)))
1517 (marks, old.hex(), hex(node)))
1518 newmarks = repo._bookmarks
1518 newmarks = repo._bookmarks
1519 for bm in marks:
1519 for bm in marks:
1520 newmarks[bm] = node
1520 newmarks[bm] = node
1521 if bm == current:
1521 if bm == current:
1522 bookmarks.activate(repo, bm)
1522 bookmarks.activate(repo, bm)
1523 newmarks.write()
1523 newmarks.write()
1524 else:
1524 else:
1525 def commitfunc(ui, repo, message, match, opts):
1525 def commitfunc(ui, repo, message, match, opts):
1526 backup = ui.backupconfig('phases', 'new-commit')
1526 backup = ui.backupconfig('phases', 'new-commit')
1527 baseui = repo.baseui
1527 baseui = repo.baseui
1528 basebackup = baseui.backupconfig('phases', 'new-commit')
1528 basebackup = baseui.backupconfig('phases', 'new-commit')
1529 try:
1529 try:
1530 if opts.get('secret'):
1530 if opts.get('secret'):
1531 ui.setconfig('phases', 'new-commit', 'secret', 'commit')
1531 ui.setconfig('phases', 'new-commit', 'secret', 'commit')
1532 # Propagate to subrepos
1532 # Propagate to subrepos
1533 baseui.setconfig('phases', 'new-commit', 'secret', 'commit')
1533 baseui.setconfig('phases', 'new-commit', 'secret', 'commit')
1534
1534
1535 editform = cmdutil.mergeeditform(repo[None], 'commit.normal')
1535 editform = cmdutil.mergeeditform(repo[None], 'commit.normal')
1536 editor = cmdutil.getcommiteditor(editform=editform, **opts)
1536 editor = cmdutil.getcommiteditor(editform=editform, **opts)
1537 return repo.commit(message, opts.get('user'), opts.get('date'),
1537 return repo.commit(message, opts.get('user'), opts.get('date'),
1538 match,
1538 match,
1539 editor=editor,
1539 editor=editor,
1540 extra=extra)
1540 extra=extra)
1541 finally:
1541 finally:
1542 ui.restoreconfig(backup)
1542 ui.restoreconfig(backup)
1543 repo.baseui.restoreconfig(basebackup)
1543 repo.baseui.restoreconfig(basebackup)
1544
1544
1545
1545
1546 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1546 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1547
1547
1548 if not node:
1548 if not node:
1549 stat = repo.status(match=scmutil.match(repo[None], pats, opts))
1549 stat = repo.status(match=scmutil.match(repo[None], pats, opts))
1550 if stat[3]:
1550 if stat[3]:
1551 ui.status(_("nothing changed (%d missing files, see "
1551 ui.status(_("nothing changed (%d missing files, see "
1552 "'hg status')\n") % len(stat[3]))
1552 "'hg status')\n") % len(stat[3]))
1553 else:
1553 else:
1554 ui.status(_("nothing changed\n"))
1554 ui.status(_("nothing changed\n"))
1555 return 1
1555 return 1
1556
1556
1557 cmdutil.commitstatus(repo, node, branch, bheads, opts)
1557 cmdutil.commitstatus(repo, node, branch, bheads, opts)
1558
1558
1559 @command('config|showconfig|debugconfig',
1559 @command('config|showconfig|debugconfig',
1560 [('u', 'untrusted', None, _('show untrusted configuration options')),
1560 [('u', 'untrusted', None, _('show untrusted configuration options')),
1561 ('e', 'edit', None, _('edit user config')),
1561 ('e', 'edit', None, _('edit user config')),
1562 ('l', 'local', None, _('edit repository config')),
1562 ('l', 'local', None, _('edit repository config')),
1563 ('g', 'global', None, _('edit global config'))],
1563 ('g', 'global', None, _('edit global config'))],
1564 _('[-u] [NAME]...'),
1564 _('[-u] [NAME]...'),
1565 optionalrepo=True)
1565 optionalrepo=True)
1566 def config(ui, repo, *values, **opts):
1566 def config(ui, repo, *values, **opts):
1567 """show combined config settings from all hgrc files
1567 """show combined config settings from all hgrc files
1568
1568
1569 With no arguments, print names and values of all config items.
1569 With no arguments, print names and values of all config items.
1570
1570
1571 With one argument of the form section.name, print just the value
1571 With one argument of the form section.name, print just the value
1572 of that config item.
1572 of that config item.
1573
1573
1574 With multiple arguments, print names and values of all config
1574 With multiple arguments, print names and values of all config
1575 items with matching section names.
1575 items with matching section names.
1576
1576
1577 With --edit, start an editor on the user-level config file. With
1577 With --edit, start an editor on the user-level config file. With
1578 --global, edit the system-wide config file. With --local, edit the
1578 --global, edit the system-wide config file. With --local, edit the
1579 repository-level config file.
1579 repository-level config file.
1580
1580
1581 With --debug, the source (filename and line number) is printed
1581 With --debug, the source (filename and line number) is printed
1582 for each config item.
1582 for each config item.
1583
1583
1584 See :hg:`help config` for more information about config files.
1584 See :hg:`help config` for more information about config files.
1585
1585
1586 Returns 0 on success, 1 if NAME does not exist.
1586 Returns 0 on success, 1 if NAME does not exist.
1587
1587
1588 """
1588 """
1589
1589
1590 if opts.get('edit') or opts.get('local') or opts.get('global'):
1590 if opts.get('edit') or opts.get('local') or opts.get('global'):
1591 if opts.get('local') and opts.get('global'):
1591 if opts.get('local') and opts.get('global'):
1592 raise util.Abort(_("can't use --local and --global together"))
1592 raise util.Abort(_("can't use --local and --global together"))
1593
1593
1594 if opts.get('local'):
1594 if opts.get('local'):
1595 if not repo:
1595 if not repo:
1596 raise util.Abort(_("can't use --local outside a repository"))
1596 raise util.Abort(_("can't use --local outside a repository"))
1597 paths = [repo.join('hgrc')]
1597 paths = [repo.join('hgrc')]
1598 elif opts.get('global'):
1598 elif opts.get('global'):
1599 paths = scmutil.systemrcpath()
1599 paths = scmutil.systemrcpath()
1600 else:
1600 else:
1601 paths = scmutil.userrcpath()
1601 paths = scmutil.userrcpath()
1602
1602
1603 for f in paths:
1603 for f in paths:
1604 if os.path.exists(f):
1604 if os.path.exists(f):
1605 break
1605 break
1606 else:
1606 else:
1607 if opts.get('global'):
1607 if opts.get('global'):
1608 samplehgrc = uimod.samplehgrcs['global']
1608 samplehgrc = uimod.samplehgrcs['global']
1609 elif opts.get('local'):
1609 elif opts.get('local'):
1610 samplehgrc = uimod.samplehgrcs['local']
1610 samplehgrc = uimod.samplehgrcs['local']
1611 else:
1611 else:
1612 samplehgrc = uimod.samplehgrcs['user']
1612 samplehgrc = uimod.samplehgrcs['user']
1613
1613
1614 f = paths[0]
1614 f = paths[0]
1615 fp = open(f, "w")
1615 fp = open(f, "w")
1616 fp.write(samplehgrc)
1616 fp.write(samplehgrc)
1617 fp.close()
1617 fp.close()
1618
1618
1619 editor = ui.geteditor()
1619 editor = ui.geteditor()
1620 ui.system("%s \"%s\"" % (editor, f),
1620 ui.system("%s \"%s\"" % (editor, f),
1621 onerr=util.Abort, errprefix=_("edit failed"))
1621 onerr=util.Abort, errprefix=_("edit failed"))
1622 return
1622 return
1623
1623
1624 for f in scmutil.rcpath():
1624 for f in scmutil.rcpath():
1625 ui.debug('read config from: %s\n' % f)
1625 ui.debug('read config from: %s\n' % f)
1626 untrusted = bool(opts.get('untrusted'))
1626 untrusted = bool(opts.get('untrusted'))
1627 if values:
1627 if values:
1628 sections = [v for v in values if '.' not in v]
1628 sections = [v for v in values if '.' not in v]
1629 items = [v for v in values if '.' in v]
1629 items = [v for v in values if '.' in v]
1630 if len(items) > 1 or items and sections:
1630 if len(items) > 1 or items and sections:
1631 raise util.Abort(_('only one config item permitted'))
1631 raise util.Abort(_('only one config item permitted'))
1632 matched = False
1632 matched = False
1633 for section, name, value in ui.walkconfig(untrusted=untrusted):
1633 for section, name, value in ui.walkconfig(untrusted=untrusted):
1634 value = str(value).replace('\n', '\\n')
1634 value = str(value).replace('\n', '\\n')
1635 sectname = section + '.' + name
1635 sectname = section + '.' + name
1636 if values:
1636 if values:
1637 for v in values:
1637 for v in values:
1638 if v == section:
1638 if v == section:
1639 ui.debug('%s: ' %
1639 ui.debug('%s: ' %
1640 ui.configsource(section, name, untrusted))
1640 ui.configsource(section, name, untrusted))
1641 ui.write('%s=%s\n' % (sectname, value))
1641 ui.write('%s=%s\n' % (sectname, value))
1642 matched = True
1642 matched = True
1643 elif v == sectname:
1643 elif v == sectname:
1644 ui.debug('%s: ' %
1644 ui.debug('%s: ' %
1645 ui.configsource(section, name, untrusted))
1645 ui.configsource(section, name, untrusted))
1646 ui.write(value, '\n')
1646 ui.write(value, '\n')
1647 matched = True
1647 matched = True
1648 else:
1648 else:
1649 ui.debug('%s: ' %
1649 ui.debug('%s: ' %
1650 ui.configsource(section, name, untrusted))
1650 ui.configsource(section, name, untrusted))
1651 ui.write('%s=%s\n' % (sectname, value))
1651 ui.write('%s=%s\n' % (sectname, value))
1652 matched = True
1652 matched = True
1653 if matched:
1653 if matched:
1654 return 0
1654 return 0
1655 return 1
1655 return 1
1656
1656
1657 @command('copy|cp',
1657 @command('copy|cp',
1658 [('A', 'after', None, _('record a copy that has already occurred')),
1658 [('A', 'after', None, _('record a copy that has already occurred')),
1659 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1659 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1660 ] + walkopts + dryrunopts,
1660 ] + walkopts + dryrunopts,
1661 _('[OPTION]... [SOURCE]... DEST'))
1661 _('[OPTION]... [SOURCE]... DEST'))
1662 def copy(ui, repo, *pats, **opts):
1662 def copy(ui, repo, *pats, **opts):
1663 """mark files as copied for the next commit
1663 """mark files as copied for the next commit
1664
1664
1665 Mark dest as having copies of source files. If dest is a
1665 Mark dest as having copies of source files. If dest is a
1666 directory, copies are put in that directory. If dest is a file,
1666 directory, copies are put in that directory. If dest is a file,
1667 the source must be a single file.
1667 the source must be a single file.
1668
1668
1669 By default, this command copies the contents of files as they
1669 By default, this command copies the contents of files as they
1670 exist in the working directory. If invoked with -A/--after, the
1670 exist in the working directory. If invoked with -A/--after, the
1671 operation is recorded, but no copying is performed.
1671 operation is recorded, but no copying is performed.
1672
1672
1673 This command takes effect with the next commit. To undo a copy
1673 This command takes effect with the next commit. To undo a copy
1674 before that, see :hg:`revert`.
1674 before that, see :hg:`revert`.
1675
1675
1676 Returns 0 on success, 1 if errors are encountered.
1676 Returns 0 on success, 1 if errors are encountered.
1677 """
1677 """
1678 wlock = repo.wlock(False)
1678 wlock = repo.wlock(False)
1679 try:
1679 try:
1680 return cmdutil.copy(ui, repo, pats, opts)
1680 return cmdutil.copy(ui, repo, pats, opts)
1681 finally:
1681 finally:
1682 wlock.release()
1682 wlock.release()
1683
1683
1684 @command('debugancestor', [], _('[INDEX] REV1 REV2'), optionalrepo=True)
1684 @command('debugancestor', [], _('[INDEX] REV1 REV2'), optionalrepo=True)
1685 def debugancestor(ui, repo, *args):
1685 def debugancestor(ui, repo, *args):
1686 """find the ancestor revision of two revisions in a given index"""
1686 """find the ancestor revision of two revisions in a given index"""
1687 if len(args) == 3:
1687 if len(args) == 3:
1688 index, rev1, rev2 = args
1688 index, rev1, rev2 = args
1689 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), index)
1689 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), index)
1690 lookup = r.lookup
1690 lookup = r.lookup
1691 elif len(args) == 2:
1691 elif len(args) == 2:
1692 if not repo:
1692 if not repo:
1693 raise util.Abort(_("there is no Mercurial repository here "
1693 raise util.Abort(_("there is no Mercurial repository here "
1694 "(.hg not found)"))
1694 "(.hg not found)"))
1695 rev1, rev2 = args
1695 rev1, rev2 = args
1696 r = repo.changelog
1696 r = repo.changelog
1697 lookup = repo.lookup
1697 lookup = repo.lookup
1698 else:
1698 else:
1699 raise util.Abort(_('either two or three arguments required'))
1699 raise util.Abort(_('either two or three arguments required'))
1700 a = r.ancestor(lookup(rev1), lookup(rev2))
1700 a = r.ancestor(lookup(rev1), lookup(rev2))
1701 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
1701 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
1702
1702
1703 @command('debugbuilddag',
1703 @command('debugbuilddag',
1704 [('m', 'mergeable-file', None, _('add single file mergeable changes')),
1704 [('m', 'mergeable-file', None, _('add single file mergeable changes')),
1705 ('o', 'overwritten-file', None, _('add single file all revs overwrite')),
1705 ('o', 'overwritten-file', None, _('add single file all revs overwrite')),
1706 ('n', 'new-file', None, _('add new file at each rev'))],
1706 ('n', 'new-file', None, _('add new file at each rev'))],
1707 _('[OPTION]... [TEXT]'))
1707 _('[OPTION]... [TEXT]'))
1708 def debugbuilddag(ui, repo, text=None,
1708 def debugbuilddag(ui, repo, text=None,
1709 mergeable_file=False,
1709 mergeable_file=False,
1710 overwritten_file=False,
1710 overwritten_file=False,
1711 new_file=False):
1711 new_file=False):
1712 """builds a repo with a given DAG from scratch in the current empty repo
1712 """builds a repo with a given DAG from scratch in the current empty repo
1713
1713
1714 The description of the DAG is read from stdin if not given on the
1714 The description of the DAG is read from stdin if not given on the
1715 command line.
1715 command line.
1716
1716
1717 Elements:
1717 Elements:
1718
1718
1719 - "+n" is a linear run of n nodes based on the current default parent
1719 - "+n" is a linear run of n nodes based on the current default parent
1720 - "." is a single node based on the current default parent
1720 - "." is a single node based on the current default parent
1721 - "$" resets the default parent to null (implied at the start);
1721 - "$" resets the default parent to null (implied at the start);
1722 otherwise the default parent is always the last node created
1722 otherwise the default parent is always the last node created
1723 - "<p" sets the default parent to the backref p
1723 - "<p" sets the default parent to the backref p
1724 - "*p" is a fork at parent p, which is a backref
1724 - "*p" is a fork at parent p, which is a backref
1725 - "*p1/p2" is a merge of parents p1 and p2, which are backrefs
1725 - "*p1/p2" is a merge of parents p1 and p2, which are backrefs
1726 - "/p2" is a merge of the preceding node and p2
1726 - "/p2" is a merge of the preceding node and p2
1727 - ":tag" defines a local tag for the preceding node
1727 - ":tag" defines a local tag for the preceding node
1728 - "@branch" sets the named branch for subsequent nodes
1728 - "@branch" sets the named branch for subsequent nodes
1729 - "#...\\n" is a comment up to the end of the line
1729 - "#...\\n" is a comment up to the end of the line
1730
1730
1731 Whitespace between the above elements is ignored.
1731 Whitespace between the above elements is ignored.
1732
1732
1733 A backref is either
1733 A backref is either
1734
1734
1735 - a number n, which references the node curr-n, where curr is the current
1735 - a number n, which references the node curr-n, where curr is the current
1736 node, or
1736 node, or
1737 - the name of a local tag you placed earlier using ":tag", or
1737 - the name of a local tag you placed earlier using ":tag", or
1738 - empty to denote the default parent.
1738 - empty to denote the default parent.
1739
1739
1740 All string valued-elements are either strictly alphanumeric, or must
1740 All string valued-elements are either strictly alphanumeric, or must
1741 be enclosed in double quotes ("..."), with "\\" as escape character.
1741 be enclosed in double quotes ("..."), with "\\" as escape character.
1742 """
1742 """
1743
1743
1744 if text is None:
1744 if text is None:
1745 ui.status(_("reading DAG from stdin\n"))
1745 ui.status(_("reading DAG from stdin\n"))
1746 text = ui.fin.read()
1746 text = ui.fin.read()
1747
1747
1748 cl = repo.changelog
1748 cl = repo.changelog
1749 if len(cl) > 0:
1749 if len(cl) > 0:
1750 raise util.Abort(_('repository is not empty'))
1750 raise util.Abort(_('repository is not empty'))
1751
1751
1752 # determine number of revs in DAG
1752 # determine number of revs in DAG
1753 total = 0
1753 total = 0
1754 for type, data in dagparser.parsedag(text):
1754 for type, data in dagparser.parsedag(text):
1755 if type == 'n':
1755 if type == 'n':
1756 total += 1
1756 total += 1
1757
1757
1758 if mergeable_file:
1758 if mergeable_file:
1759 linesperrev = 2
1759 linesperrev = 2
1760 # make a file with k lines per rev
1760 # make a file with k lines per rev
1761 initialmergedlines = [str(i) for i in xrange(0, total * linesperrev)]
1761 initialmergedlines = [str(i) for i in xrange(0, total * linesperrev)]
1762 initialmergedlines.append("")
1762 initialmergedlines.append("")
1763
1763
1764 tags = []
1764 tags = []
1765
1765
1766 lock = tr = None
1766 lock = tr = None
1767 try:
1767 try:
1768 lock = repo.lock()
1768 lock = repo.lock()
1769 tr = repo.transaction("builddag")
1769 tr = repo.transaction("builddag")
1770
1770
1771 at = -1
1771 at = -1
1772 atbranch = 'default'
1772 atbranch = 'default'
1773 nodeids = []
1773 nodeids = []
1774 id = 0
1774 id = 0
1775 ui.progress(_('building'), id, unit=_('revisions'), total=total)
1775 ui.progress(_('building'), id, unit=_('revisions'), total=total)
1776 for type, data in dagparser.parsedag(text):
1776 for type, data in dagparser.parsedag(text):
1777 if type == 'n':
1777 if type == 'n':
1778 ui.note(('node %s\n' % str(data)))
1778 ui.note(('node %s\n' % str(data)))
1779 id, ps = data
1779 id, ps = data
1780
1780
1781 files = []
1781 files = []
1782 fctxs = {}
1782 fctxs = {}
1783
1783
1784 p2 = None
1784 p2 = None
1785 if mergeable_file:
1785 if mergeable_file:
1786 fn = "mf"
1786 fn = "mf"
1787 p1 = repo[ps[0]]
1787 p1 = repo[ps[0]]
1788 if len(ps) > 1:
1788 if len(ps) > 1:
1789 p2 = repo[ps[1]]
1789 p2 = repo[ps[1]]
1790 pa = p1.ancestor(p2)
1790 pa = p1.ancestor(p2)
1791 base, local, other = [x[fn].data() for x in (pa, p1,
1791 base, local, other = [x[fn].data() for x in (pa, p1,
1792 p2)]
1792 p2)]
1793 m3 = simplemerge.Merge3Text(base, local, other)
1793 m3 = simplemerge.Merge3Text(base, local, other)
1794 ml = [l.strip() for l in m3.merge_lines()]
1794 ml = [l.strip() for l in m3.merge_lines()]
1795 ml.append("")
1795 ml.append("")
1796 elif at > 0:
1796 elif at > 0:
1797 ml = p1[fn].data().split("\n")
1797 ml = p1[fn].data().split("\n")
1798 else:
1798 else:
1799 ml = initialmergedlines
1799 ml = initialmergedlines
1800 ml[id * linesperrev] += " r%i" % id
1800 ml[id * linesperrev] += " r%i" % id
1801 mergedtext = "\n".join(ml)
1801 mergedtext = "\n".join(ml)
1802 files.append(fn)
1802 files.append(fn)
1803 fctxs[fn] = context.memfilectx(repo, fn, mergedtext)
1803 fctxs[fn] = context.memfilectx(repo, fn, mergedtext)
1804
1804
1805 if overwritten_file:
1805 if overwritten_file:
1806 fn = "of"
1806 fn = "of"
1807 files.append(fn)
1807 files.append(fn)
1808 fctxs[fn] = context.memfilectx(repo, fn, "r%i\n" % id)
1808 fctxs[fn] = context.memfilectx(repo, fn, "r%i\n" % id)
1809
1809
1810 if new_file:
1810 if new_file:
1811 fn = "nf%i" % id
1811 fn = "nf%i" % id
1812 files.append(fn)
1812 files.append(fn)
1813 fctxs[fn] = context.memfilectx(repo, fn, "r%i\n" % id)
1813 fctxs[fn] = context.memfilectx(repo, fn, "r%i\n" % id)
1814 if len(ps) > 1:
1814 if len(ps) > 1:
1815 if not p2:
1815 if not p2:
1816 p2 = repo[ps[1]]
1816 p2 = repo[ps[1]]
1817 for fn in p2:
1817 for fn in p2:
1818 if fn.startswith("nf"):
1818 if fn.startswith("nf"):
1819 files.append(fn)
1819 files.append(fn)
1820 fctxs[fn] = p2[fn]
1820 fctxs[fn] = p2[fn]
1821
1821
1822 def fctxfn(repo, cx, path):
1822 def fctxfn(repo, cx, path):
1823 return fctxs.get(path)
1823 return fctxs.get(path)
1824
1824
1825 if len(ps) == 0 or ps[0] < 0:
1825 if len(ps) == 0 or ps[0] < 0:
1826 pars = [None, None]
1826 pars = [None, None]
1827 elif len(ps) == 1:
1827 elif len(ps) == 1:
1828 pars = [nodeids[ps[0]], None]
1828 pars = [nodeids[ps[0]], None]
1829 else:
1829 else:
1830 pars = [nodeids[p] for p in ps]
1830 pars = [nodeids[p] for p in ps]
1831 cx = context.memctx(repo, pars, "r%i" % id, files, fctxfn,
1831 cx = context.memctx(repo, pars, "r%i" % id, files, fctxfn,
1832 date=(id, 0),
1832 date=(id, 0),
1833 user="debugbuilddag",
1833 user="debugbuilddag",
1834 extra={'branch': atbranch})
1834 extra={'branch': atbranch})
1835 nodeid = repo.commitctx(cx)
1835 nodeid = repo.commitctx(cx)
1836 nodeids.append(nodeid)
1836 nodeids.append(nodeid)
1837 at = id
1837 at = id
1838 elif type == 'l':
1838 elif type == 'l':
1839 id, name = data
1839 id, name = data
1840 ui.note(('tag %s\n' % name))
1840 ui.note(('tag %s\n' % name))
1841 tags.append("%s %s\n" % (hex(repo.changelog.node(id)), name))
1841 tags.append("%s %s\n" % (hex(repo.changelog.node(id)), name))
1842 elif type == 'a':
1842 elif type == 'a':
1843 ui.note(('branch %s\n' % data))
1843 ui.note(('branch %s\n' % data))
1844 atbranch = data
1844 atbranch = data
1845 ui.progress(_('building'), id, unit=_('revisions'), total=total)
1845 ui.progress(_('building'), id, unit=_('revisions'), total=total)
1846 tr.close()
1846 tr.close()
1847
1847
1848 if tags:
1848 if tags:
1849 repo.vfs.write("localtags", "".join(tags))
1849 repo.vfs.write("localtags", "".join(tags))
1850 finally:
1850 finally:
1851 ui.progress(_('building'), None)
1851 ui.progress(_('building'), None)
1852 release(tr, lock)
1852 release(tr, lock)
1853
1853
1854 @command('debugbundle',
1854 @command('debugbundle',
1855 [('a', 'all', None, _('show all details'))],
1855 [('a', 'all', None, _('show all details'))],
1856 _('FILE'),
1856 _('FILE'),
1857 norepo=True)
1857 norepo=True)
1858 def debugbundle(ui, bundlepath, all=None, **opts):
1858 def debugbundle(ui, bundlepath, all=None, **opts):
1859 """lists the contents of a bundle"""
1859 """lists the contents of a bundle"""
1860 f = hg.openpath(ui, bundlepath)
1860 f = hg.openpath(ui, bundlepath)
1861 try:
1861 try:
1862 gen = exchange.readbundle(ui, f, bundlepath)
1862 gen = exchange.readbundle(ui, f, bundlepath)
1863 if isinstance(gen, bundle2.unbundle20):
1863 if isinstance(gen, bundle2.unbundle20):
1864 return _debugbundle2(ui, gen, all=all, **opts)
1864 return _debugbundle2(ui, gen, all=all, **opts)
1865 if all:
1865 if all:
1866 ui.write(("format: id, p1, p2, cset, delta base, len(delta)\n"))
1866 ui.write(("format: id, p1, p2, cset, delta base, len(delta)\n"))
1867
1867
1868 def showchunks(named):
1868 def showchunks(named):
1869 ui.write("\n%s\n" % named)
1869 ui.write("\n%s\n" % named)
1870 chain = None
1870 chain = None
1871 while True:
1871 while True:
1872 chunkdata = gen.deltachunk(chain)
1872 chunkdata = gen.deltachunk(chain)
1873 if not chunkdata:
1873 if not chunkdata:
1874 break
1874 break
1875 node = chunkdata['node']
1875 node = chunkdata['node']
1876 p1 = chunkdata['p1']
1876 p1 = chunkdata['p1']
1877 p2 = chunkdata['p2']
1877 p2 = chunkdata['p2']
1878 cs = chunkdata['cs']
1878 cs = chunkdata['cs']
1879 deltabase = chunkdata['deltabase']
1879 deltabase = chunkdata['deltabase']
1880 delta = chunkdata['delta']
1880 delta = chunkdata['delta']
1881 ui.write("%s %s %s %s %s %s\n" %
1881 ui.write("%s %s %s %s %s %s\n" %
1882 (hex(node), hex(p1), hex(p2),
1882 (hex(node), hex(p1), hex(p2),
1883 hex(cs), hex(deltabase), len(delta)))
1883 hex(cs), hex(deltabase), len(delta)))
1884 chain = node
1884 chain = node
1885
1885
1886 chunkdata = gen.changelogheader()
1886 chunkdata = gen.changelogheader()
1887 showchunks("changelog")
1887 showchunks("changelog")
1888 chunkdata = gen.manifestheader()
1888 chunkdata = gen.manifestheader()
1889 showchunks("manifest")
1889 showchunks("manifest")
1890 while True:
1890 while True:
1891 chunkdata = gen.filelogheader()
1891 chunkdata = gen.filelogheader()
1892 if not chunkdata:
1892 if not chunkdata:
1893 break
1893 break
1894 fname = chunkdata['filename']
1894 fname = chunkdata['filename']
1895 showchunks(fname)
1895 showchunks(fname)
1896 else:
1896 else:
1897 if isinstance(gen, bundle2.unbundle20):
1897 if isinstance(gen, bundle2.unbundle20):
1898 raise util.Abort(_('use debugbundle2 for this file'))
1898 raise util.Abort(_('use debugbundle2 for this file'))
1899 chunkdata = gen.changelogheader()
1899 chunkdata = gen.changelogheader()
1900 chain = None
1900 chain = None
1901 while True:
1901 while True:
1902 chunkdata = gen.deltachunk(chain)
1902 chunkdata = gen.deltachunk(chain)
1903 if not chunkdata:
1903 if not chunkdata:
1904 break
1904 break
1905 node = chunkdata['node']
1905 node = chunkdata['node']
1906 ui.write("%s\n" % hex(node))
1906 ui.write("%s\n" % hex(node))
1907 chain = node
1907 chain = node
1908 finally:
1908 finally:
1909 f.close()
1909 f.close()
1910
1910
1911 def _debugbundle2(ui, gen, **opts):
1911 def _debugbundle2(ui, gen, **opts):
1912 """lists the contents of a bundle2"""
1912 """lists the contents of a bundle2"""
1913 if not isinstance(gen, bundle2.unbundle20):
1913 if not isinstance(gen, bundle2.unbundle20):
1914 raise util.Abort(_('not a bundle2 file'))
1914 raise util.Abort(_('not a bundle2 file'))
1915 ui.write(('Stream params: %s\n' % repr(gen.params)))
1915 ui.write(('Stream params: %s\n' % repr(gen.params)))
1916 for part in gen.iterparts():
1916 for part in gen.iterparts():
1917 ui.write('%s -- %r\n' % (part.type, repr(part.params)))
1917 ui.write('%s -- %r\n' % (part.type, repr(part.params)))
1918 if part.type == 'changegroup':
1918 if part.type == 'changegroup':
1919 version = part.params.get('version', '01')
1919 version = part.params.get('version', '01')
1920 cg = changegroup.packermap[version][1](part, 'UN')
1920 cg = changegroup.packermap[version][1](part, 'UN')
1921 chunkdata = cg.changelogheader()
1921 chunkdata = cg.changelogheader()
1922 chain = None
1922 chain = None
1923 while True:
1923 while True:
1924 chunkdata = cg.deltachunk(chain)
1924 chunkdata = cg.deltachunk(chain)
1925 if not chunkdata:
1925 if not chunkdata:
1926 break
1926 break
1927 node = chunkdata['node']
1927 node = chunkdata['node']
1928 ui.write(" %s\n" % hex(node))
1928 ui.write(" %s\n" % hex(node))
1929 chain = node
1929 chain = node
1930
1930
1931 @command('debugcheckstate', [], '')
1931 @command('debugcheckstate', [], '')
1932 def debugcheckstate(ui, repo):
1932 def debugcheckstate(ui, repo):
1933 """validate the correctness of the current dirstate"""
1933 """validate the correctness of the current dirstate"""
1934 parent1, parent2 = repo.dirstate.parents()
1934 parent1, parent2 = repo.dirstate.parents()
1935 m1 = repo[parent1].manifest()
1935 m1 = repo[parent1].manifest()
1936 m2 = repo[parent2].manifest()
1936 m2 = repo[parent2].manifest()
1937 errors = 0
1937 errors = 0
1938 for f in repo.dirstate:
1938 for f in repo.dirstate:
1939 state = repo.dirstate[f]
1939 state = repo.dirstate[f]
1940 if state in "nr" and f not in m1:
1940 if state in "nr" and f not in m1:
1941 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1941 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1942 errors += 1
1942 errors += 1
1943 if state in "a" and f in m1:
1943 if state in "a" and f in m1:
1944 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1944 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1945 errors += 1
1945 errors += 1
1946 if state in "m" and f not in m1 and f not in m2:
1946 if state in "m" and f not in m1 and f not in m2:
1947 ui.warn(_("%s in state %s, but not in either manifest\n") %
1947 ui.warn(_("%s in state %s, but not in either manifest\n") %
1948 (f, state))
1948 (f, state))
1949 errors += 1
1949 errors += 1
1950 for f in m1:
1950 for f in m1:
1951 state = repo.dirstate[f]
1951 state = repo.dirstate[f]
1952 if state not in "nrm":
1952 if state not in "nrm":
1953 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1953 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1954 errors += 1
1954 errors += 1
1955 if errors:
1955 if errors:
1956 error = _(".hg/dirstate inconsistent with current parent's manifest")
1956 error = _(".hg/dirstate inconsistent with current parent's manifest")
1957 raise util.Abort(error)
1957 raise util.Abort(error)
1958
1958
1959 @command('debugcommands', [], _('[COMMAND]'), norepo=True)
1959 @command('debugcommands', [], _('[COMMAND]'), norepo=True)
1960 def debugcommands(ui, cmd='', *args):
1960 def debugcommands(ui, cmd='', *args):
1961 """list all available commands and options"""
1961 """list all available commands and options"""
1962 for cmd, vals in sorted(table.iteritems()):
1962 for cmd, vals in sorted(table.iteritems()):
1963 cmd = cmd.split('|')[0].strip('^')
1963 cmd = cmd.split('|')[0].strip('^')
1964 opts = ', '.join([i[1] for i in vals[1]])
1964 opts = ', '.join([i[1] for i in vals[1]])
1965 ui.write('%s: %s\n' % (cmd, opts))
1965 ui.write('%s: %s\n' % (cmd, opts))
1966
1966
1967 @command('debugcomplete',
1967 @command('debugcomplete',
1968 [('o', 'options', None, _('show the command options'))],
1968 [('o', 'options', None, _('show the command options'))],
1969 _('[-o] CMD'),
1969 _('[-o] CMD'),
1970 norepo=True)
1970 norepo=True)
1971 def debugcomplete(ui, cmd='', **opts):
1971 def debugcomplete(ui, cmd='', **opts):
1972 """returns the completion list associated with the given command"""
1972 """returns the completion list associated with the given command"""
1973
1973
1974 if opts.get('options'):
1974 if opts.get('options'):
1975 options = []
1975 options = []
1976 otables = [globalopts]
1976 otables = [globalopts]
1977 if cmd:
1977 if cmd:
1978 aliases, entry = cmdutil.findcmd(cmd, table, False)
1978 aliases, entry = cmdutil.findcmd(cmd, table, False)
1979 otables.append(entry[1])
1979 otables.append(entry[1])
1980 for t in otables:
1980 for t in otables:
1981 for o in t:
1981 for o in t:
1982 if "(DEPRECATED)" in o[3]:
1982 if "(DEPRECATED)" in o[3]:
1983 continue
1983 continue
1984 if o[0]:
1984 if o[0]:
1985 options.append('-%s' % o[0])
1985 options.append('-%s' % o[0])
1986 options.append('--%s' % o[1])
1986 options.append('--%s' % o[1])
1987 ui.write("%s\n" % "\n".join(options))
1987 ui.write("%s\n" % "\n".join(options))
1988 return
1988 return
1989
1989
1990 cmdlist, unused_allcmds = cmdutil.findpossible(cmd, table)
1990 cmdlist, unused_allcmds = cmdutil.findpossible(cmd, table)
1991 if ui.verbose:
1991 if ui.verbose:
1992 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
1992 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
1993 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
1993 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
1994
1994
1995 @command('debugdag',
1995 @command('debugdag',
1996 [('t', 'tags', None, _('use tags as labels')),
1996 [('t', 'tags', None, _('use tags as labels')),
1997 ('b', 'branches', None, _('annotate with branch names')),
1997 ('b', 'branches', None, _('annotate with branch names')),
1998 ('', 'dots', None, _('use dots for runs')),
1998 ('', 'dots', None, _('use dots for runs')),
1999 ('s', 'spaces', None, _('separate elements by spaces'))],
1999 ('s', 'spaces', None, _('separate elements by spaces'))],
2000 _('[OPTION]... [FILE [REV]...]'),
2000 _('[OPTION]... [FILE [REV]...]'),
2001 optionalrepo=True)
2001 optionalrepo=True)
2002 def debugdag(ui, repo, file_=None, *revs, **opts):
2002 def debugdag(ui, repo, file_=None, *revs, **opts):
2003 """format the changelog or an index DAG as a concise textual description
2003 """format the changelog or an index DAG as a concise textual description
2004
2004
2005 If you pass a revlog index, the revlog's DAG is emitted. If you list
2005 If you pass a revlog index, the revlog's DAG is emitted. If you list
2006 revision numbers, they get labeled in the output as rN.
2006 revision numbers, they get labeled in the output as rN.
2007
2007
2008 Otherwise, the changelog DAG of the current repo is emitted.
2008 Otherwise, the changelog DAG of the current repo is emitted.
2009 """
2009 """
2010 spaces = opts.get('spaces')
2010 spaces = opts.get('spaces')
2011 dots = opts.get('dots')
2011 dots = opts.get('dots')
2012 if file_:
2012 if file_:
2013 rlog = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
2013 rlog = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
2014 revs = set((int(r) for r in revs))
2014 revs = set((int(r) for r in revs))
2015 def events():
2015 def events():
2016 for r in rlog:
2016 for r in rlog:
2017 yield 'n', (r, list(p for p in rlog.parentrevs(r)
2017 yield 'n', (r, list(p for p in rlog.parentrevs(r)
2018 if p != -1))
2018 if p != -1))
2019 if r in revs:
2019 if r in revs:
2020 yield 'l', (r, "r%i" % r)
2020 yield 'l', (r, "r%i" % r)
2021 elif repo:
2021 elif repo:
2022 cl = repo.changelog
2022 cl = repo.changelog
2023 tags = opts.get('tags')
2023 tags = opts.get('tags')
2024 branches = opts.get('branches')
2024 branches = opts.get('branches')
2025 if tags:
2025 if tags:
2026 labels = {}
2026 labels = {}
2027 for l, n in repo.tags().items():
2027 for l, n in repo.tags().items():
2028 labels.setdefault(cl.rev(n), []).append(l)
2028 labels.setdefault(cl.rev(n), []).append(l)
2029 def events():
2029 def events():
2030 b = "default"
2030 b = "default"
2031 for r in cl:
2031 for r in cl:
2032 if branches:
2032 if branches:
2033 newb = cl.read(cl.node(r))[5]['branch']
2033 newb = cl.read(cl.node(r))[5]['branch']
2034 if newb != b:
2034 if newb != b:
2035 yield 'a', newb
2035 yield 'a', newb
2036 b = newb
2036 b = newb
2037 yield 'n', (r, list(p for p in cl.parentrevs(r)
2037 yield 'n', (r, list(p for p in cl.parentrevs(r)
2038 if p != -1))
2038 if p != -1))
2039 if tags:
2039 if tags:
2040 ls = labels.get(r)
2040 ls = labels.get(r)
2041 if ls:
2041 if ls:
2042 for l in ls:
2042 for l in ls:
2043 yield 'l', (r, l)
2043 yield 'l', (r, l)
2044 else:
2044 else:
2045 raise util.Abort(_('need repo for changelog dag'))
2045 raise util.Abort(_('need repo for changelog dag'))
2046
2046
2047 for line in dagparser.dagtextlines(events(),
2047 for line in dagparser.dagtextlines(events(),
2048 addspaces=spaces,
2048 addspaces=spaces,
2049 wraplabels=True,
2049 wraplabels=True,
2050 wrapannotations=True,
2050 wrapannotations=True,
2051 wrapnonlinear=dots,
2051 wrapnonlinear=dots,
2052 usedots=dots,
2052 usedots=dots,
2053 maxlinewidth=70):
2053 maxlinewidth=70):
2054 ui.write(line)
2054 ui.write(line)
2055 ui.write("\n")
2055 ui.write("\n")
2056
2056
2057 @command('debugdata',
2057 @command('debugdata',
2058 [('c', 'changelog', False, _('open changelog')),
2058 [('c', 'changelog', False, _('open changelog')),
2059 ('m', 'manifest', False, _('open manifest')),
2059 ('m', 'manifest', False, _('open manifest')),
2060 ('', 'dir', False, _('open directory manifest'))],
2060 ('', 'dir', False, _('open directory manifest'))],
2061 _('-c|-m|FILE REV'))
2061 _('-c|-m|FILE REV'))
2062 def debugdata(ui, repo, file_, rev=None, **opts):
2062 def debugdata(ui, repo, file_, rev=None, **opts):
2063 """dump the contents of a data file revision"""
2063 """dump the contents of a data file revision"""
2064 if opts.get('changelog') or opts.get('manifest'):
2064 if opts.get('changelog') or opts.get('manifest'):
2065 file_, rev = None, file_
2065 file_, rev = None, file_
2066 elif rev is None:
2066 elif rev is None:
2067 raise error.CommandError('debugdata', _('invalid arguments'))
2067 raise error.CommandError('debugdata', _('invalid arguments'))
2068 r = cmdutil.openrevlog(repo, 'debugdata', file_, opts)
2068 r = cmdutil.openrevlog(repo, 'debugdata', file_, opts)
2069 try:
2069 try:
2070 ui.write(r.revision(r.lookup(rev)))
2070 ui.write(r.revision(r.lookup(rev)))
2071 except KeyError:
2071 except KeyError:
2072 raise util.Abort(_('invalid revision identifier %s') % rev)
2072 raise util.Abort(_('invalid revision identifier %s') % rev)
2073
2073
2074 @command('debugdate',
2074 @command('debugdate',
2075 [('e', 'extended', None, _('try extended date formats'))],
2075 [('e', 'extended', None, _('try extended date formats'))],
2076 _('[-e] DATE [RANGE]'),
2076 _('[-e] DATE [RANGE]'),
2077 norepo=True, optionalrepo=True)
2077 norepo=True, optionalrepo=True)
2078 def debugdate(ui, date, range=None, **opts):
2078 def debugdate(ui, date, range=None, **opts):
2079 """parse and display a date"""
2079 """parse and display a date"""
2080 if opts["extended"]:
2080 if opts["extended"]:
2081 d = util.parsedate(date, util.extendeddateformats)
2081 d = util.parsedate(date, util.extendeddateformats)
2082 else:
2082 else:
2083 d = util.parsedate(date)
2083 d = util.parsedate(date)
2084 ui.write(("internal: %s %s\n") % d)
2084 ui.write(("internal: %s %s\n") % d)
2085 ui.write(("standard: %s\n") % util.datestr(d))
2085 ui.write(("standard: %s\n") % util.datestr(d))
2086 if range:
2086 if range:
2087 m = util.matchdate(range)
2087 m = util.matchdate(range)
2088 ui.write(("match: %s\n") % m(d[0]))
2088 ui.write(("match: %s\n") % m(d[0]))
2089
2089
2090 @command('debugdiscovery',
2090 @command('debugdiscovery',
2091 [('', 'old', None, _('use old-style discovery')),
2091 [('', 'old', None, _('use old-style discovery')),
2092 ('', 'nonheads', None,
2092 ('', 'nonheads', None,
2093 _('use old-style discovery with non-heads included')),
2093 _('use old-style discovery with non-heads included')),
2094 ] + remoteopts,
2094 ] + remoteopts,
2095 _('[-l REV] [-r REV] [-b BRANCH]... [OTHER]'))
2095 _('[-l REV] [-r REV] [-b BRANCH]... [OTHER]'))
2096 def debugdiscovery(ui, repo, remoteurl="default", **opts):
2096 def debugdiscovery(ui, repo, remoteurl="default", **opts):
2097 """runs the changeset discovery protocol in isolation"""
2097 """runs the changeset discovery protocol in isolation"""
2098 remoteurl, branches = hg.parseurl(ui.expandpath(remoteurl),
2098 remoteurl, branches = hg.parseurl(ui.expandpath(remoteurl),
2099 opts.get('branch'))
2099 opts.get('branch'))
2100 remote = hg.peer(repo, opts, remoteurl)
2100 remote = hg.peer(repo, opts, remoteurl)
2101 ui.status(_('comparing with %s\n') % util.hidepassword(remoteurl))
2101 ui.status(_('comparing with %s\n') % util.hidepassword(remoteurl))
2102
2102
2103 # make sure tests are repeatable
2103 # make sure tests are repeatable
2104 random.seed(12323)
2104 random.seed(12323)
2105
2105
2106 def doit(localheads, remoteheads, remote=remote):
2106 def doit(localheads, remoteheads, remote=remote):
2107 if opts.get('old'):
2107 if opts.get('old'):
2108 if localheads:
2108 if localheads:
2109 raise util.Abort('cannot use localheads with old style '
2109 raise util.Abort('cannot use localheads with old style '
2110 'discovery')
2110 'discovery')
2111 if not util.safehasattr(remote, 'branches'):
2111 if not util.safehasattr(remote, 'branches'):
2112 # enable in-client legacy support
2112 # enable in-client legacy support
2113 remote = localrepo.locallegacypeer(remote.local())
2113 remote = localrepo.locallegacypeer(remote.local())
2114 common, _in, hds = treediscovery.findcommonincoming(repo, remote,
2114 common, _in, hds = treediscovery.findcommonincoming(repo, remote,
2115 force=True)
2115 force=True)
2116 common = set(common)
2116 common = set(common)
2117 if not opts.get('nonheads'):
2117 if not opts.get('nonheads'):
2118 ui.write(("unpruned common: %s\n") %
2118 ui.write(("unpruned common: %s\n") %
2119 " ".join(sorted(short(n) for n in common)))
2119 " ".join(sorted(short(n) for n in common)))
2120 dag = dagutil.revlogdag(repo.changelog)
2120 dag = dagutil.revlogdag(repo.changelog)
2121 all = dag.ancestorset(dag.internalizeall(common))
2121 all = dag.ancestorset(dag.internalizeall(common))
2122 common = dag.externalizeall(dag.headsetofconnecteds(all))
2122 common = dag.externalizeall(dag.headsetofconnecteds(all))
2123 else:
2123 else:
2124 common, any, hds = setdiscovery.findcommonheads(ui, repo, remote)
2124 common, any, hds = setdiscovery.findcommonheads(ui, repo, remote)
2125 common = set(common)
2125 common = set(common)
2126 rheads = set(hds)
2126 rheads = set(hds)
2127 lheads = set(repo.heads())
2127 lheads = set(repo.heads())
2128 ui.write(("common heads: %s\n") %
2128 ui.write(("common heads: %s\n") %
2129 " ".join(sorted(short(n) for n in common)))
2129 " ".join(sorted(short(n) for n in common)))
2130 if lheads <= common:
2130 if lheads <= common:
2131 ui.write(("local is subset\n"))
2131 ui.write(("local is subset\n"))
2132 elif rheads <= common:
2132 elif rheads <= common:
2133 ui.write(("remote is subset\n"))
2133 ui.write(("remote is subset\n"))
2134
2134
2135 serverlogs = opts.get('serverlog')
2135 serverlogs = opts.get('serverlog')
2136 if serverlogs:
2136 if serverlogs:
2137 for filename in serverlogs:
2137 for filename in serverlogs:
2138 logfile = open(filename, 'r')
2138 logfile = open(filename, 'r')
2139 try:
2139 try:
2140 line = logfile.readline()
2140 line = logfile.readline()
2141 while line:
2141 while line:
2142 parts = line.strip().split(';')
2142 parts = line.strip().split(';')
2143 op = parts[1]
2143 op = parts[1]
2144 if op == 'cg':
2144 if op == 'cg':
2145 pass
2145 pass
2146 elif op == 'cgss':
2146 elif op == 'cgss':
2147 doit(parts[2].split(' '), parts[3].split(' '))
2147 doit(parts[2].split(' '), parts[3].split(' '))
2148 elif op == 'unb':
2148 elif op == 'unb':
2149 doit(parts[3].split(' '), parts[2].split(' '))
2149 doit(parts[3].split(' '), parts[2].split(' '))
2150 line = logfile.readline()
2150 line = logfile.readline()
2151 finally:
2151 finally:
2152 logfile.close()
2152 logfile.close()
2153
2153
2154 else:
2154 else:
2155 remoterevs, _checkout = hg.addbranchrevs(repo, remote, branches,
2155 remoterevs, _checkout = hg.addbranchrevs(repo, remote, branches,
2156 opts.get('remote_head'))
2156 opts.get('remote_head'))
2157 localrevs = opts.get('local_head')
2157 localrevs = opts.get('local_head')
2158 doit(localrevs, remoterevs)
2158 doit(localrevs, remoterevs)
2159
2159
2160 @command('debugfileset',
2160 @command('debugfileset',
2161 [('r', 'rev', '', _('apply the filespec on this revision'), _('REV'))],
2161 [('r', 'rev', '', _('apply the filespec on this revision'), _('REV'))],
2162 _('[-r REV] FILESPEC'))
2162 _('[-r REV] FILESPEC'))
2163 def debugfileset(ui, repo, expr, **opts):
2163 def debugfileset(ui, repo, expr, **opts):
2164 '''parse and apply a fileset specification'''
2164 '''parse and apply a fileset specification'''
2165 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
2165 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
2166 if ui.verbose:
2166 if ui.verbose:
2167 tree = fileset.parse(expr)[0]
2167 tree = fileset.parse(expr)[0]
2168 ui.note(tree, "\n")
2168 ui.note(tree, "\n")
2169
2169
2170 for f in ctx.getfileset(expr):
2170 for f in ctx.getfileset(expr):
2171 ui.write("%s\n" % f)
2171 ui.write("%s\n" % f)
2172
2172
2173 @command('debugfsinfo', [], _('[PATH]'), norepo=True)
2173 @command('debugfsinfo', [], _('[PATH]'), norepo=True)
2174 def debugfsinfo(ui, path="."):
2174 def debugfsinfo(ui, path="."):
2175 """show information detected about current filesystem"""
2175 """show information detected about current filesystem"""
2176 util.writefile('.debugfsinfo', '')
2176 util.writefile('.debugfsinfo', '')
2177 ui.write(('exec: %s\n') % (util.checkexec(path) and 'yes' or 'no'))
2177 ui.write(('exec: %s\n') % (util.checkexec(path) and 'yes' or 'no'))
2178 ui.write(('symlink: %s\n') % (util.checklink(path) and 'yes' or 'no'))
2178 ui.write(('symlink: %s\n') % (util.checklink(path) and 'yes' or 'no'))
2179 ui.write(('hardlink: %s\n') % (util.checknlink(path) and 'yes' or 'no'))
2179 ui.write(('hardlink: %s\n') % (util.checknlink(path) and 'yes' or 'no'))
2180 ui.write(('case-sensitive: %s\n') % (util.checkcase('.debugfsinfo')
2180 ui.write(('case-sensitive: %s\n') % (util.checkcase('.debugfsinfo')
2181 and 'yes' or 'no'))
2181 and 'yes' or 'no'))
2182 os.unlink('.debugfsinfo')
2182 os.unlink('.debugfsinfo')
2183
2183
2184 @command('debuggetbundle',
2184 @command('debuggetbundle',
2185 [('H', 'head', [], _('id of head node'), _('ID')),
2185 [('H', 'head', [], _('id of head node'), _('ID')),
2186 ('C', 'common', [], _('id of common node'), _('ID')),
2186 ('C', 'common', [], _('id of common node'), _('ID')),
2187 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE'))],
2187 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE'))],
2188 _('REPO FILE [-H|-C ID]...'),
2188 _('REPO FILE [-H|-C ID]...'),
2189 norepo=True)
2189 norepo=True)
2190 def debuggetbundle(ui, repopath, bundlepath, head=None, common=None, **opts):
2190 def debuggetbundle(ui, repopath, bundlepath, head=None, common=None, **opts):
2191 """retrieves a bundle from a repo
2191 """retrieves a bundle from a repo
2192
2192
2193 Every ID must be a full-length hex node id string. Saves the bundle to the
2193 Every ID must be a full-length hex node id string. Saves the bundle to the
2194 given file.
2194 given file.
2195 """
2195 """
2196 repo = hg.peer(ui, opts, repopath)
2196 repo = hg.peer(ui, opts, repopath)
2197 if not repo.capable('getbundle'):
2197 if not repo.capable('getbundle'):
2198 raise util.Abort("getbundle() not supported by target repository")
2198 raise util.Abort("getbundle() not supported by target repository")
2199 args = {}
2199 args = {}
2200 if common:
2200 if common:
2201 args['common'] = [bin(s) for s in common]
2201 args['common'] = [bin(s) for s in common]
2202 if head:
2202 if head:
2203 args['heads'] = [bin(s) for s in head]
2203 args['heads'] = [bin(s) for s in head]
2204 # TODO: get desired bundlecaps from command line.
2204 # TODO: get desired bundlecaps from command line.
2205 args['bundlecaps'] = None
2205 args['bundlecaps'] = None
2206 bundle = repo.getbundle('debug', **args)
2206 bundle = repo.getbundle('debug', **args)
2207
2207
2208 bundletype = opts.get('type', 'bzip2').lower()
2208 bundletype = opts.get('type', 'bzip2').lower()
2209 btypes = {'none': 'HG10UN',
2209 btypes = {'none': 'HG10UN',
2210 'bzip2': 'HG10BZ',
2210 'bzip2': 'HG10BZ',
2211 'gzip': 'HG10GZ',
2211 'gzip': 'HG10GZ',
2212 'bundle2': 'HG20'}
2212 'bundle2': 'HG20'}
2213 bundletype = btypes.get(bundletype)
2213 bundletype = btypes.get(bundletype)
2214 if bundletype not in changegroup.bundletypes:
2214 if bundletype not in changegroup.bundletypes:
2215 raise util.Abort(_('unknown bundle type specified with --type'))
2215 raise util.Abort(_('unknown bundle type specified with --type'))
2216 changegroup.writebundle(ui, bundle, bundlepath, bundletype)
2216 changegroup.writebundle(ui, bundle, bundlepath, bundletype)
2217
2217
2218 @command('debugignore', [], '')
2218 @command('debugignore', [], '')
2219 def debugignore(ui, repo, *values, **opts):
2219 def debugignore(ui, repo, *values, **opts):
2220 """display the combined ignore pattern"""
2220 """display the combined ignore pattern"""
2221 ignore = repo.dirstate._ignore
2221 ignore = repo.dirstate._ignore
2222 includepat = getattr(ignore, 'includepat', None)
2222 includepat = getattr(ignore, 'includepat', None)
2223 if includepat is not None:
2223 if includepat is not None:
2224 ui.write("%s\n" % includepat)
2224 ui.write("%s\n" % includepat)
2225 else:
2225 else:
2226 raise util.Abort(_("no ignore patterns found"))
2226 raise util.Abort(_("no ignore patterns found"))
2227
2227
2228 @command('debugindex',
2228 @command('debugindex',
2229 [('c', 'changelog', False, _('open changelog')),
2229 [('c', 'changelog', False, _('open changelog')),
2230 ('m', 'manifest', False, _('open manifest')),
2230 ('m', 'manifest', False, _('open manifest')),
2231 ('', 'dir', False, _('open directory manifest')),
2231 ('', 'dir', False, _('open directory manifest')),
2232 ('f', 'format', 0, _('revlog format'), _('FORMAT'))],
2232 ('f', 'format', 0, _('revlog format'), _('FORMAT'))],
2233 _('[-f FORMAT] -c|-m|FILE'),
2233 _('[-f FORMAT] -c|-m|FILE'),
2234 optionalrepo=True)
2234 optionalrepo=True)
2235 def debugindex(ui, repo, file_=None, **opts):
2235 def debugindex(ui, repo, file_=None, **opts):
2236 """dump the contents of an index file"""
2236 """dump the contents of an index file"""
2237 r = cmdutil.openrevlog(repo, 'debugindex', file_, opts)
2237 r = cmdutil.openrevlog(repo, 'debugindex', file_, opts)
2238 format = opts.get('format', 0)
2238 format = opts.get('format', 0)
2239 if format not in (0, 1):
2239 if format not in (0, 1):
2240 raise util.Abort(_("unknown format %d") % format)
2240 raise util.Abort(_("unknown format %d") % format)
2241
2241
2242 generaldelta = r.version & revlog.REVLOGGENERALDELTA
2242 generaldelta = r.version & revlog.REVLOGGENERALDELTA
2243 if generaldelta:
2243 if generaldelta:
2244 basehdr = ' delta'
2244 basehdr = ' delta'
2245 else:
2245 else:
2246 basehdr = ' base'
2246 basehdr = ' base'
2247
2247
2248 if ui.debugflag:
2248 if ui.debugflag:
2249 shortfn = hex
2249 shortfn = hex
2250 else:
2250 else:
2251 shortfn = short
2251 shortfn = short
2252
2252
2253 # There might not be anything in r, so have a sane default
2253 # There might not be anything in r, so have a sane default
2254 idlen = 12
2254 idlen = 12
2255 for i in r:
2255 for i in r:
2256 idlen = len(shortfn(r.node(i)))
2256 idlen = len(shortfn(r.node(i)))
2257 break
2257 break
2258
2258
2259 if format == 0:
2259 if format == 0:
2260 ui.write(" rev offset length " + basehdr + " linkrev"
2260 ui.write(" rev offset length " + basehdr + " linkrev"
2261 " %s %s p2\n" % ("nodeid".ljust(idlen), "p1".ljust(idlen)))
2261 " %s %s p2\n" % ("nodeid".ljust(idlen), "p1".ljust(idlen)))
2262 elif format == 1:
2262 elif format == 1:
2263 ui.write(" rev flag offset length"
2263 ui.write(" rev flag offset length"
2264 " size " + basehdr + " link p1 p2"
2264 " size " + basehdr + " link p1 p2"
2265 " %s\n" % "nodeid".rjust(idlen))
2265 " %s\n" % "nodeid".rjust(idlen))
2266
2266
2267 for i in r:
2267 for i in r:
2268 node = r.node(i)
2268 node = r.node(i)
2269 if generaldelta:
2269 if generaldelta:
2270 base = r.deltaparent(i)
2270 base = r.deltaparent(i)
2271 else:
2271 else:
2272 base = r.chainbase(i)
2272 base = r.chainbase(i)
2273 if format == 0:
2273 if format == 0:
2274 try:
2274 try:
2275 pp = r.parents(node)
2275 pp = r.parents(node)
2276 except Exception:
2276 except Exception:
2277 pp = [nullid, nullid]
2277 pp = [nullid, nullid]
2278 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
2278 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
2279 i, r.start(i), r.length(i), base, r.linkrev(i),
2279 i, r.start(i), r.length(i), base, r.linkrev(i),
2280 shortfn(node), shortfn(pp[0]), shortfn(pp[1])))
2280 shortfn(node), shortfn(pp[0]), shortfn(pp[1])))
2281 elif format == 1:
2281 elif format == 1:
2282 pr = r.parentrevs(i)
2282 pr = r.parentrevs(i)
2283 ui.write("% 6d %04x % 8d % 8d % 8d % 6d % 6d % 6d % 6d %s\n" % (
2283 ui.write("% 6d %04x % 8d % 8d % 8d % 6d % 6d % 6d % 6d %s\n" % (
2284 i, r.flags(i), r.start(i), r.length(i), r.rawsize(i),
2284 i, r.flags(i), r.start(i), r.length(i), r.rawsize(i),
2285 base, r.linkrev(i), pr[0], pr[1], shortfn(node)))
2285 base, r.linkrev(i), pr[0], pr[1], shortfn(node)))
2286
2286
2287 @command('debugindexdot', [], _('FILE'), optionalrepo=True)
2287 @command('debugindexdot', [], _('FILE'), optionalrepo=True)
2288 def debugindexdot(ui, repo, file_):
2288 def debugindexdot(ui, repo, file_):
2289 """dump an index DAG as a graphviz dot file"""
2289 """dump an index DAG as a graphviz dot file"""
2290 r = None
2290 r = None
2291 if repo:
2291 if repo:
2292 filelog = repo.file(file_)
2292 filelog = repo.file(file_)
2293 if len(filelog):
2293 if len(filelog):
2294 r = filelog
2294 r = filelog
2295 if not r:
2295 if not r:
2296 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
2296 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
2297 ui.write(("digraph G {\n"))
2297 ui.write(("digraph G {\n"))
2298 for i in r:
2298 for i in r:
2299 node = r.node(i)
2299 node = r.node(i)
2300 pp = r.parents(node)
2300 pp = r.parents(node)
2301 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
2301 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
2302 if pp[1] != nullid:
2302 if pp[1] != nullid:
2303 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
2303 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
2304 ui.write("}\n")
2304 ui.write("}\n")
2305
2305
2306 @command('debuginstall', [], '', norepo=True)
2306 @command('debuginstall', [], '', norepo=True)
2307 def debuginstall(ui):
2307 def debuginstall(ui):
2308 '''test Mercurial installation
2308 '''test Mercurial installation
2309
2309
2310 Returns 0 on success.
2310 Returns 0 on success.
2311 '''
2311 '''
2312
2312
2313 def writetemp(contents):
2313 def writetemp(contents):
2314 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
2314 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
2315 f = os.fdopen(fd, "wb")
2315 f = os.fdopen(fd, "wb")
2316 f.write(contents)
2316 f.write(contents)
2317 f.close()
2317 f.close()
2318 return name
2318 return name
2319
2319
2320 problems = 0
2320 problems = 0
2321
2321
2322 # encoding
2322 # encoding
2323 ui.status(_("checking encoding (%s)...\n") % encoding.encoding)
2323 ui.status(_("checking encoding (%s)...\n") % encoding.encoding)
2324 try:
2324 try:
2325 encoding.fromlocal("test")
2325 encoding.fromlocal("test")
2326 except util.Abort, inst:
2326 except util.Abort, inst:
2327 ui.write(" %s\n" % inst)
2327 ui.write(" %s\n" % inst)
2328 ui.write(_(" (check that your locale is properly set)\n"))
2328 ui.write(_(" (check that your locale is properly set)\n"))
2329 problems += 1
2329 problems += 1
2330
2330
2331 # Python
2331 # Python
2332 ui.status(_("checking Python executable (%s)\n") % sys.executable)
2332 ui.status(_("checking Python executable (%s)\n") % sys.executable)
2333 ui.status(_("checking Python version (%s)\n")
2333 ui.status(_("checking Python version (%s)\n")
2334 % ("%s.%s.%s" % sys.version_info[:3]))
2334 % ("%s.%s.%s" % sys.version_info[:3]))
2335 ui.status(_("checking Python lib (%s)...\n")
2335 ui.status(_("checking Python lib (%s)...\n")
2336 % os.path.dirname(os.__file__))
2336 % os.path.dirname(os.__file__))
2337
2337
2338 # compiled modules
2338 # compiled modules
2339 ui.status(_("checking installed modules (%s)...\n")
2339 ui.status(_("checking installed modules (%s)...\n")
2340 % os.path.dirname(__file__))
2340 % os.path.dirname(__file__))
2341 try:
2341 try:
2342 import bdiff, mpatch, base85, osutil
2342 import bdiff, mpatch, base85, osutil
2343 dir(bdiff), dir(mpatch), dir(base85), dir(osutil) # quiet pyflakes
2343 dir(bdiff), dir(mpatch), dir(base85), dir(osutil) # quiet pyflakes
2344 except Exception, inst:
2344 except Exception, inst:
2345 ui.write(" %s\n" % inst)
2345 ui.write(" %s\n" % inst)
2346 ui.write(_(" One or more extensions could not be found"))
2346 ui.write(_(" One or more extensions could not be found"))
2347 ui.write(_(" (check that you compiled the extensions)\n"))
2347 ui.write(_(" (check that you compiled the extensions)\n"))
2348 problems += 1
2348 problems += 1
2349
2349
2350 # templates
2350 # templates
2351 import templater
2351 import templater
2352 p = templater.templatepaths()
2352 p = templater.templatepaths()
2353 ui.status(_("checking templates (%s)...\n") % ' '.join(p))
2353 ui.status(_("checking templates (%s)...\n") % ' '.join(p))
2354 if p:
2354 if p:
2355 m = templater.templatepath("map-cmdline.default")
2355 m = templater.templatepath("map-cmdline.default")
2356 if m:
2356 if m:
2357 # template found, check if it is working
2357 # template found, check if it is working
2358 try:
2358 try:
2359 templater.templater(m)
2359 templater.templater(m)
2360 except Exception, inst:
2360 except Exception, inst:
2361 ui.write(" %s\n" % inst)
2361 ui.write(" %s\n" % inst)
2362 p = None
2362 p = None
2363 else:
2363 else:
2364 ui.write(_(" template 'default' not found\n"))
2364 ui.write(_(" template 'default' not found\n"))
2365 p = None
2365 p = None
2366 else:
2366 else:
2367 ui.write(_(" no template directories found\n"))
2367 ui.write(_(" no template directories found\n"))
2368 if not p:
2368 if not p:
2369 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
2369 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
2370 problems += 1
2370 problems += 1
2371
2371
2372 # editor
2372 # editor
2373 ui.status(_("checking commit editor...\n"))
2373 ui.status(_("checking commit editor...\n"))
2374 editor = ui.geteditor()
2374 editor = ui.geteditor()
2375 editor = util.expandpath(editor)
2375 editor = util.expandpath(editor)
2376 cmdpath = util.findexe(shlex.split(editor)[0])
2376 cmdpath = util.findexe(shlex.split(editor)[0])
2377 if not cmdpath:
2377 if not cmdpath:
2378 if editor == 'vi':
2378 if editor == 'vi':
2379 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
2379 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
2380 ui.write(_(" (specify a commit editor in your configuration"
2380 ui.write(_(" (specify a commit editor in your configuration"
2381 " file)\n"))
2381 " file)\n"))
2382 else:
2382 else:
2383 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
2383 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
2384 ui.write(_(" (specify a commit editor in your configuration"
2384 ui.write(_(" (specify a commit editor in your configuration"
2385 " file)\n"))
2385 " file)\n"))
2386 problems += 1
2386 problems += 1
2387
2387
2388 # check username
2388 # check username
2389 ui.status(_("checking username...\n"))
2389 ui.status(_("checking username...\n"))
2390 try:
2390 try:
2391 ui.username()
2391 ui.username()
2392 except util.Abort, e:
2392 except util.Abort, e:
2393 ui.write(" %s\n" % e)
2393 ui.write(" %s\n" % e)
2394 ui.write(_(" (specify a username in your configuration file)\n"))
2394 ui.write(_(" (specify a username in your configuration file)\n"))
2395 problems += 1
2395 problems += 1
2396
2396
2397 if not problems:
2397 if not problems:
2398 ui.status(_("no problems detected\n"))
2398 ui.status(_("no problems detected\n"))
2399 else:
2399 else:
2400 ui.write(_("%s problems detected,"
2400 ui.write(_("%s problems detected,"
2401 " please check your install!\n") % problems)
2401 " please check your install!\n") % problems)
2402
2402
2403 return problems
2403 return problems
2404
2404
2405 @command('debugknown', [], _('REPO ID...'), norepo=True)
2405 @command('debugknown', [], _('REPO ID...'), norepo=True)
2406 def debugknown(ui, repopath, *ids, **opts):
2406 def debugknown(ui, repopath, *ids, **opts):
2407 """test whether node ids are known to a repo
2407 """test whether node ids are known to a repo
2408
2408
2409 Every ID must be a full-length hex node id string. Returns a list of 0s
2409 Every ID must be a full-length hex node id string. Returns a list of 0s
2410 and 1s indicating unknown/known.
2410 and 1s indicating unknown/known.
2411 """
2411 """
2412 repo = hg.peer(ui, opts, repopath)
2412 repo = hg.peer(ui, opts, repopath)
2413 if not repo.capable('known'):
2413 if not repo.capable('known'):
2414 raise util.Abort("known() not supported by target repository")
2414 raise util.Abort("known() not supported by target repository")
2415 flags = repo.known([bin(s) for s in ids])
2415 flags = repo.known([bin(s) for s in ids])
2416 ui.write("%s\n" % ("".join([f and "1" or "0" for f in flags])))
2416 ui.write("%s\n" % ("".join([f and "1" or "0" for f in flags])))
2417
2417
2418 @command('debuglabelcomplete', [], _('LABEL...'))
2418 @command('debuglabelcomplete', [], _('LABEL...'))
2419 def debuglabelcomplete(ui, repo, *args):
2419 def debuglabelcomplete(ui, repo, *args):
2420 '''backwards compatibility with old bash completion scripts (DEPRECATED)'''
2420 '''backwards compatibility with old bash completion scripts (DEPRECATED)'''
2421 debugnamecomplete(ui, repo, *args)
2421 debugnamecomplete(ui, repo, *args)
2422
2422
2423 @command('debugnamecomplete', [], _('NAME...'))
2423 @command('debugnamecomplete', [], _('NAME...'))
2424 def debugnamecomplete(ui, repo, *args):
2424 def debugnamecomplete(ui, repo, *args):
2425 '''complete "names" - tags, open branch names, bookmark names'''
2425 '''complete "names" - tags, open branch names, bookmark names'''
2426
2426
2427 names = set()
2427 names = set()
2428 # since we previously only listed open branches, we will handle that
2428 # since we previously only listed open branches, we will handle that
2429 # specially (after this for loop)
2429 # specially (after this for loop)
2430 for name, ns in repo.names.iteritems():
2430 for name, ns in repo.names.iteritems():
2431 if name != 'branches':
2431 if name != 'branches':
2432 names.update(ns.listnames(repo))
2432 names.update(ns.listnames(repo))
2433 names.update(tag for (tag, heads, tip, closed)
2433 names.update(tag for (tag, heads, tip, closed)
2434 in repo.branchmap().iterbranches() if not closed)
2434 in repo.branchmap().iterbranches() if not closed)
2435 completions = set()
2435 completions = set()
2436 if not args:
2436 if not args:
2437 args = ['']
2437 args = ['']
2438 for a in args:
2438 for a in args:
2439 completions.update(n for n in names if n.startswith(a))
2439 completions.update(n for n in names if n.startswith(a))
2440 ui.write('\n'.join(sorted(completions)))
2440 ui.write('\n'.join(sorted(completions)))
2441 ui.write('\n')
2441 ui.write('\n')
2442
2442
2443 @command('debuglocks',
2443 @command('debuglocks',
2444 [('L', 'force-lock', None, _('free the store lock (DANGEROUS)')),
2444 [('L', 'force-lock', None, _('free the store lock (DANGEROUS)')),
2445 ('W', 'force-wlock', None,
2445 ('W', 'force-wlock', None,
2446 _('free the working state lock (DANGEROUS)'))],
2446 _('free the working state lock (DANGEROUS)'))],
2447 _('[OPTION]...'))
2447 _('[OPTION]...'))
2448 def debuglocks(ui, repo, **opts):
2448 def debuglocks(ui, repo, **opts):
2449 """show or modify state of locks
2449 """show or modify state of locks
2450
2450
2451 By default, this command will show which locks are held. This
2451 By default, this command will show which locks are held. This
2452 includes the user and process holding the lock, the amount of time
2452 includes the user and process holding the lock, the amount of time
2453 the lock has been held, and the machine name where the process is
2453 the lock has been held, and the machine name where the process is
2454 running if it's not local.
2454 running if it's not local.
2455
2455
2456 Locks protect the integrity of Mercurial's data, so should be
2456 Locks protect the integrity of Mercurial's data, so should be
2457 treated with care. System crashes or other interruptions may cause
2457 treated with care. System crashes or other interruptions may cause
2458 locks to not be properly released, though Mercurial will usually
2458 locks to not be properly released, though Mercurial will usually
2459 detect and remove such stale locks automatically.
2459 detect and remove such stale locks automatically.
2460
2460
2461 However, detecting stale locks may not always be possible (for
2461 However, detecting stale locks may not always be possible (for
2462 instance, on a shared filesystem). Removing locks may also be
2462 instance, on a shared filesystem). Removing locks may also be
2463 blocked by filesystem permissions.
2463 blocked by filesystem permissions.
2464
2464
2465 Returns 0 if no locks are held.
2465 Returns 0 if no locks are held.
2466
2466
2467 """
2467 """
2468
2468
2469 if opts.get('force_lock'):
2469 if opts.get('force_lock'):
2470 repo.svfs.unlink('lock')
2470 repo.svfs.unlink('lock')
2471 if opts.get('force_wlock'):
2471 if opts.get('force_wlock'):
2472 repo.vfs.unlink('wlock')
2472 repo.vfs.unlink('wlock')
2473 if opts.get('force_lock') or opts.get('force_lock'):
2473 if opts.get('force_lock') or opts.get('force_lock'):
2474 return 0
2474 return 0
2475
2475
2476 now = time.time()
2476 now = time.time()
2477 held = 0
2477 held = 0
2478
2478
2479 def report(vfs, name, method):
2479 def report(vfs, name, method):
2480 # this causes stale locks to get reaped for more accurate reporting
2480 # this causes stale locks to get reaped for more accurate reporting
2481 try:
2481 try:
2482 l = method(False)
2482 l = method(False)
2483 except error.LockHeld:
2483 except error.LockHeld:
2484 l = None
2484 l = None
2485
2485
2486 if l:
2486 if l:
2487 l.release()
2487 l.release()
2488 else:
2488 else:
2489 try:
2489 try:
2490 stat = vfs.lstat(name)
2490 stat = vfs.lstat(name)
2491 age = now - stat.st_mtime
2491 age = now - stat.st_mtime
2492 user = util.username(stat.st_uid)
2492 user = util.username(stat.st_uid)
2493 locker = vfs.readlock(name)
2493 locker = vfs.readlock(name)
2494 if ":" in locker:
2494 if ":" in locker:
2495 host, pid = locker.split(':')
2495 host, pid = locker.split(':')
2496 if host == socket.gethostname():
2496 if host == socket.gethostname():
2497 locker = 'user %s, process %s' % (user, pid)
2497 locker = 'user %s, process %s' % (user, pid)
2498 else:
2498 else:
2499 locker = 'user %s, process %s, host %s' \
2499 locker = 'user %s, process %s, host %s' \
2500 % (user, pid, host)
2500 % (user, pid, host)
2501 ui.write("%-6s %s (%ds)\n" % (name + ":", locker, age))
2501 ui.write("%-6s %s (%ds)\n" % (name + ":", locker, age))
2502 return 1
2502 return 1
2503 except OSError, e:
2503 except OSError, e:
2504 if e.errno != errno.ENOENT:
2504 if e.errno != errno.ENOENT:
2505 raise
2505 raise
2506
2506
2507 ui.write("%-6s free\n" % (name + ":"))
2507 ui.write("%-6s free\n" % (name + ":"))
2508 return 0
2508 return 0
2509
2509
2510 held += report(repo.svfs, "lock", repo.lock)
2510 held += report(repo.svfs, "lock", repo.lock)
2511 held += report(repo.vfs, "wlock", repo.wlock)
2511 held += report(repo.vfs, "wlock", repo.wlock)
2512
2512
2513 return held
2513 return held
2514
2514
2515 @command('debugobsolete',
2515 @command('debugobsolete',
2516 [('', 'flags', 0, _('markers flag')),
2516 [('', 'flags', 0, _('markers flag')),
2517 ('', 'record-parents', False,
2517 ('', 'record-parents', False,
2518 _('record parent information for the precursor')),
2518 _('record parent information for the precursor')),
2519 ('r', 'rev', [], _('display markers relevant to REV')),
2519 ('r', 'rev', [], _('display markers relevant to REV')),
2520 ] + commitopts2,
2520 ] + commitopts2,
2521 _('[OBSOLETED [REPLACEMENT] [REPL... ]'))
2521 _('[OBSOLETED [REPLACEMENT] [REPL... ]'))
2522 def debugobsolete(ui, repo, precursor=None, *successors, **opts):
2522 def debugobsolete(ui, repo, precursor=None, *successors, **opts):
2523 """create arbitrary obsolete marker
2523 """create arbitrary obsolete marker
2524
2524
2525 With no arguments, displays the list of obsolescence markers."""
2525 With no arguments, displays the list of obsolescence markers."""
2526
2526
2527 def parsenodeid(s):
2527 def parsenodeid(s):
2528 try:
2528 try:
2529 # We do not use revsingle/revrange functions here to accept
2529 # We do not use revsingle/revrange functions here to accept
2530 # arbitrary node identifiers, possibly not present in the
2530 # arbitrary node identifiers, possibly not present in the
2531 # local repository.
2531 # local repository.
2532 n = bin(s)
2532 n = bin(s)
2533 if len(n) != len(nullid):
2533 if len(n) != len(nullid):
2534 raise TypeError()
2534 raise TypeError()
2535 return n
2535 return n
2536 except TypeError:
2536 except TypeError:
2537 raise util.Abort('changeset references must be full hexadecimal '
2537 raise util.Abort('changeset references must be full hexadecimal '
2538 'node identifiers')
2538 'node identifiers')
2539
2539
2540 if precursor is not None:
2540 if precursor is not None:
2541 if opts['rev']:
2541 if opts['rev']:
2542 raise util.Abort('cannot select revision when creating marker')
2542 raise util.Abort('cannot select revision when creating marker')
2543 metadata = {}
2543 metadata = {}
2544 metadata['user'] = opts['user'] or ui.username()
2544 metadata['user'] = opts['user'] or ui.username()
2545 succs = tuple(parsenodeid(succ) for succ in successors)
2545 succs = tuple(parsenodeid(succ) for succ in successors)
2546 l = repo.lock()
2546 l = repo.lock()
2547 try:
2547 try:
2548 tr = repo.transaction('debugobsolete')
2548 tr = repo.transaction('debugobsolete')
2549 try:
2549 try:
2550 date = opts.get('date')
2550 date = opts.get('date')
2551 if date:
2551 if date:
2552 date = util.parsedate(date)
2552 date = util.parsedate(date)
2553 else:
2553 else:
2554 date = None
2554 date = None
2555 prec = parsenodeid(precursor)
2555 prec = parsenodeid(precursor)
2556 parents = None
2556 parents = None
2557 if opts['record_parents']:
2557 if opts['record_parents']:
2558 if prec not in repo.unfiltered():
2558 if prec not in repo.unfiltered():
2559 raise util.Abort('cannot used --record-parents on '
2559 raise util.Abort('cannot used --record-parents on '
2560 'unknown changesets')
2560 'unknown changesets')
2561 parents = repo.unfiltered()[prec].parents()
2561 parents = repo.unfiltered()[prec].parents()
2562 parents = tuple(p.node() for p in parents)
2562 parents = tuple(p.node() for p in parents)
2563 repo.obsstore.create(tr, prec, succs, opts['flags'],
2563 repo.obsstore.create(tr, prec, succs, opts['flags'],
2564 parents=parents, date=date,
2564 parents=parents, date=date,
2565 metadata=metadata)
2565 metadata=metadata)
2566 tr.close()
2566 tr.close()
2567 except ValueError, exc:
2567 except ValueError, exc:
2568 raise util.Abort(_('bad obsmarker input: %s') % exc)
2568 raise util.Abort(_('bad obsmarker input: %s') % exc)
2569 finally:
2569 finally:
2570 tr.release()
2570 tr.release()
2571 finally:
2571 finally:
2572 l.release()
2572 l.release()
2573 else:
2573 else:
2574 if opts['rev']:
2574 if opts['rev']:
2575 revs = scmutil.revrange(repo, opts['rev'])
2575 revs = scmutil.revrange(repo, opts['rev'])
2576 nodes = [repo[r].node() for r in revs]
2576 nodes = [repo[r].node() for r in revs]
2577 markers = list(obsolete.getmarkers(repo, nodes=nodes))
2577 markers = list(obsolete.getmarkers(repo, nodes=nodes))
2578 markers.sort(key=lambda x: x._data)
2578 markers.sort(key=lambda x: x._data)
2579 else:
2579 else:
2580 markers = obsolete.getmarkers(repo)
2580 markers = obsolete.getmarkers(repo)
2581
2581
2582 for m in markers:
2582 for m in markers:
2583 cmdutil.showmarker(ui, m)
2583 cmdutil.showmarker(ui, m)
2584
2584
2585 @command('debugpathcomplete',
2585 @command('debugpathcomplete',
2586 [('f', 'full', None, _('complete an entire path')),
2586 [('f', 'full', None, _('complete an entire path')),
2587 ('n', 'normal', None, _('show only normal files')),
2587 ('n', 'normal', None, _('show only normal files')),
2588 ('a', 'added', None, _('show only added files')),
2588 ('a', 'added', None, _('show only added files')),
2589 ('r', 'removed', None, _('show only removed files'))],
2589 ('r', 'removed', None, _('show only removed files'))],
2590 _('FILESPEC...'))
2590 _('FILESPEC...'))
2591 def debugpathcomplete(ui, repo, *specs, **opts):
2591 def debugpathcomplete(ui, repo, *specs, **opts):
2592 '''complete part or all of a tracked path
2592 '''complete part or all of a tracked path
2593
2593
2594 This command supports shells that offer path name completion. It
2594 This command supports shells that offer path name completion. It
2595 currently completes only files already known to the dirstate.
2595 currently completes only files already known to the dirstate.
2596
2596
2597 Completion extends only to the next path segment unless
2597 Completion extends only to the next path segment unless
2598 --full is specified, in which case entire paths are used.'''
2598 --full is specified, in which case entire paths are used.'''
2599
2599
2600 def complete(path, acceptable):
2600 def complete(path, acceptable):
2601 dirstate = repo.dirstate
2601 dirstate = repo.dirstate
2602 spec = os.path.normpath(os.path.join(os.getcwd(), path))
2602 spec = os.path.normpath(os.path.join(os.getcwd(), path))
2603 rootdir = repo.root + os.sep
2603 rootdir = repo.root + os.sep
2604 if spec != repo.root and not spec.startswith(rootdir):
2604 if spec != repo.root and not spec.startswith(rootdir):
2605 return [], []
2605 return [], []
2606 if os.path.isdir(spec):
2606 if os.path.isdir(spec):
2607 spec += '/'
2607 spec += '/'
2608 spec = spec[len(rootdir):]
2608 spec = spec[len(rootdir):]
2609 fixpaths = os.sep != '/'
2609 fixpaths = os.sep != '/'
2610 if fixpaths:
2610 if fixpaths:
2611 spec = spec.replace(os.sep, '/')
2611 spec = spec.replace(os.sep, '/')
2612 speclen = len(spec)
2612 speclen = len(spec)
2613 fullpaths = opts['full']
2613 fullpaths = opts['full']
2614 files, dirs = set(), set()
2614 files, dirs = set(), set()
2615 adddir, addfile = dirs.add, files.add
2615 adddir, addfile = dirs.add, files.add
2616 for f, st in dirstate.iteritems():
2616 for f, st in dirstate.iteritems():
2617 if f.startswith(spec) and st[0] in acceptable:
2617 if f.startswith(spec) and st[0] in acceptable:
2618 if fixpaths:
2618 if fixpaths:
2619 f = f.replace('/', os.sep)
2619 f = f.replace('/', os.sep)
2620 if fullpaths:
2620 if fullpaths:
2621 addfile(f)
2621 addfile(f)
2622 continue
2622 continue
2623 s = f.find(os.sep, speclen)
2623 s = f.find(os.sep, speclen)
2624 if s >= 0:
2624 if s >= 0:
2625 adddir(f[:s])
2625 adddir(f[:s])
2626 else:
2626 else:
2627 addfile(f)
2627 addfile(f)
2628 return files, dirs
2628 return files, dirs
2629
2629
2630 acceptable = ''
2630 acceptable = ''
2631 if opts['normal']:
2631 if opts['normal']:
2632 acceptable += 'nm'
2632 acceptable += 'nm'
2633 if opts['added']:
2633 if opts['added']:
2634 acceptable += 'a'
2634 acceptable += 'a'
2635 if opts['removed']:
2635 if opts['removed']:
2636 acceptable += 'r'
2636 acceptable += 'r'
2637 cwd = repo.getcwd()
2637 cwd = repo.getcwd()
2638 if not specs:
2638 if not specs:
2639 specs = ['.']
2639 specs = ['.']
2640
2640
2641 files, dirs = set(), set()
2641 files, dirs = set(), set()
2642 for spec in specs:
2642 for spec in specs:
2643 f, d = complete(spec, acceptable or 'nmar')
2643 f, d = complete(spec, acceptable or 'nmar')
2644 files.update(f)
2644 files.update(f)
2645 dirs.update(d)
2645 dirs.update(d)
2646 files.update(dirs)
2646 files.update(dirs)
2647 ui.write('\n'.join(repo.pathto(p, cwd) for p in sorted(files)))
2647 ui.write('\n'.join(repo.pathto(p, cwd) for p in sorted(files)))
2648 ui.write('\n')
2648 ui.write('\n')
2649
2649
2650 @command('debugpushkey', [], _('REPO NAMESPACE [KEY OLD NEW]'), norepo=True)
2650 @command('debugpushkey', [], _('REPO NAMESPACE [KEY OLD NEW]'), norepo=True)
2651 def debugpushkey(ui, repopath, namespace, *keyinfo, **opts):
2651 def debugpushkey(ui, repopath, namespace, *keyinfo, **opts):
2652 '''access the pushkey key/value protocol
2652 '''access the pushkey key/value protocol
2653
2653
2654 With two args, list the keys in the given namespace.
2654 With two args, list the keys in the given namespace.
2655
2655
2656 With five args, set a key to new if it currently is set to old.
2656 With five args, set a key to new if it currently is set to old.
2657 Reports success or failure.
2657 Reports success or failure.
2658 '''
2658 '''
2659
2659
2660 target = hg.peer(ui, {}, repopath)
2660 target = hg.peer(ui, {}, repopath)
2661 if keyinfo:
2661 if keyinfo:
2662 key, old, new = keyinfo
2662 key, old, new = keyinfo
2663 r = target.pushkey(namespace, key, old, new)
2663 r = target.pushkey(namespace, key, old, new)
2664 ui.status(str(r) + '\n')
2664 ui.status(str(r) + '\n')
2665 return not r
2665 return not r
2666 else:
2666 else:
2667 for k, v in sorted(target.listkeys(namespace).iteritems()):
2667 for k, v in sorted(target.listkeys(namespace).iteritems()):
2668 ui.write("%s\t%s\n" % (k.encode('string-escape'),
2668 ui.write("%s\t%s\n" % (k.encode('string-escape'),
2669 v.encode('string-escape')))
2669 v.encode('string-escape')))
2670
2670
2671 @command('debugpvec', [], _('A B'))
2671 @command('debugpvec', [], _('A B'))
2672 def debugpvec(ui, repo, a, b=None):
2672 def debugpvec(ui, repo, a, b=None):
2673 ca = scmutil.revsingle(repo, a)
2673 ca = scmutil.revsingle(repo, a)
2674 cb = scmutil.revsingle(repo, b)
2674 cb = scmutil.revsingle(repo, b)
2675 pa = pvec.ctxpvec(ca)
2675 pa = pvec.ctxpvec(ca)
2676 pb = pvec.ctxpvec(cb)
2676 pb = pvec.ctxpvec(cb)
2677 if pa == pb:
2677 if pa == pb:
2678 rel = "="
2678 rel = "="
2679 elif pa > pb:
2679 elif pa > pb:
2680 rel = ">"
2680 rel = ">"
2681 elif pa < pb:
2681 elif pa < pb:
2682 rel = "<"
2682 rel = "<"
2683 elif pa | pb:
2683 elif pa | pb:
2684 rel = "|"
2684 rel = "|"
2685 ui.write(_("a: %s\n") % pa)
2685 ui.write(_("a: %s\n") % pa)
2686 ui.write(_("b: %s\n") % pb)
2686 ui.write(_("b: %s\n") % pb)
2687 ui.write(_("depth(a): %d depth(b): %d\n") % (pa._depth, pb._depth))
2687 ui.write(_("depth(a): %d depth(b): %d\n") % (pa._depth, pb._depth))
2688 ui.write(_("delta: %d hdist: %d distance: %d relation: %s\n") %
2688 ui.write(_("delta: %d hdist: %d distance: %d relation: %s\n") %
2689 (abs(pa._depth - pb._depth), pvec._hamming(pa._vec, pb._vec),
2689 (abs(pa._depth - pb._depth), pvec._hamming(pa._vec, pb._vec),
2690 pa.distance(pb), rel))
2690 pa.distance(pb), rel))
2691
2691
2692 @command('debugrebuilddirstate|debugrebuildstate',
2692 @command('debugrebuilddirstate|debugrebuildstate',
2693 [('r', 'rev', '', _('revision to rebuild to'), _('REV'))],
2693 [('r', 'rev', '', _('revision to rebuild to'), _('REV'))],
2694 _('[-r REV]'))
2694 _('[-r REV]'))
2695 def debugrebuilddirstate(ui, repo, rev):
2695 def debugrebuilddirstate(ui, repo, rev):
2696 """rebuild the dirstate as it would look like for the given revision
2696 """rebuild the dirstate as it would look like for the given revision
2697
2697
2698 If no revision is specified the first current parent will be used.
2698 If no revision is specified the first current parent will be used.
2699
2699
2700 The dirstate will be set to the files of the given revision.
2700 The dirstate will be set to the files of the given revision.
2701 The actual working directory content or existing dirstate
2701 The actual working directory content or existing dirstate
2702 information such as adds or removes is not considered.
2702 information such as adds or removes is not considered.
2703
2703
2704 One use of this command is to make the next :hg:`status` invocation
2704 One use of this command is to make the next :hg:`status` invocation
2705 check the actual file content.
2705 check the actual file content.
2706 """
2706 """
2707 ctx = scmutil.revsingle(repo, rev)
2707 ctx = scmutil.revsingle(repo, rev)
2708 wlock = repo.wlock()
2708 wlock = repo.wlock()
2709 try:
2709 try:
2710 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
2710 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
2711 finally:
2711 finally:
2712 wlock.release()
2712 wlock.release()
2713
2713
2714 @command('debugrename',
2714 @command('debugrename',
2715 [('r', 'rev', '', _('revision to debug'), _('REV'))],
2715 [('r', 'rev', '', _('revision to debug'), _('REV'))],
2716 _('[-r REV] FILE'))
2716 _('[-r REV] FILE'))
2717 def debugrename(ui, repo, file1, *pats, **opts):
2717 def debugrename(ui, repo, file1, *pats, **opts):
2718 """dump rename information"""
2718 """dump rename information"""
2719
2719
2720 ctx = scmutil.revsingle(repo, opts.get('rev'))
2720 ctx = scmutil.revsingle(repo, opts.get('rev'))
2721 m = scmutil.match(ctx, (file1,) + pats, opts)
2721 m = scmutil.match(ctx, (file1,) + pats, opts)
2722 for abs in ctx.walk(m):
2722 for abs in ctx.walk(m):
2723 fctx = ctx[abs]
2723 fctx = ctx[abs]
2724 o = fctx.filelog().renamed(fctx.filenode())
2724 o = fctx.filelog().renamed(fctx.filenode())
2725 rel = m.rel(abs)
2725 rel = m.rel(abs)
2726 if o:
2726 if o:
2727 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
2727 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
2728 else:
2728 else:
2729 ui.write(_("%s not renamed\n") % rel)
2729 ui.write(_("%s not renamed\n") % rel)
2730
2730
2731 @command('debugrevlog',
2731 @command('debugrevlog',
2732 [('c', 'changelog', False, _('open changelog')),
2732 [('c', 'changelog', False, _('open changelog')),
2733 ('m', 'manifest', False, _('open manifest')),
2733 ('m', 'manifest', False, _('open manifest')),
2734 ('', 'dir', False, _('open directory manifest')),
2734 ('', 'dir', False, _('open directory manifest')),
2735 ('d', 'dump', False, _('dump index data'))],
2735 ('d', 'dump', False, _('dump index data'))],
2736 _('-c|-m|FILE'),
2736 _('-c|-m|FILE'),
2737 optionalrepo=True)
2737 optionalrepo=True)
2738 def debugrevlog(ui, repo, file_=None, **opts):
2738 def debugrevlog(ui, repo, file_=None, **opts):
2739 """show data and statistics about a revlog"""
2739 """show data and statistics about a revlog"""
2740 r = cmdutil.openrevlog(repo, 'debugrevlog', file_, opts)
2740 r = cmdutil.openrevlog(repo, 'debugrevlog', file_, opts)
2741
2741
2742 if opts.get("dump"):
2742 if opts.get("dump"):
2743 numrevs = len(r)
2743 numrevs = len(r)
2744 ui.write("# rev p1rev p2rev start end deltastart base p1 p2"
2744 ui.write("# rev p1rev p2rev start end deltastart base p1 p2"
2745 " rawsize totalsize compression heads chainlen\n")
2745 " rawsize totalsize compression heads chainlen\n")
2746 ts = 0
2746 ts = 0
2747 heads = set()
2747 heads = set()
2748
2748
2749 for rev in xrange(numrevs):
2749 for rev in xrange(numrevs):
2750 dbase = r.deltaparent(rev)
2750 dbase = r.deltaparent(rev)
2751 if dbase == -1:
2751 if dbase == -1:
2752 dbase = rev
2752 dbase = rev
2753 cbase = r.chainbase(rev)
2753 cbase = r.chainbase(rev)
2754 clen = r.chainlen(rev)
2754 clen = r.chainlen(rev)
2755 p1, p2 = r.parentrevs(rev)
2755 p1, p2 = r.parentrevs(rev)
2756 rs = r.rawsize(rev)
2756 rs = r.rawsize(rev)
2757 ts = ts + rs
2757 ts = ts + rs
2758 heads -= set(r.parentrevs(rev))
2758 heads -= set(r.parentrevs(rev))
2759 heads.add(rev)
2759 heads.add(rev)
2760 ui.write("%5d %5d %5d %5d %5d %10d %4d %4d %4d %7d %9d "
2760 ui.write("%5d %5d %5d %5d %5d %10d %4d %4d %4d %7d %9d "
2761 "%11d %5d %8d\n" %
2761 "%11d %5d %8d\n" %
2762 (rev, p1, p2, r.start(rev), r.end(rev),
2762 (rev, p1, p2, r.start(rev), r.end(rev),
2763 r.start(dbase), r.start(cbase),
2763 r.start(dbase), r.start(cbase),
2764 r.start(p1), r.start(p2),
2764 r.start(p1), r.start(p2),
2765 rs, ts, ts / r.end(rev), len(heads), clen))
2765 rs, ts, ts / r.end(rev), len(heads), clen))
2766 return 0
2766 return 0
2767
2767
2768 v = r.version
2768 v = r.version
2769 format = v & 0xFFFF
2769 format = v & 0xFFFF
2770 flags = []
2770 flags = []
2771 gdelta = False
2771 gdelta = False
2772 if v & revlog.REVLOGNGINLINEDATA:
2772 if v & revlog.REVLOGNGINLINEDATA:
2773 flags.append('inline')
2773 flags.append('inline')
2774 if v & revlog.REVLOGGENERALDELTA:
2774 if v & revlog.REVLOGGENERALDELTA:
2775 gdelta = True
2775 gdelta = True
2776 flags.append('generaldelta')
2776 flags.append('generaldelta')
2777 if not flags:
2777 if not flags:
2778 flags = ['(none)']
2778 flags = ['(none)']
2779
2779
2780 nummerges = 0
2780 nummerges = 0
2781 numfull = 0
2781 numfull = 0
2782 numprev = 0
2782 numprev = 0
2783 nump1 = 0
2783 nump1 = 0
2784 nump2 = 0
2784 nump2 = 0
2785 numother = 0
2785 numother = 0
2786 nump1prev = 0
2786 nump1prev = 0
2787 nump2prev = 0
2787 nump2prev = 0
2788 chainlengths = []
2788 chainlengths = []
2789
2789
2790 datasize = [None, 0, 0L]
2790 datasize = [None, 0, 0L]
2791 fullsize = [None, 0, 0L]
2791 fullsize = [None, 0, 0L]
2792 deltasize = [None, 0, 0L]
2792 deltasize = [None, 0, 0L]
2793
2793
2794 def addsize(size, l):
2794 def addsize(size, l):
2795 if l[0] is None or size < l[0]:
2795 if l[0] is None or size < l[0]:
2796 l[0] = size
2796 l[0] = size
2797 if size > l[1]:
2797 if size > l[1]:
2798 l[1] = size
2798 l[1] = size
2799 l[2] += size
2799 l[2] += size
2800
2800
2801 numrevs = len(r)
2801 numrevs = len(r)
2802 for rev in xrange(numrevs):
2802 for rev in xrange(numrevs):
2803 p1, p2 = r.parentrevs(rev)
2803 p1, p2 = r.parentrevs(rev)
2804 delta = r.deltaparent(rev)
2804 delta = r.deltaparent(rev)
2805 if format > 0:
2805 if format > 0:
2806 addsize(r.rawsize(rev), datasize)
2806 addsize(r.rawsize(rev), datasize)
2807 if p2 != nullrev:
2807 if p2 != nullrev:
2808 nummerges += 1
2808 nummerges += 1
2809 size = r.length(rev)
2809 size = r.length(rev)
2810 if delta == nullrev:
2810 if delta == nullrev:
2811 chainlengths.append(0)
2811 chainlengths.append(0)
2812 numfull += 1
2812 numfull += 1
2813 addsize(size, fullsize)
2813 addsize(size, fullsize)
2814 else:
2814 else:
2815 chainlengths.append(chainlengths[delta] + 1)
2815 chainlengths.append(chainlengths[delta] + 1)
2816 addsize(size, deltasize)
2816 addsize(size, deltasize)
2817 if delta == rev - 1:
2817 if delta == rev - 1:
2818 numprev += 1
2818 numprev += 1
2819 if delta == p1:
2819 if delta == p1:
2820 nump1prev += 1
2820 nump1prev += 1
2821 elif delta == p2:
2821 elif delta == p2:
2822 nump2prev += 1
2822 nump2prev += 1
2823 elif delta == p1:
2823 elif delta == p1:
2824 nump1 += 1
2824 nump1 += 1
2825 elif delta == p2:
2825 elif delta == p2:
2826 nump2 += 1
2826 nump2 += 1
2827 elif delta != nullrev:
2827 elif delta != nullrev:
2828 numother += 1
2828 numother += 1
2829
2829
2830 # Adjust size min value for empty cases
2830 # Adjust size min value for empty cases
2831 for size in (datasize, fullsize, deltasize):
2831 for size in (datasize, fullsize, deltasize):
2832 if size[0] is None:
2832 if size[0] is None:
2833 size[0] = 0
2833 size[0] = 0
2834
2834
2835 numdeltas = numrevs - numfull
2835 numdeltas = numrevs - numfull
2836 numoprev = numprev - nump1prev - nump2prev
2836 numoprev = numprev - nump1prev - nump2prev
2837 totalrawsize = datasize[2]
2837 totalrawsize = datasize[2]
2838 datasize[2] /= numrevs
2838 datasize[2] /= numrevs
2839 fulltotal = fullsize[2]
2839 fulltotal = fullsize[2]
2840 fullsize[2] /= numfull
2840 fullsize[2] /= numfull
2841 deltatotal = deltasize[2]
2841 deltatotal = deltasize[2]
2842 if numrevs - numfull > 0:
2842 if numrevs - numfull > 0:
2843 deltasize[2] /= numrevs - numfull
2843 deltasize[2] /= numrevs - numfull
2844 totalsize = fulltotal + deltatotal
2844 totalsize = fulltotal + deltatotal
2845 avgchainlen = sum(chainlengths) / numrevs
2845 avgchainlen = sum(chainlengths) / numrevs
2846 maxchainlen = max(chainlengths)
2846 maxchainlen = max(chainlengths)
2847 compratio = totalrawsize / totalsize
2847 compratio = totalrawsize / totalsize
2848
2848
2849 basedfmtstr = '%%%dd\n'
2849 basedfmtstr = '%%%dd\n'
2850 basepcfmtstr = '%%%dd %s(%%5.2f%%%%)\n'
2850 basepcfmtstr = '%%%dd %s(%%5.2f%%%%)\n'
2851
2851
2852 def dfmtstr(max):
2852 def dfmtstr(max):
2853 return basedfmtstr % len(str(max))
2853 return basedfmtstr % len(str(max))
2854 def pcfmtstr(max, padding=0):
2854 def pcfmtstr(max, padding=0):
2855 return basepcfmtstr % (len(str(max)), ' ' * padding)
2855 return basepcfmtstr % (len(str(max)), ' ' * padding)
2856
2856
2857 def pcfmt(value, total):
2857 def pcfmt(value, total):
2858 return (value, 100 * float(value) / total)
2858 return (value, 100 * float(value) / total)
2859
2859
2860 ui.write(('format : %d\n') % format)
2860 ui.write(('format : %d\n') % format)
2861 ui.write(('flags : %s\n') % ', '.join(flags))
2861 ui.write(('flags : %s\n') % ', '.join(flags))
2862
2862
2863 ui.write('\n')
2863 ui.write('\n')
2864 fmt = pcfmtstr(totalsize)
2864 fmt = pcfmtstr(totalsize)
2865 fmt2 = dfmtstr(totalsize)
2865 fmt2 = dfmtstr(totalsize)
2866 ui.write(('revisions : ') + fmt2 % numrevs)
2866 ui.write(('revisions : ') + fmt2 % numrevs)
2867 ui.write((' merges : ') + fmt % pcfmt(nummerges, numrevs))
2867 ui.write((' merges : ') + fmt % pcfmt(nummerges, numrevs))
2868 ui.write((' normal : ') + fmt % pcfmt(numrevs - nummerges, numrevs))
2868 ui.write((' normal : ') + fmt % pcfmt(numrevs - nummerges, numrevs))
2869 ui.write(('revisions : ') + fmt2 % numrevs)
2869 ui.write(('revisions : ') + fmt2 % numrevs)
2870 ui.write((' full : ') + fmt % pcfmt(numfull, numrevs))
2870 ui.write((' full : ') + fmt % pcfmt(numfull, numrevs))
2871 ui.write((' deltas : ') + fmt % pcfmt(numdeltas, numrevs))
2871 ui.write((' deltas : ') + fmt % pcfmt(numdeltas, numrevs))
2872 ui.write(('revision size : ') + fmt2 % totalsize)
2872 ui.write(('revision size : ') + fmt2 % totalsize)
2873 ui.write((' full : ') + fmt % pcfmt(fulltotal, totalsize))
2873 ui.write((' full : ') + fmt % pcfmt(fulltotal, totalsize))
2874 ui.write((' deltas : ') + fmt % pcfmt(deltatotal, totalsize))
2874 ui.write((' deltas : ') + fmt % pcfmt(deltatotal, totalsize))
2875
2875
2876 ui.write('\n')
2876 ui.write('\n')
2877 fmt = dfmtstr(max(avgchainlen, compratio))
2877 fmt = dfmtstr(max(avgchainlen, compratio))
2878 ui.write(('avg chain length : ') + fmt % avgchainlen)
2878 ui.write(('avg chain length : ') + fmt % avgchainlen)
2879 ui.write(('max chain length : ') + fmt % maxchainlen)
2879 ui.write(('max chain length : ') + fmt % maxchainlen)
2880 ui.write(('compression ratio : ') + fmt % compratio)
2880 ui.write(('compression ratio : ') + fmt % compratio)
2881
2881
2882 if format > 0:
2882 if format > 0:
2883 ui.write('\n')
2883 ui.write('\n')
2884 ui.write(('uncompressed data size (min/max/avg) : %d / %d / %d\n')
2884 ui.write(('uncompressed data size (min/max/avg) : %d / %d / %d\n')
2885 % tuple(datasize))
2885 % tuple(datasize))
2886 ui.write(('full revision size (min/max/avg) : %d / %d / %d\n')
2886 ui.write(('full revision size (min/max/avg) : %d / %d / %d\n')
2887 % tuple(fullsize))
2887 % tuple(fullsize))
2888 ui.write(('delta size (min/max/avg) : %d / %d / %d\n')
2888 ui.write(('delta size (min/max/avg) : %d / %d / %d\n')
2889 % tuple(deltasize))
2889 % tuple(deltasize))
2890
2890
2891 if numdeltas > 0:
2891 if numdeltas > 0:
2892 ui.write('\n')
2892 ui.write('\n')
2893 fmt = pcfmtstr(numdeltas)
2893 fmt = pcfmtstr(numdeltas)
2894 fmt2 = pcfmtstr(numdeltas, 4)
2894 fmt2 = pcfmtstr(numdeltas, 4)
2895 ui.write(('deltas against prev : ') + fmt % pcfmt(numprev, numdeltas))
2895 ui.write(('deltas against prev : ') + fmt % pcfmt(numprev, numdeltas))
2896 if numprev > 0:
2896 if numprev > 0:
2897 ui.write((' where prev = p1 : ') + fmt2 % pcfmt(nump1prev,
2897 ui.write((' where prev = p1 : ') + fmt2 % pcfmt(nump1prev,
2898 numprev))
2898 numprev))
2899 ui.write((' where prev = p2 : ') + fmt2 % pcfmt(nump2prev,
2899 ui.write((' where prev = p2 : ') + fmt2 % pcfmt(nump2prev,
2900 numprev))
2900 numprev))
2901 ui.write((' other : ') + fmt2 % pcfmt(numoprev,
2901 ui.write((' other : ') + fmt2 % pcfmt(numoprev,
2902 numprev))
2902 numprev))
2903 if gdelta:
2903 if gdelta:
2904 ui.write(('deltas against p1 : ')
2904 ui.write(('deltas against p1 : ')
2905 + fmt % pcfmt(nump1, numdeltas))
2905 + fmt % pcfmt(nump1, numdeltas))
2906 ui.write(('deltas against p2 : ')
2906 ui.write(('deltas against p2 : ')
2907 + fmt % pcfmt(nump2, numdeltas))
2907 + fmt % pcfmt(nump2, numdeltas))
2908 ui.write(('deltas against other : ') + fmt % pcfmt(numother,
2908 ui.write(('deltas against other : ') + fmt % pcfmt(numother,
2909 numdeltas))
2909 numdeltas))
2910
2910
2911 @command('debugrevspec',
2911 @command('debugrevspec',
2912 [('', 'optimize', None, _('print parsed tree after optimizing'))],
2912 [('', 'optimize', None, _('print parsed tree after optimizing'))],
2913 ('REVSPEC'))
2913 ('REVSPEC'))
2914 def debugrevspec(ui, repo, expr, **opts):
2914 def debugrevspec(ui, repo, expr, **opts):
2915 """parse and apply a revision specification
2915 """parse and apply a revision specification
2916
2916
2917 Use --verbose to print the parsed tree before and after aliases
2917 Use --verbose to print the parsed tree before and after aliases
2918 expansion.
2918 expansion.
2919 """
2919 """
2920 if ui.verbose:
2920 if ui.verbose:
2921 tree = revset.parse(expr)[0]
2921 tree = revset.parse(expr)
2922 ui.note(revset.prettyformat(tree), "\n")
2922 ui.note(revset.prettyformat(tree), "\n")
2923 newtree = revset.findaliases(ui, tree)
2923 newtree = revset.findaliases(ui, tree)
2924 if newtree != tree:
2924 if newtree != tree:
2925 ui.note(revset.prettyformat(newtree), "\n")
2925 ui.note(revset.prettyformat(newtree), "\n")
2926 tree = newtree
2926 tree = newtree
2927 newtree = revset.foldconcat(tree)
2927 newtree = revset.foldconcat(tree)
2928 if newtree != tree:
2928 if newtree != tree:
2929 ui.note(revset.prettyformat(newtree), "\n")
2929 ui.note(revset.prettyformat(newtree), "\n")
2930 if opts["optimize"]:
2930 if opts["optimize"]:
2931 weight, optimizedtree = revset.optimize(newtree, True)
2931 weight, optimizedtree = revset.optimize(newtree, True)
2932 ui.note("* optimized:\n", revset.prettyformat(optimizedtree), "\n")
2932 ui.note("* optimized:\n", revset.prettyformat(optimizedtree), "\n")
2933 func = revset.match(ui, expr)
2933 func = revset.match(ui, expr)
2934 revs = func(repo)
2934 revs = func(repo)
2935 if ui.verbose:
2935 if ui.verbose:
2936 ui.note("* set:\n", revset.prettyformatset(revs), "\n")
2936 ui.note("* set:\n", revset.prettyformatset(revs), "\n")
2937 for c in revs:
2937 for c in revs:
2938 ui.write("%s\n" % c)
2938 ui.write("%s\n" % c)
2939
2939
2940 @command('debugsetparents', [], _('REV1 [REV2]'))
2940 @command('debugsetparents', [], _('REV1 [REV2]'))
2941 def debugsetparents(ui, repo, rev1, rev2=None):
2941 def debugsetparents(ui, repo, rev1, rev2=None):
2942 """manually set the parents of the current working directory
2942 """manually set the parents of the current working directory
2943
2943
2944 This is useful for writing repository conversion tools, but should
2944 This is useful for writing repository conversion tools, but should
2945 be used with care. For example, neither the working directory nor the
2945 be used with care. For example, neither the working directory nor the
2946 dirstate is updated, so file status may be incorrect after running this
2946 dirstate is updated, so file status may be incorrect after running this
2947 command.
2947 command.
2948
2948
2949 Returns 0 on success.
2949 Returns 0 on success.
2950 """
2950 """
2951
2951
2952 r1 = scmutil.revsingle(repo, rev1).node()
2952 r1 = scmutil.revsingle(repo, rev1).node()
2953 r2 = scmutil.revsingle(repo, rev2, 'null').node()
2953 r2 = scmutil.revsingle(repo, rev2, 'null').node()
2954
2954
2955 wlock = repo.wlock()
2955 wlock = repo.wlock()
2956 try:
2956 try:
2957 repo.dirstate.beginparentchange()
2957 repo.dirstate.beginparentchange()
2958 repo.setparents(r1, r2)
2958 repo.setparents(r1, r2)
2959 repo.dirstate.endparentchange()
2959 repo.dirstate.endparentchange()
2960 finally:
2960 finally:
2961 wlock.release()
2961 wlock.release()
2962
2962
2963 @command('debugdirstate|debugstate',
2963 @command('debugdirstate|debugstate',
2964 [('', 'nodates', None, _('do not display the saved mtime')),
2964 [('', 'nodates', None, _('do not display the saved mtime')),
2965 ('', 'datesort', None, _('sort by saved mtime'))],
2965 ('', 'datesort', None, _('sort by saved mtime'))],
2966 _('[OPTION]...'))
2966 _('[OPTION]...'))
2967 def debugstate(ui, repo, nodates=None, datesort=None):
2967 def debugstate(ui, repo, nodates=None, datesort=None):
2968 """show the contents of the current dirstate"""
2968 """show the contents of the current dirstate"""
2969 timestr = ""
2969 timestr = ""
2970 if datesort:
2970 if datesort:
2971 keyfunc = lambda x: (x[1][3], x[0]) # sort by mtime, then by filename
2971 keyfunc = lambda x: (x[1][3], x[0]) # sort by mtime, then by filename
2972 else:
2972 else:
2973 keyfunc = None # sort by filename
2973 keyfunc = None # sort by filename
2974 for file_, ent in sorted(repo.dirstate._map.iteritems(), key=keyfunc):
2974 for file_, ent in sorted(repo.dirstate._map.iteritems(), key=keyfunc):
2975 if ent[3] == -1:
2975 if ent[3] == -1:
2976 timestr = 'unset '
2976 timestr = 'unset '
2977 elif nodates:
2977 elif nodates:
2978 timestr = 'set '
2978 timestr = 'set '
2979 else:
2979 else:
2980 timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
2980 timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
2981 time.localtime(ent[3]))
2981 time.localtime(ent[3]))
2982 if ent[1] & 020000:
2982 if ent[1] & 020000:
2983 mode = 'lnk'
2983 mode = 'lnk'
2984 else:
2984 else:
2985 mode = '%3o' % (ent[1] & 0777 & ~util.umask)
2985 mode = '%3o' % (ent[1] & 0777 & ~util.umask)
2986 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
2986 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
2987 for f in repo.dirstate.copies():
2987 for f in repo.dirstate.copies():
2988 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
2988 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
2989
2989
2990 @command('debugsub',
2990 @command('debugsub',
2991 [('r', 'rev', '',
2991 [('r', 'rev', '',
2992 _('revision to check'), _('REV'))],
2992 _('revision to check'), _('REV'))],
2993 _('[-r REV] [REV]'))
2993 _('[-r REV] [REV]'))
2994 def debugsub(ui, repo, rev=None):
2994 def debugsub(ui, repo, rev=None):
2995 ctx = scmutil.revsingle(repo, rev, None)
2995 ctx = scmutil.revsingle(repo, rev, None)
2996 for k, v in sorted(ctx.substate.items()):
2996 for k, v in sorted(ctx.substate.items()):
2997 ui.write(('path %s\n') % k)
2997 ui.write(('path %s\n') % k)
2998 ui.write((' source %s\n') % v[0])
2998 ui.write((' source %s\n') % v[0])
2999 ui.write((' revision %s\n') % v[1])
2999 ui.write((' revision %s\n') % v[1])
3000
3000
3001 @command('debugsuccessorssets',
3001 @command('debugsuccessorssets',
3002 [],
3002 [],
3003 _('[REV]'))
3003 _('[REV]'))
3004 def debugsuccessorssets(ui, repo, *revs):
3004 def debugsuccessorssets(ui, repo, *revs):
3005 """show set of successors for revision
3005 """show set of successors for revision
3006
3006
3007 A successors set of changeset A is a consistent group of revisions that
3007 A successors set of changeset A is a consistent group of revisions that
3008 succeed A. It contains non-obsolete changesets only.
3008 succeed A. It contains non-obsolete changesets only.
3009
3009
3010 In most cases a changeset A has a single successors set containing a single
3010 In most cases a changeset A has a single successors set containing a single
3011 successor (changeset A replaced by A').
3011 successor (changeset A replaced by A').
3012
3012
3013 A changeset that is made obsolete with no successors are called "pruned".
3013 A changeset that is made obsolete with no successors are called "pruned".
3014 Such changesets have no successors sets at all.
3014 Such changesets have no successors sets at all.
3015
3015
3016 A changeset that has been "split" will have a successors set containing
3016 A changeset that has been "split" will have a successors set containing
3017 more than one successor.
3017 more than one successor.
3018
3018
3019 A changeset that has been rewritten in multiple different ways is called
3019 A changeset that has been rewritten in multiple different ways is called
3020 "divergent". Such changesets have multiple successor sets (each of which
3020 "divergent". Such changesets have multiple successor sets (each of which
3021 may also be split, i.e. have multiple successors).
3021 may also be split, i.e. have multiple successors).
3022
3022
3023 Results are displayed as follows::
3023 Results are displayed as follows::
3024
3024
3025 <rev1>
3025 <rev1>
3026 <successors-1A>
3026 <successors-1A>
3027 <rev2>
3027 <rev2>
3028 <successors-2A>
3028 <successors-2A>
3029 <successors-2B1> <successors-2B2> <successors-2B3>
3029 <successors-2B1> <successors-2B2> <successors-2B3>
3030
3030
3031 Here rev2 has two possible (i.e. divergent) successors sets. The first
3031 Here rev2 has two possible (i.e. divergent) successors sets. The first
3032 holds one element, whereas the second holds three (i.e. the changeset has
3032 holds one element, whereas the second holds three (i.e. the changeset has
3033 been split).
3033 been split).
3034 """
3034 """
3035 # passed to successorssets caching computation from one call to another
3035 # passed to successorssets caching computation from one call to another
3036 cache = {}
3036 cache = {}
3037 ctx2str = str
3037 ctx2str = str
3038 node2str = short
3038 node2str = short
3039 if ui.debug():
3039 if ui.debug():
3040 def ctx2str(ctx):
3040 def ctx2str(ctx):
3041 return ctx.hex()
3041 return ctx.hex()
3042 node2str = hex
3042 node2str = hex
3043 for rev in scmutil.revrange(repo, revs):
3043 for rev in scmutil.revrange(repo, revs):
3044 ctx = repo[rev]
3044 ctx = repo[rev]
3045 ui.write('%s\n'% ctx2str(ctx))
3045 ui.write('%s\n'% ctx2str(ctx))
3046 for succsset in obsolete.successorssets(repo, ctx.node(), cache):
3046 for succsset in obsolete.successorssets(repo, ctx.node(), cache):
3047 if succsset:
3047 if succsset:
3048 ui.write(' ')
3048 ui.write(' ')
3049 ui.write(node2str(succsset[0]))
3049 ui.write(node2str(succsset[0]))
3050 for node in succsset[1:]:
3050 for node in succsset[1:]:
3051 ui.write(' ')
3051 ui.write(' ')
3052 ui.write(node2str(node))
3052 ui.write(node2str(node))
3053 ui.write('\n')
3053 ui.write('\n')
3054
3054
3055 @command('debugwalk', walkopts, _('[OPTION]... [FILE]...'), inferrepo=True)
3055 @command('debugwalk', walkopts, _('[OPTION]... [FILE]...'), inferrepo=True)
3056 def debugwalk(ui, repo, *pats, **opts):
3056 def debugwalk(ui, repo, *pats, **opts):
3057 """show how files match on given patterns"""
3057 """show how files match on given patterns"""
3058 m = scmutil.match(repo[None], pats, opts)
3058 m = scmutil.match(repo[None], pats, opts)
3059 items = list(repo.walk(m))
3059 items = list(repo.walk(m))
3060 if not items:
3060 if not items:
3061 return
3061 return
3062 f = lambda fn: fn
3062 f = lambda fn: fn
3063 if ui.configbool('ui', 'slash') and os.sep != '/':
3063 if ui.configbool('ui', 'slash') and os.sep != '/':
3064 f = lambda fn: util.normpath(fn)
3064 f = lambda fn: util.normpath(fn)
3065 fmt = 'f %%-%ds %%-%ds %%s' % (
3065 fmt = 'f %%-%ds %%-%ds %%s' % (
3066 max([len(abs) for abs in items]),
3066 max([len(abs) for abs in items]),
3067 max([len(m.rel(abs)) for abs in items]))
3067 max([len(m.rel(abs)) for abs in items]))
3068 for abs in items:
3068 for abs in items:
3069 line = fmt % (abs, f(m.rel(abs)), m.exact(abs) and 'exact' or '')
3069 line = fmt % (abs, f(m.rel(abs)), m.exact(abs) and 'exact' or '')
3070 ui.write("%s\n" % line.rstrip())
3070 ui.write("%s\n" % line.rstrip())
3071
3071
3072 @command('debugwireargs',
3072 @command('debugwireargs',
3073 [('', 'three', '', 'three'),
3073 [('', 'three', '', 'three'),
3074 ('', 'four', '', 'four'),
3074 ('', 'four', '', 'four'),
3075 ('', 'five', '', 'five'),
3075 ('', 'five', '', 'five'),
3076 ] + remoteopts,
3076 ] + remoteopts,
3077 _('REPO [OPTIONS]... [ONE [TWO]]'),
3077 _('REPO [OPTIONS]... [ONE [TWO]]'),
3078 norepo=True)
3078 norepo=True)
3079 def debugwireargs(ui, repopath, *vals, **opts):
3079 def debugwireargs(ui, repopath, *vals, **opts):
3080 repo = hg.peer(ui, opts, repopath)
3080 repo = hg.peer(ui, opts, repopath)
3081 for opt in remoteopts:
3081 for opt in remoteopts:
3082 del opts[opt[1]]
3082 del opts[opt[1]]
3083 args = {}
3083 args = {}
3084 for k, v in opts.iteritems():
3084 for k, v in opts.iteritems():
3085 if v:
3085 if v:
3086 args[k] = v
3086 args[k] = v
3087 # run twice to check that we don't mess up the stream for the next command
3087 # run twice to check that we don't mess up the stream for the next command
3088 res1 = repo.debugwireargs(*vals, **args)
3088 res1 = repo.debugwireargs(*vals, **args)
3089 res2 = repo.debugwireargs(*vals, **args)
3089 res2 = repo.debugwireargs(*vals, **args)
3090 ui.write("%s\n" % res1)
3090 ui.write("%s\n" % res1)
3091 if res1 != res2:
3091 if res1 != res2:
3092 ui.warn("%s\n" % res2)
3092 ui.warn("%s\n" % res2)
3093
3093
3094 @command('^diff',
3094 @command('^diff',
3095 [('r', 'rev', [], _('revision'), _('REV')),
3095 [('r', 'rev', [], _('revision'), _('REV')),
3096 ('c', 'change', '', _('change made by revision'), _('REV'))
3096 ('c', 'change', '', _('change made by revision'), _('REV'))
3097 ] + diffopts + diffopts2 + walkopts + subrepoopts,
3097 ] + diffopts + diffopts2 + walkopts + subrepoopts,
3098 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'),
3098 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'),
3099 inferrepo=True)
3099 inferrepo=True)
3100 def diff(ui, repo, *pats, **opts):
3100 def diff(ui, repo, *pats, **opts):
3101 """diff repository (or selected files)
3101 """diff repository (or selected files)
3102
3102
3103 Show differences between revisions for the specified files.
3103 Show differences between revisions for the specified files.
3104
3104
3105 Differences between files are shown using the unified diff format.
3105 Differences between files are shown using the unified diff format.
3106
3106
3107 .. note::
3107 .. note::
3108
3108
3109 diff may generate unexpected results for merges, as it will
3109 diff may generate unexpected results for merges, as it will
3110 default to comparing against the working directory's first
3110 default to comparing against the working directory's first
3111 parent changeset if no revisions are specified.
3111 parent changeset if no revisions are specified.
3112
3112
3113 When two revision arguments are given, then changes are shown
3113 When two revision arguments are given, then changes are shown
3114 between those revisions. If only one revision is specified then
3114 between those revisions. If only one revision is specified then
3115 that revision is compared to the working directory, and, when no
3115 that revision is compared to the working directory, and, when no
3116 revisions are specified, the working directory files are compared
3116 revisions are specified, the working directory files are compared
3117 to its parent.
3117 to its parent.
3118
3118
3119 Alternatively you can specify -c/--change with a revision to see
3119 Alternatively you can specify -c/--change with a revision to see
3120 the changes in that changeset relative to its first parent.
3120 the changes in that changeset relative to its first parent.
3121
3121
3122 Without the -a/--text option, diff will avoid generating diffs of
3122 Without the -a/--text option, diff will avoid generating diffs of
3123 files it detects as binary. With -a, diff will generate a diff
3123 files it detects as binary. With -a, diff will generate a diff
3124 anyway, probably with undesirable results.
3124 anyway, probably with undesirable results.
3125
3125
3126 Use the -g/--git option to generate diffs in the git extended diff
3126 Use the -g/--git option to generate diffs in the git extended diff
3127 format. For more information, read :hg:`help diffs`.
3127 format. For more information, read :hg:`help diffs`.
3128
3128
3129 .. container:: verbose
3129 .. container:: verbose
3130
3130
3131 Examples:
3131 Examples:
3132
3132
3133 - compare a file in the current working directory to its parent::
3133 - compare a file in the current working directory to its parent::
3134
3134
3135 hg diff foo.c
3135 hg diff foo.c
3136
3136
3137 - compare two historical versions of a directory, with rename info::
3137 - compare two historical versions of a directory, with rename info::
3138
3138
3139 hg diff --git -r 1.0:1.2 lib/
3139 hg diff --git -r 1.0:1.2 lib/
3140
3140
3141 - get change stats relative to the last change on some date::
3141 - get change stats relative to the last change on some date::
3142
3142
3143 hg diff --stat -r "date('may 2')"
3143 hg diff --stat -r "date('may 2')"
3144
3144
3145 - diff all newly-added files that contain a keyword::
3145 - diff all newly-added files that contain a keyword::
3146
3146
3147 hg diff "set:added() and grep(GNU)"
3147 hg diff "set:added() and grep(GNU)"
3148
3148
3149 - compare a revision and its parents::
3149 - compare a revision and its parents::
3150
3150
3151 hg diff -c 9353 # compare against first parent
3151 hg diff -c 9353 # compare against first parent
3152 hg diff -r 9353^:9353 # same using revset syntax
3152 hg diff -r 9353^:9353 # same using revset syntax
3153 hg diff -r 9353^2:9353 # compare against the second parent
3153 hg diff -r 9353^2:9353 # compare against the second parent
3154
3154
3155 Returns 0 on success.
3155 Returns 0 on success.
3156 """
3156 """
3157
3157
3158 revs = opts.get('rev')
3158 revs = opts.get('rev')
3159 change = opts.get('change')
3159 change = opts.get('change')
3160 stat = opts.get('stat')
3160 stat = opts.get('stat')
3161 reverse = opts.get('reverse')
3161 reverse = opts.get('reverse')
3162
3162
3163 if revs and change:
3163 if revs and change:
3164 msg = _('cannot specify --rev and --change at the same time')
3164 msg = _('cannot specify --rev and --change at the same time')
3165 raise util.Abort(msg)
3165 raise util.Abort(msg)
3166 elif change:
3166 elif change:
3167 node2 = scmutil.revsingle(repo, change, None).node()
3167 node2 = scmutil.revsingle(repo, change, None).node()
3168 node1 = repo[node2].p1().node()
3168 node1 = repo[node2].p1().node()
3169 else:
3169 else:
3170 node1, node2 = scmutil.revpair(repo, revs)
3170 node1, node2 = scmutil.revpair(repo, revs)
3171
3171
3172 if reverse:
3172 if reverse:
3173 node1, node2 = node2, node1
3173 node1, node2 = node2, node1
3174
3174
3175 diffopts = patch.diffallopts(ui, opts)
3175 diffopts = patch.diffallopts(ui, opts)
3176 m = scmutil.match(repo[node2], pats, opts)
3176 m = scmutil.match(repo[node2], pats, opts)
3177 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
3177 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
3178 listsubrepos=opts.get('subrepos'),
3178 listsubrepos=opts.get('subrepos'),
3179 root=opts.get('root'))
3179 root=opts.get('root'))
3180
3180
3181 @command('^export',
3181 @command('^export',
3182 [('o', 'output', '',
3182 [('o', 'output', '',
3183 _('print output to file with formatted name'), _('FORMAT')),
3183 _('print output to file with formatted name'), _('FORMAT')),
3184 ('', 'switch-parent', None, _('diff against the second parent')),
3184 ('', 'switch-parent', None, _('diff against the second parent')),
3185 ('r', 'rev', [], _('revisions to export'), _('REV')),
3185 ('r', 'rev', [], _('revisions to export'), _('REV')),
3186 ] + diffopts,
3186 ] + diffopts,
3187 _('[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'))
3187 _('[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'))
3188 def export(ui, repo, *changesets, **opts):
3188 def export(ui, repo, *changesets, **opts):
3189 """dump the header and diffs for one or more changesets
3189 """dump the header and diffs for one or more changesets
3190
3190
3191 Print the changeset header and diffs for one or more revisions.
3191 Print the changeset header and diffs for one or more revisions.
3192 If no revision is given, the parent of the working directory is used.
3192 If no revision is given, the parent of the working directory is used.
3193
3193
3194 The information shown in the changeset header is: author, date,
3194 The information shown in the changeset header is: author, date,
3195 branch name (if non-default), changeset hash, parent(s) and commit
3195 branch name (if non-default), changeset hash, parent(s) and commit
3196 comment.
3196 comment.
3197
3197
3198 .. note::
3198 .. note::
3199
3199
3200 export may generate unexpected diff output for merge
3200 export may generate unexpected diff output for merge
3201 changesets, as it will compare the merge changeset against its
3201 changesets, as it will compare the merge changeset against its
3202 first parent only.
3202 first parent only.
3203
3203
3204 Output may be to a file, in which case the name of the file is
3204 Output may be to a file, in which case the name of the file is
3205 given using a format string. The formatting rules are as follows:
3205 given using a format string. The formatting rules are as follows:
3206
3206
3207 :``%%``: literal "%" character
3207 :``%%``: literal "%" character
3208 :``%H``: changeset hash (40 hexadecimal digits)
3208 :``%H``: changeset hash (40 hexadecimal digits)
3209 :``%N``: number of patches being generated
3209 :``%N``: number of patches being generated
3210 :``%R``: changeset revision number
3210 :``%R``: changeset revision number
3211 :``%b``: basename of the exporting repository
3211 :``%b``: basename of the exporting repository
3212 :``%h``: short-form changeset hash (12 hexadecimal digits)
3212 :``%h``: short-form changeset hash (12 hexadecimal digits)
3213 :``%m``: first line of the commit message (only alphanumeric characters)
3213 :``%m``: first line of the commit message (only alphanumeric characters)
3214 :``%n``: zero-padded sequence number, starting at 1
3214 :``%n``: zero-padded sequence number, starting at 1
3215 :``%r``: zero-padded changeset revision number
3215 :``%r``: zero-padded changeset revision number
3216
3216
3217 Without the -a/--text option, export will avoid generating diffs
3217 Without the -a/--text option, export will avoid generating diffs
3218 of files it detects as binary. With -a, export will generate a
3218 of files it detects as binary. With -a, export will generate a
3219 diff anyway, probably with undesirable results.
3219 diff anyway, probably with undesirable results.
3220
3220
3221 Use the -g/--git option to generate diffs in the git extended diff
3221 Use the -g/--git option to generate diffs in the git extended diff
3222 format. See :hg:`help diffs` for more information.
3222 format. See :hg:`help diffs` for more information.
3223
3223
3224 With the --switch-parent option, the diff will be against the
3224 With the --switch-parent option, the diff will be against the
3225 second parent. It can be useful to review a merge.
3225 second parent. It can be useful to review a merge.
3226
3226
3227 .. container:: verbose
3227 .. container:: verbose
3228
3228
3229 Examples:
3229 Examples:
3230
3230
3231 - use export and import to transplant a bugfix to the current
3231 - use export and import to transplant a bugfix to the current
3232 branch::
3232 branch::
3233
3233
3234 hg export -r 9353 | hg import -
3234 hg export -r 9353 | hg import -
3235
3235
3236 - export all the changesets between two revisions to a file with
3236 - export all the changesets between two revisions to a file with
3237 rename information::
3237 rename information::
3238
3238
3239 hg export --git -r 123:150 > changes.txt
3239 hg export --git -r 123:150 > changes.txt
3240
3240
3241 - split outgoing changes into a series of patches with
3241 - split outgoing changes into a series of patches with
3242 descriptive names::
3242 descriptive names::
3243
3243
3244 hg export -r "outgoing()" -o "%n-%m.patch"
3244 hg export -r "outgoing()" -o "%n-%m.patch"
3245
3245
3246 Returns 0 on success.
3246 Returns 0 on success.
3247 """
3247 """
3248 changesets += tuple(opts.get('rev', []))
3248 changesets += tuple(opts.get('rev', []))
3249 if not changesets:
3249 if not changesets:
3250 changesets = ['.']
3250 changesets = ['.']
3251 revs = scmutil.revrange(repo, changesets)
3251 revs = scmutil.revrange(repo, changesets)
3252 if not revs:
3252 if not revs:
3253 raise util.Abort(_("export requires at least one changeset"))
3253 raise util.Abort(_("export requires at least one changeset"))
3254 if len(revs) > 1:
3254 if len(revs) > 1:
3255 ui.note(_('exporting patches:\n'))
3255 ui.note(_('exporting patches:\n'))
3256 else:
3256 else:
3257 ui.note(_('exporting patch:\n'))
3257 ui.note(_('exporting patch:\n'))
3258 cmdutil.export(repo, revs, template=opts.get('output'),
3258 cmdutil.export(repo, revs, template=opts.get('output'),
3259 switch_parent=opts.get('switch_parent'),
3259 switch_parent=opts.get('switch_parent'),
3260 opts=patch.diffallopts(ui, opts))
3260 opts=patch.diffallopts(ui, opts))
3261
3261
3262 @command('files',
3262 @command('files',
3263 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
3263 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
3264 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
3264 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
3265 ] + walkopts + formatteropts + subrepoopts,
3265 ] + walkopts + formatteropts + subrepoopts,
3266 _('[OPTION]... [PATTERN]...'))
3266 _('[OPTION]... [PATTERN]...'))
3267 def files(ui, repo, *pats, **opts):
3267 def files(ui, repo, *pats, **opts):
3268 """list tracked files
3268 """list tracked files
3269
3269
3270 Print files under Mercurial control in the working directory or
3270 Print files under Mercurial control in the working directory or
3271 specified revision whose names match the given patterns (excluding
3271 specified revision whose names match the given patterns (excluding
3272 removed files).
3272 removed files).
3273
3273
3274 If no patterns are given to match, this command prints the names
3274 If no patterns are given to match, this command prints the names
3275 of all files under Mercurial control in the working directory.
3275 of all files under Mercurial control in the working directory.
3276
3276
3277 .. container:: verbose
3277 .. container:: verbose
3278
3278
3279 Examples:
3279 Examples:
3280
3280
3281 - list all files under the current directory::
3281 - list all files under the current directory::
3282
3282
3283 hg files .
3283 hg files .
3284
3284
3285 - shows sizes and flags for current revision::
3285 - shows sizes and flags for current revision::
3286
3286
3287 hg files -vr .
3287 hg files -vr .
3288
3288
3289 - list all files named README::
3289 - list all files named README::
3290
3290
3291 hg files -I "**/README"
3291 hg files -I "**/README"
3292
3292
3293 - list all binary files::
3293 - list all binary files::
3294
3294
3295 hg files "set:binary()"
3295 hg files "set:binary()"
3296
3296
3297 - find files containing a regular expression::
3297 - find files containing a regular expression::
3298
3298
3299 hg files "set:grep('bob')"
3299 hg files "set:grep('bob')"
3300
3300
3301 - search tracked file contents with xargs and grep::
3301 - search tracked file contents with xargs and grep::
3302
3302
3303 hg files -0 | xargs -0 grep foo
3303 hg files -0 | xargs -0 grep foo
3304
3304
3305 See :hg:`help patterns` and :hg:`help filesets` for more information
3305 See :hg:`help patterns` and :hg:`help filesets` for more information
3306 on specifying file patterns.
3306 on specifying file patterns.
3307
3307
3308 Returns 0 if a match is found, 1 otherwise.
3308 Returns 0 if a match is found, 1 otherwise.
3309
3309
3310 """
3310 """
3311 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
3311 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
3312
3312
3313 end = '\n'
3313 end = '\n'
3314 if opts.get('print0'):
3314 if opts.get('print0'):
3315 end = '\0'
3315 end = '\0'
3316 fm = ui.formatter('files', opts)
3316 fm = ui.formatter('files', opts)
3317 fmt = '%s' + end
3317 fmt = '%s' + end
3318
3318
3319 m = scmutil.match(ctx, pats, opts)
3319 m = scmutil.match(ctx, pats, opts)
3320 ret = cmdutil.files(ui, ctx, m, fm, fmt, opts.get('subrepos'))
3320 ret = cmdutil.files(ui, ctx, m, fm, fmt, opts.get('subrepos'))
3321
3321
3322 fm.end()
3322 fm.end()
3323
3323
3324 return ret
3324 return ret
3325
3325
3326 @command('^forget', walkopts, _('[OPTION]... FILE...'), inferrepo=True)
3326 @command('^forget', walkopts, _('[OPTION]... FILE...'), inferrepo=True)
3327 def forget(ui, repo, *pats, **opts):
3327 def forget(ui, repo, *pats, **opts):
3328 """forget the specified files on the next commit
3328 """forget the specified files on the next commit
3329
3329
3330 Mark the specified files so they will no longer be tracked
3330 Mark the specified files so they will no longer be tracked
3331 after the next commit.
3331 after the next commit.
3332
3332
3333 This only removes files from the current branch, not from the
3333 This only removes files from the current branch, not from the
3334 entire project history, and it does not delete them from the
3334 entire project history, and it does not delete them from the
3335 working directory.
3335 working directory.
3336
3336
3337 To undo a forget before the next commit, see :hg:`add`.
3337 To undo a forget before the next commit, see :hg:`add`.
3338
3338
3339 .. container:: verbose
3339 .. container:: verbose
3340
3340
3341 Examples:
3341 Examples:
3342
3342
3343 - forget newly-added binary files::
3343 - forget newly-added binary files::
3344
3344
3345 hg forget "set:added() and binary()"
3345 hg forget "set:added() and binary()"
3346
3346
3347 - forget files that would be excluded by .hgignore::
3347 - forget files that would be excluded by .hgignore::
3348
3348
3349 hg forget "set:hgignore()"
3349 hg forget "set:hgignore()"
3350
3350
3351 Returns 0 on success.
3351 Returns 0 on success.
3352 """
3352 """
3353
3353
3354 if not pats:
3354 if not pats:
3355 raise util.Abort(_('no files specified'))
3355 raise util.Abort(_('no files specified'))
3356
3356
3357 m = scmutil.match(repo[None], pats, opts)
3357 m = scmutil.match(repo[None], pats, opts)
3358 rejected = cmdutil.forget(ui, repo, m, prefix="", explicitonly=False)[0]
3358 rejected = cmdutil.forget(ui, repo, m, prefix="", explicitonly=False)[0]
3359 return rejected and 1 or 0
3359 return rejected and 1 or 0
3360
3360
3361 @command(
3361 @command(
3362 'graft',
3362 'graft',
3363 [('r', 'rev', [], _('revisions to graft'), _('REV')),
3363 [('r', 'rev', [], _('revisions to graft'), _('REV')),
3364 ('c', 'continue', False, _('resume interrupted graft')),
3364 ('c', 'continue', False, _('resume interrupted graft')),
3365 ('e', 'edit', False, _('invoke editor on commit messages')),
3365 ('e', 'edit', False, _('invoke editor on commit messages')),
3366 ('', 'log', None, _('append graft info to log message')),
3366 ('', 'log', None, _('append graft info to log message')),
3367 ('f', 'force', False, _('force graft')),
3367 ('f', 'force', False, _('force graft')),
3368 ('D', 'currentdate', False,
3368 ('D', 'currentdate', False,
3369 _('record the current date as commit date')),
3369 _('record the current date as commit date')),
3370 ('U', 'currentuser', False,
3370 ('U', 'currentuser', False,
3371 _('record the current user as committer'), _('DATE'))]
3371 _('record the current user as committer'), _('DATE'))]
3372 + commitopts2 + mergetoolopts + dryrunopts,
3372 + commitopts2 + mergetoolopts + dryrunopts,
3373 _('[OPTION]... [-r] REV...'))
3373 _('[OPTION]... [-r] REV...'))
3374 def graft(ui, repo, *revs, **opts):
3374 def graft(ui, repo, *revs, **opts):
3375 '''copy changes from other branches onto the current branch
3375 '''copy changes from other branches onto the current branch
3376
3376
3377 This command uses Mercurial's merge logic to copy individual
3377 This command uses Mercurial's merge logic to copy individual
3378 changes from other branches without merging branches in the
3378 changes from other branches without merging branches in the
3379 history graph. This is sometimes known as 'backporting' or
3379 history graph. This is sometimes known as 'backporting' or
3380 'cherry-picking'. By default, graft will copy user, date, and
3380 'cherry-picking'. By default, graft will copy user, date, and
3381 description from the source changesets.
3381 description from the source changesets.
3382
3382
3383 Changesets that are ancestors of the current revision, that have
3383 Changesets that are ancestors of the current revision, that have
3384 already been grafted, or that are merges will be skipped.
3384 already been grafted, or that are merges will be skipped.
3385
3385
3386 If --log is specified, log messages will have a comment appended
3386 If --log is specified, log messages will have a comment appended
3387 of the form::
3387 of the form::
3388
3388
3389 (grafted from CHANGESETHASH)
3389 (grafted from CHANGESETHASH)
3390
3390
3391 If --force is specified, revisions will be grafted even if they
3391 If --force is specified, revisions will be grafted even if they
3392 are already ancestors of or have been grafted to the destination.
3392 are already ancestors of or have been grafted to the destination.
3393 This is useful when the revisions have since been backed out.
3393 This is useful when the revisions have since been backed out.
3394
3394
3395 If a graft merge results in conflicts, the graft process is
3395 If a graft merge results in conflicts, the graft process is
3396 interrupted so that the current merge can be manually resolved.
3396 interrupted so that the current merge can be manually resolved.
3397 Once all conflicts are addressed, the graft process can be
3397 Once all conflicts are addressed, the graft process can be
3398 continued with the -c/--continue option.
3398 continued with the -c/--continue option.
3399
3399
3400 .. note::
3400 .. note::
3401
3401
3402 The -c/--continue option does not reapply earlier options, except
3402 The -c/--continue option does not reapply earlier options, except
3403 for --force.
3403 for --force.
3404
3404
3405 .. container:: verbose
3405 .. container:: verbose
3406
3406
3407 Examples:
3407 Examples:
3408
3408
3409 - copy a single change to the stable branch and edit its description::
3409 - copy a single change to the stable branch and edit its description::
3410
3410
3411 hg update stable
3411 hg update stable
3412 hg graft --edit 9393
3412 hg graft --edit 9393
3413
3413
3414 - graft a range of changesets with one exception, updating dates::
3414 - graft a range of changesets with one exception, updating dates::
3415
3415
3416 hg graft -D "2085::2093 and not 2091"
3416 hg graft -D "2085::2093 and not 2091"
3417
3417
3418 - continue a graft after resolving conflicts::
3418 - continue a graft after resolving conflicts::
3419
3419
3420 hg graft -c
3420 hg graft -c
3421
3421
3422 - show the source of a grafted changeset::
3422 - show the source of a grafted changeset::
3423
3423
3424 hg log --debug -r .
3424 hg log --debug -r .
3425
3425
3426 See :hg:`help revisions` and :hg:`help revsets` for more about
3426 See :hg:`help revisions` and :hg:`help revsets` for more about
3427 specifying revisions.
3427 specifying revisions.
3428
3428
3429 Returns 0 on successful completion.
3429 Returns 0 on successful completion.
3430 '''
3430 '''
3431
3431
3432 revs = list(revs)
3432 revs = list(revs)
3433 revs.extend(opts['rev'])
3433 revs.extend(opts['rev'])
3434
3434
3435 if not opts.get('user') and opts.get('currentuser'):
3435 if not opts.get('user') and opts.get('currentuser'):
3436 opts['user'] = ui.username()
3436 opts['user'] = ui.username()
3437 if not opts.get('date') and opts.get('currentdate'):
3437 if not opts.get('date') and opts.get('currentdate'):
3438 opts['date'] = "%d %d" % util.makedate()
3438 opts['date'] = "%d %d" % util.makedate()
3439
3439
3440 editor = cmdutil.getcommiteditor(editform='graft', **opts)
3440 editor = cmdutil.getcommiteditor(editform='graft', **opts)
3441
3441
3442 cont = False
3442 cont = False
3443 if opts['continue']:
3443 if opts['continue']:
3444 cont = True
3444 cont = True
3445 if revs:
3445 if revs:
3446 raise util.Abort(_("can't specify --continue and revisions"))
3446 raise util.Abort(_("can't specify --continue and revisions"))
3447 # read in unfinished revisions
3447 # read in unfinished revisions
3448 try:
3448 try:
3449 nodes = repo.vfs.read('graftstate').splitlines()
3449 nodes = repo.vfs.read('graftstate').splitlines()
3450 revs = [repo[node].rev() for node in nodes]
3450 revs = [repo[node].rev() for node in nodes]
3451 except IOError, inst:
3451 except IOError, inst:
3452 if inst.errno != errno.ENOENT:
3452 if inst.errno != errno.ENOENT:
3453 raise
3453 raise
3454 raise util.Abort(_("no graft state found, can't continue"))
3454 raise util.Abort(_("no graft state found, can't continue"))
3455 else:
3455 else:
3456 cmdutil.checkunfinished(repo)
3456 cmdutil.checkunfinished(repo)
3457 cmdutil.bailifchanged(repo)
3457 cmdutil.bailifchanged(repo)
3458 if not revs:
3458 if not revs:
3459 raise util.Abort(_('no revisions specified'))
3459 raise util.Abort(_('no revisions specified'))
3460 revs = scmutil.revrange(repo, revs)
3460 revs = scmutil.revrange(repo, revs)
3461
3461
3462 skipped = set()
3462 skipped = set()
3463 # check for merges
3463 # check for merges
3464 for rev in repo.revs('%ld and merge()', revs):
3464 for rev in repo.revs('%ld and merge()', revs):
3465 ui.warn(_('skipping ungraftable merge revision %s\n') % rev)
3465 ui.warn(_('skipping ungraftable merge revision %s\n') % rev)
3466 skipped.add(rev)
3466 skipped.add(rev)
3467 revs = [r for r in revs if r not in skipped]
3467 revs = [r for r in revs if r not in skipped]
3468 if not revs:
3468 if not revs:
3469 return -1
3469 return -1
3470
3470
3471 # Don't check in the --continue case, in effect retaining --force across
3471 # Don't check in the --continue case, in effect retaining --force across
3472 # --continues. That's because without --force, any revisions we decided to
3472 # --continues. That's because without --force, any revisions we decided to
3473 # skip would have been filtered out here, so they wouldn't have made their
3473 # skip would have been filtered out here, so they wouldn't have made their
3474 # way to the graftstate. With --force, any revisions we would have otherwise
3474 # way to the graftstate. With --force, any revisions we would have otherwise
3475 # skipped would not have been filtered out, and if they hadn't been applied
3475 # skipped would not have been filtered out, and if they hadn't been applied
3476 # already, they'd have been in the graftstate.
3476 # already, they'd have been in the graftstate.
3477 if not (cont or opts.get('force')):
3477 if not (cont or opts.get('force')):
3478 # check for ancestors of dest branch
3478 # check for ancestors of dest branch
3479 crev = repo['.'].rev()
3479 crev = repo['.'].rev()
3480 ancestors = repo.changelog.ancestors([crev], inclusive=True)
3480 ancestors = repo.changelog.ancestors([crev], inclusive=True)
3481 # Cannot use x.remove(y) on smart set, this has to be a list.
3481 # Cannot use x.remove(y) on smart set, this has to be a list.
3482 # XXX make this lazy in the future
3482 # XXX make this lazy in the future
3483 revs = list(revs)
3483 revs = list(revs)
3484 # don't mutate while iterating, create a copy
3484 # don't mutate while iterating, create a copy
3485 for rev in list(revs):
3485 for rev in list(revs):
3486 if rev in ancestors:
3486 if rev in ancestors:
3487 ui.warn(_('skipping ancestor revision %d:%s\n') %
3487 ui.warn(_('skipping ancestor revision %d:%s\n') %
3488 (rev, repo[rev]))
3488 (rev, repo[rev]))
3489 # XXX remove on list is slow
3489 # XXX remove on list is slow
3490 revs.remove(rev)
3490 revs.remove(rev)
3491 if not revs:
3491 if not revs:
3492 return -1
3492 return -1
3493
3493
3494 # analyze revs for earlier grafts
3494 # analyze revs for earlier grafts
3495 ids = {}
3495 ids = {}
3496 for ctx in repo.set("%ld", revs):
3496 for ctx in repo.set("%ld", revs):
3497 ids[ctx.hex()] = ctx.rev()
3497 ids[ctx.hex()] = ctx.rev()
3498 n = ctx.extra().get('source')
3498 n = ctx.extra().get('source')
3499 if n:
3499 if n:
3500 ids[n] = ctx.rev()
3500 ids[n] = ctx.rev()
3501
3501
3502 # check ancestors for earlier grafts
3502 # check ancestors for earlier grafts
3503 ui.debug('scanning for duplicate grafts\n')
3503 ui.debug('scanning for duplicate grafts\n')
3504
3504
3505 for rev in repo.changelog.findmissingrevs(revs, [crev]):
3505 for rev in repo.changelog.findmissingrevs(revs, [crev]):
3506 ctx = repo[rev]
3506 ctx = repo[rev]
3507 n = ctx.extra().get('source')
3507 n = ctx.extra().get('source')
3508 if n in ids:
3508 if n in ids:
3509 try:
3509 try:
3510 r = repo[n].rev()
3510 r = repo[n].rev()
3511 except error.RepoLookupError:
3511 except error.RepoLookupError:
3512 r = None
3512 r = None
3513 if r in revs:
3513 if r in revs:
3514 ui.warn(_('skipping revision %d:%s '
3514 ui.warn(_('skipping revision %d:%s '
3515 '(already grafted to %d:%s)\n')
3515 '(already grafted to %d:%s)\n')
3516 % (r, repo[r], rev, ctx))
3516 % (r, repo[r], rev, ctx))
3517 revs.remove(r)
3517 revs.remove(r)
3518 elif ids[n] in revs:
3518 elif ids[n] in revs:
3519 if r is None:
3519 if r is None:
3520 ui.warn(_('skipping already grafted revision %d:%s '
3520 ui.warn(_('skipping already grafted revision %d:%s '
3521 '(%d:%s also has unknown origin %s)\n')
3521 '(%d:%s also has unknown origin %s)\n')
3522 % (ids[n], repo[ids[n]], rev, ctx, n[:12]))
3522 % (ids[n], repo[ids[n]], rev, ctx, n[:12]))
3523 else:
3523 else:
3524 ui.warn(_('skipping already grafted revision %d:%s '
3524 ui.warn(_('skipping already grafted revision %d:%s '
3525 '(%d:%s also has origin %d:%s)\n')
3525 '(%d:%s also has origin %d:%s)\n')
3526 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12]))
3526 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12]))
3527 revs.remove(ids[n])
3527 revs.remove(ids[n])
3528 elif ctx.hex() in ids:
3528 elif ctx.hex() in ids:
3529 r = ids[ctx.hex()]
3529 r = ids[ctx.hex()]
3530 ui.warn(_('skipping already grafted revision %d:%s '
3530 ui.warn(_('skipping already grafted revision %d:%s '
3531 '(was grafted from %d:%s)\n') %
3531 '(was grafted from %d:%s)\n') %
3532 (r, repo[r], rev, ctx))
3532 (r, repo[r], rev, ctx))
3533 revs.remove(r)
3533 revs.remove(r)
3534 if not revs:
3534 if not revs:
3535 return -1
3535 return -1
3536
3536
3537 wlock = repo.wlock()
3537 wlock = repo.wlock()
3538 try:
3538 try:
3539 for pos, ctx in enumerate(repo.set("%ld", revs)):
3539 for pos, ctx in enumerate(repo.set("%ld", revs)):
3540 desc = '%d:%s "%s"' % (ctx.rev(), ctx,
3540 desc = '%d:%s "%s"' % (ctx.rev(), ctx,
3541 ctx.description().split('\n', 1)[0])
3541 ctx.description().split('\n', 1)[0])
3542 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
3542 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
3543 if names:
3543 if names:
3544 desc += ' (%s)' % ' '.join(names)
3544 desc += ' (%s)' % ' '.join(names)
3545 ui.status(_('grafting %s\n') % desc)
3545 ui.status(_('grafting %s\n') % desc)
3546 if opts.get('dry_run'):
3546 if opts.get('dry_run'):
3547 continue
3547 continue
3548
3548
3549 source = ctx.extra().get('source')
3549 source = ctx.extra().get('source')
3550 extra = {}
3550 extra = {}
3551 if source:
3551 if source:
3552 extra['source'] = source
3552 extra['source'] = source
3553 extra['intermediate-source'] = ctx.hex()
3553 extra['intermediate-source'] = ctx.hex()
3554 else:
3554 else:
3555 extra['source'] = ctx.hex()
3555 extra['source'] = ctx.hex()
3556 user = ctx.user()
3556 user = ctx.user()
3557 if opts.get('user'):
3557 if opts.get('user'):
3558 user = opts['user']
3558 user = opts['user']
3559 date = ctx.date()
3559 date = ctx.date()
3560 if opts.get('date'):
3560 if opts.get('date'):
3561 date = opts['date']
3561 date = opts['date']
3562 message = ctx.description()
3562 message = ctx.description()
3563 if opts.get('log'):
3563 if opts.get('log'):
3564 message += '\n(grafted from %s)' % ctx.hex()
3564 message += '\n(grafted from %s)' % ctx.hex()
3565
3565
3566 # we don't merge the first commit when continuing
3566 # we don't merge the first commit when continuing
3567 if not cont:
3567 if not cont:
3568 # perform the graft merge with p1(rev) as 'ancestor'
3568 # perform the graft merge with p1(rev) as 'ancestor'
3569 try:
3569 try:
3570 # ui.forcemerge is an internal variable, do not document
3570 # ui.forcemerge is an internal variable, do not document
3571 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
3571 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
3572 'graft')
3572 'graft')
3573 stats = mergemod.graft(repo, ctx, ctx.p1(),
3573 stats = mergemod.graft(repo, ctx, ctx.p1(),
3574 ['local', 'graft'])
3574 ['local', 'graft'])
3575 finally:
3575 finally:
3576 repo.ui.setconfig('ui', 'forcemerge', '', 'graft')
3576 repo.ui.setconfig('ui', 'forcemerge', '', 'graft')
3577 # report any conflicts
3577 # report any conflicts
3578 if stats and stats[3] > 0:
3578 if stats and stats[3] > 0:
3579 # write out state for --continue
3579 # write out state for --continue
3580 nodelines = [repo[rev].hex() + "\n" for rev in revs[pos:]]
3580 nodelines = [repo[rev].hex() + "\n" for rev in revs[pos:]]
3581 repo.vfs.write('graftstate', ''.join(nodelines))
3581 repo.vfs.write('graftstate', ''.join(nodelines))
3582 raise util.Abort(
3582 raise util.Abort(
3583 _("unresolved conflicts, can't continue"),
3583 _("unresolved conflicts, can't continue"),
3584 hint=_('use hg resolve and hg graft --continue'))
3584 hint=_('use hg resolve and hg graft --continue'))
3585 else:
3585 else:
3586 cont = False
3586 cont = False
3587
3587
3588 # commit
3588 # commit
3589 node = repo.commit(text=message, user=user,
3589 node = repo.commit(text=message, user=user,
3590 date=date, extra=extra, editor=editor)
3590 date=date, extra=extra, editor=editor)
3591 if node is None:
3591 if node is None:
3592 ui.warn(
3592 ui.warn(
3593 _('note: graft of %d:%s created no changes to commit\n') %
3593 _('note: graft of %d:%s created no changes to commit\n') %
3594 (ctx.rev(), ctx))
3594 (ctx.rev(), ctx))
3595 finally:
3595 finally:
3596 wlock.release()
3596 wlock.release()
3597
3597
3598 # remove state when we complete successfully
3598 # remove state when we complete successfully
3599 if not opts.get('dry_run'):
3599 if not opts.get('dry_run'):
3600 util.unlinkpath(repo.join('graftstate'), ignoremissing=True)
3600 util.unlinkpath(repo.join('graftstate'), ignoremissing=True)
3601
3601
3602 return 0
3602 return 0
3603
3603
3604 @command('grep',
3604 @command('grep',
3605 [('0', 'print0', None, _('end fields with NUL')),
3605 [('0', 'print0', None, _('end fields with NUL')),
3606 ('', 'all', None, _('print all revisions that match')),
3606 ('', 'all', None, _('print all revisions that match')),
3607 ('a', 'text', None, _('treat all files as text')),
3607 ('a', 'text', None, _('treat all files as text')),
3608 ('f', 'follow', None,
3608 ('f', 'follow', None,
3609 _('follow changeset history,'
3609 _('follow changeset history,'
3610 ' or file history across copies and renames')),
3610 ' or file history across copies and renames')),
3611 ('i', 'ignore-case', None, _('ignore case when matching')),
3611 ('i', 'ignore-case', None, _('ignore case when matching')),
3612 ('l', 'files-with-matches', None,
3612 ('l', 'files-with-matches', None,
3613 _('print only filenames and revisions that match')),
3613 _('print only filenames and revisions that match')),
3614 ('n', 'line-number', None, _('print matching line numbers')),
3614 ('n', 'line-number', None, _('print matching line numbers')),
3615 ('r', 'rev', [],
3615 ('r', 'rev', [],
3616 _('only search files changed within revision range'), _('REV')),
3616 _('only search files changed within revision range'), _('REV')),
3617 ('u', 'user', None, _('list the author (long with -v)')),
3617 ('u', 'user', None, _('list the author (long with -v)')),
3618 ('d', 'date', None, _('list the date (short with -q)')),
3618 ('d', 'date', None, _('list the date (short with -q)')),
3619 ] + walkopts,
3619 ] + walkopts,
3620 _('[OPTION]... PATTERN [FILE]...'),
3620 _('[OPTION]... PATTERN [FILE]...'),
3621 inferrepo=True)
3621 inferrepo=True)
3622 def grep(ui, repo, pattern, *pats, **opts):
3622 def grep(ui, repo, pattern, *pats, **opts):
3623 """search for a pattern in specified files and revisions
3623 """search for a pattern in specified files and revisions
3624
3624
3625 Search revisions of files for a regular expression.
3625 Search revisions of files for a regular expression.
3626
3626
3627 This command behaves differently than Unix grep. It only accepts
3627 This command behaves differently than Unix grep. It only accepts
3628 Python/Perl regexps. It searches repository history, not the
3628 Python/Perl regexps. It searches repository history, not the
3629 working directory. It always prints the revision number in which a
3629 working directory. It always prints the revision number in which a
3630 match appears.
3630 match appears.
3631
3631
3632 By default, grep only prints output for the first revision of a
3632 By default, grep only prints output for the first revision of a
3633 file in which it finds a match. To get it to print every revision
3633 file in which it finds a match. To get it to print every revision
3634 that contains a change in match status ("-" for a match that
3634 that contains a change in match status ("-" for a match that
3635 becomes a non-match, or "+" for a non-match that becomes a match),
3635 becomes a non-match, or "+" for a non-match that becomes a match),
3636 use the --all flag.
3636 use the --all flag.
3637
3637
3638 Returns 0 if a match is found, 1 otherwise.
3638 Returns 0 if a match is found, 1 otherwise.
3639 """
3639 """
3640 reflags = re.M
3640 reflags = re.M
3641 if opts.get('ignore_case'):
3641 if opts.get('ignore_case'):
3642 reflags |= re.I
3642 reflags |= re.I
3643 try:
3643 try:
3644 regexp = util.re.compile(pattern, reflags)
3644 regexp = util.re.compile(pattern, reflags)
3645 except re.error, inst:
3645 except re.error, inst:
3646 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
3646 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
3647 return 1
3647 return 1
3648 sep, eol = ':', '\n'
3648 sep, eol = ':', '\n'
3649 if opts.get('print0'):
3649 if opts.get('print0'):
3650 sep = eol = '\0'
3650 sep = eol = '\0'
3651
3651
3652 getfile = util.lrucachefunc(repo.file)
3652 getfile = util.lrucachefunc(repo.file)
3653
3653
3654 def matchlines(body):
3654 def matchlines(body):
3655 begin = 0
3655 begin = 0
3656 linenum = 0
3656 linenum = 0
3657 while begin < len(body):
3657 while begin < len(body):
3658 match = regexp.search(body, begin)
3658 match = regexp.search(body, begin)
3659 if not match:
3659 if not match:
3660 break
3660 break
3661 mstart, mend = match.span()
3661 mstart, mend = match.span()
3662 linenum += body.count('\n', begin, mstart) + 1
3662 linenum += body.count('\n', begin, mstart) + 1
3663 lstart = body.rfind('\n', begin, mstart) + 1 or begin
3663 lstart = body.rfind('\n', begin, mstart) + 1 or begin
3664 begin = body.find('\n', mend) + 1 or len(body) + 1
3664 begin = body.find('\n', mend) + 1 or len(body) + 1
3665 lend = begin - 1
3665 lend = begin - 1
3666 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
3666 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
3667
3667
3668 class linestate(object):
3668 class linestate(object):
3669 def __init__(self, line, linenum, colstart, colend):
3669 def __init__(self, line, linenum, colstart, colend):
3670 self.line = line
3670 self.line = line
3671 self.linenum = linenum
3671 self.linenum = linenum
3672 self.colstart = colstart
3672 self.colstart = colstart
3673 self.colend = colend
3673 self.colend = colend
3674
3674
3675 def __hash__(self):
3675 def __hash__(self):
3676 return hash((self.linenum, self.line))
3676 return hash((self.linenum, self.line))
3677
3677
3678 def __eq__(self, other):
3678 def __eq__(self, other):
3679 return self.line == other.line
3679 return self.line == other.line
3680
3680
3681 def __iter__(self):
3681 def __iter__(self):
3682 yield (self.line[:self.colstart], '')
3682 yield (self.line[:self.colstart], '')
3683 yield (self.line[self.colstart:self.colend], 'grep.match')
3683 yield (self.line[self.colstart:self.colend], 'grep.match')
3684 rest = self.line[self.colend:]
3684 rest = self.line[self.colend:]
3685 while rest != '':
3685 while rest != '':
3686 match = regexp.search(rest)
3686 match = regexp.search(rest)
3687 if not match:
3687 if not match:
3688 yield (rest, '')
3688 yield (rest, '')
3689 break
3689 break
3690 mstart, mend = match.span()
3690 mstart, mend = match.span()
3691 yield (rest[:mstart], '')
3691 yield (rest[:mstart], '')
3692 yield (rest[mstart:mend], 'grep.match')
3692 yield (rest[mstart:mend], 'grep.match')
3693 rest = rest[mend:]
3693 rest = rest[mend:]
3694
3694
3695 matches = {}
3695 matches = {}
3696 copies = {}
3696 copies = {}
3697 def grepbody(fn, rev, body):
3697 def grepbody(fn, rev, body):
3698 matches[rev].setdefault(fn, [])
3698 matches[rev].setdefault(fn, [])
3699 m = matches[rev][fn]
3699 m = matches[rev][fn]
3700 for lnum, cstart, cend, line in matchlines(body):
3700 for lnum, cstart, cend, line in matchlines(body):
3701 s = linestate(line, lnum, cstart, cend)
3701 s = linestate(line, lnum, cstart, cend)
3702 m.append(s)
3702 m.append(s)
3703
3703
3704 def difflinestates(a, b):
3704 def difflinestates(a, b):
3705 sm = difflib.SequenceMatcher(None, a, b)
3705 sm = difflib.SequenceMatcher(None, a, b)
3706 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
3706 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
3707 if tag == 'insert':
3707 if tag == 'insert':
3708 for i in xrange(blo, bhi):
3708 for i in xrange(blo, bhi):
3709 yield ('+', b[i])
3709 yield ('+', b[i])
3710 elif tag == 'delete':
3710 elif tag == 'delete':
3711 for i in xrange(alo, ahi):
3711 for i in xrange(alo, ahi):
3712 yield ('-', a[i])
3712 yield ('-', a[i])
3713 elif tag == 'replace':
3713 elif tag == 'replace':
3714 for i in xrange(alo, ahi):
3714 for i in xrange(alo, ahi):
3715 yield ('-', a[i])
3715 yield ('-', a[i])
3716 for i in xrange(blo, bhi):
3716 for i in xrange(blo, bhi):
3717 yield ('+', b[i])
3717 yield ('+', b[i])
3718
3718
3719 def display(fn, ctx, pstates, states):
3719 def display(fn, ctx, pstates, states):
3720 rev = ctx.rev()
3720 rev = ctx.rev()
3721 if ui.quiet:
3721 if ui.quiet:
3722 datefunc = util.shortdate
3722 datefunc = util.shortdate
3723 else:
3723 else:
3724 datefunc = util.datestr
3724 datefunc = util.datestr
3725 found = False
3725 found = False
3726 @util.cachefunc
3726 @util.cachefunc
3727 def binary():
3727 def binary():
3728 flog = getfile(fn)
3728 flog = getfile(fn)
3729 return util.binary(flog.read(ctx.filenode(fn)))
3729 return util.binary(flog.read(ctx.filenode(fn)))
3730
3730
3731 if opts.get('all'):
3731 if opts.get('all'):
3732 iter = difflinestates(pstates, states)
3732 iter = difflinestates(pstates, states)
3733 else:
3733 else:
3734 iter = [('', l) for l in states]
3734 iter = [('', l) for l in states]
3735 for change, l in iter:
3735 for change, l in iter:
3736 cols = [(fn, 'grep.filename'), (str(rev), 'grep.rev')]
3736 cols = [(fn, 'grep.filename'), (str(rev), 'grep.rev')]
3737
3737
3738 if opts.get('line_number'):
3738 if opts.get('line_number'):
3739 cols.append((str(l.linenum), 'grep.linenumber'))
3739 cols.append((str(l.linenum), 'grep.linenumber'))
3740 if opts.get('all'):
3740 if opts.get('all'):
3741 cols.append((change, 'grep.change'))
3741 cols.append((change, 'grep.change'))
3742 if opts.get('user'):
3742 if opts.get('user'):
3743 cols.append((ui.shortuser(ctx.user()), 'grep.user'))
3743 cols.append((ui.shortuser(ctx.user()), 'grep.user'))
3744 if opts.get('date'):
3744 if opts.get('date'):
3745 cols.append((datefunc(ctx.date()), 'grep.date'))
3745 cols.append((datefunc(ctx.date()), 'grep.date'))
3746 for col, label in cols[:-1]:
3746 for col, label in cols[:-1]:
3747 ui.write(col, label=label)
3747 ui.write(col, label=label)
3748 ui.write(sep, label='grep.sep')
3748 ui.write(sep, label='grep.sep')
3749 ui.write(cols[-1][0], label=cols[-1][1])
3749 ui.write(cols[-1][0], label=cols[-1][1])
3750 if not opts.get('files_with_matches'):
3750 if not opts.get('files_with_matches'):
3751 ui.write(sep, label='grep.sep')
3751 ui.write(sep, label='grep.sep')
3752 if not opts.get('text') and binary():
3752 if not opts.get('text') and binary():
3753 ui.write(" Binary file matches")
3753 ui.write(" Binary file matches")
3754 else:
3754 else:
3755 for s, label in l:
3755 for s, label in l:
3756 ui.write(s, label=label)
3756 ui.write(s, label=label)
3757 ui.write(eol)
3757 ui.write(eol)
3758 found = True
3758 found = True
3759 if opts.get('files_with_matches'):
3759 if opts.get('files_with_matches'):
3760 break
3760 break
3761 return found
3761 return found
3762
3762
3763 skip = {}
3763 skip = {}
3764 revfiles = {}
3764 revfiles = {}
3765 matchfn = scmutil.match(repo[None], pats, opts)
3765 matchfn = scmutil.match(repo[None], pats, opts)
3766 found = False
3766 found = False
3767 follow = opts.get('follow')
3767 follow = opts.get('follow')
3768
3768
3769 def prep(ctx, fns):
3769 def prep(ctx, fns):
3770 rev = ctx.rev()
3770 rev = ctx.rev()
3771 pctx = ctx.p1()
3771 pctx = ctx.p1()
3772 parent = pctx.rev()
3772 parent = pctx.rev()
3773 matches.setdefault(rev, {})
3773 matches.setdefault(rev, {})
3774 matches.setdefault(parent, {})
3774 matches.setdefault(parent, {})
3775 files = revfiles.setdefault(rev, [])
3775 files = revfiles.setdefault(rev, [])
3776 for fn in fns:
3776 for fn in fns:
3777 flog = getfile(fn)
3777 flog = getfile(fn)
3778 try:
3778 try:
3779 fnode = ctx.filenode(fn)
3779 fnode = ctx.filenode(fn)
3780 except error.LookupError:
3780 except error.LookupError:
3781 continue
3781 continue
3782
3782
3783 copied = flog.renamed(fnode)
3783 copied = flog.renamed(fnode)
3784 copy = follow and copied and copied[0]
3784 copy = follow and copied and copied[0]
3785 if copy:
3785 if copy:
3786 copies.setdefault(rev, {})[fn] = copy
3786 copies.setdefault(rev, {})[fn] = copy
3787 if fn in skip:
3787 if fn in skip:
3788 if copy:
3788 if copy:
3789 skip[copy] = True
3789 skip[copy] = True
3790 continue
3790 continue
3791 files.append(fn)
3791 files.append(fn)
3792
3792
3793 if fn not in matches[rev]:
3793 if fn not in matches[rev]:
3794 grepbody(fn, rev, flog.read(fnode))
3794 grepbody(fn, rev, flog.read(fnode))
3795
3795
3796 pfn = copy or fn
3796 pfn = copy or fn
3797 if pfn not in matches[parent]:
3797 if pfn not in matches[parent]:
3798 try:
3798 try:
3799 fnode = pctx.filenode(pfn)
3799 fnode = pctx.filenode(pfn)
3800 grepbody(pfn, parent, flog.read(fnode))
3800 grepbody(pfn, parent, flog.read(fnode))
3801 except error.LookupError:
3801 except error.LookupError:
3802 pass
3802 pass
3803
3803
3804 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
3804 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
3805 rev = ctx.rev()
3805 rev = ctx.rev()
3806 parent = ctx.p1().rev()
3806 parent = ctx.p1().rev()
3807 for fn in sorted(revfiles.get(rev, [])):
3807 for fn in sorted(revfiles.get(rev, [])):
3808 states = matches[rev][fn]
3808 states = matches[rev][fn]
3809 copy = copies.get(rev, {}).get(fn)
3809 copy = copies.get(rev, {}).get(fn)
3810 if fn in skip:
3810 if fn in skip:
3811 if copy:
3811 if copy:
3812 skip[copy] = True
3812 skip[copy] = True
3813 continue
3813 continue
3814 pstates = matches.get(parent, {}).get(copy or fn, [])
3814 pstates = matches.get(parent, {}).get(copy or fn, [])
3815 if pstates or states:
3815 if pstates or states:
3816 r = display(fn, ctx, pstates, states)
3816 r = display(fn, ctx, pstates, states)
3817 found = found or r
3817 found = found or r
3818 if r and not opts.get('all'):
3818 if r and not opts.get('all'):
3819 skip[fn] = True
3819 skip[fn] = True
3820 if copy:
3820 if copy:
3821 skip[copy] = True
3821 skip[copy] = True
3822 del matches[rev]
3822 del matches[rev]
3823 del revfiles[rev]
3823 del revfiles[rev]
3824
3824
3825 return not found
3825 return not found
3826
3826
3827 @command('heads',
3827 @command('heads',
3828 [('r', 'rev', '',
3828 [('r', 'rev', '',
3829 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
3829 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
3830 ('t', 'topo', False, _('show topological heads only')),
3830 ('t', 'topo', False, _('show topological heads only')),
3831 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
3831 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
3832 ('c', 'closed', False, _('show normal and closed branch heads')),
3832 ('c', 'closed', False, _('show normal and closed branch heads')),
3833 ] + templateopts,
3833 ] + templateopts,
3834 _('[-ct] [-r STARTREV] [REV]...'))
3834 _('[-ct] [-r STARTREV] [REV]...'))
3835 def heads(ui, repo, *branchrevs, **opts):
3835 def heads(ui, repo, *branchrevs, **opts):
3836 """show branch heads
3836 """show branch heads
3837
3837
3838 With no arguments, show all open branch heads in the repository.
3838 With no arguments, show all open branch heads in the repository.
3839 Branch heads are changesets that have no descendants on the
3839 Branch heads are changesets that have no descendants on the
3840 same branch. They are where development generally takes place and
3840 same branch. They are where development generally takes place and
3841 are the usual targets for update and merge operations.
3841 are the usual targets for update and merge operations.
3842
3842
3843 If one or more REVs are given, only open branch heads on the
3843 If one or more REVs are given, only open branch heads on the
3844 branches associated with the specified changesets are shown. This
3844 branches associated with the specified changesets are shown. This
3845 means that you can use :hg:`heads .` to see the heads on the
3845 means that you can use :hg:`heads .` to see the heads on the
3846 currently checked-out branch.
3846 currently checked-out branch.
3847
3847
3848 If -c/--closed is specified, also show branch heads marked closed
3848 If -c/--closed is specified, also show branch heads marked closed
3849 (see :hg:`commit --close-branch`).
3849 (see :hg:`commit --close-branch`).
3850
3850
3851 If STARTREV is specified, only those heads that are descendants of
3851 If STARTREV is specified, only those heads that are descendants of
3852 STARTREV will be displayed.
3852 STARTREV will be displayed.
3853
3853
3854 If -t/--topo is specified, named branch mechanics will be ignored and only
3854 If -t/--topo is specified, named branch mechanics will be ignored and only
3855 topological heads (changesets with no children) will be shown.
3855 topological heads (changesets with no children) will be shown.
3856
3856
3857 Returns 0 if matching heads are found, 1 if not.
3857 Returns 0 if matching heads are found, 1 if not.
3858 """
3858 """
3859
3859
3860 start = None
3860 start = None
3861 if 'rev' in opts:
3861 if 'rev' in opts:
3862 start = scmutil.revsingle(repo, opts['rev'], None).node()
3862 start = scmutil.revsingle(repo, opts['rev'], None).node()
3863
3863
3864 if opts.get('topo'):
3864 if opts.get('topo'):
3865 heads = [repo[h] for h in repo.heads(start)]
3865 heads = [repo[h] for h in repo.heads(start)]
3866 else:
3866 else:
3867 heads = []
3867 heads = []
3868 for branch in repo.branchmap():
3868 for branch in repo.branchmap():
3869 heads += repo.branchheads(branch, start, opts.get('closed'))
3869 heads += repo.branchheads(branch, start, opts.get('closed'))
3870 heads = [repo[h] for h in heads]
3870 heads = [repo[h] for h in heads]
3871
3871
3872 if branchrevs:
3872 if branchrevs:
3873 branches = set(repo[br].branch() for br in branchrevs)
3873 branches = set(repo[br].branch() for br in branchrevs)
3874 heads = [h for h in heads if h.branch() in branches]
3874 heads = [h for h in heads if h.branch() in branches]
3875
3875
3876 if opts.get('active') and branchrevs:
3876 if opts.get('active') and branchrevs:
3877 dagheads = repo.heads(start)
3877 dagheads = repo.heads(start)
3878 heads = [h for h in heads if h.node() in dagheads]
3878 heads = [h for h in heads if h.node() in dagheads]
3879
3879
3880 if branchrevs:
3880 if branchrevs:
3881 haveheads = set(h.branch() for h in heads)
3881 haveheads = set(h.branch() for h in heads)
3882 if branches - haveheads:
3882 if branches - haveheads:
3883 headless = ', '.join(b for b in branches - haveheads)
3883 headless = ', '.join(b for b in branches - haveheads)
3884 msg = _('no open branch heads found on branches %s')
3884 msg = _('no open branch heads found on branches %s')
3885 if opts.get('rev'):
3885 if opts.get('rev'):
3886 msg += _(' (started at %s)') % opts['rev']
3886 msg += _(' (started at %s)') % opts['rev']
3887 ui.warn((msg + '\n') % headless)
3887 ui.warn((msg + '\n') % headless)
3888
3888
3889 if not heads:
3889 if not heads:
3890 return 1
3890 return 1
3891
3891
3892 heads = sorted(heads, key=lambda x: -x.rev())
3892 heads = sorted(heads, key=lambda x: -x.rev())
3893 displayer = cmdutil.show_changeset(ui, repo, opts)
3893 displayer = cmdutil.show_changeset(ui, repo, opts)
3894 for ctx in heads:
3894 for ctx in heads:
3895 displayer.show(ctx)
3895 displayer.show(ctx)
3896 displayer.close()
3896 displayer.close()
3897
3897
3898 @command('help',
3898 @command('help',
3899 [('e', 'extension', None, _('show only help for extensions')),
3899 [('e', 'extension', None, _('show only help for extensions')),
3900 ('c', 'command', None, _('show only help for commands')),
3900 ('c', 'command', None, _('show only help for commands')),
3901 ('k', 'keyword', '', _('show topics matching keyword')),
3901 ('k', 'keyword', '', _('show topics matching keyword')),
3902 ],
3902 ],
3903 _('[-ec] [TOPIC]'),
3903 _('[-ec] [TOPIC]'),
3904 norepo=True)
3904 norepo=True)
3905 def help_(ui, name=None, **opts):
3905 def help_(ui, name=None, **opts):
3906 """show help for a given topic or a help overview
3906 """show help for a given topic or a help overview
3907
3907
3908 With no arguments, print a list of commands with short help messages.
3908 With no arguments, print a list of commands with short help messages.
3909
3909
3910 Given a topic, extension, or command name, print help for that
3910 Given a topic, extension, or command name, print help for that
3911 topic.
3911 topic.
3912
3912
3913 Returns 0 if successful.
3913 Returns 0 if successful.
3914 """
3914 """
3915
3915
3916 textwidth = min(ui.termwidth(), 80) - 2
3916 textwidth = min(ui.termwidth(), 80) - 2
3917
3917
3918 keep = []
3918 keep = []
3919 if ui.verbose:
3919 if ui.verbose:
3920 keep.append('verbose')
3920 keep.append('verbose')
3921 if sys.platform.startswith('win'):
3921 if sys.platform.startswith('win'):
3922 keep.append('windows')
3922 keep.append('windows')
3923 elif sys.platform == 'OpenVMS':
3923 elif sys.platform == 'OpenVMS':
3924 keep.append('vms')
3924 keep.append('vms')
3925 elif sys.platform == 'plan9':
3925 elif sys.platform == 'plan9':
3926 keep.append('plan9')
3926 keep.append('plan9')
3927 else:
3927 else:
3928 keep.append('unix')
3928 keep.append('unix')
3929 keep.append(sys.platform.lower())
3929 keep.append(sys.platform.lower())
3930
3930
3931 section = None
3931 section = None
3932 if name and '.' in name:
3932 if name and '.' in name:
3933 name, section = name.split('.', 1)
3933 name, section = name.split('.', 1)
3934
3934
3935 text = help.help_(ui, name, **opts)
3935 text = help.help_(ui, name, **opts)
3936
3936
3937 formatted, pruned = minirst.format(text, textwidth, keep=keep,
3937 formatted, pruned = minirst.format(text, textwidth, keep=keep,
3938 section=section)
3938 section=section)
3939 if section and not formatted:
3939 if section and not formatted:
3940 raise util.Abort(_("help section not found"))
3940 raise util.Abort(_("help section not found"))
3941
3941
3942 if 'verbose' in pruned:
3942 if 'verbose' in pruned:
3943 keep.append('omitted')
3943 keep.append('omitted')
3944 else:
3944 else:
3945 keep.append('notomitted')
3945 keep.append('notomitted')
3946 formatted, pruned = minirst.format(text, textwidth, keep=keep,
3946 formatted, pruned = minirst.format(text, textwidth, keep=keep,
3947 section=section)
3947 section=section)
3948 ui.write(formatted)
3948 ui.write(formatted)
3949
3949
3950
3950
3951 @command('identify|id',
3951 @command('identify|id',
3952 [('r', 'rev', '',
3952 [('r', 'rev', '',
3953 _('identify the specified revision'), _('REV')),
3953 _('identify the specified revision'), _('REV')),
3954 ('n', 'num', None, _('show local revision number')),
3954 ('n', 'num', None, _('show local revision number')),
3955 ('i', 'id', None, _('show global revision id')),
3955 ('i', 'id', None, _('show global revision id')),
3956 ('b', 'branch', None, _('show branch')),
3956 ('b', 'branch', None, _('show branch')),
3957 ('t', 'tags', None, _('show tags')),
3957 ('t', 'tags', None, _('show tags')),
3958 ('B', 'bookmarks', None, _('show bookmarks')),
3958 ('B', 'bookmarks', None, _('show bookmarks')),
3959 ] + remoteopts,
3959 ] + remoteopts,
3960 _('[-nibtB] [-r REV] [SOURCE]'),
3960 _('[-nibtB] [-r REV] [SOURCE]'),
3961 optionalrepo=True)
3961 optionalrepo=True)
3962 def identify(ui, repo, source=None, rev=None,
3962 def identify(ui, repo, source=None, rev=None,
3963 num=None, id=None, branch=None, tags=None, bookmarks=None, **opts):
3963 num=None, id=None, branch=None, tags=None, bookmarks=None, **opts):
3964 """identify the working directory or specified revision
3964 """identify the working directory or specified revision
3965
3965
3966 Print a summary identifying the repository state at REV using one or
3966 Print a summary identifying the repository state at REV using one or
3967 two parent hash identifiers, followed by a "+" if the working
3967 two parent hash identifiers, followed by a "+" if the working
3968 directory has uncommitted changes, the branch name (if not default),
3968 directory has uncommitted changes, the branch name (if not default),
3969 a list of tags, and a list of bookmarks.
3969 a list of tags, and a list of bookmarks.
3970
3970
3971 When REV is not given, print a summary of the current state of the
3971 When REV is not given, print a summary of the current state of the
3972 repository.
3972 repository.
3973
3973
3974 Specifying a path to a repository root or Mercurial bundle will
3974 Specifying a path to a repository root or Mercurial bundle will
3975 cause lookup to operate on that repository/bundle.
3975 cause lookup to operate on that repository/bundle.
3976
3976
3977 .. container:: verbose
3977 .. container:: verbose
3978
3978
3979 Examples:
3979 Examples:
3980
3980
3981 - generate a build identifier for the working directory::
3981 - generate a build identifier for the working directory::
3982
3982
3983 hg id --id > build-id.dat
3983 hg id --id > build-id.dat
3984
3984
3985 - find the revision corresponding to a tag::
3985 - find the revision corresponding to a tag::
3986
3986
3987 hg id -n -r 1.3
3987 hg id -n -r 1.3
3988
3988
3989 - check the most recent revision of a remote repository::
3989 - check the most recent revision of a remote repository::
3990
3990
3991 hg id -r tip http://selenic.com/hg/
3991 hg id -r tip http://selenic.com/hg/
3992
3992
3993 Returns 0 if successful.
3993 Returns 0 if successful.
3994 """
3994 """
3995
3995
3996 if not repo and not source:
3996 if not repo and not source:
3997 raise util.Abort(_("there is no Mercurial repository here "
3997 raise util.Abort(_("there is no Mercurial repository here "
3998 "(.hg not found)"))
3998 "(.hg not found)"))
3999
3999
4000 if ui.debugflag:
4000 if ui.debugflag:
4001 hexfunc = hex
4001 hexfunc = hex
4002 else:
4002 else:
4003 hexfunc = short
4003 hexfunc = short
4004 default = not (num or id or branch or tags or bookmarks)
4004 default = not (num or id or branch or tags or bookmarks)
4005 output = []
4005 output = []
4006 revs = []
4006 revs = []
4007
4007
4008 if source:
4008 if source:
4009 source, branches = hg.parseurl(ui.expandpath(source))
4009 source, branches = hg.parseurl(ui.expandpath(source))
4010 peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo
4010 peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo
4011 repo = peer.local()
4011 repo = peer.local()
4012 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
4012 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
4013
4013
4014 if not repo:
4014 if not repo:
4015 if num or branch or tags:
4015 if num or branch or tags:
4016 raise util.Abort(
4016 raise util.Abort(
4017 _("can't query remote revision number, branch, or tags"))
4017 _("can't query remote revision number, branch, or tags"))
4018 if not rev and revs:
4018 if not rev and revs:
4019 rev = revs[0]
4019 rev = revs[0]
4020 if not rev:
4020 if not rev:
4021 rev = "tip"
4021 rev = "tip"
4022
4022
4023 remoterev = peer.lookup(rev)
4023 remoterev = peer.lookup(rev)
4024 if default or id:
4024 if default or id:
4025 output = [hexfunc(remoterev)]
4025 output = [hexfunc(remoterev)]
4026
4026
4027 def getbms():
4027 def getbms():
4028 bms = []
4028 bms = []
4029
4029
4030 if 'bookmarks' in peer.listkeys('namespaces'):
4030 if 'bookmarks' in peer.listkeys('namespaces'):
4031 hexremoterev = hex(remoterev)
4031 hexremoterev = hex(remoterev)
4032 bms = [bm for bm, bmr in peer.listkeys('bookmarks').iteritems()
4032 bms = [bm for bm, bmr in peer.listkeys('bookmarks').iteritems()
4033 if bmr == hexremoterev]
4033 if bmr == hexremoterev]
4034
4034
4035 return sorted(bms)
4035 return sorted(bms)
4036
4036
4037 if bookmarks:
4037 if bookmarks:
4038 output.extend(getbms())
4038 output.extend(getbms())
4039 elif default and not ui.quiet:
4039 elif default and not ui.quiet:
4040 # multiple bookmarks for a single parent separated by '/'
4040 # multiple bookmarks for a single parent separated by '/'
4041 bm = '/'.join(getbms())
4041 bm = '/'.join(getbms())
4042 if bm:
4042 if bm:
4043 output.append(bm)
4043 output.append(bm)
4044 else:
4044 else:
4045 if not rev:
4045 if not rev:
4046 ctx = repo[None]
4046 ctx = repo[None]
4047 parents = ctx.parents()
4047 parents = ctx.parents()
4048 changed = ""
4048 changed = ""
4049 if default or id or num:
4049 if default or id or num:
4050 if (any(repo.status())
4050 if (any(repo.status())
4051 or any(ctx.sub(s).dirty() for s in ctx.substate)):
4051 or any(ctx.sub(s).dirty() for s in ctx.substate)):
4052 changed = '+'
4052 changed = '+'
4053 if default or id:
4053 if default or id:
4054 output = ["%s%s" %
4054 output = ["%s%s" %
4055 ('+'.join([hexfunc(p.node()) for p in parents]), changed)]
4055 ('+'.join([hexfunc(p.node()) for p in parents]), changed)]
4056 if num:
4056 if num:
4057 output.append("%s%s" %
4057 output.append("%s%s" %
4058 ('+'.join([str(p.rev()) for p in parents]), changed))
4058 ('+'.join([str(p.rev()) for p in parents]), changed))
4059 else:
4059 else:
4060 ctx = scmutil.revsingle(repo, rev)
4060 ctx = scmutil.revsingle(repo, rev)
4061 if default or id:
4061 if default or id:
4062 output = [hexfunc(ctx.node())]
4062 output = [hexfunc(ctx.node())]
4063 if num:
4063 if num:
4064 output.append(str(ctx.rev()))
4064 output.append(str(ctx.rev()))
4065
4065
4066 if default and not ui.quiet:
4066 if default and not ui.quiet:
4067 b = ctx.branch()
4067 b = ctx.branch()
4068 if b != 'default':
4068 if b != 'default':
4069 output.append("(%s)" % b)
4069 output.append("(%s)" % b)
4070
4070
4071 # multiple tags for a single parent separated by '/'
4071 # multiple tags for a single parent separated by '/'
4072 t = '/'.join(ctx.tags())
4072 t = '/'.join(ctx.tags())
4073 if t:
4073 if t:
4074 output.append(t)
4074 output.append(t)
4075
4075
4076 # multiple bookmarks for a single parent separated by '/'
4076 # multiple bookmarks for a single parent separated by '/'
4077 bm = '/'.join(ctx.bookmarks())
4077 bm = '/'.join(ctx.bookmarks())
4078 if bm:
4078 if bm:
4079 output.append(bm)
4079 output.append(bm)
4080 else:
4080 else:
4081 if branch:
4081 if branch:
4082 output.append(ctx.branch())
4082 output.append(ctx.branch())
4083
4083
4084 if tags:
4084 if tags:
4085 output.extend(ctx.tags())
4085 output.extend(ctx.tags())
4086
4086
4087 if bookmarks:
4087 if bookmarks:
4088 output.extend(ctx.bookmarks())
4088 output.extend(ctx.bookmarks())
4089
4089
4090 ui.write("%s\n" % ' '.join(output))
4090 ui.write("%s\n" % ' '.join(output))
4091
4091
4092 @command('import|patch',
4092 @command('import|patch',
4093 [('p', 'strip', 1,
4093 [('p', 'strip', 1,
4094 _('directory strip option for patch. This has the same '
4094 _('directory strip option for patch. This has the same '
4095 'meaning as the corresponding patch option'), _('NUM')),
4095 'meaning as the corresponding patch option'), _('NUM')),
4096 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
4096 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
4097 ('e', 'edit', False, _('invoke editor on commit messages')),
4097 ('e', 'edit', False, _('invoke editor on commit messages')),
4098 ('f', 'force', None,
4098 ('f', 'force', None,
4099 _('skip check for outstanding uncommitted changes (DEPRECATED)')),
4099 _('skip check for outstanding uncommitted changes (DEPRECATED)')),
4100 ('', 'no-commit', None,
4100 ('', 'no-commit', None,
4101 _("don't commit, just update the working directory")),
4101 _("don't commit, just update the working directory")),
4102 ('', 'bypass', None,
4102 ('', 'bypass', None,
4103 _("apply patch without touching the working directory")),
4103 _("apply patch without touching the working directory")),
4104 ('', 'partial', None,
4104 ('', 'partial', None,
4105 _('commit even if some hunks fail')),
4105 _('commit even if some hunks fail')),
4106 ('', 'exact', None,
4106 ('', 'exact', None,
4107 _('apply patch to the nodes from which it was generated')),
4107 _('apply patch to the nodes from which it was generated')),
4108 ('', 'prefix', '',
4108 ('', 'prefix', '',
4109 _('apply patch to subdirectory'), _('DIR')),
4109 _('apply patch to subdirectory'), _('DIR')),
4110 ('', 'import-branch', None,
4110 ('', 'import-branch', None,
4111 _('use any branch information in patch (implied by --exact)'))] +
4111 _('use any branch information in patch (implied by --exact)'))] +
4112 commitopts + commitopts2 + similarityopts,
4112 commitopts + commitopts2 + similarityopts,
4113 _('[OPTION]... PATCH...'))
4113 _('[OPTION]... PATCH...'))
4114 def import_(ui, repo, patch1=None, *patches, **opts):
4114 def import_(ui, repo, patch1=None, *patches, **opts):
4115 """import an ordered set of patches
4115 """import an ordered set of patches
4116
4116
4117 Import a list of patches and commit them individually (unless
4117 Import a list of patches and commit them individually (unless
4118 --no-commit is specified).
4118 --no-commit is specified).
4119
4119
4120 Because import first applies changes to the working directory,
4120 Because import first applies changes to the working directory,
4121 import will abort if there are outstanding changes.
4121 import will abort if there are outstanding changes.
4122
4122
4123 You can import a patch straight from a mail message. Even patches
4123 You can import a patch straight from a mail message. Even patches
4124 as attachments work (to use the body part, it must have type
4124 as attachments work (to use the body part, it must have type
4125 text/plain or text/x-patch). From and Subject headers of email
4125 text/plain or text/x-patch). From and Subject headers of email
4126 message are used as default committer and commit message. All
4126 message are used as default committer and commit message. All
4127 text/plain body parts before first diff are added to commit
4127 text/plain body parts before first diff are added to commit
4128 message.
4128 message.
4129
4129
4130 If the imported patch was generated by :hg:`export`, user and
4130 If the imported patch was generated by :hg:`export`, user and
4131 description from patch override values from message headers and
4131 description from patch override values from message headers and
4132 body. Values given on command line with -m/--message and -u/--user
4132 body. Values given on command line with -m/--message and -u/--user
4133 override these.
4133 override these.
4134
4134
4135 If --exact is specified, import will set the working directory to
4135 If --exact is specified, import will set the working directory to
4136 the parent of each patch before applying it, and will abort if the
4136 the parent of each patch before applying it, and will abort if the
4137 resulting changeset has a different ID than the one recorded in
4137 resulting changeset has a different ID than the one recorded in
4138 the patch. This may happen due to character set problems or other
4138 the patch. This may happen due to character set problems or other
4139 deficiencies in the text patch format.
4139 deficiencies in the text patch format.
4140
4140
4141 Use --bypass to apply and commit patches directly to the
4141 Use --bypass to apply and commit patches directly to the
4142 repository, not touching the working directory. Without --exact,
4142 repository, not touching the working directory. Without --exact,
4143 patches will be applied on top of the working directory parent
4143 patches will be applied on top of the working directory parent
4144 revision.
4144 revision.
4145
4145
4146 With -s/--similarity, hg will attempt to discover renames and
4146 With -s/--similarity, hg will attempt to discover renames and
4147 copies in the patch in the same way as :hg:`addremove`.
4147 copies in the patch in the same way as :hg:`addremove`.
4148
4148
4149 Use --partial to ensure a changeset will be created from the patch
4149 Use --partial to ensure a changeset will be created from the patch
4150 even if some hunks fail to apply. Hunks that fail to apply will be
4150 even if some hunks fail to apply. Hunks that fail to apply will be
4151 written to a <target-file>.rej file. Conflicts can then be resolved
4151 written to a <target-file>.rej file. Conflicts can then be resolved
4152 by hand before :hg:`commit --amend` is run to update the created
4152 by hand before :hg:`commit --amend` is run to update the created
4153 changeset. This flag exists to let people import patches that
4153 changeset. This flag exists to let people import patches that
4154 partially apply without losing the associated metadata (author,
4154 partially apply without losing the associated metadata (author,
4155 date, description, ...). Note that when none of the hunk applies
4155 date, description, ...). Note that when none of the hunk applies
4156 cleanly, :hg:`import --partial` will create an empty changeset,
4156 cleanly, :hg:`import --partial` will create an empty changeset,
4157 importing only the patch metadata.
4157 importing only the patch metadata.
4158
4158
4159 To read a patch from standard input, use "-" as the patch name. If
4159 To read a patch from standard input, use "-" as the patch name. If
4160 a URL is specified, the patch will be downloaded from it.
4160 a URL is specified, the patch will be downloaded from it.
4161 See :hg:`help dates` for a list of formats valid for -d/--date.
4161 See :hg:`help dates` for a list of formats valid for -d/--date.
4162
4162
4163 .. container:: verbose
4163 .. container:: verbose
4164
4164
4165 Examples:
4165 Examples:
4166
4166
4167 - import a traditional patch from a website and detect renames::
4167 - import a traditional patch from a website and detect renames::
4168
4168
4169 hg import -s 80 http://example.com/bugfix.patch
4169 hg import -s 80 http://example.com/bugfix.patch
4170
4170
4171 - import a changeset from an hgweb server::
4171 - import a changeset from an hgweb server::
4172
4172
4173 hg import http://www.selenic.com/hg/rev/5ca8c111e9aa
4173 hg import http://www.selenic.com/hg/rev/5ca8c111e9aa
4174
4174
4175 - import all the patches in an Unix-style mbox::
4175 - import all the patches in an Unix-style mbox::
4176
4176
4177 hg import incoming-patches.mbox
4177 hg import incoming-patches.mbox
4178
4178
4179 - attempt to exactly restore an exported changeset (not always
4179 - attempt to exactly restore an exported changeset (not always
4180 possible)::
4180 possible)::
4181
4181
4182 hg import --exact proposed-fix.patch
4182 hg import --exact proposed-fix.patch
4183
4183
4184 Returns 0 on success, 1 on partial success (see --partial).
4184 Returns 0 on success, 1 on partial success (see --partial).
4185 """
4185 """
4186
4186
4187 if not patch1:
4187 if not patch1:
4188 raise util.Abort(_('need at least one patch to import'))
4188 raise util.Abort(_('need at least one patch to import'))
4189
4189
4190 patches = (patch1,) + patches
4190 patches = (patch1,) + patches
4191
4191
4192 date = opts.get('date')
4192 date = opts.get('date')
4193 if date:
4193 if date:
4194 opts['date'] = util.parsedate(date)
4194 opts['date'] = util.parsedate(date)
4195
4195
4196 update = not opts.get('bypass')
4196 update = not opts.get('bypass')
4197 if not update and opts.get('no_commit'):
4197 if not update and opts.get('no_commit'):
4198 raise util.Abort(_('cannot use --no-commit with --bypass'))
4198 raise util.Abort(_('cannot use --no-commit with --bypass'))
4199 try:
4199 try:
4200 sim = float(opts.get('similarity') or 0)
4200 sim = float(opts.get('similarity') or 0)
4201 except ValueError:
4201 except ValueError:
4202 raise util.Abort(_('similarity must be a number'))
4202 raise util.Abort(_('similarity must be a number'))
4203 if sim < 0 or sim > 100:
4203 if sim < 0 or sim > 100:
4204 raise util.Abort(_('similarity must be between 0 and 100'))
4204 raise util.Abort(_('similarity must be between 0 and 100'))
4205 if sim and not update:
4205 if sim and not update:
4206 raise util.Abort(_('cannot use --similarity with --bypass'))
4206 raise util.Abort(_('cannot use --similarity with --bypass'))
4207 if opts.get('exact') and opts.get('edit'):
4207 if opts.get('exact') and opts.get('edit'):
4208 raise util.Abort(_('cannot use --exact with --edit'))
4208 raise util.Abort(_('cannot use --exact with --edit'))
4209 if opts.get('exact') and opts.get('prefix'):
4209 if opts.get('exact') and opts.get('prefix'):
4210 raise util.Abort(_('cannot use --exact with --prefix'))
4210 raise util.Abort(_('cannot use --exact with --prefix'))
4211
4211
4212 if update:
4212 if update:
4213 cmdutil.checkunfinished(repo)
4213 cmdutil.checkunfinished(repo)
4214 if (opts.get('exact') or not opts.get('force')) and update:
4214 if (opts.get('exact') or not opts.get('force')) and update:
4215 cmdutil.bailifchanged(repo)
4215 cmdutil.bailifchanged(repo)
4216
4216
4217 base = opts["base"]
4217 base = opts["base"]
4218 wlock = dsguard = lock = tr = None
4218 wlock = dsguard = lock = tr = None
4219 msgs = []
4219 msgs = []
4220 ret = 0
4220 ret = 0
4221
4221
4222
4222
4223 try:
4223 try:
4224 try:
4224 try:
4225 wlock = repo.wlock()
4225 wlock = repo.wlock()
4226 dsguard = cmdutil.dirstateguard(repo, 'import')
4226 dsguard = cmdutil.dirstateguard(repo, 'import')
4227 if not opts.get('no_commit'):
4227 if not opts.get('no_commit'):
4228 lock = repo.lock()
4228 lock = repo.lock()
4229 tr = repo.transaction('import')
4229 tr = repo.transaction('import')
4230 parents = repo.parents()
4230 parents = repo.parents()
4231 for patchurl in patches:
4231 for patchurl in patches:
4232 if patchurl == '-':
4232 if patchurl == '-':
4233 ui.status(_('applying patch from stdin\n'))
4233 ui.status(_('applying patch from stdin\n'))
4234 patchfile = ui.fin
4234 patchfile = ui.fin
4235 patchurl = 'stdin' # for error message
4235 patchurl = 'stdin' # for error message
4236 else:
4236 else:
4237 patchurl = os.path.join(base, patchurl)
4237 patchurl = os.path.join(base, patchurl)
4238 ui.status(_('applying %s\n') % patchurl)
4238 ui.status(_('applying %s\n') % patchurl)
4239 patchfile = hg.openpath(ui, patchurl)
4239 patchfile = hg.openpath(ui, patchurl)
4240
4240
4241 haspatch = False
4241 haspatch = False
4242 for hunk in patch.split(patchfile):
4242 for hunk in patch.split(patchfile):
4243 (msg, node, rej) = cmdutil.tryimportone(ui, repo, hunk,
4243 (msg, node, rej) = cmdutil.tryimportone(ui, repo, hunk,
4244 parents, opts,
4244 parents, opts,
4245 msgs, hg.clean)
4245 msgs, hg.clean)
4246 if msg:
4246 if msg:
4247 haspatch = True
4247 haspatch = True
4248 ui.note(msg + '\n')
4248 ui.note(msg + '\n')
4249 if update or opts.get('exact'):
4249 if update or opts.get('exact'):
4250 parents = repo.parents()
4250 parents = repo.parents()
4251 else:
4251 else:
4252 parents = [repo[node]]
4252 parents = [repo[node]]
4253 if rej:
4253 if rej:
4254 ui.write_err(_("patch applied partially\n"))
4254 ui.write_err(_("patch applied partially\n"))
4255 ui.write_err(_("(fix the .rej files and run "
4255 ui.write_err(_("(fix the .rej files and run "
4256 "`hg commit --amend`)\n"))
4256 "`hg commit --amend`)\n"))
4257 ret = 1
4257 ret = 1
4258 break
4258 break
4259
4259
4260 if not haspatch:
4260 if not haspatch:
4261 raise util.Abort(_('%s: no diffs found') % patchurl)
4261 raise util.Abort(_('%s: no diffs found') % patchurl)
4262
4262
4263 if tr:
4263 if tr:
4264 tr.close()
4264 tr.close()
4265 if msgs:
4265 if msgs:
4266 repo.savecommitmessage('\n* * *\n'.join(msgs))
4266 repo.savecommitmessage('\n* * *\n'.join(msgs))
4267 dsguard.close()
4267 dsguard.close()
4268 return ret
4268 return ret
4269 finally:
4269 finally:
4270 # TODO: get rid of this meaningless try/finally enclosing.
4270 # TODO: get rid of this meaningless try/finally enclosing.
4271 # this is kept only to reduce changes in a patch.
4271 # this is kept only to reduce changes in a patch.
4272 pass
4272 pass
4273 finally:
4273 finally:
4274 if tr:
4274 if tr:
4275 tr.release()
4275 tr.release()
4276 release(lock, dsguard, wlock)
4276 release(lock, dsguard, wlock)
4277
4277
4278 @command('incoming|in',
4278 @command('incoming|in',
4279 [('f', 'force', None,
4279 [('f', 'force', None,
4280 _('run even if remote repository is unrelated')),
4280 _('run even if remote repository is unrelated')),
4281 ('n', 'newest-first', None, _('show newest record first')),
4281 ('n', 'newest-first', None, _('show newest record first')),
4282 ('', 'bundle', '',
4282 ('', 'bundle', '',
4283 _('file to store the bundles into'), _('FILE')),
4283 _('file to store the bundles into'), _('FILE')),
4284 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
4284 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
4285 ('B', 'bookmarks', False, _("compare bookmarks")),
4285 ('B', 'bookmarks', False, _("compare bookmarks")),
4286 ('b', 'branch', [],
4286 ('b', 'branch', [],
4287 _('a specific branch you would like to pull'), _('BRANCH')),
4287 _('a specific branch you would like to pull'), _('BRANCH')),
4288 ] + logopts + remoteopts + subrepoopts,
4288 ] + logopts + remoteopts + subrepoopts,
4289 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'))
4289 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'))
4290 def incoming(ui, repo, source="default", **opts):
4290 def incoming(ui, repo, source="default", **opts):
4291 """show new changesets found in source
4291 """show new changesets found in source
4292
4292
4293 Show new changesets found in the specified path/URL or the default
4293 Show new changesets found in the specified path/URL or the default
4294 pull location. These are the changesets that would have been pulled
4294 pull location. These are the changesets that would have been pulled
4295 if a pull at the time you issued this command.
4295 if a pull at the time you issued this command.
4296
4296
4297 See pull for valid source format details.
4297 See pull for valid source format details.
4298
4298
4299 .. container:: verbose
4299 .. container:: verbose
4300
4300
4301 With -B/--bookmarks, the result of bookmark comparison between
4301 With -B/--bookmarks, the result of bookmark comparison between
4302 local and remote repositories is displayed. With -v/--verbose,
4302 local and remote repositories is displayed. With -v/--verbose,
4303 status is also displayed for each bookmark like below::
4303 status is also displayed for each bookmark like below::
4304
4304
4305 BM1 01234567890a added
4305 BM1 01234567890a added
4306 BM2 1234567890ab advanced
4306 BM2 1234567890ab advanced
4307 BM3 234567890abc diverged
4307 BM3 234567890abc diverged
4308 BM4 34567890abcd changed
4308 BM4 34567890abcd changed
4309
4309
4310 The action taken locally when pulling depends on the
4310 The action taken locally when pulling depends on the
4311 status of each bookmark:
4311 status of each bookmark:
4312
4312
4313 :``added``: pull will create it
4313 :``added``: pull will create it
4314 :``advanced``: pull will update it
4314 :``advanced``: pull will update it
4315 :``diverged``: pull will create a divergent bookmark
4315 :``diverged``: pull will create a divergent bookmark
4316 :``changed``: result depends on remote changesets
4316 :``changed``: result depends on remote changesets
4317
4317
4318 From the point of view of pulling behavior, bookmark
4318 From the point of view of pulling behavior, bookmark
4319 existing only in the remote repository are treated as ``added``,
4319 existing only in the remote repository are treated as ``added``,
4320 even if it is in fact locally deleted.
4320 even if it is in fact locally deleted.
4321
4321
4322 .. container:: verbose
4322 .. container:: verbose
4323
4323
4324 For remote repository, using --bundle avoids downloading the
4324 For remote repository, using --bundle avoids downloading the
4325 changesets twice if the incoming is followed by a pull.
4325 changesets twice if the incoming is followed by a pull.
4326
4326
4327 Examples:
4327 Examples:
4328
4328
4329 - show incoming changes with patches and full description::
4329 - show incoming changes with patches and full description::
4330
4330
4331 hg incoming -vp
4331 hg incoming -vp
4332
4332
4333 - show incoming changes excluding merges, store a bundle::
4333 - show incoming changes excluding merges, store a bundle::
4334
4334
4335 hg in -vpM --bundle incoming.hg
4335 hg in -vpM --bundle incoming.hg
4336 hg pull incoming.hg
4336 hg pull incoming.hg
4337
4337
4338 - briefly list changes inside a bundle::
4338 - briefly list changes inside a bundle::
4339
4339
4340 hg in changes.hg -T "{desc|firstline}\\n"
4340 hg in changes.hg -T "{desc|firstline}\\n"
4341
4341
4342 Returns 0 if there are incoming changes, 1 otherwise.
4342 Returns 0 if there are incoming changes, 1 otherwise.
4343 """
4343 """
4344 if opts.get('graph'):
4344 if opts.get('graph'):
4345 cmdutil.checkunsupportedgraphflags([], opts)
4345 cmdutil.checkunsupportedgraphflags([], opts)
4346 def display(other, chlist, displayer):
4346 def display(other, chlist, displayer):
4347 revdag = cmdutil.graphrevs(other, chlist, opts)
4347 revdag = cmdutil.graphrevs(other, chlist, opts)
4348 showparents = [ctx.node() for ctx in repo[None].parents()]
4348 showparents = [ctx.node() for ctx in repo[None].parents()]
4349 cmdutil.displaygraph(ui, revdag, displayer, showparents,
4349 cmdutil.displaygraph(ui, revdag, displayer, showparents,
4350 graphmod.asciiedges)
4350 graphmod.asciiedges)
4351
4351
4352 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
4352 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
4353 return 0
4353 return 0
4354
4354
4355 if opts.get('bundle') and opts.get('subrepos'):
4355 if opts.get('bundle') and opts.get('subrepos'):
4356 raise util.Abort(_('cannot combine --bundle and --subrepos'))
4356 raise util.Abort(_('cannot combine --bundle and --subrepos'))
4357
4357
4358 if opts.get('bookmarks'):
4358 if opts.get('bookmarks'):
4359 source, branches = hg.parseurl(ui.expandpath(source),
4359 source, branches = hg.parseurl(ui.expandpath(source),
4360 opts.get('branch'))
4360 opts.get('branch'))
4361 other = hg.peer(repo, opts, source)
4361 other = hg.peer(repo, opts, source)
4362 if 'bookmarks' not in other.listkeys('namespaces'):
4362 if 'bookmarks' not in other.listkeys('namespaces'):
4363 ui.warn(_("remote doesn't support bookmarks\n"))
4363 ui.warn(_("remote doesn't support bookmarks\n"))
4364 return 0
4364 return 0
4365 ui.status(_('comparing with %s\n') % util.hidepassword(source))
4365 ui.status(_('comparing with %s\n') % util.hidepassword(source))
4366 return bookmarks.incoming(ui, repo, other)
4366 return bookmarks.incoming(ui, repo, other)
4367
4367
4368 repo._subtoppath = ui.expandpath(source)
4368 repo._subtoppath = ui.expandpath(source)
4369 try:
4369 try:
4370 return hg.incoming(ui, repo, source, opts)
4370 return hg.incoming(ui, repo, source, opts)
4371 finally:
4371 finally:
4372 del repo._subtoppath
4372 del repo._subtoppath
4373
4373
4374
4374
4375 @command('^init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'),
4375 @command('^init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'),
4376 norepo=True)
4376 norepo=True)
4377 def init(ui, dest=".", **opts):
4377 def init(ui, dest=".", **opts):
4378 """create a new repository in the given directory
4378 """create a new repository in the given directory
4379
4379
4380 Initialize a new repository in the given directory. If the given
4380 Initialize a new repository in the given directory. If the given
4381 directory does not exist, it will be created.
4381 directory does not exist, it will be created.
4382
4382
4383 If no directory is given, the current directory is used.
4383 If no directory is given, the current directory is used.
4384
4384
4385 It is possible to specify an ``ssh://`` URL as the destination.
4385 It is possible to specify an ``ssh://`` URL as the destination.
4386 See :hg:`help urls` for more information.
4386 See :hg:`help urls` for more information.
4387
4387
4388 Returns 0 on success.
4388 Returns 0 on success.
4389 """
4389 """
4390 hg.peer(ui, opts, ui.expandpath(dest), create=True)
4390 hg.peer(ui, opts, ui.expandpath(dest), create=True)
4391
4391
4392 @command('locate',
4392 @command('locate',
4393 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
4393 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
4394 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
4394 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
4395 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
4395 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
4396 ] + walkopts,
4396 ] + walkopts,
4397 _('[OPTION]... [PATTERN]...'))
4397 _('[OPTION]... [PATTERN]...'))
4398 def locate(ui, repo, *pats, **opts):
4398 def locate(ui, repo, *pats, **opts):
4399 """locate files matching specific patterns (DEPRECATED)
4399 """locate files matching specific patterns (DEPRECATED)
4400
4400
4401 Print files under Mercurial control in the working directory whose
4401 Print files under Mercurial control in the working directory whose
4402 names match the given patterns.
4402 names match the given patterns.
4403
4403
4404 By default, this command searches all directories in the working
4404 By default, this command searches all directories in the working
4405 directory. To search just the current directory and its
4405 directory. To search just the current directory and its
4406 subdirectories, use "--include .".
4406 subdirectories, use "--include .".
4407
4407
4408 If no patterns are given to match, this command prints the names
4408 If no patterns are given to match, this command prints the names
4409 of all files under Mercurial control in the working directory.
4409 of all files under Mercurial control in the working directory.
4410
4410
4411 If you want to feed the output of this command into the "xargs"
4411 If you want to feed the output of this command into the "xargs"
4412 command, use the -0 option to both this command and "xargs". This
4412 command, use the -0 option to both this command and "xargs". This
4413 will avoid the problem of "xargs" treating single filenames that
4413 will avoid the problem of "xargs" treating single filenames that
4414 contain whitespace as multiple filenames.
4414 contain whitespace as multiple filenames.
4415
4415
4416 See :hg:`help files` for a more versatile command.
4416 See :hg:`help files` for a more versatile command.
4417
4417
4418 Returns 0 if a match is found, 1 otherwise.
4418 Returns 0 if a match is found, 1 otherwise.
4419 """
4419 """
4420 if opts.get('print0'):
4420 if opts.get('print0'):
4421 end = '\0'
4421 end = '\0'
4422 else:
4422 else:
4423 end = '\n'
4423 end = '\n'
4424 rev = scmutil.revsingle(repo, opts.get('rev'), None).node()
4424 rev = scmutil.revsingle(repo, opts.get('rev'), None).node()
4425
4425
4426 ret = 1
4426 ret = 1
4427 ctx = repo[rev]
4427 ctx = repo[rev]
4428 m = scmutil.match(ctx, pats, opts, default='relglob')
4428 m = scmutil.match(ctx, pats, opts, default='relglob')
4429 m.bad = lambda x, y: False
4429 m.bad = lambda x, y: False
4430
4430
4431 for abs in ctx.matches(m):
4431 for abs in ctx.matches(m):
4432 if opts.get('fullpath'):
4432 if opts.get('fullpath'):
4433 ui.write(repo.wjoin(abs), end)
4433 ui.write(repo.wjoin(abs), end)
4434 else:
4434 else:
4435 ui.write(((pats and m.rel(abs)) or abs), end)
4435 ui.write(((pats and m.rel(abs)) or abs), end)
4436 ret = 0
4436 ret = 0
4437
4437
4438 return ret
4438 return ret
4439
4439
4440 @command('^log|history',
4440 @command('^log|history',
4441 [('f', 'follow', None,
4441 [('f', 'follow', None,
4442 _('follow changeset history, or file history across copies and renames')),
4442 _('follow changeset history, or file history across copies and renames')),
4443 ('', 'follow-first', None,
4443 ('', 'follow-first', None,
4444 _('only follow the first parent of merge changesets (DEPRECATED)')),
4444 _('only follow the first parent of merge changesets (DEPRECATED)')),
4445 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
4445 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
4446 ('C', 'copies', None, _('show copied files')),
4446 ('C', 'copies', None, _('show copied files')),
4447 ('k', 'keyword', [],
4447 ('k', 'keyword', [],
4448 _('do case-insensitive search for a given text'), _('TEXT')),
4448 _('do case-insensitive search for a given text'), _('TEXT')),
4449 ('r', 'rev', [], _('show the specified revision or revset'), _('REV')),
4449 ('r', 'rev', [], _('show the specified revision or revset'), _('REV')),
4450 ('', 'removed', None, _('include revisions where files were removed')),
4450 ('', 'removed', None, _('include revisions where files were removed')),
4451 ('m', 'only-merges', None, _('show only merges (DEPRECATED)')),
4451 ('m', 'only-merges', None, _('show only merges (DEPRECATED)')),
4452 ('u', 'user', [], _('revisions committed by user'), _('USER')),
4452 ('u', 'user', [], _('revisions committed by user'), _('USER')),
4453 ('', 'only-branch', [],
4453 ('', 'only-branch', [],
4454 _('show only changesets within the given named branch (DEPRECATED)'),
4454 _('show only changesets within the given named branch (DEPRECATED)'),
4455 _('BRANCH')),
4455 _('BRANCH')),
4456 ('b', 'branch', [],
4456 ('b', 'branch', [],
4457 _('show changesets within the given named branch'), _('BRANCH')),
4457 _('show changesets within the given named branch'), _('BRANCH')),
4458 ('P', 'prune', [],
4458 ('P', 'prune', [],
4459 _('do not display revision or any of its ancestors'), _('REV')),
4459 _('do not display revision or any of its ancestors'), _('REV')),
4460 ] + logopts + walkopts,
4460 ] + logopts + walkopts,
4461 _('[OPTION]... [FILE]'),
4461 _('[OPTION]... [FILE]'),
4462 inferrepo=True)
4462 inferrepo=True)
4463 def log(ui, repo, *pats, **opts):
4463 def log(ui, repo, *pats, **opts):
4464 """show revision history of entire repository or files
4464 """show revision history of entire repository or files
4465
4465
4466 Print the revision history of the specified files or the entire
4466 Print the revision history of the specified files or the entire
4467 project.
4467 project.
4468
4468
4469 If no revision range is specified, the default is ``tip:0`` unless
4469 If no revision range is specified, the default is ``tip:0`` unless
4470 --follow is set, in which case the working directory parent is
4470 --follow is set, in which case the working directory parent is
4471 used as the starting revision.
4471 used as the starting revision.
4472
4472
4473 File history is shown without following rename or copy history of
4473 File history is shown without following rename or copy history of
4474 files. Use -f/--follow with a filename to follow history across
4474 files. Use -f/--follow with a filename to follow history across
4475 renames and copies. --follow without a filename will only show
4475 renames and copies. --follow without a filename will only show
4476 ancestors or descendants of the starting revision.
4476 ancestors or descendants of the starting revision.
4477
4477
4478 By default this command prints revision number and changeset id,
4478 By default this command prints revision number and changeset id,
4479 tags, non-trivial parents, user, date and time, and a summary for
4479 tags, non-trivial parents, user, date and time, and a summary for
4480 each commit. When the -v/--verbose switch is used, the list of
4480 each commit. When the -v/--verbose switch is used, the list of
4481 changed files and full commit message are shown.
4481 changed files and full commit message are shown.
4482
4482
4483 With --graph the revisions are shown as an ASCII art DAG with the most
4483 With --graph the revisions are shown as an ASCII art DAG with the most
4484 recent changeset at the top.
4484 recent changeset at the top.
4485 'o' is a changeset, '@' is a working directory parent, 'x' is obsolete,
4485 'o' is a changeset, '@' is a working directory parent, 'x' is obsolete,
4486 and '+' represents a fork where the changeset from the lines below is a
4486 and '+' represents a fork where the changeset from the lines below is a
4487 parent of the 'o' merge on the same line.
4487 parent of the 'o' merge on the same line.
4488
4488
4489 .. note::
4489 .. note::
4490
4490
4491 log -p/--patch may generate unexpected diff output for merge
4491 log -p/--patch may generate unexpected diff output for merge
4492 changesets, as it will only compare the merge changeset against
4492 changesets, as it will only compare the merge changeset against
4493 its first parent. Also, only files different from BOTH parents
4493 its first parent. Also, only files different from BOTH parents
4494 will appear in files:.
4494 will appear in files:.
4495
4495
4496 .. note::
4496 .. note::
4497
4497
4498 for performance reasons, log FILE may omit duplicate changes
4498 for performance reasons, log FILE may omit duplicate changes
4499 made on branches and will not show removals or mode changes. To
4499 made on branches and will not show removals or mode changes. To
4500 see all such changes, use the --removed switch.
4500 see all such changes, use the --removed switch.
4501
4501
4502 .. container:: verbose
4502 .. container:: verbose
4503
4503
4504 Some examples:
4504 Some examples:
4505
4505
4506 - changesets with full descriptions and file lists::
4506 - changesets with full descriptions and file lists::
4507
4507
4508 hg log -v
4508 hg log -v
4509
4509
4510 - changesets ancestral to the working directory::
4510 - changesets ancestral to the working directory::
4511
4511
4512 hg log -f
4512 hg log -f
4513
4513
4514 - last 10 commits on the current branch::
4514 - last 10 commits on the current branch::
4515
4515
4516 hg log -l 10 -b .
4516 hg log -l 10 -b .
4517
4517
4518 - changesets showing all modifications of a file, including removals::
4518 - changesets showing all modifications of a file, including removals::
4519
4519
4520 hg log --removed file.c
4520 hg log --removed file.c
4521
4521
4522 - all changesets that touch a directory, with diffs, excluding merges::
4522 - all changesets that touch a directory, with diffs, excluding merges::
4523
4523
4524 hg log -Mp lib/
4524 hg log -Mp lib/
4525
4525
4526 - all revision numbers that match a keyword::
4526 - all revision numbers that match a keyword::
4527
4527
4528 hg log -k bug --template "{rev}\\n"
4528 hg log -k bug --template "{rev}\\n"
4529
4529
4530 - list available log templates::
4530 - list available log templates::
4531
4531
4532 hg log -T list
4532 hg log -T list
4533
4533
4534 - check if a given changeset is included in a tagged release::
4534 - check if a given changeset is included in a tagged release::
4535
4535
4536 hg log -r "a21ccf and ancestor(1.9)"
4536 hg log -r "a21ccf and ancestor(1.9)"
4537
4537
4538 - find all changesets by some user in a date range::
4538 - find all changesets by some user in a date range::
4539
4539
4540 hg log -k alice -d "may 2008 to jul 2008"
4540 hg log -k alice -d "may 2008 to jul 2008"
4541
4541
4542 - summary of all changesets after the last tag::
4542 - summary of all changesets after the last tag::
4543
4543
4544 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
4544 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
4545
4545
4546 See :hg:`help dates` for a list of formats valid for -d/--date.
4546 See :hg:`help dates` for a list of formats valid for -d/--date.
4547
4547
4548 See :hg:`help revisions` and :hg:`help revsets` for more about
4548 See :hg:`help revisions` and :hg:`help revsets` for more about
4549 specifying revisions.
4549 specifying revisions.
4550
4550
4551 See :hg:`help templates` for more about pre-packaged styles and
4551 See :hg:`help templates` for more about pre-packaged styles and
4552 specifying custom templates.
4552 specifying custom templates.
4553
4553
4554 Returns 0 on success.
4554 Returns 0 on success.
4555
4555
4556 """
4556 """
4557 if opts.get('follow') and opts.get('rev'):
4557 if opts.get('follow') and opts.get('rev'):
4558 opts['rev'] = [revset.formatspec('reverse(::%lr)', opts.get('rev'))]
4558 opts['rev'] = [revset.formatspec('reverse(::%lr)', opts.get('rev'))]
4559 del opts['follow']
4559 del opts['follow']
4560
4560
4561 if opts.get('graph'):
4561 if opts.get('graph'):
4562 return cmdutil.graphlog(ui, repo, *pats, **opts)
4562 return cmdutil.graphlog(ui, repo, *pats, **opts)
4563
4563
4564 revs, expr, filematcher = cmdutil.getlogrevs(repo, pats, opts)
4564 revs, expr, filematcher = cmdutil.getlogrevs(repo, pats, opts)
4565 limit = cmdutil.loglimit(opts)
4565 limit = cmdutil.loglimit(opts)
4566 count = 0
4566 count = 0
4567
4567
4568 getrenamed = None
4568 getrenamed = None
4569 if opts.get('copies'):
4569 if opts.get('copies'):
4570 endrev = None
4570 endrev = None
4571 if opts.get('rev'):
4571 if opts.get('rev'):
4572 endrev = scmutil.revrange(repo, opts.get('rev')).max() + 1
4572 endrev = scmutil.revrange(repo, opts.get('rev')).max() + 1
4573 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
4573 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
4574
4574
4575 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
4575 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
4576 for rev in revs:
4576 for rev in revs:
4577 if count == limit:
4577 if count == limit:
4578 break
4578 break
4579 ctx = repo[rev]
4579 ctx = repo[rev]
4580 copies = None
4580 copies = None
4581 if getrenamed is not None and rev:
4581 if getrenamed is not None and rev:
4582 copies = []
4582 copies = []
4583 for fn in ctx.files():
4583 for fn in ctx.files():
4584 rename = getrenamed(fn, rev)
4584 rename = getrenamed(fn, rev)
4585 if rename:
4585 if rename:
4586 copies.append((fn, rename[0]))
4586 copies.append((fn, rename[0]))
4587 if filematcher:
4587 if filematcher:
4588 revmatchfn = filematcher(ctx.rev())
4588 revmatchfn = filematcher(ctx.rev())
4589 else:
4589 else:
4590 revmatchfn = None
4590 revmatchfn = None
4591 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
4591 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
4592 if displayer.flush(rev):
4592 if displayer.flush(rev):
4593 count += 1
4593 count += 1
4594
4594
4595 displayer.close()
4595 displayer.close()
4596
4596
4597 @command('manifest',
4597 @command('manifest',
4598 [('r', 'rev', '', _('revision to display'), _('REV')),
4598 [('r', 'rev', '', _('revision to display'), _('REV')),
4599 ('', 'all', False, _("list files from all revisions"))]
4599 ('', 'all', False, _("list files from all revisions"))]
4600 + formatteropts,
4600 + formatteropts,
4601 _('[-r REV]'))
4601 _('[-r REV]'))
4602 def manifest(ui, repo, node=None, rev=None, **opts):
4602 def manifest(ui, repo, node=None, rev=None, **opts):
4603 """output the current or given revision of the project manifest
4603 """output the current or given revision of the project manifest
4604
4604
4605 Print a list of version controlled files for the given revision.
4605 Print a list of version controlled files for the given revision.
4606 If no revision is given, the first parent of the working directory
4606 If no revision is given, the first parent of the working directory
4607 is used, or the null revision if no revision is checked out.
4607 is used, or the null revision if no revision is checked out.
4608
4608
4609 With -v, print file permissions, symlink and executable bits.
4609 With -v, print file permissions, symlink and executable bits.
4610 With --debug, print file revision hashes.
4610 With --debug, print file revision hashes.
4611
4611
4612 If option --all is specified, the list of all files from all revisions
4612 If option --all is specified, the list of all files from all revisions
4613 is printed. This includes deleted and renamed files.
4613 is printed. This includes deleted and renamed files.
4614
4614
4615 Returns 0 on success.
4615 Returns 0 on success.
4616 """
4616 """
4617
4617
4618 fm = ui.formatter('manifest', opts)
4618 fm = ui.formatter('manifest', opts)
4619
4619
4620 if opts.get('all'):
4620 if opts.get('all'):
4621 if rev or node:
4621 if rev or node:
4622 raise util.Abort(_("can't specify a revision with --all"))
4622 raise util.Abort(_("can't specify a revision with --all"))
4623
4623
4624 res = []
4624 res = []
4625 prefix = "data/"
4625 prefix = "data/"
4626 suffix = ".i"
4626 suffix = ".i"
4627 plen = len(prefix)
4627 plen = len(prefix)
4628 slen = len(suffix)
4628 slen = len(suffix)
4629 lock = repo.lock()
4629 lock = repo.lock()
4630 try:
4630 try:
4631 for fn, b, size in repo.store.datafiles():
4631 for fn, b, size in repo.store.datafiles():
4632 if size != 0 and fn[-slen:] == suffix and fn[:plen] == prefix:
4632 if size != 0 and fn[-slen:] == suffix and fn[:plen] == prefix:
4633 res.append(fn[plen:-slen])
4633 res.append(fn[plen:-slen])
4634 finally:
4634 finally:
4635 lock.release()
4635 lock.release()
4636 for f in res:
4636 for f in res:
4637 fm.startitem()
4637 fm.startitem()
4638 fm.write("path", '%s\n', f)
4638 fm.write("path", '%s\n', f)
4639 fm.end()
4639 fm.end()
4640 return
4640 return
4641
4641
4642 if rev and node:
4642 if rev and node:
4643 raise util.Abort(_("please specify just one revision"))
4643 raise util.Abort(_("please specify just one revision"))
4644
4644
4645 if not node:
4645 if not node:
4646 node = rev
4646 node = rev
4647
4647
4648 char = {'l': '@', 'x': '*', '': ''}
4648 char = {'l': '@', 'x': '*', '': ''}
4649 mode = {'l': '644', 'x': '755', '': '644'}
4649 mode = {'l': '644', 'x': '755', '': '644'}
4650 ctx = scmutil.revsingle(repo, node)
4650 ctx = scmutil.revsingle(repo, node)
4651 mf = ctx.manifest()
4651 mf = ctx.manifest()
4652 for f in ctx:
4652 for f in ctx:
4653 fm.startitem()
4653 fm.startitem()
4654 fl = ctx[f].flags()
4654 fl = ctx[f].flags()
4655 fm.condwrite(ui.debugflag, 'hash', '%s ', hex(mf[f]))
4655 fm.condwrite(ui.debugflag, 'hash', '%s ', hex(mf[f]))
4656 fm.condwrite(ui.verbose, 'mode type', '%s %1s ', mode[fl], char[fl])
4656 fm.condwrite(ui.verbose, 'mode type', '%s %1s ', mode[fl], char[fl])
4657 fm.write('path', '%s\n', f)
4657 fm.write('path', '%s\n', f)
4658 fm.end()
4658 fm.end()
4659
4659
4660 @command('^merge',
4660 @command('^merge',
4661 [('f', 'force', None,
4661 [('f', 'force', None,
4662 _('force a merge including outstanding changes (DEPRECATED)')),
4662 _('force a merge including outstanding changes (DEPRECATED)')),
4663 ('r', 'rev', '', _('revision to merge'), _('REV')),
4663 ('r', 'rev', '', _('revision to merge'), _('REV')),
4664 ('P', 'preview', None,
4664 ('P', 'preview', None,
4665 _('review revisions to merge (no merge is performed)'))
4665 _('review revisions to merge (no merge is performed)'))
4666 ] + mergetoolopts,
4666 ] + mergetoolopts,
4667 _('[-P] [-f] [[-r] REV]'))
4667 _('[-P] [-f] [[-r] REV]'))
4668 def merge(ui, repo, node=None, **opts):
4668 def merge(ui, repo, node=None, **opts):
4669 """merge another revision into working directory
4669 """merge another revision into working directory
4670
4670
4671 The current working directory is updated with all changes made in
4671 The current working directory is updated with all changes made in
4672 the requested revision since the last common predecessor revision.
4672 the requested revision since the last common predecessor revision.
4673
4673
4674 Files that changed between either parent are marked as changed for
4674 Files that changed between either parent are marked as changed for
4675 the next commit and a commit must be performed before any further
4675 the next commit and a commit must be performed before any further
4676 updates to the repository are allowed. The next commit will have
4676 updates to the repository are allowed. The next commit will have
4677 two parents.
4677 two parents.
4678
4678
4679 ``--tool`` can be used to specify the merge tool used for file
4679 ``--tool`` can be used to specify the merge tool used for file
4680 merges. It overrides the HGMERGE environment variable and your
4680 merges. It overrides the HGMERGE environment variable and your
4681 configuration files. See :hg:`help merge-tools` for options.
4681 configuration files. See :hg:`help merge-tools` for options.
4682
4682
4683 If no revision is specified, the working directory's parent is a
4683 If no revision is specified, the working directory's parent is a
4684 head revision, and the current branch contains exactly one other
4684 head revision, and the current branch contains exactly one other
4685 head, the other head is merged with by default. Otherwise, an
4685 head, the other head is merged with by default. Otherwise, an
4686 explicit revision with which to merge with must be provided.
4686 explicit revision with which to merge with must be provided.
4687
4687
4688 :hg:`resolve` must be used to resolve unresolved files.
4688 :hg:`resolve` must be used to resolve unresolved files.
4689
4689
4690 To undo an uncommitted merge, use :hg:`update --clean .` which
4690 To undo an uncommitted merge, use :hg:`update --clean .` which
4691 will check out a clean copy of the original merge parent, losing
4691 will check out a clean copy of the original merge parent, losing
4692 all changes.
4692 all changes.
4693
4693
4694 Returns 0 on success, 1 if there are unresolved files.
4694 Returns 0 on success, 1 if there are unresolved files.
4695 """
4695 """
4696
4696
4697 if opts.get('rev') and node:
4697 if opts.get('rev') and node:
4698 raise util.Abort(_("please specify just one revision"))
4698 raise util.Abort(_("please specify just one revision"))
4699 if not node:
4699 if not node:
4700 node = opts.get('rev')
4700 node = opts.get('rev')
4701
4701
4702 if node:
4702 if node:
4703 node = scmutil.revsingle(repo, node).node()
4703 node = scmutil.revsingle(repo, node).node()
4704
4704
4705 if not node and repo._activebookmark:
4705 if not node and repo._activebookmark:
4706 bmheads = repo.bookmarkheads(repo._activebookmark)
4706 bmheads = repo.bookmarkheads(repo._activebookmark)
4707 curhead = repo[repo._activebookmark].node()
4707 curhead = repo[repo._activebookmark].node()
4708 if len(bmheads) == 2:
4708 if len(bmheads) == 2:
4709 if curhead == bmheads[0]:
4709 if curhead == bmheads[0]:
4710 node = bmheads[1]
4710 node = bmheads[1]
4711 else:
4711 else:
4712 node = bmheads[0]
4712 node = bmheads[0]
4713 elif len(bmheads) > 2:
4713 elif len(bmheads) > 2:
4714 raise util.Abort(_("multiple matching bookmarks to merge - "
4714 raise util.Abort(_("multiple matching bookmarks to merge - "
4715 "please merge with an explicit rev or bookmark"),
4715 "please merge with an explicit rev or bookmark"),
4716 hint=_("run 'hg heads' to see all heads"))
4716 hint=_("run 'hg heads' to see all heads"))
4717 elif len(bmheads) <= 1:
4717 elif len(bmheads) <= 1:
4718 raise util.Abort(_("no matching bookmark to merge - "
4718 raise util.Abort(_("no matching bookmark to merge - "
4719 "please merge with an explicit rev or bookmark"),
4719 "please merge with an explicit rev or bookmark"),
4720 hint=_("run 'hg heads' to see all heads"))
4720 hint=_("run 'hg heads' to see all heads"))
4721
4721
4722 if not node and not repo._activebookmark:
4722 if not node and not repo._activebookmark:
4723 branch = repo[None].branch()
4723 branch = repo[None].branch()
4724 bheads = repo.branchheads(branch)
4724 bheads = repo.branchheads(branch)
4725 nbhs = [bh for bh in bheads if not repo[bh].bookmarks()]
4725 nbhs = [bh for bh in bheads if not repo[bh].bookmarks()]
4726
4726
4727 if len(nbhs) > 2:
4727 if len(nbhs) > 2:
4728 raise util.Abort(_("branch '%s' has %d heads - "
4728 raise util.Abort(_("branch '%s' has %d heads - "
4729 "please merge with an explicit rev")
4729 "please merge with an explicit rev")
4730 % (branch, len(bheads)),
4730 % (branch, len(bheads)),
4731 hint=_("run 'hg heads .' to see heads"))
4731 hint=_("run 'hg heads .' to see heads"))
4732
4732
4733 parent = repo.dirstate.p1()
4733 parent = repo.dirstate.p1()
4734 if len(nbhs) <= 1:
4734 if len(nbhs) <= 1:
4735 if len(bheads) > 1:
4735 if len(bheads) > 1:
4736 raise util.Abort(_("heads are bookmarked - "
4736 raise util.Abort(_("heads are bookmarked - "
4737 "please merge with an explicit rev"),
4737 "please merge with an explicit rev"),
4738 hint=_("run 'hg heads' to see all heads"))
4738 hint=_("run 'hg heads' to see all heads"))
4739 if len(repo.heads()) > 1:
4739 if len(repo.heads()) > 1:
4740 raise util.Abort(_("branch '%s' has one head - "
4740 raise util.Abort(_("branch '%s' has one head - "
4741 "please merge with an explicit rev")
4741 "please merge with an explicit rev")
4742 % branch,
4742 % branch,
4743 hint=_("run 'hg heads' to see all heads"))
4743 hint=_("run 'hg heads' to see all heads"))
4744 msg, hint = _('nothing to merge'), None
4744 msg, hint = _('nothing to merge'), None
4745 if parent != repo.lookup(branch):
4745 if parent != repo.lookup(branch):
4746 hint = _("use 'hg update' instead")
4746 hint = _("use 'hg update' instead")
4747 raise util.Abort(msg, hint=hint)
4747 raise util.Abort(msg, hint=hint)
4748
4748
4749 if parent not in bheads:
4749 if parent not in bheads:
4750 raise util.Abort(_('working directory not at a head revision'),
4750 raise util.Abort(_('working directory not at a head revision'),
4751 hint=_("use 'hg update' or merge with an "
4751 hint=_("use 'hg update' or merge with an "
4752 "explicit revision"))
4752 "explicit revision"))
4753 if parent == nbhs[0]:
4753 if parent == nbhs[0]:
4754 node = nbhs[-1]
4754 node = nbhs[-1]
4755 else:
4755 else:
4756 node = nbhs[0]
4756 node = nbhs[0]
4757
4757
4758 if opts.get('preview'):
4758 if opts.get('preview'):
4759 # find nodes that are ancestors of p2 but not of p1
4759 # find nodes that are ancestors of p2 but not of p1
4760 p1 = repo.lookup('.')
4760 p1 = repo.lookup('.')
4761 p2 = repo.lookup(node)
4761 p2 = repo.lookup(node)
4762 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
4762 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
4763
4763
4764 displayer = cmdutil.show_changeset(ui, repo, opts)
4764 displayer = cmdutil.show_changeset(ui, repo, opts)
4765 for node in nodes:
4765 for node in nodes:
4766 displayer.show(repo[node])
4766 displayer.show(repo[node])
4767 displayer.close()
4767 displayer.close()
4768 return 0
4768 return 0
4769
4769
4770 try:
4770 try:
4771 # ui.forcemerge is an internal variable, do not document
4771 # ui.forcemerge is an internal variable, do not document
4772 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''), 'merge')
4772 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''), 'merge')
4773 return hg.merge(repo, node, force=opts.get('force'))
4773 return hg.merge(repo, node, force=opts.get('force'))
4774 finally:
4774 finally:
4775 ui.setconfig('ui', 'forcemerge', '', 'merge')
4775 ui.setconfig('ui', 'forcemerge', '', 'merge')
4776
4776
4777 @command('outgoing|out',
4777 @command('outgoing|out',
4778 [('f', 'force', None, _('run even when the destination is unrelated')),
4778 [('f', 'force', None, _('run even when the destination is unrelated')),
4779 ('r', 'rev', [],
4779 ('r', 'rev', [],
4780 _('a changeset intended to be included in the destination'), _('REV')),
4780 _('a changeset intended to be included in the destination'), _('REV')),
4781 ('n', 'newest-first', None, _('show newest record first')),
4781 ('n', 'newest-first', None, _('show newest record first')),
4782 ('B', 'bookmarks', False, _('compare bookmarks')),
4782 ('B', 'bookmarks', False, _('compare bookmarks')),
4783 ('b', 'branch', [], _('a specific branch you would like to push'),
4783 ('b', 'branch', [], _('a specific branch you would like to push'),
4784 _('BRANCH')),
4784 _('BRANCH')),
4785 ] + logopts + remoteopts + subrepoopts,
4785 ] + logopts + remoteopts + subrepoopts,
4786 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'))
4786 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'))
4787 def outgoing(ui, repo, dest=None, **opts):
4787 def outgoing(ui, repo, dest=None, **opts):
4788 """show changesets not found in the destination
4788 """show changesets not found in the destination
4789
4789
4790 Show changesets not found in the specified destination repository
4790 Show changesets not found in the specified destination repository
4791 or the default push location. These are the changesets that would
4791 or the default push location. These are the changesets that would
4792 be pushed if a push was requested.
4792 be pushed if a push was requested.
4793
4793
4794 See pull for details of valid destination formats.
4794 See pull for details of valid destination formats.
4795
4795
4796 .. container:: verbose
4796 .. container:: verbose
4797
4797
4798 With -B/--bookmarks, the result of bookmark comparison between
4798 With -B/--bookmarks, the result of bookmark comparison between
4799 local and remote repositories is displayed. With -v/--verbose,
4799 local and remote repositories is displayed. With -v/--verbose,
4800 status is also displayed for each bookmark like below::
4800 status is also displayed for each bookmark like below::
4801
4801
4802 BM1 01234567890a added
4802 BM1 01234567890a added
4803 BM2 deleted
4803 BM2 deleted
4804 BM3 234567890abc advanced
4804 BM3 234567890abc advanced
4805 BM4 34567890abcd diverged
4805 BM4 34567890abcd diverged
4806 BM5 4567890abcde changed
4806 BM5 4567890abcde changed
4807
4807
4808 The action taken when pushing depends on the
4808 The action taken when pushing depends on the
4809 status of each bookmark:
4809 status of each bookmark:
4810
4810
4811 :``added``: push with ``-B`` will create it
4811 :``added``: push with ``-B`` will create it
4812 :``deleted``: push with ``-B`` will delete it
4812 :``deleted``: push with ``-B`` will delete it
4813 :``advanced``: push will update it
4813 :``advanced``: push will update it
4814 :``diverged``: push with ``-B`` will update it
4814 :``diverged``: push with ``-B`` will update it
4815 :``changed``: push with ``-B`` will update it
4815 :``changed``: push with ``-B`` will update it
4816
4816
4817 From the point of view of pushing behavior, bookmarks
4817 From the point of view of pushing behavior, bookmarks
4818 existing only in the remote repository are treated as
4818 existing only in the remote repository are treated as
4819 ``deleted``, even if it is in fact added remotely.
4819 ``deleted``, even if it is in fact added remotely.
4820
4820
4821 Returns 0 if there are outgoing changes, 1 otherwise.
4821 Returns 0 if there are outgoing changes, 1 otherwise.
4822 """
4822 """
4823 if opts.get('graph'):
4823 if opts.get('graph'):
4824 cmdutil.checkunsupportedgraphflags([], opts)
4824 cmdutil.checkunsupportedgraphflags([], opts)
4825 o, other = hg._outgoing(ui, repo, dest, opts)
4825 o, other = hg._outgoing(ui, repo, dest, opts)
4826 if not o:
4826 if not o:
4827 cmdutil.outgoinghooks(ui, repo, other, opts, o)
4827 cmdutil.outgoinghooks(ui, repo, other, opts, o)
4828 return
4828 return
4829
4829
4830 revdag = cmdutil.graphrevs(repo, o, opts)
4830 revdag = cmdutil.graphrevs(repo, o, opts)
4831 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
4831 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
4832 showparents = [ctx.node() for ctx in repo[None].parents()]
4832 showparents = [ctx.node() for ctx in repo[None].parents()]
4833 cmdutil.displaygraph(ui, revdag, displayer, showparents,
4833 cmdutil.displaygraph(ui, revdag, displayer, showparents,
4834 graphmod.asciiedges)
4834 graphmod.asciiedges)
4835 cmdutil.outgoinghooks(ui, repo, other, opts, o)
4835 cmdutil.outgoinghooks(ui, repo, other, opts, o)
4836 return 0
4836 return 0
4837
4837
4838 if opts.get('bookmarks'):
4838 if opts.get('bookmarks'):
4839 dest = ui.expandpath(dest or 'default-push', dest or 'default')
4839 dest = ui.expandpath(dest or 'default-push', dest or 'default')
4840 dest, branches = hg.parseurl(dest, opts.get('branch'))
4840 dest, branches = hg.parseurl(dest, opts.get('branch'))
4841 other = hg.peer(repo, opts, dest)
4841 other = hg.peer(repo, opts, dest)
4842 if 'bookmarks' not in other.listkeys('namespaces'):
4842 if 'bookmarks' not in other.listkeys('namespaces'):
4843 ui.warn(_("remote doesn't support bookmarks\n"))
4843 ui.warn(_("remote doesn't support bookmarks\n"))
4844 return 0
4844 return 0
4845 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
4845 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
4846 return bookmarks.outgoing(ui, repo, other)
4846 return bookmarks.outgoing(ui, repo, other)
4847
4847
4848 repo._subtoppath = ui.expandpath(dest or 'default-push', dest or 'default')
4848 repo._subtoppath = ui.expandpath(dest or 'default-push', dest or 'default')
4849 try:
4849 try:
4850 return hg.outgoing(ui, repo, dest, opts)
4850 return hg.outgoing(ui, repo, dest, opts)
4851 finally:
4851 finally:
4852 del repo._subtoppath
4852 del repo._subtoppath
4853
4853
4854 @command('parents',
4854 @command('parents',
4855 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
4855 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
4856 ] + templateopts,
4856 ] + templateopts,
4857 _('[-r REV] [FILE]'),
4857 _('[-r REV] [FILE]'),
4858 inferrepo=True)
4858 inferrepo=True)
4859 def parents(ui, repo, file_=None, **opts):
4859 def parents(ui, repo, file_=None, **opts):
4860 """show the parents of the working directory or revision (DEPRECATED)
4860 """show the parents of the working directory or revision (DEPRECATED)
4861
4861
4862 Print the working directory's parent revisions. If a revision is
4862 Print the working directory's parent revisions. If a revision is
4863 given via -r/--rev, the parent of that revision will be printed.
4863 given via -r/--rev, the parent of that revision will be printed.
4864 If a file argument is given, the revision in which the file was
4864 If a file argument is given, the revision in which the file was
4865 last changed (before the working directory revision or the
4865 last changed (before the working directory revision or the
4866 argument to --rev if given) is printed.
4866 argument to --rev if given) is printed.
4867
4867
4868 See :hg:`summary` and :hg:`help revsets` for related information.
4868 See :hg:`summary` and :hg:`help revsets` for related information.
4869
4869
4870 Returns 0 on success.
4870 Returns 0 on success.
4871 """
4871 """
4872
4872
4873 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
4873 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
4874
4874
4875 if file_:
4875 if file_:
4876 m = scmutil.match(ctx, (file_,), opts)
4876 m = scmutil.match(ctx, (file_,), opts)
4877 if m.anypats() or len(m.files()) != 1:
4877 if m.anypats() or len(m.files()) != 1:
4878 raise util.Abort(_('can only specify an explicit filename'))
4878 raise util.Abort(_('can only specify an explicit filename'))
4879 file_ = m.files()[0]
4879 file_ = m.files()[0]
4880 filenodes = []
4880 filenodes = []
4881 for cp in ctx.parents():
4881 for cp in ctx.parents():
4882 if not cp:
4882 if not cp:
4883 continue
4883 continue
4884 try:
4884 try:
4885 filenodes.append(cp.filenode(file_))
4885 filenodes.append(cp.filenode(file_))
4886 except error.LookupError:
4886 except error.LookupError:
4887 pass
4887 pass
4888 if not filenodes:
4888 if not filenodes:
4889 raise util.Abort(_("'%s' not found in manifest!") % file_)
4889 raise util.Abort(_("'%s' not found in manifest!") % file_)
4890 p = []
4890 p = []
4891 for fn in filenodes:
4891 for fn in filenodes:
4892 fctx = repo.filectx(file_, fileid=fn)
4892 fctx = repo.filectx(file_, fileid=fn)
4893 p.append(fctx.node())
4893 p.append(fctx.node())
4894 else:
4894 else:
4895 p = [cp.node() for cp in ctx.parents()]
4895 p = [cp.node() for cp in ctx.parents()]
4896
4896
4897 displayer = cmdutil.show_changeset(ui, repo, opts)
4897 displayer = cmdutil.show_changeset(ui, repo, opts)
4898 for n in p:
4898 for n in p:
4899 if n != nullid:
4899 if n != nullid:
4900 displayer.show(repo[n])
4900 displayer.show(repo[n])
4901 displayer.close()
4901 displayer.close()
4902
4902
4903 @command('paths', [], _('[NAME]'), optionalrepo=True)
4903 @command('paths', [], _('[NAME]'), optionalrepo=True)
4904 def paths(ui, repo, search=None):
4904 def paths(ui, repo, search=None):
4905 """show aliases for remote repositories
4905 """show aliases for remote repositories
4906
4906
4907 Show definition of symbolic path name NAME. If no name is given,
4907 Show definition of symbolic path name NAME. If no name is given,
4908 show definition of all available names.
4908 show definition of all available names.
4909
4909
4910 Option -q/--quiet suppresses all output when searching for NAME
4910 Option -q/--quiet suppresses all output when searching for NAME
4911 and shows only the path names when listing all definitions.
4911 and shows only the path names when listing all definitions.
4912
4912
4913 Path names are defined in the [paths] section of your
4913 Path names are defined in the [paths] section of your
4914 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
4914 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
4915 repository, ``.hg/hgrc`` is used, too.
4915 repository, ``.hg/hgrc`` is used, too.
4916
4916
4917 The path names ``default`` and ``default-push`` have a special
4917 The path names ``default`` and ``default-push`` have a special
4918 meaning. When performing a push or pull operation, they are used
4918 meaning. When performing a push or pull operation, they are used
4919 as fallbacks if no location is specified on the command-line.
4919 as fallbacks if no location is specified on the command-line.
4920 When ``default-push`` is set, it will be used for push and
4920 When ``default-push`` is set, it will be used for push and
4921 ``default`` will be used for pull; otherwise ``default`` is used
4921 ``default`` will be used for pull; otherwise ``default`` is used
4922 as the fallback for both. When cloning a repository, the clone
4922 as the fallback for both. When cloning a repository, the clone
4923 source is written as ``default`` in ``.hg/hgrc``. Note that
4923 source is written as ``default`` in ``.hg/hgrc``. Note that
4924 ``default`` and ``default-push`` apply to all inbound (e.g.
4924 ``default`` and ``default-push`` apply to all inbound (e.g.
4925 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email` and
4925 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email` and
4926 :hg:`bundle`) operations.
4926 :hg:`bundle`) operations.
4927
4927
4928 See :hg:`help urls` for more information.
4928 See :hg:`help urls` for more information.
4929
4929
4930 Returns 0 on success.
4930 Returns 0 on success.
4931 """
4931 """
4932 if search:
4932 if search:
4933 for name, path in sorted(ui.paths.iteritems()):
4933 for name, path in sorted(ui.paths.iteritems()):
4934 if name == search:
4934 if name == search:
4935 ui.status("%s\n" % util.hidepassword(path.loc))
4935 ui.status("%s\n" % util.hidepassword(path.loc))
4936 return
4936 return
4937 if not ui.quiet:
4937 if not ui.quiet:
4938 ui.warn(_("not found!\n"))
4938 ui.warn(_("not found!\n"))
4939 return 1
4939 return 1
4940 else:
4940 else:
4941 for name, path in sorted(ui.paths.iteritems()):
4941 for name, path in sorted(ui.paths.iteritems()):
4942 if ui.quiet:
4942 if ui.quiet:
4943 ui.write("%s\n" % name)
4943 ui.write("%s\n" % name)
4944 else:
4944 else:
4945 ui.write("%s = %s\n" % (name,
4945 ui.write("%s = %s\n" % (name,
4946 util.hidepassword(path.loc)))
4946 util.hidepassword(path.loc)))
4947
4947
4948 @command('phase',
4948 @command('phase',
4949 [('p', 'public', False, _('set changeset phase to public')),
4949 [('p', 'public', False, _('set changeset phase to public')),
4950 ('d', 'draft', False, _('set changeset phase to draft')),
4950 ('d', 'draft', False, _('set changeset phase to draft')),
4951 ('s', 'secret', False, _('set changeset phase to secret')),
4951 ('s', 'secret', False, _('set changeset phase to secret')),
4952 ('f', 'force', False, _('allow to move boundary backward')),
4952 ('f', 'force', False, _('allow to move boundary backward')),
4953 ('r', 'rev', [], _('target revision'), _('REV')),
4953 ('r', 'rev', [], _('target revision'), _('REV')),
4954 ],
4954 ],
4955 _('[-p|-d|-s] [-f] [-r] [REV...]'))
4955 _('[-p|-d|-s] [-f] [-r] [REV...]'))
4956 def phase(ui, repo, *revs, **opts):
4956 def phase(ui, repo, *revs, **opts):
4957 """set or show the current phase name
4957 """set or show the current phase name
4958
4958
4959 With no argument, show the phase name of the current revision(s).
4959 With no argument, show the phase name of the current revision(s).
4960
4960
4961 With one of -p/--public, -d/--draft or -s/--secret, change the
4961 With one of -p/--public, -d/--draft or -s/--secret, change the
4962 phase value of the specified revisions.
4962 phase value of the specified revisions.
4963
4963
4964 Unless -f/--force is specified, :hg:`phase` won't move changeset from a
4964 Unless -f/--force is specified, :hg:`phase` won't move changeset from a
4965 lower phase to an higher phase. Phases are ordered as follows::
4965 lower phase to an higher phase. Phases are ordered as follows::
4966
4966
4967 public < draft < secret
4967 public < draft < secret
4968
4968
4969 Returns 0 on success, 1 if no phases were changed or some could not
4969 Returns 0 on success, 1 if no phases were changed or some could not
4970 be changed.
4970 be changed.
4971 """
4971 """
4972 # search for a unique phase argument
4972 # search for a unique phase argument
4973 targetphase = None
4973 targetphase = None
4974 for idx, name in enumerate(phases.phasenames):
4974 for idx, name in enumerate(phases.phasenames):
4975 if opts[name]:
4975 if opts[name]:
4976 if targetphase is not None:
4976 if targetphase is not None:
4977 raise util.Abort(_('only one phase can be specified'))
4977 raise util.Abort(_('only one phase can be specified'))
4978 targetphase = idx
4978 targetphase = idx
4979
4979
4980 # look for specified revision
4980 # look for specified revision
4981 revs = list(revs)
4981 revs = list(revs)
4982 revs.extend(opts['rev'])
4982 revs.extend(opts['rev'])
4983 if not revs:
4983 if not revs:
4984 # display both parents as the second parent phase can influence
4984 # display both parents as the second parent phase can influence
4985 # the phase of a merge commit
4985 # the phase of a merge commit
4986 revs = [c.rev() for c in repo[None].parents()]
4986 revs = [c.rev() for c in repo[None].parents()]
4987
4987
4988 revs = scmutil.revrange(repo, revs)
4988 revs = scmutil.revrange(repo, revs)
4989
4989
4990 lock = None
4990 lock = None
4991 ret = 0
4991 ret = 0
4992 if targetphase is None:
4992 if targetphase is None:
4993 # display
4993 # display
4994 for r in revs:
4994 for r in revs:
4995 ctx = repo[r]
4995 ctx = repo[r]
4996 ui.write('%i: %s\n' % (ctx.rev(), ctx.phasestr()))
4996 ui.write('%i: %s\n' % (ctx.rev(), ctx.phasestr()))
4997 else:
4997 else:
4998 tr = None
4998 tr = None
4999 lock = repo.lock()
4999 lock = repo.lock()
5000 try:
5000 try:
5001 tr = repo.transaction("phase")
5001 tr = repo.transaction("phase")
5002 # set phase
5002 # set phase
5003 if not revs:
5003 if not revs:
5004 raise util.Abort(_('empty revision set'))
5004 raise util.Abort(_('empty revision set'))
5005 nodes = [repo[r].node() for r in revs]
5005 nodes = [repo[r].node() for r in revs]
5006 # moving revision from public to draft may hide them
5006 # moving revision from public to draft may hide them
5007 # We have to check result on an unfiltered repository
5007 # We have to check result on an unfiltered repository
5008 unfi = repo.unfiltered()
5008 unfi = repo.unfiltered()
5009 getphase = unfi._phasecache.phase
5009 getphase = unfi._phasecache.phase
5010 olddata = [getphase(unfi, r) for r in unfi]
5010 olddata = [getphase(unfi, r) for r in unfi]
5011 phases.advanceboundary(repo, tr, targetphase, nodes)
5011 phases.advanceboundary(repo, tr, targetphase, nodes)
5012 if opts['force']:
5012 if opts['force']:
5013 phases.retractboundary(repo, tr, targetphase, nodes)
5013 phases.retractboundary(repo, tr, targetphase, nodes)
5014 tr.close()
5014 tr.close()
5015 finally:
5015 finally:
5016 if tr is not None:
5016 if tr is not None:
5017 tr.release()
5017 tr.release()
5018 lock.release()
5018 lock.release()
5019 getphase = unfi._phasecache.phase
5019 getphase = unfi._phasecache.phase
5020 newdata = [getphase(unfi, r) for r in unfi]
5020 newdata = [getphase(unfi, r) for r in unfi]
5021 changes = sum(newdata[r] != olddata[r] for r in unfi)
5021 changes = sum(newdata[r] != olddata[r] for r in unfi)
5022 cl = unfi.changelog
5022 cl = unfi.changelog
5023 rejected = [n for n in nodes
5023 rejected = [n for n in nodes
5024 if newdata[cl.rev(n)] < targetphase]
5024 if newdata[cl.rev(n)] < targetphase]
5025 if rejected:
5025 if rejected:
5026 ui.warn(_('cannot move %i changesets to a higher '
5026 ui.warn(_('cannot move %i changesets to a higher '
5027 'phase, use --force\n') % len(rejected))
5027 'phase, use --force\n') % len(rejected))
5028 ret = 1
5028 ret = 1
5029 if changes:
5029 if changes:
5030 msg = _('phase changed for %i changesets\n') % changes
5030 msg = _('phase changed for %i changesets\n') % changes
5031 if ret:
5031 if ret:
5032 ui.status(msg)
5032 ui.status(msg)
5033 else:
5033 else:
5034 ui.note(msg)
5034 ui.note(msg)
5035 else:
5035 else:
5036 ui.warn(_('no phases changed\n'))
5036 ui.warn(_('no phases changed\n'))
5037 ret = 1
5037 ret = 1
5038 return ret
5038 return ret
5039
5039
5040 def postincoming(ui, repo, modheads, optupdate, checkout):
5040 def postincoming(ui, repo, modheads, optupdate, checkout):
5041 if modheads == 0:
5041 if modheads == 0:
5042 return
5042 return
5043 if optupdate:
5043 if optupdate:
5044 checkout, movemarkfrom = bookmarks.calculateupdate(ui, repo, checkout)
5044 checkout, movemarkfrom = bookmarks.calculateupdate(ui, repo, checkout)
5045 try:
5045 try:
5046 ret = hg.update(repo, checkout)
5046 ret = hg.update(repo, checkout)
5047 except util.Abort, inst:
5047 except util.Abort, inst:
5048 ui.warn(_("not updating: %s\n") % str(inst))
5048 ui.warn(_("not updating: %s\n") % str(inst))
5049 if inst.hint:
5049 if inst.hint:
5050 ui.warn(_("(%s)\n") % inst.hint)
5050 ui.warn(_("(%s)\n") % inst.hint)
5051 return 0
5051 return 0
5052 if not ret and not checkout:
5052 if not ret and not checkout:
5053 if bookmarks.update(repo, [movemarkfrom], repo['.'].node()):
5053 if bookmarks.update(repo, [movemarkfrom], repo['.'].node()):
5054 ui.status(_("updating bookmark %s\n") % repo._activebookmark)
5054 ui.status(_("updating bookmark %s\n") % repo._activebookmark)
5055 return ret
5055 return ret
5056 if modheads > 1:
5056 if modheads > 1:
5057 currentbranchheads = len(repo.branchheads())
5057 currentbranchheads = len(repo.branchheads())
5058 if currentbranchheads == modheads:
5058 if currentbranchheads == modheads:
5059 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
5059 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
5060 elif currentbranchheads > 1:
5060 elif currentbranchheads > 1:
5061 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to "
5061 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to "
5062 "merge)\n"))
5062 "merge)\n"))
5063 else:
5063 else:
5064 ui.status(_("(run 'hg heads' to see heads)\n"))
5064 ui.status(_("(run 'hg heads' to see heads)\n"))
5065 else:
5065 else:
5066 ui.status(_("(run 'hg update' to get a working copy)\n"))
5066 ui.status(_("(run 'hg update' to get a working copy)\n"))
5067
5067
5068 @command('^pull',
5068 @command('^pull',
5069 [('u', 'update', None,
5069 [('u', 'update', None,
5070 _('update to new branch head if changesets were pulled')),
5070 _('update to new branch head if changesets were pulled')),
5071 ('f', 'force', None, _('run even when remote repository is unrelated')),
5071 ('f', 'force', None, _('run even when remote repository is unrelated')),
5072 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
5072 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
5073 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
5073 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
5074 ('b', 'branch', [], _('a specific branch you would like to pull'),
5074 ('b', 'branch', [], _('a specific branch you would like to pull'),
5075 _('BRANCH')),
5075 _('BRANCH')),
5076 ] + remoteopts,
5076 ] + remoteopts,
5077 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'))
5077 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'))
5078 def pull(ui, repo, source="default", **opts):
5078 def pull(ui, repo, source="default", **opts):
5079 """pull changes from the specified source
5079 """pull changes from the specified source
5080
5080
5081 Pull changes from a remote repository to a local one.
5081 Pull changes from a remote repository to a local one.
5082
5082
5083 This finds all changes from the repository at the specified path
5083 This finds all changes from the repository at the specified path
5084 or URL and adds them to a local repository (the current one unless
5084 or URL and adds them to a local repository (the current one unless
5085 -R is specified). By default, this does not update the copy of the
5085 -R is specified). By default, this does not update the copy of the
5086 project in the working directory.
5086 project in the working directory.
5087
5087
5088 Use :hg:`incoming` if you want to see what would have been added
5088 Use :hg:`incoming` if you want to see what would have been added
5089 by a pull at the time you issued this command. If you then decide
5089 by a pull at the time you issued this command. If you then decide
5090 to add those changes to the repository, you should use :hg:`pull
5090 to add those changes to the repository, you should use :hg:`pull
5091 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
5091 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
5092
5092
5093 If SOURCE is omitted, the 'default' path will be used.
5093 If SOURCE is omitted, the 'default' path will be used.
5094 See :hg:`help urls` for more information.
5094 See :hg:`help urls` for more information.
5095
5095
5096 Returns 0 on success, 1 if an update had unresolved files.
5096 Returns 0 on success, 1 if an update had unresolved files.
5097 """
5097 """
5098 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
5098 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
5099 ui.status(_('pulling from %s\n') % util.hidepassword(source))
5099 ui.status(_('pulling from %s\n') % util.hidepassword(source))
5100 other = hg.peer(repo, opts, source)
5100 other = hg.peer(repo, opts, source)
5101 try:
5101 try:
5102 revs, checkout = hg.addbranchrevs(repo, other, branches,
5102 revs, checkout = hg.addbranchrevs(repo, other, branches,
5103 opts.get('rev'))
5103 opts.get('rev'))
5104
5104
5105 remotebookmarks = other.listkeys('bookmarks')
5105 remotebookmarks = other.listkeys('bookmarks')
5106
5106
5107 if opts.get('bookmark'):
5107 if opts.get('bookmark'):
5108 if not revs:
5108 if not revs:
5109 revs = []
5109 revs = []
5110 for b in opts['bookmark']:
5110 for b in opts['bookmark']:
5111 if b not in remotebookmarks:
5111 if b not in remotebookmarks:
5112 raise util.Abort(_('remote bookmark %s not found!') % b)
5112 raise util.Abort(_('remote bookmark %s not found!') % b)
5113 revs.append(remotebookmarks[b])
5113 revs.append(remotebookmarks[b])
5114
5114
5115 if revs:
5115 if revs:
5116 try:
5116 try:
5117 revs = [other.lookup(rev) for rev in revs]
5117 revs = [other.lookup(rev) for rev in revs]
5118 except error.CapabilityError:
5118 except error.CapabilityError:
5119 err = _("other repository doesn't support revision lookup, "
5119 err = _("other repository doesn't support revision lookup, "
5120 "so a rev cannot be specified.")
5120 "so a rev cannot be specified.")
5121 raise util.Abort(err)
5121 raise util.Abort(err)
5122
5122
5123 modheads = exchange.pull(repo, other, heads=revs,
5123 modheads = exchange.pull(repo, other, heads=revs,
5124 force=opts.get('force'),
5124 force=opts.get('force'),
5125 bookmarks=opts.get('bookmark', ())).cgresult
5125 bookmarks=opts.get('bookmark', ())).cgresult
5126 if checkout:
5126 if checkout:
5127 checkout = str(repo.changelog.rev(other.lookup(checkout)))
5127 checkout = str(repo.changelog.rev(other.lookup(checkout)))
5128 repo._subtoppath = source
5128 repo._subtoppath = source
5129 try:
5129 try:
5130 ret = postincoming(ui, repo, modheads, opts.get('update'), checkout)
5130 ret = postincoming(ui, repo, modheads, opts.get('update'), checkout)
5131
5131
5132 finally:
5132 finally:
5133 del repo._subtoppath
5133 del repo._subtoppath
5134
5134
5135 finally:
5135 finally:
5136 other.close()
5136 other.close()
5137 return ret
5137 return ret
5138
5138
5139 @command('^push',
5139 @command('^push',
5140 [('f', 'force', None, _('force push')),
5140 [('f', 'force', None, _('force push')),
5141 ('r', 'rev', [],
5141 ('r', 'rev', [],
5142 _('a changeset intended to be included in the destination'),
5142 _('a changeset intended to be included in the destination'),
5143 _('REV')),
5143 _('REV')),
5144 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
5144 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
5145 ('b', 'branch', [],
5145 ('b', 'branch', [],
5146 _('a specific branch you would like to push'), _('BRANCH')),
5146 _('a specific branch you would like to push'), _('BRANCH')),
5147 ('', 'new-branch', False, _('allow pushing a new branch')),
5147 ('', 'new-branch', False, _('allow pushing a new branch')),
5148 ] + remoteopts,
5148 ] + remoteopts,
5149 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'))
5149 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'))
5150 def push(ui, repo, dest=None, **opts):
5150 def push(ui, repo, dest=None, **opts):
5151 """push changes to the specified destination
5151 """push changes to the specified destination
5152
5152
5153 Push changesets from the local repository to the specified
5153 Push changesets from the local repository to the specified
5154 destination.
5154 destination.
5155
5155
5156 This operation is symmetrical to pull: it is identical to a pull
5156 This operation is symmetrical to pull: it is identical to a pull
5157 in the destination repository from the current one.
5157 in the destination repository from the current one.
5158
5158
5159 By default, push will not allow creation of new heads at the
5159 By default, push will not allow creation of new heads at the
5160 destination, since multiple heads would make it unclear which head
5160 destination, since multiple heads would make it unclear which head
5161 to use. In this situation, it is recommended to pull and merge
5161 to use. In this situation, it is recommended to pull and merge
5162 before pushing.
5162 before pushing.
5163
5163
5164 Use --new-branch if you want to allow push to create a new named
5164 Use --new-branch if you want to allow push to create a new named
5165 branch that is not present at the destination. This allows you to
5165 branch that is not present at the destination. This allows you to
5166 only create a new branch without forcing other changes.
5166 only create a new branch without forcing other changes.
5167
5167
5168 .. note::
5168 .. note::
5169
5169
5170 Extra care should be taken with the -f/--force option,
5170 Extra care should be taken with the -f/--force option,
5171 which will push all new heads on all branches, an action which will
5171 which will push all new heads on all branches, an action which will
5172 almost always cause confusion for collaborators.
5172 almost always cause confusion for collaborators.
5173
5173
5174 If -r/--rev is used, the specified revision and all its ancestors
5174 If -r/--rev is used, the specified revision and all its ancestors
5175 will be pushed to the remote repository.
5175 will be pushed to the remote repository.
5176
5176
5177 If -B/--bookmark is used, the specified bookmarked revision, its
5177 If -B/--bookmark is used, the specified bookmarked revision, its
5178 ancestors, and the bookmark will be pushed to the remote
5178 ancestors, and the bookmark will be pushed to the remote
5179 repository.
5179 repository.
5180
5180
5181 Please see :hg:`help urls` for important details about ``ssh://``
5181 Please see :hg:`help urls` for important details about ``ssh://``
5182 URLs. If DESTINATION is omitted, a default path will be used.
5182 URLs. If DESTINATION is omitted, a default path will be used.
5183
5183
5184 Returns 0 if push was successful, 1 if nothing to push.
5184 Returns 0 if push was successful, 1 if nothing to push.
5185 """
5185 """
5186
5186
5187 if opts.get('bookmark'):
5187 if opts.get('bookmark'):
5188 ui.setconfig('bookmarks', 'pushing', opts['bookmark'], 'push')
5188 ui.setconfig('bookmarks', 'pushing', opts['bookmark'], 'push')
5189 for b in opts['bookmark']:
5189 for b in opts['bookmark']:
5190 # translate -B options to -r so changesets get pushed
5190 # translate -B options to -r so changesets get pushed
5191 if b in repo._bookmarks:
5191 if b in repo._bookmarks:
5192 opts.setdefault('rev', []).append(b)
5192 opts.setdefault('rev', []).append(b)
5193 else:
5193 else:
5194 # if we try to push a deleted bookmark, translate it to null
5194 # if we try to push a deleted bookmark, translate it to null
5195 # this lets simultaneous -r, -b options continue working
5195 # this lets simultaneous -r, -b options continue working
5196 opts.setdefault('rev', []).append("null")
5196 opts.setdefault('rev', []).append("null")
5197
5197
5198 dest = ui.expandpath(dest or 'default-push', dest or 'default')
5198 dest = ui.expandpath(dest or 'default-push', dest or 'default')
5199 dest, branches = hg.parseurl(dest, opts.get('branch'))
5199 dest, branches = hg.parseurl(dest, opts.get('branch'))
5200 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
5200 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
5201 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
5201 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
5202 try:
5202 try:
5203 other = hg.peer(repo, opts, dest)
5203 other = hg.peer(repo, opts, dest)
5204 except error.RepoError:
5204 except error.RepoError:
5205 if dest == "default-push":
5205 if dest == "default-push":
5206 raise util.Abort(_("default repository not configured!"),
5206 raise util.Abort(_("default repository not configured!"),
5207 hint=_('see the "path" section in "hg help config"'))
5207 hint=_('see the "path" section in "hg help config"'))
5208 else:
5208 else:
5209 raise
5209 raise
5210
5210
5211 if revs:
5211 if revs:
5212 revs = [repo.lookup(r) for r in scmutil.revrange(repo, revs)]
5212 revs = [repo.lookup(r) for r in scmutil.revrange(repo, revs)]
5213 if not revs:
5213 if not revs:
5214 raise util.Abort(_("specified revisions evaluate to an empty set"),
5214 raise util.Abort(_("specified revisions evaluate to an empty set"),
5215 hint=_("use different revision arguments"))
5215 hint=_("use different revision arguments"))
5216
5216
5217 repo._subtoppath = dest
5217 repo._subtoppath = dest
5218 try:
5218 try:
5219 # push subrepos depth-first for coherent ordering
5219 # push subrepos depth-first for coherent ordering
5220 c = repo['']
5220 c = repo['']
5221 subs = c.substate # only repos that are committed
5221 subs = c.substate # only repos that are committed
5222 for s in sorted(subs):
5222 for s in sorted(subs):
5223 result = c.sub(s).push(opts)
5223 result = c.sub(s).push(opts)
5224 if result == 0:
5224 if result == 0:
5225 return not result
5225 return not result
5226 finally:
5226 finally:
5227 del repo._subtoppath
5227 del repo._subtoppath
5228 pushop = exchange.push(repo, other, opts.get('force'), revs=revs,
5228 pushop = exchange.push(repo, other, opts.get('force'), revs=revs,
5229 newbranch=opts.get('new_branch'),
5229 newbranch=opts.get('new_branch'),
5230 bookmarks=opts.get('bookmark', ()))
5230 bookmarks=opts.get('bookmark', ()))
5231
5231
5232 result = not pushop.cgresult
5232 result = not pushop.cgresult
5233
5233
5234 if pushop.bkresult is not None:
5234 if pushop.bkresult is not None:
5235 if pushop.bkresult == 2:
5235 if pushop.bkresult == 2:
5236 result = 2
5236 result = 2
5237 elif not result and pushop.bkresult:
5237 elif not result and pushop.bkresult:
5238 result = 2
5238 result = 2
5239
5239
5240 return result
5240 return result
5241
5241
5242 @command('recover', [])
5242 @command('recover', [])
5243 def recover(ui, repo):
5243 def recover(ui, repo):
5244 """roll back an interrupted transaction
5244 """roll back an interrupted transaction
5245
5245
5246 Recover from an interrupted commit or pull.
5246 Recover from an interrupted commit or pull.
5247
5247
5248 This command tries to fix the repository status after an
5248 This command tries to fix the repository status after an
5249 interrupted operation. It should only be necessary when Mercurial
5249 interrupted operation. It should only be necessary when Mercurial
5250 suggests it.
5250 suggests it.
5251
5251
5252 Returns 0 if successful, 1 if nothing to recover or verify fails.
5252 Returns 0 if successful, 1 if nothing to recover or verify fails.
5253 """
5253 """
5254 if repo.recover():
5254 if repo.recover():
5255 return hg.verify(repo)
5255 return hg.verify(repo)
5256 return 1
5256 return 1
5257
5257
5258 @command('^remove|rm',
5258 @command('^remove|rm',
5259 [('A', 'after', None, _('record delete for missing files')),
5259 [('A', 'after', None, _('record delete for missing files')),
5260 ('f', 'force', None,
5260 ('f', 'force', None,
5261 _('remove (and delete) file even if added or modified')),
5261 _('remove (and delete) file even if added or modified')),
5262 ] + subrepoopts + walkopts,
5262 ] + subrepoopts + walkopts,
5263 _('[OPTION]... FILE...'),
5263 _('[OPTION]... FILE...'),
5264 inferrepo=True)
5264 inferrepo=True)
5265 def remove(ui, repo, *pats, **opts):
5265 def remove(ui, repo, *pats, **opts):
5266 """remove the specified files on the next commit
5266 """remove the specified files on the next commit
5267
5267
5268 Schedule the indicated files for removal from the current branch.
5268 Schedule the indicated files for removal from the current branch.
5269
5269
5270 This command schedules the files to be removed at the next commit.
5270 This command schedules the files to be removed at the next commit.
5271 To undo a remove before that, see :hg:`revert`. To undo added
5271 To undo a remove before that, see :hg:`revert`. To undo added
5272 files, see :hg:`forget`.
5272 files, see :hg:`forget`.
5273
5273
5274 .. container:: verbose
5274 .. container:: verbose
5275
5275
5276 -A/--after can be used to remove only files that have already
5276 -A/--after can be used to remove only files that have already
5277 been deleted, -f/--force can be used to force deletion, and -Af
5277 been deleted, -f/--force can be used to force deletion, and -Af
5278 can be used to remove files from the next revision without
5278 can be used to remove files from the next revision without
5279 deleting them from the working directory.
5279 deleting them from the working directory.
5280
5280
5281 The following table details the behavior of remove for different
5281 The following table details the behavior of remove for different
5282 file states (columns) and option combinations (rows). The file
5282 file states (columns) and option combinations (rows). The file
5283 states are Added [A], Clean [C], Modified [M] and Missing [!]
5283 states are Added [A], Clean [C], Modified [M] and Missing [!]
5284 (as reported by :hg:`status`). The actions are Warn, Remove
5284 (as reported by :hg:`status`). The actions are Warn, Remove
5285 (from branch) and Delete (from disk):
5285 (from branch) and Delete (from disk):
5286
5286
5287 ========= == == == ==
5287 ========= == == == ==
5288 opt/state A C M !
5288 opt/state A C M !
5289 ========= == == == ==
5289 ========= == == == ==
5290 none W RD W R
5290 none W RD W R
5291 -f R RD RD R
5291 -f R RD RD R
5292 -A W W W R
5292 -A W W W R
5293 -Af R R R R
5293 -Af R R R R
5294 ========= == == == ==
5294 ========= == == == ==
5295
5295
5296 Note that remove never deletes files in Added [A] state from the
5296 Note that remove never deletes files in Added [A] state from the
5297 working directory, not even if option --force is specified.
5297 working directory, not even if option --force is specified.
5298
5298
5299 Returns 0 on success, 1 if any warnings encountered.
5299 Returns 0 on success, 1 if any warnings encountered.
5300 """
5300 """
5301
5301
5302 after, force = opts.get('after'), opts.get('force')
5302 after, force = opts.get('after'), opts.get('force')
5303 if not pats and not after:
5303 if not pats and not after:
5304 raise util.Abort(_('no files specified'))
5304 raise util.Abort(_('no files specified'))
5305
5305
5306 m = scmutil.match(repo[None], pats, opts)
5306 m = scmutil.match(repo[None], pats, opts)
5307 subrepos = opts.get('subrepos')
5307 subrepos = opts.get('subrepos')
5308 return cmdutil.remove(ui, repo, m, "", after, force, subrepos)
5308 return cmdutil.remove(ui, repo, m, "", after, force, subrepos)
5309
5309
5310 @command('rename|move|mv',
5310 @command('rename|move|mv',
5311 [('A', 'after', None, _('record a rename that has already occurred')),
5311 [('A', 'after', None, _('record a rename that has already occurred')),
5312 ('f', 'force', None, _('forcibly copy over an existing managed file')),
5312 ('f', 'force', None, _('forcibly copy over an existing managed file')),
5313 ] + walkopts + dryrunopts,
5313 ] + walkopts + dryrunopts,
5314 _('[OPTION]... SOURCE... DEST'))
5314 _('[OPTION]... SOURCE... DEST'))
5315 def rename(ui, repo, *pats, **opts):
5315 def rename(ui, repo, *pats, **opts):
5316 """rename files; equivalent of copy + remove
5316 """rename files; equivalent of copy + remove
5317
5317
5318 Mark dest as copies of sources; mark sources for deletion. If dest
5318 Mark dest as copies of sources; mark sources for deletion. If dest
5319 is a directory, copies are put in that directory. If dest is a
5319 is a directory, copies are put in that directory. If dest is a
5320 file, there can only be one source.
5320 file, there can only be one source.
5321
5321
5322 By default, this command copies the contents of files as they
5322 By default, this command copies the contents of files as they
5323 exist in the working directory. If invoked with -A/--after, the
5323 exist in the working directory. If invoked with -A/--after, the
5324 operation is recorded, but no copying is performed.
5324 operation is recorded, but no copying is performed.
5325
5325
5326 This command takes effect at the next commit. To undo a rename
5326 This command takes effect at the next commit. To undo a rename
5327 before that, see :hg:`revert`.
5327 before that, see :hg:`revert`.
5328
5328
5329 Returns 0 on success, 1 if errors are encountered.
5329 Returns 0 on success, 1 if errors are encountered.
5330 """
5330 """
5331 wlock = repo.wlock(False)
5331 wlock = repo.wlock(False)
5332 try:
5332 try:
5333 return cmdutil.copy(ui, repo, pats, opts, rename=True)
5333 return cmdutil.copy(ui, repo, pats, opts, rename=True)
5334 finally:
5334 finally:
5335 wlock.release()
5335 wlock.release()
5336
5336
5337 @command('resolve',
5337 @command('resolve',
5338 [('a', 'all', None, _('select all unresolved files')),
5338 [('a', 'all', None, _('select all unresolved files')),
5339 ('l', 'list', None, _('list state of files needing merge')),
5339 ('l', 'list', None, _('list state of files needing merge')),
5340 ('m', 'mark', None, _('mark files as resolved')),
5340 ('m', 'mark', None, _('mark files as resolved')),
5341 ('u', 'unmark', None, _('mark files as unresolved')),
5341 ('u', 'unmark', None, _('mark files as unresolved')),
5342 ('n', 'no-status', None, _('hide status prefix'))]
5342 ('n', 'no-status', None, _('hide status prefix'))]
5343 + mergetoolopts + walkopts + formatteropts,
5343 + mergetoolopts + walkopts + formatteropts,
5344 _('[OPTION]... [FILE]...'),
5344 _('[OPTION]... [FILE]...'),
5345 inferrepo=True)
5345 inferrepo=True)
5346 def resolve(ui, repo, *pats, **opts):
5346 def resolve(ui, repo, *pats, **opts):
5347 """redo merges or set/view the merge status of files
5347 """redo merges or set/view the merge status of files
5348
5348
5349 Merges with unresolved conflicts are often the result of
5349 Merges with unresolved conflicts are often the result of
5350 non-interactive merging using the ``internal:merge`` configuration
5350 non-interactive merging using the ``internal:merge`` configuration
5351 setting, or a command-line merge tool like ``diff3``. The resolve
5351 setting, or a command-line merge tool like ``diff3``. The resolve
5352 command is used to manage the files involved in a merge, after
5352 command is used to manage the files involved in a merge, after
5353 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
5353 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
5354 working directory must have two parents). See :hg:`help
5354 working directory must have two parents). See :hg:`help
5355 merge-tools` for information on configuring merge tools.
5355 merge-tools` for information on configuring merge tools.
5356
5356
5357 The resolve command can be used in the following ways:
5357 The resolve command can be used in the following ways:
5358
5358
5359 - :hg:`resolve [--tool TOOL] FILE...`: attempt to re-merge the specified
5359 - :hg:`resolve [--tool TOOL] FILE...`: attempt to re-merge the specified
5360 files, discarding any previous merge attempts. Re-merging is not
5360 files, discarding any previous merge attempts. Re-merging is not
5361 performed for files already marked as resolved. Use ``--all/-a``
5361 performed for files already marked as resolved. Use ``--all/-a``
5362 to select all unresolved files. ``--tool`` can be used to specify
5362 to select all unresolved files. ``--tool`` can be used to specify
5363 the merge tool used for the given files. It overrides the HGMERGE
5363 the merge tool used for the given files. It overrides the HGMERGE
5364 environment variable and your configuration files. Previous file
5364 environment variable and your configuration files. Previous file
5365 contents are saved with a ``.orig`` suffix.
5365 contents are saved with a ``.orig`` suffix.
5366
5366
5367 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
5367 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
5368 (e.g. after having manually fixed-up the files). The default is
5368 (e.g. after having manually fixed-up the files). The default is
5369 to mark all unresolved files.
5369 to mark all unresolved files.
5370
5370
5371 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
5371 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
5372 default is to mark all resolved files.
5372 default is to mark all resolved files.
5373
5373
5374 - :hg:`resolve -l`: list files which had or still have conflicts.
5374 - :hg:`resolve -l`: list files which had or still have conflicts.
5375 In the printed list, ``U`` = unresolved and ``R`` = resolved.
5375 In the printed list, ``U`` = unresolved and ``R`` = resolved.
5376
5376
5377 Note that Mercurial will not let you commit files with unresolved
5377 Note that Mercurial will not let you commit files with unresolved
5378 merge conflicts. You must use :hg:`resolve -m ...` before you can
5378 merge conflicts. You must use :hg:`resolve -m ...` before you can
5379 commit after a conflicting merge.
5379 commit after a conflicting merge.
5380
5380
5381 Returns 0 on success, 1 if any files fail a resolve attempt.
5381 Returns 0 on success, 1 if any files fail a resolve attempt.
5382 """
5382 """
5383
5383
5384 all, mark, unmark, show, nostatus = \
5384 all, mark, unmark, show, nostatus = \
5385 [opts.get(o) for o in 'all mark unmark list no_status'.split()]
5385 [opts.get(o) for o in 'all mark unmark list no_status'.split()]
5386
5386
5387 if (show and (mark or unmark)) or (mark and unmark):
5387 if (show and (mark or unmark)) or (mark and unmark):
5388 raise util.Abort(_("too many options specified"))
5388 raise util.Abort(_("too many options specified"))
5389 if pats and all:
5389 if pats and all:
5390 raise util.Abort(_("can't specify --all and patterns"))
5390 raise util.Abort(_("can't specify --all and patterns"))
5391 if not (all or pats or show or mark or unmark):
5391 if not (all or pats or show or mark or unmark):
5392 raise util.Abort(_('no files or directories specified'),
5392 raise util.Abort(_('no files or directories specified'),
5393 hint=('use --all to remerge all files'))
5393 hint=('use --all to remerge all files'))
5394
5394
5395 if show:
5395 if show:
5396 fm = ui.formatter('resolve', opts)
5396 fm = ui.formatter('resolve', opts)
5397 ms = mergemod.mergestate(repo)
5397 ms = mergemod.mergestate(repo)
5398 m = scmutil.match(repo[None], pats, opts)
5398 m = scmutil.match(repo[None], pats, opts)
5399 for f in ms:
5399 for f in ms:
5400 if not m(f):
5400 if not m(f):
5401 continue
5401 continue
5402 l = 'resolve.' + {'u': 'unresolved', 'r': 'resolved'}[ms[f]]
5402 l = 'resolve.' + {'u': 'unresolved', 'r': 'resolved'}[ms[f]]
5403 fm.startitem()
5403 fm.startitem()
5404 fm.condwrite(not nostatus, 'status', '%s ', ms[f].upper(), label=l)
5404 fm.condwrite(not nostatus, 'status', '%s ', ms[f].upper(), label=l)
5405 fm.write('path', '%s\n', f, label=l)
5405 fm.write('path', '%s\n', f, label=l)
5406 fm.end()
5406 fm.end()
5407 return 0
5407 return 0
5408
5408
5409 wlock = repo.wlock()
5409 wlock = repo.wlock()
5410 try:
5410 try:
5411 ms = mergemod.mergestate(repo)
5411 ms = mergemod.mergestate(repo)
5412
5412
5413 if not (ms.active() or repo.dirstate.p2() != nullid):
5413 if not (ms.active() or repo.dirstate.p2() != nullid):
5414 raise util.Abort(
5414 raise util.Abort(
5415 _('resolve command not applicable when not merging'))
5415 _('resolve command not applicable when not merging'))
5416
5416
5417 m = scmutil.match(repo[None], pats, opts)
5417 m = scmutil.match(repo[None], pats, opts)
5418 ret = 0
5418 ret = 0
5419 didwork = False
5419 didwork = False
5420
5420
5421 for f in ms:
5421 for f in ms:
5422 if not m(f):
5422 if not m(f):
5423 continue
5423 continue
5424
5424
5425 didwork = True
5425 didwork = True
5426
5426
5427 if mark:
5427 if mark:
5428 ms.mark(f, "r")
5428 ms.mark(f, "r")
5429 elif unmark:
5429 elif unmark:
5430 ms.mark(f, "u")
5430 ms.mark(f, "u")
5431 else:
5431 else:
5432 wctx = repo[None]
5432 wctx = repo[None]
5433
5433
5434 # backup pre-resolve (merge uses .orig for its own purposes)
5434 # backup pre-resolve (merge uses .orig for its own purposes)
5435 a = repo.wjoin(f)
5435 a = repo.wjoin(f)
5436 util.copyfile(a, a + ".resolve")
5436 util.copyfile(a, a + ".resolve")
5437
5437
5438 try:
5438 try:
5439 # resolve file
5439 # resolve file
5440 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
5440 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
5441 'resolve')
5441 'resolve')
5442 if ms.resolve(f, wctx):
5442 if ms.resolve(f, wctx):
5443 ret = 1
5443 ret = 1
5444 finally:
5444 finally:
5445 ui.setconfig('ui', 'forcemerge', '', 'resolve')
5445 ui.setconfig('ui', 'forcemerge', '', 'resolve')
5446 ms.commit()
5446 ms.commit()
5447
5447
5448 # replace filemerge's .orig file with our resolve file
5448 # replace filemerge's .orig file with our resolve file
5449 util.rename(a + ".resolve", a + ".orig")
5449 util.rename(a + ".resolve", a + ".orig")
5450
5450
5451 ms.commit()
5451 ms.commit()
5452
5452
5453 if not didwork and pats:
5453 if not didwork and pats:
5454 ui.warn(_("arguments do not match paths that need resolving\n"))
5454 ui.warn(_("arguments do not match paths that need resolving\n"))
5455
5455
5456 finally:
5456 finally:
5457 wlock.release()
5457 wlock.release()
5458
5458
5459 # Nudge users into finishing an unfinished operation
5459 # Nudge users into finishing an unfinished operation
5460 if not list(ms.unresolved()):
5460 if not list(ms.unresolved()):
5461 ui.status(_('(no more unresolved files)\n'))
5461 ui.status(_('(no more unresolved files)\n'))
5462
5462
5463 return ret
5463 return ret
5464
5464
5465 @command('revert',
5465 @command('revert',
5466 [('a', 'all', None, _('revert all changes when no arguments given')),
5466 [('a', 'all', None, _('revert all changes when no arguments given')),
5467 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5467 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5468 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
5468 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
5469 ('C', 'no-backup', None, _('do not save backup copies of files')),
5469 ('C', 'no-backup', None, _('do not save backup copies of files')),
5470 ('i', 'interactive', None,
5470 ('i', 'interactive', None,
5471 _('interactively select the changes (EXPERIMENTAL)')),
5471 _('interactively select the changes (EXPERIMENTAL)')),
5472 ] + walkopts + dryrunopts,
5472 ] + walkopts + dryrunopts,
5473 _('[OPTION]... [-r REV] [NAME]...'))
5473 _('[OPTION]... [-r REV] [NAME]...'))
5474 def revert(ui, repo, *pats, **opts):
5474 def revert(ui, repo, *pats, **opts):
5475 """restore files to their checkout state
5475 """restore files to their checkout state
5476
5476
5477 .. note::
5477 .. note::
5478
5478
5479 To check out earlier revisions, you should use :hg:`update REV`.
5479 To check out earlier revisions, you should use :hg:`update REV`.
5480 To cancel an uncommitted merge (and lose your changes),
5480 To cancel an uncommitted merge (and lose your changes),
5481 use :hg:`update --clean .`.
5481 use :hg:`update --clean .`.
5482
5482
5483 With no revision specified, revert the specified files or directories
5483 With no revision specified, revert the specified files or directories
5484 to the contents they had in the parent of the working directory.
5484 to the contents they had in the parent of the working directory.
5485 This restores the contents of files to an unmodified
5485 This restores the contents of files to an unmodified
5486 state and unschedules adds, removes, copies, and renames. If the
5486 state and unschedules adds, removes, copies, and renames. If the
5487 working directory has two parents, you must explicitly specify a
5487 working directory has two parents, you must explicitly specify a
5488 revision.
5488 revision.
5489
5489
5490 Using the -r/--rev or -d/--date options, revert the given files or
5490 Using the -r/--rev or -d/--date options, revert the given files or
5491 directories to their states as of a specific revision. Because
5491 directories to their states as of a specific revision. Because
5492 revert does not change the working directory parents, this will
5492 revert does not change the working directory parents, this will
5493 cause these files to appear modified. This can be helpful to "back
5493 cause these files to appear modified. This can be helpful to "back
5494 out" some or all of an earlier change. See :hg:`backout` for a
5494 out" some or all of an earlier change. See :hg:`backout` for a
5495 related method.
5495 related method.
5496
5496
5497 Modified files are saved with a .orig suffix before reverting.
5497 Modified files are saved with a .orig suffix before reverting.
5498 To disable these backups, use --no-backup.
5498 To disable these backups, use --no-backup.
5499
5499
5500 See :hg:`help dates` for a list of formats valid for -d/--date.
5500 See :hg:`help dates` for a list of formats valid for -d/--date.
5501
5501
5502 Returns 0 on success.
5502 Returns 0 on success.
5503 """
5503 """
5504
5504
5505 if opts.get("date"):
5505 if opts.get("date"):
5506 if opts.get("rev"):
5506 if opts.get("rev"):
5507 raise util.Abort(_("you can't specify a revision and a date"))
5507 raise util.Abort(_("you can't specify a revision and a date"))
5508 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
5508 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
5509
5509
5510 parent, p2 = repo.dirstate.parents()
5510 parent, p2 = repo.dirstate.parents()
5511 if not opts.get('rev') and p2 != nullid:
5511 if not opts.get('rev') and p2 != nullid:
5512 # revert after merge is a trap for new users (issue2915)
5512 # revert after merge is a trap for new users (issue2915)
5513 raise util.Abort(_('uncommitted merge with no revision specified'),
5513 raise util.Abort(_('uncommitted merge with no revision specified'),
5514 hint=_('use "hg update" or see "hg help revert"'))
5514 hint=_('use "hg update" or see "hg help revert"'))
5515
5515
5516 ctx = scmutil.revsingle(repo, opts.get('rev'))
5516 ctx = scmutil.revsingle(repo, opts.get('rev'))
5517
5517
5518 if (not (pats or opts.get('include') or opts.get('exclude') or
5518 if (not (pats or opts.get('include') or opts.get('exclude') or
5519 opts.get('all') or opts.get('interactive'))):
5519 opts.get('all') or opts.get('interactive'))):
5520 msg = _("no files or directories specified")
5520 msg = _("no files or directories specified")
5521 if p2 != nullid:
5521 if p2 != nullid:
5522 hint = _("uncommitted merge, use --all to discard all changes,"
5522 hint = _("uncommitted merge, use --all to discard all changes,"
5523 " or 'hg update -C .' to abort the merge")
5523 " or 'hg update -C .' to abort the merge")
5524 raise util.Abort(msg, hint=hint)
5524 raise util.Abort(msg, hint=hint)
5525 dirty = any(repo.status())
5525 dirty = any(repo.status())
5526 node = ctx.node()
5526 node = ctx.node()
5527 if node != parent:
5527 if node != parent:
5528 if dirty:
5528 if dirty:
5529 hint = _("uncommitted changes, use --all to discard all"
5529 hint = _("uncommitted changes, use --all to discard all"
5530 " changes, or 'hg update %s' to update") % ctx.rev()
5530 " changes, or 'hg update %s' to update") % ctx.rev()
5531 else:
5531 else:
5532 hint = _("use --all to revert all files,"
5532 hint = _("use --all to revert all files,"
5533 " or 'hg update %s' to update") % ctx.rev()
5533 " or 'hg update %s' to update") % ctx.rev()
5534 elif dirty:
5534 elif dirty:
5535 hint = _("uncommitted changes, use --all to discard all changes")
5535 hint = _("uncommitted changes, use --all to discard all changes")
5536 else:
5536 else:
5537 hint = _("use --all to revert all files")
5537 hint = _("use --all to revert all files")
5538 raise util.Abort(msg, hint=hint)
5538 raise util.Abort(msg, hint=hint)
5539
5539
5540 return cmdutil.revert(ui, repo, ctx, (parent, p2), *pats, **opts)
5540 return cmdutil.revert(ui, repo, ctx, (parent, p2), *pats, **opts)
5541
5541
5542 @command('rollback', dryrunopts +
5542 @command('rollback', dryrunopts +
5543 [('f', 'force', False, _('ignore safety measures'))])
5543 [('f', 'force', False, _('ignore safety measures'))])
5544 def rollback(ui, repo, **opts):
5544 def rollback(ui, repo, **opts):
5545 """roll back the last transaction (DANGEROUS) (DEPRECATED)
5545 """roll back the last transaction (DANGEROUS) (DEPRECATED)
5546
5546
5547 Please use :hg:`commit --amend` instead of rollback to correct
5547 Please use :hg:`commit --amend` instead of rollback to correct
5548 mistakes in the last commit.
5548 mistakes in the last commit.
5549
5549
5550 This command should be used with care. There is only one level of
5550 This command should be used with care. There is only one level of
5551 rollback, and there is no way to undo a rollback. It will also
5551 rollback, and there is no way to undo a rollback. It will also
5552 restore the dirstate at the time of the last transaction, losing
5552 restore the dirstate at the time of the last transaction, losing
5553 any dirstate changes since that time. This command does not alter
5553 any dirstate changes since that time. This command does not alter
5554 the working directory.
5554 the working directory.
5555
5555
5556 Transactions are used to encapsulate the effects of all commands
5556 Transactions are used to encapsulate the effects of all commands
5557 that create new changesets or propagate existing changesets into a
5557 that create new changesets or propagate existing changesets into a
5558 repository.
5558 repository.
5559
5559
5560 .. container:: verbose
5560 .. container:: verbose
5561
5561
5562 For example, the following commands are transactional, and their
5562 For example, the following commands are transactional, and their
5563 effects can be rolled back:
5563 effects can be rolled back:
5564
5564
5565 - commit
5565 - commit
5566 - import
5566 - import
5567 - pull
5567 - pull
5568 - push (with this repository as the destination)
5568 - push (with this repository as the destination)
5569 - unbundle
5569 - unbundle
5570
5570
5571 To avoid permanent data loss, rollback will refuse to rollback a
5571 To avoid permanent data loss, rollback will refuse to rollback a
5572 commit transaction if it isn't checked out. Use --force to
5572 commit transaction if it isn't checked out. Use --force to
5573 override this protection.
5573 override this protection.
5574
5574
5575 This command is not intended for use on public repositories. Once
5575 This command is not intended for use on public repositories. Once
5576 changes are visible for pull by other users, rolling a transaction
5576 changes are visible for pull by other users, rolling a transaction
5577 back locally is ineffective (someone else may already have pulled
5577 back locally is ineffective (someone else may already have pulled
5578 the changes). Furthermore, a race is possible with readers of the
5578 the changes). Furthermore, a race is possible with readers of the
5579 repository; for example an in-progress pull from the repository
5579 repository; for example an in-progress pull from the repository
5580 may fail if a rollback is performed.
5580 may fail if a rollback is performed.
5581
5581
5582 Returns 0 on success, 1 if no rollback data is available.
5582 Returns 0 on success, 1 if no rollback data is available.
5583 """
5583 """
5584 return repo.rollback(dryrun=opts.get('dry_run'),
5584 return repo.rollback(dryrun=opts.get('dry_run'),
5585 force=opts.get('force'))
5585 force=opts.get('force'))
5586
5586
5587 @command('root', [])
5587 @command('root', [])
5588 def root(ui, repo):
5588 def root(ui, repo):
5589 """print the root (top) of the current working directory
5589 """print the root (top) of the current working directory
5590
5590
5591 Print the root directory of the current repository.
5591 Print the root directory of the current repository.
5592
5592
5593 Returns 0 on success.
5593 Returns 0 on success.
5594 """
5594 """
5595 ui.write(repo.root + "\n")
5595 ui.write(repo.root + "\n")
5596
5596
5597 @command('^serve',
5597 @command('^serve',
5598 [('A', 'accesslog', '', _('name of access log file to write to'),
5598 [('A', 'accesslog', '', _('name of access log file to write to'),
5599 _('FILE')),
5599 _('FILE')),
5600 ('d', 'daemon', None, _('run server in background')),
5600 ('d', 'daemon', None, _('run server in background')),
5601 ('', 'daemon-pipefds', '', _('used internally by daemon mode'), _('FILE')),
5601 ('', 'daemon-pipefds', '', _('used internally by daemon mode'), _('FILE')),
5602 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
5602 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
5603 # use string type, then we can check if something was passed
5603 # use string type, then we can check if something was passed
5604 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
5604 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
5605 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
5605 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
5606 _('ADDR')),
5606 _('ADDR')),
5607 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
5607 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
5608 _('PREFIX')),
5608 _('PREFIX')),
5609 ('n', 'name', '',
5609 ('n', 'name', '',
5610 _('name to show in web pages (default: working directory)'), _('NAME')),
5610 _('name to show in web pages (default: working directory)'), _('NAME')),
5611 ('', 'web-conf', '',
5611 ('', 'web-conf', '',
5612 _('name of the hgweb config file (see "hg help hgweb")'), _('FILE')),
5612 _('name of the hgweb config file (see "hg help hgweb")'), _('FILE')),
5613 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
5613 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
5614 _('FILE')),
5614 _('FILE')),
5615 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
5615 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
5616 ('', 'stdio', None, _('for remote clients')),
5616 ('', 'stdio', None, _('for remote clients')),
5617 ('', 'cmdserver', '', _('for remote clients'), _('MODE')),
5617 ('', 'cmdserver', '', _('for remote clients'), _('MODE')),
5618 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
5618 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
5619 ('', 'style', '', _('template style to use'), _('STYLE')),
5619 ('', 'style', '', _('template style to use'), _('STYLE')),
5620 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
5620 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
5621 ('', 'certificate', '', _('SSL certificate file'), _('FILE'))],
5621 ('', 'certificate', '', _('SSL certificate file'), _('FILE'))],
5622 _('[OPTION]...'),
5622 _('[OPTION]...'),
5623 optionalrepo=True)
5623 optionalrepo=True)
5624 def serve(ui, repo, **opts):
5624 def serve(ui, repo, **opts):
5625 """start stand-alone webserver
5625 """start stand-alone webserver
5626
5626
5627 Start a local HTTP repository browser and pull server. You can use
5627 Start a local HTTP repository browser and pull server. You can use
5628 this for ad-hoc sharing and browsing of repositories. It is
5628 this for ad-hoc sharing and browsing of repositories. It is
5629 recommended to use a real web server to serve a repository for
5629 recommended to use a real web server to serve a repository for
5630 longer periods of time.
5630 longer periods of time.
5631
5631
5632 Please note that the server does not implement access control.
5632 Please note that the server does not implement access control.
5633 This means that, by default, anybody can read from the server and
5633 This means that, by default, anybody can read from the server and
5634 nobody can write to it by default. Set the ``web.allow_push``
5634 nobody can write to it by default. Set the ``web.allow_push``
5635 option to ``*`` to allow everybody to push to the server. You
5635 option to ``*`` to allow everybody to push to the server. You
5636 should use a real web server if you need to authenticate users.
5636 should use a real web server if you need to authenticate users.
5637
5637
5638 By default, the server logs accesses to stdout and errors to
5638 By default, the server logs accesses to stdout and errors to
5639 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
5639 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
5640 files.
5640 files.
5641
5641
5642 To have the server choose a free port number to listen on, specify
5642 To have the server choose a free port number to listen on, specify
5643 a port number of 0; in this case, the server will print the port
5643 a port number of 0; in this case, the server will print the port
5644 number it uses.
5644 number it uses.
5645
5645
5646 Returns 0 on success.
5646 Returns 0 on success.
5647 """
5647 """
5648
5648
5649 if opts["stdio"] and opts["cmdserver"]:
5649 if opts["stdio"] and opts["cmdserver"]:
5650 raise util.Abort(_("cannot use --stdio with --cmdserver"))
5650 raise util.Abort(_("cannot use --stdio with --cmdserver"))
5651
5651
5652 if opts["stdio"]:
5652 if opts["stdio"]:
5653 if repo is None:
5653 if repo is None:
5654 raise error.RepoError(_("there is no Mercurial repository here"
5654 raise error.RepoError(_("there is no Mercurial repository here"
5655 " (.hg not found)"))
5655 " (.hg not found)"))
5656 s = sshserver.sshserver(ui, repo)
5656 s = sshserver.sshserver(ui, repo)
5657 s.serve_forever()
5657 s.serve_forever()
5658
5658
5659 if opts["cmdserver"]:
5659 if opts["cmdserver"]:
5660 service = commandserver.createservice(ui, repo, opts)
5660 service = commandserver.createservice(ui, repo, opts)
5661 return cmdutil.service(opts, initfn=service.init, runfn=service.run)
5661 return cmdutil.service(opts, initfn=service.init, runfn=service.run)
5662
5662
5663 # this way we can check if something was given in the command-line
5663 # this way we can check if something was given in the command-line
5664 if opts.get('port'):
5664 if opts.get('port'):
5665 opts['port'] = util.getport(opts.get('port'))
5665 opts['port'] = util.getport(opts.get('port'))
5666
5666
5667 if repo:
5667 if repo:
5668 baseui = repo.baseui
5668 baseui = repo.baseui
5669 else:
5669 else:
5670 baseui = ui
5670 baseui = ui
5671 optlist = ("name templates style address port prefix ipv6"
5671 optlist = ("name templates style address port prefix ipv6"
5672 " accesslog errorlog certificate encoding")
5672 " accesslog errorlog certificate encoding")
5673 for o in optlist.split():
5673 for o in optlist.split():
5674 val = opts.get(o, '')
5674 val = opts.get(o, '')
5675 if val in (None, ''): # should check against default options instead
5675 if val in (None, ''): # should check against default options instead
5676 continue
5676 continue
5677 baseui.setconfig("web", o, val, 'serve')
5677 baseui.setconfig("web", o, val, 'serve')
5678 if repo and repo.ui != baseui:
5678 if repo and repo.ui != baseui:
5679 repo.ui.setconfig("web", o, val, 'serve')
5679 repo.ui.setconfig("web", o, val, 'serve')
5680
5680
5681 o = opts.get('web_conf') or opts.get('webdir_conf')
5681 o = opts.get('web_conf') or opts.get('webdir_conf')
5682 if not o:
5682 if not o:
5683 if not repo:
5683 if not repo:
5684 raise error.RepoError(_("there is no Mercurial repository"
5684 raise error.RepoError(_("there is no Mercurial repository"
5685 " here (.hg not found)"))
5685 " here (.hg not found)"))
5686 o = repo
5686 o = repo
5687
5687
5688 app = hgweb.hgweb(o, baseui=baseui)
5688 app = hgweb.hgweb(o, baseui=baseui)
5689 service = httpservice(ui, app, opts)
5689 service = httpservice(ui, app, opts)
5690 cmdutil.service(opts, initfn=service.init, runfn=service.run)
5690 cmdutil.service(opts, initfn=service.init, runfn=service.run)
5691
5691
5692 class httpservice(object):
5692 class httpservice(object):
5693 def __init__(self, ui, app, opts):
5693 def __init__(self, ui, app, opts):
5694 self.ui = ui
5694 self.ui = ui
5695 self.app = app
5695 self.app = app
5696 self.opts = opts
5696 self.opts = opts
5697
5697
5698 def init(self):
5698 def init(self):
5699 util.setsignalhandler()
5699 util.setsignalhandler()
5700 self.httpd = hgweb_server.create_server(self.ui, self.app)
5700 self.httpd = hgweb_server.create_server(self.ui, self.app)
5701
5701
5702 if self.opts['port'] and not self.ui.verbose:
5702 if self.opts['port'] and not self.ui.verbose:
5703 return
5703 return
5704
5704
5705 if self.httpd.prefix:
5705 if self.httpd.prefix:
5706 prefix = self.httpd.prefix.strip('/') + '/'
5706 prefix = self.httpd.prefix.strip('/') + '/'
5707 else:
5707 else:
5708 prefix = ''
5708 prefix = ''
5709
5709
5710 port = ':%d' % self.httpd.port
5710 port = ':%d' % self.httpd.port
5711 if port == ':80':
5711 if port == ':80':
5712 port = ''
5712 port = ''
5713
5713
5714 bindaddr = self.httpd.addr
5714 bindaddr = self.httpd.addr
5715 if bindaddr == '0.0.0.0':
5715 if bindaddr == '0.0.0.0':
5716 bindaddr = '*'
5716 bindaddr = '*'
5717 elif ':' in bindaddr: # IPv6
5717 elif ':' in bindaddr: # IPv6
5718 bindaddr = '[%s]' % bindaddr
5718 bindaddr = '[%s]' % bindaddr
5719
5719
5720 fqaddr = self.httpd.fqaddr
5720 fqaddr = self.httpd.fqaddr
5721 if ':' in fqaddr:
5721 if ':' in fqaddr:
5722 fqaddr = '[%s]' % fqaddr
5722 fqaddr = '[%s]' % fqaddr
5723 if self.opts['port']:
5723 if self.opts['port']:
5724 write = self.ui.status
5724 write = self.ui.status
5725 else:
5725 else:
5726 write = self.ui.write
5726 write = self.ui.write
5727 write(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
5727 write(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
5728 (fqaddr, port, prefix, bindaddr, self.httpd.port))
5728 (fqaddr, port, prefix, bindaddr, self.httpd.port))
5729 self.ui.flush() # avoid buffering of status message
5729 self.ui.flush() # avoid buffering of status message
5730
5730
5731 def run(self):
5731 def run(self):
5732 self.httpd.serve_forever()
5732 self.httpd.serve_forever()
5733
5733
5734
5734
5735 @command('^status|st',
5735 @command('^status|st',
5736 [('A', 'all', None, _('show status of all files')),
5736 [('A', 'all', None, _('show status of all files')),
5737 ('m', 'modified', None, _('show only modified files')),
5737 ('m', 'modified', None, _('show only modified files')),
5738 ('a', 'added', None, _('show only added files')),
5738 ('a', 'added', None, _('show only added files')),
5739 ('r', 'removed', None, _('show only removed files')),
5739 ('r', 'removed', None, _('show only removed files')),
5740 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
5740 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
5741 ('c', 'clean', None, _('show only files without changes')),
5741 ('c', 'clean', None, _('show only files without changes')),
5742 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
5742 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
5743 ('i', 'ignored', None, _('show only ignored files')),
5743 ('i', 'ignored', None, _('show only ignored files')),
5744 ('n', 'no-status', None, _('hide status prefix')),
5744 ('n', 'no-status', None, _('hide status prefix')),
5745 ('C', 'copies', None, _('show source of copied files')),
5745 ('C', 'copies', None, _('show source of copied files')),
5746 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
5746 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
5747 ('', 'rev', [], _('show difference from revision'), _('REV')),
5747 ('', 'rev', [], _('show difference from revision'), _('REV')),
5748 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
5748 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
5749 ] + walkopts + subrepoopts + formatteropts,
5749 ] + walkopts + subrepoopts + formatteropts,
5750 _('[OPTION]... [FILE]...'),
5750 _('[OPTION]... [FILE]...'),
5751 inferrepo=True)
5751 inferrepo=True)
5752 def status(ui, repo, *pats, **opts):
5752 def status(ui, repo, *pats, **opts):
5753 """show changed files in the working directory
5753 """show changed files in the working directory
5754
5754
5755 Show status of files in the repository. If names are given, only
5755 Show status of files in the repository. If names are given, only
5756 files that match are shown. Files that are clean or ignored or
5756 files that match are shown. Files that are clean or ignored or
5757 the source of a copy/move operation, are not listed unless
5757 the source of a copy/move operation, are not listed unless
5758 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
5758 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
5759 Unless options described with "show only ..." are given, the
5759 Unless options described with "show only ..." are given, the
5760 options -mardu are used.
5760 options -mardu are used.
5761
5761
5762 Option -q/--quiet hides untracked (unknown and ignored) files
5762 Option -q/--quiet hides untracked (unknown and ignored) files
5763 unless explicitly requested with -u/--unknown or -i/--ignored.
5763 unless explicitly requested with -u/--unknown or -i/--ignored.
5764
5764
5765 .. note::
5765 .. note::
5766
5766
5767 status may appear to disagree with diff if permissions have
5767 status may appear to disagree with diff if permissions have
5768 changed or a merge has occurred. The standard diff format does
5768 changed or a merge has occurred. The standard diff format does
5769 not report permission changes and diff only reports changes
5769 not report permission changes and diff only reports changes
5770 relative to one merge parent.
5770 relative to one merge parent.
5771
5771
5772 If one revision is given, it is used as the base revision.
5772 If one revision is given, it is used as the base revision.
5773 If two revisions are given, the differences between them are
5773 If two revisions are given, the differences between them are
5774 shown. The --change option can also be used as a shortcut to list
5774 shown. The --change option can also be used as a shortcut to list
5775 the changed files of a revision from its first parent.
5775 the changed files of a revision from its first parent.
5776
5776
5777 The codes used to show the status of files are::
5777 The codes used to show the status of files are::
5778
5778
5779 M = modified
5779 M = modified
5780 A = added
5780 A = added
5781 R = removed
5781 R = removed
5782 C = clean
5782 C = clean
5783 ! = missing (deleted by non-hg command, but still tracked)
5783 ! = missing (deleted by non-hg command, but still tracked)
5784 ? = not tracked
5784 ? = not tracked
5785 I = ignored
5785 I = ignored
5786 = origin of the previous file (with --copies)
5786 = origin of the previous file (with --copies)
5787
5787
5788 .. container:: verbose
5788 .. container:: verbose
5789
5789
5790 Examples:
5790 Examples:
5791
5791
5792 - show changes in the working directory relative to a
5792 - show changes in the working directory relative to a
5793 changeset::
5793 changeset::
5794
5794
5795 hg status --rev 9353
5795 hg status --rev 9353
5796
5796
5797 - show changes in the working directory relative to the
5797 - show changes in the working directory relative to the
5798 current directory (see :hg:`help patterns` for more information)::
5798 current directory (see :hg:`help patterns` for more information)::
5799
5799
5800 hg status re:
5800 hg status re:
5801
5801
5802 - show all changes including copies in an existing changeset::
5802 - show all changes including copies in an existing changeset::
5803
5803
5804 hg status --copies --change 9353
5804 hg status --copies --change 9353
5805
5805
5806 - get a NUL separated list of added files, suitable for xargs::
5806 - get a NUL separated list of added files, suitable for xargs::
5807
5807
5808 hg status -an0
5808 hg status -an0
5809
5809
5810 Returns 0 on success.
5810 Returns 0 on success.
5811 """
5811 """
5812
5812
5813 revs = opts.get('rev')
5813 revs = opts.get('rev')
5814 change = opts.get('change')
5814 change = opts.get('change')
5815
5815
5816 if revs and change:
5816 if revs and change:
5817 msg = _('cannot specify --rev and --change at the same time')
5817 msg = _('cannot specify --rev and --change at the same time')
5818 raise util.Abort(msg)
5818 raise util.Abort(msg)
5819 elif change:
5819 elif change:
5820 node2 = scmutil.revsingle(repo, change, None).node()
5820 node2 = scmutil.revsingle(repo, change, None).node()
5821 node1 = repo[node2].p1().node()
5821 node1 = repo[node2].p1().node()
5822 else:
5822 else:
5823 node1, node2 = scmutil.revpair(repo, revs)
5823 node1, node2 = scmutil.revpair(repo, revs)
5824
5824
5825 if pats:
5825 if pats:
5826 cwd = repo.getcwd()
5826 cwd = repo.getcwd()
5827 else:
5827 else:
5828 cwd = ''
5828 cwd = ''
5829
5829
5830 if opts.get('print0'):
5830 if opts.get('print0'):
5831 end = '\0'
5831 end = '\0'
5832 else:
5832 else:
5833 end = '\n'
5833 end = '\n'
5834 copy = {}
5834 copy = {}
5835 states = 'modified added removed deleted unknown ignored clean'.split()
5835 states = 'modified added removed deleted unknown ignored clean'.split()
5836 show = [k for k in states if opts.get(k)]
5836 show = [k for k in states if opts.get(k)]
5837 if opts.get('all'):
5837 if opts.get('all'):
5838 show += ui.quiet and (states[:4] + ['clean']) or states
5838 show += ui.quiet and (states[:4] + ['clean']) or states
5839 if not show:
5839 if not show:
5840 if ui.quiet:
5840 if ui.quiet:
5841 show = states[:4]
5841 show = states[:4]
5842 else:
5842 else:
5843 show = states[:5]
5843 show = states[:5]
5844
5844
5845 m = scmutil.match(repo[node2], pats, opts)
5845 m = scmutil.match(repo[node2], pats, opts)
5846 stat = repo.status(node1, node2, m,
5846 stat = repo.status(node1, node2, m,
5847 'ignored' in show, 'clean' in show, 'unknown' in show,
5847 'ignored' in show, 'clean' in show, 'unknown' in show,
5848 opts.get('subrepos'))
5848 opts.get('subrepos'))
5849 changestates = zip(states, 'MAR!?IC', stat)
5849 changestates = zip(states, 'MAR!?IC', stat)
5850
5850
5851 if (opts.get('all') or opts.get('copies')
5851 if (opts.get('all') or opts.get('copies')
5852 or ui.configbool('ui', 'statuscopies')) and not opts.get('no_status'):
5852 or ui.configbool('ui', 'statuscopies')) and not opts.get('no_status'):
5853 copy = copies.pathcopies(repo[node1], repo[node2], m)
5853 copy = copies.pathcopies(repo[node1], repo[node2], m)
5854
5854
5855 fm = ui.formatter('status', opts)
5855 fm = ui.formatter('status', opts)
5856 fmt = '%s' + end
5856 fmt = '%s' + end
5857 showchar = not opts.get('no_status')
5857 showchar = not opts.get('no_status')
5858
5858
5859 for state, char, files in changestates:
5859 for state, char, files in changestates:
5860 if state in show:
5860 if state in show:
5861 label = 'status.' + state
5861 label = 'status.' + state
5862 for f in files:
5862 for f in files:
5863 fm.startitem()
5863 fm.startitem()
5864 fm.condwrite(showchar, 'status', '%s ', char, label=label)
5864 fm.condwrite(showchar, 'status', '%s ', char, label=label)
5865 fm.write('path', fmt, repo.pathto(f, cwd), label=label)
5865 fm.write('path', fmt, repo.pathto(f, cwd), label=label)
5866 if f in copy:
5866 if f in copy:
5867 fm.write("copy", ' %s' + end, repo.pathto(copy[f], cwd),
5867 fm.write("copy", ' %s' + end, repo.pathto(copy[f], cwd),
5868 label='status.copied')
5868 label='status.copied')
5869 fm.end()
5869 fm.end()
5870
5870
5871 @command('^summary|sum',
5871 @command('^summary|sum',
5872 [('', 'remote', None, _('check for push and pull'))], '[--remote]')
5872 [('', 'remote', None, _('check for push and pull'))], '[--remote]')
5873 def summary(ui, repo, **opts):
5873 def summary(ui, repo, **opts):
5874 """summarize working directory state
5874 """summarize working directory state
5875
5875
5876 This generates a brief summary of the working directory state,
5876 This generates a brief summary of the working directory state,
5877 including parents, branch, commit status, phase and available updates.
5877 including parents, branch, commit status, phase and available updates.
5878
5878
5879 With the --remote option, this will check the default paths for
5879 With the --remote option, this will check the default paths for
5880 incoming and outgoing changes. This can be time-consuming.
5880 incoming and outgoing changes. This can be time-consuming.
5881
5881
5882 Returns 0 on success.
5882 Returns 0 on success.
5883 """
5883 """
5884
5884
5885 ctx = repo[None]
5885 ctx = repo[None]
5886 parents = ctx.parents()
5886 parents = ctx.parents()
5887 pnode = parents[0].node()
5887 pnode = parents[0].node()
5888 marks = []
5888 marks = []
5889
5889
5890 for p in parents:
5890 for p in parents:
5891 # label with log.changeset (instead of log.parent) since this
5891 # label with log.changeset (instead of log.parent) since this
5892 # shows a working directory parent *changeset*:
5892 # shows a working directory parent *changeset*:
5893 # i18n: column positioning for "hg summary"
5893 # i18n: column positioning for "hg summary"
5894 ui.write(_('parent: %d:%s ') % (p.rev(), str(p)),
5894 ui.write(_('parent: %d:%s ') % (p.rev(), str(p)),
5895 label='log.changeset changeset.%s' % p.phasestr())
5895 label='log.changeset changeset.%s' % p.phasestr())
5896 ui.write(' '.join(p.tags()), label='log.tag')
5896 ui.write(' '.join(p.tags()), label='log.tag')
5897 if p.bookmarks():
5897 if p.bookmarks():
5898 marks.extend(p.bookmarks())
5898 marks.extend(p.bookmarks())
5899 if p.rev() == -1:
5899 if p.rev() == -1:
5900 if not len(repo):
5900 if not len(repo):
5901 ui.write(_(' (empty repository)'))
5901 ui.write(_(' (empty repository)'))
5902 else:
5902 else:
5903 ui.write(_(' (no revision checked out)'))
5903 ui.write(_(' (no revision checked out)'))
5904 ui.write('\n')
5904 ui.write('\n')
5905 if p.description():
5905 if p.description():
5906 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
5906 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
5907 label='log.summary')
5907 label='log.summary')
5908
5908
5909 branch = ctx.branch()
5909 branch = ctx.branch()
5910 bheads = repo.branchheads(branch)
5910 bheads = repo.branchheads(branch)
5911 # i18n: column positioning for "hg summary"
5911 # i18n: column positioning for "hg summary"
5912 m = _('branch: %s\n') % branch
5912 m = _('branch: %s\n') % branch
5913 if branch != 'default':
5913 if branch != 'default':
5914 ui.write(m, label='log.branch')
5914 ui.write(m, label='log.branch')
5915 else:
5915 else:
5916 ui.status(m, label='log.branch')
5916 ui.status(m, label='log.branch')
5917
5917
5918 if marks:
5918 if marks:
5919 current = repo._activebookmark
5919 current = repo._activebookmark
5920 # i18n: column positioning for "hg summary"
5920 # i18n: column positioning for "hg summary"
5921 ui.write(_('bookmarks:'), label='log.bookmark')
5921 ui.write(_('bookmarks:'), label='log.bookmark')
5922 if current is not None:
5922 if current is not None:
5923 if current in marks:
5923 if current in marks:
5924 ui.write(' *' + current, label='bookmarks.current')
5924 ui.write(' *' + current, label='bookmarks.current')
5925 marks.remove(current)
5925 marks.remove(current)
5926 else:
5926 else:
5927 ui.write(' [%s]' % current, label='bookmarks.current')
5927 ui.write(' [%s]' % current, label='bookmarks.current')
5928 for m in marks:
5928 for m in marks:
5929 ui.write(' ' + m, label='log.bookmark')
5929 ui.write(' ' + m, label='log.bookmark')
5930 ui.write('\n', label='log.bookmark')
5930 ui.write('\n', label='log.bookmark')
5931
5931
5932 status = repo.status(unknown=True)
5932 status = repo.status(unknown=True)
5933
5933
5934 c = repo.dirstate.copies()
5934 c = repo.dirstate.copies()
5935 copied, renamed = [], []
5935 copied, renamed = [], []
5936 for d, s in c.iteritems():
5936 for d, s in c.iteritems():
5937 if s in status.removed:
5937 if s in status.removed:
5938 status.removed.remove(s)
5938 status.removed.remove(s)
5939 renamed.append(d)
5939 renamed.append(d)
5940 else:
5940 else:
5941 copied.append(d)
5941 copied.append(d)
5942 if d in status.added:
5942 if d in status.added:
5943 status.added.remove(d)
5943 status.added.remove(d)
5944
5944
5945 ms = mergemod.mergestate(repo)
5945 ms = mergemod.mergestate(repo)
5946 unresolved = [f for f in ms if ms[f] == 'u']
5946 unresolved = [f for f in ms if ms[f] == 'u']
5947
5947
5948 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
5948 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
5949
5949
5950 labels = [(ui.label(_('%d modified'), 'status.modified'), status.modified),
5950 labels = [(ui.label(_('%d modified'), 'status.modified'), status.modified),
5951 (ui.label(_('%d added'), 'status.added'), status.added),
5951 (ui.label(_('%d added'), 'status.added'), status.added),
5952 (ui.label(_('%d removed'), 'status.removed'), status.removed),
5952 (ui.label(_('%d removed'), 'status.removed'), status.removed),
5953 (ui.label(_('%d renamed'), 'status.copied'), renamed),
5953 (ui.label(_('%d renamed'), 'status.copied'), renamed),
5954 (ui.label(_('%d copied'), 'status.copied'), copied),
5954 (ui.label(_('%d copied'), 'status.copied'), copied),
5955 (ui.label(_('%d deleted'), 'status.deleted'), status.deleted),
5955 (ui.label(_('%d deleted'), 'status.deleted'), status.deleted),
5956 (ui.label(_('%d unknown'), 'status.unknown'), status.unknown),
5956 (ui.label(_('%d unknown'), 'status.unknown'), status.unknown),
5957 (ui.label(_('%d unresolved'), 'resolve.unresolved'), unresolved),
5957 (ui.label(_('%d unresolved'), 'resolve.unresolved'), unresolved),
5958 (ui.label(_('%d subrepos'), 'status.modified'), subs)]
5958 (ui.label(_('%d subrepos'), 'status.modified'), subs)]
5959 t = []
5959 t = []
5960 for l, s in labels:
5960 for l, s in labels:
5961 if s:
5961 if s:
5962 t.append(l % len(s))
5962 t.append(l % len(s))
5963
5963
5964 t = ', '.join(t)
5964 t = ', '.join(t)
5965 cleanworkdir = False
5965 cleanworkdir = False
5966
5966
5967 if repo.vfs.exists('updatestate'):
5967 if repo.vfs.exists('updatestate'):
5968 t += _(' (interrupted update)')
5968 t += _(' (interrupted update)')
5969 elif len(parents) > 1:
5969 elif len(parents) > 1:
5970 t += _(' (merge)')
5970 t += _(' (merge)')
5971 elif branch != parents[0].branch():
5971 elif branch != parents[0].branch():
5972 t += _(' (new branch)')
5972 t += _(' (new branch)')
5973 elif (parents[0].closesbranch() and
5973 elif (parents[0].closesbranch() and
5974 pnode in repo.branchheads(branch, closed=True)):
5974 pnode in repo.branchheads(branch, closed=True)):
5975 t += _(' (head closed)')
5975 t += _(' (head closed)')
5976 elif not (status.modified or status.added or status.removed or renamed or
5976 elif not (status.modified or status.added or status.removed or renamed or
5977 copied or subs):
5977 copied or subs):
5978 t += _(' (clean)')
5978 t += _(' (clean)')
5979 cleanworkdir = True
5979 cleanworkdir = True
5980 elif pnode not in bheads:
5980 elif pnode not in bheads:
5981 t += _(' (new branch head)')
5981 t += _(' (new branch head)')
5982
5982
5983 if cleanworkdir:
5983 if cleanworkdir:
5984 # i18n: column positioning for "hg summary"
5984 # i18n: column positioning for "hg summary"
5985 ui.status(_('commit: %s\n') % t.strip())
5985 ui.status(_('commit: %s\n') % t.strip())
5986 else:
5986 else:
5987 # i18n: column positioning for "hg summary"
5987 # i18n: column positioning for "hg summary"
5988 ui.write(_('commit: %s\n') % t.strip())
5988 ui.write(_('commit: %s\n') % t.strip())
5989
5989
5990 # all ancestors of branch heads - all ancestors of parent = new csets
5990 # all ancestors of branch heads - all ancestors of parent = new csets
5991 new = len(repo.changelog.findmissing([pctx.node() for pctx in parents],
5991 new = len(repo.changelog.findmissing([pctx.node() for pctx in parents],
5992 bheads))
5992 bheads))
5993
5993
5994 if new == 0:
5994 if new == 0:
5995 # i18n: column positioning for "hg summary"
5995 # i18n: column positioning for "hg summary"
5996 ui.status(_('update: (current)\n'))
5996 ui.status(_('update: (current)\n'))
5997 elif pnode not in bheads:
5997 elif pnode not in bheads:
5998 # i18n: column positioning for "hg summary"
5998 # i18n: column positioning for "hg summary"
5999 ui.write(_('update: %d new changesets (update)\n') % new)
5999 ui.write(_('update: %d new changesets (update)\n') % new)
6000 else:
6000 else:
6001 # i18n: column positioning for "hg summary"
6001 # i18n: column positioning for "hg summary"
6002 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
6002 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
6003 (new, len(bheads)))
6003 (new, len(bheads)))
6004
6004
6005 t = []
6005 t = []
6006 draft = len(repo.revs('draft()'))
6006 draft = len(repo.revs('draft()'))
6007 if draft:
6007 if draft:
6008 t.append(_('%d draft') % draft)
6008 t.append(_('%d draft') % draft)
6009 secret = len(repo.revs('secret()'))
6009 secret = len(repo.revs('secret()'))
6010 if secret:
6010 if secret:
6011 t.append(_('%d secret') % secret)
6011 t.append(_('%d secret') % secret)
6012
6012
6013 if parents:
6013 if parents:
6014 parentphase = max(p.phase() for p in parents)
6014 parentphase = max(p.phase() for p in parents)
6015 else:
6015 else:
6016 parentphase = phases.public
6016 parentphase = phases.public
6017
6017
6018 if draft or secret:
6018 if draft or secret:
6019 ui.status(_('phases: %s (%s)\n') % (', '.join(t),
6019 ui.status(_('phases: %s (%s)\n') % (', '.join(t),
6020 phases.phasenames[parentphase]))
6020 phases.phasenames[parentphase]))
6021 else:
6021 else:
6022 ui.note(_('phases: (%s)\n') % phases.phasenames[parentphase])
6022 ui.note(_('phases: (%s)\n') % phases.phasenames[parentphase])
6023
6023
6024 cmdutil.summaryhooks(ui, repo)
6024 cmdutil.summaryhooks(ui, repo)
6025
6025
6026 if opts.get('remote'):
6026 if opts.get('remote'):
6027 needsincoming, needsoutgoing = True, True
6027 needsincoming, needsoutgoing = True, True
6028 else:
6028 else:
6029 needsincoming, needsoutgoing = False, False
6029 needsincoming, needsoutgoing = False, False
6030 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
6030 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
6031 if i:
6031 if i:
6032 needsincoming = True
6032 needsincoming = True
6033 if o:
6033 if o:
6034 needsoutgoing = True
6034 needsoutgoing = True
6035 if not needsincoming and not needsoutgoing:
6035 if not needsincoming and not needsoutgoing:
6036 return
6036 return
6037
6037
6038 def getincoming():
6038 def getincoming():
6039 source, branches = hg.parseurl(ui.expandpath('default'))
6039 source, branches = hg.parseurl(ui.expandpath('default'))
6040 sbranch = branches[0]
6040 sbranch = branches[0]
6041 try:
6041 try:
6042 other = hg.peer(repo, {}, source)
6042 other = hg.peer(repo, {}, source)
6043 except error.RepoError:
6043 except error.RepoError:
6044 if opts.get('remote'):
6044 if opts.get('remote'):
6045 raise
6045 raise
6046 return source, sbranch, None, None, None
6046 return source, sbranch, None, None, None
6047 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
6047 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
6048 if revs:
6048 if revs:
6049 revs = [other.lookup(rev) for rev in revs]
6049 revs = [other.lookup(rev) for rev in revs]
6050 ui.debug('comparing with %s\n' % util.hidepassword(source))
6050 ui.debug('comparing with %s\n' % util.hidepassword(source))
6051 repo.ui.pushbuffer()
6051 repo.ui.pushbuffer()
6052 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
6052 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
6053 repo.ui.popbuffer()
6053 repo.ui.popbuffer()
6054 return source, sbranch, other, commoninc, commoninc[1]
6054 return source, sbranch, other, commoninc, commoninc[1]
6055
6055
6056 if needsincoming:
6056 if needsincoming:
6057 source, sbranch, sother, commoninc, incoming = getincoming()
6057 source, sbranch, sother, commoninc, incoming = getincoming()
6058 else:
6058 else:
6059 source = sbranch = sother = commoninc = incoming = None
6059 source = sbranch = sother = commoninc = incoming = None
6060
6060
6061 def getoutgoing():
6061 def getoutgoing():
6062 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
6062 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
6063 dbranch = branches[0]
6063 dbranch = branches[0]
6064 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
6064 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
6065 if source != dest:
6065 if source != dest:
6066 try:
6066 try:
6067 dother = hg.peer(repo, {}, dest)
6067 dother = hg.peer(repo, {}, dest)
6068 except error.RepoError:
6068 except error.RepoError:
6069 if opts.get('remote'):
6069 if opts.get('remote'):
6070 raise
6070 raise
6071 return dest, dbranch, None, None
6071 return dest, dbranch, None, None
6072 ui.debug('comparing with %s\n' % util.hidepassword(dest))
6072 ui.debug('comparing with %s\n' % util.hidepassword(dest))
6073 elif sother is None:
6073 elif sother is None:
6074 # there is no explicit destination peer, but source one is invalid
6074 # there is no explicit destination peer, but source one is invalid
6075 return dest, dbranch, None, None
6075 return dest, dbranch, None, None
6076 else:
6076 else:
6077 dother = sother
6077 dother = sother
6078 if (source != dest or (sbranch is not None and sbranch != dbranch)):
6078 if (source != dest or (sbranch is not None and sbranch != dbranch)):
6079 common = None
6079 common = None
6080 else:
6080 else:
6081 common = commoninc
6081 common = commoninc
6082 if revs:
6082 if revs:
6083 revs = [repo.lookup(rev) for rev in revs]
6083 revs = [repo.lookup(rev) for rev in revs]
6084 repo.ui.pushbuffer()
6084 repo.ui.pushbuffer()
6085 outgoing = discovery.findcommonoutgoing(repo, dother, onlyheads=revs,
6085 outgoing = discovery.findcommonoutgoing(repo, dother, onlyheads=revs,
6086 commoninc=common)
6086 commoninc=common)
6087 repo.ui.popbuffer()
6087 repo.ui.popbuffer()
6088 return dest, dbranch, dother, outgoing
6088 return dest, dbranch, dother, outgoing
6089
6089
6090 if needsoutgoing:
6090 if needsoutgoing:
6091 dest, dbranch, dother, outgoing = getoutgoing()
6091 dest, dbranch, dother, outgoing = getoutgoing()
6092 else:
6092 else:
6093 dest = dbranch = dother = outgoing = None
6093 dest = dbranch = dother = outgoing = None
6094
6094
6095 if opts.get('remote'):
6095 if opts.get('remote'):
6096 t = []
6096 t = []
6097 if incoming:
6097 if incoming:
6098 t.append(_('1 or more incoming'))
6098 t.append(_('1 or more incoming'))
6099 o = outgoing.missing
6099 o = outgoing.missing
6100 if o:
6100 if o:
6101 t.append(_('%d outgoing') % len(o))
6101 t.append(_('%d outgoing') % len(o))
6102 other = dother or sother
6102 other = dother or sother
6103 if 'bookmarks' in other.listkeys('namespaces'):
6103 if 'bookmarks' in other.listkeys('namespaces'):
6104 counts = bookmarks.summary(repo, other)
6104 counts = bookmarks.summary(repo, other)
6105 if counts[0] > 0:
6105 if counts[0] > 0:
6106 t.append(_('%d incoming bookmarks') % counts[0])
6106 t.append(_('%d incoming bookmarks') % counts[0])
6107 if counts[1] > 0:
6107 if counts[1] > 0:
6108 t.append(_('%d outgoing bookmarks') % counts[1])
6108 t.append(_('%d outgoing bookmarks') % counts[1])
6109
6109
6110 if t:
6110 if t:
6111 # i18n: column positioning for "hg summary"
6111 # i18n: column positioning for "hg summary"
6112 ui.write(_('remote: %s\n') % (', '.join(t)))
6112 ui.write(_('remote: %s\n') % (', '.join(t)))
6113 else:
6113 else:
6114 # i18n: column positioning for "hg summary"
6114 # i18n: column positioning for "hg summary"
6115 ui.status(_('remote: (synced)\n'))
6115 ui.status(_('remote: (synced)\n'))
6116
6116
6117 cmdutil.summaryremotehooks(ui, repo, opts,
6117 cmdutil.summaryremotehooks(ui, repo, opts,
6118 ((source, sbranch, sother, commoninc),
6118 ((source, sbranch, sother, commoninc),
6119 (dest, dbranch, dother, outgoing)))
6119 (dest, dbranch, dother, outgoing)))
6120
6120
6121 @command('tag',
6121 @command('tag',
6122 [('f', 'force', None, _('force tag')),
6122 [('f', 'force', None, _('force tag')),
6123 ('l', 'local', None, _('make the tag local')),
6123 ('l', 'local', None, _('make the tag local')),
6124 ('r', 'rev', '', _('revision to tag'), _('REV')),
6124 ('r', 'rev', '', _('revision to tag'), _('REV')),
6125 ('', 'remove', None, _('remove a tag')),
6125 ('', 'remove', None, _('remove a tag')),
6126 # -l/--local is already there, commitopts cannot be used
6126 # -l/--local is already there, commitopts cannot be used
6127 ('e', 'edit', None, _('invoke editor on commit messages')),
6127 ('e', 'edit', None, _('invoke editor on commit messages')),
6128 ('m', 'message', '', _('use text as commit message'), _('TEXT')),
6128 ('m', 'message', '', _('use text as commit message'), _('TEXT')),
6129 ] + commitopts2,
6129 ] + commitopts2,
6130 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'))
6130 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'))
6131 def tag(ui, repo, name1, *names, **opts):
6131 def tag(ui, repo, name1, *names, **opts):
6132 """add one or more tags for the current or given revision
6132 """add one or more tags for the current or given revision
6133
6133
6134 Name a particular revision using <name>.
6134 Name a particular revision using <name>.
6135
6135
6136 Tags are used to name particular revisions of the repository and are
6136 Tags are used to name particular revisions of the repository and are
6137 very useful to compare different revisions, to go back to significant
6137 very useful to compare different revisions, to go back to significant
6138 earlier versions or to mark branch points as releases, etc. Changing
6138 earlier versions or to mark branch points as releases, etc. Changing
6139 an existing tag is normally disallowed; use -f/--force to override.
6139 an existing tag is normally disallowed; use -f/--force to override.
6140
6140
6141 If no revision is given, the parent of the working directory is
6141 If no revision is given, the parent of the working directory is
6142 used.
6142 used.
6143
6143
6144 To facilitate version control, distribution, and merging of tags,
6144 To facilitate version control, distribution, and merging of tags,
6145 they are stored as a file named ".hgtags" which is managed similarly
6145 they are stored as a file named ".hgtags" which is managed similarly
6146 to other project files and can be hand-edited if necessary. This
6146 to other project files and can be hand-edited if necessary. This
6147 also means that tagging creates a new commit. The file
6147 also means that tagging creates a new commit. The file
6148 ".hg/localtags" is used for local tags (not shared among
6148 ".hg/localtags" is used for local tags (not shared among
6149 repositories).
6149 repositories).
6150
6150
6151 Tag commits are usually made at the head of a branch. If the parent
6151 Tag commits are usually made at the head of a branch. If the parent
6152 of the working directory is not a branch head, :hg:`tag` aborts; use
6152 of the working directory is not a branch head, :hg:`tag` aborts; use
6153 -f/--force to force the tag commit to be based on a non-head
6153 -f/--force to force the tag commit to be based on a non-head
6154 changeset.
6154 changeset.
6155
6155
6156 See :hg:`help dates` for a list of formats valid for -d/--date.
6156 See :hg:`help dates` for a list of formats valid for -d/--date.
6157
6157
6158 Since tag names have priority over branch names during revision
6158 Since tag names have priority over branch names during revision
6159 lookup, using an existing branch name as a tag name is discouraged.
6159 lookup, using an existing branch name as a tag name is discouraged.
6160
6160
6161 Returns 0 on success.
6161 Returns 0 on success.
6162 """
6162 """
6163 wlock = lock = None
6163 wlock = lock = None
6164 try:
6164 try:
6165 wlock = repo.wlock()
6165 wlock = repo.wlock()
6166 lock = repo.lock()
6166 lock = repo.lock()
6167 rev_ = "."
6167 rev_ = "."
6168 names = [t.strip() for t in (name1,) + names]
6168 names = [t.strip() for t in (name1,) + names]
6169 if len(names) != len(set(names)):
6169 if len(names) != len(set(names)):
6170 raise util.Abort(_('tag names must be unique'))
6170 raise util.Abort(_('tag names must be unique'))
6171 for n in names:
6171 for n in names:
6172 scmutil.checknewlabel(repo, n, 'tag')
6172 scmutil.checknewlabel(repo, n, 'tag')
6173 if not n:
6173 if not n:
6174 raise util.Abort(_('tag names cannot consist entirely of '
6174 raise util.Abort(_('tag names cannot consist entirely of '
6175 'whitespace'))
6175 'whitespace'))
6176 if opts.get('rev') and opts.get('remove'):
6176 if opts.get('rev') and opts.get('remove'):
6177 raise util.Abort(_("--rev and --remove are incompatible"))
6177 raise util.Abort(_("--rev and --remove are incompatible"))
6178 if opts.get('rev'):
6178 if opts.get('rev'):
6179 rev_ = opts['rev']
6179 rev_ = opts['rev']
6180 message = opts.get('message')
6180 message = opts.get('message')
6181 if opts.get('remove'):
6181 if opts.get('remove'):
6182 if opts.get('local'):
6182 if opts.get('local'):
6183 expectedtype = 'local'
6183 expectedtype = 'local'
6184 else:
6184 else:
6185 expectedtype = 'global'
6185 expectedtype = 'global'
6186
6186
6187 for n in names:
6187 for n in names:
6188 if not repo.tagtype(n):
6188 if not repo.tagtype(n):
6189 raise util.Abort(_("tag '%s' does not exist") % n)
6189 raise util.Abort(_("tag '%s' does not exist") % n)
6190 if repo.tagtype(n) != expectedtype:
6190 if repo.tagtype(n) != expectedtype:
6191 if expectedtype == 'global':
6191 if expectedtype == 'global':
6192 raise util.Abort(_("tag '%s' is not a global tag") % n)
6192 raise util.Abort(_("tag '%s' is not a global tag") % n)
6193 else:
6193 else:
6194 raise util.Abort(_("tag '%s' is not a local tag") % n)
6194 raise util.Abort(_("tag '%s' is not a local tag") % n)
6195 rev_ = nullid
6195 rev_ = nullid
6196 if not message:
6196 if not message:
6197 # we don't translate commit messages
6197 # we don't translate commit messages
6198 message = 'Removed tag %s' % ', '.join(names)
6198 message = 'Removed tag %s' % ', '.join(names)
6199 elif not opts.get('force'):
6199 elif not opts.get('force'):
6200 for n in names:
6200 for n in names:
6201 if n in repo.tags():
6201 if n in repo.tags():
6202 raise util.Abort(_("tag '%s' already exists "
6202 raise util.Abort(_("tag '%s' already exists "
6203 "(use -f to force)") % n)
6203 "(use -f to force)") % n)
6204 if not opts.get('local'):
6204 if not opts.get('local'):
6205 p1, p2 = repo.dirstate.parents()
6205 p1, p2 = repo.dirstate.parents()
6206 if p2 != nullid:
6206 if p2 != nullid:
6207 raise util.Abort(_('uncommitted merge'))
6207 raise util.Abort(_('uncommitted merge'))
6208 bheads = repo.branchheads()
6208 bheads = repo.branchheads()
6209 if not opts.get('force') and bheads and p1 not in bheads:
6209 if not opts.get('force') and bheads and p1 not in bheads:
6210 raise util.Abort(_('not at a branch head (use -f to force)'))
6210 raise util.Abort(_('not at a branch head (use -f to force)'))
6211 r = scmutil.revsingle(repo, rev_).node()
6211 r = scmutil.revsingle(repo, rev_).node()
6212
6212
6213 if not message:
6213 if not message:
6214 # we don't translate commit messages
6214 # we don't translate commit messages
6215 message = ('Added tag %s for changeset %s' %
6215 message = ('Added tag %s for changeset %s' %
6216 (', '.join(names), short(r)))
6216 (', '.join(names), short(r)))
6217
6217
6218 date = opts.get('date')
6218 date = opts.get('date')
6219 if date:
6219 if date:
6220 date = util.parsedate(date)
6220 date = util.parsedate(date)
6221
6221
6222 if opts.get('remove'):
6222 if opts.get('remove'):
6223 editform = 'tag.remove'
6223 editform = 'tag.remove'
6224 else:
6224 else:
6225 editform = 'tag.add'
6225 editform = 'tag.add'
6226 editor = cmdutil.getcommiteditor(editform=editform, **opts)
6226 editor = cmdutil.getcommiteditor(editform=editform, **opts)
6227
6227
6228 # don't allow tagging the null rev
6228 # don't allow tagging the null rev
6229 if (not opts.get('remove') and
6229 if (not opts.get('remove') and
6230 scmutil.revsingle(repo, rev_).rev() == nullrev):
6230 scmutil.revsingle(repo, rev_).rev() == nullrev):
6231 raise util.Abort(_("cannot tag null revision"))
6231 raise util.Abort(_("cannot tag null revision"))
6232
6232
6233 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date,
6233 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date,
6234 editor=editor)
6234 editor=editor)
6235 finally:
6235 finally:
6236 release(lock, wlock)
6236 release(lock, wlock)
6237
6237
6238 @command('tags', formatteropts, '')
6238 @command('tags', formatteropts, '')
6239 def tags(ui, repo, **opts):
6239 def tags(ui, repo, **opts):
6240 """list repository tags
6240 """list repository tags
6241
6241
6242 This lists both regular and local tags. When the -v/--verbose
6242 This lists both regular and local tags. When the -v/--verbose
6243 switch is used, a third column "local" is printed for local tags.
6243 switch is used, a third column "local" is printed for local tags.
6244
6244
6245 Returns 0 on success.
6245 Returns 0 on success.
6246 """
6246 """
6247
6247
6248 fm = ui.formatter('tags', opts)
6248 fm = ui.formatter('tags', opts)
6249 hexfunc = fm.hexfunc
6249 hexfunc = fm.hexfunc
6250 tagtype = ""
6250 tagtype = ""
6251
6251
6252 for t, n in reversed(repo.tagslist()):
6252 for t, n in reversed(repo.tagslist()):
6253 hn = hexfunc(n)
6253 hn = hexfunc(n)
6254 label = 'tags.normal'
6254 label = 'tags.normal'
6255 tagtype = ''
6255 tagtype = ''
6256 if repo.tagtype(t) == 'local':
6256 if repo.tagtype(t) == 'local':
6257 label = 'tags.local'
6257 label = 'tags.local'
6258 tagtype = 'local'
6258 tagtype = 'local'
6259
6259
6260 fm.startitem()
6260 fm.startitem()
6261 fm.write('tag', '%s', t, label=label)
6261 fm.write('tag', '%s', t, label=label)
6262 fmt = " " * (30 - encoding.colwidth(t)) + ' %5d:%s'
6262 fmt = " " * (30 - encoding.colwidth(t)) + ' %5d:%s'
6263 fm.condwrite(not ui.quiet, 'rev node', fmt,
6263 fm.condwrite(not ui.quiet, 'rev node', fmt,
6264 repo.changelog.rev(n), hn, label=label)
6264 repo.changelog.rev(n), hn, label=label)
6265 fm.condwrite(ui.verbose and tagtype, 'type', ' %s',
6265 fm.condwrite(ui.verbose and tagtype, 'type', ' %s',
6266 tagtype, label=label)
6266 tagtype, label=label)
6267 fm.plain('\n')
6267 fm.plain('\n')
6268 fm.end()
6268 fm.end()
6269
6269
6270 @command('tip',
6270 @command('tip',
6271 [('p', 'patch', None, _('show patch')),
6271 [('p', 'patch', None, _('show patch')),
6272 ('g', 'git', None, _('use git extended diff format')),
6272 ('g', 'git', None, _('use git extended diff format')),
6273 ] + templateopts,
6273 ] + templateopts,
6274 _('[-p] [-g]'))
6274 _('[-p] [-g]'))
6275 def tip(ui, repo, **opts):
6275 def tip(ui, repo, **opts):
6276 """show the tip revision (DEPRECATED)
6276 """show the tip revision (DEPRECATED)
6277
6277
6278 The tip revision (usually just called the tip) is the changeset
6278 The tip revision (usually just called the tip) is the changeset
6279 most recently added to the repository (and therefore the most
6279 most recently added to the repository (and therefore the most
6280 recently changed head).
6280 recently changed head).
6281
6281
6282 If you have just made a commit, that commit will be the tip. If
6282 If you have just made a commit, that commit will be the tip. If
6283 you have just pulled changes from another repository, the tip of
6283 you have just pulled changes from another repository, the tip of
6284 that repository becomes the current tip. The "tip" tag is special
6284 that repository becomes the current tip. The "tip" tag is special
6285 and cannot be renamed or assigned to a different changeset.
6285 and cannot be renamed or assigned to a different changeset.
6286
6286
6287 This command is deprecated, please use :hg:`heads` instead.
6287 This command is deprecated, please use :hg:`heads` instead.
6288
6288
6289 Returns 0 on success.
6289 Returns 0 on success.
6290 """
6290 """
6291 displayer = cmdutil.show_changeset(ui, repo, opts)
6291 displayer = cmdutil.show_changeset(ui, repo, opts)
6292 displayer.show(repo['tip'])
6292 displayer.show(repo['tip'])
6293 displayer.close()
6293 displayer.close()
6294
6294
6295 @command('unbundle',
6295 @command('unbundle',
6296 [('u', 'update', None,
6296 [('u', 'update', None,
6297 _('update to new branch head if changesets were unbundled'))],
6297 _('update to new branch head if changesets were unbundled'))],
6298 _('[-u] FILE...'))
6298 _('[-u] FILE...'))
6299 def unbundle(ui, repo, fname1, *fnames, **opts):
6299 def unbundle(ui, repo, fname1, *fnames, **opts):
6300 """apply one or more changegroup files
6300 """apply one or more changegroup files
6301
6301
6302 Apply one or more compressed changegroup files generated by the
6302 Apply one or more compressed changegroup files generated by the
6303 bundle command.
6303 bundle command.
6304
6304
6305 Returns 0 on success, 1 if an update has unresolved files.
6305 Returns 0 on success, 1 if an update has unresolved files.
6306 """
6306 """
6307 fnames = (fname1,) + fnames
6307 fnames = (fname1,) + fnames
6308
6308
6309 lock = repo.lock()
6309 lock = repo.lock()
6310 try:
6310 try:
6311 for fname in fnames:
6311 for fname in fnames:
6312 f = hg.openpath(ui, fname)
6312 f = hg.openpath(ui, fname)
6313 gen = exchange.readbundle(ui, f, fname)
6313 gen = exchange.readbundle(ui, f, fname)
6314 if isinstance(gen, bundle2.unbundle20):
6314 if isinstance(gen, bundle2.unbundle20):
6315 tr = repo.transaction('unbundle')
6315 tr = repo.transaction('unbundle')
6316 try:
6316 try:
6317 op = bundle2.processbundle(repo, gen, lambda: tr)
6317 op = bundle2.processbundle(repo, gen, lambda: tr)
6318 tr.close()
6318 tr.close()
6319 finally:
6319 finally:
6320 if tr:
6320 if tr:
6321 tr.release()
6321 tr.release()
6322 changes = [r.get('result', 0)
6322 changes = [r.get('result', 0)
6323 for r in op.records['changegroup']]
6323 for r in op.records['changegroup']]
6324 modheads = changegroup.combineresults(changes)
6324 modheads = changegroup.combineresults(changes)
6325 else:
6325 else:
6326 modheads = changegroup.addchangegroup(repo, gen, 'unbundle',
6326 modheads = changegroup.addchangegroup(repo, gen, 'unbundle',
6327 'bundle:' + fname)
6327 'bundle:' + fname)
6328 finally:
6328 finally:
6329 lock.release()
6329 lock.release()
6330
6330
6331 return postincoming(ui, repo, modheads, opts.get('update'), None)
6331 return postincoming(ui, repo, modheads, opts.get('update'), None)
6332
6332
6333 @command('^update|up|checkout|co',
6333 @command('^update|up|checkout|co',
6334 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
6334 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
6335 ('c', 'check', None,
6335 ('c', 'check', None,
6336 _('update across branches if no uncommitted changes')),
6336 _('update across branches if no uncommitted changes')),
6337 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
6337 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
6338 ('r', 'rev', '', _('revision'), _('REV'))
6338 ('r', 'rev', '', _('revision'), _('REV'))
6339 ] + mergetoolopts,
6339 ] + mergetoolopts,
6340 _('[-c] [-C] [-d DATE] [[-r] REV]'))
6340 _('[-c] [-C] [-d DATE] [[-r] REV]'))
6341 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False,
6341 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False,
6342 tool=None):
6342 tool=None):
6343 """update working directory (or switch revisions)
6343 """update working directory (or switch revisions)
6344
6344
6345 Update the repository's working directory to the specified
6345 Update the repository's working directory to the specified
6346 changeset. If no changeset is specified, update to the tip of the
6346 changeset. If no changeset is specified, update to the tip of the
6347 current named branch and move the current bookmark (see :hg:`help
6347 current named branch and move the current bookmark (see :hg:`help
6348 bookmarks`).
6348 bookmarks`).
6349
6349
6350 Update sets the working directory's parent revision to the specified
6350 Update sets the working directory's parent revision to the specified
6351 changeset (see :hg:`help parents`).
6351 changeset (see :hg:`help parents`).
6352
6352
6353 If the changeset is not a descendant or ancestor of the working
6353 If the changeset is not a descendant or ancestor of the working
6354 directory's parent, the update is aborted. With the -c/--check
6354 directory's parent, the update is aborted. With the -c/--check
6355 option, the working directory is checked for uncommitted changes; if
6355 option, the working directory is checked for uncommitted changes; if
6356 none are found, the working directory is updated to the specified
6356 none are found, the working directory is updated to the specified
6357 changeset.
6357 changeset.
6358
6358
6359 .. container:: verbose
6359 .. container:: verbose
6360
6360
6361 The following rules apply when the working directory contains
6361 The following rules apply when the working directory contains
6362 uncommitted changes:
6362 uncommitted changes:
6363
6363
6364 1. If neither -c/--check nor -C/--clean is specified, and if
6364 1. If neither -c/--check nor -C/--clean is specified, and if
6365 the requested changeset is an ancestor or descendant of
6365 the requested changeset is an ancestor or descendant of
6366 the working directory's parent, the uncommitted changes
6366 the working directory's parent, the uncommitted changes
6367 are merged into the requested changeset and the merged
6367 are merged into the requested changeset and the merged
6368 result is left uncommitted. If the requested changeset is
6368 result is left uncommitted. If the requested changeset is
6369 not an ancestor or descendant (that is, it is on another
6369 not an ancestor or descendant (that is, it is on another
6370 branch), the update is aborted and the uncommitted changes
6370 branch), the update is aborted and the uncommitted changes
6371 are preserved.
6371 are preserved.
6372
6372
6373 2. With the -c/--check option, the update is aborted and the
6373 2. With the -c/--check option, the update is aborted and the
6374 uncommitted changes are preserved.
6374 uncommitted changes are preserved.
6375
6375
6376 3. With the -C/--clean option, uncommitted changes are discarded and
6376 3. With the -C/--clean option, uncommitted changes are discarded and
6377 the working directory is updated to the requested changeset.
6377 the working directory is updated to the requested changeset.
6378
6378
6379 To cancel an uncommitted merge (and lose your changes), use
6379 To cancel an uncommitted merge (and lose your changes), use
6380 :hg:`update --clean .`.
6380 :hg:`update --clean .`.
6381
6381
6382 Use null as the changeset to remove the working directory (like
6382 Use null as the changeset to remove the working directory (like
6383 :hg:`clone -U`).
6383 :hg:`clone -U`).
6384
6384
6385 If you want to revert just one file to an older revision, use
6385 If you want to revert just one file to an older revision, use
6386 :hg:`revert [-r REV] NAME`.
6386 :hg:`revert [-r REV] NAME`.
6387
6387
6388 See :hg:`help dates` for a list of formats valid for -d/--date.
6388 See :hg:`help dates` for a list of formats valid for -d/--date.
6389
6389
6390 Returns 0 on success, 1 if there are unresolved files.
6390 Returns 0 on success, 1 if there are unresolved files.
6391 """
6391 """
6392 if rev and node:
6392 if rev and node:
6393 raise util.Abort(_("please specify just one revision"))
6393 raise util.Abort(_("please specify just one revision"))
6394
6394
6395 if rev is None or rev == '':
6395 if rev is None or rev == '':
6396 rev = node
6396 rev = node
6397
6397
6398 cmdutil.clearunfinished(repo)
6398 cmdutil.clearunfinished(repo)
6399
6399
6400 # with no argument, we also move the current bookmark, if any
6400 # with no argument, we also move the current bookmark, if any
6401 rev, movemarkfrom = bookmarks.calculateupdate(ui, repo, rev)
6401 rev, movemarkfrom = bookmarks.calculateupdate(ui, repo, rev)
6402
6402
6403 # if we defined a bookmark, we have to remember the original bookmark name
6403 # if we defined a bookmark, we have to remember the original bookmark name
6404 brev = rev
6404 brev = rev
6405 rev = scmutil.revsingle(repo, rev, rev).rev()
6405 rev = scmutil.revsingle(repo, rev, rev).rev()
6406
6406
6407 if check and clean:
6407 if check and clean:
6408 raise util.Abort(_("cannot specify both -c/--check and -C/--clean"))
6408 raise util.Abort(_("cannot specify both -c/--check and -C/--clean"))
6409
6409
6410 if date:
6410 if date:
6411 if rev is not None:
6411 if rev is not None:
6412 raise util.Abort(_("you can't specify a revision and a date"))
6412 raise util.Abort(_("you can't specify a revision and a date"))
6413 rev = cmdutil.finddate(ui, repo, date)
6413 rev = cmdutil.finddate(ui, repo, date)
6414
6414
6415 if check:
6415 if check:
6416 cmdutil.bailifchanged(repo, merge=False)
6416 cmdutil.bailifchanged(repo, merge=False)
6417 if rev is None:
6417 if rev is None:
6418 rev = repo[repo[None].branch()].rev()
6418 rev = repo[repo[None].branch()].rev()
6419
6419
6420 repo.ui.setconfig('ui', 'forcemerge', tool, 'update')
6420 repo.ui.setconfig('ui', 'forcemerge', tool, 'update')
6421
6421
6422 if clean:
6422 if clean:
6423 ret = hg.clean(repo, rev)
6423 ret = hg.clean(repo, rev)
6424 else:
6424 else:
6425 ret = hg.update(repo, rev)
6425 ret = hg.update(repo, rev)
6426
6426
6427 if not ret and movemarkfrom:
6427 if not ret and movemarkfrom:
6428 if bookmarks.update(repo, [movemarkfrom], repo['.'].node()):
6428 if bookmarks.update(repo, [movemarkfrom], repo['.'].node()):
6429 ui.status(_("updating bookmark %s\n") % repo._activebookmark)
6429 ui.status(_("updating bookmark %s\n") % repo._activebookmark)
6430 elif brev in repo._bookmarks:
6430 elif brev in repo._bookmarks:
6431 bookmarks.activate(repo, brev)
6431 bookmarks.activate(repo, brev)
6432 ui.status(_("(activating bookmark %s)\n") % brev)
6432 ui.status(_("(activating bookmark %s)\n") % brev)
6433 elif brev:
6433 elif brev:
6434 if repo._activebookmark:
6434 if repo._activebookmark:
6435 ui.status(_("(leaving bookmark %s)\n") %
6435 ui.status(_("(leaving bookmark %s)\n") %
6436 repo._activebookmark)
6436 repo._activebookmark)
6437 bookmarks.deactivate(repo)
6437 bookmarks.deactivate(repo)
6438
6438
6439 return ret
6439 return ret
6440
6440
6441 @command('verify', [])
6441 @command('verify', [])
6442 def verify(ui, repo):
6442 def verify(ui, repo):
6443 """verify the integrity of the repository
6443 """verify the integrity of the repository
6444
6444
6445 Verify the integrity of the current repository.
6445 Verify the integrity of the current repository.
6446
6446
6447 This will perform an extensive check of the repository's
6447 This will perform an extensive check of the repository's
6448 integrity, validating the hashes and checksums of each entry in
6448 integrity, validating the hashes and checksums of each entry in
6449 the changelog, manifest, and tracked files, as well as the
6449 the changelog, manifest, and tracked files, as well as the
6450 integrity of their crosslinks and indices.
6450 integrity of their crosslinks and indices.
6451
6451
6452 Please see http://mercurial.selenic.com/wiki/RepositoryCorruption
6452 Please see http://mercurial.selenic.com/wiki/RepositoryCorruption
6453 for more information about recovery from corruption of the
6453 for more information about recovery from corruption of the
6454 repository.
6454 repository.
6455
6455
6456 Returns 0 on success, 1 if errors are encountered.
6456 Returns 0 on success, 1 if errors are encountered.
6457 """
6457 """
6458 return hg.verify(repo)
6458 return hg.verify(repo)
6459
6459
6460 @command('version', [], norepo=True)
6460 @command('version', [], norepo=True)
6461 def version_(ui):
6461 def version_(ui):
6462 """output version and copyright information"""
6462 """output version and copyright information"""
6463 ui.write(_("Mercurial Distributed SCM (version %s)\n")
6463 ui.write(_("Mercurial Distributed SCM (version %s)\n")
6464 % util.version())
6464 % util.version())
6465 ui.status(_(
6465 ui.status(_(
6466 "(see http://mercurial.selenic.com for more information)\n"
6466 "(see http://mercurial.selenic.com for more information)\n"
6467 "\nCopyright (C) 2005-2015 Matt Mackall and others\n"
6467 "\nCopyright (C) 2005-2015 Matt Mackall and others\n"
6468 "This is free software; see the source for copying conditions. "
6468 "This is free software; see the source for copying conditions. "
6469 "There is NO\nwarranty; "
6469 "There is NO\nwarranty; "
6470 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
6470 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
6471 ))
6471 ))
6472
6472
6473 ui.note(_("\nEnabled extensions:\n\n"))
6473 ui.note(_("\nEnabled extensions:\n\n"))
6474 if ui.verbose:
6474 if ui.verbose:
6475 # format names and versions into columns
6475 # format names and versions into columns
6476 names = []
6476 names = []
6477 vers = []
6477 vers = []
6478 for name, module in extensions.extensions():
6478 for name, module in extensions.extensions():
6479 names.append(name)
6479 names.append(name)
6480 vers.append(extensions.moduleversion(module))
6480 vers.append(extensions.moduleversion(module))
6481 if names:
6481 if names:
6482 maxnamelen = max(len(n) for n in names)
6482 maxnamelen = max(len(n) for n in names)
6483 for i, name in enumerate(names):
6483 for i, name in enumerate(names):
6484 ui.write(" %-*s %s\n" % (maxnamelen, name, vers[i]))
6484 ui.write(" %-*s %s\n" % (maxnamelen, name, vers[i]))
@@ -1,1332 +1,1332 b''
1 #
1 #
2 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
2 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
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 import os, mimetypes, re, cgi, copy
8 import os, mimetypes, re, cgi, copy
9 import webutil
9 import webutil
10 from mercurial import error, encoding, archival, templater, templatefilters
10 from mercurial import error, encoding, archival, templater, templatefilters
11 from mercurial.node import short, hex
11 from mercurial.node import short, hex
12 from mercurial import util
12 from mercurial import util
13 from common import paritygen, staticfile, get_contact, ErrorResponse
13 from common import paritygen, staticfile, get_contact, ErrorResponse
14 from common import HTTP_OK, HTTP_FORBIDDEN, HTTP_NOT_FOUND
14 from common import HTTP_OK, HTTP_FORBIDDEN, HTTP_NOT_FOUND
15 from mercurial import graphmod, patch
15 from mercurial import graphmod, patch
16 from mercurial import scmutil
16 from mercurial import scmutil
17 from mercurial.i18n import _
17 from mercurial.i18n import _
18 from mercurial.error import ParseError, RepoLookupError, Abort
18 from mercurial.error import ParseError, RepoLookupError, Abort
19 from mercurial import revset
19 from mercurial import revset
20
20
21 __all__ = []
21 __all__ = []
22 commands = {}
22 commands = {}
23
23
24 class webcommand(object):
24 class webcommand(object):
25 """Decorator used to register a web command handler.
25 """Decorator used to register a web command handler.
26
26
27 The decorator takes as its positional arguments the name/path the
27 The decorator takes as its positional arguments the name/path the
28 command should be accessible under.
28 command should be accessible under.
29
29
30 Usage:
30 Usage:
31
31
32 @webcommand('mycommand')
32 @webcommand('mycommand')
33 def mycommand(web, req, tmpl):
33 def mycommand(web, req, tmpl):
34 pass
34 pass
35 """
35 """
36
36
37 def __init__(self, name):
37 def __init__(self, name):
38 self.name = name
38 self.name = name
39
39
40 def __call__(self, func):
40 def __call__(self, func):
41 __all__.append(self.name)
41 __all__.append(self.name)
42 commands[self.name] = func
42 commands[self.name] = func
43 return func
43 return func
44
44
45 @webcommand('log')
45 @webcommand('log')
46 def log(web, req, tmpl):
46 def log(web, req, tmpl):
47 """
47 """
48 /log[/{revision}[/{path}]]
48 /log[/{revision}[/{path}]]
49 --------------------------
49 --------------------------
50
50
51 Show repository or file history.
51 Show repository or file history.
52
52
53 For URLs of the form ``/log/{revision}``, a list of changesets starting at
53 For URLs of the form ``/log/{revision}``, a list of changesets starting at
54 the specified changeset identifier is shown. If ``{revision}`` is not
54 the specified changeset identifier is shown. If ``{revision}`` is not
55 defined, the default is ``tip``. This form is equivalent to the
55 defined, the default is ``tip``. This form is equivalent to the
56 ``changelog`` handler.
56 ``changelog`` handler.
57
57
58 For URLs of the form ``/log/{revision}/{file}``, the history for a specific
58 For URLs of the form ``/log/{revision}/{file}``, the history for a specific
59 file will be shown. This form is equivalent to the ``filelog`` handler.
59 file will be shown. This form is equivalent to the ``filelog`` handler.
60 """
60 """
61
61
62 if 'file' in req.form and req.form['file'][0]:
62 if 'file' in req.form and req.form['file'][0]:
63 return filelog(web, req, tmpl)
63 return filelog(web, req, tmpl)
64 else:
64 else:
65 return changelog(web, req, tmpl)
65 return changelog(web, req, tmpl)
66
66
67 @webcommand('rawfile')
67 @webcommand('rawfile')
68 def rawfile(web, req, tmpl):
68 def rawfile(web, req, tmpl):
69 guessmime = web.configbool('web', 'guessmime', False)
69 guessmime = web.configbool('web', 'guessmime', False)
70
70
71 path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
71 path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
72 if not path:
72 if not path:
73 content = manifest(web, req, tmpl)
73 content = manifest(web, req, tmpl)
74 req.respond(HTTP_OK, web.ctype)
74 req.respond(HTTP_OK, web.ctype)
75 return content
75 return content
76
76
77 try:
77 try:
78 fctx = webutil.filectx(web.repo, req)
78 fctx = webutil.filectx(web.repo, req)
79 except error.LookupError, inst:
79 except error.LookupError, inst:
80 try:
80 try:
81 content = manifest(web, req, tmpl)
81 content = manifest(web, req, tmpl)
82 req.respond(HTTP_OK, web.ctype)
82 req.respond(HTTP_OK, web.ctype)
83 return content
83 return content
84 except ErrorResponse:
84 except ErrorResponse:
85 raise inst
85 raise inst
86
86
87 path = fctx.path()
87 path = fctx.path()
88 text = fctx.data()
88 text = fctx.data()
89 mt = 'application/binary'
89 mt = 'application/binary'
90 if guessmime:
90 if guessmime:
91 mt = mimetypes.guess_type(path)[0]
91 mt = mimetypes.guess_type(path)[0]
92 if mt is None:
92 if mt is None:
93 if util.binary(text):
93 if util.binary(text):
94 mt = 'application/binary'
94 mt = 'application/binary'
95 else:
95 else:
96 mt = 'text/plain'
96 mt = 'text/plain'
97 if mt.startswith('text/'):
97 if mt.startswith('text/'):
98 mt += '; charset="%s"' % encoding.encoding
98 mt += '; charset="%s"' % encoding.encoding
99
99
100 req.respond(HTTP_OK, mt, path, body=text)
100 req.respond(HTTP_OK, mt, path, body=text)
101 return []
101 return []
102
102
103 def _filerevision(web, tmpl, fctx):
103 def _filerevision(web, tmpl, fctx):
104 f = fctx.path()
104 f = fctx.path()
105 text = fctx.data()
105 text = fctx.data()
106 parity = paritygen(web.stripecount)
106 parity = paritygen(web.stripecount)
107
107
108 if util.binary(text):
108 if util.binary(text):
109 mt = mimetypes.guess_type(f)[0] or 'application/octet-stream'
109 mt = mimetypes.guess_type(f)[0] or 'application/octet-stream'
110 text = '(binary:%s)' % mt
110 text = '(binary:%s)' % mt
111
111
112 def lines():
112 def lines():
113 for lineno, t in enumerate(text.splitlines(True)):
113 for lineno, t in enumerate(text.splitlines(True)):
114 yield {"line": t,
114 yield {"line": t,
115 "lineid": "l%d" % (lineno + 1),
115 "lineid": "l%d" % (lineno + 1),
116 "linenumber": "% 6d" % (lineno + 1),
116 "linenumber": "% 6d" % (lineno + 1),
117 "parity": parity.next()}
117 "parity": parity.next()}
118
118
119 return tmpl("filerevision",
119 return tmpl("filerevision",
120 file=f,
120 file=f,
121 path=webutil.up(f),
121 path=webutil.up(f),
122 text=lines(),
122 text=lines(),
123 rev=fctx.rev(),
123 rev=fctx.rev(),
124 node=fctx.hex(),
124 node=fctx.hex(),
125 author=fctx.user(),
125 author=fctx.user(),
126 date=fctx.date(),
126 date=fctx.date(),
127 desc=fctx.description(),
127 desc=fctx.description(),
128 extra=fctx.extra(),
128 extra=fctx.extra(),
129 branch=webutil.nodebranchnodefault(fctx),
129 branch=webutil.nodebranchnodefault(fctx),
130 parent=webutil.parents(fctx),
130 parent=webutil.parents(fctx),
131 child=webutil.children(fctx),
131 child=webutil.children(fctx),
132 rename=webutil.renamelink(fctx),
132 rename=webutil.renamelink(fctx),
133 tags=webutil.nodetagsdict(web.repo, fctx.node()),
133 tags=webutil.nodetagsdict(web.repo, fctx.node()),
134 bookmarks=webutil.nodebookmarksdict(web.repo, fctx.node()),
134 bookmarks=webutil.nodebookmarksdict(web.repo, fctx.node()),
135 permissions=fctx.manifest().flags(f))
135 permissions=fctx.manifest().flags(f))
136
136
137 @webcommand('file')
137 @webcommand('file')
138 def file(web, req, tmpl):
138 def file(web, req, tmpl):
139 """
139 """
140 /file/{revision}[/{path}]
140 /file/{revision}[/{path}]
141 -------------------------
141 -------------------------
142
142
143 Show information about a directory or file in the repository.
143 Show information about a directory or file in the repository.
144
144
145 Info about the ``path`` given as a URL parameter will be rendered.
145 Info about the ``path`` given as a URL parameter will be rendered.
146
146
147 If ``path`` is a directory, information about the entries in that
147 If ``path`` is a directory, information about the entries in that
148 directory will be rendered. This form is equivalent to the ``manifest``
148 directory will be rendered. This form is equivalent to the ``manifest``
149 handler.
149 handler.
150
150
151 If ``path`` is a file, information about that file will be shown via
151 If ``path`` is a file, information about that file will be shown via
152 the ``filerevision`` template.
152 the ``filerevision`` template.
153
153
154 If ``path`` is not defined, information about the root directory will
154 If ``path`` is not defined, information about the root directory will
155 be rendered.
155 be rendered.
156 """
156 """
157 path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
157 path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
158 if not path:
158 if not path:
159 return manifest(web, req, tmpl)
159 return manifest(web, req, tmpl)
160 try:
160 try:
161 return _filerevision(web, tmpl, webutil.filectx(web.repo, req))
161 return _filerevision(web, tmpl, webutil.filectx(web.repo, req))
162 except error.LookupError, inst:
162 except error.LookupError, inst:
163 try:
163 try:
164 return manifest(web, req, tmpl)
164 return manifest(web, req, tmpl)
165 except ErrorResponse:
165 except ErrorResponse:
166 raise inst
166 raise inst
167
167
168 def _search(web, req, tmpl):
168 def _search(web, req, tmpl):
169 MODE_REVISION = 'rev'
169 MODE_REVISION = 'rev'
170 MODE_KEYWORD = 'keyword'
170 MODE_KEYWORD = 'keyword'
171 MODE_REVSET = 'revset'
171 MODE_REVSET = 'revset'
172
172
173 def revsearch(ctx):
173 def revsearch(ctx):
174 yield ctx
174 yield ctx
175
175
176 def keywordsearch(query):
176 def keywordsearch(query):
177 lower = encoding.lower
177 lower = encoding.lower
178 qw = lower(query).split()
178 qw = lower(query).split()
179
179
180 def revgen():
180 def revgen():
181 cl = web.repo.changelog
181 cl = web.repo.changelog
182 for i in xrange(len(web.repo) - 1, 0, -100):
182 for i in xrange(len(web.repo) - 1, 0, -100):
183 l = []
183 l = []
184 for j in cl.revs(max(0, i - 99), i):
184 for j in cl.revs(max(0, i - 99), i):
185 ctx = web.repo[j]
185 ctx = web.repo[j]
186 l.append(ctx)
186 l.append(ctx)
187 l.reverse()
187 l.reverse()
188 for e in l:
188 for e in l:
189 yield e
189 yield e
190
190
191 for ctx in revgen():
191 for ctx in revgen():
192 miss = 0
192 miss = 0
193 for q in qw:
193 for q in qw:
194 if not (q in lower(ctx.user()) or
194 if not (q in lower(ctx.user()) or
195 q in lower(ctx.description()) or
195 q in lower(ctx.description()) or
196 q in lower(" ".join(ctx.files()))):
196 q in lower(" ".join(ctx.files()))):
197 miss = 1
197 miss = 1
198 break
198 break
199 if miss:
199 if miss:
200 continue
200 continue
201
201
202 yield ctx
202 yield ctx
203
203
204 def revsetsearch(revs):
204 def revsetsearch(revs):
205 for r in revs:
205 for r in revs:
206 yield web.repo[r]
206 yield web.repo[r]
207
207
208 searchfuncs = {
208 searchfuncs = {
209 MODE_REVISION: (revsearch, 'exact revision search'),
209 MODE_REVISION: (revsearch, 'exact revision search'),
210 MODE_KEYWORD: (keywordsearch, 'literal keyword search'),
210 MODE_KEYWORD: (keywordsearch, 'literal keyword search'),
211 MODE_REVSET: (revsetsearch, 'revset expression search'),
211 MODE_REVSET: (revsetsearch, 'revset expression search'),
212 }
212 }
213
213
214 def getsearchmode(query):
214 def getsearchmode(query):
215 try:
215 try:
216 ctx = web.repo[query]
216 ctx = web.repo[query]
217 except (error.RepoError, error.LookupError):
217 except (error.RepoError, error.LookupError):
218 # query is not an exact revision pointer, need to
218 # query is not an exact revision pointer, need to
219 # decide if it's a revset expression or keywords
219 # decide if it's a revset expression or keywords
220 pass
220 pass
221 else:
221 else:
222 return MODE_REVISION, ctx
222 return MODE_REVISION, ctx
223
223
224 revdef = 'reverse(%s)' % query
224 revdef = 'reverse(%s)' % query
225 try:
225 try:
226 tree, pos = revset.parse(revdef)
226 tree = revset.parse(revdef)
227 except ParseError:
227 except ParseError:
228 # can't parse to a revset tree
228 # can't parse to a revset tree
229 return MODE_KEYWORD, query
229 return MODE_KEYWORD, query
230
230
231 if revset.depth(tree) <= 2:
231 if revset.depth(tree) <= 2:
232 # no revset syntax used
232 # no revset syntax used
233 return MODE_KEYWORD, query
233 return MODE_KEYWORD, query
234
234
235 if any((token, (value or '')[:3]) == ('string', 're:')
235 if any((token, (value or '')[:3]) == ('string', 're:')
236 for token, value, pos in revset.tokenize(revdef)):
236 for token, value, pos in revset.tokenize(revdef)):
237 return MODE_KEYWORD, query
237 return MODE_KEYWORD, query
238
238
239 funcsused = revset.funcsused(tree)
239 funcsused = revset.funcsused(tree)
240 if not funcsused.issubset(revset.safesymbols):
240 if not funcsused.issubset(revset.safesymbols):
241 return MODE_KEYWORD, query
241 return MODE_KEYWORD, query
242
242
243 mfunc = revset.match(web.repo.ui, revdef)
243 mfunc = revset.match(web.repo.ui, revdef)
244 try:
244 try:
245 revs = mfunc(web.repo)
245 revs = mfunc(web.repo)
246 return MODE_REVSET, revs
246 return MODE_REVSET, revs
247 # ParseError: wrongly placed tokens, wrongs arguments, etc
247 # ParseError: wrongly placed tokens, wrongs arguments, etc
248 # RepoLookupError: no such revision, e.g. in 'revision:'
248 # RepoLookupError: no such revision, e.g. in 'revision:'
249 # Abort: bookmark/tag not exists
249 # Abort: bookmark/tag not exists
250 # LookupError: ambiguous identifier, e.g. in '(bc)' on a large repo
250 # LookupError: ambiguous identifier, e.g. in '(bc)' on a large repo
251 except (ParseError, RepoLookupError, Abort, LookupError):
251 except (ParseError, RepoLookupError, Abort, LookupError):
252 return MODE_KEYWORD, query
252 return MODE_KEYWORD, query
253
253
254 def changelist(**map):
254 def changelist(**map):
255 count = 0
255 count = 0
256
256
257 for ctx in searchfunc[0](funcarg):
257 for ctx in searchfunc[0](funcarg):
258 count += 1
258 count += 1
259 n = ctx.node()
259 n = ctx.node()
260 showtags = webutil.showtag(web.repo, tmpl, 'changelogtag', n)
260 showtags = webutil.showtag(web.repo, tmpl, 'changelogtag', n)
261 files = webutil.listfilediffs(tmpl, ctx.files(), n, web.maxfiles)
261 files = webutil.listfilediffs(tmpl, ctx.files(), n, web.maxfiles)
262
262
263 yield tmpl('searchentry',
263 yield tmpl('searchentry',
264 parity=parity.next(),
264 parity=parity.next(),
265 author=ctx.user(),
265 author=ctx.user(),
266 parent=webutil.parents(ctx),
266 parent=webutil.parents(ctx),
267 child=webutil.children(ctx),
267 child=webutil.children(ctx),
268 changelogtag=showtags,
268 changelogtag=showtags,
269 desc=ctx.description(),
269 desc=ctx.description(),
270 extra=ctx.extra(),
270 extra=ctx.extra(),
271 date=ctx.date(),
271 date=ctx.date(),
272 files=files,
272 files=files,
273 rev=ctx.rev(),
273 rev=ctx.rev(),
274 node=hex(n),
274 node=hex(n),
275 tags=webutil.nodetagsdict(web.repo, n),
275 tags=webutil.nodetagsdict(web.repo, n),
276 bookmarks=webutil.nodebookmarksdict(web.repo, n),
276 bookmarks=webutil.nodebookmarksdict(web.repo, n),
277 inbranch=webutil.nodeinbranch(web.repo, ctx),
277 inbranch=webutil.nodeinbranch(web.repo, ctx),
278 branches=webutil.nodebranchdict(web.repo, ctx))
278 branches=webutil.nodebranchdict(web.repo, ctx))
279
279
280 if count >= revcount:
280 if count >= revcount:
281 break
281 break
282
282
283 query = req.form['rev'][0]
283 query = req.form['rev'][0]
284 revcount = web.maxchanges
284 revcount = web.maxchanges
285 if 'revcount' in req.form:
285 if 'revcount' in req.form:
286 try:
286 try:
287 revcount = int(req.form.get('revcount', [revcount])[0])
287 revcount = int(req.form.get('revcount', [revcount])[0])
288 revcount = max(revcount, 1)
288 revcount = max(revcount, 1)
289 tmpl.defaults['sessionvars']['revcount'] = revcount
289 tmpl.defaults['sessionvars']['revcount'] = revcount
290 except ValueError:
290 except ValueError:
291 pass
291 pass
292
292
293 lessvars = copy.copy(tmpl.defaults['sessionvars'])
293 lessvars = copy.copy(tmpl.defaults['sessionvars'])
294 lessvars['revcount'] = max(revcount / 2, 1)
294 lessvars['revcount'] = max(revcount / 2, 1)
295 lessvars['rev'] = query
295 lessvars['rev'] = query
296 morevars = copy.copy(tmpl.defaults['sessionvars'])
296 morevars = copy.copy(tmpl.defaults['sessionvars'])
297 morevars['revcount'] = revcount * 2
297 morevars['revcount'] = revcount * 2
298 morevars['rev'] = query
298 morevars['rev'] = query
299
299
300 mode, funcarg = getsearchmode(query)
300 mode, funcarg = getsearchmode(query)
301
301
302 if 'forcekw' in req.form:
302 if 'forcekw' in req.form:
303 showforcekw = ''
303 showforcekw = ''
304 showunforcekw = searchfuncs[mode][1]
304 showunforcekw = searchfuncs[mode][1]
305 mode = MODE_KEYWORD
305 mode = MODE_KEYWORD
306 funcarg = query
306 funcarg = query
307 else:
307 else:
308 if mode != MODE_KEYWORD:
308 if mode != MODE_KEYWORD:
309 showforcekw = searchfuncs[MODE_KEYWORD][1]
309 showforcekw = searchfuncs[MODE_KEYWORD][1]
310 else:
310 else:
311 showforcekw = ''
311 showforcekw = ''
312 showunforcekw = ''
312 showunforcekw = ''
313
313
314 searchfunc = searchfuncs[mode]
314 searchfunc = searchfuncs[mode]
315
315
316 tip = web.repo['tip']
316 tip = web.repo['tip']
317 parity = paritygen(web.stripecount)
317 parity = paritygen(web.stripecount)
318
318
319 return tmpl('search', query=query, node=tip.hex(),
319 return tmpl('search', query=query, node=tip.hex(),
320 entries=changelist, archives=web.archivelist("tip"),
320 entries=changelist, archives=web.archivelist("tip"),
321 morevars=morevars, lessvars=lessvars,
321 morevars=morevars, lessvars=lessvars,
322 modedesc=searchfunc[1],
322 modedesc=searchfunc[1],
323 showforcekw=showforcekw, showunforcekw=showunforcekw)
323 showforcekw=showforcekw, showunforcekw=showunforcekw)
324
324
325 @webcommand('changelog')
325 @webcommand('changelog')
326 def changelog(web, req, tmpl, shortlog=False):
326 def changelog(web, req, tmpl, shortlog=False):
327 """
327 """
328 /changelog[/{revision}]
328 /changelog[/{revision}]
329 -----------------------
329 -----------------------
330
330
331 Show information about multiple changesets.
331 Show information about multiple changesets.
332
332
333 If the optional ``revision`` URL argument is absent, information about
333 If the optional ``revision`` URL argument is absent, information about
334 all changesets starting at ``tip`` will be rendered. If the ``revision``
334 all changesets starting at ``tip`` will be rendered. If the ``revision``
335 argument is present, changesets will be shown starting from the specified
335 argument is present, changesets will be shown starting from the specified
336 revision.
336 revision.
337
337
338 If ``revision`` is absent, the ``rev`` query string argument may be
338 If ``revision`` is absent, the ``rev`` query string argument may be
339 defined. This will perform a search for changesets.
339 defined. This will perform a search for changesets.
340
340
341 The argument for ``rev`` can be a single revision, a revision set,
341 The argument for ``rev`` can be a single revision, a revision set,
342 or a literal keyword to search for in changeset data (equivalent to
342 or a literal keyword to search for in changeset data (equivalent to
343 :hg:`log -k`).
343 :hg:`log -k`).
344
344
345 The ``revcount`` query string argument defines the maximum numbers of
345 The ``revcount`` query string argument defines the maximum numbers of
346 changesets to render.
346 changesets to render.
347
347
348 For non-searches, the ``changelog`` template will be rendered.
348 For non-searches, the ``changelog`` template will be rendered.
349 """
349 """
350
350
351 query = ''
351 query = ''
352 if 'node' in req.form:
352 if 'node' in req.form:
353 ctx = webutil.changectx(web.repo, req)
353 ctx = webutil.changectx(web.repo, req)
354 elif 'rev' in req.form:
354 elif 'rev' in req.form:
355 return _search(web, req, tmpl)
355 return _search(web, req, tmpl)
356 else:
356 else:
357 ctx = web.repo['tip']
357 ctx = web.repo['tip']
358
358
359 def changelist():
359 def changelist():
360 revs = []
360 revs = []
361 if pos != -1:
361 if pos != -1:
362 revs = web.repo.changelog.revs(pos, 0)
362 revs = web.repo.changelog.revs(pos, 0)
363 curcount = 0
363 curcount = 0
364 for rev in revs:
364 for rev in revs:
365 curcount += 1
365 curcount += 1
366 if curcount > revcount + 1:
366 if curcount > revcount + 1:
367 break
367 break
368
368
369 entry = webutil.changelistentry(web, web.repo[rev], tmpl)
369 entry = webutil.changelistentry(web, web.repo[rev], tmpl)
370 entry['parity'] = parity.next()
370 entry['parity'] = parity.next()
371 yield entry
371 yield entry
372
372
373 if shortlog:
373 if shortlog:
374 revcount = web.maxshortchanges
374 revcount = web.maxshortchanges
375 else:
375 else:
376 revcount = web.maxchanges
376 revcount = web.maxchanges
377
377
378 if 'revcount' in req.form:
378 if 'revcount' in req.form:
379 try:
379 try:
380 revcount = int(req.form.get('revcount', [revcount])[0])
380 revcount = int(req.form.get('revcount', [revcount])[0])
381 revcount = max(revcount, 1)
381 revcount = max(revcount, 1)
382 tmpl.defaults['sessionvars']['revcount'] = revcount
382 tmpl.defaults['sessionvars']['revcount'] = revcount
383 except ValueError:
383 except ValueError:
384 pass
384 pass
385
385
386 lessvars = copy.copy(tmpl.defaults['sessionvars'])
386 lessvars = copy.copy(tmpl.defaults['sessionvars'])
387 lessvars['revcount'] = max(revcount / 2, 1)
387 lessvars['revcount'] = max(revcount / 2, 1)
388 morevars = copy.copy(tmpl.defaults['sessionvars'])
388 morevars = copy.copy(tmpl.defaults['sessionvars'])
389 morevars['revcount'] = revcount * 2
389 morevars['revcount'] = revcount * 2
390
390
391 count = len(web.repo)
391 count = len(web.repo)
392 pos = ctx.rev()
392 pos = ctx.rev()
393 parity = paritygen(web.stripecount)
393 parity = paritygen(web.stripecount)
394
394
395 changenav = webutil.revnav(web.repo).gen(pos, revcount, count)
395 changenav = webutil.revnav(web.repo).gen(pos, revcount, count)
396
396
397 entries = list(changelist())
397 entries = list(changelist())
398 latestentry = entries[:1]
398 latestentry = entries[:1]
399 if len(entries) > revcount:
399 if len(entries) > revcount:
400 nextentry = entries[-1:]
400 nextentry = entries[-1:]
401 entries = entries[:-1]
401 entries = entries[:-1]
402 else:
402 else:
403 nextentry = []
403 nextentry = []
404
404
405 return tmpl(shortlog and 'shortlog' or 'changelog', changenav=changenav,
405 return tmpl(shortlog and 'shortlog' or 'changelog', changenav=changenav,
406 node=ctx.hex(), rev=pos, changesets=count,
406 node=ctx.hex(), rev=pos, changesets=count,
407 entries=entries,
407 entries=entries,
408 latestentry=latestentry, nextentry=nextentry,
408 latestentry=latestentry, nextentry=nextentry,
409 archives=web.archivelist("tip"), revcount=revcount,
409 archives=web.archivelist("tip"), revcount=revcount,
410 morevars=morevars, lessvars=lessvars, query=query)
410 morevars=morevars, lessvars=lessvars, query=query)
411
411
412 @webcommand('shortlog')
412 @webcommand('shortlog')
413 def shortlog(web, req, tmpl):
413 def shortlog(web, req, tmpl):
414 """
414 """
415 /shortlog
415 /shortlog
416 ---------
416 ---------
417
417
418 Show basic information about a set of changesets.
418 Show basic information about a set of changesets.
419
419
420 This accepts the same parameters as the ``changelog`` handler. The only
420 This accepts the same parameters as the ``changelog`` handler. The only
421 difference is the ``shortlog`` template will be rendered instead of the
421 difference is the ``shortlog`` template will be rendered instead of the
422 ``changelog`` template.
422 ``changelog`` template.
423 """
423 """
424 return changelog(web, req, tmpl, shortlog=True)
424 return changelog(web, req, tmpl, shortlog=True)
425
425
426 @webcommand('changeset')
426 @webcommand('changeset')
427 def changeset(web, req, tmpl):
427 def changeset(web, req, tmpl):
428 """
428 """
429 /changeset[/{revision}]
429 /changeset[/{revision}]
430 -----------------------
430 -----------------------
431
431
432 Show information about a single changeset.
432 Show information about a single changeset.
433
433
434 A URL path argument is the changeset identifier to show. See ``hg help
434 A URL path argument is the changeset identifier to show. See ``hg help
435 revisions`` for possible values. If not defined, the ``tip`` changeset
435 revisions`` for possible values. If not defined, the ``tip`` changeset
436 will be shown.
436 will be shown.
437
437
438 The ``changeset`` template is rendered. Contents of the ``changesettag``,
438 The ``changeset`` template is rendered. Contents of the ``changesettag``,
439 ``changesetbookmark``, ``filenodelink``, ``filenolink``, and the many
439 ``changesetbookmark``, ``filenodelink``, ``filenolink``, and the many
440 templates related to diffs may all be used to produce the output.
440 templates related to diffs may all be used to produce the output.
441 """
441 """
442 ctx = webutil.changectx(web.repo, req)
442 ctx = webutil.changectx(web.repo, req)
443
443
444 return tmpl('changeset', **webutil.changesetentry(web, req, tmpl, ctx))
444 return tmpl('changeset', **webutil.changesetentry(web, req, tmpl, ctx))
445
445
446 rev = webcommand('rev')(changeset)
446 rev = webcommand('rev')(changeset)
447
447
448 def decodepath(path):
448 def decodepath(path):
449 """Hook for mapping a path in the repository to a path in the
449 """Hook for mapping a path in the repository to a path in the
450 working copy.
450 working copy.
451
451
452 Extensions (e.g., largefiles) can override this to remap files in
452 Extensions (e.g., largefiles) can override this to remap files in
453 the virtual file system presented by the manifest command below."""
453 the virtual file system presented by the manifest command below."""
454 return path
454 return path
455
455
456 @webcommand('manifest')
456 @webcommand('manifest')
457 def manifest(web, req, tmpl):
457 def manifest(web, req, tmpl):
458 """
458 """
459 /manifest[/{revision}[/{path}]]
459 /manifest[/{revision}[/{path}]]
460 -------------------------------
460 -------------------------------
461
461
462 Show information about a directory.
462 Show information about a directory.
463
463
464 If the URL path arguments are omitted, information about the root
464 If the URL path arguments are omitted, information about the root
465 directory for the ``tip`` changeset will be shown.
465 directory for the ``tip`` changeset will be shown.
466
466
467 Because this handler can only show information for directories, it
467 Because this handler can only show information for directories, it
468 is recommended to use the ``file`` handler instead, as it can handle both
468 is recommended to use the ``file`` handler instead, as it can handle both
469 directories and files.
469 directories and files.
470
470
471 The ``manifest`` template will be rendered for this handler.
471 The ``manifest`` template will be rendered for this handler.
472 """
472 """
473 ctx = webutil.changectx(web.repo, req)
473 ctx = webutil.changectx(web.repo, req)
474 path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
474 path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
475 mf = ctx.manifest()
475 mf = ctx.manifest()
476 node = ctx.node()
476 node = ctx.node()
477
477
478 files = {}
478 files = {}
479 dirs = {}
479 dirs = {}
480 parity = paritygen(web.stripecount)
480 parity = paritygen(web.stripecount)
481
481
482 if path and path[-1] != "/":
482 if path and path[-1] != "/":
483 path += "/"
483 path += "/"
484 l = len(path)
484 l = len(path)
485 abspath = "/" + path
485 abspath = "/" + path
486
486
487 for full, n in mf.iteritems():
487 for full, n in mf.iteritems():
488 # the virtual path (working copy path) used for the full
488 # the virtual path (working copy path) used for the full
489 # (repository) path
489 # (repository) path
490 f = decodepath(full)
490 f = decodepath(full)
491
491
492 if f[:l] != path:
492 if f[:l] != path:
493 continue
493 continue
494 remain = f[l:]
494 remain = f[l:]
495 elements = remain.split('/')
495 elements = remain.split('/')
496 if len(elements) == 1:
496 if len(elements) == 1:
497 files[remain] = full
497 files[remain] = full
498 else:
498 else:
499 h = dirs # need to retain ref to dirs (root)
499 h = dirs # need to retain ref to dirs (root)
500 for elem in elements[0:-1]:
500 for elem in elements[0:-1]:
501 if elem not in h:
501 if elem not in h:
502 h[elem] = {}
502 h[elem] = {}
503 h = h[elem]
503 h = h[elem]
504 if len(h) > 1:
504 if len(h) > 1:
505 break
505 break
506 h[None] = None # denotes files present
506 h[None] = None # denotes files present
507
507
508 if mf and not files and not dirs:
508 if mf and not files and not dirs:
509 raise ErrorResponse(HTTP_NOT_FOUND, 'path not found: ' + path)
509 raise ErrorResponse(HTTP_NOT_FOUND, 'path not found: ' + path)
510
510
511 def filelist(**map):
511 def filelist(**map):
512 for f in sorted(files):
512 for f in sorted(files):
513 full = files[f]
513 full = files[f]
514
514
515 fctx = ctx.filectx(full)
515 fctx = ctx.filectx(full)
516 yield {"file": full,
516 yield {"file": full,
517 "parity": parity.next(),
517 "parity": parity.next(),
518 "basename": f,
518 "basename": f,
519 "date": fctx.date(),
519 "date": fctx.date(),
520 "size": fctx.size(),
520 "size": fctx.size(),
521 "permissions": mf.flags(full)}
521 "permissions": mf.flags(full)}
522
522
523 def dirlist(**map):
523 def dirlist(**map):
524 for d in sorted(dirs):
524 for d in sorted(dirs):
525
525
526 emptydirs = []
526 emptydirs = []
527 h = dirs[d]
527 h = dirs[d]
528 while isinstance(h, dict) and len(h) == 1:
528 while isinstance(h, dict) and len(h) == 1:
529 k, v = h.items()[0]
529 k, v = h.items()[0]
530 if v:
530 if v:
531 emptydirs.append(k)
531 emptydirs.append(k)
532 h = v
532 h = v
533
533
534 path = "%s%s" % (abspath, d)
534 path = "%s%s" % (abspath, d)
535 yield {"parity": parity.next(),
535 yield {"parity": parity.next(),
536 "path": path,
536 "path": path,
537 "emptydirs": "/".join(emptydirs),
537 "emptydirs": "/".join(emptydirs),
538 "basename": d}
538 "basename": d}
539
539
540 return tmpl("manifest",
540 return tmpl("manifest",
541 rev=ctx.rev(),
541 rev=ctx.rev(),
542 node=hex(node),
542 node=hex(node),
543 path=abspath,
543 path=abspath,
544 up=webutil.up(abspath),
544 up=webutil.up(abspath),
545 upparity=parity.next(),
545 upparity=parity.next(),
546 fentries=filelist,
546 fentries=filelist,
547 dentries=dirlist,
547 dentries=dirlist,
548 archives=web.archivelist(hex(node)),
548 archives=web.archivelist(hex(node)),
549 tags=webutil.nodetagsdict(web.repo, node),
549 tags=webutil.nodetagsdict(web.repo, node),
550 bookmarks=webutil.nodebookmarksdict(web.repo, node),
550 bookmarks=webutil.nodebookmarksdict(web.repo, node),
551 branch=webutil.nodebranchnodefault(ctx),
551 branch=webutil.nodebranchnodefault(ctx),
552 inbranch=webutil.nodeinbranch(web.repo, ctx),
552 inbranch=webutil.nodeinbranch(web.repo, ctx),
553 branches=webutil.nodebranchdict(web.repo, ctx))
553 branches=webutil.nodebranchdict(web.repo, ctx))
554
554
555 @webcommand('tags')
555 @webcommand('tags')
556 def tags(web, req, tmpl):
556 def tags(web, req, tmpl):
557 """
557 """
558 /tags
558 /tags
559 -----
559 -----
560
560
561 Show information about tags.
561 Show information about tags.
562
562
563 No arguments are accepted.
563 No arguments are accepted.
564
564
565 The ``tags`` template is rendered.
565 The ``tags`` template is rendered.
566 """
566 """
567 i = list(reversed(web.repo.tagslist()))
567 i = list(reversed(web.repo.tagslist()))
568 parity = paritygen(web.stripecount)
568 parity = paritygen(web.stripecount)
569
569
570 def entries(notip, latestonly, **map):
570 def entries(notip, latestonly, **map):
571 t = i
571 t = i
572 if notip:
572 if notip:
573 t = [(k, n) for k, n in i if k != "tip"]
573 t = [(k, n) for k, n in i if k != "tip"]
574 if latestonly:
574 if latestonly:
575 t = t[:1]
575 t = t[:1]
576 for k, n in t:
576 for k, n in t:
577 yield {"parity": parity.next(),
577 yield {"parity": parity.next(),
578 "tag": k,
578 "tag": k,
579 "date": web.repo[n].date(),
579 "date": web.repo[n].date(),
580 "node": hex(n)}
580 "node": hex(n)}
581
581
582 return tmpl("tags",
582 return tmpl("tags",
583 node=hex(web.repo.changelog.tip()),
583 node=hex(web.repo.changelog.tip()),
584 entries=lambda **x: entries(False, False, **x),
584 entries=lambda **x: entries(False, False, **x),
585 entriesnotip=lambda **x: entries(True, False, **x),
585 entriesnotip=lambda **x: entries(True, False, **x),
586 latestentry=lambda **x: entries(True, True, **x))
586 latestentry=lambda **x: entries(True, True, **x))
587
587
588 @webcommand('bookmarks')
588 @webcommand('bookmarks')
589 def bookmarks(web, req, tmpl):
589 def bookmarks(web, req, tmpl):
590 """
590 """
591 /bookmarks
591 /bookmarks
592 ----------
592 ----------
593
593
594 Show information about bookmarks.
594 Show information about bookmarks.
595
595
596 No arguments are accepted.
596 No arguments are accepted.
597
597
598 The ``bookmarks`` template is rendered.
598 The ``bookmarks`` template is rendered.
599 """
599 """
600 i = [b for b in web.repo._bookmarks.items() if b[1] in web.repo]
600 i = [b for b in web.repo._bookmarks.items() if b[1] in web.repo]
601 parity = paritygen(web.stripecount)
601 parity = paritygen(web.stripecount)
602
602
603 def entries(latestonly, **map):
603 def entries(latestonly, **map):
604 if latestonly:
604 if latestonly:
605 t = [min(i)]
605 t = [min(i)]
606 else:
606 else:
607 t = sorted(i)
607 t = sorted(i)
608 for k, n in t:
608 for k, n in t:
609 yield {"parity": parity.next(),
609 yield {"parity": parity.next(),
610 "bookmark": k,
610 "bookmark": k,
611 "date": web.repo[n].date(),
611 "date": web.repo[n].date(),
612 "node": hex(n)}
612 "node": hex(n)}
613
613
614 return tmpl("bookmarks",
614 return tmpl("bookmarks",
615 node=hex(web.repo.changelog.tip()),
615 node=hex(web.repo.changelog.tip()),
616 entries=lambda **x: entries(latestonly=False, **x),
616 entries=lambda **x: entries(latestonly=False, **x),
617 latestentry=lambda **x: entries(latestonly=True, **x))
617 latestentry=lambda **x: entries(latestonly=True, **x))
618
618
619 @webcommand('branches')
619 @webcommand('branches')
620 def branches(web, req, tmpl):
620 def branches(web, req, tmpl):
621 """
621 """
622 /branches
622 /branches
623 ---------
623 ---------
624
624
625 Show information about branches.
625 Show information about branches.
626
626
627 All known branches are contained in the output, even closed branches.
627 All known branches are contained in the output, even closed branches.
628
628
629 No arguments are accepted.
629 No arguments are accepted.
630
630
631 The ``branches`` template is rendered.
631 The ``branches`` template is rendered.
632 """
632 """
633 tips = []
633 tips = []
634 heads = web.repo.heads()
634 heads = web.repo.heads()
635 parity = paritygen(web.stripecount)
635 parity = paritygen(web.stripecount)
636 sortkey = lambda item: (not item[1], item[0].rev())
636 sortkey = lambda item: (not item[1], item[0].rev())
637
637
638 def entries(limit, **map):
638 def entries(limit, **map):
639 count = 0
639 count = 0
640 if not tips:
640 if not tips:
641 for tag, hs, tip, closed in web.repo.branchmap().iterbranches():
641 for tag, hs, tip, closed in web.repo.branchmap().iterbranches():
642 tips.append((web.repo[tip], closed))
642 tips.append((web.repo[tip], closed))
643 for ctx, closed in sorted(tips, key=sortkey, reverse=True):
643 for ctx, closed in sorted(tips, key=sortkey, reverse=True):
644 if limit > 0 and count >= limit:
644 if limit > 0 and count >= limit:
645 return
645 return
646 count += 1
646 count += 1
647 if closed:
647 if closed:
648 status = 'closed'
648 status = 'closed'
649 elif ctx.node() not in heads:
649 elif ctx.node() not in heads:
650 status = 'inactive'
650 status = 'inactive'
651 else:
651 else:
652 status = 'open'
652 status = 'open'
653 yield {'parity': parity.next(),
653 yield {'parity': parity.next(),
654 'branch': ctx.branch(),
654 'branch': ctx.branch(),
655 'status': status,
655 'status': status,
656 'node': ctx.hex(),
656 'node': ctx.hex(),
657 'date': ctx.date()}
657 'date': ctx.date()}
658
658
659 return tmpl('branches', node=hex(web.repo.changelog.tip()),
659 return tmpl('branches', node=hex(web.repo.changelog.tip()),
660 entries=lambda **x: entries(0, **x),
660 entries=lambda **x: entries(0, **x),
661 latestentry=lambda **x: entries(1, **x))
661 latestentry=lambda **x: entries(1, **x))
662
662
663 @webcommand('summary')
663 @webcommand('summary')
664 def summary(web, req, tmpl):
664 def summary(web, req, tmpl):
665 """
665 """
666 /summary
666 /summary
667 --------
667 --------
668
668
669 Show a summary of repository state.
669 Show a summary of repository state.
670
670
671 Information about the latest changesets, bookmarks, tags, and branches
671 Information about the latest changesets, bookmarks, tags, and branches
672 is captured by this handler.
672 is captured by this handler.
673
673
674 The ``summary`` template is rendered.
674 The ``summary`` template is rendered.
675 """
675 """
676 i = reversed(web.repo.tagslist())
676 i = reversed(web.repo.tagslist())
677
677
678 def tagentries(**map):
678 def tagentries(**map):
679 parity = paritygen(web.stripecount)
679 parity = paritygen(web.stripecount)
680 count = 0
680 count = 0
681 for k, n in i:
681 for k, n in i:
682 if k == "tip": # skip tip
682 if k == "tip": # skip tip
683 continue
683 continue
684
684
685 count += 1
685 count += 1
686 if count > 10: # limit to 10 tags
686 if count > 10: # limit to 10 tags
687 break
687 break
688
688
689 yield tmpl("tagentry",
689 yield tmpl("tagentry",
690 parity=parity.next(),
690 parity=parity.next(),
691 tag=k,
691 tag=k,
692 node=hex(n),
692 node=hex(n),
693 date=web.repo[n].date())
693 date=web.repo[n].date())
694
694
695 def bookmarks(**map):
695 def bookmarks(**map):
696 parity = paritygen(web.stripecount)
696 parity = paritygen(web.stripecount)
697 marks = [b for b in web.repo._bookmarks.items() if b[1] in web.repo]
697 marks = [b for b in web.repo._bookmarks.items() if b[1] in web.repo]
698 for k, n in sorted(marks)[:10]: # limit to 10 bookmarks
698 for k, n in sorted(marks)[:10]: # limit to 10 bookmarks
699 yield {'parity': parity.next(),
699 yield {'parity': parity.next(),
700 'bookmark': k,
700 'bookmark': k,
701 'date': web.repo[n].date(),
701 'date': web.repo[n].date(),
702 'node': hex(n)}
702 'node': hex(n)}
703
703
704 def branches(**map):
704 def branches(**map):
705 parity = paritygen(web.stripecount)
705 parity = paritygen(web.stripecount)
706
706
707 b = web.repo.branchmap()
707 b = web.repo.branchmap()
708 l = [(-web.repo.changelog.rev(tip), tip, tag)
708 l = [(-web.repo.changelog.rev(tip), tip, tag)
709 for tag, heads, tip, closed in b.iterbranches()]
709 for tag, heads, tip, closed in b.iterbranches()]
710 for r, n, t in sorted(l):
710 for r, n, t in sorted(l):
711 yield {'parity': parity.next(),
711 yield {'parity': parity.next(),
712 'branch': t,
712 'branch': t,
713 'node': hex(n),
713 'node': hex(n),
714 'date': web.repo[n].date()}
714 'date': web.repo[n].date()}
715
715
716 def changelist(**map):
716 def changelist(**map):
717 parity = paritygen(web.stripecount, offset=start - end)
717 parity = paritygen(web.stripecount, offset=start - end)
718 l = [] # build a list in forward order for efficiency
718 l = [] # build a list in forward order for efficiency
719 revs = []
719 revs = []
720 if start < end:
720 if start < end:
721 revs = web.repo.changelog.revs(start, end - 1)
721 revs = web.repo.changelog.revs(start, end - 1)
722 for i in revs:
722 for i in revs:
723 ctx = web.repo[i]
723 ctx = web.repo[i]
724 n = ctx.node()
724 n = ctx.node()
725 hn = hex(n)
725 hn = hex(n)
726
726
727 l.append(tmpl(
727 l.append(tmpl(
728 'shortlogentry',
728 'shortlogentry',
729 parity=parity.next(),
729 parity=parity.next(),
730 author=ctx.user(),
730 author=ctx.user(),
731 desc=ctx.description(),
731 desc=ctx.description(),
732 extra=ctx.extra(),
732 extra=ctx.extra(),
733 date=ctx.date(),
733 date=ctx.date(),
734 rev=i,
734 rev=i,
735 node=hn,
735 node=hn,
736 tags=webutil.nodetagsdict(web.repo, n),
736 tags=webutil.nodetagsdict(web.repo, n),
737 bookmarks=webutil.nodebookmarksdict(web.repo, n),
737 bookmarks=webutil.nodebookmarksdict(web.repo, n),
738 inbranch=webutil.nodeinbranch(web.repo, ctx),
738 inbranch=webutil.nodeinbranch(web.repo, ctx),
739 branches=webutil.nodebranchdict(web.repo, ctx)))
739 branches=webutil.nodebranchdict(web.repo, ctx)))
740
740
741 l.reverse()
741 l.reverse()
742 yield l
742 yield l
743
743
744 tip = web.repo['tip']
744 tip = web.repo['tip']
745 count = len(web.repo)
745 count = len(web.repo)
746 start = max(0, count - web.maxchanges)
746 start = max(0, count - web.maxchanges)
747 end = min(count, start + web.maxchanges)
747 end = min(count, start + web.maxchanges)
748
748
749 return tmpl("summary",
749 return tmpl("summary",
750 desc=web.config("web", "description", "unknown"),
750 desc=web.config("web", "description", "unknown"),
751 owner=get_contact(web.config) or "unknown",
751 owner=get_contact(web.config) or "unknown",
752 lastchange=tip.date(),
752 lastchange=tip.date(),
753 tags=tagentries,
753 tags=tagentries,
754 bookmarks=bookmarks,
754 bookmarks=bookmarks,
755 branches=branches,
755 branches=branches,
756 shortlog=changelist,
756 shortlog=changelist,
757 node=tip.hex(),
757 node=tip.hex(),
758 archives=web.archivelist("tip"))
758 archives=web.archivelist("tip"))
759
759
760 @webcommand('filediff')
760 @webcommand('filediff')
761 def filediff(web, req, tmpl):
761 def filediff(web, req, tmpl):
762 """
762 """
763 /diff/{revision}/{path}
763 /diff/{revision}/{path}
764 -----------------------
764 -----------------------
765
765
766 Show how a file changed in a particular commit.
766 Show how a file changed in a particular commit.
767
767
768 The ``filediff`` template is rendered.
768 The ``filediff`` template is rendered.
769
769
770 This hander is registered under both the ``/diff`` and ``/filediff``
770 This hander is registered under both the ``/diff`` and ``/filediff``
771 paths. ``/diff`` is used in modern code.
771 paths. ``/diff`` is used in modern code.
772 """
772 """
773 fctx, ctx = None, None
773 fctx, ctx = None, None
774 try:
774 try:
775 fctx = webutil.filectx(web.repo, req)
775 fctx = webutil.filectx(web.repo, req)
776 except LookupError:
776 except LookupError:
777 ctx = webutil.changectx(web.repo, req)
777 ctx = webutil.changectx(web.repo, req)
778 path = webutil.cleanpath(web.repo, req.form['file'][0])
778 path = webutil.cleanpath(web.repo, req.form['file'][0])
779 if path not in ctx.files():
779 if path not in ctx.files():
780 raise
780 raise
781
781
782 if fctx is not None:
782 if fctx is not None:
783 n = fctx.node()
783 n = fctx.node()
784 path = fctx.path()
784 path = fctx.path()
785 ctx = fctx.changectx()
785 ctx = fctx.changectx()
786 else:
786 else:
787 n = ctx.node()
787 n = ctx.node()
788 # path already defined in except clause
788 # path already defined in except clause
789
789
790 parity = paritygen(web.stripecount)
790 parity = paritygen(web.stripecount)
791 style = web.config('web', 'style', 'paper')
791 style = web.config('web', 'style', 'paper')
792 if 'style' in req.form:
792 if 'style' in req.form:
793 style = req.form['style'][0]
793 style = req.form['style'][0]
794
794
795 diffs = webutil.diffs(web.repo, tmpl, ctx, None, [path], parity, style)
795 diffs = webutil.diffs(web.repo, tmpl, ctx, None, [path], parity, style)
796 if fctx:
796 if fctx:
797 rename = webutil.renamelink(fctx)
797 rename = webutil.renamelink(fctx)
798 ctx = fctx
798 ctx = fctx
799 else:
799 else:
800 rename = []
800 rename = []
801 ctx = ctx
801 ctx = ctx
802 return tmpl("filediff",
802 return tmpl("filediff",
803 file=path,
803 file=path,
804 node=hex(n),
804 node=hex(n),
805 rev=ctx.rev(),
805 rev=ctx.rev(),
806 date=ctx.date(),
806 date=ctx.date(),
807 desc=ctx.description(),
807 desc=ctx.description(),
808 extra=ctx.extra(),
808 extra=ctx.extra(),
809 author=ctx.user(),
809 author=ctx.user(),
810 rename=rename,
810 rename=rename,
811 branch=webutil.nodebranchnodefault(ctx),
811 branch=webutil.nodebranchnodefault(ctx),
812 parent=webutil.parents(ctx),
812 parent=webutil.parents(ctx),
813 child=webutil.children(ctx),
813 child=webutil.children(ctx),
814 tags=webutil.nodetagsdict(web.repo, n),
814 tags=webutil.nodetagsdict(web.repo, n),
815 bookmarks=webutil.nodebookmarksdict(web.repo, n),
815 bookmarks=webutil.nodebookmarksdict(web.repo, n),
816 diff=diffs)
816 diff=diffs)
817
817
818 diff = webcommand('diff')(filediff)
818 diff = webcommand('diff')(filediff)
819
819
820 @webcommand('comparison')
820 @webcommand('comparison')
821 def comparison(web, req, tmpl):
821 def comparison(web, req, tmpl):
822 """
822 """
823 /comparison/{revision}/{path}
823 /comparison/{revision}/{path}
824 -----------------------------
824 -----------------------------
825
825
826 Show a comparison between the old and new versions of a file from changes
826 Show a comparison between the old and new versions of a file from changes
827 made on a particular revision.
827 made on a particular revision.
828
828
829 This is similar to the ``diff`` handler. However, this form features
829 This is similar to the ``diff`` handler. However, this form features
830 a split or side-by-side diff rather than a unified diff.
830 a split or side-by-side diff rather than a unified diff.
831
831
832 The ``context`` query string argument can be used to control the lines of
832 The ``context`` query string argument can be used to control the lines of
833 context in the diff.
833 context in the diff.
834
834
835 The ``filecomparison`` template is rendered.
835 The ``filecomparison`` template is rendered.
836 """
836 """
837 ctx = webutil.changectx(web.repo, req)
837 ctx = webutil.changectx(web.repo, req)
838 if 'file' not in req.form:
838 if 'file' not in req.form:
839 raise ErrorResponse(HTTP_NOT_FOUND, 'file not given')
839 raise ErrorResponse(HTTP_NOT_FOUND, 'file not given')
840 path = webutil.cleanpath(web.repo, req.form['file'][0])
840 path = webutil.cleanpath(web.repo, req.form['file'][0])
841 rename = path in ctx and webutil.renamelink(ctx[path]) or []
841 rename = path in ctx and webutil.renamelink(ctx[path]) or []
842
842
843 parsecontext = lambda v: v == 'full' and -1 or int(v)
843 parsecontext = lambda v: v == 'full' and -1 or int(v)
844 if 'context' in req.form:
844 if 'context' in req.form:
845 context = parsecontext(req.form['context'][0])
845 context = parsecontext(req.form['context'][0])
846 else:
846 else:
847 context = parsecontext(web.config('web', 'comparisoncontext', '5'))
847 context = parsecontext(web.config('web', 'comparisoncontext', '5'))
848
848
849 def filelines(f):
849 def filelines(f):
850 if util.binary(f.data()):
850 if util.binary(f.data()):
851 mt = mimetypes.guess_type(f.path())[0]
851 mt = mimetypes.guess_type(f.path())[0]
852 if not mt:
852 if not mt:
853 mt = 'application/octet-stream'
853 mt = 'application/octet-stream'
854 return [_('(binary file %s, hash: %s)') % (mt, hex(f.filenode()))]
854 return [_('(binary file %s, hash: %s)') % (mt, hex(f.filenode()))]
855 return f.data().splitlines()
855 return f.data().splitlines()
856
856
857 parent = ctx.p1()
857 parent = ctx.p1()
858 leftrev = parent.rev()
858 leftrev = parent.rev()
859 leftnode = parent.node()
859 leftnode = parent.node()
860 rightrev = ctx.rev()
860 rightrev = ctx.rev()
861 rightnode = ctx.node()
861 rightnode = ctx.node()
862 if path in ctx:
862 if path in ctx:
863 fctx = ctx[path]
863 fctx = ctx[path]
864 rightlines = filelines(fctx)
864 rightlines = filelines(fctx)
865 if path not in parent:
865 if path not in parent:
866 leftlines = ()
866 leftlines = ()
867 else:
867 else:
868 pfctx = parent[path]
868 pfctx = parent[path]
869 leftlines = filelines(pfctx)
869 leftlines = filelines(pfctx)
870 else:
870 else:
871 rightlines = ()
871 rightlines = ()
872 fctx = ctx.parents()[0][path]
872 fctx = ctx.parents()[0][path]
873 leftlines = filelines(fctx)
873 leftlines = filelines(fctx)
874
874
875 comparison = webutil.compare(tmpl, context, leftlines, rightlines)
875 comparison = webutil.compare(tmpl, context, leftlines, rightlines)
876 return tmpl('filecomparison',
876 return tmpl('filecomparison',
877 file=path,
877 file=path,
878 node=hex(ctx.node()),
878 node=hex(ctx.node()),
879 rev=ctx.rev(),
879 rev=ctx.rev(),
880 date=ctx.date(),
880 date=ctx.date(),
881 desc=ctx.description(),
881 desc=ctx.description(),
882 extra=ctx.extra(),
882 extra=ctx.extra(),
883 author=ctx.user(),
883 author=ctx.user(),
884 rename=rename,
884 rename=rename,
885 branch=webutil.nodebranchnodefault(ctx),
885 branch=webutil.nodebranchnodefault(ctx),
886 parent=webutil.parents(fctx),
886 parent=webutil.parents(fctx),
887 child=webutil.children(fctx),
887 child=webutil.children(fctx),
888 tags=webutil.nodetagsdict(web.repo, ctx.node()),
888 tags=webutil.nodetagsdict(web.repo, ctx.node()),
889 bookmarks=webutil.nodebookmarksdict(web.repo, ctx.node()),
889 bookmarks=webutil.nodebookmarksdict(web.repo, ctx.node()),
890 leftrev=leftrev,
890 leftrev=leftrev,
891 leftnode=hex(leftnode),
891 leftnode=hex(leftnode),
892 rightrev=rightrev,
892 rightrev=rightrev,
893 rightnode=hex(rightnode),
893 rightnode=hex(rightnode),
894 comparison=comparison)
894 comparison=comparison)
895
895
896 @webcommand('annotate')
896 @webcommand('annotate')
897 def annotate(web, req, tmpl):
897 def annotate(web, req, tmpl):
898 """
898 """
899 /annotate/{revision}/{path}
899 /annotate/{revision}/{path}
900 ---------------------------
900 ---------------------------
901
901
902 Show changeset information for each line in a file.
902 Show changeset information for each line in a file.
903
903
904 The ``fileannotate`` template is rendered.
904 The ``fileannotate`` template is rendered.
905 """
905 """
906 fctx = webutil.filectx(web.repo, req)
906 fctx = webutil.filectx(web.repo, req)
907 f = fctx.path()
907 f = fctx.path()
908 parity = paritygen(web.stripecount)
908 parity = paritygen(web.stripecount)
909 diffopts = patch.difffeatureopts(web.repo.ui, untrusted=True,
909 diffopts = patch.difffeatureopts(web.repo.ui, untrusted=True,
910 section='annotate', whitespace=True)
910 section='annotate', whitespace=True)
911
911
912 def annotate(**map):
912 def annotate(**map):
913 last = None
913 last = None
914 if util.binary(fctx.data()):
914 if util.binary(fctx.data()):
915 mt = (mimetypes.guess_type(fctx.path())[0]
915 mt = (mimetypes.guess_type(fctx.path())[0]
916 or 'application/octet-stream')
916 or 'application/octet-stream')
917 lines = enumerate([((fctx.filectx(fctx.filerev()), 1),
917 lines = enumerate([((fctx.filectx(fctx.filerev()), 1),
918 '(binary:%s)' % mt)])
918 '(binary:%s)' % mt)])
919 else:
919 else:
920 lines = enumerate(fctx.annotate(follow=True, linenumber=True,
920 lines = enumerate(fctx.annotate(follow=True, linenumber=True,
921 diffopts=diffopts))
921 diffopts=diffopts))
922 for lineno, ((f, targetline), l) in lines:
922 for lineno, ((f, targetline), l) in lines:
923 fnode = f.filenode()
923 fnode = f.filenode()
924
924
925 if last != fnode:
925 if last != fnode:
926 last = fnode
926 last = fnode
927
927
928 yield {"parity": parity.next(),
928 yield {"parity": parity.next(),
929 "node": f.hex(),
929 "node": f.hex(),
930 "rev": f.rev(),
930 "rev": f.rev(),
931 "author": f.user(),
931 "author": f.user(),
932 "desc": f.description(),
932 "desc": f.description(),
933 "extra": f.extra(),
933 "extra": f.extra(),
934 "file": f.path(),
934 "file": f.path(),
935 "targetline": targetline,
935 "targetline": targetline,
936 "line": l,
936 "line": l,
937 "lineno": lineno + 1,
937 "lineno": lineno + 1,
938 "lineid": "l%d" % (lineno + 1),
938 "lineid": "l%d" % (lineno + 1),
939 "linenumber": "% 6d" % (lineno + 1),
939 "linenumber": "% 6d" % (lineno + 1),
940 "revdate": f.date()}
940 "revdate": f.date()}
941
941
942 return tmpl("fileannotate",
942 return tmpl("fileannotate",
943 file=f,
943 file=f,
944 annotate=annotate,
944 annotate=annotate,
945 path=webutil.up(f),
945 path=webutil.up(f),
946 rev=fctx.rev(),
946 rev=fctx.rev(),
947 node=fctx.hex(),
947 node=fctx.hex(),
948 author=fctx.user(),
948 author=fctx.user(),
949 date=fctx.date(),
949 date=fctx.date(),
950 desc=fctx.description(),
950 desc=fctx.description(),
951 extra=fctx.extra(),
951 extra=fctx.extra(),
952 rename=webutil.renamelink(fctx),
952 rename=webutil.renamelink(fctx),
953 branch=webutil.nodebranchnodefault(fctx),
953 branch=webutil.nodebranchnodefault(fctx),
954 parent=webutil.parents(fctx),
954 parent=webutil.parents(fctx),
955 child=webutil.children(fctx),
955 child=webutil.children(fctx),
956 tags=webutil.nodetagsdict(web.repo, fctx.node()),
956 tags=webutil.nodetagsdict(web.repo, fctx.node()),
957 bookmarks=webutil.nodebookmarksdict(web.repo, fctx.node()),
957 bookmarks=webutil.nodebookmarksdict(web.repo, fctx.node()),
958 permissions=fctx.manifest().flags(f))
958 permissions=fctx.manifest().flags(f))
959
959
960 @webcommand('filelog')
960 @webcommand('filelog')
961 def filelog(web, req, tmpl):
961 def filelog(web, req, tmpl):
962 """
962 """
963 /filelog/{revision}/{path}
963 /filelog/{revision}/{path}
964 --------------------------
964 --------------------------
965
965
966 Show information about the history of a file in the repository.
966 Show information about the history of a file in the repository.
967
967
968 The ``revcount`` query string argument can be defined to control the
968 The ``revcount`` query string argument can be defined to control the
969 maximum number of entries to show.
969 maximum number of entries to show.
970
970
971 The ``filelog`` template will be rendered.
971 The ``filelog`` template will be rendered.
972 """
972 """
973
973
974 try:
974 try:
975 fctx = webutil.filectx(web.repo, req)
975 fctx = webutil.filectx(web.repo, req)
976 f = fctx.path()
976 f = fctx.path()
977 fl = fctx.filelog()
977 fl = fctx.filelog()
978 except error.LookupError:
978 except error.LookupError:
979 f = webutil.cleanpath(web.repo, req.form['file'][0])
979 f = webutil.cleanpath(web.repo, req.form['file'][0])
980 fl = web.repo.file(f)
980 fl = web.repo.file(f)
981 numrevs = len(fl)
981 numrevs = len(fl)
982 if not numrevs: # file doesn't exist at all
982 if not numrevs: # file doesn't exist at all
983 raise
983 raise
984 rev = webutil.changectx(web.repo, req).rev()
984 rev = webutil.changectx(web.repo, req).rev()
985 first = fl.linkrev(0)
985 first = fl.linkrev(0)
986 if rev < first: # current rev is from before file existed
986 if rev < first: # current rev is from before file existed
987 raise
987 raise
988 frev = numrevs - 1
988 frev = numrevs - 1
989 while fl.linkrev(frev) > rev:
989 while fl.linkrev(frev) > rev:
990 frev -= 1
990 frev -= 1
991 fctx = web.repo.filectx(f, fl.linkrev(frev))
991 fctx = web.repo.filectx(f, fl.linkrev(frev))
992
992
993 revcount = web.maxshortchanges
993 revcount = web.maxshortchanges
994 if 'revcount' in req.form:
994 if 'revcount' in req.form:
995 try:
995 try:
996 revcount = int(req.form.get('revcount', [revcount])[0])
996 revcount = int(req.form.get('revcount', [revcount])[0])
997 revcount = max(revcount, 1)
997 revcount = max(revcount, 1)
998 tmpl.defaults['sessionvars']['revcount'] = revcount
998 tmpl.defaults['sessionvars']['revcount'] = revcount
999 except ValueError:
999 except ValueError:
1000 pass
1000 pass
1001
1001
1002 lessvars = copy.copy(tmpl.defaults['sessionvars'])
1002 lessvars = copy.copy(tmpl.defaults['sessionvars'])
1003 lessvars['revcount'] = max(revcount / 2, 1)
1003 lessvars['revcount'] = max(revcount / 2, 1)
1004 morevars = copy.copy(tmpl.defaults['sessionvars'])
1004 morevars = copy.copy(tmpl.defaults['sessionvars'])
1005 morevars['revcount'] = revcount * 2
1005 morevars['revcount'] = revcount * 2
1006
1006
1007 count = fctx.filerev() + 1
1007 count = fctx.filerev() + 1
1008 start = max(0, fctx.filerev() - revcount + 1) # first rev on this page
1008 start = max(0, fctx.filerev() - revcount + 1) # first rev on this page
1009 end = min(count, start + revcount) # last rev on this page
1009 end = min(count, start + revcount) # last rev on this page
1010 parity = paritygen(web.stripecount, offset=start - end)
1010 parity = paritygen(web.stripecount, offset=start - end)
1011
1011
1012 def entries():
1012 def entries():
1013 l = []
1013 l = []
1014
1014
1015 repo = web.repo
1015 repo = web.repo
1016 revs = fctx.filelog().revs(start, end - 1)
1016 revs = fctx.filelog().revs(start, end - 1)
1017 for i in revs:
1017 for i in revs:
1018 iterfctx = fctx.filectx(i)
1018 iterfctx = fctx.filectx(i)
1019
1019
1020 l.append({"parity": parity.next(),
1020 l.append({"parity": parity.next(),
1021 "filerev": i,
1021 "filerev": i,
1022 "file": f,
1022 "file": f,
1023 "node": iterfctx.hex(),
1023 "node": iterfctx.hex(),
1024 "author": iterfctx.user(),
1024 "author": iterfctx.user(),
1025 "date": iterfctx.date(),
1025 "date": iterfctx.date(),
1026 "rename": webutil.renamelink(iterfctx),
1026 "rename": webutil.renamelink(iterfctx),
1027 "parent": webutil.parents(iterfctx),
1027 "parent": webutil.parents(iterfctx),
1028 "child": webutil.children(iterfctx),
1028 "child": webutil.children(iterfctx),
1029 "desc": iterfctx.description(),
1029 "desc": iterfctx.description(),
1030 "extra": iterfctx.extra(),
1030 "extra": iterfctx.extra(),
1031 "tags": webutil.nodetagsdict(repo, iterfctx.node()),
1031 "tags": webutil.nodetagsdict(repo, iterfctx.node()),
1032 "bookmarks": webutil.nodebookmarksdict(
1032 "bookmarks": webutil.nodebookmarksdict(
1033 repo, iterfctx.node()),
1033 repo, iterfctx.node()),
1034 "branch": webutil.nodebranchnodefault(iterfctx),
1034 "branch": webutil.nodebranchnodefault(iterfctx),
1035 "inbranch": webutil.nodeinbranch(repo, iterfctx),
1035 "inbranch": webutil.nodeinbranch(repo, iterfctx),
1036 "branches": webutil.nodebranchdict(repo, iterfctx)})
1036 "branches": webutil.nodebranchdict(repo, iterfctx)})
1037 for e in reversed(l):
1037 for e in reversed(l):
1038 yield e
1038 yield e
1039
1039
1040 entries = list(entries())
1040 entries = list(entries())
1041 latestentry = entries[:1]
1041 latestentry = entries[:1]
1042
1042
1043 revnav = webutil.filerevnav(web.repo, fctx.path())
1043 revnav = webutil.filerevnav(web.repo, fctx.path())
1044 nav = revnav.gen(end - 1, revcount, count)
1044 nav = revnav.gen(end - 1, revcount, count)
1045 return tmpl("filelog", file=f, node=fctx.hex(), nav=nav,
1045 return tmpl("filelog", file=f, node=fctx.hex(), nav=nav,
1046 entries=entries,
1046 entries=entries,
1047 latestentry=latestentry,
1047 latestentry=latestentry,
1048 revcount=revcount, morevars=morevars, lessvars=lessvars)
1048 revcount=revcount, morevars=morevars, lessvars=lessvars)
1049
1049
1050 @webcommand('archive')
1050 @webcommand('archive')
1051 def archive(web, req, tmpl):
1051 def archive(web, req, tmpl):
1052 """
1052 """
1053 /archive/{revision}.{format}[/{path}]
1053 /archive/{revision}.{format}[/{path}]
1054 -------------------------------------
1054 -------------------------------------
1055
1055
1056 Obtain an archive of repository content.
1056 Obtain an archive of repository content.
1057
1057
1058 The content and type of the archive is defined by a URL path parameter.
1058 The content and type of the archive is defined by a URL path parameter.
1059 ``format`` is the file extension of the archive type to be generated. e.g.
1059 ``format`` is the file extension of the archive type to be generated. e.g.
1060 ``zip`` or ``tar.bz2``. Not all archive types may be allowed by your
1060 ``zip`` or ``tar.bz2``. Not all archive types may be allowed by your
1061 server configuration.
1061 server configuration.
1062
1062
1063 The optional ``path`` URL parameter controls content to include in the
1063 The optional ``path`` URL parameter controls content to include in the
1064 archive. If omitted, every file in the specified revision is present in the
1064 archive. If omitted, every file in the specified revision is present in the
1065 archive. If included, only the specified file or contents of the specified
1065 archive. If included, only the specified file or contents of the specified
1066 directory will be included in the archive.
1066 directory will be included in the archive.
1067
1067
1068 No template is used for this handler. Raw, binary content is generated.
1068 No template is used for this handler. Raw, binary content is generated.
1069 """
1069 """
1070
1070
1071 type_ = req.form.get('type', [None])[0]
1071 type_ = req.form.get('type', [None])[0]
1072 allowed = web.configlist("web", "allow_archive")
1072 allowed = web.configlist("web", "allow_archive")
1073 key = req.form['node'][0]
1073 key = req.form['node'][0]
1074
1074
1075 if type_ not in web.archives:
1075 if type_ not in web.archives:
1076 msg = 'Unsupported archive type: %s' % type_
1076 msg = 'Unsupported archive type: %s' % type_
1077 raise ErrorResponse(HTTP_NOT_FOUND, msg)
1077 raise ErrorResponse(HTTP_NOT_FOUND, msg)
1078
1078
1079 if not ((type_ in allowed or
1079 if not ((type_ in allowed or
1080 web.configbool("web", "allow" + type_, False))):
1080 web.configbool("web", "allow" + type_, False))):
1081 msg = 'Archive type not allowed: %s' % type_
1081 msg = 'Archive type not allowed: %s' % type_
1082 raise ErrorResponse(HTTP_FORBIDDEN, msg)
1082 raise ErrorResponse(HTTP_FORBIDDEN, msg)
1083
1083
1084 reponame = re.sub(r"\W+", "-", os.path.basename(web.reponame))
1084 reponame = re.sub(r"\W+", "-", os.path.basename(web.reponame))
1085 cnode = web.repo.lookup(key)
1085 cnode = web.repo.lookup(key)
1086 arch_version = key
1086 arch_version = key
1087 if cnode == key or key == 'tip':
1087 if cnode == key or key == 'tip':
1088 arch_version = short(cnode)
1088 arch_version = short(cnode)
1089 name = "%s-%s" % (reponame, arch_version)
1089 name = "%s-%s" % (reponame, arch_version)
1090
1090
1091 ctx = webutil.changectx(web.repo, req)
1091 ctx = webutil.changectx(web.repo, req)
1092 pats = []
1092 pats = []
1093 matchfn = scmutil.match(ctx, [])
1093 matchfn = scmutil.match(ctx, [])
1094 file = req.form.get('file', None)
1094 file = req.form.get('file', None)
1095 if file:
1095 if file:
1096 pats = ['path:' + file[0]]
1096 pats = ['path:' + file[0]]
1097 matchfn = scmutil.match(ctx, pats, default='path')
1097 matchfn = scmutil.match(ctx, pats, default='path')
1098 if pats:
1098 if pats:
1099 files = [f for f in ctx.manifest().keys() if matchfn(f)]
1099 files = [f for f in ctx.manifest().keys() if matchfn(f)]
1100 if not files:
1100 if not files:
1101 raise ErrorResponse(HTTP_NOT_FOUND,
1101 raise ErrorResponse(HTTP_NOT_FOUND,
1102 'file(s) not found: %s' % file[0])
1102 'file(s) not found: %s' % file[0])
1103
1103
1104 mimetype, artype, extension, encoding = web.archive_specs[type_]
1104 mimetype, artype, extension, encoding = web.archive_specs[type_]
1105 headers = [
1105 headers = [
1106 ('Content-Disposition', 'attachment; filename=%s%s' % (name, extension))
1106 ('Content-Disposition', 'attachment; filename=%s%s' % (name, extension))
1107 ]
1107 ]
1108 if encoding:
1108 if encoding:
1109 headers.append(('Content-Encoding', encoding))
1109 headers.append(('Content-Encoding', encoding))
1110 req.headers.extend(headers)
1110 req.headers.extend(headers)
1111 req.respond(HTTP_OK, mimetype)
1111 req.respond(HTTP_OK, mimetype)
1112
1112
1113 archival.archive(web.repo, req, cnode, artype, prefix=name,
1113 archival.archive(web.repo, req, cnode, artype, prefix=name,
1114 matchfn=matchfn,
1114 matchfn=matchfn,
1115 subrepos=web.configbool("web", "archivesubrepos"))
1115 subrepos=web.configbool("web", "archivesubrepos"))
1116 return []
1116 return []
1117
1117
1118
1118
1119 @webcommand('static')
1119 @webcommand('static')
1120 def static(web, req, tmpl):
1120 def static(web, req, tmpl):
1121 fname = req.form['file'][0]
1121 fname = req.form['file'][0]
1122 # a repo owner may set web.static in .hg/hgrc to get any file
1122 # a repo owner may set web.static in .hg/hgrc to get any file
1123 # readable by the user running the CGI script
1123 # readable by the user running the CGI script
1124 static = web.config("web", "static", None, untrusted=False)
1124 static = web.config("web", "static", None, untrusted=False)
1125 if not static:
1125 if not static:
1126 tp = web.templatepath or templater.templatepaths()
1126 tp = web.templatepath or templater.templatepaths()
1127 if isinstance(tp, str):
1127 if isinstance(tp, str):
1128 tp = [tp]
1128 tp = [tp]
1129 static = [os.path.join(p, 'static') for p in tp]
1129 static = [os.path.join(p, 'static') for p in tp]
1130 staticfile(static, fname, req)
1130 staticfile(static, fname, req)
1131 return []
1131 return []
1132
1132
1133 @webcommand('graph')
1133 @webcommand('graph')
1134 def graph(web, req, tmpl):
1134 def graph(web, req, tmpl):
1135 """
1135 """
1136 /graph[/{revision}]
1136 /graph[/{revision}]
1137 -------------------
1137 -------------------
1138
1138
1139 Show information about the graphical topology of the repository.
1139 Show information about the graphical topology of the repository.
1140
1140
1141 Information rendered by this handler can be used to create visual
1141 Information rendered by this handler can be used to create visual
1142 representations of repository topology.
1142 representations of repository topology.
1143
1143
1144 The ``revision`` URL parameter controls the starting changeset.
1144 The ``revision`` URL parameter controls the starting changeset.
1145
1145
1146 The ``revcount`` query string argument can define the number of changesets
1146 The ``revcount`` query string argument can define the number of changesets
1147 to show information for.
1147 to show information for.
1148
1148
1149 This handler will render the ``graph`` template.
1149 This handler will render the ``graph`` template.
1150 """
1150 """
1151
1151
1152 ctx = webutil.changectx(web.repo, req)
1152 ctx = webutil.changectx(web.repo, req)
1153 rev = ctx.rev()
1153 rev = ctx.rev()
1154
1154
1155 bg_height = 39
1155 bg_height = 39
1156 revcount = web.maxshortchanges
1156 revcount = web.maxshortchanges
1157 if 'revcount' in req.form:
1157 if 'revcount' in req.form:
1158 try:
1158 try:
1159 revcount = int(req.form.get('revcount', [revcount])[0])
1159 revcount = int(req.form.get('revcount', [revcount])[0])
1160 revcount = max(revcount, 1)
1160 revcount = max(revcount, 1)
1161 tmpl.defaults['sessionvars']['revcount'] = revcount
1161 tmpl.defaults['sessionvars']['revcount'] = revcount
1162 except ValueError:
1162 except ValueError:
1163 pass
1163 pass
1164
1164
1165 lessvars = copy.copy(tmpl.defaults['sessionvars'])
1165 lessvars = copy.copy(tmpl.defaults['sessionvars'])
1166 lessvars['revcount'] = max(revcount / 2, 1)
1166 lessvars['revcount'] = max(revcount / 2, 1)
1167 morevars = copy.copy(tmpl.defaults['sessionvars'])
1167 morevars = copy.copy(tmpl.defaults['sessionvars'])
1168 morevars['revcount'] = revcount * 2
1168 morevars['revcount'] = revcount * 2
1169
1169
1170 count = len(web.repo)
1170 count = len(web.repo)
1171 pos = rev
1171 pos = rev
1172
1172
1173 uprev = min(max(0, count - 1), rev + revcount)
1173 uprev = min(max(0, count - 1), rev + revcount)
1174 downrev = max(0, rev - revcount)
1174 downrev = max(0, rev - revcount)
1175 changenav = webutil.revnav(web.repo).gen(pos, revcount, count)
1175 changenav = webutil.revnav(web.repo).gen(pos, revcount, count)
1176
1176
1177 tree = []
1177 tree = []
1178 if pos != -1:
1178 if pos != -1:
1179 allrevs = web.repo.changelog.revs(pos, 0)
1179 allrevs = web.repo.changelog.revs(pos, 0)
1180 revs = []
1180 revs = []
1181 for i in allrevs:
1181 for i in allrevs:
1182 revs.append(i)
1182 revs.append(i)
1183 if len(revs) >= revcount:
1183 if len(revs) >= revcount:
1184 break
1184 break
1185
1185
1186 # We have to feed a baseset to dagwalker as it is expecting smartset
1186 # We have to feed a baseset to dagwalker as it is expecting smartset
1187 # object. This does not have a big impact on hgweb performance itself
1187 # object. This does not have a big impact on hgweb performance itself
1188 # since hgweb graphing code is not itself lazy yet.
1188 # since hgweb graphing code is not itself lazy yet.
1189 dag = graphmod.dagwalker(web.repo, revset.baseset(revs))
1189 dag = graphmod.dagwalker(web.repo, revset.baseset(revs))
1190 # As we said one line above... not lazy.
1190 # As we said one line above... not lazy.
1191 tree = list(graphmod.colored(dag, web.repo))
1191 tree = list(graphmod.colored(dag, web.repo))
1192
1192
1193 def getcolumns(tree):
1193 def getcolumns(tree):
1194 cols = 0
1194 cols = 0
1195 for (id, type, ctx, vtx, edges) in tree:
1195 for (id, type, ctx, vtx, edges) in tree:
1196 if type != graphmod.CHANGESET:
1196 if type != graphmod.CHANGESET:
1197 continue
1197 continue
1198 cols = max(cols, max([edge[0] for edge in edges] or [0]),
1198 cols = max(cols, max([edge[0] for edge in edges] or [0]),
1199 max([edge[1] for edge in edges] or [0]))
1199 max([edge[1] for edge in edges] or [0]))
1200 return cols
1200 return cols
1201
1201
1202 def graphdata(usetuples, **map):
1202 def graphdata(usetuples, **map):
1203 data = []
1203 data = []
1204
1204
1205 row = 0
1205 row = 0
1206 for (id, type, ctx, vtx, edges) in tree:
1206 for (id, type, ctx, vtx, edges) in tree:
1207 if type != graphmod.CHANGESET:
1207 if type != graphmod.CHANGESET:
1208 continue
1208 continue
1209 node = str(ctx)
1209 node = str(ctx)
1210 age = templatefilters.age(ctx.date())
1210 age = templatefilters.age(ctx.date())
1211 desc = templatefilters.firstline(ctx.description())
1211 desc = templatefilters.firstline(ctx.description())
1212 desc = cgi.escape(templatefilters.nonempty(desc))
1212 desc = cgi.escape(templatefilters.nonempty(desc))
1213 user = cgi.escape(templatefilters.person(ctx.user()))
1213 user = cgi.escape(templatefilters.person(ctx.user()))
1214 branch = cgi.escape(ctx.branch())
1214 branch = cgi.escape(ctx.branch())
1215 try:
1215 try:
1216 branchnode = web.repo.branchtip(branch)
1216 branchnode = web.repo.branchtip(branch)
1217 except error.RepoLookupError:
1217 except error.RepoLookupError:
1218 branchnode = None
1218 branchnode = None
1219 branch = branch, branchnode == ctx.node()
1219 branch = branch, branchnode == ctx.node()
1220
1220
1221 if usetuples:
1221 if usetuples:
1222 data.append((node, vtx, edges, desc, user, age, branch,
1222 data.append((node, vtx, edges, desc, user, age, branch,
1223 [cgi.escape(x) for x in ctx.tags()],
1223 [cgi.escape(x) for x in ctx.tags()],
1224 [cgi.escape(x) for x in ctx.bookmarks()]))
1224 [cgi.escape(x) for x in ctx.bookmarks()]))
1225 else:
1225 else:
1226 edgedata = [{'col': edge[0], 'nextcol': edge[1],
1226 edgedata = [{'col': edge[0], 'nextcol': edge[1],
1227 'color': (edge[2] - 1) % 6 + 1,
1227 'color': (edge[2] - 1) % 6 + 1,
1228 'width': edge[3], 'bcolor': edge[4]}
1228 'width': edge[3], 'bcolor': edge[4]}
1229 for edge in edges]
1229 for edge in edges]
1230
1230
1231 data.append(
1231 data.append(
1232 {'node': node,
1232 {'node': node,
1233 'col': vtx[0],
1233 'col': vtx[0],
1234 'color': (vtx[1] - 1) % 6 + 1,
1234 'color': (vtx[1] - 1) % 6 + 1,
1235 'edges': edgedata,
1235 'edges': edgedata,
1236 'row': row,
1236 'row': row,
1237 'nextrow': row + 1,
1237 'nextrow': row + 1,
1238 'desc': desc,
1238 'desc': desc,
1239 'user': user,
1239 'user': user,
1240 'age': age,
1240 'age': age,
1241 'bookmarks': webutil.nodebookmarksdict(
1241 'bookmarks': webutil.nodebookmarksdict(
1242 web.repo, ctx.node()),
1242 web.repo, ctx.node()),
1243 'branches': webutil.nodebranchdict(web.repo, ctx),
1243 'branches': webutil.nodebranchdict(web.repo, ctx),
1244 'inbranch': webutil.nodeinbranch(web.repo, ctx),
1244 'inbranch': webutil.nodeinbranch(web.repo, ctx),
1245 'tags': webutil.nodetagsdict(web.repo, ctx.node())})
1245 'tags': webutil.nodetagsdict(web.repo, ctx.node())})
1246
1246
1247 row += 1
1247 row += 1
1248
1248
1249 return data
1249 return data
1250
1250
1251 cols = getcolumns(tree)
1251 cols = getcolumns(tree)
1252 rows = len(tree)
1252 rows = len(tree)
1253 canvasheight = (rows + 1) * bg_height - 27
1253 canvasheight = (rows + 1) * bg_height - 27
1254
1254
1255 return tmpl('graph', rev=rev, revcount=revcount, uprev=uprev,
1255 return tmpl('graph', rev=rev, revcount=revcount, uprev=uprev,
1256 lessvars=lessvars, morevars=morevars, downrev=downrev,
1256 lessvars=lessvars, morevars=morevars, downrev=downrev,
1257 cols=cols, rows=rows,
1257 cols=cols, rows=rows,
1258 canvaswidth=(cols + 1) * bg_height,
1258 canvaswidth=(cols + 1) * bg_height,
1259 truecanvasheight=rows * bg_height,
1259 truecanvasheight=rows * bg_height,
1260 canvasheight=canvasheight, bg_height=bg_height,
1260 canvasheight=canvasheight, bg_height=bg_height,
1261 jsdata=lambda **x: graphdata(True, **x),
1261 jsdata=lambda **x: graphdata(True, **x),
1262 nodes=lambda **x: graphdata(False, **x),
1262 nodes=lambda **x: graphdata(False, **x),
1263 node=ctx.hex(), changenav=changenav)
1263 node=ctx.hex(), changenav=changenav)
1264
1264
1265 def _getdoc(e):
1265 def _getdoc(e):
1266 doc = e[0].__doc__
1266 doc = e[0].__doc__
1267 if doc:
1267 if doc:
1268 doc = _(doc).split('\n')[0]
1268 doc = _(doc).split('\n')[0]
1269 else:
1269 else:
1270 doc = _('(no help text available)')
1270 doc = _('(no help text available)')
1271 return doc
1271 return doc
1272
1272
1273 @webcommand('help')
1273 @webcommand('help')
1274 def help(web, req, tmpl):
1274 def help(web, req, tmpl):
1275 """
1275 """
1276 /help[/{topic}]
1276 /help[/{topic}]
1277 ---------------
1277 ---------------
1278
1278
1279 Render help documentation.
1279 Render help documentation.
1280
1280
1281 This web command is roughly equivalent to :hg:`help`. If a ``topic``
1281 This web command is roughly equivalent to :hg:`help`. If a ``topic``
1282 is defined, that help topic will be rendered. If not, an index of
1282 is defined, that help topic will be rendered. If not, an index of
1283 available help topics will be rendered.
1283 available help topics will be rendered.
1284
1284
1285 The ``help`` template will be rendered when requesting help for a topic.
1285 The ``help`` template will be rendered when requesting help for a topic.
1286 ``helptopics`` will be rendered for the index of help topics.
1286 ``helptopics`` will be rendered for the index of help topics.
1287 """
1287 """
1288 from mercurial import commands # avoid cycle
1288 from mercurial import commands # avoid cycle
1289 from mercurial import help as helpmod # avoid cycle
1289 from mercurial import help as helpmod # avoid cycle
1290
1290
1291 topicname = req.form.get('node', [None])[0]
1291 topicname = req.form.get('node', [None])[0]
1292 if not topicname:
1292 if not topicname:
1293 def topics(**map):
1293 def topics(**map):
1294 for entries, summary, _doc in helpmod.helptable:
1294 for entries, summary, _doc in helpmod.helptable:
1295 yield {'topic': entries[0], 'summary': summary}
1295 yield {'topic': entries[0], 'summary': summary}
1296
1296
1297 early, other = [], []
1297 early, other = [], []
1298 primary = lambda s: s.split('|')[0]
1298 primary = lambda s: s.split('|')[0]
1299 for c, e in commands.table.iteritems():
1299 for c, e in commands.table.iteritems():
1300 doc = _getdoc(e)
1300 doc = _getdoc(e)
1301 if 'DEPRECATED' in doc or c.startswith('debug'):
1301 if 'DEPRECATED' in doc or c.startswith('debug'):
1302 continue
1302 continue
1303 cmd = primary(c)
1303 cmd = primary(c)
1304 if cmd.startswith('^'):
1304 if cmd.startswith('^'):
1305 early.append((cmd[1:], doc))
1305 early.append((cmd[1:], doc))
1306 else:
1306 else:
1307 other.append((cmd, doc))
1307 other.append((cmd, doc))
1308
1308
1309 early.sort()
1309 early.sort()
1310 other.sort()
1310 other.sort()
1311
1311
1312 def earlycommands(**map):
1312 def earlycommands(**map):
1313 for c, doc in early:
1313 for c, doc in early:
1314 yield {'topic': c, 'summary': doc}
1314 yield {'topic': c, 'summary': doc}
1315
1315
1316 def othercommands(**map):
1316 def othercommands(**map):
1317 for c, doc in other:
1317 for c, doc in other:
1318 yield {'topic': c, 'summary': doc}
1318 yield {'topic': c, 'summary': doc}
1319
1319
1320 return tmpl('helptopics', topics=topics, earlycommands=earlycommands,
1320 return tmpl('helptopics', topics=topics, earlycommands=earlycommands,
1321 othercommands=othercommands, title='Index')
1321 othercommands=othercommands, title='Index')
1322
1322
1323 u = webutil.wsgiui()
1323 u = webutil.wsgiui()
1324 u.verbose = True
1324 u.verbose = True
1325 try:
1325 try:
1326 doc = helpmod.help_(u, topicname)
1326 doc = helpmod.help_(u, topicname)
1327 except error.UnknownCommand:
1327 except error.UnknownCommand:
1328 raise ErrorResponse(HTTP_NOT_FOUND)
1328 raise ErrorResponse(HTTP_NOT_FOUND)
1329 return tmpl('help', topic=topicname, doc=doc)
1329 return tmpl('help', topic=topicname, doc=doc)
1330
1330
1331 # tell hggettext to extract docstrings from these functions:
1331 # tell hggettext to extract docstrings from these functions:
1332 i18nfunctions = commands.values()
1332 i18nfunctions = commands.values()
@@ -1,3522 +1,3523 b''
1 # revset.py - revision set queries for mercurial
1 # revset.py - revision set queries for mercurial
2 #
2 #
3 # Copyright 2010 Matt Mackall <mpm@selenic.com>
3 # Copyright 2010 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 import re
8 import re
9 import parser, util, error, hbisect, phases
9 import parser, util, error, hbisect, phases
10 import node
10 import node
11 import heapq
11 import heapq
12 import match as matchmod
12 import match as matchmod
13 from i18n import _
13 from i18n import _
14 import encoding
14 import encoding
15 import obsolete as obsmod
15 import obsolete as obsmod
16 import pathutil
16 import pathutil
17 import repoview
17 import repoview
18
18
19 def _revancestors(repo, revs, followfirst):
19 def _revancestors(repo, revs, followfirst):
20 """Like revlog.ancestors(), but supports followfirst."""
20 """Like revlog.ancestors(), but supports followfirst."""
21 if followfirst:
21 if followfirst:
22 cut = 1
22 cut = 1
23 else:
23 else:
24 cut = None
24 cut = None
25 cl = repo.changelog
25 cl = repo.changelog
26
26
27 def iterate():
27 def iterate():
28 revs.sort(reverse=True)
28 revs.sort(reverse=True)
29 irevs = iter(revs)
29 irevs = iter(revs)
30 h = []
30 h = []
31
31
32 inputrev = next(irevs, None)
32 inputrev = next(irevs, None)
33 if inputrev is not None:
33 if inputrev is not None:
34 heapq.heappush(h, -inputrev)
34 heapq.heappush(h, -inputrev)
35
35
36 seen = set()
36 seen = set()
37 while h:
37 while h:
38 current = -heapq.heappop(h)
38 current = -heapq.heappop(h)
39 if current == inputrev:
39 if current == inputrev:
40 inputrev = next(irevs, None)
40 inputrev = next(irevs, None)
41 if inputrev is not None:
41 if inputrev is not None:
42 heapq.heappush(h, -inputrev)
42 heapq.heappush(h, -inputrev)
43 if current not in seen:
43 if current not in seen:
44 seen.add(current)
44 seen.add(current)
45 yield current
45 yield current
46 for parent in cl.parentrevs(current)[:cut]:
46 for parent in cl.parentrevs(current)[:cut]:
47 if parent != node.nullrev:
47 if parent != node.nullrev:
48 heapq.heappush(h, -parent)
48 heapq.heappush(h, -parent)
49
49
50 return generatorset(iterate(), iterasc=False)
50 return generatorset(iterate(), iterasc=False)
51
51
52 def _revdescendants(repo, revs, followfirst):
52 def _revdescendants(repo, revs, followfirst):
53 """Like revlog.descendants() but supports followfirst."""
53 """Like revlog.descendants() but supports followfirst."""
54 if followfirst:
54 if followfirst:
55 cut = 1
55 cut = 1
56 else:
56 else:
57 cut = None
57 cut = None
58
58
59 def iterate():
59 def iterate():
60 cl = repo.changelog
60 cl = repo.changelog
61 first = min(revs)
61 first = min(revs)
62 nullrev = node.nullrev
62 nullrev = node.nullrev
63 if first == nullrev:
63 if first == nullrev:
64 # Are there nodes with a null first parent and a non-null
64 # Are there nodes with a null first parent and a non-null
65 # second one? Maybe. Do we care? Probably not.
65 # second one? Maybe. Do we care? Probably not.
66 for i in cl:
66 for i in cl:
67 yield i
67 yield i
68 else:
68 else:
69 seen = set(revs)
69 seen = set(revs)
70 for i in cl.revs(first + 1):
70 for i in cl.revs(first + 1):
71 for x in cl.parentrevs(i)[:cut]:
71 for x in cl.parentrevs(i)[:cut]:
72 if x != nullrev and x in seen:
72 if x != nullrev and x in seen:
73 seen.add(i)
73 seen.add(i)
74 yield i
74 yield i
75 break
75 break
76
76
77 return generatorset(iterate(), iterasc=True)
77 return generatorset(iterate(), iterasc=True)
78
78
79 def _revsbetween(repo, roots, heads):
79 def _revsbetween(repo, roots, heads):
80 """Return all paths between roots and heads, inclusive of both endpoint
80 """Return all paths between roots and heads, inclusive of both endpoint
81 sets."""
81 sets."""
82 if not roots:
82 if not roots:
83 return baseset()
83 return baseset()
84 parentrevs = repo.changelog.parentrevs
84 parentrevs = repo.changelog.parentrevs
85 visit = list(heads)
85 visit = list(heads)
86 reachable = set()
86 reachable = set()
87 seen = {}
87 seen = {}
88 minroot = min(roots)
88 minroot = min(roots)
89 roots = set(roots)
89 roots = set(roots)
90 # open-code the post-order traversal due to the tiny size of
90 # open-code the post-order traversal due to the tiny size of
91 # sys.getrecursionlimit()
91 # sys.getrecursionlimit()
92 while visit:
92 while visit:
93 rev = visit.pop()
93 rev = visit.pop()
94 if rev in roots:
94 if rev in roots:
95 reachable.add(rev)
95 reachable.add(rev)
96 parents = parentrevs(rev)
96 parents = parentrevs(rev)
97 seen[rev] = parents
97 seen[rev] = parents
98 for parent in parents:
98 for parent in parents:
99 if parent >= minroot and parent not in seen:
99 if parent >= minroot and parent not in seen:
100 visit.append(parent)
100 visit.append(parent)
101 if not reachable:
101 if not reachable:
102 return baseset()
102 return baseset()
103 for rev in sorted(seen):
103 for rev in sorted(seen):
104 for parent in seen[rev]:
104 for parent in seen[rev]:
105 if parent in reachable:
105 if parent in reachable:
106 reachable.add(rev)
106 reachable.add(rev)
107 return baseset(sorted(reachable))
107 return baseset(sorted(reachable))
108
108
109 elements = {
109 elements = {
110 "(": (21, ("group", 1, ")"), ("func", 1, ")")),
110 "(": (21, ("group", 1, ")"), ("func", 1, ")")),
111 "##": (20, None, ("_concat", 20)),
111 "##": (20, None, ("_concat", 20)),
112 "~": (18, None, ("ancestor", 18)),
112 "~": (18, None, ("ancestor", 18)),
113 "^": (18, None, ("parent", 18), ("parentpost", 18)),
113 "^": (18, None, ("parent", 18), ("parentpost", 18)),
114 "-": (5, ("negate", 19), ("minus", 5)),
114 "-": (5, ("negate", 19), ("minus", 5)),
115 "::": (17, ("dagrangepre", 17), ("dagrange", 17),
115 "::": (17, ("dagrangepre", 17), ("dagrange", 17),
116 ("dagrangepost", 17)),
116 ("dagrangepost", 17)),
117 "..": (17, ("dagrangepre", 17), ("dagrange", 17),
117 "..": (17, ("dagrangepre", 17), ("dagrange", 17),
118 ("dagrangepost", 17)),
118 ("dagrangepost", 17)),
119 ":": (15, ("rangepre", 15), ("range", 15), ("rangepost", 15)),
119 ":": (15, ("rangepre", 15), ("range", 15), ("rangepost", 15)),
120 "not": (10, ("not", 10)),
120 "not": (10, ("not", 10)),
121 "!": (10, ("not", 10)),
121 "!": (10, ("not", 10)),
122 "and": (5, None, ("and", 5)),
122 "and": (5, None, ("and", 5)),
123 "&": (5, None, ("and", 5)),
123 "&": (5, None, ("and", 5)),
124 "%": (5, None, ("only", 5), ("onlypost", 5)),
124 "%": (5, None, ("only", 5), ("onlypost", 5)),
125 "or": (4, None, ("or", 4)),
125 "or": (4, None, ("or", 4)),
126 "|": (4, None, ("or", 4)),
126 "|": (4, None, ("or", 4)),
127 "+": (4, None, ("or", 4)),
127 "+": (4, None, ("or", 4)),
128 ",": (2, None, ("list", 2)),
128 ",": (2, None, ("list", 2)),
129 ")": (0, None, None),
129 ")": (0, None, None),
130 "symbol": (0, ("symbol",), None),
130 "symbol": (0, ("symbol",), None),
131 "string": (0, ("string",), None),
131 "string": (0, ("string",), None),
132 "end": (0, None, None),
132 "end": (0, None, None),
133 }
133 }
134
134
135 keywords = set(['and', 'or', 'not'])
135 keywords = set(['and', 'or', 'not'])
136
136
137 # default set of valid characters for the initial letter of symbols
137 # default set of valid characters for the initial letter of symbols
138 _syminitletters = set(c for c in [chr(i) for i in xrange(256)]
138 _syminitletters = set(c for c in [chr(i) for i in xrange(256)]
139 if c.isalnum() or c in '._@' or ord(c) > 127)
139 if c.isalnum() or c in '._@' or ord(c) > 127)
140
140
141 # default set of valid characters for non-initial letters of symbols
141 # default set of valid characters for non-initial letters of symbols
142 _symletters = set(c for c in [chr(i) for i in xrange(256)]
142 _symletters = set(c for c in [chr(i) for i in xrange(256)]
143 if c.isalnum() or c in '-._/@' or ord(c) > 127)
143 if c.isalnum() or c in '-._/@' or ord(c) > 127)
144
144
145 def tokenize(program, lookup=None, syminitletters=None, symletters=None):
145 def tokenize(program, lookup=None, syminitletters=None, symletters=None):
146 '''
146 '''
147 Parse a revset statement into a stream of tokens
147 Parse a revset statement into a stream of tokens
148
148
149 ``syminitletters`` is the set of valid characters for the initial
149 ``syminitletters`` is the set of valid characters for the initial
150 letter of symbols.
150 letter of symbols.
151
151
152 By default, character ``c`` is recognized as valid for initial
152 By default, character ``c`` is recognized as valid for initial
153 letter of symbols, if ``c.isalnum() or c in '._@' or ord(c) > 127``.
153 letter of symbols, if ``c.isalnum() or c in '._@' or ord(c) > 127``.
154
154
155 ``symletters`` is the set of valid characters for non-initial
155 ``symletters`` is the set of valid characters for non-initial
156 letters of symbols.
156 letters of symbols.
157
157
158 By default, character ``c`` is recognized as valid for non-initial
158 By default, character ``c`` is recognized as valid for non-initial
159 letters of symbols, if ``c.isalnum() or c in '-._/@' or ord(c) > 127``.
159 letters of symbols, if ``c.isalnum() or c in '-._/@' or ord(c) > 127``.
160
160
161 Check that @ is a valid unquoted token character (issue3686):
161 Check that @ is a valid unquoted token character (issue3686):
162 >>> list(tokenize("@::"))
162 >>> list(tokenize("@::"))
163 [('symbol', '@', 0), ('::', None, 1), ('end', None, 3)]
163 [('symbol', '@', 0), ('::', None, 1), ('end', None, 3)]
164
164
165 '''
165 '''
166 if syminitletters is None:
166 if syminitletters is None:
167 syminitletters = _syminitletters
167 syminitletters = _syminitletters
168 if symletters is None:
168 if symletters is None:
169 symletters = _symletters
169 symletters = _symletters
170
170
171 pos, l = 0, len(program)
171 pos, l = 0, len(program)
172 while pos < l:
172 while pos < l:
173 c = program[pos]
173 c = program[pos]
174 if c.isspace(): # skip inter-token whitespace
174 if c.isspace(): # skip inter-token whitespace
175 pass
175 pass
176 elif c == ':' and program[pos:pos + 2] == '::': # look ahead carefully
176 elif c == ':' and program[pos:pos + 2] == '::': # look ahead carefully
177 yield ('::', None, pos)
177 yield ('::', None, pos)
178 pos += 1 # skip ahead
178 pos += 1 # skip ahead
179 elif c == '.' and program[pos:pos + 2] == '..': # look ahead carefully
179 elif c == '.' and program[pos:pos + 2] == '..': # look ahead carefully
180 yield ('..', None, pos)
180 yield ('..', None, pos)
181 pos += 1 # skip ahead
181 pos += 1 # skip ahead
182 elif c == '#' and program[pos:pos + 2] == '##': # look ahead carefully
182 elif c == '#' and program[pos:pos + 2] == '##': # look ahead carefully
183 yield ('##', None, pos)
183 yield ('##', None, pos)
184 pos += 1 # skip ahead
184 pos += 1 # skip ahead
185 elif c in "():,-|&+!~^%": # handle simple operators
185 elif c in "():,-|&+!~^%": # handle simple operators
186 yield (c, None, pos)
186 yield (c, None, pos)
187 elif (c in '"\'' or c == 'r' and
187 elif (c in '"\'' or c == 'r' and
188 program[pos:pos + 2] in ("r'", 'r"')): # handle quoted strings
188 program[pos:pos + 2] in ("r'", 'r"')): # handle quoted strings
189 if c == 'r':
189 if c == 'r':
190 pos += 1
190 pos += 1
191 c = program[pos]
191 c = program[pos]
192 decode = lambda x: x
192 decode = lambda x: x
193 else:
193 else:
194 decode = lambda x: x.decode('string-escape')
194 decode = lambda x: x.decode('string-escape')
195 pos += 1
195 pos += 1
196 s = pos
196 s = pos
197 while pos < l: # find closing quote
197 while pos < l: # find closing quote
198 d = program[pos]
198 d = program[pos]
199 if d == '\\': # skip over escaped characters
199 if d == '\\': # skip over escaped characters
200 pos += 2
200 pos += 2
201 continue
201 continue
202 if d == c:
202 if d == c:
203 yield ('string', decode(program[s:pos]), s)
203 yield ('string', decode(program[s:pos]), s)
204 break
204 break
205 pos += 1
205 pos += 1
206 else:
206 else:
207 raise error.ParseError(_("unterminated string"), s)
207 raise error.ParseError(_("unterminated string"), s)
208 # gather up a symbol/keyword
208 # gather up a symbol/keyword
209 elif c in syminitletters:
209 elif c in syminitletters:
210 s = pos
210 s = pos
211 pos += 1
211 pos += 1
212 while pos < l: # find end of symbol
212 while pos < l: # find end of symbol
213 d = program[pos]
213 d = program[pos]
214 if d not in symletters:
214 if d not in symletters:
215 break
215 break
216 if d == '.' and program[pos - 1] == '.': # special case for ..
216 if d == '.' and program[pos - 1] == '.': # special case for ..
217 pos -= 1
217 pos -= 1
218 break
218 break
219 pos += 1
219 pos += 1
220 sym = program[s:pos]
220 sym = program[s:pos]
221 if sym in keywords: # operator keywords
221 if sym in keywords: # operator keywords
222 yield (sym, None, s)
222 yield (sym, None, s)
223 elif '-' in sym:
223 elif '-' in sym:
224 # some jerk gave us foo-bar-baz, try to check if it's a symbol
224 # some jerk gave us foo-bar-baz, try to check if it's a symbol
225 if lookup and lookup(sym):
225 if lookup and lookup(sym):
226 # looks like a real symbol
226 # looks like a real symbol
227 yield ('symbol', sym, s)
227 yield ('symbol', sym, s)
228 else:
228 else:
229 # looks like an expression
229 # looks like an expression
230 parts = sym.split('-')
230 parts = sym.split('-')
231 for p in parts[:-1]:
231 for p in parts[:-1]:
232 if p: # possible consecutive -
232 if p: # possible consecutive -
233 yield ('symbol', p, s)
233 yield ('symbol', p, s)
234 s += len(p)
234 s += len(p)
235 yield ('-', None, pos)
235 yield ('-', None, pos)
236 s += 1
236 s += 1
237 if parts[-1]: # possible trailing -
237 if parts[-1]: # possible trailing -
238 yield ('symbol', parts[-1], s)
238 yield ('symbol', parts[-1], s)
239 else:
239 else:
240 yield ('symbol', sym, s)
240 yield ('symbol', sym, s)
241 pos -= 1
241 pos -= 1
242 else:
242 else:
243 raise error.ParseError(_("syntax error in revset '%s'") %
243 raise error.ParseError(_("syntax error in revset '%s'") %
244 program, pos)
244 program, pos)
245 pos += 1
245 pos += 1
246 yield ('end', None, pos)
246 yield ('end', None, pos)
247
247
248 def parseerrordetail(inst):
248 def parseerrordetail(inst):
249 """Compose error message from specified ParseError object
249 """Compose error message from specified ParseError object
250 """
250 """
251 if len(inst.args) > 1:
251 if len(inst.args) > 1:
252 return _('at %s: %s') % (inst.args[1], inst.args[0])
252 return _('at %s: %s') % (inst.args[1], inst.args[0])
253 else:
253 else:
254 return inst.args[0]
254 return inst.args[0]
255
255
256 # helpers
256 # helpers
257
257
258 def getstring(x, err):
258 def getstring(x, err):
259 if x and (x[0] == 'string' or x[0] == 'symbol'):
259 if x and (x[0] == 'string' or x[0] == 'symbol'):
260 return x[1]
260 return x[1]
261 raise error.ParseError(err)
261 raise error.ParseError(err)
262
262
263 def getlist(x):
263 def getlist(x):
264 if not x:
264 if not x:
265 return []
265 return []
266 if x[0] == 'list':
266 if x[0] == 'list':
267 return getlist(x[1]) + [x[2]]
267 return getlist(x[1]) + [x[2]]
268 return [x]
268 return [x]
269
269
270 def getargs(x, min, max, err):
270 def getargs(x, min, max, err):
271 l = getlist(x)
271 l = getlist(x)
272 if len(l) < min or (max >= 0 and len(l) > max):
272 if len(l) < min or (max >= 0 and len(l) > max):
273 raise error.ParseError(err)
273 raise error.ParseError(err)
274 return l
274 return l
275
275
276 def isvalidsymbol(tree):
276 def isvalidsymbol(tree):
277 """Examine whether specified ``tree`` is valid ``symbol`` or not
277 """Examine whether specified ``tree`` is valid ``symbol`` or not
278 """
278 """
279 return tree[0] == 'symbol' and len(tree) > 1
279 return tree[0] == 'symbol' and len(tree) > 1
280
280
281 def getsymbol(tree):
281 def getsymbol(tree):
282 """Get symbol name from valid ``symbol`` in ``tree``
282 """Get symbol name from valid ``symbol`` in ``tree``
283
283
284 This assumes that ``tree`` is already examined by ``isvalidsymbol``.
284 This assumes that ``tree`` is already examined by ``isvalidsymbol``.
285 """
285 """
286 return tree[1]
286 return tree[1]
287
287
288 def isvalidfunc(tree):
288 def isvalidfunc(tree):
289 """Examine whether specified ``tree`` is valid ``func`` or not
289 """Examine whether specified ``tree`` is valid ``func`` or not
290 """
290 """
291 return tree[0] == 'func' and len(tree) > 1 and isvalidsymbol(tree[1])
291 return tree[0] == 'func' and len(tree) > 1 and isvalidsymbol(tree[1])
292
292
293 def getfuncname(tree):
293 def getfuncname(tree):
294 """Get function name from valid ``func`` in ``tree``
294 """Get function name from valid ``func`` in ``tree``
295
295
296 This assumes that ``tree`` is already examined by ``isvalidfunc``.
296 This assumes that ``tree`` is already examined by ``isvalidfunc``.
297 """
297 """
298 return getsymbol(tree[1])
298 return getsymbol(tree[1])
299
299
300 def getfuncargs(tree):
300 def getfuncargs(tree):
301 """Get list of function arguments from valid ``func`` in ``tree``
301 """Get list of function arguments from valid ``func`` in ``tree``
302
302
303 This assumes that ``tree`` is already examined by ``isvalidfunc``.
303 This assumes that ``tree`` is already examined by ``isvalidfunc``.
304 """
304 """
305 if len(tree) > 2:
305 if len(tree) > 2:
306 return getlist(tree[2])
306 return getlist(tree[2])
307 else:
307 else:
308 return []
308 return []
309
309
310 def getset(repo, subset, x):
310 def getset(repo, subset, x):
311 if not x:
311 if not x:
312 raise error.ParseError(_("missing argument"))
312 raise error.ParseError(_("missing argument"))
313 s = methods[x[0]](repo, subset, *x[1:])
313 s = methods[x[0]](repo, subset, *x[1:])
314 if util.safehasattr(s, 'isascending'):
314 if util.safehasattr(s, 'isascending'):
315 return s
315 return s
316 return baseset(s)
316 return baseset(s)
317
317
318 def _getrevsource(repo, r):
318 def _getrevsource(repo, r):
319 extra = repo[r].extra()
319 extra = repo[r].extra()
320 for label in ('source', 'transplant_source', 'rebase_source'):
320 for label in ('source', 'transplant_source', 'rebase_source'):
321 if label in extra:
321 if label in extra:
322 try:
322 try:
323 return repo[extra[label]].rev()
323 return repo[extra[label]].rev()
324 except error.RepoLookupError:
324 except error.RepoLookupError:
325 pass
325 pass
326 return None
326 return None
327
327
328 # operator methods
328 # operator methods
329
329
330 def stringset(repo, subset, x):
330 def stringset(repo, subset, x):
331 x = repo[x].rev()
331 x = repo[x].rev()
332 if x in subset:
332 if x in subset:
333 return baseset([x])
333 return baseset([x])
334 return baseset()
334 return baseset()
335
335
336 def rangeset(repo, subset, x, y):
336 def rangeset(repo, subset, x, y):
337 m = getset(repo, fullreposet(repo), x)
337 m = getset(repo, fullreposet(repo), x)
338 n = getset(repo, fullreposet(repo), y)
338 n = getset(repo, fullreposet(repo), y)
339
339
340 if not m or not n:
340 if not m or not n:
341 return baseset()
341 return baseset()
342 m, n = m.first(), n.last()
342 m, n = m.first(), n.last()
343
343
344 if m < n:
344 if m < n:
345 r = spanset(repo, m, n + 1)
345 r = spanset(repo, m, n + 1)
346 else:
346 else:
347 r = spanset(repo, m, n - 1)
347 r = spanset(repo, m, n - 1)
348 return r & subset
348 return r & subset
349
349
350 def dagrange(repo, subset, x, y):
350 def dagrange(repo, subset, x, y):
351 r = fullreposet(repo)
351 r = fullreposet(repo)
352 xs = _revsbetween(repo, getset(repo, r, x), getset(repo, r, y))
352 xs = _revsbetween(repo, getset(repo, r, x), getset(repo, r, y))
353 return xs & subset
353 return xs & subset
354
354
355 def andset(repo, subset, x, y):
355 def andset(repo, subset, x, y):
356 return getset(repo, getset(repo, subset, x), y)
356 return getset(repo, getset(repo, subset, x), y)
357
357
358 def orset(repo, subset, x, y):
358 def orset(repo, subset, x, y):
359 xl = getset(repo, subset, x)
359 xl = getset(repo, subset, x)
360 yl = getset(repo, subset, y)
360 yl = getset(repo, subset, y)
361 return xl + yl
361 return xl + yl
362
362
363 def notset(repo, subset, x):
363 def notset(repo, subset, x):
364 return subset - getset(repo, subset, x)
364 return subset - getset(repo, subset, x)
365
365
366 def listset(repo, subset, a, b):
366 def listset(repo, subset, a, b):
367 raise error.ParseError(_("can't use a list in this context"))
367 raise error.ParseError(_("can't use a list in this context"))
368
368
369 def func(repo, subset, a, b):
369 def func(repo, subset, a, b):
370 if a[0] == 'symbol' and a[1] in symbols:
370 if a[0] == 'symbol' and a[1] in symbols:
371 return symbols[a[1]](repo, subset, b)
371 return symbols[a[1]](repo, subset, b)
372 raise error.UnknownIdentifier(a[1], symbols.keys())
372 raise error.UnknownIdentifier(a[1], symbols.keys())
373
373
374 # functions
374 # functions
375
375
376 def adds(repo, subset, x):
376 def adds(repo, subset, x):
377 """``adds(pattern)``
377 """``adds(pattern)``
378 Changesets that add a file matching pattern.
378 Changesets that add a file matching pattern.
379
379
380 The pattern without explicit kind like ``glob:`` is expected to be
380 The pattern without explicit kind like ``glob:`` is expected to be
381 relative to the current directory and match against a file or a
381 relative to the current directory and match against a file or a
382 directory.
382 directory.
383 """
383 """
384 # i18n: "adds" is a keyword
384 # i18n: "adds" is a keyword
385 pat = getstring(x, _("adds requires a pattern"))
385 pat = getstring(x, _("adds requires a pattern"))
386 return checkstatus(repo, subset, pat, 1)
386 return checkstatus(repo, subset, pat, 1)
387
387
388 def ancestor(repo, subset, x):
388 def ancestor(repo, subset, x):
389 """``ancestor(*changeset)``
389 """``ancestor(*changeset)``
390 A greatest common ancestor of the changesets.
390 A greatest common ancestor of the changesets.
391
391
392 Accepts 0 or more changesets.
392 Accepts 0 or more changesets.
393 Will return empty list when passed no args.
393 Will return empty list when passed no args.
394 Greatest common ancestor of a single changeset is that changeset.
394 Greatest common ancestor of a single changeset is that changeset.
395 """
395 """
396 # i18n: "ancestor" is a keyword
396 # i18n: "ancestor" is a keyword
397 l = getlist(x)
397 l = getlist(x)
398 rl = fullreposet(repo)
398 rl = fullreposet(repo)
399 anc = None
399 anc = None
400
400
401 # (getset(repo, rl, i) for i in l) generates a list of lists
401 # (getset(repo, rl, i) for i in l) generates a list of lists
402 for revs in (getset(repo, rl, i) for i in l):
402 for revs in (getset(repo, rl, i) for i in l):
403 for r in revs:
403 for r in revs:
404 if anc is None:
404 if anc is None:
405 anc = repo[r]
405 anc = repo[r]
406 else:
406 else:
407 anc = anc.ancestor(repo[r])
407 anc = anc.ancestor(repo[r])
408
408
409 if anc is not None and anc.rev() in subset:
409 if anc is not None and anc.rev() in subset:
410 return baseset([anc.rev()])
410 return baseset([anc.rev()])
411 return baseset()
411 return baseset()
412
412
413 def _ancestors(repo, subset, x, followfirst=False):
413 def _ancestors(repo, subset, x, followfirst=False):
414 heads = getset(repo, fullreposet(repo), x)
414 heads = getset(repo, fullreposet(repo), x)
415 if not heads:
415 if not heads:
416 return baseset()
416 return baseset()
417 s = _revancestors(repo, heads, followfirst)
417 s = _revancestors(repo, heads, followfirst)
418 return subset & s
418 return subset & s
419
419
420 def ancestors(repo, subset, x):
420 def ancestors(repo, subset, x):
421 """``ancestors(set)``
421 """``ancestors(set)``
422 Changesets that are ancestors of a changeset in set.
422 Changesets that are ancestors of a changeset in set.
423 """
423 """
424 return _ancestors(repo, subset, x)
424 return _ancestors(repo, subset, x)
425
425
426 def _firstancestors(repo, subset, x):
426 def _firstancestors(repo, subset, x):
427 # ``_firstancestors(set)``
427 # ``_firstancestors(set)``
428 # Like ``ancestors(set)`` but follows only the first parents.
428 # Like ``ancestors(set)`` but follows only the first parents.
429 return _ancestors(repo, subset, x, followfirst=True)
429 return _ancestors(repo, subset, x, followfirst=True)
430
430
431 def ancestorspec(repo, subset, x, n):
431 def ancestorspec(repo, subset, x, n):
432 """``set~n``
432 """``set~n``
433 Changesets that are the Nth ancestor (first parents only) of a changeset
433 Changesets that are the Nth ancestor (first parents only) of a changeset
434 in set.
434 in set.
435 """
435 """
436 try:
436 try:
437 n = int(n[1])
437 n = int(n[1])
438 except (TypeError, ValueError):
438 except (TypeError, ValueError):
439 raise error.ParseError(_("~ expects a number"))
439 raise error.ParseError(_("~ expects a number"))
440 ps = set()
440 ps = set()
441 cl = repo.changelog
441 cl = repo.changelog
442 for r in getset(repo, fullreposet(repo), x):
442 for r in getset(repo, fullreposet(repo), x):
443 for i in range(n):
443 for i in range(n):
444 r = cl.parentrevs(r)[0]
444 r = cl.parentrevs(r)[0]
445 ps.add(r)
445 ps.add(r)
446 return subset & ps
446 return subset & ps
447
447
448 def author(repo, subset, x):
448 def author(repo, subset, x):
449 """``author(string)``
449 """``author(string)``
450 Alias for ``user(string)``.
450 Alias for ``user(string)``.
451 """
451 """
452 # i18n: "author" is a keyword
452 # i18n: "author" is a keyword
453 n = encoding.lower(getstring(x, _("author requires a string")))
453 n = encoding.lower(getstring(x, _("author requires a string")))
454 kind, pattern, matcher = _substringmatcher(n)
454 kind, pattern, matcher = _substringmatcher(n)
455 return subset.filter(lambda x: matcher(encoding.lower(repo[x].user())))
455 return subset.filter(lambda x: matcher(encoding.lower(repo[x].user())))
456
456
457 def bisect(repo, subset, x):
457 def bisect(repo, subset, x):
458 """``bisect(string)``
458 """``bisect(string)``
459 Changesets marked in the specified bisect status:
459 Changesets marked in the specified bisect status:
460
460
461 - ``good``, ``bad``, ``skip``: csets explicitly marked as good/bad/skip
461 - ``good``, ``bad``, ``skip``: csets explicitly marked as good/bad/skip
462 - ``goods``, ``bads`` : csets topologically good/bad
462 - ``goods``, ``bads`` : csets topologically good/bad
463 - ``range`` : csets taking part in the bisection
463 - ``range`` : csets taking part in the bisection
464 - ``pruned`` : csets that are goods, bads or skipped
464 - ``pruned`` : csets that are goods, bads or skipped
465 - ``untested`` : csets whose fate is yet unknown
465 - ``untested`` : csets whose fate is yet unknown
466 - ``ignored`` : csets ignored due to DAG topology
466 - ``ignored`` : csets ignored due to DAG topology
467 - ``current`` : the cset currently being bisected
467 - ``current`` : the cset currently being bisected
468 """
468 """
469 # i18n: "bisect" is a keyword
469 # i18n: "bisect" is a keyword
470 status = getstring(x, _("bisect requires a string")).lower()
470 status = getstring(x, _("bisect requires a string")).lower()
471 state = set(hbisect.get(repo, status))
471 state = set(hbisect.get(repo, status))
472 return subset & state
472 return subset & state
473
473
474 # Backward-compatibility
474 # Backward-compatibility
475 # - no help entry so that we do not advertise it any more
475 # - no help entry so that we do not advertise it any more
476 def bisected(repo, subset, x):
476 def bisected(repo, subset, x):
477 return bisect(repo, subset, x)
477 return bisect(repo, subset, x)
478
478
479 def bookmark(repo, subset, x):
479 def bookmark(repo, subset, x):
480 """``bookmark([name])``
480 """``bookmark([name])``
481 The named bookmark or all bookmarks.
481 The named bookmark or all bookmarks.
482
482
483 If `name` starts with `re:`, the remainder of the name is treated as
483 If `name` starts with `re:`, the remainder of the name is treated as
484 a regular expression. To match a bookmark that actually starts with `re:`,
484 a regular expression. To match a bookmark that actually starts with `re:`,
485 use the prefix `literal:`.
485 use the prefix `literal:`.
486 """
486 """
487 # i18n: "bookmark" is a keyword
487 # i18n: "bookmark" is a keyword
488 args = getargs(x, 0, 1, _('bookmark takes one or no arguments'))
488 args = getargs(x, 0, 1, _('bookmark takes one or no arguments'))
489 if args:
489 if args:
490 bm = getstring(args[0],
490 bm = getstring(args[0],
491 # i18n: "bookmark" is a keyword
491 # i18n: "bookmark" is a keyword
492 _('the argument to bookmark must be a string'))
492 _('the argument to bookmark must be a string'))
493 kind, pattern, matcher = _stringmatcher(bm)
493 kind, pattern, matcher = _stringmatcher(bm)
494 bms = set()
494 bms = set()
495 if kind == 'literal':
495 if kind == 'literal':
496 bmrev = repo._bookmarks.get(pattern, None)
496 bmrev = repo._bookmarks.get(pattern, None)
497 if not bmrev:
497 if not bmrev:
498 raise error.RepoLookupError(_("bookmark '%s' does not exist")
498 raise error.RepoLookupError(_("bookmark '%s' does not exist")
499 % bm)
499 % bm)
500 bms.add(repo[bmrev].rev())
500 bms.add(repo[bmrev].rev())
501 else:
501 else:
502 matchrevs = set()
502 matchrevs = set()
503 for name, bmrev in repo._bookmarks.iteritems():
503 for name, bmrev in repo._bookmarks.iteritems():
504 if matcher(name):
504 if matcher(name):
505 matchrevs.add(bmrev)
505 matchrevs.add(bmrev)
506 if not matchrevs:
506 if not matchrevs:
507 raise error.RepoLookupError(_("no bookmarks exist"
507 raise error.RepoLookupError(_("no bookmarks exist"
508 " that match '%s'") % pattern)
508 " that match '%s'") % pattern)
509 for bmrev in matchrevs:
509 for bmrev in matchrevs:
510 bms.add(repo[bmrev].rev())
510 bms.add(repo[bmrev].rev())
511 else:
511 else:
512 bms = set([repo[r].rev()
512 bms = set([repo[r].rev()
513 for r in repo._bookmarks.values()])
513 for r in repo._bookmarks.values()])
514 bms -= set([node.nullrev])
514 bms -= set([node.nullrev])
515 return subset & bms
515 return subset & bms
516
516
517 def branch(repo, subset, x):
517 def branch(repo, subset, x):
518 """``branch(string or set)``
518 """``branch(string or set)``
519 All changesets belonging to the given branch or the branches of the given
519 All changesets belonging to the given branch or the branches of the given
520 changesets.
520 changesets.
521
521
522 If `string` starts with `re:`, the remainder of the name is treated as
522 If `string` starts with `re:`, the remainder of the name is treated as
523 a regular expression. To match a branch that actually starts with `re:`,
523 a regular expression. To match a branch that actually starts with `re:`,
524 use the prefix `literal:`.
524 use the prefix `literal:`.
525 """
525 """
526 getbi = repo.revbranchcache().branchinfo
526 getbi = repo.revbranchcache().branchinfo
527
527
528 try:
528 try:
529 b = getstring(x, '')
529 b = getstring(x, '')
530 except error.ParseError:
530 except error.ParseError:
531 # not a string, but another revspec, e.g. tip()
531 # not a string, but another revspec, e.g. tip()
532 pass
532 pass
533 else:
533 else:
534 kind, pattern, matcher = _stringmatcher(b)
534 kind, pattern, matcher = _stringmatcher(b)
535 if kind == 'literal':
535 if kind == 'literal':
536 # note: falls through to the revspec case if no branch with
536 # note: falls through to the revspec case if no branch with
537 # this name exists
537 # this name exists
538 if pattern in repo.branchmap():
538 if pattern in repo.branchmap():
539 return subset.filter(lambda r: matcher(getbi(r)[0]))
539 return subset.filter(lambda r: matcher(getbi(r)[0]))
540 else:
540 else:
541 return subset.filter(lambda r: matcher(getbi(r)[0]))
541 return subset.filter(lambda r: matcher(getbi(r)[0]))
542
542
543 s = getset(repo, fullreposet(repo), x)
543 s = getset(repo, fullreposet(repo), x)
544 b = set()
544 b = set()
545 for r in s:
545 for r in s:
546 b.add(getbi(r)[0])
546 b.add(getbi(r)[0])
547 c = s.__contains__
547 c = s.__contains__
548 return subset.filter(lambda r: c(r) or getbi(r)[0] in b)
548 return subset.filter(lambda r: c(r) or getbi(r)[0] in b)
549
549
550 def bumped(repo, subset, x):
550 def bumped(repo, subset, x):
551 """``bumped()``
551 """``bumped()``
552 Mutable changesets marked as successors of public changesets.
552 Mutable changesets marked as successors of public changesets.
553
553
554 Only non-public and non-obsolete changesets can be `bumped`.
554 Only non-public and non-obsolete changesets can be `bumped`.
555 """
555 """
556 # i18n: "bumped" is a keyword
556 # i18n: "bumped" is a keyword
557 getargs(x, 0, 0, _("bumped takes no arguments"))
557 getargs(x, 0, 0, _("bumped takes no arguments"))
558 bumped = obsmod.getrevs(repo, 'bumped')
558 bumped = obsmod.getrevs(repo, 'bumped')
559 return subset & bumped
559 return subset & bumped
560
560
561 def bundle(repo, subset, x):
561 def bundle(repo, subset, x):
562 """``bundle()``
562 """``bundle()``
563 Changesets in the bundle.
563 Changesets in the bundle.
564
564
565 Bundle must be specified by the -R option."""
565 Bundle must be specified by the -R option."""
566
566
567 try:
567 try:
568 bundlerevs = repo.changelog.bundlerevs
568 bundlerevs = repo.changelog.bundlerevs
569 except AttributeError:
569 except AttributeError:
570 raise util.Abort(_("no bundle provided - specify with -R"))
570 raise util.Abort(_("no bundle provided - specify with -R"))
571 return subset & bundlerevs
571 return subset & bundlerevs
572
572
573 def checkstatus(repo, subset, pat, field):
573 def checkstatus(repo, subset, pat, field):
574 hasset = matchmod.patkind(pat) == 'set'
574 hasset = matchmod.patkind(pat) == 'set'
575
575
576 mcache = [None]
576 mcache = [None]
577 def matches(x):
577 def matches(x):
578 c = repo[x]
578 c = repo[x]
579 if not mcache[0] or hasset:
579 if not mcache[0] or hasset:
580 mcache[0] = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
580 mcache[0] = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
581 m = mcache[0]
581 m = mcache[0]
582 fname = None
582 fname = None
583 if not m.anypats() and len(m.files()) == 1:
583 if not m.anypats() and len(m.files()) == 1:
584 fname = m.files()[0]
584 fname = m.files()[0]
585 if fname is not None:
585 if fname is not None:
586 if fname not in c.files():
586 if fname not in c.files():
587 return False
587 return False
588 else:
588 else:
589 for f in c.files():
589 for f in c.files():
590 if m(f):
590 if m(f):
591 break
591 break
592 else:
592 else:
593 return False
593 return False
594 files = repo.status(c.p1().node(), c.node())[field]
594 files = repo.status(c.p1().node(), c.node())[field]
595 if fname is not None:
595 if fname is not None:
596 if fname in files:
596 if fname in files:
597 return True
597 return True
598 else:
598 else:
599 for f in files:
599 for f in files:
600 if m(f):
600 if m(f):
601 return True
601 return True
602
602
603 return subset.filter(matches)
603 return subset.filter(matches)
604
604
605 def _children(repo, narrow, parentset):
605 def _children(repo, narrow, parentset):
606 cs = set()
606 cs = set()
607 if not parentset:
607 if not parentset:
608 return baseset(cs)
608 return baseset(cs)
609 pr = repo.changelog.parentrevs
609 pr = repo.changelog.parentrevs
610 minrev = min(parentset)
610 minrev = min(parentset)
611 for r in narrow:
611 for r in narrow:
612 if r <= minrev:
612 if r <= minrev:
613 continue
613 continue
614 for p in pr(r):
614 for p in pr(r):
615 if p in parentset:
615 if p in parentset:
616 cs.add(r)
616 cs.add(r)
617 return baseset(cs)
617 return baseset(cs)
618
618
619 def children(repo, subset, x):
619 def children(repo, subset, x):
620 """``children(set)``
620 """``children(set)``
621 Child changesets of changesets in set.
621 Child changesets of changesets in set.
622 """
622 """
623 s = getset(repo, fullreposet(repo), x)
623 s = getset(repo, fullreposet(repo), x)
624 cs = _children(repo, subset, s)
624 cs = _children(repo, subset, s)
625 return subset & cs
625 return subset & cs
626
626
627 def closed(repo, subset, x):
627 def closed(repo, subset, x):
628 """``closed()``
628 """``closed()``
629 Changeset is closed.
629 Changeset is closed.
630 """
630 """
631 # i18n: "closed" is a keyword
631 # i18n: "closed" is a keyword
632 getargs(x, 0, 0, _("closed takes no arguments"))
632 getargs(x, 0, 0, _("closed takes no arguments"))
633 return subset.filter(lambda r: repo[r].closesbranch())
633 return subset.filter(lambda r: repo[r].closesbranch())
634
634
635 def contains(repo, subset, x):
635 def contains(repo, subset, x):
636 """``contains(pattern)``
636 """``contains(pattern)``
637 The revision's manifest contains a file matching pattern (but might not
637 The revision's manifest contains a file matching pattern (but might not
638 modify it). See :hg:`help patterns` for information about file patterns.
638 modify it). See :hg:`help patterns` for information about file patterns.
639
639
640 The pattern without explicit kind like ``glob:`` is expected to be
640 The pattern without explicit kind like ``glob:`` is expected to be
641 relative to the current directory and match against a file exactly
641 relative to the current directory and match against a file exactly
642 for efficiency.
642 for efficiency.
643 """
643 """
644 # i18n: "contains" is a keyword
644 # i18n: "contains" is a keyword
645 pat = getstring(x, _("contains requires a pattern"))
645 pat = getstring(x, _("contains requires a pattern"))
646
646
647 def matches(x):
647 def matches(x):
648 if not matchmod.patkind(pat):
648 if not matchmod.patkind(pat):
649 pats = pathutil.canonpath(repo.root, repo.getcwd(), pat)
649 pats = pathutil.canonpath(repo.root, repo.getcwd(), pat)
650 if pats in repo[x]:
650 if pats in repo[x]:
651 return True
651 return True
652 else:
652 else:
653 c = repo[x]
653 c = repo[x]
654 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
654 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
655 for f in c.manifest():
655 for f in c.manifest():
656 if m(f):
656 if m(f):
657 return True
657 return True
658 return False
658 return False
659
659
660 return subset.filter(matches)
660 return subset.filter(matches)
661
661
662 def converted(repo, subset, x):
662 def converted(repo, subset, x):
663 """``converted([id])``
663 """``converted([id])``
664 Changesets converted from the given identifier in the old repository if
664 Changesets converted from the given identifier in the old repository if
665 present, or all converted changesets if no identifier is specified.
665 present, or all converted changesets if no identifier is specified.
666 """
666 """
667
667
668 # There is exactly no chance of resolving the revision, so do a simple
668 # There is exactly no chance of resolving the revision, so do a simple
669 # string compare and hope for the best
669 # string compare and hope for the best
670
670
671 rev = None
671 rev = None
672 # i18n: "converted" is a keyword
672 # i18n: "converted" is a keyword
673 l = getargs(x, 0, 1, _('converted takes one or no arguments'))
673 l = getargs(x, 0, 1, _('converted takes one or no arguments'))
674 if l:
674 if l:
675 # i18n: "converted" is a keyword
675 # i18n: "converted" is a keyword
676 rev = getstring(l[0], _('converted requires a revision'))
676 rev = getstring(l[0], _('converted requires a revision'))
677
677
678 def _matchvalue(r):
678 def _matchvalue(r):
679 source = repo[r].extra().get('convert_revision', None)
679 source = repo[r].extra().get('convert_revision', None)
680 return source is not None and (rev is None or source.startswith(rev))
680 return source is not None and (rev is None or source.startswith(rev))
681
681
682 return subset.filter(lambda r: _matchvalue(r))
682 return subset.filter(lambda r: _matchvalue(r))
683
683
684 def date(repo, subset, x):
684 def date(repo, subset, x):
685 """``date(interval)``
685 """``date(interval)``
686 Changesets within the interval, see :hg:`help dates`.
686 Changesets within the interval, see :hg:`help dates`.
687 """
687 """
688 # i18n: "date" is a keyword
688 # i18n: "date" is a keyword
689 ds = getstring(x, _("date requires a string"))
689 ds = getstring(x, _("date requires a string"))
690 dm = util.matchdate(ds)
690 dm = util.matchdate(ds)
691 return subset.filter(lambda x: dm(repo[x].date()[0]))
691 return subset.filter(lambda x: dm(repo[x].date()[0]))
692
692
693 def desc(repo, subset, x):
693 def desc(repo, subset, x):
694 """``desc(string)``
694 """``desc(string)``
695 Search commit message for string. The match is case-insensitive.
695 Search commit message for string. The match is case-insensitive.
696 """
696 """
697 # i18n: "desc" is a keyword
697 # i18n: "desc" is a keyword
698 ds = encoding.lower(getstring(x, _("desc requires a string")))
698 ds = encoding.lower(getstring(x, _("desc requires a string")))
699
699
700 def matches(x):
700 def matches(x):
701 c = repo[x]
701 c = repo[x]
702 return ds in encoding.lower(c.description())
702 return ds in encoding.lower(c.description())
703
703
704 return subset.filter(matches)
704 return subset.filter(matches)
705
705
706 def _descendants(repo, subset, x, followfirst=False):
706 def _descendants(repo, subset, x, followfirst=False):
707 roots = getset(repo, fullreposet(repo), x)
707 roots = getset(repo, fullreposet(repo), x)
708 if not roots:
708 if not roots:
709 return baseset()
709 return baseset()
710 s = _revdescendants(repo, roots, followfirst)
710 s = _revdescendants(repo, roots, followfirst)
711
711
712 # Both sets need to be ascending in order to lazily return the union
712 # Both sets need to be ascending in order to lazily return the union
713 # in the correct order.
713 # in the correct order.
714 base = subset & roots
714 base = subset & roots
715 desc = subset & s
715 desc = subset & s
716 result = base + desc
716 result = base + desc
717 if subset.isascending():
717 if subset.isascending():
718 result.sort()
718 result.sort()
719 elif subset.isdescending():
719 elif subset.isdescending():
720 result.sort(reverse=True)
720 result.sort(reverse=True)
721 else:
721 else:
722 result = subset & result
722 result = subset & result
723 return result
723 return result
724
724
725 def descendants(repo, subset, x):
725 def descendants(repo, subset, x):
726 """``descendants(set)``
726 """``descendants(set)``
727 Changesets which are descendants of changesets in set.
727 Changesets which are descendants of changesets in set.
728 """
728 """
729 return _descendants(repo, subset, x)
729 return _descendants(repo, subset, x)
730
730
731 def _firstdescendants(repo, subset, x):
731 def _firstdescendants(repo, subset, x):
732 # ``_firstdescendants(set)``
732 # ``_firstdescendants(set)``
733 # Like ``descendants(set)`` but follows only the first parents.
733 # Like ``descendants(set)`` but follows only the first parents.
734 return _descendants(repo, subset, x, followfirst=True)
734 return _descendants(repo, subset, x, followfirst=True)
735
735
736 def destination(repo, subset, x):
736 def destination(repo, subset, x):
737 """``destination([set])``
737 """``destination([set])``
738 Changesets that were created by a graft, transplant or rebase operation,
738 Changesets that were created by a graft, transplant or rebase operation,
739 with the given revisions specified as the source. Omitting the optional set
739 with the given revisions specified as the source. Omitting the optional set
740 is the same as passing all().
740 is the same as passing all().
741 """
741 """
742 if x is not None:
742 if x is not None:
743 sources = getset(repo, fullreposet(repo), x)
743 sources = getset(repo, fullreposet(repo), x)
744 else:
744 else:
745 sources = fullreposet(repo)
745 sources = fullreposet(repo)
746
746
747 dests = set()
747 dests = set()
748
748
749 # subset contains all of the possible destinations that can be returned, so
749 # subset contains all of the possible destinations that can be returned, so
750 # iterate over them and see if their source(s) were provided in the arg set.
750 # iterate over them and see if their source(s) were provided in the arg set.
751 # Even if the immediate src of r is not in the arg set, src's source (or
751 # Even if the immediate src of r is not in the arg set, src's source (or
752 # further back) may be. Scanning back further than the immediate src allows
752 # further back) may be. Scanning back further than the immediate src allows
753 # transitive transplants and rebases to yield the same results as transitive
753 # transitive transplants and rebases to yield the same results as transitive
754 # grafts.
754 # grafts.
755 for r in subset:
755 for r in subset:
756 src = _getrevsource(repo, r)
756 src = _getrevsource(repo, r)
757 lineage = None
757 lineage = None
758
758
759 while src is not None:
759 while src is not None:
760 if lineage is None:
760 if lineage is None:
761 lineage = list()
761 lineage = list()
762
762
763 lineage.append(r)
763 lineage.append(r)
764
764
765 # The visited lineage is a match if the current source is in the arg
765 # The visited lineage is a match if the current source is in the arg
766 # set. Since every candidate dest is visited by way of iterating
766 # set. Since every candidate dest is visited by way of iterating
767 # subset, any dests further back in the lineage will be tested by a
767 # subset, any dests further back in the lineage will be tested by a
768 # different iteration over subset. Likewise, if the src was already
768 # different iteration over subset. Likewise, if the src was already
769 # selected, the current lineage can be selected without going back
769 # selected, the current lineage can be selected without going back
770 # further.
770 # further.
771 if src in sources or src in dests:
771 if src in sources or src in dests:
772 dests.update(lineage)
772 dests.update(lineage)
773 break
773 break
774
774
775 r = src
775 r = src
776 src = _getrevsource(repo, r)
776 src = _getrevsource(repo, r)
777
777
778 return subset.filter(dests.__contains__)
778 return subset.filter(dests.__contains__)
779
779
780 def divergent(repo, subset, x):
780 def divergent(repo, subset, x):
781 """``divergent()``
781 """``divergent()``
782 Final successors of changesets with an alternative set of final successors.
782 Final successors of changesets with an alternative set of final successors.
783 """
783 """
784 # i18n: "divergent" is a keyword
784 # i18n: "divergent" is a keyword
785 getargs(x, 0, 0, _("divergent takes no arguments"))
785 getargs(x, 0, 0, _("divergent takes no arguments"))
786 divergent = obsmod.getrevs(repo, 'divergent')
786 divergent = obsmod.getrevs(repo, 'divergent')
787 return subset & divergent
787 return subset & divergent
788
788
789 def draft(repo, subset, x):
789 def draft(repo, subset, x):
790 """``draft()``
790 """``draft()``
791 Changeset in draft phase."""
791 Changeset in draft phase."""
792 # i18n: "draft" is a keyword
792 # i18n: "draft" is a keyword
793 getargs(x, 0, 0, _("draft takes no arguments"))
793 getargs(x, 0, 0, _("draft takes no arguments"))
794 phase = repo._phasecache.phase
794 phase = repo._phasecache.phase
795 target = phases.draft
795 target = phases.draft
796 condition = lambda r: phase(repo, r) == target
796 condition = lambda r: phase(repo, r) == target
797 return subset.filter(condition, cache=False)
797 return subset.filter(condition, cache=False)
798
798
799 def extinct(repo, subset, x):
799 def extinct(repo, subset, x):
800 """``extinct()``
800 """``extinct()``
801 Obsolete changesets with obsolete descendants only.
801 Obsolete changesets with obsolete descendants only.
802 """
802 """
803 # i18n: "extinct" is a keyword
803 # i18n: "extinct" is a keyword
804 getargs(x, 0, 0, _("extinct takes no arguments"))
804 getargs(x, 0, 0, _("extinct takes no arguments"))
805 extincts = obsmod.getrevs(repo, 'extinct')
805 extincts = obsmod.getrevs(repo, 'extinct')
806 return subset & extincts
806 return subset & extincts
807
807
808 def extra(repo, subset, x):
808 def extra(repo, subset, x):
809 """``extra(label, [value])``
809 """``extra(label, [value])``
810 Changesets with the given label in the extra metadata, with the given
810 Changesets with the given label in the extra metadata, with the given
811 optional value.
811 optional value.
812
812
813 If `value` starts with `re:`, the remainder of the value is treated as
813 If `value` starts with `re:`, the remainder of the value is treated as
814 a regular expression. To match a value that actually starts with `re:`,
814 a regular expression. To match a value that actually starts with `re:`,
815 use the prefix `literal:`.
815 use the prefix `literal:`.
816 """
816 """
817
817
818 # i18n: "extra" is a keyword
818 # i18n: "extra" is a keyword
819 l = getargs(x, 1, 2, _('extra takes at least 1 and at most 2 arguments'))
819 l = getargs(x, 1, 2, _('extra takes at least 1 and at most 2 arguments'))
820 # i18n: "extra" is a keyword
820 # i18n: "extra" is a keyword
821 label = getstring(l[0], _('first argument to extra must be a string'))
821 label = getstring(l[0], _('first argument to extra must be a string'))
822 value = None
822 value = None
823
823
824 if len(l) > 1:
824 if len(l) > 1:
825 # i18n: "extra" is a keyword
825 # i18n: "extra" is a keyword
826 value = getstring(l[1], _('second argument to extra must be a string'))
826 value = getstring(l[1], _('second argument to extra must be a string'))
827 kind, value, matcher = _stringmatcher(value)
827 kind, value, matcher = _stringmatcher(value)
828
828
829 def _matchvalue(r):
829 def _matchvalue(r):
830 extra = repo[r].extra()
830 extra = repo[r].extra()
831 return label in extra and (value is None or matcher(extra[label]))
831 return label in extra and (value is None or matcher(extra[label]))
832
832
833 return subset.filter(lambda r: _matchvalue(r))
833 return subset.filter(lambda r: _matchvalue(r))
834
834
835 def filelog(repo, subset, x):
835 def filelog(repo, subset, x):
836 """``filelog(pattern)``
836 """``filelog(pattern)``
837 Changesets connected to the specified filelog.
837 Changesets connected to the specified filelog.
838
838
839 For performance reasons, visits only revisions mentioned in the file-level
839 For performance reasons, visits only revisions mentioned in the file-level
840 filelog, rather than filtering through all changesets (much faster, but
840 filelog, rather than filtering through all changesets (much faster, but
841 doesn't include deletes or duplicate changes). For a slower, more accurate
841 doesn't include deletes or duplicate changes). For a slower, more accurate
842 result, use ``file()``.
842 result, use ``file()``.
843
843
844 The pattern without explicit kind like ``glob:`` is expected to be
844 The pattern without explicit kind like ``glob:`` is expected to be
845 relative to the current directory and match against a file exactly
845 relative to the current directory and match against a file exactly
846 for efficiency.
846 for efficiency.
847
847
848 If some linkrev points to revisions filtered by the current repoview, we'll
848 If some linkrev points to revisions filtered by the current repoview, we'll
849 work around it to return a non-filtered value.
849 work around it to return a non-filtered value.
850 """
850 """
851
851
852 # i18n: "filelog" is a keyword
852 # i18n: "filelog" is a keyword
853 pat = getstring(x, _("filelog requires a pattern"))
853 pat = getstring(x, _("filelog requires a pattern"))
854 s = set()
854 s = set()
855 cl = repo.changelog
855 cl = repo.changelog
856
856
857 if not matchmod.patkind(pat):
857 if not matchmod.patkind(pat):
858 f = pathutil.canonpath(repo.root, repo.getcwd(), pat)
858 f = pathutil.canonpath(repo.root, repo.getcwd(), pat)
859 files = [f]
859 files = [f]
860 else:
860 else:
861 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=repo[None])
861 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=repo[None])
862 files = (f for f in repo[None] if m(f))
862 files = (f for f in repo[None] if m(f))
863
863
864 for f in files:
864 for f in files:
865 backrevref = {} # final value for: filerev -> changerev
865 backrevref = {} # final value for: filerev -> changerev
866 lowestchild = {} # lowest known filerev child of a filerev
866 lowestchild = {} # lowest known filerev child of a filerev
867 delayed = [] # filerev with filtered linkrev, for post-processing
867 delayed = [] # filerev with filtered linkrev, for post-processing
868 lowesthead = None # cache for manifest content of all head revisions
868 lowesthead = None # cache for manifest content of all head revisions
869 fl = repo.file(f)
869 fl = repo.file(f)
870 for fr in list(fl):
870 for fr in list(fl):
871 rev = fl.linkrev(fr)
871 rev = fl.linkrev(fr)
872 if rev not in cl:
872 if rev not in cl:
873 # changerev pointed in linkrev is filtered
873 # changerev pointed in linkrev is filtered
874 # record it for post processing.
874 # record it for post processing.
875 delayed.append((fr, rev))
875 delayed.append((fr, rev))
876 continue
876 continue
877 for p in fl.parentrevs(fr):
877 for p in fl.parentrevs(fr):
878 if 0 <= p and p not in lowestchild:
878 if 0 <= p and p not in lowestchild:
879 lowestchild[p] = fr
879 lowestchild[p] = fr
880 backrevref[fr] = rev
880 backrevref[fr] = rev
881 s.add(rev)
881 s.add(rev)
882
882
883 # Post-processing of all filerevs we skipped because they were
883 # Post-processing of all filerevs we skipped because they were
884 # filtered. If such filerevs have known and unfiltered children, this
884 # filtered. If such filerevs have known and unfiltered children, this
885 # means they have an unfiltered appearance out there. We'll use linkrev
885 # means they have an unfiltered appearance out there. We'll use linkrev
886 # adjustment to find one of these appearances. The lowest known child
886 # adjustment to find one of these appearances. The lowest known child
887 # will be used as a starting point because it is the best upper-bound we
887 # will be used as a starting point because it is the best upper-bound we
888 # have.
888 # have.
889 #
889 #
890 # This approach will fail when an unfiltered but linkrev-shadowed
890 # This approach will fail when an unfiltered but linkrev-shadowed
891 # appearance exists in a head changeset without unfiltered filerev
891 # appearance exists in a head changeset without unfiltered filerev
892 # children anywhere.
892 # children anywhere.
893 while delayed:
893 while delayed:
894 # must be a descending iteration. To slowly fill lowest child
894 # must be a descending iteration. To slowly fill lowest child
895 # information that is of potential use by the next item.
895 # information that is of potential use by the next item.
896 fr, rev = delayed.pop()
896 fr, rev = delayed.pop()
897 lkr = rev
897 lkr = rev
898
898
899 child = lowestchild.get(fr)
899 child = lowestchild.get(fr)
900
900
901 if child is None:
901 if child is None:
902 # search for existence of this file revision in a head revision.
902 # search for existence of this file revision in a head revision.
903 # There are three possibilities:
903 # There are three possibilities:
904 # - the revision exists in a head and we can find an
904 # - the revision exists in a head and we can find an
905 # introduction from there,
905 # introduction from there,
906 # - the revision does not exist in a head because it has been
906 # - the revision does not exist in a head because it has been
907 # changed since its introduction: we would have found a child
907 # changed since its introduction: we would have found a child
908 # and be in the other 'else' clause,
908 # and be in the other 'else' clause,
909 # - all versions of the revision are hidden.
909 # - all versions of the revision are hidden.
910 if lowesthead is None:
910 if lowesthead is None:
911 lowesthead = {}
911 lowesthead = {}
912 for h in repo.heads():
912 for h in repo.heads():
913 fnode = repo[h].manifest().get(f)
913 fnode = repo[h].manifest().get(f)
914 if fnode is not None:
914 if fnode is not None:
915 lowesthead[fl.rev(fnode)] = h
915 lowesthead[fl.rev(fnode)] = h
916 headrev = lowesthead.get(fr)
916 headrev = lowesthead.get(fr)
917 if headrev is None:
917 if headrev is None:
918 # content is nowhere unfiltered
918 # content is nowhere unfiltered
919 continue
919 continue
920 rev = repo[headrev][f].introrev()
920 rev = repo[headrev][f].introrev()
921 else:
921 else:
922 # the lowest known child is a good upper bound
922 # the lowest known child is a good upper bound
923 childcrev = backrevref[child]
923 childcrev = backrevref[child]
924 # XXX this does not guarantee returning the lowest
924 # XXX this does not guarantee returning the lowest
925 # introduction of this revision, but this gives a
925 # introduction of this revision, but this gives a
926 # result which is a good start and will fit in most
926 # result which is a good start and will fit in most
927 # cases. We probably need to fix the multiple
927 # cases. We probably need to fix the multiple
928 # introductions case properly (report each
928 # introductions case properly (report each
929 # introduction, even for identical file revisions)
929 # introduction, even for identical file revisions)
930 # once and for all at some point anyway.
930 # once and for all at some point anyway.
931 for p in repo[childcrev][f].parents():
931 for p in repo[childcrev][f].parents():
932 if p.filerev() == fr:
932 if p.filerev() == fr:
933 rev = p.rev()
933 rev = p.rev()
934 break
934 break
935 if rev == lkr: # no shadowed entry found
935 if rev == lkr: # no shadowed entry found
936 # XXX This should never happen unless some manifest points
936 # XXX This should never happen unless some manifest points
937 # to biggish file revisions (like a revision that uses a
937 # to biggish file revisions (like a revision that uses a
938 # parent that never appears in the manifest ancestors)
938 # parent that never appears in the manifest ancestors)
939 continue
939 continue
940
940
941 # Fill the data for the next iteration.
941 # Fill the data for the next iteration.
942 for p in fl.parentrevs(fr):
942 for p in fl.parentrevs(fr):
943 if 0 <= p and p not in lowestchild:
943 if 0 <= p and p not in lowestchild:
944 lowestchild[p] = fr
944 lowestchild[p] = fr
945 backrevref[fr] = rev
945 backrevref[fr] = rev
946 s.add(rev)
946 s.add(rev)
947
947
948 return subset & s
948 return subset & s
949
949
950 def first(repo, subset, x):
950 def first(repo, subset, x):
951 """``first(set, [n])``
951 """``first(set, [n])``
952 An alias for limit().
952 An alias for limit().
953 """
953 """
954 return limit(repo, subset, x)
954 return limit(repo, subset, x)
955
955
956 def _follow(repo, subset, x, name, followfirst=False):
956 def _follow(repo, subset, x, name, followfirst=False):
957 l = getargs(x, 0, 1, _("%s takes no arguments or a filename") % name)
957 l = getargs(x, 0, 1, _("%s takes no arguments or a filename") % name)
958 c = repo['.']
958 c = repo['.']
959 if l:
959 if l:
960 x = getstring(l[0], _("%s expected a filename") % name)
960 x = getstring(l[0], _("%s expected a filename") % name)
961 if x in c:
961 if x in c:
962 cx = c[x]
962 cx = c[x]
963 s = set(ctx.rev() for ctx in cx.ancestors(followfirst=followfirst))
963 s = set(ctx.rev() for ctx in cx.ancestors(followfirst=followfirst))
964 # include the revision responsible for the most recent version
964 # include the revision responsible for the most recent version
965 s.add(cx.introrev())
965 s.add(cx.introrev())
966 else:
966 else:
967 return baseset()
967 return baseset()
968 else:
968 else:
969 s = _revancestors(repo, baseset([c.rev()]), followfirst)
969 s = _revancestors(repo, baseset([c.rev()]), followfirst)
970
970
971 return subset & s
971 return subset & s
972
972
973 def follow(repo, subset, x):
973 def follow(repo, subset, x):
974 """``follow([file])``
974 """``follow([file])``
975 An alias for ``::.`` (ancestors of the working directory's first parent).
975 An alias for ``::.`` (ancestors of the working directory's first parent).
976 If a filename is specified, the history of the given file is followed,
976 If a filename is specified, the history of the given file is followed,
977 including copies.
977 including copies.
978 """
978 """
979 return _follow(repo, subset, x, 'follow')
979 return _follow(repo, subset, x, 'follow')
980
980
981 def _followfirst(repo, subset, x):
981 def _followfirst(repo, subset, x):
982 # ``followfirst([file])``
982 # ``followfirst([file])``
983 # Like ``follow([file])`` but follows only the first parent of
983 # Like ``follow([file])`` but follows only the first parent of
984 # every revision or file revision.
984 # every revision or file revision.
985 return _follow(repo, subset, x, '_followfirst', followfirst=True)
985 return _follow(repo, subset, x, '_followfirst', followfirst=True)
986
986
987 def getall(repo, subset, x):
987 def getall(repo, subset, x):
988 """``all()``
988 """``all()``
989 All changesets, the same as ``0:tip``.
989 All changesets, the same as ``0:tip``.
990 """
990 """
991 # i18n: "all" is a keyword
991 # i18n: "all" is a keyword
992 getargs(x, 0, 0, _("all takes no arguments"))
992 getargs(x, 0, 0, _("all takes no arguments"))
993 return subset & spanset(repo) # drop "null" if any
993 return subset & spanset(repo) # drop "null" if any
994
994
995 def grep(repo, subset, x):
995 def grep(repo, subset, x):
996 """``grep(regex)``
996 """``grep(regex)``
997 Like ``keyword(string)`` but accepts a regex. Use ``grep(r'...')``
997 Like ``keyword(string)`` but accepts a regex. Use ``grep(r'...')``
998 to ensure special escape characters are handled correctly. Unlike
998 to ensure special escape characters are handled correctly. Unlike
999 ``keyword(string)``, the match is case-sensitive.
999 ``keyword(string)``, the match is case-sensitive.
1000 """
1000 """
1001 try:
1001 try:
1002 # i18n: "grep" is a keyword
1002 # i18n: "grep" is a keyword
1003 gr = re.compile(getstring(x, _("grep requires a string")))
1003 gr = re.compile(getstring(x, _("grep requires a string")))
1004 except re.error, e:
1004 except re.error, e:
1005 raise error.ParseError(_('invalid match pattern: %s') % e)
1005 raise error.ParseError(_('invalid match pattern: %s') % e)
1006
1006
1007 def matches(x):
1007 def matches(x):
1008 c = repo[x]
1008 c = repo[x]
1009 for e in c.files() + [c.user(), c.description()]:
1009 for e in c.files() + [c.user(), c.description()]:
1010 if gr.search(e):
1010 if gr.search(e):
1011 return True
1011 return True
1012 return False
1012 return False
1013
1013
1014 return subset.filter(matches)
1014 return subset.filter(matches)
1015
1015
1016 def _matchfiles(repo, subset, x):
1016 def _matchfiles(repo, subset, x):
1017 # _matchfiles takes a revset list of prefixed arguments:
1017 # _matchfiles takes a revset list of prefixed arguments:
1018 #
1018 #
1019 # [p:foo, i:bar, x:baz]
1019 # [p:foo, i:bar, x:baz]
1020 #
1020 #
1021 # builds a match object from them and filters subset. Allowed
1021 # builds a match object from them and filters subset. Allowed
1022 # prefixes are 'p:' for regular patterns, 'i:' for include
1022 # prefixes are 'p:' for regular patterns, 'i:' for include
1023 # patterns and 'x:' for exclude patterns. Use 'r:' prefix to pass
1023 # patterns and 'x:' for exclude patterns. Use 'r:' prefix to pass
1024 # a revision identifier, or the empty string to reference the
1024 # a revision identifier, or the empty string to reference the
1025 # working directory, from which the match object is
1025 # working directory, from which the match object is
1026 # initialized. Use 'd:' to set the default matching mode, default
1026 # initialized. Use 'd:' to set the default matching mode, default
1027 # to 'glob'. At most one 'r:' and 'd:' argument can be passed.
1027 # to 'glob'. At most one 'r:' and 'd:' argument can be passed.
1028
1028
1029 # i18n: "_matchfiles" is a keyword
1029 # i18n: "_matchfiles" is a keyword
1030 l = getargs(x, 1, -1, _("_matchfiles requires at least one argument"))
1030 l = getargs(x, 1, -1, _("_matchfiles requires at least one argument"))
1031 pats, inc, exc = [], [], []
1031 pats, inc, exc = [], [], []
1032 rev, default = None, None
1032 rev, default = None, None
1033 for arg in l:
1033 for arg in l:
1034 # i18n: "_matchfiles" is a keyword
1034 # i18n: "_matchfiles" is a keyword
1035 s = getstring(arg, _("_matchfiles requires string arguments"))
1035 s = getstring(arg, _("_matchfiles requires string arguments"))
1036 prefix, value = s[:2], s[2:]
1036 prefix, value = s[:2], s[2:]
1037 if prefix == 'p:':
1037 if prefix == 'p:':
1038 pats.append(value)
1038 pats.append(value)
1039 elif prefix == 'i:':
1039 elif prefix == 'i:':
1040 inc.append(value)
1040 inc.append(value)
1041 elif prefix == 'x:':
1041 elif prefix == 'x:':
1042 exc.append(value)
1042 exc.append(value)
1043 elif prefix == 'r:':
1043 elif prefix == 'r:':
1044 if rev is not None:
1044 if rev is not None:
1045 # i18n: "_matchfiles" is a keyword
1045 # i18n: "_matchfiles" is a keyword
1046 raise error.ParseError(_('_matchfiles expected at most one '
1046 raise error.ParseError(_('_matchfiles expected at most one '
1047 'revision'))
1047 'revision'))
1048 if value != '': # empty means working directory; leave rev as None
1048 if value != '': # empty means working directory; leave rev as None
1049 rev = value
1049 rev = value
1050 elif prefix == 'd:':
1050 elif prefix == 'd:':
1051 if default is not None:
1051 if default is not None:
1052 # i18n: "_matchfiles" is a keyword
1052 # i18n: "_matchfiles" is a keyword
1053 raise error.ParseError(_('_matchfiles expected at most one '
1053 raise error.ParseError(_('_matchfiles expected at most one '
1054 'default mode'))
1054 'default mode'))
1055 default = value
1055 default = value
1056 else:
1056 else:
1057 # i18n: "_matchfiles" is a keyword
1057 # i18n: "_matchfiles" is a keyword
1058 raise error.ParseError(_('invalid _matchfiles prefix: %s') % prefix)
1058 raise error.ParseError(_('invalid _matchfiles prefix: %s') % prefix)
1059 if not default:
1059 if not default:
1060 default = 'glob'
1060 default = 'glob'
1061
1061
1062 m = matchmod.match(repo.root, repo.getcwd(), pats, include=inc,
1062 m = matchmod.match(repo.root, repo.getcwd(), pats, include=inc,
1063 exclude=exc, ctx=repo[rev], default=default)
1063 exclude=exc, ctx=repo[rev], default=default)
1064
1064
1065 def matches(x):
1065 def matches(x):
1066 for f in repo[x].files():
1066 for f in repo[x].files():
1067 if m(f):
1067 if m(f):
1068 return True
1068 return True
1069 return False
1069 return False
1070
1070
1071 return subset.filter(matches)
1071 return subset.filter(matches)
1072
1072
1073 def hasfile(repo, subset, x):
1073 def hasfile(repo, subset, x):
1074 """``file(pattern)``
1074 """``file(pattern)``
1075 Changesets affecting files matched by pattern.
1075 Changesets affecting files matched by pattern.
1076
1076
1077 For a faster but less accurate result, consider using ``filelog()``
1077 For a faster but less accurate result, consider using ``filelog()``
1078 instead.
1078 instead.
1079
1079
1080 This predicate uses ``glob:`` as the default kind of pattern.
1080 This predicate uses ``glob:`` as the default kind of pattern.
1081 """
1081 """
1082 # i18n: "file" is a keyword
1082 # i18n: "file" is a keyword
1083 pat = getstring(x, _("file requires a pattern"))
1083 pat = getstring(x, _("file requires a pattern"))
1084 return _matchfiles(repo, subset, ('string', 'p:' + pat))
1084 return _matchfiles(repo, subset, ('string', 'p:' + pat))
1085
1085
1086 def head(repo, subset, x):
1086 def head(repo, subset, x):
1087 """``head()``
1087 """``head()``
1088 Changeset is a named branch head.
1088 Changeset is a named branch head.
1089 """
1089 """
1090 # i18n: "head" is a keyword
1090 # i18n: "head" is a keyword
1091 getargs(x, 0, 0, _("head takes no arguments"))
1091 getargs(x, 0, 0, _("head takes no arguments"))
1092 hs = set()
1092 hs = set()
1093 for b, ls in repo.branchmap().iteritems():
1093 for b, ls in repo.branchmap().iteritems():
1094 hs.update(repo[h].rev() for h in ls)
1094 hs.update(repo[h].rev() for h in ls)
1095 return baseset(hs).filter(subset.__contains__)
1095 return baseset(hs).filter(subset.__contains__)
1096
1096
1097 def heads(repo, subset, x):
1097 def heads(repo, subset, x):
1098 """``heads(set)``
1098 """``heads(set)``
1099 Members of set with no children in set.
1099 Members of set with no children in set.
1100 """
1100 """
1101 s = getset(repo, subset, x)
1101 s = getset(repo, subset, x)
1102 ps = parents(repo, subset, x)
1102 ps = parents(repo, subset, x)
1103 return s - ps
1103 return s - ps
1104
1104
1105 def hidden(repo, subset, x):
1105 def hidden(repo, subset, x):
1106 """``hidden()``
1106 """``hidden()``
1107 Hidden changesets.
1107 Hidden changesets.
1108 """
1108 """
1109 # i18n: "hidden" is a keyword
1109 # i18n: "hidden" is a keyword
1110 getargs(x, 0, 0, _("hidden takes no arguments"))
1110 getargs(x, 0, 0, _("hidden takes no arguments"))
1111 hiddenrevs = repoview.filterrevs(repo, 'visible')
1111 hiddenrevs = repoview.filterrevs(repo, 'visible')
1112 return subset & hiddenrevs
1112 return subset & hiddenrevs
1113
1113
1114 def keyword(repo, subset, x):
1114 def keyword(repo, subset, x):
1115 """``keyword(string)``
1115 """``keyword(string)``
1116 Search commit message, user name, and names of changed files for
1116 Search commit message, user name, and names of changed files for
1117 string. The match is case-insensitive.
1117 string. The match is case-insensitive.
1118 """
1118 """
1119 # i18n: "keyword" is a keyword
1119 # i18n: "keyword" is a keyword
1120 kw = encoding.lower(getstring(x, _("keyword requires a string")))
1120 kw = encoding.lower(getstring(x, _("keyword requires a string")))
1121
1121
1122 def matches(r):
1122 def matches(r):
1123 c = repo[r]
1123 c = repo[r]
1124 return any(kw in encoding.lower(t) for t in c.files() + [c.user(),
1124 return any(kw in encoding.lower(t) for t in c.files() + [c.user(),
1125 c.description()])
1125 c.description()])
1126
1126
1127 return subset.filter(matches)
1127 return subset.filter(matches)
1128
1128
1129 def limit(repo, subset, x):
1129 def limit(repo, subset, x):
1130 """``limit(set, [n])``
1130 """``limit(set, [n])``
1131 First n members of set, defaulting to 1.
1131 First n members of set, defaulting to 1.
1132 """
1132 """
1133 # i18n: "limit" is a keyword
1133 # i18n: "limit" is a keyword
1134 l = getargs(x, 1, 2, _("limit requires one or two arguments"))
1134 l = getargs(x, 1, 2, _("limit requires one or two arguments"))
1135 try:
1135 try:
1136 lim = 1
1136 lim = 1
1137 if len(l) == 2:
1137 if len(l) == 2:
1138 # i18n: "limit" is a keyword
1138 # i18n: "limit" is a keyword
1139 lim = int(getstring(l[1], _("limit requires a number")))
1139 lim = int(getstring(l[1], _("limit requires a number")))
1140 except (TypeError, ValueError):
1140 except (TypeError, ValueError):
1141 # i18n: "limit" is a keyword
1141 # i18n: "limit" is a keyword
1142 raise error.ParseError(_("limit expects a number"))
1142 raise error.ParseError(_("limit expects a number"))
1143 ss = subset
1143 ss = subset
1144 os = getset(repo, fullreposet(repo), l[0])
1144 os = getset(repo, fullreposet(repo), l[0])
1145 result = []
1145 result = []
1146 it = iter(os)
1146 it = iter(os)
1147 for x in xrange(lim):
1147 for x in xrange(lim):
1148 y = next(it, None)
1148 y = next(it, None)
1149 if y is None:
1149 if y is None:
1150 break
1150 break
1151 elif y in ss:
1151 elif y in ss:
1152 result.append(y)
1152 result.append(y)
1153 return baseset(result)
1153 return baseset(result)
1154
1154
1155 def last(repo, subset, x):
1155 def last(repo, subset, x):
1156 """``last(set, [n])``
1156 """``last(set, [n])``
1157 Last n members of set, defaulting to 1.
1157 Last n members of set, defaulting to 1.
1158 """
1158 """
1159 # i18n: "last" is a keyword
1159 # i18n: "last" is a keyword
1160 l = getargs(x, 1, 2, _("last requires one or two arguments"))
1160 l = getargs(x, 1, 2, _("last requires one or two arguments"))
1161 try:
1161 try:
1162 lim = 1
1162 lim = 1
1163 if len(l) == 2:
1163 if len(l) == 2:
1164 # i18n: "last" is a keyword
1164 # i18n: "last" is a keyword
1165 lim = int(getstring(l[1], _("last requires a number")))
1165 lim = int(getstring(l[1], _("last requires a number")))
1166 except (TypeError, ValueError):
1166 except (TypeError, ValueError):
1167 # i18n: "last" is a keyword
1167 # i18n: "last" is a keyword
1168 raise error.ParseError(_("last expects a number"))
1168 raise error.ParseError(_("last expects a number"))
1169 ss = subset
1169 ss = subset
1170 os = getset(repo, fullreposet(repo), l[0])
1170 os = getset(repo, fullreposet(repo), l[0])
1171 os.reverse()
1171 os.reverse()
1172 result = []
1172 result = []
1173 it = iter(os)
1173 it = iter(os)
1174 for x in xrange(lim):
1174 for x in xrange(lim):
1175 y = next(it, None)
1175 y = next(it, None)
1176 if y is None:
1176 if y is None:
1177 break
1177 break
1178 elif y in ss:
1178 elif y in ss:
1179 result.append(y)
1179 result.append(y)
1180 return baseset(result)
1180 return baseset(result)
1181
1181
1182 def maxrev(repo, subset, x):
1182 def maxrev(repo, subset, x):
1183 """``max(set)``
1183 """``max(set)``
1184 Changeset with highest revision number in set.
1184 Changeset with highest revision number in set.
1185 """
1185 """
1186 os = getset(repo, fullreposet(repo), x)
1186 os = getset(repo, fullreposet(repo), x)
1187 if os:
1187 if os:
1188 m = os.max()
1188 m = os.max()
1189 if m in subset:
1189 if m in subset:
1190 return baseset([m])
1190 return baseset([m])
1191 return baseset()
1191 return baseset()
1192
1192
1193 def merge(repo, subset, x):
1193 def merge(repo, subset, x):
1194 """``merge()``
1194 """``merge()``
1195 Changeset is a merge changeset.
1195 Changeset is a merge changeset.
1196 """
1196 """
1197 # i18n: "merge" is a keyword
1197 # i18n: "merge" is a keyword
1198 getargs(x, 0, 0, _("merge takes no arguments"))
1198 getargs(x, 0, 0, _("merge takes no arguments"))
1199 cl = repo.changelog
1199 cl = repo.changelog
1200 return subset.filter(lambda r: cl.parentrevs(r)[1] != -1)
1200 return subset.filter(lambda r: cl.parentrevs(r)[1] != -1)
1201
1201
1202 def branchpoint(repo, subset, x):
1202 def branchpoint(repo, subset, x):
1203 """``branchpoint()``
1203 """``branchpoint()``
1204 Changesets with more than one child.
1204 Changesets with more than one child.
1205 """
1205 """
1206 # i18n: "branchpoint" is a keyword
1206 # i18n: "branchpoint" is a keyword
1207 getargs(x, 0, 0, _("branchpoint takes no arguments"))
1207 getargs(x, 0, 0, _("branchpoint takes no arguments"))
1208 cl = repo.changelog
1208 cl = repo.changelog
1209 if not subset:
1209 if not subset:
1210 return baseset()
1210 return baseset()
1211 baserev = min(subset)
1211 baserev = min(subset)
1212 parentscount = [0]*(len(repo) - baserev)
1212 parentscount = [0]*(len(repo) - baserev)
1213 for r in cl.revs(start=baserev + 1):
1213 for r in cl.revs(start=baserev + 1):
1214 for p in cl.parentrevs(r):
1214 for p in cl.parentrevs(r):
1215 if p >= baserev:
1215 if p >= baserev:
1216 parentscount[p - baserev] += 1
1216 parentscount[p - baserev] += 1
1217 return subset.filter(lambda r: parentscount[r - baserev] > 1)
1217 return subset.filter(lambda r: parentscount[r - baserev] > 1)
1218
1218
1219 def minrev(repo, subset, x):
1219 def minrev(repo, subset, x):
1220 """``min(set)``
1220 """``min(set)``
1221 Changeset with lowest revision number in set.
1221 Changeset with lowest revision number in set.
1222 """
1222 """
1223 os = getset(repo, fullreposet(repo), x)
1223 os = getset(repo, fullreposet(repo), x)
1224 if os:
1224 if os:
1225 m = os.min()
1225 m = os.min()
1226 if m in subset:
1226 if m in subset:
1227 return baseset([m])
1227 return baseset([m])
1228 return baseset()
1228 return baseset()
1229
1229
1230 def modifies(repo, subset, x):
1230 def modifies(repo, subset, x):
1231 """``modifies(pattern)``
1231 """``modifies(pattern)``
1232 Changesets modifying files matched by pattern.
1232 Changesets modifying files matched by pattern.
1233
1233
1234 The pattern without explicit kind like ``glob:`` is expected to be
1234 The pattern without explicit kind like ``glob:`` is expected to be
1235 relative to the current directory and match against a file or a
1235 relative to the current directory and match against a file or a
1236 directory.
1236 directory.
1237 """
1237 """
1238 # i18n: "modifies" is a keyword
1238 # i18n: "modifies" is a keyword
1239 pat = getstring(x, _("modifies requires a pattern"))
1239 pat = getstring(x, _("modifies requires a pattern"))
1240 return checkstatus(repo, subset, pat, 0)
1240 return checkstatus(repo, subset, pat, 0)
1241
1241
1242 def named(repo, subset, x):
1242 def named(repo, subset, x):
1243 """``named(namespace)``
1243 """``named(namespace)``
1244 The changesets in a given namespace.
1244 The changesets in a given namespace.
1245
1245
1246 If `namespace` starts with `re:`, the remainder of the string is treated as
1246 If `namespace` starts with `re:`, the remainder of the string is treated as
1247 a regular expression. To match a namespace that actually starts with `re:`,
1247 a regular expression. To match a namespace that actually starts with `re:`,
1248 use the prefix `literal:`.
1248 use the prefix `literal:`.
1249 """
1249 """
1250 # i18n: "named" is a keyword
1250 # i18n: "named" is a keyword
1251 args = getargs(x, 1, 1, _('named requires a namespace argument'))
1251 args = getargs(x, 1, 1, _('named requires a namespace argument'))
1252
1252
1253 ns = getstring(args[0],
1253 ns = getstring(args[0],
1254 # i18n: "named" is a keyword
1254 # i18n: "named" is a keyword
1255 _('the argument to named must be a string'))
1255 _('the argument to named must be a string'))
1256 kind, pattern, matcher = _stringmatcher(ns)
1256 kind, pattern, matcher = _stringmatcher(ns)
1257 namespaces = set()
1257 namespaces = set()
1258 if kind == 'literal':
1258 if kind == 'literal':
1259 if pattern not in repo.names:
1259 if pattern not in repo.names:
1260 raise error.RepoLookupError(_("namespace '%s' does not exist")
1260 raise error.RepoLookupError(_("namespace '%s' does not exist")
1261 % ns)
1261 % ns)
1262 namespaces.add(repo.names[pattern])
1262 namespaces.add(repo.names[pattern])
1263 else:
1263 else:
1264 for name, ns in repo.names.iteritems():
1264 for name, ns in repo.names.iteritems():
1265 if matcher(name):
1265 if matcher(name):
1266 namespaces.add(ns)
1266 namespaces.add(ns)
1267 if not namespaces:
1267 if not namespaces:
1268 raise error.RepoLookupError(_("no namespace exists"
1268 raise error.RepoLookupError(_("no namespace exists"
1269 " that match '%s'") % pattern)
1269 " that match '%s'") % pattern)
1270
1270
1271 names = set()
1271 names = set()
1272 for ns in namespaces:
1272 for ns in namespaces:
1273 for name in ns.listnames(repo):
1273 for name in ns.listnames(repo):
1274 if name not in ns.deprecated:
1274 if name not in ns.deprecated:
1275 names.update(repo[n].rev() for n in ns.nodes(repo, name))
1275 names.update(repo[n].rev() for n in ns.nodes(repo, name))
1276
1276
1277 names -= set([node.nullrev])
1277 names -= set([node.nullrev])
1278 return subset & names
1278 return subset & names
1279
1279
1280 def node_(repo, subset, x):
1280 def node_(repo, subset, x):
1281 """``id(string)``
1281 """``id(string)``
1282 Revision non-ambiguously specified by the given hex string prefix.
1282 Revision non-ambiguously specified by the given hex string prefix.
1283 """
1283 """
1284 # i18n: "id" is a keyword
1284 # i18n: "id" is a keyword
1285 l = getargs(x, 1, 1, _("id requires one argument"))
1285 l = getargs(x, 1, 1, _("id requires one argument"))
1286 # i18n: "id" is a keyword
1286 # i18n: "id" is a keyword
1287 n = getstring(l[0], _("id requires a string"))
1287 n = getstring(l[0], _("id requires a string"))
1288 if len(n) == 40:
1288 if len(n) == 40:
1289 try:
1289 try:
1290 rn = repo.changelog.rev(node.bin(n))
1290 rn = repo.changelog.rev(node.bin(n))
1291 except (LookupError, TypeError):
1291 except (LookupError, TypeError):
1292 rn = None
1292 rn = None
1293 else:
1293 else:
1294 rn = None
1294 rn = None
1295 pm = repo.changelog._partialmatch(n)
1295 pm = repo.changelog._partialmatch(n)
1296 if pm is not None:
1296 if pm is not None:
1297 rn = repo.changelog.rev(pm)
1297 rn = repo.changelog.rev(pm)
1298
1298
1299 if rn is None:
1299 if rn is None:
1300 return baseset()
1300 return baseset()
1301 result = baseset([rn])
1301 result = baseset([rn])
1302 return result & subset
1302 return result & subset
1303
1303
1304 def obsolete(repo, subset, x):
1304 def obsolete(repo, subset, x):
1305 """``obsolete()``
1305 """``obsolete()``
1306 Mutable changeset with a newer version."""
1306 Mutable changeset with a newer version."""
1307 # i18n: "obsolete" is a keyword
1307 # i18n: "obsolete" is a keyword
1308 getargs(x, 0, 0, _("obsolete takes no arguments"))
1308 getargs(x, 0, 0, _("obsolete takes no arguments"))
1309 obsoletes = obsmod.getrevs(repo, 'obsolete')
1309 obsoletes = obsmod.getrevs(repo, 'obsolete')
1310 return subset & obsoletes
1310 return subset & obsoletes
1311
1311
1312 def only(repo, subset, x):
1312 def only(repo, subset, x):
1313 """``only(set, [set])``
1313 """``only(set, [set])``
1314 Changesets that are ancestors of the first set that are not ancestors
1314 Changesets that are ancestors of the first set that are not ancestors
1315 of any other head in the repo. If a second set is specified, the result
1315 of any other head in the repo. If a second set is specified, the result
1316 is ancestors of the first set that are not ancestors of the second set
1316 is ancestors of the first set that are not ancestors of the second set
1317 (i.e. ::<set1> - ::<set2>).
1317 (i.e. ::<set1> - ::<set2>).
1318 """
1318 """
1319 cl = repo.changelog
1319 cl = repo.changelog
1320 # i18n: "only" is a keyword
1320 # i18n: "only" is a keyword
1321 args = getargs(x, 1, 2, _('only takes one or two arguments'))
1321 args = getargs(x, 1, 2, _('only takes one or two arguments'))
1322 include = getset(repo, fullreposet(repo), args[0])
1322 include = getset(repo, fullreposet(repo), args[0])
1323 if len(args) == 1:
1323 if len(args) == 1:
1324 if not include:
1324 if not include:
1325 return baseset()
1325 return baseset()
1326
1326
1327 descendants = set(_revdescendants(repo, include, False))
1327 descendants = set(_revdescendants(repo, include, False))
1328 exclude = [rev for rev in cl.headrevs()
1328 exclude = [rev for rev in cl.headrevs()
1329 if not rev in descendants and not rev in include]
1329 if not rev in descendants and not rev in include]
1330 else:
1330 else:
1331 exclude = getset(repo, fullreposet(repo), args[1])
1331 exclude = getset(repo, fullreposet(repo), args[1])
1332
1332
1333 results = set(cl.findmissingrevs(common=exclude, heads=include))
1333 results = set(cl.findmissingrevs(common=exclude, heads=include))
1334 return subset & results
1334 return subset & results
1335
1335
1336 def origin(repo, subset, x):
1336 def origin(repo, subset, x):
1337 """``origin([set])``
1337 """``origin([set])``
1338 Changesets that were specified as a source for the grafts, transplants or
1338 Changesets that were specified as a source for the grafts, transplants or
1339 rebases that created the given revisions. Omitting the optional set is the
1339 rebases that created the given revisions. Omitting the optional set is the
1340 same as passing all(). If a changeset created by these operations is itself
1340 same as passing all(). If a changeset created by these operations is itself
1341 specified as a source for one of these operations, only the source changeset
1341 specified as a source for one of these operations, only the source changeset
1342 for the first operation is selected.
1342 for the first operation is selected.
1343 """
1343 """
1344 if x is not None:
1344 if x is not None:
1345 dests = getset(repo, fullreposet(repo), x)
1345 dests = getset(repo, fullreposet(repo), x)
1346 else:
1346 else:
1347 dests = fullreposet(repo)
1347 dests = fullreposet(repo)
1348
1348
1349 def _firstsrc(rev):
1349 def _firstsrc(rev):
1350 src = _getrevsource(repo, rev)
1350 src = _getrevsource(repo, rev)
1351 if src is None:
1351 if src is None:
1352 return None
1352 return None
1353
1353
1354 while True:
1354 while True:
1355 prev = _getrevsource(repo, src)
1355 prev = _getrevsource(repo, src)
1356
1356
1357 if prev is None:
1357 if prev is None:
1358 return src
1358 return src
1359 src = prev
1359 src = prev
1360
1360
1361 o = set([_firstsrc(r) for r in dests])
1361 o = set([_firstsrc(r) for r in dests])
1362 o -= set([None])
1362 o -= set([None])
1363 return subset & o
1363 return subset & o
1364
1364
1365 def outgoing(repo, subset, x):
1365 def outgoing(repo, subset, x):
1366 """``outgoing([path])``
1366 """``outgoing([path])``
1367 Changesets not found in the specified destination repository, or the
1367 Changesets not found in the specified destination repository, or the
1368 default push location.
1368 default push location.
1369 """
1369 """
1370 # Avoid cycles.
1370 # Avoid cycles.
1371 import discovery
1371 import discovery
1372 import hg
1372 import hg
1373 # i18n: "outgoing" is a keyword
1373 # i18n: "outgoing" is a keyword
1374 l = getargs(x, 0, 1, _("outgoing takes one or no arguments"))
1374 l = getargs(x, 0, 1, _("outgoing takes one or no arguments"))
1375 # i18n: "outgoing" is a keyword
1375 # i18n: "outgoing" is a keyword
1376 dest = l and getstring(l[0], _("outgoing requires a repository path")) or ''
1376 dest = l and getstring(l[0], _("outgoing requires a repository path")) or ''
1377 dest = repo.ui.expandpath(dest or 'default-push', dest or 'default')
1377 dest = repo.ui.expandpath(dest or 'default-push', dest or 'default')
1378 dest, branches = hg.parseurl(dest)
1378 dest, branches = hg.parseurl(dest)
1379 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1379 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1380 if revs:
1380 if revs:
1381 revs = [repo.lookup(rev) for rev in revs]
1381 revs = [repo.lookup(rev) for rev in revs]
1382 other = hg.peer(repo, {}, dest)
1382 other = hg.peer(repo, {}, dest)
1383 repo.ui.pushbuffer()
1383 repo.ui.pushbuffer()
1384 outgoing = discovery.findcommonoutgoing(repo, other, onlyheads=revs)
1384 outgoing = discovery.findcommonoutgoing(repo, other, onlyheads=revs)
1385 repo.ui.popbuffer()
1385 repo.ui.popbuffer()
1386 cl = repo.changelog
1386 cl = repo.changelog
1387 o = set([cl.rev(r) for r in outgoing.missing])
1387 o = set([cl.rev(r) for r in outgoing.missing])
1388 return subset & o
1388 return subset & o
1389
1389
1390 def p1(repo, subset, x):
1390 def p1(repo, subset, x):
1391 """``p1([set])``
1391 """``p1([set])``
1392 First parent of changesets in set, or the working directory.
1392 First parent of changesets in set, or the working directory.
1393 """
1393 """
1394 if x is None:
1394 if x is None:
1395 p = repo[x].p1().rev()
1395 p = repo[x].p1().rev()
1396 if p >= 0:
1396 if p >= 0:
1397 return subset & baseset([p])
1397 return subset & baseset([p])
1398 return baseset()
1398 return baseset()
1399
1399
1400 ps = set()
1400 ps = set()
1401 cl = repo.changelog
1401 cl = repo.changelog
1402 for r in getset(repo, fullreposet(repo), x):
1402 for r in getset(repo, fullreposet(repo), x):
1403 ps.add(cl.parentrevs(r)[0])
1403 ps.add(cl.parentrevs(r)[0])
1404 ps -= set([node.nullrev])
1404 ps -= set([node.nullrev])
1405 return subset & ps
1405 return subset & ps
1406
1406
1407 def p2(repo, subset, x):
1407 def p2(repo, subset, x):
1408 """``p2([set])``
1408 """``p2([set])``
1409 Second parent of changesets in set, or the working directory.
1409 Second parent of changesets in set, or the working directory.
1410 """
1410 """
1411 if x is None:
1411 if x is None:
1412 ps = repo[x].parents()
1412 ps = repo[x].parents()
1413 try:
1413 try:
1414 p = ps[1].rev()
1414 p = ps[1].rev()
1415 if p >= 0:
1415 if p >= 0:
1416 return subset & baseset([p])
1416 return subset & baseset([p])
1417 return baseset()
1417 return baseset()
1418 except IndexError:
1418 except IndexError:
1419 return baseset()
1419 return baseset()
1420
1420
1421 ps = set()
1421 ps = set()
1422 cl = repo.changelog
1422 cl = repo.changelog
1423 for r in getset(repo, fullreposet(repo), x):
1423 for r in getset(repo, fullreposet(repo), x):
1424 ps.add(cl.parentrevs(r)[1])
1424 ps.add(cl.parentrevs(r)[1])
1425 ps -= set([node.nullrev])
1425 ps -= set([node.nullrev])
1426 return subset & ps
1426 return subset & ps
1427
1427
1428 def parents(repo, subset, x):
1428 def parents(repo, subset, x):
1429 """``parents([set])``
1429 """``parents([set])``
1430 The set of all parents for all changesets in set, or the working directory.
1430 The set of all parents for all changesets in set, or the working directory.
1431 """
1431 """
1432 if x is None:
1432 if x is None:
1433 ps = set(p.rev() for p in repo[x].parents())
1433 ps = set(p.rev() for p in repo[x].parents())
1434 else:
1434 else:
1435 ps = set()
1435 ps = set()
1436 cl = repo.changelog
1436 cl = repo.changelog
1437 for r in getset(repo, fullreposet(repo), x):
1437 for r in getset(repo, fullreposet(repo), x):
1438 ps.update(cl.parentrevs(r))
1438 ps.update(cl.parentrevs(r))
1439 ps -= set([node.nullrev])
1439 ps -= set([node.nullrev])
1440 return subset & ps
1440 return subset & ps
1441
1441
1442 def parentspec(repo, subset, x, n):
1442 def parentspec(repo, subset, x, n):
1443 """``set^0``
1443 """``set^0``
1444 The set.
1444 The set.
1445 ``set^1`` (or ``set^``), ``set^2``
1445 ``set^1`` (or ``set^``), ``set^2``
1446 First or second parent, respectively, of all changesets in set.
1446 First or second parent, respectively, of all changesets in set.
1447 """
1447 """
1448 try:
1448 try:
1449 n = int(n[1])
1449 n = int(n[1])
1450 if n not in (0, 1, 2):
1450 if n not in (0, 1, 2):
1451 raise ValueError
1451 raise ValueError
1452 except (TypeError, ValueError):
1452 except (TypeError, ValueError):
1453 raise error.ParseError(_("^ expects a number 0, 1, or 2"))
1453 raise error.ParseError(_("^ expects a number 0, 1, or 2"))
1454 ps = set()
1454 ps = set()
1455 cl = repo.changelog
1455 cl = repo.changelog
1456 for r in getset(repo, fullreposet(repo), x):
1456 for r in getset(repo, fullreposet(repo), x):
1457 if n == 0:
1457 if n == 0:
1458 ps.add(r)
1458 ps.add(r)
1459 elif n == 1:
1459 elif n == 1:
1460 ps.add(cl.parentrevs(r)[0])
1460 ps.add(cl.parentrevs(r)[0])
1461 elif n == 2:
1461 elif n == 2:
1462 parents = cl.parentrevs(r)
1462 parents = cl.parentrevs(r)
1463 if len(parents) > 1:
1463 if len(parents) > 1:
1464 ps.add(parents[1])
1464 ps.add(parents[1])
1465 return subset & ps
1465 return subset & ps
1466
1466
1467 def present(repo, subset, x):
1467 def present(repo, subset, x):
1468 """``present(set)``
1468 """``present(set)``
1469 An empty set, if any revision in set isn't found; otherwise,
1469 An empty set, if any revision in set isn't found; otherwise,
1470 all revisions in set.
1470 all revisions in set.
1471
1471
1472 If any of specified revisions is not present in the local repository,
1472 If any of specified revisions is not present in the local repository,
1473 the query is normally aborted. But this predicate allows the query
1473 the query is normally aborted. But this predicate allows the query
1474 to continue even in such cases.
1474 to continue even in such cases.
1475 """
1475 """
1476 try:
1476 try:
1477 return getset(repo, subset, x)
1477 return getset(repo, subset, x)
1478 except error.RepoLookupError:
1478 except error.RepoLookupError:
1479 return baseset()
1479 return baseset()
1480
1480
1481 # for internal use
1481 # for internal use
1482 def _notpublic(repo, subset, x):
1482 def _notpublic(repo, subset, x):
1483 getargs(x, 0, 0, "_notpublic takes no arguments")
1483 getargs(x, 0, 0, "_notpublic takes no arguments")
1484 if repo._phasecache._phasesets:
1484 if repo._phasecache._phasesets:
1485 s = set()
1485 s = set()
1486 for u in repo._phasecache._phasesets[1:]:
1486 for u in repo._phasecache._phasesets[1:]:
1487 s.update(u)
1487 s.update(u)
1488 return subset & s
1488 return subset & s
1489 else:
1489 else:
1490 phase = repo._phasecache.phase
1490 phase = repo._phasecache.phase
1491 target = phases.public
1491 target = phases.public
1492 condition = lambda r: phase(repo, r) != target
1492 condition = lambda r: phase(repo, r) != target
1493 return subset.filter(condition, cache=False)
1493 return subset.filter(condition, cache=False)
1494
1494
1495 def public(repo, subset, x):
1495 def public(repo, subset, x):
1496 """``public()``
1496 """``public()``
1497 Changeset in public phase."""
1497 Changeset in public phase."""
1498 # i18n: "public" is a keyword
1498 # i18n: "public" is a keyword
1499 getargs(x, 0, 0, _("public takes no arguments"))
1499 getargs(x, 0, 0, _("public takes no arguments"))
1500 phase = repo._phasecache.phase
1500 phase = repo._phasecache.phase
1501 target = phases.public
1501 target = phases.public
1502 condition = lambda r: phase(repo, r) == target
1502 condition = lambda r: phase(repo, r) == target
1503 return subset.filter(condition, cache=False)
1503 return subset.filter(condition, cache=False)
1504
1504
1505 def remote(repo, subset, x):
1505 def remote(repo, subset, x):
1506 """``remote([id [,path]])``
1506 """``remote([id [,path]])``
1507 Local revision that corresponds to the given identifier in a
1507 Local revision that corresponds to the given identifier in a
1508 remote repository, if present. Here, the '.' identifier is a
1508 remote repository, if present. Here, the '.' identifier is a
1509 synonym for the current local branch.
1509 synonym for the current local branch.
1510 """
1510 """
1511
1511
1512 import hg # avoid start-up nasties
1512 import hg # avoid start-up nasties
1513 # i18n: "remote" is a keyword
1513 # i18n: "remote" is a keyword
1514 l = getargs(x, 0, 2, _("remote takes one, two or no arguments"))
1514 l = getargs(x, 0, 2, _("remote takes one, two or no arguments"))
1515
1515
1516 q = '.'
1516 q = '.'
1517 if len(l) > 0:
1517 if len(l) > 0:
1518 # i18n: "remote" is a keyword
1518 # i18n: "remote" is a keyword
1519 q = getstring(l[0], _("remote requires a string id"))
1519 q = getstring(l[0], _("remote requires a string id"))
1520 if q == '.':
1520 if q == '.':
1521 q = repo['.'].branch()
1521 q = repo['.'].branch()
1522
1522
1523 dest = ''
1523 dest = ''
1524 if len(l) > 1:
1524 if len(l) > 1:
1525 # i18n: "remote" is a keyword
1525 # i18n: "remote" is a keyword
1526 dest = getstring(l[1], _("remote requires a repository path"))
1526 dest = getstring(l[1], _("remote requires a repository path"))
1527 dest = repo.ui.expandpath(dest or 'default')
1527 dest = repo.ui.expandpath(dest or 'default')
1528 dest, branches = hg.parseurl(dest)
1528 dest, branches = hg.parseurl(dest)
1529 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1529 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1530 if revs:
1530 if revs:
1531 revs = [repo.lookup(rev) for rev in revs]
1531 revs = [repo.lookup(rev) for rev in revs]
1532 other = hg.peer(repo, {}, dest)
1532 other = hg.peer(repo, {}, dest)
1533 n = other.lookup(q)
1533 n = other.lookup(q)
1534 if n in repo:
1534 if n in repo:
1535 r = repo[n].rev()
1535 r = repo[n].rev()
1536 if r in subset:
1536 if r in subset:
1537 return baseset([r])
1537 return baseset([r])
1538 return baseset()
1538 return baseset()
1539
1539
1540 def removes(repo, subset, x):
1540 def removes(repo, subset, x):
1541 """``removes(pattern)``
1541 """``removes(pattern)``
1542 Changesets which remove files matching pattern.
1542 Changesets which remove files matching pattern.
1543
1543
1544 The pattern without explicit kind like ``glob:`` is expected to be
1544 The pattern without explicit kind like ``glob:`` is expected to be
1545 relative to the current directory and match against a file or a
1545 relative to the current directory and match against a file or a
1546 directory.
1546 directory.
1547 """
1547 """
1548 # i18n: "removes" is a keyword
1548 # i18n: "removes" is a keyword
1549 pat = getstring(x, _("removes requires a pattern"))
1549 pat = getstring(x, _("removes requires a pattern"))
1550 return checkstatus(repo, subset, pat, 2)
1550 return checkstatus(repo, subset, pat, 2)
1551
1551
1552 def rev(repo, subset, x):
1552 def rev(repo, subset, x):
1553 """``rev(number)``
1553 """``rev(number)``
1554 Revision with the given numeric identifier.
1554 Revision with the given numeric identifier.
1555 """
1555 """
1556 # i18n: "rev" is a keyword
1556 # i18n: "rev" is a keyword
1557 l = getargs(x, 1, 1, _("rev requires one argument"))
1557 l = getargs(x, 1, 1, _("rev requires one argument"))
1558 try:
1558 try:
1559 # i18n: "rev" is a keyword
1559 # i18n: "rev" is a keyword
1560 l = int(getstring(l[0], _("rev requires a number")))
1560 l = int(getstring(l[0], _("rev requires a number")))
1561 except (TypeError, ValueError):
1561 except (TypeError, ValueError):
1562 # i18n: "rev" is a keyword
1562 # i18n: "rev" is a keyword
1563 raise error.ParseError(_("rev expects a number"))
1563 raise error.ParseError(_("rev expects a number"))
1564 if l not in repo.changelog and l != node.nullrev:
1564 if l not in repo.changelog and l != node.nullrev:
1565 return baseset()
1565 return baseset()
1566 return subset & baseset([l])
1566 return subset & baseset([l])
1567
1567
1568 def matching(repo, subset, x):
1568 def matching(repo, subset, x):
1569 """``matching(revision [, field])``
1569 """``matching(revision [, field])``
1570 Changesets in which a given set of fields match the set of fields in the
1570 Changesets in which a given set of fields match the set of fields in the
1571 selected revision or set.
1571 selected revision or set.
1572
1572
1573 To match more than one field pass the list of fields to match separated
1573 To match more than one field pass the list of fields to match separated
1574 by spaces (e.g. ``author description``).
1574 by spaces (e.g. ``author description``).
1575
1575
1576 Valid fields are most regular revision fields and some special fields.
1576 Valid fields are most regular revision fields and some special fields.
1577
1577
1578 Regular revision fields are ``description``, ``author``, ``branch``,
1578 Regular revision fields are ``description``, ``author``, ``branch``,
1579 ``date``, ``files``, ``phase``, ``parents``, ``substate``, ``user``
1579 ``date``, ``files``, ``phase``, ``parents``, ``substate``, ``user``
1580 and ``diff``.
1580 and ``diff``.
1581 Note that ``author`` and ``user`` are synonyms. ``diff`` refers to the
1581 Note that ``author`` and ``user`` are synonyms. ``diff`` refers to the
1582 contents of the revision. Two revisions matching their ``diff`` will
1582 contents of the revision. Two revisions matching their ``diff`` will
1583 also match their ``files``.
1583 also match their ``files``.
1584
1584
1585 Special fields are ``summary`` and ``metadata``:
1585 Special fields are ``summary`` and ``metadata``:
1586 ``summary`` matches the first line of the description.
1586 ``summary`` matches the first line of the description.
1587 ``metadata`` is equivalent to matching ``description user date``
1587 ``metadata`` is equivalent to matching ``description user date``
1588 (i.e. it matches the main metadata fields).
1588 (i.e. it matches the main metadata fields).
1589
1589
1590 ``metadata`` is the default field which is used when no fields are
1590 ``metadata`` is the default field which is used when no fields are
1591 specified. You can match more than one field at a time.
1591 specified. You can match more than one field at a time.
1592 """
1592 """
1593 # i18n: "matching" is a keyword
1593 # i18n: "matching" is a keyword
1594 l = getargs(x, 1, 2, _("matching takes 1 or 2 arguments"))
1594 l = getargs(x, 1, 2, _("matching takes 1 or 2 arguments"))
1595
1595
1596 revs = getset(repo, fullreposet(repo), l[0])
1596 revs = getset(repo, fullreposet(repo), l[0])
1597
1597
1598 fieldlist = ['metadata']
1598 fieldlist = ['metadata']
1599 if len(l) > 1:
1599 if len(l) > 1:
1600 fieldlist = getstring(l[1],
1600 fieldlist = getstring(l[1],
1601 # i18n: "matching" is a keyword
1601 # i18n: "matching" is a keyword
1602 _("matching requires a string "
1602 _("matching requires a string "
1603 "as its second argument")).split()
1603 "as its second argument")).split()
1604
1604
1605 # Make sure that there are no repeated fields,
1605 # Make sure that there are no repeated fields,
1606 # expand the 'special' 'metadata' field type
1606 # expand the 'special' 'metadata' field type
1607 # and check the 'files' whenever we check the 'diff'
1607 # and check the 'files' whenever we check the 'diff'
1608 fields = []
1608 fields = []
1609 for field in fieldlist:
1609 for field in fieldlist:
1610 if field == 'metadata':
1610 if field == 'metadata':
1611 fields += ['user', 'description', 'date']
1611 fields += ['user', 'description', 'date']
1612 elif field == 'diff':
1612 elif field == 'diff':
1613 # a revision matching the diff must also match the files
1613 # a revision matching the diff must also match the files
1614 # since matching the diff is very costly, make sure to
1614 # since matching the diff is very costly, make sure to
1615 # also match the files first
1615 # also match the files first
1616 fields += ['files', 'diff']
1616 fields += ['files', 'diff']
1617 else:
1617 else:
1618 if field == 'author':
1618 if field == 'author':
1619 field = 'user'
1619 field = 'user'
1620 fields.append(field)
1620 fields.append(field)
1621 fields = set(fields)
1621 fields = set(fields)
1622 if 'summary' in fields and 'description' in fields:
1622 if 'summary' in fields and 'description' in fields:
1623 # If a revision matches its description it also matches its summary
1623 # If a revision matches its description it also matches its summary
1624 fields.discard('summary')
1624 fields.discard('summary')
1625
1625
1626 # We may want to match more than one field
1626 # We may want to match more than one field
1627 # Not all fields take the same amount of time to be matched
1627 # Not all fields take the same amount of time to be matched
1628 # Sort the selected fields in order of increasing matching cost
1628 # Sort the selected fields in order of increasing matching cost
1629 fieldorder = ['phase', 'parents', 'user', 'date', 'branch', 'summary',
1629 fieldorder = ['phase', 'parents', 'user', 'date', 'branch', 'summary',
1630 'files', 'description', 'substate', 'diff']
1630 'files', 'description', 'substate', 'diff']
1631 def fieldkeyfunc(f):
1631 def fieldkeyfunc(f):
1632 try:
1632 try:
1633 return fieldorder.index(f)
1633 return fieldorder.index(f)
1634 except ValueError:
1634 except ValueError:
1635 # assume an unknown field is very costly
1635 # assume an unknown field is very costly
1636 return len(fieldorder)
1636 return len(fieldorder)
1637 fields = list(fields)
1637 fields = list(fields)
1638 fields.sort(key=fieldkeyfunc)
1638 fields.sort(key=fieldkeyfunc)
1639
1639
1640 # Each field will be matched with its own "getfield" function
1640 # Each field will be matched with its own "getfield" function
1641 # which will be added to the getfieldfuncs array of functions
1641 # which will be added to the getfieldfuncs array of functions
1642 getfieldfuncs = []
1642 getfieldfuncs = []
1643 _funcs = {
1643 _funcs = {
1644 'user': lambda r: repo[r].user(),
1644 'user': lambda r: repo[r].user(),
1645 'branch': lambda r: repo[r].branch(),
1645 'branch': lambda r: repo[r].branch(),
1646 'date': lambda r: repo[r].date(),
1646 'date': lambda r: repo[r].date(),
1647 'description': lambda r: repo[r].description(),
1647 'description': lambda r: repo[r].description(),
1648 'files': lambda r: repo[r].files(),
1648 'files': lambda r: repo[r].files(),
1649 'parents': lambda r: repo[r].parents(),
1649 'parents': lambda r: repo[r].parents(),
1650 'phase': lambda r: repo[r].phase(),
1650 'phase': lambda r: repo[r].phase(),
1651 'substate': lambda r: repo[r].substate,
1651 'substate': lambda r: repo[r].substate,
1652 'summary': lambda r: repo[r].description().splitlines()[0],
1652 'summary': lambda r: repo[r].description().splitlines()[0],
1653 'diff': lambda r: list(repo[r].diff(git=True),)
1653 'diff': lambda r: list(repo[r].diff(git=True),)
1654 }
1654 }
1655 for info in fields:
1655 for info in fields:
1656 getfield = _funcs.get(info, None)
1656 getfield = _funcs.get(info, None)
1657 if getfield is None:
1657 if getfield is None:
1658 raise error.ParseError(
1658 raise error.ParseError(
1659 # i18n: "matching" is a keyword
1659 # i18n: "matching" is a keyword
1660 _("unexpected field name passed to matching: %s") % info)
1660 _("unexpected field name passed to matching: %s") % info)
1661 getfieldfuncs.append(getfield)
1661 getfieldfuncs.append(getfield)
1662 # convert the getfield array of functions into a "getinfo" function
1662 # convert the getfield array of functions into a "getinfo" function
1663 # which returns an array of field values (or a single value if there
1663 # which returns an array of field values (or a single value if there
1664 # is only one field to match)
1664 # is only one field to match)
1665 getinfo = lambda r: [f(r) for f in getfieldfuncs]
1665 getinfo = lambda r: [f(r) for f in getfieldfuncs]
1666
1666
1667 def matches(x):
1667 def matches(x):
1668 for rev in revs:
1668 for rev in revs:
1669 target = getinfo(rev)
1669 target = getinfo(rev)
1670 match = True
1670 match = True
1671 for n, f in enumerate(getfieldfuncs):
1671 for n, f in enumerate(getfieldfuncs):
1672 if target[n] != f(x):
1672 if target[n] != f(x):
1673 match = False
1673 match = False
1674 if match:
1674 if match:
1675 return True
1675 return True
1676 return False
1676 return False
1677
1677
1678 return subset.filter(matches)
1678 return subset.filter(matches)
1679
1679
1680 def reverse(repo, subset, x):
1680 def reverse(repo, subset, x):
1681 """``reverse(set)``
1681 """``reverse(set)``
1682 Reverse order of set.
1682 Reverse order of set.
1683 """
1683 """
1684 l = getset(repo, subset, x)
1684 l = getset(repo, subset, x)
1685 l.reverse()
1685 l.reverse()
1686 return l
1686 return l
1687
1687
1688 def roots(repo, subset, x):
1688 def roots(repo, subset, x):
1689 """``roots(set)``
1689 """``roots(set)``
1690 Changesets in set with no parent changeset in set.
1690 Changesets in set with no parent changeset in set.
1691 """
1691 """
1692 s = getset(repo, fullreposet(repo), x)
1692 s = getset(repo, fullreposet(repo), x)
1693 subset = subset & s# baseset([r for r in s if r in subset])
1693 subset = subset & s# baseset([r for r in s if r in subset])
1694 cs = _children(repo, subset, s)
1694 cs = _children(repo, subset, s)
1695 return subset - cs
1695 return subset - cs
1696
1696
1697 def secret(repo, subset, x):
1697 def secret(repo, subset, x):
1698 """``secret()``
1698 """``secret()``
1699 Changeset in secret phase."""
1699 Changeset in secret phase."""
1700 # i18n: "secret" is a keyword
1700 # i18n: "secret" is a keyword
1701 getargs(x, 0, 0, _("secret takes no arguments"))
1701 getargs(x, 0, 0, _("secret takes no arguments"))
1702 phase = repo._phasecache.phase
1702 phase = repo._phasecache.phase
1703 target = phases.secret
1703 target = phases.secret
1704 condition = lambda r: phase(repo, r) == target
1704 condition = lambda r: phase(repo, r) == target
1705 return subset.filter(condition, cache=False)
1705 return subset.filter(condition, cache=False)
1706
1706
1707 def sort(repo, subset, x):
1707 def sort(repo, subset, x):
1708 """``sort(set[, [-]key...])``
1708 """``sort(set[, [-]key...])``
1709 Sort set by keys. The default sort order is ascending, specify a key
1709 Sort set by keys. The default sort order is ascending, specify a key
1710 as ``-key`` to sort in descending order.
1710 as ``-key`` to sort in descending order.
1711
1711
1712 The keys can be:
1712 The keys can be:
1713
1713
1714 - ``rev`` for the revision number,
1714 - ``rev`` for the revision number,
1715 - ``branch`` for the branch name,
1715 - ``branch`` for the branch name,
1716 - ``desc`` for the commit message (description),
1716 - ``desc`` for the commit message (description),
1717 - ``user`` for user name (``author`` can be used as an alias),
1717 - ``user`` for user name (``author`` can be used as an alias),
1718 - ``date`` for the commit date
1718 - ``date`` for the commit date
1719 """
1719 """
1720 # i18n: "sort" is a keyword
1720 # i18n: "sort" is a keyword
1721 l = getargs(x, 1, 2, _("sort requires one or two arguments"))
1721 l = getargs(x, 1, 2, _("sort requires one or two arguments"))
1722 keys = "rev"
1722 keys = "rev"
1723 if len(l) == 2:
1723 if len(l) == 2:
1724 # i18n: "sort" is a keyword
1724 # i18n: "sort" is a keyword
1725 keys = getstring(l[1], _("sort spec must be a string"))
1725 keys = getstring(l[1], _("sort spec must be a string"))
1726
1726
1727 s = l[0]
1727 s = l[0]
1728 keys = keys.split()
1728 keys = keys.split()
1729 l = []
1729 l = []
1730 def invert(s):
1730 def invert(s):
1731 return "".join(chr(255 - ord(c)) for c in s)
1731 return "".join(chr(255 - ord(c)) for c in s)
1732 revs = getset(repo, subset, s)
1732 revs = getset(repo, subset, s)
1733 if keys == ["rev"]:
1733 if keys == ["rev"]:
1734 revs.sort()
1734 revs.sort()
1735 return revs
1735 return revs
1736 elif keys == ["-rev"]:
1736 elif keys == ["-rev"]:
1737 revs.sort(reverse=True)
1737 revs.sort(reverse=True)
1738 return revs
1738 return revs
1739 for r in revs:
1739 for r in revs:
1740 c = repo[r]
1740 c = repo[r]
1741 e = []
1741 e = []
1742 for k in keys:
1742 for k in keys:
1743 if k == 'rev':
1743 if k == 'rev':
1744 e.append(r)
1744 e.append(r)
1745 elif k == '-rev':
1745 elif k == '-rev':
1746 e.append(-r)
1746 e.append(-r)
1747 elif k == 'branch':
1747 elif k == 'branch':
1748 e.append(c.branch())
1748 e.append(c.branch())
1749 elif k == '-branch':
1749 elif k == '-branch':
1750 e.append(invert(c.branch()))
1750 e.append(invert(c.branch()))
1751 elif k == 'desc':
1751 elif k == 'desc':
1752 e.append(c.description())
1752 e.append(c.description())
1753 elif k == '-desc':
1753 elif k == '-desc':
1754 e.append(invert(c.description()))
1754 e.append(invert(c.description()))
1755 elif k in 'user author':
1755 elif k in 'user author':
1756 e.append(c.user())
1756 e.append(c.user())
1757 elif k in '-user -author':
1757 elif k in '-user -author':
1758 e.append(invert(c.user()))
1758 e.append(invert(c.user()))
1759 elif k == 'date':
1759 elif k == 'date':
1760 e.append(c.date()[0])
1760 e.append(c.date()[0])
1761 elif k == '-date':
1761 elif k == '-date':
1762 e.append(-c.date()[0])
1762 e.append(-c.date()[0])
1763 else:
1763 else:
1764 raise error.ParseError(_("unknown sort key %r") % k)
1764 raise error.ParseError(_("unknown sort key %r") % k)
1765 e.append(r)
1765 e.append(r)
1766 l.append(e)
1766 l.append(e)
1767 l.sort()
1767 l.sort()
1768 return baseset([e[-1] for e in l])
1768 return baseset([e[-1] for e in l])
1769
1769
1770 def subrepo(repo, subset, x):
1770 def subrepo(repo, subset, x):
1771 """``subrepo([pattern])``
1771 """``subrepo([pattern])``
1772 Changesets that add, modify or remove the given subrepo. If no subrepo
1772 Changesets that add, modify or remove the given subrepo. If no subrepo
1773 pattern is named, any subrepo changes are returned.
1773 pattern is named, any subrepo changes are returned.
1774 """
1774 """
1775 # i18n: "subrepo" is a keyword
1775 # i18n: "subrepo" is a keyword
1776 args = getargs(x, 0, 1, _('subrepo takes at most one argument'))
1776 args = getargs(x, 0, 1, _('subrepo takes at most one argument'))
1777 if len(args) != 0:
1777 if len(args) != 0:
1778 pat = getstring(args[0], _("subrepo requires a pattern"))
1778 pat = getstring(args[0], _("subrepo requires a pattern"))
1779
1779
1780 m = matchmod.exact(repo.root, repo.root, ['.hgsubstate'])
1780 m = matchmod.exact(repo.root, repo.root, ['.hgsubstate'])
1781
1781
1782 def submatches(names):
1782 def submatches(names):
1783 k, p, m = _stringmatcher(pat)
1783 k, p, m = _stringmatcher(pat)
1784 for name in names:
1784 for name in names:
1785 if m(name):
1785 if m(name):
1786 yield name
1786 yield name
1787
1787
1788 def matches(x):
1788 def matches(x):
1789 c = repo[x]
1789 c = repo[x]
1790 s = repo.status(c.p1().node(), c.node(), match=m)
1790 s = repo.status(c.p1().node(), c.node(), match=m)
1791
1791
1792 if len(args) == 0:
1792 if len(args) == 0:
1793 return s.added or s.modified or s.removed
1793 return s.added or s.modified or s.removed
1794
1794
1795 if s.added:
1795 if s.added:
1796 return any(submatches(c.substate.keys()))
1796 return any(submatches(c.substate.keys()))
1797
1797
1798 if s.modified:
1798 if s.modified:
1799 subs = set(c.p1().substate.keys())
1799 subs = set(c.p1().substate.keys())
1800 subs.update(c.substate.keys())
1800 subs.update(c.substate.keys())
1801
1801
1802 for path in submatches(subs):
1802 for path in submatches(subs):
1803 if c.p1().substate.get(path) != c.substate.get(path):
1803 if c.p1().substate.get(path) != c.substate.get(path):
1804 return True
1804 return True
1805
1805
1806 if s.removed:
1806 if s.removed:
1807 return any(submatches(c.p1().substate.keys()))
1807 return any(submatches(c.p1().substate.keys()))
1808
1808
1809 return False
1809 return False
1810
1810
1811 return subset.filter(matches)
1811 return subset.filter(matches)
1812
1812
1813 def _stringmatcher(pattern):
1813 def _stringmatcher(pattern):
1814 """
1814 """
1815 accepts a string, possibly starting with 're:' or 'literal:' prefix.
1815 accepts a string, possibly starting with 're:' or 'literal:' prefix.
1816 returns the matcher name, pattern, and matcher function.
1816 returns the matcher name, pattern, and matcher function.
1817 missing or unknown prefixes are treated as literal matches.
1817 missing or unknown prefixes are treated as literal matches.
1818
1818
1819 helper for tests:
1819 helper for tests:
1820 >>> def test(pattern, *tests):
1820 >>> def test(pattern, *tests):
1821 ... kind, pattern, matcher = _stringmatcher(pattern)
1821 ... kind, pattern, matcher = _stringmatcher(pattern)
1822 ... return (kind, pattern, [bool(matcher(t)) for t in tests])
1822 ... return (kind, pattern, [bool(matcher(t)) for t in tests])
1823
1823
1824 exact matching (no prefix):
1824 exact matching (no prefix):
1825 >>> test('abcdefg', 'abc', 'def', 'abcdefg')
1825 >>> test('abcdefg', 'abc', 'def', 'abcdefg')
1826 ('literal', 'abcdefg', [False, False, True])
1826 ('literal', 'abcdefg', [False, False, True])
1827
1827
1828 regex matching ('re:' prefix)
1828 regex matching ('re:' prefix)
1829 >>> test('re:a.+b', 'nomatch', 'fooadef', 'fooadefbar')
1829 >>> test('re:a.+b', 'nomatch', 'fooadef', 'fooadefbar')
1830 ('re', 'a.+b', [False, False, True])
1830 ('re', 'a.+b', [False, False, True])
1831
1831
1832 force exact matches ('literal:' prefix)
1832 force exact matches ('literal:' prefix)
1833 >>> test('literal:re:foobar', 'foobar', 're:foobar')
1833 >>> test('literal:re:foobar', 'foobar', 're:foobar')
1834 ('literal', 're:foobar', [False, True])
1834 ('literal', 're:foobar', [False, True])
1835
1835
1836 unknown prefixes are ignored and treated as literals
1836 unknown prefixes are ignored and treated as literals
1837 >>> test('foo:bar', 'foo', 'bar', 'foo:bar')
1837 >>> test('foo:bar', 'foo', 'bar', 'foo:bar')
1838 ('literal', 'foo:bar', [False, False, True])
1838 ('literal', 'foo:bar', [False, False, True])
1839 """
1839 """
1840 if pattern.startswith('re:'):
1840 if pattern.startswith('re:'):
1841 pattern = pattern[3:]
1841 pattern = pattern[3:]
1842 try:
1842 try:
1843 regex = re.compile(pattern)
1843 regex = re.compile(pattern)
1844 except re.error, e:
1844 except re.error, e:
1845 raise error.ParseError(_('invalid regular expression: %s')
1845 raise error.ParseError(_('invalid regular expression: %s')
1846 % e)
1846 % e)
1847 return 're', pattern, regex.search
1847 return 're', pattern, regex.search
1848 elif pattern.startswith('literal:'):
1848 elif pattern.startswith('literal:'):
1849 pattern = pattern[8:]
1849 pattern = pattern[8:]
1850 return 'literal', pattern, pattern.__eq__
1850 return 'literal', pattern, pattern.__eq__
1851
1851
1852 def _substringmatcher(pattern):
1852 def _substringmatcher(pattern):
1853 kind, pattern, matcher = _stringmatcher(pattern)
1853 kind, pattern, matcher = _stringmatcher(pattern)
1854 if kind == 'literal':
1854 if kind == 'literal':
1855 matcher = lambda s: pattern in s
1855 matcher = lambda s: pattern in s
1856 return kind, pattern, matcher
1856 return kind, pattern, matcher
1857
1857
1858 def tag(repo, subset, x):
1858 def tag(repo, subset, x):
1859 """``tag([name])``
1859 """``tag([name])``
1860 The specified tag by name, or all tagged revisions if no name is given.
1860 The specified tag by name, or all tagged revisions if no name is given.
1861
1861
1862 If `name` starts with `re:`, the remainder of the name is treated as
1862 If `name` starts with `re:`, the remainder of the name is treated as
1863 a regular expression. To match a tag that actually starts with `re:`,
1863 a regular expression. To match a tag that actually starts with `re:`,
1864 use the prefix `literal:`.
1864 use the prefix `literal:`.
1865 """
1865 """
1866 # i18n: "tag" is a keyword
1866 # i18n: "tag" is a keyword
1867 args = getargs(x, 0, 1, _("tag takes one or no arguments"))
1867 args = getargs(x, 0, 1, _("tag takes one or no arguments"))
1868 cl = repo.changelog
1868 cl = repo.changelog
1869 if args:
1869 if args:
1870 pattern = getstring(args[0],
1870 pattern = getstring(args[0],
1871 # i18n: "tag" is a keyword
1871 # i18n: "tag" is a keyword
1872 _('the argument to tag must be a string'))
1872 _('the argument to tag must be a string'))
1873 kind, pattern, matcher = _stringmatcher(pattern)
1873 kind, pattern, matcher = _stringmatcher(pattern)
1874 if kind == 'literal':
1874 if kind == 'literal':
1875 # avoid resolving all tags
1875 # avoid resolving all tags
1876 tn = repo._tagscache.tags.get(pattern, None)
1876 tn = repo._tagscache.tags.get(pattern, None)
1877 if tn is None:
1877 if tn is None:
1878 raise error.RepoLookupError(_("tag '%s' does not exist")
1878 raise error.RepoLookupError(_("tag '%s' does not exist")
1879 % pattern)
1879 % pattern)
1880 s = set([repo[tn].rev()])
1880 s = set([repo[tn].rev()])
1881 else:
1881 else:
1882 s = set([cl.rev(n) for t, n in repo.tagslist() if matcher(t)])
1882 s = set([cl.rev(n) for t, n in repo.tagslist() if matcher(t)])
1883 else:
1883 else:
1884 s = set([cl.rev(n) for t, n in repo.tagslist() if t != 'tip'])
1884 s = set([cl.rev(n) for t, n in repo.tagslist() if t != 'tip'])
1885 return subset & s
1885 return subset & s
1886
1886
1887 def tagged(repo, subset, x):
1887 def tagged(repo, subset, x):
1888 return tag(repo, subset, x)
1888 return tag(repo, subset, x)
1889
1889
1890 def unstable(repo, subset, x):
1890 def unstable(repo, subset, x):
1891 """``unstable()``
1891 """``unstable()``
1892 Non-obsolete changesets with obsolete ancestors.
1892 Non-obsolete changesets with obsolete ancestors.
1893 """
1893 """
1894 # i18n: "unstable" is a keyword
1894 # i18n: "unstable" is a keyword
1895 getargs(x, 0, 0, _("unstable takes no arguments"))
1895 getargs(x, 0, 0, _("unstable takes no arguments"))
1896 unstables = obsmod.getrevs(repo, 'unstable')
1896 unstables = obsmod.getrevs(repo, 'unstable')
1897 return subset & unstables
1897 return subset & unstables
1898
1898
1899
1899
1900 def user(repo, subset, x):
1900 def user(repo, subset, x):
1901 """``user(string)``
1901 """``user(string)``
1902 User name contains string. The match is case-insensitive.
1902 User name contains string. The match is case-insensitive.
1903
1903
1904 If `string` starts with `re:`, the remainder of the string is treated as
1904 If `string` starts with `re:`, the remainder of the string is treated as
1905 a regular expression. To match a user that actually contains `re:`, use
1905 a regular expression. To match a user that actually contains `re:`, use
1906 the prefix `literal:`.
1906 the prefix `literal:`.
1907 """
1907 """
1908 return author(repo, subset, x)
1908 return author(repo, subset, x)
1909
1909
1910 # experimental
1910 # experimental
1911 def wdir(repo, subset, x):
1911 def wdir(repo, subset, x):
1912 # i18n: "wdir" is a keyword
1912 # i18n: "wdir" is a keyword
1913 getargs(x, 0, 0, _("wdir takes no arguments"))
1913 getargs(x, 0, 0, _("wdir takes no arguments"))
1914 if None in subset:
1914 if None in subset:
1915 return baseset([None])
1915 return baseset([None])
1916 return baseset()
1916 return baseset()
1917
1917
1918 # for internal use
1918 # for internal use
1919 def _list(repo, subset, x):
1919 def _list(repo, subset, x):
1920 s = getstring(x, "internal error")
1920 s = getstring(x, "internal error")
1921 if not s:
1921 if not s:
1922 return baseset()
1922 return baseset()
1923 ls = [repo[r].rev() for r in s.split('\0')]
1923 ls = [repo[r].rev() for r in s.split('\0')]
1924 s = subset
1924 s = subset
1925 return baseset([r for r in ls if r in s])
1925 return baseset([r for r in ls if r in s])
1926
1926
1927 # for internal use
1927 # for internal use
1928 def _intlist(repo, subset, x):
1928 def _intlist(repo, subset, x):
1929 s = getstring(x, "internal error")
1929 s = getstring(x, "internal error")
1930 if not s:
1930 if not s:
1931 return baseset()
1931 return baseset()
1932 ls = [int(r) for r in s.split('\0')]
1932 ls = [int(r) for r in s.split('\0')]
1933 s = subset
1933 s = subset
1934 return baseset([r for r in ls if r in s])
1934 return baseset([r for r in ls if r in s])
1935
1935
1936 # for internal use
1936 # for internal use
1937 def _hexlist(repo, subset, x):
1937 def _hexlist(repo, subset, x):
1938 s = getstring(x, "internal error")
1938 s = getstring(x, "internal error")
1939 if not s:
1939 if not s:
1940 return baseset()
1940 return baseset()
1941 cl = repo.changelog
1941 cl = repo.changelog
1942 ls = [cl.rev(node.bin(r)) for r in s.split('\0')]
1942 ls = [cl.rev(node.bin(r)) for r in s.split('\0')]
1943 s = subset
1943 s = subset
1944 return baseset([r for r in ls if r in s])
1944 return baseset([r for r in ls if r in s])
1945
1945
1946 symbols = {
1946 symbols = {
1947 "adds": adds,
1947 "adds": adds,
1948 "all": getall,
1948 "all": getall,
1949 "ancestor": ancestor,
1949 "ancestor": ancestor,
1950 "ancestors": ancestors,
1950 "ancestors": ancestors,
1951 "_firstancestors": _firstancestors,
1951 "_firstancestors": _firstancestors,
1952 "author": author,
1952 "author": author,
1953 "bisect": bisect,
1953 "bisect": bisect,
1954 "bisected": bisected,
1954 "bisected": bisected,
1955 "bookmark": bookmark,
1955 "bookmark": bookmark,
1956 "branch": branch,
1956 "branch": branch,
1957 "branchpoint": branchpoint,
1957 "branchpoint": branchpoint,
1958 "bumped": bumped,
1958 "bumped": bumped,
1959 "bundle": bundle,
1959 "bundle": bundle,
1960 "children": children,
1960 "children": children,
1961 "closed": closed,
1961 "closed": closed,
1962 "contains": contains,
1962 "contains": contains,
1963 "converted": converted,
1963 "converted": converted,
1964 "date": date,
1964 "date": date,
1965 "desc": desc,
1965 "desc": desc,
1966 "descendants": descendants,
1966 "descendants": descendants,
1967 "_firstdescendants": _firstdescendants,
1967 "_firstdescendants": _firstdescendants,
1968 "destination": destination,
1968 "destination": destination,
1969 "divergent": divergent,
1969 "divergent": divergent,
1970 "draft": draft,
1970 "draft": draft,
1971 "extinct": extinct,
1971 "extinct": extinct,
1972 "extra": extra,
1972 "extra": extra,
1973 "file": hasfile,
1973 "file": hasfile,
1974 "filelog": filelog,
1974 "filelog": filelog,
1975 "first": first,
1975 "first": first,
1976 "follow": follow,
1976 "follow": follow,
1977 "_followfirst": _followfirst,
1977 "_followfirst": _followfirst,
1978 "grep": grep,
1978 "grep": grep,
1979 "head": head,
1979 "head": head,
1980 "heads": heads,
1980 "heads": heads,
1981 "hidden": hidden,
1981 "hidden": hidden,
1982 "id": node_,
1982 "id": node_,
1983 "keyword": keyword,
1983 "keyword": keyword,
1984 "last": last,
1984 "last": last,
1985 "limit": limit,
1985 "limit": limit,
1986 "_matchfiles": _matchfiles,
1986 "_matchfiles": _matchfiles,
1987 "max": maxrev,
1987 "max": maxrev,
1988 "merge": merge,
1988 "merge": merge,
1989 "min": minrev,
1989 "min": minrev,
1990 "modifies": modifies,
1990 "modifies": modifies,
1991 "named": named,
1991 "named": named,
1992 "obsolete": obsolete,
1992 "obsolete": obsolete,
1993 "only": only,
1993 "only": only,
1994 "origin": origin,
1994 "origin": origin,
1995 "outgoing": outgoing,
1995 "outgoing": outgoing,
1996 "p1": p1,
1996 "p1": p1,
1997 "p2": p2,
1997 "p2": p2,
1998 "parents": parents,
1998 "parents": parents,
1999 "present": present,
1999 "present": present,
2000 "public": public,
2000 "public": public,
2001 "_notpublic": _notpublic,
2001 "_notpublic": _notpublic,
2002 "remote": remote,
2002 "remote": remote,
2003 "removes": removes,
2003 "removes": removes,
2004 "rev": rev,
2004 "rev": rev,
2005 "reverse": reverse,
2005 "reverse": reverse,
2006 "roots": roots,
2006 "roots": roots,
2007 "sort": sort,
2007 "sort": sort,
2008 "secret": secret,
2008 "secret": secret,
2009 "subrepo": subrepo,
2009 "subrepo": subrepo,
2010 "matching": matching,
2010 "matching": matching,
2011 "tag": tag,
2011 "tag": tag,
2012 "tagged": tagged,
2012 "tagged": tagged,
2013 "user": user,
2013 "user": user,
2014 "unstable": unstable,
2014 "unstable": unstable,
2015 "wdir": wdir,
2015 "wdir": wdir,
2016 "_list": _list,
2016 "_list": _list,
2017 "_intlist": _intlist,
2017 "_intlist": _intlist,
2018 "_hexlist": _hexlist,
2018 "_hexlist": _hexlist,
2019 }
2019 }
2020
2020
2021 # symbols which can't be used for a DoS attack for any given input
2021 # symbols which can't be used for a DoS attack for any given input
2022 # (e.g. those which accept regexes as plain strings shouldn't be included)
2022 # (e.g. those which accept regexes as plain strings shouldn't be included)
2023 # functions that just return a lot of changesets (like all) don't count here
2023 # functions that just return a lot of changesets (like all) don't count here
2024 safesymbols = set([
2024 safesymbols = set([
2025 "adds",
2025 "adds",
2026 "all",
2026 "all",
2027 "ancestor",
2027 "ancestor",
2028 "ancestors",
2028 "ancestors",
2029 "_firstancestors",
2029 "_firstancestors",
2030 "author",
2030 "author",
2031 "bisect",
2031 "bisect",
2032 "bisected",
2032 "bisected",
2033 "bookmark",
2033 "bookmark",
2034 "branch",
2034 "branch",
2035 "branchpoint",
2035 "branchpoint",
2036 "bumped",
2036 "bumped",
2037 "bundle",
2037 "bundle",
2038 "children",
2038 "children",
2039 "closed",
2039 "closed",
2040 "converted",
2040 "converted",
2041 "date",
2041 "date",
2042 "desc",
2042 "desc",
2043 "descendants",
2043 "descendants",
2044 "_firstdescendants",
2044 "_firstdescendants",
2045 "destination",
2045 "destination",
2046 "divergent",
2046 "divergent",
2047 "draft",
2047 "draft",
2048 "extinct",
2048 "extinct",
2049 "extra",
2049 "extra",
2050 "file",
2050 "file",
2051 "filelog",
2051 "filelog",
2052 "first",
2052 "first",
2053 "follow",
2053 "follow",
2054 "_followfirst",
2054 "_followfirst",
2055 "head",
2055 "head",
2056 "heads",
2056 "heads",
2057 "hidden",
2057 "hidden",
2058 "id",
2058 "id",
2059 "keyword",
2059 "keyword",
2060 "last",
2060 "last",
2061 "limit",
2061 "limit",
2062 "_matchfiles",
2062 "_matchfiles",
2063 "max",
2063 "max",
2064 "merge",
2064 "merge",
2065 "min",
2065 "min",
2066 "modifies",
2066 "modifies",
2067 "obsolete",
2067 "obsolete",
2068 "only",
2068 "only",
2069 "origin",
2069 "origin",
2070 "outgoing",
2070 "outgoing",
2071 "p1",
2071 "p1",
2072 "p2",
2072 "p2",
2073 "parents",
2073 "parents",
2074 "present",
2074 "present",
2075 "public",
2075 "public",
2076 "_notpublic",
2076 "_notpublic",
2077 "remote",
2077 "remote",
2078 "removes",
2078 "removes",
2079 "rev",
2079 "rev",
2080 "reverse",
2080 "reverse",
2081 "roots",
2081 "roots",
2082 "sort",
2082 "sort",
2083 "secret",
2083 "secret",
2084 "matching",
2084 "matching",
2085 "tag",
2085 "tag",
2086 "tagged",
2086 "tagged",
2087 "user",
2087 "user",
2088 "unstable",
2088 "unstable",
2089 "wdir",
2089 "wdir",
2090 "_list",
2090 "_list",
2091 "_intlist",
2091 "_intlist",
2092 "_hexlist",
2092 "_hexlist",
2093 ])
2093 ])
2094
2094
2095 methods = {
2095 methods = {
2096 "range": rangeset,
2096 "range": rangeset,
2097 "dagrange": dagrange,
2097 "dagrange": dagrange,
2098 "string": stringset,
2098 "string": stringset,
2099 "symbol": stringset,
2099 "symbol": stringset,
2100 "and": andset,
2100 "and": andset,
2101 "or": orset,
2101 "or": orset,
2102 "not": notset,
2102 "not": notset,
2103 "list": listset,
2103 "list": listset,
2104 "func": func,
2104 "func": func,
2105 "ancestor": ancestorspec,
2105 "ancestor": ancestorspec,
2106 "parent": parentspec,
2106 "parent": parentspec,
2107 "parentpost": p1,
2107 "parentpost": p1,
2108 }
2108 }
2109
2109
2110 def optimize(x, small):
2110 def optimize(x, small):
2111 if x is None:
2111 if x is None:
2112 return 0, x
2112 return 0, x
2113
2113
2114 smallbonus = 1
2114 smallbonus = 1
2115 if small:
2115 if small:
2116 smallbonus = .5
2116 smallbonus = .5
2117
2117
2118 op = x[0]
2118 op = x[0]
2119 if op == 'minus':
2119 if op == 'minus':
2120 return optimize(('and', x[1], ('not', x[2])), small)
2120 return optimize(('and', x[1], ('not', x[2])), small)
2121 elif op == 'only':
2121 elif op == 'only':
2122 return optimize(('func', ('symbol', 'only'),
2122 return optimize(('func', ('symbol', 'only'),
2123 ('list', x[1], x[2])), small)
2123 ('list', x[1], x[2])), small)
2124 elif op == 'onlypost':
2124 elif op == 'onlypost':
2125 return optimize(('func', ('symbol', 'only'), x[1]), small)
2125 return optimize(('func', ('symbol', 'only'), x[1]), small)
2126 elif op == 'dagrangepre':
2126 elif op == 'dagrangepre':
2127 return optimize(('func', ('symbol', 'ancestors'), x[1]), small)
2127 return optimize(('func', ('symbol', 'ancestors'), x[1]), small)
2128 elif op == 'dagrangepost':
2128 elif op == 'dagrangepost':
2129 return optimize(('func', ('symbol', 'descendants'), x[1]), small)
2129 return optimize(('func', ('symbol', 'descendants'), x[1]), small)
2130 elif op == 'rangepre':
2130 elif op == 'rangepre':
2131 return optimize(('range', ('string', '0'), x[1]), small)
2131 return optimize(('range', ('string', '0'), x[1]), small)
2132 elif op == 'rangepost':
2132 elif op == 'rangepost':
2133 return optimize(('range', x[1], ('string', 'tip')), small)
2133 return optimize(('range', x[1], ('string', 'tip')), small)
2134 elif op == 'negate':
2134 elif op == 'negate':
2135 return optimize(('string',
2135 return optimize(('string',
2136 '-' + getstring(x[1], _("can't negate that"))), small)
2136 '-' + getstring(x[1], _("can't negate that"))), small)
2137 elif op in 'string symbol negate':
2137 elif op in 'string symbol negate':
2138 return smallbonus, x # single revisions are small
2138 return smallbonus, x # single revisions are small
2139 elif op == 'and':
2139 elif op == 'and':
2140 wa, ta = optimize(x[1], True)
2140 wa, ta = optimize(x[1], True)
2141 wb, tb = optimize(x[2], True)
2141 wb, tb = optimize(x[2], True)
2142
2142
2143 # (::x and not ::y)/(not ::y and ::x) have a fast path
2143 # (::x and not ::y)/(not ::y and ::x) have a fast path
2144 def isonly(revs, bases):
2144 def isonly(revs, bases):
2145 return (
2145 return (
2146 revs[0] == 'func'
2146 revs[0] == 'func'
2147 and getstring(revs[1], _('not a symbol')) == 'ancestors'
2147 and getstring(revs[1], _('not a symbol')) == 'ancestors'
2148 and bases[0] == 'not'
2148 and bases[0] == 'not'
2149 and bases[1][0] == 'func'
2149 and bases[1][0] == 'func'
2150 and getstring(bases[1][1], _('not a symbol')) == 'ancestors')
2150 and getstring(bases[1][1], _('not a symbol')) == 'ancestors')
2151
2151
2152 w = min(wa, wb)
2152 w = min(wa, wb)
2153 if isonly(ta, tb):
2153 if isonly(ta, tb):
2154 return w, ('func', ('symbol', 'only'), ('list', ta[2], tb[1][2]))
2154 return w, ('func', ('symbol', 'only'), ('list', ta[2], tb[1][2]))
2155 if isonly(tb, ta):
2155 if isonly(tb, ta):
2156 return w, ('func', ('symbol', 'only'), ('list', tb[2], ta[1][2]))
2156 return w, ('func', ('symbol', 'only'), ('list', tb[2], ta[1][2]))
2157
2157
2158 if wa > wb:
2158 if wa > wb:
2159 return w, (op, tb, ta)
2159 return w, (op, tb, ta)
2160 return w, (op, ta, tb)
2160 return w, (op, ta, tb)
2161 elif op == 'or':
2161 elif op == 'or':
2162 wa, ta = optimize(x[1], False)
2162 wa, ta = optimize(x[1], False)
2163 wb, tb = optimize(x[2], False)
2163 wb, tb = optimize(x[2], False)
2164 if wb < wa:
2164 if wb < wa:
2165 wb, wa = wa, wb
2165 wb, wa = wa, wb
2166 return max(wa, wb), (op, ta, tb)
2166 return max(wa, wb), (op, ta, tb)
2167 elif op == 'not':
2167 elif op == 'not':
2168 # Optimize not public() to _notpublic() because we have a fast version
2168 # Optimize not public() to _notpublic() because we have a fast version
2169 if x[1] == ('func', ('symbol', 'public'), None):
2169 if x[1] == ('func', ('symbol', 'public'), None):
2170 newsym = ('func', ('symbol', '_notpublic'), None)
2170 newsym = ('func', ('symbol', '_notpublic'), None)
2171 o = optimize(newsym, not small)
2171 o = optimize(newsym, not small)
2172 return o[0], o[1]
2172 return o[0], o[1]
2173 else:
2173 else:
2174 o = optimize(x[1], not small)
2174 o = optimize(x[1], not small)
2175 return o[0], (op, o[1])
2175 return o[0], (op, o[1])
2176 elif op == 'parentpost':
2176 elif op == 'parentpost':
2177 o = optimize(x[1], small)
2177 o = optimize(x[1], small)
2178 return o[0], (op, o[1])
2178 return o[0], (op, o[1])
2179 elif op == 'group':
2179 elif op == 'group':
2180 return optimize(x[1], small)
2180 return optimize(x[1], small)
2181 elif op in 'dagrange range list parent ancestorspec':
2181 elif op in 'dagrange range list parent ancestorspec':
2182 if op == 'parent':
2182 if op == 'parent':
2183 # x^:y means (x^) : y, not x ^ (:y)
2183 # x^:y means (x^) : y, not x ^ (:y)
2184 post = ('parentpost', x[1])
2184 post = ('parentpost', x[1])
2185 if x[2][0] == 'dagrangepre':
2185 if x[2][0] == 'dagrangepre':
2186 return optimize(('dagrange', post, x[2][1]), small)
2186 return optimize(('dagrange', post, x[2][1]), small)
2187 elif x[2][0] == 'rangepre':
2187 elif x[2][0] == 'rangepre':
2188 return optimize(('range', post, x[2][1]), small)
2188 return optimize(('range', post, x[2][1]), small)
2189
2189
2190 wa, ta = optimize(x[1], small)
2190 wa, ta = optimize(x[1], small)
2191 wb, tb = optimize(x[2], small)
2191 wb, tb = optimize(x[2], small)
2192 return wa + wb, (op, ta, tb)
2192 return wa + wb, (op, ta, tb)
2193 elif op == 'func':
2193 elif op == 'func':
2194 f = getstring(x[1], _("not a symbol"))
2194 f = getstring(x[1], _("not a symbol"))
2195 wa, ta = optimize(x[2], small)
2195 wa, ta = optimize(x[2], small)
2196 if f in ("author branch closed date desc file grep keyword "
2196 if f in ("author branch closed date desc file grep keyword "
2197 "outgoing user"):
2197 "outgoing user"):
2198 w = 10 # slow
2198 w = 10 # slow
2199 elif f in "modifies adds removes":
2199 elif f in "modifies adds removes":
2200 w = 30 # slower
2200 w = 30 # slower
2201 elif f == "contains":
2201 elif f == "contains":
2202 w = 100 # very slow
2202 w = 100 # very slow
2203 elif f == "ancestor":
2203 elif f == "ancestor":
2204 w = 1 * smallbonus
2204 w = 1 * smallbonus
2205 elif f in "reverse limit first _intlist":
2205 elif f in "reverse limit first _intlist":
2206 w = 0
2206 w = 0
2207 elif f in "sort":
2207 elif f in "sort":
2208 w = 10 # assume most sorts look at changelog
2208 w = 10 # assume most sorts look at changelog
2209 else:
2209 else:
2210 w = 1
2210 w = 1
2211 return w + wa, (op, x[1], ta)
2211 return w + wa, (op, x[1], ta)
2212 return 1, x
2212 return 1, x
2213
2213
2214 _aliasarg = ('func', ('symbol', '_aliasarg'))
2214 _aliasarg = ('func', ('symbol', '_aliasarg'))
2215 def _getaliasarg(tree):
2215 def _getaliasarg(tree):
2216 """If tree matches ('func', ('symbol', '_aliasarg'), ('string', X))
2216 """If tree matches ('func', ('symbol', '_aliasarg'), ('string', X))
2217 return X, None otherwise.
2217 return X, None otherwise.
2218 """
2218 """
2219 if (len(tree) == 3 and tree[:2] == _aliasarg
2219 if (len(tree) == 3 and tree[:2] == _aliasarg
2220 and tree[2][0] == 'string'):
2220 and tree[2][0] == 'string'):
2221 return tree[2][1]
2221 return tree[2][1]
2222 return None
2222 return None
2223
2223
2224 def _checkaliasarg(tree, known=None):
2224 def _checkaliasarg(tree, known=None):
2225 """Check tree contains no _aliasarg construct or only ones which
2225 """Check tree contains no _aliasarg construct or only ones which
2226 value is in known. Used to avoid alias placeholders injection.
2226 value is in known. Used to avoid alias placeholders injection.
2227 """
2227 """
2228 if isinstance(tree, tuple):
2228 if isinstance(tree, tuple):
2229 arg = _getaliasarg(tree)
2229 arg = _getaliasarg(tree)
2230 if arg is not None and (not known or arg not in known):
2230 if arg is not None and (not known or arg not in known):
2231 raise error.UnknownIdentifier('_aliasarg', [])
2231 raise error.UnknownIdentifier('_aliasarg', [])
2232 for t in tree:
2232 for t in tree:
2233 _checkaliasarg(t, known)
2233 _checkaliasarg(t, known)
2234
2234
2235 # the set of valid characters for the initial letter of symbols in
2235 # the set of valid characters for the initial letter of symbols in
2236 # alias declarations and definitions
2236 # alias declarations and definitions
2237 _aliassyminitletters = set(c for c in [chr(i) for i in xrange(256)]
2237 _aliassyminitletters = set(c for c in [chr(i) for i in xrange(256)]
2238 if c.isalnum() or c in '._@$' or ord(c) > 127)
2238 if c.isalnum() or c in '._@$' or ord(c) > 127)
2239
2239
2240 def _tokenizealias(program, lookup=None):
2240 def _tokenizealias(program, lookup=None):
2241 """Parse alias declaration/definition into a stream of tokens
2241 """Parse alias declaration/definition into a stream of tokens
2242
2242
2243 This allows symbol names to use also ``$`` as an initial letter
2243 This allows symbol names to use also ``$`` as an initial letter
2244 (for backward compatibility), and callers of this function should
2244 (for backward compatibility), and callers of this function should
2245 examine whether ``$`` is used also for unexpected symbols or not.
2245 examine whether ``$`` is used also for unexpected symbols or not.
2246 """
2246 """
2247 return tokenize(program, lookup=lookup,
2247 return tokenize(program, lookup=lookup,
2248 syminitletters=_aliassyminitletters)
2248 syminitletters=_aliassyminitletters)
2249
2249
2250 def _parsealiasdecl(decl):
2250 def _parsealiasdecl(decl):
2251 """Parse alias declaration ``decl``
2251 """Parse alias declaration ``decl``
2252
2252
2253 This returns ``(name, tree, args, errorstr)`` tuple:
2253 This returns ``(name, tree, args, errorstr)`` tuple:
2254
2254
2255 - ``name``: of declared alias (may be ``decl`` itself at error)
2255 - ``name``: of declared alias (may be ``decl`` itself at error)
2256 - ``tree``: parse result (or ``None`` at error)
2256 - ``tree``: parse result (or ``None`` at error)
2257 - ``args``: list of alias argument names (or None for symbol declaration)
2257 - ``args``: list of alias argument names (or None for symbol declaration)
2258 - ``errorstr``: detail about detected error (or None)
2258 - ``errorstr``: detail about detected error (or None)
2259
2259
2260 >>> _parsealiasdecl('foo')
2260 >>> _parsealiasdecl('foo')
2261 ('foo', ('symbol', 'foo'), None, None)
2261 ('foo', ('symbol', 'foo'), None, None)
2262 >>> _parsealiasdecl('$foo')
2262 >>> _parsealiasdecl('$foo')
2263 ('$foo', None, None, "'$' not for alias arguments")
2263 ('$foo', None, None, "'$' not for alias arguments")
2264 >>> _parsealiasdecl('foo::bar')
2264 >>> _parsealiasdecl('foo::bar')
2265 ('foo::bar', None, None, 'invalid format')
2265 ('foo::bar', None, None, 'invalid format')
2266 >>> _parsealiasdecl('foo bar')
2266 >>> _parsealiasdecl('foo bar')
2267 ('foo bar', None, None, 'at 4: invalid token')
2267 ('foo bar', None, None, 'at 4: invalid token')
2268 >>> _parsealiasdecl('foo()')
2268 >>> _parsealiasdecl('foo()')
2269 ('foo', ('func', ('symbol', 'foo')), [], None)
2269 ('foo', ('func', ('symbol', 'foo')), [], None)
2270 >>> _parsealiasdecl('$foo()')
2270 >>> _parsealiasdecl('$foo()')
2271 ('$foo()', None, None, "'$' not for alias arguments")
2271 ('$foo()', None, None, "'$' not for alias arguments")
2272 >>> _parsealiasdecl('foo($1, $2)')
2272 >>> _parsealiasdecl('foo($1, $2)')
2273 ('foo', ('func', ('symbol', 'foo')), ['$1', '$2'], None)
2273 ('foo', ('func', ('symbol', 'foo')), ['$1', '$2'], None)
2274 >>> _parsealiasdecl('foo(bar_bar, baz.baz)')
2274 >>> _parsealiasdecl('foo(bar_bar, baz.baz)')
2275 ('foo', ('func', ('symbol', 'foo')), ['bar_bar', 'baz.baz'], None)
2275 ('foo', ('func', ('symbol', 'foo')), ['bar_bar', 'baz.baz'], None)
2276 >>> _parsealiasdecl('foo($1, $2, nested($1, $2))')
2276 >>> _parsealiasdecl('foo($1, $2, nested($1, $2))')
2277 ('foo($1, $2, nested($1, $2))', None, None, 'invalid argument list')
2277 ('foo($1, $2, nested($1, $2))', None, None, 'invalid argument list')
2278 >>> _parsealiasdecl('foo(bar($1, $2))')
2278 >>> _parsealiasdecl('foo(bar($1, $2))')
2279 ('foo(bar($1, $2))', None, None, 'invalid argument list')
2279 ('foo(bar($1, $2))', None, None, 'invalid argument list')
2280 >>> _parsealiasdecl('foo("string")')
2280 >>> _parsealiasdecl('foo("string")')
2281 ('foo("string")', None, None, 'invalid argument list')
2281 ('foo("string")', None, None, 'invalid argument list')
2282 >>> _parsealiasdecl('foo($1, $2')
2282 >>> _parsealiasdecl('foo($1, $2')
2283 ('foo($1, $2', None, None, 'at 10: unexpected token: end')
2283 ('foo($1, $2', None, None, 'at 10: unexpected token: end')
2284 >>> _parsealiasdecl('foo("string')
2284 >>> _parsealiasdecl('foo("string')
2285 ('foo("string', None, None, 'at 5: unterminated string')
2285 ('foo("string', None, None, 'at 5: unterminated string')
2286 >>> _parsealiasdecl('foo($1, $2, $1)')
2286 >>> _parsealiasdecl('foo($1, $2, $1)')
2287 ('foo', None, None, 'argument names collide with each other')
2287 ('foo', None, None, 'argument names collide with each other')
2288 """
2288 """
2289 p = parser.parser(_tokenizealias, elements)
2289 p = parser.parser(_tokenizealias, elements)
2290 try:
2290 try:
2291 tree, pos = p.parse(decl)
2291 tree, pos = p.parse(decl)
2292 if (pos != len(decl)):
2292 if (pos != len(decl)):
2293 raise error.ParseError(_('invalid token'), pos)
2293 raise error.ParseError(_('invalid token'), pos)
2294
2294
2295 if isvalidsymbol(tree):
2295 if isvalidsymbol(tree):
2296 # "name = ...." style
2296 # "name = ...." style
2297 name = getsymbol(tree)
2297 name = getsymbol(tree)
2298 if name.startswith('$'):
2298 if name.startswith('$'):
2299 return (decl, None, None, _("'$' not for alias arguments"))
2299 return (decl, None, None, _("'$' not for alias arguments"))
2300 return (name, ('symbol', name), None, None)
2300 return (name, ('symbol', name), None, None)
2301
2301
2302 if isvalidfunc(tree):
2302 if isvalidfunc(tree):
2303 # "name(arg, ....) = ...." style
2303 # "name(arg, ....) = ...." style
2304 name = getfuncname(tree)
2304 name = getfuncname(tree)
2305 if name.startswith('$'):
2305 if name.startswith('$'):
2306 return (decl, None, None, _("'$' not for alias arguments"))
2306 return (decl, None, None, _("'$' not for alias arguments"))
2307 args = []
2307 args = []
2308 for arg in getfuncargs(tree):
2308 for arg in getfuncargs(tree):
2309 if not isvalidsymbol(arg):
2309 if not isvalidsymbol(arg):
2310 return (decl, None, None, _("invalid argument list"))
2310 return (decl, None, None, _("invalid argument list"))
2311 args.append(getsymbol(arg))
2311 args.append(getsymbol(arg))
2312 if len(args) != len(set(args)):
2312 if len(args) != len(set(args)):
2313 return (name, None, None,
2313 return (name, None, None,
2314 _("argument names collide with each other"))
2314 _("argument names collide with each other"))
2315 return (name, ('func', ('symbol', name)), args, None)
2315 return (name, ('func', ('symbol', name)), args, None)
2316
2316
2317 return (decl, None, None, _("invalid format"))
2317 return (decl, None, None, _("invalid format"))
2318 except error.ParseError, inst:
2318 except error.ParseError, inst:
2319 return (decl, None, None, parseerrordetail(inst))
2319 return (decl, None, None, parseerrordetail(inst))
2320
2320
2321 def _parsealiasdefn(defn, args):
2321 def _parsealiasdefn(defn, args):
2322 """Parse alias definition ``defn``
2322 """Parse alias definition ``defn``
2323
2323
2324 This function also replaces alias argument references in the
2324 This function also replaces alias argument references in the
2325 specified definition by ``_aliasarg(ARGNAME)``.
2325 specified definition by ``_aliasarg(ARGNAME)``.
2326
2326
2327 ``args`` is a list of alias argument names, or None if the alias
2327 ``args`` is a list of alias argument names, or None if the alias
2328 is declared as a symbol.
2328 is declared as a symbol.
2329
2329
2330 This returns "tree" as parsing result.
2330 This returns "tree" as parsing result.
2331
2331
2332 >>> args = ['$1', '$2', 'foo']
2332 >>> args = ['$1', '$2', 'foo']
2333 >>> print prettyformat(_parsealiasdefn('$1 or foo', args))
2333 >>> print prettyformat(_parsealiasdefn('$1 or foo', args))
2334 (or
2334 (or
2335 (func
2335 (func
2336 ('symbol', '_aliasarg')
2336 ('symbol', '_aliasarg')
2337 ('string', '$1'))
2337 ('string', '$1'))
2338 (func
2338 (func
2339 ('symbol', '_aliasarg')
2339 ('symbol', '_aliasarg')
2340 ('string', 'foo')))
2340 ('string', 'foo')))
2341 >>> try:
2341 >>> try:
2342 ... _parsealiasdefn('$1 or $bar', args)
2342 ... _parsealiasdefn('$1 or $bar', args)
2343 ... except error.ParseError, inst:
2343 ... except error.ParseError, inst:
2344 ... print parseerrordetail(inst)
2344 ... print parseerrordetail(inst)
2345 at 6: '$' not for alias arguments
2345 at 6: '$' not for alias arguments
2346 >>> args = ['$1', '$10', 'foo']
2346 >>> args = ['$1', '$10', 'foo']
2347 >>> print prettyformat(_parsealiasdefn('$10 or foobar', args))
2347 >>> print prettyformat(_parsealiasdefn('$10 or foobar', args))
2348 (or
2348 (or
2349 (func
2349 (func
2350 ('symbol', '_aliasarg')
2350 ('symbol', '_aliasarg')
2351 ('string', '$10'))
2351 ('string', '$10'))
2352 ('symbol', 'foobar'))
2352 ('symbol', 'foobar'))
2353 >>> print prettyformat(_parsealiasdefn('"$1" or "foo"', args))
2353 >>> print prettyformat(_parsealiasdefn('"$1" or "foo"', args))
2354 (or
2354 (or
2355 ('string', '$1')
2355 ('string', '$1')
2356 ('string', 'foo'))
2356 ('string', 'foo'))
2357 """
2357 """
2358 def tokenizedefn(program, lookup=None):
2358 def tokenizedefn(program, lookup=None):
2359 if args:
2359 if args:
2360 argset = set(args)
2360 argset = set(args)
2361 else:
2361 else:
2362 argset = set()
2362 argset = set()
2363
2363
2364 for t, value, pos in _tokenizealias(program, lookup=lookup):
2364 for t, value, pos in _tokenizealias(program, lookup=lookup):
2365 if t == 'symbol':
2365 if t == 'symbol':
2366 if value in argset:
2366 if value in argset:
2367 # emulate tokenization of "_aliasarg('ARGNAME')":
2367 # emulate tokenization of "_aliasarg('ARGNAME')":
2368 # "_aliasarg()" is an unknown symbol only used separate
2368 # "_aliasarg()" is an unknown symbol only used separate
2369 # alias argument placeholders from regular strings.
2369 # alias argument placeholders from regular strings.
2370 yield ('symbol', '_aliasarg', pos)
2370 yield ('symbol', '_aliasarg', pos)
2371 yield ('(', None, pos)
2371 yield ('(', None, pos)
2372 yield ('string', value, pos)
2372 yield ('string', value, pos)
2373 yield (')', None, pos)
2373 yield (')', None, pos)
2374 continue
2374 continue
2375 elif value.startswith('$'):
2375 elif value.startswith('$'):
2376 raise error.ParseError(_("'$' not for alias arguments"),
2376 raise error.ParseError(_("'$' not for alias arguments"),
2377 pos)
2377 pos)
2378 yield (t, value, pos)
2378 yield (t, value, pos)
2379
2379
2380 p = parser.parser(tokenizedefn, elements)
2380 p = parser.parser(tokenizedefn, elements)
2381 tree, pos = p.parse(defn)
2381 tree, pos = p.parse(defn)
2382 if pos != len(defn):
2382 if pos != len(defn):
2383 raise error.ParseError(_('invalid token'), pos)
2383 raise error.ParseError(_('invalid token'), pos)
2384 return tree
2384 return tree
2385
2385
2386 class revsetalias(object):
2386 class revsetalias(object):
2387 # whether own `error` information is already shown or not.
2387 # whether own `error` information is already shown or not.
2388 # this avoids showing same warning multiple times at each `findaliases`.
2388 # this avoids showing same warning multiple times at each `findaliases`.
2389 warned = False
2389 warned = False
2390
2390
2391 def __init__(self, name, value):
2391 def __init__(self, name, value):
2392 '''Aliases like:
2392 '''Aliases like:
2393
2393
2394 h = heads(default)
2394 h = heads(default)
2395 b($1) = ancestors($1) - ancestors(default)
2395 b($1) = ancestors($1) - ancestors(default)
2396 '''
2396 '''
2397 self.name, self.tree, self.args, self.error = _parsealiasdecl(name)
2397 self.name, self.tree, self.args, self.error = _parsealiasdecl(name)
2398 if self.error:
2398 if self.error:
2399 self.error = _('failed to parse the declaration of revset alias'
2399 self.error = _('failed to parse the declaration of revset alias'
2400 ' "%s": %s') % (self.name, self.error)
2400 ' "%s": %s') % (self.name, self.error)
2401 return
2401 return
2402
2402
2403 try:
2403 try:
2404 self.replacement = _parsealiasdefn(value, self.args)
2404 self.replacement = _parsealiasdefn(value, self.args)
2405 # Check for placeholder injection
2405 # Check for placeholder injection
2406 _checkaliasarg(self.replacement, self.args)
2406 _checkaliasarg(self.replacement, self.args)
2407 except error.ParseError, inst:
2407 except error.ParseError, inst:
2408 self.error = _('failed to parse the definition of revset alias'
2408 self.error = _('failed to parse the definition of revset alias'
2409 ' "%s": %s') % (self.name, parseerrordetail(inst))
2409 ' "%s": %s') % (self.name, parseerrordetail(inst))
2410
2410
2411 def _getalias(aliases, tree):
2411 def _getalias(aliases, tree):
2412 """If tree looks like an unexpanded alias, return it. Return None
2412 """If tree looks like an unexpanded alias, return it. Return None
2413 otherwise.
2413 otherwise.
2414 """
2414 """
2415 if isinstance(tree, tuple) and tree:
2415 if isinstance(tree, tuple) and tree:
2416 if tree[0] == 'symbol' and len(tree) == 2:
2416 if tree[0] == 'symbol' and len(tree) == 2:
2417 name = tree[1]
2417 name = tree[1]
2418 alias = aliases.get(name)
2418 alias = aliases.get(name)
2419 if alias and alias.args is None and alias.tree == tree:
2419 if alias and alias.args is None and alias.tree == tree:
2420 return alias
2420 return alias
2421 if tree[0] == 'func' and len(tree) > 1:
2421 if tree[0] == 'func' and len(tree) > 1:
2422 if tree[1][0] == 'symbol' and len(tree[1]) == 2:
2422 if tree[1][0] == 'symbol' and len(tree[1]) == 2:
2423 name = tree[1][1]
2423 name = tree[1][1]
2424 alias = aliases.get(name)
2424 alias = aliases.get(name)
2425 if alias and alias.args is not None and alias.tree == tree[:2]:
2425 if alias and alias.args is not None and alias.tree == tree[:2]:
2426 return alias
2426 return alias
2427 return None
2427 return None
2428
2428
2429 def _expandargs(tree, args):
2429 def _expandargs(tree, args):
2430 """Replace _aliasarg instances with the substitution value of the
2430 """Replace _aliasarg instances with the substitution value of the
2431 same name in args, recursively.
2431 same name in args, recursively.
2432 """
2432 """
2433 if not tree or not isinstance(tree, tuple):
2433 if not tree or not isinstance(tree, tuple):
2434 return tree
2434 return tree
2435 arg = _getaliasarg(tree)
2435 arg = _getaliasarg(tree)
2436 if arg is not None:
2436 if arg is not None:
2437 return args[arg]
2437 return args[arg]
2438 return tuple(_expandargs(t, args) for t in tree)
2438 return tuple(_expandargs(t, args) for t in tree)
2439
2439
2440 def _expandaliases(aliases, tree, expanding, cache):
2440 def _expandaliases(aliases, tree, expanding, cache):
2441 """Expand aliases in tree, recursively.
2441 """Expand aliases in tree, recursively.
2442
2442
2443 'aliases' is a dictionary mapping user defined aliases to
2443 'aliases' is a dictionary mapping user defined aliases to
2444 revsetalias objects.
2444 revsetalias objects.
2445 """
2445 """
2446 if not isinstance(tree, tuple):
2446 if not isinstance(tree, tuple):
2447 # Do not expand raw strings
2447 # Do not expand raw strings
2448 return tree
2448 return tree
2449 alias = _getalias(aliases, tree)
2449 alias = _getalias(aliases, tree)
2450 if alias is not None:
2450 if alias is not None:
2451 if alias.error:
2451 if alias.error:
2452 raise util.Abort(alias.error)
2452 raise util.Abort(alias.error)
2453 if alias in expanding:
2453 if alias in expanding:
2454 raise error.ParseError(_('infinite expansion of revset alias "%s" '
2454 raise error.ParseError(_('infinite expansion of revset alias "%s" '
2455 'detected') % alias.name)
2455 'detected') % alias.name)
2456 expanding.append(alias)
2456 expanding.append(alias)
2457 if alias.name not in cache:
2457 if alias.name not in cache:
2458 cache[alias.name] = _expandaliases(aliases, alias.replacement,
2458 cache[alias.name] = _expandaliases(aliases, alias.replacement,
2459 expanding, cache)
2459 expanding, cache)
2460 result = cache[alias.name]
2460 result = cache[alias.name]
2461 expanding.pop()
2461 expanding.pop()
2462 if alias.args is not None:
2462 if alias.args is not None:
2463 l = getlist(tree[2])
2463 l = getlist(tree[2])
2464 if len(l) != len(alias.args):
2464 if len(l) != len(alias.args):
2465 raise error.ParseError(
2465 raise error.ParseError(
2466 _('invalid number of arguments: %s') % len(l))
2466 _('invalid number of arguments: %s') % len(l))
2467 l = [_expandaliases(aliases, a, [], cache) for a in l]
2467 l = [_expandaliases(aliases, a, [], cache) for a in l]
2468 result = _expandargs(result, dict(zip(alias.args, l)))
2468 result = _expandargs(result, dict(zip(alias.args, l)))
2469 else:
2469 else:
2470 result = tuple(_expandaliases(aliases, t, expanding, cache)
2470 result = tuple(_expandaliases(aliases, t, expanding, cache)
2471 for t in tree)
2471 for t in tree)
2472 return result
2472 return result
2473
2473
2474 def findaliases(ui, tree, showwarning=None):
2474 def findaliases(ui, tree, showwarning=None):
2475 _checkaliasarg(tree)
2475 _checkaliasarg(tree)
2476 aliases = {}
2476 aliases = {}
2477 for k, v in ui.configitems('revsetalias'):
2477 for k, v in ui.configitems('revsetalias'):
2478 alias = revsetalias(k, v)
2478 alias = revsetalias(k, v)
2479 aliases[alias.name] = alias
2479 aliases[alias.name] = alias
2480 tree = _expandaliases(aliases, tree, [], {})
2480 tree = _expandaliases(aliases, tree, [], {})
2481 if showwarning:
2481 if showwarning:
2482 # warn about problematic (but not referred) aliases
2482 # warn about problematic (but not referred) aliases
2483 for name, alias in sorted(aliases.iteritems()):
2483 for name, alias in sorted(aliases.iteritems()):
2484 if alias.error and not alias.warned:
2484 if alias.error and not alias.warned:
2485 showwarning(_('warning: %s\n') % (alias.error))
2485 showwarning(_('warning: %s\n') % (alias.error))
2486 alias.warned = True
2486 alias.warned = True
2487 return tree
2487 return tree
2488
2488
2489 def foldconcat(tree):
2489 def foldconcat(tree):
2490 """Fold elements to be concatenated by `##`
2490 """Fold elements to be concatenated by `##`
2491 """
2491 """
2492 if not isinstance(tree, tuple) or tree[0] in ('string', 'symbol'):
2492 if not isinstance(tree, tuple) or tree[0] in ('string', 'symbol'):
2493 return tree
2493 return tree
2494 if tree[0] == '_concat':
2494 if tree[0] == '_concat':
2495 pending = [tree]
2495 pending = [tree]
2496 l = []
2496 l = []
2497 while pending:
2497 while pending:
2498 e = pending.pop()
2498 e = pending.pop()
2499 if e[0] == '_concat':
2499 if e[0] == '_concat':
2500 pending.extend(reversed(e[1:]))
2500 pending.extend(reversed(e[1:]))
2501 elif e[0] in ('string', 'symbol'):
2501 elif e[0] in ('string', 'symbol'):
2502 l.append(e[1])
2502 l.append(e[1])
2503 else:
2503 else:
2504 msg = _("\"##\" can't concatenate \"%s\" element") % (e[0])
2504 msg = _("\"##\" can't concatenate \"%s\" element") % (e[0])
2505 raise error.ParseError(msg)
2505 raise error.ParseError(msg)
2506 return ('string', ''.join(l))
2506 return ('string', ''.join(l))
2507 else:
2507 else:
2508 return tuple(foldconcat(t) for t in tree)
2508 return tuple(foldconcat(t) for t in tree)
2509
2509
2510 def parse(spec, lookup=None):
2510 def parse(spec, lookup=None):
2511 p = parser.parser(tokenize, elements)
2511 p = parser.parser(tokenize, elements)
2512 return p.parse(spec, lookup=lookup)
2512 tree, pos = p.parse(spec, lookup=lookup)
2513 if pos != len(spec):
2514 raise error.ParseError(_("invalid token"), pos)
2515 return tree
2513
2516
2514 def posttreebuilthook(tree, repo):
2517 def posttreebuilthook(tree, repo):
2515 # hook for extensions to execute code on the optimized tree
2518 # hook for extensions to execute code on the optimized tree
2516 pass
2519 pass
2517
2520
2518 def match(ui, spec, repo=None):
2521 def match(ui, spec, repo=None):
2519 if not spec:
2522 if not spec:
2520 raise error.ParseError(_("empty query"))
2523 raise error.ParseError(_("empty query"))
2521 lookup = None
2524 lookup = None
2522 if repo:
2525 if repo:
2523 lookup = repo.__contains__
2526 lookup = repo.__contains__
2524 tree, pos = parse(spec, lookup)
2527 tree = parse(spec, lookup)
2525 if (pos != len(spec)):
2526 raise error.ParseError(_("invalid token"), pos)
2527 if ui:
2528 if ui:
2528 tree = findaliases(ui, tree, showwarning=ui.warn)
2529 tree = findaliases(ui, tree, showwarning=ui.warn)
2529 tree = foldconcat(tree)
2530 tree = foldconcat(tree)
2530 weight, tree = optimize(tree, True)
2531 weight, tree = optimize(tree, True)
2531 posttreebuilthook(tree, repo)
2532 posttreebuilthook(tree, repo)
2532 def mfunc(repo, subset=None):
2533 def mfunc(repo, subset=None):
2533 if subset is None:
2534 if subset is None:
2534 subset = fullreposet(repo)
2535 subset = fullreposet(repo)
2535 if util.safehasattr(subset, 'isascending'):
2536 if util.safehasattr(subset, 'isascending'):
2536 result = getset(repo, subset, tree)
2537 result = getset(repo, subset, tree)
2537 else:
2538 else:
2538 result = getset(repo, baseset(subset), tree)
2539 result = getset(repo, baseset(subset), tree)
2539 return result
2540 return result
2540 return mfunc
2541 return mfunc
2541
2542
2542 def formatspec(expr, *args):
2543 def formatspec(expr, *args):
2543 '''
2544 '''
2544 This is a convenience function for using revsets internally, and
2545 This is a convenience function for using revsets internally, and
2545 escapes arguments appropriately. Aliases are intentionally ignored
2546 escapes arguments appropriately. Aliases are intentionally ignored
2546 so that intended expression behavior isn't accidentally subverted.
2547 so that intended expression behavior isn't accidentally subverted.
2547
2548
2548 Supported arguments:
2549 Supported arguments:
2549
2550
2550 %r = revset expression, parenthesized
2551 %r = revset expression, parenthesized
2551 %d = int(arg), no quoting
2552 %d = int(arg), no quoting
2552 %s = string(arg), escaped and single-quoted
2553 %s = string(arg), escaped and single-quoted
2553 %b = arg.branch(), escaped and single-quoted
2554 %b = arg.branch(), escaped and single-quoted
2554 %n = hex(arg), single-quoted
2555 %n = hex(arg), single-quoted
2555 %% = a literal '%'
2556 %% = a literal '%'
2556
2557
2557 Prefixing the type with 'l' specifies a parenthesized list of that type.
2558 Prefixing the type with 'l' specifies a parenthesized list of that type.
2558
2559
2559 >>> formatspec('%r:: and %lr', '10 or 11', ("this()", "that()"))
2560 >>> formatspec('%r:: and %lr', '10 or 11', ("this()", "that()"))
2560 '(10 or 11):: and ((this()) or (that()))'
2561 '(10 or 11):: and ((this()) or (that()))'
2561 >>> formatspec('%d:: and not %d::', 10, 20)
2562 >>> formatspec('%d:: and not %d::', 10, 20)
2562 '10:: and not 20::'
2563 '10:: and not 20::'
2563 >>> formatspec('%ld or %ld', [], [1])
2564 >>> formatspec('%ld or %ld', [], [1])
2564 "_list('') or 1"
2565 "_list('') or 1"
2565 >>> formatspec('keyword(%s)', 'foo\\xe9')
2566 >>> formatspec('keyword(%s)', 'foo\\xe9')
2566 "keyword('foo\\\\xe9')"
2567 "keyword('foo\\\\xe9')"
2567 >>> b = lambda: 'default'
2568 >>> b = lambda: 'default'
2568 >>> b.branch = b
2569 >>> b.branch = b
2569 >>> formatspec('branch(%b)', b)
2570 >>> formatspec('branch(%b)', b)
2570 "branch('default')"
2571 "branch('default')"
2571 >>> formatspec('root(%ls)', ['a', 'b', 'c', 'd'])
2572 >>> formatspec('root(%ls)', ['a', 'b', 'c', 'd'])
2572 "root(_list('a\\x00b\\x00c\\x00d'))"
2573 "root(_list('a\\x00b\\x00c\\x00d'))"
2573 '''
2574 '''
2574
2575
2575 def quote(s):
2576 def quote(s):
2576 return repr(str(s))
2577 return repr(str(s))
2577
2578
2578 def argtype(c, arg):
2579 def argtype(c, arg):
2579 if c == 'd':
2580 if c == 'd':
2580 return str(int(arg))
2581 return str(int(arg))
2581 elif c == 's':
2582 elif c == 's':
2582 return quote(arg)
2583 return quote(arg)
2583 elif c == 'r':
2584 elif c == 'r':
2584 parse(arg) # make sure syntax errors are confined
2585 parse(arg) # make sure syntax errors are confined
2585 return '(%s)' % arg
2586 return '(%s)' % arg
2586 elif c == 'n':
2587 elif c == 'n':
2587 return quote(node.hex(arg))
2588 return quote(node.hex(arg))
2588 elif c == 'b':
2589 elif c == 'b':
2589 return quote(arg.branch())
2590 return quote(arg.branch())
2590
2591
2591 def listexp(s, t):
2592 def listexp(s, t):
2592 l = len(s)
2593 l = len(s)
2593 if l == 0:
2594 if l == 0:
2594 return "_list('')"
2595 return "_list('')"
2595 elif l == 1:
2596 elif l == 1:
2596 return argtype(t, s[0])
2597 return argtype(t, s[0])
2597 elif t == 'd':
2598 elif t == 'd':
2598 return "_intlist('%s')" % "\0".join(str(int(a)) for a in s)
2599 return "_intlist('%s')" % "\0".join(str(int(a)) for a in s)
2599 elif t == 's':
2600 elif t == 's':
2600 return "_list('%s')" % "\0".join(s)
2601 return "_list('%s')" % "\0".join(s)
2601 elif t == 'n':
2602 elif t == 'n':
2602 return "_hexlist('%s')" % "\0".join(node.hex(a) for a in s)
2603 return "_hexlist('%s')" % "\0".join(node.hex(a) for a in s)
2603 elif t == 'b':
2604 elif t == 'b':
2604 return "_list('%s')" % "\0".join(a.branch() for a in s)
2605 return "_list('%s')" % "\0".join(a.branch() for a in s)
2605
2606
2606 m = l // 2
2607 m = l // 2
2607 return '(%s or %s)' % (listexp(s[:m], t), listexp(s[m:], t))
2608 return '(%s or %s)' % (listexp(s[:m], t), listexp(s[m:], t))
2608
2609
2609 ret = ''
2610 ret = ''
2610 pos = 0
2611 pos = 0
2611 arg = 0
2612 arg = 0
2612 while pos < len(expr):
2613 while pos < len(expr):
2613 c = expr[pos]
2614 c = expr[pos]
2614 if c == '%':
2615 if c == '%':
2615 pos += 1
2616 pos += 1
2616 d = expr[pos]
2617 d = expr[pos]
2617 if d == '%':
2618 if d == '%':
2618 ret += d
2619 ret += d
2619 elif d in 'dsnbr':
2620 elif d in 'dsnbr':
2620 ret += argtype(d, args[arg])
2621 ret += argtype(d, args[arg])
2621 arg += 1
2622 arg += 1
2622 elif d == 'l':
2623 elif d == 'l':
2623 # a list of some type
2624 # a list of some type
2624 pos += 1
2625 pos += 1
2625 d = expr[pos]
2626 d = expr[pos]
2626 ret += listexp(list(args[arg]), d)
2627 ret += listexp(list(args[arg]), d)
2627 arg += 1
2628 arg += 1
2628 else:
2629 else:
2629 raise util.Abort('unexpected revspec format character %s' % d)
2630 raise util.Abort('unexpected revspec format character %s' % d)
2630 else:
2631 else:
2631 ret += c
2632 ret += c
2632 pos += 1
2633 pos += 1
2633
2634
2634 return ret
2635 return ret
2635
2636
2636 def prettyformat(tree):
2637 def prettyformat(tree):
2637 def _prettyformat(tree, level, lines):
2638 def _prettyformat(tree, level, lines):
2638 if not isinstance(tree, tuple) or tree[0] in ('string', 'symbol'):
2639 if not isinstance(tree, tuple) or tree[0] in ('string', 'symbol'):
2639 lines.append((level, str(tree)))
2640 lines.append((level, str(tree)))
2640 else:
2641 else:
2641 lines.append((level, '(%s' % tree[0]))
2642 lines.append((level, '(%s' % tree[0]))
2642 for s in tree[1:]:
2643 for s in tree[1:]:
2643 _prettyformat(s, level + 1, lines)
2644 _prettyformat(s, level + 1, lines)
2644 lines[-1:] = [(lines[-1][0], lines[-1][1] + ')')]
2645 lines[-1:] = [(lines[-1][0], lines[-1][1] + ')')]
2645
2646
2646 lines = []
2647 lines = []
2647 _prettyformat(tree, 0, lines)
2648 _prettyformat(tree, 0, lines)
2648 output = '\n'.join((' '*l + s) for l, s in lines)
2649 output = '\n'.join((' '*l + s) for l, s in lines)
2649 return output
2650 return output
2650
2651
2651 def depth(tree):
2652 def depth(tree):
2652 if isinstance(tree, tuple):
2653 if isinstance(tree, tuple):
2653 return max(map(depth, tree)) + 1
2654 return max(map(depth, tree)) + 1
2654 else:
2655 else:
2655 return 0
2656 return 0
2656
2657
2657 def funcsused(tree):
2658 def funcsused(tree):
2658 if not isinstance(tree, tuple) or tree[0] in ('string', 'symbol'):
2659 if not isinstance(tree, tuple) or tree[0] in ('string', 'symbol'):
2659 return set()
2660 return set()
2660 else:
2661 else:
2661 funcs = set()
2662 funcs = set()
2662 for s in tree[1:]:
2663 for s in tree[1:]:
2663 funcs |= funcsused(s)
2664 funcs |= funcsused(s)
2664 if tree[0] == 'func':
2665 if tree[0] == 'func':
2665 funcs.add(tree[1][1])
2666 funcs.add(tree[1][1])
2666 return funcs
2667 return funcs
2667
2668
2668 class abstractsmartset(object):
2669 class abstractsmartset(object):
2669
2670
2670 def __nonzero__(self):
2671 def __nonzero__(self):
2671 """True if the smartset is not empty"""
2672 """True if the smartset is not empty"""
2672 raise NotImplementedError()
2673 raise NotImplementedError()
2673
2674
2674 def __contains__(self, rev):
2675 def __contains__(self, rev):
2675 """provide fast membership testing"""
2676 """provide fast membership testing"""
2676 raise NotImplementedError()
2677 raise NotImplementedError()
2677
2678
2678 def __iter__(self):
2679 def __iter__(self):
2679 """iterate the set in the order it is supposed to be iterated"""
2680 """iterate the set in the order it is supposed to be iterated"""
2680 raise NotImplementedError()
2681 raise NotImplementedError()
2681
2682
2682 # Attributes containing a function to perform a fast iteration in a given
2683 # Attributes containing a function to perform a fast iteration in a given
2683 # direction. A smartset can have none, one, or both defined.
2684 # direction. A smartset can have none, one, or both defined.
2684 #
2685 #
2685 # Default value is None instead of a function returning None to avoid
2686 # Default value is None instead of a function returning None to avoid
2686 # initializing an iterator just for testing if a fast method exists.
2687 # initializing an iterator just for testing if a fast method exists.
2687 fastasc = None
2688 fastasc = None
2688 fastdesc = None
2689 fastdesc = None
2689
2690
2690 def isascending(self):
2691 def isascending(self):
2691 """True if the set will iterate in ascending order"""
2692 """True if the set will iterate in ascending order"""
2692 raise NotImplementedError()
2693 raise NotImplementedError()
2693
2694
2694 def isdescending(self):
2695 def isdescending(self):
2695 """True if the set will iterate in descending order"""
2696 """True if the set will iterate in descending order"""
2696 raise NotImplementedError()
2697 raise NotImplementedError()
2697
2698
2698 def min(self):
2699 def min(self):
2699 """return the minimum element in the set"""
2700 """return the minimum element in the set"""
2700 if self.fastasc is not None:
2701 if self.fastasc is not None:
2701 for r in self.fastasc():
2702 for r in self.fastasc():
2702 return r
2703 return r
2703 raise ValueError('arg is an empty sequence')
2704 raise ValueError('arg is an empty sequence')
2704 return min(self)
2705 return min(self)
2705
2706
2706 def max(self):
2707 def max(self):
2707 """return the maximum element in the set"""
2708 """return the maximum element in the set"""
2708 if self.fastdesc is not None:
2709 if self.fastdesc is not None:
2709 for r in self.fastdesc():
2710 for r in self.fastdesc():
2710 return r
2711 return r
2711 raise ValueError('arg is an empty sequence')
2712 raise ValueError('arg is an empty sequence')
2712 return max(self)
2713 return max(self)
2713
2714
2714 def first(self):
2715 def first(self):
2715 """return the first element in the set (user iteration perspective)
2716 """return the first element in the set (user iteration perspective)
2716
2717
2717 Return None if the set is empty"""
2718 Return None if the set is empty"""
2718 raise NotImplementedError()
2719 raise NotImplementedError()
2719
2720
2720 def last(self):
2721 def last(self):
2721 """return the last element in the set (user iteration perspective)
2722 """return the last element in the set (user iteration perspective)
2722
2723
2723 Return None if the set is empty"""
2724 Return None if the set is empty"""
2724 raise NotImplementedError()
2725 raise NotImplementedError()
2725
2726
2726 def __len__(self):
2727 def __len__(self):
2727 """return the length of the smartsets
2728 """return the length of the smartsets
2728
2729
2729 This can be expensive on smartset that could be lazy otherwise."""
2730 This can be expensive on smartset that could be lazy otherwise."""
2730 raise NotImplementedError()
2731 raise NotImplementedError()
2731
2732
2732 def reverse(self):
2733 def reverse(self):
2733 """reverse the expected iteration order"""
2734 """reverse the expected iteration order"""
2734 raise NotImplementedError()
2735 raise NotImplementedError()
2735
2736
2736 def sort(self, reverse=True):
2737 def sort(self, reverse=True):
2737 """get the set to iterate in an ascending or descending order"""
2738 """get the set to iterate in an ascending or descending order"""
2738 raise NotImplementedError()
2739 raise NotImplementedError()
2739
2740
2740 def __and__(self, other):
2741 def __and__(self, other):
2741 """Returns a new object with the intersection of the two collections.
2742 """Returns a new object with the intersection of the two collections.
2742
2743
2743 This is part of the mandatory API for smartset."""
2744 This is part of the mandatory API for smartset."""
2744 if isinstance(other, fullreposet):
2745 if isinstance(other, fullreposet):
2745 return self
2746 return self
2746 return self.filter(other.__contains__, cache=False)
2747 return self.filter(other.__contains__, cache=False)
2747
2748
2748 def __add__(self, other):
2749 def __add__(self, other):
2749 """Returns a new object with the union of the two collections.
2750 """Returns a new object with the union of the two collections.
2750
2751
2751 This is part of the mandatory API for smartset."""
2752 This is part of the mandatory API for smartset."""
2752 return addset(self, other)
2753 return addset(self, other)
2753
2754
2754 def __sub__(self, other):
2755 def __sub__(self, other):
2755 """Returns a new object with the substraction of the two collections.
2756 """Returns a new object with the substraction of the two collections.
2756
2757
2757 This is part of the mandatory API for smartset."""
2758 This is part of the mandatory API for smartset."""
2758 c = other.__contains__
2759 c = other.__contains__
2759 return self.filter(lambda r: not c(r), cache=False)
2760 return self.filter(lambda r: not c(r), cache=False)
2760
2761
2761 def filter(self, condition, cache=True):
2762 def filter(self, condition, cache=True):
2762 """Returns this smartset filtered by condition as a new smartset.
2763 """Returns this smartset filtered by condition as a new smartset.
2763
2764
2764 `condition` is a callable which takes a revision number and returns a
2765 `condition` is a callable which takes a revision number and returns a
2765 boolean.
2766 boolean.
2766
2767
2767 This is part of the mandatory API for smartset."""
2768 This is part of the mandatory API for smartset."""
2768 # builtin cannot be cached. but do not needs to
2769 # builtin cannot be cached. but do not needs to
2769 if cache and util.safehasattr(condition, 'func_code'):
2770 if cache and util.safehasattr(condition, 'func_code'):
2770 condition = util.cachefunc(condition)
2771 condition = util.cachefunc(condition)
2771 return filteredset(self, condition)
2772 return filteredset(self, condition)
2772
2773
2773 class baseset(abstractsmartset):
2774 class baseset(abstractsmartset):
2774 """Basic data structure that represents a revset and contains the basic
2775 """Basic data structure that represents a revset and contains the basic
2775 operation that it should be able to perform.
2776 operation that it should be able to perform.
2776
2777
2777 Every method in this class should be implemented by any smartset class.
2778 Every method in this class should be implemented by any smartset class.
2778 """
2779 """
2779 def __init__(self, data=()):
2780 def __init__(self, data=()):
2780 if not isinstance(data, list):
2781 if not isinstance(data, list):
2781 data = list(data)
2782 data = list(data)
2782 self._list = data
2783 self._list = data
2783 self._ascending = None
2784 self._ascending = None
2784
2785
2785 @util.propertycache
2786 @util.propertycache
2786 def _set(self):
2787 def _set(self):
2787 return set(self._list)
2788 return set(self._list)
2788
2789
2789 @util.propertycache
2790 @util.propertycache
2790 def _asclist(self):
2791 def _asclist(self):
2791 asclist = self._list[:]
2792 asclist = self._list[:]
2792 asclist.sort()
2793 asclist.sort()
2793 return asclist
2794 return asclist
2794
2795
2795 def __iter__(self):
2796 def __iter__(self):
2796 if self._ascending is None:
2797 if self._ascending is None:
2797 return iter(self._list)
2798 return iter(self._list)
2798 elif self._ascending:
2799 elif self._ascending:
2799 return iter(self._asclist)
2800 return iter(self._asclist)
2800 else:
2801 else:
2801 return reversed(self._asclist)
2802 return reversed(self._asclist)
2802
2803
2803 def fastasc(self):
2804 def fastasc(self):
2804 return iter(self._asclist)
2805 return iter(self._asclist)
2805
2806
2806 def fastdesc(self):
2807 def fastdesc(self):
2807 return reversed(self._asclist)
2808 return reversed(self._asclist)
2808
2809
2809 @util.propertycache
2810 @util.propertycache
2810 def __contains__(self):
2811 def __contains__(self):
2811 return self._set.__contains__
2812 return self._set.__contains__
2812
2813
2813 def __nonzero__(self):
2814 def __nonzero__(self):
2814 return bool(self._list)
2815 return bool(self._list)
2815
2816
2816 def sort(self, reverse=False):
2817 def sort(self, reverse=False):
2817 self._ascending = not bool(reverse)
2818 self._ascending = not bool(reverse)
2818
2819
2819 def reverse(self):
2820 def reverse(self):
2820 if self._ascending is None:
2821 if self._ascending is None:
2821 self._list.reverse()
2822 self._list.reverse()
2822 else:
2823 else:
2823 self._ascending = not self._ascending
2824 self._ascending = not self._ascending
2824
2825
2825 def __len__(self):
2826 def __len__(self):
2826 return len(self._list)
2827 return len(self._list)
2827
2828
2828 def isascending(self):
2829 def isascending(self):
2829 """Returns True if the collection is ascending order, False if not.
2830 """Returns True if the collection is ascending order, False if not.
2830
2831
2831 This is part of the mandatory API for smartset."""
2832 This is part of the mandatory API for smartset."""
2832 if len(self) <= 1:
2833 if len(self) <= 1:
2833 return True
2834 return True
2834 return self._ascending is not None and self._ascending
2835 return self._ascending is not None and self._ascending
2835
2836
2836 def isdescending(self):
2837 def isdescending(self):
2837 """Returns True if the collection is descending order, False if not.
2838 """Returns True if the collection is descending order, False if not.
2838
2839
2839 This is part of the mandatory API for smartset."""
2840 This is part of the mandatory API for smartset."""
2840 if len(self) <= 1:
2841 if len(self) <= 1:
2841 return True
2842 return True
2842 return self._ascending is not None and not self._ascending
2843 return self._ascending is not None and not self._ascending
2843
2844
2844 def first(self):
2845 def first(self):
2845 if self:
2846 if self:
2846 if self._ascending is None:
2847 if self._ascending is None:
2847 return self._list[0]
2848 return self._list[0]
2848 elif self._ascending:
2849 elif self._ascending:
2849 return self._asclist[0]
2850 return self._asclist[0]
2850 else:
2851 else:
2851 return self._asclist[-1]
2852 return self._asclist[-1]
2852 return None
2853 return None
2853
2854
2854 def last(self):
2855 def last(self):
2855 if self:
2856 if self:
2856 if self._ascending is None:
2857 if self._ascending is None:
2857 return self._list[-1]
2858 return self._list[-1]
2858 elif self._ascending:
2859 elif self._ascending:
2859 return self._asclist[-1]
2860 return self._asclist[-1]
2860 else:
2861 else:
2861 return self._asclist[0]
2862 return self._asclist[0]
2862 return None
2863 return None
2863
2864
2864 def __repr__(self):
2865 def __repr__(self):
2865 d = {None: '', False: '-', True: '+'}[self._ascending]
2866 d = {None: '', False: '-', True: '+'}[self._ascending]
2866 return '<%s%s %r>' % (type(self).__name__, d, self._list)
2867 return '<%s%s %r>' % (type(self).__name__, d, self._list)
2867
2868
2868 class filteredset(abstractsmartset):
2869 class filteredset(abstractsmartset):
2869 """Duck type for baseset class which iterates lazily over the revisions in
2870 """Duck type for baseset class which iterates lazily over the revisions in
2870 the subset and contains a function which tests for membership in the
2871 the subset and contains a function which tests for membership in the
2871 revset
2872 revset
2872 """
2873 """
2873 def __init__(self, subset, condition=lambda x: True):
2874 def __init__(self, subset, condition=lambda x: True):
2874 """
2875 """
2875 condition: a function that decide whether a revision in the subset
2876 condition: a function that decide whether a revision in the subset
2876 belongs to the revset or not.
2877 belongs to the revset or not.
2877 """
2878 """
2878 self._subset = subset
2879 self._subset = subset
2879 self._condition = condition
2880 self._condition = condition
2880 self._cache = {}
2881 self._cache = {}
2881
2882
2882 def __contains__(self, x):
2883 def __contains__(self, x):
2883 c = self._cache
2884 c = self._cache
2884 if x not in c:
2885 if x not in c:
2885 v = c[x] = x in self._subset and self._condition(x)
2886 v = c[x] = x in self._subset and self._condition(x)
2886 return v
2887 return v
2887 return c[x]
2888 return c[x]
2888
2889
2889 def __iter__(self):
2890 def __iter__(self):
2890 return self._iterfilter(self._subset)
2891 return self._iterfilter(self._subset)
2891
2892
2892 def _iterfilter(self, it):
2893 def _iterfilter(self, it):
2893 cond = self._condition
2894 cond = self._condition
2894 for x in it:
2895 for x in it:
2895 if cond(x):
2896 if cond(x):
2896 yield x
2897 yield x
2897
2898
2898 @property
2899 @property
2899 def fastasc(self):
2900 def fastasc(self):
2900 it = self._subset.fastasc
2901 it = self._subset.fastasc
2901 if it is None:
2902 if it is None:
2902 return None
2903 return None
2903 return lambda: self._iterfilter(it())
2904 return lambda: self._iterfilter(it())
2904
2905
2905 @property
2906 @property
2906 def fastdesc(self):
2907 def fastdesc(self):
2907 it = self._subset.fastdesc
2908 it = self._subset.fastdesc
2908 if it is None:
2909 if it is None:
2909 return None
2910 return None
2910 return lambda: self._iterfilter(it())
2911 return lambda: self._iterfilter(it())
2911
2912
2912 def __nonzero__(self):
2913 def __nonzero__(self):
2913 for r in self:
2914 for r in self:
2914 return True
2915 return True
2915 return False
2916 return False
2916
2917
2917 def __len__(self):
2918 def __len__(self):
2918 # Basic implementation to be changed in future patches.
2919 # Basic implementation to be changed in future patches.
2919 l = baseset([r for r in self])
2920 l = baseset([r for r in self])
2920 return len(l)
2921 return len(l)
2921
2922
2922 def sort(self, reverse=False):
2923 def sort(self, reverse=False):
2923 self._subset.sort(reverse=reverse)
2924 self._subset.sort(reverse=reverse)
2924
2925
2925 def reverse(self):
2926 def reverse(self):
2926 self._subset.reverse()
2927 self._subset.reverse()
2927
2928
2928 def isascending(self):
2929 def isascending(self):
2929 return self._subset.isascending()
2930 return self._subset.isascending()
2930
2931
2931 def isdescending(self):
2932 def isdescending(self):
2932 return self._subset.isdescending()
2933 return self._subset.isdescending()
2933
2934
2934 def first(self):
2935 def first(self):
2935 for x in self:
2936 for x in self:
2936 return x
2937 return x
2937 return None
2938 return None
2938
2939
2939 def last(self):
2940 def last(self):
2940 it = None
2941 it = None
2941 if self._subset.isascending:
2942 if self._subset.isascending:
2942 it = self.fastdesc
2943 it = self.fastdesc
2943 elif self._subset.isdescending:
2944 elif self._subset.isdescending:
2944 it = self.fastdesc
2945 it = self.fastdesc
2945 if it is None:
2946 if it is None:
2946 # slowly consume everything. This needs improvement
2947 # slowly consume everything. This needs improvement
2947 it = lambda: reversed(list(self))
2948 it = lambda: reversed(list(self))
2948 for x in it():
2949 for x in it():
2949 return x
2950 return x
2950 return None
2951 return None
2951
2952
2952 def __repr__(self):
2953 def __repr__(self):
2953 return '<%s %r>' % (type(self).__name__, self._subset)
2954 return '<%s %r>' % (type(self).__name__, self._subset)
2954
2955
2955 def _iterordered(ascending, iter1, iter2):
2956 def _iterordered(ascending, iter1, iter2):
2956 """produce an ordered iteration from two iterators with the same order
2957 """produce an ordered iteration from two iterators with the same order
2957
2958
2958 The ascending is used to indicated the iteration direction.
2959 The ascending is used to indicated the iteration direction.
2959 """
2960 """
2960 choice = max
2961 choice = max
2961 if ascending:
2962 if ascending:
2962 choice = min
2963 choice = min
2963
2964
2964 val1 = None
2965 val1 = None
2965 val2 = None
2966 val2 = None
2966 try:
2967 try:
2967 # Consume both iterators in an ordered way until one is empty
2968 # Consume both iterators in an ordered way until one is empty
2968 while True:
2969 while True:
2969 if val1 is None:
2970 if val1 is None:
2970 val1 = iter1.next()
2971 val1 = iter1.next()
2971 if val2 is None:
2972 if val2 is None:
2972 val2 = iter2.next()
2973 val2 = iter2.next()
2973 next = choice(val1, val2)
2974 next = choice(val1, val2)
2974 yield next
2975 yield next
2975 if val1 == next:
2976 if val1 == next:
2976 val1 = None
2977 val1 = None
2977 if val2 == next:
2978 if val2 == next:
2978 val2 = None
2979 val2 = None
2979 except StopIteration:
2980 except StopIteration:
2980 # Flush any remaining values and consume the other one
2981 # Flush any remaining values and consume the other one
2981 it = iter2
2982 it = iter2
2982 if val1 is not None:
2983 if val1 is not None:
2983 yield val1
2984 yield val1
2984 it = iter1
2985 it = iter1
2985 elif val2 is not None:
2986 elif val2 is not None:
2986 # might have been equality and both are empty
2987 # might have been equality and both are empty
2987 yield val2
2988 yield val2
2988 for val in it:
2989 for val in it:
2989 yield val
2990 yield val
2990
2991
2991 class addset(abstractsmartset):
2992 class addset(abstractsmartset):
2992 """Represent the addition of two sets
2993 """Represent the addition of two sets
2993
2994
2994 Wrapper structure for lazily adding two structures without losing much
2995 Wrapper structure for lazily adding two structures without losing much
2995 performance on the __contains__ method
2996 performance on the __contains__ method
2996
2997
2997 If the ascending attribute is set, that means the two structures are
2998 If the ascending attribute is set, that means the two structures are
2998 ordered in either an ascending or descending way. Therefore, we can add
2999 ordered in either an ascending or descending way. Therefore, we can add
2999 them maintaining the order by iterating over both at the same time
3000 them maintaining the order by iterating over both at the same time
3000
3001
3001 >>> xs = baseset([0, 3, 2])
3002 >>> xs = baseset([0, 3, 2])
3002 >>> ys = baseset([5, 2, 4])
3003 >>> ys = baseset([5, 2, 4])
3003
3004
3004 >>> rs = addset(xs, ys)
3005 >>> rs = addset(xs, ys)
3005 >>> bool(rs), 0 in rs, 1 in rs, 5 in rs, rs.first(), rs.last()
3006 >>> bool(rs), 0 in rs, 1 in rs, 5 in rs, rs.first(), rs.last()
3006 (True, True, False, True, 0, 4)
3007 (True, True, False, True, 0, 4)
3007 >>> rs = addset(xs, baseset([]))
3008 >>> rs = addset(xs, baseset([]))
3008 >>> bool(rs), 0 in rs, 1 in rs, rs.first(), rs.last()
3009 >>> bool(rs), 0 in rs, 1 in rs, rs.first(), rs.last()
3009 (True, True, False, 0, 2)
3010 (True, True, False, 0, 2)
3010 >>> rs = addset(baseset([]), baseset([]))
3011 >>> rs = addset(baseset([]), baseset([]))
3011 >>> bool(rs), 0 in rs, rs.first(), rs.last()
3012 >>> bool(rs), 0 in rs, rs.first(), rs.last()
3012 (False, False, None, None)
3013 (False, False, None, None)
3013
3014
3014 iterate unsorted:
3015 iterate unsorted:
3015 >>> rs = addset(xs, ys)
3016 >>> rs = addset(xs, ys)
3016 >>> [x for x in rs] # without _genlist
3017 >>> [x for x in rs] # without _genlist
3017 [0, 3, 2, 5, 4]
3018 [0, 3, 2, 5, 4]
3018 >>> assert not rs._genlist
3019 >>> assert not rs._genlist
3019 >>> len(rs)
3020 >>> len(rs)
3020 5
3021 5
3021 >>> [x for x in rs] # with _genlist
3022 >>> [x for x in rs] # with _genlist
3022 [0, 3, 2, 5, 4]
3023 [0, 3, 2, 5, 4]
3023 >>> assert rs._genlist
3024 >>> assert rs._genlist
3024
3025
3025 iterate ascending:
3026 iterate ascending:
3026 >>> rs = addset(xs, ys, ascending=True)
3027 >>> rs = addset(xs, ys, ascending=True)
3027 >>> [x for x in rs], [x for x in rs.fastasc()] # without _asclist
3028 >>> [x for x in rs], [x for x in rs.fastasc()] # without _asclist
3028 ([0, 2, 3, 4, 5], [0, 2, 3, 4, 5])
3029 ([0, 2, 3, 4, 5], [0, 2, 3, 4, 5])
3029 >>> assert not rs._asclist
3030 >>> assert not rs._asclist
3030 >>> len(rs)
3031 >>> len(rs)
3031 5
3032 5
3032 >>> [x for x in rs], [x for x in rs.fastasc()]
3033 >>> [x for x in rs], [x for x in rs.fastasc()]
3033 ([0, 2, 3, 4, 5], [0, 2, 3, 4, 5])
3034 ([0, 2, 3, 4, 5], [0, 2, 3, 4, 5])
3034 >>> assert rs._asclist
3035 >>> assert rs._asclist
3035
3036
3036 iterate descending:
3037 iterate descending:
3037 >>> rs = addset(xs, ys, ascending=False)
3038 >>> rs = addset(xs, ys, ascending=False)
3038 >>> [x for x in rs], [x for x in rs.fastdesc()] # without _asclist
3039 >>> [x for x in rs], [x for x in rs.fastdesc()] # without _asclist
3039 ([5, 4, 3, 2, 0], [5, 4, 3, 2, 0])
3040 ([5, 4, 3, 2, 0], [5, 4, 3, 2, 0])
3040 >>> assert not rs._asclist
3041 >>> assert not rs._asclist
3041 >>> len(rs)
3042 >>> len(rs)
3042 5
3043 5
3043 >>> [x for x in rs], [x for x in rs.fastdesc()]
3044 >>> [x for x in rs], [x for x in rs.fastdesc()]
3044 ([5, 4, 3, 2, 0], [5, 4, 3, 2, 0])
3045 ([5, 4, 3, 2, 0], [5, 4, 3, 2, 0])
3045 >>> assert rs._asclist
3046 >>> assert rs._asclist
3046
3047
3047 iterate ascending without fastasc:
3048 iterate ascending without fastasc:
3048 >>> rs = addset(xs, generatorset(ys), ascending=True)
3049 >>> rs = addset(xs, generatorset(ys), ascending=True)
3049 >>> assert rs.fastasc is None
3050 >>> assert rs.fastasc is None
3050 >>> [x for x in rs]
3051 >>> [x for x in rs]
3051 [0, 2, 3, 4, 5]
3052 [0, 2, 3, 4, 5]
3052
3053
3053 iterate descending without fastdesc:
3054 iterate descending without fastdesc:
3054 >>> rs = addset(generatorset(xs), ys, ascending=False)
3055 >>> rs = addset(generatorset(xs), ys, ascending=False)
3055 >>> assert rs.fastdesc is None
3056 >>> assert rs.fastdesc is None
3056 >>> [x for x in rs]
3057 >>> [x for x in rs]
3057 [5, 4, 3, 2, 0]
3058 [5, 4, 3, 2, 0]
3058 """
3059 """
3059 def __init__(self, revs1, revs2, ascending=None):
3060 def __init__(self, revs1, revs2, ascending=None):
3060 self._r1 = revs1
3061 self._r1 = revs1
3061 self._r2 = revs2
3062 self._r2 = revs2
3062 self._iter = None
3063 self._iter = None
3063 self._ascending = ascending
3064 self._ascending = ascending
3064 self._genlist = None
3065 self._genlist = None
3065 self._asclist = None
3066 self._asclist = None
3066
3067
3067 def __len__(self):
3068 def __len__(self):
3068 return len(self._list)
3069 return len(self._list)
3069
3070
3070 def __nonzero__(self):
3071 def __nonzero__(self):
3071 return bool(self._r1) or bool(self._r2)
3072 return bool(self._r1) or bool(self._r2)
3072
3073
3073 @util.propertycache
3074 @util.propertycache
3074 def _list(self):
3075 def _list(self):
3075 if not self._genlist:
3076 if not self._genlist:
3076 self._genlist = baseset(iter(self))
3077 self._genlist = baseset(iter(self))
3077 return self._genlist
3078 return self._genlist
3078
3079
3079 def __iter__(self):
3080 def __iter__(self):
3080 """Iterate over both collections without repeating elements
3081 """Iterate over both collections without repeating elements
3081
3082
3082 If the ascending attribute is not set, iterate over the first one and
3083 If the ascending attribute is not set, iterate over the first one and
3083 then over the second one checking for membership on the first one so we
3084 then over the second one checking for membership on the first one so we
3084 dont yield any duplicates.
3085 dont yield any duplicates.
3085
3086
3086 If the ascending attribute is set, iterate over both collections at the
3087 If the ascending attribute is set, iterate over both collections at the
3087 same time, yielding only one value at a time in the given order.
3088 same time, yielding only one value at a time in the given order.
3088 """
3089 """
3089 if self._ascending is None:
3090 if self._ascending is None:
3090 if self._genlist:
3091 if self._genlist:
3091 return iter(self._genlist)
3092 return iter(self._genlist)
3092 def arbitraryordergen():
3093 def arbitraryordergen():
3093 for r in self._r1:
3094 for r in self._r1:
3094 yield r
3095 yield r
3095 inr1 = self._r1.__contains__
3096 inr1 = self._r1.__contains__
3096 for r in self._r2:
3097 for r in self._r2:
3097 if not inr1(r):
3098 if not inr1(r):
3098 yield r
3099 yield r
3099 return arbitraryordergen()
3100 return arbitraryordergen()
3100 # try to use our own fast iterator if it exists
3101 # try to use our own fast iterator if it exists
3101 self._trysetasclist()
3102 self._trysetasclist()
3102 if self._ascending:
3103 if self._ascending:
3103 attr = 'fastasc'
3104 attr = 'fastasc'
3104 else:
3105 else:
3105 attr = 'fastdesc'
3106 attr = 'fastdesc'
3106 it = getattr(self, attr)
3107 it = getattr(self, attr)
3107 if it is not None:
3108 if it is not None:
3108 return it()
3109 return it()
3109 # maybe half of the component supports fast
3110 # maybe half of the component supports fast
3110 # get iterator for _r1
3111 # get iterator for _r1
3111 iter1 = getattr(self._r1, attr)
3112 iter1 = getattr(self._r1, attr)
3112 if iter1 is None:
3113 if iter1 is None:
3113 # let's avoid side effect (not sure it matters)
3114 # let's avoid side effect (not sure it matters)
3114 iter1 = iter(sorted(self._r1, reverse=not self._ascending))
3115 iter1 = iter(sorted(self._r1, reverse=not self._ascending))
3115 else:
3116 else:
3116 iter1 = iter1()
3117 iter1 = iter1()
3117 # get iterator for _r2
3118 # get iterator for _r2
3118 iter2 = getattr(self._r2, attr)
3119 iter2 = getattr(self._r2, attr)
3119 if iter2 is None:
3120 if iter2 is None:
3120 # let's avoid side effect (not sure it matters)
3121 # let's avoid side effect (not sure it matters)
3121 iter2 = iter(sorted(self._r2, reverse=not self._ascending))
3122 iter2 = iter(sorted(self._r2, reverse=not self._ascending))
3122 else:
3123 else:
3123 iter2 = iter2()
3124 iter2 = iter2()
3124 return _iterordered(self._ascending, iter1, iter2)
3125 return _iterordered(self._ascending, iter1, iter2)
3125
3126
3126 def _trysetasclist(self):
3127 def _trysetasclist(self):
3127 """populate the _asclist attribute if possible and necessary"""
3128 """populate the _asclist attribute if possible and necessary"""
3128 if self._genlist is not None and self._asclist is None:
3129 if self._genlist is not None and self._asclist is None:
3129 self._asclist = sorted(self._genlist)
3130 self._asclist = sorted(self._genlist)
3130
3131
3131 @property
3132 @property
3132 def fastasc(self):
3133 def fastasc(self):
3133 self._trysetasclist()
3134 self._trysetasclist()
3134 if self._asclist is not None:
3135 if self._asclist is not None:
3135 return self._asclist.__iter__
3136 return self._asclist.__iter__
3136 iter1 = self._r1.fastasc
3137 iter1 = self._r1.fastasc
3137 iter2 = self._r2.fastasc
3138 iter2 = self._r2.fastasc
3138 if None in (iter1, iter2):
3139 if None in (iter1, iter2):
3139 return None
3140 return None
3140 return lambda: _iterordered(True, iter1(), iter2())
3141 return lambda: _iterordered(True, iter1(), iter2())
3141
3142
3142 @property
3143 @property
3143 def fastdesc(self):
3144 def fastdesc(self):
3144 self._trysetasclist()
3145 self._trysetasclist()
3145 if self._asclist is not None:
3146 if self._asclist is not None:
3146 return self._asclist.__reversed__
3147 return self._asclist.__reversed__
3147 iter1 = self._r1.fastdesc
3148 iter1 = self._r1.fastdesc
3148 iter2 = self._r2.fastdesc
3149 iter2 = self._r2.fastdesc
3149 if None in (iter1, iter2):
3150 if None in (iter1, iter2):
3150 return None
3151 return None
3151 return lambda: _iterordered(False, iter1(), iter2())
3152 return lambda: _iterordered(False, iter1(), iter2())
3152
3153
3153 def __contains__(self, x):
3154 def __contains__(self, x):
3154 return x in self._r1 or x in self._r2
3155 return x in self._r1 or x in self._r2
3155
3156
3156 def sort(self, reverse=False):
3157 def sort(self, reverse=False):
3157 """Sort the added set
3158 """Sort the added set
3158
3159
3159 For this we use the cached list with all the generated values and if we
3160 For this we use the cached list with all the generated values and if we
3160 know they are ascending or descending we can sort them in a smart way.
3161 know they are ascending or descending we can sort them in a smart way.
3161 """
3162 """
3162 self._ascending = not reverse
3163 self._ascending = not reverse
3163
3164
3164 def isascending(self):
3165 def isascending(self):
3165 return self._ascending is not None and self._ascending
3166 return self._ascending is not None and self._ascending
3166
3167
3167 def isdescending(self):
3168 def isdescending(self):
3168 return self._ascending is not None and not self._ascending
3169 return self._ascending is not None and not self._ascending
3169
3170
3170 def reverse(self):
3171 def reverse(self):
3171 if self._ascending is None:
3172 if self._ascending is None:
3172 self._list.reverse()
3173 self._list.reverse()
3173 else:
3174 else:
3174 self._ascending = not self._ascending
3175 self._ascending = not self._ascending
3175
3176
3176 def first(self):
3177 def first(self):
3177 for x in self:
3178 for x in self:
3178 return x
3179 return x
3179 return None
3180 return None
3180
3181
3181 def last(self):
3182 def last(self):
3182 self.reverse()
3183 self.reverse()
3183 val = self.first()
3184 val = self.first()
3184 self.reverse()
3185 self.reverse()
3185 return val
3186 return val
3186
3187
3187 def __repr__(self):
3188 def __repr__(self):
3188 d = {None: '', False: '-', True: '+'}[self._ascending]
3189 d = {None: '', False: '-', True: '+'}[self._ascending]
3189 return '<%s%s %r, %r>' % (type(self).__name__, d, self._r1, self._r2)
3190 return '<%s%s %r, %r>' % (type(self).__name__, d, self._r1, self._r2)
3190
3191
3191 class generatorset(abstractsmartset):
3192 class generatorset(abstractsmartset):
3192 """Wrap a generator for lazy iteration
3193 """Wrap a generator for lazy iteration
3193
3194
3194 Wrapper structure for generators that provides lazy membership and can
3195 Wrapper structure for generators that provides lazy membership and can
3195 be iterated more than once.
3196 be iterated more than once.
3196 When asked for membership it generates values until either it finds the
3197 When asked for membership it generates values until either it finds the
3197 requested one or has gone through all the elements in the generator
3198 requested one or has gone through all the elements in the generator
3198 """
3199 """
3199 def __init__(self, gen, iterasc=None):
3200 def __init__(self, gen, iterasc=None):
3200 """
3201 """
3201 gen: a generator producing the values for the generatorset.
3202 gen: a generator producing the values for the generatorset.
3202 """
3203 """
3203 self._gen = gen
3204 self._gen = gen
3204 self._asclist = None
3205 self._asclist = None
3205 self._cache = {}
3206 self._cache = {}
3206 self._genlist = []
3207 self._genlist = []
3207 self._finished = False
3208 self._finished = False
3208 self._ascending = True
3209 self._ascending = True
3209 if iterasc is not None:
3210 if iterasc is not None:
3210 if iterasc:
3211 if iterasc:
3211 self.fastasc = self._iterator
3212 self.fastasc = self._iterator
3212 self.__contains__ = self._asccontains
3213 self.__contains__ = self._asccontains
3213 else:
3214 else:
3214 self.fastdesc = self._iterator
3215 self.fastdesc = self._iterator
3215 self.__contains__ = self._desccontains
3216 self.__contains__ = self._desccontains
3216
3217
3217 def __nonzero__(self):
3218 def __nonzero__(self):
3218 # Do not use 'for r in self' because it will enforce the iteration
3219 # Do not use 'for r in self' because it will enforce the iteration
3219 # order (default ascending), possibly unrolling a whole descending
3220 # order (default ascending), possibly unrolling a whole descending
3220 # iterator.
3221 # iterator.
3221 if self._genlist:
3222 if self._genlist:
3222 return True
3223 return True
3223 for r in self._consumegen():
3224 for r in self._consumegen():
3224 return True
3225 return True
3225 return False
3226 return False
3226
3227
3227 def __contains__(self, x):
3228 def __contains__(self, x):
3228 if x in self._cache:
3229 if x in self._cache:
3229 return self._cache[x]
3230 return self._cache[x]
3230
3231
3231 # Use new values only, as existing values would be cached.
3232 # Use new values only, as existing values would be cached.
3232 for l in self._consumegen():
3233 for l in self._consumegen():
3233 if l == x:
3234 if l == x:
3234 return True
3235 return True
3235
3236
3236 self._cache[x] = False
3237 self._cache[x] = False
3237 return False
3238 return False
3238
3239
3239 def _asccontains(self, x):
3240 def _asccontains(self, x):
3240 """version of contains optimised for ascending generator"""
3241 """version of contains optimised for ascending generator"""
3241 if x in self._cache:
3242 if x in self._cache:
3242 return self._cache[x]
3243 return self._cache[x]
3243
3244
3244 # Use new values only, as existing values would be cached.
3245 # Use new values only, as existing values would be cached.
3245 for l in self._consumegen():
3246 for l in self._consumegen():
3246 if l == x:
3247 if l == x:
3247 return True
3248 return True
3248 if l > x:
3249 if l > x:
3249 break
3250 break
3250
3251
3251 self._cache[x] = False
3252 self._cache[x] = False
3252 return False
3253 return False
3253
3254
3254 def _desccontains(self, x):
3255 def _desccontains(self, x):
3255 """version of contains optimised for descending generator"""
3256 """version of contains optimised for descending generator"""
3256 if x in self._cache:
3257 if x in self._cache:
3257 return self._cache[x]
3258 return self._cache[x]
3258
3259
3259 # Use new values only, as existing values would be cached.
3260 # Use new values only, as existing values would be cached.
3260 for l in self._consumegen():
3261 for l in self._consumegen():
3261 if l == x:
3262 if l == x:
3262 return True
3263 return True
3263 if l < x:
3264 if l < x:
3264 break
3265 break
3265
3266
3266 self._cache[x] = False
3267 self._cache[x] = False
3267 return False
3268 return False
3268
3269
3269 def __iter__(self):
3270 def __iter__(self):
3270 if self._ascending:
3271 if self._ascending:
3271 it = self.fastasc
3272 it = self.fastasc
3272 else:
3273 else:
3273 it = self.fastdesc
3274 it = self.fastdesc
3274 if it is not None:
3275 if it is not None:
3275 return it()
3276 return it()
3276 # we need to consume the iterator
3277 # we need to consume the iterator
3277 for x in self._consumegen():
3278 for x in self._consumegen():
3278 pass
3279 pass
3279 # recall the same code
3280 # recall the same code
3280 return iter(self)
3281 return iter(self)
3281
3282
3282 def _iterator(self):
3283 def _iterator(self):
3283 if self._finished:
3284 if self._finished:
3284 return iter(self._genlist)
3285 return iter(self._genlist)
3285
3286
3286 # We have to use this complex iteration strategy to allow multiple
3287 # We have to use this complex iteration strategy to allow multiple
3287 # iterations at the same time. We need to be able to catch revision
3288 # iterations at the same time. We need to be able to catch revision
3288 # removed from _consumegen and added to genlist in another instance.
3289 # removed from _consumegen and added to genlist in another instance.
3289 #
3290 #
3290 # Getting rid of it would provide an about 15% speed up on this
3291 # Getting rid of it would provide an about 15% speed up on this
3291 # iteration.
3292 # iteration.
3292 genlist = self._genlist
3293 genlist = self._genlist
3293 nextrev = self._consumegen().next
3294 nextrev = self._consumegen().next
3294 _len = len # cache global lookup
3295 _len = len # cache global lookup
3295 def gen():
3296 def gen():
3296 i = 0
3297 i = 0
3297 while True:
3298 while True:
3298 if i < _len(genlist):
3299 if i < _len(genlist):
3299 yield genlist[i]
3300 yield genlist[i]
3300 else:
3301 else:
3301 yield nextrev()
3302 yield nextrev()
3302 i += 1
3303 i += 1
3303 return gen()
3304 return gen()
3304
3305
3305 def _consumegen(self):
3306 def _consumegen(self):
3306 cache = self._cache
3307 cache = self._cache
3307 genlist = self._genlist.append
3308 genlist = self._genlist.append
3308 for item in self._gen:
3309 for item in self._gen:
3309 cache[item] = True
3310 cache[item] = True
3310 genlist(item)
3311 genlist(item)
3311 yield item
3312 yield item
3312 if not self._finished:
3313 if not self._finished:
3313 self._finished = True
3314 self._finished = True
3314 asc = self._genlist[:]
3315 asc = self._genlist[:]
3315 asc.sort()
3316 asc.sort()
3316 self._asclist = asc
3317 self._asclist = asc
3317 self.fastasc = asc.__iter__
3318 self.fastasc = asc.__iter__
3318 self.fastdesc = asc.__reversed__
3319 self.fastdesc = asc.__reversed__
3319
3320
3320 def __len__(self):
3321 def __len__(self):
3321 for x in self._consumegen():
3322 for x in self._consumegen():
3322 pass
3323 pass
3323 return len(self._genlist)
3324 return len(self._genlist)
3324
3325
3325 def sort(self, reverse=False):
3326 def sort(self, reverse=False):
3326 self._ascending = not reverse
3327 self._ascending = not reverse
3327
3328
3328 def reverse(self):
3329 def reverse(self):
3329 self._ascending = not self._ascending
3330 self._ascending = not self._ascending
3330
3331
3331 def isascending(self):
3332 def isascending(self):
3332 return self._ascending
3333 return self._ascending
3333
3334
3334 def isdescending(self):
3335 def isdescending(self):
3335 return not self._ascending
3336 return not self._ascending
3336
3337
3337 def first(self):
3338 def first(self):
3338 if self._ascending:
3339 if self._ascending:
3339 it = self.fastasc
3340 it = self.fastasc
3340 else:
3341 else:
3341 it = self.fastdesc
3342 it = self.fastdesc
3342 if it is None:
3343 if it is None:
3343 # we need to consume all and try again
3344 # we need to consume all and try again
3344 for x in self._consumegen():
3345 for x in self._consumegen():
3345 pass
3346 pass
3346 return self.first()
3347 return self.first()
3347 return next(it(), None)
3348 return next(it(), None)
3348
3349
3349 def last(self):
3350 def last(self):
3350 if self._ascending:
3351 if self._ascending:
3351 it = self.fastdesc
3352 it = self.fastdesc
3352 else:
3353 else:
3353 it = self.fastasc
3354 it = self.fastasc
3354 if it is None:
3355 if it is None:
3355 # we need to consume all and try again
3356 # we need to consume all and try again
3356 for x in self._consumegen():
3357 for x in self._consumegen():
3357 pass
3358 pass
3358 return self.first()
3359 return self.first()
3359 return next(it(), None)
3360 return next(it(), None)
3360
3361
3361 def __repr__(self):
3362 def __repr__(self):
3362 d = {False: '-', True: '+'}[self._ascending]
3363 d = {False: '-', True: '+'}[self._ascending]
3363 return '<%s%s>' % (type(self).__name__, d)
3364 return '<%s%s>' % (type(self).__name__, d)
3364
3365
3365 class spanset(abstractsmartset):
3366 class spanset(abstractsmartset):
3366 """Duck type for baseset class which represents a range of revisions and
3367 """Duck type for baseset class which represents a range of revisions and
3367 can work lazily and without having all the range in memory
3368 can work lazily and without having all the range in memory
3368
3369
3369 Note that spanset(x, y) behave almost like xrange(x, y) except for two
3370 Note that spanset(x, y) behave almost like xrange(x, y) except for two
3370 notable points:
3371 notable points:
3371 - when x < y it will be automatically descending,
3372 - when x < y it will be automatically descending,
3372 - revision filtered with this repoview will be skipped.
3373 - revision filtered with this repoview will be skipped.
3373
3374
3374 """
3375 """
3375 def __init__(self, repo, start=0, end=None):
3376 def __init__(self, repo, start=0, end=None):
3376 """
3377 """
3377 start: first revision included the set
3378 start: first revision included the set
3378 (default to 0)
3379 (default to 0)
3379 end: first revision excluded (last+1)
3380 end: first revision excluded (last+1)
3380 (default to len(repo)
3381 (default to len(repo)
3381
3382
3382 Spanset will be descending if `end` < `start`.
3383 Spanset will be descending if `end` < `start`.
3383 """
3384 """
3384 if end is None:
3385 if end is None:
3385 end = len(repo)
3386 end = len(repo)
3386 self._ascending = start <= end
3387 self._ascending = start <= end
3387 if not self._ascending:
3388 if not self._ascending:
3388 start, end = end + 1, start +1
3389 start, end = end + 1, start +1
3389 self._start = start
3390 self._start = start
3390 self._end = end
3391 self._end = end
3391 self._hiddenrevs = repo.changelog.filteredrevs
3392 self._hiddenrevs = repo.changelog.filteredrevs
3392
3393
3393 def sort(self, reverse=False):
3394 def sort(self, reverse=False):
3394 self._ascending = not reverse
3395 self._ascending = not reverse
3395
3396
3396 def reverse(self):
3397 def reverse(self):
3397 self._ascending = not self._ascending
3398 self._ascending = not self._ascending
3398
3399
3399 def _iterfilter(self, iterrange):
3400 def _iterfilter(self, iterrange):
3400 s = self._hiddenrevs
3401 s = self._hiddenrevs
3401 for r in iterrange:
3402 for r in iterrange:
3402 if r not in s:
3403 if r not in s:
3403 yield r
3404 yield r
3404
3405
3405 def __iter__(self):
3406 def __iter__(self):
3406 if self._ascending:
3407 if self._ascending:
3407 return self.fastasc()
3408 return self.fastasc()
3408 else:
3409 else:
3409 return self.fastdesc()
3410 return self.fastdesc()
3410
3411
3411 def fastasc(self):
3412 def fastasc(self):
3412 iterrange = xrange(self._start, self._end)
3413 iterrange = xrange(self._start, self._end)
3413 if self._hiddenrevs:
3414 if self._hiddenrevs:
3414 return self._iterfilter(iterrange)
3415 return self._iterfilter(iterrange)
3415 return iter(iterrange)
3416 return iter(iterrange)
3416
3417
3417 def fastdesc(self):
3418 def fastdesc(self):
3418 iterrange = xrange(self._end - 1, self._start - 1, -1)
3419 iterrange = xrange(self._end - 1, self._start - 1, -1)
3419 if self._hiddenrevs:
3420 if self._hiddenrevs:
3420 return self._iterfilter(iterrange)
3421 return self._iterfilter(iterrange)
3421 return iter(iterrange)
3422 return iter(iterrange)
3422
3423
3423 def __contains__(self, rev):
3424 def __contains__(self, rev):
3424 hidden = self._hiddenrevs
3425 hidden = self._hiddenrevs
3425 return ((self._start <= rev < self._end)
3426 return ((self._start <= rev < self._end)
3426 and not (hidden and rev in hidden))
3427 and not (hidden and rev in hidden))
3427
3428
3428 def __nonzero__(self):
3429 def __nonzero__(self):
3429 for r in self:
3430 for r in self:
3430 return True
3431 return True
3431 return False
3432 return False
3432
3433
3433 def __len__(self):
3434 def __len__(self):
3434 if not self._hiddenrevs:
3435 if not self._hiddenrevs:
3435 return abs(self._end - self._start)
3436 return abs(self._end - self._start)
3436 else:
3437 else:
3437 count = 0
3438 count = 0
3438 start = self._start
3439 start = self._start
3439 end = self._end
3440 end = self._end
3440 for rev in self._hiddenrevs:
3441 for rev in self._hiddenrevs:
3441 if (end < rev <= start) or (start <= rev < end):
3442 if (end < rev <= start) or (start <= rev < end):
3442 count += 1
3443 count += 1
3443 return abs(self._end - self._start) - count
3444 return abs(self._end - self._start) - count
3444
3445
3445 def isascending(self):
3446 def isascending(self):
3446 return self._ascending
3447 return self._ascending
3447
3448
3448 def isdescending(self):
3449 def isdescending(self):
3449 return not self._ascending
3450 return not self._ascending
3450
3451
3451 def first(self):
3452 def first(self):
3452 if self._ascending:
3453 if self._ascending:
3453 it = self.fastasc
3454 it = self.fastasc
3454 else:
3455 else:
3455 it = self.fastdesc
3456 it = self.fastdesc
3456 for x in it():
3457 for x in it():
3457 return x
3458 return x
3458 return None
3459 return None
3459
3460
3460 def last(self):
3461 def last(self):
3461 if self._ascending:
3462 if self._ascending:
3462 it = self.fastdesc
3463 it = self.fastdesc
3463 else:
3464 else:
3464 it = self.fastasc
3465 it = self.fastasc
3465 for x in it():
3466 for x in it():
3466 return x
3467 return x
3467 return None
3468 return None
3468
3469
3469 def __repr__(self):
3470 def __repr__(self):
3470 d = {False: '-', True: '+'}[self._ascending]
3471 d = {False: '-', True: '+'}[self._ascending]
3471 return '<%s%s %d:%d>' % (type(self).__name__, d,
3472 return '<%s%s %d:%d>' % (type(self).__name__, d,
3472 self._start, self._end - 1)
3473 self._start, self._end - 1)
3473
3474
3474 class fullreposet(spanset):
3475 class fullreposet(spanset):
3475 """a set containing all revisions in the repo
3476 """a set containing all revisions in the repo
3476
3477
3477 This class exists to host special optimization and magic to handle virtual
3478 This class exists to host special optimization and magic to handle virtual
3478 revisions such as "null".
3479 revisions such as "null".
3479 """
3480 """
3480
3481
3481 def __init__(self, repo):
3482 def __init__(self, repo):
3482 super(fullreposet, self).__init__(repo)
3483 super(fullreposet, self).__init__(repo)
3483
3484
3484 def __contains__(self, rev):
3485 def __contains__(self, rev):
3485 # assumes the given rev is valid
3486 # assumes the given rev is valid
3486 hidden = self._hiddenrevs
3487 hidden = self._hiddenrevs
3487 return not (hidden and rev in hidden)
3488 return not (hidden and rev in hidden)
3488
3489
3489 def __and__(self, other):
3490 def __and__(self, other):
3490 """As self contains the whole repo, all of the other set should also be
3491 """As self contains the whole repo, all of the other set should also be
3491 in self. Therefore `self & other = other`.
3492 in self. Therefore `self & other = other`.
3492
3493
3493 This boldly assumes the other contains valid revs only.
3494 This boldly assumes the other contains valid revs only.
3494 """
3495 """
3495 # other not a smartset, make is so
3496 # other not a smartset, make is so
3496 if not util.safehasattr(other, 'isascending'):
3497 if not util.safehasattr(other, 'isascending'):
3497 # filter out hidden revision
3498 # filter out hidden revision
3498 # (this boldly assumes all smartset are pure)
3499 # (this boldly assumes all smartset are pure)
3499 #
3500 #
3500 # `other` was used with "&", let's assume this is a set like
3501 # `other` was used with "&", let's assume this is a set like
3501 # object.
3502 # object.
3502 other = baseset(other - self._hiddenrevs)
3503 other = baseset(other - self._hiddenrevs)
3503
3504
3504 other.sort(reverse=self.isdescending())
3505 other.sort(reverse=self.isdescending())
3505 return other
3506 return other
3506
3507
3507 def prettyformatset(revs):
3508 def prettyformatset(revs):
3508 lines = []
3509 lines = []
3509 rs = repr(revs)
3510 rs = repr(revs)
3510 p = 0
3511 p = 0
3511 while p < len(rs):
3512 while p < len(rs):
3512 q = rs.find('<', p + 1)
3513 q = rs.find('<', p + 1)
3513 if q < 0:
3514 if q < 0:
3514 q = len(rs)
3515 q = len(rs)
3515 l = rs.count('<', 0, p) - rs.count('>', 0, p)
3516 l = rs.count('<', 0, p) - rs.count('>', 0, p)
3516 assert l >= 0
3517 assert l >= 0
3517 lines.append((l, rs[p:q].rstrip()))
3518 lines.append((l, rs[p:q].rstrip()))
3518 p = q
3519 p = q
3519 return '\n'.join(' ' * l + s for l, s in lines)
3520 return '\n'.join(' ' * l + s for l, s in lines)
3520
3521
3521 # tell hggettext to extract docstrings from these functions:
3522 # tell hggettext to extract docstrings from these functions:
3522 i18nfunctions = symbols.values()
3523 i18nfunctions = symbols.values()
@@ -1,2378 +1,2378 b''
1 @ (34) head
1 @ (34) head
2 |
2 |
3 | o (33) head
3 | o (33) head
4 | |
4 | |
5 o | (32) expand
5 o | (32) expand
6 |\ \
6 |\ \
7 | o \ (31) expand
7 | o \ (31) expand
8 | |\ \
8 | |\ \
9 | | o \ (30) expand
9 | | o \ (30) expand
10 | | |\ \
10 | | |\ \
11 | | | o | (29) regular commit
11 | | | o | (29) regular commit
12 | | | | |
12 | | | | |
13 | | o | | (28) merge zero known
13 | | o | | (28) merge zero known
14 | | |\ \ \
14 | | |\ \ \
15 o | | | | | (27) collapse
15 o | | | | | (27) collapse
16 |/ / / / /
16 |/ / / / /
17 | | o---+ (26) merge one known; far right
17 | | o---+ (26) merge one known; far right
18 | | | | |
18 | | | | |
19 +---o | | (25) merge one known; far left
19 +---o | | (25) merge one known; far left
20 | | | | |
20 | | | | |
21 | | o | | (24) merge one known; immediate right
21 | | o | | (24) merge one known; immediate right
22 | | |\| |
22 | | |\| |
23 | | o | | (23) merge one known; immediate left
23 | | o | | (23) merge one known; immediate left
24 | |/| | |
24 | |/| | |
25 +---o---+ (22) merge two known; one far left, one far right
25 +---o---+ (22) merge two known; one far left, one far right
26 | | / /
26 | | / /
27 o | | | (21) expand
27 o | | | (21) expand
28 |\ \ \ \
28 |\ \ \ \
29 | o---+-+ (20) merge two known; two far right
29 | o---+-+ (20) merge two known; two far right
30 | / / /
30 | / / /
31 o | | | (19) expand
31 o | | | (19) expand
32 |\ \ \ \
32 |\ \ \ \
33 +---+---o (18) merge two known; two far left
33 +---+---o (18) merge two known; two far left
34 | | | |
34 | | | |
35 | o | | (17) expand
35 | o | | (17) expand
36 | |\ \ \
36 | |\ \ \
37 | | o---+ (16) merge two known; one immediate right, one near right
37 | | o---+ (16) merge two known; one immediate right, one near right
38 | | |/ /
38 | | |/ /
39 o | | | (15) expand
39 o | | | (15) expand
40 |\ \ \ \
40 |\ \ \ \
41 | o-----+ (14) merge two known; one immediate right, one far right
41 | o-----+ (14) merge two known; one immediate right, one far right
42 | |/ / /
42 | |/ / /
43 o | | | (13) expand
43 o | | | (13) expand
44 |\ \ \ \
44 |\ \ \ \
45 +---o | | (12) merge two known; one immediate right, one far left
45 +---o | | (12) merge two known; one immediate right, one far left
46 | | |/ /
46 | | |/ /
47 | o | | (11) expand
47 | o | | (11) expand
48 | |\ \ \
48 | |\ \ \
49 | | o---+ (10) merge two known; one immediate left, one near right
49 | | o---+ (10) merge two known; one immediate left, one near right
50 | |/ / /
50 | |/ / /
51 o | | | (9) expand
51 o | | | (9) expand
52 |\ \ \ \
52 |\ \ \ \
53 | o-----+ (8) merge two known; one immediate left, one far right
53 | o-----+ (8) merge two known; one immediate left, one far right
54 |/ / / /
54 |/ / / /
55 o | | | (7) expand
55 o | | | (7) expand
56 |\ \ \ \
56 |\ \ \ \
57 +---o | | (6) merge two known; one immediate left, one far left
57 +---o | | (6) merge two known; one immediate left, one far left
58 | |/ / /
58 | |/ / /
59 | o | | (5) expand
59 | o | | (5) expand
60 | |\ \ \
60 | |\ \ \
61 | | o | | (4) merge two known; one immediate left, one immediate right
61 | | o | | (4) merge two known; one immediate left, one immediate right
62 | |/|/ /
62 | |/|/ /
63 | o / / (3) collapse
63 | o / / (3) collapse
64 |/ / /
64 |/ / /
65 o / / (2) collapse
65 o / / (2) collapse
66 |/ /
66 |/ /
67 o / (1) collapse
67 o / (1) collapse
68 |/
68 |/
69 o (0) root
69 o (0) root
70
70
71
71
72 $ commit()
72 $ commit()
73 > {
73 > {
74 > rev=$1
74 > rev=$1
75 > msg=$2
75 > msg=$2
76 > shift 2
76 > shift 2
77 > if [ "$#" -gt 0 ]; then
77 > if [ "$#" -gt 0 ]; then
78 > hg debugsetparents "$@"
78 > hg debugsetparents "$@"
79 > fi
79 > fi
80 > echo $rev > a
80 > echo $rev > a
81 > hg commit -Aqd "$rev 0" -m "($rev) $msg"
81 > hg commit -Aqd "$rev 0" -m "($rev) $msg"
82 > }
82 > }
83
83
84 $ cat > printrevset.py <<EOF
84 $ cat > printrevset.py <<EOF
85 > from mercurial import extensions, revset, commands, cmdutil
85 > from mercurial import extensions, revset, commands, cmdutil
86 >
86 >
87 > def uisetup(ui):
87 > def uisetup(ui):
88 > def printrevset(orig, ui, repo, *pats, **opts):
88 > def printrevset(orig, ui, repo, *pats, **opts):
89 > if opts.get('print_revset'):
89 > if opts.get('print_revset'):
90 > expr = cmdutil.getgraphlogrevs(repo, pats, opts)[1]
90 > expr = cmdutil.getgraphlogrevs(repo, pats, opts)[1]
91 > if expr:
91 > if expr:
92 > tree = revset.parse(expr)[0]
92 > tree = revset.parse(expr)
93 > else:
93 > else:
94 > tree = []
94 > tree = []
95 > ui.write('%r\n' % (opts.get('rev', []),))
95 > ui.write('%r\n' % (opts.get('rev', []),))
96 > ui.write(revset.prettyformat(tree) + '\n')
96 > ui.write(revset.prettyformat(tree) + '\n')
97 > return 0
97 > return 0
98 > return orig(ui, repo, *pats, **opts)
98 > return orig(ui, repo, *pats, **opts)
99 > entry = extensions.wrapcommand(commands.table, 'log', printrevset)
99 > entry = extensions.wrapcommand(commands.table, 'log', printrevset)
100 > entry[1].append(('', 'print-revset', False,
100 > entry[1].append(('', 'print-revset', False,
101 > 'print generated revset and exit (DEPRECATED)'))
101 > 'print generated revset and exit (DEPRECATED)'))
102 > EOF
102 > EOF
103
103
104 $ echo "[extensions]" >> $HGRCPATH
104 $ echo "[extensions]" >> $HGRCPATH
105 $ echo "printrevset=`pwd`/printrevset.py" >> $HGRCPATH
105 $ echo "printrevset=`pwd`/printrevset.py" >> $HGRCPATH
106
106
107 $ hg init repo
107 $ hg init repo
108 $ cd repo
108 $ cd repo
109
109
110 Empty repo:
110 Empty repo:
111
111
112 $ hg log -G
112 $ hg log -G
113
113
114
114
115 Building DAG:
115 Building DAG:
116
116
117 $ commit 0 "root"
117 $ commit 0 "root"
118 $ commit 1 "collapse" 0
118 $ commit 1 "collapse" 0
119 $ commit 2 "collapse" 1
119 $ commit 2 "collapse" 1
120 $ commit 3 "collapse" 2
120 $ commit 3 "collapse" 2
121 $ commit 4 "merge two known; one immediate left, one immediate right" 1 3
121 $ commit 4 "merge two known; one immediate left, one immediate right" 1 3
122 $ commit 5 "expand" 3 4
122 $ commit 5 "expand" 3 4
123 $ commit 6 "merge two known; one immediate left, one far left" 2 5
123 $ commit 6 "merge two known; one immediate left, one far left" 2 5
124 $ commit 7 "expand" 2 5
124 $ commit 7 "expand" 2 5
125 $ commit 8 "merge two known; one immediate left, one far right" 0 7
125 $ commit 8 "merge two known; one immediate left, one far right" 0 7
126 $ commit 9 "expand" 7 8
126 $ commit 9 "expand" 7 8
127 $ commit 10 "merge two known; one immediate left, one near right" 0 6
127 $ commit 10 "merge two known; one immediate left, one near right" 0 6
128 $ commit 11 "expand" 6 10
128 $ commit 11 "expand" 6 10
129 $ commit 12 "merge two known; one immediate right, one far left" 1 9
129 $ commit 12 "merge two known; one immediate right, one far left" 1 9
130 $ commit 13 "expand" 9 11
130 $ commit 13 "expand" 9 11
131 $ commit 14 "merge two known; one immediate right, one far right" 0 12
131 $ commit 14 "merge two known; one immediate right, one far right" 0 12
132 $ commit 15 "expand" 13 14
132 $ commit 15 "expand" 13 14
133 $ commit 16 "merge two known; one immediate right, one near right" 0 1
133 $ commit 16 "merge two known; one immediate right, one near right" 0 1
134 $ commit 17 "expand" 12 16
134 $ commit 17 "expand" 12 16
135 $ commit 18 "merge two known; two far left" 1 15
135 $ commit 18 "merge two known; two far left" 1 15
136 $ commit 19 "expand" 15 17
136 $ commit 19 "expand" 15 17
137 $ commit 20 "merge two known; two far right" 0 18
137 $ commit 20 "merge two known; two far right" 0 18
138 $ commit 21 "expand" 19 20
138 $ commit 21 "expand" 19 20
139 $ commit 22 "merge two known; one far left, one far right" 18 21
139 $ commit 22 "merge two known; one far left, one far right" 18 21
140 $ commit 23 "merge one known; immediate left" 1 22
140 $ commit 23 "merge one known; immediate left" 1 22
141 $ commit 24 "merge one known; immediate right" 0 23
141 $ commit 24 "merge one known; immediate right" 0 23
142 $ commit 25 "merge one known; far left" 21 24
142 $ commit 25 "merge one known; far left" 21 24
143 $ commit 26 "merge one known; far right" 18 25
143 $ commit 26 "merge one known; far right" 18 25
144 $ commit 27 "collapse" 21
144 $ commit 27 "collapse" 21
145 $ commit 28 "merge zero known" 1 26
145 $ commit 28 "merge zero known" 1 26
146 $ commit 29 "regular commit" 0
146 $ commit 29 "regular commit" 0
147 $ commit 30 "expand" 28 29
147 $ commit 30 "expand" 28 29
148 $ commit 31 "expand" 21 30
148 $ commit 31 "expand" 21 30
149 $ commit 32 "expand" 27 31
149 $ commit 32 "expand" 27 31
150 $ commit 33 "head" 18
150 $ commit 33 "head" 18
151 $ commit 34 "head" 32
151 $ commit 34 "head" 32
152
152
153
153
154 $ hg log -G -q
154 $ hg log -G -q
155 @ 34:fea3ac5810e0
155 @ 34:fea3ac5810e0
156 |
156 |
157 | o 33:68608f5145f9
157 | o 33:68608f5145f9
158 | |
158 | |
159 o | 32:d06dffa21a31
159 o | 32:d06dffa21a31
160 |\ \
160 |\ \
161 | o \ 31:621d83e11f67
161 | o \ 31:621d83e11f67
162 | |\ \
162 | |\ \
163 | | o \ 30:6e11cd4b648f
163 | | o \ 30:6e11cd4b648f
164 | | |\ \
164 | | |\ \
165 | | | o | 29:cd9bb2be7593
165 | | | o | 29:cd9bb2be7593
166 | | | | |
166 | | | | |
167 | | o | | 28:44ecd0b9ae99
167 | | o | | 28:44ecd0b9ae99
168 | | |\ \ \
168 | | |\ \ \
169 o | | | | | 27:886ed638191b
169 o | | | | | 27:886ed638191b
170 |/ / / / /
170 |/ / / / /
171 | | o---+ 26:7f25b6c2f0b9
171 | | o---+ 26:7f25b6c2f0b9
172 | | | | |
172 | | | | |
173 +---o | | 25:91da8ed57247
173 +---o | | 25:91da8ed57247
174 | | | | |
174 | | | | |
175 | | o | | 24:a9c19a3d96b7
175 | | o | | 24:a9c19a3d96b7
176 | | |\| |
176 | | |\| |
177 | | o | | 23:a01cddf0766d
177 | | o | | 23:a01cddf0766d
178 | |/| | |
178 | |/| | |
179 +---o---+ 22:e0d9cccacb5d
179 +---o---+ 22:e0d9cccacb5d
180 | | / /
180 | | / /
181 o | | | 21:d42a756af44d
181 o | | | 21:d42a756af44d
182 |\ \ \ \
182 |\ \ \ \
183 | o---+-+ 20:d30ed6450e32
183 | o---+-+ 20:d30ed6450e32
184 | / / /
184 | / / /
185 o | | | 19:31ddc2c1573b
185 o | | | 19:31ddc2c1573b
186 |\ \ \ \
186 |\ \ \ \
187 +---+---o 18:1aa84d96232a
187 +---+---o 18:1aa84d96232a
188 | | | |
188 | | | |
189 | o | | 17:44765d7c06e0
189 | o | | 17:44765d7c06e0
190 | |\ \ \
190 | |\ \ \
191 | | o---+ 16:3677d192927d
191 | | o---+ 16:3677d192927d
192 | | |/ /
192 | | |/ /
193 o | | | 15:1dda3f72782d
193 o | | | 15:1dda3f72782d
194 |\ \ \ \
194 |\ \ \ \
195 | o-----+ 14:8eac370358ef
195 | o-----+ 14:8eac370358ef
196 | |/ / /
196 | |/ / /
197 o | | | 13:22d8966a97e3
197 o | | | 13:22d8966a97e3
198 |\ \ \ \
198 |\ \ \ \
199 +---o | | 12:86b91144a6e9
199 +---o | | 12:86b91144a6e9
200 | | |/ /
200 | | |/ /
201 | o | | 11:832d76e6bdf2
201 | o | | 11:832d76e6bdf2
202 | |\ \ \
202 | |\ \ \
203 | | o---+ 10:74c64d036d72
203 | | o---+ 10:74c64d036d72
204 | |/ / /
204 | |/ / /
205 o | | | 9:7010c0af0a35
205 o | | | 9:7010c0af0a35
206 |\ \ \ \
206 |\ \ \ \
207 | o-----+ 8:7a0b11f71937
207 | o-----+ 8:7a0b11f71937
208 |/ / / /
208 |/ / / /
209 o | | | 7:b632bb1b1224
209 o | | | 7:b632bb1b1224
210 |\ \ \ \
210 |\ \ \ \
211 +---o | | 6:b105a072e251
211 +---o | | 6:b105a072e251
212 | |/ / /
212 | |/ / /
213 | o | | 5:4409d547b708
213 | o | | 5:4409d547b708
214 | |\ \ \
214 | |\ \ \
215 | | o | | 4:26a8bac39d9f
215 | | o | | 4:26a8bac39d9f
216 | |/|/ /
216 | |/|/ /
217 | o / / 3:27eef8ed80b4
217 | o / / 3:27eef8ed80b4
218 |/ / /
218 |/ / /
219 o / / 2:3d9a33b8d1e1
219 o / / 2:3d9a33b8d1e1
220 |/ /
220 |/ /
221 o / 1:6db2ef61d156
221 o / 1:6db2ef61d156
222 |/
222 |/
223 o 0:e6eb3150255d
223 o 0:e6eb3150255d
224
224
225
225
226 $ hg log -G
226 $ hg log -G
227 @ changeset: 34:fea3ac5810e0
227 @ changeset: 34:fea3ac5810e0
228 | tag: tip
228 | tag: tip
229 | parent: 32:d06dffa21a31
229 | parent: 32:d06dffa21a31
230 | user: test
230 | user: test
231 | date: Thu Jan 01 00:00:34 1970 +0000
231 | date: Thu Jan 01 00:00:34 1970 +0000
232 | summary: (34) head
232 | summary: (34) head
233 |
233 |
234 | o changeset: 33:68608f5145f9
234 | o changeset: 33:68608f5145f9
235 | | parent: 18:1aa84d96232a
235 | | parent: 18:1aa84d96232a
236 | | user: test
236 | | user: test
237 | | date: Thu Jan 01 00:00:33 1970 +0000
237 | | date: Thu Jan 01 00:00:33 1970 +0000
238 | | summary: (33) head
238 | | summary: (33) head
239 | |
239 | |
240 o | changeset: 32:d06dffa21a31
240 o | changeset: 32:d06dffa21a31
241 |\ \ parent: 27:886ed638191b
241 |\ \ parent: 27:886ed638191b
242 | | | parent: 31:621d83e11f67
242 | | | parent: 31:621d83e11f67
243 | | | user: test
243 | | | user: test
244 | | | date: Thu Jan 01 00:00:32 1970 +0000
244 | | | date: Thu Jan 01 00:00:32 1970 +0000
245 | | | summary: (32) expand
245 | | | summary: (32) expand
246 | | |
246 | | |
247 | o | changeset: 31:621d83e11f67
247 | o | changeset: 31:621d83e11f67
248 | |\ \ parent: 21:d42a756af44d
248 | |\ \ parent: 21:d42a756af44d
249 | | | | parent: 30:6e11cd4b648f
249 | | | | parent: 30:6e11cd4b648f
250 | | | | user: test
250 | | | | user: test
251 | | | | date: Thu Jan 01 00:00:31 1970 +0000
251 | | | | date: Thu Jan 01 00:00:31 1970 +0000
252 | | | | summary: (31) expand
252 | | | | summary: (31) expand
253 | | | |
253 | | | |
254 | | o | changeset: 30:6e11cd4b648f
254 | | o | changeset: 30:6e11cd4b648f
255 | | |\ \ parent: 28:44ecd0b9ae99
255 | | |\ \ parent: 28:44ecd0b9ae99
256 | | | | | parent: 29:cd9bb2be7593
256 | | | | | parent: 29:cd9bb2be7593
257 | | | | | user: test
257 | | | | | user: test
258 | | | | | date: Thu Jan 01 00:00:30 1970 +0000
258 | | | | | date: Thu Jan 01 00:00:30 1970 +0000
259 | | | | | summary: (30) expand
259 | | | | | summary: (30) expand
260 | | | | |
260 | | | | |
261 | | | o | changeset: 29:cd9bb2be7593
261 | | | o | changeset: 29:cd9bb2be7593
262 | | | | | parent: 0:e6eb3150255d
262 | | | | | parent: 0:e6eb3150255d
263 | | | | | user: test
263 | | | | | user: test
264 | | | | | date: Thu Jan 01 00:00:29 1970 +0000
264 | | | | | date: Thu Jan 01 00:00:29 1970 +0000
265 | | | | | summary: (29) regular commit
265 | | | | | summary: (29) regular commit
266 | | | | |
266 | | | | |
267 | | o | | changeset: 28:44ecd0b9ae99
267 | | o | | changeset: 28:44ecd0b9ae99
268 | | |\ \ \ parent: 1:6db2ef61d156
268 | | |\ \ \ parent: 1:6db2ef61d156
269 | | | | | | parent: 26:7f25b6c2f0b9
269 | | | | | | parent: 26:7f25b6c2f0b9
270 | | | | | | user: test
270 | | | | | | user: test
271 | | | | | | date: Thu Jan 01 00:00:28 1970 +0000
271 | | | | | | date: Thu Jan 01 00:00:28 1970 +0000
272 | | | | | | summary: (28) merge zero known
272 | | | | | | summary: (28) merge zero known
273 | | | | | |
273 | | | | | |
274 o | | | | | changeset: 27:886ed638191b
274 o | | | | | changeset: 27:886ed638191b
275 |/ / / / / parent: 21:d42a756af44d
275 |/ / / / / parent: 21:d42a756af44d
276 | | | | | user: test
276 | | | | | user: test
277 | | | | | date: Thu Jan 01 00:00:27 1970 +0000
277 | | | | | date: Thu Jan 01 00:00:27 1970 +0000
278 | | | | | summary: (27) collapse
278 | | | | | summary: (27) collapse
279 | | | | |
279 | | | | |
280 | | o---+ changeset: 26:7f25b6c2f0b9
280 | | o---+ changeset: 26:7f25b6c2f0b9
281 | | | | | parent: 18:1aa84d96232a
281 | | | | | parent: 18:1aa84d96232a
282 | | | | | parent: 25:91da8ed57247
282 | | | | | parent: 25:91da8ed57247
283 | | | | | user: test
283 | | | | | user: test
284 | | | | | date: Thu Jan 01 00:00:26 1970 +0000
284 | | | | | date: Thu Jan 01 00:00:26 1970 +0000
285 | | | | | summary: (26) merge one known; far right
285 | | | | | summary: (26) merge one known; far right
286 | | | | |
286 | | | | |
287 +---o | | changeset: 25:91da8ed57247
287 +---o | | changeset: 25:91da8ed57247
288 | | | | | parent: 21:d42a756af44d
288 | | | | | parent: 21:d42a756af44d
289 | | | | | parent: 24:a9c19a3d96b7
289 | | | | | parent: 24:a9c19a3d96b7
290 | | | | | user: test
290 | | | | | user: test
291 | | | | | date: Thu Jan 01 00:00:25 1970 +0000
291 | | | | | date: Thu Jan 01 00:00:25 1970 +0000
292 | | | | | summary: (25) merge one known; far left
292 | | | | | summary: (25) merge one known; far left
293 | | | | |
293 | | | | |
294 | | o | | changeset: 24:a9c19a3d96b7
294 | | o | | changeset: 24:a9c19a3d96b7
295 | | |\| | parent: 0:e6eb3150255d
295 | | |\| | parent: 0:e6eb3150255d
296 | | | | | parent: 23:a01cddf0766d
296 | | | | | parent: 23:a01cddf0766d
297 | | | | | user: test
297 | | | | | user: test
298 | | | | | date: Thu Jan 01 00:00:24 1970 +0000
298 | | | | | date: Thu Jan 01 00:00:24 1970 +0000
299 | | | | | summary: (24) merge one known; immediate right
299 | | | | | summary: (24) merge one known; immediate right
300 | | | | |
300 | | | | |
301 | | o | | changeset: 23:a01cddf0766d
301 | | o | | changeset: 23:a01cddf0766d
302 | |/| | | parent: 1:6db2ef61d156
302 | |/| | | parent: 1:6db2ef61d156
303 | | | | | parent: 22:e0d9cccacb5d
303 | | | | | parent: 22:e0d9cccacb5d
304 | | | | | user: test
304 | | | | | user: test
305 | | | | | date: Thu Jan 01 00:00:23 1970 +0000
305 | | | | | date: Thu Jan 01 00:00:23 1970 +0000
306 | | | | | summary: (23) merge one known; immediate left
306 | | | | | summary: (23) merge one known; immediate left
307 | | | | |
307 | | | | |
308 +---o---+ changeset: 22:e0d9cccacb5d
308 +---o---+ changeset: 22:e0d9cccacb5d
309 | | | | parent: 18:1aa84d96232a
309 | | | | parent: 18:1aa84d96232a
310 | | / / parent: 21:d42a756af44d
310 | | / / parent: 21:d42a756af44d
311 | | | | user: test
311 | | | | user: test
312 | | | | date: Thu Jan 01 00:00:22 1970 +0000
312 | | | | date: Thu Jan 01 00:00:22 1970 +0000
313 | | | | summary: (22) merge two known; one far left, one far right
313 | | | | summary: (22) merge two known; one far left, one far right
314 | | | |
314 | | | |
315 o | | | changeset: 21:d42a756af44d
315 o | | | changeset: 21:d42a756af44d
316 |\ \ \ \ parent: 19:31ddc2c1573b
316 |\ \ \ \ parent: 19:31ddc2c1573b
317 | | | | | parent: 20:d30ed6450e32
317 | | | | | parent: 20:d30ed6450e32
318 | | | | | user: test
318 | | | | | user: test
319 | | | | | date: Thu Jan 01 00:00:21 1970 +0000
319 | | | | | date: Thu Jan 01 00:00:21 1970 +0000
320 | | | | | summary: (21) expand
320 | | | | | summary: (21) expand
321 | | | | |
321 | | | | |
322 | o---+-+ changeset: 20:d30ed6450e32
322 | o---+-+ changeset: 20:d30ed6450e32
323 | | | | parent: 0:e6eb3150255d
323 | | | | parent: 0:e6eb3150255d
324 | / / / parent: 18:1aa84d96232a
324 | / / / parent: 18:1aa84d96232a
325 | | | | user: test
325 | | | | user: test
326 | | | | date: Thu Jan 01 00:00:20 1970 +0000
326 | | | | date: Thu Jan 01 00:00:20 1970 +0000
327 | | | | summary: (20) merge two known; two far right
327 | | | | summary: (20) merge two known; two far right
328 | | | |
328 | | | |
329 o | | | changeset: 19:31ddc2c1573b
329 o | | | changeset: 19:31ddc2c1573b
330 |\ \ \ \ parent: 15:1dda3f72782d
330 |\ \ \ \ parent: 15:1dda3f72782d
331 | | | | | parent: 17:44765d7c06e0
331 | | | | | parent: 17:44765d7c06e0
332 | | | | | user: test
332 | | | | | user: test
333 | | | | | date: Thu Jan 01 00:00:19 1970 +0000
333 | | | | | date: Thu Jan 01 00:00:19 1970 +0000
334 | | | | | summary: (19) expand
334 | | | | | summary: (19) expand
335 | | | | |
335 | | | | |
336 +---+---o changeset: 18:1aa84d96232a
336 +---+---o changeset: 18:1aa84d96232a
337 | | | | parent: 1:6db2ef61d156
337 | | | | parent: 1:6db2ef61d156
338 | | | | parent: 15:1dda3f72782d
338 | | | | parent: 15:1dda3f72782d
339 | | | | user: test
339 | | | | user: test
340 | | | | date: Thu Jan 01 00:00:18 1970 +0000
340 | | | | date: Thu Jan 01 00:00:18 1970 +0000
341 | | | | summary: (18) merge two known; two far left
341 | | | | summary: (18) merge two known; two far left
342 | | | |
342 | | | |
343 | o | | changeset: 17:44765d7c06e0
343 | o | | changeset: 17:44765d7c06e0
344 | |\ \ \ parent: 12:86b91144a6e9
344 | |\ \ \ parent: 12:86b91144a6e9
345 | | | | | parent: 16:3677d192927d
345 | | | | | parent: 16:3677d192927d
346 | | | | | user: test
346 | | | | | user: test
347 | | | | | date: Thu Jan 01 00:00:17 1970 +0000
347 | | | | | date: Thu Jan 01 00:00:17 1970 +0000
348 | | | | | summary: (17) expand
348 | | | | | summary: (17) expand
349 | | | | |
349 | | | | |
350 | | o---+ changeset: 16:3677d192927d
350 | | o---+ changeset: 16:3677d192927d
351 | | | | | parent: 0:e6eb3150255d
351 | | | | | parent: 0:e6eb3150255d
352 | | |/ / parent: 1:6db2ef61d156
352 | | |/ / parent: 1:6db2ef61d156
353 | | | | user: test
353 | | | | user: test
354 | | | | date: Thu Jan 01 00:00:16 1970 +0000
354 | | | | date: Thu Jan 01 00:00:16 1970 +0000
355 | | | | summary: (16) merge two known; one immediate right, one near right
355 | | | | summary: (16) merge two known; one immediate right, one near right
356 | | | |
356 | | | |
357 o | | | changeset: 15:1dda3f72782d
357 o | | | changeset: 15:1dda3f72782d
358 |\ \ \ \ parent: 13:22d8966a97e3
358 |\ \ \ \ parent: 13:22d8966a97e3
359 | | | | | parent: 14:8eac370358ef
359 | | | | | parent: 14:8eac370358ef
360 | | | | | user: test
360 | | | | | user: test
361 | | | | | date: Thu Jan 01 00:00:15 1970 +0000
361 | | | | | date: Thu Jan 01 00:00:15 1970 +0000
362 | | | | | summary: (15) expand
362 | | | | | summary: (15) expand
363 | | | | |
363 | | | | |
364 | o-----+ changeset: 14:8eac370358ef
364 | o-----+ changeset: 14:8eac370358ef
365 | | | | | parent: 0:e6eb3150255d
365 | | | | | parent: 0:e6eb3150255d
366 | |/ / / parent: 12:86b91144a6e9
366 | |/ / / parent: 12:86b91144a6e9
367 | | | | user: test
367 | | | | user: test
368 | | | | date: Thu Jan 01 00:00:14 1970 +0000
368 | | | | date: Thu Jan 01 00:00:14 1970 +0000
369 | | | | summary: (14) merge two known; one immediate right, one far right
369 | | | | summary: (14) merge two known; one immediate right, one far right
370 | | | |
370 | | | |
371 o | | | changeset: 13:22d8966a97e3
371 o | | | changeset: 13:22d8966a97e3
372 |\ \ \ \ parent: 9:7010c0af0a35
372 |\ \ \ \ parent: 9:7010c0af0a35
373 | | | | | parent: 11:832d76e6bdf2
373 | | | | | parent: 11:832d76e6bdf2
374 | | | | | user: test
374 | | | | | user: test
375 | | | | | date: Thu Jan 01 00:00:13 1970 +0000
375 | | | | | date: Thu Jan 01 00:00:13 1970 +0000
376 | | | | | summary: (13) expand
376 | | | | | summary: (13) expand
377 | | | | |
377 | | | | |
378 +---o | | changeset: 12:86b91144a6e9
378 +---o | | changeset: 12:86b91144a6e9
379 | | |/ / parent: 1:6db2ef61d156
379 | | |/ / parent: 1:6db2ef61d156
380 | | | | parent: 9:7010c0af0a35
380 | | | | parent: 9:7010c0af0a35
381 | | | | user: test
381 | | | | user: test
382 | | | | date: Thu Jan 01 00:00:12 1970 +0000
382 | | | | date: Thu Jan 01 00:00:12 1970 +0000
383 | | | | summary: (12) merge two known; one immediate right, one far left
383 | | | | summary: (12) merge two known; one immediate right, one far left
384 | | | |
384 | | | |
385 | o | | changeset: 11:832d76e6bdf2
385 | o | | changeset: 11:832d76e6bdf2
386 | |\ \ \ parent: 6:b105a072e251
386 | |\ \ \ parent: 6:b105a072e251
387 | | | | | parent: 10:74c64d036d72
387 | | | | | parent: 10:74c64d036d72
388 | | | | | user: test
388 | | | | | user: test
389 | | | | | date: Thu Jan 01 00:00:11 1970 +0000
389 | | | | | date: Thu Jan 01 00:00:11 1970 +0000
390 | | | | | summary: (11) expand
390 | | | | | summary: (11) expand
391 | | | | |
391 | | | | |
392 | | o---+ changeset: 10:74c64d036d72
392 | | o---+ changeset: 10:74c64d036d72
393 | | | | | parent: 0:e6eb3150255d
393 | | | | | parent: 0:e6eb3150255d
394 | |/ / / parent: 6:b105a072e251
394 | |/ / / parent: 6:b105a072e251
395 | | | | user: test
395 | | | | user: test
396 | | | | date: Thu Jan 01 00:00:10 1970 +0000
396 | | | | date: Thu Jan 01 00:00:10 1970 +0000
397 | | | | summary: (10) merge two known; one immediate left, one near right
397 | | | | summary: (10) merge two known; one immediate left, one near right
398 | | | |
398 | | | |
399 o | | | changeset: 9:7010c0af0a35
399 o | | | changeset: 9:7010c0af0a35
400 |\ \ \ \ parent: 7:b632bb1b1224
400 |\ \ \ \ parent: 7:b632bb1b1224
401 | | | | | parent: 8:7a0b11f71937
401 | | | | | parent: 8:7a0b11f71937
402 | | | | | user: test
402 | | | | | user: test
403 | | | | | date: Thu Jan 01 00:00:09 1970 +0000
403 | | | | | date: Thu Jan 01 00:00:09 1970 +0000
404 | | | | | summary: (9) expand
404 | | | | | summary: (9) expand
405 | | | | |
405 | | | | |
406 | o-----+ changeset: 8:7a0b11f71937
406 | o-----+ changeset: 8:7a0b11f71937
407 | | | | | parent: 0:e6eb3150255d
407 | | | | | parent: 0:e6eb3150255d
408 |/ / / / parent: 7:b632bb1b1224
408 |/ / / / parent: 7:b632bb1b1224
409 | | | | user: test
409 | | | | user: test
410 | | | | date: Thu Jan 01 00:00:08 1970 +0000
410 | | | | date: Thu Jan 01 00:00:08 1970 +0000
411 | | | | summary: (8) merge two known; one immediate left, one far right
411 | | | | summary: (8) merge two known; one immediate left, one far right
412 | | | |
412 | | | |
413 o | | | changeset: 7:b632bb1b1224
413 o | | | changeset: 7:b632bb1b1224
414 |\ \ \ \ parent: 2:3d9a33b8d1e1
414 |\ \ \ \ parent: 2:3d9a33b8d1e1
415 | | | | | parent: 5:4409d547b708
415 | | | | | parent: 5:4409d547b708
416 | | | | | user: test
416 | | | | | user: test
417 | | | | | date: Thu Jan 01 00:00:07 1970 +0000
417 | | | | | date: Thu Jan 01 00:00:07 1970 +0000
418 | | | | | summary: (7) expand
418 | | | | | summary: (7) expand
419 | | | | |
419 | | | | |
420 +---o | | changeset: 6:b105a072e251
420 +---o | | changeset: 6:b105a072e251
421 | |/ / / parent: 2:3d9a33b8d1e1
421 | |/ / / parent: 2:3d9a33b8d1e1
422 | | | | parent: 5:4409d547b708
422 | | | | parent: 5:4409d547b708
423 | | | | user: test
423 | | | | user: test
424 | | | | date: Thu Jan 01 00:00:06 1970 +0000
424 | | | | date: Thu Jan 01 00:00:06 1970 +0000
425 | | | | summary: (6) merge two known; one immediate left, one far left
425 | | | | summary: (6) merge two known; one immediate left, one far left
426 | | | |
426 | | | |
427 | o | | changeset: 5:4409d547b708
427 | o | | changeset: 5:4409d547b708
428 | |\ \ \ parent: 3:27eef8ed80b4
428 | |\ \ \ parent: 3:27eef8ed80b4
429 | | | | | parent: 4:26a8bac39d9f
429 | | | | | parent: 4:26a8bac39d9f
430 | | | | | user: test
430 | | | | | user: test
431 | | | | | date: Thu Jan 01 00:00:05 1970 +0000
431 | | | | | date: Thu Jan 01 00:00:05 1970 +0000
432 | | | | | summary: (5) expand
432 | | | | | summary: (5) expand
433 | | | | |
433 | | | | |
434 | | o | | changeset: 4:26a8bac39d9f
434 | | o | | changeset: 4:26a8bac39d9f
435 | |/|/ / parent: 1:6db2ef61d156
435 | |/|/ / parent: 1:6db2ef61d156
436 | | | | parent: 3:27eef8ed80b4
436 | | | | parent: 3:27eef8ed80b4
437 | | | | user: test
437 | | | | user: test
438 | | | | date: Thu Jan 01 00:00:04 1970 +0000
438 | | | | date: Thu Jan 01 00:00:04 1970 +0000
439 | | | | summary: (4) merge two known; one immediate left, one immediate right
439 | | | | summary: (4) merge two known; one immediate left, one immediate right
440 | | | |
440 | | | |
441 | o | | changeset: 3:27eef8ed80b4
441 | o | | changeset: 3:27eef8ed80b4
442 |/ / / user: test
442 |/ / / user: test
443 | | | date: Thu Jan 01 00:00:03 1970 +0000
443 | | | date: Thu Jan 01 00:00:03 1970 +0000
444 | | | summary: (3) collapse
444 | | | summary: (3) collapse
445 | | |
445 | | |
446 o | | changeset: 2:3d9a33b8d1e1
446 o | | changeset: 2:3d9a33b8d1e1
447 |/ / user: test
447 |/ / user: test
448 | | date: Thu Jan 01 00:00:02 1970 +0000
448 | | date: Thu Jan 01 00:00:02 1970 +0000
449 | | summary: (2) collapse
449 | | summary: (2) collapse
450 | |
450 | |
451 o | changeset: 1:6db2ef61d156
451 o | changeset: 1:6db2ef61d156
452 |/ user: test
452 |/ user: test
453 | date: Thu Jan 01 00:00:01 1970 +0000
453 | date: Thu Jan 01 00:00:01 1970 +0000
454 | summary: (1) collapse
454 | summary: (1) collapse
455 |
455 |
456 o changeset: 0:e6eb3150255d
456 o changeset: 0:e6eb3150255d
457 user: test
457 user: test
458 date: Thu Jan 01 00:00:00 1970 +0000
458 date: Thu Jan 01 00:00:00 1970 +0000
459 summary: (0) root
459 summary: (0) root
460
460
461
461
462 File glog:
462 File glog:
463 $ hg log -G a
463 $ hg log -G a
464 @ changeset: 34:fea3ac5810e0
464 @ changeset: 34:fea3ac5810e0
465 | tag: tip
465 | tag: tip
466 | parent: 32:d06dffa21a31
466 | parent: 32:d06dffa21a31
467 | user: test
467 | user: test
468 | date: Thu Jan 01 00:00:34 1970 +0000
468 | date: Thu Jan 01 00:00:34 1970 +0000
469 | summary: (34) head
469 | summary: (34) head
470 |
470 |
471 | o changeset: 33:68608f5145f9
471 | o changeset: 33:68608f5145f9
472 | | parent: 18:1aa84d96232a
472 | | parent: 18:1aa84d96232a
473 | | user: test
473 | | user: test
474 | | date: Thu Jan 01 00:00:33 1970 +0000
474 | | date: Thu Jan 01 00:00:33 1970 +0000
475 | | summary: (33) head
475 | | summary: (33) head
476 | |
476 | |
477 o | changeset: 32:d06dffa21a31
477 o | changeset: 32:d06dffa21a31
478 |\ \ parent: 27:886ed638191b
478 |\ \ parent: 27:886ed638191b
479 | | | parent: 31:621d83e11f67
479 | | | parent: 31:621d83e11f67
480 | | | user: test
480 | | | user: test
481 | | | date: Thu Jan 01 00:00:32 1970 +0000
481 | | | date: Thu Jan 01 00:00:32 1970 +0000
482 | | | summary: (32) expand
482 | | | summary: (32) expand
483 | | |
483 | | |
484 | o | changeset: 31:621d83e11f67
484 | o | changeset: 31:621d83e11f67
485 | |\ \ parent: 21:d42a756af44d
485 | |\ \ parent: 21:d42a756af44d
486 | | | | parent: 30:6e11cd4b648f
486 | | | | parent: 30:6e11cd4b648f
487 | | | | user: test
487 | | | | user: test
488 | | | | date: Thu Jan 01 00:00:31 1970 +0000
488 | | | | date: Thu Jan 01 00:00:31 1970 +0000
489 | | | | summary: (31) expand
489 | | | | summary: (31) expand
490 | | | |
490 | | | |
491 | | o | changeset: 30:6e11cd4b648f
491 | | o | changeset: 30:6e11cd4b648f
492 | | |\ \ parent: 28:44ecd0b9ae99
492 | | |\ \ parent: 28:44ecd0b9ae99
493 | | | | | parent: 29:cd9bb2be7593
493 | | | | | parent: 29:cd9bb2be7593
494 | | | | | user: test
494 | | | | | user: test
495 | | | | | date: Thu Jan 01 00:00:30 1970 +0000
495 | | | | | date: Thu Jan 01 00:00:30 1970 +0000
496 | | | | | summary: (30) expand
496 | | | | | summary: (30) expand
497 | | | | |
497 | | | | |
498 | | | o | changeset: 29:cd9bb2be7593
498 | | | o | changeset: 29:cd9bb2be7593
499 | | | | | parent: 0:e6eb3150255d
499 | | | | | parent: 0:e6eb3150255d
500 | | | | | user: test
500 | | | | | user: test
501 | | | | | date: Thu Jan 01 00:00:29 1970 +0000
501 | | | | | date: Thu Jan 01 00:00:29 1970 +0000
502 | | | | | summary: (29) regular commit
502 | | | | | summary: (29) regular commit
503 | | | | |
503 | | | | |
504 | | o | | changeset: 28:44ecd0b9ae99
504 | | o | | changeset: 28:44ecd0b9ae99
505 | | |\ \ \ parent: 1:6db2ef61d156
505 | | |\ \ \ parent: 1:6db2ef61d156
506 | | | | | | parent: 26:7f25b6c2f0b9
506 | | | | | | parent: 26:7f25b6c2f0b9
507 | | | | | | user: test
507 | | | | | | user: test
508 | | | | | | date: Thu Jan 01 00:00:28 1970 +0000
508 | | | | | | date: Thu Jan 01 00:00:28 1970 +0000
509 | | | | | | summary: (28) merge zero known
509 | | | | | | summary: (28) merge zero known
510 | | | | | |
510 | | | | | |
511 o | | | | | changeset: 27:886ed638191b
511 o | | | | | changeset: 27:886ed638191b
512 |/ / / / / parent: 21:d42a756af44d
512 |/ / / / / parent: 21:d42a756af44d
513 | | | | | user: test
513 | | | | | user: test
514 | | | | | date: Thu Jan 01 00:00:27 1970 +0000
514 | | | | | date: Thu Jan 01 00:00:27 1970 +0000
515 | | | | | summary: (27) collapse
515 | | | | | summary: (27) collapse
516 | | | | |
516 | | | | |
517 | | o---+ changeset: 26:7f25b6c2f0b9
517 | | o---+ changeset: 26:7f25b6c2f0b9
518 | | | | | parent: 18:1aa84d96232a
518 | | | | | parent: 18:1aa84d96232a
519 | | | | | parent: 25:91da8ed57247
519 | | | | | parent: 25:91da8ed57247
520 | | | | | user: test
520 | | | | | user: test
521 | | | | | date: Thu Jan 01 00:00:26 1970 +0000
521 | | | | | date: Thu Jan 01 00:00:26 1970 +0000
522 | | | | | summary: (26) merge one known; far right
522 | | | | | summary: (26) merge one known; far right
523 | | | | |
523 | | | | |
524 +---o | | changeset: 25:91da8ed57247
524 +---o | | changeset: 25:91da8ed57247
525 | | | | | parent: 21:d42a756af44d
525 | | | | | parent: 21:d42a756af44d
526 | | | | | parent: 24:a9c19a3d96b7
526 | | | | | parent: 24:a9c19a3d96b7
527 | | | | | user: test
527 | | | | | user: test
528 | | | | | date: Thu Jan 01 00:00:25 1970 +0000
528 | | | | | date: Thu Jan 01 00:00:25 1970 +0000
529 | | | | | summary: (25) merge one known; far left
529 | | | | | summary: (25) merge one known; far left
530 | | | | |
530 | | | | |
531 | | o | | changeset: 24:a9c19a3d96b7
531 | | o | | changeset: 24:a9c19a3d96b7
532 | | |\| | parent: 0:e6eb3150255d
532 | | |\| | parent: 0:e6eb3150255d
533 | | | | | parent: 23:a01cddf0766d
533 | | | | | parent: 23:a01cddf0766d
534 | | | | | user: test
534 | | | | | user: test
535 | | | | | date: Thu Jan 01 00:00:24 1970 +0000
535 | | | | | date: Thu Jan 01 00:00:24 1970 +0000
536 | | | | | summary: (24) merge one known; immediate right
536 | | | | | summary: (24) merge one known; immediate right
537 | | | | |
537 | | | | |
538 | | o | | changeset: 23:a01cddf0766d
538 | | o | | changeset: 23:a01cddf0766d
539 | |/| | | parent: 1:6db2ef61d156
539 | |/| | | parent: 1:6db2ef61d156
540 | | | | | parent: 22:e0d9cccacb5d
540 | | | | | parent: 22:e0d9cccacb5d
541 | | | | | user: test
541 | | | | | user: test
542 | | | | | date: Thu Jan 01 00:00:23 1970 +0000
542 | | | | | date: Thu Jan 01 00:00:23 1970 +0000
543 | | | | | summary: (23) merge one known; immediate left
543 | | | | | summary: (23) merge one known; immediate left
544 | | | | |
544 | | | | |
545 +---o---+ changeset: 22:e0d9cccacb5d
545 +---o---+ changeset: 22:e0d9cccacb5d
546 | | | | parent: 18:1aa84d96232a
546 | | | | parent: 18:1aa84d96232a
547 | | / / parent: 21:d42a756af44d
547 | | / / parent: 21:d42a756af44d
548 | | | | user: test
548 | | | | user: test
549 | | | | date: Thu Jan 01 00:00:22 1970 +0000
549 | | | | date: Thu Jan 01 00:00:22 1970 +0000
550 | | | | summary: (22) merge two known; one far left, one far right
550 | | | | summary: (22) merge two known; one far left, one far right
551 | | | |
551 | | | |
552 o | | | changeset: 21:d42a756af44d
552 o | | | changeset: 21:d42a756af44d
553 |\ \ \ \ parent: 19:31ddc2c1573b
553 |\ \ \ \ parent: 19:31ddc2c1573b
554 | | | | | parent: 20:d30ed6450e32
554 | | | | | parent: 20:d30ed6450e32
555 | | | | | user: test
555 | | | | | user: test
556 | | | | | date: Thu Jan 01 00:00:21 1970 +0000
556 | | | | | date: Thu Jan 01 00:00:21 1970 +0000
557 | | | | | summary: (21) expand
557 | | | | | summary: (21) expand
558 | | | | |
558 | | | | |
559 | o---+-+ changeset: 20:d30ed6450e32
559 | o---+-+ changeset: 20:d30ed6450e32
560 | | | | parent: 0:e6eb3150255d
560 | | | | parent: 0:e6eb3150255d
561 | / / / parent: 18:1aa84d96232a
561 | / / / parent: 18:1aa84d96232a
562 | | | | user: test
562 | | | | user: test
563 | | | | date: Thu Jan 01 00:00:20 1970 +0000
563 | | | | date: Thu Jan 01 00:00:20 1970 +0000
564 | | | | summary: (20) merge two known; two far right
564 | | | | summary: (20) merge two known; two far right
565 | | | |
565 | | | |
566 o | | | changeset: 19:31ddc2c1573b
566 o | | | changeset: 19:31ddc2c1573b
567 |\ \ \ \ parent: 15:1dda3f72782d
567 |\ \ \ \ parent: 15:1dda3f72782d
568 | | | | | parent: 17:44765d7c06e0
568 | | | | | parent: 17:44765d7c06e0
569 | | | | | user: test
569 | | | | | user: test
570 | | | | | date: Thu Jan 01 00:00:19 1970 +0000
570 | | | | | date: Thu Jan 01 00:00:19 1970 +0000
571 | | | | | summary: (19) expand
571 | | | | | summary: (19) expand
572 | | | | |
572 | | | | |
573 +---+---o changeset: 18:1aa84d96232a
573 +---+---o changeset: 18:1aa84d96232a
574 | | | | parent: 1:6db2ef61d156
574 | | | | parent: 1:6db2ef61d156
575 | | | | parent: 15:1dda3f72782d
575 | | | | parent: 15:1dda3f72782d
576 | | | | user: test
576 | | | | user: test
577 | | | | date: Thu Jan 01 00:00:18 1970 +0000
577 | | | | date: Thu Jan 01 00:00:18 1970 +0000
578 | | | | summary: (18) merge two known; two far left
578 | | | | summary: (18) merge two known; two far left
579 | | | |
579 | | | |
580 | o | | changeset: 17:44765d7c06e0
580 | o | | changeset: 17:44765d7c06e0
581 | |\ \ \ parent: 12:86b91144a6e9
581 | |\ \ \ parent: 12:86b91144a6e9
582 | | | | | parent: 16:3677d192927d
582 | | | | | parent: 16:3677d192927d
583 | | | | | user: test
583 | | | | | user: test
584 | | | | | date: Thu Jan 01 00:00:17 1970 +0000
584 | | | | | date: Thu Jan 01 00:00:17 1970 +0000
585 | | | | | summary: (17) expand
585 | | | | | summary: (17) expand
586 | | | | |
586 | | | | |
587 | | o---+ changeset: 16:3677d192927d
587 | | o---+ changeset: 16:3677d192927d
588 | | | | | parent: 0:e6eb3150255d
588 | | | | | parent: 0:e6eb3150255d
589 | | |/ / parent: 1:6db2ef61d156
589 | | |/ / parent: 1:6db2ef61d156
590 | | | | user: test
590 | | | | user: test
591 | | | | date: Thu Jan 01 00:00:16 1970 +0000
591 | | | | date: Thu Jan 01 00:00:16 1970 +0000
592 | | | | summary: (16) merge two known; one immediate right, one near right
592 | | | | summary: (16) merge two known; one immediate right, one near right
593 | | | |
593 | | | |
594 o | | | changeset: 15:1dda3f72782d
594 o | | | changeset: 15:1dda3f72782d
595 |\ \ \ \ parent: 13:22d8966a97e3
595 |\ \ \ \ parent: 13:22d8966a97e3
596 | | | | | parent: 14:8eac370358ef
596 | | | | | parent: 14:8eac370358ef
597 | | | | | user: test
597 | | | | | user: test
598 | | | | | date: Thu Jan 01 00:00:15 1970 +0000
598 | | | | | date: Thu Jan 01 00:00:15 1970 +0000
599 | | | | | summary: (15) expand
599 | | | | | summary: (15) expand
600 | | | | |
600 | | | | |
601 | o-----+ changeset: 14:8eac370358ef
601 | o-----+ changeset: 14:8eac370358ef
602 | | | | | parent: 0:e6eb3150255d
602 | | | | | parent: 0:e6eb3150255d
603 | |/ / / parent: 12:86b91144a6e9
603 | |/ / / parent: 12:86b91144a6e9
604 | | | | user: test
604 | | | | user: test
605 | | | | date: Thu Jan 01 00:00:14 1970 +0000
605 | | | | date: Thu Jan 01 00:00:14 1970 +0000
606 | | | | summary: (14) merge two known; one immediate right, one far right
606 | | | | summary: (14) merge two known; one immediate right, one far right
607 | | | |
607 | | | |
608 o | | | changeset: 13:22d8966a97e3
608 o | | | changeset: 13:22d8966a97e3
609 |\ \ \ \ parent: 9:7010c0af0a35
609 |\ \ \ \ parent: 9:7010c0af0a35
610 | | | | | parent: 11:832d76e6bdf2
610 | | | | | parent: 11:832d76e6bdf2
611 | | | | | user: test
611 | | | | | user: test
612 | | | | | date: Thu Jan 01 00:00:13 1970 +0000
612 | | | | | date: Thu Jan 01 00:00:13 1970 +0000
613 | | | | | summary: (13) expand
613 | | | | | summary: (13) expand
614 | | | | |
614 | | | | |
615 +---o | | changeset: 12:86b91144a6e9
615 +---o | | changeset: 12:86b91144a6e9
616 | | |/ / parent: 1:6db2ef61d156
616 | | |/ / parent: 1:6db2ef61d156
617 | | | | parent: 9:7010c0af0a35
617 | | | | parent: 9:7010c0af0a35
618 | | | | user: test
618 | | | | user: test
619 | | | | date: Thu Jan 01 00:00:12 1970 +0000
619 | | | | date: Thu Jan 01 00:00:12 1970 +0000
620 | | | | summary: (12) merge two known; one immediate right, one far left
620 | | | | summary: (12) merge two known; one immediate right, one far left
621 | | | |
621 | | | |
622 | o | | changeset: 11:832d76e6bdf2
622 | o | | changeset: 11:832d76e6bdf2
623 | |\ \ \ parent: 6:b105a072e251
623 | |\ \ \ parent: 6:b105a072e251
624 | | | | | parent: 10:74c64d036d72
624 | | | | | parent: 10:74c64d036d72
625 | | | | | user: test
625 | | | | | user: test
626 | | | | | date: Thu Jan 01 00:00:11 1970 +0000
626 | | | | | date: Thu Jan 01 00:00:11 1970 +0000
627 | | | | | summary: (11) expand
627 | | | | | summary: (11) expand
628 | | | | |
628 | | | | |
629 | | o---+ changeset: 10:74c64d036d72
629 | | o---+ changeset: 10:74c64d036d72
630 | | | | | parent: 0:e6eb3150255d
630 | | | | | parent: 0:e6eb3150255d
631 | |/ / / parent: 6:b105a072e251
631 | |/ / / parent: 6:b105a072e251
632 | | | | user: test
632 | | | | user: test
633 | | | | date: Thu Jan 01 00:00:10 1970 +0000
633 | | | | date: Thu Jan 01 00:00:10 1970 +0000
634 | | | | summary: (10) merge two known; one immediate left, one near right
634 | | | | summary: (10) merge two known; one immediate left, one near right
635 | | | |
635 | | | |
636 o | | | changeset: 9:7010c0af0a35
636 o | | | changeset: 9:7010c0af0a35
637 |\ \ \ \ parent: 7:b632bb1b1224
637 |\ \ \ \ parent: 7:b632bb1b1224
638 | | | | | parent: 8:7a0b11f71937
638 | | | | | parent: 8:7a0b11f71937
639 | | | | | user: test
639 | | | | | user: test
640 | | | | | date: Thu Jan 01 00:00:09 1970 +0000
640 | | | | | date: Thu Jan 01 00:00:09 1970 +0000
641 | | | | | summary: (9) expand
641 | | | | | summary: (9) expand
642 | | | | |
642 | | | | |
643 | o-----+ changeset: 8:7a0b11f71937
643 | o-----+ changeset: 8:7a0b11f71937
644 | | | | | parent: 0:e6eb3150255d
644 | | | | | parent: 0:e6eb3150255d
645 |/ / / / parent: 7:b632bb1b1224
645 |/ / / / parent: 7:b632bb1b1224
646 | | | | user: test
646 | | | | user: test
647 | | | | date: Thu Jan 01 00:00:08 1970 +0000
647 | | | | date: Thu Jan 01 00:00:08 1970 +0000
648 | | | | summary: (8) merge two known; one immediate left, one far right
648 | | | | summary: (8) merge two known; one immediate left, one far right
649 | | | |
649 | | | |
650 o | | | changeset: 7:b632bb1b1224
650 o | | | changeset: 7:b632bb1b1224
651 |\ \ \ \ parent: 2:3d9a33b8d1e1
651 |\ \ \ \ parent: 2:3d9a33b8d1e1
652 | | | | | parent: 5:4409d547b708
652 | | | | | parent: 5:4409d547b708
653 | | | | | user: test
653 | | | | | user: test
654 | | | | | date: Thu Jan 01 00:00:07 1970 +0000
654 | | | | | date: Thu Jan 01 00:00:07 1970 +0000
655 | | | | | summary: (7) expand
655 | | | | | summary: (7) expand
656 | | | | |
656 | | | | |
657 +---o | | changeset: 6:b105a072e251
657 +---o | | changeset: 6:b105a072e251
658 | |/ / / parent: 2:3d9a33b8d1e1
658 | |/ / / parent: 2:3d9a33b8d1e1
659 | | | | parent: 5:4409d547b708
659 | | | | parent: 5:4409d547b708
660 | | | | user: test
660 | | | | user: test
661 | | | | date: Thu Jan 01 00:00:06 1970 +0000
661 | | | | date: Thu Jan 01 00:00:06 1970 +0000
662 | | | | summary: (6) merge two known; one immediate left, one far left
662 | | | | summary: (6) merge two known; one immediate left, one far left
663 | | | |
663 | | | |
664 | o | | changeset: 5:4409d547b708
664 | o | | changeset: 5:4409d547b708
665 | |\ \ \ parent: 3:27eef8ed80b4
665 | |\ \ \ parent: 3:27eef8ed80b4
666 | | | | | parent: 4:26a8bac39d9f
666 | | | | | parent: 4:26a8bac39d9f
667 | | | | | user: test
667 | | | | | user: test
668 | | | | | date: Thu Jan 01 00:00:05 1970 +0000
668 | | | | | date: Thu Jan 01 00:00:05 1970 +0000
669 | | | | | summary: (5) expand
669 | | | | | summary: (5) expand
670 | | | | |
670 | | | | |
671 | | o | | changeset: 4:26a8bac39d9f
671 | | o | | changeset: 4:26a8bac39d9f
672 | |/|/ / parent: 1:6db2ef61d156
672 | |/|/ / parent: 1:6db2ef61d156
673 | | | | parent: 3:27eef8ed80b4
673 | | | | parent: 3:27eef8ed80b4
674 | | | | user: test
674 | | | | user: test
675 | | | | date: Thu Jan 01 00:00:04 1970 +0000
675 | | | | date: Thu Jan 01 00:00:04 1970 +0000
676 | | | | summary: (4) merge two known; one immediate left, one immediate right
676 | | | | summary: (4) merge two known; one immediate left, one immediate right
677 | | | |
677 | | | |
678 | o | | changeset: 3:27eef8ed80b4
678 | o | | changeset: 3:27eef8ed80b4
679 |/ / / user: test
679 |/ / / user: test
680 | | | date: Thu Jan 01 00:00:03 1970 +0000
680 | | | date: Thu Jan 01 00:00:03 1970 +0000
681 | | | summary: (3) collapse
681 | | | summary: (3) collapse
682 | | |
682 | | |
683 o | | changeset: 2:3d9a33b8d1e1
683 o | | changeset: 2:3d9a33b8d1e1
684 |/ / user: test
684 |/ / user: test
685 | | date: Thu Jan 01 00:00:02 1970 +0000
685 | | date: Thu Jan 01 00:00:02 1970 +0000
686 | | summary: (2) collapse
686 | | summary: (2) collapse
687 | |
687 | |
688 o | changeset: 1:6db2ef61d156
688 o | changeset: 1:6db2ef61d156
689 |/ user: test
689 |/ user: test
690 | date: Thu Jan 01 00:00:01 1970 +0000
690 | date: Thu Jan 01 00:00:01 1970 +0000
691 | summary: (1) collapse
691 | summary: (1) collapse
692 |
692 |
693 o changeset: 0:e6eb3150255d
693 o changeset: 0:e6eb3150255d
694 user: test
694 user: test
695 date: Thu Jan 01 00:00:00 1970 +0000
695 date: Thu Jan 01 00:00:00 1970 +0000
696 summary: (0) root
696 summary: (0) root
697
697
698
698
699 File glog per revset:
699 File glog per revset:
700
700
701 $ hg log -G -r 'file("a")'
701 $ hg log -G -r 'file("a")'
702 @ changeset: 34:fea3ac5810e0
702 @ changeset: 34:fea3ac5810e0
703 | tag: tip
703 | tag: tip
704 | parent: 32:d06dffa21a31
704 | parent: 32:d06dffa21a31
705 | user: test
705 | user: test
706 | date: Thu Jan 01 00:00:34 1970 +0000
706 | date: Thu Jan 01 00:00:34 1970 +0000
707 | summary: (34) head
707 | summary: (34) head
708 |
708 |
709 | o changeset: 33:68608f5145f9
709 | o changeset: 33:68608f5145f9
710 | | parent: 18:1aa84d96232a
710 | | parent: 18:1aa84d96232a
711 | | user: test
711 | | user: test
712 | | date: Thu Jan 01 00:00:33 1970 +0000
712 | | date: Thu Jan 01 00:00:33 1970 +0000
713 | | summary: (33) head
713 | | summary: (33) head
714 | |
714 | |
715 o | changeset: 32:d06dffa21a31
715 o | changeset: 32:d06dffa21a31
716 |\ \ parent: 27:886ed638191b
716 |\ \ parent: 27:886ed638191b
717 | | | parent: 31:621d83e11f67
717 | | | parent: 31:621d83e11f67
718 | | | user: test
718 | | | user: test
719 | | | date: Thu Jan 01 00:00:32 1970 +0000
719 | | | date: Thu Jan 01 00:00:32 1970 +0000
720 | | | summary: (32) expand
720 | | | summary: (32) expand
721 | | |
721 | | |
722 | o | changeset: 31:621d83e11f67
722 | o | changeset: 31:621d83e11f67
723 | |\ \ parent: 21:d42a756af44d
723 | |\ \ parent: 21:d42a756af44d
724 | | | | parent: 30:6e11cd4b648f
724 | | | | parent: 30:6e11cd4b648f
725 | | | | user: test
725 | | | | user: test
726 | | | | date: Thu Jan 01 00:00:31 1970 +0000
726 | | | | date: Thu Jan 01 00:00:31 1970 +0000
727 | | | | summary: (31) expand
727 | | | | summary: (31) expand
728 | | | |
728 | | | |
729 | | o | changeset: 30:6e11cd4b648f
729 | | o | changeset: 30:6e11cd4b648f
730 | | |\ \ parent: 28:44ecd0b9ae99
730 | | |\ \ parent: 28:44ecd0b9ae99
731 | | | | | parent: 29:cd9bb2be7593
731 | | | | | parent: 29:cd9bb2be7593
732 | | | | | user: test
732 | | | | | user: test
733 | | | | | date: Thu Jan 01 00:00:30 1970 +0000
733 | | | | | date: Thu Jan 01 00:00:30 1970 +0000
734 | | | | | summary: (30) expand
734 | | | | | summary: (30) expand
735 | | | | |
735 | | | | |
736 | | | o | changeset: 29:cd9bb2be7593
736 | | | o | changeset: 29:cd9bb2be7593
737 | | | | | parent: 0:e6eb3150255d
737 | | | | | parent: 0:e6eb3150255d
738 | | | | | user: test
738 | | | | | user: test
739 | | | | | date: Thu Jan 01 00:00:29 1970 +0000
739 | | | | | date: Thu Jan 01 00:00:29 1970 +0000
740 | | | | | summary: (29) regular commit
740 | | | | | summary: (29) regular commit
741 | | | | |
741 | | | | |
742 | | o | | changeset: 28:44ecd0b9ae99
742 | | o | | changeset: 28:44ecd0b9ae99
743 | | |\ \ \ parent: 1:6db2ef61d156
743 | | |\ \ \ parent: 1:6db2ef61d156
744 | | | | | | parent: 26:7f25b6c2f0b9
744 | | | | | | parent: 26:7f25b6c2f0b9
745 | | | | | | user: test
745 | | | | | | user: test
746 | | | | | | date: Thu Jan 01 00:00:28 1970 +0000
746 | | | | | | date: Thu Jan 01 00:00:28 1970 +0000
747 | | | | | | summary: (28) merge zero known
747 | | | | | | summary: (28) merge zero known
748 | | | | | |
748 | | | | | |
749 o | | | | | changeset: 27:886ed638191b
749 o | | | | | changeset: 27:886ed638191b
750 |/ / / / / parent: 21:d42a756af44d
750 |/ / / / / parent: 21:d42a756af44d
751 | | | | | user: test
751 | | | | | user: test
752 | | | | | date: Thu Jan 01 00:00:27 1970 +0000
752 | | | | | date: Thu Jan 01 00:00:27 1970 +0000
753 | | | | | summary: (27) collapse
753 | | | | | summary: (27) collapse
754 | | | | |
754 | | | | |
755 | | o---+ changeset: 26:7f25b6c2f0b9
755 | | o---+ changeset: 26:7f25b6c2f0b9
756 | | | | | parent: 18:1aa84d96232a
756 | | | | | parent: 18:1aa84d96232a
757 | | | | | parent: 25:91da8ed57247
757 | | | | | parent: 25:91da8ed57247
758 | | | | | user: test
758 | | | | | user: test
759 | | | | | date: Thu Jan 01 00:00:26 1970 +0000
759 | | | | | date: Thu Jan 01 00:00:26 1970 +0000
760 | | | | | summary: (26) merge one known; far right
760 | | | | | summary: (26) merge one known; far right
761 | | | | |
761 | | | | |
762 +---o | | changeset: 25:91da8ed57247
762 +---o | | changeset: 25:91da8ed57247
763 | | | | | parent: 21:d42a756af44d
763 | | | | | parent: 21:d42a756af44d
764 | | | | | parent: 24:a9c19a3d96b7
764 | | | | | parent: 24:a9c19a3d96b7
765 | | | | | user: test
765 | | | | | user: test
766 | | | | | date: Thu Jan 01 00:00:25 1970 +0000
766 | | | | | date: Thu Jan 01 00:00:25 1970 +0000
767 | | | | | summary: (25) merge one known; far left
767 | | | | | summary: (25) merge one known; far left
768 | | | | |
768 | | | | |
769 | | o | | changeset: 24:a9c19a3d96b7
769 | | o | | changeset: 24:a9c19a3d96b7
770 | | |\| | parent: 0:e6eb3150255d
770 | | |\| | parent: 0:e6eb3150255d
771 | | | | | parent: 23:a01cddf0766d
771 | | | | | parent: 23:a01cddf0766d
772 | | | | | user: test
772 | | | | | user: test
773 | | | | | date: Thu Jan 01 00:00:24 1970 +0000
773 | | | | | date: Thu Jan 01 00:00:24 1970 +0000
774 | | | | | summary: (24) merge one known; immediate right
774 | | | | | summary: (24) merge one known; immediate right
775 | | | | |
775 | | | | |
776 | | o | | changeset: 23:a01cddf0766d
776 | | o | | changeset: 23:a01cddf0766d
777 | |/| | | parent: 1:6db2ef61d156
777 | |/| | | parent: 1:6db2ef61d156
778 | | | | | parent: 22:e0d9cccacb5d
778 | | | | | parent: 22:e0d9cccacb5d
779 | | | | | user: test
779 | | | | | user: test
780 | | | | | date: Thu Jan 01 00:00:23 1970 +0000
780 | | | | | date: Thu Jan 01 00:00:23 1970 +0000
781 | | | | | summary: (23) merge one known; immediate left
781 | | | | | summary: (23) merge one known; immediate left
782 | | | | |
782 | | | | |
783 +---o---+ changeset: 22:e0d9cccacb5d
783 +---o---+ changeset: 22:e0d9cccacb5d
784 | | | | parent: 18:1aa84d96232a
784 | | | | parent: 18:1aa84d96232a
785 | | / / parent: 21:d42a756af44d
785 | | / / parent: 21:d42a756af44d
786 | | | | user: test
786 | | | | user: test
787 | | | | date: Thu Jan 01 00:00:22 1970 +0000
787 | | | | date: Thu Jan 01 00:00:22 1970 +0000
788 | | | | summary: (22) merge two known; one far left, one far right
788 | | | | summary: (22) merge two known; one far left, one far right
789 | | | |
789 | | | |
790 o | | | changeset: 21:d42a756af44d
790 o | | | changeset: 21:d42a756af44d
791 |\ \ \ \ parent: 19:31ddc2c1573b
791 |\ \ \ \ parent: 19:31ddc2c1573b
792 | | | | | parent: 20:d30ed6450e32
792 | | | | | parent: 20:d30ed6450e32
793 | | | | | user: test
793 | | | | | user: test
794 | | | | | date: Thu Jan 01 00:00:21 1970 +0000
794 | | | | | date: Thu Jan 01 00:00:21 1970 +0000
795 | | | | | summary: (21) expand
795 | | | | | summary: (21) expand
796 | | | | |
796 | | | | |
797 | o---+-+ changeset: 20:d30ed6450e32
797 | o---+-+ changeset: 20:d30ed6450e32
798 | | | | parent: 0:e6eb3150255d
798 | | | | parent: 0:e6eb3150255d
799 | / / / parent: 18:1aa84d96232a
799 | / / / parent: 18:1aa84d96232a
800 | | | | user: test
800 | | | | user: test
801 | | | | date: Thu Jan 01 00:00:20 1970 +0000
801 | | | | date: Thu Jan 01 00:00:20 1970 +0000
802 | | | | summary: (20) merge two known; two far right
802 | | | | summary: (20) merge two known; two far right
803 | | | |
803 | | | |
804 o | | | changeset: 19:31ddc2c1573b
804 o | | | changeset: 19:31ddc2c1573b
805 |\ \ \ \ parent: 15:1dda3f72782d
805 |\ \ \ \ parent: 15:1dda3f72782d
806 | | | | | parent: 17:44765d7c06e0
806 | | | | | parent: 17:44765d7c06e0
807 | | | | | user: test
807 | | | | | user: test
808 | | | | | date: Thu Jan 01 00:00:19 1970 +0000
808 | | | | | date: Thu Jan 01 00:00:19 1970 +0000
809 | | | | | summary: (19) expand
809 | | | | | summary: (19) expand
810 | | | | |
810 | | | | |
811 +---+---o changeset: 18:1aa84d96232a
811 +---+---o changeset: 18:1aa84d96232a
812 | | | | parent: 1:6db2ef61d156
812 | | | | parent: 1:6db2ef61d156
813 | | | | parent: 15:1dda3f72782d
813 | | | | parent: 15:1dda3f72782d
814 | | | | user: test
814 | | | | user: test
815 | | | | date: Thu Jan 01 00:00:18 1970 +0000
815 | | | | date: Thu Jan 01 00:00:18 1970 +0000
816 | | | | summary: (18) merge two known; two far left
816 | | | | summary: (18) merge two known; two far left
817 | | | |
817 | | | |
818 | o | | changeset: 17:44765d7c06e0
818 | o | | changeset: 17:44765d7c06e0
819 | |\ \ \ parent: 12:86b91144a6e9
819 | |\ \ \ parent: 12:86b91144a6e9
820 | | | | | parent: 16:3677d192927d
820 | | | | | parent: 16:3677d192927d
821 | | | | | user: test
821 | | | | | user: test
822 | | | | | date: Thu Jan 01 00:00:17 1970 +0000
822 | | | | | date: Thu Jan 01 00:00:17 1970 +0000
823 | | | | | summary: (17) expand
823 | | | | | summary: (17) expand
824 | | | | |
824 | | | | |
825 | | o---+ changeset: 16:3677d192927d
825 | | o---+ changeset: 16:3677d192927d
826 | | | | | parent: 0:e6eb3150255d
826 | | | | | parent: 0:e6eb3150255d
827 | | |/ / parent: 1:6db2ef61d156
827 | | |/ / parent: 1:6db2ef61d156
828 | | | | user: test
828 | | | | user: test
829 | | | | date: Thu Jan 01 00:00:16 1970 +0000
829 | | | | date: Thu Jan 01 00:00:16 1970 +0000
830 | | | | summary: (16) merge two known; one immediate right, one near right
830 | | | | summary: (16) merge two known; one immediate right, one near right
831 | | | |
831 | | | |
832 o | | | changeset: 15:1dda3f72782d
832 o | | | changeset: 15:1dda3f72782d
833 |\ \ \ \ parent: 13:22d8966a97e3
833 |\ \ \ \ parent: 13:22d8966a97e3
834 | | | | | parent: 14:8eac370358ef
834 | | | | | parent: 14:8eac370358ef
835 | | | | | user: test
835 | | | | | user: test
836 | | | | | date: Thu Jan 01 00:00:15 1970 +0000
836 | | | | | date: Thu Jan 01 00:00:15 1970 +0000
837 | | | | | summary: (15) expand
837 | | | | | summary: (15) expand
838 | | | | |
838 | | | | |
839 | o-----+ changeset: 14:8eac370358ef
839 | o-----+ changeset: 14:8eac370358ef
840 | | | | | parent: 0:e6eb3150255d
840 | | | | | parent: 0:e6eb3150255d
841 | |/ / / parent: 12:86b91144a6e9
841 | |/ / / parent: 12:86b91144a6e9
842 | | | | user: test
842 | | | | user: test
843 | | | | date: Thu Jan 01 00:00:14 1970 +0000
843 | | | | date: Thu Jan 01 00:00:14 1970 +0000
844 | | | | summary: (14) merge two known; one immediate right, one far right
844 | | | | summary: (14) merge two known; one immediate right, one far right
845 | | | |
845 | | | |
846 o | | | changeset: 13:22d8966a97e3
846 o | | | changeset: 13:22d8966a97e3
847 |\ \ \ \ parent: 9:7010c0af0a35
847 |\ \ \ \ parent: 9:7010c0af0a35
848 | | | | | parent: 11:832d76e6bdf2
848 | | | | | parent: 11:832d76e6bdf2
849 | | | | | user: test
849 | | | | | user: test
850 | | | | | date: Thu Jan 01 00:00:13 1970 +0000
850 | | | | | date: Thu Jan 01 00:00:13 1970 +0000
851 | | | | | summary: (13) expand
851 | | | | | summary: (13) expand
852 | | | | |
852 | | | | |
853 +---o | | changeset: 12:86b91144a6e9
853 +---o | | changeset: 12:86b91144a6e9
854 | | |/ / parent: 1:6db2ef61d156
854 | | |/ / parent: 1:6db2ef61d156
855 | | | | parent: 9:7010c0af0a35
855 | | | | parent: 9:7010c0af0a35
856 | | | | user: test
856 | | | | user: test
857 | | | | date: Thu Jan 01 00:00:12 1970 +0000
857 | | | | date: Thu Jan 01 00:00:12 1970 +0000
858 | | | | summary: (12) merge two known; one immediate right, one far left
858 | | | | summary: (12) merge two known; one immediate right, one far left
859 | | | |
859 | | | |
860 | o | | changeset: 11:832d76e6bdf2
860 | o | | changeset: 11:832d76e6bdf2
861 | |\ \ \ parent: 6:b105a072e251
861 | |\ \ \ parent: 6:b105a072e251
862 | | | | | parent: 10:74c64d036d72
862 | | | | | parent: 10:74c64d036d72
863 | | | | | user: test
863 | | | | | user: test
864 | | | | | date: Thu Jan 01 00:00:11 1970 +0000
864 | | | | | date: Thu Jan 01 00:00:11 1970 +0000
865 | | | | | summary: (11) expand
865 | | | | | summary: (11) expand
866 | | | | |
866 | | | | |
867 | | o---+ changeset: 10:74c64d036d72
867 | | o---+ changeset: 10:74c64d036d72
868 | | | | | parent: 0:e6eb3150255d
868 | | | | | parent: 0:e6eb3150255d
869 | |/ / / parent: 6:b105a072e251
869 | |/ / / parent: 6:b105a072e251
870 | | | | user: test
870 | | | | user: test
871 | | | | date: Thu Jan 01 00:00:10 1970 +0000
871 | | | | date: Thu Jan 01 00:00:10 1970 +0000
872 | | | | summary: (10) merge two known; one immediate left, one near right
872 | | | | summary: (10) merge two known; one immediate left, one near right
873 | | | |
873 | | | |
874 o | | | changeset: 9:7010c0af0a35
874 o | | | changeset: 9:7010c0af0a35
875 |\ \ \ \ parent: 7:b632bb1b1224
875 |\ \ \ \ parent: 7:b632bb1b1224
876 | | | | | parent: 8:7a0b11f71937
876 | | | | | parent: 8:7a0b11f71937
877 | | | | | user: test
877 | | | | | user: test
878 | | | | | date: Thu Jan 01 00:00:09 1970 +0000
878 | | | | | date: Thu Jan 01 00:00:09 1970 +0000
879 | | | | | summary: (9) expand
879 | | | | | summary: (9) expand
880 | | | | |
880 | | | | |
881 | o-----+ changeset: 8:7a0b11f71937
881 | o-----+ changeset: 8:7a0b11f71937
882 | | | | | parent: 0:e6eb3150255d
882 | | | | | parent: 0:e6eb3150255d
883 |/ / / / parent: 7:b632bb1b1224
883 |/ / / / parent: 7:b632bb1b1224
884 | | | | user: test
884 | | | | user: test
885 | | | | date: Thu Jan 01 00:00:08 1970 +0000
885 | | | | date: Thu Jan 01 00:00:08 1970 +0000
886 | | | | summary: (8) merge two known; one immediate left, one far right
886 | | | | summary: (8) merge two known; one immediate left, one far right
887 | | | |
887 | | | |
888 o | | | changeset: 7:b632bb1b1224
888 o | | | changeset: 7:b632bb1b1224
889 |\ \ \ \ parent: 2:3d9a33b8d1e1
889 |\ \ \ \ parent: 2:3d9a33b8d1e1
890 | | | | | parent: 5:4409d547b708
890 | | | | | parent: 5:4409d547b708
891 | | | | | user: test
891 | | | | | user: test
892 | | | | | date: Thu Jan 01 00:00:07 1970 +0000
892 | | | | | date: Thu Jan 01 00:00:07 1970 +0000
893 | | | | | summary: (7) expand
893 | | | | | summary: (7) expand
894 | | | | |
894 | | | | |
895 +---o | | changeset: 6:b105a072e251
895 +---o | | changeset: 6:b105a072e251
896 | |/ / / parent: 2:3d9a33b8d1e1
896 | |/ / / parent: 2:3d9a33b8d1e1
897 | | | | parent: 5:4409d547b708
897 | | | | parent: 5:4409d547b708
898 | | | | user: test
898 | | | | user: test
899 | | | | date: Thu Jan 01 00:00:06 1970 +0000
899 | | | | date: Thu Jan 01 00:00:06 1970 +0000
900 | | | | summary: (6) merge two known; one immediate left, one far left
900 | | | | summary: (6) merge two known; one immediate left, one far left
901 | | | |
901 | | | |
902 | o | | changeset: 5:4409d547b708
902 | o | | changeset: 5:4409d547b708
903 | |\ \ \ parent: 3:27eef8ed80b4
903 | |\ \ \ parent: 3:27eef8ed80b4
904 | | | | | parent: 4:26a8bac39d9f
904 | | | | | parent: 4:26a8bac39d9f
905 | | | | | user: test
905 | | | | | user: test
906 | | | | | date: Thu Jan 01 00:00:05 1970 +0000
906 | | | | | date: Thu Jan 01 00:00:05 1970 +0000
907 | | | | | summary: (5) expand
907 | | | | | summary: (5) expand
908 | | | | |
908 | | | | |
909 | | o | | changeset: 4:26a8bac39d9f
909 | | o | | changeset: 4:26a8bac39d9f
910 | |/|/ / parent: 1:6db2ef61d156
910 | |/|/ / parent: 1:6db2ef61d156
911 | | | | parent: 3:27eef8ed80b4
911 | | | | parent: 3:27eef8ed80b4
912 | | | | user: test
912 | | | | user: test
913 | | | | date: Thu Jan 01 00:00:04 1970 +0000
913 | | | | date: Thu Jan 01 00:00:04 1970 +0000
914 | | | | summary: (4) merge two known; one immediate left, one immediate right
914 | | | | summary: (4) merge two known; one immediate left, one immediate right
915 | | | |
915 | | | |
916 | o | | changeset: 3:27eef8ed80b4
916 | o | | changeset: 3:27eef8ed80b4
917 |/ / / user: test
917 |/ / / user: test
918 | | | date: Thu Jan 01 00:00:03 1970 +0000
918 | | | date: Thu Jan 01 00:00:03 1970 +0000
919 | | | summary: (3) collapse
919 | | | summary: (3) collapse
920 | | |
920 | | |
921 o | | changeset: 2:3d9a33b8d1e1
921 o | | changeset: 2:3d9a33b8d1e1
922 |/ / user: test
922 |/ / user: test
923 | | date: Thu Jan 01 00:00:02 1970 +0000
923 | | date: Thu Jan 01 00:00:02 1970 +0000
924 | | summary: (2) collapse
924 | | summary: (2) collapse
925 | |
925 | |
926 o | changeset: 1:6db2ef61d156
926 o | changeset: 1:6db2ef61d156
927 |/ user: test
927 |/ user: test
928 | date: Thu Jan 01 00:00:01 1970 +0000
928 | date: Thu Jan 01 00:00:01 1970 +0000
929 | summary: (1) collapse
929 | summary: (1) collapse
930 |
930 |
931 o changeset: 0:e6eb3150255d
931 o changeset: 0:e6eb3150255d
932 user: test
932 user: test
933 date: Thu Jan 01 00:00:00 1970 +0000
933 date: Thu Jan 01 00:00:00 1970 +0000
934 summary: (0) root
934 summary: (0) root
935
935
936
936
937
937
938 File glog per revset (only merges):
938 File glog per revset (only merges):
939
939
940 $ hg log -G -r 'file("a")' -m
940 $ hg log -G -r 'file("a")' -m
941 o changeset: 32:d06dffa21a31
941 o changeset: 32:d06dffa21a31
942 |\ parent: 27:886ed638191b
942 |\ parent: 27:886ed638191b
943 | | parent: 31:621d83e11f67
943 | | parent: 31:621d83e11f67
944 | | user: test
944 | | user: test
945 | | date: Thu Jan 01 00:00:32 1970 +0000
945 | | date: Thu Jan 01 00:00:32 1970 +0000
946 | | summary: (32) expand
946 | | summary: (32) expand
947 | |
947 | |
948 o | changeset: 31:621d83e11f67
948 o | changeset: 31:621d83e11f67
949 |\| parent: 21:d42a756af44d
949 |\| parent: 21:d42a756af44d
950 | | parent: 30:6e11cd4b648f
950 | | parent: 30:6e11cd4b648f
951 | | user: test
951 | | user: test
952 | | date: Thu Jan 01 00:00:31 1970 +0000
952 | | date: Thu Jan 01 00:00:31 1970 +0000
953 | | summary: (31) expand
953 | | summary: (31) expand
954 | |
954 | |
955 o | changeset: 30:6e11cd4b648f
955 o | changeset: 30:6e11cd4b648f
956 |\ \ parent: 28:44ecd0b9ae99
956 |\ \ parent: 28:44ecd0b9ae99
957 | | | parent: 29:cd9bb2be7593
957 | | | parent: 29:cd9bb2be7593
958 | | | user: test
958 | | | user: test
959 | | | date: Thu Jan 01 00:00:30 1970 +0000
959 | | | date: Thu Jan 01 00:00:30 1970 +0000
960 | | | summary: (30) expand
960 | | | summary: (30) expand
961 | | |
961 | | |
962 o | | changeset: 28:44ecd0b9ae99
962 o | | changeset: 28:44ecd0b9ae99
963 |\ \ \ parent: 1:6db2ef61d156
963 |\ \ \ parent: 1:6db2ef61d156
964 | | | | parent: 26:7f25b6c2f0b9
964 | | | | parent: 26:7f25b6c2f0b9
965 | | | | user: test
965 | | | | user: test
966 | | | | date: Thu Jan 01 00:00:28 1970 +0000
966 | | | | date: Thu Jan 01 00:00:28 1970 +0000
967 | | | | summary: (28) merge zero known
967 | | | | summary: (28) merge zero known
968 | | | |
968 | | | |
969 o | | | changeset: 26:7f25b6c2f0b9
969 o | | | changeset: 26:7f25b6c2f0b9
970 |\ \ \ \ parent: 18:1aa84d96232a
970 |\ \ \ \ parent: 18:1aa84d96232a
971 | | | | | parent: 25:91da8ed57247
971 | | | | | parent: 25:91da8ed57247
972 | | | | | user: test
972 | | | | | user: test
973 | | | | | date: Thu Jan 01 00:00:26 1970 +0000
973 | | | | | date: Thu Jan 01 00:00:26 1970 +0000
974 | | | | | summary: (26) merge one known; far right
974 | | | | | summary: (26) merge one known; far right
975 | | | | |
975 | | | | |
976 | o-----+ changeset: 25:91da8ed57247
976 | o-----+ changeset: 25:91da8ed57247
977 | | | | | parent: 21:d42a756af44d
977 | | | | | parent: 21:d42a756af44d
978 | | | | | parent: 24:a9c19a3d96b7
978 | | | | | parent: 24:a9c19a3d96b7
979 | | | | | user: test
979 | | | | | user: test
980 | | | | | date: Thu Jan 01 00:00:25 1970 +0000
980 | | | | | date: Thu Jan 01 00:00:25 1970 +0000
981 | | | | | summary: (25) merge one known; far left
981 | | | | | summary: (25) merge one known; far left
982 | | | | |
982 | | | | |
983 | o | | | changeset: 24:a9c19a3d96b7
983 | o | | | changeset: 24:a9c19a3d96b7
984 | |\ \ \ \ parent: 0:e6eb3150255d
984 | |\ \ \ \ parent: 0:e6eb3150255d
985 | | | | | | parent: 23:a01cddf0766d
985 | | | | | | parent: 23:a01cddf0766d
986 | | | | | | user: test
986 | | | | | | user: test
987 | | | | | | date: Thu Jan 01 00:00:24 1970 +0000
987 | | | | | | date: Thu Jan 01 00:00:24 1970 +0000
988 | | | | | | summary: (24) merge one known; immediate right
988 | | | | | | summary: (24) merge one known; immediate right
989 | | | | | |
989 | | | | | |
990 | o---+ | | changeset: 23:a01cddf0766d
990 | o---+ | | changeset: 23:a01cddf0766d
991 | | | | | | parent: 1:6db2ef61d156
991 | | | | | | parent: 1:6db2ef61d156
992 | | | | | | parent: 22:e0d9cccacb5d
992 | | | | | | parent: 22:e0d9cccacb5d
993 | | | | | | user: test
993 | | | | | | user: test
994 | | | | | | date: Thu Jan 01 00:00:23 1970 +0000
994 | | | | | | date: Thu Jan 01 00:00:23 1970 +0000
995 | | | | | | summary: (23) merge one known; immediate left
995 | | | | | | summary: (23) merge one known; immediate left
996 | | | | | |
996 | | | | | |
997 | o-------+ changeset: 22:e0d9cccacb5d
997 | o-------+ changeset: 22:e0d9cccacb5d
998 | | | | | | parent: 18:1aa84d96232a
998 | | | | | | parent: 18:1aa84d96232a
999 |/ / / / / parent: 21:d42a756af44d
999 |/ / / / / parent: 21:d42a756af44d
1000 | | | | | user: test
1000 | | | | | user: test
1001 | | | | | date: Thu Jan 01 00:00:22 1970 +0000
1001 | | | | | date: Thu Jan 01 00:00:22 1970 +0000
1002 | | | | | summary: (22) merge two known; one far left, one far right
1002 | | | | | summary: (22) merge two known; one far left, one far right
1003 | | | | |
1003 | | | | |
1004 | | | | o changeset: 21:d42a756af44d
1004 | | | | o changeset: 21:d42a756af44d
1005 | | | | |\ parent: 19:31ddc2c1573b
1005 | | | | |\ parent: 19:31ddc2c1573b
1006 | | | | | | parent: 20:d30ed6450e32
1006 | | | | | | parent: 20:d30ed6450e32
1007 | | | | | | user: test
1007 | | | | | | user: test
1008 | | | | | | date: Thu Jan 01 00:00:21 1970 +0000
1008 | | | | | | date: Thu Jan 01 00:00:21 1970 +0000
1009 | | | | | | summary: (21) expand
1009 | | | | | | summary: (21) expand
1010 | | | | | |
1010 | | | | | |
1011 +-+-------o changeset: 20:d30ed6450e32
1011 +-+-------o changeset: 20:d30ed6450e32
1012 | | | | | parent: 0:e6eb3150255d
1012 | | | | | parent: 0:e6eb3150255d
1013 | | | | | parent: 18:1aa84d96232a
1013 | | | | | parent: 18:1aa84d96232a
1014 | | | | | user: test
1014 | | | | | user: test
1015 | | | | | date: Thu Jan 01 00:00:20 1970 +0000
1015 | | | | | date: Thu Jan 01 00:00:20 1970 +0000
1016 | | | | | summary: (20) merge two known; two far right
1016 | | | | | summary: (20) merge two known; two far right
1017 | | | | |
1017 | | | | |
1018 | | | | o changeset: 19:31ddc2c1573b
1018 | | | | o changeset: 19:31ddc2c1573b
1019 | | | | |\ parent: 15:1dda3f72782d
1019 | | | | |\ parent: 15:1dda3f72782d
1020 | | | | | | parent: 17:44765d7c06e0
1020 | | | | | | parent: 17:44765d7c06e0
1021 | | | | | | user: test
1021 | | | | | | user: test
1022 | | | | | | date: Thu Jan 01 00:00:19 1970 +0000
1022 | | | | | | date: Thu Jan 01 00:00:19 1970 +0000
1023 | | | | | | summary: (19) expand
1023 | | | | | | summary: (19) expand
1024 | | | | | |
1024 | | | | | |
1025 o---+---+ | changeset: 18:1aa84d96232a
1025 o---+---+ | changeset: 18:1aa84d96232a
1026 | | | | | parent: 1:6db2ef61d156
1026 | | | | | parent: 1:6db2ef61d156
1027 / / / / / parent: 15:1dda3f72782d
1027 / / / / / parent: 15:1dda3f72782d
1028 | | | | | user: test
1028 | | | | | user: test
1029 | | | | | date: Thu Jan 01 00:00:18 1970 +0000
1029 | | | | | date: Thu Jan 01 00:00:18 1970 +0000
1030 | | | | | summary: (18) merge two known; two far left
1030 | | | | | summary: (18) merge two known; two far left
1031 | | | | |
1031 | | | | |
1032 | | | | o changeset: 17:44765d7c06e0
1032 | | | | o changeset: 17:44765d7c06e0
1033 | | | | |\ parent: 12:86b91144a6e9
1033 | | | | |\ parent: 12:86b91144a6e9
1034 | | | | | | parent: 16:3677d192927d
1034 | | | | | | parent: 16:3677d192927d
1035 | | | | | | user: test
1035 | | | | | | user: test
1036 | | | | | | date: Thu Jan 01 00:00:17 1970 +0000
1036 | | | | | | date: Thu Jan 01 00:00:17 1970 +0000
1037 | | | | | | summary: (17) expand
1037 | | | | | | summary: (17) expand
1038 | | | | | |
1038 | | | | | |
1039 +-+-------o changeset: 16:3677d192927d
1039 +-+-------o changeset: 16:3677d192927d
1040 | | | | | parent: 0:e6eb3150255d
1040 | | | | | parent: 0:e6eb3150255d
1041 | | | | | parent: 1:6db2ef61d156
1041 | | | | | parent: 1:6db2ef61d156
1042 | | | | | user: test
1042 | | | | | user: test
1043 | | | | | date: Thu Jan 01 00:00:16 1970 +0000
1043 | | | | | date: Thu Jan 01 00:00:16 1970 +0000
1044 | | | | | summary: (16) merge two known; one immediate right, one near right
1044 | | | | | summary: (16) merge two known; one immediate right, one near right
1045 | | | | |
1045 | | | | |
1046 | | | o | changeset: 15:1dda3f72782d
1046 | | | o | changeset: 15:1dda3f72782d
1047 | | | |\ \ parent: 13:22d8966a97e3
1047 | | | |\ \ parent: 13:22d8966a97e3
1048 | | | | | | parent: 14:8eac370358ef
1048 | | | | | | parent: 14:8eac370358ef
1049 | | | | | | user: test
1049 | | | | | | user: test
1050 | | | | | | date: Thu Jan 01 00:00:15 1970 +0000
1050 | | | | | | date: Thu Jan 01 00:00:15 1970 +0000
1051 | | | | | | summary: (15) expand
1051 | | | | | | summary: (15) expand
1052 | | | | | |
1052 | | | | | |
1053 +-------o | changeset: 14:8eac370358ef
1053 +-------o | changeset: 14:8eac370358ef
1054 | | | | |/ parent: 0:e6eb3150255d
1054 | | | | |/ parent: 0:e6eb3150255d
1055 | | | | | parent: 12:86b91144a6e9
1055 | | | | | parent: 12:86b91144a6e9
1056 | | | | | user: test
1056 | | | | | user: test
1057 | | | | | date: Thu Jan 01 00:00:14 1970 +0000
1057 | | | | | date: Thu Jan 01 00:00:14 1970 +0000
1058 | | | | | summary: (14) merge two known; one immediate right, one far right
1058 | | | | | summary: (14) merge two known; one immediate right, one far right
1059 | | | | |
1059 | | | | |
1060 | | | o | changeset: 13:22d8966a97e3
1060 | | | o | changeset: 13:22d8966a97e3
1061 | | | |\ \ parent: 9:7010c0af0a35
1061 | | | |\ \ parent: 9:7010c0af0a35
1062 | | | | | | parent: 11:832d76e6bdf2
1062 | | | | | | parent: 11:832d76e6bdf2
1063 | | | | | | user: test
1063 | | | | | | user: test
1064 | | | | | | date: Thu Jan 01 00:00:13 1970 +0000
1064 | | | | | | date: Thu Jan 01 00:00:13 1970 +0000
1065 | | | | | | summary: (13) expand
1065 | | | | | | summary: (13) expand
1066 | | | | | |
1066 | | | | | |
1067 | +---+---o changeset: 12:86b91144a6e9
1067 | +---+---o changeset: 12:86b91144a6e9
1068 | | | | | parent: 1:6db2ef61d156
1068 | | | | | parent: 1:6db2ef61d156
1069 | | | | | parent: 9:7010c0af0a35
1069 | | | | | parent: 9:7010c0af0a35
1070 | | | | | user: test
1070 | | | | | user: test
1071 | | | | | date: Thu Jan 01 00:00:12 1970 +0000
1071 | | | | | date: Thu Jan 01 00:00:12 1970 +0000
1072 | | | | | summary: (12) merge two known; one immediate right, one far left
1072 | | | | | summary: (12) merge two known; one immediate right, one far left
1073 | | | | |
1073 | | | | |
1074 | | | | o changeset: 11:832d76e6bdf2
1074 | | | | o changeset: 11:832d76e6bdf2
1075 | | | | |\ parent: 6:b105a072e251
1075 | | | | |\ parent: 6:b105a072e251
1076 | | | | | | parent: 10:74c64d036d72
1076 | | | | | | parent: 10:74c64d036d72
1077 | | | | | | user: test
1077 | | | | | | user: test
1078 | | | | | | date: Thu Jan 01 00:00:11 1970 +0000
1078 | | | | | | date: Thu Jan 01 00:00:11 1970 +0000
1079 | | | | | | summary: (11) expand
1079 | | | | | | summary: (11) expand
1080 | | | | | |
1080 | | | | | |
1081 +---------o changeset: 10:74c64d036d72
1081 +---------o changeset: 10:74c64d036d72
1082 | | | | |/ parent: 0:e6eb3150255d
1082 | | | | |/ parent: 0:e6eb3150255d
1083 | | | | | parent: 6:b105a072e251
1083 | | | | | parent: 6:b105a072e251
1084 | | | | | user: test
1084 | | | | | user: test
1085 | | | | | date: Thu Jan 01 00:00:10 1970 +0000
1085 | | | | | date: Thu Jan 01 00:00:10 1970 +0000
1086 | | | | | summary: (10) merge two known; one immediate left, one near right
1086 | | | | | summary: (10) merge two known; one immediate left, one near right
1087 | | | | |
1087 | | | | |
1088 | | | o | changeset: 9:7010c0af0a35
1088 | | | o | changeset: 9:7010c0af0a35
1089 | | | |\ \ parent: 7:b632bb1b1224
1089 | | | |\ \ parent: 7:b632bb1b1224
1090 | | | | | | parent: 8:7a0b11f71937
1090 | | | | | | parent: 8:7a0b11f71937
1091 | | | | | | user: test
1091 | | | | | | user: test
1092 | | | | | | date: Thu Jan 01 00:00:09 1970 +0000
1092 | | | | | | date: Thu Jan 01 00:00:09 1970 +0000
1093 | | | | | | summary: (9) expand
1093 | | | | | | summary: (9) expand
1094 | | | | | |
1094 | | | | | |
1095 +-------o | changeset: 8:7a0b11f71937
1095 +-------o | changeset: 8:7a0b11f71937
1096 | | | |/ / parent: 0:e6eb3150255d
1096 | | | |/ / parent: 0:e6eb3150255d
1097 | | | | | parent: 7:b632bb1b1224
1097 | | | | | parent: 7:b632bb1b1224
1098 | | | | | user: test
1098 | | | | | user: test
1099 | | | | | date: Thu Jan 01 00:00:08 1970 +0000
1099 | | | | | date: Thu Jan 01 00:00:08 1970 +0000
1100 | | | | | summary: (8) merge two known; one immediate left, one far right
1100 | | | | | summary: (8) merge two known; one immediate left, one far right
1101 | | | | |
1101 | | | | |
1102 | | | o | changeset: 7:b632bb1b1224
1102 | | | o | changeset: 7:b632bb1b1224
1103 | | | |\ \ parent: 2:3d9a33b8d1e1
1103 | | | |\ \ parent: 2:3d9a33b8d1e1
1104 | | | | | | parent: 5:4409d547b708
1104 | | | | | | parent: 5:4409d547b708
1105 | | | | | | user: test
1105 | | | | | | user: test
1106 | | | | | | date: Thu Jan 01 00:00:07 1970 +0000
1106 | | | | | | date: Thu Jan 01 00:00:07 1970 +0000
1107 | | | | | | summary: (7) expand
1107 | | | | | | summary: (7) expand
1108 | | | | | |
1108 | | | | | |
1109 | | | +---o changeset: 6:b105a072e251
1109 | | | +---o changeset: 6:b105a072e251
1110 | | | | |/ parent: 2:3d9a33b8d1e1
1110 | | | | |/ parent: 2:3d9a33b8d1e1
1111 | | | | | parent: 5:4409d547b708
1111 | | | | | parent: 5:4409d547b708
1112 | | | | | user: test
1112 | | | | | user: test
1113 | | | | | date: Thu Jan 01 00:00:06 1970 +0000
1113 | | | | | date: Thu Jan 01 00:00:06 1970 +0000
1114 | | | | | summary: (6) merge two known; one immediate left, one far left
1114 | | | | | summary: (6) merge two known; one immediate left, one far left
1115 | | | | |
1115 | | | | |
1116 | | | o | changeset: 5:4409d547b708
1116 | | | o | changeset: 5:4409d547b708
1117 | | | |\ \ parent: 3:27eef8ed80b4
1117 | | | |\ \ parent: 3:27eef8ed80b4
1118 | | | | | | parent: 4:26a8bac39d9f
1118 | | | | | | parent: 4:26a8bac39d9f
1119 | | | | | | user: test
1119 | | | | | | user: test
1120 | | | | | | date: Thu Jan 01 00:00:05 1970 +0000
1120 | | | | | | date: Thu Jan 01 00:00:05 1970 +0000
1121 | | | | | | summary: (5) expand
1121 | | | | | | summary: (5) expand
1122 | | | | | |
1122 | | | | | |
1123 | +---o | | changeset: 4:26a8bac39d9f
1123 | +---o | | changeset: 4:26a8bac39d9f
1124 | | | |/ / parent: 1:6db2ef61d156
1124 | | | |/ / parent: 1:6db2ef61d156
1125 | | | | | parent: 3:27eef8ed80b4
1125 | | | | | parent: 3:27eef8ed80b4
1126 | | | | | user: test
1126 | | | | | user: test
1127 | | | | | date: Thu Jan 01 00:00:04 1970 +0000
1127 | | | | | date: Thu Jan 01 00:00:04 1970 +0000
1128 | | | | | summary: (4) merge two known; one immediate left, one immediate right
1128 | | | | | summary: (4) merge two known; one immediate left, one immediate right
1129 | | | | |
1129 | | | | |
1130
1130
1131
1131
1132 Empty revision range - display nothing:
1132 Empty revision range - display nothing:
1133 $ hg log -G -r 1..0
1133 $ hg log -G -r 1..0
1134
1134
1135 $ cd ..
1135 $ cd ..
1136
1136
1137 #if no-outer-repo
1137 #if no-outer-repo
1138
1138
1139 From outer space:
1139 From outer space:
1140 $ hg log -G -l1 repo
1140 $ hg log -G -l1 repo
1141 @ changeset: 34:fea3ac5810e0
1141 @ changeset: 34:fea3ac5810e0
1142 | tag: tip
1142 | tag: tip
1143 | parent: 32:d06dffa21a31
1143 | parent: 32:d06dffa21a31
1144 | user: test
1144 | user: test
1145 | date: Thu Jan 01 00:00:34 1970 +0000
1145 | date: Thu Jan 01 00:00:34 1970 +0000
1146 | summary: (34) head
1146 | summary: (34) head
1147 |
1147 |
1148 $ hg log -G -l1 repo/a
1148 $ hg log -G -l1 repo/a
1149 @ changeset: 34:fea3ac5810e0
1149 @ changeset: 34:fea3ac5810e0
1150 | tag: tip
1150 | tag: tip
1151 | parent: 32:d06dffa21a31
1151 | parent: 32:d06dffa21a31
1152 | user: test
1152 | user: test
1153 | date: Thu Jan 01 00:00:34 1970 +0000
1153 | date: Thu Jan 01 00:00:34 1970 +0000
1154 | summary: (34) head
1154 | summary: (34) head
1155 |
1155 |
1156 $ hg log -G -l1 repo/missing
1156 $ hg log -G -l1 repo/missing
1157
1157
1158 #endif
1158 #endif
1159
1159
1160 File log with revs != cset revs:
1160 File log with revs != cset revs:
1161 $ hg init flog
1161 $ hg init flog
1162 $ cd flog
1162 $ cd flog
1163 $ echo one >one
1163 $ echo one >one
1164 $ hg add one
1164 $ hg add one
1165 $ hg commit -mone
1165 $ hg commit -mone
1166 $ echo two >two
1166 $ echo two >two
1167 $ hg add two
1167 $ hg add two
1168 $ hg commit -mtwo
1168 $ hg commit -mtwo
1169 $ echo more >two
1169 $ echo more >two
1170 $ hg commit -mmore
1170 $ hg commit -mmore
1171 $ hg log -G two
1171 $ hg log -G two
1172 @ changeset: 2:12c28321755b
1172 @ changeset: 2:12c28321755b
1173 | tag: tip
1173 | tag: tip
1174 | user: test
1174 | user: test
1175 | date: Thu Jan 01 00:00:00 1970 +0000
1175 | date: Thu Jan 01 00:00:00 1970 +0000
1176 | summary: more
1176 | summary: more
1177 |
1177 |
1178 o changeset: 1:5ac72c0599bf
1178 o changeset: 1:5ac72c0599bf
1179 | user: test
1179 | user: test
1180 | date: Thu Jan 01 00:00:00 1970 +0000
1180 | date: Thu Jan 01 00:00:00 1970 +0000
1181 | summary: two
1181 | summary: two
1182 |
1182 |
1183
1183
1184 Issue1896: File log with explicit style
1184 Issue1896: File log with explicit style
1185 $ hg log -G --style=default one
1185 $ hg log -G --style=default one
1186 o changeset: 0:3d578b4a1f53
1186 o changeset: 0:3d578b4a1f53
1187 user: test
1187 user: test
1188 date: Thu Jan 01 00:00:00 1970 +0000
1188 date: Thu Jan 01 00:00:00 1970 +0000
1189 summary: one
1189 summary: one
1190
1190
1191 Issue2395: glog --style header and footer
1191 Issue2395: glog --style header and footer
1192 $ hg log -G --style=xml one
1192 $ hg log -G --style=xml one
1193 <?xml version="1.0"?>
1193 <?xml version="1.0"?>
1194 <log>
1194 <log>
1195 o <logentry revision="0" node="3d578b4a1f537d5fcf7301bfa9c0b97adfaa6fb1">
1195 o <logentry revision="0" node="3d578b4a1f537d5fcf7301bfa9c0b97adfaa6fb1">
1196 <author email="test">test</author>
1196 <author email="test">test</author>
1197 <date>1970-01-01T00:00:00+00:00</date>
1197 <date>1970-01-01T00:00:00+00:00</date>
1198 <msg xml:space="preserve">one</msg>
1198 <msg xml:space="preserve">one</msg>
1199 </logentry>
1199 </logentry>
1200 </log>
1200 </log>
1201
1201
1202 $ cd ..
1202 $ cd ..
1203
1203
1204 Incoming and outgoing:
1204 Incoming and outgoing:
1205
1205
1206 $ hg clone -U -r31 repo repo2
1206 $ hg clone -U -r31 repo repo2
1207 adding changesets
1207 adding changesets
1208 adding manifests
1208 adding manifests
1209 adding file changes
1209 adding file changes
1210 added 31 changesets with 31 changes to 1 files
1210 added 31 changesets with 31 changes to 1 files
1211 $ cd repo2
1211 $ cd repo2
1212
1212
1213 $ hg incoming --graph ../repo
1213 $ hg incoming --graph ../repo
1214 comparing with ../repo
1214 comparing with ../repo
1215 searching for changes
1215 searching for changes
1216 o changeset: 34:fea3ac5810e0
1216 o changeset: 34:fea3ac5810e0
1217 | tag: tip
1217 | tag: tip
1218 | parent: 32:d06dffa21a31
1218 | parent: 32:d06dffa21a31
1219 | user: test
1219 | user: test
1220 | date: Thu Jan 01 00:00:34 1970 +0000
1220 | date: Thu Jan 01 00:00:34 1970 +0000
1221 | summary: (34) head
1221 | summary: (34) head
1222 |
1222 |
1223 | o changeset: 33:68608f5145f9
1223 | o changeset: 33:68608f5145f9
1224 | parent: 18:1aa84d96232a
1224 | parent: 18:1aa84d96232a
1225 | user: test
1225 | user: test
1226 | date: Thu Jan 01 00:00:33 1970 +0000
1226 | date: Thu Jan 01 00:00:33 1970 +0000
1227 | summary: (33) head
1227 | summary: (33) head
1228 |
1228 |
1229 o changeset: 32:d06dffa21a31
1229 o changeset: 32:d06dffa21a31
1230 | parent: 27:886ed638191b
1230 | parent: 27:886ed638191b
1231 | parent: 31:621d83e11f67
1231 | parent: 31:621d83e11f67
1232 | user: test
1232 | user: test
1233 | date: Thu Jan 01 00:00:32 1970 +0000
1233 | date: Thu Jan 01 00:00:32 1970 +0000
1234 | summary: (32) expand
1234 | summary: (32) expand
1235 |
1235 |
1236 o changeset: 27:886ed638191b
1236 o changeset: 27:886ed638191b
1237 parent: 21:d42a756af44d
1237 parent: 21:d42a756af44d
1238 user: test
1238 user: test
1239 date: Thu Jan 01 00:00:27 1970 +0000
1239 date: Thu Jan 01 00:00:27 1970 +0000
1240 summary: (27) collapse
1240 summary: (27) collapse
1241
1241
1242 $ cd ..
1242 $ cd ..
1243
1243
1244 $ hg -R repo outgoing --graph repo2
1244 $ hg -R repo outgoing --graph repo2
1245 comparing with repo2
1245 comparing with repo2
1246 searching for changes
1246 searching for changes
1247 @ changeset: 34:fea3ac5810e0
1247 @ changeset: 34:fea3ac5810e0
1248 | tag: tip
1248 | tag: tip
1249 | parent: 32:d06dffa21a31
1249 | parent: 32:d06dffa21a31
1250 | user: test
1250 | user: test
1251 | date: Thu Jan 01 00:00:34 1970 +0000
1251 | date: Thu Jan 01 00:00:34 1970 +0000
1252 | summary: (34) head
1252 | summary: (34) head
1253 |
1253 |
1254 | o changeset: 33:68608f5145f9
1254 | o changeset: 33:68608f5145f9
1255 | parent: 18:1aa84d96232a
1255 | parent: 18:1aa84d96232a
1256 | user: test
1256 | user: test
1257 | date: Thu Jan 01 00:00:33 1970 +0000
1257 | date: Thu Jan 01 00:00:33 1970 +0000
1258 | summary: (33) head
1258 | summary: (33) head
1259 |
1259 |
1260 o changeset: 32:d06dffa21a31
1260 o changeset: 32:d06dffa21a31
1261 | parent: 27:886ed638191b
1261 | parent: 27:886ed638191b
1262 | parent: 31:621d83e11f67
1262 | parent: 31:621d83e11f67
1263 | user: test
1263 | user: test
1264 | date: Thu Jan 01 00:00:32 1970 +0000
1264 | date: Thu Jan 01 00:00:32 1970 +0000
1265 | summary: (32) expand
1265 | summary: (32) expand
1266 |
1266 |
1267 o changeset: 27:886ed638191b
1267 o changeset: 27:886ed638191b
1268 parent: 21:d42a756af44d
1268 parent: 21:d42a756af44d
1269 user: test
1269 user: test
1270 date: Thu Jan 01 00:00:27 1970 +0000
1270 date: Thu Jan 01 00:00:27 1970 +0000
1271 summary: (27) collapse
1271 summary: (27) collapse
1272
1272
1273
1273
1274 File + limit with revs != cset revs:
1274 File + limit with revs != cset revs:
1275 $ cd repo
1275 $ cd repo
1276 $ touch b
1276 $ touch b
1277 $ hg ci -Aqm0
1277 $ hg ci -Aqm0
1278 $ hg log -G -l2 a
1278 $ hg log -G -l2 a
1279 o changeset: 34:fea3ac5810e0
1279 o changeset: 34:fea3ac5810e0
1280 | parent: 32:d06dffa21a31
1280 | parent: 32:d06dffa21a31
1281 | user: test
1281 | user: test
1282 | date: Thu Jan 01 00:00:34 1970 +0000
1282 | date: Thu Jan 01 00:00:34 1970 +0000
1283 | summary: (34) head
1283 | summary: (34) head
1284 |
1284 |
1285 | o changeset: 33:68608f5145f9
1285 | o changeset: 33:68608f5145f9
1286 | | parent: 18:1aa84d96232a
1286 | | parent: 18:1aa84d96232a
1287 | | user: test
1287 | | user: test
1288 | | date: Thu Jan 01 00:00:33 1970 +0000
1288 | | date: Thu Jan 01 00:00:33 1970 +0000
1289 | | summary: (33) head
1289 | | summary: (33) head
1290 | |
1290 | |
1291
1291
1292 File + limit + -ra:b, (b - a) < limit:
1292 File + limit + -ra:b, (b - a) < limit:
1293 $ hg log -G -l3000 -r32:tip a
1293 $ hg log -G -l3000 -r32:tip a
1294 o changeset: 34:fea3ac5810e0
1294 o changeset: 34:fea3ac5810e0
1295 | parent: 32:d06dffa21a31
1295 | parent: 32:d06dffa21a31
1296 | user: test
1296 | user: test
1297 | date: Thu Jan 01 00:00:34 1970 +0000
1297 | date: Thu Jan 01 00:00:34 1970 +0000
1298 | summary: (34) head
1298 | summary: (34) head
1299 |
1299 |
1300 | o changeset: 33:68608f5145f9
1300 | o changeset: 33:68608f5145f9
1301 | | parent: 18:1aa84d96232a
1301 | | parent: 18:1aa84d96232a
1302 | | user: test
1302 | | user: test
1303 | | date: Thu Jan 01 00:00:33 1970 +0000
1303 | | date: Thu Jan 01 00:00:33 1970 +0000
1304 | | summary: (33) head
1304 | | summary: (33) head
1305 | |
1305 | |
1306 o | changeset: 32:d06dffa21a31
1306 o | changeset: 32:d06dffa21a31
1307 |\ \ parent: 27:886ed638191b
1307 |\ \ parent: 27:886ed638191b
1308 | | | parent: 31:621d83e11f67
1308 | | | parent: 31:621d83e11f67
1309 | | | user: test
1309 | | | user: test
1310 | | | date: Thu Jan 01 00:00:32 1970 +0000
1310 | | | date: Thu Jan 01 00:00:32 1970 +0000
1311 | | | summary: (32) expand
1311 | | | summary: (32) expand
1312 | | |
1312 | | |
1313
1313
1314 Point out a common and an uncommon unshown parent
1314 Point out a common and an uncommon unshown parent
1315
1315
1316 $ hg log -G -r 'rev(8) or rev(9)'
1316 $ hg log -G -r 'rev(8) or rev(9)'
1317 o changeset: 9:7010c0af0a35
1317 o changeset: 9:7010c0af0a35
1318 |\ parent: 7:b632bb1b1224
1318 |\ parent: 7:b632bb1b1224
1319 | | parent: 8:7a0b11f71937
1319 | | parent: 8:7a0b11f71937
1320 | | user: test
1320 | | user: test
1321 | | date: Thu Jan 01 00:00:09 1970 +0000
1321 | | date: Thu Jan 01 00:00:09 1970 +0000
1322 | | summary: (9) expand
1322 | | summary: (9) expand
1323 | |
1323 | |
1324 o | changeset: 8:7a0b11f71937
1324 o | changeset: 8:7a0b11f71937
1325 |\| parent: 0:e6eb3150255d
1325 |\| parent: 0:e6eb3150255d
1326 | | parent: 7:b632bb1b1224
1326 | | parent: 7:b632bb1b1224
1327 | | user: test
1327 | | user: test
1328 | | date: Thu Jan 01 00:00:08 1970 +0000
1328 | | date: Thu Jan 01 00:00:08 1970 +0000
1329 | | summary: (8) merge two known; one immediate left, one far right
1329 | | summary: (8) merge two known; one immediate left, one far right
1330 | |
1330 | |
1331
1331
1332 File + limit + -ra:b, b < tip:
1332 File + limit + -ra:b, b < tip:
1333
1333
1334 $ hg log -G -l1 -r32:34 a
1334 $ hg log -G -l1 -r32:34 a
1335 o changeset: 34:fea3ac5810e0
1335 o changeset: 34:fea3ac5810e0
1336 | parent: 32:d06dffa21a31
1336 | parent: 32:d06dffa21a31
1337 | user: test
1337 | user: test
1338 | date: Thu Jan 01 00:00:34 1970 +0000
1338 | date: Thu Jan 01 00:00:34 1970 +0000
1339 | summary: (34) head
1339 | summary: (34) head
1340 |
1340 |
1341
1341
1342 file(File) + limit + -ra:b, b < tip:
1342 file(File) + limit + -ra:b, b < tip:
1343
1343
1344 $ hg log -G -l1 -r32:34 -r 'file("a")'
1344 $ hg log -G -l1 -r32:34 -r 'file("a")'
1345 o changeset: 34:fea3ac5810e0
1345 o changeset: 34:fea3ac5810e0
1346 | parent: 32:d06dffa21a31
1346 | parent: 32:d06dffa21a31
1347 | user: test
1347 | user: test
1348 | date: Thu Jan 01 00:00:34 1970 +0000
1348 | date: Thu Jan 01 00:00:34 1970 +0000
1349 | summary: (34) head
1349 | summary: (34) head
1350 |
1350 |
1351
1351
1352 limit(file(File) and a::b), b < tip:
1352 limit(file(File) and a::b), b < tip:
1353
1353
1354 $ hg log -G -r 'limit(file("a") and 32::34, 1)'
1354 $ hg log -G -r 'limit(file("a") and 32::34, 1)'
1355 o changeset: 32:d06dffa21a31
1355 o changeset: 32:d06dffa21a31
1356 |\ parent: 27:886ed638191b
1356 |\ parent: 27:886ed638191b
1357 | | parent: 31:621d83e11f67
1357 | | parent: 31:621d83e11f67
1358 | | user: test
1358 | | user: test
1359 | | date: Thu Jan 01 00:00:32 1970 +0000
1359 | | date: Thu Jan 01 00:00:32 1970 +0000
1360 | | summary: (32) expand
1360 | | summary: (32) expand
1361 | |
1361 | |
1362
1362
1363 File + limit + -ra:b, b < tip:
1363 File + limit + -ra:b, b < tip:
1364
1364
1365 $ hg log -G -r 'limit(file("a") and 34::32, 1)'
1365 $ hg log -G -r 'limit(file("a") and 34::32, 1)'
1366
1366
1367 File + limit + -ra:b, b < tip, (b - a) < limit:
1367 File + limit + -ra:b, b < tip, (b - a) < limit:
1368
1368
1369 $ hg log -G -l10 -r33:34 a
1369 $ hg log -G -l10 -r33:34 a
1370 o changeset: 34:fea3ac5810e0
1370 o changeset: 34:fea3ac5810e0
1371 | parent: 32:d06dffa21a31
1371 | parent: 32:d06dffa21a31
1372 | user: test
1372 | user: test
1373 | date: Thu Jan 01 00:00:34 1970 +0000
1373 | date: Thu Jan 01 00:00:34 1970 +0000
1374 | summary: (34) head
1374 | summary: (34) head
1375 |
1375 |
1376 | o changeset: 33:68608f5145f9
1376 | o changeset: 33:68608f5145f9
1377 | | parent: 18:1aa84d96232a
1377 | | parent: 18:1aa84d96232a
1378 | | user: test
1378 | | user: test
1379 | | date: Thu Jan 01 00:00:33 1970 +0000
1379 | | date: Thu Jan 01 00:00:33 1970 +0000
1380 | | summary: (33) head
1380 | | summary: (33) head
1381 | |
1381 | |
1382
1382
1383 Do not crash or produce strange graphs if history is buggy
1383 Do not crash or produce strange graphs if history is buggy
1384
1384
1385 $ hg branch branch
1385 $ hg branch branch
1386 marked working directory as branch branch
1386 marked working directory as branch branch
1387 (branches are permanent and global, did you want a bookmark?)
1387 (branches are permanent and global, did you want a bookmark?)
1388 $ commit 36 "buggy merge: identical parents" 35 35
1388 $ commit 36 "buggy merge: identical parents" 35 35
1389 $ hg log -G -l5
1389 $ hg log -G -l5
1390 @ changeset: 36:08a19a744424
1390 @ changeset: 36:08a19a744424
1391 | branch: branch
1391 | branch: branch
1392 | tag: tip
1392 | tag: tip
1393 | parent: 35:9159c3644c5e
1393 | parent: 35:9159c3644c5e
1394 | parent: 35:9159c3644c5e
1394 | parent: 35:9159c3644c5e
1395 | user: test
1395 | user: test
1396 | date: Thu Jan 01 00:00:36 1970 +0000
1396 | date: Thu Jan 01 00:00:36 1970 +0000
1397 | summary: (36) buggy merge: identical parents
1397 | summary: (36) buggy merge: identical parents
1398 |
1398 |
1399 o changeset: 35:9159c3644c5e
1399 o changeset: 35:9159c3644c5e
1400 | user: test
1400 | user: test
1401 | date: Thu Jan 01 00:00:00 1970 +0000
1401 | date: Thu Jan 01 00:00:00 1970 +0000
1402 | summary: 0
1402 | summary: 0
1403 |
1403 |
1404 o changeset: 34:fea3ac5810e0
1404 o changeset: 34:fea3ac5810e0
1405 | parent: 32:d06dffa21a31
1405 | parent: 32:d06dffa21a31
1406 | user: test
1406 | user: test
1407 | date: Thu Jan 01 00:00:34 1970 +0000
1407 | date: Thu Jan 01 00:00:34 1970 +0000
1408 | summary: (34) head
1408 | summary: (34) head
1409 |
1409 |
1410 | o changeset: 33:68608f5145f9
1410 | o changeset: 33:68608f5145f9
1411 | | parent: 18:1aa84d96232a
1411 | | parent: 18:1aa84d96232a
1412 | | user: test
1412 | | user: test
1413 | | date: Thu Jan 01 00:00:33 1970 +0000
1413 | | date: Thu Jan 01 00:00:33 1970 +0000
1414 | | summary: (33) head
1414 | | summary: (33) head
1415 | |
1415 | |
1416 o | changeset: 32:d06dffa21a31
1416 o | changeset: 32:d06dffa21a31
1417 |\ \ parent: 27:886ed638191b
1417 |\ \ parent: 27:886ed638191b
1418 | | | parent: 31:621d83e11f67
1418 | | | parent: 31:621d83e11f67
1419 | | | user: test
1419 | | | user: test
1420 | | | date: Thu Jan 01 00:00:32 1970 +0000
1420 | | | date: Thu Jan 01 00:00:32 1970 +0000
1421 | | | summary: (32) expand
1421 | | | summary: (32) expand
1422 | | |
1422 | | |
1423
1423
1424 Test log -G options
1424 Test log -G options
1425
1425
1426 $ testlog() {
1426 $ testlog() {
1427 > hg log -G --print-revset "$@"
1427 > hg log -G --print-revset "$@"
1428 > hg log --template 'nodetag {rev}\n' "$@" | grep nodetag \
1428 > hg log --template 'nodetag {rev}\n' "$@" | grep nodetag \
1429 > | sed 's/.*nodetag/nodetag/' > log.nodes
1429 > | sed 's/.*nodetag/nodetag/' > log.nodes
1430 > hg log -G --template 'nodetag {rev}\n' "$@" | grep nodetag \
1430 > hg log -G --template 'nodetag {rev}\n' "$@" | grep nodetag \
1431 > | sed 's/.*nodetag/nodetag/' > glog.nodes
1431 > | sed 's/.*nodetag/nodetag/' > glog.nodes
1432 > diff -u log.nodes glog.nodes | grep '^[-+@ ]' || :
1432 > diff -u log.nodes glog.nodes | grep '^[-+@ ]' || :
1433 > }
1433 > }
1434
1434
1435 glog always reorders nodes which explains the difference with log
1435 glog always reorders nodes which explains the difference with log
1436
1436
1437 $ testlog -r 27 -r 25 -r 21 -r 34 -r 32 -r 31
1437 $ testlog -r 27 -r 25 -r 21 -r 34 -r 32 -r 31
1438 ['27', '25', '21', '34', '32', '31']
1438 ['27', '25', '21', '34', '32', '31']
1439 []
1439 []
1440 --- log.nodes * (glob)
1440 --- log.nodes * (glob)
1441 +++ glog.nodes * (glob)
1441 +++ glog.nodes * (glob)
1442 @@ -1,6 +1,6 @@
1442 @@ -1,6 +1,6 @@
1443 -nodetag 27
1443 -nodetag 27
1444 -nodetag 25
1444 -nodetag 25
1445 -nodetag 21
1445 -nodetag 21
1446 nodetag 34
1446 nodetag 34
1447 nodetag 32
1447 nodetag 32
1448 nodetag 31
1448 nodetag 31
1449 +nodetag 27
1449 +nodetag 27
1450 +nodetag 25
1450 +nodetag 25
1451 +nodetag 21
1451 +nodetag 21
1452 $ testlog -u test -u not-a-user
1452 $ testlog -u test -u not-a-user
1453 []
1453 []
1454 (group
1454 (group
1455 (group
1455 (group
1456 (or
1456 (or
1457 (func
1457 (func
1458 ('symbol', 'user')
1458 ('symbol', 'user')
1459 ('string', 'test'))
1459 ('string', 'test'))
1460 (func
1460 (func
1461 ('symbol', 'user')
1461 ('symbol', 'user')
1462 ('string', 'not-a-user')))))
1462 ('string', 'not-a-user')))))
1463 $ testlog -b not-a-branch
1463 $ testlog -b not-a-branch
1464 abort: unknown revision 'not-a-branch'!
1464 abort: unknown revision 'not-a-branch'!
1465 abort: unknown revision 'not-a-branch'!
1465 abort: unknown revision 'not-a-branch'!
1466 abort: unknown revision 'not-a-branch'!
1466 abort: unknown revision 'not-a-branch'!
1467 $ testlog -b 35 -b 36 --only-branch branch
1467 $ testlog -b 35 -b 36 --only-branch branch
1468 []
1468 []
1469 (group
1469 (group
1470 (group
1470 (group
1471 (or
1471 (or
1472 (or
1472 (or
1473 (func
1473 (func
1474 ('symbol', 'branch')
1474 ('symbol', 'branch')
1475 ('string', 'default'))
1475 ('string', 'default'))
1476 (func
1476 (func
1477 ('symbol', 'branch')
1477 ('symbol', 'branch')
1478 ('string', 'branch')))
1478 ('string', 'branch')))
1479 (func
1479 (func
1480 ('symbol', 'branch')
1480 ('symbol', 'branch')
1481 ('string', 'branch')))))
1481 ('string', 'branch')))))
1482 $ testlog -k expand -k merge
1482 $ testlog -k expand -k merge
1483 []
1483 []
1484 (group
1484 (group
1485 (group
1485 (group
1486 (or
1486 (or
1487 (func
1487 (func
1488 ('symbol', 'keyword')
1488 ('symbol', 'keyword')
1489 ('string', 'expand'))
1489 ('string', 'expand'))
1490 (func
1490 (func
1491 ('symbol', 'keyword')
1491 ('symbol', 'keyword')
1492 ('string', 'merge')))))
1492 ('string', 'merge')))))
1493 $ testlog --only-merges
1493 $ testlog --only-merges
1494 []
1494 []
1495 (group
1495 (group
1496 (func
1496 (func
1497 ('symbol', 'merge')
1497 ('symbol', 'merge')
1498 None))
1498 None))
1499 $ testlog --no-merges
1499 $ testlog --no-merges
1500 []
1500 []
1501 (group
1501 (group
1502 (not
1502 (not
1503 (func
1503 (func
1504 ('symbol', 'merge')
1504 ('symbol', 'merge')
1505 None)))
1505 None)))
1506 $ testlog --date '2 0 to 4 0'
1506 $ testlog --date '2 0 to 4 0'
1507 []
1507 []
1508 (group
1508 (group
1509 (func
1509 (func
1510 ('symbol', 'date')
1510 ('symbol', 'date')
1511 ('string', '2 0 to 4 0')))
1511 ('string', '2 0 to 4 0')))
1512 $ hg log -G -d 'brace ) in a date'
1512 $ hg log -G -d 'brace ) in a date'
1513 abort: invalid date: 'brace ) in a date'
1513 abort: invalid date: 'brace ) in a date'
1514 [255]
1514 [255]
1515 $ testlog --prune 31 --prune 32
1515 $ testlog --prune 31 --prune 32
1516 []
1516 []
1517 (group
1517 (group
1518 (group
1518 (group
1519 (and
1519 (and
1520 (not
1520 (not
1521 (group
1521 (group
1522 (or
1522 (or
1523 ('string', '31')
1523 ('string', '31')
1524 (func
1524 (func
1525 ('symbol', 'ancestors')
1525 ('symbol', 'ancestors')
1526 ('string', '31')))))
1526 ('string', '31')))))
1527 (not
1527 (not
1528 (group
1528 (group
1529 (or
1529 (or
1530 ('string', '32')
1530 ('string', '32')
1531 (func
1531 (func
1532 ('symbol', 'ancestors')
1532 ('symbol', 'ancestors')
1533 ('string', '32'))))))))
1533 ('string', '32'))))))))
1534
1534
1535 Dedicated repo for --follow and paths filtering. The g is crafted to
1535 Dedicated repo for --follow and paths filtering. The g is crafted to
1536 have 2 filelog topological heads in a linear changeset graph.
1536 have 2 filelog topological heads in a linear changeset graph.
1537
1537
1538 $ cd ..
1538 $ cd ..
1539 $ hg init follow
1539 $ hg init follow
1540 $ cd follow
1540 $ cd follow
1541 $ testlog --follow
1541 $ testlog --follow
1542 []
1542 []
1543 []
1543 []
1544 $ testlog -rnull
1544 $ testlog -rnull
1545 ['null']
1545 ['null']
1546 []
1546 []
1547 $ echo a > a
1547 $ echo a > a
1548 $ echo aa > aa
1548 $ echo aa > aa
1549 $ echo f > f
1549 $ echo f > f
1550 $ hg ci -Am "add a" a aa f
1550 $ hg ci -Am "add a" a aa f
1551 $ hg cp a b
1551 $ hg cp a b
1552 $ hg cp f g
1552 $ hg cp f g
1553 $ hg ci -m "copy a b"
1553 $ hg ci -m "copy a b"
1554 $ mkdir dir
1554 $ mkdir dir
1555 $ hg mv b dir
1555 $ hg mv b dir
1556 $ echo g >> g
1556 $ echo g >> g
1557 $ echo f >> f
1557 $ echo f >> f
1558 $ hg ci -m "mv b dir/b"
1558 $ hg ci -m "mv b dir/b"
1559 $ hg mv a b
1559 $ hg mv a b
1560 $ hg cp -f f g
1560 $ hg cp -f f g
1561 $ echo a > d
1561 $ echo a > d
1562 $ hg add d
1562 $ hg add d
1563 $ hg ci -m "mv a b; add d"
1563 $ hg ci -m "mv a b; add d"
1564 $ hg mv dir/b e
1564 $ hg mv dir/b e
1565 $ hg ci -m "mv dir/b e"
1565 $ hg ci -m "mv dir/b e"
1566 $ hg log -G --template '({rev}) {desc|firstline}\n'
1566 $ hg log -G --template '({rev}) {desc|firstline}\n'
1567 @ (4) mv dir/b e
1567 @ (4) mv dir/b e
1568 |
1568 |
1569 o (3) mv a b; add d
1569 o (3) mv a b; add d
1570 |
1570 |
1571 o (2) mv b dir/b
1571 o (2) mv b dir/b
1572 |
1572 |
1573 o (1) copy a b
1573 o (1) copy a b
1574 |
1574 |
1575 o (0) add a
1575 o (0) add a
1576
1576
1577
1577
1578 $ testlog a
1578 $ testlog a
1579 []
1579 []
1580 (group
1580 (group
1581 (group
1581 (group
1582 (func
1582 (func
1583 ('symbol', 'filelog')
1583 ('symbol', 'filelog')
1584 ('string', 'a'))))
1584 ('string', 'a'))))
1585 $ testlog a b
1585 $ testlog a b
1586 []
1586 []
1587 (group
1587 (group
1588 (group
1588 (group
1589 (or
1589 (or
1590 (func
1590 (func
1591 ('symbol', 'filelog')
1591 ('symbol', 'filelog')
1592 ('string', 'a'))
1592 ('string', 'a'))
1593 (func
1593 (func
1594 ('symbol', 'filelog')
1594 ('symbol', 'filelog')
1595 ('string', 'b')))))
1595 ('string', 'b')))))
1596
1596
1597 Test falling back to slow path for non-existing files
1597 Test falling back to slow path for non-existing files
1598
1598
1599 $ testlog a c
1599 $ testlog a c
1600 []
1600 []
1601 (group
1601 (group
1602 (func
1602 (func
1603 ('symbol', '_matchfiles')
1603 ('symbol', '_matchfiles')
1604 (list
1604 (list
1605 (list
1605 (list
1606 (list
1606 (list
1607 ('string', 'r:')
1607 ('string', 'r:')
1608 ('string', 'd:relpath'))
1608 ('string', 'd:relpath'))
1609 ('string', 'p:a'))
1609 ('string', 'p:a'))
1610 ('string', 'p:c'))))
1610 ('string', 'p:c'))))
1611
1611
1612 Test multiple --include/--exclude/paths
1612 Test multiple --include/--exclude/paths
1613
1613
1614 $ testlog --include a --include e --exclude b --exclude e a e
1614 $ testlog --include a --include e --exclude b --exclude e a e
1615 []
1615 []
1616 (group
1616 (group
1617 (func
1617 (func
1618 ('symbol', '_matchfiles')
1618 ('symbol', '_matchfiles')
1619 (list
1619 (list
1620 (list
1620 (list
1621 (list
1621 (list
1622 (list
1622 (list
1623 (list
1623 (list
1624 (list
1624 (list
1625 (list
1625 (list
1626 ('string', 'r:')
1626 ('string', 'r:')
1627 ('string', 'd:relpath'))
1627 ('string', 'd:relpath'))
1628 ('string', 'p:a'))
1628 ('string', 'p:a'))
1629 ('string', 'p:e'))
1629 ('string', 'p:e'))
1630 ('string', 'i:a'))
1630 ('string', 'i:a'))
1631 ('string', 'i:e'))
1631 ('string', 'i:e'))
1632 ('string', 'x:b'))
1632 ('string', 'x:b'))
1633 ('string', 'x:e'))))
1633 ('string', 'x:e'))))
1634
1634
1635 Test glob expansion of pats
1635 Test glob expansion of pats
1636
1636
1637 $ expandglobs=`$PYTHON -c "import mercurial.util; \
1637 $ expandglobs=`$PYTHON -c "import mercurial.util; \
1638 > print mercurial.util.expandglobs and 'true' or 'false'"`
1638 > print mercurial.util.expandglobs and 'true' or 'false'"`
1639 $ if [ $expandglobs = "true" ]; then
1639 $ if [ $expandglobs = "true" ]; then
1640 > testlog 'a*';
1640 > testlog 'a*';
1641 > else
1641 > else
1642 > testlog a*;
1642 > testlog a*;
1643 > fi;
1643 > fi;
1644 []
1644 []
1645 (group
1645 (group
1646 (group
1646 (group
1647 (func
1647 (func
1648 ('symbol', 'filelog')
1648 ('symbol', 'filelog')
1649 ('string', 'aa'))))
1649 ('string', 'aa'))))
1650
1650
1651 Test --follow on a non-existent directory
1651 Test --follow on a non-existent directory
1652
1652
1653 $ testlog -f dir
1653 $ testlog -f dir
1654 abort: cannot follow file not in parent revision: "dir"
1654 abort: cannot follow file not in parent revision: "dir"
1655 abort: cannot follow file not in parent revision: "dir"
1655 abort: cannot follow file not in parent revision: "dir"
1656 abort: cannot follow file not in parent revision: "dir"
1656 abort: cannot follow file not in parent revision: "dir"
1657
1657
1658 Test --follow on a directory
1658 Test --follow on a directory
1659
1659
1660 $ hg up -q '.^'
1660 $ hg up -q '.^'
1661 $ testlog -f dir
1661 $ testlog -f dir
1662 []
1662 []
1663 (group
1663 (group
1664 (and
1664 (and
1665 (func
1665 (func
1666 ('symbol', 'ancestors')
1666 ('symbol', 'ancestors')
1667 ('symbol', '.'))
1667 ('symbol', '.'))
1668 (func
1668 (func
1669 ('symbol', '_matchfiles')
1669 ('symbol', '_matchfiles')
1670 (list
1670 (list
1671 (list
1671 (list
1672 ('string', 'r:')
1672 ('string', 'r:')
1673 ('string', 'd:relpath'))
1673 ('string', 'd:relpath'))
1674 ('string', 'p:dir')))))
1674 ('string', 'p:dir')))))
1675 $ hg up -q tip
1675 $ hg up -q tip
1676
1676
1677 Test --follow on file not in parent revision
1677 Test --follow on file not in parent revision
1678
1678
1679 $ testlog -f a
1679 $ testlog -f a
1680 abort: cannot follow file not in parent revision: "a"
1680 abort: cannot follow file not in parent revision: "a"
1681 abort: cannot follow file not in parent revision: "a"
1681 abort: cannot follow file not in parent revision: "a"
1682 abort: cannot follow file not in parent revision: "a"
1682 abort: cannot follow file not in parent revision: "a"
1683
1683
1684 Test --follow and patterns
1684 Test --follow and patterns
1685
1685
1686 $ testlog -f 'glob:*'
1686 $ testlog -f 'glob:*'
1687 []
1687 []
1688 (group
1688 (group
1689 (and
1689 (and
1690 (func
1690 (func
1691 ('symbol', 'ancestors')
1691 ('symbol', 'ancestors')
1692 ('symbol', '.'))
1692 ('symbol', '.'))
1693 (func
1693 (func
1694 ('symbol', '_matchfiles')
1694 ('symbol', '_matchfiles')
1695 (list
1695 (list
1696 (list
1696 (list
1697 ('string', 'r:')
1697 ('string', 'r:')
1698 ('string', 'd:relpath'))
1698 ('string', 'd:relpath'))
1699 ('string', 'p:glob:*')))))
1699 ('string', 'p:glob:*')))))
1700
1700
1701 Test --follow on a single rename
1701 Test --follow on a single rename
1702
1702
1703 $ hg up -q 2
1703 $ hg up -q 2
1704 $ testlog -f a
1704 $ testlog -f a
1705 []
1705 []
1706 (group
1706 (group
1707 (group
1707 (group
1708 (func
1708 (func
1709 ('symbol', 'follow')
1709 ('symbol', 'follow')
1710 ('string', 'a'))))
1710 ('string', 'a'))))
1711
1711
1712 Test --follow and multiple renames
1712 Test --follow and multiple renames
1713
1713
1714 $ hg up -q tip
1714 $ hg up -q tip
1715 $ testlog -f e
1715 $ testlog -f e
1716 []
1716 []
1717 (group
1717 (group
1718 (group
1718 (group
1719 (func
1719 (func
1720 ('symbol', 'follow')
1720 ('symbol', 'follow')
1721 ('string', 'e'))))
1721 ('string', 'e'))))
1722
1722
1723 Test --follow and multiple filelog heads
1723 Test --follow and multiple filelog heads
1724
1724
1725 $ hg up -q 2
1725 $ hg up -q 2
1726 $ testlog -f g
1726 $ testlog -f g
1727 []
1727 []
1728 (group
1728 (group
1729 (group
1729 (group
1730 (func
1730 (func
1731 ('symbol', 'follow')
1731 ('symbol', 'follow')
1732 ('string', 'g'))))
1732 ('string', 'g'))))
1733 $ cat log.nodes
1733 $ cat log.nodes
1734 nodetag 2
1734 nodetag 2
1735 nodetag 1
1735 nodetag 1
1736 nodetag 0
1736 nodetag 0
1737 $ hg up -q tip
1737 $ hg up -q tip
1738 $ testlog -f g
1738 $ testlog -f g
1739 []
1739 []
1740 (group
1740 (group
1741 (group
1741 (group
1742 (func
1742 (func
1743 ('symbol', 'follow')
1743 ('symbol', 'follow')
1744 ('string', 'g'))))
1744 ('string', 'g'))))
1745 $ cat log.nodes
1745 $ cat log.nodes
1746 nodetag 3
1746 nodetag 3
1747 nodetag 2
1747 nodetag 2
1748 nodetag 0
1748 nodetag 0
1749
1749
1750 Test --follow and multiple files
1750 Test --follow and multiple files
1751
1751
1752 $ testlog -f g e
1752 $ testlog -f g e
1753 []
1753 []
1754 (group
1754 (group
1755 (group
1755 (group
1756 (or
1756 (or
1757 (func
1757 (func
1758 ('symbol', 'follow')
1758 ('symbol', 'follow')
1759 ('string', 'g'))
1759 ('string', 'g'))
1760 (func
1760 (func
1761 ('symbol', 'follow')
1761 ('symbol', 'follow')
1762 ('string', 'e')))))
1762 ('string', 'e')))))
1763 $ cat log.nodes
1763 $ cat log.nodes
1764 nodetag 4
1764 nodetag 4
1765 nodetag 3
1765 nodetag 3
1766 nodetag 2
1766 nodetag 2
1767 nodetag 1
1767 nodetag 1
1768 nodetag 0
1768 nodetag 0
1769
1769
1770 Test --follow null parent
1770 Test --follow null parent
1771
1771
1772 $ hg up -q null
1772 $ hg up -q null
1773 $ testlog -f
1773 $ testlog -f
1774 []
1774 []
1775 []
1775 []
1776
1776
1777 Test --follow-first
1777 Test --follow-first
1778
1778
1779 $ hg up -q 3
1779 $ hg up -q 3
1780 $ echo ee > e
1780 $ echo ee > e
1781 $ hg ci -Am "add another e" e
1781 $ hg ci -Am "add another e" e
1782 created new head
1782 created new head
1783 $ hg merge --tool internal:other 4
1783 $ hg merge --tool internal:other 4
1784 0 files updated, 1 files merged, 1 files removed, 0 files unresolved
1784 0 files updated, 1 files merged, 1 files removed, 0 files unresolved
1785 (branch merge, don't forget to commit)
1785 (branch merge, don't forget to commit)
1786 $ echo merge > e
1786 $ echo merge > e
1787 $ hg ci -m "merge 5 and 4"
1787 $ hg ci -m "merge 5 and 4"
1788 $ testlog --follow-first
1788 $ testlog --follow-first
1789 []
1789 []
1790 (group
1790 (group
1791 (func
1791 (func
1792 ('symbol', '_firstancestors')
1792 ('symbol', '_firstancestors')
1793 (func
1793 (func
1794 ('symbol', 'rev')
1794 ('symbol', 'rev')
1795 ('symbol', '6'))))
1795 ('symbol', '6'))))
1796
1796
1797 Cannot compare with log --follow-first FILE as it never worked
1797 Cannot compare with log --follow-first FILE as it never worked
1798
1798
1799 $ hg log -G --print-revset --follow-first e
1799 $ hg log -G --print-revset --follow-first e
1800 []
1800 []
1801 (group
1801 (group
1802 (group
1802 (group
1803 (func
1803 (func
1804 ('symbol', '_followfirst')
1804 ('symbol', '_followfirst')
1805 ('string', 'e'))))
1805 ('string', 'e'))))
1806 $ hg log -G --follow-first e --template '{rev} {desc|firstline}\n'
1806 $ hg log -G --follow-first e --template '{rev} {desc|firstline}\n'
1807 @ 6 merge 5 and 4
1807 @ 6 merge 5 and 4
1808 |\
1808 |\
1809 o | 5 add another e
1809 o | 5 add another e
1810 | |
1810 | |
1811
1811
1812 Test --copies
1812 Test --copies
1813
1813
1814 $ hg log -G --copies --template "{rev} {desc|firstline} \
1814 $ hg log -G --copies --template "{rev} {desc|firstline} \
1815 > copies: {file_copies_switch}\n"
1815 > copies: {file_copies_switch}\n"
1816 @ 6 merge 5 and 4 copies:
1816 @ 6 merge 5 and 4 copies:
1817 |\
1817 |\
1818 | o 5 add another e copies:
1818 | o 5 add another e copies:
1819 | |
1819 | |
1820 o | 4 mv dir/b e copies: e (dir/b)
1820 o | 4 mv dir/b e copies: e (dir/b)
1821 |/
1821 |/
1822 o 3 mv a b; add d copies: b (a)g (f)
1822 o 3 mv a b; add d copies: b (a)g (f)
1823 |
1823 |
1824 o 2 mv b dir/b copies: dir/b (b)
1824 o 2 mv b dir/b copies: dir/b (b)
1825 |
1825 |
1826 o 1 copy a b copies: b (a)g (f)
1826 o 1 copy a b copies: b (a)g (f)
1827 |
1827 |
1828 o 0 add a copies:
1828 o 0 add a copies:
1829
1829
1830 Test "set:..." and parent revision
1830 Test "set:..." and parent revision
1831
1831
1832 $ hg up -q 4
1832 $ hg up -q 4
1833 $ testlog "set:copied()"
1833 $ testlog "set:copied()"
1834 []
1834 []
1835 (group
1835 (group
1836 (func
1836 (func
1837 ('symbol', '_matchfiles')
1837 ('symbol', '_matchfiles')
1838 (list
1838 (list
1839 (list
1839 (list
1840 ('string', 'r:')
1840 ('string', 'r:')
1841 ('string', 'd:relpath'))
1841 ('string', 'd:relpath'))
1842 ('string', 'p:set:copied()'))))
1842 ('string', 'p:set:copied()'))))
1843 $ testlog --include "set:copied()"
1843 $ testlog --include "set:copied()"
1844 []
1844 []
1845 (group
1845 (group
1846 (func
1846 (func
1847 ('symbol', '_matchfiles')
1847 ('symbol', '_matchfiles')
1848 (list
1848 (list
1849 (list
1849 (list
1850 ('string', 'r:')
1850 ('string', 'r:')
1851 ('string', 'd:relpath'))
1851 ('string', 'd:relpath'))
1852 ('string', 'i:set:copied()'))))
1852 ('string', 'i:set:copied()'))))
1853 $ testlog -r "sort(file('set:copied()'), -rev)"
1853 $ testlog -r "sort(file('set:copied()'), -rev)"
1854 ["sort(file('set:copied()'), -rev)"]
1854 ["sort(file('set:copied()'), -rev)"]
1855 []
1855 []
1856
1856
1857 Test --removed
1857 Test --removed
1858
1858
1859 $ testlog --removed
1859 $ testlog --removed
1860 []
1860 []
1861 []
1861 []
1862 $ testlog --removed a
1862 $ testlog --removed a
1863 []
1863 []
1864 (group
1864 (group
1865 (func
1865 (func
1866 ('symbol', '_matchfiles')
1866 ('symbol', '_matchfiles')
1867 (list
1867 (list
1868 (list
1868 (list
1869 ('string', 'r:')
1869 ('string', 'r:')
1870 ('string', 'd:relpath'))
1870 ('string', 'd:relpath'))
1871 ('string', 'p:a'))))
1871 ('string', 'p:a'))))
1872 $ testlog --removed --follow a
1872 $ testlog --removed --follow a
1873 []
1873 []
1874 (group
1874 (group
1875 (and
1875 (and
1876 (func
1876 (func
1877 ('symbol', 'ancestors')
1877 ('symbol', 'ancestors')
1878 ('symbol', '.'))
1878 ('symbol', '.'))
1879 (func
1879 (func
1880 ('symbol', '_matchfiles')
1880 ('symbol', '_matchfiles')
1881 (list
1881 (list
1882 (list
1882 (list
1883 ('string', 'r:')
1883 ('string', 'r:')
1884 ('string', 'd:relpath'))
1884 ('string', 'd:relpath'))
1885 ('string', 'p:a')))))
1885 ('string', 'p:a')))))
1886
1886
1887 Test --patch and --stat with --follow and --follow-first
1887 Test --patch and --stat with --follow and --follow-first
1888
1888
1889 $ hg up -q 3
1889 $ hg up -q 3
1890 $ hg log -G --git --patch b
1890 $ hg log -G --git --patch b
1891 o changeset: 1:216d4c92cf98
1891 o changeset: 1:216d4c92cf98
1892 | user: test
1892 | user: test
1893 | date: Thu Jan 01 00:00:00 1970 +0000
1893 | date: Thu Jan 01 00:00:00 1970 +0000
1894 | summary: copy a b
1894 | summary: copy a b
1895 |
1895 |
1896 | diff --git a/a b/b
1896 | diff --git a/a b/b
1897 | copy from a
1897 | copy from a
1898 | copy to b
1898 | copy to b
1899 |
1899 |
1900
1900
1901 $ hg log -G --git --stat b
1901 $ hg log -G --git --stat b
1902 o changeset: 1:216d4c92cf98
1902 o changeset: 1:216d4c92cf98
1903 | user: test
1903 | user: test
1904 | date: Thu Jan 01 00:00:00 1970 +0000
1904 | date: Thu Jan 01 00:00:00 1970 +0000
1905 | summary: copy a b
1905 | summary: copy a b
1906 |
1906 |
1907 | b | 0
1907 | b | 0
1908 | 1 files changed, 0 insertions(+), 0 deletions(-)
1908 | 1 files changed, 0 insertions(+), 0 deletions(-)
1909 |
1909 |
1910
1910
1911 $ hg log -G --git --patch --follow b
1911 $ hg log -G --git --patch --follow b
1912 o changeset: 1:216d4c92cf98
1912 o changeset: 1:216d4c92cf98
1913 | user: test
1913 | user: test
1914 | date: Thu Jan 01 00:00:00 1970 +0000
1914 | date: Thu Jan 01 00:00:00 1970 +0000
1915 | summary: copy a b
1915 | summary: copy a b
1916 |
1916 |
1917 | diff --git a/a b/b
1917 | diff --git a/a b/b
1918 | copy from a
1918 | copy from a
1919 | copy to b
1919 | copy to b
1920 |
1920 |
1921 o changeset: 0:f8035bb17114
1921 o changeset: 0:f8035bb17114
1922 user: test
1922 user: test
1923 date: Thu Jan 01 00:00:00 1970 +0000
1923 date: Thu Jan 01 00:00:00 1970 +0000
1924 summary: add a
1924 summary: add a
1925
1925
1926 diff --git a/a b/a
1926 diff --git a/a b/a
1927 new file mode 100644
1927 new file mode 100644
1928 --- /dev/null
1928 --- /dev/null
1929 +++ b/a
1929 +++ b/a
1930 @@ -0,0 +1,1 @@
1930 @@ -0,0 +1,1 @@
1931 +a
1931 +a
1932
1932
1933
1933
1934 $ hg log -G --git --stat --follow b
1934 $ hg log -G --git --stat --follow b
1935 o changeset: 1:216d4c92cf98
1935 o changeset: 1:216d4c92cf98
1936 | user: test
1936 | user: test
1937 | date: Thu Jan 01 00:00:00 1970 +0000
1937 | date: Thu Jan 01 00:00:00 1970 +0000
1938 | summary: copy a b
1938 | summary: copy a b
1939 |
1939 |
1940 | b | 0
1940 | b | 0
1941 | 1 files changed, 0 insertions(+), 0 deletions(-)
1941 | 1 files changed, 0 insertions(+), 0 deletions(-)
1942 |
1942 |
1943 o changeset: 0:f8035bb17114
1943 o changeset: 0:f8035bb17114
1944 user: test
1944 user: test
1945 date: Thu Jan 01 00:00:00 1970 +0000
1945 date: Thu Jan 01 00:00:00 1970 +0000
1946 summary: add a
1946 summary: add a
1947
1947
1948 a | 1 +
1948 a | 1 +
1949 1 files changed, 1 insertions(+), 0 deletions(-)
1949 1 files changed, 1 insertions(+), 0 deletions(-)
1950
1950
1951
1951
1952 $ hg up -q 6
1952 $ hg up -q 6
1953 $ hg log -G --git --patch --follow-first e
1953 $ hg log -G --git --patch --follow-first e
1954 @ changeset: 6:fc281d8ff18d
1954 @ changeset: 6:fc281d8ff18d
1955 |\ tag: tip
1955 |\ tag: tip
1956 | | parent: 5:99b31f1c2782
1956 | | parent: 5:99b31f1c2782
1957 | | parent: 4:17d952250a9d
1957 | | parent: 4:17d952250a9d
1958 | | user: test
1958 | | user: test
1959 | | date: Thu Jan 01 00:00:00 1970 +0000
1959 | | date: Thu Jan 01 00:00:00 1970 +0000
1960 | | summary: merge 5 and 4
1960 | | summary: merge 5 and 4
1961 | |
1961 | |
1962 | | diff --git a/e b/e
1962 | | diff --git a/e b/e
1963 | | --- a/e
1963 | | --- a/e
1964 | | +++ b/e
1964 | | +++ b/e
1965 | | @@ -1,1 +1,1 @@
1965 | | @@ -1,1 +1,1 @@
1966 | | -ee
1966 | | -ee
1967 | | +merge
1967 | | +merge
1968 | |
1968 | |
1969 o | changeset: 5:99b31f1c2782
1969 o | changeset: 5:99b31f1c2782
1970 | | parent: 3:5918b8d165d1
1970 | | parent: 3:5918b8d165d1
1971 | | user: test
1971 | | user: test
1972 | | date: Thu Jan 01 00:00:00 1970 +0000
1972 | | date: Thu Jan 01 00:00:00 1970 +0000
1973 | | summary: add another e
1973 | | summary: add another e
1974 | |
1974 | |
1975 | | diff --git a/e b/e
1975 | | diff --git a/e b/e
1976 | | new file mode 100644
1976 | | new file mode 100644
1977 | | --- /dev/null
1977 | | --- /dev/null
1978 | | +++ b/e
1978 | | +++ b/e
1979 | | @@ -0,0 +1,1 @@
1979 | | @@ -0,0 +1,1 @@
1980 | | +ee
1980 | | +ee
1981 | |
1981 | |
1982
1982
1983 Test old-style --rev
1983 Test old-style --rev
1984
1984
1985 $ hg tag 'foo-bar'
1985 $ hg tag 'foo-bar'
1986 $ testlog -r 'foo-bar'
1986 $ testlog -r 'foo-bar'
1987 ['foo-bar']
1987 ['foo-bar']
1988 []
1988 []
1989
1989
1990 Test --follow and forward --rev
1990 Test --follow and forward --rev
1991
1991
1992 $ hg up -q 6
1992 $ hg up -q 6
1993 $ echo g > g
1993 $ echo g > g
1994 $ hg ci -Am 'add g' g
1994 $ hg ci -Am 'add g' g
1995 created new head
1995 created new head
1996 $ hg up -q 2
1996 $ hg up -q 2
1997 $ hg log -G --template "{rev} {desc|firstline}\n"
1997 $ hg log -G --template "{rev} {desc|firstline}\n"
1998 o 8 add g
1998 o 8 add g
1999 |
1999 |
2000 | o 7 Added tag foo-bar for changeset fc281d8ff18d
2000 | o 7 Added tag foo-bar for changeset fc281d8ff18d
2001 |/
2001 |/
2002 o 6 merge 5 and 4
2002 o 6 merge 5 and 4
2003 |\
2003 |\
2004 | o 5 add another e
2004 | o 5 add another e
2005 | |
2005 | |
2006 o | 4 mv dir/b e
2006 o | 4 mv dir/b e
2007 |/
2007 |/
2008 o 3 mv a b; add d
2008 o 3 mv a b; add d
2009 |
2009 |
2010 @ 2 mv b dir/b
2010 @ 2 mv b dir/b
2011 |
2011 |
2012 o 1 copy a b
2012 o 1 copy a b
2013 |
2013 |
2014 o 0 add a
2014 o 0 add a
2015
2015
2016 $ hg export 'all()'
2016 $ hg export 'all()'
2017 # HG changeset patch
2017 # HG changeset patch
2018 # User test
2018 # User test
2019 # Date 0 0
2019 # Date 0 0
2020 # Thu Jan 01 00:00:00 1970 +0000
2020 # Thu Jan 01 00:00:00 1970 +0000
2021 # Node ID f8035bb17114da16215af3436ec5222428ace8ee
2021 # Node ID f8035bb17114da16215af3436ec5222428ace8ee
2022 # Parent 0000000000000000000000000000000000000000
2022 # Parent 0000000000000000000000000000000000000000
2023 add a
2023 add a
2024
2024
2025 diff -r 000000000000 -r f8035bb17114 a
2025 diff -r 000000000000 -r f8035bb17114 a
2026 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2026 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2027 +++ b/a Thu Jan 01 00:00:00 1970 +0000
2027 +++ b/a Thu Jan 01 00:00:00 1970 +0000
2028 @@ -0,0 +1,1 @@
2028 @@ -0,0 +1,1 @@
2029 +a
2029 +a
2030 diff -r 000000000000 -r f8035bb17114 aa
2030 diff -r 000000000000 -r f8035bb17114 aa
2031 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2031 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2032 +++ b/aa Thu Jan 01 00:00:00 1970 +0000
2032 +++ b/aa Thu Jan 01 00:00:00 1970 +0000
2033 @@ -0,0 +1,1 @@
2033 @@ -0,0 +1,1 @@
2034 +aa
2034 +aa
2035 diff -r 000000000000 -r f8035bb17114 f
2035 diff -r 000000000000 -r f8035bb17114 f
2036 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2036 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2037 +++ b/f Thu Jan 01 00:00:00 1970 +0000
2037 +++ b/f Thu Jan 01 00:00:00 1970 +0000
2038 @@ -0,0 +1,1 @@
2038 @@ -0,0 +1,1 @@
2039 +f
2039 +f
2040 # HG changeset patch
2040 # HG changeset patch
2041 # User test
2041 # User test
2042 # Date 0 0
2042 # Date 0 0
2043 # Thu Jan 01 00:00:00 1970 +0000
2043 # Thu Jan 01 00:00:00 1970 +0000
2044 # Node ID 216d4c92cf98ff2b4641d508b76b529f3d424c92
2044 # Node ID 216d4c92cf98ff2b4641d508b76b529f3d424c92
2045 # Parent f8035bb17114da16215af3436ec5222428ace8ee
2045 # Parent f8035bb17114da16215af3436ec5222428ace8ee
2046 copy a b
2046 copy a b
2047
2047
2048 diff -r f8035bb17114 -r 216d4c92cf98 b
2048 diff -r f8035bb17114 -r 216d4c92cf98 b
2049 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2049 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2050 +++ b/b Thu Jan 01 00:00:00 1970 +0000
2050 +++ b/b Thu Jan 01 00:00:00 1970 +0000
2051 @@ -0,0 +1,1 @@
2051 @@ -0,0 +1,1 @@
2052 +a
2052 +a
2053 diff -r f8035bb17114 -r 216d4c92cf98 g
2053 diff -r f8035bb17114 -r 216d4c92cf98 g
2054 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2054 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2055 +++ b/g Thu Jan 01 00:00:00 1970 +0000
2055 +++ b/g Thu Jan 01 00:00:00 1970 +0000
2056 @@ -0,0 +1,1 @@
2056 @@ -0,0 +1,1 @@
2057 +f
2057 +f
2058 # HG changeset patch
2058 # HG changeset patch
2059 # User test
2059 # User test
2060 # Date 0 0
2060 # Date 0 0
2061 # Thu Jan 01 00:00:00 1970 +0000
2061 # Thu Jan 01 00:00:00 1970 +0000
2062 # Node ID bb573313a9e8349099b6ea2b2fb1fc7f424446f3
2062 # Node ID bb573313a9e8349099b6ea2b2fb1fc7f424446f3
2063 # Parent 216d4c92cf98ff2b4641d508b76b529f3d424c92
2063 # Parent 216d4c92cf98ff2b4641d508b76b529f3d424c92
2064 mv b dir/b
2064 mv b dir/b
2065
2065
2066 diff -r 216d4c92cf98 -r bb573313a9e8 b
2066 diff -r 216d4c92cf98 -r bb573313a9e8 b
2067 --- a/b Thu Jan 01 00:00:00 1970 +0000
2067 --- a/b Thu Jan 01 00:00:00 1970 +0000
2068 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2068 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2069 @@ -1,1 +0,0 @@
2069 @@ -1,1 +0,0 @@
2070 -a
2070 -a
2071 diff -r 216d4c92cf98 -r bb573313a9e8 dir/b
2071 diff -r 216d4c92cf98 -r bb573313a9e8 dir/b
2072 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2072 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2073 +++ b/dir/b Thu Jan 01 00:00:00 1970 +0000
2073 +++ b/dir/b Thu Jan 01 00:00:00 1970 +0000
2074 @@ -0,0 +1,1 @@
2074 @@ -0,0 +1,1 @@
2075 +a
2075 +a
2076 diff -r 216d4c92cf98 -r bb573313a9e8 f
2076 diff -r 216d4c92cf98 -r bb573313a9e8 f
2077 --- a/f Thu Jan 01 00:00:00 1970 +0000
2077 --- a/f Thu Jan 01 00:00:00 1970 +0000
2078 +++ b/f Thu Jan 01 00:00:00 1970 +0000
2078 +++ b/f Thu Jan 01 00:00:00 1970 +0000
2079 @@ -1,1 +1,2 @@
2079 @@ -1,1 +1,2 @@
2080 f
2080 f
2081 +f
2081 +f
2082 diff -r 216d4c92cf98 -r bb573313a9e8 g
2082 diff -r 216d4c92cf98 -r bb573313a9e8 g
2083 --- a/g Thu Jan 01 00:00:00 1970 +0000
2083 --- a/g Thu Jan 01 00:00:00 1970 +0000
2084 +++ b/g Thu Jan 01 00:00:00 1970 +0000
2084 +++ b/g Thu Jan 01 00:00:00 1970 +0000
2085 @@ -1,1 +1,2 @@
2085 @@ -1,1 +1,2 @@
2086 f
2086 f
2087 +g
2087 +g
2088 # HG changeset patch
2088 # HG changeset patch
2089 # User test
2089 # User test
2090 # Date 0 0
2090 # Date 0 0
2091 # Thu Jan 01 00:00:00 1970 +0000
2091 # Thu Jan 01 00:00:00 1970 +0000
2092 # Node ID 5918b8d165d1364e78a66d02e66caa0133c5d1ed
2092 # Node ID 5918b8d165d1364e78a66d02e66caa0133c5d1ed
2093 # Parent bb573313a9e8349099b6ea2b2fb1fc7f424446f3
2093 # Parent bb573313a9e8349099b6ea2b2fb1fc7f424446f3
2094 mv a b; add d
2094 mv a b; add d
2095
2095
2096 diff -r bb573313a9e8 -r 5918b8d165d1 a
2096 diff -r bb573313a9e8 -r 5918b8d165d1 a
2097 --- a/a Thu Jan 01 00:00:00 1970 +0000
2097 --- a/a Thu Jan 01 00:00:00 1970 +0000
2098 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2098 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2099 @@ -1,1 +0,0 @@
2099 @@ -1,1 +0,0 @@
2100 -a
2100 -a
2101 diff -r bb573313a9e8 -r 5918b8d165d1 b
2101 diff -r bb573313a9e8 -r 5918b8d165d1 b
2102 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2102 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2103 +++ b/b Thu Jan 01 00:00:00 1970 +0000
2103 +++ b/b Thu Jan 01 00:00:00 1970 +0000
2104 @@ -0,0 +1,1 @@
2104 @@ -0,0 +1,1 @@
2105 +a
2105 +a
2106 diff -r bb573313a9e8 -r 5918b8d165d1 d
2106 diff -r bb573313a9e8 -r 5918b8d165d1 d
2107 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2107 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2108 +++ b/d Thu Jan 01 00:00:00 1970 +0000
2108 +++ b/d Thu Jan 01 00:00:00 1970 +0000
2109 @@ -0,0 +1,1 @@
2109 @@ -0,0 +1,1 @@
2110 +a
2110 +a
2111 diff -r bb573313a9e8 -r 5918b8d165d1 g
2111 diff -r bb573313a9e8 -r 5918b8d165d1 g
2112 --- a/g Thu Jan 01 00:00:00 1970 +0000
2112 --- a/g Thu Jan 01 00:00:00 1970 +0000
2113 +++ b/g Thu Jan 01 00:00:00 1970 +0000
2113 +++ b/g Thu Jan 01 00:00:00 1970 +0000
2114 @@ -1,2 +1,2 @@
2114 @@ -1,2 +1,2 @@
2115 f
2115 f
2116 -g
2116 -g
2117 +f
2117 +f
2118 # HG changeset patch
2118 # HG changeset patch
2119 # User test
2119 # User test
2120 # Date 0 0
2120 # Date 0 0
2121 # Thu Jan 01 00:00:00 1970 +0000
2121 # Thu Jan 01 00:00:00 1970 +0000
2122 # Node ID 17d952250a9d03cc3dc77b199ab60e959b9b0260
2122 # Node ID 17d952250a9d03cc3dc77b199ab60e959b9b0260
2123 # Parent 5918b8d165d1364e78a66d02e66caa0133c5d1ed
2123 # Parent 5918b8d165d1364e78a66d02e66caa0133c5d1ed
2124 mv dir/b e
2124 mv dir/b e
2125
2125
2126 diff -r 5918b8d165d1 -r 17d952250a9d dir/b
2126 diff -r 5918b8d165d1 -r 17d952250a9d dir/b
2127 --- a/dir/b Thu Jan 01 00:00:00 1970 +0000
2127 --- a/dir/b Thu Jan 01 00:00:00 1970 +0000
2128 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2128 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2129 @@ -1,1 +0,0 @@
2129 @@ -1,1 +0,0 @@
2130 -a
2130 -a
2131 diff -r 5918b8d165d1 -r 17d952250a9d e
2131 diff -r 5918b8d165d1 -r 17d952250a9d e
2132 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2132 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2133 +++ b/e Thu Jan 01 00:00:00 1970 +0000
2133 +++ b/e Thu Jan 01 00:00:00 1970 +0000
2134 @@ -0,0 +1,1 @@
2134 @@ -0,0 +1,1 @@
2135 +a
2135 +a
2136 # HG changeset patch
2136 # HG changeset patch
2137 # User test
2137 # User test
2138 # Date 0 0
2138 # Date 0 0
2139 # Thu Jan 01 00:00:00 1970 +0000
2139 # Thu Jan 01 00:00:00 1970 +0000
2140 # Node ID 99b31f1c2782e2deb1723cef08930f70fc84b37b
2140 # Node ID 99b31f1c2782e2deb1723cef08930f70fc84b37b
2141 # Parent 5918b8d165d1364e78a66d02e66caa0133c5d1ed
2141 # Parent 5918b8d165d1364e78a66d02e66caa0133c5d1ed
2142 add another e
2142 add another e
2143
2143
2144 diff -r 5918b8d165d1 -r 99b31f1c2782 e
2144 diff -r 5918b8d165d1 -r 99b31f1c2782 e
2145 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2145 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2146 +++ b/e Thu Jan 01 00:00:00 1970 +0000
2146 +++ b/e Thu Jan 01 00:00:00 1970 +0000
2147 @@ -0,0 +1,1 @@
2147 @@ -0,0 +1,1 @@
2148 +ee
2148 +ee
2149 # HG changeset patch
2149 # HG changeset patch
2150 # User test
2150 # User test
2151 # Date 0 0
2151 # Date 0 0
2152 # Thu Jan 01 00:00:00 1970 +0000
2152 # Thu Jan 01 00:00:00 1970 +0000
2153 # Node ID fc281d8ff18d999ad6497b3d27390bcd695dcc73
2153 # Node ID fc281d8ff18d999ad6497b3d27390bcd695dcc73
2154 # Parent 99b31f1c2782e2deb1723cef08930f70fc84b37b
2154 # Parent 99b31f1c2782e2deb1723cef08930f70fc84b37b
2155 # Parent 17d952250a9d03cc3dc77b199ab60e959b9b0260
2155 # Parent 17d952250a9d03cc3dc77b199ab60e959b9b0260
2156 merge 5 and 4
2156 merge 5 and 4
2157
2157
2158 diff -r 99b31f1c2782 -r fc281d8ff18d dir/b
2158 diff -r 99b31f1c2782 -r fc281d8ff18d dir/b
2159 --- a/dir/b Thu Jan 01 00:00:00 1970 +0000
2159 --- a/dir/b Thu Jan 01 00:00:00 1970 +0000
2160 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2160 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2161 @@ -1,1 +0,0 @@
2161 @@ -1,1 +0,0 @@
2162 -a
2162 -a
2163 diff -r 99b31f1c2782 -r fc281d8ff18d e
2163 diff -r 99b31f1c2782 -r fc281d8ff18d e
2164 --- a/e Thu Jan 01 00:00:00 1970 +0000
2164 --- a/e Thu Jan 01 00:00:00 1970 +0000
2165 +++ b/e Thu Jan 01 00:00:00 1970 +0000
2165 +++ b/e Thu Jan 01 00:00:00 1970 +0000
2166 @@ -1,1 +1,1 @@
2166 @@ -1,1 +1,1 @@
2167 -ee
2167 -ee
2168 +merge
2168 +merge
2169 # HG changeset patch
2169 # HG changeset patch
2170 # User test
2170 # User test
2171 # Date 0 0
2171 # Date 0 0
2172 # Thu Jan 01 00:00:00 1970 +0000
2172 # Thu Jan 01 00:00:00 1970 +0000
2173 # Node ID 02dbb8e276b8ab7abfd07cab50c901647e75c2dd
2173 # Node ID 02dbb8e276b8ab7abfd07cab50c901647e75c2dd
2174 # Parent fc281d8ff18d999ad6497b3d27390bcd695dcc73
2174 # Parent fc281d8ff18d999ad6497b3d27390bcd695dcc73
2175 Added tag foo-bar for changeset fc281d8ff18d
2175 Added tag foo-bar for changeset fc281d8ff18d
2176
2176
2177 diff -r fc281d8ff18d -r 02dbb8e276b8 .hgtags
2177 diff -r fc281d8ff18d -r 02dbb8e276b8 .hgtags
2178 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2178 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2179 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
2179 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
2180 @@ -0,0 +1,1 @@
2180 @@ -0,0 +1,1 @@
2181 +fc281d8ff18d999ad6497b3d27390bcd695dcc73 foo-bar
2181 +fc281d8ff18d999ad6497b3d27390bcd695dcc73 foo-bar
2182 # HG changeset patch
2182 # HG changeset patch
2183 # User test
2183 # User test
2184 # Date 0 0
2184 # Date 0 0
2185 # Thu Jan 01 00:00:00 1970 +0000
2185 # Thu Jan 01 00:00:00 1970 +0000
2186 # Node ID 24c2e826ddebf80f9dcd60b856bdb8e6715c5449
2186 # Node ID 24c2e826ddebf80f9dcd60b856bdb8e6715c5449
2187 # Parent fc281d8ff18d999ad6497b3d27390bcd695dcc73
2187 # Parent fc281d8ff18d999ad6497b3d27390bcd695dcc73
2188 add g
2188 add g
2189
2189
2190 diff -r fc281d8ff18d -r 24c2e826ddeb g
2190 diff -r fc281d8ff18d -r 24c2e826ddeb g
2191 --- a/g Thu Jan 01 00:00:00 1970 +0000
2191 --- a/g Thu Jan 01 00:00:00 1970 +0000
2192 +++ b/g Thu Jan 01 00:00:00 1970 +0000
2192 +++ b/g Thu Jan 01 00:00:00 1970 +0000
2193 @@ -1,2 +1,1 @@
2193 @@ -1,2 +1,1 @@
2194 -f
2194 -f
2195 -f
2195 -f
2196 +g
2196 +g
2197 $ testlog --follow -r6 -r8 -r5 -r7 -r4
2197 $ testlog --follow -r6 -r8 -r5 -r7 -r4
2198 ['6', '8', '5', '7', '4']
2198 ['6', '8', '5', '7', '4']
2199 (group
2199 (group
2200 (func
2200 (func
2201 ('symbol', 'descendants')
2201 ('symbol', 'descendants')
2202 (func
2202 (func
2203 ('symbol', 'rev')
2203 ('symbol', 'rev')
2204 ('symbol', '6'))))
2204 ('symbol', '6'))))
2205
2205
2206 Test --follow-first and forward --rev
2206 Test --follow-first and forward --rev
2207
2207
2208 $ testlog --follow-first -r6 -r8 -r5 -r7 -r4
2208 $ testlog --follow-first -r6 -r8 -r5 -r7 -r4
2209 ['6', '8', '5', '7', '4']
2209 ['6', '8', '5', '7', '4']
2210 (group
2210 (group
2211 (func
2211 (func
2212 ('symbol', '_firstdescendants')
2212 ('symbol', '_firstdescendants')
2213 (func
2213 (func
2214 ('symbol', 'rev')
2214 ('symbol', 'rev')
2215 ('symbol', '6'))))
2215 ('symbol', '6'))))
2216 --- log.nodes * (glob)
2216 --- log.nodes * (glob)
2217 +++ glog.nodes * (glob)
2217 +++ glog.nodes * (glob)
2218 @@ -1,3 +1,3 @@
2218 @@ -1,3 +1,3 @@
2219 -nodetag 6
2219 -nodetag 6
2220 nodetag 8
2220 nodetag 8
2221 nodetag 7
2221 nodetag 7
2222 +nodetag 6
2222 +nodetag 6
2223
2223
2224 Test --follow and backward --rev
2224 Test --follow and backward --rev
2225
2225
2226 $ testlog --follow -r6 -r5 -r7 -r8 -r4
2226 $ testlog --follow -r6 -r5 -r7 -r8 -r4
2227 ['6', '5', '7', '8', '4']
2227 ['6', '5', '7', '8', '4']
2228 (group
2228 (group
2229 (func
2229 (func
2230 ('symbol', 'ancestors')
2230 ('symbol', 'ancestors')
2231 (func
2231 (func
2232 ('symbol', 'rev')
2232 ('symbol', 'rev')
2233 ('symbol', '6'))))
2233 ('symbol', '6'))))
2234
2234
2235 Test --follow-first and backward --rev
2235 Test --follow-first and backward --rev
2236
2236
2237 $ testlog --follow-first -r6 -r5 -r7 -r8 -r4
2237 $ testlog --follow-first -r6 -r5 -r7 -r8 -r4
2238 ['6', '5', '7', '8', '4']
2238 ['6', '5', '7', '8', '4']
2239 (group
2239 (group
2240 (func
2240 (func
2241 ('symbol', '_firstancestors')
2241 ('symbol', '_firstancestors')
2242 (func
2242 (func
2243 ('symbol', 'rev')
2243 ('symbol', 'rev')
2244 ('symbol', '6'))))
2244 ('symbol', '6'))))
2245
2245
2246 Test --follow with --rev of graphlog extension
2246 Test --follow with --rev of graphlog extension
2247
2247
2248 $ hg --config extensions.graphlog= glog -qfr1
2248 $ hg --config extensions.graphlog= glog -qfr1
2249 o 1:216d4c92cf98
2249 o 1:216d4c92cf98
2250 |
2250 |
2251 o 0:f8035bb17114
2251 o 0:f8035bb17114
2252
2252
2253
2253
2254 Test subdir
2254 Test subdir
2255
2255
2256 $ hg up -q 3
2256 $ hg up -q 3
2257 $ cd dir
2257 $ cd dir
2258 $ testlog .
2258 $ testlog .
2259 []
2259 []
2260 (group
2260 (group
2261 (func
2261 (func
2262 ('symbol', '_matchfiles')
2262 ('symbol', '_matchfiles')
2263 (list
2263 (list
2264 (list
2264 (list
2265 ('string', 'r:')
2265 ('string', 'r:')
2266 ('string', 'd:relpath'))
2266 ('string', 'd:relpath'))
2267 ('string', 'p:.'))))
2267 ('string', 'p:.'))))
2268 $ testlog ../b
2268 $ testlog ../b
2269 []
2269 []
2270 (group
2270 (group
2271 (group
2271 (group
2272 (func
2272 (func
2273 ('symbol', 'filelog')
2273 ('symbol', 'filelog')
2274 ('string', '../b'))))
2274 ('string', '../b'))))
2275 $ testlog -f ../b
2275 $ testlog -f ../b
2276 []
2276 []
2277 (group
2277 (group
2278 (group
2278 (group
2279 (func
2279 (func
2280 ('symbol', 'follow')
2280 ('symbol', 'follow')
2281 ('string', 'b'))))
2281 ('string', 'b'))))
2282 $ cd ..
2282 $ cd ..
2283
2283
2284 Test --hidden
2284 Test --hidden
2285 (enable obsolete)
2285 (enable obsolete)
2286
2286
2287 $ cat >> $HGRCPATH << EOF
2287 $ cat >> $HGRCPATH << EOF
2288 > [experimental]
2288 > [experimental]
2289 > evolution=createmarkers
2289 > evolution=createmarkers
2290 > EOF
2290 > EOF
2291
2291
2292 $ hg debugobsolete `hg id --debug -i -r 8`
2292 $ hg debugobsolete `hg id --debug -i -r 8`
2293 $ testlog
2293 $ testlog
2294 []
2294 []
2295 []
2295 []
2296 $ testlog --hidden
2296 $ testlog --hidden
2297 []
2297 []
2298 []
2298 []
2299 $ hg log -G --template '{rev} {desc}\n'
2299 $ hg log -G --template '{rev} {desc}\n'
2300 o 7 Added tag foo-bar for changeset fc281d8ff18d
2300 o 7 Added tag foo-bar for changeset fc281d8ff18d
2301 |
2301 |
2302 o 6 merge 5 and 4
2302 o 6 merge 5 and 4
2303 |\
2303 |\
2304 | o 5 add another e
2304 | o 5 add another e
2305 | |
2305 | |
2306 o | 4 mv dir/b e
2306 o | 4 mv dir/b e
2307 |/
2307 |/
2308 @ 3 mv a b; add d
2308 @ 3 mv a b; add d
2309 |
2309 |
2310 o 2 mv b dir/b
2310 o 2 mv b dir/b
2311 |
2311 |
2312 o 1 copy a b
2312 o 1 copy a b
2313 |
2313 |
2314 o 0 add a
2314 o 0 add a
2315
2315
2316
2316
2317 A template without trailing newline should do something sane
2317 A template without trailing newline should do something sane
2318
2318
2319 $ hg log -G -r ::2 --template '{rev} {desc}'
2319 $ hg log -G -r ::2 --template '{rev} {desc}'
2320 o 2 mv b dir/b
2320 o 2 mv b dir/b
2321 |
2321 |
2322 o 1 copy a b
2322 o 1 copy a b
2323 |
2323 |
2324 o 0 add a
2324 o 0 add a
2325
2325
2326
2326
2327 Extra newlines must be preserved
2327 Extra newlines must be preserved
2328
2328
2329 $ hg log -G -r ::2 --template '\n{rev} {desc}\n\n'
2329 $ hg log -G -r ::2 --template '\n{rev} {desc}\n\n'
2330 o
2330 o
2331 | 2 mv b dir/b
2331 | 2 mv b dir/b
2332 |
2332 |
2333 o
2333 o
2334 | 1 copy a b
2334 | 1 copy a b
2335 |
2335 |
2336 o
2336 o
2337 0 add a
2337 0 add a
2338
2338
2339
2339
2340 The almost-empty template should do something sane too ...
2340 The almost-empty template should do something sane too ...
2341
2341
2342 $ hg log -G -r ::2 --template '\n'
2342 $ hg log -G -r ::2 --template '\n'
2343 o
2343 o
2344 |
2344 |
2345 o
2345 o
2346 |
2346 |
2347 o
2347 o
2348
2348
2349
2349
2350 issue3772
2350 issue3772
2351
2351
2352 $ hg log -G -r :null
2352 $ hg log -G -r :null
2353 o changeset: 0:f8035bb17114
2353 o changeset: 0:f8035bb17114
2354 | user: test
2354 | user: test
2355 | date: Thu Jan 01 00:00:00 1970 +0000
2355 | date: Thu Jan 01 00:00:00 1970 +0000
2356 | summary: add a
2356 | summary: add a
2357 |
2357 |
2358 o changeset: -1:000000000000
2358 o changeset: -1:000000000000
2359 user:
2359 user:
2360 date: Thu Jan 01 00:00:00 1970 +0000
2360 date: Thu Jan 01 00:00:00 1970 +0000
2361
2361
2362 $ hg log -G -r null:null
2362 $ hg log -G -r null:null
2363 o changeset: -1:000000000000
2363 o changeset: -1:000000000000
2364 user:
2364 user:
2365 date: Thu Jan 01 00:00:00 1970 +0000
2365 date: Thu Jan 01 00:00:00 1970 +0000
2366
2366
2367
2367
2368 should not draw line down to null due to the magic of fullreposet
2368 should not draw line down to null due to the magic of fullreposet
2369
2369
2370 $ hg log -G -r 'all()' | tail -6
2370 $ hg log -G -r 'all()' | tail -6
2371 |
2371 |
2372 o changeset: 0:f8035bb17114
2372 o changeset: 0:f8035bb17114
2373 user: test
2373 user: test
2374 date: Thu Jan 01 00:00:00 1970 +0000
2374 date: Thu Jan 01 00:00:00 1970 +0000
2375 summary: add a
2375 summary: add a
2376
2376
2377
2377
2378 $ cd ..
2378 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now