##// END OF EJS Templates
wireproto: add known([id]) function...
Peter Arrenbrecht -
r13723:e615765f default
parent child Browse files
Show More
@@ -0,0 +1,37 b''
1
2 = Test the known() protocol function =
3
4 Create a test repository:
5
6 $ hg init repo
7 $ cd repo
8 $ touch a ; hg add a ; hg ci -ma
9 $ touch b ; hg add b ; hg ci -mb
10 $ touch c ; hg add c ; hg ci -mc
11 $ hg log --template '{node}\n'
12 991a3460af53952d10ec8a295d3d2cc2e5fa9690
13 0e067c57feba1a5694ca4844f05588bb1bf82342
14 3903775176ed42b1458a6281db4a0ccf4d9f287a
15 $ cd ..
16
17 Test locally:
18
19 $ hg debugknown repo 991a3460af53952d10ec8a295d3d2cc2e5fa9690 0e067c57feba1a5694ca4844f05588bb1bf82342 3903775176ed42b1458a6281db4a0ccf4d9f287a
20 111
21 $ hg debugknown repo 000a3460af53952d10ec8a295d3d2cc2e5fa9690 0e067c57feba1a5694ca4844f05588bb1bf82342 0003775176ed42b1458a6281db4a0ccf4d9f287a
22 010
23 $ hg debugknown repo
24
25
26 Test via HTTP:
27
28 $ hg serve -R repo -p $HGPORT -d --pid-file=hg.pid -E error.log -A access.log
29 $ cat hg.pid >> $DAEMON_PIDS
30 $ hg debugknown http://localhost:$HGPORT/ 991a3460af53952d10ec8a295d3d2cc2e5fa9690 0e067c57feba1a5694ca4844f05588bb1bf82342 3903775176ed42b1458a6281db4a0ccf4d9f287a
31 111
32 $ hg debugknown http://localhost:$HGPORT/ 000a3460af53952d10ec8a295d3d2cc2e5fa9690 0e067c57feba1a5694ca4844f05588bb1bf82342 0003775176ed42b1458a6281db4a0ccf4d9f287a
33 010
34 $ hg debugknown http://localhost:$HGPORT/
35
36 $ cat error.log
37
@@ -1,4815 +1,4829 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, 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 _, gettext
10 from i18n import _, gettext
11 import os, re, sys, difflib, time, tempfile
11 import os, re, sys, difflib, time, tempfile
12 import hg, util, revlog, extensions, copies, error, bookmarks
12 import hg, util, revlog, extensions, copies, error, bookmarks
13 import patch, help, mdiff, url, encoding, templatekw, discovery
13 import patch, help, mdiff, url, encoding, templatekw, discovery
14 import archival, changegroup, cmdutil, sshserver, hbisect, hgweb, hgweb.server
14 import archival, changegroup, cmdutil, sshserver, hbisect, hgweb, hgweb.server
15 import merge as mergemod
15 import merge as mergemod
16 import minirst, revset, templatefilters
16 import minirst, revset, templatefilters
17 import dagparser
17 import dagparser
18
18
19 # Commands start here, listed alphabetically
19 # Commands start here, listed alphabetically
20
20
21 def add(ui, repo, *pats, **opts):
21 def add(ui, repo, *pats, **opts):
22 """add the specified files on the next commit
22 """add the specified files on the next commit
23
23
24 Schedule files to be version controlled and added to the
24 Schedule files to be version controlled and added to the
25 repository.
25 repository.
26
26
27 The files will be added to the repository at the next commit. To
27 The files will be added to the repository at the next commit. To
28 undo an add before that, see :hg:`forget`.
28 undo an add before that, see :hg:`forget`.
29
29
30 If no names are given, add all files to the repository.
30 If no names are given, add all files to the repository.
31
31
32 .. container:: verbose
32 .. container:: verbose
33
33
34 An example showing how new (unknown) files are added
34 An example showing how new (unknown) files are added
35 automatically by :hg:`add`::
35 automatically by :hg:`add`::
36
36
37 $ ls
37 $ ls
38 foo.c
38 foo.c
39 $ hg status
39 $ hg status
40 ? foo.c
40 ? foo.c
41 $ hg add
41 $ hg add
42 adding foo.c
42 adding foo.c
43 $ hg status
43 $ hg status
44 A foo.c
44 A foo.c
45
45
46 Returns 0 if all files are successfully added.
46 Returns 0 if all files are successfully added.
47 """
47 """
48
48
49 m = cmdutil.match(repo, pats, opts)
49 m = cmdutil.match(repo, pats, opts)
50 rejected = cmdutil.add(ui, repo, m, opts.get('dry_run'),
50 rejected = cmdutil.add(ui, repo, m, opts.get('dry_run'),
51 opts.get('subrepos'), prefix="")
51 opts.get('subrepos'), prefix="")
52 return rejected and 1 or 0
52 return rejected and 1 or 0
53
53
54 def addremove(ui, repo, *pats, **opts):
54 def addremove(ui, repo, *pats, **opts):
55 """add all new files, delete all missing files
55 """add all new files, delete all missing files
56
56
57 Add all new files and remove all missing files from the
57 Add all new files and remove all missing files from the
58 repository.
58 repository.
59
59
60 New files are ignored if they match any of the patterns in
60 New files are ignored if they match any of the patterns in
61 ``.hgignore``. As with add, these changes take effect at the next
61 ``.hgignore``. As with add, these changes take effect at the next
62 commit.
62 commit.
63
63
64 Use the -s/--similarity option to detect renamed files. With a
64 Use the -s/--similarity option to detect renamed files. With a
65 parameter greater than 0, this compares every removed file with
65 parameter greater than 0, this compares every removed file with
66 every added file and records those similar enough as renames. This
66 every added file and records those similar enough as renames. This
67 option takes a percentage between 0 (disabled) and 100 (files must
67 option takes a percentage between 0 (disabled) and 100 (files must
68 be identical) as its parameter. Detecting renamed files this way
68 be identical) as its parameter. Detecting renamed files this way
69 can be expensive. After using this option, :hg:`status -C` can be
69 can be expensive. After using this option, :hg:`status -C` can be
70 used to check which files were identified as moved or renamed.
70 used to check which files were identified as moved or renamed.
71
71
72 Returns 0 if all files are successfully added.
72 Returns 0 if all files are successfully added.
73 """
73 """
74 try:
74 try:
75 sim = float(opts.get('similarity') or 100)
75 sim = float(opts.get('similarity') or 100)
76 except ValueError:
76 except ValueError:
77 raise util.Abort(_('similarity must be a number'))
77 raise util.Abort(_('similarity must be a number'))
78 if sim < 0 or sim > 100:
78 if sim < 0 or sim > 100:
79 raise util.Abort(_('similarity must be between 0 and 100'))
79 raise util.Abort(_('similarity must be between 0 and 100'))
80 return cmdutil.addremove(repo, pats, opts, similarity=sim / 100.0)
80 return cmdutil.addremove(repo, pats, opts, similarity=sim / 100.0)
81
81
82 def annotate(ui, repo, *pats, **opts):
82 def annotate(ui, repo, *pats, **opts):
83 """show changeset information by line for each file
83 """show changeset information by line for each file
84
84
85 List changes in files, showing the revision id responsible for
85 List changes in files, showing the revision id responsible for
86 each line
86 each line
87
87
88 This command is useful for discovering when a change was made and
88 This command is useful for discovering when a change was made and
89 by whom.
89 by whom.
90
90
91 Without the -a/--text option, annotate will avoid processing files
91 Without the -a/--text option, annotate will avoid processing files
92 it detects as binary. With -a, annotate will annotate the file
92 it detects as binary. With -a, annotate will annotate the file
93 anyway, although the results will probably be neither useful
93 anyway, although the results will probably be neither useful
94 nor desirable.
94 nor desirable.
95
95
96 Returns 0 on success.
96 Returns 0 on success.
97 """
97 """
98 if opts.get('follow'):
98 if opts.get('follow'):
99 # --follow is deprecated and now just an alias for -f/--file
99 # --follow is deprecated and now just an alias for -f/--file
100 # to mimic the behavior of Mercurial before version 1.5
100 # to mimic the behavior of Mercurial before version 1.5
101 opts['file'] = 1
101 opts['file'] = 1
102
102
103 datefunc = ui.quiet and util.shortdate or util.datestr
103 datefunc = ui.quiet and util.shortdate or util.datestr
104 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
104 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
105
105
106 if not pats:
106 if not pats:
107 raise util.Abort(_('at least one filename or pattern is required'))
107 raise util.Abort(_('at least one filename or pattern is required'))
108
108
109 opmap = [('user', lambda x: ui.shortuser(x[0].user())),
109 opmap = [('user', lambda x: ui.shortuser(x[0].user())),
110 ('number', lambda x: str(x[0].rev())),
110 ('number', lambda x: str(x[0].rev())),
111 ('changeset', lambda x: short(x[0].node())),
111 ('changeset', lambda x: short(x[0].node())),
112 ('date', getdate),
112 ('date', getdate),
113 ('file', lambda x: x[0].path()),
113 ('file', lambda x: x[0].path()),
114 ]
114 ]
115
115
116 if (not opts.get('user') and not opts.get('changeset')
116 if (not opts.get('user') and not opts.get('changeset')
117 and not opts.get('date') and not opts.get('file')):
117 and not opts.get('date') and not opts.get('file')):
118 opts['number'] = 1
118 opts['number'] = 1
119
119
120 linenumber = opts.get('line_number') is not None
120 linenumber = opts.get('line_number') is not None
121 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
121 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
122 raise util.Abort(_('at least one of -n/-c is required for -l'))
122 raise util.Abort(_('at least one of -n/-c is required for -l'))
123
123
124 funcmap = [func for op, func in opmap if opts.get(op)]
124 funcmap = [func for op, func in opmap if opts.get(op)]
125 if linenumber:
125 if linenumber:
126 lastfunc = funcmap[-1]
126 lastfunc = funcmap[-1]
127 funcmap[-1] = lambda x: "%s:%s" % (lastfunc(x), x[1])
127 funcmap[-1] = lambda x: "%s:%s" % (lastfunc(x), x[1])
128
128
129 def bad(x, y):
129 def bad(x, y):
130 raise util.Abort("%s: %s" % (x, y))
130 raise util.Abort("%s: %s" % (x, y))
131
131
132 ctx = cmdutil.revsingle(repo, opts.get('rev'))
132 ctx = cmdutil.revsingle(repo, opts.get('rev'))
133 m = cmdutil.match(repo, pats, opts)
133 m = cmdutil.match(repo, pats, opts)
134 m.bad = bad
134 m.bad = bad
135 follow = not opts.get('no_follow')
135 follow = not opts.get('no_follow')
136 for abs in ctx.walk(m):
136 for abs in ctx.walk(m):
137 fctx = ctx[abs]
137 fctx = ctx[abs]
138 if not opts.get('text') and util.binary(fctx.data()):
138 if not opts.get('text') and util.binary(fctx.data()):
139 ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
139 ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
140 continue
140 continue
141
141
142 lines = fctx.annotate(follow=follow, linenumber=linenumber)
142 lines = fctx.annotate(follow=follow, linenumber=linenumber)
143 pieces = []
143 pieces = []
144
144
145 for f in funcmap:
145 for f in funcmap:
146 l = [f(n) for n, dummy in lines]
146 l = [f(n) for n, dummy in lines]
147 if l:
147 if l:
148 sized = [(x, encoding.colwidth(x)) for x in l]
148 sized = [(x, encoding.colwidth(x)) for x in l]
149 ml = max([w for x, w in sized])
149 ml = max([w for x, w in sized])
150 pieces.append(["%s%s" % (' ' * (ml - w), x) for x, w in sized])
150 pieces.append(["%s%s" % (' ' * (ml - w), x) for x, w in sized])
151
151
152 if pieces:
152 if pieces:
153 for p, l in zip(zip(*pieces), lines):
153 for p, l in zip(zip(*pieces), lines):
154 ui.write("%s: %s" % (" ".join(p), l[1]))
154 ui.write("%s: %s" % (" ".join(p), l[1]))
155
155
156 def archive(ui, repo, dest, **opts):
156 def archive(ui, repo, dest, **opts):
157 '''create an unversioned archive of a repository revision
157 '''create an unversioned archive of a repository revision
158
158
159 By default, the revision used is the parent of the working
159 By default, the revision used is the parent of the working
160 directory; use -r/--rev to specify a different revision.
160 directory; use -r/--rev to specify a different revision.
161
161
162 The archive type is automatically detected based on file
162 The archive type is automatically detected based on file
163 extension (or override using -t/--type).
163 extension (or override using -t/--type).
164
164
165 Valid types are:
165 Valid types are:
166
166
167 :``files``: a directory full of files (default)
167 :``files``: a directory full of files (default)
168 :``tar``: tar archive, uncompressed
168 :``tar``: tar archive, uncompressed
169 :``tbz2``: tar archive, compressed using bzip2
169 :``tbz2``: tar archive, compressed using bzip2
170 :``tgz``: tar archive, compressed using gzip
170 :``tgz``: tar archive, compressed using gzip
171 :``uzip``: zip archive, uncompressed
171 :``uzip``: zip archive, uncompressed
172 :``zip``: zip archive, compressed using deflate
172 :``zip``: zip archive, compressed using deflate
173
173
174 The exact name of the destination archive or directory is given
174 The exact name of the destination archive or directory is given
175 using a format string; see :hg:`help export` for details.
175 using a format string; see :hg:`help export` for details.
176
176
177 Each member added to an archive file has a directory prefix
177 Each member added to an archive file has a directory prefix
178 prepended. Use -p/--prefix to specify a format string for the
178 prepended. Use -p/--prefix to specify a format string for the
179 prefix. The default is the basename of the archive, with suffixes
179 prefix. The default is the basename of the archive, with suffixes
180 removed.
180 removed.
181
181
182 Returns 0 on success.
182 Returns 0 on success.
183 '''
183 '''
184
184
185 ctx = cmdutil.revsingle(repo, opts.get('rev'))
185 ctx = cmdutil.revsingle(repo, opts.get('rev'))
186 if not ctx:
186 if not ctx:
187 raise util.Abort(_('no working directory: please specify a revision'))
187 raise util.Abort(_('no working directory: please specify a revision'))
188 node = ctx.node()
188 node = ctx.node()
189 dest = cmdutil.make_filename(repo, dest, node)
189 dest = cmdutil.make_filename(repo, dest, node)
190 if os.path.realpath(dest) == repo.root:
190 if os.path.realpath(dest) == repo.root:
191 raise util.Abort(_('repository root cannot be destination'))
191 raise util.Abort(_('repository root cannot be destination'))
192
192
193 kind = opts.get('type') or archival.guesskind(dest) or 'files'
193 kind = opts.get('type') or archival.guesskind(dest) or 'files'
194 prefix = opts.get('prefix')
194 prefix = opts.get('prefix')
195
195
196 if dest == '-':
196 if dest == '-':
197 if kind == 'files':
197 if kind == 'files':
198 raise util.Abort(_('cannot archive plain files to stdout'))
198 raise util.Abort(_('cannot archive plain files to stdout'))
199 dest = sys.stdout
199 dest = sys.stdout
200 if not prefix:
200 if not prefix:
201 prefix = os.path.basename(repo.root) + '-%h'
201 prefix = os.path.basename(repo.root) + '-%h'
202
202
203 prefix = cmdutil.make_filename(repo, prefix, node)
203 prefix = cmdutil.make_filename(repo, prefix, node)
204 matchfn = cmdutil.match(repo, [], opts)
204 matchfn = cmdutil.match(repo, [], opts)
205 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
205 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
206 matchfn, prefix, subrepos=opts.get('subrepos'))
206 matchfn, prefix, subrepos=opts.get('subrepos'))
207
207
208 def backout(ui, repo, node=None, rev=None, **opts):
208 def backout(ui, repo, node=None, rev=None, **opts):
209 '''reverse effect of earlier changeset
209 '''reverse effect of earlier changeset
210
210
211 Prepare a new changeset with the effect of REV undone in the
211 Prepare a new changeset with the effect of REV undone in the
212 current working directory.
212 current working directory.
213
213
214 If REV is the parent of the working directory, then this new changeset
214 If REV is the parent of the working directory, then this new changeset
215 is committed automatically. Otherwise, hg needs to merge the
215 is committed automatically. Otherwise, hg needs to merge the
216 changes and the merged result is left uncommitted.
216 changes and the merged result is left uncommitted.
217
217
218 By default, the pending changeset will have one parent,
218 By default, the pending changeset will have one parent,
219 maintaining a linear history. With --merge, the pending changeset
219 maintaining a linear history. With --merge, the pending changeset
220 will instead have two parents: the old parent of the working
220 will instead have two parents: the old parent of the working
221 directory and a new child of REV that simply undoes REV.
221 directory and a new child of REV that simply undoes REV.
222
222
223 Before version 1.7, the behavior without --merge was equivalent to
223 Before version 1.7, the behavior without --merge was equivalent to
224 specifying --merge followed by :hg:`update --clean .` to cancel
224 specifying --merge followed by :hg:`update --clean .` to cancel
225 the merge and leave the child of REV as a head to be merged
225 the merge and leave the child of REV as a head to be merged
226 separately.
226 separately.
227
227
228 See :hg:`help dates` for a list of formats valid for -d/--date.
228 See :hg:`help dates` for a list of formats valid for -d/--date.
229
229
230 Returns 0 on success.
230 Returns 0 on success.
231 '''
231 '''
232 if rev and node:
232 if rev and node:
233 raise util.Abort(_("please specify just one revision"))
233 raise util.Abort(_("please specify just one revision"))
234
234
235 if not rev:
235 if not rev:
236 rev = node
236 rev = node
237
237
238 if not rev:
238 if not rev:
239 raise util.Abort(_("please specify a revision to backout"))
239 raise util.Abort(_("please specify a revision to backout"))
240
240
241 date = opts.get('date')
241 date = opts.get('date')
242 if date:
242 if date:
243 opts['date'] = util.parsedate(date)
243 opts['date'] = util.parsedate(date)
244
244
245 cmdutil.bail_if_changed(repo)
245 cmdutil.bail_if_changed(repo)
246 node = cmdutil.revsingle(repo, rev).node()
246 node = cmdutil.revsingle(repo, rev).node()
247
247
248 op1, op2 = repo.dirstate.parents()
248 op1, op2 = repo.dirstate.parents()
249 a = repo.changelog.ancestor(op1, node)
249 a = repo.changelog.ancestor(op1, node)
250 if a != node:
250 if a != node:
251 raise util.Abort(_('cannot backout change on a different branch'))
251 raise util.Abort(_('cannot backout change on a different branch'))
252
252
253 p1, p2 = repo.changelog.parents(node)
253 p1, p2 = repo.changelog.parents(node)
254 if p1 == nullid:
254 if p1 == nullid:
255 raise util.Abort(_('cannot backout a change with no parents'))
255 raise util.Abort(_('cannot backout a change with no parents'))
256 if p2 != nullid:
256 if p2 != nullid:
257 if not opts.get('parent'):
257 if not opts.get('parent'):
258 raise util.Abort(_('cannot backout a merge changeset without '
258 raise util.Abort(_('cannot backout a merge changeset without '
259 '--parent'))
259 '--parent'))
260 p = repo.lookup(opts['parent'])
260 p = repo.lookup(opts['parent'])
261 if p not in (p1, p2):
261 if p not in (p1, p2):
262 raise util.Abort(_('%s is not a parent of %s') %
262 raise util.Abort(_('%s is not a parent of %s') %
263 (short(p), short(node)))
263 (short(p), short(node)))
264 parent = p
264 parent = p
265 else:
265 else:
266 if opts.get('parent'):
266 if opts.get('parent'):
267 raise util.Abort(_('cannot use --parent on non-merge changeset'))
267 raise util.Abort(_('cannot use --parent on non-merge changeset'))
268 parent = p1
268 parent = p1
269
269
270 # the backout should appear on the same branch
270 # the backout should appear on the same branch
271 branch = repo.dirstate.branch()
271 branch = repo.dirstate.branch()
272 hg.clean(repo, node, show_stats=False)
272 hg.clean(repo, node, show_stats=False)
273 repo.dirstate.setbranch(branch)
273 repo.dirstate.setbranch(branch)
274 revert_opts = opts.copy()
274 revert_opts = opts.copy()
275 revert_opts['date'] = None
275 revert_opts['date'] = None
276 revert_opts['all'] = True
276 revert_opts['all'] = True
277 revert_opts['rev'] = hex(parent)
277 revert_opts['rev'] = hex(parent)
278 revert_opts['no_backup'] = None
278 revert_opts['no_backup'] = None
279 revert(ui, repo, **revert_opts)
279 revert(ui, repo, **revert_opts)
280 if not opts.get('merge') and op1 != node:
280 if not opts.get('merge') and op1 != node:
281 try:
281 try:
282 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
282 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
283 return hg.update(repo, op1)
283 return hg.update(repo, op1)
284 finally:
284 finally:
285 ui.setconfig('ui', 'forcemerge', '')
285 ui.setconfig('ui', 'forcemerge', '')
286
286
287 commit_opts = opts.copy()
287 commit_opts = opts.copy()
288 commit_opts['addremove'] = False
288 commit_opts['addremove'] = False
289 if not commit_opts['message'] and not commit_opts['logfile']:
289 if not commit_opts['message'] and not commit_opts['logfile']:
290 # we don't translate commit messages
290 # we don't translate commit messages
291 commit_opts['message'] = "Backed out changeset %s" % short(node)
291 commit_opts['message'] = "Backed out changeset %s" % short(node)
292 commit_opts['force_editor'] = True
292 commit_opts['force_editor'] = True
293 commit(ui, repo, **commit_opts)
293 commit(ui, repo, **commit_opts)
294 def nice(node):
294 def nice(node):
295 return '%d:%s' % (repo.changelog.rev(node), short(node))
295 return '%d:%s' % (repo.changelog.rev(node), short(node))
296 ui.status(_('changeset %s backs out changeset %s\n') %
296 ui.status(_('changeset %s backs out changeset %s\n') %
297 (nice(repo.changelog.tip()), nice(node)))
297 (nice(repo.changelog.tip()), nice(node)))
298 if opts.get('merge') and op1 != node:
298 if opts.get('merge') and op1 != node:
299 hg.clean(repo, op1, show_stats=False)
299 hg.clean(repo, op1, show_stats=False)
300 ui.status(_('merging with changeset %s\n')
300 ui.status(_('merging with changeset %s\n')
301 % nice(repo.changelog.tip()))
301 % nice(repo.changelog.tip()))
302 try:
302 try:
303 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
303 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
304 return hg.merge(repo, hex(repo.changelog.tip()))
304 return hg.merge(repo, hex(repo.changelog.tip()))
305 finally:
305 finally:
306 ui.setconfig('ui', 'forcemerge', '')
306 ui.setconfig('ui', 'forcemerge', '')
307 return 0
307 return 0
308
308
309 def bisect(ui, repo, rev=None, extra=None, command=None,
309 def bisect(ui, repo, rev=None, extra=None, command=None,
310 reset=None, good=None, bad=None, skip=None, extend=None,
310 reset=None, good=None, bad=None, skip=None, extend=None,
311 noupdate=None):
311 noupdate=None):
312 """subdivision search of changesets
312 """subdivision search of changesets
313
313
314 This command helps to find changesets which introduce problems. To
314 This command helps to find changesets which introduce problems. To
315 use, mark the earliest changeset you know exhibits the problem as
315 use, mark the earliest changeset you know exhibits the problem as
316 bad, then mark the latest changeset which is free from the problem
316 bad, then mark the latest changeset which is free from the problem
317 as good. Bisect will update your working directory to a revision
317 as good. Bisect will update your working directory to a revision
318 for testing (unless the -U/--noupdate option is specified). Once
318 for testing (unless the -U/--noupdate option is specified). Once
319 you have performed tests, mark the working directory as good or
319 you have performed tests, mark the working directory as good or
320 bad, and bisect will either update to another candidate changeset
320 bad, and bisect will either update to another candidate changeset
321 or announce that it has found the bad revision.
321 or announce that it has found the bad revision.
322
322
323 As a shortcut, you can also use the revision argument to mark a
323 As a shortcut, you can also use the revision argument to mark a
324 revision as good or bad without checking it out first.
324 revision as good or bad without checking it out first.
325
325
326 If you supply a command, it will be used for automatic bisection.
326 If you supply a command, it will be used for automatic bisection.
327 Its exit status will be used to mark revisions as good or bad:
327 Its exit status will be used to mark revisions as good or bad:
328 status 0 means good, 125 means to skip the revision, 127
328 status 0 means good, 125 means to skip the revision, 127
329 (command not found) will abort the bisection, and any other
329 (command not found) will abort the bisection, and any other
330 non-zero exit status means the revision is bad.
330 non-zero exit status means the revision is bad.
331
331
332 Returns 0 on success.
332 Returns 0 on success.
333 """
333 """
334 def extendbisectrange(nodes, good):
334 def extendbisectrange(nodes, good):
335 # bisect is incomplete when it ends on a merge node and
335 # bisect is incomplete when it ends on a merge node and
336 # one of the parent was not checked.
336 # one of the parent was not checked.
337 parents = repo[nodes[0]].parents()
337 parents = repo[nodes[0]].parents()
338 if len(parents) > 1:
338 if len(parents) > 1:
339 side = good and state['bad'] or state['good']
339 side = good and state['bad'] or state['good']
340 num = len(set(i.node() for i in parents) & set(side))
340 num = len(set(i.node() for i in parents) & set(side))
341 if num == 1:
341 if num == 1:
342 return parents[0].ancestor(parents[1])
342 return parents[0].ancestor(parents[1])
343 return None
343 return None
344
344
345 def print_result(nodes, good):
345 def print_result(nodes, good):
346 displayer = cmdutil.show_changeset(ui, repo, {})
346 displayer = cmdutil.show_changeset(ui, repo, {})
347 if len(nodes) == 1:
347 if len(nodes) == 1:
348 # narrowed it down to a single revision
348 # narrowed it down to a single revision
349 if good:
349 if good:
350 ui.write(_("The first good revision is:\n"))
350 ui.write(_("The first good revision is:\n"))
351 else:
351 else:
352 ui.write(_("The first bad revision is:\n"))
352 ui.write(_("The first bad revision is:\n"))
353 displayer.show(repo[nodes[0]])
353 displayer.show(repo[nodes[0]])
354 parents = repo[nodes[0]].parents()
354 parents = repo[nodes[0]].parents()
355 extendnode = extendbisectrange(nodes, good)
355 extendnode = extendbisectrange(nodes, good)
356 if extendnode is not None:
356 if extendnode is not None:
357 ui.write(_('Not all ancestors of this changeset have been'
357 ui.write(_('Not all ancestors of this changeset have been'
358 ' checked.\nUse bisect --extend to continue the '
358 ' checked.\nUse bisect --extend to continue the '
359 'bisection from\nthe common ancestor, %s.\n')
359 'bisection from\nthe common ancestor, %s.\n')
360 % short(extendnode.node()))
360 % short(extendnode.node()))
361 else:
361 else:
362 # multiple possible revisions
362 # multiple possible revisions
363 if good:
363 if good:
364 ui.write(_("Due to skipped revisions, the first "
364 ui.write(_("Due to skipped revisions, the first "
365 "good revision could be any of:\n"))
365 "good revision could be any of:\n"))
366 else:
366 else:
367 ui.write(_("Due to skipped revisions, the first "
367 ui.write(_("Due to skipped revisions, the first "
368 "bad revision could be any of:\n"))
368 "bad revision could be any of:\n"))
369 for n in nodes:
369 for n in nodes:
370 displayer.show(repo[n])
370 displayer.show(repo[n])
371 displayer.close()
371 displayer.close()
372
372
373 def check_state(state, interactive=True):
373 def check_state(state, interactive=True):
374 if not state['good'] or not state['bad']:
374 if not state['good'] or not state['bad']:
375 if (good or bad or skip or reset) and interactive:
375 if (good or bad or skip or reset) and interactive:
376 return
376 return
377 if not state['good']:
377 if not state['good']:
378 raise util.Abort(_('cannot bisect (no known good revisions)'))
378 raise util.Abort(_('cannot bisect (no known good revisions)'))
379 else:
379 else:
380 raise util.Abort(_('cannot bisect (no known bad revisions)'))
380 raise util.Abort(_('cannot bisect (no known bad revisions)'))
381 return True
381 return True
382
382
383 # backward compatibility
383 # backward compatibility
384 if rev in "good bad reset init".split():
384 if rev in "good bad reset init".split():
385 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
385 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
386 cmd, rev, extra = rev, extra, None
386 cmd, rev, extra = rev, extra, None
387 if cmd == "good":
387 if cmd == "good":
388 good = True
388 good = True
389 elif cmd == "bad":
389 elif cmd == "bad":
390 bad = True
390 bad = True
391 else:
391 else:
392 reset = True
392 reset = True
393 elif extra or good + bad + skip + reset + extend + bool(command) > 1:
393 elif extra or good + bad + skip + reset + extend + bool(command) > 1:
394 raise util.Abort(_('incompatible arguments'))
394 raise util.Abort(_('incompatible arguments'))
395
395
396 if reset:
396 if reset:
397 p = repo.join("bisect.state")
397 p = repo.join("bisect.state")
398 if os.path.exists(p):
398 if os.path.exists(p):
399 os.unlink(p)
399 os.unlink(p)
400 return
400 return
401
401
402 state = hbisect.load_state(repo)
402 state = hbisect.load_state(repo)
403
403
404 if command:
404 if command:
405 changesets = 1
405 changesets = 1
406 try:
406 try:
407 while changesets:
407 while changesets:
408 # update state
408 # update state
409 status = util.system(command)
409 status = util.system(command)
410 if status == 125:
410 if status == 125:
411 transition = "skip"
411 transition = "skip"
412 elif status == 0:
412 elif status == 0:
413 transition = "good"
413 transition = "good"
414 # status < 0 means process was killed
414 # status < 0 means process was killed
415 elif status == 127:
415 elif status == 127:
416 raise util.Abort(_("failed to execute %s") % command)
416 raise util.Abort(_("failed to execute %s") % command)
417 elif status < 0:
417 elif status < 0:
418 raise util.Abort(_("%s killed") % command)
418 raise util.Abort(_("%s killed") % command)
419 else:
419 else:
420 transition = "bad"
420 transition = "bad"
421 ctx = cmdutil.revsingle(repo, rev)
421 ctx = cmdutil.revsingle(repo, rev)
422 rev = None # clear for future iterations
422 rev = None # clear for future iterations
423 state[transition].append(ctx.node())
423 state[transition].append(ctx.node())
424 ui.status(_('Changeset %d:%s: %s\n') % (ctx, ctx, transition))
424 ui.status(_('Changeset %d:%s: %s\n') % (ctx, ctx, transition))
425 check_state(state, interactive=False)
425 check_state(state, interactive=False)
426 # bisect
426 # bisect
427 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
427 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
428 # update to next check
428 # update to next check
429 cmdutil.bail_if_changed(repo)
429 cmdutil.bail_if_changed(repo)
430 hg.clean(repo, nodes[0], show_stats=False)
430 hg.clean(repo, nodes[0], show_stats=False)
431 finally:
431 finally:
432 hbisect.save_state(repo, state)
432 hbisect.save_state(repo, state)
433 print_result(nodes, good)
433 print_result(nodes, good)
434 return
434 return
435
435
436 # update state
436 # update state
437
437
438 if rev:
438 if rev:
439 nodes = [repo.lookup(i) for i in cmdutil.revrange(repo, [rev])]
439 nodes = [repo.lookup(i) for i in cmdutil.revrange(repo, [rev])]
440 else:
440 else:
441 nodes = [repo.lookup('.')]
441 nodes = [repo.lookup('.')]
442
442
443 if good or bad or skip:
443 if good or bad or skip:
444 if good:
444 if good:
445 state['good'] += nodes
445 state['good'] += nodes
446 elif bad:
446 elif bad:
447 state['bad'] += nodes
447 state['bad'] += nodes
448 elif skip:
448 elif skip:
449 state['skip'] += nodes
449 state['skip'] += nodes
450 hbisect.save_state(repo, state)
450 hbisect.save_state(repo, state)
451
451
452 if not check_state(state):
452 if not check_state(state):
453 return
453 return
454
454
455 # actually bisect
455 # actually bisect
456 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
456 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
457 if extend:
457 if extend:
458 if not changesets:
458 if not changesets:
459 extendnode = extendbisectrange(nodes, good)
459 extendnode = extendbisectrange(nodes, good)
460 if extendnode is not None:
460 if extendnode is not None:
461 ui.write(_("Extending search to changeset %d:%s\n"
461 ui.write(_("Extending search to changeset %d:%s\n"
462 % (extendnode.rev(), short(extendnode.node()))))
462 % (extendnode.rev(), short(extendnode.node()))))
463 if noupdate:
463 if noupdate:
464 return
464 return
465 cmdutil.bail_if_changed(repo)
465 cmdutil.bail_if_changed(repo)
466 return hg.clean(repo, extendnode.node())
466 return hg.clean(repo, extendnode.node())
467 raise util.Abort(_("nothing to extend"))
467 raise util.Abort(_("nothing to extend"))
468
468
469 if changesets == 0:
469 if changesets == 0:
470 print_result(nodes, good)
470 print_result(nodes, good)
471 else:
471 else:
472 assert len(nodes) == 1 # only a single node can be tested next
472 assert len(nodes) == 1 # only a single node can be tested next
473 node = nodes[0]
473 node = nodes[0]
474 # compute the approximate number of remaining tests
474 # compute the approximate number of remaining tests
475 tests, size = 0, 2
475 tests, size = 0, 2
476 while size <= changesets:
476 while size <= changesets:
477 tests, size = tests + 1, size * 2
477 tests, size = tests + 1, size * 2
478 rev = repo.changelog.rev(node)
478 rev = repo.changelog.rev(node)
479 ui.write(_("Testing changeset %d:%s "
479 ui.write(_("Testing changeset %d:%s "
480 "(%d changesets remaining, ~%d tests)\n")
480 "(%d changesets remaining, ~%d tests)\n")
481 % (rev, short(node), changesets, tests))
481 % (rev, short(node), changesets, tests))
482 if not noupdate:
482 if not noupdate:
483 cmdutil.bail_if_changed(repo)
483 cmdutil.bail_if_changed(repo)
484 return hg.clean(repo, node)
484 return hg.clean(repo, node)
485
485
486 def bookmark(ui, repo, mark=None, rev=None, force=False, delete=False, rename=None):
486 def bookmark(ui, repo, mark=None, rev=None, force=False, delete=False, rename=None):
487 '''track a line of development with movable markers
487 '''track a line of development with movable markers
488
488
489 Bookmarks are pointers to certain commits that move when
489 Bookmarks are pointers to certain commits that move when
490 committing. Bookmarks are local. They can be renamed, copied and
490 committing. Bookmarks are local. They can be renamed, copied and
491 deleted. It is possible to use bookmark names in :hg:`merge` and
491 deleted. It is possible to use bookmark names in :hg:`merge` and
492 :hg:`update` to merge and update respectively to a given bookmark.
492 :hg:`update` to merge and update respectively to a given bookmark.
493
493
494 You can use :hg:`bookmark NAME` to set a bookmark on the working
494 You can use :hg:`bookmark NAME` to set a bookmark on the working
495 directory's parent revision with the given name. If you specify
495 directory's parent revision with the given name. If you specify
496 a revision using -r REV (where REV may be an existing bookmark),
496 a revision using -r REV (where REV may be an existing bookmark),
497 the bookmark is assigned to that revision.
497 the bookmark is assigned to that revision.
498
498
499 Bookmarks can be pushed and pulled between repositories (see :hg:`help
499 Bookmarks can be pushed and pulled between repositories (see :hg:`help
500 push` and :hg:`help pull`). This requires both the local and remote
500 push` and :hg:`help pull`). This requires both the local and remote
501 repositories to support bookmarks. For versions prior to 1.8, this means
501 repositories to support bookmarks. For versions prior to 1.8, this means
502 the bookmarks extension must be enabled.
502 the bookmarks extension must be enabled.
503 '''
503 '''
504 hexfn = ui.debugflag and hex or short
504 hexfn = ui.debugflag and hex or short
505 marks = repo._bookmarks
505 marks = repo._bookmarks
506 cur = repo.changectx('.').node()
506 cur = repo.changectx('.').node()
507
507
508 if rename:
508 if rename:
509 if rename not in marks:
509 if rename not in marks:
510 raise util.Abort(_("a bookmark of this name does not exist"))
510 raise util.Abort(_("a bookmark of this name does not exist"))
511 if mark in marks and not force:
511 if mark in marks and not force:
512 raise util.Abort(_("a bookmark of the same name already exists"))
512 raise util.Abort(_("a bookmark of the same name already exists"))
513 if mark is None:
513 if mark is None:
514 raise util.Abort(_("new bookmark name required"))
514 raise util.Abort(_("new bookmark name required"))
515 marks[mark] = marks[rename]
515 marks[mark] = marks[rename]
516 if repo._bookmarkcurrent == rename:
516 if repo._bookmarkcurrent == rename:
517 bookmarks.setcurrent(repo, mark)
517 bookmarks.setcurrent(repo, mark)
518 del marks[rename]
518 del marks[rename]
519 bookmarks.write(repo)
519 bookmarks.write(repo)
520 return
520 return
521
521
522 if delete:
522 if delete:
523 if mark is None:
523 if mark is None:
524 raise util.Abort(_("bookmark name required"))
524 raise util.Abort(_("bookmark name required"))
525 if mark not in marks:
525 if mark not in marks:
526 raise util.Abort(_("a bookmark of this name does not exist"))
526 raise util.Abort(_("a bookmark of this name does not exist"))
527 if mark == repo._bookmarkcurrent:
527 if mark == repo._bookmarkcurrent:
528 bookmarks.setcurrent(repo, None)
528 bookmarks.setcurrent(repo, None)
529 del marks[mark]
529 del marks[mark]
530 bookmarks.write(repo)
530 bookmarks.write(repo)
531 return
531 return
532
532
533 if mark is not None:
533 if mark is not None:
534 if "\n" in mark:
534 if "\n" in mark:
535 raise util.Abort(_("bookmark name cannot contain newlines"))
535 raise util.Abort(_("bookmark name cannot contain newlines"))
536 mark = mark.strip()
536 mark = mark.strip()
537 if not mark:
537 if not mark:
538 raise util.Abort(_("bookmark names cannot consist entirely of "
538 raise util.Abort(_("bookmark names cannot consist entirely of "
539 "whitespace"))
539 "whitespace"))
540 if mark in marks and not force:
540 if mark in marks and not force:
541 raise util.Abort(_("a bookmark of the same name already exists"))
541 raise util.Abort(_("a bookmark of the same name already exists"))
542 if ((mark in repo.branchtags() or mark == repo.dirstate.branch())
542 if ((mark in repo.branchtags() or mark == repo.dirstate.branch())
543 and not force):
543 and not force):
544 raise util.Abort(
544 raise util.Abort(
545 _("a bookmark cannot have the name of an existing branch"))
545 _("a bookmark cannot have the name of an existing branch"))
546 if rev:
546 if rev:
547 marks[mark] = repo.lookup(rev)
547 marks[mark] = repo.lookup(rev)
548 else:
548 else:
549 marks[mark] = repo.changectx('.').node()
549 marks[mark] = repo.changectx('.').node()
550 if repo.changectx('.').node() == marks[mark]:
550 if repo.changectx('.').node() == marks[mark]:
551 bookmarks.setcurrent(repo, mark)
551 bookmarks.setcurrent(repo, mark)
552 bookmarks.write(repo)
552 bookmarks.write(repo)
553 return
553 return
554
554
555 if mark is None:
555 if mark is None:
556 if rev:
556 if rev:
557 raise util.Abort(_("bookmark name required"))
557 raise util.Abort(_("bookmark name required"))
558 if len(marks) == 0:
558 if len(marks) == 0:
559 ui.status(_("no bookmarks set\n"))
559 ui.status(_("no bookmarks set\n"))
560 else:
560 else:
561 for bmark, n in sorted(marks.iteritems()):
561 for bmark, n in sorted(marks.iteritems()):
562 current = repo._bookmarkcurrent
562 current = repo._bookmarkcurrent
563 if bmark == current and n == cur:
563 if bmark == current and n == cur:
564 prefix, label = '*', 'bookmarks.current'
564 prefix, label = '*', 'bookmarks.current'
565 else:
565 else:
566 prefix, label = ' ', ''
566 prefix, label = ' ', ''
567
567
568 if ui.quiet:
568 if ui.quiet:
569 ui.write("%s\n" % bmark, label=label)
569 ui.write("%s\n" % bmark, label=label)
570 else:
570 else:
571 ui.write(" %s %-25s %d:%s\n" % (
571 ui.write(" %s %-25s %d:%s\n" % (
572 prefix, bmark, repo.changelog.rev(n), hexfn(n)),
572 prefix, bmark, repo.changelog.rev(n), hexfn(n)),
573 label=label)
573 label=label)
574 return
574 return
575
575
576 def branch(ui, repo, label=None, **opts):
576 def branch(ui, repo, label=None, **opts):
577 """set or show the current branch name
577 """set or show the current branch name
578
578
579 With no argument, show the current branch name. With one argument,
579 With no argument, show the current branch name. With one argument,
580 set the working directory branch name (the branch will not exist
580 set the working directory branch name (the branch will not exist
581 in the repository until the next commit). Standard practice
581 in the repository until the next commit). Standard practice
582 recommends that primary development take place on the 'default'
582 recommends that primary development take place on the 'default'
583 branch.
583 branch.
584
584
585 Unless -f/--force is specified, branch will not let you set a
585 Unless -f/--force is specified, branch will not let you set a
586 branch name that already exists, even if it's inactive.
586 branch name that already exists, even if it's inactive.
587
587
588 Use -C/--clean to reset the working directory branch to that of
588 Use -C/--clean to reset the working directory branch to that of
589 the parent of the working directory, negating a previous branch
589 the parent of the working directory, negating a previous branch
590 change.
590 change.
591
591
592 Use the command :hg:`update` to switch to an existing branch. Use
592 Use the command :hg:`update` to switch to an existing branch. Use
593 :hg:`commit --close-branch` to mark this branch as closed.
593 :hg:`commit --close-branch` to mark this branch as closed.
594
594
595 Returns 0 on success.
595 Returns 0 on success.
596 """
596 """
597
597
598 if opts.get('clean'):
598 if opts.get('clean'):
599 label = repo[None].parents()[0].branch()
599 label = repo[None].parents()[0].branch()
600 repo.dirstate.setbranch(label)
600 repo.dirstate.setbranch(label)
601 ui.status(_('reset working directory to branch %s\n') % label)
601 ui.status(_('reset working directory to branch %s\n') % label)
602 elif label:
602 elif label:
603 if not opts.get('force') and label in repo.branchtags():
603 if not opts.get('force') and label in repo.branchtags():
604 if label not in [p.branch() for p in repo.parents()]:
604 if label not in [p.branch() for p in repo.parents()]:
605 raise util.Abort(_('a branch of the same name already exists'
605 raise util.Abort(_('a branch of the same name already exists'
606 " (use 'hg update' to switch to it)"))
606 " (use 'hg update' to switch to it)"))
607 repo.dirstate.setbranch(label)
607 repo.dirstate.setbranch(label)
608 ui.status(_('marked working directory as branch %s\n') % label)
608 ui.status(_('marked working directory as branch %s\n') % label)
609 else:
609 else:
610 ui.write("%s\n" % repo.dirstate.branch())
610 ui.write("%s\n" % repo.dirstate.branch())
611
611
612 def branches(ui, repo, active=False, closed=False):
612 def branches(ui, repo, active=False, closed=False):
613 """list repository named branches
613 """list repository named branches
614
614
615 List the repository's named branches, indicating which ones are
615 List the repository's named branches, indicating which ones are
616 inactive. If -c/--closed is specified, also list branches which have
616 inactive. If -c/--closed is specified, also list branches which have
617 been marked closed (see :hg:`commit --close-branch`).
617 been marked closed (see :hg:`commit --close-branch`).
618
618
619 If -a/--active is specified, only show active branches. A branch
619 If -a/--active is specified, only show active branches. A branch
620 is considered active if it contains repository heads.
620 is considered active if it contains repository heads.
621
621
622 Use the command :hg:`update` to switch to an existing branch.
622 Use the command :hg:`update` to switch to an existing branch.
623
623
624 Returns 0.
624 Returns 0.
625 """
625 """
626
626
627 hexfunc = ui.debugflag and hex or short
627 hexfunc = ui.debugflag and hex or short
628 activebranches = [repo[n].branch() for n in repo.heads()]
628 activebranches = [repo[n].branch() for n in repo.heads()]
629 def testactive(tag, node):
629 def testactive(tag, node):
630 realhead = tag in activebranches
630 realhead = tag in activebranches
631 open = node in repo.branchheads(tag, closed=False)
631 open = node in repo.branchheads(tag, closed=False)
632 return realhead and open
632 return realhead and open
633 branches = sorted([(testactive(tag, node), repo.changelog.rev(node), tag)
633 branches = sorted([(testactive(tag, node), repo.changelog.rev(node), tag)
634 for tag, node in repo.branchtags().items()],
634 for tag, node in repo.branchtags().items()],
635 reverse=True)
635 reverse=True)
636
636
637 for isactive, node, tag in branches:
637 for isactive, node, tag in branches:
638 if (not active) or isactive:
638 if (not active) or isactive:
639 if ui.quiet:
639 if ui.quiet:
640 ui.write("%s\n" % tag)
640 ui.write("%s\n" % tag)
641 else:
641 else:
642 hn = repo.lookup(node)
642 hn = repo.lookup(node)
643 if isactive:
643 if isactive:
644 label = 'branches.active'
644 label = 'branches.active'
645 notice = ''
645 notice = ''
646 elif hn not in repo.branchheads(tag, closed=False):
646 elif hn not in repo.branchheads(tag, closed=False):
647 if not closed:
647 if not closed:
648 continue
648 continue
649 label = 'branches.closed'
649 label = 'branches.closed'
650 notice = _(' (closed)')
650 notice = _(' (closed)')
651 else:
651 else:
652 label = 'branches.inactive'
652 label = 'branches.inactive'
653 notice = _(' (inactive)')
653 notice = _(' (inactive)')
654 if tag == repo.dirstate.branch():
654 if tag == repo.dirstate.branch():
655 label = 'branches.current'
655 label = 'branches.current'
656 rev = str(node).rjust(31 - encoding.colwidth(tag))
656 rev = str(node).rjust(31 - encoding.colwidth(tag))
657 rev = ui.label('%s:%s' % (rev, hexfunc(hn)), 'log.changeset')
657 rev = ui.label('%s:%s' % (rev, hexfunc(hn)), 'log.changeset')
658 tag = ui.label(tag, label)
658 tag = ui.label(tag, label)
659 ui.write("%s %s%s\n" % (tag, rev, notice))
659 ui.write("%s %s%s\n" % (tag, rev, notice))
660
660
661 def bundle(ui, repo, fname, dest=None, **opts):
661 def bundle(ui, repo, fname, dest=None, **opts):
662 """create a changegroup file
662 """create a changegroup file
663
663
664 Generate a compressed changegroup file collecting changesets not
664 Generate a compressed changegroup file collecting changesets not
665 known to be in another repository.
665 known to be in another repository.
666
666
667 If you omit the destination repository, then hg assumes the
667 If you omit the destination repository, then hg assumes the
668 destination will have all the nodes you specify with --base
668 destination will have all the nodes you specify with --base
669 parameters. To create a bundle containing all changesets, use
669 parameters. To create a bundle containing all changesets, use
670 -a/--all (or --base null).
670 -a/--all (or --base null).
671
671
672 You can change compression method with the -t/--type option.
672 You can change compression method with the -t/--type option.
673 The available compression methods are: none, bzip2, and
673 The available compression methods are: none, bzip2, and
674 gzip (by default, bundles are compressed using bzip2).
674 gzip (by default, bundles are compressed using bzip2).
675
675
676 The bundle file can then be transferred using conventional means
676 The bundle file can then be transferred using conventional means
677 and applied to another repository with the unbundle or pull
677 and applied to another repository with the unbundle or pull
678 command. This is useful when direct push and pull are not
678 command. This is useful when direct push and pull are not
679 available or when exporting an entire repository is undesirable.
679 available or when exporting an entire repository is undesirable.
680
680
681 Applying bundles preserves all changeset contents including
681 Applying bundles preserves all changeset contents including
682 permissions, copy/rename information, and revision history.
682 permissions, copy/rename information, and revision history.
683
683
684 Returns 0 on success, 1 if no changes found.
684 Returns 0 on success, 1 if no changes found.
685 """
685 """
686 revs = None
686 revs = None
687 if 'rev' in opts:
687 if 'rev' in opts:
688 revs = cmdutil.revrange(repo, opts['rev'])
688 revs = cmdutil.revrange(repo, opts['rev'])
689
689
690 if opts.get('all'):
690 if opts.get('all'):
691 base = ['null']
691 base = ['null']
692 else:
692 else:
693 base = cmdutil.revrange(repo, opts.get('base'))
693 base = cmdutil.revrange(repo, opts.get('base'))
694 if base:
694 if base:
695 if dest:
695 if dest:
696 raise util.Abort(_("--base is incompatible with specifying "
696 raise util.Abort(_("--base is incompatible with specifying "
697 "a destination"))
697 "a destination"))
698 base = [repo.lookup(rev) for rev in base]
698 base = [repo.lookup(rev) for rev in base]
699 # create the right base
699 # create the right base
700 # XXX: nodesbetween / changegroup* should be "fixed" instead
700 # XXX: nodesbetween / changegroup* should be "fixed" instead
701 o = []
701 o = []
702 has = set((nullid,))
702 has = set((nullid,))
703 for n in base:
703 for n in base:
704 has.update(repo.changelog.reachable(n))
704 has.update(repo.changelog.reachable(n))
705 if revs:
705 if revs:
706 revs = [repo.lookup(rev) for rev in revs]
706 revs = [repo.lookup(rev) for rev in revs]
707 visit = revs[:]
707 visit = revs[:]
708 has.difference_update(visit)
708 has.difference_update(visit)
709 else:
709 else:
710 visit = repo.changelog.heads()
710 visit = repo.changelog.heads()
711 seen = {}
711 seen = {}
712 while visit:
712 while visit:
713 n = visit.pop(0)
713 n = visit.pop(0)
714 parents = [p for p in repo.changelog.parents(n) if p not in has]
714 parents = [p for p in repo.changelog.parents(n) if p not in has]
715 if len(parents) == 0:
715 if len(parents) == 0:
716 if n not in has:
716 if n not in has:
717 o.append(n)
717 o.append(n)
718 else:
718 else:
719 for p in parents:
719 for p in parents:
720 if p not in seen:
720 if p not in seen:
721 seen[p] = 1
721 seen[p] = 1
722 visit.append(p)
722 visit.append(p)
723 else:
723 else:
724 dest = ui.expandpath(dest or 'default-push', dest or 'default')
724 dest = ui.expandpath(dest or 'default-push', dest or 'default')
725 dest, branches = hg.parseurl(dest, opts.get('branch'))
725 dest, branches = hg.parseurl(dest, opts.get('branch'))
726 other = hg.repository(hg.remoteui(repo, opts), dest)
726 other = hg.repository(hg.remoteui(repo, opts), dest)
727 revs, checkout = hg.addbranchrevs(repo, other, branches, revs)
727 revs, checkout = hg.addbranchrevs(repo, other, branches, revs)
728 if revs:
728 if revs:
729 revs = [repo.lookup(rev) for rev in revs]
729 revs = [repo.lookup(rev) for rev in revs]
730 o = discovery.findoutgoing(repo, other, force=opts.get('force'))
730 o = discovery.findoutgoing(repo, other, force=opts.get('force'))
731
731
732 if not o:
732 if not o:
733 ui.status(_("no changes found\n"))
733 ui.status(_("no changes found\n"))
734 return 1
734 return 1
735
735
736 if revs:
736 if revs:
737 cg = repo.changegroupsubset(o, revs, 'bundle')
737 cg = repo.changegroupsubset(o, revs, 'bundle')
738 else:
738 else:
739 cg = repo.changegroup(o, 'bundle')
739 cg = repo.changegroup(o, 'bundle')
740
740
741 bundletype = opts.get('type', 'bzip2').lower()
741 bundletype = opts.get('type', 'bzip2').lower()
742 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
742 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
743 bundletype = btypes.get(bundletype)
743 bundletype = btypes.get(bundletype)
744 if bundletype not in changegroup.bundletypes:
744 if bundletype not in changegroup.bundletypes:
745 raise util.Abort(_('unknown bundle type specified with --type'))
745 raise util.Abort(_('unknown bundle type specified with --type'))
746
746
747 changegroup.writebundle(cg, fname, bundletype)
747 changegroup.writebundle(cg, fname, bundletype)
748
748
749 def cat(ui, repo, file1, *pats, **opts):
749 def cat(ui, repo, file1, *pats, **opts):
750 """output the current or given revision of files
750 """output the current or given revision of files
751
751
752 Print the specified files as they were at the given revision. If
752 Print the specified files as they were at the given revision. If
753 no revision is given, the parent of the working directory is used,
753 no revision is given, the parent of the working directory is used,
754 or tip if no revision is checked out.
754 or tip if no revision is checked out.
755
755
756 Output may be to a file, in which case the name of the file is
756 Output may be to a file, in which case the name of the file is
757 given using a format string. The formatting rules are the same as
757 given using a format string. The formatting rules are the same as
758 for the export command, with the following additions:
758 for the export command, with the following additions:
759
759
760 :``%s``: basename of file being printed
760 :``%s``: basename of file being printed
761 :``%d``: dirname of file being printed, or '.' if in repository root
761 :``%d``: dirname of file being printed, or '.' if in repository root
762 :``%p``: root-relative path name of file being printed
762 :``%p``: root-relative path name of file being printed
763
763
764 Returns 0 on success.
764 Returns 0 on success.
765 """
765 """
766 ctx = cmdutil.revsingle(repo, opts.get('rev'))
766 ctx = cmdutil.revsingle(repo, opts.get('rev'))
767 err = 1
767 err = 1
768 m = cmdutil.match(repo, (file1,) + pats, opts)
768 m = cmdutil.match(repo, (file1,) + pats, opts)
769 for abs in ctx.walk(m):
769 for abs in ctx.walk(m):
770 fp = cmdutil.make_file(repo, opts.get('output'), ctx.node(), pathname=abs)
770 fp = cmdutil.make_file(repo, opts.get('output'), ctx.node(), pathname=abs)
771 data = ctx[abs].data()
771 data = ctx[abs].data()
772 if opts.get('decode'):
772 if opts.get('decode'):
773 data = repo.wwritedata(abs, data)
773 data = repo.wwritedata(abs, data)
774 fp.write(data)
774 fp.write(data)
775 fp.close()
775 fp.close()
776 err = 0
776 err = 0
777 return err
777 return err
778
778
779 def clone(ui, source, dest=None, **opts):
779 def clone(ui, source, dest=None, **opts):
780 """make a copy of an existing repository
780 """make a copy of an existing repository
781
781
782 Create a copy of an existing repository in a new directory.
782 Create a copy of an existing repository in a new directory.
783
783
784 If no destination directory name is specified, it defaults to the
784 If no destination directory name is specified, it defaults to the
785 basename of the source.
785 basename of the source.
786
786
787 The location of the source is added to the new repository's
787 The location of the source is added to the new repository's
788 ``.hg/hgrc`` file, as the default to be used for future pulls.
788 ``.hg/hgrc`` file, as the default to be used for future pulls.
789
789
790 See :hg:`help urls` for valid source format details.
790 See :hg:`help urls` for valid source format details.
791
791
792 It is possible to specify an ``ssh://`` URL as the destination, but no
792 It is possible to specify an ``ssh://`` URL as the destination, but no
793 ``.hg/hgrc`` and working directory will be created on the remote side.
793 ``.hg/hgrc`` and working directory will be created on the remote side.
794 Please see :hg:`help urls` for important details about ``ssh://`` URLs.
794 Please see :hg:`help urls` for important details about ``ssh://`` URLs.
795
795
796 A set of changesets (tags, or branch names) to pull may be specified
796 A set of changesets (tags, or branch names) to pull may be specified
797 by listing each changeset (tag, or branch name) with -r/--rev.
797 by listing each changeset (tag, or branch name) with -r/--rev.
798 If -r/--rev is used, the cloned repository will contain only a subset
798 If -r/--rev is used, the cloned repository will contain only a subset
799 of the changesets of the source repository. Only the set of changesets
799 of the changesets of the source repository. Only the set of changesets
800 defined by all -r/--rev options (including all their ancestors)
800 defined by all -r/--rev options (including all their ancestors)
801 will be pulled into the destination repository.
801 will be pulled into the destination repository.
802 No subsequent changesets (including subsequent tags) will be present
802 No subsequent changesets (including subsequent tags) will be present
803 in the destination.
803 in the destination.
804
804
805 Using -r/--rev (or 'clone src#rev dest') implies --pull, even for
805 Using -r/--rev (or 'clone src#rev dest') implies --pull, even for
806 local source repositories.
806 local source repositories.
807
807
808 For efficiency, hardlinks are used for cloning whenever the source
808 For efficiency, hardlinks are used for cloning whenever the source
809 and destination are on the same filesystem (note this applies only
809 and destination are on the same filesystem (note this applies only
810 to the repository data, not to the working directory). Some
810 to the repository data, not to the working directory). Some
811 filesystems, such as AFS, implement hardlinking incorrectly, but
811 filesystems, such as AFS, implement hardlinking incorrectly, but
812 do not report errors. In these cases, use the --pull option to
812 do not report errors. In these cases, use the --pull option to
813 avoid hardlinking.
813 avoid hardlinking.
814
814
815 In some cases, you can clone repositories and the working directory
815 In some cases, you can clone repositories and the working directory
816 using full hardlinks with ::
816 using full hardlinks with ::
817
817
818 $ cp -al REPO REPOCLONE
818 $ cp -al REPO REPOCLONE
819
819
820 This is the fastest way to clone, but it is not always safe. The
820 This is the fastest way to clone, but it is not always safe. The
821 operation is not atomic (making sure REPO is not modified during
821 operation is not atomic (making sure REPO is not modified during
822 the operation is up to you) and you have to make sure your editor
822 the operation is up to you) and you have to make sure your editor
823 breaks hardlinks (Emacs and most Linux Kernel tools do so). Also,
823 breaks hardlinks (Emacs and most Linux Kernel tools do so). Also,
824 this is not compatible with certain extensions that place their
824 this is not compatible with certain extensions that place their
825 metadata under the .hg directory, such as mq.
825 metadata under the .hg directory, such as mq.
826
826
827 Mercurial will update the working directory to the first applicable
827 Mercurial will update the working directory to the first applicable
828 revision from this list:
828 revision from this list:
829
829
830 a) null if -U or the source repository has no changesets
830 a) null if -U or the source repository has no changesets
831 b) if -u . and the source repository is local, the first parent of
831 b) if -u . and the source repository is local, the first parent of
832 the source repository's working directory
832 the source repository's working directory
833 c) the changeset specified with -u (if a branch name, this means the
833 c) the changeset specified with -u (if a branch name, this means the
834 latest head of that branch)
834 latest head of that branch)
835 d) the changeset specified with -r
835 d) the changeset specified with -r
836 e) the tipmost head specified with -b
836 e) the tipmost head specified with -b
837 f) the tipmost head specified with the url#branch source syntax
837 f) the tipmost head specified with the url#branch source syntax
838 g) the tipmost head of the default branch
838 g) the tipmost head of the default branch
839 h) tip
839 h) tip
840
840
841 Returns 0 on success.
841 Returns 0 on success.
842 """
842 """
843 if opts.get('noupdate') and opts.get('updaterev'):
843 if opts.get('noupdate') and opts.get('updaterev'):
844 raise util.Abort(_("cannot specify both --noupdate and --updaterev"))
844 raise util.Abort(_("cannot specify both --noupdate and --updaterev"))
845
845
846 r = hg.clone(hg.remoteui(ui, opts), source, dest,
846 r = hg.clone(hg.remoteui(ui, opts), source, dest,
847 pull=opts.get('pull'),
847 pull=opts.get('pull'),
848 stream=opts.get('uncompressed'),
848 stream=opts.get('uncompressed'),
849 rev=opts.get('rev'),
849 rev=opts.get('rev'),
850 update=opts.get('updaterev') or not opts.get('noupdate'),
850 update=opts.get('updaterev') or not opts.get('noupdate'),
851 branch=opts.get('branch'))
851 branch=opts.get('branch'))
852
852
853 return r is None
853 return r is None
854
854
855 def commit(ui, repo, *pats, **opts):
855 def commit(ui, repo, *pats, **opts):
856 """commit the specified files or all outstanding changes
856 """commit the specified files or all outstanding changes
857
857
858 Commit changes to the given files into the repository. Unlike a
858 Commit changes to the given files into the repository. Unlike a
859 centralized SCM, this operation is a local operation. See
859 centralized SCM, this operation is a local operation. See
860 :hg:`push` for a way to actively distribute your changes.
860 :hg:`push` for a way to actively distribute your changes.
861
861
862 If a list of files is omitted, all changes reported by :hg:`status`
862 If a list of files is omitted, all changes reported by :hg:`status`
863 will be committed.
863 will be committed.
864
864
865 If you are committing the result of a merge, do not provide any
865 If you are committing the result of a merge, do not provide any
866 filenames or -I/-X filters.
866 filenames or -I/-X filters.
867
867
868 If no commit message is specified, Mercurial starts your
868 If no commit message is specified, Mercurial starts your
869 configured editor where you can enter a message. In case your
869 configured editor where you can enter a message. In case your
870 commit fails, you will find a backup of your message in
870 commit fails, you will find a backup of your message in
871 ``.hg/last-message.txt``.
871 ``.hg/last-message.txt``.
872
872
873 See :hg:`help dates` for a list of formats valid for -d/--date.
873 See :hg:`help dates` for a list of formats valid for -d/--date.
874
874
875 Returns 0 on success, 1 if nothing changed.
875 Returns 0 on success, 1 if nothing changed.
876 """
876 """
877 extra = {}
877 extra = {}
878 if opts.get('close_branch'):
878 if opts.get('close_branch'):
879 if repo['.'].node() not in repo.branchheads():
879 if repo['.'].node() not in repo.branchheads():
880 # The topo heads set is included in the branch heads set of the
880 # The topo heads set is included in the branch heads set of the
881 # current branch, so it's sufficient to test branchheads
881 # current branch, so it's sufficient to test branchheads
882 raise util.Abort(_('can only close branch heads'))
882 raise util.Abort(_('can only close branch heads'))
883 extra['close'] = 1
883 extra['close'] = 1
884 e = cmdutil.commiteditor
884 e = cmdutil.commiteditor
885 if opts.get('force_editor'):
885 if opts.get('force_editor'):
886 e = cmdutil.commitforceeditor
886 e = cmdutil.commitforceeditor
887
887
888 def commitfunc(ui, repo, message, match, opts):
888 def commitfunc(ui, repo, message, match, opts):
889 return repo.commit(message, opts.get('user'), opts.get('date'), match,
889 return repo.commit(message, opts.get('user'), opts.get('date'), match,
890 editor=e, extra=extra)
890 editor=e, extra=extra)
891
891
892 branch = repo[None].branch()
892 branch = repo[None].branch()
893 bheads = repo.branchheads(branch)
893 bheads = repo.branchheads(branch)
894
894
895 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
895 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
896 if not node:
896 if not node:
897 ui.status(_("nothing changed\n"))
897 ui.status(_("nothing changed\n"))
898 return 1
898 return 1
899
899
900 ctx = repo[node]
900 ctx = repo[node]
901 parents = ctx.parents()
901 parents = ctx.parents()
902
902
903 if bheads and not [x for x in parents
903 if bheads and not [x for x in parents
904 if x.node() in bheads and x.branch() == branch]:
904 if x.node() in bheads and x.branch() == branch]:
905 ui.status(_('created new head\n'))
905 ui.status(_('created new head\n'))
906 # The message is not printed for initial roots. For the other
906 # The message is not printed for initial roots. For the other
907 # changesets, it is printed in the following situations:
907 # changesets, it is printed in the following situations:
908 #
908 #
909 # Par column: for the 2 parents with ...
909 # Par column: for the 2 parents with ...
910 # N: null or no parent
910 # N: null or no parent
911 # B: parent is on another named branch
911 # B: parent is on another named branch
912 # C: parent is a regular non head changeset
912 # C: parent is a regular non head changeset
913 # H: parent was a branch head of the current branch
913 # H: parent was a branch head of the current branch
914 # Msg column: whether we print "created new head" message
914 # Msg column: whether we print "created new head" message
915 # In the following, it is assumed that there already exists some
915 # In the following, it is assumed that there already exists some
916 # initial branch heads of the current branch, otherwise nothing is
916 # initial branch heads of the current branch, otherwise nothing is
917 # printed anyway.
917 # printed anyway.
918 #
918 #
919 # Par Msg Comment
919 # Par Msg Comment
920 # NN y additional topo root
920 # NN y additional topo root
921 #
921 #
922 # BN y additional branch root
922 # BN y additional branch root
923 # CN y additional topo head
923 # CN y additional topo head
924 # HN n usual case
924 # HN n usual case
925 #
925 #
926 # BB y weird additional branch root
926 # BB y weird additional branch root
927 # CB y branch merge
927 # CB y branch merge
928 # HB n merge with named branch
928 # HB n merge with named branch
929 #
929 #
930 # CC y additional head from merge
930 # CC y additional head from merge
931 # CH n merge with a head
931 # CH n merge with a head
932 #
932 #
933 # HH n head merge: head count decreases
933 # HH n head merge: head count decreases
934
934
935 if not opts.get('close_branch'):
935 if not opts.get('close_branch'):
936 for r in parents:
936 for r in parents:
937 if r.extra().get('close') and r.branch() == branch:
937 if r.extra().get('close') and r.branch() == branch:
938 ui.status(_('reopening closed branch head %d\n') % r)
938 ui.status(_('reopening closed branch head %d\n') % r)
939
939
940 if ui.debugflag:
940 if ui.debugflag:
941 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx.hex()))
941 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx.hex()))
942 elif ui.verbose:
942 elif ui.verbose:
943 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx))
943 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx))
944
944
945 def copy(ui, repo, *pats, **opts):
945 def copy(ui, repo, *pats, **opts):
946 """mark files as copied for the next commit
946 """mark files as copied for the next commit
947
947
948 Mark dest as having copies of source files. If dest is a
948 Mark dest as having copies of source files. If dest is a
949 directory, copies are put in that directory. If dest is a file,
949 directory, copies are put in that directory. If dest is a file,
950 the source must be a single file.
950 the source must be a single file.
951
951
952 By default, this command copies the contents of files as they
952 By default, this command copies the contents of files as they
953 exist in the working directory. If invoked with -A/--after, the
953 exist in the working directory. If invoked with -A/--after, the
954 operation is recorded, but no copying is performed.
954 operation is recorded, but no copying is performed.
955
955
956 This command takes effect with the next commit. To undo a copy
956 This command takes effect with the next commit. To undo a copy
957 before that, see :hg:`revert`.
957 before that, see :hg:`revert`.
958
958
959 Returns 0 on success, 1 if errors are encountered.
959 Returns 0 on success, 1 if errors are encountered.
960 """
960 """
961 wlock = repo.wlock(False)
961 wlock = repo.wlock(False)
962 try:
962 try:
963 return cmdutil.copy(ui, repo, pats, opts)
963 return cmdutil.copy(ui, repo, pats, opts)
964 finally:
964 finally:
965 wlock.release()
965 wlock.release()
966
966
967 def debugancestor(ui, repo, *args):
967 def debugancestor(ui, repo, *args):
968 """find the ancestor revision of two revisions in a given index"""
968 """find the ancestor revision of two revisions in a given index"""
969 if len(args) == 3:
969 if len(args) == 3:
970 index, rev1, rev2 = args
970 index, rev1, rev2 = args
971 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index)
971 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index)
972 lookup = r.lookup
972 lookup = r.lookup
973 elif len(args) == 2:
973 elif len(args) == 2:
974 if not repo:
974 if not repo:
975 raise util.Abort(_("there is no Mercurial repository here "
975 raise util.Abort(_("there is no Mercurial repository here "
976 "(.hg not found)"))
976 "(.hg not found)"))
977 rev1, rev2 = args
977 rev1, rev2 = args
978 r = repo.changelog
978 r = repo.changelog
979 lookup = repo.lookup
979 lookup = repo.lookup
980 else:
980 else:
981 raise util.Abort(_('either two or three arguments required'))
981 raise util.Abort(_('either two or three arguments required'))
982 a = r.ancestor(lookup(rev1), lookup(rev2))
982 a = r.ancestor(lookup(rev1), lookup(rev2))
983 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
983 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
984
984
985 def debugbuilddag(ui, repo, text,
985 def debugbuilddag(ui, repo, text,
986 mergeable_file=False,
986 mergeable_file=False,
987 appended_file=False,
987 appended_file=False,
988 overwritten_file=False,
988 overwritten_file=False,
989 new_file=False):
989 new_file=False):
990 """builds a repo with a given dag from scratch in the current empty repo
990 """builds a repo with a given dag from scratch in the current empty repo
991
991
992 Elements:
992 Elements:
993
993
994 - "+n" is a linear run of n nodes based on the current default parent
994 - "+n" is a linear run of n nodes based on the current default parent
995 - "." is a single node based on the current default parent
995 - "." is a single node based on the current default parent
996 - "$" resets the default parent to null (implied at the start);
996 - "$" resets the default parent to null (implied at the start);
997 otherwise the default parent is always the last node created
997 otherwise the default parent is always the last node created
998 - "<p" sets the default parent to the backref p
998 - "<p" sets the default parent to the backref p
999 - "*p" is a fork at parent p, which is a backref
999 - "*p" is a fork at parent p, which is a backref
1000 - "*p1/p2" is a merge of parents p1 and p2, which are backrefs
1000 - "*p1/p2" is a merge of parents p1 and p2, which are backrefs
1001 - "/p2" is a merge of the preceding node and p2
1001 - "/p2" is a merge of the preceding node and p2
1002 - ":tag" defines a local tag for the preceding node
1002 - ":tag" defines a local tag for the preceding node
1003 - "@branch" sets the named branch for subsequent nodes
1003 - "@branch" sets the named branch for subsequent nodes
1004 - "!command" runs the command using your shell
1004 - "!command" runs the command using your shell
1005 - "!!my command\\n" is like "!", but to the end of the line
1005 - "!!my command\\n" is like "!", but to the end of the line
1006 - "#...\\n" is a comment up to the end of the line
1006 - "#...\\n" is a comment up to the end of the line
1007
1007
1008 Whitespace between the above elements is ignored.
1008 Whitespace between the above elements is ignored.
1009
1009
1010 A backref is either
1010 A backref is either
1011
1011
1012 - a number n, which references the node curr-n, where curr is the current
1012 - a number n, which references the node curr-n, where curr is the current
1013 node, or
1013 node, or
1014 - the name of a local tag you placed earlier using ":tag", or
1014 - the name of a local tag you placed earlier using ":tag", or
1015 - empty to denote the default parent.
1015 - empty to denote the default parent.
1016
1016
1017 All string valued-elements are either strictly alphanumeric, or must
1017 All string valued-elements are either strictly alphanumeric, or must
1018 be enclosed in double quotes ("..."), with "\\" as escape character.
1018 be enclosed in double quotes ("..."), with "\\" as escape character.
1019
1019
1020 Note that the --overwritten-file and --appended-file options imply the
1020 Note that the --overwritten-file and --appended-file options imply the
1021 use of "HGMERGE=internal:local" during DAG buildup.
1021 use of "HGMERGE=internal:local" during DAG buildup.
1022 """
1022 """
1023
1023
1024 if not (mergeable_file or appended_file or overwritten_file or new_file):
1024 if not (mergeable_file or appended_file or overwritten_file or new_file):
1025 raise util.Abort(_('need at least one of -m, -a, -o, -n'))
1025 raise util.Abort(_('need at least one of -m, -a, -o, -n'))
1026
1026
1027 if len(repo.changelog) > 0:
1027 if len(repo.changelog) > 0:
1028 raise util.Abort(_('repository is not empty'))
1028 raise util.Abort(_('repository is not empty'))
1029
1029
1030 if overwritten_file or appended_file:
1030 if overwritten_file or appended_file:
1031 # we don't want to fail in merges during buildup
1031 # we don't want to fail in merges during buildup
1032 os.environ['HGMERGE'] = 'internal:local'
1032 os.environ['HGMERGE'] = 'internal:local'
1033
1033
1034 def writefile(fname, text, fmode="wb"):
1034 def writefile(fname, text, fmode="wb"):
1035 f = open(fname, fmode)
1035 f = open(fname, fmode)
1036 try:
1036 try:
1037 f.write(text)
1037 f.write(text)
1038 finally:
1038 finally:
1039 f.close()
1039 f.close()
1040
1040
1041 if mergeable_file:
1041 if mergeable_file:
1042 linesperrev = 2
1042 linesperrev = 2
1043 # determine number of revs in DAG
1043 # determine number of revs in DAG
1044 n = 0
1044 n = 0
1045 for type, data in dagparser.parsedag(text):
1045 for type, data in dagparser.parsedag(text):
1046 if type == 'n':
1046 if type == 'n':
1047 n += 1
1047 n += 1
1048 # make a file with k lines per rev
1048 # make a file with k lines per rev
1049 writefile("mf", "\n".join(str(i) for i in xrange(0, n * linesperrev))
1049 writefile("mf", "\n".join(str(i) for i in xrange(0, n * linesperrev))
1050 + "\n")
1050 + "\n")
1051
1051
1052 at = -1
1052 at = -1
1053 atbranch = 'default'
1053 atbranch = 'default'
1054 for type, data in dagparser.parsedag(text):
1054 for type, data in dagparser.parsedag(text):
1055 if type == 'n':
1055 if type == 'n':
1056 ui.status('node %s\n' % str(data))
1056 ui.status('node %s\n' % str(data))
1057 id, ps = data
1057 id, ps = data
1058 p1 = ps[0]
1058 p1 = ps[0]
1059 if p1 != at:
1059 if p1 != at:
1060 update(ui, repo, node=str(p1), clean=True)
1060 update(ui, repo, node=str(p1), clean=True)
1061 at = p1
1061 at = p1
1062 if repo.dirstate.branch() != atbranch:
1062 if repo.dirstate.branch() != atbranch:
1063 branch(ui, repo, atbranch, force=True)
1063 branch(ui, repo, atbranch, force=True)
1064 if len(ps) > 1:
1064 if len(ps) > 1:
1065 p2 = ps[1]
1065 p2 = ps[1]
1066 merge(ui, repo, node=p2)
1066 merge(ui, repo, node=p2)
1067
1067
1068 if mergeable_file:
1068 if mergeable_file:
1069 f = open("mf", "rb+")
1069 f = open("mf", "rb+")
1070 try:
1070 try:
1071 lines = f.read().split("\n")
1071 lines = f.read().split("\n")
1072 lines[id * linesperrev] += " r%i" % id
1072 lines[id * linesperrev] += " r%i" % id
1073 f.seek(0)
1073 f.seek(0)
1074 f.write("\n".join(lines))
1074 f.write("\n".join(lines))
1075 finally:
1075 finally:
1076 f.close()
1076 f.close()
1077
1077
1078 if appended_file:
1078 if appended_file:
1079 writefile("af", "r%i\n" % id, "ab")
1079 writefile("af", "r%i\n" % id, "ab")
1080
1080
1081 if overwritten_file:
1081 if overwritten_file:
1082 writefile("of", "r%i\n" % id)
1082 writefile("of", "r%i\n" % id)
1083
1083
1084 if new_file:
1084 if new_file:
1085 writefile("nf%i" % id, "r%i\n" % id)
1085 writefile("nf%i" % id, "r%i\n" % id)
1086
1086
1087 commit(ui, repo, addremove=True, message="r%i" % id, date=(id, 0))
1087 commit(ui, repo, addremove=True, message="r%i" % id, date=(id, 0))
1088 at = id
1088 at = id
1089 elif type == 'l':
1089 elif type == 'l':
1090 id, name = data
1090 id, name = data
1091 ui.status('tag %s\n' % name)
1091 ui.status('tag %s\n' % name)
1092 tag(ui, repo, name, local=True)
1092 tag(ui, repo, name, local=True)
1093 elif type == 'a':
1093 elif type == 'a':
1094 ui.status('branch %s\n' % data)
1094 ui.status('branch %s\n' % data)
1095 atbranch = data
1095 atbranch = data
1096 elif type in 'cC':
1096 elif type in 'cC':
1097 r = util.system(data, cwd=repo.root)
1097 r = util.system(data, cwd=repo.root)
1098 if r:
1098 if r:
1099 desc, r = util.explain_exit(r)
1099 desc, r = util.explain_exit(r)
1100 raise util.Abort(_('%s command %s') % (data, desc))
1100 raise util.Abort(_('%s command %s') % (data, desc))
1101
1101
1102 def debugcommands(ui, cmd='', *args):
1102 def debugcommands(ui, cmd='', *args):
1103 """list all available commands and options"""
1103 """list all available commands and options"""
1104 for cmd, vals in sorted(table.iteritems()):
1104 for cmd, vals in sorted(table.iteritems()):
1105 cmd = cmd.split('|')[0].strip('^')
1105 cmd = cmd.split('|')[0].strip('^')
1106 opts = ', '.join([i[1] for i in vals[1]])
1106 opts = ', '.join([i[1] for i in vals[1]])
1107 ui.write('%s: %s\n' % (cmd, opts))
1107 ui.write('%s: %s\n' % (cmd, opts))
1108
1108
1109 def debugcomplete(ui, cmd='', **opts):
1109 def debugcomplete(ui, cmd='', **opts):
1110 """returns the completion list associated with the given command"""
1110 """returns the completion list associated with the given command"""
1111
1111
1112 if opts.get('options'):
1112 if opts.get('options'):
1113 options = []
1113 options = []
1114 otables = [globalopts]
1114 otables = [globalopts]
1115 if cmd:
1115 if cmd:
1116 aliases, entry = cmdutil.findcmd(cmd, table, False)
1116 aliases, entry = cmdutil.findcmd(cmd, table, False)
1117 otables.append(entry[1])
1117 otables.append(entry[1])
1118 for t in otables:
1118 for t in otables:
1119 for o in t:
1119 for o in t:
1120 if "(DEPRECATED)" in o[3]:
1120 if "(DEPRECATED)" in o[3]:
1121 continue
1121 continue
1122 if o[0]:
1122 if o[0]:
1123 options.append('-%s' % o[0])
1123 options.append('-%s' % o[0])
1124 options.append('--%s' % o[1])
1124 options.append('--%s' % o[1])
1125 ui.write("%s\n" % "\n".join(options))
1125 ui.write("%s\n" % "\n".join(options))
1126 return
1126 return
1127
1127
1128 cmdlist = cmdutil.findpossible(cmd, table)
1128 cmdlist = cmdutil.findpossible(cmd, table)
1129 if ui.verbose:
1129 if ui.verbose:
1130 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
1130 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
1131 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
1131 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
1132
1132
1133 def debugfsinfo(ui, path = "."):
1133 def debugfsinfo(ui, path = "."):
1134 """show information detected about current filesystem"""
1134 """show information detected about current filesystem"""
1135 open('.debugfsinfo', 'w').write('')
1135 open('.debugfsinfo', 'w').write('')
1136 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
1136 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
1137 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
1137 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
1138 ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
1138 ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
1139 and 'yes' or 'no'))
1139 and 'yes' or 'no'))
1140 os.unlink('.debugfsinfo')
1140 os.unlink('.debugfsinfo')
1141
1141
1142 def debugrebuildstate(ui, repo, rev="tip"):
1142 def debugrebuildstate(ui, repo, rev="tip"):
1143 """rebuild the dirstate as it would look like for the given revision"""
1143 """rebuild the dirstate as it would look like for the given revision"""
1144 ctx = cmdutil.revsingle(repo, rev)
1144 ctx = cmdutil.revsingle(repo, rev)
1145 wlock = repo.wlock()
1145 wlock = repo.wlock()
1146 try:
1146 try:
1147 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1147 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1148 finally:
1148 finally:
1149 wlock.release()
1149 wlock.release()
1150
1150
1151 def debugcheckstate(ui, repo):
1151 def debugcheckstate(ui, repo):
1152 """validate the correctness of the current dirstate"""
1152 """validate the correctness of the current dirstate"""
1153 parent1, parent2 = repo.dirstate.parents()
1153 parent1, parent2 = repo.dirstate.parents()
1154 m1 = repo[parent1].manifest()
1154 m1 = repo[parent1].manifest()
1155 m2 = repo[parent2].manifest()
1155 m2 = repo[parent2].manifest()
1156 errors = 0
1156 errors = 0
1157 for f in repo.dirstate:
1157 for f in repo.dirstate:
1158 state = repo.dirstate[f]
1158 state = repo.dirstate[f]
1159 if state in "nr" and f not in m1:
1159 if state in "nr" and f not in m1:
1160 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1160 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1161 errors += 1
1161 errors += 1
1162 if state in "a" and f in m1:
1162 if state in "a" and f in m1:
1163 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1163 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1164 errors += 1
1164 errors += 1
1165 if state in "m" and f not in m1 and f not in m2:
1165 if state in "m" and f not in m1 and f not in m2:
1166 ui.warn(_("%s in state %s, but not in either manifest\n") %
1166 ui.warn(_("%s in state %s, but not in either manifest\n") %
1167 (f, state))
1167 (f, state))
1168 errors += 1
1168 errors += 1
1169 for f in m1:
1169 for f in m1:
1170 state = repo.dirstate[f]
1170 state = repo.dirstate[f]
1171 if state not in "nrm":
1171 if state not in "nrm":
1172 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1172 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1173 errors += 1
1173 errors += 1
1174 if errors:
1174 if errors:
1175 error = _(".hg/dirstate inconsistent with current parent's manifest")
1175 error = _(".hg/dirstate inconsistent with current parent's manifest")
1176 raise util.Abort(error)
1176 raise util.Abort(error)
1177
1177
1178 def showconfig(ui, repo, *values, **opts):
1178 def showconfig(ui, repo, *values, **opts):
1179 """show combined config settings from all hgrc files
1179 """show combined config settings from all hgrc files
1180
1180
1181 With no arguments, print names and values of all config items.
1181 With no arguments, print names and values of all config items.
1182
1182
1183 With one argument of the form section.name, print just the value
1183 With one argument of the form section.name, print just the value
1184 of that config item.
1184 of that config item.
1185
1185
1186 With multiple arguments, print names and values of all config
1186 With multiple arguments, print names and values of all config
1187 items with matching section names.
1187 items with matching section names.
1188
1188
1189 With --debug, the source (filename and line number) is printed
1189 With --debug, the source (filename and line number) is printed
1190 for each config item.
1190 for each config item.
1191
1191
1192 Returns 0 on success.
1192 Returns 0 on success.
1193 """
1193 """
1194
1194
1195 for f in util.rcpath():
1195 for f in util.rcpath():
1196 ui.debug(_('read config from: %s\n') % f)
1196 ui.debug(_('read config from: %s\n') % f)
1197 untrusted = bool(opts.get('untrusted'))
1197 untrusted = bool(opts.get('untrusted'))
1198 if values:
1198 if values:
1199 sections = [v for v in values if '.' not in v]
1199 sections = [v for v in values if '.' not in v]
1200 items = [v for v in values if '.' in v]
1200 items = [v for v in values if '.' in v]
1201 if len(items) > 1 or items and sections:
1201 if len(items) > 1 or items and sections:
1202 raise util.Abort(_('only one config item permitted'))
1202 raise util.Abort(_('only one config item permitted'))
1203 for section, name, value in ui.walkconfig(untrusted=untrusted):
1203 for section, name, value in ui.walkconfig(untrusted=untrusted):
1204 value = str(value).replace('\n', '\\n')
1204 value = str(value).replace('\n', '\\n')
1205 sectname = section + '.' + name
1205 sectname = section + '.' + name
1206 if values:
1206 if values:
1207 for v in values:
1207 for v in values:
1208 if v == section:
1208 if v == section:
1209 ui.debug('%s: ' %
1209 ui.debug('%s: ' %
1210 ui.configsource(section, name, untrusted))
1210 ui.configsource(section, name, untrusted))
1211 ui.write('%s=%s\n' % (sectname, value))
1211 ui.write('%s=%s\n' % (sectname, value))
1212 elif v == sectname:
1212 elif v == sectname:
1213 ui.debug('%s: ' %
1213 ui.debug('%s: ' %
1214 ui.configsource(section, name, untrusted))
1214 ui.configsource(section, name, untrusted))
1215 ui.write(value, '\n')
1215 ui.write(value, '\n')
1216 else:
1216 else:
1217 ui.debug('%s: ' %
1217 ui.debug('%s: ' %
1218 ui.configsource(section, name, untrusted))
1218 ui.configsource(section, name, untrusted))
1219 ui.write('%s=%s\n' % (sectname, value))
1219 ui.write('%s=%s\n' % (sectname, value))
1220
1220
1221 def debugknown(ui, repopath, *ids, **opts):
1222 """test whether node ids are known to a repo
1223
1224 Every ID must be a full-length hex node id string. Returns a list of 0s and 1s
1225 indicating unknown/known.
1226 """
1227 repo = hg.repository(ui, repopath)
1228 if not repo.capable('known'):
1229 raise util.Abort("known() not supported by target repository")
1230 flags = repo.known([bin(s) for s in ids])
1231 ui.write("%s\n" % ("".join([f and "1" or "0" for f in flags])))
1232
1221 def debugpushkey(ui, repopath, namespace, *keyinfo):
1233 def debugpushkey(ui, repopath, namespace, *keyinfo):
1222 '''access the pushkey key/value protocol
1234 '''access the pushkey key/value protocol
1223
1235
1224 With two args, list the keys in the given namespace.
1236 With two args, list the keys in the given namespace.
1225
1237
1226 With five args, set a key to new if it currently is set to old.
1238 With five args, set a key to new if it currently is set to old.
1227 Reports success or failure.
1239 Reports success or failure.
1228 '''
1240 '''
1229
1241
1230 target = hg.repository(ui, repopath)
1242 target = hg.repository(ui, repopath)
1231 if keyinfo:
1243 if keyinfo:
1232 key, old, new = keyinfo
1244 key, old, new = keyinfo
1233 r = target.pushkey(namespace, key, old, new)
1245 r = target.pushkey(namespace, key, old, new)
1234 ui.status(str(r) + '\n')
1246 ui.status(str(r) + '\n')
1235 return not r
1247 return not r
1236 else:
1248 else:
1237 for k, v in target.listkeys(namespace).iteritems():
1249 for k, v in target.listkeys(namespace).iteritems():
1238 ui.write("%s\t%s\n" % (k.encode('string-escape'),
1250 ui.write("%s\t%s\n" % (k.encode('string-escape'),
1239 v.encode('string-escape')))
1251 v.encode('string-escape')))
1240
1252
1241 def debugrevspec(ui, repo, expr):
1253 def debugrevspec(ui, repo, expr):
1242 '''parse and apply a revision specification'''
1254 '''parse and apply a revision specification'''
1243 if ui.verbose:
1255 if ui.verbose:
1244 tree = revset.parse(expr)[0]
1256 tree = revset.parse(expr)[0]
1245 ui.note(tree, "\n")
1257 ui.note(tree, "\n")
1246 func = revset.match(expr)
1258 func = revset.match(expr)
1247 for c in func(repo, range(len(repo))):
1259 for c in func(repo, range(len(repo))):
1248 ui.write("%s\n" % c)
1260 ui.write("%s\n" % c)
1249
1261
1250 def debugsetparents(ui, repo, rev1, rev2=None):
1262 def debugsetparents(ui, repo, rev1, rev2=None):
1251 """manually set the parents of the current working directory
1263 """manually set the parents of the current working directory
1252
1264
1253 This is useful for writing repository conversion tools, but should
1265 This is useful for writing repository conversion tools, but should
1254 be used with care.
1266 be used with care.
1255
1267
1256 Returns 0 on success.
1268 Returns 0 on success.
1257 """
1269 """
1258
1270
1259 r1 = cmdutil.revsingle(repo, rev1).node()
1271 r1 = cmdutil.revsingle(repo, rev1).node()
1260 r2 = cmdutil.revsingle(repo, rev2, 'null').node()
1272 r2 = cmdutil.revsingle(repo, rev2, 'null').node()
1261
1273
1262 wlock = repo.wlock()
1274 wlock = repo.wlock()
1263 try:
1275 try:
1264 repo.dirstate.setparents(r1, r2)
1276 repo.dirstate.setparents(r1, r2)
1265 finally:
1277 finally:
1266 wlock.release()
1278 wlock.release()
1267
1279
1268 def debugstate(ui, repo, nodates=None):
1280 def debugstate(ui, repo, nodates=None):
1269 """show the contents of the current dirstate"""
1281 """show the contents of the current dirstate"""
1270 timestr = ""
1282 timestr = ""
1271 showdate = not nodates
1283 showdate = not nodates
1272 for file_, ent in sorted(repo.dirstate._map.iteritems()):
1284 for file_, ent in sorted(repo.dirstate._map.iteritems()):
1273 if showdate:
1285 if showdate:
1274 if ent[3] == -1:
1286 if ent[3] == -1:
1275 # Pad or slice to locale representation
1287 # Pad or slice to locale representation
1276 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ",
1288 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ",
1277 time.localtime(0)))
1289 time.localtime(0)))
1278 timestr = 'unset'
1290 timestr = 'unset'
1279 timestr = (timestr[:locale_len] +
1291 timestr = (timestr[:locale_len] +
1280 ' ' * (locale_len - len(timestr)))
1292 ' ' * (locale_len - len(timestr)))
1281 else:
1293 else:
1282 timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
1294 timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
1283 time.localtime(ent[3]))
1295 time.localtime(ent[3]))
1284 if ent[1] & 020000:
1296 if ent[1] & 020000:
1285 mode = 'lnk'
1297 mode = 'lnk'
1286 else:
1298 else:
1287 mode = '%3o' % (ent[1] & 0777)
1299 mode = '%3o' % (ent[1] & 0777)
1288 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
1300 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
1289 for f in repo.dirstate.copies():
1301 for f in repo.dirstate.copies():
1290 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
1302 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
1291
1303
1292 def debugsub(ui, repo, rev=None):
1304 def debugsub(ui, repo, rev=None):
1293 ctx = cmdutil.revsingle(repo, rev, None)
1305 ctx = cmdutil.revsingle(repo, rev, None)
1294 for k, v in sorted(ctx.substate.items()):
1306 for k, v in sorted(ctx.substate.items()):
1295 ui.write('path %s\n' % k)
1307 ui.write('path %s\n' % k)
1296 ui.write(' source %s\n' % v[0])
1308 ui.write(' source %s\n' % v[0])
1297 ui.write(' revision %s\n' % v[1])
1309 ui.write(' revision %s\n' % v[1])
1298
1310
1299 def debugdag(ui, repo, file_=None, *revs, **opts):
1311 def debugdag(ui, repo, file_=None, *revs, **opts):
1300 """format the changelog or an index DAG as a concise textual description
1312 """format the changelog or an index DAG as a concise textual description
1301
1313
1302 If you pass a revlog index, the revlog's DAG is emitted. If you list
1314 If you pass a revlog index, the revlog's DAG is emitted. If you list
1303 revision numbers, they get labelled in the output as rN.
1315 revision numbers, they get labelled in the output as rN.
1304
1316
1305 Otherwise, the changelog DAG of the current repo is emitted.
1317 Otherwise, the changelog DAG of the current repo is emitted.
1306 """
1318 """
1307 spaces = opts.get('spaces')
1319 spaces = opts.get('spaces')
1308 dots = opts.get('dots')
1320 dots = opts.get('dots')
1309 if file_:
1321 if file_:
1310 rlog = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
1322 rlog = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
1311 revs = set((int(r) for r in revs))
1323 revs = set((int(r) for r in revs))
1312 def events():
1324 def events():
1313 for r in rlog:
1325 for r in rlog:
1314 yield 'n', (r, list(set(p for p in rlog.parentrevs(r) if p != -1)))
1326 yield 'n', (r, list(set(p for p in rlog.parentrevs(r) if p != -1)))
1315 if r in revs:
1327 if r in revs:
1316 yield 'l', (r, "r%i" % r)
1328 yield 'l', (r, "r%i" % r)
1317 elif repo:
1329 elif repo:
1318 cl = repo.changelog
1330 cl = repo.changelog
1319 tags = opts.get('tags')
1331 tags = opts.get('tags')
1320 branches = opts.get('branches')
1332 branches = opts.get('branches')
1321 if tags:
1333 if tags:
1322 labels = {}
1334 labels = {}
1323 for l, n in repo.tags().items():
1335 for l, n in repo.tags().items():
1324 labels.setdefault(cl.rev(n), []).append(l)
1336 labels.setdefault(cl.rev(n), []).append(l)
1325 def events():
1337 def events():
1326 b = "default"
1338 b = "default"
1327 for r in cl:
1339 for r in cl:
1328 if branches:
1340 if branches:
1329 newb = cl.read(cl.node(r))[5]['branch']
1341 newb = cl.read(cl.node(r))[5]['branch']
1330 if newb != b:
1342 if newb != b:
1331 yield 'a', newb
1343 yield 'a', newb
1332 b = newb
1344 b = newb
1333 yield 'n', (r, list(set(p for p in cl.parentrevs(r) if p != -1)))
1345 yield 'n', (r, list(set(p for p in cl.parentrevs(r) if p != -1)))
1334 if tags:
1346 if tags:
1335 ls = labels.get(r)
1347 ls = labels.get(r)
1336 if ls:
1348 if ls:
1337 for l in ls:
1349 for l in ls:
1338 yield 'l', (r, l)
1350 yield 'l', (r, l)
1339 else:
1351 else:
1340 raise util.Abort(_('need repo for changelog dag'))
1352 raise util.Abort(_('need repo for changelog dag'))
1341
1353
1342 for line in dagparser.dagtextlines(events(),
1354 for line in dagparser.dagtextlines(events(),
1343 addspaces=spaces,
1355 addspaces=spaces,
1344 wraplabels=True,
1356 wraplabels=True,
1345 wrapannotations=True,
1357 wrapannotations=True,
1346 wrapnonlinear=dots,
1358 wrapnonlinear=dots,
1347 usedots=dots,
1359 usedots=dots,
1348 maxlinewidth=70):
1360 maxlinewidth=70):
1349 ui.write(line)
1361 ui.write(line)
1350 ui.write("\n")
1362 ui.write("\n")
1351
1363
1352 def debugdata(ui, repo, file_, rev):
1364 def debugdata(ui, repo, file_, rev):
1353 """dump the contents of a data file revision"""
1365 """dump the contents of a data file revision"""
1354 r = None
1366 r = None
1355 if repo:
1367 if repo:
1356 filelog = repo.file(file_)
1368 filelog = repo.file(file_)
1357 if len(filelog):
1369 if len(filelog):
1358 r = filelog
1370 r = filelog
1359 if not r:
1371 if not r:
1360 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_[:-2] + ".i")
1372 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_[:-2] + ".i")
1361 try:
1373 try:
1362 ui.write(r.revision(r.lookup(rev)))
1374 ui.write(r.revision(r.lookup(rev)))
1363 except KeyError:
1375 except KeyError:
1364 raise util.Abort(_('invalid revision identifier %s') % rev)
1376 raise util.Abort(_('invalid revision identifier %s') % rev)
1365
1377
1366 def debugdate(ui, date, range=None, **opts):
1378 def debugdate(ui, date, range=None, **opts):
1367 """parse and display a date"""
1379 """parse and display a date"""
1368 if opts["extended"]:
1380 if opts["extended"]:
1369 d = util.parsedate(date, util.extendeddateformats)
1381 d = util.parsedate(date, util.extendeddateformats)
1370 else:
1382 else:
1371 d = util.parsedate(date)
1383 d = util.parsedate(date)
1372 ui.write("internal: %s %s\n" % d)
1384 ui.write("internal: %s %s\n" % d)
1373 ui.write("standard: %s\n" % util.datestr(d))
1385 ui.write("standard: %s\n" % util.datestr(d))
1374 if range:
1386 if range:
1375 m = util.matchdate(range)
1387 m = util.matchdate(range)
1376 ui.write("match: %s\n" % m(d[0]))
1388 ui.write("match: %s\n" % m(d[0]))
1377
1389
1378 def debugignore(ui, repo, *values, **opts):
1390 def debugignore(ui, repo, *values, **opts):
1379 """display the combined ignore pattern"""
1391 """display the combined ignore pattern"""
1380 ignore = repo.dirstate._ignore
1392 ignore = repo.dirstate._ignore
1381 if hasattr(ignore, 'includepat'):
1393 if hasattr(ignore, 'includepat'):
1382 ui.write("%s\n" % ignore.includepat)
1394 ui.write("%s\n" % ignore.includepat)
1383 else:
1395 else:
1384 raise util.Abort(_("no ignore patterns found"))
1396 raise util.Abort(_("no ignore patterns found"))
1385
1397
1386 def debugindex(ui, repo, file_, **opts):
1398 def debugindex(ui, repo, file_, **opts):
1387 """dump the contents of an index file"""
1399 """dump the contents of an index file"""
1388 r = None
1400 r = None
1389 if repo:
1401 if repo:
1390 filelog = repo.file(file_)
1402 filelog = repo.file(file_)
1391 if len(filelog):
1403 if len(filelog):
1392 r = filelog
1404 r = filelog
1393
1405
1394 format = opts.get('format', 0)
1406 format = opts.get('format', 0)
1395 if format not in (0, 1):
1407 if format not in (0, 1):
1396 raise util.Abort(_("unknown format %d") % format)
1408 raise util.Abort(_("unknown format %d") % format)
1397
1409
1398 if not r:
1410 if not r:
1399 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
1411 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
1400
1412
1401 if format == 0:
1413 if format == 0:
1402 ui.write(" rev offset length base linkrev"
1414 ui.write(" rev offset length base linkrev"
1403 " nodeid p1 p2\n")
1415 " nodeid p1 p2\n")
1404 elif format == 1:
1416 elif format == 1:
1405 ui.write(" rev flag offset length"
1417 ui.write(" rev flag offset length"
1406 " size base link p1 p2 nodeid\n")
1418 " size base link p1 p2 nodeid\n")
1407
1419
1408 for i in r:
1420 for i in r:
1409 node = r.node(i)
1421 node = r.node(i)
1410 if format == 0:
1422 if format == 0:
1411 try:
1423 try:
1412 pp = r.parents(node)
1424 pp = r.parents(node)
1413 except:
1425 except:
1414 pp = [nullid, nullid]
1426 pp = [nullid, nullid]
1415 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
1427 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
1416 i, r.start(i), r.length(i), r.base(i), r.linkrev(i),
1428 i, r.start(i), r.length(i), r.base(i), r.linkrev(i),
1417 short(node), short(pp[0]), short(pp[1])))
1429 short(node), short(pp[0]), short(pp[1])))
1418 elif format == 1:
1430 elif format == 1:
1419 pr = r.parentrevs(i)
1431 pr = r.parentrevs(i)
1420 ui.write("% 6d %04x % 8d % 8d % 8d % 6d % 6d % 6d % 6d %s\n" % (
1432 ui.write("% 6d %04x % 8d % 8d % 8d % 6d % 6d % 6d % 6d %s\n" % (
1421 i, r.flags(i), r.start(i), r.length(i), r.rawsize(i),
1433 i, r.flags(i), r.start(i), r.length(i), r.rawsize(i),
1422 r.base(i), r.linkrev(i), pr[0], pr[1], short(node)))
1434 r.base(i), r.linkrev(i), pr[0], pr[1], short(node)))
1423
1435
1424 def debugindexdot(ui, repo, file_):
1436 def debugindexdot(ui, repo, file_):
1425 """dump an index DAG as a graphviz dot file"""
1437 """dump an index DAG as a graphviz dot file"""
1426 r = None
1438 r = None
1427 if repo:
1439 if repo:
1428 filelog = repo.file(file_)
1440 filelog = repo.file(file_)
1429 if len(filelog):
1441 if len(filelog):
1430 r = filelog
1442 r = filelog
1431 if not r:
1443 if not r:
1432 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
1444 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
1433 ui.write("digraph G {\n")
1445 ui.write("digraph G {\n")
1434 for i in r:
1446 for i in r:
1435 node = r.node(i)
1447 node = r.node(i)
1436 pp = r.parents(node)
1448 pp = r.parents(node)
1437 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
1449 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
1438 if pp[1] != nullid:
1450 if pp[1] != nullid:
1439 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
1451 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
1440 ui.write("}\n")
1452 ui.write("}\n")
1441
1453
1442 def debuginstall(ui):
1454 def debuginstall(ui):
1443 '''test Mercurial installation
1455 '''test Mercurial installation
1444
1456
1445 Returns 0 on success.
1457 Returns 0 on success.
1446 '''
1458 '''
1447
1459
1448 def writetemp(contents):
1460 def writetemp(contents):
1449 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
1461 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
1450 f = os.fdopen(fd, "wb")
1462 f = os.fdopen(fd, "wb")
1451 f.write(contents)
1463 f.write(contents)
1452 f.close()
1464 f.close()
1453 return name
1465 return name
1454
1466
1455 problems = 0
1467 problems = 0
1456
1468
1457 # encoding
1469 # encoding
1458 ui.status(_("Checking encoding (%s)...\n") % encoding.encoding)
1470 ui.status(_("Checking encoding (%s)...\n") % encoding.encoding)
1459 try:
1471 try:
1460 encoding.fromlocal("test")
1472 encoding.fromlocal("test")
1461 except util.Abort, inst:
1473 except util.Abort, inst:
1462 ui.write(" %s\n" % inst)
1474 ui.write(" %s\n" % inst)
1463 ui.write(_(" (check that your locale is properly set)\n"))
1475 ui.write(_(" (check that your locale is properly set)\n"))
1464 problems += 1
1476 problems += 1
1465
1477
1466 # compiled modules
1478 # compiled modules
1467 ui.status(_("Checking installed modules (%s)...\n")
1479 ui.status(_("Checking installed modules (%s)...\n")
1468 % os.path.dirname(__file__))
1480 % os.path.dirname(__file__))
1469 try:
1481 try:
1470 import bdiff, mpatch, base85, osutil
1482 import bdiff, mpatch, base85, osutil
1471 except Exception, inst:
1483 except Exception, inst:
1472 ui.write(" %s\n" % inst)
1484 ui.write(" %s\n" % inst)
1473 ui.write(_(" One or more extensions could not be found"))
1485 ui.write(_(" One or more extensions could not be found"))
1474 ui.write(_(" (check that you compiled the extensions)\n"))
1486 ui.write(_(" (check that you compiled the extensions)\n"))
1475 problems += 1
1487 problems += 1
1476
1488
1477 # templates
1489 # templates
1478 ui.status(_("Checking templates...\n"))
1490 ui.status(_("Checking templates...\n"))
1479 try:
1491 try:
1480 import templater
1492 import templater
1481 templater.templater(templater.templatepath("map-cmdline.default"))
1493 templater.templater(templater.templatepath("map-cmdline.default"))
1482 except Exception, inst:
1494 except Exception, inst:
1483 ui.write(" %s\n" % inst)
1495 ui.write(" %s\n" % inst)
1484 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
1496 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
1485 problems += 1
1497 problems += 1
1486
1498
1487 # patch
1499 # patch
1488 ui.status(_("Checking patch...\n"))
1500 ui.status(_("Checking patch...\n"))
1489 patchproblems = 0
1501 patchproblems = 0
1490 a = "1\n2\n3\n4\n"
1502 a = "1\n2\n3\n4\n"
1491 b = "1\n2\n3\ninsert\n4\n"
1503 b = "1\n2\n3\ninsert\n4\n"
1492 fa = writetemp(a)
1504 fa = writetemp(a)
1493 d = mdiff.unidiff(a, None, b, None, os.path.basename(fa),
1505 d = mdiff.unidiff(a, None, b, None, os.path.basename(fa),
1494 os.path.basename(fa))
1506 os.path.basename(fa))
1495 fd = writetemp(d)
1507 fd = writetemp(d)
1496
1508
1497 files = {}
1509 files = {}
1498 try:
1510 try:
1499 patch.patch(fd, ui, cwd=os.path.dirname(fa), files=files)
1511 patch.patch(fd, ui, cwd=os.path.dirname(fa), files=files)
1500 except util.Abort, e:
1512 except util.Abort, e:
1501 ui.write(_(" patch call failed:\n"))
1513 ui.write(_(" patch call failed:\n"))
1502 ui.write(" " + str(e) + "\n")
1514 ui.write(" " + str(e) + "\n")
1503 patchproblems += 1
1515 patchproblems += 1
1504 else:
1516 else:
1505 if list(files) != [os.path.basename(fa)]:
1517 if list(files) != [os.path.basename(fa)]:
1506 ui.write(_(" unexpected patch output!\n"))
1518 ui.write(_(" unexpected patch output!\n"))
1507 patchproblems += 1
1519 patchproblems += 1
1508 a = open(fa).read()
1520 a = open(fa).read()
1509 if a != b:
1521 if a != b:
1510 ui.write(_(" patch test failed!\n"))
1522 ui.write(_(" patch test failed!\n"))
1511 patchproblems += 1
1523 patchproblems += 1
1512
1524
1513 if patchproblems:
1525 if patchproblems:
1514 if ui.config('ui', 'patch'):
1526 if ui.config('ui', 'patch'):
1515 ui.write(_(" (Current patch tool may be incompatible with patch,"
1527 ui.write(_(" (Current patch tool may be incompatible with patch,"
1516 " or misconfigured. Please check your configuration"
1528 " or misconfigured. Please check your configuration"
1517 " file)\n"))
1529 " file)\n"))
1518 else:
1530 else:
1519 ui.write(_(" Internal patcher failure, please report this error"
1531 ui.write(_(" Internal patcher failure, please report this error"
1520 " to http://mercurial.selenic.com/wiki/BugTracker\n"))
1532 " to http://mercurial.selenic.com/wiki/BugTracker\n"))
1521 problems += patchproblems
1533 problems += patchproblems
1522
1534
1523 os.unlink(fa)
1535 os.unlink(fa)
1524 os.unlink(fd)
1536 os.unlink(fd)
1525
1537
1526 # editor
1538 # editor
1527 ui.status(_("Checking commit editor...\n"))
1539 ui.status(_("Checking commit editor...\n"))
1528 editor = ui.geteditor()
1540 editor = ui.geteditor()
1529 cmdpath = util.find_exe(editor) or util.find_exe(editor.split()[0])
1541 cmdpath = util.find_exe(editor) or util.find_exe(editor.split()[0])
1530 if not cmdpath:
1542 if not cmdpath:
1531 if editor == 'vi':
1543 if editor == 'vi':
1532 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
1544 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
1533 ui.write(_(" (specify a commit editor in your configuration"
1545 ui.write(_(" (specify a commit editor in your configuration"
1534 " file)\n"))
1546 " file)\n"))
1535 else:
1547 else:
1536 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
1548 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
1537 ui.write(_(" (specify a commit editor in your configuration"
1549 ui.write(_(" (specify a commit editor in your configuration"
1538 " file)\n"))
1550 " file)\n"))
1539 problems += 1
1551 problems += 1
1540
1552
1541 # check username
1553 # check username
1542 ui.status(_("Checking username...\n"))
1554 ui.status(_("Checking username...\n"))
1543 try:
1555 try:
1544 ui.username()
1556 ui.username()
1545 except util.Abort, e:
1557 except util.Abort, e:
1546 ui.write(" %s\n" % e)
1558 ui.write(" %s\n" % e)
1547 ui.write(_(" (specify a username in your configuration file)\n"))
1559 ui.write(_(" (specify a username in your configuration file)\n"))
1548 problems += 1
1560 problems += 1
1549
1561
1550 if not problems:
1562 if not problems:
1551 ui.status(_("No problems detected\n"))
1563 ui.status(_("No problems detected\n"))
1552 else:
1564 else:
1553 ui.write(_("%s problems detected,"
1565 ui.write(_("%s problems detected,"
1554 " please check your install!\n") % problems)
1566 " please check your install!\n") % problems)
1555
1567
1556 return problems
1568 return problems
1557
1569
1558 def debugrename(ui, repo, file1, *pats, **opts):
1570 def debugrename(ui, repo, file1, *pats, **opts):
1559 """dump rename information"""
1571 """dump rename information"""
1560
1572
1561 ctx = cmdutil.revsingle(repo, opts.get('rev'))
1573 ctx = cmdutil.revsingle(repo, opts.get('rev'))
1562 m = cmdutil.match(repo, (file1,) + pats, opts)
1574 m = cmdutil.match(repo, (file1,) + pats, opts)
1563 for abs in ctx.walk(m):
1575 for abs in ctx.walk(m):
1564 fctx = ctx[abs]
1576 fctx = ctx[abs]
1565 o = fctx.filelog().renamed(fctx.filenode())
1577 o = fctx.filelog().renamed(fctx.filenode())
1566 rel = m.rel(abs)
1578 rel = m.rel(abs)
1567 if o:
1579 if o:
1568 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
1580 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
1569 else:
1581 else:
1570 ui.write(_("%s not renamed\n") % rel)
1582 ui.write(_("%s not renamed\n") % rel)
1571
1583
1572 def debugwalk(ui, repo, *pats, **opts):
1584 def debugwalk(ui, repo, *pats, **opts):
1573 """show how files match on given patterns"""
1585 """show how files match on given patterns"""
1574 m = cmdutil.match(repo, pats, opts)
1586 m = cmdutil.match(repo, pats, opts)
1575 items = list(repo.walk(m))
1587 items = list(repo.walk(m))
1576 if not items:
1588 if not items:
1577 return
1589 return
1578 fmt = 'f %%-%ds %%-%ds %%s' % (
1590 fmt = 'f %%-%ds %%-%ds %%s' % (
1579 max([len(abs) for abs in items]),
1591 max([len(abs) for abs in items]),
1580 max([len(m.rel(abs)) for abs in items]))
1592 max([len(m.rel(abs)) for abs in items]))
1581 for abs in items:
1593 for abs in items:
1582 line = fmt % (abs, m.rel(abs), m.exact(abs) and 'exact' or '')
1594 line = fmt % (abs, m.rel(abs), m.exact(abs) and 'exact' or '')
1583 ui.write("%s\n" % line.rstrip())
1595 ui.write("%s\n" % line.rstrip())
1584
1596
1585 def debugwireargs(ui, repopath, *vals, **opts):
1597 def debugwireargs(ui, repopath, *vals, **opts):
1586 repo = hg.repository(hg.remoteui(ui, opts), repopath)
1598 repo = hg.repository(hg.remoteui(ui, opts), repopath)
1587 for opt in remoteopts:
1599 for opt in remoteopts:
1588 del opts[opt[1]]
1600 del opts[opt[1]]
1589 args = {}
1601 args = {}
1590 for k, v in opts.iteritems():
1602 for k, v in opts.iteritems():
1591 if v:
1603 if v:
1592 args[k] = v
1604 args[k] = v
1593 # run twice to check that we don't mess up the stream for the next command
1605 # run twice to check that we don't mess up the stream for the next command
1594 res1 = repo.debugwireargs(*vals, **args)
1606 res1 = repo.debugwireargs(*vals, **args)
1595 res2 = repo.debugwireargs(*vals, **args)
1607 res2 = repo.debugwireargs(*vals, **args)
1596 ui.write("%s\n" % res1)
1608 ui.write("%s\n" % res1)
1597 if res1 != res2:
1609 if res1 != res2:
1598 ui.warn("%s\n" % res2)
1610 ui.warn("%s\n" % res2)
1599
1611
1600 def diff(ui, repo, *pats, **opts):
1612 def diff(ui, repo, *pats, **opts):
1601 """diff repository (or selected files)
1613 """diff repository (or selected files)
1602
1614
1603 Show differences between revisions for the specified files.
1615 Show differences between revisions for the specified files.
1604
1616
1605 Differences between files are shown using the unified diff format.
1617 Differences between files are shown using the unified diff format.
1606
1618
1607 .. note::
1619 .. note::
1608 diff may generate unexpected results for merges, as it will
1620 diff may generate unexpected results for merges, as it will
1609 default to comparing against the working directory's first
1621 default to comparing against the working directory's first
1610 parent changeset if no revisions are specified.
1622 parent changeset if no revisions are specified.
1611
1623
1612 When two revision arguments are given, then changes are shown
1624 When two revision arguments are given, then changes are shown
1613 between those revisions. If only one revision is specified then
1625 between those revisions. If only one revision is specified then
1614 that revision is compared to the working directory, and, when no
1626 that revision is compared to the working directory, and, when no
1615 revisions are specified, the working directory files are compared
1627 revisions are specified, the working directory files are compared
1616 to its parent.
1628 to its parent.
1617
1629
1618 Alternatively you can specify -c/--change with a revision to see
1630 Alternatively you can specify -c/--change with a revision to see
1619 the changes in that changeset relative to its first parent.
1631 the changes in that changeset relative to its first parent.
1620
1632
1621 Without the -a/--text option, diff will avoid generating diffs of
1633 Without the -a/--text option, diff will avoid generating diffs of
1622 files it detects as binary. With -a, diff will generate a diff
1634 files it detects as binary. With -a, diff will generate a diff
1623 anyway, probably with undesirable results.
1635 anyway, probably with undesirable results.
1624
1636
1625 Use the -g/--git option to generate diffs in the git extended diff
1637 Use the -g/--git option to generate diffs in the git extended diff
1626 format. For more information, read :hg:`help diffs`.
1638 format. For more information, read :hg:`help diffs`.
1627
1639
1628 Returns 0 on success.
1640 Returns 0 on success.
1629 """
1641 """
1630
1642
1631 revs = opts.get('rev')
1643 revs = opts.get('rev')
1632 change = opts.get('change')
1644 change = opts.get('change')
1633 stat = opts.get('stat')
1645 stat = opts.get('stat')
1634 reverse = opts.get('reverse')
1646 reverse = opts.get('reverse')
1635
1647
1636 if revs and change:
1648 if revs and change:
1637 msg = _('cannot specify --rev and --change at the same time')
1649 msg = _('cannot specify --rev and --change at the same time')
1638 raise util.Abort(msg)
1650 raise util.Abort(msg)
1639 elif change:
1651 elif change:
1640 node2 = cmdutil.revsingle(repo, change, None).node()
1652 node2 = cmdutil.revsingle(repo, change, None).node()
1641 node1 = repo[node2].parents()[0].node()
1653 node1 = repo[node2].parents()[0].node()
1642 else:
1654 else:
1643 node1, node2 = cmdutil.revpair(repo, revs)
1655 node1, node2 = cmdutil.revpair(repo, revs)
1644
1656
1645 if reverse:
1657 if reverse:
1646 node1, node2 = node2, node1
1658 node1, node2 = node2, node1
1647
1659
1648 diffopts = patch.diffopts(ui, opts)
1660 diffopts = patch.diffopts(ui, opts)
1649 m = cmdutil.match(repo, pats, opts)
1661 m = cmdutil.match(repo, pats, opts)
1650 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
1662 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
1651 listsubrepos=opts.get('subrepos'))
1663 listsubrepos=opts.get('subrepos'))
1652
1664
1653 def export(ui, repo, *changesets, **opts):
1665 def export(ui, repo, *changesets, **opts):
1654 """dump the header and diffs for one or more changesets
1666 """dump the header and diffs for one or more changesets
1655
1667
1656 Print the changeset header and diffs for one or more revisions.
1668 Print the changeset header and diffs for one or more revisions.
1657
1669
1658 The information shown in the changeset header is: author, date,
1670 The information shown in the changeset header is: author, date,
1659 branch name (if non-default), changeset hash, parent(s) and commit
1671 branch name (if non-default), changeset hash, parent(s) and commit
1660 comment.
1672 comment.
1661
1673
1662 .. note::
1674 .. note::
1663 export may generate unexpected diff output for merge
1675 export may generate unexpected diff output for merge
1664 changesets, as it will compare the merge changeset against its
1676 changesets, as it will compare the merge changeset against its
1665 first parent only.
1677 first parent only.
1666
1678
1667 Output may be to a file, in which case the name of the file is
1679 Output may be to a file, in which case the name of the file is
1668 given using a format string. The formatting rules are as follows:
1680 given using a format string. The formatting rules are as follows:
1669
1681
1670 :``%%``: literal "%" character
1682 :``%%``: literal "%" character
1671 :``%H``: changeset hash (40 hexadecimal digits)
1683 :``%H``: changeset hash (40 hexadecimal digits)
1672 :``%N``: number of patches being generated
1684 :``%N``: number of patches being generated
1673 :``%R``: changeset revision number
1685 :``%R``: changeset revision number
1674 :``%b``: basename of the exporting repository
1686 :``%b``: basename of the exporting repository
1675 :``%h``: short-form changeset hash (12 hexadecimal digits)
1687 :``%h``: short-form changeset hash (12 hexadecimal digits)
1676 :``%n``: zero-padded sequence number, starting at 1
1688 :``%n``: zero-padded sequence number, starting at 1
1677 :``%r``: zero-padded changeset revision number
1689 :``%r``: zero-padded changeset revision number
1678
1690
1679 Without the -a/--text option, export will avoid generating diffs
1691 Without the -a/--text option, export will avoid generating diffs
1680 of files it detects as binary. With -a, export will generate a
1692 of files it detects as binary. With -a, export will generate a
1681 diff anyway, probably with undesirable results.
1693 diff anyway, probably with undesirable results.
1682
1694
1683 Use the -g/--git option to generate diffs in the git extended diff
1695 Use the -g/--git option to generate diffs in the git extended diff
1684 format. See :hg:`help diffs` for more information.
1696 format. See :hg:`help diffs` for more information.
1685
1697
1686 With the --switch-parent option, the diff will be against the
1698 With the --switch-parent option, the diff will be against the
1687 second parent. It can be useful to review a merge.
1699 second parent. It can be useful to review a merge.
1688
1700
1689 Returns 0 on success.
1701 Returns 0 on success.
1690 """
1702 """
1691 changesets += tuple(opts.get('rev', []))
1703 changesets += tuple(opts.get('rev', []))
1692 if not changesets:
1704 if not changesets:
1693 raise util.Abort(_("export requires at least one changeset"))
1705 raise util.Abort(_("export requires at least one changeset"))
1694 revs = cmdutil.revrange(repo, changesets)
1706 revs = cmdutil.revrange(repo, changesets)
1695 if len(revs) > 1:
1707 if len(revs) > 1:
1696 ui.note(_('exporting patches:\n'))
1708 ui.note(_('exporting patches:\n'))
1697 else:
1709 else:
1698 ui.note(_('exporting patch:\n'))
1710 ui.note(_('exporting patch:\n'))
1699 cmdutil.export(repo, revs, template=opts.get('output'),
1711 cmdutil.export(repo, revs, template=opts.get('output'),
1700 switch_parent=opts.get('switch_parent'),
1712 switch_parent=opts.get('switch_parent'),
1701 opts=patch.diffopts(ui, opts))
1713 opts=patch.diffopts(ui, opts))
1702
1714
1703 def forget(ui, repo, *pats, **opts):
1715 def forget(ui, repo, *pats, **opts):
1704 """forget the specified files on the next commit
1716 """forget the specified files on the next commit
1705
1717
1706 Mark the specified files so they will no longer be tracked
1718 Mark the specified files so they will no longer be tracked
1707 after the next commit.
1719 after the next commit.
1708
1720
1709 This only removes files from the current branch, not from the
1721 This only removes files from the current branch, not from the
1710 entire project history, and it does not delete them from the
1722 entire project history, and it does not delete them from the
1711 working directory.
1723 working directory.
1712
1724
1713 To undo a forget before the next commit, see :hg:`add`.
1725 To undo a forget before the next commit, see :hg:`add`.
1714
1726
1715 Returns 0 on success.
1727 Returns 0 on success.
1716 """
1728 """
1717
1729
1718 if not pats:
1730 if not pats:
1719 raise util.Abort(_('no files specified'))
1731 raise util.Abort(_('no files specified'))
1720
1732
1721 m = cmdutil.match(repo, pats, opts)
1733 m = cmdutil.match(repo, pats, opts)
1722 s = repo.status(match=m, clean=True)
1734 s = repo.status(match=m, clean=True)
1723 forget = sorted(s[0] + s[1] + s[3] + s[6])
1735 forget = sorted(s[0] + s[1] + s[3] + s[6])
1724 errs = 0
1736 errs = 0
1725
1737
1726 for f in m.files():
1738 for f in m.files():
1727 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
1739 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
1728 ui.warn(_('not removing %s: file is already untracked\n')
1740 ui.warn(_('not removing %s: file is already untracked\n')
1729 % m.rel(f))
1741 % m.rel(f))
1730 errs = 1
1742 errs = 1
1731
1743
1732 for f in forget:
1744 for f in forget:
1733 if ui.verbose or not m.exact(f):
1745 if ui.verbose or not m.exact(f):
1734 ui.status(_('removing %s\n') % m.rel(f))
1746 ui.status(_('removing %s\n') % m.rel(f))
1735
1747
1736 repo[None].remove(forget, unlink=False)
1748 repo[None].remove(forget, unlink=False)
1737 return errs
1749 return errs
1738
1750
1739 def grep(ui, repo, pattern, *pats, **opts):
1751 def grep(ui, repo, pattern, *pats, **opts):
1740 """search for a pattern in specified files and revisions
1752 """search for a pattern in specified files and revisions
1741
1753
1742 Search revisions of files for a regular expression.
1754 Search revisions of files for a regular expression.
1743
1755
1744 This command behaves differently than Unix grep. It only accepts
1756 This command behaves differently than Unix grep. It only accepts
1745 Python/Perl regexps. It searches repository history, not the
1757 Python/Perl regexps. It searches repository history, not the
1746 working directory. It always prints the revision number in which a
1758 working directory. It always prints the revision number in which a
1747 match appears.
1759 match appears.
1748
1760
1749 By default, grep only prints output for the first revision of a
1761 By default, grep only prints output for the first revision of a
1750 file in which it finds a match. To get it to print every revision
1762 file in which it finds a match. To get it to print every revision
1751 that contains a change in match status ("-" for a match that
1763 that contains a change in match status ("-" for a match that
1752 becomes a non-match, or "+" for a non-match that becomes a match),
1764 becomes a non-match, or "+" for a non-match that becomes a match),
1753 use the --all flag.
1765 use the --all flag.
1754
1766
1755 Returns 0 if a match is found, 1 otherwise.
1767 Returns 0 if a match is found, 1 otherwise.
1756 """
1768 """
1757 reflags = 0
1769 reflags = 0
1758 if opts.get('ignore_case'):
1770 if opts.get('ignore_case'):
1759 reflags |= re.I
1771 reflags |= re.I
1760 try:
1772 try:
1761 regexp = re.compile(pattern, reflags)
1773 regexp = re.compile(pattern, reflags)
1762 except re.error, inst:
1774 except re.error, inst:
1763 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
1775 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
1764 return 1
1776 return 1
1765 sep, eol = ':', '\n'
1777 sep, eol = ':', '\n'
1766 if opts.get('print0'):
1778 if opts.get('print0'):
1767 sep = eol = '\0'
1779 sep = eol = '\0'
1768
1780
1769 getfile = util.lrucachefunc(repo.file)
1781 getfile = util.lrucachefunc(repo.file)
1770
1782
1771 def matchlines(body):
1783 def matchlines(body):
1772 begin = 0
1784 begin = 0
1773 linenum = 0
1785 linenum = 0
1774 while True:
1786 while True:
1775 match = regexp.search(body, begin)
1787 match = regexp.search(body, begin)
1776 if not match:
1788 if not match:
1777 break
1789 break
1778 mstart, mend = match.span()
1790 mstart, mend = match.span()
1779 linenum += body.count('\n', begin, mstart) + 1
1791 linenum += body.count('\n', begin, mstart) + 1
1780 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1792 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1781 begin = body.find('\n', mend) + 1 or len(body)
1793 begin = body.find('\n', mend) + 1 or len(body)
1782 lend = begin - 1
1794 lend = begin - 1
1783 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1795 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1784
1796
1785 class linestate(object):
1797 class linestate(object):
1786 def __init__(self, line, linenum, colstart, colend):
1798 def __init__(self, line, linenum, colstart, colend):
1787 self.line = line
1799 self.line = line
1788 self.linenum = linenum
1800 self.linenum = linenum
1789 self.colstart = colstart
1801 self.colstart = colstart
1790 self.colend = colend
1802 self.colend = colend
1791
1803
1792 def __hash__(self):
1804 def __hash__(self):
1793 return hash((self.linenum, self.line))
1805 return hash((self.linenum, self.line))
1794
1806
1795 def __eq__(self, other):
1807 def __eq__(self, other):
1796 return self.line == other.line
1808 return self.line == other.line
1797
1809
1798 matches = {}
1810 matches = {}
1799 copies = {}
1811 copies = {}
1800 def grepbody(fn, rev, body):
1812 def grepbody(fn, rev, body):
1801 matches[rev].setdefault(fn, [])
1813 matches[rev].setdefault(fn, [])
1802 m = matches[rev][fn]
1814 m = matches[rev][fn]
1803 for lnum, cstart, cend, line in matchlines(body):
1815 for lnum, cstart, cend, line in matchlines(body):
1804 s = linestate(line, lnum, cstart, cend)
1816 s = linestate(line, lnum, cstart, cend)
1805 m.append(s)
1817 m.append(s)
1806
1818
1807 def difflinestates(a, b):
1819 def difflinestates(a, b):
1808 sm = difflib.SequenceMatcher(None, a, b)
1820 sm = difflib.SequenceMatcher(None, a, b)
1809 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1821 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1810 if tag == 'insert':
1822 if tag == 'insert':
1811 for i in xrange(blo, bhi):
1823 for i in xrange(blo, bhi):
1812 yield ('+', b[i])
1824 yield ('+', b[i])
1813 elif tag == 'delete':
1825 elif tag == 'delete':
1814 for i in xrange(alo, ahi):
1826 for i in xrange(alo, ahi):
1815 yield ('-', a[i])
1827 yield ('-', a[i])
1816 elif tag == 'replace':
1828 elif tag == 'replace':
1817 for i in xrange(alo, ahi):
1829 for i in xrange(alo, ahi):
1818 yield ('-', a[i])
1830 yield ('-', a[i])
1819 for i in xrange(blo, bhi):
1831 for i in xrange(blo, bhi):
1820 yield ('+', b[i])
1832 yield ('+', b[i])
1821
1833
1822 def display(fn, ctx, pstates, states):
1834 def display(fn, ctx, pstates, states):
1823 rev = ctx.rev()
1835 rev = ctx.rev()
1824 datefunc = ui.quiet and util.shortdate or util.datestr
1836 datefunc = ui.quiet and util.shortdate or util.datestr
1825 found = False
1837 found = False
1826 filerevmatches = {}
1838 filerevmatches = {}
1827 if opts.get('all'):
1839 if opts.get('all'):
1828 iter = difflinestates(pstates, states)
1840 iter = difflinestates(pstates, states)
1829 else:
1841 else:
1830 iter = [('', l) for l in states]
1842 iter = [('', l) for l in states]
1831 for change, l in iter:
1843 for change, l in iter:
1832 cols = [fn, str(rev)]
1844 cols = [fn, str(rev)]
1833 before, match, after = None, None, None
1845 before, match, after = None, None, None
1834 if opts.get('line_number'):
1846 if opts.get('line_number'):
1835 cols.append(str(l.linenum))
1847 cols.append(str(l.linenum))
1836 if opts.get('all'):
1848 if opts.get('all'):
1837 cols.append(change)
1849 cols.append(change)
1838 if opts.get('user'):
1850 if opts.get('user'):
1839 cols.append(ui.shortuser(ctx.user()))
1851 cols.append(ui.shortuser(ctx.user()))
1840 if opts.get('date'):
1852 if opts.get('date'):
1841 cols.append(datefunc(ctx.date()))
1853 cols.append(datefunc(ctx.date()))
1842 if opts.get('files_with_matches'):
1854 if opts.get('files_with_matches'):
1843 c = (fn, rev)
1855 c = (fn, rev)
1844 if c in filerevmatches:
1856 if c in filerevmatches:
1845 continue
1857 continue
1846 filerevmatches[c] = 1
1858 filerevmatches[c] = 1
1847 else:
1859 else:
1848 before = l.line[:l.colstart]
1860 before = l.line[:l.colstart]
1849 match = l.line[l.colstart:l.colend]
1861 match = l.line[l.colstart:l.colend]
1850 after = l.line[l.colend:]
1862 after = l.line[l.colend:]
1851 ui.write(sep.join(cols))
1863 ui.write(sep.join(cols))
1852 if before is not None:
1864 if before is not None:
1853 ui.write(sep + before)
1865 ui.write(sep + before)
1854 ui.write(match, label='grep.match')
1866 ui.write(match, label='grep.match')
1855 ui.write(after)
1867 ui.write(after)
1856 ui.write(eol)
1868 ui.write(eol)
1857 found = True
1869 found = True
1858 return found
1870 return found
1859
1871
1860 skip = {}
1872 skip = {}
1861 revfiles = {}
1873 revfiles = {}
1862 matchfn = cmdutil.match(repo, pats, opts)
1874 matchfn = cmdutil.match(repo, pats, opts)
1863 found = False
1875 found = False
1864 follow = opts.get('follow')
1876 follow = opts.get('follow')
1865
1877
1866 def prep(ctx, fns):
1878 def prep(ctx, fns):
1867 rev = ctx.rev()
1879 rev = ctx.rev()
1868 pctx = ctx.parents()[0]
1880 pctx = ctx.parents()[0]
1869 parent = pctx.rev()
1881 parent = pctx.rev()
1870 matches.setdefault(rev, {})
1882 matches.setdefault(rev, {})
1871 matches.setdefault(parent, {})
1883 matches.setdefault(parent, {})
1872 files = revfiles.setdefault(rev, [])
1884 files = revfiles.setdefault(rev, [])
1873 for fn in fns:
1885 for fn in fns:
1874 flog = getfile(fn)
1886 flog = getfile(fn)
1875 try:
1887 try:
1876 fnode = ctx.filenode(fn)
1888 fnode = ctx.filenode(fn)
1877 except error.LookupError:
1889 except error.LookupError:
1878 continue
1890 continue
1879
1891
1880 copied = flog.renamed(fnode)
1892 copied = flog.renamed(fnode)
1881 copy = follow and copied and copied[0]
1893 copy = follow and copied and copied[0]
1882 if copy:
1894 if copy:
1883 copies.setdefault(rev, {})[fn] = copy
1895 copies.setdefault(rev, {})[fn] = copy
1884 if fn in skip:
1896 if fn in skip:
1885 if copy:
1897 if copy:
1886 skip[copy] = True
1898 skip[copy] = True
1887 continue
1899 continue
1888 files.append(fn)
1900 files.append(fn)
1889
1901
1890 if fn not in matches[rev]:
1902 if fn not in matches[rev]:
1891 grepbody(fn, rev, flog.read(fnode))
1903 grepbody(fn, rev, flog.read(fnode))
1892
1904
1893 pfn = copy or fn
1905 pfn = copy or fn
1894 if pfn not in matches[parent]:
1906 if pfn not in matches[parent]:
1895 try:
1907 try:
1896 fnode = pctx.filenode(pfn)
1908 fnode = pctx.filenode(pfn)
1897 grepbody(pfn, parent, flog.read(fnode))
1909 grepbody(pfn, parent, flog.read(fnode))
1898 except error.LookupError:
1910 except error.LookupError:
1899 pass
1911 pass
1900
1912
1901 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
1913 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
1902 rev = ctx.rev()
1914 rev = ctx.rev()
1903 parent = ctx.parents()[0].rev()
1915 parent = ctx.parents()[0].rev()
1904 for fn in sorted(revfiles.get(rev, [])):
1916 for fn in sorted(revfiles.get(rev, [])):
1905 states = matches[rev][fn]
1917 states = matches[rev][fn]
1906 copy = copies.get(rev, {}).get(fn)
1918 copy = copies.get(rev, {}).get(fn)
1907 if fn in skip:
1919 if fn in skip:
1908 if copy:
1920 if copy:
1909 skip[copy] = True
1921 skip[copy] = True
1910 continue
1922 continue
1911 pstates = matches.get(parent, {}).get(copy or fn, [])
1923 pstates = matches.get(parent, {}).get(copy or fn, [])
1912 if pstates or states:
1924 if pstates or states:
1913 r = display(fn, ctx, pstates, states)
1925 r = display(fn, ctx, pstates, states)
1914 found = found or r
1926 found = found or r
1915 if r and not opts.get('all'):
1927 if r and not opts.get('all'):
1916 skip[fn] = True
1928 skip[fn] = True
1917 if copy:
1929 if copy:
1918 skip[copy] = True
1930 skip[copy] = True
1919 del matches[rev]
1931 del matches[rev]
1920 del revfiles[rev]
1932 del revfiles[rev]
1921
1933
1922 return not found
1934 return not found
1923
1935
1924 def heads(ui, repo, *branchrevs, **opts):
1936 def heads(ui, repo, *branchrevs, **opts):
1925 """show current repository heads or show branch heads
1937 """show current repository heads or show branch heads
1926
1938
1927 With no arguments, show all repository branch heads.
1939 With no arguments, show all repository branch heads.
1928
1940
1929 Repository "heads" are changesets with no child changesets. They are
1941 Repository "heads" are changesets with no child changesets. They are
1930 where development generally takes place and are the usual targets
1942 where development generally takes place and are the usual targets
1931 for update and merge operations. Branch heads are changesets that have
1943 for update and merge operations. Branch heads are changesets that have
1932 no child changeset on the same branch.
1944 no child changeset on the same branch.
1933
1945
1934 If one or more REVs are given, only branch heads on the branches
1946 If one or more REVs are given, only branch heads on the branches
1935 associated with the specified changesets are shown.
1947 associated with the specified changesets are shown.
1936
1948
1937 If -c/--closed is specified, also show branch heads marked closed
1949 If -c/--closed is specified, also show branch heads marked closed
1938 (see :hg:`commit --close-branch`).
1950 (see :hg:`commit --close-branch`).
1939
1951
1940 If STARTREV is specified, only those heads that are descendants of
1952 If STARTREV is specified, only those heads that are descendants of
1941 STARTREV will be displayed.
1953 STARTREV will be displayed.
1942
1954
1943 If -t/--topo is specified, named branch mechanics will be ignored and only
1955 If -t/--topo is specified, named branch mechanics will be ignored and only
1944 changesets without children will be shown.
1956 changesets without children will be shown.
1945
1957
1946 Returns 0 if matching heads are found, 1 if not.
1958 Returns 0 if matching heads are found, 1 if not.
1947 """
1959 """
1948
1960
1949 start = None
1961 start = None
1950 if 'rev' in opts:
1962 if 'rev' in opts:
1951 start = cmdutil.revsingle(repo, opts['rev'], None).node()
1963 start = cmdutil.revsingle(repo, opts['rev'], None).node()
1952
1964
1953 if opts.get('topo'):
1965 if opts.get('topo'):
1954 heads = [repo[h] for h in repo.heads(start)]
1966 heads = [repo[h] for h in repo.heads(start)]
1955 else:
1967 else:
1956 heads = []
1968 heads = []
1957 for b, ls in repo.branchmap().iteritems():
1969 for b, ls in repo.branchmap().iteritems():
1958 if start is None:
1970 if start is None:
1959 heads += [repo[h] for h in ls]
1971 heads += [repo[h] for h in ls]
1960 continue
1972 continue
1961 startrev = repo.changelog.rev(start)
1973 startrev = repo.changelog.rev(start)
1962 descendants = set(repo.changelog.descendants(startrev))
1974 descendants = set(repo.changelog.descendants(startrev))
1963 descendants.add(startrev)
1975 descendants.add(startrev)
1964 rev = repo.changelog.rev
1976 rev = repo.changelog.rev
1965 heads += [repo[h] for h in ls if rev(h) in descendants]
1977 heads += [repo[h] for h in ls if rev(h) in descendants]
1966
1978
1967 if branchrevs:
1979 if branchrevs:
1968 branches = set(repo[br].branch() for br in branchrevs)
1980 branches = set(repo[br].branch() for br in branchrevs)
1969 heads = [h for h in heads if h.branch() in branches]
1981 heads = [h for h in heads if h.branch() in branches]
1970
1982
1971 if not opts.get('closed'):
1983 if not opts.get('closed'):
1972 heads = [h for h in heads if not h.extra().get('close')]
1984 heads = [h for h in heads if not h.extra().get('close')]
1973
1985
1974 if opts.get('active') and branchrevs:
1986 if opts.get('active') and branchrevs:
1975 dagheads = repo.heads(start)
1987 dagheads = repo.heads(start)
1976 heads = [h for h in heads if h.node() in dagheads]
1988 heads = [h for h in heads if h.node() in dagheads]
1977
1989
1978 if branchrevs:
1990 if branchrevs:
1979 haveheads = set(h.branch() for h in heads)
1991 haveheads = set(h.branch() for h in heads)
1980 if branches - haveheads:
1992 if branches - haveheads:
1981 headless = ', '.join(b for b in branches - haveheads)
1993 headless = ', '.join(b for b in branches - haveheads)
1982 msg = _('no open branch heads found on branches %s')
1994 msg = _('no open branch heads found on branches %s')
1983 if opts.get('rev'):
1995 if opts.get('rev'):
1984 msg += _(' (started at %s)' % opts['rev'])
1996 msg += _(' (started at %s)' % opts['rev'])
1985 ui.warn((msg + '\n') % headless)
1997 ui.warn((msg + '\n') % headless)
1986
1998
1987 if not heads:
1999 if not heads:
1988 return 1
2000 return 1
1989
2001
1990 heads = sorted(heads, key=lambda x: -x.rev())
2002 heads = sorted(heads, key=lambda x: -x.rev())
1991 displayer = cmdutil.show_changeset(ui, repo, opts)
2003 displayer = cmdutil.show_changeset(ui, repo, opts)
1992 for ctx in heads:
2004 for ctx in heads:
1993 displayer.show(ctx)
2005 displayer.show(ctx)
1994 displayer.close()
2006 displayer.close()
1995
2007
1996 def help_(ui, name=None, with_version=False, unknowncmd=False):
2008 def help_(ui, name=None, with_version=False, unknowncmd=False):
1997 """show help for a given topic or a help overview
2009 """show help for a given topic or a help overview
1998
2010
1999 With no arguments, print a list of commands with short help messages.
2011 With no arguments, print a list of commands with short help messages.
2000
2012
2001 Given a topic, extension, or command name, print help for that
2013 Given a topic, extension, or command name, print help for that
2002 topic.
2014 topic.
2003
2015
2004 Returns 0 if successful.
2016 Returns 0 if successful.
2005 """
2017 """
2006 option_lists = []
2018 option_lists = []
2007 textwidth = min(ui.termwidth(), 80) - 2
2019 textwidth = min(ui.termwidth(), 80) - 2
2008
2020
2009 def addglobalopts(aliases):
2021 def addglobalopts(aliases):
2010 if ui.verbose:
2022 if ui.verbose:
2011 option_lists.append((_("global options:"), globalopts))
2023 option_lists.append((_("global options:"), globalopts))
2012 if name == 'shortlist':
2024 if name == 'shortlist':
2013 option_lists.append((_('use "hg help" for the full list '
2025 option_lists.append((_('use "hg help" for the full list '
2014 'of commands'), ()))
2026 'of commands'), ()))
2015 else:
2027 else:
2016 if name == 'shortlist':
2028 if name == 'shortlist':
2017 msg = _('use "hg help" for the full list of commands '
2029 msg = _('use "hg help" for the full list of commands '
2018 'or "hg -v" for details')
2030 'or "hg -v" for details')
2019 elif aliases:
2031 elif aliases:
2020 msg = _('use "hg -v help%s" to show builtin aliases and '
2032 msg = _('use "hg -v help%s" to show builtin aliases and '
2021 'global options') % (name and " " + name or "")
2033 'global options') % (name and " " + name or "")
2022 else:
2034 else:
2023 msg = _('use "hg -v help %s" to show global options') % name
2035 msg = _('use "hg -v help %s" to show global options') % name
2024 option_lists.append((msg, ()))
2036 option_lists.append((msg, ()))
2025
2037
2026 def helpcmd(name):
2038 def helpcmd(name):
2027 if with_version:
2039 if with_version:
2028 version_(ui)
2040 version_(ui)
2029 ui.write('\n')
2041 ui.write('\n')
2030
2042
2031 try:
2043 try:
2032 aliases, entry = cmdutil.findcmd(name, table, strict=unknowncmd)
2044 aliases, entry = cmdutil.findcmd(name, table, strict=unknowncmd)
2033 except error.AmbiguousCommand, inst:
2045 except error.AmbiguousCommand, inst:
2034 # py3k fix: except vars can't be used outside the scope of the
2046 # py3k fix: except vars can't be used outside the scope of the
2035 # except block, nor can be used inside a lambda. python issue4617
2047 # except block, nor can be used inside a lambda. python issue4617
2036 prefix = inst.args[0]
2048 prefix = inst.args[0]
2037 select = lambda c: c.lstrip('^').startswith(prefix)
2049 select = lambda c: c.lstrip('^').startswith(prefix)
2038 helplist(_('list of commands:\n\n'), select)
2050 helplist(_('list of commands:\n\n'), select)
2039 return
2051 return
2040
2052
2041 # check if it's an invalid alias and display its error if it is
2053 # check if it's an invalid alias and display its error if it is
2042 if getattr(entry[0], 'badalias', False):
2054 if getattr(entry[0], 'badalias', False):
2043 if not unknowncmd:
2055 if not unknowncmd:
2044 entry[0](ui)
2056 entry[0](ui)
2045 return
2057 return
2046
2058
2047 # synopsis
2059 # synopsis
2048 if len(entry) > 2:
2060 if len(entry) > 2:
2049 if entry[2].startswith('hg'):
2061 if entry[2].startswith('hg'):
2050 ui.write("%s\n" % entry[2])
2062 ui.write("%s\n" % entry[2])
2051 else:
2063 else:
2052 ui.write('hg %s %s\n' % (aliases[0], entry[2]))
2064 ui.write('hg %s %s\n' % (aliases[0], entry[2]))
2053 else:
2065 else:
2054 ui.write('hg %s\n' % aliases[0])
2066 ui.write('hg %s\n' % aliases[0])
2055
2067
2056 # aliases
2068 # aliases
2057 if not ui.quiet and len(aliases) > 1:
2069 if not ui.quiet and len(aliases) > 1:
2058 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
2070 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
2059
2071
2060 # description
2072 # description
2061 doc = gettext(entry[0].__doc__)
2073 doc = gettext(entry[0].__doc__)
2062 if not doc:
2074 if not doc:
2063 doc = _("(no help text available)")
2075 doc = _("(no help text available)")
2064 if hasattr(entry[0], 'definition'): # aliased command
2076 if hasattr(entry[0], 'definition'): # aliased command
2065 if entry[0].definition.startswith('!'): # shell alias
2077 if entry[0].definition.startswith('!'): # shell alias
2066 doc = _('shell alias for::\n\n %s') % entry[0].definition[1:]
2078 doc = _('shell alias for::\n\n %s') % entry[0].definition[1:]
2067 else:
2079 else:
2068 doc = _('alias for: hg %s\n\n%s') % (entry[0].definition, doc)
2080 doc = _('alias for: hg %s\n\n%s') % (entry[0].definition, doc)
2069 if ui.quiet:
2081 if ui.quiet:
2070 doc = doc.splitlines()[0]
2082 doc = doc.splitlines()[0]
2071 keep = ui.verbose and ['verbose'] or []
2083 keep = ui.verbose and ['verbose'] or []
2072 formatted, pruned = minirst.format(doc, textwidth, keep=keep)
2084 formatted, pruned = minirst.format(doc, textwidth, keep=keep)
2073 ui.write("\n%s\n" % formatted)
2085 ui.write("\n%s\n" % formatted)
2074 if pruned:
2086 if pruned:
2075 ui.write(_('\nuse "hg -v help %s" to show verbose help\n') % name)
2087 ui.write(_('\nuse "hg -v help %s" to show verbose help\n') % name)
2076
2088
2077 if not ui.quiet:
2089 if not ui.quiet:
2078 # options
2090 # options
2079 if entry[1]:
2091 if entry[1]:
2080 option_lists.append((_("options:\n"), entry[1]))
2092 option_lists.append((_("options:\n"), entry[1]))
2081
2093
2082 addglobalopts(False)
2094 addglobalopts(False)
2083
2095
2084 def helplist(header, select=None):
2096 def helplist(header, select=None):
2085 h = {}
2097 h = {}
2086 cmds = {}
2098 cmds = {}
2087 for c, e in table.iteritems():
2099 for c, e in table.iteritems():
2088 f = c.split("|", 1)[0]
2100 f = c.split("|", 1)[0]
2089 if select and not select(f):
2101 if select and not select(f):
2090 continue
2102 continue
2091 if (not select and name != 'shortlist' and
2103 if (not select and name != 'shortlist' and
2092 e[0].__module__ != __name__):
2104 e[0].__module__ != __name__):
2093 continue
2105 continue
2094 if name == "shortlist" and not f.startswith("^"):
2106 if name == "shortlist" and not f.startswith("^"):
2095 continue
2107 continue
2096 f = f.lstrip("^")
2108 f = f.lstrip("^")
2097 if not ui.debugflag and f.startswith("debug"):
2109 if not ui.debugflag and f.startswith("debug"):
2098 continue
2110 continue
2099 doc = e[0].__doc__
2111 doc = e[0].__doc__
2100 if doc and 'DEPRECATED' in doc and not ui.verbose:
2112 if doc and 'DEPRECATED' in doc and not ui.verbose:
2101 continue
2113 continue
2102 doc = gettext(doc)
2114 doc = gettext(doc)
2103 if not doc:
2115 if not doc:
2104 doc = _("(no help text available)")
2116 doc = _("(no help text available)")
2105 h[f] = doc.splitlines()[0].rstrip()
2117 h[f] = doc.splitlines()[0].rstrip()
2106 cmds[f] = c.lstrip("^")
2118 cmds[f] = c.lstrip("^")
2107
2119
2108 if not h:
2120 if not h:
2109 ui.status(_('no commands defined\n'))
2121 ui.status(_('no commands defined\n'))
2110 return
2122 return
2111
2123
2112 ui.status(header)
2124 ui.status(header)
2113 fns = sorted(h)
2125 fns = sorted(h)
2114 m = max(map(len, fns))
2126 m = max(map(len, fns))
2115 for f in fns:
2127 for f in fns:
2116 if ui.verbose:
2128 if ui.verbose:
2117 commands = cmds[f].replace("|",", ")
2129 commands = cmds[f].replace("|",", ")
2118 ui.write(" %s:\n %s\n"%(commands, h[f]))
2130 ui.write(" %s:\n %s\n"%(commands, h[f]))
2119 else:
2131 else:
2120 ui.write('%s\n' % (util.wrap(h[f], textwidth,
2132 ui.write('%s\n' % (util.wrap(h[f], textwidth,
2121 initindent=' %-*s ' % (m, f),
2133 initindent=' %-*s ' % (m, f),
2122 hangindent=' ' * (m + 4))))
2134 hangindent=' ' * (m + 4))))
2123
2135
2124 if not ui.quiet:
2136 if not ui.quiet:
2125 addglobalopts(True)
2137 addglobalopts(True)
2126
2138
2127 def helptopic(name):
2139 def helptopic(name):
2128 for names, header, doc in help.helptable:
2140 for names, header, doc in help.helptable:
2129 if name in names:
2141 if name in names:
2130 break
2142 break
2131 else:
2143 else:
2132 raise error.UnknownCommand(name)
2144 raise error.UnknownCommand(name)
2133
2145
2134 # description
2146 # description
2135 if not doc:
2147 if not doc:
2136 doc = _("(no help text available)")
2148 doc = _("(no help text available)")
2137 if hasattr(doc, '__call__'):
2149 if hasattr(doc, '__call__'):
2138 doc = doc()
2150 doc = doc()
2139
2151
2140 ui.write("%s\n\n" % header)
2152 ui.write("%s\n\n" % header)
2141 ui.write("%s\n" % minirst.format(doc, textwidth, indent=4))
2153 ui.write("%s\n" % minirst.format(doc, textwidth, indent=4))
2142
2154
2143 def helpext(name):
2155 def helpext(name):
2144 try:
2156 try:
2145 mod = extensions.find(name)
2157 mod = extensions.find(name)
2146 doc = gettext(mod.__doc__) or _('no help text available')
2158 doc = gettext(mod.__doc__) or _('no help text available')
2147 except KeyError:
2159 except KeyError:
2148 mod = None
2160 mod = None
2149 doc = extensions.disabledext(name)
2161 doc = extensions.disabledext(name)
2150 if not doc:
2162 if not doc:
2151 raise error.UnknownCommand(name)
2163 raise error.UnknownCommand(name)
2152
2164
2153 if '\n' not in doc:
2165 if '\n' not in doc:
2154 head, tail = doc, ""
2166 head, tail = doc, ""
2155 else:
2167 else:
2156 head, tail = doc.split('\n', 1)
2168 head, tail = doc.split('\n', 1)
2157 ui.write(_('%s extension - %s\n\n') % (name.split('.')[-1], head))
2169 ui.write(_('%s extension - %s\n\n') % (name.split('.')[-1], head))
2158 if tail:
2170 if tail:
2159 ui.write(minirst.format(tail, textwidth))
2171 ui.write(minirst.format(tail, textwidth))
2160 ui.status('\n\n')
2172 ui.status('\n\n')
2161
2173
2162 if mod:
2174 if mod:
2163 try:
2175 try:
2164 ct = mod.cmdtable
2176 ct = mod.cmdtable
2165 except AttributeError:
2177 except AttributeError:
2166 ct = {}
2178 ct = {}
2167 modcmds = set([c.split('|', 1)[0] for c in ct])
2179 modcmds = set([c.split('|', 1)[0] for c in ct])
2168 helplist(_('list of commands:\n\n'), modcmds.__contains__)
2180 helplist(_('list of commands:\n\n'), modcmds.__contains__)
2169 else:
2181 else:
2170 ui.write(_('use "hg help extensions" for information on enabling '
2182 ui.write(_('use "hg help extensions" for information on enabling '
2171 'extensions\n'))
2183 'extensions\n'))
2172
2184
2173 def helpextcmd(name):
2185 def helpextcmd(name):
2174 cmd, ext, mod = extensions.disabledcmd(ui, name, ui.config('ui', 'strict'))
2186 cmd, ext, mod = extensions.disabledcmd(ui, name, ui.config('ui', 'strict'))
2175 doc = gettext(mod.__doc__).splitlines()[0]
2187 doc = gettext(mod.__doc__).splitlines()[0]
2176
2188
2177 msg = help.listexts(_("'%s' is provided by the following "
2189 msg = help.listexts(_("'%s' is provided by the following "
2178 "extension:") % cmd, {ext: doc}, len(ext),
2190 "extension:") % cmd, {ext: doc}, len(ext),
2179 indent=4)
2191 indent=4)
2180 ui.write(minirst.format(msg, textwidth))
2192 ui.write(minirst.format(msg, textwidth))
2181 ui.write('\n\n')
2193 ui.write('\n\n')
2182 ui.write(_('use "hg help extensions" for information on enabling '
2194 ui.write(_('use "hg help extensions" for information on enabling '
2183 'extensions\n'))
2195 'extensions\n'))
2184
2196
2185 help.addtopichook('revsets', revset.makedoc)
2197 help.addtopichook('revsets', revset.makedoc)
2186 help.addtopichook('templates', templatekw.makedoc)
2198 help.addtopichook('templates', templatekw.makedoc)
2187 help.addtopichook('templates', templatefilters.makedoc)
2199 help.addtopichook('templates', templatefilters.makedoc)
2188
2200
2189 if name and name != 'shortlist':
2201 if name and name != 'shortlist':
2190 i = None
2202 i = None
2191 if unknowncmd:
2203 if unknowncmd:
2192 queries = (helpextcmd,)
2204 queries = (helpextcmd,)
2193 else:
2205 else:
2194 queries = (helptopic, helpcmd, helpext, helpextcmd)
2206 queries = (helptopic, helpcmd, helpext, helpextcmd)
2195 for f in queries:
2207 for f in queries:
2196 try:
2208 try:
2197 f(name)
2209 f(name)
2198 i = None
2210 i = None
2199 break
2211 break
2200 except error.UnknownCommand, inst:
2212 except error.UnknownCommand, inst:
2201 i = inst
2213 i = inst
2202 if i:
2214 if i:
2203 raise i
2215 raise i
2204
2216
2205 else:
2217 else:
2206 # program name
2218 # program name
2207 if ui.verbose or with_version:
2219 if ui.verbose or with_version:
2208 version_(ui)
2220 version_(ui)
2209 else:
2221 else:
2210 ui.status(_("Mercurial Distributed SCM\n"))
2222 ui.status(_("Mercurial Distributed SCM\n"))
2211 ui.status('\n')
2223 ui.status('\n')
2212
2224
2213 # list of commands
2225 # list of commands
2214 if name == "shortlist":
2226 if name == "shortlist":
2215 header = _('basic commands:\n\n')
2227 header = _('basic commands:\n\n')
2216 else:
2228 else:
2217 header = _('list of commands:\n\n')
2229 header = _('list of commands:\n\n')
2218
2230
2219 helplist(header)
2231 helplist(header)
2220 if name != 'shortlist':
2232 if name != 'shortlist':
2221 exts, maxlength = extensions.enabled()
2233 exts, maxlength = extensions.enabled()
2222 text = help.listexts(_('enabled extensions:'), exts, maxlength)
2234 text = help.listexts(_('enabled extensions:'), exts, maxlength)
2223 if text:
2235 if text:
2224 ui.write("\n%s\n" % minirst.format(text, textwidth))
2236 ui.write("\n%s\n" % minirst.format(text, textwidth))
2225
2237
2226 # list all option lists
2238 # list all option lists
2227 opt_output = []
2239 opt_output = []
2228 multioccur = False
2240 multioccur = False
2229 for title, options in option_lists:
2241 for title, options in option_lists:
2230 opt_output.append(("\n%s" % title, None))
2242 opt_output.append(("\n%s" % title, None))
2231 for option in options:
2243 for option in options:
2232 if len(option) == 5:
2244 if len(option) == 5:
2233 shortopt, longopt, default, desc, optlabel = option
2245 shortopt, longopt, default, desc, optlabel = option
2234 else:
2246 else:
2235 shortopt, longopt, default, desc = option
2247 shortopt, longopt, default, desc = option
2236 optlabel = _("VALUE") # default label
2248 optlabel = _("VALUE") # default label
2237
2249
2238 if _("DEPRECATED") in desc and not ui.verbose:
2250 if _("DEPRECATED") in desc and not ui.verbose:
2239 continue
2251 continue
2240 if isinstance(default, list):
2252 if isinstance(default, list):
2241 numqualifier = " %s [+]" % optlabel
2253 numqualifier = " %s [+]" % optlabel
2242 multioccur = True
2254 multioccur = True
2243 elif (default is not None) and not isinstance(default, bool):
2255 elif (default is not None) and not isinstance(default, bool):
2244 numqualifier = " %s" % optlabel
2256 numqualifier = " %s" % optlabel
2245 else:
2257 else:
2246 numqualifier = ""
2258 numqualifier = ""
2247 opt_output.append(("%2s%s" %
2259 opt_output.append(("%2s%s" %
2248 (shortopt and "-%s" % shortopt,
2260 (shortopt and "-%s" % shortopt,
2249 longopt and " --%s%s" %
2261 longopt and " --%s%s" %
2250 (longopt, numqualifier)),
2262 (longopt, numqualifier)),
2251 "%s%s" % (desc,
2263 "%s%s" % (desc,
2252 default
2264 default
2253 and _(" (default: %s)") % default
2265 and _(" (default: %s)") % default
2254 or "")))
2266 or "")))
2255 if multioccur:
2267 if multioccur:
2256 msg = _("\n[+] marked option can be specified multiple times")
2268 msg = _("\n[+] marked option can be specified multiple times")
2257 if ui.verbose and name != 'shortlist':
2269 if ui.verbose and name != 'shortlist':
2258 opt_output.append((msg, None))
2270 opt_output.append((msg, None))
2259 else:
2271 else:
2260 opt_output.insert(-1, (msg, None))
2272 opt_output.insert(-1, (msg, None))
2261
2273
2262 if not name:
2274 if not name:
2263 ui.write(_("\nadditional help topics:\n\n"))
2275 ui.write(_("\nadditional help topics:\n\n"))
2264 topics = []
2276 topics = []
2265 for names, header, doc in help.helptable:
2277 for names, header, doc in help.helptable:
2266 topics.append((sorted(names, key=len, reverse=True)[0], header))
2278 topics.append((sorted(names, key=len, reverse=True)[0], header))
2267 topics_len = max([len(s[0]) for s in topics])
2279 topics_len = max([len(s[0]) for s in topics])
2268 for t, desc in topics:
2280 for t, desc in topics:
2269 ui.write(" %-*s %s\n" % (topics_len, t, desc))
2281 ui.write(" %-*s %s\n" % (topics_len, t, desc))
2270
2282
2271 if opt_output:
2283 if opt_output:
2272 colwidth = encoding.colwidth
2284 colwidth = encoding.colwidth
2273 # normalize: (opt or message, desc or None, width of opt)
2285 # normalize: (opt or message, desc or None, width of opt)
2274 entries = [desc and (opt, desc, colwidth(opt)) or (opt, None, 0)
2286 entries = [desc and (opt, desc, colwidth(opt)) or (opt, None, 0)
2275 for opt, desc in opt_output]
2287 for opt, desc in opt_output]
2276 hanging = max([e[2] for e in entries])
2288 hanging = max([e[2] for e in entries])
2277 for opt, desc, width in entries:
2289 for opt, desc, width in entries:
2278 if desc:
2290 if desc:
2279 initindent = ' %s%s ' % (opt, ' ' * (hanging - width))
2291 initindent = ' %s%s ' % (opt, ' ' * (hanging - width))
2280 hangindent = ' ' * (hanging + 3)
2292 hangindent = ' ' * (hanging + 3)
2281 ui.write('%s\n' % (util.wrap(desc, textwidth,
2293 ui.write('%s\n' % (util.wrap(desc, textwidth,
2282 initindent=initindent,
2294 initindent=initindent,
2283 hangindent=hangindent)))
2295 hangindent=hangindent)))
2284 else:
2296 else:
2285 ui.write("%s\n" % opt)
2297 ui.write("%s\n" % opt)
2286
2298
2287 def identify(ui, repo, source=None, rev=None,
2299 def identify(ui, repo, source=None, rev=None,
2288 num=None, id=None, branch=None, tags=None, bookmarks=None):
2300 num=None, id=None, branch=None, tags=None, bookmarks=None):
2289 """identify the working copy or specified revision
2301 """identify the working copy or specified revision
2290
2302
2291 With no revision, print a summary of the current state of the
2303 With no revision, print a summary of the current state of the
2292 repository.
2304 repository.
2293
2305
2294 Specifying a path to a repository root or Mercurial bundle will
2306 Specifying a path to a repository root or Mercurial bundle will
2295 cause lookup to operate on that repository/bundle.
2307 cause lookup to operate on that repository/bundle.
2296
2308
2297 This summary identifies the repository state using one or two
2309 This summary identifies the repository state using one or two
2298 parent hash identifiers, followed by a "+" if there are
2310 parent hash identifiers, followed by a "+" if there are
2299 uncommitted changes in the working directory, a list of tags for
2311 uncommitted changes in the working directory, a list of tags for
2300 this revision and a branch name for non-default branches.
2312 this revision and a branch name for non-default branches.
2301
2313
2302 Returns 0 if successful.
2314 Returns 0 if successful.
2303 """
2315 """
2304
2316
2305 if not repo and not source:
2317 if not repo and not source:
2306 raise util.Abort(_("there is no Mercurial repository here "
2318 raise util.Abort(_("there is no Mercurial repository here "
2307 "(.hg not found)"))
2319 "(.hg not found)"))
2308
2320
2309 hexfunc = ui.debugflag and hex or short
2321 hexfunc = ui.debugflag and hex or short
2310 default = not (num or id or branch or tags or bookmarks)
2322 default = not (num or id or branch or tags or bookmarks)
2311 output = []
2323 output = []
2312
2324
2313 revs = []
2325 revs = []
2314 bms = []
2326 bms = []
2315 if source:
2327 if source:
2316 source, branches = hg.parseurl(ui.expandpath(source))
2328 source, branches = hg.parseurl(ui.expandpath(source))
2317 repo = hg.repository(ui, source)
2329 repo = hg.repository(ui, source)
2318 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
2330 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
2319
2331
2320 if not repo.local():
2332 if not repo.local():
2321 if not rev and revs:
2333 if not rev and revs:
2322 rev = revs[0]
2334 rev = revs[0]
2323 if not rev:
2335 if not rev:
2324 rev = "tip"
2336 rev = "tip"
2325 if num or branch or tags:
2337 if num or branch or tags:
2326 raise util.Abort(
2338 raise util.Abort(
2327 _("can't query remote revision number, branch, or tags"))
2339 _("can't query remote revision number, branch, or tags"))
2328
2340
2329 remoterev = repo.lookup(rev)
2341 remoterev = repo.lookup(rev)
2330 if default or id:
2342 if default or id:
2331 output = [hexfunc(remoterev)]
2343 output = [hexfunc(remoterev)]
2332
2344
2333 if 'bookmarks' in repo.listkeys('namespaces'):
2345 if 'bookmarks' in repo.listkeys('namespaces'):
2334 hexremoterev = hex(remoterev)
2346 hexremoterev = hex(remoterev)
2335 bms = [bm for bm, bmrev in repo.listkeys('bookmarks').iteritems()
2347 bms = [bm for bm, bmrev in repo.listkeys('bookmarks').iteritems()
2336 if bmrev == hexremoterev]
2348 if bmrev == hexremoterev]
2337
2349
2338 elif not rev:
2350 elif not rev:
2339 ctx = repo[None]
2351 ctx = repo[None]
2340 parents = ctx.parents()
2352 parents = ctx.parents()
2341 changed = False
2353 changed = False
2342 if default or id or num:
2354 if default or id or num:
2343 changed = util.any(repo.status())
2355 changed = util.any(repo.status())
2344 if default or id:
2356 if default or id:
2345 output = ["%s%s" % ('+'.join([hexfunc(p.node()) for p in parents]),
2357 output = ["%s%s" % ('+'.join([hexfunc(p.node()) for p in parents]),
2346 (changed) and "+" or "")]
2358 (changed) and "+" or "")]
2347 if num:
2359 if num:
2348 output.append("%s%s" % ('+'.join([str(p.rev()) for p in parents]),
2360 output.append("%s%s" % ('+'.join([str(p.rev()) for p in parents]),
2349 (changed) and "+" or ""))
2361 (changed) and "+" or ""))
2350 else:
2362 else:
2351 ctx = cmdutil.revsingle(repo, rev)
2363 ctx = cmdutil.revsingle(repo, rev)
2352 if default or id:
2364 if default or id:
2353 output = [hexfunc(ctx.node())]
2365 output = [hexfunc(ctx.node())]
2354 if num:
2366 if num:
2355 output.append(str(ctx.rev()))
2367 output.append(str(ctx.rev()))
2356
2368
2357 if repo.local():
2369 if repo.local():
2358 bms = ctx.bookmarks()
2370 bms = ctx.bookmarks()
2359
2371
2360 if repo.local() and default and not ui.quiet:
2372 if repo.local() and default and not ui.quiet:
2361 b = ctx.branch()
2373 b = ctx.branch()
2362 if b != 'default':
2374 if b != 'default':
2363 output.append("(%s)" % b)
2375 output.append("(%s)" % b)
2364
2376
2365 # multiple tags for a single parent separated by '/'
2377 # multiple tags for a single parent separated by '/'
2366 t = "/".join(ctx.tags())
2378 t = "/".join(ctx.tags())
2367 if t:
2379 if t:
2368 output.append(t)
2380 output.append(t)
2369
2381
2370 if default and not ui.quiet:
2382 if default and not ui.quiet:
2371 # multiple bookmarks for a single parent separated by '/'
2383 # multiple bookmarks for a single parent separated by '/'
2372 bm = '/'.join(bms)
2384 bm = '/'.join(bms)
2373 if bm:
2385 if bm:
2374 output.append(bm)
2386 output.append(bm)
2375
2387
2376 if branch:
2388 if branch:
2377 output.append(ctx.branch())
2389 output.append(ctx.branch())
2378
2390
2379 if tags:
2391 if tags:
2380 output.extend(ctx.tags())
2392 output.extend(ctx.tags())
2381
2393
2382 if bookmarks:
2394 if bookmarks:
2383 output.extend(bms)
2395 output.extend(bms)
2384
2396
2385 ui.write("%s\n" % ' '.join(output))
2397 ui.write("%s\n" % ' '.join(output))
2386
2398
2387 def import_(ui, repo, patch1, *patches, **opts):
2399 def import_(ui, repo, patch1, *patches, **opts):
2388 """import an ordered set of patches
2400 """import an ordered set of patches
2389
2401
2390 Import a list of patches and commit them individually (unless
2402 Import a list of patches and commit them individually (unless
2391 --no-commit is specified).
2403 --no-commit is specified).
2392
2404
2393 If there are outstanding changes in the working directory, import
2405 If there are outstanding changes in the working directory, import
2394 will abort unless given the -f/--force flag.
2406 will abort unless given the -f/--force flag.
2395
2407
2396 You can import a patch straight from a mail message. Even patches
2408 You can import a patch straight from a mail message. Even patches
2397 as attachments work (to use the body part, it must have type
2409 as attachments work (to use the body part, it must have type
2398 text/plain or text/x-patch). From and Subject headers of email
2410 text/plain or text/x-patch). From and Subject headers of email
2399 message are used as default committer and commit message. All
2411 message are used as default committer and commit message. All
2400 text/plain body parts before first diff are added to commit
2412 text/plain body parts before first diff are added to commit
2401 message.
2413 message.
2402
2414
2403 If the imported patch was generated by :hg:`export`, user and
2415 If the imported patch was generated by :hg:`export`, user and
2404 description from patch override values from message headers and
2416 description from patch override values from message headers and
2405 body. Values given on command line with -m/--message and -u/--user
2417 body. Values given on command line with -m/--message and -u/--user
2406 override these.
2418 override these.
2407
2419
2408 If --exact is specified, import will set the working directory to
2420 If --exact is specified, import will set the working directory to
2409 the parent of each patch before applying it, and will abort if the
2421 the parent of each patch before applying it, and will abort if the
2410 resulting changeset has a different ID than the one recorded in
2422 resulting changeset has a different ID than the one recorded in
2411 the patch. This may happen due to character set problems or other
2423 the patch. This may happen due to character set problems or other
2412 deficiencies in the text patch format.
2424 deficiencies in the text patch format.
2413
2425
2414 With -s/--similarity, hg will attempt to discover renames and
2426 With -s/--similarity, hg will attempt to discover renames and
2415 copies in the patch in the same way as 'addremove'.
2427 copies in the patch in the same way as 'addremove'.
2416
2428
2417 To read a patch from standard input, use "-" as the patch name. If
2429 To read a patch from standard input, use "-" as the patch name. If
2418 a URL is specified, the patch will be downloaded from it.
2430 a URL is specified, the patch will be downloaded from it.
2419 See :hg:`help dates` for a list of formats valid for -d/--date.
2431 See :hg:`help dates` for a list of formats valid for -d/--date.
2420
2432
2421 Returns 0 on success.
2433 Returns 0 on success.
2422 """
2434 """
2423 patches = (patch1,) + patches
2435 patches = (patch1,) + patches
2424
2436
2425 date = opts.get('date')
2437 date = opts.get('date')
2426 if date:
2438 if date:
2427 opts['date'] = util.parsedate(date)
2439 opts['date'] = util.parsedate(date)
2428
2440
2429 try:
2441 try:
2430 sim = float(opts.get('similarity') or 0)
2442 sim = float(opts.get('similarity') or 0)
2431 except ValueError:
2443 except ValueError:
2432 raise util.Abort(_('similarity must be a number'))
2444 raise util.Abort(_('similarity must be a number'))
2433 if sim < 0 or sim > 100:
2445 if sim < 0 or sim > 100:
2434 raise util.Abort(_('similarity must be between 0 and 100'))
2446 raise util.Abort(_('similarity must be between 0 and 100'))
2435
2447
2436 if opts.get('exact') or not opts.get('force'):
2448 if opts.get('exact') or not opts.get('force'):
2437 cmdutil.bail_if_changed(repo)
2449 cmdutil.bail_if_changed(repo)
2438
2450
2439 d = opts["base"]
2451 d = opts["base"]
2440 strip = opts["strip"]
2452 strip = opts["strip"]
2441 wlock = lock = None
2453 wlock = lock = None
2442 msgs = []
2454 msgs = []
2443
2455
2444 def tryone(ui, hunk):
2456 def tryone(ui, hunk):
2445 tmpname, message, user, date, branch, nodeid, p1, p2 = \
2457 tmpname, message, user, date, branch, nodeid, p1, p2 = \
2446 patch.extract(ui, hunk)
2458 patch.extract(ui, hunk)
2447
2459
2448 if not tmpname:
2460 if not tmpname:
2449 return None
2461 return None
2450 commitid = _('to working directory')
2462 commitid = _('to working directory')
2451
2463
2452 try:
2464 try:
2453 cmdline_message = cmdutil.logmessage(opts)
2465 cmdline_message = cmdutil.logmessage(opts)
2454 if cmdline_message:
2466 if cmdline_message:
2455 # pickup the cmdline msg
2467 # pickup the cmdline msg
2456 message = cmdline_message
2468 message = cmdline_message
2457 elif message:
2469 elif message:
2458 # pickup the patch msg
2470 # pickup the patch msg
2459 message = message.strip()
2471 message = message.strip()
2460 else:
2472 else:
2461 # launch the editor
2473 # launch the editor
2462 message = None
2474 message = None
2463 ui.debug('message:\n%s\n' % message)
2475 ui.debug('message:\n%s\n' % message)
2464
2476
2465 wp = repo.parents()
2477 wp = repo.parents()
2466 if opts.get('exact'):
2478 if opts.get('exact'):
2467 if not nodeid or not p1:
2479 if not nodeid or not p1:
2468 raise util.Abort(_('not a Mercurial patch'))
2480 raise util.Abort(_('not a Mercurial patch'))
2469 p1 = repo.lookup(p1)
2481 p1 = repo.lookup(p1)
2470 p2 = repo.lookup(p2 or hex(nullid))
2482 p2 = repo.lookup(p2 or hex(nullid))
2471
2483
2472 if p1 != wp[0].node():
2484 if p1 != wp[0].node():
2473 hg.clean(repo, p1)
2485 hg.clean(repo, p1)
2474 repo.dirstate.setparents(p1, p2)
2486 repo.dirstate.setparents(p1, p2)
2475 elif p2:
2487 elif p2:
2476 try:
2488 try:
2477 p1 = repo.lookup(p1)
2489 p1 = repo.lookup(p1)
2478 p2 = repo.lookup(p2)
2490 p2 = repo.lookup(p2)
2479 if p1 == wp[0].node():
2491 if p1 == wp[0].node():
2480 repo.dirstate.setparents(p1, p2)
2492 repo.dirstate.setparents(p1, p2)
2481 except error.RepoError:
2493 except error.RepoError:
2482 pass
2494 pass
2483 if opts.get('exact') or opts.get('import_branch'):
2495 if opts.get('exact') or opts.get('import_branch'):
2484 repo.dirstate.setbranch(branch or 'default')
2496 repo.dirstate.setbranch(branch or 'default')
2485
2497
2486 files = {}
2498 files = {}
2487 try:
2499 try:
2488 patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
2500 patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
2489 files=files, eolmode=None)
2501 files=files, eolmode=None)
2490 finally:
2502 finally:
2491 files = cmdutil.updatedir(ui, repo, files,
2503 files = cmdutil.updatedir(ui, repo, files,
2492 similarity=sim / 100.0)
2504 similarity=sim / 100.0)
2493 if opts.get('no_commit'):
2505 if opts.get('no_commit'):
2494 if message:
2506 if message:
2495 msgs.append(message)
2507 msgs.append(message)
2496 else:
2508 else:
2497 if opts.get('exact'):
2509 if opts.get('exact'):
2498 m = None
2510 m = None
2499 else:
2511 else:
2500 m = cmdutil.matchfiles(repo, files or [])
2512 m = cmdutil.matchfiles(repo, files or [])
2501 n = repo.commit(message, opts.get('user') or user,
2513 n = repo.commit(message, opts.get('user') or user,
2502 opts.get('date') or date, match=m,
2514 opts.get('date') or date, match=m,
2503 editor=cmdutil.commiteditor)
2515 editor=cmdutil.commiteditor)
2504 if opts.get('exact'):
2516 if opts.get('exact'):
2505 if hex(n) != nodeid:
2517 if hex(n) != nodeid:
2506 repo.rollback()
2518 repo.rollback()
2507 raise util.Abort(_('patch is damaged'
2519 raise util.Abort(_('patch is damaged'
2508 ' or loses information'))
2520 ' or loses information'))
2509 # Force a dirstate write so that the next transaction
2521 # Force a dirstate write so that the next transaction
2510 # backups an up-do-date file.
2522 # backups an up-do-date file.
2511 repo.dirstate.write()
2523 repo.dirstate.write()
2512 if n:
2524 if n:
2513 commitid = short(n)
2525 commitid = short(n)
2514
2526
2515 return commitid
2527 return commitid
2516 finally:
2528 finally:
2517 os.unlink(tmpname)
2529 os.unlink(tmpname)
2518
2530
2519 try:
2531 try:
2520 wlock = repo.wlock()
2532 wlock = repo.wlock()
2521 lock = repo.lock()
2533 lock = repo.lock()
2522 lastcommit = None
2534 lastcommit = None
2523 for p in patches:
2535 for p in patches:
2524 pf = os.path.join(d, p)
2536 pf = os.path.join(d, p)
2525
2537
2526 if pf == '-':
2538 if pf == '-':
2527 ui.status(_("applying patch from stdin\n"))
2539 ui.status(_("applying patch from stdin\n"))
2528 pf = sys.stdin
2540 pf = sys.stdin
2529 else:
2541 else:
2530 ui.status(_("applying %s\n") % p)
2542 ui.status(_("applying %s\n") % p)
2531 pf = url.open(ui, pf)
2543 pf = url.open(ui, pf)
2532
2544
2533 haspatch = False
2545 haspatch = False
2534 for hunk in patch.split(pf):
2546 for hunk in patch.split(pf):
2535 commitid = tryone(ui, hunk)
2547 commitid = tryone(ui, hunk)
2536 if commitid:
2548 if commitid:
2537 haspatch = True
2549 haspatch = True
2538 if lastcommit:
2550 if lastcommit:
2539 ui.status(_('applied %s\n') % lastcommit)
2551 ui.status(_('applied %s\n') % lastcommit)
2540 lastcommit = commitid
2552 lastcommit = commitid
2541
2553
2542 if not haspatch:
2554 if not haspatch:
2543 raise util.Abort(_('no diffs found'))
2555 raise util.Abort(_('no diffs found'))
2544
2556
2545 if msgs:
2557 if msgs:
2546 repo.opener('last-message.txt', 'wb').write('\n* * *\n'.join(msgs))
2558 repo.opener('last-message.txt', 'wb').write('\n* * *\n'.join(msgs))
2547 finally:
2559 finally:
2548 release(lock, wlock)
2560 release(lock, wlock)
2549
2561
2550 def incoming(ui, repo, source="default", **opts):
2562 def incoming(ui, repo, source="default", **opts):
2551 """show new changesets found in source
2563 """show new changesets found in source
2552
2564
2553 Show new changesets found in the specified path/URL or the default
2565 Show new changesets found in the specified path/URL or the default
2554 pull location. These are the changesets that would have been pulled
2566 pull location. These are the changesets that would have been pulled
2555 if a pull at the time you issued this command.
2567 if a pull at the time you issued this command.
2556
2568
2557 For remote repository, using --bundle avoids downloading the
2569 For remote repository, using --bundle avoids downloading the
2558 changesets twice if the incoming is followed by a pull.
2570 changesets twice if the incoming is followed by a pull.
2559
2571
2560 See pull for valid source format details.
2572 See pull for valid source format details.
2561
2573
2562 Returns 0 if there are incoming changes, 1 otherwise.
2574 Returns 0 if there are incoming changes, 1 otherwise.
2563 """
2575 """
2564 if opts.get('bundle') and opts.get('subrepos'):
2576 if opts.get('bundle') and opts.get('subrepos'):
2565 raise util.Abort(_('cannot combine --bundle and --subrepos'))
2577 raise util.Abort(_('cannot combine --bundle and --subrepos'))
2566
2578
2567 if opts.get('bookmarks'):
2579 if opts.get('bookmarks'):
2568 source, branches = hg.parseurl(ui.expandpath(source),
2580 source, branches = hg.parseurl(ui.expandpath(source),
2569 opts.get('branch'))
2581 opts.get('branch'))
2570 other = hg.repository(hg.remoteui(repo, opts), source)
2582 other = hg.repository(hg.remoteui(repo, opts), source)
2571 if 'bookmarks' not in other.listkeys('namespaces'):
2583 if 'bookmarks' not in other.listkeys('namespaces'):
2572 ui.warn(_("remote doesn't support bookmarks\n"))
2584 ui.warn(_("remote doesn't support bookmarks\n"))
2573 return 0
2585 return 0
2574 ui.status(_('comparing with %s\n') % url.hidepassword(source))
2586 ui.status(_('comparing with %s\n') % url.hidepassword(source))
2575 return bookmarks.diff(ui, repo, other)
2587 return bookmarks.diff(ui, repo, other)
2576
2588
2577 ret = hg.incoming(ui, repo, source, opts)
2589 ret = hg.incoming(ui, repo, source, opts)
2578 return ret
2590 return ret
2579
2591
2580 def init(ui, dest=".", **opts):
2592 def init(ui, dest=".", **opts):
2581 """create a new repository in the given directory
2593 """create a new repository in the given directory
2582
2594
2583 Initialize a new repository in the given directory. If the given
2595 Initialize a new repository in the given directory. If the given
2584 directory does not exist, it will be created.
2596 directory does not exist, it will be created.
2585
2597
2586 If no directory is given, the current directory is used.
2598 If no directory is given, the current directory is used.
2587
2599
2588 It is possible to specify an ``ssh://`` URL as the destination.
2600 It is possible to specify an ``ssh://`` URL as the destination.
2589 See :hg:`help urls` for more information.
2601 See :hg:`help urls` for more information.
2590
2602
2591 Returns 0 on success.
2603 Returns 0 on success.
2592 """
2604 """
2593 hg.repository(hg.remoteui(ui, opts), ui.expandpath(dest), create=1)
2605 hg.repository(hg.remoteui(ui, opts), ui.expandpath(dest), create=1)
2594
2606
2595 def locate(ui, repo, *pats, **opts):
2607 def locate(ui, repo, *pats, **opts):
2596 """locate files matching specific patterns
2608 """locate files matching specific patterns
2597
2609
2598 Print files under Mercurial control in the working directory whose
2610 Print files under Mercurial control in the working directory whose
2599 names match the given patterns.
2611 names match the given patterns.
2600
2612
2601 By default, this command searches all directories in the working
2613 By default, this command searches all directories in the working
2602 directory. To search just the current directory and its
2614 directory. To search just the current directory and its
2603 subdirectories, use "--include .".
2615 subdirectories, use "--include .".
2604
2616
2605 If no patterns are given to match, this command prints the names
2617 If no patterns are given to match, this command prints the names
2606 of all files under Mercurial control in the working directory.
2618 of all files under Mercurial control in the working directory.
2607
2619
2608 If you want to feed the output of this command into the "xargs"
2620 If you want to feed the output of this command into the "xargs"
2609 command, use the -0 option to both this command and "xargs". This
2621 command, use the -0 option to both this command and "xargs". This
2610 will avoid the problem of "xargs" treating single filenames that
2622 will avoid the problem of "xargs" treating single filenames that
2611 contain whitespace as multiple filenames.
2623 contain whitespace as multiple filenames.
2612
2624
2613 Returns 0 if a match is found, 1 otherwise.
2625 Returns 0 if a match is found, 1 otherwise.
2614 """
2626 """
2615 end = opts.get('print0') and '\0' or '\n'
2627 end = opts.get('print0') and '\0' or '\n'
2616 rev = cmdutil.revsingle(repo, opts.get('rev'), None).node()
2628 rev = cmdutil.revsingle(repo, opts.get('rev'), None).node()
2617
2629
2618 ret = 1
2630 ret = 1
2619 m = cmdutil.match(repo, pats, opts, default='relglob')
2631 m = cmdutil.match(repo, pats, opts, default='relglob')
2620 m.bad = lambda x, y: False
2632 m.bad = lambda x, y: False
2621 for abs in repo[rev].walk(m):
2633 for abs in repo[rev].walk(m):
2622 if not rev and abs not in repo.dirstate:
2634 if not rev and abs not in repo.dirstate:
2623 continue
2635 continue
2624 if opts.get('fullpath'):
2636 if opts.get('fullpath'):
2625 ui.write(repo.wjoin(abs), end)
2637 ui.write(repo.wjoin(abs), end)
2626 else:
2638 else:
2627 ui.write(((pats and m.rel(abs)) or abs), end)
2639 ui.write(((pats and m.rel(abs)) or abs), end)
2628 ret = 0
2640 ret = 0
2629
2641
2630 return ret
2642 return ret
2631
2643
2632 def log(ui, repo, *pats, **opts):
2644 def log(ui, repo, *pats, **opts):
2633 """show revision history of entire repository or files
2645 """show revision history of entire repository or files
2634
2646
2635 Print the revision history of the specified files or the entire
2647 Print the revision history of the specified files or the entire
2636 project.
2648 project.
2637
2649
2638 File history is shown without following rename or copy history of
2650 File history is shown without following rename or copy history of
2639 files. Use -f/--follow with a filename to follow history across
2651 files. Use -f/--follow with a filename to follow history across
2640 renames and copies. --follow without a filename will only show
2652 renames and copies. --follow without a filename will only show
2641 ancestors or descendants of the starting revision. --follow-first
2653 ancestors or descendants of the starting revision. --follow-first
2642 only follows the first parent of merge revisions.
2654 only follows the first parent of merge revisions.
2643
2655
2644 If no revision range is specified, the default is ``tip:0`` unless
2656 If no revision range is specified, the default is ``tip:0`` unless
2645 --follow is set, in which case the working directory parent is
2657 --follow is set, in which case the working directory parent is
2646 used as the starting revision. You can specify a revision set for
2658 used as the starting revision. You can specify a revision set for
2647 log, see :hg:`help revsets` for more information.
2659 log, see :hg:`help revsets` for more information.
2648
2660
2649 See :hg:`help dates` for a list of formats valid for -d/--date.
2661 See :hg:`help dates` for a list of formats valid for -d/--date.
2650
2662
2651 By default this command prints revision number and changeset id,
2663 By default this command prints revision number and changeset id,
2652 tags, non-trivial parents, user, date and time, and a summary for
2664 tags, non-trivial parents, user, date and time, and a summary for
2653 each commit. When the -v/--verbose switch is used, the list of
2665 each commit. When the -v/--verbose switch is used, the list of
2654 changed files and full commit message are shown.
2666 changed files and full commit message are shown.
2655
2667
2656 .. note::
2668 .. note::
2657 log -p/--patch may generate unexpected diff output for merge
2669 log -p/--patch may generate unexpected diff output for merge
2658 changesets, as it will only compare the merge changeset against
2670 changesets, as it will only compare the merge changeset against
2659 its first parent. Also, only files different from BOTH parents
2671 its first parent. Also, only files different from BOTH parents
2660 will appear in files:.
2672 will appear in files:.
2661
2673
2662 Returns 0 on success.
2674 Returns 0 on success.
2663 """
2675 """
2664
2676
2665 matchfn = cmdutil.match(repo, pats, opts)
2677 matchfn = cmdutil.match(repo, pats, opts)
2666 limit = cmdutil.loglimit(opts)
2678 limit = cmdutil.loglimit(opts)
2667 count = 0
2679 count = 0
2668
2680
2669 endrev = None
2681 endrev = None
2670 if opts.get('copies') and opts.get('rev'):
2682 if opts.get('copies') and opts.get('rev'):
2671 endrev = max(cmdutil.revrange(repo, opts.get('rev'))) + 1
2683 endrev = max(cmdutil.revrange(repo, opts.get('rev'))) + 1
2672
2684
2673 df = False
2685 df = False
2674 if opts["date"]:
2686 if opts["date"]:
2675 df = util.matchdate(opts["date"])
2687 df = util.matchdate(opts["date"])
2676
2688
2677 branches = opts.get('branch', []) + opts.get('only_branch', [])
2689 branches = opts.get('branch', []) + opts.get('only_branch', [])
2678 opts['branch'] = [repo.lookupbranch(b) for b in branches]
2690 opts['branch'] = [repo.lookupbranch(b) for b in branches]
2679
2691
2680 displayer = cmdutil.show_changeset(ui, repo, opts, True)
2692 displayer = cmdutil.show_changeset(ui, repo, opts, True)
2681 def prep(ctx, fns):
2693 def prep(ctx, fns):
2682 rev = ctx.rev()
2694 rev = ctx.rev()
2683 parents = [p for p in repo.changelog.parentrevs(rev)
2695 parents = [p for p in repo.changelog.parentrevs(rev)
2684 if p != nullrev]
2696 if p != nullrev]
2685 if opts.get('no_merges') and len(parents) == 2:
2697 if opts.get('no_merges') and len(parents) == 2:
2686 return
2698 return
2687 if opts.get('only_merges') and len(parents) != 2:
2699 if opts.get('only_merges') and len(parents) != 2:
2688 return
2700 return
2689 if opts.get('branch') and ctx.branch() not in opts['branch']:
2701 if opts.get('branch') and ctx.branch() not in opts['branch']:
2690 return
2702 return
2691 if df and not df(ctx.date()[0]):
2703 if df and not df(ctx.date()[0]):
2692 return
2704 return
2693 if opts['user'] and not [k for k in opts['user']
2705 if opts['user'] and not [k for k in opts['user']
2694 if k.lower() in ctx.user().lower()]:
2706 if k.lower() in ctx.user().lower()]:
2695 return
2707 return
2696 if opts.get('keyword'):
2708 if opts.get('keyword'):
2697 for k in [kw.lower() for kw in opts['keyword']]:
2709 for k in [kw.lower() for kw in opts['keyword']]:
2698 if (k in ctx.user().lower() or
2710 if (k in ctx.user().lower() or
2699 k in ctx.description().lower() or
2711 k in ctx.description().lower() or
2700 k in " ".join(ctx.files()).lower()):
2712 k in " ".join(ctx.files()).lower()):
2701 break
2713 break
2702 else:
2714 else:
2703 return
2715 return
2704
2716
2705 copies = None
2717 copies = None
2706 if opts.get('copies') and rev:
2718 if opts.get('copies') and rev:
2707 copies = []
2719 copies = []
2708 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
2720 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
2709 for fn in ctx.files():
2721 for fn in ctx.files():
2710 rename = getrenamed(fn, rev)
2722 rename = getrenamed(fn, rev)
2711 if rename:
2723 if rename:
2712 copies.append((fn, rename[0]))
2724 copies.append((fn, rename[0]))
2713
2725
2714 revmatchfn = None
2726 revmatchfn = None
2715 if opts.get('patch') or opts.get('stat'):
2727 if opts.get('patch') or opts.get('stat'):
2716 if opts.get('follow') or opts.get('follow_first'):
2728 if opts.get('follow') or opts.get('follow_first'):
2717 # note: this might be wrong when following through merges
2729 # note: this might be wrong when following through merges
2718 revmatchfn = cmdutil.match(repo, fns, default='path')
2730 revmatchfn = cmdutil.match(repo, fns, default='path')
2719 else:
2731 else:
2720 revmatchfn = matchfn
2732 revmatchfn = matchfn
2721
2733
2722 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
2734 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
2723
2735
2724 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
2736 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
2725 if count == limit:
2737 if count == limit:
2726 break
2738 break
2727 if displayer.flush(ctx.rev()):
2739 if displayer.flush(ctx.rev()):
2728 count += 1
2740 count += 1
2729 displayer.close()
2741 displayer.close()
2730
2742
2731 def manifest(ui, repo, node=None, rev=None):
2743 def manifest(ui, repo, node=None, rev=None):
2732 """output the current or given revision of the project manifest
2744 """output the current or given revision of the project manifest
2733
2745
2734 Print a list of version controlled files for the given revision.
2746 Print a list of version controlled files for the given revision.
2735 If no revision is given, the first parent of the working directory
2747 If no revision is given, the first parent of the working directory
2736 is used, or the null revision if no revision is checked out.
2748 is used, or the null revision if no revision is checked out.
2737
2749
2738 With -v, print file permissions, symlink and executable bits.
2750 With -v, print file permissions, symlink and executable bits.
2739 With --debug, print file revision hashes.
2751 With --debug, print file revision hashes.
2740
2752
2741 Returns 0 on success.
2753 Returns 0 on success.
2742 """
2754 """
2743
2755
2744 if rev and node:
2756 if rev and node:
2745 raise util.Abort(_("please specify just one revision"))
2757 raise util.Abort(_("please specify just one revision"))
2746
2758
2747 if not node:
2759 if not node:
2748 node = rev
2760 node = rev
2749
2761
2750 decor = {'l':'644 @ ', 'x':'755 * ', '':'644 '}
2762 decor = {'l':'644 @ ', 'x':'755 * ', '':'644 '}
2751 ctx = cmdutil.revsingle(repo, node)
2763 ctx = cmdutil.revsingle(repo, node)
2752 for f in ctx:
2764 for f in ctx:
2753 if ui.debugflag:
2765 if ui.debugflag:
2754 ui.write("%40s " % hex(ctx.manifest()[f]))
2766 ui.write("%40s " % hex(ctx.manifest()[f]))
2755 if ui.verbose:
2767 if ui.verbose:
2756 ui.write(decor[ctx.flags(f)])
2768 ui.write(decor[ctx.flags(f)])
2757 ui.write("%s\n" % f)
2769 ui.write("%s\n" % f)
2758
2770
2759 def merge(ui, repo, node=None, **opts):
2771 def merge(ui, repo, node=None, **opts):
2760 """merge working directory with another revision
2772 """merge working directory with another revision
2761
2773
2762 The current working directory is updated with all changes made in
2774 The current working directory is updated with all changes made in
2763 the requested revision since the last common predecessor revision.
2775 the requested revision since the last common predecessor revision.
2764
2776
2765 Files that changed between either parent are marked as changed for
2777 Files that changed between either parent are marked as changed for
2766 the next commit and a commit must be performed before any further
2778 the next commit and a commit must be performed before any further
2767 updates to the repository are allowed. The next commit will have
2779 updates to the repository are allowed. The next commit will have
2768 two parents.
2780 two parents.
2769
2781
2770 ``--tool`` can be used to specify the merge tool used for file
2782 ``--tool`` can be used to specify the merge tool used for file
2771 merges. It overrides the HGMERGE environment variable and your
2783 merges. It overrides the HGMERGE environment variable and your
2772 configuration files.
2784 configuration files.
2773
2785
2774 If no revision is specified, the working directory's parent is a
2786 If no revision is specified, the working directory's parent is a
2775 head revision, and the current branch contains exactly one other
2787 head revision, and the current branch contains exactly one other
2776 head, the other head is merged with by default. Otherwise, an
2788 head, the other head is merged with by default. Otherwise, an
2777 explicit revision with which to merge with must be provided.
2789 explicit revision with which to merge with must be provided.
2778
2790
2779 :hg:`resolve` must be used to resolve unresolved files.
2791 :hg:`resolve` must be used to resolve unresolved files.
2780
2792
2781 To undo an uncommitted merge, use :hg:`update --clean .` which
2793 To undo an uncommitted merge, use :hg:`update --clean .` which
2782 will check out a clean copy of the original merge parent, losing
2794 will check out a clean copy of the original merge parent, losing
2783 all changes.
2795 all changes.
2784
2796
2785 Returns 0 on success, 1 if there are unresolved files.
2797 Returns 0 on success, 1 if there are unresolved files.
2786 """
2798 """
2787
2799
2788 if opts.get('rev') and node:
2800 if opts.get('rev') and node:
2789 raise util.Abort(_("please specify just one revision"))
2801 raise util.Abort(_("please specify just one revision"))
2790 if not node:
2802 if not node:
2791 node = opts.get('rev')
2803 node = opts.get('rev')
2792
2804
2793 if not node:
2805 if not node:
2794 branch = repo[None].branch()
2806 branch = repo[None].branch()
2795 bheads = repo.branchheads(branch)
2807 bheads = repo.branchheads(branch)
2796 if len(bheads) > 2:
2808 if len(bheads) > 2:
2797 raise util.Abort(_(
2809 raise util.Abort(_(
2798 'branch \'%s\' has %d heads - '
2810 'branch \'%s\' has %d heads - '
2799 'please merge with an explicit rev\n'
2811 'please merge with an explicit rev\n'
2800 '(run \'hg heads .\' to see heads)')
2812 '(run \'hg heads .\' to see heads)')
2801 % (branch, len(bheads)))
2813 % (branch, len(bheads)))
2802
2814
2803 parent = repo.dirstate.parents()[0]
2815 parent = repo.dirstate.parents()[0]
2804 if len(bheads) == 1:
2816 if len(bheads) == 1:
2805 if len(repo.heads()) > 1:
2817 if len(repo.heads()) > 1:
2806 raise util.Abort(_(
2818 raise util.Abort(_(
2807 'branch \'%s\' has one head - '
2819 'branch \'%s\' has one head - '
2808 'please merge with an explicit rev\n'
2820 'please merge with an explicit rev\n'
2809 '(run \'hg heads\' to see all heads)')
2821 '(run \'hg heads\' to see all heads)')
2810 % branch)
2822 % branch)
2811 msg = _('there is nothing to merge')
2823 msg = _('there is nothing to merge')
2812 if parent != repo.lookup(repo[None].branch()):
2824 if parent != repo.lookup(repo[None].branch()):
2813 msg = _('%s - use "hg update" instead') % msg
2825 msg = _('%s - use "hg update" instead') % msg
2814 raise util.Abort(msg)
2826 raise util.Abort(msg)
2815
2827
2816 if parent not in bheads:
2828 if parent not in bheads:
2817 raise util.Abort(_('working dir not at a head rev - '
2829 raise util.Abort(_('working dir not at a head rev - '
2818 'use "hg update" or merge with an explicit rev'))
2830 'use "hg update" or merge with an explicit rev'))
2819 node = parent == bheads[0] and bheads[-1] or bheads[0]
2831 node = parent == bheads[0] and bheads[-1] or bheads[0]
2820 else:
2832 else:
2821 node = cmdutil.revsingle(repo, node).node()
2833 node = cmdutil.revsingle(repo, node).node()
2822
2834
2823 if opts.get('preview'):
2835 if opts.get('preview'):
2824 # find nodes that are ancestors of p2 but not of p1
2836 # find nodes that are ancestors of p2 but not of p1
2825 p1 = repo.lookup('.')
2837 p1 = repo.lookup('.')
2826 p2 = repo.lookup(node)
2838 p2 = repo.lookup(node)
2827 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
2839 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
2828
2840
2829 displayer = cmdutil.show_changeset(ui, repo, opts)
2841 displayer = cmdutil.show_changeset(ui, repo, opts)
2830 for node in nodes:
2842 for node in nodes:
2831 displayer.show(repo[node])
2843 displayer.show(repo[node])
2832 displayer.close()
2844 displayer.close()
2833 return 0
2845 return 0
2834
2846
2835 try:
2847 try:
2836 # ui.forcemerge is an internal variable, do not document
2848 # ui.forcemerge is an internal variable, do not document
2837 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
2849 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
2838 return hg.merge(repo, node, force=opts.get('force'))
2850 return hg.merge(repo, node, force=opts.get('force'))
2839 finally:
2851 finally:
2840 ui.setconfig('ui', 'forcemerge', '')
2852 ui.setconfig('ui', 'forcemerge', '')
2841
2853
2842 def outgoing(ui, repo, dest=None, **opts):
2854 def outgoing(ui, repo, dest=None, **opts):
2843 """show changesets not found in the destination
2855 """show changesets not found in the destination
2844
2856
2845 Show changesets not found in the specified destination repository
2857 Show changesets not found in the specified destination repository
2846 or the default push location. These are the changesets that would
2858 or the default push location. These are the changesets that would
2847 be pushed if a push was requested.
2859 be pushed if a push was requested.
2848
2860
2849 See pull for details of valid destination formats.
2861 See pull for details of valid destination formats.
2850
2862
2851 Returns 0 if there are outgoing changes, 1 otherwise.
2863 Returns 0 if there are outgoing changes, 1 otherwise.
2852 """
2864 """
2853
2865
2854 if opts.get('bookmarks'):
2866 if opts.get('bookmarks'):
2855 dest = ui.expandpath(dest or 'default-push', dest or 'default')
2867 dest = ui.expandpath(dest or 'default-push', dest or 'default')
2856 dest, branches = hg.parseurl(dest, opts.get('branch'))
2868 dest, branches = hg.parseurl(dest, opts.get('branch'))
2857 other = hg.repository(hg.remoteui(repo, opts), dest)
2869 other = hg.repository(hg.remoteui(repo, opts), dest)
2858 if 'bookmarks' not in other.listkeys('namespaces'):
2870 if 'bookmarks' not in other.listkeys('namespaces'):
2859 ui.warn(_("remote doesn't support bookmarks\n"))
2871 ui.warn(_("remote doesn't support bookmarks\n"))
2860 return 0
2872 return 0
2861 ui.status(_('comparing with %s\n') % url.hidepassword(dest))
2873 ui.status(_('comparing with %s\n') % url.hidepassword(dest))
2862 return bookmarks.diff(ui, other, repo)
2874 return bookmarks.diff(ui, other, repo)
2863
2875
2864 ret = hg.outgoing(ui, repo, dest, opts)
2876 ret = hg.outgoing(ui, repo, dest, opts)
2865 return ret
2877 return ret
2866
2878
2867 def parents(ui, repo, file_=None, **opts):
2879 def parents(ui, repo, file_=None, **opts):
2868 """show the parents of the working directory or revision
2880 """show the parents of the working directory or revision
2869
2881
2870 Print the working directory's parent revisions. If a revision is
2882 Print the working directory's parent revisions. If a revision is
2871 given via -r/--rev, the parent of that revision will be printed.
2883 given via -r/--rev, the parent of that revision will be printed.
2872 If a file argument is given, the revision in which the file was
2884 If a file argument is given, the revision in which the file was
2873 last changed (before the working directory revision or the
2885 last changed (before the working directory revision or the
2874 argument to --rev if given) is printed.
2886 argument to --rev if given) is printed.
2875
2887
2876 Returns 0 on success.
2888 Returns 0 on success.
2877 """
2889 """
2878
2890
2879 ctx = cmdutil.revsingle(repo, opts.get('rev'), None)
2891 ctx = cmdutil.revsingle(repo, opts.get('rev'), None)
2880
2892
2881 if file_:
2893 if file_:
2882 m = cmdutil.match(repo, (file_,), opts)
2894 m = cmdutil.match(repo, (file_,), opts)
2883 if m.anypats() or len(m.files()) != 1:
2895 if m.anypats() or len(m.files()) != 1:
2884 raise util.Abort(_('can only specify an explicit filename'))
2896 raise util.Abort(_('can only specify an explicit filename'))
2885 file_ = m.files()[0]
2897 file_ = m.files()[0]
2886 filenodes = []
2898 filenodes = []
2887 for cp in ctx.parents():
2899 for cp in ctx.parents():
2888 if not cp:
2900 if not cp:
2889 continue
2901 continue
2890 try:
2902 try:
2891 filenodes.append(cp.filenode(file_))
2903 filenodes.append(cp.filenode(file_))
2892 except error.LookupError:
2904 except error.LookupError:
2893 pass
2905 pass
2894 if not filenodes:
2906 if not filenodes:
2895 raise util.Abort(_("'%s' not found in manifest!") % file_)
2907 raise util.Abort(_("'%s' not found in manifest!") % file_)
2896 fl = repo.file(file_)
2908 fl = repo.file(file_)
2897 p = [repo.lookup(fl.linkrev(fl.rev(fn))) for fn in filenodes]
2909 p = [repo.lookup(fl.linkrev(fl.rev(fn))) for fn in filenodes]
2898 else:
2910 else:
2899 p = [cp.node() for cp in ctx.parents()]
2911 p = [cp.node() for cp in ctx.parents()]
2900
2912
2901 displayer = cmdutil.show_changeset(ui, repo, opts)
2913 displayer = cmdutil.show_changeset(ui, repo, opts)
2902 for n in p:
2914 for n in p:
2903 if n != nullid:
2915 if n != nullid:
2904 displayer.show(repo[n])
2916 displayer.show(repo[n])
2905 displayer.close()
2917 displayer.close()
2906
2918
2907 def paths(ui, repo, search=None):
2919 def paths(ui, repo, search=None):
2908 """show aliases for remote repositories
2920 """show aliases for remote repositories
2909
2921
2910 Show definition of symbolic path name NAME. If no name is given,
2922 Show definition of symbolic path name NAME. If no name is given,
2911 show definition of all available names.
2923 show definition of all available names.
2912
2924
2913 Path names are defined in the [paths] section of your
2925 Path names are defined in the [paths] section of your
2914 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
2926 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
2915 repository, ``.hg/hgrc`` is used, too.
2927 repository, ``.hg/hgrc`` is used, too.
2916
2928
2917 The path names ``default`` and ``default-push`` have a special
2929 The path names ``default`` and ``default-push`` have a special
2918 meaning. When performing a push or pull operation, they are used
2930 meaning. When performing a push or pull operation, they are used
2919 as fallbacks if no location is specified on the command-line.
2931 as fallbacks if no location is specified on the command-line.
2920 When ``default-push`` is set, it will be used for push and
2932 When ``default-push`` is set, it will be used for push and
2921 ``default`` will be used for pull; otherwise ``default`` is used
2933 ``default`` will be used for pull; otherwise ``default`` is used
2922 as the fallback for both. When cloning a repository, the clone
2934 as the fallback for both. When cloning a repository, the clone
2923 source is written as ``default`` in ``.hg/hgrc``. Note that
2935 source is written as ``default`` in ``.hg/hgrc``. Note that
2924 ``default`` and ``default-push`` apply to all inbound (e.g.
2936 ``default`` and ``default-push`` apply to all inbound (e.g.
2925 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email` and
2937 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email` and
2926 :hg:`bundle`) operations.
2938 :hg:`bundle`) operations.
2927
2939
2928 See :hg:`help urls` for more information.
2940 See :hg:`help urls` for more information.
2929
2941
2930 Returns 0 on success.
2942 Returns 0 on success.
2931 """
2943 """
2932 if search:
2944 if search:
2933 for name, path in ui.configitems("paths"):
2945 for name, path in ui.configitems("paths"):
2934 if name == search:
2946 if name == search:
2935 ui.write("%s\n" % url.hidepassword(path))
2947 ui.write("%s\n" % url.hidepassword(path))
2936 return
2948 return
2937 ui.warn(_("not found!\n"))
2949 ui.warn(_("not found!\n"))
2938 return 1
2950 return 1
2939 else:
2951 else:
2940 for name, path in ui.configitems("paths"):
2952 for name, path in ui.configitems("paths"):
2941 ui.write("%s = %s\n" % (name, url.hidepassword(path)))
2953 ui.write("%s = %s\n" % (name, url.hidepassword(path)))
2942
2954
2943 def postincoming(ui, repo, modheads, optupdate, checkout):
2955 def postincoming(ui, repo, modheads, optupdate, checkout):
2944 if modheads == 0:
2956 if modheads == 0:
2945 return
2957 return
2946 if optupdate:
2958 if optupdate:
2947 if (modheads <= 1 or len(repo.branchheads()) == 1) or checkout:
2959 if (modheads <= 1 or len(repo.branchheads()) == 1) or checkout:
2948 return hg.update(repo, checkout)
2960 return hg.update(repo, checkout)
2949 else:
2961 else:
2950 ui.status(_("not updating, since new heads added\n"))
2962 ui.status(_("not updating, since new heads added\n"))
2951 if modheads > 1:
2963 if modheads > 1:
2952 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2964 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2953 else:
2965 else:
2954 ui.status(_("(run 'hg update' to get a working copy)\n"))
2966 ui.status(_("(run 'hg update' to get a working copy)\n"))
2955
2967
2956 def pull(ui, repo, source="default", **opts):
2968 def pull(ui, repo, source="default", **opts):
2957 """pull changes from the specified source
2969 """pull changes from the specified source
2958
2970
2959 Pull changes from a remote repository to a local one.
2971 Pull changes from a remote repository to a local one.
2960
2972
2961 This finds all changes from the repository at the specified path
2973 This finds all changes from the repository at the specified path
2962 or URL and adds them to a local repository (the current one unless
2974 or URL and adds them to a local repository (the current one unless
2963 -R is specified). By default, this does not update the copy of the
2975 -R is specified). By default, this does not update the copy of the
2964 project in the working directory.
2976 project in the working directory.
2965
2977
2966 Use :hg:`incoming` if you want to see what would have been added
2978 Use :hg:`incoming` if you want to see what would have been added
2967 by a pull at the time you issued this command. If you then decide
2979 by a pull at the time you issued this command. If you then decide
2968 to add those changes to the repository, you should use :hg:`pull
2980 to add those changes to the repository, you should use :hg:`pull
2969 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
2981 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
2970
2982
2971 If SOURCE is omitted, the 'default' path will be used.
2983 If SOURCE is omitted, the 'default' path will be used.
2972 See :hg:`help urls` for more information.
2984 See :hg:`help urls` for more information.
2973
2985
2974 Returns 0 on success, 1 if an update had unresolved files.
2986 Returns 0 on success, 1 if an update had unresolved files.
2975 """
2987 """
2976 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
2988 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
2977 other = hg.repository(hg.remoteui(repo, opts), source)
2989 other = hg.repository(hg.remoteui(repo, opts), source)
2978 ui.status(_('pulling from %s\n') % url.hidepassword(source))
2990 ui.status(_('pulling from %s\n') % url.hidepassword(source))
2979 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
2991 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
2980
2992
2981 if opts.get('bookmark'):
2993 if opts.get('bookmark'):
2982 if not revs:
2994 if not revs:
2983 revs = []
2995 revs = []
2984 rb = other.listkeys('bookmarks')
2996 rb = other.listkeys('bookmarks')
2985 for b in opts['bookmark']:
2997 for b in opts['bookmark']:
2986 if b not in rb:
2998 if b not in rb:
2987 raise util.Abort(_('remote bookmark %s not found!') % b)
2999 raise util.Abort(_('remote bookmark %s not found!') % b)
2988 revs.append(rb[b])
3000 revs.append(rb[b])
2989
3001
2990 if revs:
3002 if revs:
2991 try:
3003 try:
2992 revs = [other.lookup(rev) for rev in revs]
3004 revs = [other.lookup(rev) for rev in revs]
2993 except error.CapabilityError:
3005 except error.CapabilityError:
2994 err = _("other repository doesn't support revision lookup, "
3006 err = _("other repository doesn't support revision lookup, "
2995 "so a rev cannot be specified.")
3007 "so a rev cannot be specified.")
2996 raise util.Abort(err)
3008 raise util.Abort(err)
2997
3009
2998 modheads = repo.pull(other, heads=revs, force=opts.get('force'))
3010 modheads = repo.pull(other, heads=revs, force=opts.get('force'))
2999 bookmarks.updatefromremote(ui, repo, other)
3011 bookmarks.updatefromremote(ui, repo, other)
3000 if checkout:
3012 if checkout:
3001 checkout = str(repo.changelog.rev(other.lookup(checkout)))
3013 checkout = str(repo.changelog.rev(other.lookup(checkout)))
3002 repo._subtoppath = source
3014 repo._subtoppath = source
3003 try:
3015 try:
3004 ret = postincoming(ui, repo, modheads, opts.get('update'), checkout)
3016 ret = postincoming(ui, repo, modheads, opts.get('update'), checkout)
3005
3017
3006 finally:
3018 finally:
3007 del repo._subtoppath
3019 del repo._subtoppath
3008
3020
3009 # update specified bookmarks
3021 # update specified bookmarks
3010 if opts.get('bookmark'):
3022 if opts.get('bookmark'):
3011 for b in opts['bookmark']:
3023 for b in opts['bookmark']:
3012 # explicit pull overrides local bookmark if any
3024 # explicit pull overrides local bookmark if any
3013 ui.status(_("importing bookmark %s\n") % b)
3025 ui.status(_("importing bookmark %s\n") % b)
3014 repo._bookmarks[b] = repo[rb[b]].node()
3026 repo._bookmarks[b] = repo[rb[b]].node()
3015 bookmarks.write(repo)
3027 bookmarks.write(repo)
3016
3028
3017 return ret
3029 return ret
3018
3030
3019 def push(ui, repo, dest=None, **opts):
3031 def push(ui, repo, dest=None, **opts):
3020 """push changes to the specified destination
3032 """push changes to the specified destination
3021
3033
3022 Push changesets from the local repository to the specified
3034 Push changesets from the local repository to the specified
3023 destination.
3035 destination.
3024
3036
3025 This operation is symmetrical to pull: it is identical to a pull
3037 This operation is symmetrical to pull: it is identical to a pull
3026 in the destination repository from the current one.
3038 in the destination repository from the current one.
3027
3039
3028 By default, push will not allow creation of new heads at the
3040 By default, push will not allow creation of new heads at the
3029 destination, since multiple heads would make it unclear which head
3041 destination, since multiple heads would make it unclear which head
3030 to use. In this situation, it is recommended to pull and merge
3042 to use. In this situation, it is recommended to pull and merge
3031 before pushing.
3043 before pushing.
3032
3044
3033 Use --new-branch if you want to allow push to create a new named
3045 Use --new-branch if you want to allow push to create a new named
3034 branch that is not present at the destination. This allows you to
3046 branch that is not present at the destination. This allows you to
3035 only create a new branch without forcing other changes.
3047 only create a new branch without forcing other changes.
3036
3048
3037 Use -f/--force to override the default behavior and push all
3049 Use -f/--force to override the default behavior and push all
3038 changesets on all branches.
3050 changesets on all branches.
3039
3051
3040 If -r/--rev is used, the specified revision and all its ancestors
3052 If -r/--rev is used, the specified revision and all its ancestors
3041 will be pushed to the remote repository.
3053 will be pushed to the remote repository.
3042
3054
3043 Please see :hg:`help urls` for important details about ``ssh://``
3055 Please see :hg:`help urls` for important details about ``ssh://``
3044 URLs. If DESTINATION is omitted, a default path will be used.
3056 URLs. If DESTINATION is omitted, a default path will be used.
3045
3057
3046 Returns 0 if push was successful, 1 if nothing to push.
3058 Returns 0 if push was successful, 1 if nothing to push.
3047 """
3059 """
3048
3060
3049 if opts.get('bookmark'):
3061 if opts.get('bookmark'):
3050 for b in opts['bookmark']:
3062 for b in opts['bookmark']:
3051 # translate -B options to -r so changesets get pushed
3063 # translate -B options to -r so changesets get pushed
3052 if b in repo._bookmarks:
3064 if b in repo._bookmarks:
3053 opts.setdefault('rev', []).append(b)
3065 opts.setdefault('rev', []).append(b)
3054 else:
3066 else:
3055 # if we try to push a deleted bookmark, translate it to null
3067 # if we try to push a deleted bookmark, translate it to null
3056 # this lets simultaneous -r, -b options continue working
3068 # this lets simultaneous -r, -b options continue working
3057 opts.setdefault('rev', []).append("null")
3069 opts.setdefault('rev', []).append("null")
3058
3070
3059 dest = ui.expandpath(dest or 'default-push', dest or 'default')
3071 dest = ui.expandpath(dest or 'default-push', dest or 'default')
3060 dest, branches = hg.parseurl(dest, opts.get('branch'))
3072 dest, branches = hg.parseurl(dest, opts.get('branch'))
3061 ui.status(_('pushing to %s\n') % url.hidepassword(dest))
3073 ui.status(_('pushing to %s\n') % url.hidepassword(dest))
3062 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
3074 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
3063 other = hg.repository(hg.remoteui(repo, opts), dest)
3075 other = hg.repository(hg.remoteui(repo, opts), dest)
3064 if revs:
3076 if revs:
3065 revs = [repo.lookup(rev) for rev in revs]
3077 revs = [repo.lookup(rev) for rev in revs]
3066
3078
3067 repo._subtoppath = dest
3079 repo._subtoppath = dest
3068 try:
3080 try:
3069 # push subrepos depth-first for coherent ordering
3081 # push subrepos depth-first for coherent ordering
3070 c = repo['']
3082 c = repo['']
3071 subs = c.substate # only repos that are committed
3083 subs = c.substate # only repos that are committed
3072 for s in sorted(subs):
3084 for s in sorted(subs):
3073 if not c.sub(s).push(opts.get('force')):
3085 if not c.sub(s).push(opts.get('force')):
3074 return False
3086 return False
3075 finally:
3087 finally:
3076 del repo._subtoppath
3088 del repo._subtoppath
3077 result = repo.push(other, opts.get('force'), revs=revs,
3089 result = repo.push(other, opts.get('force'), revs=revs,
3078 newbranch=opts.get('new_branch'))
3090 newbranch=opts.get('new_branch'))
3079
3091
3080 result = (result == 0)
3092 result = (result == 0)
3081
3093
3082 if opts.get('bookmark'):
3094 if opts.get('bookmark'):
3083 rb = other.listkeys('bookmarks')
3095 rb = other.listkeys('bookmarks')
3084 for b in opts['bookmark']:
3096 for b in opts['bookmark']:
3085 # explicit push overrides remote bookmark if any
3097 # explicit push overrides remote bookmark if any
3086 if b in repo._bookmarks:
3098 if b in repo._bookmarks:
3087 ui.status(_("exporting bookmark %s\n") % b)
3099 ui.status(_("exporting bookmark %s\n") % b)
3088 new = repo[b].hex()
3100 new = repo[b].hex()
3089 elif b in rb:
3101 elif b in rb:
3090 ui.status(_("deleting remote bookmark %s\n") % b)
3102 ui.status(_("deleting remote bookmark %s\n") % b)
3091 new = '' # delete
3103 new = '' # delete
3092 else:
3104 else:
3093 ui.warn(_('bookmark %s does not exist on the local '
3105 ui.warn(_('bookmark %s does not exist on the local '
3094 'or remote repository!\n') % b)
3106 'or remote repository!\n') % b)
3095 return 2
3107 return 2
3096 old = rb.get(b, '')
3108 old = rb.get(b, '')
3097 r = other.pushkey('bookmarks', b, old, new)
3109 r = other.pushkey('bookmarks', b, old, new)
3098 if not r:
3110 if not r:
3099 ui.warn(_('updating bookmark %s failed!\n') % b)
3111 ui.warn(_('updating bookmark %s failed!\n') % b)
3100 if not result:
3112 if not result:
3101 result = 2
3113 result = 2
3102
3114
3103 return result
3115 return result
3104
3116
3105 def recover(ui, repo):
3117 def recover(ui, repo):
3106 """roll back an interrupted transaction
3118 """roll back an interrupted transaction
3107
3119
3108 Recover from an interrupted commit or pull.
3120 Recover from an interrupted commit or pull.
3109
3121
3110 This command tries to fix the repository status after an
3122 This command tries to fix the repository status after an
3111 interrupted operation. It should only be necessary when Mercurial
3123 interrupted operation. It should only be necessary when Mercurial
3112 suggests it.
3124 suggests it.
3113
3125
3114 Returns 0 if successful, 1 if nothing to recover or verify fails.
3126 Returns 0 if successful, 1 if nothing to recover or verify fails.
3115 """
3127 """
3116 if repo.recover():
3128 if repo.recover():
3117 return hg.verify(repo)
3129 return hg.verify(repo)
3118 return 1
3130 return 1
3119
3131
3120 def remove(ui, repo, *pats, **opts):
3132 def remove(ui, repo, *pats, **opts):
3121 """remove the specified files on the next commit
3133 """remove the specified files on the next commit
3122
3134
3123 Schedule the indicated files for removal from the repository.
3135 Schedule the indicated files for removal from the repository.
3124
3136
3125 This only removes files from the current branch, not from the
3137 This only removes files from the current branch, not from the
3126 entire project history. -A/--after can be used to remove only
3138 entire project history. -A/--after can be used to remove only
3127 files that have already been deleted, -f/--force can be used to
3139 files that have already been deleted, -f/--force can be used to
3128 force deletion, and -Af can be used to remove files from the next
3140 force deletion, and -Af can be used to remove files from the next
3129 revision without deleting them from the working directory.
3141 revision without deleting them from the working directory.
3130
3142
3131 The following table details the behavior of remove for different
3143 The following table details the behavior of remove for different
3132 file states (columns) and option combinations (rows). The file
3144 file states (columns) and option combinations (rows). The file
3133 states are Added [A], Clean [C], Modified [M] and Missing [!] (as
3145 states are Added [A], Clean [C], Modified [M] and Missing [!] (as
3134 reported by :hg:`status`). The actions are Warn, Remove (from
3146 reported by :hg:`status`). The actions are Warn, Remove (from
3135 branch) and Delete (from disk)::
3147 branch) and Delete (from disk)::
3136
3148
3137 A C M !
3149 A C M !
3138 none W RD W R
3150 none W RD W R
3139 -f R RD RD R
3151 -f R RD RD R
3140 -A W W W R
3152 -A W W W R
3141 -Af R R R R
3153 -Af R R R R
3142
3154
3143 This command schedules the files to be removed at the next commit.
3155 This command schedules the files to be removed at the next commit.
3144 To undo a remove before that, see :hg:`revert`.
3156 To undo a remove before that, see :hg:`revert`.
3145
3157
3146 Returns 0 on success, 1 if any warnings encountered.
3158 Returns 0 on success, 1 if any warnings encountered.
3147 """
3159 """
3148
3160
3149 ret = 0
3161 ret = 0
3150 after, force = opts.get('after'), opts.get('force')
3162 after, force = opts.get('after'), opts.get('force')
3151 if not pats and not after:
3163 if not pats and not after:
3152 raise util.Abort(_('no files specified'))
3164 raise util.Abort(_('no files specified'))
3153
3165
3154 m = cmdutil.match(repo, pats, opts)
3166 m = cmdutil.match(repo, pats, opts)
3155 s = repo.status(match=m, clean=True)
3167 s = repo.status(match=m, clean=True)
3156 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
3168 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
3157
3169
3158 for f in m.files():
3170 for f in m.files():
3159 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
3171 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
3160 ui.warn(_('not removing %s: file is untracked\n') % m.rel(f))
3172 ui.warn(_('not removing %s: file is untracked\n') % m.rel(f))
3161 ret = 1
3173 ret = 1
3162
3174
3163 if force:
3175 if force:
3164 remove, forget = modified + deleted + clean, added
3176 remove, forget = modified + deleted + clean, added
3165 elif after:
3177 elif after:
3166 remove, forget = deleted, []
3178 remove, forget = deleted, []
3167 for f in modified + added + clean:
3179 for f in modified + added + clean:
3168 ui.warn(_('not removing %s: file still exists (use -f'
3180 ui.warn(_('not removing %s: file still exists (use -f'
3169 ' to force removal)\n') % m.rel(f))
3181 ' to force removal)\n') % m.rel(f))
3170 ret = 1
3182 ret = 1
3171 else:
3183 else:
3172 remove, forget = deleted + clean, []
3184 remove, forget = deleted + clean, []
3173 for f in modified:
3185 for f in modified:
3174 ui.warn(_('not removing %s: file is modified (use -f'
3186 ui.warn(_('not removing %s: file is modified (use -f'
3175 ' to force removal)\n') % m.rel(f))
3187 ' to force removal)\n') % m.rel(f))
3176 ret = 1
3188 ret = 1
3177 for f in added:
3189 for f in added:
3178 ui.warn(_('not removing %s: file has been marked for add (use -f'
3190 ui.warn(_('not removing %s: file has been marked for add (use -f'
3179 ' to force removal)\n') % m.rel(f))
3191 ' to force removal)\n') % m.rel(f))
3180 ret = 1
3192 ret = 1
3181
3193
3182 for f in sorted(remove + forget):
3194 for f in sorted(remove + forget):
3183 if ui.verbose or not m.exact(f):
3195 if ui.verbose or not m.exact(f):
3184 ui.status(_('removing %s\n') % m.rel(f))
3196 ui.status(_('removing %s\n') % m.rel(f))
3185
3197
3186 repo[None].forget(forget)
3198 repo[None].forget(forget)
3187 repo[None].remove(remove, unlink=not after)
3199 repo[None].remove(remove, unlink=not after)
3188 return ret
3200 return ret
3189
3201
3190 def rename(ui, repo, *pats, **opts):
3202 def rename(ui, repo, *pats, **opts):
3191 """rename files; equivalent of copy + remove
3203 """rename files; equivalent of copy + remove
3192
3204
3193 Mark dest as copies of sources; mark sources for deletion. If dest
3205 Mark dest as copies of sources; mark sources for deletion. If dest
3194 is a directory, copies are put in that directory. If dest is a
3206 is a directory, copies are put in that directory. If dest is a
3195 file, there can only be one source.
3207 file, there can only be one source.
3196
3208
3197 By default, this command copies the contents of files as they
3209 By default, this command copies the contents of files as they
3198 exist in the working directory. If invoked with -A/--after, the
3210 exist in the working directory. If invoked with -A/--after, the
3199 operation is recorded, but no copying is performed.
3211 operation is recorded, but no copying is performed.
3200
3212
3201 This command takes effect at the next commit. To undo a rename
3213 This command takes effect at the next commit. To undo a rename
3202 before that, see :hg:`revert`.
3214 before that, see :hg:`revert`.
3203
3215
3204 Returns 0 on success, 1 if errors are encountered.
3216 Returns 0 on success, 1 if errors are encountered.
3205 """
3217 """
3206 wlock = repo.wlock(False)
3218 wlock = repo.wlock(False)
3207 try:
3219 try:
3208 return cmdutil.copy(ui, repo, pats, opts, rename=True)
3220 return cmdutil.copy(ui, repo, pats, opts, rename=True)
3209 finally:
3221 finally:
3210 wlock.release()
3222 wlock.release()
3211
3223
3212 def resolve(ui, repo, *pats, **opts):
3224 def resolve(ui, repo, *pats, **opts):
3213 """redo merges or set/view the merge status of files
3225 """redo merges or set/view the merge status of files
3214
3226
3215 Merges with unresolved conflicts are often the result of
3227 Merges with unresolved conflicts are often the result of
3216 non-interactive merging using the ``internal:merge`` configuration
3228 non-interactive merging using the ``internal:merge`` configuration
3217 setting, or a command-line merge tool like ``diff3``. The resolve
3229 setting, or a command-line merge tool like ``diff3``. The resolve
3218 command is used to manage the files involved in a merge, after
3230 command is used to manage the files involved in a merge, after
3219 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
3231 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
3220 working directory must have two parents).
3232 working directory must have two parents).
3221
3233
3222 The resolve command can be used in the following ways:
3234 The resolve command can be used in the following ways:
3223
3235
3224 - :hg:`resolve [--tool TOOL] FILE...`: attempt to re-merge the specified
3236 - :hg:`resolve [--tool TOOL] FILE...`: attempt to re-merge the specified
3225 files, discarding any previous merge attempts. Re-merging is not
3237 files, discarding any previous merge attempts. Re-merging is not
3226 performed for files already marked as resolved. Use ``--all/-a``
3238 performed for files already marked as resolved. Use ``--all/-a``
3227 to selects all unresolved files. ``--tool`` can be used to specify
3239 to selects all unresolved files. ``--tool`` can be used to specify
3228 the merge tool used for the given files. It overrides the HGMERGE
3240 the merge tool used for the given files. It overrides the HGMERGE
3229 environment variable and your configuration files.
3241 environment variable and your configuration files.
3230
3242
3231 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
3243 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
3232 (e.g. after having manually fixed-up the files). The default is
3244 (e.g. after having manually fixed-up the files). The default is
3233 to mark all unresolved files.
3245 to mark all unresolved files.
3234
3246
3235 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
3247 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
3236 default is to mark all resolved files.
3248 default is to mark all resolved files.
3237
3249
3238 - :hg:`resolve -l`: list files which had or still have conflicts.
3250 - :hg:`resolve -l`: list files which had or still have conflicts.
3239 In the printed list, ``U`` = unresolved and ``R`` = resolved.
3251 In the printed list, ``U`` = unresolved and ``R`` = resolved.
3240
3252
3241 Note that Mercurial will not let you commit files with unresolved
3253 Note that Mercurial will not let you commit files with unresolved
3242 merge conflicts. You must use :hg:`resolve -m ...` before you can
3254 merge conflicts. You must use :hg:`resolve -m ...` before you can
3243 commit after a conflicting merge.
3255 commit after a conflicting merge.
3244
3256
3245 Returns 0 on success, 1 if any files fail a resolve attempt.
3257 Returns 0 on success, 1 if any files fail a resolve attempt.
3246 """
3258 """
3247
3259
3248 all, mark, unmark, show, nostatus = \
3260 all, mark, unmark, show, nostatus = \
3249 [opts.get(o) for o in 'all mark unmark list no_status'.split()]
3261 [opts.get(o) for o in 'all mark unmark list no_status'.split()]
3250
3262
3251 if (show and (mark or unmark)) or (mark and unmark):
3263 if (show and (mark or unmark)) or (mark and unmark):
3252 raise util.Abort(_("too many options specified"))
3264 raise util.Abort(_("too many options specified"))
3253 if pats and all:
3265 if pats and all:
3254 raise util.Abort(_("can't specify --all and patterns"))
3266 raise util.Abort(_("can't specify --all and patterns"))
3255 if not (all or pats or show or mark or unmark):
3267 if not (all or pats or show or mark or unmark):
3256 raise util.Abort(_('no files or directories specified; '
3268 raise util.Abort(_('no files or directories specified; '
3257 'use --all to remerge all files'))
3269 'use --all to remerge all files'))
3258
3270
3259 ms = mergemod.mergestate(repo)
3271 ms = mergemod.mergestate(repo)
3260 m = cmdutil.match(repo, pats, opts)
3272 m = cmdutil.match(repo, pats, opts)
3261 ret = 0
3273 ret = 0
3262
3274
3263 for f in ms:
3275 for f in ms:
3264 if m(f):
3276 if m(f):
3265 if show:
3277 if show:
3266 if nostatus:
3278 if nostatus:
3267 ui.write("%s\n" % f)
3279 ui.write("%s\n" % f)
3268 else:
3280 else:
3269 ui.write("%s %s\n" % (ms[f].upper(), f),
3281 ui.write("%s %s\n" % (ms[f].upper(), f),
3270 label='resolve.' +
3282 label='resolve.' +
3271 {'u': 'unresolved', 'r': 'resolved'}[ms[f]])
3283 {'u': 'unresolved', 'r': 'resolved'}[ms[f]])
3272 elif mark:
3284 elif mark:
3273 ms.mark(f, "r")
3285 ms.mark(f, "r")
3274 elif unmark:
3286 elif unmark:
3275 ms.mark(f, "u")
3287 ms.mark(f, "u")
3276 else:
3288 else:
3277 wctx = repo[None]
3289 wctx = repo[None]
3278 mctx = wctx.parents()[-1]
3290 mctx = wctx.parents()[-1]
3279
3291
3280 # backup pre-resolve (merge uses .orig for its own purposes)
3292 # backup pre-resolve (merge uses .orig for its own purposes)
3281 a = repo.wjoin(f)
3293 a = repo.wjoin(f)
3282 util.copyfile(a, a + ".resolve")
3294 util.copyfile(a, a + ".resolve")
3283
3295
3284 try:
3296 try:
3285 # resolve file
3297 # resolve file
3286 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
3298 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
3287 if ms.resolve(f, wctx, mctx):
3299 if ms.resolve(f, wctx, mctx):
3288 ret = 1
3300 ret = 1
3289 finally:
3301 finally:
3290 ui.setconfig('ui', 'forcemerge', '')
3302 ui.setconfig('ui', 'forcemerge', '')
3291
3303
3292 # replace filemerge's .orig file with our resolve file
3304 # replace filemerge's .orig file with our resolve file
3293 util.rename(a + ".resolve", a + ".orig")
3305 util.rename(a + ".resolve", a + ".orig")
3294
3306
3295 ms.commit()
3307 ms.commit()
3296 return ret
3308 return ret
3297
3309
3298 def revert(ui, repo, *pats, **opts):
3310 def revert(ui, repo, *pats, **opts):
3299 """restore individual files or directories to an earlier state
3311 """restore individual files or directories to an earlier state
3300
3312
3301 .. note::
3313 .. note::
3302 This command is most likely not what you are looking for.
3314 This command is most likely not what you are looking for.
3303 Revert will partially overwrite content in the working
3315 Revert will partially overwrite content in the working
3304 directory without changing the working directory parents. Use
3316 directory without changing the working directory parents. Use
3305 :hg:`update -r rev` to check out earlier revisions, or
3317 :hg:`update -r rev` to check out earlier revisions, or
3306 :hg:`update --clean .` to undo a merge which has added another
3318 :hg:`update --clean .` to undo a merge which has added another
3307 parent.
3319 parent.
3308
3320
3309 With no revision specified, revert the named files or directories
3321 With no revision specified, revert the named files or directories
3310 to the contents they had in the parent of the working directory.
3322 to the contents they had in the parent of the working directory.
3311 This restores the contents of the affected files to an unmodified
3323 This restores the contents of the affected files to an unmodified
3312 state and unschedules adds, removes, copies, and renames. If the
3324 state and unschedules adds, removes, copies, and renames. If the
3313 working directory has two parents, you must explicitly specify a
3325 working directory has two parents, you must explicitly specify a
3314 revision.
3326 revision.
3315
3327
3316 Using the -r/--rev option, revert the given files or directories
3328 Using the -r/--rev option, revert the given files or directories
3317 to their contents as of a specific revision. This can be helpful
3329 to their contents as of a specific revision. This can be helpful
3318 to "roll back" some or all of an earlier change. See :hg:`help
3330 to "roll back" some or all of an earlier change. See :hg:`help
3319 dates` for a list of formats valid for -d/--date.
3331 dates` for a list of formats valid for -d/--date.
3320
3332
3321 Revert modifies the working directory. It does not commit any
3333 Revert modifies the working directory. It does not commit any
3322 changes, or change the parent of the working directory. If you
3334 changes, or change the parent of the working directory. If you
3323 revert to a revision other than the parent of the working
3335 revert to a revision other than the parent of the working
3324 directory, the reverted files will thus appear modified
3336 directory, the reverted files will thus appear modified
3325 afterwards.
3337 afterwards.
3326
3338
3327 If a file has been deleted, it is restored. If the executable mode
3339 If a file has been deleted, it is restored. If the executable mode
3328 of a file was changed, it is reset.
3340 of a file was changed, it is reset.
3329
3341
3330 If names are given, all files matching the names are reverted.
3342 If names are given, all files matching the names are reverted.
3331 If no arguments are given, no files are reverted.
3343 If no arguments are given, no files are reverted.
3332
3344
3333 Modified files are saved with a .orig suffix before reverting.
3345 Modified files are saved with a .orig suffix before reverting.
3334 To disable these backups, use --no-backup.
3346 To disable these backups, use --no-backup.
3335
3347
3336 Returns 0 on success.
3348 Returns 0 on success.
3337 """
3349 """
3338
3350
3339 if opts.get("date"):
3351 if opts.get("date"):
3340 if opts.get("rev"):
3352 if opts.get("rev"):
3341 raise util.Abort(_("you can't specify a revision and a date"))
3353 raise util.Abort(_("you can't specify a revision and a date"))
3342 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
3354 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
3343
3355
3344 parent, p2 = repo.dirstate.parents()
3356 parent, p2 = repo.dirstate.parents()
3345 if not opts.get('rev') and p2 != nullid:
3357 if not opts.get('rev') and p2 != nullid:
3346 raise util.Abort(_('uncommitted merge - '
3358 raise util.Abort(_('uncommitted merge - '
3347 'use "hg update", see "hg help revert"'))
3359 'use "hg update", see "hg help revert"'))
3348
3360
3349 if not pats and not opts.get('all'):
3361 if not pats and not opts.get('all'):
3350 raise util.Abort(_('no files or directories specified; '
3362 raise util.Abort(_('no files or directories specified; '
3351 'use --all to revert the whole repo'))
3363 'use --all to revert the whole repo'))
3352
3364
3353 ctx = cmdutil.revsingle(repo, opts.get('rev'))
3365 ctx = cmdutil.revsingle(repo, opts.get('rev'))
3354 node = ctx.node()
3366 node = ctx.node()
3355 mf = ctx.manifest()
3367 mf = ctx.manifest()
3356 if node == parent:
3368 if node == parent:
3357 pmf = mf
3369 pmf = mf
3358 else:
3370 else:
3359 pmf = None
3371 pmf = None
3360
3372
3361 # need all matching names in dirstate and manifest of target rev,
3373 # need all matching names in dirstate and manifest of target rev,
3362 # so have to walk both. do not print errors if files exist in one
3374 # so have to walk both. do not print errors if files exist in one
3363 # but not other.
3375 # but not other.
3364
3376
3365 names = {}
3377 names = {}
3366
3378
3367 wlock = repo.wlock()
3379 wlock = repo.wlock()
3368 try:
3380 try:
3369 # walk dirstate.
3381 # walk dirstate.
3370
3382
3371 m = cmdutil.match(repo, pats, opts)
3383 m = cmdutil.match(repo, pats, opts)
3372 m.bad = lambda x, y: False
3384 m.bad = lambda x, y: False
3373 for abs in repo.walk(m):
3385 for abs in repo.walk(m):
3374 names[abs] = m.rel(abs), m.exact(abs)
3386 names[abs] = m.rel(abs), m.exact(abs)
3375
3387
3376 # walk target manifest.
3388 # walk target manifest.
3377
3389
3378 def badfn(path, msg):
3390 def badfn(path, msg):
3379 if path in names:
3391 if path in names:
3380 return
3392 return
3381 path_ = path + '/'
3393 path_ = path + '/'
3382 for f in names:
3394 for f in names:
3383 if f.startswith(path_):
3395 if f.startswith(path_):
3384 return
3396 return
3385 ui.warn("%s: %s\n" % (m.rel(path), msg))
3397 ui.warn("%s: %s\n" % (m.rel(path), msg))
3386
3398
3387 m = cmdutil.match(repo, pats, opts)
3399 m = cmdutil.match(repo, pats, opts)
3388 m.bad = badfn
3400 m.bad = badfn
3389 for abs in repo[node].walk(m):
3401 for abs in repo[node].walk(m):
3390 if abs not in names:
3402 if abs not in names:
3391 names[abs] = m.rel(abs), m.exact(abs)
3403 names[abs] = m.rel(abs), m.exact(abs)
3392
3404
3393 m = cmdutil.matchfiles(repo, names)
3405 m = cmdutil.matchfiles(repo, names)
3394 changes = repo.status(match=m)[:4]
3406 changes = repo.status(match=m)[:4]
3395 modified, added, removed, deleted = map(set, changes)
3407 modified, added, removed, deleted = map(set, changes)
3396
3408
3397 # if f is a rename, also revert the source
3409 # if f is a rename, also revert the source
3398 cwd = repo.getcwd()
3410 cwd = repo.getcwd()
3399 for f in added:
3411 for f in added:
3400 src = repo.dirstate.copied(f)
3412 src = repo.dirstate.copied(f)
3401 if src and src not in names and repo.dirstate[src] == 'r':
3413 if src and src not in names and repo.dirstate[src] == 'r':
3402 removed.add(src)
3414 removed.add(src)
3403 names[src] = (repo.pathto(src, cwd), True)
3415 names[src] = (repo.pathto(src, cwd), True)
3404
3416
3405 def removeforget(abs):
3417 def removeforget(abs):
3406 if repo.dirstate[abs] == 'a':
3418 if repo.dirstate[abs] == 'a':
3407 return _('forgetting %s\n')
3419 return _('forgetting %s\n')
3408 return _('removing %s\n')
3420 return _('removing %s\n')
3409
3421
3410 revert = ([], _('reverting %s\n'))
3422 revert = ([], _('reverting %s\n'))
3411 add = ([], _('adding %s\n'))
3423 add = ([], _('adding %s\n'))
3412 remove = ([], removeforget)
3424 remove = ([], removeforget)
3413 undelete = ([], _('undeleting %s\n'))
3425 undelete = ([], _('undeleting %s\n'))
3414
3426
3415 disptable = (
3427 disptable = (
3416 # dispatch table:
3428 # dispatch table:
3417 # file state
3429 # file state
3418 # action if in target manifest
3430 # action if in target manifest
3419 # action if not in target manifest
3431 # action if not in target manifest
3420 # make backup if in target manifest
3432 # make backup if in target manifest
3421 # make backup if not in target manifest
3433 # make backup if not in target manifest
3422 (modified, revert, remove, True, True),
3434 (modified, revert, remove, True, True),
3423 (added, revert, remove, True, False),
3435 (added, revert, remove, True, False),
3424 (removed, undelete, None, False, False),
3436 (removed, undelete, None, False, False),
3425 (deleted, revert, remove, False, False),
3437 (deleted, revert, remove, False, False),
3426 )
3438 )
3427
3439
3428 for abs, (rel, exact) in sorted(names.items()):
3440 for abs, (rel, exact) in sorted(names.items()):
3429 mfentry = mf.get(abs)
3441 mfentry = mf.get(abs)
3430 target = repo.wjoin(abs)
3442 target = repo.wjoin(abs)
3431 def handle(xlist, dobackup):
3443 def handle(xlist, dobackup):
3432 xlist[0].append(abs)
3444 xlist[0].append(abs)
3433 if (dobackup and not opts.get('no_backup') and
3445 if (dobackup and not opts.get('no_backup') and
3434 os.path.lexists(target)):
3446 os.path.lexists(target)):
3435 bakname = "%s.orig" % rel
3447 bakname = "%s.orig" % rel
3436 ui.note(_('saving current version of %s as %s\n') %
3448 ui.note(_('saving current version of %s as %s\n') %
3437 (rel, bakname))
3449 (rel, bakname))
3438 if not opts.get('dry_run'):
3450 if not opts.get('dry_run'):
3439 util.rename(target, bakname)
3451 util.rename(target, bakname)
3440 if ui.verbose or not exact:
3452 if ui.verbose or not exact:
3441 msg = xlist[1]
3453 msg = xlist[1]
3442 if not isinstance(msg, basestring):
3454 if not isinstance(msg, basestring):
3443 msg = msg(abs)
3455 msg = msg(abs)
3444 ui.status(msg % rel)
3456 ui.status(msg % rel)
3445 for table, hitlist, misslist, backuphit, backupmiss in disptable:
3457 for table, hitlist, misslist, backuphit, backupmiss in disptable:
3446 if abs not in table:
3458 if abs not in table:
3447 continue
3459 continue
3448 # file has changed in dirstate
3460 # file has changed in dirstate
3449 if mfentry:
3461 if mfentry:
3450 handle(hitlist, backuphit)
3462 handle(hitlist, backuphit)
3451 elif misslist is not None:
3463 elif misslist is not None:
3452 handle(misslist, backupmiss)
3464 handle(misslist, backupmiss)
3453 break
3465 break
3454 else:
3466 else:
3455 if abs not in repo.dirstate:
3467 if abs not in repo.dirstate:
3456 if mfentry:
3468 if mfentry:
3457 handle(add, True)
3469 handle(add, True)
3458 elif exact:
3470 elif exact:
3459 ui.warn(_('file not managed: %s\n') % rel)
3471 ui.warn(_('file not managed: %s\n') % rel)
3460 continue
3472 continue
3461 # file has not changed in dirstate
3473 # file has not changed in dirstate
3462 if node == parent:
3474 if node == parent:
3463 if exact:
3475 if exact:
3464 ui.warn(_('no changes needed to %s\n') % rel)
3476 ui.warn(_('no changes needed to %s\n') % rel)
3465 continue
3477 continue
3466 if pmf is None:
3478 if pmf is None:
3467 # only need parent manifest in this unlikely case,
3479 # only need parent manifest in this unlikely case,
3468 # so do not read by default
3480 # so do not read by default
3469 pmf = repo[parent].manifest()
3481 pmf = repo[parent].manifest()
3470 if abs in pmf:
3482 if abs in pmf:
3471 if mfentry:
3483 if mfentry:
3472 # if version of file is same in parent and target
3484 # if version of file is same in parent and target
3473 # manifests, do nothing
3485 # manifests, do nothing
3474 if (pmf[abs] != mfentry or
3486 if (pmf[abs] != mfentry or
3475 pmf.flags(abs) != mf.flags(abs)):
3487 pmf.flags(abs) != mf.flags(abs)):
3476 handle(revert, False)
3488 handle(revert, False)
3477 else:
3489 else:
3478 handle(remove, False)
3490 handle(remove, False)
3479
3491
3480 if not opts.get('dry_run'):
3492 if not opts.get('dry_run'):
3481 def checkout(f):
3493 def checkout(f):
3482 fc = ctx[f]
3494 fc = ctx[f]
3483 repo.wwrite(f, fc.data(), fc.flags())
3495 repo.wwrite(f, fc.data(), fc.flags())
3484
3496
3485 audit_path = util.path_auditor(repo.root)
3497 audit_path = util.path_auditor(repo.root)
3486 for f in remove[0]:
3498 for f in remove[0]:
3487 if repo.dirstate[f] == 'a':
3499 if repo.dirstate[f] == 'a':
3488 repo.dirstate.forget(f)
3500 repo.dirstate.forget(f)
3489 continue
3501 continue
3490 audit_path(f)
3502 audit_path(f)
3491 try:
3503 try:
3492 util.unlinkpath(repo.wjoin(f))
3504 util.unlinkpath(repo.wjoin(f))
3493 except OSError:
3505 except OSError:
3494 pass
3506 pass
3495 repo.dirstate.remove(f)
3507 repo.dirstate.remove(f)
3496
3508
3497 normal = None
3509 normal = None
3498 if node == parent:
3510 if node == parent:
3499 # We're reverting to our parent. If possible, we'd like status
3511 # We're reverting to our parent. If possible, we'd like status
3500 # to report the file as clean. We have to use normallookup for
3512 # to report the file as clean. We have to use normallookup for
3501 # merges to avoid losing information about merged/dirty files.
3513 # merges to avoid losing information about merged/dirty files.
3502 if p2 != nullid:
3514 if p2 != nullid:
3503 normal = repo.dirstate.normallookup
3515 normal = repo.dirstate.normallookup
3504 else:
3516 else:
3505 normal = repo.dirstate.normal
3517 normal = repo.dirstate.normal
3506 for f in revert[0]:
3518 for f in revert[0]:
3507 checkout(f)
3519 checkout(f)
3508 if normal:
3520 if normal:
3509 normal(f)
3521 normal(f)
3510
3522
3511 for f in add[0]:
3523 for f in add[0]:
3512 checkout(f)
3524 checkout(f)
3513 repo.dirstate.add(f)
3525 repo.dirstate.add(f)
3514
3526
3515 normal = repo.dirstate.normallookup
3527 normal = repo.dirstate.normallookup
3516 if node == parent and p2 == nullid:
3528 if node == parent and p2 == nullid:
3517 normal = repo.dirstate.normal
3529 normal = repo.dirstate.normal
3518 for f in undelete[0]:
3530 for f in undelete[0]:
3519 checkout(f)
3531 checkout(f)
3520 normal(f)
3532 normal(f)
3521
3533
3522 finally:
3534 finally:
3523 wlock.release()
3535 wlock.release()
3524
3536
3525 def rollback(ui, repo, **opts):
3537 def rollback(ui, repo, **opts):
3526 """roll back the last transaction (dangerous)
3538 """roll back the last transaction (dangerous)
3527
3539
3528 This command should be used with care. There is only one level of
3540 This command should be used with care. There is only one level of
3529 rollback, and there is no way to undo a rollback. It will also
3541 rollback, and there is no way to undo a rollback. It will also
3530 restore the dirstate at the time of the last transaction, losing
3542 restore the dirstate at the time of the last transaction, losing
3531 any dirstate changes since that time. This command does not alter
3543 any dirstate changes since that time. This command does not alter
3532 the working directory.
3544 the working directory.
3533
3545
3534 Transactions are used to encapsulate the effects of all commands
3546 Transactions are used to encapsulate the effects of all commands
3535 that create new changesets or propagate existing changesets into a
3547 that create new changesets or propagate existing changesets into a
3536 repository. For example, the following commands are transactional,
3548 repository. For example, the following commands are transactional,
3537 and their effects can be rolled back:
3549 and their effects can be rolled back:
3538
3550
3539 - commit
3551 - commit
3540 - import
3552 - import
3541 - pull
3553 - pull
3542 - push (with this repository as the destination)
3554 - push (with this repository as the destination)
3543 - unbundle
3555 - unbundle
3544
3556
3545 This command is not intended for use on public repositories. Once
3557 This command is not intended for use on public repositories. Once
3546 changes are visible for pull by other users, rolling a transaction
3558 changes are visible for pull by other users, rolling a transaction
3547 back locally is ineffective (someone else may already have pulled
3559 back locally is ineffective (someone else may already have pulled
3548 the changes). Furthermore, a race is possible with readers of the
3560 the changes). Furthermore, a race is possible with readers of the
3549 repository; for example an in-progress pull from the repository
3561 repository; for example an in-progress pull from the repository
3550 may fail if a rollback is performed.
3562 may fail if a rollback is performed.
3551
3563
3552 Returns 0 on success, 1 if no rollback data is available.
3564 Returns 0 on success, 1 if no rollback data is available.
3553 """
3565 """
3554 return repo.rollback(opts.get('dry_run'))
3566 return repo.rollback(opts.get('dry_run'))
3555
3567
3556 def root(ui, repo):
3568 def root(ui, repo):
3557 """print the root (top) of the current working directory
3569 """print the root (top) of the current working directory
3558
3570
3559 Print the root directory of the current repository.
3571 Print the root directory of the current repository.
3560
3572
3561 Returns 0 on success.
3573 Returns 0 on success.
3562 """
3574 """
3563 ui.write(repo.root + "\n")
3575 ui.write(repo.root + "\n")
3564
3576
3565 def serve(ui, repo, **opts):
3577 def serve(ui, repo, **opts):
3566 """start stand-alone webserver
3578 """start stand-alone webserver
3567
3579
3568 Start a local HTTP repository browser and pull server. You can use
3580 Start a local HTTP repository browser and pull server. You can use
3569 this for ad-hoc sharing and browsing of repositories. It is
3581 this for ad-hoc sharing and browsing of repositories. It is
3570 recommended to use a real web server to serve a repository for
3582 recommended to use a real web server to serve a repository for
3571 longer periods of time.
3583 longer periods of time.
3572
3584
3573 Please note that the server does not implement access control.
3585 Please note that the server does not implement access control.
3574 This means that, by default, anybody can read from the server and
3586 This means that, by default, anybody can read from the server and
3575 nobody can write to it by default. Set the ``web.allow_push``
3587 nobody can write to it by default. Set the ``web.allow_push``
3576 option to ``*`` to allow everybody to push to the server. You
3588 option to ``*`` to allow everybody to push to the server. You
3577 should use a real web server if you need to authenticate users.
3589 should use a real web server if you need to authenticate users.
3578
3590
3579 By default, the server logs accesses to stdout and errors to
3591 By default, the server logs accesses to stdout and errors to
3580 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
3592 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
3581 files.
3593 files.
3582
3594
3583 To have the server choose a free port number to listen on, specify
3595 To have the server choose a free port number to listen on, specify
3584 a port number of 0; in this case, the server will print the port
3596 a port number of 0; in this case, the server will print the port
3585 number it uses.
3597 number it uses.
3586
3598
3587 Returns 0 on success.
3599 Returns 0 on success.
3588 """
3600 """
3589
3601
3590 if opts["stdio"]:
3602 if opts["stdio"]:
3591 if repo is None:
3603 if repo is None:
3592 raise error.RepoError(_("There is no Mercurial repository here"
3604 raise error.RepoError(_("There is no Mercurial repository here"
3593 " (.hg not found)"))
3605 " (.hg not found)"))
3594 s = sshserver.sshserver(ui, repo)
3606 s = sshserver.sshserver(ui, repo)
3595 s.serve_forever()
3607 s.serve_forever()
3596
3608
3597 # this way we can check if something was given in the command-line
3609 # this way we can check if something was given in the command-line
3598 if opts.get('port'):
3610 if opts.get('port'):
3599 opts['port'] = util.getport(opts.get('port'))
3611 opts['port'] = util.getport(opts.get('port'))
3600
3612
3601 baseui = repo and repo.baseui or ui
3613 baseui = repo and repo.baseui or ui
3602 optlist = ("name templates style address port prefix ipv6"
3614 optlist = ("name templates style address port prefix ipv6"
3603 " accesslog errorlog certificate encoding")
3615 " accesslog errorlog certificate encoding")
3604 for o in optlist.split():
3616 for o in optlist.split():
3605 val = opts.get(o, '')
3617 val = opts.get(o, '')
3606 if val in (None, ''): # should check against default options instead
3618 if val in (None, ''): # should check against default options instead
3607 continue
3619 continue
3608 baseui.setconfig("web", o, val)
3620 baseui.setconfig("web", o, val)
3609 if repo and repo.ui != baseui:
3621 if repo and repo.ui != baseui:
3610 repo.ui.setconfig("web", o, val)
3622 repo.ui.setconfig("web", o, val)
3611
3623
3612 o = opts.get('web_conf') or opts.get('webdir_conf')
3624 o = opts.get('web_conf') or opts.get('webdir_conf')
3613 if not o:
3625 if not o:
3614 if not repo:
3626 if not repo:
3615 raise error.RepoError(_("There is no Mercurial repository"
3627 raise error.RepoError(_("There is no Mercurial repository"
3616 " here (.hg not found)"))
3628 " here (.hg not found)"))
3617 o = repo.root
3629 o = repo.root
3618
3630
3619 app = hgweb.hgweb(o, baseui=ui)
3631 app = hgweb.hgweb(o, baseui=ui)
3620
3632
3621 class service(object):
3633 class service(object):
3622 def init(self):
3634 def init(self):
3623 util.set_signal_handler()
3635 util.set_signal_handler()
3624 self.httpd = hgweb.server.create_server(ui, app)
3636 self.httpd = hgweb.server.create_server(ui, app)
3625
3637
3626 if opts['port'] and not ui.verbose:
3638 if opts['port'] and not ui.verbose:
3627 return
3639 return
3628
3640
3629 if self.httpd.prefix:
3641 if self.httpd.prefix:
3630 prefix = self.httpd.prefix.strip('/') + '/'
3642 prefix = self.httpd.prefix.strip('/') + '/'
3631 else:
3643 else:
3632 prefix = ''
3644 prefix = ''
3633
3645
3634 port = ':%d' % self.httpd.port
3646 port = ':%d' % self.httpd.port
3635 if port == ':80':
3647 if port == ':80':
3636 port = ''
3648 port = ''
3637
3649
3638 bindaddr = self.httpd.addr
3650 bindaddr = self.httpd.addr
3639 if bindaddr == '0.0.0.0':
3651 if bindaddr == '0.0.0.0':
3640 bindaddr = '*'
3652 bindaddr = '*'
3641 elif ':' in bindaddr: # IPv6
3653 elif ':' in bindaddr: # IPv6
3642 bindaddr = '[%s]' % bindaddr
3654 bindaddr = '[%s]' % bindaddr
3643
3655
3644 fqaddr = self.httpd.fqaddr
3656 fqaddr = self.httpd.fqaddr
3645 if ':' in fqaddr:
3657 if ':' in fqaddr:
3646 fqaddr = '[%s]' % fqaddr
3658 fqaddr = '[%s]' % fqaddr
3647 if opts['port']:
3659 if opts['port']:
3648 write = ui.status
3660 write = ui.status
3649 else:
3661 else:
3650 write = ui.write
3662 write = ui.write
3651 write(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
3663 write(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
3652 (fqaddr, port, prefix, bindaddr, self.httpd.port))
3664 (fqaddr, port, prefix, bindaddr, self.httpd.port))
3653
3665
3654 def run(self):
3666 def run(self):
3655 self.httpd.serve_forever()
3667 self.httpd.serve_forever()
3656
3668
3657 service = service()
3669 service = service()
3658
3670
3659 cmdutil.service(opts, initfn=service.init, runfn=service.run)
3671 cmdutil.service(opts, initfn=service.init, runfn=service.run)
3660
3672
3661 def status(ui, repo, *pats, **opts):
3673 def status(ui, repo, *pats, **opts):
3662 """show changed files in the working directory
3674 """show changed files in the working directory
3663
3675
3664 Show status of files in the repository. If names are given, only
3676 Show status of files in the repository. If names are given, only
3665 files that match are shown. Files that are clean or ignored or
3677 files that match are shown. Files that are clean or ignored or
3666 the source of a copy/move operation, are not listed unless
3678 the source of a copy/move operation, are not listed unless
3667 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
3679 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
3668 Unless options described with "show only ..." are given, the
3680 Unless options described with "show only ..." are given, the
3669 options -mardu are used.
3681 options -mardu are used.
3670
3682
3671 Option -q/--quiet hides untracked (unknown and ignored) files
3683 Option -q/--quiet hides untracked (unknown and ignored) files
3672 unless explicitly requested with -u/--unknown or -i/--ignored.
3684 unless explicitly requested with -u/--unknown or -i/--ignored.
3673
3685
3674 .. note::
3686 .. note::
3675 status may appear to disagree with diff if permissions have
3687 status may appear to disagree with diff if permissions have
3676 changed or a merge has occurred. The standard diff format does
3688 changed or a merge has occurred. The standard diff format does
3677 not report permission changes and diff only reports changes
3689 not report permission changes and diff only reports changes
3678 relative to one merge parent.
3690 relative to one merge parent.
3679
3691
3680 If one revision is given, it is used as the base revision.
3692 If one revision is given, it is used as the base revision.
3681 If two revisions are given, the differences between them are
3693 If two revisions are given, the differences between them are
3682 shown. The --change option can also be used as a shortcut to list
3694 shown. The --change option can also be used as a shortcut to list
3683 the changed files of a revision from its first parent.
3695 the changed files of a revision from its first parent.
3684
3696
3685 The codes used to show the status of files are::
3697 The codes used to show the status of files are::
3686
3698
3687 M = modified
3699 M = modified
3688 A = added
3700 A = added
3689 R = removed
3701 R = removed
3690 C = clean
3702 C = clean
3691 ! = missing (deleted by non-hg command, but still tracked)
3703 ! = missing (deleted by non-hg command, but still tracked)
3692 ? = not tracked
3704 ? = not tracked
3693 I = ignored
3705 I = ignored
3694 = origin of the previous file listed as A (added)
3706 = origin of the previous file listed as A (added)
3695
3707
3696 Returns 0 on success.
3708 Returns 0 on success.
3697 """
3709 """
3698
3710
3699 revs = opts.get('rev')
3711 revs = opts.get('rev')
3700 change = opts.get('change')
3712 change = opts.get('change')
3701
3713
3702 if revs and change:
3714 if revs and change:
3703 msg = _('cannot specify --rev and --change at the same time')
3715 msg = _('cannot specify --rev and --change at the same time')
3704 raise util.Abort(msg)
3716 raise util.Abort(msg)
3705 elif change:
3717 elif change:
3706 node2 = repo.lookup(change)
3718 node2 = repo.lookup(change)
3707 node1 = repo[node2].parents()[0].node()
3719 node1 = repo[node2].parents()[0].node()
3708 else:
3720 else:
3709 node1, node2 = cmdutil.revpair(repo, revs)
3721 node1, node2 = cmdutil.revpair(repo, revs)
3710
3722
3711 cwd = (pats and repo.getcwd()) or ''
3723 cwd = (pats and repo.getcwd()) or ''
3712 end = opts.get('print0') and '\0' or '\n'
3724 end = opts.get('print0') and '\0' or '\n'
3713 copy = {}
3725 copy = {}
3714 states = 'modified added removed deleted unknown ignored clean'.split()
3726 states = 'modified added removed deleted unknown ignored clean'.split()
3715 show = [k for k in states if opts.get(k)]
3727 show = [k for k in states if opts.get(k)]
3716 if opts.get('all'):
3728 if opts.get('all'):
3717 show += ui.quiet and (states[:4] + ['clean']) or states
3729 show += ui.quiet and (states[:4] + ['clean']) or states
3718 if not show:
3730 if not show:
3719 show = ui.quiet and states[:4] or states[:5]
3731 show = ui.quiet and states[:4] or states[:5]
3720
3732
3721 stat = repo.status(node1, node2, cmdutil.match(repo, pats, opts),
3733 stat = repo.status(node1, node2, cmdutil.match(repo, pats, opts),
3722 'ignored' in show, 'clean' in show, 'unknown' in show,
3734 'ignored' in show, 'clean' in show, 'unknown' in show,
3723 opts.get('subrepos'))
3735 opts.get('subrepos'))
3724 changestates = zip(states, 'MAR!?IC', stat)
3736 changestates = zip(states, 'MAR!?IC', stat)
3725
3737
3726 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
3738 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
3727 ctxn = repo[nullid]
3739 ctxn = repo[nullid]
3728 ctx1 = repo[node1]
3740 ctx1 = repo[node1]
3729 ctx2 = repo[node2]
3741 ctx2 = repo[node2]
3730 added = stat[1]
3742 added = stat[1]
3731 if node2 is None:
3743 if node2 is None:
3732 added = stat[0] + stat[1] # merged?
3744 added = stat[0] + stat[1] # merged?
3733
3745
3734 for k, v in copies.copies(repo, ctx1, ctx2, ctxn)[0].iteritems():
3746 for k, v in copies.copies(repo, ctx1, ctx2, ctxn)[0].iteritems():
3735 if k in added:
3747 if k in added:
3736 copy[k] = v
3748 copy[k] = v
3737 elif v in added:
3749 elif v in added:
3738 copy[v] = k
3750 copy[v] = k
3739
3751
3740 for state, char, files in changestates:
3752 for state, char, files in changestates:
3741 if state in show:
3753 if state in show:
3742 format = "%s %%s%s" % (char, end)
3754 format = "%s %%s%s" % (char, end)
3743 if opts.get('no_status'):
3755 if opts.get('no_status'):
3744 format = "%%s%s" % end
3756 format = "%%s%s" % end
3745
3757
3746 for f in files:
3758 for f in files:
3747 ui.write(format % repo.pathto(f, cwd),
3759 ui.write(format % repo.pathto(f, cwd),
3748 label='status.' + state)
3760 label='status.' + state)
3749 if f in copy:
3761 if f in copy:
3750 ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end),
3762 ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end),
3751 label='status.copied')
3763 label='status.copied')
3752
3764
3753 def summary(ui, repo, **opts):
3765 def summary(ui, repo, **opts):
3754 """summarize working directory state
3766 """summarize working directory state
3755
3767
3756 This generates a brief summary of the working directory state,
3768 This generates a brief summary of the working directory state,
3757 including parents, branch, commit status, and available updates.
3769 including parents, branch, commit status, and available updates.
3758
3770
3759 With the --remote option, this will check the default paths for
3771 With the --remote option, this will check the default paths for
3760 incoming and outgoing changes. This can be time-consuming.
3772 incoming and outgoing changes. This can be time-consuming.
3761
3773
3762 Returns 0 on success.
3774 Returns 0 on success.
3763 """
3775 """
3764
3776
3765 ctx = repo[None]
3777 ctx = repo[None]
3766 parents = ctx.parents()
3778 parents = ctx.parents()
3767 pnode = parents[0].node()
3779 pnode = parents[0].node()
3768
3780
3769 for p in parents:
3781 for p in parents:
3770 # label with log.changeset (instead of log.parent) since this
3782 # label with log.changeset (instead of log.parent) since this
3771 # shows a working directory parent *changeset*:
3783 # shows a working directory parent *changeset*:
3772 ui.write(_('parent: %d:%s ') % (p.rev(), str(p)),
3784 ui.write(_('parent: %d:%s ') % (p.rev(), str(p)),
3773 label='log.changeset')
3785 label='log.changeset')
3774 ui.write(' '.join(p.tags()), label='log.tag')
3786 ui.write(' '.join(p.tags()), label='log.tag')
3775 if p.bookmarks():
3787 if p.bookmarks():
3776 ui.write(' ' + ' '.join(p.bookmarks()), label='log.bookmark')
3788 ui.write(' ' + ' '.join(p.bookmarks()), label='log.bookmark')
3777 if p.rev() == -1:
3789 if p.rev() == -1:
3778 if not len(repo):
3790 if not len(repo):
3779 ui.write(_(' (empty repository)'))
3791 ui.write(_(' (empty repository)'))
3780 else:
3792 else:
3781 ui.write(_(' (no revision checked out)'))
3793 ui.write(_(' (no revision checked out)'))
3782 ui.write('\n')
3794 ui.write('\n')
3783 if p.description():
3795 if p.description():
3784 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
3796 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
3785 label='log.summary')
3797 label='log.summary')
3786
3798
3787 branch = ctx.branch()
3799 branch = ctx.branch()
3788 bheads = repo.branchheads(branch)
3800 bheads = repo.branchheads(branch)
3789 m = _('branch: %s\n') % branch
3801 m = _('branch: %s\n') % branch
3790 if branch != 'default':
3802 if branch != 'default':
3791 ui.write(m, label='log.branch')
3803 ui.write(m, label='log.branch')
3792 else:
3804 else:
3793 ui.status(m, label='log.branch')
3805 ui.status(m, label='log.branch')
3794
3806
3795 st = list(repo.status(unknown=True))[:6]
3807 st = list(repo.status(unknown=True))[:6]
3796
3808
3797 c = repo.dirstate.copies()
3809 c = repo.dirstate.copies()
3798 copied, renamed = [], []
3810 copied, renamed = [], []
3799 for d, s in c.iteritems():
3811 for d, s in c.iteritems():
3800 if s in st[2]:
3812 if s in st[2]:
3801 st[2].remove(s)
3813 st[2].remove(s)
3802 renamed.append(d)
3814 renamed.append(d)
3803 else:
3815 else:
3804 copied.append(d)
3816 copied.append(d)
3805 if d in st[1]:
3817 if d in st[1]:
3806 st[1].remove(d)
3818 st[1].remove(d)
3807 st.insert(3, renamed)
3819 st.insert(3, renamed)
3808 st.insert(4, copied)
3820 st.insert(4, copied)
3809
3821
3810 ms = mergemod.mergestate(repo)
3822 ms = mergemod.mergestate(repo)
3811 st.append([f for f in ms if ms[f] == 'u'])
3823 st.append([f for f in ms if ms[f] == 'u'])
3812
3824
3813 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
3825 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
3814 st.append(subs)
3826 st.append(subs)
3815
3827
3816 labels = [ui.label(_('%d modified'), 'status.modified'),
3828 labels = [ui.label(_('%d modified'), 'status.modified'),
3817 ui.label(_('%d added'), 'status.added'),
3829 ui.label(_('%d added'), 'status.added'),
3818 ui.label(_('%d removed'), 'status.removed'),
3830 ui.label(_('%d removed'), 'status.removed'),
3819 ui.label(_('%d renamed'), 'status.copied'),
3831 ui.label(_('%d renamed'), 'status.copied'),
3820 ui.label(_('%d copied'), 'status.copied'),
3832 ui.label(_('%d copied'), 'status.copied'),
3821 ui.label(_('%d deleted'), 'status.deleted'),
3833 ui.label(_('%d deleted'), 'status.deleted'),
3822 ui.label(_('%d unknown'), 'status.unknown'),
3834 ui.label(_('%d unknown'), 'status.unknown'),
3823 ui.label(_('%d ignored'), 'status.ignored'),
3835 ui.label(_('%d ignored'), 'status.ignored'),
3824 ui.label(_('%d unresolved'), 'resolve.unresolved'),
3836 ui.label(_('%d unresolved'), 'resolve.unresolved'),
3825 ui.label(_('%d subrepos'), 'status.modified')]
3837 ui.label(_('%d subrepos'), 'status.modified')]
3826 t = []
3838 t = []
3827 for s, l in zip(st, labels):
3839 for s, l in zip(st, labels):
3828 if s:
3840 if s:
3829 t.append(l % len(s))
3841 t.append(l % len(s))
3830
3842
3831 t = ', '.join(t)
3843 t = ', '.join(t)
3832 cleanworkdir = False
3844 cleanworkdir = False
3833
3845
3834 if len(parents) > 1:
3846 if len(parents) > 1:
3835 t += _(' (merge)')
3847 t += _(' (merge)')
3836 elif branch != parents[0].branch():
3848 elif branch != parents[0].branch():
3837 t += _(' (new branch)')
3849 t += _(' (new branch)')
3838 elif (parents[0].extra().get('close') and
3850 elif (parents[0].extra().get('close') and
3839 pnode in repo.branchheads(branch, closed=True)):
3851 pnode in repo.branchheads(branch, closed=True)):
3840 t += _(' (head closed)')
3852 t += _(' (head closed)')
3841 elif not (st[0] or st[1] or st[2] or st[3] or st[4] or st[9]):
3853 elif not (st[0] or st[1] or st[2] or st[3] or st[4] or st[9]):
3842 t += _(' (clean)')
3854 t += _(' (clean)')
3843 cleanworkdir = True
3855 cleanworkdir = True
3844 elif pnode not in bheads:
3856 elif pnode not in bheads:
3845 t += _(' (new branch head)')
3857 t += _(' (new branch head)')
3846
3858
3847 if cleanworkdir:
3859 if cleanworkdir:
3848 ui.status(_('commit: %s\n') % t.strip())
3860 ui.status(_('commit: %s\n') % t.strip())
3849 else:
3861 else:
3850 ui.write(_('commit: %s\n') % t.strip())
3862 ui.write(_('commit: %s\n') % t.strip())
3851
3863
3852 # all ancestors of branch heads - all ancestors of parent = new csets
3864 # all ancestors of branch heads - all ancestors of parent = new csets
3853 new = [0] * len(repo)
3865 new = [0] * len(repo)
3854 cl = repo.changelog
3866 cl = repo.changelog
3855 for a in [cl.rev(n) for n in bheads]:
3867 for a in [cl.rev(n) for n in bheads]:
3856 new[a] = 1
3868 new[a] = 1
3857 for a in cl.ancestors(*[cl.rev(n) for n in bheads]):
3869 for a in cl.ancestors(*[cl.rev(n) for n in bheads]):
3858 new[a] = 1
3870 new[a] = 1
3859 for a in [p.rev() for p in parents]:
3871 for a in [p.rev() for p in parents]:
3860 if a >= 0:
3872 if a >= 0:
3861 new[a] = 0
3873 new[a] = 0
3862 for a in cl.ancestors(*[p.rev() for p in parents]):
3874 for a in cl.ancestors(*[p.rev() for p in parents]):
3863 new[a] = 0
3875 new[a] = 0
3864 new = sum(new)
3876 new = sum(new)
3865
3877
3866 if new == 0:
3878 if new == 0:
3867 ui.status(_('update: (current)\n'))
3879 ui.status(_('update: (current)\n'))
3868 elif pnode not in bheads:
3880 elif pnode not in bheads:
3869 ui.write(_('update: %d new changesets (update)\n') % new)
3881 ui.write(_('update: %d new changesets (update)\n') % new)
3870 else:
3882 else:
3871 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
3883 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
3872 (new, len(bheads)))
3884 (new, len(bheads)))
3873
3885
3874 if opts.get('remote'):
3886 if opts.get('remote'):
3875 t = []
3887 t = []
3876 source, branches = hg.parseurl(ui.expandpath('default'))
3888 source, branches = hg.parseurl(ui.expandpath('default'))
3877 other = hg.repository(hg.remoteui(repo, {}), source)
3889 other = hg.repository(hg.remoteui(repo, {}), source)
3878 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
3890 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
3879 ui.debug('comparing with %s\n' % url.hidepassword(source))
3891 ui.debug('comparing with %s\n' % url.hidepassword(source))
3880 repo.ui.pushbuffer()
3892 repo.ui.pushbuffer()
3881 common, incoming, rheads = discovery.findcommonincoming(repo, other)
3893 common, incoming, rheads = discovery.findcommonincoming(repo, other)
3882 repo.ui.popbuffer()
3894 repo.ui.popbuffer()
3883 if incoming:
3895 if incoming:
3884 t.append(_('1 or more incoming'))
3896 t.append(_('1 or more incoming'))
3885
3897
3886 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
3898 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
3887 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
3899 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
3888 other = hg.repository(hg.remoteui(repo, {}), dest)
3900 other = hg.repository(hg.remoteui(repo, {}), dest)
3889 ui.debug('comparing with %s\n' % url.hidepassword(dest))
3901 ui.debug('comparing with %s\n' % url.hidepassword(dest))
3890 repo.ui.pushbuffer()
3902 repo.ui.pushbuffer()
3891 o = discovery.findoutgoing(repo, other)
3903 o = discovery.findoutgoing(repo, other)
3892 repo.ui.popbuffer()
3904 repo.ui.popbuffer()
3893 o = repo.changelog.nodesbetween(o, None)[0]
3905 o = repo.changelog.nodesbetween(o, None)[0]
3894 if o:
3906 if o:
3895 t.append(_('%d outgoing') % len(o))
3907 t.append(_('%d outgoing') % len(o))
3896 if 'bookmarks' in other.listkeys('namespaces'):
3908 if 'bookmarks' in other.listkeys('namespaces'):
3897 lmarks = repo.listkeys('bookmarks')
3909 lmarks = repo.listkeys('bookmarks')
3898 rmarks = other.listkeys('bookmarks')
3910 rmarks = other.listkeys('bookmarks')
3899 diff = set(rmarks) - set(lmarks)
3911 diff = set(rmarks) - set(lmarks)
3900 if len(diff) > 0:
3912 if len(diff) > 0:
3901 t.append(_('%d incoming bookmarks') % len(diff))
3913 t.append(_('%d incoming bookmarks') % len(diff))
3902 diff = set(lmarks) - set(rmarks)
3914 diff = set(lmarks) - set(rmarks)
3903 if len(diff) > 0:
3915 if len(diff) > 0:
3904 t.append(_('%d outgoing bookmarks') % len(diff))
3916 t.append(_('%d outgoing bookmarks') % len(diff))
3905
3917
3906 if t:
3918 if t:
3907 ui.write(_('remote: %s\n') % (', '.join(t)))
3919 ui.write(_('remote: %s\n') % (', '.join(t)))
3908 else:
3920 else:
3909 ui.status(_('remote: (synced)\n'))
3921 ui.status(_('remote: (synced)\n'))
3910
3922
3911 def tag(ui, repo, name1, *names, **opts):
3923 def tag(ui, repo, name1, *names, **opts):
3912 """add one or more tags for the current or given revision
3924 """add one or more tags for the current or given revision
3913
3925
3914 Name a particular revision using <name>.
3926 Name a particular revision using <name>.
3915
3927
3916 Tags are used to name particular revisions of the repository and are
3928 Tags are used to name particular revisions of the repository and are
3917 very useful to compare different revisions, to go back to significant
3929 very useful to compare different revisions, to go back to significant
3918 earlier versions or to mark branch points as releases, etc. Changing
3930 earlier versions or to mark branch points as releases, etc. Changing
3919 an existing tag is normally disallowed; use -f/--force to override.
3931 an existing tag is normally disallowed; use -f/--force to override.
3920
3932
3921 If no revision is given, the parent of the working directory is
3933 If no revision is given, the parent of the working directory is
3922 used, or tip if no revision is checked out.
3934 used, or tip if no revision is checked out.
3923
3935
3924 To facilitate version control, distribution, and merging of tags,
3936 To facilitate version control, distribution, and merging of tags,
3925 they are stored as a file named ".hgtags" which is managed similarly
3937 they are stored as a file named ".hgtags" which is managed similarly
3926 to other project files and can be hand-edited if necessary. This
3938 to other project files and can be hand-edited if necessary. This
3927 also means that tagging creates a new commit. The file
3939 also means that tagging creates a new commit. The file
3928 ".hg/localtags" is used for local tags (not shared among
3940 ".hg/localtags" is used for local tags (not shared among
3929 repositories).
3941 repositories).
3930
3942
3931 Tag commits are usually made at the head of a branch. If the parent
3943 Tag commits are usually made at the head of a branch. If the parent
3932 of the working directory is not a branch head, :hg:`tag` aborts; use
3944 of the working directory is not a branch head, :hg:`tag` aborts; use
3933 -f/--force to force the tag commit to be based on a non-head
3945 -f/--force to force the tag commit to be based on a non-head
3934 changeset.
3946 changeset.
3935
3947
3936 See :hg:`help dates` for a list of formats valid for -d/--date.
3948 See :hg:`help dates` for a list of formats valid for -d/--date.
3937
3949
3938 Since tag names have priority over branch names during revision
3950 Since tag names have priority over branch names during revision
3939 lookup, using an existing branch name as a tag name is discouraged.
3951 lookup, using an existing branch name as a tag name is discouraged.
3940
3952
3941 Returns 0 on success.
3953 Returns 0 on success.
3942 """
3954 """
3943
3955
3944 rev_ = "."
3956 rev_ = "."
3945 names = [t.strip() for t in (name1,) + names]
3957 names = [t.strip() for t in (name1,) + names]
3946 if len(names) != len(set(names)):
3958 if len(names) != len(set(names)):
3947 raise util.Abort(_('tag names must be unique'))
3959 raise util.Abort(_('tag names must be unique'))
3948 for n in names:
3960 for n in names:
3949 if n in ['tip', '.', 'null']:
3961 if n in ['tip', '.', 'null']:
3950 raise util.Abort(_('the name \'%s\' is reserved') % n)
3962 raise util.Abort(_('the name \'%s\' is reserved') % n)
3951 if not n:
3963 if not n:
3952 raise util.Abort(_('tag names cannot consist entirely of whitespace'))
3964 raise util.Abort(_('tag names cannot consist entirely of whitespace'))
3953 if opts.get('rev') and opts.get('remove'):
3965 if opts.get('rev') and opts.get('remove'):
3954 raise util.Abort(_("--rev and --remove are incompatible"))
3966 raise util.Abort(_("--rev and --remove are incompatible"))
3955 if opts.get('rev'):
3967 if opts.get('rev'):
3956 rev_ = opts['rev']
3968 rev_ = opts['rev']
3957 message = opts.get('message')
3969 message = opts.get('message')
3958 if opts.get('remove'):
3970 if opts.get('remove'):
3959 expectedtype = opts.get('local') and 'local' or 'global'
3971 expectedtype = opts.get('local') and 'local' or 'global'
3960 for n in names:
3972 for n in names:
3961 if not repo.tagtype(n):
3973 if not repo.tagtype(n):
3962 raise util.Abort(_('tag \'%s\' does not exist') % n)
3974 raise util.Abort(_('tag \'%s\' does not exist') % n)
3963 if repo.tagtype(n) != expectedtype:
3975 if repo.tagtype(n) != expectedtype:
3964 if expectedtype == 'global':
3976 if expectedtype == 'global':
3965 raise util.Abort(_('tag \'%s\' is not a global tag') % n)
3977 raise util.Abort(_('tag \'%s\' is not a global tag') % n)
3966 else:
3978 else:
3967 raise util.Abort(_('tag \'%s\' is not a local tag') % n)
3979 raise util.Abort(_('tag \'%s\' is not a local tag') % n)
3968 rev_ = nullid
3980 rev_ = nullid
3969 if not message:
3981 if not message:
3970 # we don't translate commit messages
3982 # we don't translate commit messages
3971 message = 'Removed tag %s' % ', '.join(names)
3983 message = 'Removed tag %s' % ', '.join(names)
3972 elif not opts.get('force'):
3984 elif not opts.get('force'):
3973 for n in names:
3985 for n in names:
3974 if n in repo.tags():
3986 if n in repo.tags():
3975 raise util.Abort(_('tag \'%s\' already exists '
3987 raise util.Abort(_('tag \'%s\' already exists '
3976 '(use -f to force)') % n)
3988 '(use -f to force)') % n)
3977 if not opts.get('local'):
3989 if not opts.get('local'):
3978 p1, p2 = repo.dirstate.parents()
3990 p1, p2 = repo.dirstate.parents()
3979 if p2 != nullid:
3991 if p2 != nullid:
3980 raise util.Abort(_('uncommitted merge'))
3992 raise util.Abort(_('uncommitted merge'))
3981 bheads = repo.branchheads()
3993 bheads = repo.branchheads()
3982 if not opts.get('force') and bheads and p1 not in bheads:
3994 if not opts.get('force') and bheads and p1 not in bheads:
3983 raise util.Abort(_('not at a branch head (use -f to force)'))
3995 raise util.Abort(_('not at a branch head (use -f to force)'))
3984 r = cmdutil.revsingle(repo, rev_).node()
3996 r = cmdutil.revsingle(repo, rev_).node()
3985
3997
3986 if not message:
3998 if not message:
3987 # we don't translate commit messages
3999 # we don't translate commit messages
3988 message = ('Added tag %s for changeset %s' %
4000 message = ('Added tag %s for changeset %s' %
3989 (', '.join(names), short(r)))
4001 (', '.join(names), short(r)))
3990
4002
3991 date = opts.get('date')
4003 date = opts.get('date')
3992 if date:
4004 if date:
3993 date = util.parsedate(date)
4005 date = util.parsedate(date)
3994
4006
3995 if opts.get('edit'):
4007 if opts.get('edit'):
3996 message = ui.edit(message, ui.username())
4008 message = ui.edit(message, ui.username())
3997
4009
3998 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date)
4010 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date)
3999
4011
4000 def tags(ui, repo):
4012 def tags(ui, repo):
4001 """list repository tags
4013 """list repository tags
4002
4014
4003 This lists both regular and local tags. When the -v/--verbose
4015 This lists both regular and local tags. When the -v/--verbose
4004 switch is used, a third column "local" is printed for local tags.
4016 switch is used, a third column "local" is printed for local tags.
4005
4017
4006 Returns 0 on success.
4018 Returns 0 on success.
4007 """
4019 """
4008
4020
4009 hexfunc = ui.debugflag and hex or short
4021 hexfunc = ui.debugflag and hex or short
4010 tagtype = ""
4022 tagtype = ""
4011
4023
4012 for t, n in reversed(repo.tagslist()):
4024 for t, n in reversed(repo.tagslist()):
4013 if ui.quiet:
4025 if ui.quiet:
4014 ui.write("%s\n" % t)
4026 ui.write("%s\n" % t)
4015 continue
4027 continue
4016
4028
4017 try:
4029 try:
4018 hn = hexfunc(n)
4030 hn = hexfunc(n)
4019 r = "%5d:%s" % (repo.changelog.rev(n), hn)
4031 r = "%5d:%s" % (repo.changelog.rev(n), hn)
4020 except error.LookupError:
4032 except error.LookupError:
4021 r = " ?:%s" % hn
4033 r = " ?:%s" % hn
4022 else:
4034 else:
4023 spaces = " " * (30 - encoding.colwidth(t))
4035 spaces = " " * (30 - encoding.colwidth(t))
4024 if ui.verbose:
4036 if ui.verbose:
4025 if repo.tagtype(t) == 'local':
4037 if repo.tagtype(t) == 'local':
4026 tagtype = " local"
4038 tagtype = " local"
4027 else:
4039 else:
4028 tagtype = ""
4040 tagtype = ""
4029 ui.write("%s%s %s%s\n" % (t, spaces, r, tagtype))
4041 ui.write("%s%s %s%s\n" % (t, spaces, r, tagtype))
4030
4042
4031 def tip(ui, repo, **opts):
4043 def tip(ui, repo, **opts):
4032 """show the tip revision
4044 """show the tip revision
4033
4045
4034 The tip revision (usually just called the tip) is the changeset
4046 The tip revision (usually just called the tip) is the changeset
4035 most recently added to the repository (and therefore the most
4047 most recently added to the repository (and therefore the most
4036 recently changed head).
4048 recently changed head).
4037
4049
4038 If you have just made a commit, that commit will be the tip. If
4050 If you have just made a commit, that commit will be the tip. If
4039 you have just pulled changes from another repository, the tip of
4051 you have just pulled changes from another repository, the tip of
4040 that repository becomes the current tip. The "tip" tag is special
4052 that repository becomes the current tip. The "tip" tag is special
4041 and cannot be renamed or assigned to a different changeset.
4053 and cannot be renamed or assigned to a different changeset.
4042
4054
4043 Returns 0 on success.
4055 Returns 0 on success.
4044 """
4056 """
4045 displayer = cmdutil.show_changeset(ui, repo, opts)
4057 displayer = cmdutil.show_changeset(ui, repo, opts)
4046 displayer.show(repo[len(repo) - 1])
4058 displayer.show(repo[len(repo) - 1])
4047 displayer.close()
4059 displayer.close()
4048
4060
4049 def unbundle(ui, repo, fname1, *fnames, **opts):
4061 def unbundle(ui, repo, fname1, *fnames, **opts):
4050 """apply one or more changegroup files
4062 """apply one or more changegroup files
4051
4063
4052 Apply one or more compressed changegroup files generated by the
4064 Apply one or more compressed changegroup files generated by the
4053 bundle command.
4065 bundle command.
4054
4066
4055 Returns 0 on success, 1 if an update has unresolved files.
4067 Returns 0 on success, 1 if an update has unresolved files.
4056 """
4068 """
4057 fnames = (fname1,) + fnames
4069 fnames = (fname1,) + fnames
4058
4070
4059 lock = repo.lock()
4071 lock = repo.lock()
4060 wc = repo['.']
4072 wc = repo['.']
4061 try:
4073 try:
4062 for fname in fnames:
4074 for fname in fnames:
4063 f = url.open(ui, fname)
4075 f = url.open(ui, fname)
4064 gen = changegroup.readbundle(f, fname)
4076 gen = changegroup.readbundle(f, fname)
4065 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname,
4077 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname,
4066 lock=lock)
4078 lock=lock)
4067 bookmarks.updatecurrentbookmark(repo, wc.node(), wc.branch())
4079 bookmarks.updatecurrentbookmark(repo, wc.node(), wc.branch())
4068 finally:
4080 finally:
4069 lock.release()
4081 lock.release()
4070 return postincoming(ui, repo, modheads, opts.get('update'), None)
4082 return postincoming(ui, repo, modheads, opts.get('update'), None)
4071
4083
4072 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False):
4084 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False):
4073 """update working directory (or switch revisions)
4085 """update working directory (or switch revisions)
4074
4086
4075 Update the repository's working directory to the specified
4087 Update the repository's working directory to the specified
4076 changeset. If no changeset is specified, update to the tip of the
4088 changeset. If no changeset is specified, update to the tip of the
4077 current named branch.
4089 current named branch.
4078
4090
4079 If the changeset is not a descendant of the working directory's
4091 If the changeset is not a descendant of the working directory's
4080 parent, the update is aborted. With the -c/--check option, the
4092 parent, the update is aborted. With the -c/--check option, the
4081 working directory is checked for uncommitted changes; if none are
4093 working directory is checked for uncommitted changes; if none are
4082 found, the working directory is updated to the specified
4094 found, the working directory is updated to the specified
4083 changeset.
4095 changeset.
4084
4096
4085 The following rules apply when the working directory contains
4097 The following rules apply when the working directory contains
4086 uncommitted changes:
4098 uncommitted changes:
4087
4099
4088 1. If neither -c/--check nor -C/--clean is specified, and if
4100 1. If neither -c/--check nor -C/--clean is specified, and if
4089 the requested changeset is an ancestor or descendant of
4101 the requested changeset is an ancestor or descendant of
4090 the working directory's parent, the uncommitted changes
4102 the working directory's parent, the uncommitted changes
4091 are merged into the requested changeset and the merged
4103 are merged into the requested changeset and the merged
4092 result is left uncommitted. If the requested changeset is
4104 result is left uncommitted. If the requested changeset is
4093 not an ancestor or descendant (that is, it is on another
4105 not an ancestor or descendant (that is, it is on another
4094 branch), the update is aborted and the uncommitted changes
4106 branch), the update is aborted and the uncommitted changes
4095 are preserved.
4107 are preserved.
4096
4108
4097 2. With the -c/--check option, the update is aborted and the
4109 2. With the -c/--check option, the update is aborted and the
4098 uncommitted changes are preserved.
4110 uncommitted changes are preserved.
4099
4111
4100 3. With the -C/--clean option, uncommitted changes are discarded and
4112 3. With the -C/--clean option, uncommitted changes are discarded and
4101 the working directory is updated to the requested changeset.
4113 the working directory is updated to the requested changeset.
4102
4114
4103 Use null as the changeset to remove the working directory (like
4115 Use null as the changeset to remove the working directory (like
4104 :hg:`clone -U`).
4116 :hg:`clone -U`).
4105
4117
4106 If you want to update just one file to an older changeset, use
4118 If you want to update just one file to an older changeset, use
4107 :hg:`revert`.
4119 :hg:`revert`.
4108
4120
4109 See :hg:`help dates` for a list of formats valid for -d/--date.
4121 See :hg:`help dates` for a list of formats valid for -d/--date.
4110
4122
4111 Returns 0 on success, 1 if there are unresolved files.
4123 Returns 0 on success, 1 if there are unresolved files.
4112 """
4124 """
4113 if rev and node:
4125 if rev and node:
4114 raise util.Abort(_("please specify just one revision"))
4126 raise util.Abort(_("please specify just one revision"))
4115
4127
4116 if rev is None or rev == '':
4128 if rev is None or rev == '':
4117 rev = node
4129 rev = node
4118
4130
4119 # if we defined a bookmark, we have to remember the original bookmark name
4131 # if we defined a bookmark, we have to remember the original bookmark name
4120 brev = rev
4132 brev = rev
4121 rev = cmdutil.revsingle(repo, rev, rev).rev()
4133 rev = cmdutil.revsingle(repo, rev, rev).rev()
4122
4134
4123 if check and clean:
4135 if check and clean:
4124 raise util.Abort(_("cannot specify both -c/--check and -C/--clean"))
4136 raise util.Abort(_("cannot specify both -c/--check and -C/--clean"))
4125
4137
4126 if check:
4138 if check:
4127 # we could use dirty() but we can ignore merge and branch trivia
4139 # we could use dirty() but we can ignore merge and branch trivia
4128 c = repo[None]
4140 c = repo[None]
4129 if c.modified() or c.added() or c.removed():
4141 if c.modified() or c.added() or c.removed():
4130 raise util.Abort(_("uncommitted local changes"))
4142 raise util.Abort(_("uncommitted local changes"))
4131
4143
4132 if date:
4144 if date:
4133 if rev:
4145 if rev:
4134 raise util.Abort(_("you can't specify a revision and a date"))
4146 raise util.Abort(_("you can't specify a revision and a date"))
4135 rev = cmdutil.finddate(ui, repo, date)
4147 rev = cmdutil.finddate(ui, repo, date)
4136
4148
4137 if clean or check:
4149 if clean or check:
4138 ret = hg.clean(repo, rev)
4150 ret = hg.clean(repo, rev)
4139 else:
4151 else:
4140 ret = hg.update(repo, rev)
4152 ret = hg.update(repo, rev)
4141
4153
4142 if brev in repo._bookmarks:
4154 if brev in repo._bookmarks:
4143 bookmarks.setcurrent(repo, brev)
4155 bookmarks.setcurrent(repo, brev)
4144
4156
4145 return ret
4157 return ret
4146
4158
4147 def verify(ui, repo):
4159 def verify(ui, repo):
4148 """verify the integrity of the repository
4160 """verify the integrity of the repository
4149
4161
4150 Verify the integrity of the current repository.
4162 Verify the integrity of the current repository.
4151
4163
4152 This will perform an extensive check of the repository's
4164 This will perform an extensive check of the repository's
4153 integrity, validating the hashes and checksums of each entry in
4165 integrity, validating the hashes and checksums of each entry in
4154 the changelog, manifest, and tracked files, as well as the
4166 the changelog, manifest, and tracked files, as well as the
4155 integrity of their crosslinks and indices.
4167 integrity of their crosslinks and indices.
4156
4168
4157 Returns 0 on success, 1 if errors are encountered.
4169 Returns 0 on success, 1 if errors are encountered.
4158 """
4170 """
4159 return hg.verify(repo)
4171 return hg.verify(repo)
4160
4172
4161 def version_(ui):
4173 def version_(ui):
4162 """output version and copyright information"""
4174 """output version and copyright information"""
4163 ui.write(_("Mercurial Distributed SCM (version %s)\n")
4175 ui.write(_("Mercurial Distributed SCM (version %s)\n")
4164 % util.version())
4176 % util.version())
4165 ui.status(_(
4177 ui.status(_(
4166 "(see http://mercurial.selenic.com for more information)\n"
4178 "(see http://mercurial.selenic.com for more information)\n"
4167 "\nCopyright (C) 2005-2011 Matt Mackall and others\n"
4179 "\nCopyright (C) 2005-2011 Matt Mackall and others\n"
4168 "This is free software; see the source for copying conditions. "
4180 "This is free software; see the source for copying conditions. "
4169 "There is NO\nwarranty; "
4181 "There is NO\nwarranty; "
4170 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
4182 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
4171 ))
4183 ))
4172
4184
4173 # Command options and aliases are listed here, alphabetically
4185 # Command options and aliases are listed here, alphabetically
4174
4186
4175 globalopts = [
4187 globalopts = [
4176 ('R', 'repository', '',
4188 ('R', 'repository', '',
4177 _('repository root directory or name of overlay bundle file'),
4189 _('repository root directory or name of overlay bundle file'),
4178 _('REPO')),
4190 _('REPO')),
4179 ('', 'cwd', '',
4191 ('', 'cwd', '',
4180 _('change working directory'), _('DIR')),
4192 _('change working directory'), _('DIR')),
4181 ('y', 'noninteractive', None,
4193 ('y', 'noninteractive', None,
4182 _('do not prompt, assume \'yes\' for any required answers')),
4194 _('do not prompt, assume \'yes\' for any required answers')),
4183 ('q', 'quiet', None, _('suppress output')),
4195 ('q', 'quiet', None, _('suppress output')),
4184 ('v', 'verbose', None, _('enable additional output')),
4196 ('v', 'verbose', None, _('enable additional output')),
4185 ('', 'config', [],
4197 ('', 'config', [],
4186 _('set/override config option (use \'section.name=value\')'),
4198 _('set/override config option (use \'section.name=value\')'),
4187 _('CONFIG')),
4199 _('CONFIG')),
4188 ('', 'debug', None, _('enable debugging output')),
4200 ('', 'debug', None, _('enable debugging output')),
4189 ('', 'debugger', None, _('start debugger')),
4201 ('', 'debugger', None, _('start debugger')),
4190 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
4202 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
4191 _('ENCODE')),
4203 _('ENCODE')),
4192 ('', 'encodingmode', encoding.encodingmode,
4204 ('', 'encodingmode', encoding.encodingmode,
4193 _('set the charset encoding mode'), _('MODE')),
4205 _('set the charset encoding mode'), _('MODE')),
4194 ('', 'traceback', None, _('always print a traceback on exception')),
4206 ('', 'traceback', None, _('always print a traceback on exception')),
4195 ('', 'time', None, _('time how long the command takes')),
4207 ('', 'time', None, _('time how long the command takes')),
4196 ('', 'profile', None, _('print command execution profile')),
4208 ('', 'profile', None, _('print command execution profile')),
4197 ('', 'version', None, _('output version information and exit')),
4209 ('', 'version', None, _('output version information and exit')),
4198 ('h', 'help', None, _('display help and exit')),
4210 ('h', 'help', None, _('display help and exit')),
4199 ]
4211 ]
4200
4212
4201 dryrunopts = [('n', 'dry-run', None,
4213 dryrunopts = [('n', 'dry-run', None,
4202 _('do not perform actions, just print output'))]
4214 _('do not perform actions, just print output'))]
4203
4215
4204 remoteopts = [
4216 remoteopts = [
4205 ('e', 'ssh', '',
4217 ('e', 'ssh', '',
4206 _('specify ssh command to use'), _('CMD')),
4218 _('specify ssh command to use'), _('CMD')),
4207 ('', 'remotecmd', '',
4219 ('', 'remotecmd', '',
4208 _('specify hg command to run on the remote side'), _('CMD')),
4220 _('specify hg command to run on the remote side'), _('CMD')),
4209 ('', 'insecure', None,
4221 ('', 'insecure', None,
4210 _('do not verify server certificate (ignoring web.cacerts config)')),
4222 _('do not verify server certificate (ignoring web.cacerts config)')),
4211 ]
4223 ]
4212
4224
4213 walkopts = [
4225 walkopts = [
4214 ('I', 'include', [],
4226 ('I', 'include', [],
4215 _('include names matching the given patterns'), _('PATTERN')),
4227 _('include names matching the given patterns'), _('PATTERN')),
4216 ('X', 'exclude', [],
4228 ('X', 'exclude', [],
4217 _('exclude names matching the given patterns'), _('PATTERN')),
4229 _('exclude names matching the given patterns'), _('PATTERN')),
4218 ]
4230 ]
4219
4231
4220 commitopts = [
4232 commitopts = [
4221 ('m', 'message', '',
4233 ('m', 'message', '',
4222 _('use text as commit message'), _('TEXT')),
4234 _('use text as commit message'), _('TEXT')),
4223 ('l', 'logfile', '',
4235 ('l', 'logfile', '',
4224 _('read commit message from file'), _('FILE')),
4236 _('read commit message from file'), _('FILE')),
4225 ]
4237 ]
4226
4238
4227 commitopts2 = [
4239 commitopts2 = [
4228 ('d', 'date', '',
4240 ('d', 'date', '',
4229 _('record datecode as commit date'), _('DATE')),
4241 _('record datecode as commit date'), _('DATE')),
4230 ('u', 'user', '',
4242 ('u', 'user', '',
4231 _('record the specified user as committer'), _('USER')),
4243 _('record the specified user as committer'), _('USER')),
4232 ]
4244 ]
4233
4245
4234 templateopts = [
4246 templateopts = [
4235 ('', 'style', '',
4247 ('', 'style', '',
4236 _('display using template map file'), _('STYLE')),
4248 _('display using template map file'), _('STYLE')),
4237 ('', 'template', '',
4249 ('', 'template', '',
4238 _('display with template'), _('TEMPLATE')),
4250 _('display with template'), _('TEMPLATE')),
4239 ]
4251 ]
4240
4252
4241 logopts = [
4253 logopts = [
4242 ('p', 'patch', None, _('show patch')),
4254 ('p', 'patch', None, _('show patch')),
4243 ('g', 'git', None, _('use git extended diff format')),
4255 ('g', 'git', None, _('use git extended diff format')),
4244 ('l', 'limit', '',
4256 ('l', 'limit', '',
4245 _('limit number of changes displayed'), _('NUM')),
4257 _('limit number of changes displayed'), _('NUM')),
4246 ('M', 'no-merges', None, _('do not show merges')),
4258 ('M', 'no-merges', None, _('do not show merges')),
4247 ('', 'stat', None, _('output diffstat-style summary of changes')),
4259 ('', 'stat', None, _('output diffstat-style summary of changes')),
4248 ] + templateopts
4260 ] + templateopts
4249
4261
4250 diffopts = [
4262 diffopts = [
4251 ('a', 'text', None, _('treat all files as text')),
4263 ('a', 'text', None, _('treat all files as text')),
4252 ('g', 'git', None, _('use git extended diff format')),
4264 ('g', 'git', None, _('use git extended diff format')),
4253 ('', 'nodates', None, _('omit dates from diff headers'))
4265 ('', 'nodates', None, _('omit dates from diff headers'))
4254 ]
4266 ]
4255
4267
4256 diffopts2 = [
4268 diffopts2 = [
4257 ('p', 'show-function', None, _('show which function each change is in')),
4269 ('p', 'show-function', None, _('show which function each change is in')),
4258 ('', 'reverse', None, _('produce a diff that undoes the changes')),
4270 ('', 'reverse', None, _('produce a diff that undoes the changes')),
4259 ('w', 'ignore-all-space', None,
4271 ('w', 'ignore-all-space', None,
4260 _('ignore white space when comparing lines')),
4272 _('ignore white space when comparing lines')),
4261 ('b', 'ignore-space-change', None,
4273 ('b', 'ignore-space-change', None,
4262 _('ignore changes in the amount of white space')),
4274 _('ignore changes in the amount of white space')),
4263 ('B', 'ignore-blank-lines', None,
4275 ('B', 'ignore-blank-lines', None,
4264 _('ignore changes whose lines are all blank')),
4276 _('ignore changes whose lines are all blank')),
4265 ('U', 'unified', '',
4277 ('U', 'unified', '',
4266 _('number of lines of context to show'), _('NUM')),
4278 _('number of lines of context to show'), _('NUM')),
4267 ('', 'stat', None, _('output diffstat-style summary of changes')),
4279 ('', 'stat', None, _('output diffstat-style summary of changes')),
4268 ]
4280 ]
4269
4281
4270 similarityopts = [
4282 similarityopts = [
4271 ('s', 'similarity', '',
4283 ('s', 'similarity', '',
4272 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
4284 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
4273 ]
4285 ]
4274
4286
4275 subrepoopts = [
4287 subrepoopts = [
4276 ('S', 'subrepos', None,
4288 ('S', 'subrepos', None,
4277 _('recurse into subrepositories'))
4289 _('recurse into subrepositories'))
4278 ]
4290 ]
4279
4291
4280 table = {
4292 table = {
4281 "^add": (add, walkopts + subrepoopts + dryrunopts,
4293 "^add": (add, walkopts + subrepoopts + dryrunopts,
4282 _('[OPTION]... [FILE]...')),
4294 _('[OPTION]... [FILE]...')),
4283 "addremove":
4295 "addremove":
4284 (addremove, similarityopts + walkopts + dryrunopts,
4296 (addremove, similarityopts + walkopts + dryrunopts,
4285 _('[OPTION]... [FILE]...')),
4297 _('[OPTION]... [FILE]...')),
4286 "^annotate|blame":
4298 "^annotate|blame":
4287 (annotate,
4299 (annotate,
4288 [('r', 'rev', '',
4300 [('r', 'rev', '',
4289 _('annotate the specified revision'), _('REV')),
4301 _('annotate the specified revision'), _('REV')),
4290 ('', 'follow', None,
4302 ('', 'follow', None,
4291 _('follow copies/renames and list the filename (DEPRECATED)')),
4303 _('follow copies/renames and list the filename (DEPRECATED)')),
4292 ('', 'no-follow', None, _("don't follow copies and renames")),
4304 ('', 'no-follow', None, _("don't follow copies and renames")),
4293 ('a', 'text', None, _('treat all files as text')),
4305 ('a', 'text', None, _('treat all files as text')),
4294 ('u', 'user', None, _('list the author (long with -v)')),
4306 ('u', 'user', None, _('list the author (long with -v)')),
4295 ('f', 'file', None, _('list the filename')),
4307 ('f', 'file', None, _('list the filename')),
4296 ('d', 'date', None, _('list the date (short with -q)')),
4308 ('d', 'date', None, _('list the date (short with -q)')),
4297 ('n', 'number', None, _('list the revision number (default)')),
4309 ('n', 'number', None, _('list the revision number (default)')),
4298 ('c', 'changeset', None, _('list the changeset')),
4310 ('c', 'changeset', None, _('list the changeset')),
4299 ('l', 'line-number', None,
4311 ('l', 'line-number', None,
4300 _('show line number at the first appearance'))
4312 _('show line number at the first appearance'))
4301 ] + walkopts,
4313 ] + walkopts,
4302 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...')),
4314 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...')),
4303 "archive":
4315 "archive":
4304 (archive,
4316 (archive,
4305 [('', 'no-decode', None, _('do not pass files through decoders')),
4317 [('', 'no-decode', None, _('do not pass files through decoders')),
4306 ('p', 'prefix', '',
4318 ('p', 'prefix', '',
4307 _('directory prefix for files in archive'), _('PREFIX')),
4319 _('directory prefix for files in archive'), _('PREFIX')),
4308 ('r', 'rev', '',
4320 ('r', 'rev', '',
4309 _('revision to distribute'), _('REV')),
4321 _('revision to distribute'), _('REV')),
4310 ('t', 'type', '',
4322 ('t', 'type', '',
4311 _('type of distribution to create'), _('TYPE')),
4323 _('type of distribution to create'), _('TYPE')),
4312 ] + subrepoopts + walkopts,
4324 ] + subrepoopts + walkopts,
4313 _('[OPTION]... DEST')),
4325 _('[OPTION]... DEST')),
4314 "backout":
4326 "backout":
4315 (backout,
4327 (backout,
4316 [('', 'merge', None,
4328 [('', 'merge', None,
4317 _('merge with old dirstate parent after backout')),
4329 _('merge with old dirstate parent after backout')),
4318 ('', 'parent', '',
4330 ('', 'parent', '',
4319 _('parent to choose when backing out merge'), _('REV')),
4331 _('parent to choose when backing out merge'), _('REV')),
4320 ('t', 'tool', '',
4332 ('t', 'tool', '',
4321 _('specify merge tool')),
4333 _('specify merge tool')),
4322 ('r', 'rev', '',
4334 ('r', 'rev', '',
4323 _('revision to backout'), _('REV')),
4335 _('revision to backout'), _('REV')),
4324 ] + walkopts + commitopts + commitopts2,
4336 ] + walkopts + commitopts + commitopts2,
4325 _('[OPTION]... [-r] REV')),
4337 _('[OPTION]... [-r] REV')),
4326 "bisect":
4338 "bisect":
4327 (bisect,
4339 (bisect,
4328 [('r', 'reset', False, _('reset bisect state')),
4340 [('r', 'reset', False, _('reset bisect state')),
4329 ('g', 'good', False, _('mark changeset good')),
4341 ('g', 'good', False, _('mark changeset good')),
4330 ('b', 'bad', False, _('mark changeset bad')),
4342 ('b', 'bad', False, _('mark changeset bad')),
4331 ('s', 'skip', False, _('skip testing changeset')),
4343 ('s', 'skip', False, _('skip testing changeset')),
4332 ('e', 'extend', False, _('extend the bisect range')),
4344 ('e', 'extend', False, _('extend the bisect range')),
4333 ('c', 'command', '',
4345 ('c', 'command', '',
4334 _('use command to check changeset state'), _('CMD')),
4346 _('use command to check changeset state'), _('CMD')),
4335 ('U', 'noupdate', False, _('do not update to target'))],
4347 ('U', 'noupdate', False, _('do not update to target'))],
4336 _("[-gbsr] [-U] [-c CMD] [REV]")),
4348 _("[-gbsr] [-U] [-c CMD] [REV]")),
4337 "bookmarks":
4349 "bookmarks":
4338 (bookmark,
4350 (bookmark,
4339 [('f', 'force', False, _('force')),
4351 [('f', 'force', False, _('force')),
4340 ('r', 'rev', '', _('revision'), _('REV')),
4352 ('r', 'rev', '', _('revision'), _('REV')),
4341 ('d', 'delete', False, _('delete a given bookmark')),
4353 ('d', 'delete', False, _('delete a given bookmark')),
4342 ('m', 'rename', '', _('rename a given bookmark'), _('NAME'))],
4354 ('m', 'rename', '', _('rename a given bookmark'), _('NAME'))],
4343 _('hg bookmarks [-f] [-d] [-m NAME] [-r REV] [NAME]')),
4355 _('hg bookmarks [-f] [-d] [-m NAME] [-r REV] [NAME]')),
4344 "branch":
4356 "branch":
4345 (branch,
4357 (branch,
4346 [('f', 'force', None,
4358 [('f', 'force', None,
4347 _('set branch name even if it shadows an existing branch')),
4359 _('set branch name even if it shadows an existing branch')),
4348 ('C', 'clean', None, _('reset branch name to parent branch name'))],
4360 ('C', 'clean', None, _('reset branch name to parent branch name'))],
4349 _('[-fC] [NAME]')),
4361 _('[-fC] [NAME]')),
4350 "branches":
4362 "branches":
4351 (branches,
4363 (branches,
4352 [('a', 'active', False,
4364 [('a', 'active', False,
4353 _('show only branches that have unmerged heads')),
4365 _('show only branches that have unmerged heads')),
4354 ('c', 'closed', False,
4366 ('c', 'closed', False,
4355 _('show normal and closed branches'))],
4367 _('show normal and closed branches'))],
4356 _('[-ac]')),
4368 _('[-ac]')),
4357 "bundle":
4369 "bundle":
4358 (bundle,
4370 (bundle,
4359 [('f', 'force', None,
4371 [('f', 'force', None,
4360 _('run even when the destination is unrelated')),
4372 _('run even when the destination is unrelated')),
4361 ('r', 'rev', [],
4373 ('r', 'rev', [],
4362 _('a changeset intended to be added to the destination'),
4374 _('a changeset intended to be added to the destination'),
4363 _('REV')),
4375 _('REV')),
4364 ('b', 'branch', [],
4376 ('b', 'branch', [],
4365 _('a specific branch you would like to bundle'),
4377 _('a specific branch you would like to bundle'),
4366 _('BRANCH')),
4378 _('BRANCH')),
4367 ('', 'base', [],
4379 ('', 'base', [],
4368 _('a base changeset assumed to be available at the destination'),
4380 _('a base changeset assumed to be available at the destination'),
4369 _('REV')),
4381 _('REV')),
4370 ('a', 'all', None, _('bundle all changesets in the repository')),
4382 ('a', 'all', None, _('bundle all changesets in the repository')),
4371 ('t', 'type', 'bzip2',
4383 ('t', 'type', 'bzip2',
4372 _('bundle compression type to use'), _('TYPE')),
4384 _('bundle compression type to use'), _('TYPE')),
4373 ] + remoteopts,
4385 ] + remoteopts,
4374 _('[-f] [-t TYPE] [-a] [-r REV]... [--base REV]... FILE [DEST]')),
4386 _('[-f] [-t TYPE] [-a] [-r REV]... [--base REV]... FILE [DEST]')),
4375 "cat":
4387 "cat":
4376 (cat,
4388 (cat,
4377 [('o', 'output', '',
4389 [('o', 'output', '',
4378 _('print output to file with formatted name'), _('FORMAT')),
4390 _('print output to file with formatted name'), _('FORMAT')),
4379 ('r', 'rev', '',
4391 ('r', 'rev', '',
4380 _('print the given revision'), _('REV')),
4392 _('print the given revision'), _('REV')),
4381 ('', 'decode', None, _('apply any matching decode filter')),
4393 ('', 'decode', None, _('apply any matching decode filter')),
4382 ] + walkopts,
4394 ] + walkopts,
4383 _('[OPTION]... FILE...')),
4395 _('[OPTION]... FILE...')),
4384 "^clone":
4396 "^clone":
4385 (clone,
4397 (clone,
4386 [('U', 'noupdate', None,
4398 [('U', 'noupdate', None,
4387 _('the clone will include an empty working copy (only a repository)')),
4399 _('the clone will include an empty working copy (only a repository)')),
4388 ('u', 'updaterev', '',
4400 ('u', 'updaterev', '',
4389 _('revision, tag or branch to check out'), _('REV')),
4401 _('revision, tag or branch to check out'), _('REV')),
4390 ('r', 'rev', [],
4402 ('r', 'rev', [],
4391 _('include the specified changeset'), _('REV')),
4403 _('include the specified changeset'), _('REV')),
4392 ('b', 'branch', [],
4404 ('b', 'branch', [],
4393 _('clone only the specified branch'), _('BRANCH')),
4405 _('clone only the specified branch'), _('BRANCH')),
4394 ('', 'pull', None, _('use pull protocol to copy metadata')),
4406 ('', 'pull', None, _('use pull protocol to copy metadata')),
4395 ('', 'uncompressed', None,
4407 ('', 'uncompressed', None,
4396 _('use uncompressed transfer (fast over LAN)')),
4408 _('use uncompressed transfer (fast over LAN)')),
4397 ] + remoteopts,
4409 ] + remoteopts,
4398 _('[OPTION]... SOURCE [DEST]')),
4410 _('[OPTION]... SOURCE [DEST]')),
4399 "^commit|ci":
4411 "^commit|ci":
4400 (commit,
4412 (commit,
4401 [('A', 'addremove', None,
4413 [('A', 'addremove', None,
4402 _('mark new/missing files as added/removed before committing')),
4414 _('mark new/missing files as added/removed before committing')),
4403 ('', 'close-branch', None,
4415 ('', 'close-branch', None,
4404 _('mark a branch as closed, hiding it from the branch list')),
4416 _('mark a branch as closed, hiding it from the branch list')),
4405 ] + walkopts + commitopts + commitopts2,
4417 ] + walkopts + commitopts + commitopts2,
4406 _('[OPTION]... [FILE]...')),
4418 _('[OPTION]... [FILE]...')),
4407 "copy|cp":
4419 "copy|cp":
4408 (copy,
4420 (copy,
4409 [('A', 'after', None, _('record a copy that has already occurred')),
4421 [('A', 'after', None, _('record a copy that has already occurred')),
4410 ('f', 'force', None,
4422 ('f', 'force', None,
4411 _('forcibly copy over an existing managed file')),
4423 _('forcibly copy over an existing managed file')),
4412 ] + walkopts + dryrunopts,
4424 ] + walkopts + dryrunopts,
4413 _('[OPTION]... [SOURCE]... DEST')),
4425 _('[OPTION]... [SOURCE]... DEST')),
4414 "debugancestor": (debugancestor, [], _('[INDEX] REV1 REV2')),
4426 "debugancestor": (debugancestor, [], _('[INDEX] REV1 REV2')),
4415 "debugbuilddag":
4427 "debugbuilddag":
4416 (debugbuilddag,
4428 (debugbuilddag,
4417 [('m', 'mergeable-file', None, _('add single file mergeable changes')),
4429 [('m', 'mergeable-file', None, _('add single file mergeable changes')),
4418 ('a', 'appended-file', None, _('add single file all revs append to')),
4430 ('a', 'appended-file', None, _('add single file all revs append to')),
4419 ('o', 'overwritten-file', None, _('add single file all revs overwrite')),
4431 ('o', 'overwritten-file', None, _('add single file all revs overwrite')),
4420 ('n', 'new-file', None, _('add new file at each rev')),
4432 ('n', 'new-file', None, _('add new file at each rev')),
4421 ],
4433 ],
4422 _('[OPTION]... TEXT')),
4434 _('[OPTION]... TEXT')),
4423 "debugcheckstate": (debugcheckstate, [], ''),
4435 "debugcheckstate": (debugcheckstate, [], ''),
4424 "debugcommands": (debugcommands, [], _('[COMMAND]')),
4436 "debugcommands": (debugcommands, [], _('[COMMAND]')),
4425 "debugcomplete":
4437 "debugcomplete":
4426 (debugcomplete,
4438 (debugcomplete,
4427 [('o', 'options', None, _('show the command options'))],
4439 [('o', 'options', None, _('show the command options'))],
4428 _('[-o] CMD')),
4440 _('[-o] CMD')),
4429 "debugdag":
4441 "debugdag":
4430 (debugdag,
4442 (debugdag,
4431 [('t', 'tags', None, _('use tags as labels')),
4443 [('t', 'tags', None, _('use tags as labels')),
4432 ('b', 'branches', None, _('annotate with branch names')),
4444 ('b', 'branches', None, _('annotate with branch names')),
4433 ('', 'dots', None, _('use dots for runs')),
4445 ('', 'dots', None, _('use dots for runs')),
4434 ('s', 'spaces', None, _('separate elements by spaces')),
4446 ('s', 'spaces', None, _('separate elements by spaces')),
4435 ],
4447 ],
4436 _('[OPTION]... [FILE [REV]...]')),
4448 _('[OPTION]... [FILE [REV]...]')),
4437 "debugdate":
4449 "debugdate":
4438 (debugdate,
4450 (debugdate,
4439 [('e', 'extended', None, _('try extended date formats'))],
4451 [('e', 'extended', None, _('try extended date formats'))],
4440 _('[-e] DATE [RANGE]')),
4452 _('[-e] DATE [RANGE]')),
4441 "debugdata": (debugdata, [], _('FILE REV')),
4453 "debugdata": (debugdata, [], _('FILE REV')),
4442 "debugfsinfo": (debugfsinfo, [], _('[PATH]')),
4454 "debugfsinfo": (debugfsinfo, [], _('[PATH]')),
4443 "debugignore": (debugignore, [], ''),
4455 "debugignore": (debugignore, [], ''),
4444 "debugindex": (debugindex,
4456 "debugindex": (debugindex,
4445 [('f', 'format', 0, _('revlog format'), _('FORMAT'))],
4457 [('f', 'format', 0, _('revlog format'), _('FORMAT'))],
4446 _('FILE')),
4458 _('FILE')),
4447 "debugindexdot": (debugindexdot, [], _('FILE')),
4459 "debugindexdot": (debugindexdot, [], _('FILE')),
4448 "debuginstall": (debuginstall, [], ''),
4460 "debuginstall": (debuginstall, [], ''),
4461 "debugknown": (debugknown, [], _('REPO ID...')),
4449 "debugpushkey": (debugpushkey, [], _('REPO NAMESPACE [KEY OLD NEW]')),
4462 "debugpushkey": (debugpushkey, [], _('REPO NAMESPACE [KEY OLD NEW]')),
4450 "debugrebuildstate":
4463 "debugrebuildstate":
4451 (debugrebuildstate,
4464 (debugrebuildstate,
4452 [('r', 'rev', '',
4465 [('r', 'rev', '',
4453 _('revision to rebuild to'), _('REV'))],
4466 _('revision to rebuild to'), _('REV'))],
4454 _('[-r REV] [REV]')),
4467 _('[-r REV] [REV]')),
4455 "debugrename":
4468 "debugrename":
4456 (debugrename,
4469 (debugrename,
4457 [('r', 'rev', '',
4470 [('r', 'rev', '',
4458 _('revision to debug'), _('REV'))],
4471 _('revision to debug'), _('REV'))],
4459 _('[-r REV] FILE')),
4472 _('[-r REV] FILE')),
4460 "debugrevspec":
4473 "debugrevspec":
4461 (debugrevspec, [], ('REVSPEC')),
4474 (debugrevspec, [], ('REVSPEC')),
4462 "debugsetparents":
4475 "debugsetparents":
4463 (debugsetparents, [], _('REV1 [REV2]')),
4476 (debugsetparents, [], _('REV1 [REV2]')),
4464 "debugstate":
4477 "debugstate":
4465 (debugstate,
4478 (debugstate,
4466 [('', 'nodates', None, _('do not display the saved mtime'))],
4479 [('', 'nodates', None, _('do not display the saved mtime'))],
4467 _('[OPTION]...')),
4480 _('[OPTION]...')),
4468 "debugsub":
4481 "debugsub":
4469 (debugsub,
4482 (debugsub,
4470 [('r', 'rev', '',
4483 [('r', 'rev', '',
4471 _('revision to check'), _('REV'))],
4484 _('revision to check'), _('REV'))],
4472 _('[-r REV] [REV]')),
4485 _('[-r REV] [REV]')),
4473 "debugwalk": (debugwalk, walkopts, _('[OPTION]... [FILE]...')),
4486 "debugwalk": (debugwalk, walkopts, _('[OPTION]... [FILE]...')),
4474 "debugwireargs":
4487 "debugwireargs":
4475 (debugwireargs,
4488 (debugwireargs,
4476 [('', 'three', '', 'three'),
4489 [('', 'three', '', 'three'),
4477 ('', 'four', '', 'four'),
4490 ('', 'four', '', 'four'),
4478 ] + remoteopts,
4491 ] + remoteopts,
4479 _('REPO [OPTIONS]... [ONE [TWO]]')),
4492 _('REPO [OPTIONS]... [ONE [TWO]]')),
4480 "^diff":
4493 "^diff":
4481 (diff,
4494 (diff,
4482 [('r', 'rev', [],
4495 [('r', 'rev', [],
4483 _('revision'), _('REV')),
4496 _('revision'), _('REV')),
4484 ('c', 'change', '',
4497 ('c', 'change', '',
4485 _('change made by revision'), _('REV'))
4498 _('change made by revision'), _('REV'))
4486 ] + diffopts + diffopts2 + walkopts + subrepoopts,
4499 ] + diffopts + diffopts2 + walkopts + subrepoopts,
4487 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...')),
4500 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...')),
4488 "^export":
4501 "^export":
4489 (export,
4502 (export,
4490 [('o', 'output', '',
4503 [('o', 'output', '',
4491 _('print output to file with formatted name'), _('FORMAT')),
4504 _('print output to file with formatted name'), _('FORMAT')),
4492 ('', 'switch-parent', None, _('diff against the second parent')),
4505 ('', 'switch-parent', None, _('diff against the second parent')),
4493 ('r', 'rev', [],
4506 ('r', 'rev', [],
4494 _('revisions to export'), _('REV')),
4507 _('revisions to export'), _('REV')),
4495 ] + diffopts,
4508 ] + diffopts,
4496 _('[OPTION]... [-o OUTFILESPEC] REV...')),
4509 _('[OPTION]... [-o OUTFILESPEC] REV...')),
4497 "^forget":
4510 "^forget":
4498 (forget,
4511 (forget,
4499 [] + walkopts,
4512 [] + walkopts,
4500 _('[OPTION]... FILE...')),
4513 _('[OPTION]... FILE...')),
4501 "grep":
4514 "grep":
4502 (grep,
4515 (grep,
4503 [('0', 'print0', None, _('end fields with NUL')),
4516 [('0', 'print0', None, _('end fields with NUL')),
4504 ('', 'all', None, _('print all revisions that match')),
4517 ('', 'all', None, _('print all revisions that match')),
4505 ('f', 'follow', None,
4518 ('f', 'follow', None,
4506 _('follow changeset history,'
4519 _('follow changeset history,'
4507 ' or file history across copies and renames')),
4520 ' or file history across copies and renames')),
4508 ('i', 'ignore-case', None, _('ignore case when matching')),
4521 ('i', 'ignore-case', None, _('ignore case when matching')),
4509 ('l', 'files-with-matches', None,
4522 ('l', 'files-with-matches', None,
4510 _('print only filenames and revisions that match')),
4523 _('print only filenames and revisions that match')),
4511 ('n', 'line-number', None, _('print matching line numbers')),
4524 ('n', 'line-number', None, _('print matching line numbers')),
4512 ('r', 'rev', [],
4525 ('r', 'rev', [],
4513 _('only search files changed within revision range'), _('REV')),
4526 _('only search files changed within revision range'), _('REV')),
4514 ('u', 'user', None, _('list the author (long with -v)')),
4527 ('u', 'user', None, _('list the author (long with -v)')),
4515 ('d', 'date', None, _('list the date (short with -q)')),
4528 ('d', 'date', None, _('list the date (short with -q)')),
4516 ] + walkopts,
4529 ] + walkopts,
4517 _('[OPTION]... PATTERN [FILE]...')),
4530 _('[OPTION]... PATTERN [FILE]...')),
4518 "heads":
4531 "heads":
4519 (heads,
4532 (heads,
4520 [('r', 'rev', '',
4533 [('r', 'rev', '',
4521 _('show only heads which are descendants of STARTREV'),
4534 _('show only heads which are descendants of STARTREV'),
4522 _('STARTREV')),
4535 _('STARTREV')),
4523 ('t', 'topo', False, _('show topological heads only')),
4536 ('t', 'topo', False, _('show topological heads only')),
4524 ('a', 'active', False,
4537 ('a', 'active', False,
4525 _('show active branchheads only (DEPRECATED)')),
4538 _('show active branchheads only (DEPRECATED)')),
4526 ('c', 'closed', False,
4539 ('c', 'closed', False,
4527 _('show normal and closed branch heads')),
4540 _('show normal and closed branch heads')),
4528 ] + templateopts,
4541 ] + templateopts,
4529 _('[-ac] [-r STARTREV] [REV]...')),
4542 _('[-ac] [-r STARTREV] [REV]...')),
4530 "help": (help_, [], _('[TOPIC]')),
4543 "help": (help_, [], _('[TOPIC]')),
4531 "identify|id":
4544 "identify|id":
4532 (identify,
4545 (identify,
4533 [('r', 'rev', '',
4546 [('r', 'rev', '',
4534 _('identify the specified revision'), _('REV')),
4547 _('identify the specified revision'), _('REV')),
4535 ('n', 'num', None, _('show local revision number')),
4548 ('n', 'num', None, _('show local revision number')),
4536 ('i', 'id', None, _('show global revision id')),
4549 ('i', 'id', None, _('show global revision id')),
4537 ('b', 'branch', None, _('show branch')),
4550 ('b', 'branch', None, _('show branch')),
4538 ('t', 'tags', None, _('show tags')),
4551 ('t', 'tags', None, _('show tags')),
4539 ('B', 'bookmarks', None, _('show bookmarks'))],
4552 ('B', 'bookmarks', None, _('show bookmarks'))],
4540 _('[-nibtB] [-r REV] [SOURCE]')),
4553 _('[-nibtB] [-r REV] [SOURCE]')),
4541 "import|patch":
4554 "import|patch":
4542 (import_,
4555 (import_,
4543 [('p', 'strip', 1,
4556 [('p', 'strip', 1,
4544 _('directory strip option for patch. This has the same '
4557 _('directory strip option for patch. This has the same '
4545 'meaning as the corresponding patch option'),
4558 'meaning as the corresponding patch option'),
4546 _('NUM')),
4559 _('NUM')),
4547 ('b', 'base', '',
4560 ('b', 'base', '',
4548 _('base path'), _('PATH')),
4561 _('base path'), _('PATH')),
4549 ('f', 'force', None,
4562 ('f', 'force', None,
4550 _('skip check for outstanding uncommitted changes')),
4563 _('skip check for outstanding uncommitted changes')),
4551 ('', 'no-commit', None,
4564 ('', 'no-commit', None,
4552 _("don't commit, just update the working directory")),
4565 _("don't commit, just update the working directory")),
4553 ('', 'exact', None,
4566 ('', 'exact', None,
4554 _('apply patch to the nodes from which it was generated')),
4567 _('apply patch to the nodes from which it was generated')),
4555 ('', 'import-branch', None,
4568 ('', 'import-branch', None,
4556 _('use any branch information in patch (implied by --exact)'))] +
4569 _('use any branch information in patch (implied by --exact)'))] +
4557 commitopts + commitopts2 + similarityopts,
4570 commitopts + commitopts2 + similarityopts,
4558 _('[OPTION]... PATCH...')),
4571 _('[OPTION]... PATCH...')),
4559 "incoming|in":
4572 "incoming|in":
4560 (incoming,
4573 (incoming,
4561 [('f', 'force', None,
4574 [('f', 'force', None,
4562 _('run even if remote repository is unrelated')),
4575 _('run even if remote repository is unrelated')),
4563 ('n', 'newest-first', None, _('show newest record first')),
4576 ('n', 'newest-first', None, _('show newest record first')),
4564 ('', 'bundle', '',
4577 ('', 'bundle', '',
4565 _('file to store the bundles into'), _('FILE')),
4578 _('file to store the bundles into'), _('FILE')),
4566 ('r', 'rev', [],
4579 ('r', 'rev', [],
4567 _('a remote changeset intended to be added'), _('REV')),
4580 _('a remote changeset intended to be added'), _('REV')),
4568 ('B', 'bookmarks', False, _("compare bookmarks")),
4581 ('B', 'bookmarks', False, _("compare bookmarks")),
4569 ('b', 'branch', [],
4582 ('b', 'branch', [],
4570 _('a specific branch you would like to pull'), _('BRANCH')),
4583 _('a specific branch you would like to pull'), _('BRANCH')),
4571 ] + logopts + remoteopts + subrepoopts,
4584 ] + logopts + remoteopts + subrepoopts,
4572 _('[-p] [-n] [-M] [-f] [-r REV]...'
4585 _('[-p] [-n] [-M] [-f] [-r REV]...'
4573 ' [--bundle FILENAME] [SOURCE]')),
4586 ' [--bundle FILENAME] [SOURCE]')),
4574 "^init":
4587 "^init":
4575 (init,
4588 (init,
4576 remoteopts,
4589 remoteopts,
4577 _('[-e CMD] [--remotecmd CMD] [DEST]')),
4590 _('[-e CMD] [--remotecmd CMD] [DEST]')),
4578 "locate":
4591 "locate":
4579 (locate,
4592 (locate,
4580 [('r', 'rev', '',
4593 [('r', 'rev', '',
4581 _('search the repository as it is in REV'), _('REV')),
4594 _('search the repository as it is in REV'), _('REV')),
4582 ('0', 'print0', None,
4595 ('0', 'print0', None,
4583 _('end filenames with NUL, for use with xargs')),
4596 _('end filenames with NUL, for use with xargs')),
4584 ('f', 'fullpath', None,
4597 ('f', 'fullpath', None,
4585 _('print complete paths from the filesystem root')),
4598 _('print complete paths from the filesystem root')),
4586 ] + walkopts,
4599 ] + walkopts,
4587 _('[OPTION]... [PATTERN]...')),
4600 _('[OPTION]... [PATTERN]...')),
4588 "^log|history":
4601 "^log|history":
4589 (log,
4602 (log,
4590 [('f', 'follow', None,
4603 [('f', 'follow', None,
4591 _('follow changeset history,'
4604 _('follow changeset history,'
4592 ' or file history across copies and renames')),
4605 ' or file history across copies and renames')),
4593 ('', 'follow-first', None,
4606 ('', 'follow-first', None,
4594 _('only follow the first parent of merge changesets')),
4607 _('only follow the first parent of merge changesets')),
4595 ('d', 'date', '',
4608 ('d', 'date', '',
4596 _('show revisions matching date spec'), _('DATE')),
4609 _('show revisions matching date spec'), _('DATE')),
4597 ('C', 'copies', None, _('show copied files')),
4610 ('C', 'copies', None, _('show copied files')),
4598 ('k', 'keyword', [],
4611 ('k', 'keyword', [],
4599 _('do case-insensitive search for a given text'), _('TEXT')),
4612 _('do case-insensitive search for a given text'), _('TEXT')),
4600 ('r', 'rev', [],
4613 ('r', 'rev', [],
4601 _('show the specified revision or range'), _('REV')),
4614 _('show the specified revision or range'), _('REV')),
4602 ('', 'removed', None, _('include revisions where files were removed')),
4615 ('', 'removed', None, _('include revisions where files were removed')),
4603 ('m', 'only-merges', None, _('show only merges')),
4616 ('m', 'only-merges', None, _('show only merges')),
4604 ('u', 'user', [],
4617 ('u', 'user', [],
4605 _('revisions committed by user'), _('USER')),
4618 _('revisions committed by user'), _('USER')),
4606 ('', 'only-branch', [],
4619 ('', 'only-branch', [],
4607 _('show only changesets within the given named branch (DEPRECATED)'),
4620 _('show only changesets within the given named branch (DEPRECATED)'),
4608 _('BRANCH')),
4621 _('BRANCH')),
4609 ('b', 'branch', [],
4622 ('b', 'branch', [],
4610 _('show changesets within the given named branch'), _('BRANCH')),
4623 _('show changesets within the given named branch'), _('BRANCH')),
4611 ('P', 'prune', [],
4624 ('P', 'prune', [],
4612 _('do not display revision or any of its ancestors'), _('REV')),
4625 _('do not display revision or any of its ancestors'), _('REV')),
4613 ] + logopts + walkopts,
4626 ] + logopts + walkopts,
4614 _('[OPTION]... [FILE]')),
4627 _('[OPTION]... [FILE]')),
4615 "manifest":
4628 "manifest":
4616 (manifest,
4629 (manifest,
4617 [('r', 'rev', '',
4630 [('r', 'rev', '',
4618 _('revision to display'), _('REV'))],
4631 _('revision to display'), _('REV'))],
4619 _('[-r REV]')),
4632 _('[-r REV]')),
4620 "^merge":
4633 "^merge":
4621 (merge,
4634 (merge,
4622 [('f', 'force', None, _('force a merge with outstanding changes')),
4635 [('f', 'force', None, _('force a merge with outstanding changes')),
4623 ('t', 'tool', '', _('specify merge tool')),
4636 ('t', 'tool', '', _('specify merge tool')),
4624 ('r', 'rev', '',
4637 ('r', 'rev', '',
4625 _('revision to merge'), _('REV')),
4638 _('revision to merge'), _('REV')),
4626 ('P', 'preview', None,
4639 ('P', 'preview', None,
4627 _('review revisions to merge (no merge is performed)'))],
4640 _('review revisions to merge (no merge is performed)'))],
4628 _('[-P] [-f] [[-r] REV]')),
4641 _('[-P] [-f] [[-r] REV]')),
4629 "outgoing|out":
4642 "outgoing|out":
4630 (outgoing,
4643 (outgoing,
4631 [('f', 'force', None,
4644 [('f', 'force', None,
4632 _('run even when the destination is unrelated')),
4645 _('run even when the destination is unrelated')),
4633 ('r', 'rev', [],
4646 ('r', 'rev', [],
4634 _('a changeset intended to be included in the destination'),
4647 _('a changeset intended to be included in the destination'),
4635 _('REV')),
4648 _('REV')),
4636 ('n', 'newest-first', None, _('show newest record first')),
4649 ('n', 'newest-first', None, _('show newest record first')),
4637 ('B', 'bookmarks', False, _("compare bookmarks")),
4650 ('B', 'bookmarks', False, _("compare bookmarks")),
4638 ('b', 'branch', [],
4651 ('b', 'branch', [],
4639 _('a specific branch you would like to push'), _('BRANCH')),
4652 _('a specific branch you would like to push'), _('BRANCH')),
4640 ] + logopts + remoteopts + subrepoopts,
4653 ] + logopts + remoteopts + subrepoopts,
4641 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
4654 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
4642 "parents":
4655 "parents":
4643 (parents,
4656 (parents,
4644 [('r', 'rev', '',
4657 [('r', 'rev', '',
4645 _('show parents of the specified revision'), _('REV')),
4658 _('show parents of the specified revision'), _('REV')),
4646 ] + templateopts,
4659 ] + templateopts,
4647 _('[-r REV] [FILE]')),
4660 _('[-r REV] [FILE]')),
4648 "paths": (paths, [], _('[NAME]')),
4661 "paths": (paths, [], _('[NAME]')),
4649 "^pull":
4662 "^pull":
4650 (pull,
4663 (pull,
4651 [('u', 'update', None,
4664 [('u', 'update', None,
4652 _('update to new branch head if changesets were pulled')),
4665 _('update to new branch head if changesets were pulled')),
4653 ('f', 'force', None,
4666 ('f', 'force', None,
4654 _('run even when remote repository is unrelated')),
4667 _('run even when remote repository is unrelated')),
4655 ('r', 'rev', [],
4668 ('r', 'rev', [],
4656 _('a remote changeset intended to be added'), _('REV')),
4669 _('a remote changeset intended to be added'), _('REV')),
4657 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
4670 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
4658 ('b', 'branch', [],
4671 ('b', 'branch', [],
4659 _('a specific branch you would like to pull'), _('BRANCH')),
4672 _('a specific branch you would like to pull'), _('BRANCH')),
4660 ] + remoteopts,
4673 ] + remoteopts,
4661 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
4674 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
4662 "^push":
4675 "^push":
4663 (push,
4676 (push,
4664 [('f', 'force', None, _('force push')),
4677 [('f', 'force', None, _('force push')),
4665 ('r', 'rev', [],
4678 ('r', 'rev', [],
4666 _('a changeset intended to be included in the destination'),
4679 _('a changeset intended to be included in the destination'),
4667 _('REV')),
4680 _('REV')),
4668 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
4681 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
4669 ('b', 'branch', [],
4682 ('b', 'branch', [],
4670 _('a specific branch you would like to push'), _('BRANCH')),
4683 _('a specific branch you would like to push'), _('BRANCH')),
4671 ('', 'new-branch', False, _('allow pushing a new branch')),
4684 ('', 'new-branch', False, _('allow pushing a new branch')),
4672 ] + remoteopts,
4685 ] + remoteopts,
4673 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
4686 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
4674 "recover": (recover, []),
4687 "recover": (recover, []),
4675 "^remove|rm":
4688 "^remove|rm":
4676 (remove,
4689 (remove,
4677 [('A', 'after', None, _('record delete for missing files')),
4690 [('A', 'after', None, _('record delete for missing files')),
4678 ('f', 'force', None,
4691 ('f', 'force', None,
4679 _('remove (and delete) file even if added or modified')),
4692 _('remove (and delete) file even if added or modified')),
4680 ] + walkopts,
4693 ] + walkopts,
4681 _('[OPTION]... FILE...')),
4694 _('[OPTION]... FILE...')),
4682 "rename|move|mv":
4695 "rename|move|mv":
4683 (rename,
4696 (rename,
4684 [('A', 'after', None, _('record a rename that has already occurred')),
4697 [('A', 'after', None, _('record a rename that has already occurred')),
4685 ('f', 'force', None,
4698 ('f', 'force', None,
4686 _('forcibly copy over an existing managed file')),
4699 _('forcibly copy over an existing managed file')),
4687 ] + walkopts + dryrunopts,
4700 ] + walkopts + dryrunopts,
4688 _('[OPTION]... SOURCE... DEST')),
4701 _('[OPTION]... SOURCE... DEST')),
4689 "resolve":
4702 "resolve":
4690 (resolve,
4703 (resolve,
4691 [('a', 'all', None, _('select all unresolved files')),
4704 [('a', 'all', None, _('select all unresolved files')),
4692 ('l', 'list', None, _('list state of files needing merge')),
4705 ('l', 'list', None, _('list state of files needing merge')),
4693 ('m', 'mark', None, _('mark files as resolved')),
4706 ('m', 'mark', None, _('mark files as resolved')),
4694 ('u', 'unmark', None, _('mark files as unresolved')),
4707 ('u', 'unmark', None, _('mark files as unresolved')),
4695 ('t', 'tool', '', _('specify merge tool')),
4708 ('t', 'tool', '', _('specify merge tool')),
4696 ('n', 'no-status', None, _('hide status prefix'))]
4709 ('n', 'no-status', None, _('hide status prefix'))]
4697 + walkopts,
4710 + walkopts,
4698 _('[OPTION]... [FILE]...')),
4711 _('[OPTION]... [FILE]...')),
4699 "revert":
4712 "revert":
4700 (revert,
4713 (revert,
4701 [('a', 'all', None, _('revert all changes when no arguments given')),
4714 [('a', 'all', None, _('revert all changes when no arguments given')),
4702 ('d', 'date', '',
4715 ('d', 'date', '',
4703 _('tipmost revision matching date'), _('DATE')),
4716 _('tipmost revision matching date'), _('DATE')),
4704 ('r', 'rev', '',
4717 ('r', 'rev', '',
4705 _('revert to the specified revision'), _('REV')),
4718 _('revert to the specified revision'), _('REV')),
4706 ('', 'no-backup', None, _('do not save backup copies of files')),
4719 ('', 'no-backup', None, _('do not save backup copies of files')),
4707 ] + walkopts + dryrunopts,
4720 ] + walkopts + dryrunopts,
4708 _('[OPTION]... [-r REV] [NAME]...')),
4721 _('[OPTION]... [-r REV] [NAME]...')),
4709 "rollback": (rollback, dryrunopts),
4722 "rollback": (rollback, dryrunopts),
4710 "root": (root, []),
4723 "root": (root, []),
4711 "^serve":
4724 "^serve":
4712 (serve,
4725 (serve,
4713 [('A', 'accesslog', '',
4726 [('A', 'accesslog', '',
4714 _('name of access log file to write to'), _('FILE')),
4727 _('name of access log file to write to'), _('FILE')),
4715 ('d', 'daemon', None, _('run server in background')),
4728 ('d', 'daemon', None, _('run server in background')),
4716 ('', 'daemon-pipefds', '',
4729 ('', 'daemon-pipefds', '',
4717 _('used internally by daemon mode'), _('NUM')),
4730 _('used internally by daemon mode'), _('NUM')),
4718 ('E', 'errorlog', '',
4731 ('E', 'errorlog', '',
4719 _('name of error log file to write to'), _('FILE')),
4732 _('name of error log file to write to'), _('FILE')),
4720 # use string type, then we can check if something was passed
4733 # use string type, then we can check if something was passed
4721 ('p', 'port', '',
4734 ('p', 'port', '',
4722 _('port to listen on (default: 8000)'), _('PORT')),
4735 _('port to listen on (default: 8000)'), _('PORT')),
4723 ('a', 'address', '',
4736 ('a', 'address', '',
4724 _('address to listen on (default: all interfaces)'), _('ADDR')),
4737 _('address to listen on (default: all interfaces)'), _('ADDR')),
4725 ('', 'prefix', '',
4738 ('', 'prefix', '',
4726 _('prefix path to serve from (default: server root)'), _('PREFIX')),
4739 _('prefix path to serve from (default: server root)'), _('PREFIX')),
4727 ('n', 'name', '',
4740 ('n', 'name', '',
4728 _('name to show in web pages (default: working directory)'),
4741 _('name to show in web pages (default: working directory)'),
4729 _('NAME')),
4742 _('NAME')),
4730 ('', 'web-conf', '',
4743 ('', 'web-conf', '',
4731 _('name of the hgweb config file (see "hg help hgweb")'),
4744 _('name of the hgweb config file (see "hg help hgweb")'),
4732 _('FILE')),
4745 _('FILE')),
4733 ('', 'webdir-conf', '',
4746 ('', 'webdir-conf', '',
4734 _('name of the hgweb config file (DEPRECATED)'), _('FILE')),
4747 _('name of the hgweb config file (DEPRECATED)'), _('FILE')),
4735 ('', 'pid-file', '',
4748 ('', 'pid-file', '',
4736 _('name of file to write process ID to'), _('FILE')),
4749 _('name of file to write process ID to'), _('FILE')),
4737 ('', 'stdio', None, _('for remote clients')),
4750 ('', 'stdio', None, _('for remote clients')),
4738 ('t', 'templates', '',
4751 ('t', 'templates', '',
4739 _('web templates to use'), _('TEMPLATE')),
4752 _('web templates to use'), _('TEMPLATE')),
4740 ('', 'style', '',
4753 ('', 'style', '',
4741 _('template style to use'), _('STYLE')),
4754 _('template style to use'), _('STYLE')),
4742 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
4755 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
4743 ('', 'certificate', '',
4756 ('', 'certificate', '',
4744 _('SSL certificate file'), _('FILE'))],
4757 _('SSL certificate file'), _('FILE'))],
4745 _('[OPTION]...')),
4758 _('[OPTION]...')),
4746 "showconfig|debugconfig":
4759 "showconfig|debugconfig":
4747 (showconfig,
4760 (showconfig,
4748 [('u', 'untrusted', None, _('show untrusted configuration options'))],
4761 [('u', 'untrusted', None, _('show untrusted configuration options'))],
4749 _('[-u] [NAME]...')),
4762 _('[-u] [NAME]...')),
4750 "^summary|sum":
4763 "^summary|sum":
4751 (summary,
4764 (summary,
4752 [('', 'remote', None, _('check for push and pull'))], '[--remote]'),
4765 [('', 'remote', None, _('check for push and pull'))], '[--remote]'),
4753 "^status|st":
4766 "^status|st":
4754 (status,
4767 (status,
4755 [('A', 'all', None, _('show status of all files')),
4768 [('A', 'all', None, _('show status of all files')),
4756 ('m', 'modified', None, _('show only modified files')),
4769 ('m', 'modified', None, _('show only modified files')),
4757 ('a', 'added', None, _('show only added files')),
4770 ('a', 'added', None, _('show only added files')),
4758 ('r', 'removed', None, _('show only removed files')),
4771 ('r', 'removed', None, _('show only removed files')),
4759 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
4772 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
4760 ('c', 'clean', None, _('show only files without changes')),
4773 ('c', 'clean', None, _('show only files without changes')),
4761 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
4774 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
4762 ('i', 'ignored', None, _('show only ignored files')),
4775 ('i', 'ignored', None, _('show only ignored files')),
4763 ('n', 'no-status', None, _('hide status prefix')),
4776 ('n', 'no-status', None, _('hide status prefix')),
4764 ('C', 'copies', None, _('show source of copied files')),
4777 ('C', 'copies', None, _('show source of copied files')),
4765 ('0', 'print0', None,
4778 ('0', 'print0', None,
4766 _('end filenames with NUL, for use with xargs')),
4779 _('end filenames with NUL, for use with xargs')),
4767 ('', 'rev', [],
4780 ('', 'rev', [],
4768 _('show difference from revision'), _('REV')),
4781 _('show difference from revision'), _('REV')),
4769 ('', 'change', '',
4782 ('', 'change', '',
4770 _('list the changed files of a revision'), _('REV')),
4783 _('list the changed files of a revision'), _('REV')),
4771 ] + walkopts + subrepoopts,
4784 ] + walkopts + subrepoopts,
4772 _('[OPTION]... [FILE]...')),
4785 _('[OPTION]... [FILE]...')),
4773 "tag":
4786 "tag":
4774 (tag,
4787 (tag,
4775 [('f', 'force', None, _('force tag')),
4788 [('f', 'force', None, _('force tag')),
4776 ('l', 'local', None, _('make the tag local')),
4789 ('l', 'local', None, _('make the tag local')),
4777 ('r', 'rev', '',
4790 ('r', 'rev', '',
4778 _('revision to tag'), _('REV')),
4791 _('revision to tag'), _('REV')),
4779 ('', 'remove', None, _('remove a tag')),
4792 ('', 'remove', None, _('remove a tag')),
4780 # -l/--local is already there, commitopts cannot be used
4793 # -l/--local is already there, commitopts cannot be used
4781 ('e', 'edit', None, _('edit commit message')),
4794 ('e', 'edit', None, _('edit commit message')),
4782 ('m', 'message', '',
4795 ('m', 'message', '',
4783 _('use <text> as commit message'), _('TEXT')),
4796 _('use <text> as commit message'), _('TEXT')),
4784 ] + commitopts2,
4797 ] + commitopts2,
4785 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...')),
4798 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...')),
4786 "tags": (tags, [], ''),
4799 "tags": (tags, [], ''),
4787 "tip":
4800 "tip":
4788 (tip,
4801 (tip,
4789 [('p', 'patch', None, _('show patch')),
4802 [('p', 'patch', None, _('show patch')),
4790 ('g', 'git', None, _('use git extended diff format')),
4803 ('g', 'git', None, _('use git extended diff format')),
4791 ] + templateopts,
4804 ] + templateopts,
4792 _('[-p] [-g]')),
4805 _('[-p] [-g]')),
4793 "unbundle":
4806 "unbundle":
4794 (unbundle,
4807 (unbundle,
4795 [('u', 'update', None,
4808 [('u', 'update', None,
4796 _('update to new branch head if changesets were unbundled'))],
4809 _('update to new branch head if changesets were unbundled'))],
4797 _('[-u] FILE...')),
4810 _('[-u] FILE...')),
4798 "^update|up|checkout|co":
4811 "^update|up|checkout|co":
4799 (update,
4812 (update,
4800 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
4813 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
4801 ('c', 'check', None,
4814 ('c', 'check', None,
4802 _('update across branches if no uncommitted changes')),
4815 _('update across branches if no uncommitted changes')),
4803 ('d', 'date', '',
4816 ('d', 'date', '',
4804 _('tipmost revision matching date'), _('DATE')),
4817 _('tipmost revision matching date'), _('DATE')),
4805 ('r', 'rev', '',
4818 ('r', 'rev', '',
4806 _('revision'), _('REV'))],
4819 _('revision'), _('REV'))],
4807 _('[-c] [-C] [-d DATE] [[-r] REV]')),
4820 _('[-c] [-C] [-d DATE] [[-r] REV]')),
4808 "verify": (verify, []),
4821 "verify": (verify, []),
4809 "version": (version_, []),
4822 "version": (version_, []),
4810 }
4823 }
4811
4824
4812 norepo = ("clone init version help debugcommands debugcomplete"
4825 norepo = ("clone init version help debugcommands debugcomplete"
4813 " debugdate debuginstall debugfsinfo debugpushkey debugwireargs")
4826 " debugdate debuginstall debugfsinfo debugpushkey debugwireargs"
4827 " debugknown")
4814 optionalrepo = ("identify paths serve showconfig debugancestor debugdag"
4828 optionalrepo = ("identify paths serve showconfig debugancestor debugdag"
4815 " debugdata debugindex debugindexdot")
4829 " debugdata debugindex debugindexdot")
@@ -1,1924 +1,1929 b''
1 # localrepo.py - read/write repository class for mercurial
1 # localrepo.py - read/write repository class 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 bin, hex, nullid, nullrev, short
8 from node import bin, hex, nullid, nullrev, short
9 from i18n import _
9 from i18n import _
10 import repo, changegroup, subrepo, discovery, pushkey
10 import repo, changegroup, subrepo, discovery, pushkey
11 import changelog, dirstate, filelog, manifest, context, bookmarks
11 import changelog, dirstate, filelog, manifest, context, bookmarks
12 import lock, transaction, store, encoding
12 import lock, transaction, store, encoding
13 import util, extensions, hook, error
13 import util, extensions, hook, error
14 import match as matchmod
14 import match as matchmod
15 import merge as mergemod
15 import merge as mergemod
16 import tags as tagsmod
16 import tags as tagsmod
17 import url as urlmod
17 import url as urlmod
18 from lock import release
18 from lock import release
19 import weakref, errno, os, time, inspect
19 import weakref, errno, os, time, inspect
20 propertycache = util.propertycache
20 propertycache = util.propertycache
21
21
22 class localrepository(repo.repository):
22 class localrepository(repo.repository):
23 capabilities = set(('lookup', 'changegroupsubset', 'branchmap', 'pushkey'))
23 capabilities = set(('lookup', 'changegroupsubset', 'branchmap', 'pushkey',
24 'known'))
24 supportedformats = set(('revlogv1', 'parentdelta'))
25 supportedformats = set(('revlogv1', 'parentdelta'))
25 supported = supportedformats | set(('store', 'fncache', 'shared',
26 supported = supportedformats | set(('store', 'fncache', 'shared',
26 'dotencode'))
27 'dotencode'))
27
28
28 def __init__(self, baseui, path=None, create=0):
29 def __init__(self, baseui, path=None, create=0):
29 repo.repository.__init__(self)
30 repo.repository.__init__(self)
30 self.root = os.path.realpath(util.expandpath(path))
31 self.root = os.path.realpath(util.expandpath(path))
31 self.path = os.path.join(self.root, ".hg")
32 self.path = os.path.join(self.root, ".hg")
32 self.origroot = path
33 self.origroot = path
33 self.auditor = util.path_auditor(self.root, self._checknested)
34 self.auditor = util.path_auditor(self.root, self._checknested)
34 self.opener = util.opener(self.path)
35 self.opener = util.opener(self.path)
35 self.wopener = util.opener(self.root)
36 self.wopener = util.opener(self.root)
36 self.baseui = baseui
37 self.baseui = baseui
37 self.ui = baseui.copy()
38 self.ui = baseui.copy()
38
39
39 try:
40 try:
40 self.ui.readconfig(self.join("hgrc"), self.root)
41 self.ui.readconfig(self.join("hgrc"), self.root)
41 extensions.loadall(self.ui)
42 extensions.loadall(self.ui)
42 except IOError:
43 except IOError:
43 pass
44 pass
44
45
45 if not os.path.isdir(self.path):
46 if not os.path.isdir(self.path):
46 if create:
47 if create:
47 if not os.path.exists(path):
48 if not os.path.exists(path):
48 util.makedirs(path)
49 util.makedirs(path)
49 os.mkdir(self.path)
50 os.mkdir(self.path)
50 requirements = ["revlogv1"]
51 requirements = ["revlogv1"]
51 if self.ui.configbool('format', 'usestore', True):
52 if self.ui.configbool('format', 'usestore', True):
52 os.mkdir(os.path.join(self.path, "store"))
53 os.mkdir(os.path.join(self.path, "store"))
53 requirements.append("store")
54 requirements.append("store")
54 if self.ui.configbool('format', 'usefncache', True):
55 if self.ui.configbool('format', 'usefncache', True):
55 requirements.append("fncache")
56 requirements.append("fncache")
56 if self.ui.configbool('format', 'dotencode', True):
57 if self.ui.configbool('format', 'dotencode', True):
57 requirements.append('dotencode')
58 requirements.append('dotencode')
58 # create an invalid changelog
59 # create an invalid changelog
59 self.opener("00changelog.i", "a").write(
60 self.opener("00changelog.i", "a").write(
60 '\0\0\0\2' # represents revlogv2
61 '\0\0\0\2' # represents revlogv2
61 ' dummy changelog to prevent using the old repo layout'
62 ' dummy changelog to prevent using the old repo layout'
62 )
63 )
63 if self.ui.configbool('format', 'parentdelta', False):
64 if self.ui.configbool('format', 'parentdelta', False):
64 requirements.append("parentdelta")
65 requirements.append("parentdelta")
65 else:
66 else:
66 raise error.RepoError(_("repository %s not found") % path)
67 raise error.RepoError(_("repository %s not found") % path)
67 elif create:
68 elif create:
68 raise error.RepoError(_("repository %s already exists") % path)
69 raise error.RepoError(_("repository %s already exists") % path)
69 else:
70 else:
70 # find requirements
71 # find requirements
71 requirements = set()
72 requirements = set()
72 try:
73 try:
73 requirements = set(self.opener("requires").read().splitlines())
74 requirements = set(self.opener("requires").read().splitlines())
74 except IOError, inst:
75 except IOError, inst:
75 if inst.errno != errno.ENOENT:
76 if inst.errno != errno.ENOENT:
76 raise
77 raise
77 for r in requirements - self.supported:
78 for r in requirements - self.supported:
78 raise error.RequirementError(
79 raise error.RequirementError(
79 _("requirement '%s' not supported") % r)
80 _("requirement '%s' not supported") % r)
80
81
81 self.sharedpath = self.path
82 self.sharedpath = self.path
82 try:
83 try:
83 s = os.path.realpath(self.opener("sharedpath").read())
84 s = os.path.realpath(self.opener("sharedpath").read())
84 if not os.path.exists(s):
85 if not os.path.exists(s):
85 raise error.RepoError(
86 raise error.RepoError(
86 _('.hg/sharedpath points to nonexistent directory %s') % s)
87 _('.hg/sharedpath points to nonexistent directory %s') % s)
87 self.sharedpath = s
88 self.sharedpath = s
88 except IOError, inst:
89 except IOError, inst:
89 if inst.errno != errno.ENOENT:
90 if inst.errno != errno.ENOENT:
90 raise
91 raise
91
92
92 self.store = store.store(requirements, self.sharedpath, util.opener)
93 self.store = store.store(requirements, self.sharedpath, util.opener)
93 self.spath = self.store.path
94 self.spath = self.store.path
94 self.sopener = self.store.opener
95 self.sopener = self.store.opener
95 self.sjoin = self.store.join
96 self.sjoin = self.store.join
96 self.opener.createmode = self.store.createmode
97 self.opener.createmode = self.store.createmode
97 self._applyrequirements(requirements)
98 self._applyrequirements(requirements)
98 if create:
99 if create:
99 self._writerequirements()
100 self._writerequirements()
100
101
101 # These two define the set of tags for this repository. _tags
102 # These two define the set of tags for this repository. _tags
102 # maps tag name to node; _tagtypes maps tag name to 'global' or
103 # maps tag name to node; _tagtypes maps tag name to 'global' or
103 # 'local'. (Global tags are defined by .hgtags across all
104 # 'local'. (Global tags are defined by .hgtags across all
104 # heads, and local tags are defined in .hg/localtags.) They
105 # heads, and local tags are defined in .hg/localtags.) They
105 # constitute the in-memory cache of tags.
106 # constitute the in-memory cache of tags.
106 self._tags = None
107 self._tags = None
107 self._tagtypes = None
108 self._tagtypes = None
108
109
109 self._branchcache = None
110 self._branchcache = None
110 self._branchcachetip = None
111 self._branchcachetip = None
111 self.nodetagscache = None
112 self.nodetagscache = None
112 self.filterpats = {}
113 self.filterpats = {}
113 self._datafilters = {}
114 self._datafilters = {}
114 self._transref = self._lockref = self._wlockref = None
115 self._transref = self._lockref = self._wlockref = None
115
116
116 def _applyrequirements(self, requirements):
117 def _applyrequirements(self, requirements):
117 self.requirements = requirements
118 self.requirements = requirements
118 self.sopener.options = {}
119 self.sopener.options = {}
119 if 'parentdelta' in requirements:
120 if 'parentdelta' in requirements:
120 self.sopener.options['parentdelta'] = 1
121 self.sopener.options['parentdelta'] = 1
121
122
122 def _writerequirements(self):
123 def _writerequirements(self):
123 reqfile = self.opener("requires", "w")
124 reqfile = self.opener("requires", "w")
124 for r in self.requirements:
125 for r in self.requirements:
125 reqfile.write("%s\n" % r)
126 reqfile.write("%s\n" % r)
126 reqfile.close()
127 reqfile.close()
127
128
128 def _checknested(self, path):
129 def _checknested(self, path):
129 """Determine if path is a legal nested repository."""
130 """Determine if path is a legal nested repository."""
130 if not path.startswith(self.root):
131 if not path.startswith(self.root):
131 return False
132 return False
132 subpath = path[len(self.root) + 1:]
133 subpath = path[len(self.root) + 1:]
133
134
134 # XXX: Checking against the current working copy is wrong in
135 # XXX: Checking against the current working copy is wrong in
135 # the sense that it can reject things like
136 # the sense that it can reject things like
136 #
137 #
137 # $ hg cat -r 10 sub/x.txt
138 # $ hg cat -r 10 sub/x.txt
138 #
139 #
139 # if sub/ is no longer a subrepository in the working copy
140 # if sub/ is no longer a subrepository in the working copy
140 # parent revision.
141 # parent revision.
141 #
142 #
142 # However, it can of course also allow things that would have
143 # However, it can of course also allow things that would have
143 # been rejected before, such as the above cat command if sub/
144 # been rejected before, such as the above cat command if sub/
144 # is a subrepository now, but was a normal directory before.
145 # is a subrepository now, but was a normal directory before.
145 # The old path auditor would have rejected by mistake since it
146 # The old path auditor would have rejected by mistake since it
146 # panics when it sees sub/.hg/.
147 # panics when it sees sub/.hg/.
147 #
148 #
148 # All in all, checking against the working copy seems sensible
149 # All in all, checking against the working copy seems sensible
149 # since we want to prevent access to nested repositories on
150 # since we want to prevent access to nested repositories on
150 # the filesystem *now*.
151 # the filesystem *now*.
151 ctx = self[None]
152 ctx = self[None]
152 parts = util.splitpath(subpath)
153 parts = util.splitpath(subpath)
153 while parts:
154 while parts:
154 prefix = os.sep.join(parts)
155 prefix = os.sep.join(parts)
155 if prefix in ctx.substate:
156 if prefix in ctx.substate:
156 if prefix == subpath:
157 if prefix == subpath:
157 return True
158 return True
158 else:
159 else:
159 sub = ctx.sub(prefix)
160 sub = ctx.sub(prefix)
160 return sub.checknested(subpath[len(prefix) + 1:])
161 return sub.checknested(subpath[len(prefix) + 1:])
161 else:
162 else:
162 parts.pop()
163 parts.pop()
163 return False
164 return False
164
165
165 @util.propertycache
166 @util.propertycache
166 def _bookmarks(self):
167 def _bookmarks(self):
167 return bookmarks.read(self)
168 return bookmarks.read(self)
168
169
169 @util.propertycache
170 @util.propertycache
170 def _bookmarkcurrent(self):
171 def _bookmarkcurrent(self):
171 return bookmarks.readcurrent(self)
172 return bookmarks.readcurrent(self)
172
173
173 @propertycache
174 @propertycache
174 def changelog(self):
175 def changelog(self):
175 c = changelog.changelog(self.sopener)
176 c = changelog.changelog(self.sopener)
176 if 'HG_PENDING' in os.environ:
177 if 'HG_PENDING' in os.environ:
177 p = os.environ['HG_PENDING']
178 p = os.environ['HG_PENDING']
178 if p.startswith(self.root):
179 if p.startswith(self.root):
179 c.readpending('00changelog.i.a')
180 c.readpending('00changelog.i.a')
180 self.sopener.options['defversion'] = c.version
181 self.sopener.options['defversion'] = c.version
181 return c
182 return c
182
183
183 @propertycache
184 @propertycache
184 def manifest(self):
185 def manifest(self):
185 return manifest.manifest(self.sopener)
186 return manifest.manifest(self.sopener)
186
187
187 @propertycache
188 @propertycache
188 def dirstate(self):
189 def dirstate(self):
189 warned = [0]
190 warned = [0]
190 def validate(node):
191 def validate(node):
191 try:
192 try:
192 r = self.changelog.rev(node)
193 r = self.changelog.rev(node)
193 return node
194 return node
194 except error.LookupError:
195 except error.LookupError:
195 if not warned[0]:
196 if not warned[0]:
196 warned[0] = True
197 warned[0] = True
197 self.ui.warn(_("warning: ignoring unknown"
198 self.ui.warn(_("warning: ignoring unknown"
198 " working parent %s!\n") % short(node))
199 " working parent %s!\n") % short(node))
199 return nullid
200 return nullid
200
201
201 return dirstate.dirstate(self.opener, self.ui, self.root, validate)
202 return dirstate.dirstate(self.opener, self.ui, self.root, validate)
202
203
203 def __getitem__(self, changeid):
204 def __getitem__(self, changeid):
204 if changeid is None:
205 if changeid is None:
205 return context.workingctx(self)
206 return context.workingctx(self)
206 return context.changectx(self, changeid)
207 return context.changectx(self, changeid)
207
208
208 def __contains__(self, changeid):
209 def __contains__(self, changeid):
209 try:
210 try:
210 return bool(self.lookup(changeid))
211 return bool(self.lookup(changeid))
211 except error.RepoLookupError:
212 except error.RepoLookupError:
212 return False
213 return False
213
214
214 def __nonzero__(self):
215 def __nonzero__(self):
215 return True
216 return True
216
217
217 def __len__(self):
218 def __len__(self):
218 return len(self.changelog)
219 return len(self.changelog)
219
220
220 def __iter__(self):
221 def __iter__(self):
221 for i in xrange(len(self)):
222 for i in xrange(len(self)):
222 yield i
223 yield i
223
224
224 def url(self):
225 def url(self):
225 return 'file:' + self.root
226 return 'file:' + self.root
226
227
227 def hook(self, name, throw=False, **args):
228 def hook(self, name, throw=False, **args):
228 return hook.hook(self.ui, self, name, throw, **args)
229 return hook.hook(self.ui, self, name, throw, **args)
229
230
230 tag_disallowed = ':\r\n'
231 tag_disallowed = ':\r\n'
231
232
232 def _tag(self, names, node, message, local, user, date, extra={}):
233 def _tag(self, names, node, message, local, user, date, extra={}):
233 if isinstance(names, str):
234 if isinstance(names, str):
234 allchars = names
235 allchars = names
235 names = (names,)
236 names = (names,)
236 else:
237 else:
237 allchars = ''.join(names)
238 allchars = ''.join(names)
238 for c in self.tag_disallowed:
239 for c in self.tag_disallowed:
239 if c in allchars:
240 if c in allchars:
240 raise util.Abort(_('%r cannot be used in a tag name') % c)
241 raise util.Abort(_('%r cannot be used in a tag name') % c)
241
242
242 branches = self.branchmap()
243 branches = self.branchmap()
243 for name in names:
244 for name in names:
244 self.hook('pretag', throw=True, node=hex(node), tag=name,
245 self.hook('pretag', throw=True, node=hex(node), tag=name,
245 local=local)
246 local=local)
246 if name in branches:
247 if name in branches:
247 self.ui.warn(_("warning: tag %s conflicts with existing"
248 self.ui.warn(_("warning: tag %s conflicts with existing"
248 " branch name\n") % name)
249 " branch name\n") % name)
249
250
250 def writetags(fp, names, munge, prevtags):
251 def writetags(fp, names, munge, prevtags):
251 fp.seek(0, 2)
252 fp.seek(0, 2)
252 if prevtags and prevtags[-1] != '\n':
253 if prevtags and prevtags[-1] != '\n':
253 fp.write('\n')
254 fp.write('\n')
254 for name in names:
255 for name in names:
255 m = munge and munge(name) or name
256 m = munge and munge(name) or name
256 if self._tagtypes and name in self._tagtypes:
257 if self._tagtypes and name in self._tagtypes:
257 old = self._tags.get(name, nullid)
258 old = self._tags.get(name, nullid)
258 fp.write('%s %s\n' % (hex(old), m))
259 fp.write('%s %s\n' % (hex(old), m))
259 fp.write('%s %s\n' % (hex(node), m))
260 fp.write('%s %s\n' % (hex(node), m))
260 fp.close()
261 fp.close()
261
262
262 prevtags = ''
263 prevtags = ''
263 if local:
264 if local:
264 try:
265 try:
265 fp = self.opener('localtags', 'r+')
266 fp = self.opener('localtags', 'r+')
266 except IOError:
267 except IOError:
267 fp = self.opener('localtags', 'a')
268 fp = self.opener('localtags', 'a')
268 else:
269 else:
269 prevtags = fp.read()
270 prevtags = fp.read()
270
271
271 # local tags are stored in the current charset
272 # local tags are stored in the current charset
272 writetags(fp, names, None, prevtags)
273 writetags(fp, names, None, prevtags)
273 for name in names:
274 for name in names:
274 self.hook('tag', node=hex(node), tag=name, local=local)
275 self.hook('tag', node=hex(node), tag=name, local=local)
275 return
276 return
276
277
277 try:
278 try:
278 fp = self.wfile('.hgtags', 'rb+')
279 fp = self.wfile('.hgtags', 'rb+')
279 except IOError:
280 except IOError:
280 fp = self.wfile('.hgtags', 'ab')
281 fp = self.wfile('.hgtags', 'ab')
281 else:
282 else:
282 prevtags = fp.read()
283 prevtags = fp.read()
283
284
284 # committed tags are stored in UTF-8
285 # committed tags are stored in UTF-8
285 writetags(fp, names, encoding.fromlocal, prevtags)
286 writetags(fp, names, encoding.fromlocal, prevtags)
286
287
287 fp.close()
288 fp.close()
288
289
289 if '.hgtags' not in self.dirstate:
290 if '.hgtags' not in self.dirstate:
290 self[None].add(['.hgtags'])
291 self[None].add(['.hgtags'])
291
292
292 m = matchmod.exact(self.root, '', ['.hgtags'])
293 m = matchmod.exact(self.root, '', ['.hgtags'])
293 tagnode = self.commit(message, user, date, extra=extra, match=m)
294 tagnode = self.commit(message, user, date, extra=extra, match=m)
294
295
295 for name in names:
296 for name in names:
296 self.hook('tag', node=hex(node), tag=name, local=local)
297 self.hook('tag', node=hex(node), tag=name, local=local)
297
298
298 return tagnode
299 return tagnode
299
300
300 def tag(self, names, node, message, local, user, date):
301 def tag(self, names, node, message, local, user, date):
301 '''tag a revision with one or more symbolic names.
302 '''tag a revision with one or more symbolic names.
302
303
303 names is a list of strings or, when adding a single tag, names may be a
304 names is a list of strings or, when adding a single tag, names may be a
304 string.
305 string.
305
306
306 if local is True, the tags are stored in a per-repository file.
307 if local is True, the tags are stored in a per-repository file.
307 otherwise, they are stored in the .hgtags file, and a new
308 otherwise, they are stored in the .hgtags file, and a new
308 changeset is committed with the change.
309 changeset is committed with the change.
309
310
310 keyword arguments:
311 keyword arguments:
311
312
312 local: whether to store tags in non-version-controlled file
313 local: whether to store tags in non-version-controlled file
313 (default False)
314 (default False)
314
315
315 message: commit message to use if committing
316 message: commit message to use if committing
316
317
317 user: name of user to use if committing
318 user: name of user to use if committing
318
319
319 date: date tuple to use if committing'''
320 date: date tuple to use if committing'''
320
321
321 if not local:
322 if not local:
322 for x in self.status()[:5]:
323 for x in self.status()[:5]:
323 if '.hgtags' in x:
324 if '.hgtags' in x:
324 raise util.Abort(_('working copy of .hgtags is changed '
325 raise util.Abort(_('working copy of .hgtags is changed '
325 '(please commit .hgtags manually)'))
326 '(please commit .hgtags manually)'))
326
327
327 self.tags() # instantiate the cache
328 self.tags() # instantiate the cache
328 self._tag(names, node, message, local, user, date)
329 self._tag(names, node, message, local, user, date)
329
330
330 def tags(self):
331 def tags(self):
331 '''return a mapping of tag to node'''
332 '''return a mapping of tag to node'''
332 if self._tags is None:
333 if self._tags is None:
333 (self._tags, self._tagtypes) = self._findtags()
334 (self._tags, self._tagtypes) = self._findtags()
334
335
335 return self._tags
336 return self._tags
336
337
337 def _findtags(self):
338 def _findtags(self):
338 '''Do the hard work of finding tags. Return a pair of dicts
339 '''Do the hard work of finding tags. Return a pair of dicts
339 (tags, tagtypes) where tags maps tag name to node, and tagtypes
340 (tags, tagtypes) where tags maps tag name to node, and tagtypes
340 maps tag name to a string like \'global\' or \'local\'.
341 maps tag name to a string like \'global\' or \'local\'.
341 Subclasses or extensions are free to add their own tags, but
342 Subclasses or extensions are free to add their own tags, but
342 should be aware that the returned dicts will be retained for the
343 should be aware that the returned dicts will be retained for the
343 duration of the localrepo object.'''
344 duration of the localrepo object.'''
344
345
345 # XXX what tagtype should subclasses/extensions use? Currently
346 # XXX what tagtype should subclasses/extensions use? Currently
346 # mq and bookmarks add tags, but do not set the tagtype at all.
347 # mq and bookmarks add tags, but do not set the tagtype at all.
347 # Should each extension invent its own tag type? Should there
348 # Should each extension invent its own tag type? Should there
348 # be one tagtype for all such "virtual" tags? Or is the status
349 # be one tagtype for all such "virtual" tags? Or is the status
349 # quo fine?
350 # quo fine?
350
351
351 alltags = {} # map tag name to (node, hist)
352 alltags = {} # map tag name to (node, hist)
352 tagtypes = {}
353 tagtypes = {}
353
354
354 tagsmod.findglobaltags(self.ui, self, alltags, tagtypes)
355 tagsmod.findglobaltags(self.ui, self, alltags, tagtypes)
355 tagsmod.readlocaltags(self.ui, self, alltags, tagtypes)
356 tagsmod.readlocaltags(self.ui, self, alltags, tagtypes)
356
357
357 # Build the return dicts. Have to re-encode tag names because
358 # Build the return dicts. Have to re-encode tag names because
358 # the tags module always uses UTF-8 (in order not to lose info
359 # the tags module always uses UTF-8 (in order not to lose info
359 # writing to the cache), but the rest of Mercurial wants them in
360 # writing to the cache), but the rest of Mercurial wants them in
360 # local encoding.
361 # local encoding.
361 tags = {}
362 tags = {}
362 for (name, (node, hist)) in alltags.iteritems():
363 for (name, (node, hist)) in alltags.iteritems():
363 if node != nullid:
364 if node != nullid:
364 tags[encoding.tolocal(name)] = node
365 tags[encoding.tolocal(name)] = node
365 tags['tip'] = self.changelog.tip()
366 tags['tip'] = self.changelog.tip()
366 tagtypes = dict([(encoding.tolocal(name), value)
367 tagtypes = dict([(encoding.tolocal(name), value)
367 for (name, value) in tagtypes.iteritems()])
368 for (name, value) in tagtypes.iteritems()])
368 return (tags, tagtypes)
369 return (tags, tagtypes)
369
370
370 def tagtype(self, tagname):
371 def tagtype(self, tagname):
371 '''
372 '''
372 return the type of the given tag. result can be:
373 return the type of the given tag. result can be:
373
374
374 'local' : a local tag
375 'local' : a local tag
375 'global' : a global tag
376 'global' : a global tag
376 None : tag does not exist
377 None : tag does not exist
377 '''
378 '''
378
379
379 self.tags()
380 self.tags()
380
381
381 return self._tagtypes.get(tagname)
382 return self._tagtypes.get(tagname)
382
383
383 def tagslist(self):
384 def tagslist(self):
384 '''return a list of tags ordered by revision'''
385 '''return a list of tags ordered by revision'''
385 l = []
386 l = []
386 for t, n in self.tags().iteritems():
387 for t, n in self.tags().iteritems():
387 try:
388 try:
388 r = self.changelog.rev(n)
389 r = self.changelog.rev(n)
389 except:
390 except:
390 r = -2 # sort to the beginning of the list if unknown
391 r = -2 # sort to the beginning of the list if unknown
391 l.append((r, t, n))
392 l.append((r, t, n))
392 return [(t, n) for r, t, n in sorted(l)]
393 return [(t, n) for r, t, n in sorted(l)]
393
394
394 def nodetags(self, node):
395 def nodetags(self, node):
395 '''return the tags associated with a node'''
396 '''return the tags associated with a node'''
396 if not self.nodetagscache:
397 if not self.nodetagscache:
397 self.nodetagscache = {}
398 self.nodetagscache = {}
398 for t, n in self.tags().iteritems():
399 for t, n in self.tags().iteritems():
399 self.nodetagscache.setdefault(n, []).append(t)
400 self.nodetagscache.setdefault(n, []).append(t)
400 for tags in self.nodetagscache.itervalues():
401 for tags in self.nodetagscache.itervalues():
401 tags.sort()
402 tags.sort()
402 return self.nodetagscache.get(node, [])
403 return self.nodetagscache.get(node, [])
403
404
404 def nodebookmarks(self, node):
405 def nodebookmarks(self, node):
405 marks = []
406 marks = []
406 for bookmark, n in self._bookmarks.iteritems():
407 for bookmark, n in self._bookmarks.iteritems():
407 if n == node:
408 if n == node:
408 marks.append(bookmark)
409 marks.append(bookmark)
409 return sorted(marks)
410 return sorted(marks)
410
411
411 def _branchtags(self, partial, lrev):
412 def _branchtags(self, partial, lrev):
412 # TODO: rename this function?
413 # TODO: rename this function?
413 tiprev = len(self) - 1
414 tiprev = len(self) - 1
414 if lrev != tiprev:
415 if lrev != tiprev:
415 ctxgen = (self[r] for r in xrange(lrev + 1, tiprev + 1))
416 ctxgen = (self[r] for r in xrange(lrev + 1, tiprev + 1))
416 self._updatebranchcache(partial, ctxgen)
417 self._updatebranchcache(partial, ctxgen)
417 self._writebranchcache(partial, self.changelog.tip(), tiprev)
418 self._writebranchcache(partial, self.changelog.tip(), tiprev)
418
419
419 return partial
420 return partial
420
421
421 def updatebranchcache(self):
422 def updatebranchcache(self):
422 tip = self.changelog.tip()
423 tip = self.changelog.tip()
423 if self._branchcache is not None and self._branchcachetip == tip:
424 if self._branchcache is not None and self._branchcachetip == tip:
424 return self._branchcache
425 return self._branchcache
425
426
426 oldtip = self._branchcachetip
427 oldtip = self._branchcachetip
427 self._branchcachetip = tip
428 self._branchcachetip = tip
428 if oldtip is None or oldtip not in self.changelog.nodemap:
429 if oldtip is None or oldtip not in self.changelog.nodemap:
429 partial, last, lrev = self._readbranchcache()
430 partial, last, lrev = self._readbranchcache()
430 else:
431 else:
431 lrev = self.changelog.rev(oldtip)
432 lrev = self.changelog.rev(oldtip)
432 partial = self._branchcache
433 partial = self._branchcache
433
434
434 self._branchtags(partial, lrev)
435 self._branchtags(partial, lrev)
435 # this private cache holds all heads (not just tips)
436 # this private cache holds all heads (not just tips)
436 self._branchcache = partial
437 self._branchcache = partial
437
438
438 def branchmap(self):
439 def branchmap(self):
439 '''returns a dictionary {branch: [branchheads]}'''
440 '''returns a dictionary {branch: [branchheads]}'''
440 self.updatebranchcache()
441 self.updatebranchcache()
441 return self._branchcache
442 return self._branchcache
442
443
443 def branchtags(self):
444 def branchtags(self):
444 '''return a dict where branch names map to the tipmost head of
445 '''return a dict where branch names map to the tipmost head of
445 the branch, open heads come before closed'''
446 the branch, open heads come before closed'''
446 bt = {}
447 bt = {}
447 for bn, heads in self.branchmap().iteritems():
448 for bn, heads in self.branchmap().iteritems():
448 tip = heads[-1]
449 tip = heads[-1]
449 for h in reversed(heads):
450 for h in reversed(heads):
450 if 'close' not in self.changelog.read(h)[5]:
451 if 'close' not in self.changelog.read(h)[5]:
451 tip = h
452 tip = h
452 break
453 break
453 bt[bn] = tip
454 bt[bn] = tip
454 return bt
455 return bt
455
456
456 def _readbranchcache(self):
457 def _readbranchcache(self):
457 partial = {}
458 partial = {}
458 try:
459 try:
459 f = self.opener("cache/branchheads")
460 f = self.opener("cache/branchheads")
460 lines = f.read().split('\n')
461 lines = f.read().split('\n')
461 f.close()
462 f.close()
462 except (IOError, OSError):
463 except (IOError, OSError):
463 return {}, nullid, nullrev
464 return {}, nullid, nullrev
464
465
465 try:
466 try:
466 last, lrev = lines.pop(0).split(" ", 1)
467 last, lrev = lines.pop(0).split(" ", 1)
467 last, lrev = bin(last), int(lrev)
468 last, lrev = bin(last), int(lrev)
468 if lrev >= len(self) or self[lrev].node() != last:
469 if lrev >= len(self) or self[lrev].node() != last:
469 # invalidate the cache
470 # invalidate the cache
470 raise ValueError('invalidating branch cache (tip differs)')
471 raise ValueError('invalidating branch cache (tip differs)')
471 for l in lines:
472 for l in lines:
472 if not l:
473 if not l:
473 continue
474 continue
474 node, label = l.split(" ", 1)
475 node, label = l.split(" ", 1)
475 label = encoding.tolocal(label.strip())
476 label = encoding.tolocal(label.strip())
476 partial.setdefault(label, []).append(bin(node))
477 partial.setdefault(label, []).append(bin(node))
477 except KeyboardInterrupt:
478 except KeyboardInterrupt:
478 raise
479 raise
479 except Exception, inst:
480 except Exception, inst:
480 if self.ui.debugflag:
481 if self.ui.debugflag:
481 self.ui.warn(str(inst), '\n')
482 self.ui.warn(str(inst), '\n')
482 partial, last, lrev = {}, nullid, nullrev
483 partial, last, lrev = {}, nullid, nullrev
483 return partial, last, lrev
484 return partial, last, lrev
484
485
485 def _writebranchcache(self, branches, tip, tiprev):
486 def _writebranchcache(self, branches, tip, tiprev):
486 try:
487 try:
487 f = self.opener("cache/branchheads", "w", atomictemp=True)
488 f = self.opener("cache/branchheads", "w", atomictemp=True)
488 f.write("%s %s\n" % (hex(tip), tiprev))
489 f.write("%s %s\n" % (hex(tip), tiprev))
489 for label, nodes in branches.iteritems():
490 for label, nodes in branches.iteritems():
490 for node in nodes:
491 for node in nodes:
491 f.write("%s %s\n" % (hex(node), encoding.fromlocal(label)))
492 f.write("%s %s\n" % (hex(node), encoding.fromlocal(label)))
492 f.rename()
493 f.rename()
493 except (IOError, OSError):
494 except (IOError, OSError):
494 pass
495 pass
495
496
496 def _updatebranchcache(self, partial, ctxgen):
497 def _updatebranchcache(self, partial, ctxgen):
497 # collect new branch entries
498 # collect new branch entries
498 newbranches = {}
499 newbranches = {}
499 for c in ctxgen:
500 for c in ctxgen:
500 newbranches.setdefault(c.branch(), []).append(c.node())
501 newbranches.setdefault(c.branch(), []).append(c.node())
501 # if older branchheads are reachable from new ones, they aren't
502 # if older branchheads are reachable from new ones, they aren't
502 # really branchheads. Note checking parents is insufficient:
503 # really branchheads. Note checking parents is insufficient:
503 # 1 (branch a) -> 2 (branch b) -> 3 (branch a)
504 # 1 (branch a) -> 2 (branch b) -> 3 (branch a)
504 for branch, newnodes in newbranches.iteritems():
505 for branch, newnodes in newbranches.iteritems():
505 bheads = partial.setdefault(branch, [])
506 bheads = partial.setdefault(branch, [])
506 bheads.extend(newnodes)
507 bheads.extend(newnodes)
507 if len(bheads) <= 1:
508 if len(bheads) <= 1:
508 continue
509 continue
509 # starting from tip means fewer passes over reachable
510 # starting from tip means fewer passes over reachable
510 while newnodes:
511 while newnodes:
511 latest = newnodes.pop()
512 latest = newnodes.pop()
512 if latest not in bheads:
513 if latest not in bheads:
513 continue
514 continue
514 minbhrev = self[min([self[bh].rev() for bh in bheads])].node()
515 minbhrev = self[min([self[bh].rev() for bh in bheads])].node()
515 reachable = self.changelog.reachable(latest, minbhrev)
516 reachable = self.changelog.reachable(latest, minbhrev)
516 reachable.remove(latest)
517 reachable.remove(latest)
517 bheads = [b for b in bheads if b not in reachable]
518 bheads = [b for b in bheads if b not in reachable]
518 partial[branch] = bheads
519 partial[branch] = bheads
519
520
520 def lookup(self, key):
521 def lookup(self, key):
521 if isinstance(key, int):
522 if isinstance(key, int):
522 return self.changelog.node(key)
523 return self.changelog.node(key)
523 elif key == '.':
524 elif key == '.':
524 return self.dirstate.parents()[0]
525 return self.dirstate.parents()[0]
525 elif key == 'null':
526 elif key == 'null':
526 return nullid
527 return nullid
527 elif key == 'tip':
528 elif key == 'tip':
528 return self.changelog.tip()
529 return self.changelog.tip()
529 n = self.changelog._match(key)
530 n = self.changelog._match(key)
530 if n:
531 if n:
531 return n
532 return n
532 if key in self._bookmarks:
533 if key in self._bookmarks:
533 return self._bookmarks[key]
534 return self._bookmarks[key]
534 if key in self.tags():
535 if key in self.tags():
535 return self.tags()[key]
536 return self.tags()[key]
536 if key in self.branchtags():
537 if key in self.branchtags():
537 return self.branchtags()[key]
538 return self.branchtags()[key]
538 n = self.changelog._partialmatch(key)
539 n = self.changelog._partialmatch(key)
539 if n:
540 if n:
540 return n
541 return n
541
542
542 # can't find key, check if it might have come from damaged dirstate
543 # can't find key, check if it might have come from damaged dirstate
543 if key in self.dirstate.parents():
544 if key in self.dirstate.parents():
544 raise error.Abort(_("working directory has unknown parent '%s'!")
545 raise error.Abort(_("working directory has unknown parent '%s'!")
545 % short(key))
546 % short(key))
546 try:
547 try:
547 if len(key) == 20:
548 if len(key) == 20:
548 key = hex(key)
549 key = hex(key)
549 except:
550 except:
550 pass
551 pass
551 raise error.RepoLookupError(_("unknown revision '%s'") % key)
552 raise error.RepoLookupError(_("unknown revision '%s'") % key)
552
553
553 def lookupbranch(self, key, remote=None):
554 def lookupbranch(self, key, remote=None):
554 repo = remote or self
555 repo = remote or self
555 if key in repo.branchmap():
556 if key in repo.branchmap():
556 return key
557 return key
557
558
558 repo = (remote and remote.local()) and remote or self
559 repo = (remote and remote.local()) and remote or self
559 return repo[key].branch()
560 return repo[key].branch()
560
561
562 def known(self, nodes):
563 nm = self.changelog.nodemap
564 return [(n in nm) for n in nodes]
565
561 def local(self):
566 def local(self):
562 return True
567 return True
563
568
564 def join(self, f):
569 def join(self, f):
565 return os.path.join(self.path, f)
570 return os.path.join(self.path, f)
566
571
567 def wjoin(self, f):
572 def wjoin(self, f):
568 return os.path.join(self.root, f)
573 return os.path.join(self.root, f)
569
574
570 def file(self, f):
575 def file(self, f):
571 if f[0] == '/':
576 if f[0] == '/':
572 f = f[1:]
577 f = f[1:]
573 return filelog.filelog(self.sopener, f)
578 return filelog.filelog(self.sopener, f)
574
579
575 def changectx(self, changeid):
580 def changectx(self, changeid):
576 return self[changeid]
581 return self[changeid]
577
582
578 def parents(self, changeid=None):
583 def parents(self, changeid=None):
579 '''get list of changectxs for parents of changeid'''
584 '''get list of changectxs for parents of changeid'''
580 return self[changeid].parents()
585 return self[changeid].parents()
581
586
582 def filectx(self, path, changeid=None, fileid=None):
587 def filectx(self, path, changeid=None, fileid=None):
583 """changeid can be a changeset revision, node, or tag.
588 """changeid can be a changeset revision, node, or tag.
584 fileid can be a file revision or node."""
589 fileid can be a file revision or node."""
585 return context.filectx(self, path, changeid, fileid)
590 return context.filectx(self, path, changeid, fileid)
586
591
587 def getcwd(self):
592 def getcwd(self):
588 return self.dirstate.getcwd()
593 return self.dirstate.getcwd()
589
594
590 def pathto(self, f, cwd=None):
595 def pathto(self, f, cwd=None):
591 return self.dirstate.pathto(f, cwd)
596 return self.dirstate.pathto(f, cwd)
592
597
593 def wfile(self, f, mode='r'):
598 def wfile(self, f, mode='r'):
594 return self.wopener(f, mode)
599 return self.wopener(f, mode)
595
600
596 def _link(self, f):
601 def _link(self, f):
597 return os.path.islink(self.wjoin(f))
602 return os.path.islink(self.wjoin(f))
598
603
599 def _loadfilter(self, filter):
604 def _loadfilter(self, filter):
600 if filter not in self.filterpats:
605 if filter not in self.filterpats:
601 l = []
606 l = []
602 for pat, cmd in self.ui.configitems(filter):
607 for pat, cmd in self.ui.configitems(filter):
603 if cmd == '!':
608 if cmd == '!':
604 continue
609 continue
605 mf = matchmod.match(self.root, '', [pat])
610 mf = matchmod.match(self.root, '', [pat])
606 fn = None
611 fn = None
607 params = cmd
612 params = cmd
608 for name, filterfn in self._datafilters.iteritems():
613 for name, filterfn in self._datafilters.iteritems():
609 if cmd.startswith(name):
614 if cmd.startswith(name):
610 fn = filterfn
615 fn = filterfn
611 params = cmd[len(name):].lstrip()
616 params = cmd[len(name):].lstrip()
612 break
617 break
613 if not fn:
618 if not fn:
614 fn = lambda s, c, **kwargs: util.filter(s, c)
619 fn = lambda s, c, **kwargs: util.filter(s, c)
615 # Wrap old filters not supporting keyword arguments
620 # Wrap old filters not supporting keyword arguments
616 if not inspect.getargspec(fn)[2]:
621 if not inspect.getargspec(fn)[2]:
617 oldfn = fn
622 oldfn = fn
618 fn = lambda s, c, **kwargs: oldfn(s, c)
623 fn = lambda s, c, **kwargs: oldfn(s, c)
619 l.append((mf, fn, params))
624 l.append((mf, fn, params))
620 self.filterpats[filter] = l
625 self.filterpats[filter] = l
621 return self.filterpats[filter]
626 return self.filterpats[filter]
622
627
623 def _filter(self, filterpats, filename, data):
628 def _filter(self, filterpats, filename, data):
624 for mf, fn, cmd in filterpats:
629 for mf, fn, cmd in filterpats:
625 if mf(filename):
630 if mf(filename):
626 self.ui.debug("filtering %s through %s\n" % (filename, cmd))
631 self.ui.debug("filtering %s through %s\n" % (filename, cmd))
627 data = fn(data, cmd, ui=self.ui, repo=self, filename=filename)
632 data = fn(data, cmd, ui=self.ui, repo=self, filename=filename)
628 break
633 break
629
634
630 return data
635 return data
631
636
632 @propertycache
637 @propertycache
633 def _encodefilterpats(self):
638 def _encodefilterpats(self):
634 return self._loadfilter('encode')
639 return self._loadfilter('encode')
635
640
636 @propertycache
641 @propertycache
637 def _decodefilterpats(self):
642 def _decodefilterpats(self):
638 return self._loadfilter('decode')
643 return self._loadfilter('decode')
639
644
640 def adddatafilter(self, name, filter):
645 def adddatafilter(self, name, filter):
641 self._datafilters[name] = filter
646 self._datafilters[name] = filter
642
647
643 def wread(self, filename):
648 def wread(self, filename):
644 if self._link(filename):
649 if self._link(filename):
645 data = os.readlink(self.wjoin(filename))
650 data = os.readlink(self.wjoin(filename))
646 else:
651 else:
647 data = self.wopener(filename, 'r').read()
652 data = self.wopener(filename, 'r').read()
648 return self._filter(self._encodefilterpats, filename, data)
653 return self._filter(self._encodefilterpats, filename, data)
649
654
650 def wwrite(self, filename, data, flags):
655 def wwrite(self, filename, data, flags):
651 data = self._filter(self._decodefilterpats, filename, data)
656 data = self._filter(self._decodefilterpats, filename, data)
652 if 'l' in flags:
657 if 'l' in flags:
653 self.wopener.symlink(data, filename)
658 self.wopener.symlink(data, filename)
654 else:
659 else:
655 self.wopener(filename, 'w').write(data)
660 self.wopener(filename, 'w').write(data)
656 if 'x' in flags:
661 if 'x' in flags:
657 util.set_flags(self.wjoin(filename), False, True)
662 util.set_flags(self.wjoin(filename), False, True)
658
663
659 def wwritedata(self, filename, data):
664 def wwritedata(self, filename, data):
660 return self._filter(self._decodefilterpats, filename, data)
665 return self._filter(self._decodefilterpats, filename, data)
661
666
662 def transaction(self, desc):
667 def transaction(self, desc):
663 tr = self._transref and self._transref() or None
668 tr = self._transref and self._transref() or None
664 if tr and tr.running():
669 if tr and tr.running():
665 return tr.nest()
670 return tr.nest()
666
671
667 # abort here if the journal already exists
672 # abort here if the journal already exists
668 if os.path.exists(self.sjoin("journal")):
673 if os.path.exists(self.sjoin("journal")):
669 raise error.RepoError(
674 raise error.RepoError(
670 _("abandoned transaction found - run hg recover"))
675 _("abandoned transaction found - run hg recover"))
671
676
672 # save dirstate for rollback
677 # save dirstate for rollback
673 try:
678 try:
674 ds = self.opener("dirstate").read()
679 ds = self.opener("dirstate").read()
675 except IOError:
680 except IOError:
676 ds = ""
681 ds = ""
677 self.opener("journal.dirstate", "w").write(ds)
682 self.opener("journal.dirstate", "w").write(ds)
678 self.opener("journal.branch", "w").write(
683 self.opener("journal.branch", "w").write(
679 encoding.fromlocal(self.dirstate.branch()))
684 encoding.fromlocal(self.dirstate.branch()))
680 self.opener("journal.desc", "w").write("%d\n%s\n" % (len(self), desc))
685 self.opener("journal.desc", "w").write("%d\n%s\n" % (len(self), desc))
681
686
682 renames = [(self.sjoin("journal"), self.sjoin("undo")),
687 renames = [(self.sjoin("journal"), self.sjoin("undo")),
683 (self.join("journal.dirstate"), self.join("undo.dirstate")),
688 (self.join("journal.dirstate"), self.join("undo.dirstate")),
684 (self.join("journal.branch"), self.join("undo.branch")),
689 (self.join("journal.branch"), self.join("undo.branch")),
685 (self.join("journal.desc"), self.join("undo.desc"))]
690 (self.join("journal.desc"), self.join("undo.desc"))]
686 tr = transaction.transaction(self.ui.warn, self.sopener,
691 tr = transaction.transaction(self.ui.warn, self.sopener,
687 self.sjoin("journal"),
692 self.sjoin("journal"),
688 aftertrans(renames),
693 aftertrans(renames),
689 self.store.createmode)
694 self.store.createmode)
690 self._transref = weakref.ref(tr)
695 self._transref = weakref.ref(tr)
691 return tr
696 return tr
692
697
693 def recover(self):
698 def recover(self):
694 lock = self.lock()
699 lock = self.lock()
695 try:
700 try:
696 if os.path.exists(self.sjoin("journal")):
701 if os.path.exists(self.sjoin("journal")):
697 self.ui.status(_("rolling back interrupted transaction\n"))
702 self.ui.status(_("rolling back interrupted transaction\n"))
698 transaction.rollback(self.sopener, self.sjoin("journal"),
703 transaction.rollback(self.sopener, self.sjoin("journal"),
699 self.ui.warn)
704 self.ui.warn)
700 self.invalidate()
705 self.invalidate()
701 return True
706 return True
702 else:
707 else:
703 self.ui.warn(_("no interrupted transaction available\n"))
708 self.ui.warn(_("no interrupted transaction available\n"))
704 return False
709 return False
705 finally:
710 finally:
706 lock.release()
711 lock.release()
707
712
708 def rollback(self, dryrun=False):
713 def rollback(self, dryrun=False):
709 wlock = lock = None
714 wlock = lock = None
710 try:
715 try:
711 wlock = self.wlock()
716 wlock = self.wlock()
712 lock = self.lock()
717 lock = self.lock()
713 if os.path.exists(self.sjoin("undo")):
718 if os.path.exists(self.sjoin("undo")):
714 try:
719 try:
715 args = self.opener("undo.desc", "r").read().splitlines()
720 args = self.opener("undo.desc", "r").read().splitlines()
716 if len(args) >= 3 and self.ui.verbose:
721 if len(args) >= 3 and self.ui.verbose:
717 desc = _("repository tip rolled back to revision %s"
722 desc = _("repository tip rolled back to revision %s"
718 " (undo %s: %s)\n") % (
723 " (undo %s: %s)\n") % (
719 int(args[0]) - 1, args[1], args[2])
724 int(args[0]) - 1, args[1], args[2])
720 elif len(args) >= 2:
725 elif len(args) >= 2:
721 desc = _("repository tip rolled back to revision %s"
726 desc = _("repository tip rolled back to revision %s"
722 " (undo %s)\n") % (
727 " (undo %s)\n") % (
723 int(args[0]) - 1, args[1])
728 int(args[0]) - 1, args[1])
724 except IOError:
729 except IOError:
725 desc = _("rolling back unknown transaction\n")
730 desc = _("rolling back unknown transaction\n")
726 self.ui.status(desc)
731 self.ui.status(desc)
727 if dryrun:
732 if dryrun:
728 return
733 return
729 transaction.rollback(self.sopener, self.sjoin("undo"),
734 transaction.rollback(self.sopener, self.sjoin("undo"),
730 self.ui.warn)
735 self.ui.warn)
731 util.rename(self.join("undo.dirstate"), self.join("dirstate"))
736 util.rename(self.join("undo.dirstate"), self.join("dirstate"))
732 if os.path.exists(self.join('undo.bookmarks')):
737 if os.path.exists(self.join('undo.bookmarks')):
733 util.rename(self.join('undo.bookmarks'),
738 util.rename(self.join('undo.bookmarks'),
734 self.join('bookmarks'))
739 self.join('bookmarks'))
735 try:
740 try:
736 branch = self.opener("undo.branch").read()
741 branch = self.opener("undo.branch").read()
737 self.dirstate.setbranch(branch)
742 self.dirstate.setbranch(branch)
738 except IOError:
743 except IOError:
739 self.ui.warn(_("Named branch could not be reset, "
744 self.ui.warn(_("Named branch could not be reset, "
740 "current branch still is: %s\n")
745 "current branch still is: %s\n")
741 % self.dirstate.branch())
746 % self.dirstate.branch())
742 self.invalidate()
747 self.invalidate()
743 self.dirstate.invalidate()
748 self.dirstate.invalidate()
744 self.destroyed()
749 self.destroyed()
745 parents = tuple([p.rev() for p in self.parents()])
750 parents = tuple([p.rev() for p in self.parents()])
746 if len(parents) > 1:
751 if len(parents) > 1:
747 self.ui.status(_("working directory now based on "
752 self.ui.status(_("working directory now based on "
748 "revisions %d and %d\n") % parents)
753 "revisions %d and %d\n") % parents)
749 else:
754 else:
750 self.ui.status(_("working directory now based on "
755 self.ui.status(_("working directory now based on "
751 "revision %d\n") % parents)
756 "revision %d\n") % parents)
752 else:
757 else:
753 self.ui.warn(_("no rollback information available\n"))
758 self.ui.warn(_("no rollback information available\n"))
754 return 1
759 return 1
755 finally:
760 finally:
756 release(lock, wlock)
761 release(lock, wlock)
757
762
758 def invalidatecaches(self):
763 def invalidatecaches(self):
759 self._tags = None
764 self._tags = None
760 self._tagtypes = None
765 self._tagtypes = None
761 self.nodetagscache = None
766 self.nodetagscache = None
762 self._branchcache = None # in UTF-8
767 self._branchcache = None # in UTF-8
763 self._branchcachetip = None
768 self._branchcachetip = None
764
769
765 def invalidate(self):
770 def invalidate(self):
766 for a in ("changelog", "manifest", "_bookmarks", "_bookmarkcurrent"):
771 for a in ("changelog", "manifest", "_bookmarks", "_bookmarkcurrent"):
767 if a in self.__dict__:
772 if a in self.__dict__:
768 delattr(self, a)
773 delattr(self, a)
769 self.invalidatecaches()
774 self.invalidatecaches()
770
775
771 def _lock(self, lockname, wait, releasefn, acquirefn, desc):
776 def _lock(self, lockname, wait, releasefn, acquirefn, desc):
772 try:
777 try:
773 l = lock.lock(lockname, 0, releasefn, desc=desc)
778 l = lock.lock(lockname, 0, releasefn, desc=desc)
774 except error.LockHeld, inst:
779 except error.LockHeld, inst:
775 if not wait:
780 if not wait:
776 raise
781 raise
777 self.ui.warn(_("waiting for lock on %s held by %r\n") %
782 self.ui.warn(_("waiting for lock on %s held by %r\n") %
778 (desc, inst.locker))
783 (desc, inst.locker))
779 # default to 600 seconds timeout
784 # default to 600 seconds timeout
780 l = lock.lock(lockname, int(self.ui.config("ui", "timeout", "600")),
785 l = lock.lock(lockname, int(self.ui.config("ui", "timeout", "600")),
781 releasefn, desc=desc)
786 releasefn, desc=desc)
782 if acquirefn:
787 if acquirefn:
783 acquirefn()
788 acquirefn()
784 return l
789 return l
785
790
786 def lock(self, wait=True):
791 def lock(self, wait=True):
787 '''Lock the repository store (.hg/store) and return a weak reference
792 '''Lock the repository store (.hg/store) and return a weak reference
788 to the lock. Use this before modifying the store (e.g. committing or
793 to the lock. Use this before modifying the store (e.g. committing or
789 stripping). If you are opening a transaction, get a lock as well.)'''
794 stripping). If you are opening a transaction, get a lock as well.)'''
790 l = self._lockref and self._lockref()
795 l = self._lockref and self._lockref()
791 if l is not None and l.held:
796 if l is not None and l.held:
792 l.lock()
797 l.lock()
793 return l
798 return l
794
799
795 l = self._lock(self.sjoin("lock"), wait, self.store.write,
800 l = self._lock(self.sjoin("lock"), wait, self.store.write,
796 self.invalidate, _('repository %s') % self.origroot)
801 self.invalidate, _('repository %s') % self.origroot)
797 self._lockref = weakref.ref(l)
802 self._lockref = weakref.ref(l)
798 return l
803 return l
799
804
800 def wlock(self, wait=True):
805 def wlock(self, wait=True):
801 '''Lock the non-store parts of the repository (everything under
806 '''Lock the non-store parts of the repository (everything under
802 .hg except .hg/store) and return a weak reference to the lock.
807 .hg except .hg/store) and return a weak reference to the lock.
803 Use this before modifying files in .hg.'''
808 Use this before modifying files in .hg.'''
804 l = self._wlockref and self._wlockref()
809 l = self._wlockref and self._wlockref()
805 if l is not None and l.held:
810 if l is not None and l.held:
806 l.lock()
811 l.lock()
807 return l
812 return l
808
813
809 l = self._lock(self.join("wlock"), wait, self.dirstate.write,
814 l = self._lock(self.join("wlock"), wait, self.dirstate.write,
810 self.dirstate.invalidate, _('working directory of %s') %
815 self.dirstate.invalidate, _('working directory of %s') %
811 self.origroot)
816 self.origroot)
812 self._wlockref = weakref.ref(l)
817 self._wlockref = weakref.ref(l)
813 return l
818 return l
814
819
815 def _filecommit(self, fctx, manifest1, manifest2, linkrev, tr, changelist):
820 def _filecommit(self, fctx, manifest1, manifest2, linkrev, tr, changelist):
816 """
821 """
817 commit an individual file as part of a larger transaction
822 commit an individual file as part of a larger transaction
818 """
823 """
819
824
820 fname = fctx.path()
825 fname = fctx.path()
821 text = fctx.data()
826 text = fctx.data()
822 flog = self.file(fname)
827 flog = self.file(fname)
823 fparent1 = manifest1.get(fname, nullid)
828 fparent1 = manifest1.get(fname, nullid)
824 fparent2 = fparent2o = manifest2.get(fname, nullid)
829 fparent2 = fparent2o = manifest2.get(fname, nullid)
825
830
826 meta = {}
831 meta = {}
827 copy = fctx.renamed()
832 copy = fctx.renamed()
828 if copy and copy[0] != fname:
833 if copy and copy[0] != fname:
829 # Mark the new revision of this file as a copy of another
834 # Mark the new revision of this file as a copy of another
830 # file. This copy data will effectively act as a parent
835 # file. This copy data will effectively act as a parent
831 # of this new revision. If this is a merge, the first
836 # of this new revision. If this is a merge, the first
832 # parent will be the nullid (meaning "look up the copy data")
837 # parent will be the nullid (meaning "look up the copy data")
833 # and the second one will be the other parent. For example:
838 # and the second one will be the other parent. For example:
834 #
839 #
835 # 0 --- 1 --- 3 rev1 changes file foo
840 # 0 --- 1 --- 3 rev1 changes file foo
836 # \ / rev2 renames foo to bar and changes it
841 # \ / rev2 renames foo to bar and changes it
837 # \- 2 -/ rev3 should have bar with all changes and
842 # \- 2 -/ rev3 should have bar with all changes and
838 # should record that bar descends from
843 # should record that bar descends from
839 # bar in rev2 and foo in rev1
844 # bar in rev2 and foo in rev1
840 #
845 #
841 # this allows this merge to succeed:
846 # this allows this merge to succeed:
842 #
847 #
843 # 0 --- 1 --- 3 rev4 reverts the content change from rev2
848 # 0 --- 1 --- 3 rev4 reverts the content change from rev2
844 # \ / merging rev3 and rev4 should use bar@rev2
849 # \ / merging rev3 and rev4 should use bar@rev2
845 # \- 2 --- 4 as the merge base
850 # \- 2 --- 4 as the merge base
846 #
851 #
847
852
848 cfname = copy[0]
853 cfname = copy[0]
849 crev = manifest1.get(cfname)
854 crev = manifest1.get(cfname)
850 newfparent = fparent2
855 newfparent = fparent2
851
856
852 if manifest2: # branch merge
857 if manifest2: # branch merge
853 if fparent2 == nullid or crev is None: # copied on remote side
858 if fparent2 == nullid or crev is None: # copied on remote side
854 if cfname in manifest2:
859 if cfname in manifest2:
855 crev = manifest2[cfname]
860 crev = manifest2[cfname]
856 newfparent = fparent1
861 newfparent = fparent1
857
862
858 # find source in nearest ancestor if we've lost track
863 # find source in nearest ancestor if we've lost track
859 if not crev:
864 if not crev:
860 self.ui.debug(" %s: searching for copy revision for %s\n" %
865 self.ui.debug(" %s: searching for copy revision for %s\n" %
861 (fname, cfname))
866 (fname, cfname))
862 for ancestor in self[None].ancestors():
867 for ancestor in self[None].ancestors():
863 if cfname in ancestor:
868 if cfname in ancestor:
864 crev = ancestor[cfname].filenode()
869 crev = ancestor[cfname].filenode()
865 break
870 break
866
871
867 if crev:
872 if crev:
868 self.ui.debug(" %s: copy %s:%s\n" % (fname, cfname, hex(crev)))
873 self.ui.debug(" %s: copy %s:%s\n" % (fname, cfname, hex(crev)))
869 meta["copy"] = cfname
874 meta["copy"] = cfname
870 meta["copyrev"] = hex(crev)
875 meta["copyrev"] = hex(crev)
871 fparent1, fparent2 = nullid, newfparent
876 fparent1, fparent2 = nullid, newfparent
872 else:
877 else:
873 self.ui.warn(_("warning: can't find ancestor for '%s' "
878 self.ui.warn(_("warning: can't find ancestor for '%s' "
874 "copied from '%s'!\n") % (fname, cfname))
879 "copied from '%s'!\n") % (fname, cfname))
875
880
876 elif fparent2 != nullid:
881 elif fparent2 != nullid:
877 # is one parent an ancestor of the other?
882 # is one parent an ancestor of the other?
878 fparentancestor = flog.ancestor(fparent1, fparent2)
883 fparentancestor = flog.ancestor(fparent1, fparent2)
879 if fparentancestor == fparent1:
884 if fparentancestor == fparent1:
880 fparent1, fparent2 = fparent2, nullid
885 fparent1, fparent2 = fparent2, nullid
881 elif fparentancestor == fparent2:
886 elif fparentancestor == fparent2:
882 fparent2 = nullid
887 fparent2 = nullid
883
888
884 # is the file changed?
889 # is the file changed?
885 if fparent2 != nullid or flog.cmp(fparent1, text) or meta:
890 if fparent2 != nullid or flog.cmp(fparent1, text) or meta:
886 changelist.append(fname)
891 changelist.append(fname)
887 return flog.add(text, meta, tr, linkrev, fparent1, fparent2)
892 return flog.add(text, meta, tr, linkrev, fparent1, fparent2)
888
893
889 # are just the flags changed during merge?
894 # are just the flags changed during merge?
890 if fparent1 != fparent2o and manifest1.flags(fname) != fctx.flags():
895 if fparent1 != fparent2o and manifest1.flags(fname) != fctx.flags():
891 changelist.append(fname)
896 changelist.append(fname)
892
897
893 return fparent1
898 return fparent1
894
899
895 def commit(self, text="", user=None, date=None, match=None, force=False,
900 def commit(self, text="", user=None, date=None, match=None, force=False,
896 editor=False, extra={}):
901 editor=False, extra={}):
897 """Add a new revision to current repository.
902 """Add a new revision to current repository.
898
903
899 Revision information is gathered from the working directory,
904 Revision information is gathered from the working directory,
900 match can be used to filter the committed files. If editor is
905 match can be used to filter the committed files. If editor is
901 supplied, it is called to get a commit message.
906 supplied, it is called to get a commit message.
902 """
907 """
903
908
904 def fail(f, msg):
909 def fail(f, msg):
905 raise util.Abort('%s: %s' % (f, msg))
910 raise util.Abort('%s: %s' % (f, msg))
906
911
907 if not match:
912 if not match:
908 match = matchmod.always(self.root, '')
913 match = matchmod.always(self.root, '')
909
914
910 if not force:
915 if not force:
911 vdirs = []
916 vdirs = []
912 match.dir = vdirs.append
917 match.dir = vdirs.append
913 match.bad = fail
918 match.bad = fail
914
919
915 wlock = self.wlock()
920 wlock = self.wlock()
916 try:
921 try:
917 wctx = self[None]
922 wctx = self[None]
918 merge = len(wctx.parents()) > 1
923 merge = len(wctx.parents()) > 1
919
924
920 if (not force and merge and match and
925 if (not force and merge and match and
921 (match.files() or match.anypats())):
926 (match.files() or match.anypats())):
922 raise util.Abort(_('cannot partially commit a merge '
927 raise util.Abort(_('cannot partially commit a merge '
923 '(do not specify files or patterns)'))
928 '(do not specify files or patterns)'))
924
929
925 changes = self.status(match=match, clean=force)
930 changes = self.status(match=match, clean=force)
926 if force:
931 if force:
927 changes[0].extend(changes[6]) # mq may commit unchanged files
932 changes[0].extend(changes[6]) # mq may commit unchanged files
928
933
929 # check subrepos
934 # check subrepos
930 subs = []
935 subs = []
931 removedsubs = set()
936 removedsubs = set()
932 for p in wctx.parents():
937 for p in wctx.parents():
933 removedsubs.update(s for s in p.substate if match(s))
938 removedsubs.update(s for s in p.substate if match(s))
934 for s in wctx.substate:
939 for s in wctx.substate:
935 removedsubs.discard(s)
940 removedsubs.discard(s)
936 if match(s) and wctx.sub(s).dirty():
941 if match(s) and wctx.sub(s).dirty():
937 subs.append(s)
942 subs.append(s)
938 if (subs or removedsubs):
943 if (subs or removedsubs):
939 if (not match('.hgsub') and
944 if (not match('.hgsub') and
940 '.hgsub' in (wctx.modified() + wctx.added())):
945 '.hgsub' in (wctx.modified() + wctx.added())):
941 raise util.Abort(_("can't commit subrepos without .hgsub"))
946 raise util.Abort(_("can't commit subrepos without .hgsub"))
942 if '.hgsubstate' not in changes[0]:
947 if '.hgsubstate' not in changes[0]:
943 changes[0].insert(0, '.hgsubstate')
948 changes[0].insert(0, '.hgsubstate')
944
949
945 if subs and not self.ui.configbool('ui', 'commitsubrepos', True):
950 if subs and not self.ui.configbool('ui', 'commitsubrepos', True):
946 changedsubs = [s for s in subs if wctx.sub(s).dirty(True)]
951 changedsubs = [s for s in subs if wctx.sub(s).dirty(True)]
947 if changedsubs:
952 if changedsubs:
948 raise util.Abort(_("uncommitted changes in subrepo %s")
953 raise util.Abort(_("uncommitted changes in subrepo %s")
949 % changedsubs[0])
954 % changedsubs[0])
950
955
951 # make sure all explicit patterns are matched
956 # make sure all explicit patterns are matched
952 if not force and match.files():
957 if not force and match.files():
953 matched = set(changes[0] + changes[1] + changes[2])
958 matched = set(changes[0] + changes[1] + changes[2])
954
959
955 for f in match.files():
960 for f in match.files():
956 if f == '.' or f in matched or f in wctx.substate:
961 if f == '.' or f in matched or f in wctx.substate:
957 continue
962 continue
958 if f in changes[3]: # missing
963 if f in changes[3]: # missing
959 fail(f, _('file not found!'))
964 fail(f, _('file not found!'))
960 if f in vdirs: # visited directory
965 if f in vdirs: # visited directory
961 d = f + '/'
966 d = f + '/'
962 for mf in matched:
967 for mf in matched:
963 if mf.startswith(d):
968 if mf.startswith(d):
964 break
969 break
965 else:
970 else:
966 fail(f, _("no match under directory!"))
971 fail(f, _("no match under directory!"))
967 elif f not in self.dirstate:
972 elif f not in self.dirstate:
968 fail(f, _("file not tracked!"))
973 fail(f, _("file not tracked!"))
969
974
970 if (not force and not extra.get("close") and not merge
975 if (not force and not extra.get("close") and not merge
971 and not (changes[0] or changes[1] or changes[2])
976 and not (changes[0] or changes[1] or changes[2])
972 and wctx.branch() == wctx.p1().branch()):
977 and wctx.branch() == wctx.p1().branch()):
973 return None
978 return None
974
979
975 ms = mergemod.mergestate(self)
980 ms = mergemod.mergestate(self)
976 for f in changes[0]:
981 for f in changes[0]:
977 if f in ms and ms[f] == 'u':
982 if f in ms and ms[f] == 'u':
978 raise util.Abort(_("unresolved merge conflicts "
983 raise util.Abort(_("unresolved merge conflicts "
979 "(see hg help resolve)"))
984 "(see hg help resolve)"))
980
985
981 cctx = context.workingctx(self, text, user, date, extra, changes)
986 cctx = context.workingctx(self, text, user, date, extra, changes)
982 if editor:
987 if editor:
983 cctx._text = editor(self, cctx, subs)
988 cctx._text = editor(self, cctx, subs)
984 edited = (text != cctx._text)
989 edited = (text != cctx._text)
985
990
986 # commit subs
991 # commit subs
987 if subs or removedsubs:
992 if subs or removedsubs:
988 state = wctx.substate.copy()
993 state = wctx.substate.copy()
989 for s in sorted(subs):
994 for s in sorted(subs):
990 sub = wctx.sub(s)
995 sub = wctx.sub(s)
991 self.ui.status(_('committing subrepository %s\n') %
996 self.ui.status(_('committing subrepository %s\n') %
992 subrepo.subrelpath(sub))
997 subrepo.subrelpath(sub))
993 sr = sub.commit(cctx._text, user, date)
998 sr = sub.commit(cctx._text, user, date)
994 state[s] = (state[s][0], sr)
999 state[s] = (state[s][0], sr)
995 subrepo.writestate(self, state)
1000 subrepo.writestate(self, state)
996
1001
997 # Save commit message in case this transaction gets rolled back
1002 # Save commit message in case this transaction gets rolled back
998 # (e.g. by a pretxncommit hook). Leave the content alone on
1003 # (e.g. by a pretxncommit hook). Leave the content alone on
999 # the assumption that the user will use the same editor again.
1004 # the assumption that the user will use the same editor again.
1000 msgfile = self.opener('last-message.txt', 'wb')
1005 msgfile = self.opener('last-message.txt', 'wb')
1001 msgfile.write(cctx._text)
1006 msgfile.write(cctx._text)
1002 msgfile.close()
1007 msgfile.close()
1003
1008
1004 p1, p2 = self.dirstate.parents()
1009 p1, p2 = self.dirstate.parents()
1005 hookp1, hookp2 = hex(p1), (p2 != nullid and hex(p2) or '')
1010 hookp1, hookp2 = hex(p1), (p2 != nullid and hex(p2) or '')
1006 try:
1011 try:
1007 self.hook("precommit", throw=True, parent1=hookp1, parent2=hookp2)
1012 self.hook("precommit", throw=True, parent1=hookp1, parent2=hookp2)
1008 ret = self.commitctx(cctx, True)
1013 ret = self.commitctx(cctx, True)
1009 except:
1014 except:
1010 if edited:
1015 if edited:
1011 msgfn = self.pathto(msgfile.name[len(self.root)+1:])
1016 msgfn = self.pathto(msgfile.name[len(self.root)+1:])
1012 self.ui.write(
1017 self.ui.write(
1013 _('note: commit message saved in %s\n') % msgfn)
1018 _('note: commit message saved in %s\n') % msgfn)
1014 raise
1019 raise
1015
1020
1016 # update bookmarks, dirstate and mergestate
1021 # update bookmarks, dirstate and mergestate
1017 parents = (p1, p2)
1022 parents = (p1, p2)
1018 if p2 == nullid:
1023 if p2 == nullid:
1019 parents = (p1,)
1024 parents = (p1,)
1020 bookmarks.update(self, parents, ret)
1025 bookmarks.update(self, parents, ret)
1021 for f in changes[0] + changes[1]:
1026 for f in changes[0] + changes[1]:
1022 self.dirstate.normal(f)
1027 self.dirstate.normal(f)
1023 for f in changes[2]:
1028 for f in changes[2]:
1024 self.dirstate.forget(f)
1029 self.dirstate.forget(f)
1025 self.dirstate.setparents(ret)
1030 self.dirstate.setparents(ret)
1026 ms.reset()
1031 ms.reset()
1027 finally:
1032 finally:
1028 wlock.release()
1033 wlock.release()
1029
1034
1030 self.hook("commit", node=hex(ret), parent1=hookp1, parent2=hookp2)
1035 self.hook("commit", node=hex(ret), parent1=hookp1, parent2=hookp2)
1031 return ret
1036 return ret
1032
1037
1033 def commitctx(self, ctx, error=False):
1038 def commitctx(self, ctx, error=False):
1034 """Add a new revision to current repository.
1039 """Add a new revision to current repository.
1035 Revision information is passed via the context argument.
1040 Revision information is passed via the context argument.
1036 """
1041 """
1037
1042
1038 tr = lock = None
1043 tr = lock = None
1039 removed = list(ctx.removed())
1044 removed = list(ctx.removed())
1040 p1, p2 = ctx.p1(), ctx.p2()
1045 p1, p2 = ctx.p1(), ctx.p2()
1041 m1 = p1.manifest().copy()
1046 m1 = p1.manifest().copy()
1042 m2 = p2.manifest()
1047 m2 = p2.manifest()
1043 user = ctx.user()
1048 user = ctx.user()
1044
1049
1045 lock = self.lock()
1050 lock = self.lock()
1046 try:
1051 try:
1047 tr = self.transaction("commit")
1052 tr = self.transaction("commit")
1048 trp = weakref.proxy(tr)
1053 trp = weakref.proxy(tr)
1049
1054
1050 # check in files
1055 # check in files
1051 new = {}
1056 new = {}
1052 changed = []
1057 changed = []
1053 linkrev = len(self)
1058 linkrev = len(self)
1054 for f in sorted(ctx.modified() + ctx.added()):
1059 for f in sorted(ctx.modified() + ctx.added()):
1055 self.ui.note(f + "\n")
1060 self.ui.note(f + "\n")
1056 try:
1061 try:
1057 fctx = ctx[f]
1062 fctx = ctx[f]
1058 new[f] = self._filecommit(fctx, m1, m2, linkrev, trp,
1063 new[f] = self._filecommit(fctx, m1, m2, linkrev, trp,
1059 changed)
1064 changed)
1060 m1.set(f, fctx.flags())
1065 m1.set(f, fctx.flags())
1061 except OSError, inst:
1066 except OSError, inst:
1062 self.ui.warn(_("trouble committing %s!\n") % f)
1067 self.ui.warn(_("trouble committing %s!\n") % f)
1063 raise
1068 raise
1064 except IOError, inst:
1069 except IOError, inst:
1065 errcode = getattr(inst, 'errno', errno.ENOENT)
1070 errcode = getattr(inst, 'errno', errno.ENOENT)
1066 if error or errcode and errcode != errno.ENOENT:
1071 if error or errcode and errcode != errno.ENOENT:
1067 self.ui.warn(_("trouble committing %s!\n") % f)
1072 self.ui.warn(_("trouble committing %s!\n") % f)
1068 raise
1073 raise
1069 else:
1074 else:
1070 removed.append(f)
1075 removed.append(f)
1071
1076
1072 # update manifest
1077 # update manifest
1073 m1.update(new)
1078 m1.update(new)
1074 removed = [f for f in sorted(removed) if f in m1 or f in m2]
1079 removed = [f for f in sorted(removed) if f in m1 or f in m2]
1075 drop = [f for f in removed if f in m1]
1080 drop = [f for f in removed if f in m1]
1076 for f in drop:
1081 for f in drop:
1077 del m1[f]
1082 del m1[f]
1078 mn = self.manifest.add(m1, trp, linkrev, p1.manifestnode(),
1083 mn = self.manifest.add(m1, trp, linkrev, p1.manifestnode(),
1079 p2.manifestnode(), (new, drop))
1084 p2.manifestnode(), (new, drop))
1080
1085
1081 # update changelog
1086 # update changelog
1082 self.changelog.delayupdate()
1087 self.changelog.delayupdate()
1083 n = self.changelog.add(mn, changed + removed, ctx.description(),
1088 n = self.changelog.add(mn, changed + removed, ctx.description(),
1084 trp, p1.node(), p2.node(),
1089 trp, p1.node(), p2.node(),
1085 user, ctx.date(), ctx.extra().copy())
1090 user, ctx.date(), ctx.extra().copy())
1086 p = lambda: self.changelog.writepending() and self.root or ""
1091 p = lambda: self.changelog.writepending() and self.root or ""
1087 xp1, xp2 = p1.hex(), p2 and p2.hex() or ''
1092 xp1, xp2 = p1.hex(), p2 and p2.hex() or ''
1088 self.hook('pretxncommit', throw=True, node=hex(n), parent1=xp1,
1093 self.hook('pretxncommit', throw=True, node=hex(n), parent1=xp1,
1089 parent2=xp2, pending=p)
1094 parent2=xp2, pending=p)
1090 self.changelog.finalize(trp)
1095 self.changelog.finalize(trp)
1091 tr.close()
1096 tr.close()
1092
1097
1093 if self._branchcache:
1098 if self._branchcache:
1094 self.updatebranchcache()
1099 self.updatebranchcache()
1095 return n
1100 return n
1096 finally:
1101 finally:
1097 if tr:
1102 if tr:
1098 tr.release()
1103 tr.release()
1099 lock.release()
1104 lock.release()
1100
1105
1101 def destroyed(self):
1106 def destroyed(self):
1102 '''Inform the repository that nodes have been destroyed.
1107 '''Inform the repository that nodes have been destroyed.
1103 Intended for use by strip and rollback, so there's a common
1108 Intended for use by strip and rollback, so there's a common
1104 place for anything that has to be done after destroying history.'''
1109 place for anything that has to be done after destroying history.'''
1105 # XXX it might be nice if we could take the list of destroyed
1110 # XXX it might be nice if we could take the list of destroyed
1106 # nodes, but I don't see an easy way for rollback() to do that
1111 # nodes, but I don't see an easy way for rollback() to do that
1107
1112
1108 # Ensure the persistent tag cache is updated. Doing it now
1113 # Ensure the persistent tag cache is updated. Doing it now
1109 # means that the tag cache only has to worry about destroyed
1114 # means that the tag cache only has to worry about destroyed
1110 # heads immediately after a strip/rollback. That in turn
1115 # heads immediately after a strip/rollback. That in turn
1111 # guarantees that "cachetip == currenttip" (comparing both rev
1116 # guarantees that "cachetip == currenttip" (comparing both rev
1112 # and node) always means no nodes have been added or destroyed.
1117 # and node) always means no nodes have been added or destroyed.
1113
1118
1114 # XXX this is suboptimal when qrefresh'ing: we strip the current
1119 # XXX this is suboptimal when qrefresh'ing: we strip the current
1115 # head, refresh the tag cache, then immediately add a new head.
1120 # head, refresh the tag cache, then immediately add a new head.
1116 # But I think doing it this way is necessary for the "instant
1121 # But I think doing it this way is necessary for the "instant
1117 # tag cache retrieval" case to work.
1122 # tag cache retrieval" case to work.
1118 self.invalidatecaches()
1123 self.invalidatecaches()
1119
1124
1120 def walk(self, match, node=None):
1125 def walk(self, match, node=None):
1121 '''
1126 '''
1122 walk recursively through the directory tree or a given
1127 walk recursively through the directory tree or a given
1123 changeset, finding all files matched by the match
1128 changeset, finding all files matched by the match
1124 function
1129 function
1125 '''
1130 '''
1126 return self[node].walk(match)
1131 return self[node].walk(match)
1127
1132
1128 def status(self, node1='.', node2=None, match=None,
1133 def status(self, node1='.', node2=None, match=None,
1129 ignored=False, clean=False, unknown=False,
1134 ignored=False, clean=False, unknown=False,
1130 listsubrepos=False):
1135 listsubrepos=False):
1131 """return status of files between two nodes or node and working directory
1136 """return status of files between two nodes or node and working directory
1132
1137
1133 If node1 is None, use the first dirstate parent instead.
1138 If node1 is None, use the first dirstate parent instead.
1134 If node2 is None, compare node1 with working directory.
1139 If node2 is None, compare node1 with working directory.
1135 """
1140 """
1136
1141
1137 def mfmatches(ctx):
1142 def mfmatches(ctx):
1138 mf = ctx.manifest().copy()
1143 mf = ctx.manifest().copy()
1139 for fn in mf.keys():
1144 for fn in mf.keys():
1140 if not match(fn):
1145 if not match(fn):
1141 del mf[fn]
1146 del mf[fn]
1142 return mf
1147 return mf
1143
1148
1144 if isinstance(node1, context.changectx):
1149 if isinstance(node1, context.changectx):
1145 ctx1 = node1
1150 ctx1 = node1
1146 else:
1151 else:
1147 ctx1 = self[node1]
1152 ctx1 = self[node1]
1148 if isinstance(node2, context.changectx):
1153 if isinstance(node2, context.changectx):
1149 ctx2 = node2
1154 ctx2 = node2
1150 else:
1155 else:
1151 ctx2 = self[node2]
1156 ctx2 = self[node2]
1152
1157
1153 working = ctx2.rev() is None
1158 working = ctx2.rev() is None
1154 parentworking = working and ctx1 == self['.']
1159 parentworking = working and ctx1 == self['.']
1155 match = match or matchmod.always(self.root, self.getcwd())
1160 match = match or matchmod.always(self.root, self.getcwd())
1156 listignored, listclean, listunknown = ignored, clean, unknown
1161 listignored, listclean, listunknown = ignored, clean, unknown
1157
1162
1158 # load earliest manifest first for caching reasons
1163 # load earliest manifest first for caching reasons
1159 if not working and ctx2.rev() < ctx1.rev():
1164 if not working and ctx2.rev() < ctx1.rev():
1160 ctx2.manifest()
1165 ctx2.manifest()
1161
1166
1162 if not parentworking:
1167 if not parentworking:
1163 def bad(f, msg):
1168 def bad(f, msg):
1164 if f not in ctx1:
1169 if f not in ctx1:
1165 self.ui.warn('%s: %s\n' % (self.dirstate.pathto(f), msg))
1170 self.ui.warn('%s: %s\n' % (self.dirstate.pathto(f), msg))
1166 match.bad = bad
1171 match.bad = bad
1167
1172
1168 if working: # we need to scan the working dir
1173 if working: # we need to scan the working dir
1169 subrepos = []
1174 subrepos = []
1170 if '.hgsub' in self.dirstate:
1175 if '.hgsub' in self.dirstate:
1171 subrepos = ctx1.substate.keys()
1176 subrepos = ctx1.substate.keys()
1172 s = self.dirstate.status(match, subrepos, listignored,
1177 s = self.dirstate.status(match, subrepos, listignored,
1173 listclean, listunknown)
1178 listclean, listunknown)
1174 cmp, modified, added, removed, deleted, unknown, ignored, clean = s
1179 cmp, modified, added, removed, deleted, unknown, ignored, clean = s
1175
1180
1176 # check for any possibly clean files
1181 # check for any possibly clean files
1177 if parentworking and cmp:
1182 if parentworking and cmp:
1178 fixup = []
1183 fixup = []
1179 # do a full compare of any files that might have changed
1184 # do a full compare of any files that might have changed
1180 for f in sorted(cmp):
1185 for f in sorted(cmp):
1181 if (f not in ctx1 or ctx2.flags(f) != ctx1.flags(f)
1186 if (f not in ctx1 or ctx2.flags(f) != ctx1.flags(f)
1182 or ctx1[f].cmp(ctx2[f])):
1187 or ctx1[f].cmp(ctx2[f])):
1183 modified.append(f)
1188 modified.append(f)
1184 else:
1189 else:
1185 fixup.append(f)
1190 fixup.append(f)
1186
1191
1187 # update dirstate for files that are actually clean
1192 # update dirstate for files that are actually clean
1188 if fixup:
1193 if fixup:
1189 if listclean:
1194 if listclean:
1190 clean += fixup
1195 clean += fixup
1191
1196
1192 try:
1197 try:
1193 # updating the dirstate is optional
1198 # updating the dirstate is optional
1194 # so we don't wait on the lock
1199 # so we don't wait on the lock
1195 wlock = self.wlock(False)
1200 wlock = self.wlock(False)
1196 try:
1201 try:
1197 for f in fixup:
1202 for f in fixup:
1198 self.dirstate.normal(f)
1203 self.dirstate.normal(f)
1199 finally:
1204 finally:
1200 wlock.release()
1205 wlock.release()
1201 except error.LockError:
1206 except error.LockError:
1202 pass
1207 pass
1203
1208
1204 if not parentworking:
1209 if not parentworking:
1205 mf1 = mfmatches(ctx1)
1210 mf1 = mfmatches(ctx1)
1206 if working:
1211 if working:
1207 # we are comparing working dir against non-parent
1212 # we are comparing working dir against non-parent
1208 # generate a pseudo-manifest for the working dir
1213 # generate a pseudo-manifest for the working dir
1209 mf2 = mfmatches(self['.'])
1214 mf2 = mfmatches(self['.'])
1210 for f in cmp + modified + added:
1215 for f in cmp + modified + added:
1211 mf2[f] = None
1216 mf2[f] = None
1212 mf2.set(f, ctx2.flags(f))
1217 mf2.set(f, ctx2.flags(f))
1213 for f in removed:
1218 for f in removed:
1214 if f in mf2:
1219 if f in mf2:
1215 del mf2[f]
1220 del mf2[f]
1216 else:
1221 else:
1217 # we are comparing two revisions
1222 # we are comparing two revisions
1218 deleted, unknown, ignored = [], [], []
1223 deleted, unknown, ignored = [], [], []
1219 mf2 = mfmatches(ctx2)
1224 mf2 = mfmatches(ctx2)
1220
1225
1221 modified, added, clean = [], [], []
1226 modified, added, clean = [], [], []
1222 for fn in mf2:
1227 for fn in mf2:
1223 if fn in mf1:
1228 if fn in mf1:
1224 if (mf1.flags(fn) != mf2.flags(fn) or
1229 if (mf1.flags(fn) != mf2.flags(fn) or
1225 (mf1[fn] != mf2[fn] and
1230 (mf1[fn] != mf2[fn] and
1226 (mf2[fn] or ctx1[fn].cmp(ctx2[fn])))):
1231 (mf2[fn] or ctx1[fn].cmp(ctx2[fn])))):
1227 modified.append(fn)
1232 modified.append(fn)
1228 elif listclean:
1233 elif listclean:
1229 clean.append(fn)
1234 clean.append(fn)
1230 del mf1[fn]
1235 del mf1[fn]
1231 else:
1236 else:
1232 added.append(fn)
1237 added.append(fn)
1233 removed = mf1.keys()
1238 removed = mf1.keys()
1234
1239
1235 r = modified, added, removed, deleted, unknown, ignored, clean
1240 r = modified, added, removed, deleted, unknown, ignored, clean
1236
1241
1237 if listsubrepos:
1242 if listsubrepos:
1238 for subpath, sub in subrepo.itersubrepos(ctx1, ctx2):
1243 for subpath, sub in subrepo.itersubrepos(ctx1, ctx2):
1239 if working:
1244 if working:
1240 rev2 = None
1245 rev2 = None
1241 else:
1246 else:
1242 rev2 = ctx2.substate[subpath][1]
1247 rev2 = ctx2.substate[subpath][1]
1243 try:
1248 try:
1244 submatch = matchmod.narrowmatcher(subpath, match)
1249 submatch = matchmod.narrowmatcher(subpath, match)
1245 s = sub.status(rev2, match=submatch, ignored=listignored,
1250 s = sub.status(rev2, match=submatch, ignored=listignored,
1246 clean=listclean, unknown=listunknown,
1251 clean=listclean, unknown=listunknown,
1247 listsubrepos=True)
1252 listsubrepos=True)
1248 for rfiles, sfiles in zip(r, s):
1253 for rfiles, sfiles in zip(r, s):
1249 rfiles.extend("%s/%s" % (subpath, f) for f in sfiles)
1254 rfiles.extend("%s/%s" % (subpath, f) for f in sfiles)
1250 except error.LookupError:
1255 except error.LookupError:
1251 self.ui.status(_("skipping missing subrepository: %s\n")
1256 self.ui.status(_("skipping missing subrepository: %s\n")
1252 % subpath)
1257 % subpath)
1253
1258
1254 for l in r:
1259 for l in r:
1255 l.sort()
1260 l.sort()
1256 return r
1261 return r
1257
1262
1258 def heads(self, start=None):
1263 def heads(self, start=None):
1259 heads = self.changelog.heads(start)
1264 heads = self.changelog.heads(start)
1260 # sort the output in rev descending order
1265 # sort the output in rev descending order
1261 return sorted(heads, key=self.changelog.rev, reverse=True)
1266 return sorted(heads, key=self.changelog.rev, reverse=True)
1262
1267
1263 def branchheads(self, branch=None, start=None, closed=False):
1268 def branchheads(self, branch=None, start=None, closed=False):
1264 '''return a (possibly filtered) list of heads for the given branch
1269 '''return a (possibly filtered) list of heads for the given branch
1265
1270
1266 Heads are returned in topological order, from newest to oldest.
1271 Heads are returned in topological order, from newest to oldest.
1267 If branch is None, use the dirstate branch.
1272 If branch is None, use the dirstate branch.
1268 If start is not None, return only heads reachable from start.
1273 If start is not None, return only heads reachable from start.
1269 If closed is True, return heads that are marked as closed as well.
1274 If closed is True, return heads that are marked as closed as well.
1270 '''
1275 '''
1271 if branch is None:
1276 if branch is None:
1272 branch = self[None].branch()
1277 branch = self[None].branch()
1273 branches = self.branchmap()
1278 branches = self.branchmap()
1274 if branch not in branches:
1279 if branch not in branches:
1275 return []
1280 return []
1276 # the cache returns heads ordered lowest to highest
1281 # the cache returns heads ordered lowest to highest
1277 bheads = list(reversed(branches[branch]))
1282 bheads = list(reversed(branches[branch]))
1278 if start is not None:
1283 if start is not None:
1279 # filter out the heads that cannot be reached from startrev
1284 # filter out the heads that cannot be reached from startrev
1280 fbheads = set(self.changelog.nodesbetween([start], bheads)[2])
1285 fbheads = set(self.changelog.nodesbetween([start], bheads)[2])
1281 bheads = [h for h in bheads if h in fbheads]
1286 bheads = [h for h in bheads if h in fbheads]
1282 if not closed:
1287 if not closed:
1283 bheads = [h for h in bheads if
1288 bheads = [h for h in bheads if
1284 ('close' not in self.changelog.read(h)[5])]
1289 ('close' not in self.changelog.read(h)[5])]
1285 return bheads
1290 return bheads
1286
1291
1287 def branches(self, nodes):
1292 def branches(self, nodes):
1288 if not nodes:
1293 if not nodes:
1289 nodes = [self.changelog.tip()]
1294 nodes = [self.changelog.tip()]
1290 b = []
1295 b = []
1291 for n in nodes:
1296 for n in nodes:
1292 t = n
1297 t = n
1293 while 1:
1298 while 1:
1294 p = self.changelog.parents(n)
1299 p = self.changelog.parents(n)
1295 if p[1] != nullid or p[0] == nullid:
1300 if p[1] != nullid or p[0] == nullid:
1296 b.append((t, n, p[0], p[1]))
1301 b.append((t, n, p[0], p[1]))
1297 break
1302 break
1298 n = p[0]
1303 n = p[0]
1299 return b
1304 return b
1300
1305
1301 def between(self, pairs):
1306 def between(self, pairs):
1302 r = []
1307 r = []
1303
1308
1304 for top, bottom in pairs:
1309 for top, bottom in pairs:
1305 n, l, i = top, [], 0
1310 n, l, i = top, [], 0
1306 f = 1
1311 f = 1
1307
1312
1308 while n != bottom and n != nullid:
1313 while n != bottom and n != nullid:
1309 p = self.changelog.parents(n)[0]
1314 p = self.changelog.parents(n)[0]
1310 if i == f:
1315 if i == f:
1311 l.append(n)
1316 l.append(n)
1312 f = f * 2
1317 f = f * 2
1313 n = p
1318 n = p
1314 i += 1
1319 i += 1
1315
1320
1316 r.append(l)
1321 r.append(l)
1317
1322
1318 return r
1323 return r
1319
1324
1320 def pull(self, remote, heads=None, force=False):
1325 def pull(self, remote, heads=None, force=False):
1321 lock = self.lock()
1326 lock = self.lock()
1322 try:
1327 try:
1323 tmp = discovery.findcommonincoming(self, remote, heads=heads,
1328 tmp = discovery.findcommonincoming(self, remote, heads=heads,
1324 force=force)
1329 force=force)
1325 common, fetch, rheads = tmp
1330 common, fetch, rheads = tmp
1326 if not fetch:
1331 if not fetch:
1327 self.ui.status(_("no changes found\n"))
1332 self.ui.status(_("no changes found\n"))
1328 result = 0
1333 result = 0
1329 else:
1334 else:
1330 if heads is None and fetch == [nullid]:
1335 if heads is None and fetch == [nullid]:
1331 self.ui.status(_("requesting all changes\n"))
1336 self.ui.status(_("requesting all changes\n"))
1332 elif heads is None and remote.capable('changegroupsubset'):
1337 elif heads is None and remote.capable('changegroupsubset'):
1333 # issue1320, avoid a race if remote changed after discovery
1338 # issue1320, avoid a race if remote changed after discovery
1334 heads = rheads
1339 heads = rheads
1335
1340
1336 if heads is None:
1341 if heads is None:
1337 cg = remote.changegroup(fetch, 'pull')
1342 cg = remote.changegroup(fetch, 'pull')
1338 elif not remote.capable('changegroupsubset'):
1343 elif not remote.capable('changegroupsubset'):
1339 raise util.Abort(_("partial pull cannot be done because "
1344 raise util.Abort(_("partial pull cannot be done because "
1340 "other repository doesn't support "
1345 "other repository doesn't support "
1341 "changegroupsubset."))
1346 "changegroupsubset."))
1342 else:
1347 else:
1343 cg = remote.changegroupsubset(fetch, heads, 'pull')
1348 cg = remote.changegroupsubset(fetch, heads, 'pull')
1344 result = self.addchangegroup(cg, 'pull', remote.url(),
1349 result = self.addchangegroup(cg, 'pull', remote.url(),
1345 lock=lock)
1350 lock=lock)
1346 finally:
1351 finally:
1347 lock.release()
1352 lock.release()
1348
1353
1349 return result
1354 return result
1350
1355
1351 def checkpush(self, force, revs):
1356 def checkpush(self, force, revs):
1352 """Extensions can override this function if additional checks have
1357 """Extensions can override this function if additional checks have
1353 to be performed before pushing, or call it if they override push
1358 to be performed before pushing, or call it if they override push
1354 command.
1359 command.
1355 """
1360 """
1356 pass
1361 pass
1357
1362
1358 def push(self, remote, force=False, revs=None, newbranch=False):
1363 def push(self, remote, force=False, revs=None, newbranch=False):
1359 '''Push outgoing changesets (limited by revs) from the current
1364 '''Push outgoing changesets (limited by revs) from the current
1360 repository to remote. Return an integer:
1365 repository to remote. Return an integer:
1361 - 0 means HTTP error *or* nothing to push
1366 - 0 means HTTP error *or* nothing to push
1362 - 1 means we pushed and remote head count is unchanged *or*
1367 - 1 means we pushed and remote head count is unchanged *or*
1363 we have outgoing changesets but refused to push
1368 we have outgoing changesets but refused to push
1364 - other values as described by addchangegroup()
1369 - other values as described by addchangegroup()
1365 '''
1370 '''
1366 # there are two ways to push to remote repo:
1371 # there are two ways to push to remote repo:
1367 #
1372 #
1368 # addchangegroup assumes local user can lock remote
1373 # addchangegroup assumes local user can lock remote
1369 # repo (local filesystem, old ssh servers).
1374 # repo (local filesystem, old ssh servers).
1370 #
1375 #
1371 # unbundle assumes local user cannot lock remote repo (new ssh
1376 # unbundle assumes local user cannot lock remote repo (new ssh
1372 # servers, http servers).
1377 # servers, http servers).
1373
1378
1374 self.checkpush(force, revs)
1379 self.checkpush(force, revs)
1375 lock = None
1380 lock = None
1376 unbundle = remote.capable('unbundle')
1381 unbundle = remote.capable('unbundle')
1377 if not unbundle:
1382 if not unbundle:
1378 lock = remote.lock()
1383 lock = remote.lock()
1379 try:
1384 try:
1380 cg, remote_heads = discovery.prepush(self, remote, force, revs,
1385 cg, remote_heads = discovery.prepush(self, remote, force, revs,
1381 newbranch)
1386 newbranch)
1382 ret = remote_heads
1387 ret = remote_heads
1383 if cg is not None:
1388 if cg is not None:
1384 if unbundle:
1389 if unbundle:
1385 # local repo finds heads on server, finds out what
1390 # local repo finds heads on server, finds out what
1386 # revs it must push. once revs transferred, if server
1391 # revs it must push. once revs transferred, if server
1387 # finds it has different heads (someone else won
1392 # finds it has different heads (someone else won
1388 # commit/push race), server aborts.
1393 # commit/push race), server aborts.
1389 if force:
1394 if force:
1390 remote_heads = ['force']
1395 remote_heads = ['force']
1391 # ssh: return remote's addchangegroup()
1396 # ssh: return remote's addchangegroup()
1392 # http: return remote's addchangegroup() or 0 for error
1397 # http: return remote's addchangegroup() or 0 for error
1393 ret = remote.unbundle(cg, remote_heads, 'push')
1398 ret = remote.unbundle(cg, remote_heads, 'push')
1394 else:
1399 else:
1395 # we return an integer indicating remote head count change
1400 # we return an integer indicating remote head count change
1396 ret = remote.addchangegroup(cg, 'push', self.url(),
1401 ret = remote.addchangegroup(cg, 'push', self.url(),
1397 lock=lock)
1402 lock=lock)
1398 finally:
1403 finally:
1399 if lock is not None:
1404 if lock is not None:
1400 lock.release()
1405 lock.release()
1401
1406
1402 self.ui.debug("checking for updated bookmarks\n")
1407 self.ui.debug("checking for updated bookmarks\n")
1403 rb = remote.listkeys('bookmarks')
1408 rb = remote.listkeys('bookmarks')
1404 for k in rb.keys():
1409 for k in rb.keys():
1405 if k in self._bookmarks:
1410 if k in self._bookmarks:
1406 nr, nl = rb[k], hex(self._bookmarks[k])
1411 nr, nl = rb[k], hex(self._bookmarks[k])
1407 if nr in self:
1412 if nr in self:
1408 cr = self[nr]
1413 cr = self[nr]
1409 cl = self[nl]
1414 cl = self[nl]
1410 if cl in cr.descendants():
1415 if cl in cr.descendants():
1411 r = remote.pushkey('bookmarks', k, nr, nl)
1416 r = remote.pushkey('bookmarks', k, nr, nl)
1412 if r:
1417 if r:
1413 self.ui.status(_("updating bookmark %s\n") % k)
1418 self.ui.status(_("updating bookmark %s\n") % k)
1414 else:
1419 else:
1415 self.ui.warn(_('updating bookmark %s'
1420 self.ui.warn(_('updating bookmark %s'
1416 ' failed!\n') % k)
1421 ' failed!\n') % k)
1417
1422
1418 return ret
1423 return ret
1419
1424
1420 def changegroupinfo(self, nodes, source):
1425 def changegroupinfo(self, nodes, source):
1421 if self.ui.verbose or source == 'bundle':
1426 if self.ui.verbose or source == 'bundle':
1422 self.ui.status(_("%d changesets found\n") % len(nodes))
1427 self.ui.status(_("%d changesets found\n") % len(nodes))
1423 if self.ui.debugflag:
1428 if self.ui.debugflag:
1424 self.ui.debug("list of changesets:\n")
1429 self.ui.debug("list of changesets:\n")
1425 for node in nodes:
1430 for node in nodes:
1426 self.ui.debug("%s\n" % hex(node))
1431 self.ui.debug("%s\n" % hex(node))
1427
1432
1428 def changegroupsubset(self, bases, heads, source):
1433 def changegroupsubset(self, bases, heads, source):
1429 """Compute a changegroup consisting of all the nodes that are
1434 """Compute a changegroup consisting of all the nodes that are
1430 descendents of any of the bases and ancestors of any of the heads.
1435 descendents of any of the bases and ancestors of any of the heads.
1431 Return a chunkbuffer object whose read() method will return
1436 Return a chunkbuffer object whose read() method will return
1432 successive changegroup chunks.
1437 successive changegroup chunks.
1433
1438
1434 It is fairly complex as determining which filenodes and which
1439 It is fairly complex as determining which filenodes and which
1435 manifest nodes need to be included for the changeset to be complete
1440 manifest nodes need to be included for the changeset to be complete
1436 is non-trivial.
1441 is non-trivial.
1437
1442
1438 Another wrinkle is doing the reverse, figuring out which changeset in
1443 Another wrinkle is doing the reverse, figuring out which changeset in
1439 the changegroup a particular filenode or manifestnode belongs to.
1444 the changegroup a particular filenode or manifestnode belongs to.
1440 """
1445 """
1441
1446
1442 cl = self.changelog
1447 cl = self.changelog
1443 mf = self.manifest
1448 mf = self.manifest
1444 mfs = {} # needed manifests
1449 mfs = {} # needed manifests
1445 fnodes = {} # needed file nodes
1450 fnodes = {} # needed file nodes
1446
1451
1447 if not bases:
1452 if not bases:
1448 bases = [nullid]
1453 bases = [nullid]
1449 csets, bases, heads = cl.nodesbetween(bases, heads)
1454 csets, bases, heads = cl.nodesbetween(bases, heads)
1450
1455
1451 # can we go through the fast path ?
1456 # can we go through the fast path ?
1452 heads.sort()
1457 heads.sort()
1453 if heads == sorted(self.heads()):
1458 if heads == sorted(self.heads()):
1454 return self._changegroup(csets, source)
1459 return self._changegroup(csets, source)
1455
1460
1456 # slow path
1461 # slow path
1457 self.hook('preoutgoing', throw=True, source=source)
1462 self.hook('preoutgoing', throw=True, source=source)
1458 self.changegroupinfo(csets, source)
1463 self.changegroupinfo(csets, source)
1459
1464
1460 # We assume that all ancestors of bases are known
1465 # We assume that all ancestors of bases are known
1461 commonrevs = set(cl.ancestors(*[cl.rev(n) for n in bases]))
1466 commonrevs = set(cl.ancestors(*[cl.rev(n) for n in bases]))
1462
1467
1463 # A function generating function that sets up the initial environment
1468 # A function generating function that sets up the initial environment
1464 # the inner function.
1469 # the inner function.
1465 def filenode_collector(changedfiles):
1470 def filenode_collector(changedfiles):
1466 # This gathers information from each manifestnode included in the
1471 # This gathers information from each manifestnode included in the
1467 # changegroup about which filenodes the manifest node references
1472 # changegroup about which filenodes the manifest node references
1468 # so we can include those in the changegroup too.
1473 # so we can include those in the changegroup too.
1469 #
1474 #
1470 # It also remembers which changenode each filenode belongs to. It
1475 # It also remembers which changenode each filenode belongs to. It
1471 # does this by assuming the a filenode belongs to the changenode
1476 # does this by assuming the a filenode belongs to the changenode
1472 # the first manifest that references it belongs to.
1477 # the first manifest that references it belongs to.
1473 def collect(mnode):
1478 def collect(mnode):
1474 r = mf.rev(mnode)
1479 r = mf.rev(mnode)
1475 clnode = mfs[mnode]
1480 clnode = mfs[mnode]
1476 mdata = mf.readfast(mnode)
1481 mdata = mf.readfast(mnode)
1477 for f in changedfiles:
1482 for f in changedfiles:
1478 if f in mdata:
1483 if f in mdata:
1479 fnodes.setdefault(f, {}).setdefault(mdata[f], clnode)
1484 fnodes.setdefault(f, {}).setdefault(mdata[f], clnode)
1480
1485
1481 return collect
1486 return collect
1482
1487
1483 # If we determine that a particular file or manifest node must be a
1488 # If we determine that a particular file or manifest node must be a
1484 # node that the recipient of the changegroup will already have, we can
1489 # node that the recipient of the changegroup will already have, we can
1485 # also assume the recipient will have all the parents. This function
1490 # also assume the recipient will have all the parents. This function
1486 # prunes them from the set of missing nodes.
1491 # prunes them from the set of missing nodes.
1487 def prune(revlog, missingnodes):
1492 def prune(revlog, missingnodes):
1488 # drop any nodes that claim to be part of a cset in commonrevs
1493 # drop any nodes that claim to be part of a cset in commonrevs
1489 drop = set()
1494 drop = set()
1490 for n in missingnodes:
1495 for n in missingnodes:
1491 if revlog.linkrev(revlog.rev(n)) in commonrevs:
1496 if revlog.linkrev(revlog.rev(n)) in commonrevs:
1492 drop.add(n)
1497 drop.add(n)
1493 for n in drop:
1498 for n in drop:
1494 missingnodes.pop(n, None)
1499 missingnodes.pop(n, None)
1495
1500
1496 # Now that we have all theses utility functions to help out and
1501 # Now that we have all theses utility functions to help out and
1497 # logically divide up the task, generate the group.
1502 # logically divide up the task, generate the group.
1498 def gengroup():
1503 def gengroup():
1499 # The set of changed files starts empty.
1504 # The set of changed files starts empty.
1500 changedfiles = set()
1505 changedfiles = set()
1501 collect = changegroup.collector(cl, mfs, changedfiles)
1506 collect = changegroup.collector(cl, mfs, changedfiles)
1502
1507
1503 # Create a changenode group generator that will call our functions
1508 # Create a changenode group generator that will call our functions
1504 # back to lookup the owning changenode and collect information.
1509 # back to lookup the owning changenode and collect information.
1505 group = cl.group(csets, lambda x: x, collect)
1510 group = cl.group(csets, lambda x: x, collect)
1506 for count, chunk in enumerate(group):
1511 for count, chunk in enumerate(group):
1507 yield chunk
1512 yield chunk
1508 # revlog.group yields three entries per node, so
1513 # revlog.group yields three entries per node, so
1509 # dividing by 3 gives an approximation of how many
1514 # dividing by 3 gives an approximation of how many
1510 # nodes have been processed.
1515 # nodes have been processed.
1511 self.ui.progress(_('bundling'), count / 3,
1516 self.ui.progress(_('bundling'), count / 3,
1512 unit=_('changesets'))
1517 unit=_('changesets'))
1513 changecount = count / 3
1518 changecount = count / 3
1514 efiles = len(changedfiles)
1519 efiles = len(changedfiles)
1515 self.ui.progress(_('bundling'), None)
1520 self.ui.progress(_('bundling'), None)
1516
1521
1517 prune(mf, mfs)
1522 prune(mf, mfs)
1518 # Create a generator for the manifestnodes that calls our lookup
1523 # Create a generator for the manifestnodes that calls our lookup
1519 # and data collection functions back.
1524 # and data collection functions back.
1520 group = mf.group(sorted(mfs, key=mf.rev),
1525 group = mf.group(sorted(mfs, key=mf.rev),
1521 lambda mnode: mfs[mnode],
1526 lambda mnode: mfs[mnode],
1522 filenode_collector(changedfiles))
1527 filenode_collector(changedfiles))
1523 for count, chunk in enumerate(group):
1528 for count, chunk in enumerate(group):
1524 yield chunk
1529 yield chunk
1525 # see above comment for why we divide by 3
1530 # see above comment for why we divide by 3
1526 self.ui.progress(_('bundling'), count / 3,
1531 self.ui.progress(_('bundling'), count / 3,
1527 unit=_('manifests'), total=changecount)
1532 unit=_('manifests'), total=changecount)
1528 self.ui.progress(_('bundling'), None)
1533 self.ui.progress(_('bundling'), None)
1529
1534
1530 mfs.clear()
1535 mfs.clear()
1531
1536
1532 # Go through all our files in order sorted by name.
1537 # Go through all our files in order sorted by name.
1533 for idx, fname in enumerate(sorted(changedfiles)):
1538 for idx, fname in enumerate(sorted(changedfiles)):
1534 filerevlog = self.file(fname)
1539 filerevlog = self.file(fname)
1535 if not len(filerevlog):
1540 if not len(filerevlog):
1536 raise util.Abort(_("empty or missing revlog for %s") % fname)
1541 raise util.Abort(_("empty or missing revlog for %s") % fname)
1537 # Toss out the filenodes that the recipient isn't really
1542 # Toss out the filenodes that the recipient isn't really
1538 # missing.
1543 # missing.
1539 missingfnodes = fnodes.pop(fname, {})
1544 missingfnodes = fnodes.pop(fname, {})
1540 prune(filerevlog, missingfnodes)
1545 prune(filerevlog, missingfnodes)
1541 # If any filenodes are left, generate the group for them,
1546 # If any filenodes are left, generate the group for them,
1542 # otherwise don't bother.
1547 # otherwise don't bother.
1543 if missingfnodes:
1548 if missingfnodes:
1544 yield changegroup.chunkheader(len(fname))
1549 yield changegroup.chunkheader(len(fname))
1545 yield fname
1550 yield fname
1546 # Create a group generator and only pass in a changenode
1551 # Create a group generator and only pass in a changenode
1547 # lookup function as we need to collect no information
1552 # lookup function as we need to collect no information
1548 # from filenodes.
1553 # from filenodes.
1549 group = filerevlog.group(
1554 group = filerevlog.group(
1550 sorted(missingfnodes, key=filerevlog.rev),
1555 sorted(missingfnodes, key=filerevlog.rev),
1551 lambda fnode: missingfnodes[fnode])
1556 lambda fnode: missingfnodes[fnode])
1552 for chunk in group:
1557 for chunk in group:
1553 # even though we print the same progress on
1558 # even though we print the same progress on
1554 # most loop iterations, put the progress call
1559 # most loop iterations, put the progress call
1555 # here so that time estimates (if any) can be updated
1560 # here so that time estimates (if any) can be updated
1556 self.ui.progress(
1561 self.ui.progress(
1557 _('bundling'), idx, item=fname,
1562 _('bundling'), idx, item=fname,
1558 unit=_('files'), total=efiles)
1563 unit=_('files'), total=efiles)
1559 yield chunk
1564 yield chunk
1560 # Signal that no more groups are left.
1565 # Signal that no more groups are left.
1561 yield changegroup.closechunk()
1566 yield changegroup.closechunk()
1562 self.ui.progress(_('bundling'), None)
1567 self.ui.progress(_('bundling'), None)
1563
1568
1564 if csets:
1569 if csets:
1565 self.hook('outgoing', node=hex(csets[0]), source=source)
1570 self.hook('outgoing', node=hex(csets[0]), source=source)
1566
1571
1567 return changegroup.unbundle10(util.chunkbuffer(gengroup()), 'UN')
1572 return changegroup.unbundle10(util.chunkbuffer(gengroup()), 'UN')
1568
1573
1569 def changegroup(self, basenodes, source):
1574 def changegroup(self, basenodes, source):
1570 # to avoid a race we use changegroupsubset() (issue1320)
1575 # to avoid a race we use changegroupsubset() (issue1320)
1571 return self.changegroupsubset(basenodes, self.heads(), source)
1576 return self.changegroupsubset(basenodes, self.heads(), source)
1572
1577
1573 def _changegroup(self, nodes, source):
1578 def _changegroup(self, nodes, source):
1574 """Compute the changegroup of all nodes that we have that a recipient
1579 """Compute the changegroup of all nodes that we have that a recipient
1575 doesn't. Return a chunkbuffer object whose read() method will return
1580 doesn't. Return a chunkbuffer object whose read() method will return
1576 successive changegroup chunks.
1581 successive changegroup chunks.
1577
1582
1578 This is much easier than the previous function as we can assume that
1583 This is much easier than the previous function as we can assume that
1579 the recipient has any changenode we aren't sending them.
1584 the recipient has any changenode we aren't sending them.
1580
1585
1581 nodes is the set of nodes to send"""
1586 nodes is the set of nodes to send"""
1582
1587
1583 self.hook('preoutgoing', throw=True, source=source)
1588 self.hook('preoutgoing', throw=True, source=source)
1584
1589
1585 cl = self.changelog
1590 cl = self.changelog
1586 revset = set([cl.rev(n) for n in nodes])
1591 revset = set([cl.rev(n) for n in nodes])
1587 self.changegroupinfo(nodes, source)
1592 self.changegroupinfo(nodes, source)
1588
1593
1589 def gennodelst(log):
1594 def gennodelst(log):
1590 for r in log:
1595 for r in log:
1591 if log.linkrev(r) in revset:
1596 if log.linkrev(r) in revset:
1592 yield log.node(r)
1597 yield log.node(r)
1593
1598
1594 def lookuplinkrev_func(revlog):
1599 def lookuplinkrev_func(revlog):
1595 def lookuplinkrev(n):
1600 def lookuplinkrev(n):
1596 return cl.node(revlog.linkrev(revlog.rev(n)))
1601 return cl.node(revlog.linkrev(revlog.rev(n)))
1597 return lookuplinkrev
1602 return lookuplinkrev
1598
1603
1599 def gengroup():
1604 def gengroup():
1600 '''yield a sequence of changegroup chunks (strings)'''
1605 '''yield a sequence of changegroup chunks (strings)'''
1601 # construct a list of all changed files
1606 # construct a list of all changed files
1602 changedfiles = set()
1607 changedfiles = set()
1603 mmfs = {}
1608 mmfs = {}
1604 collect = changegroup.collector(cl, mmfs, changedfiles)
1609 collect = changegroup.collector(cl, mmfs, changedfiles)
1605
1610
1606 for count, chunk in enumerate(cl.group(nodes, lambda x: x, collect)):
1611 for count, chunk in enumerate(cl.group(nodes, lambda x: x, collect)):
1607 # revlog.group yields three entries per node, so
1612 # revlog.group yields three entries per node, so
1608 # dividing by 3 gives an approximation of how many
1613 # dividing by 3 gives an approximation of how many
1609 # nodes have been processed.
1614 # nodes have been processed.
1610 self.ui.progress(_('bundling'), count / 3, unit=_('changesets'))
1615 self.ui.progress(_('bundling'), count / 3, unit=_('changesets'))
1611 yield chunk
1616 yield chunk
1612 efiles = len(changedfiles)
1617 efiles = len(changedfiles)
1613 changecount = count / 3
1618 changecount = count / 3
1614 self.ui.progress(_('bundling'), None)
1619 self.ui.progress(_('bundling'), None)
1615
1620
1616 mnfst = self.manifest
1621 mnfst = self.manifest
1617 nodeiter = gennodelst(mnfst)
1622 nodeiter = gennodelst(mnfst)
1618 for count, chunk in enumerate(mnfst.group(nodeiter,
1623 for count, chunk in enumerate(mnfst.group(nodeiter,
1619 lookuplinkrev_func(mnfst))):
1624 lookuplinkrev_func(mnfst))):
1620 # see above comment for why we divide by 3
1625 # see above comment for why we divide by 3
1621 self.ui.progress(_('bundling'), count / 3,
1626 self.ui.progress(_('bundling'), count / 3,
1622 unit=_('manifests'), total=changecount)
1627 unit=_('manifests'), total=changecount)
1623 yield chunk
1628 yield chunk
1624 self.ui.progress(_('bundling'), None)
1629 self.ui.progress(_('bundling'), None)
1625
1630
1626 for idx, fname in enumerate(sorted(changedfiles)):
1631 for idx, fname in enumerate(sorted(changedfiles)):
1627 filerevlog = self.file(fname)
1632 filerevlog = self.file(fname)
1628 if not len(filerevlog):
1633 if not len(filerevlog):
1629 raise util.Abort(_("empty or missing revlog for %s") % fname)
1634 raise util.Abort(_("empty or missing revlog for %s") % fname)
1630 nodeiter = gennodelst(filerevlog)
1635 nodeiter = gennodelst(filerevlog)
1631 nodeiter = list(nodeiter)
1636 nodeiter = list(nodeiter)
1632 if nodeiter:
1637 if nodeiter:
1633 yield changegroup.chunkheader(len(fname))
1638 yield changegroup.chunkheader(len(fname))
1634 yield fname
1639 yield fname
1635 lookup = lookuplinkrev_func(filerevlog)
1640 lookup = lookuplinkrev_func(filerevlog)
1636 for chunk in filerevlog.group(nodeiter, lookup):
1641 for chunk in filerevlog.group(nodeiter, lookup):
1637 self.ui.progress(
1642 self.ui.progress(
1638 _('bundling'), idx, item=fname,
1643 _('bundling'), idx, item=fname,
1639 total=efiles, unit=_('files'))
1644 total=efiles, unit=_('files'))
1640 yield chunk
1645 yield chunk
1641 self.ui.progress(_('bundling'), None)
1646 self.ui.progress(_('bundling'), None)
1642
1647
1643 yield changegroup.closechunk()
1648 yield changegroup.closechunk()
1644
1649
1645 if nodes:
1650 if nodes:
1646 self.hook('outgoing', node=hex(nodes[0]), source=source)
1651 self.hook('outgoing', node=hex(nodes[0]), source=source)
1647
1652
1648 return changegroup.unbundle10(util.chunkbuffer(gengroup()), 'UN')
1653 return changegroup.unbundle10(util.chunkbuffer(gengroup()), 'UN')
1649
1654
1650 def addchangegroup(self, source, srctype, url, emptyok=False, lock=None):
1655 def addchangegroup(self, source, srctype, url, emptyok=False, lock=None):
1651 """Add the changegroup returned by source.read() to this repo.
1656 """Add the changegroup returned by source.read() to this repo.
1652 srctype is a string like 'push', 'pull', or 'unbundle'. url is
1657 srctype is a string like 'push', 'pull', or 'unbundle'. url is
1653 the URL of the repo where this changegroup is coming from.
1658 the URL of the repo where this changegroup is coming from.
1654 If lock is not None, the function takes ownership of the lock
1659 If lock is not None, the function takes ownership of the lock
1655 and releases it after the changegroup is added.
1660 and releases it after the changegroup is added.
1656
1661
1657 Return an integer summarizing the change to this repo:
1662 Return an integer summarizing the change to this repo:
1658 - nothing changed or no source: 0
1663 - nothing changed or no source: 0
1659 - more heads than before: 1+added heads (2..n)
1664 - more heads than before: 1+added heads (2..n)
1660 - fewer heads than before: -1-removed heads (-2..-n)
1665 - fewer heads than before: -1-removed heads (-2..-n)
1661 - number of heads stays the same: 1
1666 - number of heads stays the same: 1
1662 """
1667 """
1663 def csmap(x):
1668 def csmap(x):
1664 self.ui.debug("add changeset %s\n" % short(x))
1669 self.ui.debug("add changeset %s\n" % short(x))
1665 return len(cl)
1670 return len(cl)
1666
1671
1667 def revmap(x):
1672 def revmap(x):
1668 return cl.rev(x)
1673 return cl.rev(x)
1669
1674
1670 if not source:
1675 if not source:
1671 return 0
1676 return 0
1672
1677
1673 self.hook('prechangegroup', throw=True, source=srctype, url=url)
1678 self.hook('prechangegroup', throw=True, source=srctype, url=url)
1674
1679
1675 changesets = files = revisions = 0
1680 changesets = files = revisions = 0
1676 efiles = set()
1681 efiles = set()
1677
1682
1678 # write changelog data to temp files so concurrent readers will not see
1683 # write changelog data to temp files so concurrent readers will not see
1679 # inconsistent view
1684 # inconsistent view
1680 cl = self.changelog
1685 cl = self.changelog
1681 cl.delayupdate()
1686 cl.delayupdate()
1682 oldheads = len(cl.heads())
1687 oldheads = len(cl.heads())
1683
1688
1684 tr = self.transaction("\n".join([srctype, urlmod.hidepassword(url)]))
1689 tr = self.transaction("\n".join([srctype, urlmod.hidepassword(url)]))
1685 try:
1690 try:
1686 trp = weakref.proxy(tr)
1691 trp = weakref.proxy(tr)
1687 # pull off the changeset group
1692 # pull off the changeset group
1688 self.ui.status(_("adding changesets\n"))
1693 self.ui.status(_("adding changesets\n"))
1689 clstart = len(cl)
1694 clstart = len(cl)
1690 class prog(object):
1695 class prog(object):
1691 step = _('changesets')
1696 step = _('changesets')
1692 count = 1
1697 count = 1
1693 ui = self.ui
1698 ui = self.ui
1694 total = None
1699 total = None
1695 def __call__(self):
1700 def __call__(self):
1696 self.ui.progress(self.step, self.count, unit=_('chunks'),
1701 self.ui.progress(self.step, self.count, unit=_('chunks'),
1697 total=self.total)
1702 total=self.total)
1698 self.count += 1
1703 self.count += 1
1699 pr = prog()
1704 pr = prog()
1700 source.callback = pr
1705 source.callback = pr
1701
1706
1702 if (cl.addgroup(source, csmap, trp) is None
1707 if (cl.addgroup(source, csmap, trp) is None
1703 and not emptyok):
1708 and not emptyok):
1704 raise util.Abort(_("received changelog group is empty"))
1709 raise util.Abort(_("received changelog group is empty"))
1705 clend = len(cl)
1710 clend = len(cl)
1706 changesets = clend - clstart
1711 changesets = clend - clstart
1707 for c in xrange(clstart, clend):
1712 for c in xrange(clstart, clend):
1708 efiles.update(self[c].files())
1713 efiles.update(self[c].files())
1709 efiles = len(efiles)
1714 efiles = len(efiles)
1710 self.ui.progress(_('changesets'), None)
1715 self.ui.progress(_('changesets'), None)
1711
1716
1712 # pull off the manifest group
1717 # pull off the manifest group
1713 self.ui.status(_("adding manifests\n"))
1718 self.ui.status(_("adding manifests\n"))
1714 pr.step = _('manifests')
1719 pr.step = _('manifests')
1715 pr.count = 1
1720 pr.count = 1
1716 pr.total = changesets # manifests <= changesets
1721 pr.total = changesets # manifests <= changesets
1717 # no need to check for empty manifest group here:
1722 # no need to check for empty manifest group here:
1718 # if the result of the merge of 1 and 2 is the same in 3 and 4,
1723 # if the result of the merge of 1 and 2 is the same in 3 and 4,
1719 # no new manifest will be created and the manifest group will
1724 # no new manifest will be created and the manifest group will
1720 # be empty during the pull
1725 # be empty during the pull
1721 self.manifest.addgroup(source, revmap, trp)
1726 self.manifest.addgroup(source, revmap, trp)
1722 self.ui.progress(_('manifests'), None)
1727 self.ui.progress(_('manifests'), None)
1723
1728
1724 needfiles = {}
1729 needfiles = {}
1725 if self.ui.configbool('server', 'validate', default=False):
1730 if self.ui.configbool('server', 'validate', default=False):
1726 # validate incoming csets have their manifests
1731 # validate incoming csets have their manifests
1727 for cset in xrange(clstart, clend):
1732 for cset in xrange(clstart, clend):
1728 mfest = self.changelog.read(self.changelog.node(cset))[0]
1733 mfest = self.changelog.read(self.changelog.node(cset))[0]
1729 mfest = self.manifest.readdelta(mfest)
1734 mfest = self.manifest.readdelta(mfest)
1730 # store file nodes we must see
1735 # store file nodes we must see
1731 for f, n in mfest.iteritems():
1736 for f, n in mfest.iteritems():
1732 needfiles.setdefault(f, set()).add(n)
1737 needfiles.setdefault(f, set()).add(n)
1733
1738
1734 # process the files
1739 # process the files
1735 self.ui.status(_("adding file changes\n"))
1740 self.ui.status(_("adding file changes\n"))
1736 pr.step = 'files'
1741 pr.step = 'files'
1737 pr.count = 1
1742 pr.count = 1
1738 pr.total = efiles
1743 pr.total = efiles
1739 source.callback = None
1744 source.callback = None
1740
1745
1741 while 1:
1746 while 1:
1742 f = source.chunk()
1747 f = source.chunk()
1743 if not f:
1748 if not f:
1744 break
1749 break
1745 self.ui.debug("adding %s revisions\n" % f)
1750 self.ui.debug("adding %s revisions\n" % f)
1746 pr()
1751 pr()
1747 fl = self.file(f)
1752 fl = self.file(f)
1748 o = len(fl)
1753 o = len(fl)
1749 if fl.addgroup(source, revmap, trp) is None:
1754 if fl.addgroup(source, revmap, trp) is None:
1750 raise util.Abort(_("received file revlog group is empty"))
1755 raise util.Abort(_("received file revlog group is empty"))
1751 revisions += len(fl) - o
1756 revisions += len(fl) - o
1752 files += 1
1757 files += 1
1753 if f in needfiles:
1758 if f in needfiles:
1754 needs = needfiles[f]
1759 needs = needfiles[f]
1755 for new in xrange(o, len(fl)):
1760 for new in xrange(o, len(fl)):
1756 n = fl.node(new)
1761 n = fl.node(new)
1757 if n in needs:
1762 if n in needs:
1758 needs.remove(n)
1763 needs.remove(n)
1759 if not needs:
1764 if not needs:
1760 del needfiles[f]
1765 del needfiles[f]
1761 self.ui.progress(_('files'), None)
1766 self.ui.progress(_('files'), None)
1762
1767
1763 for f, needs in needfiles.iteritems():
1768 for f, needs in needfiles.iteritems():
1764 fl = self.file(f)
1769 fl = self.file(f)
1765 for n in needs:
1770 for n in needs:
1766 try:
1771 try:
1767 fl.rev(n)
1772 fl.rev(n)
1768 except error.LookupError:
1773 except error.LookupError:
1769 raise util.Abort(
1774 raise util.Abort(
1770 _('missing file data for %s:%s - run hg verify') %
1775 _('missing file data for %s:%s - run hg verify') %
1771 (f, hex(n)))
1776 (f, hex(n)))
1772
1777
1773 newheads = len(cl.heads())
1778 newheads = len(cl.heads())
1774 heads = ""
1779 heads = ""
1775 if oldheads and newheads != oldheads:
1780 if oldheads and newheads != oldheads:
1776 heads = _(" (%+d heads)") % (newheads - oldheads)
1781 heads = _(" (%+d heads)") % (newheads - oldheads)
1777
1782
1778 self.ui.status(_("added %d changesets"
1783 self.ui.status(_("added %d changesets"
1779 " with %d changes to %d files%s\n")
1784 " with %d changes to %d files%s\n")
1780 % (changesets, revisions, files, heads))
1785 % (changesets, revisions, files, heads))
1781
1786
1782 if changesets > 0:
1787 if changesets > 0:
1783 p = lambda: cl.writepending() and self.root or ""
1788 p = lambda: cl.writepending() and self.root or ""
1784 self.hook('pretxnchangegroup', throw=True,
1789 self.hook('pretxnchangegroup', throw=True,
1785 node=hex(cl.node(clstart)), source=srctype,
1790 node=hex(cl.node(clstart)), source=srctype,
1786 url=url, pending=p)
1791 url=url, pending=p)
1787
1792
1788 # make changelog see real files again
1793 # make changelog see real files again
1789 cl.finalize(trp)
1794 cl.finalize(trp)
1790
1795
1791 tr.close()
1796 tr.close()
1792 finally:
1797 finally:
1793 tr.release()
1798 tr.release()
1794 if lock:
1799 if lock:
1795 lock.release()
1800 lock.release()
1796
1801
1797 if changesets > 0:
1802 if changesets > 0:
1798 # forcefully update the on-disk branch cache
1803 # forcefully update the on-disk branch cache
1799 self.ui.debug("updating the branch cache\n")
1804 self.ui.debug("updating the branch cache\n")
1800 self.updatebranchcache()
1805 self.updatebranchcache()
1801 self.hook("changegroup", node=hex(cl.node(clstart)),
1806 self.hook("changegroup", node=hex(cl.node(clstart)),
1802 source=srctype, url=url)
1807 source=srctype, url=url)
1803
1808
1804 for i in xrange(clstart, clend):
1809 for i in xrange(clstart, clend):
1805 self.hook("incoming", node=hex(cl.node(i)),
1810 self.hook("incoming", node=hex(cl.node(i)),
1806 source=srctype, url=url)
1811 source=srctype, url=url)
1807
1812
1808 # never return 0 here:
1813 # never return 0 here:
1809 if newheads < oldheads:
1814 if newheads < oldheads:
1810 return newheads - oldheads - 1
1815 return newheads - oldheads - 1
1811 else:
1816 else:
1812 return newheads - oldheads + 1
1817 return newheads - oldheads + 1
1813
1818
1814
1819
1815 def stream_in(self, remote, requirements):
1820 def stream_in(self, remote, requirements):
1816 lock = self.lock()
1821 lock = self.lock()
1817 try:
1822 try:
1818 fp = remote.stream_out()
1823 fp = remote.stream_out()
1819 l = fp.readline()
1824 l = fp.readline()
1820 try:
1825 try:
1821 resp = int(l)
1826 resp = int(l)
1822 except ValueError:
1827 except ValueError:
1823 raise error.ResponseError(
1828 raise error.ResponseError(
1824 _('Unexpected response from remote server:'), l)
1829 _('Unexpected response from remote server:'), l)
1825 if resp == 1:
1830 if resp == 1:
1826 raise util.Abort(_('operation forbidden by server'))
1831 raise util.Abort(_('operation forbidden by server'))
1827 elif resp == 2:
1832 elif resp == 2:
1828 raise util.Abort(_('locking the remote repository failed'))
1833 raise util.Abort(_('locking the remote repository failed'))
1829 elif resp != 0:
1834 elif resp != 0:
1830 raise util.Abort(_('the server sent an unknown error code'))
1835 raise util.Abort(_('the server sent an unknown error code'))
1831 self.ui.status(_('streaming all changes\n'))
1836 self.ui.status(_('streaming all changes\n'))
1832 l = fp.readline()
1837 l = fp.readline()
1833 try:
1838 try:
1834 total_files, total_bytes = map(int, l.split(' ', 1))
1839 total_files, total_bytes = map(int, l.split(' ', 1))
1835 except (ValueError, TypeError):
1840 except (ValueError, TypeError):
1836 raise error.ResponseError(
1841 raise error.ResponseError(
1837 _('Unexpected response from remote server:'), l)
1842 _('Unexpected response from remote server:'), l)
1838 self.ui.status(_('%d files to transfer, %s of data\n') %
1843 self.ui.status(_('%d files to transfer, %s of data\n') %
1839 (total_files, util.bytecount(total_bytes)))
1844 (total_files, util.bytecount(total_bytes)))
1840 start = time.time()
1845 start = time.time()
1841 for i in xrange(total_files):
1846 for i in xrange(total_files):
1842 # XXX doesn't support '\n' or '\r' in filenames
1847 # XXX doesn't support '\n' or '\r' in filenames
1843 l = fp.readline()
1848 l = fp.readline()
1844 try:
1849 try:
1845 name, size = l.split('\0', 1)
1850 name, size = l.split('\0', 1)
1846 size = int(size)
1851 size = int(size)
1847 except (ValueError, TypeError):
1852 except (ValueError, TypeError):
1848 raise error.ResponseError(
1853 raise error.ResponseError(
1849 _('Unexpected response from remote server:'), l)
1854 _('Unexpected response from remote server:'), l)
1850 self.ui.debug('adding %s (%s)\n' % (name, util.bytecount(size)))
1855 self.ui.debug('adding %s (%s)\n' % (name, util.bytecount(size)))
1851 # for backwards compat, name was partially encoded
1856 # for backwards compat, name was partially encoded
1852 ofp = self.sopener(store.decodedir(name), 'w')
1857 ofp = self.sopener(store.decodedir(name), 'w')
1853 for chunk in util.filechunkiter(fp, limit=size):
1858 for chunk in util.filechunkiter(fp, limit=size):
1854 ofp.write(chunk)
1859 ofp.write(chunk)
1855 ofp.close()
1860 ofp.close()
1856 elapsed = time.time() - start
1861 elapsed = time.time() - start
1857 if elapsed <= 0:
1862 if elapsed <= 0:
1858 elapsed = 0.001
1863 elapsed = 0.001
1859 self.ui.status(_('transferred %s in %.1f seconds (%s/sec)\n') %
1864 self.ui.status(_('transferred %s in %.1f seconds (%s/sec)\n') %
1860 (util.bytecount(total_bytes), elapsed,
1865 (util.bytecount(total_bytes), elapsed,
1861 util.bytecount(total_bytes / elapsed)))
1866 util.bytecount(total_bytes / elapsed)))
1862
1867
1863 # new requirements = old non-format requirements + new format-related
1868 # new requirements = old non-format requirements + new format-related
1864 # requirements from the streamed-in repository
1869 # requirements from the streamed-in repository
1865 requirements.update(set(self.requirements) - self.supportedformats)
1870 requirements.update(set(self.requirements) - self.supportedformats)
1866 self._applyrequirements(requirements)
1871 self._applyrequirements(requirements)
1867 self._writerequirements()
1872 self._writerequirements()
1868
1873
1869 self.invalidate()
1874 self.invalidate()
1870 return len(self.heads()) + 1
1875 return len(self.heads()) + 1
1871 finally:
1876 finally:
1872 lock.release()
1877 lock.release()
1873
1878
1874 def clone(self, remote, heads=[], stream=False):
1879 def clone(self, remote, heads=[], stream=False):
1875 '''clone remote repository.
1880 '''clone remote repository.
1876
1881
1877 keyword arguments:
1882 keyword arguments:
1878 heads: list of revs to clone (forces use of pull)
1883 heads: list of revs to clone (forces use of pull)
1879 stream: use streaming clone if possible'''
1884 stream: use streaming clone if possible'''
1880
1885
1881 # now, all clients that can request uncompressed clones can
1886 # now, all clients that can request uncompressed clones can
1882 # read repo formats supported by all servers that can serve
1887 # read repo formats supported by all servers that can serve
1883 # them.
1888 # them.
1884
1889
1885 # if revlog format changes, client will have to check version
1890 # if revlog format changes, client will have to check version
1886 # and format flags on "stream" capability, and use
1891 # and format flags on "stream" capability, and use
1887 # uncompressed only if compatible.
1892 # uncompressed only if compatible.
1888
1893
1889 if stream and not heads:
1894 if stream and not heads:
1890 # 'stream' means remote revlog format is revlogv1 only
1895 # 'stream' means remote revlog format is revlogv1 only
1891 if remote.capable('stream'):
1896 if remote.capable('stream'):
1892 return self.stream_in(remote, set(('revlogv1',)))
1897 return self.stream_in(remote, set(('revlogv1',)))
1893 # otherwise, 'streamreqs' contains the remote revlog format
1898 # otherwise, 'streamreqs' contains the remote revlog format
1894 streamreqs = remote.capable('streamreqs')
1899 streamreqs = remote.capable('streamreqs')
1895 if streamreqs:
1900 if streamreqs:
1896 streamreqs = set(streamreqs.split(','))
1901 streamreqs = set(streamreqs.split(','))
1897 # if we support it, stream in and adjust our requirements
1902 # if we support it, stream in and adjust our requirements
1898 if not streamreqs - self.supportedformats:
1903 if not streamreqs - self.supportedformats:
1899 return self.stream_in(remote, streamreqs)
1904 return self.stream_in(remote, streamreqs)
1900 return self.pull(remote, heads)
1905 return self.pull(remote, heads)
1901
1906
1902 def pushkey(self, namespace, key, old, new):
1907 def pushkey(self, namespace, key, old, new):
1903 return pushkey.push(self, namespace, key, old, new)
1908 return pushkey.push(self, namespace, key, old, new)
1904
1909
1905 def listkeys(self, namespace):
1910 def listkeys(self, namespace):
1906 return pushkey.list(self, namespace)
1911 return pushkey.list(self, namespace)
1907
1912
1908 def debugwireargs(self, one, two, three=None, four=None):
1913 def debugwireargs(self, one, two, three=None, four=None):
1909 '''used to test argument passing over the wire'''
1914 '''used to test argument passing over the wire'''
1910 return "%s %s %s %s" % (one, two, three, four)
1915 return "%s %s %s %s" % (one, two, three, four)
1911
1916
1912 # used to avoid circular references so destructors work
1917 # used to avoid circular references so destructors work
1913 def aftertrans(files):
1918 def aftertrans(files):
1914 renamefiles = [tuple(t) for t in files]
1919 renamefiles = [tuple(t) for t in files]
1915 def a():
1920 def a():
1916 for src, dest in renamefiles:
1921 for src, dest in renamefiles:
1917 util.rename(src, dest)
1922 util.rename(src, dest)
1918 return a
1923 return a
1919
1924
1920 def instance(ui, path, create):
1925 def instance(ui, path, create):
1921 return localrepository(ui, util.drop_scheme('file', path), create)
1926 return localrepository(ui, util.drop_scheme('file', path), create)
1922
1927
1923 def islocal(path):
1928 def islocal(path):
1924 return True
1929 return True
@@ -1,381 +1,393 b''
1 # wireproto.py - generic wire protocol support functions
1 # wireproto.py - generic wire protocol support functions
2 #
2 #
3 # Copyright 2005-2010 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-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 urllib, tempfile, os, sys
8 import urllib, tempfile, os, sys
9 from i18n import _
9 from i18n import _
10 from node import bin, hex
10 from node import bin, hex
11 import changegroup as changegroupmod
11 import changegroup as changegroupmod
12 import repo, error, encoding, util, store
12 import repo, error, encoding, util, store
13 import pushkey as pushkeymod
13 import pushkey as pushkeymod
14
14
15 # list of nodes encoding / decoding
15 # list of nodes encoding / decoding
16
16
17 def decodelist(l, sep=' '):
17 def decodelist(l, sep=' '):
18 if l:
18 if l:
19 return map(bin, l.split(sep))
19 return map(bin, l.split(sep))
20 return []
20 return []
21
21
22 def encodelist(l, sep=' '):
22 def encodelist(l, sep=' '):
23 return sep.join(map(hex, l))
23 return sep.join(map(hex, l))
24
24
25 # client side
25 # client side
26
26
27 class wirerepository(repo.repository):
27 class wirerepository(repo.repository):
28 def lookup(self, key):
28 def lookup(self, key):
29 self.requirecap('lookup', _('look up remote revision'))
29 self.requirecap('lookup', _('look up remote revision'))
30 d = self._call("lookup", key=encoding.fromlocal(key))
30 d = self._call("lookup", key=encoding.fromlocal(key))
31 success, data = d[:-1].split(" ", 1)
31 success, data = d[:-1].split(" ", 1)
32 if int(success):
32 if int(success):
33 return bin(data)
33 return bin(data)
34 self._abort(error.RepoError(data))
34 self._abort(error.RepoError(data))
35
35
36 def heads(self):
36 def heads(self):
37 d = self._call("heads")
37 d = self._call("heads")
38 try:
38 try:
39 return decodelist(d[:-1])
39 return decodelist(d[:-1])
40 except:
40 except:
41 self._abort(error.ResponseError(_("unexpected response:"), d))
41 self._abort(error.ResponseError(_("unexpected response:"), d))
42
42
43 def known(self, nodes):
44 n = encodelist(nodes)
45 d = self._call("known", nodes=n)
46 try:
47 return [bool(int(f)) for f in d]
48 except:
49 self._abort(error.ResponseError(_("unexpected response:"), d))
50
43 def branchmap(self):
51 def branchmap(self):
44 d = self._call("branchmap")
52 d = self._call("branchmap")
45 try:
53 try:
46 branchmap = {}
54 branchmap = {}
47 for branchpart in d.splitlines():
55 for branchpart in d.splitlines():
48 branchname, branchheads = branchpart.split(' ', 1)
56 branchname, branchheads = branchpart.split(' ', 1)
49 branchname = encoding.tolocal(urllib.unquote(branchname))
57 branchname = encoding.tolocal(urllib.unquote(branchname))
50 branchheads = decodelist(branchheads)
58 branchheads = decodelist(branchheads)
51 branchmap[branchname] = branchheads
59 branchmap[branchname] = branchheads
52 return branchmap
60 return branchmap
53 except TypeError:
61 except TypeError:
54 self._abort(error.ResponseError(_("unexpected response:"), d))
62 self._abort(error.ResponseError(_("unexpected response:"), d))
55
63
56 def branches(self, nodes):
64 def branches(self, nodes):
57 n = encodelist(nodes)
65 n = encodelist(nodes)
58 d = self._call("branches", nodes=n)
66 d = self._call("branches", nodes=n)
59 try:
67 try:
60 br = [tuple(decodelist(b)) for b in d.splitlines()]
68 br = [tuple(decodelist(b)) for b in d.splitlines()]
61 return br
69 return br
62 except:
70 except:
63 self._abort(error.ResponseError(_("unexpected response:"), d))
71 self._abort(error.ResponseError(_("unexpected response:"), d))
64
72
65 def between(self, pairs):
73 def between(self, pairs):
66 batch = 8 # avoid giant requests
74 batch = 8 # avoid giant requests
67 r = []
75 r = []
68 for i in xrange(0, len(pairs), batch):
76 for i in xrange(0, len(pairs), batch):
69 n = " ".join([encodelist(p, '-') for p in pairs[i:i + batch]])
77 n = " ".join([encodelist(p, '-') for p in pairs[i:i + batch]])
70 d = self._call("between", pairs=n)
78 d = self._call("between", pairs=n)
71 try:
79 try:
72 r.extend(l and decodelist(l) or [] for l in d.splitlines())
80 r.extend(l and decodelist(l) or [] for l in d.splitlines())
73 except:
81 except:
74 self._abort(error.ResponseError(_("unexpected response:"), d))
82 self._abort(error.ResponseError(_("unexpected response:"), d))
75 return r
83 return r
76
84
77 def pushkey(self, namespace, key, old, new):
85 def pushkey(self, namespace, key, old, new):
78 if not self.capable('pushkey'):
86 if not self.capable('pushkey'):
79 return False
87 return False
80 d = self._call("pushkey",
88 d = self._call("pushkey",
81 namespace=encoding.fromlocal(namespace),
89 namespace=encoding.fromlocal(namespace),
82 key=encoding.fromlocal(key),
90 key=encoding.fromlocal(key),
83 old=encoding.fromlocal(old),
91 old=encoding.fromlocal(old),
84 new=encoding.fromlocal(new))
92 new=encoding.fromlocal(new))
85 try:
93 try:
86 d = bool(int(d))
94 d = bool(int(d))
87 except ValueError:
95 except ValueError:
88 raise error.ResponseError(
96 raise error.ResponseError(
89 _('push failed (unexpected response):'), d)
97 _('push failed (unexpected response):'), d)
90 return d
98 return d
91
99
92 def listkeys(self, namespace):
100 def listkeys(self, namespace):
93 if not self.capable('pushkey'):
101 if not self.capable('pushkey'):
94 return {}
102 return {}
95 d = self._call("listkeys", namespace=encoding.fromlocal(namespace))
103 d = self._call("listkeys", namespace=encoding.fromlocal(namespace))
96 r = {}
104 r = {}
97 for l in d.splitlines():
105 for l in d.splitlines():
98 k, v = l.split('\t')
106 k, v = l.split('\t')
99 r[encoding.tolocal(k)] = encoding.tolocal(v)
107 r[encoding.tolocal(k)] = encoding.tolocal(v)
100 return r
108 return r
101
109
102 def stream_out(self):
110 def stream_out(self):
103 return self._callstream('stream_out')
111 return self._callstream('stream_out')
104
112
105 def changegroup(self, nodes, kind):
113 def changegroup(self, nodes, kind):
106 n = encodelist(nodes)
114 n = encodelist(nodes)
107 f = self._callstream("changegroup", roots=n)
115 f = self._callstream("changegroup", roots=n)
108 return changegroupmod.unbundle10(self._decompress(f), 'UN')
116 return changegroupmod.unbundle10(self._decompress(f), 'UN')
109
117
110 def changegroupsubset(self, bases, heads, kind):
118 def changegroupsubset(self, bases, heads, kind):
111 self.requirecap('changegroupsubset', _('look up remote changes'))
119 self.requirecap('changegroupsubset', _('look up remote changes'))
112 bases = encodelist(bases)
120 bases = encodelist(bases)
113 heads = encodelist(heads)
121 heads = encodelist(heads)
114 f = self._callstream("changegroupsubset",
122 f = self._callstream("changegroupsubset",
115 bases=bases, heads=heads)
123 bases=bases, heads=heads)
116 return changegroupmod.unbundle10(self._decompress(f), 'UN')
124 return changegroupmod.unbundle10(self._decompress(f), 'UN')
117
125
118 def unbundle(self, cg, heads, source):
126 def unbundle(self, cg, heads, source):
119 '''Send cg (a readable file-like object representing the
127 '''Send cg (a readable file-like object representing the
120 changegroup to push, typically a chunkbuffer object) to the
128 changegroup to push, typically a chunkbuffer object) to the
121 remote server as a bundle. Return an integer indicating the
129 remote server as a bundle. Return an integer indicating the
122 result of the push (see localrepository.addchangegroup()).'''
130 result of the push (see localrepository.addchangegroup()).'''
123
131
124 ret, output = self._callpush("unbundle", cg, heads=encodelist(heads))
132 ret, output = self._callpush("unbundle", cg, heads=encodelist(heads))
125 if ret == "":
133 if ret == "":
126 raise error.ResponseError(
134 raise error.ResponseError(
127 _('push failed:'), output)
135 _('push failed:'), output)
128 try:
136 try:
129 ret = int(ret)
137 ret = int(ret)
130 except ValueError:
138 except ValueError:
131 raise error.ResponseError(
139 raise error.ResponseError(
132 _('push failed (unexpected response):'), ret)
140 _('push failed (unexpected response):'), ret)
133
141
134 for l in output.splitlines(True):
142 for l in output.splitlines(True):
135 self.ui.status(_('remote: '), l)
143 self.ui.status(_('remote: '), l)
136 return ret
144 return ret
137
145
138 def debugwireargs(self, one, two, three=None, four=None):
146 def debugwireargs(self, one, two, three=None, four=None):
139 # don't pass optional arguments left at their default value
147 # don't pass optional arguments left at their default value
140 opts = {}
148 opts = {}
141 if three is not None:
149 if three is not None:
142 opts['three'] = three
150 opts['three'] = three
143 if four is not None:
151 if four is not None:
144 opts['four'] = four
152 opts['four'] = four
145 return self._call('debugwireargs', one=one, two=two, **opts)
153 return self._call('debugwireargs', one=one, two=two, **opts)
146
154
147 # server side
155 # server side
148
156
149 class streamres(object):
157 class streamres(object):
150 def __init__(self, gen):
158 def __init__(self, gen):
151 self.gen = gen
159 self.gen = gen
152
160
153 class pushres(object):
161 class pushres(object):
154 def __init__(self, res):
162 def __init__(self, res):
155 self.res = res
163 self.res = res
156
164
157 class pusherr(object):
165 class pusherr(object):
158 def __init__(self, res):
166 def __init__(self, res):
159 self.res = res
167 self.res = res
160
168
161 def dispatch(repo, proto, command):
169 def dispatch(repo, proto, command):
162 func, spec = commands[command]
170 func, spec = commands[command]
163 args = proto.getargs(spec)
171 args = proto.getargs(spec)
164 return func(repo, proto, *args)
172 return func(repo, proto, *args)
165
173
166 def options(cmd, keys, others):
174 def options(cmd, keys, others):
167 opts = {}
175 opts = {}
168 for k in keys:
176 for k in keys:
169 if k in others:
177 if k in others:
170 opts[k] = others[k]
178 opts[k] = others[k]
171 del others[k]
179 del others[k]
172 if others:
180 if others:
173 sys.stderr.write("abort: %s got unexpected arguments %s\n"
181 sys.stderr.write("abort: %s got unexpected arguments %s\n"
174 % (cmd, ",".join(others)))
182 % (cmd, ",".join(others)))
175 return opts
183 return opts
176
184
177 def between(repo, proto, pairs):
185 def between(repo, proto, pairs):
178 pairs = [decodelist(p, '-') for p in pairs.split(" ")]
186 pairs = [decodelist(p, '-') for p in pairs.split(" ")]
179 r = []
187 r = []
180 for b in repo.between(pairs):
188 for b in repo.between(pairs):
181 r.append(encodelist(b) + "\n")
189 r.append(encodelist(b) + "\n")
182 return "".join(r)
190 return "".join(r)
183
191
184 def branchmap(repo, proto):
192 def branchmap(repo, proto):
185 branchmap = repo.branchmap()
193 branchmap = repo.branchmap()
186 heads = []
194 heads = []
187 for branch, nodes in branchmap.iteritems():
195 for branch, nodes in branchmap.iteritems():
188 branchname = urllib.quote(encoding.fromlocal(branch))
196 branchname = urllib.quote(encoding.fromlocal(branch))
189 branchnodes = encodelist(nodes)
197 branchnodes = encodelist(nodes)
190 heads.append('%s %s' % (branchname, branchnodes))
198 heads.append('%s %s' % (branchname, branchnodes))
191 return '\n'.join(heads)
199 return '\n'.join(heads)
192
200
193 def branches(repo, proto, nodes):
201 def branches(repo, proto, nodes):
194 nodes = decodelist(nodes)
202 nodes = decodelist(nodes)
195 r = []
203 r = []
196 for b in repo.branches(nodes):
204 for b in repo.branches(nodes):
197 r.append(encodelist(b) + "\n")
205 r.append(encodelist(b) + "\n")
198 return "".join(r)
206 return "".join(r)
199
207
200 def capabilities(repo, proto):
208 def capabilities(repo, proto):
201 caps = 'lookup changegroupsubset branchmap pushkey'.split()
209 caps = 'lookup changegroupsubset branchmap pushkey known'.split()
202 if _allowstream(repo.ui):
210 if _allowstream(repo.ui):
203 requiredformats = repo.requirements & repo.supportedformats
211 requiredformats = repo.requirements & repo.supportedformats
204 # if our local revlogs are just revlogv1, add 'stream' cap
212 # if our local revlogs are just revlogv1, add 'stream' cap
205 if not requiredformats - set(('revlogv1',)):
213 if not requiredformats - set(('revlogv1',)):
206 caps.append('stream')
214 caps.append('stream')
207 # otherwise, add 'streamreqs' detailing our local revlog format
215 # otherwise, add 'streamreqs' detailing our local revlog format
208 else:
216 else:
209 caps.append('streamreqs=%s' % ','.join(requiredformats))
217 caps.append('streamreqs=%s' % ','.join(requiredformats))
210 caps.append('unbundle=%s' % ','.join(changegroupmod.bundlepriority))
218 caps.append('unbundle=%s' % ','.join(changegroupmod.bundlepriority))
211 return ' '.join(caps)
219 return ' '.join(caps)
212
220
213 def changegroup(repo, proto, roots):
221 def changegroup(repo, proto, roots):
214 nodes = decodelist(roots)
222 nodes = decodelist(roots)
215 cg = repo.changegroup(nodes, 'serve')
223 cg = repo.changegroup(nodes, 'serve')
216 return streamres(proto.groupchunks(cg))
224 return streamres(proto.groupchunks(cg))
217
225
218 def changegroupsubset(repo, proto, bases, heads):
226 def changegroupsubset(repo, proto, bases, heads):
219 bases = decodelist(bases)
227 bases = decodelist(bases)
220 heads = decodelist(heads)
228 heads = decodelist(heads)
221 cg = repo.changegroupsubset(bases, heads, 'serve')
229 cg = repo.changegroupsubset(bases, heads, 'serve')
222 return streamres(proto.groupchunks(cg))
230 return streamres(proto.groupchunks(cg))
223
231
224 def debugwireargs(repo, proto, one, two, others):
232 def debugwireargs(repo, proto, one, two, others):
225 # only accept optional args from the known set
233 # only accept optional args from the known set
226 opts = options('debugwireargs', ['three', 'four'], others)
234 opts = options('debugwireargs', ['three', 'four'], others)
227 return repo.debugwireargs(one, two, **opts)
235 return repo.debugwireargs(one, two, **opts)
228
236
229 def heads(repo, proto):
237 def heads(repo, proto):
230 h = repo.heads()
238 h = repo.heads()
231 return encodelist(h) + "\n"
239 return encodelist(h) + "\n"
232
240
233 def hello(repo, proto):
241 def hello(repo, proto):
234 '''the hello command returns a set of lines describing various
242 '''the hello command returns a set of lines describing various
235 interesting things about the server, in an RFC822-like format.
243 interesting things about the server, in an RFC822-like format.
236 Currently the only one defined is "capabilities", which
244 Currently the only one defined is "capabilities", which
237 consists of a line in the form:
245 consists of a line in the form:
238
246
239 capabilities: space separated list of tokens
247 capabilities: space separated list of tokens
240 '''
248 '''
241 return "capabilities: %s\n" % (capabilities(repo, proto))
249 return "capabilities: %s\n" % (capabilities(repo, proto))
242
250
243 def listkeys(repo, proto, namespace):
251 def listkeys(repo, proto, namespace):
244 d = pushkeymod.list(repo, encoding.tolocal(namespace)).items()
252 d = pushkeymod.list(repo, encoding.tolocal(namespace)).items()
245 t = '\n'.join(['%s\t%s' % (encoding.fromlocal(k), encoding.fromlocal(v))
253 t = '\n'.join(['%s\t%s' % (encoding.fromlocal(k), encoding.fromlocal(v))
246 for k, v in d])
254 for k, v in d])
247 return t
255 return t
248
256
249 def lookup(repo, proto, key):
257 def lookup(repo, proto, key):
250 try:
258 try:
251 r = hex(repo.lookup(encoding.tolocal(key)))
259 r = hex(repo.lookup(encoding.tolocal(key)))
252 success = 1
260 success = 1
253 except Exception, inst:
261 except Exception, inst:
254 r = str(inst)
262 r = str(inst)
255 success = 0
263 success = 0
256 return "%s %s\n" % (success, r)
264 return "%s %s\n" % (success, r)
257
265
266 def known(repo, proto, nodes):
267 return ''.join(b and "1" or "0" for b in repo.known(decodelist(nodes)))
268
258 def pushkey(repo, proto, namespace, key, old, new):
269 def pushkey(repo, proto, namespace, key, old, new):
259 # compatibility with pre-1.8 clients which were accidentally
270 # compatibility with pre-1.8 clients which were accidentally
260 # sending raw binary nodes rather than utf-8-encoded hex
271 # sending raw binary nodes rather than utf-8-encoded hex
261 if len(new) == 20 and new.encode('string-escape') != new:
272 if len(new) == 20 and new.encode('string-escape') != new:
262 # looks like it could be a binary node
273 # looks like it could be a binary node
263 try:
274 try:
264 u = new.decode('utf-8')
275 u = new.decode('utf-8')
265 new = encoding.tolocal(new) # but cleanly decodes as UTF-8
276 new = encoding.tolocal(new) # but cleanly decodes as UTF-8
266 except UnicodeDecodeError:
277 except UnicodeDecodeError:
267 pass # binary, leave unmodified
278 pass # binary, leave unmodified
268 else:
279 else:
269 new = encoding.tolocal(new) # normal path
280 new = encoding.tolocal(new) # normal path
270
281
271 r = pushkeymod.push(repo,
282 r = pushkeymod.push(repo,
272 encoding.tolocal(namespace), encoding.tolocal(key),
283 encoding.tolocal(namespace), encoding.tolocal(key),
273 encoding.tolocal(old), new)
284 encoding.tolocal(old), new)
274 return '%s\n' % int(r)
285 return '%s\n' % int(r)
275
286
276 def _allowstream(ui):
287 def _allowstream(ui):
277 return ui.configbool('server', 'uncompressed', True, untrusted=True)
288 return ui.configbool('server', 'uncompressed', True, untrusted=True)
278
289
279 def stream(repo, proto):
290 def stream(repo, proto):
280 '''If the server supports streaming clone, it advertises the "stream"
291 '''If the server supports streaming clone, it advertises the "stream"
281 capability with a value representing the version and flags of the repo
292 capability with a value representing the version and flags of the repo
282 it is serving. Client checks to see if it understands the format.
293 it is serving. Client checks to see if it understands the format.
283
294
284 The format is simple: the server writes out a line with the amount
295 The format is simple: the server writes out a line with the amount
285 of files, then the total amount of bytes to be transfered (separated
296 of files, then the total amount of bytes to be transfered (separated
286 by a space). Then, for each file, the server first writes the filename
297 by a space). Then, for each file, the server first writes the filename
287 and filesize (separated by the null character), then the file contents.
298 and filesize (separated by the null character), then the file contents.
288 '''
299 '''
289
300
290 if not _allowstream(repo.ui):
301 if not _allowstream(repo.ui):
291 return '1\n'
302 return '1\n'
292
303
293 entries = []
304 entries = []
294 total_bytes = 0
305 total_bytes = 0
295 try:
306 try:
296 # get consistent snapshot of repo, lock during scan
307 # get consistent snapshot of repo, lock during scan
297 lock = repo.lock()
308 lock = repo.lock()
298 try:
309 try:
299 repo.ui.debug('scanning\n')
310 repo.ui.debug('scanning\n')
300 for name, ename, size in repo.store.walk():
311 for name, ename, size in repo.store.walk():
301 entries.append((name, size))
312 entries.append((name, size))
302 total_bytes += size
313 total_bytes += size
303 finally:
314 finally:
304 lock.release()
315 lock.release()
305 except error.LockError:
316 except error.LockError:
306 return '2\n' # error: 2
317 return '2\n' # error: 2
307
318
308 def streamer(repo, entries, total):
319 def streamer(repo, entries, total):
309 '''stream out all metadata files in repository.'''
320 '''stream out all metadata files in repository.'''
310 yield '0\n' # success
321 yield '0\n' # success
311 repo.ui.debug('%d files, %d bytes to transfer\n' %
322 repo.ui.debug('%d files, %d bytes to transfer\n' %
312 (len(entries), total_bytes))
323 (len(entries), total_bytes))
313 yield '%d %d\n' % (len(entries), total_bytes)
324 yield '%d %d\n' % (len(entries), total_bytes)
314 for name, size in entries:
325 for name, size in entries:
315 repo.ui.debug('sending %s (%d bytes)\n' % (name, size))
326 repo.ui.debug('sending %s (%d bytes)\n' % (name, size))
316 # partially encode name over the wire for backwards compat
327 # partially encode name over the wire for backwards compat
317 yield '%s\0%d\n' % (store.encodedir(name), size)
328 yield '%s\0%d\n' % (store.encodedir(name), size)
318 for chunk in util.filechunkiter(repo.sopener(name), limit=size):
329 for chunk in util.filechunkiter(repo.sopener(name), limit=size):
319 yield chunk
330 yield chunk
320
331
321 return streamres(streamer(repo, entries, total_bytes))
332 return streamres(streamer(repo, entries, total_bytes))
322
333
323 def unbundle(repo, proto, heads):
334 def unbundle(repo, proto, heads):
324 their_heads = decodelist(heads)
335 their_heads = decodelist(heads)
325
336
326 def check_heads():
337 def check_heads():
327 heads = repo.heads()
338 heads = repo.heads()
328 return their_heads == ['force'] or their_heads == heads
339 return their_heads == ['force'] or their_heads == heads
329
340
330 proto.redirect()
341 proto.redirect()
331
342
332 # fail early if possible
343 # fail early if possible
333 if not check_heads():
344 if not check_heads():
334 return pusherr('unsynced changes')
345 return pusherr('unsynced changes')
335
346
336 # write bundle data to temporary file because it can be big
347 # write bundle data to temporary file because it can be big
337 fd, tempname = tempfile.mkstemp(prefix='hg-unbundle-')
348 fd, tempname = tempfile.mkstemp(prefix='hg-unbundle-')
338 fp = os.fdopen(fd, 'wb+')
349 fp = os.fdopen(fd, 'wb+')
339 r = 0
350 r = 0
340 try:
351 try:
341 proto.getfile(fp)
352 proto.getfile(fp)
342 lock = repo.lock()
353 lock = repo.lock()
343 try:
354 try:
344 if not check_heads():
355 if not check_heads():
345 # someone else committed/pushed/unbundled while we
356 # someone else committed/pushed/unbundled while we
346 # were transferring data
357 # were transferring data
347 return pusherr('unsynced changes')
358 return pusherr('unsynced changes')
348
359
349 # push can proceed
360 # push can proceed
350 fp.seek(0)
361 fp.seek(0)
351 gen = changegroupmod.readbundle(fp, None)
362 gen = changegroupmod.readbundle(fp, None)
352
363
353 try:
364 try:
354 r = repo.addchangegroup(gen, 'serve', proto._client(),
365 r = repo.addchangegroup(gen, 'serve', proto._client(),
355 lock=lock)
366 lock=lock)
356 except util.Abort, inst:
367 except util.Abort, inst:
357 sys.stderr.write("abort: %s\n" % inst)
368 sys.stderr.write("abort: %s\n" % inst)
358 finally:
369 finally:
359 lock.release()
370 lock.release()
360 return pushres(r)
371 return pushres(r)
361
372
362 finally:
373 finally:
363 fp.close()
374 fp.close()
364 os.unlink(tempname)
375 os.unlink(tempname)
365
376
366 commands = {
377 commands = {
367 'between': (between, 'pairs'),
378 'between': (between, 'pairs'),
368 'branchmap': (branchmap, ''),
379 'branchmap': (branchmap, ''),
369 'branches': (branches, 'nodes'),
380 'branches': (branches, 'nodes'),
370 'capabilities': (capabilities, ''),
381 'capabilities': (capabilities, ''),
371 'changegroup': (changegroup, 'roots'),
382 'changegroup': (changegroup, 'roots'),
372 'changegroupsubset': (changegroupsubset, 'bases heads'),
383 'changegroupsubset': (changegroupsubset, 'bases heads'),
373 'debugwireargs': (debugwireargs, 'one two *'),
384 'debugwireargs': (debugwireargs, 'one two *'),
374 'heads': (heads, ''),
385 'heads': (heads, ''),
375 'hello': (hello, ''),
386 'hello': (hello, ''),
387 'known': (known, 'nodes'),
376 'listkeys': (listkeys, 'namespace'),
388 'listkeys': (listkeys, 'namespace'),
377 'lookup': (lookup, 'key'),
389 'lookup': (lookup, 'key'),
378 'pushkey': (pushkey, 'namespace key old new'),
390 'pushkey': (pushkey, 'namespace key old new'),
379 'stream_out': (stream, ''),
391 'stream_out': (stream, ''),
380 'unbundle': (unbundle, 'heads'),
392 'unbundle': (unbundle, 'heads'),
381 }
393 }
@@ -1,255 +1,257 b''
1 Show all commands except debug commands
1 Show all commands except debug commands
2 $ hg debugcomplete
2 $ hg debugcomplete
3 add
3 add
4 addremove
4 addremove
5 annotate
5 annotate
6 archive
6 archive
7 backout
7 backout
8 bisect
8 bisect
9 bookmarks
9 bookmarks
10 branch
10 branch
11 branches
11 branches
12 bundle
12 bundle
13 cat
13 cat
14 clone
14 clone
15 commit
15 commit
16 copy
16 copy
17 diff
17 diff
18 export
18 export
19 forget
19 forget
20 grep
20 grep
21 heads
21 heads
22 help
22 help
23 identify
23 identify
24 import
24 import
25 incoming
25 incoming
26 init
26 init
27 locate
27 locate
28 log
28 log
29 manifest
29 manifest
30 merge
30 merge
31 outgoing
31 outgoing
32 parents
32 parents
33 paths
33 paths
34 pull
34 pull
35 push
35 push
36 recover
36 recover
37 remove
37 remove
38 rename
38 rename
39 resolve
39 resolve
40 revert
40 revert
41 rollback
41 rollback
42 root
42 root
43 serve
43 serve
44 showconfig
44 showconfig
45 status
45 status
46 summary
46 summary
47 tag
47 tag
48 tags
48 tags
49 tip
49 tip
50 unbundle
50 unbundle
51 update
51 update
52 verify
52 verify
53 version
53 version
54
54
55 Show all commands that start with "a"
55 Show all commands that start with "a"
56 $ hg debugcomplete a
56 $ hg debugcomplete a
57 add
57 add
58 addremove
58 addremove
59 annotate
59 annotate
60 archive
60 archive
61
61
62 Do not show debug commands if there are other candidates
62 Do not show debug commands if there are other candidates
63 $ hg debugcomplete d
63 $ hg debugcomplete d
64 diff
64 diff
65
65
66 Show debug commands if there are no other candidates
66 Show debug commands if there are no other candidates
67 $ hg debugcomplete debug
67 $ hg debugcomplete debug
68 debugancestor
68 debugancestor
69 debugbuilddag
69 debugbuilddag
70 debugcheckstate
70 debugcheckstate
71 debugcommands
71 debugcommands
72 debugcomplete
72 debugcomplete
73 debugconfig
73 debugconfig
74 debugdag
74 debugdag
75 debugdata
75 debugdata
76 debugdate
76 debugdate
77 debugfsinfo
77 debugfsinfo
78 debugignore
78 debugignore
79 debugindex
79 debugindex
80 debugindexdot
80 debugindexdot
81 debuginstall
81 debuginstall
82 debugknown
82 debugpushkey
83 debugpushkey
83 debugrebuildstate
84 debugrebuildstate
84 debugrename
85 debugrename
85 debugrevspec
86 debugrevspec
86 debugsetparents
87 debugsetparents
87 debugstate
88 debugstate
88 debugsub
89 debugsub
89 debugwalk
90 debugwalk
90 debugwireargs
91 debugwireargs
91
92
92 Do not show the alias of a debug command if there are other candidates
93 Do not show the alias of a debug command if there are other candidates
93 (this should hide rawcommit)
94 (this should hide rawcommit)
94 $ hg debugcomplete r
95 $ hg debugcomplete r
95 recover
96 recover
96 remove
97 remove
97 rename
98 rename
98 resolve
99 resolve
99 revert
100 revert
100 rollback
101 rollback
101 root
102 root
102 Show the alias of a debug command if there are no other candidates
103 Show the alias of a debug command if there are no other candidates
103 $ hg debugcomplete rawc
104 $ hg debugcomplete rawc
104
105
105
106
106 Show the global options
107 Show the global options
107 $ hg debugcomplete --options | sort
108 $ hg debugcomplete --options | sort
108 --config
109 --config
109 --cwd
110 --cwd
110 --debug
111 --debug
111 --debugger
112 --debugger
112 --encoding
113 --encoding
113 --encodingmode
114 --encodingmode
114 --help
115 --help
115 --noninteractive
116 --noninteractive
116 --profile
117 --profile
117 --quiet
118 --quiet
118 --repository
119 --repository
119 --time
120 --time
120 --traceback
121 --traceback
121 --verbose
122 --verbose
122 --version
123 --version
123 -R
124 -R
124 -h
125 -h
125 -q
126 -q
126 -v
127 -v
127 -y
128 -y
128
129
129 Show the options for the "serve" command
130 Show the options for the "serve" command
130 $ hg debugcomplete --options serve | sort
131 $ hg debugcomplete --options serve | sort
131 --accesslog
132 --accesslog
132 --address
133 --address
133 --certificate
134 --certificate
134 --config
135 --config
135 --cwd
136 --cwd
136 --daemon
137 --daemon
137 --daemon-pipefds
138 --daemon-pipefds
138 --debug
139 --debug
139 --debugger
140 --debugger
140 --encoding
141 --encoding
141 --encodingmode
142 --encodingmode
142 --errorlog
143 --errorlog
143 --help
144 --help
144 --ipv6
145 --ipv6
145 --name
146 --name
146 --noninteractive
147 --noninteractive
147 --pid-file
148 --pid-file
148 --port
149 --port
149 --prefix
150 --prefix
150 --profile
151 --profile
151 --quiet
152 --quiet
152 --repository
153 --repository
153 --stdio
154 --stdio
154 --style
155 --style
155 --templates
156 --templates
156 --time
157 --time
157 --traceback
158 --traceback
158 --verbose
159 --verbose
159 --version
160 --version
160 --web-conf
161 --web-conf
161 -6
162 -6
162 -A
163 -A
163 -E
164 -E
164 -R
165 -R
165 -a
166 -a
166 -d
167 -d
167 -h
168 -h
168 -n
169 -n
169 -p
170 -p
170 -q
171 -q
171 -t
172 -t
172 -v
173 -v
173 -y
174 -y
174
175
175 Show an error if we use --options with an ambiguous abbreviation
176 Show an error if we use --options with an ambiguous abbreviation
176 $ hg debugcomplete --options s
177 $ hg debugcomplete --options s
177 hg: command 's' is ambiguous:
178 hg: command 's' is ambiguous:
178 serve showconfig status summary
179 serve showconfig status summary
179 [255]
180 [255]
180
181
181 Show all commands + options
182 Show all commands + options
182 $ hg debugcommands
183 $ hg debugcommands
183 add: include, exclude, subrepos, dry-run
184 add: include, exclude, subrepos, dry-run
184 annotate: rev, follow, no-follow, text, user, file, date, number, changeset, line-number, include, exclude
185 annotate: rev, follow, no-follow, text, user, file, date, number, changeset, line-number, include, exclude
185 clone: noupdate, updaterev, rev, branch, pull, uncompressed, ssh, remotecmd, insecure
186 clone: noupdate, updaterev, rev, branch, pull, uncompressed, ssh, remotecmd, insecure
186 commit: addremove, close-branch, include, exclude, message, logfile, date, user
187 commit: addremove, close-branch, include, exclude, message, logfile, date, user
187 diff: rev, change, text, git, nodates, show-function, reverse, ignore-all-space, ignore-space-change, ignore-blank-lines, unified, stat, include, exclude, subrepos
188 diff: rev, change, text, git, nodates, show-function, reverse, ignore-all-space, ignore-space-change, ignore-blank-lines, unified, stat, include, exclude, subrepos
188 export: output, switch-parent, rev, text, git, nodates
189 export: output, switch-parent, rev, text, git, nodates
189 forget: include, exclude
190 forget: include, exclude
190 init: ssh, remotecmd, insecure
191 init: ssh, remotecmd, insecure
191 log: follow, follow-first, date, copies, keyword, rev, removed, only-merges, user, only-branch, branch, prune, patch, git, limit, no-merges, stat, style, template, include, exclude
192 log: follow, follow-first, date, copies, keyword, rev, removed, only-merges, user, only-branch, branch, prune, patch, git, limit, no-merges, stat, style, template, include, exclude
192 merge: force, tool, rev, preview
193 merge: force, tool, rev, preview
193 pull: update, force, rev, bookmark, branch, ssh, remotecmd, insecure
194 pull: update, force, rev, bookmark, branch, ssh, remotecmd, insecure
194 push: force, rev, bookmark, branch, new-branch, ssh, remotecmd, insecure
195 push: force, rev, bookmark, branch, new-branch, ssh, remotecmd, insecure
195 remove: after, force, include, exclude
196 remove: after, force, include, exclude
196 serve: accesslog, daemon, daemon-pipefds, errorlog, port, address, prefix, name, web-conf, webdir-conf, pid-file, stdio, templates, style, ipv6, certificate
197 serve: accesslog, daemon, daemon-pipefds, errorlog, port, address, prefix, name, web-conf, webdir-conf, pid-file, stdio, templates, style, ipv6, certificate
197 status: all, modified, added, removed, deleted, clean, unknown, ignored, no-status, copies, print0, rev, change, include, exclude, subrepos
198 status: all, modified, added, removed, deleted, clean, unknown, ignored, no-status, copies, print0, rev, change, include, exclude, subrepos
198 summary: remote
199 summary: remote
199 update: clean, check, date, rev
200 update: clean, check, date, rev
200 addremove: similarity, include, exclude, dry-run
201 addremove: similarity, include, exclude, dry-run
201 archive: no-decode, prefix, rev, type, subrepos, include, exclude
202 archive: no-decode, prefix, rev, type, subrepos, include, exclude
202 backout: merge, parent, tool, rev, include, exclude, message, logfile, date, user
203 backout: merge, parent, tool, rev, include, exclude, message, logfile, date, user
203 bisect: reset, good, bad, skip, extend, command, noupdate
204 bisect: reset, good, bad, skip, extend, command, noupdate
204 bookmarks: force, rev, delete, rename
205 bookmarks: force, rev, delete, rename
205 branch: force, clean
206 branch: force, clean
206 branches: active, closed
207 branches: active, closed
207 bundle: force, rev, branch, base, all, type, ssh, remotecmd, insecure
208 bundle: force, rev, branch, base, all, type, ssh, remotecmd, insecure
208 cat: output, rev, decode, include, exclude
209 cat: output, rev, decode, include, exclude
209 copy: after, force, include, exclude, dry-run
210 copy: after, force, include, exclude, dry-run
210 debugancestor:
211 debugancestor:
211 debugbuilddag: mergeable-file, appended-file, overwritten-file, new-file
212 debugbuilddag: mergeable-file, appended-file, overwritten-file, new-file
212 debugcheckstate:
213 debugcheckstate:
213 debugcommands:
214 debugcommands:
214 debugcomplete: options
215 debugcomplete: options
215 debugdag: tags, branches, dots, spaces
216 debugdag: tags, branches, dots, spaces
216 debugdata:
217 debugdata:
217 debugdate: extended
218 debugdate: extended
218 debugfsinfo:
219 debugfsinfo:
219 debugignore:
220 debugignore:
220 debugindex: format
221 debugindex: format
221 debugindexdot:
222 debugindexdot:
222 debuginstall:
223 debuginstall:
224 debugknown:
223 debugpushkey:
225 debugpushkey:
224 debugrebuildstate: rev
226 debugrebuildstate: rev
225 debugrename: rev
227 debugrename: rev
226 debugrevspec:
228 debugrevspec:
227 debugsetparents:
229 debugsetparents:
228 debugstate: nodates
230 debugstate: nodates
229 debugsub: rev
231 debugsub: rev
230 debugwalk: include, exclude
232 debugwalk: include, exclude
231 debugwireargs: three, four, ssh, remotecmd, insecure
233 debugwireargs: three, four, ssh, remotecmd, insecure
232 grep: print0, all, follow, ignore-case, files-with-matches, line-number, rev, user, date, include, exclude
234 grep: print0, all, follow, ignore-case, files-with-matches, line-number, rev, user, date, include, exclude
233 heads: rev, topo, active, closed, style, template
235 heads: rev, topo, active, closed, style, template
234 help:
236 help:
235 identify: rev, num, id, branch, tags, bookmarks
237 identify: rev, num, id, branch, tags, bookmarks
236 import: strip, base, force, no-commit, exact, import-branch, message, logfile, date, user, similarity
238 import: strip, base, force, no-commit, exact, import-branch, message, logfile, date, user, similarity
237 incoming: force, newest-first, bundle, rev, bookmarks, branch, patch, git, limit, no-merges, stat, style, template, ssh, remotecmd, insecure, subrepos
239 incoming: force, newest-first, bundle, rev, bookmarks, branch, patch, git, limit, no-merges, stat, style, template, ssh, remotecmd, insecure, subrepos
238 locate: rev, print0, fullpath, include, exclude
240 locate: rev, print0, fullpath, include, exclude
239 manifest: rev
241 manifest: rev
240 outgoing: force, rev, newest-first, bookmarks, branch, patch, git, limit, no-merges, stat, style, template, ssh, remotecmd, insecure, subrepos
242 outgoing: force, rev, newest-first, bookmarks, branch, patch, git, limit, no-merges, stat, style, template, ssh, remotecmd, insecure, subrepos
241 parents: rev, style, template
243 parents: rev, style, template
242 paths:
244 paths:
243 recover:
245 recover:
244 rename: after, force, include, exclude, dry-run
246 rename: after, force, include, exclude, dry-run
245 resolve: all, list, mark, unmark, tool, no-status, include, exclude
247 resolve: all, list, mark, unmark, tool, no-status, include, exclude
246 revert: all, date, rev, no-backup, include, exclude, dry-run
248 revert: all, date, rev, no-backup, include, exclude, dry-run
247 rollback: dry-run
249 rollback: dry-run
248 root:
250 root:
249 showconfig: untrusted
251 showconfig: untrusted
250 tag: force, local, rev, remove, edit, message, date, user
252 tag: force, local, rev, remove, edit, message, date, user
251 tags:
253 tags:
252 tip: patch, git, style, template
254 tip: patch, git, style, template
253 unbundle: update
255 unbundle: update
254 verify:
256 verify:
255 version:
257 version:
@@ -1,1081 +1,1081 b''
1 An attempt at more fully testing the hgweb web interface.
1 An attempt at more fully testing the hgweb web interface.
2 The following things are tested elsewhere and are therefore omitted:
2 The following things are tested elsewhere and are therefore omitted:
3 - archive, tested in test-archive
3 - archive, tested in test-archive
4 - unbundle, tested in test-push-http
4 - unbundle, tested in test-push-http
5 - changegroupsubset, tested in test-pull
5 - changegroupsubset, tested in test-pull
6
6
7 Set up the repo
7 Set up the repo
8
8
9 $ hg init test
9 $ hg init test
10 $ cd test
10 $ cd test
11 $ mkdir da
11 $ mkdir da
12 $ echo foo > da/foo
12 $ echo foo > da/foo
13 $ echo foo > foo
13 $ echo foo > foo
14 $ hg ci -Ambase
14 $ hg ci -Ambase
15 adding da/foo
15 adding da/foo
16 adding foo
16 adding foo
17 $ hg tag 1.0
17 $ hg tag 1.0
18 $ hg bookmark something
18 $ hg bookmark something
19 $ echo another > foo
19 $ echo another > foo
20 $ hg branch stable
20 $ hg branch stable
21 marked working directory as branch stable
21 marked working directory as branch stable
22 $ hg ci -Ambranch
22 $ hg ci -Ambranch
23 $ hg serve --config server.uncompressed=False -n test -p $HGPORT -d --pid-file=hg.pid -E errors.log
23 $ hg serve --config server.uncompressed=False -n test -p $HGPORT -d --pid-file=hg.pid -E errors.log
24 $ cat hg.pid >> $DAEMON_PIDS
24 $ cat hg.pid >> $DAEMON_PIDS
25
25
26 Logs and changes
26 Logs and changes
27
27
28 $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/log/?style=atom'
28 $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/log/?style=atom'
29 200 Script output follows
29 200 Script output follows
30
30
31 <?xml version="1.0" encoding="ascii"?>
31 <?xml version="1.0" encoding="ascii"?>
32 <feed xmlns="http://www.w3.org/2005/Atom">
32 <feed xmlns="http://www.w3.org/2005/Atom">
33 <!-- Changelog -->
33 <!-- Changelog -->
34 <id>http://*:$HGPORT/</id> (glob)
34 <id>http://*:$HGPORT/</id> (glob)
35 <link rel="self" href="http://*:$HGPORT/atom-log"/> (glob)
35 <link rel="self" href="http://*:$HGPORT/atom-log"/> (glob)
36 <link rel="alternate" href="http://*:$HGPORT/"/> (glob)
36 <link rel="alternate" href="http://*:$HGPORT/"/> (glob)
37 <title>test Changelog</title>
37 <title>test Changelog</title>
38 <updated>1970-01-01T00:00:00+00:00</updated>
38 <updated>1970-01-01T00:00:00+00:00</updated>
39
39
40 <entry>
40 <entry>
41 <title>branch</title>
41 <title>branch</title>
42 <id>http://*:$HGPORT/#changeset-1d22e65f027e5a0609357e7d8e7508cd2ba5d2fe</id> (glob)
42 <id>http://*:$HGPORT/#changeset-1d22e65f027e5a0609357e7d8e7508cd2ba5d2fe</id> (glob)
43 <link href="http://*:$HGPORT/rev/1d22e65f027e"/> (glob)
43 <link href="http://*:$HGPORT/rev/1d22e65f027e"/> (glob)
44 <author>
44 <author>
45 <name>test</name>
45 <name>test</name>
46 <email>&#116;&#101;&#115;&#116;</email>
46 <email>&#116;&#101;&#115;&#116;</email>
47 </author>
47 </author>
48 <updated>1970-01-01T00:00:00+00:00</updated>
48 <updated>1970-01-01T00:00:00+00:00</updated>
49 <published>1970-01-01T00:00:00+00:00</published>
49 <published>1970-01-01T00:00:00+00:00</published>
50 <content type="xhtml">
50 <content type="xhtml">
51 <div xmlns="http://www.w3.org/1999/xhtml">
51 <div xmlns="http://www.w3.org/1999/xhtml">
52 <pre xml:space="preserve">branch</pre>
52 <pre xml:space="preserve">branch</pre>
53 </div>
53 </div>
54 </content>
54 </content>
55 </entry>
55 </entry>
56 <entry>
56 <entry>
57 <title>Added tag 1.0 for changeset 2ef0ac749a14</title>
57 <title>Added tag 1.0 for changeset 2ef0ac749a14</title>
58 <id>http://*:$HGPORT/#changeset-a4f92ed23982be056b9852de5dfe873eaac7f0de</id> (glob)
58 <id>http://*:$HGPORT/#changeset-a4f92ed23982be056b9852de5dfe873eaac7f0de</id> (glob)
59 <link href="http://*:$HGPORT/rev/a4f92ed23982"/> (glob)
59 <link href="http://*:$HGPORT/rev/a4f92ed23982"/> (glob)
60 <author>
60 <author>
61 <name>test</name>
61 <name>test</name>
62 <email>&#116;&#101;&#115;&#116;</email>
62 <email>&#116;&#101;&#115;&#116;</email>
63 </author>
63 </author>
64 <updated>1970-01-01T00:00:00+00:00</updated>
64 <updated>1970-01-01T00:00:00+00:00</updated>
65 <published>1970-01-01T00:00:00+00:00</published>
65 <published>1970-01-01T00:00:00+00:00</published>
66 <content type="xhtml">
66 <content type="xhtml">
67 <div xmlns="http://www.w3.org/1999/xhtml">
67 <div xmlns="http://www.w3.org/1999/xhtml">
68 <pre xml:space="preserve">Added tag 1.0 for changeset 2ef0ac749a14</pre>
68 <pre xml:space="preserve">Added tag 1.0 for changeset 2ef0ac749a14</pre>
69 </div>
69 </div>
70 </content>
70 </content>
71 </entry>
71 </entry>
72 <entry>
72 <entry>
73 <title>base</title>
73 <title>base</title>
74 <id>http://*:$HGPORT/#changeset-2ef0ac749a14e4f57a5a822464a0902c6f7f448f</id> (glob)
74 <id>http://*:$HGPORT/#changeset-2ef0ac749a14e4f57a5a822464a0902c6f7f448f</id> (glob)
75 <link href="http://*:$HGPORT/rev/2ef0ac749a14"/> (glob)
75 <link href="http://*:$HGPORT/rev/2ef0ac749a14"/> (glob)
76 <author>
76 <author>
77 <name>test</name>
77 <name>test</name>
78 <email>&#116;&#101;&#115;&#116;</email>
78 <email>&#116;&#101;&#115;&#116;</email>
79 </author>
79 </author>
80 <updated>1970-01-01T00:00:00+00:00</updated>
80 <updated>1970-01-01T00:00:00+00:00</updated>
81 <published>1970-01-01T00:00:00+00:00</published>
81 <published>1970-01-01T00:00:00+00:00</published>
82 <content type="xhtml">
82 <content type="xhtml">
83 <div xmlns="http://www.w3.org/1999/xhtml">
83 <div xmlns="http://www.w3.org/1999/xhtml">
84 <pre xml:space="preserve">base</pre>
84 <pre xml:space="preserve">base</pre>
85 </div>
85 </div>
86 </content>
86 </content>
87 </entry>
87 </entry>
88
88
89 </feed>
89 </feed>
90 $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/log/1/?style=atom'
90 $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/log/1/?style=atom'
91 200 Script output follows
91 200 Script output follows
92
92
93 <?xml version="1.0" encoding="ascii"?>
93 <?xml version="1.0" encoding="ascii"?>
94 <feed xmlns="http://www.w3.org/2005/Atom">
94 <feed xmlns="http://www.w3.org/2005/Atom">
95 <!-- Changelog -->
95 <!-- Changelog -->
96 <id>http://*:$HGPORT/</id> (glob)
96 <id>http://*:$HGPORT/</id> (glob)
97 <link rel="self" href="http://*:$HGPORT/atom-log"/> (glob)
97 <link rel="self" href="http://*:$HGPORT/atom-log"/> (glob)
98 <link rel="alternate" href="http://*:$HGPORT/"/> (glob)
98 <link rel="alternate" href="http://*:$HGPORT/"/> (glob)
99 <title>test Changelog</title>
99 <title>test Changelog</title>
100 <updated>1970-01-01T00:00:00+00:00</updated>
100 <updated>1970-01-01T00:00:00+00:00</updated>
101
101
102 <entry>
102 <entry>
103 <title>branch</title>
103 <title>branch</title>
104 <id>http://*:$HGPORT/#changeset-1d22e65f027e5a0609357e7d8e7508cd2ba5d2fe</id> (glob)
104 <id>http://*:$HGPORT/#changeset-1d22e65f027e5a0609357e7d8e7508cd2ba5d2fe</id> (glob)
105 <link href="http://*:$HGPORT/rev/1d22e65f027e"/> (glob)
105 <link href="http://*:$HGPORT/rev/1d22e65f027e"/> (glob)
106 <author>
106 <author>
107 <name>test</name>
107 <name>test</name>
108 <email>&#116;&#101;&#115;&#116;</email>
108 <email>&#116;&#101;&#115;&#116;</email>
109 </author>
109 </author>
110 <updated>1970-01-01T00:00:00+00:00</updated>
110 <updated>1970-01-01T00:00:00+00:00</updated>
111 <published>1970-01-01T00:00:00+00:00</published>
111 <published>1970-01-01T00:00:00+00:00</published>
112 <content type="xhtml">
112 <content type="xhtml">
113 <div xmlns="http://www.w3.org/1999/xhtml">
113 <div xmlns="http://www.w3.org/1999/xhtml">
114 <pre xml:space="preserve">branch</pre>
114 <pre xml:space="preserve">branch</pre>
115 </div>
115 </div>
116 </content>
116 </content>
117 </entry>
117 </entry>
118 <entry>
118 <entry>
119 <title>Added tag 1.0 for changeset 2ef0ac749a14</title>
119 <title>Added tag 1.0 for changeset 2ef0ac749a14</title>
120 <id>http://*:$HGPORT/#changeset-a4f92ed23982be056b9852de5dfe873eaac7f0de</id> (glob)
120 <id>http://*:$HGPORT/#changeset-a4f92ed23982be056b9852de5dfe873eaac7f0de</id> (glob)
121 <link href="http://*:$HGPORT/rev/a4f92ed23982"/> (glob)
121 <link href="http://*:$HGPORT/rev/a4f92ed23982"/> (glob)
122 <author>
122 <author>
123 <name>test</name>
123 <name>test</name>
124 <email>&#116;&#101;&#115;&#116;</email>
124 <email>&#116;&#101;&#115;&#116;</email>
125 </author>
125 </author>
126 <updated>1970-01-01T00:00:00+00:00</updated>
126 <updated>1970-01-01T00:00:00+00:00</updated>
127 <published>1970-01-01T00:00:00+00:00</published>
127 <published>1970-01-01T00:00:00+00:00</published>
128 <content type="xhtml">
128 <content type="xhtml">
129 <div xmlns="http://www.w3.org/1999/xhtml">
129 <div xmlns="http://www.w3.org/1999/xhtml">
130 <pre xml:space="preserve">Added tag 1.0 for changeset 2ef0ac749a14</pre>
130 <pre xml:space="preserve">Added tag 1.0 for changeset 2ef0ac749a14</pre>
131 </div>
131 </div>
132 </content>
132 </content>
133 </entry>
133 </entry>
134 <entry>
134 <entry>
135 <title>base</title>
135 <title>base</title>
136 <id>http://*:$HGPORT/#changeset-2ef0ac749a14e4f57a5a822464a0902c6f7f448f</id> (glob)
136 <id>http://*:$HGPORT/#changeset-2ef0ac749a14e4f57a5a822464a0902c6f7f448f</id> (glob)
137 <link href="http://*:$HGPORT/rev/2ef0ac749a14"/> (glob)
137 <link href="http://*:$HGPORT/rev/2ef0ac749a14"/> (glob)
138 <author>
138 <author>
139 <name>test</name>
139 <name>test</name>
140 <email>&#116;&#101;&#115;&#116;</email>
140 <email>&#116;&#101;&#115;&#116;</email>
141 </author>
141 </author>
142 <updated>1970-01-01T00:00:00+00:00</updated>
142 <updated>1970-01-01T00:00:00+00:00</updated>
143 <published>1970-01-01T00:00:00+00:00</published>
143 <published>1970-01-01T00:00:00+00:00</published>
144 <content type="xhtml">
144 <content type="xhtml">
145 <div xmlns="http://www.w3.org/1999/xhtml">
145 <div xmlns="http://www.w3.org/1999/xhtml">
146 <pre xml:space="preserve">base</pre>
146 <pre xml:space="preserve">base</pre>
147 </div>
147 </div>
148 </content>
148 </content>
149 </entry>
149 </entry>
150
150
151 </feed>
151 </feed>
152 $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/log/1/foo/?style=atom'
152 $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/log/1/foo/?style=atom'
153 200 Script output follows
153 200 Script output follows
154
154
155 <?xml version="1.0" encoding="ascii"?>
155 <?xml version="1.0" encoding="ascii"?>
156 <feed xmlns="http://www.w3.org/2005/Atom">
156 <feed xmlns="http://www.w3.org/2005/Atom">
157 <id>http://*:$HGPORT/atom-log/tip/foo</id> (glob)
157 <id>http://*:$HGPORT/atom-log/tip/foo</id> (glob)
158 <link rel="self" href="http://*:$HGPORT/atom-log/tip/foo"/> (glob)
158 <link rel="self" href="http://*:$HGPORT/atom-log/tip/foo"/> (glob)
159 <title>test: foo history</title>
159 <title>test: foo history</title>
160 <updated>1970-01-01T00:00:00+00:00</updated>
160 <updated>1970-01-01T00:00:00+00:00</updated>
161
161
162 <entry>
162 <entry>
163 <title>base</title>
163 <title>base</title>
164 <id>http://*:$HGPORT/#changeset-2ef0ac749a14e4f57a5a822464a0902c6f7f448f</id> (glob)
164 <id>http://*:$HGPORT/#changeset-2ef0ac749a14e4f57a5a822464a0902c6f7f448f</id> (glob)
165 <link href="http://*:$HGPORT/rev/2ef0ac749a14"/> (glob)
165 <link href="http://*:$HGPORT/rev/2ef0ac749a14"/> (glob)
166 <author>
166 <author>
167 <name>test</name>
167 <name>test</name>
168 <email>&#116;&#101;&#115;&#116;</email>
168 <email>&#116;&#101;&#115;&#116;</email>
169 </author>
169 </author>
170 <updated>1970-01-01T00:00:00+00:00</updated>
170 <updated>1970-01-01T00:00:00+00:00</updated>
171 <published>1970-01-01T00:00:00+00:00</published>
171 <published>1970-01-01T00:00:00+00:00</published>
172 <content type="xhtml">
172 <content type="xhtml">
173 <div xmlns="http://www.w3.org/1999/xhtml">
173 <div xmlns="http://www.w3.org/1999/xhtml">
174 <pre xml:space="preserve">base</pre>
174 <pre xml:space="preserve">base</pre>
175 </div>
175 </div>
176 </content>
176 </content>
177 </entry>
177 </entry>
178
178
179 </feed>
179 </feed>
180 $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/shortlog/'
180 $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/shortlog/'
181 200 Script output follows
181 200 Script output follows
182
182
183 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
183 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
184 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
184 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
185 <head>
185 <head>
186 <link rel="icon" href="/static/hgicon.png" type="image/png" />
186 <link rel="icon" href="/static/hgicon.png" type="image/png" />
187 <meta name="robots" content="index, nofollow" />
187 <meta name="robots" content="index, nofollow" />
188 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
188 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
189
189
190 <title>test: log</title>
190 <title>test: log</title>
191 <link rel="alternate" type="application/atom+xml"
191 <link rel="alternate" type="application/atom+xml"
192 href="/atom-log" title="Atom feed for test" />
192 href="/atom-log" title="Atom feed for test" />
193 <link rel="alternate" type="application/rss+xml"
193 <link rel="alternate" type="application/rss+xml"
194 href="/rss-log" title="RSS feed for test" />
194 href="/rss-log" title="RSS feed for test" />
195 </head>
195 </head>
196 <body>
196 <body>
197
197
198 <div class="container">
198 <div class="container">
199 <div class="menu">
199 <div class="menu">
200 <div class="logo">
200 <div class="logo">
201 <a href="http://mercurial.selenic.com/">
201 <a href="http://mercurial.selenic.com/">
202 <img src="/static/hglogo.png" alt="mercurial" /></a>
202 <img src="/static/hglogo.png" alt="mercurial" /></a>
203 </div>
203 </div>
204 <ul>
204 <ul>
205 <li class="active">log</li>
205 <li class="active">log</li>
206 <li><a href="/graph/1d22e65f027e">graph</a></li>
206 <li><a href="/graph/1d22e65f027e">graph</a></li>
207 <li><a href="/tags">tags</a></li>
207 <li><a href="/tags">tags</a></li>
208 <li><a href="/bookmarks">bookmarks</a></li>
208 <li><a href="/bookmarks">bookmarks</a></li>
209 <li><a href="/branches">branches</a></li>
209 <li><a href="/branches">branches</a></li>
210 </ul>
210 </ul>
211 <ul>
211 <ul>
212 <li><a href="/rev/1d22e65f027e">changeset</a></li>
212 <li><a href="/rev/1d22e65f027e">changeset</a></li>
213 <li><a href="/file/1d22e65f027e">browse</a></li>
213 <li><a href="/file/1d22e65f027e">browse</a></li>
214 </ul>
214 </ul>
215 <ul>
215 <ul>
216
216
217 </ul>
217 </ul>
218 <ul>
218 <ul>
219 <li><a href="/help">help</a></li>
219 <li><a href="/help">help</a></li>
220 </ul>
220 </ul>
221 </div>
221 </div>
222
222
223 <div class="main">
223 <div class="main">
224 <h2><a href="/">test</a></h2>
224 <h2><a href="/">test</a></h2>
225 <h3>log</h3>
225 <h3>log</h3>
226
226
227 <form class="search" action="/log">
227 <form class="search" action="/log">
228
228
229 <p><input name="rev" id="search1" type="text" size="30" /></p>
229 <p><input name="rev" id="search1" type="text" size="30" /></p>
230 <div id="hint">find changesets by author, revision,
230 <div id="hint">find changesets by author, revision,
231 files, or words in the commit message</div>
231 files, or words in the commit message</div>
232 </form>
232 </form>
233
233
234 <div class="navigate">
234 <div class="navigate">
235 <a href="/shortlog/2?revcount=30">less</a>
235 <a href="/shortlog/2?revcount=30">less</a>
236 <a href="/shortlog/2?revcount=120">more</a>
236 <a href="/shortlog/2?revcount=120">more</a>
237 | rev 2: <a href="/shortlog/2ef0ac749a14">(0)</a> <a href="/shortlog/tip">tip</a>
237 | rev 2: <a href="/shortlog/2ef0ac749a14">(0)</a> <a href="/shortlog/tip">tip</a>
238 </div>
238 </div>
239
239
240 <table class="bigtable">
240 <table class="bigtable">
241 <tr>
241 <tr>
242 <th class="age">age</th>
242 <th class="age">age</th>
243 <th class="author">author</th>
243 <th class="author">author</th>
244 <th class="description">description</th>
244 <th class="description">description</th>
245 </tr>
245 </tr>
246 <tr class="parity0">
246 <tr class="parity0">
247 <td class="age">1970-01-01</td>
247 <td class="age">1970-01-01</td>
248 <td class="author">test</td>
248 <td class="author">test</td>
249 <td class="description"><a href="/rev/1d22e65f027e">branch</a><span class="branchhead">stable</span> <span class="tag">tip</span> <span class="tag">something</span> </td>
249 <td class="description"><a href="/rev/1d22e65f027e">branch</a><span class="branchhead">stable</span> <span class="tag">tip</span> <span class="tag">something</span> </td>
250 </tr>
250 </tr>
251 <tr class="parity1">
251 <tr class="parity1">
252 <td class="age">1970-01-01</td>
252 <td class="age">1970-01-01</td>
253 <td class="author">test</td>
253 <td class="author">test</td>
254 <td class="description"><a href="/rev/a4f92ed23982">Added tag 1.0 for changeset 2ef0ac749a14</a><span class="branchhead">default</span> </td>
254 <td class="description"><a href="/rev/a4f92ed23982">Added tag 1.0 for changeset 2ef0ac749a14</a><span class="branchhead">default</span> </td>
255 </tr>
255 </tr>
256 <tr class="parity0">
256 <tr class="parity0">
257 <td class="age">1970-01-01</td>
257 <td class="age">1970-01-01</td>
258 <td class="author">test</td>
258 <td class="author">test</td>
259 <td class="description"><a href="/rev/2ef0ac749a14">base</a><span class="tag">1.0</span> </td>
259 <td class="description"><a href="/rev/2ef0ac749a14">base</a><span class="tag">1.0</span> </td>
260 </tr>
260 </tr>
261
261
262 </table>
262 </table>
263
263
264 <div class="navigate">
264 <div class="navigate">
265 <a href="/shortlog/2?revcount=30">less</a>
265 <a href="/shortlog/2?revcount=30">less</a>
266 <a href="/shortlog/2?revcount=120">more</a>
266 <a href="/shortlog/2?revcount=120">more</a>
267 | rev 2: <a href="/shortlog/2ef0ac749a14">(0)</a> <a href="/shortlog/tip">tip</a>
267 | rev 2: <a href="/shortlog/2ef0ac749a14">(0)</a> <a href="/shortlog/tip">tip</a>
268 </div>
268 </div>
269
269
270 </div>
270 </div>
271 </div>
271 </div>
272
272
273
273
274
274
275 </body>
275 </body>
276 </html>
276 </html>
277
277
278 $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/rev/0/'
278 $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/rev/0/'
279 200 Script output follows
279 200 Script output follows
280
280
281 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
281 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
282 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
282 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
283 <head>
283 <head>
284 <link rel="icon" href="/static/hgicon.png" type="image/png" />
284 <link rel="icon" href="/static/hgicon.png" type="image/png" />
285 <meta name="robots" content="index, nofollow" />
285 <meta name="robots" content="index, nofollow" />
286 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
286 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
287
287
288 <title>test: 2ef0ac749a14</title>
288 <title>test: 2ef0ac749a14</title>
289 </head>
289 </head>
290 <body>
290 <body>
291 <div class="container">
291 <div class="container">
292 <div class="menu">
292 <div class="menu">
293 <div class="logo">
293 <div class="logo">
294 <a href="http://mercurial.selenic.com/">
294 <a href="http://mercurial.selenic.com/">
295 <img src="/static/hglogo.png" alt="mercurial" /></a>
295 <img src="/static/hglogo.png" alt="mercurial" /></a>
296 </div>
296 </div>
297 <ul>
297 <ul>
298 <li><a href="/shortlog/2ef0ac749a14">log</a></li>
298 <li><a href="/shortlog/2ef0ac749a14">log</a></li>
299 <li><a href="/graph/2ef0ac749a14">graph</a></li>
299 <li><a href="/graph/2ef0ac749a14">graph</a></li>
300 <li><a href="/tags">tags</a></li>
300 <li><a href="/tags">tags</a></li>
301 <li><a href="/bookmarks">bookmarks</a></li>
301 <li><a href="/bookmarks">bookmarks</a></li>
302 <li><a href="/branches">branches</a></li>
302 <li><a href="/branches">branches</a></li>
303 </ul>
303 </ul>
304 <ul>
304 <ul>
305 <li class="active">changeset</li>
305 <li class="active">changeset</li>
306 <li><a href="/raw-rev/2ef0ac749a14">raw</a></li>
306 <li><a href="/raw-rev/2ef0ac749a14">raw</a></li>
307 <li><a href="/file/2ef0ac749a14">browse</a></li>
307 <li><a href="/file/2ef0ac749a14">browse</a></li>
308 </ul>
308 </ul>
309 <ul>
309 <ul>
310
310
311 </ul>
311 </ul>
312 <ul>
312 <ul>
313 <li><a href="/help">help</a></li>
313 <li><a href="/help">help</a></li>
314 </ul>
314 </ul>
315 </div>
315 </div>
316
316
317 <div class="main">
317 <div class="main">
318
318
319 <h2><a href="/">test</a></h2>
319 <h2><a href="/">test</a></h2>
320 <h3>changeset 0:2ef0ac749a14 <span class="tag">1.0</span> </h3>
320 <h3>changeset 0:2ef0ac749a14 <span class="tag">1.0</span> </h3>
321
321
322 <form class="search" action="/log">
322 <form class="search" action="/log">
323
323
324 <p><input name="rev" id="search1" type="text" size="30" /></p>
324 <p><input name="rev" id="search1" type="text" size="30" /></p>
325 <div id="hint">find changesets by author, revision,
325 <div id="hint">find changesets by author, revision,
326 files, or words in the commit message</div>
326 files, or words in the commit message</div>
327 </form>
327 </form>
328
328
329 <div class="description">base</div>
329 <div class="description">base</div>
330
330
331 <table id="changesetEntry">
331 <table id="changesetEntry">
332 <tr>
332 <tr>
333 <th class="author">author</th>
333 <th class="author">author</th>
334 <td class="author">&#116;&#101;&#115;&#116;</td>
334 <td class="author">&#116;&#101;&#115;&#116;</td>
335 </tr>
335 </tr>
336 <tr>
336 <tr>
337 <th class="date">date</th>
337 <th class="date">date</th>
338 <td class="date">Thu Jan 01 00:00:00 1970 +0000 (1970-01-01)</td></tr>
338 <td class="date">Thu Jan 01 00:00:00 1970 +0000 (1970-01-01)</td></tr>
339 <tr>
339 <tr>
340 <th class="author">parents</th>
340 <th class="author">parents</th>
341 <td class="author"></td>
341 <td class="author"></td>
342 </tr>
342 </tr>
343 <tr>
343 <tr>
344 <th class="author">children</th>
344 <th class="author">children</th>
345 <td class="author"> <a href="/rev/a4f92ed23982">a4f92ed23982</a></td>
345 <td class="author"> <a href="/rev/a4f92ed23982">a4f92ed23982</a></td>
346 </tr>
346 </tr>
347 <tr>
347 <tr>
348 <th class="files">files</th>
348 <th class="files">files</th>
349 <td class="files"><a href="/file/2ef0ac749a14/da/foo">da/foo</a> <a href="/file/2ef0ac749a14/foo">foo</a> </td>
349 <td class="files"><a href="/file/2ef0ac749a14/da/foo">da/foo</a> <a href="/file/2ef0ac749a14/foo">foo</a> </td>
350 </tr>
350 </tr>
351 </table>
351 </table>
352
352
353 <div class="overflow">
353 <div class="overflow">
354 <div class="sourcefirst"> line diff</div>
354 <div class="sourcefirst"> line diff</div>
355
355
356 <div class="source bottomline parity0"><pre><a href="#l1.1" id="l1.1"> 1.1</a> <span class="minusline">--- /dev/null Thu Jan 01 00:00:00 1970 +0000
356 <div class="source bottomline parity0"><pre><a href="#l1.1" id="l1.1"> 1.1</a> <span class="minusline">--- /dev/null Thu Jan 01 00:00:00 1970 +0000
357 </span><a href="#l1.2" id="l1.2"> 1.2</a> <span class="plusline">+++ b/da/foo Thu Jan 01 00:00:00 1970 +0000
357 </span><a href="#l1.2" id="l1.2"> 1.2</a> <span class="plusline">+++ b/da/foo Thu Jan 01 00:00:00 1970 +0000
358 </span><a href="#l1.3" id="l1.3"> 1.3</a> <span class="atline">@@ -0,0 +1,1 @@
358 </span><a href="#l1.3" id="l1.3"> 1.3</a> <span class="atline">@@ -0,0 +1,1 @@
359 </span><a href="#l1.4" id="l1.4"> 1.4</a> <span class="plusline">+foo
359 </span><a href="#l1.4" id="l1.4"> 1.4</a> <span class="plusline">+foo
360 </span></pre></div><div class="source bottomline parity1"><pre><a href="#l2.1" id="l2.1"> 2.1</a> <span class="minusline">--- /dev/null Thu Jan 01 00:00:00 1970 +0000
360 </span></pre></div><div class="source bottomline parity1"><pre><a href="#l2.1" id="l2.1"> 2.1</a> <span class="minusline">--- /dev/null Thu Jan 01 00:00:00 1970 +0000
361 </span><a href="#l2.2" id="l2.2"> 2.2</a> <span class="plusline">+++ b/foo Thu Jan 01 00:00:00 1970 +0000
361 </span><a href="#l2.2" id="l2.2"> 2.2</a> <span class="plusline">+++ b/foo Thu Jan 01 00:00:00 1970 +0000
362 </span><a href="#l2.3" id="l2.3"> 2.3</a> <span class="atline">@@ -0,0 +1,1 @@
362 </span><a href="#l2.3" id="l2.3"> 2.3</a> <span class="atline">@@ -0,0 +1,1 @@
363 </span><a href="#l2.4" id="l2.4"> 2.4</a> <span class="plusline">+foo
363 </span><a href="#l2.4" id="l2.4"> 2.4</a> <span class="plusline">+foo
364 </span></pre></div>
364 </span></pre></div>
365 </div>
365 </div>
366
366
367 </div>
367 </div>
368 </div>
368 </div>
369
369
370
370
371 </body>
371 </body>
372 </html>
372 </html>
373
373
374 $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/rev/1/?style=raw'
374 $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/rev/1/?style=raw'
375 200 Script output follows
375 200 Script output follows
376
376
377
377
378 # HG changeset patch
378 # HG changeset patch
379 # User test
379 # User test
380 # Date 0 0
380 # Date 0 0
381 # Node ID a4f92ed23982be056b9852de5dfe873eaac7f0de
381 # Node ID a4f92ed23982be056b9852de5dfe873eaac7f0de
382 # Parent 2ef0ac749a14e4f57a5a822464a0902c6f7f448f
382 # Parent 2ef0ac749a14e4f57a5a822464a0902c6f7f448f
383 Added tag 1.0 for changeset 2ef0ac749a14
383 Added tag 1.0 for changeset 2ef0ac749a14
384
384
385 diff -r 2ef0ac749a14 -r a4f92ed23982 .hgtags
385 diff -r 2ef0ac749a14 -r a4f92ed23982 .hgtags
386 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
386 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
387 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
387 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
388 @@ -0,0 +1,1 @@
388 @@ -0,0 +1,1 @@
389 +2ef0ac749a14e4f57a5a822464a0902c6f7f448f 1.0
389 +2ef0ac749a14e4f57a5a822464a0902c6f7f448f 1.0
390
390
391 $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/log?rev=base'
391 $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/log?rev=base'
392 200 Script output follows
392 200 Script output follows
393
393
394 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
394 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
395 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
395 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
396 <head>
396 <head>
397 <link rel="icon" href="/static/hgicon.png" type="image/png" />
397 <link rel="icon" href="/static/hgicon.png" type="image/png" />
398 <meta name="robots" content="index, nofollow" />
398 <meta name="robots" content="index, nofollow" />
399 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
399 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
400
400
401 <title>test: searching for base</title>
401 <title>test: searching for base</title>
402 </head>
402 </head>
403 <body>
403 <body>
404
404
405 <div class="container">
405 <div class="container">
406 <div class="menu">
406 <div class="menu">
407 <div class="logo">
407 <div class="logo">
408 <a href="http://mercurial.selenic.com/">
408 <a href="http://mercurial.selenic.com/">
409 <img src="/static/hglogo.png" width=75 height=90 border=0 alt="mercurial"></a>
409 <img src="/static/hglogo.png" width=75 height=90 border=0 alt="mercurial"></a>
410 </div>
410 </div>
411 <ul>
411 <ul>
412 <li><a href="/shortlog">log</a></li>
412 <li><a href="/shortlog">log</a></li>
413 <li><a href="/graph">graph</a></li>
413 <li><a href="/graph">graph</a></li>
414 <li><a href="/tags">tags</a></li>
414 <li><a href="/tags">tags</a></li>
415 <li><a href="/bookmarks">bookmarks</a></li>
415 <li><a href="/bookmarks">bookmarks</a></li>
416 <li><a href="/branches">branches</a></li>
416 <li><a href="/branches">branches</a></li>
417 <li><a href="/help">help</a></li>
417 <li><a href="/help">help</a></li>
418 </ul>
418 </ul>
419 </div>
419 </div>
420
420
421 <div class="main">
421 <div class="main">
422 <h2><a href="/">test</a></h2>
422 <h2><a href="/">test</a></h2>
423 <h3>searching for 'base'</h3>
423 <h3>searching for 'base'</h3>
424
424
425 <form class="search" action="/log">
425 <form class="search" action="/log">
426
426
427 <p><input name="rev" id="search1" type="text" size="30"></p>
427 <p><input name="rev" id="search1" type="text" size="30"></p>
428 <div id="hint">find changesets by author, revision,
428 <div id="hint">find changesets by author, revision,
429 files, or words in the commit message</div>
429 files, or words in the commit message</div>
430 </form>
430 </form>
431
431
432 <div class="navigate">
432 <div class="navigate">
433 <a href="/search/?rev=base&revcount=5">less</a>
433 <a href="/search/?rev=base&revcount=5">less</a>
434 <a href="/search/?rev=base&revcount=20">more</a>
434 <a href="/search/?rev=base&revcount=20">more</a>
435 </div>
435 </div>
436
436
437 <table class="bigtable">
437 <table class="bigtable">
438 <tr>
438 <tr>
439 <th class="age">age</th>
439 <th class="age">age</th>
440 <th class="author">author</th>
440 <th class="author">author</th>
441 <th class="description">description</th>
441 <th class="description">description</th>
442 </tr>
442 </tr>
443 <tr class="parity0">
443 <tr class="parity0">
444 <td class="age">1970-01-01</td>
444 <td class="age">1970-01-01</td>
445 <td class="author">test</td>
445 <td class="author">test</td>
446 <td class="description"><a href="/rev/2ef0ac749a14">base</a><span class="tag">1.0</span> </td>
446 <td class="description"><a href="/rev/2ef0ac749a14">base</a><span class="tag">1.0</span> </td>
447 </tr>
447 </tr>
448
448
449 </table>
449 </table>
450
450
451 <div class="navigate">
451 <div class="navigate">
452 <a href="/search/?rev=base&revcount=5">less</a>
452 <a href="/search/?rev=base&revcount=5">less</a>
453 <a href="/search/?rev=base&revcount=20">more</a>
453 <a href="/search/?rev=base&revcount=20">more</a>
454 </div>
454 </div>
455
455
456 </div>
456 </div>
457 </div>
457 </div>
458
458
459
459
460
460
461 </body>
461 </body>
462 </html>
462 </html>
463
463
464
464
465 File-related
465 File-related
466
466
467 $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/file/1/foo/?style=raw'
467 $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/file/1/foo/?style=raw'
468 200 Script output follows
468 200 Script output follows
469
469
470 foo
470 foo
471 $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/annotate/1/foo/?style=raw'
471 $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/annotate/1/foo/?style=raw'
472 200 Script output follows
472 200 Script output follows
473
473
474
474
475 test@0: foo
475 test@0: foo
476
476
477
477
478
478
479
479
480 $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/file/1/?style=raw'
480 $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/file/1/?style=raw'
481 200 Script output follows
481 200 Script output follows
482
482
483
483
484 drwxr-xr-x da
484 drwxr-xr-x da
485 -rw-r--r-- 45 .hgtags
485 -rw-r--r-- 45 .hgtags
486 -rw-r--r-- 4 foo
486 -rw-r--r-- 4 foo
487
487
488
488
489 $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/file/1/foo'
489 $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/file/1/foo'
490 200 Script output follows
490 200 Script output follows
491
491
492 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
492 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
493 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
493 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
494 <head>
494 <head>
495 <link rel="icon" href="/static/hgicon.png" type="image/png" />
495 <link rel="icon" href="/static/hgicon.png" type="image/png" />
496 <meta name="robots" content="index, nofollow" />
496 <meta name="robots" content="index, nofollow" />
497 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
497 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
498
498
499 <title>test: a4f92ed23982 foo</title>
499 <title>test: a4f92ed23982 foo</title>
500 </head>
500 </head>
501 <body>
501 <body>
502
502
503 <div class="container">
503 <div class="container">
504 <div class="menu">
504 <div class="menu">
505 <div class="logo">
505 <div class="logo">
506 <a href="http://mercurial.selenic.com/">
506 <a href="http://mercurial.selenic.com/">
507 <img src="/static/hglogo.png" alt="mercurial" /></a>
507 <img src="/static/hglogo.png" alt="mercurial" /></a>
508 </div>
508 </div>
509 <ul>
509 <ul>
510 <li><a href="/shortlog/a4f92ed23982">log</a></li>
510 <li><a href="/shortlog/a4f92ed23982">log</a></li>
511 <li><a href="/graph/a4f92ed23982">graph</a></li>
511 <li><a href="/graph/a4f92ed23982">graph</a></li>
512 <li><a href="/tags">tags</a></li>
512 <li><a href="/tags">tags</a></li>
513 <li><a href="/branches">branches</a></li>
513 <li><a href="/branches">branches</a></li>
514 </ul>
514 </ul>
515 <ul>
515 <ul>
516 <li><a href="/rev/a4f92ed23982">changeset</a></li>
516 <li><a href="/rev/a4f92ed23982">changeset</a></li>
517 <li><a href="/file/a4f92ed23982/">browse</a></li>
517 <li><a href="/file/a4f92ed23982/">browse</a></li>
518 </ul>
518 </ul>
519 <ul>
519 <ul>
520 <li class="active">file</li>
520 <li class="active">file</li>
521 <li><a href="/file/tip/foo">latest</a></li>
521 <li><a href="/file/tip/foo">latest</a></li>
522 <li><a href="/diff/a4f92ed23982/foo">diff</a></li>
522 <li><a href="/diff/a4f92ed23982/foo">diff</a></li>
523 <li><a href="/annotate/a4f92ed23982/foo">annotate</a></li>
523 <li><a href="/annotate/a4f92ed23982/foo">annotate</a></li>
524 <li><a href="/log/a4f92ed23982/foo">file log</a></li>
524 <li><a href="/log/a4f92ed23982/foo">file log</a></li>
525 <li><a href="/raw-file/a4f92ed23982/foo">raw</a></li>
525 <li><a href="/raw-file/a4f92ed23982/foo">raw</a></li>
526 </ul>
526 </ul>
527 <ul>
527 <ul>
528 <li><a href="/help">help</a></li>
528 <li><a href="/help">help</a></li>
529 </ul>
529 </ul>
530 </div>
530 </div>
531
531
532 <div class="main">
532 <div class="main">
533 <h2><a href="/">test</a></h2>
533 <h2><a href="/">test</a></h2>
534 <h3>view foo @ 1:a4f92ed23982</h3>
534 <h3>view foo @ 1:a4f92ed23982</h3>
535
535
536 <form class="search" action="/log">
536 <form class="search" action="/log">
537
537
538 <p><input name="rev" id="search1" type="text" size="30" /></p>
538 <p><input name="rev" id="search1" type="text" size="30" /></p>
539 <div id="hint">find changesets by author, revision,
539 <div id="hint">find changesets by author, revision,
540 files, or words in the commit message</div>
540 files, or words in the commit message</div>
541 </form>
541 </form>
542
542
543 <div class="description">Added tag 1.0 for changeset 2ef0ac749a14</div>
543 <div class="description">Added tag 1.0 for changeset 2ef0ac749a14</div>
544
544
545 <table id="changesetEntry">
545 <table id="changesetEntry">
546 <tr>
546 <tr>
547 <th class="author">author</th>
547 <th class="author">author</th>
548 <td class="author">&#116;&#101;&#115;&#116;</td>
548 <td class="author">&#116;&#101;&#115;&#116;</td>
549 </tr>
549 </tr>
550 <tr>
550 <tr>
551 <th class="date">date</th>
551 <th class="date">date</th>
552 <td class="date">Thu Jan 01 00:00:00 1970 +0000 (1970-01-01)</td>
552 <td class="date">Thu Jan 01 00:00:00 1970 +0000 (1970-01-01)</td>
553 </tr>
553 </tr>
554 <tr>
554 <tr>
555 <th class="author">parents</th>
555 <th class="author">parents</th>
556 <td class="author"></td>
556 <td class="author"></td>
557 </tr>
557 </tr>
558 <tr>
558 <tr>
559 <th class="author">children</th>
559 <th class="author">children</th>
560 <td class="author"><a href="/file/1d22e65f027e/foo">1d22e65f027e</a> </td>
560 <td class="author"><a href="/file/1d22e65f027e/foo">1d22e65f027e</a> </td>
561 </tr>
561 </tr>
562
562
563 </table>
563 </table>
564
564
565 <div class="overflow">
565 <div class="overflow">
566 <div class="sourcefirst"> line source</div>
566 <div class="sourcefirst"> line source</div>
567
567
568 <div class="parity0 source"><a href="#l1" id="l1"> 1</a> foo
568 <div class="parity0 source"><a href="#l1" id="l1"> 1</a> foo
569 </div>
569 </div>
570 <div class="sourcelast"></div>
570 <div class="sourcelast"></div>
571 </div>
571 </div>
572 </div>
572 </div>
573 </div>
573 </div>
574
574
575
575
576
576
577 </body>
577 </body>
578 </html>
578 </html>
579
579
580 $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/filediff/1/foo/?style=raw'
580 $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/filediff/1/foo/?style=raw'
581 200 Script output follows
581 200 Script output follows
582
582
583
583
584 diff -r 000000000000 -r a4f92ed23982 foo
584 diff -r 000000000000 -r a4f92ed23982 foo
585 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
585 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
586 +++ b/foo Thu Jan 01 00:00:00 1970 +0000
586 +++ b/foo Thu Jan 01 00:00:00 1970 +0000
587 @@ -0,0 +1,1 @@
587 @@ -0,0 +1,1 @@
588 +foo
588 +foo
589
589
590
590
591
591
592
592
593
593
594 Overviews
594 Overviews
595
595
596 $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/raw-tags'
596 $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/raw-tags'
597 200 Script output follows
597 200 Script output follows
598
598
599 tip 1d22e65f027e5a0609357e7d8e7508cd2ba5d2fe
599 tip 1d22e65f027e5a0609357e7d8e7508cd2ba5d2fe
600 1.0 2ef0ac749a14e4f57a5a822464a0902c6f7f448f
600 1.0 2ef0ac749a14e4f57a5a822464a0902c6f7f448f
601 $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/raw-branches'
601 $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/raw-branches'
602 200 Script output follows
602 200 Script output follows
603
603
604 stable 1d22e65f027e5a0609357e7d8e7508cd2ba5d2fe open
604 stable 1d22e65f027e5a0609357e7d8e7508cd2ba5d2fe open
605 default a4f92ed23982be056b9852de5dfe873eaac7f0de inactive
605 default a4f92ed23982be056b9852de5dfe873eaac7f0de inactive
606 $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/summary/?style=gitweb'
606 $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/summary/?style=gitweb'
607 200 Script output follows
607 200 Script output follows
608
608
609 <?xml version="1.0" encoding="ascii"?>
609 <?xml version="1.0" encoding="ascii"?>
610 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
610 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
611 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US" lang="en-US">
611 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US" lang="en-US">
612 <head>
612 <head>
613 <link rel="icon" href="/static/hgicon.png" type="image/png" />
613 <link rel="icon" href="/static/hgicon.png" type="image/png" />
614 <meta name="robots" content="index, nofollow"/>
614 <meta name="robots" content="index, nofollow"/>
615 <link rel="stylesheet" href="/static/style-gitweb.css" type="text/css" />
615 <link rel="stylesheet" href="/static/style-gitweb.css" type="text/css" />
616
616
617
617
618 <title>test: Summary</title>
618 <title>test: Summary</title>
619 <link rel="alternate" type="application/atom+xml"
619 <link rel="alternate" type="application/atom+xml"
620 href="/atom-log" title="Atom feed for test"/>
620 href="/atom-log" title="Atom feed for test"/>
621 <link rel="alternate" type="application/rss+xml"
621 <link rel="alternate" type="application/rss+xml"
622 href="/rss-log" title="RSS feed for test"/>
622 href="/rss-log" title="RSS feed for test"/>
623 </head>
623 </head>
624 <body>
624 <body>
625
625
626 <div class="page_header">
626 <div class="page_header">
627 <a href="http://mercurial.selenic.com/" title="Mercurial" style="float: right;">Mercurial</a><a href="/summary?style=gitweb">test</a> / summary
627 <a href="http://mercurial.selenic.com/" title="Mercurial" style="float: right;">Mercurial</a><a href="/summary?style=gitweb">test</a> / summary
628
628
629 <form action="/log">
629 <form action="/log">
630 <input type="hidden" name="style" value="gitweb" />
630 <input type="hidden" name="style" value="gitweb" />
631 <div class="search">
631 <div class="search">
632 <input type="text" name="rev" />
632 <input type="text" name="rev" />
633 </div>
633 </div>
634 </form>
634 </form>
635 </div>
635 </div>
636
636
637 <div class="page_nav">
637 <div class="page_nav">
638 summary |
638 summary |
639 <a href="/shortlog?style=gitweb">shortlog</a> |
639 <a href="/shortlog?style=gitweb">shortlog</a> |
640 <a href="/log?style=gitweb">changelog</a> |
640 <a href="/log?style=gitweb">changelog</a> |
641 <a href="/graph?style=gitweb">graph</a> |
641 <a href="/graph?style=gitweb">graph</a> |
642 <a href="/tags?style=gitweb">tags</a> |
642 <a href="/tags?style=gitweb">tags</a> |
643 <a href="/branches?style=gitweb">branches</a> |
643 <a href="/branches?style=gitweb">branches</a> |
644 <a href="/file/1d22e65f027e?style=gitweb">files</a> |
644 <a href="/file/1d22e65f027e?style=gitweb">files</a> |
645 <a href="/help?style=gitweb">help</a>
645 <a href="/help?style=gitweb">help</a>
646 <br/>
646 <br/>
647 </div>
647 </div>
648
648
649 <div class="title">&nbsp;</div>
649 <div class="title">&nbsp;</div>
650 <table cellspacing="0">
650 <table cellspacing="0">
651 <tr><td>description</td><td>unknown</td></tr>
651 <tr><td>description</td><td>unknown</td></tr>
652 <tr><td>owner</td><td>&#70;&#111;&#111;&#32;&#66;&#97;&#114;&#32;&#60;&#102;&#111;&#111;&#46;&#98;&#97;&#114;&#64;&#101;&#120;&#97;&#109;&#112;&#108;&#101;&#46;&#99;&#111;&#109;&#62;</td></tr>
652 <tr><td>owner</td><td>&#70;&#111;&#111;&#32;&#66;&#97;&#114;&#32;&#60;&#102;&#111;&#111;&#46;&#98;&#97;&#114;&#64;&#101;&#120;&#97;&#109;&#112;&#108;&#101;&#46;&#99;&#111;&#109;&#62;</td></tr>
653 <tr><td>last change</td><td>Thu, 01 Jan 1970 00:00:00 +0000</td></tr>
653 <tr><td>last change</td><td>Thu, 01 Jan 1970 00:00:00 +0000</td></tr>
654 </table>
654 </table>
655
655
656 <div><a class="title" href="/shortlog?style=gitweb">changes</a></div>
656 <div><a class="title" href="/shortlog?style=gitweb">changes</a></div>
657 <table cellspacing="0">
657 <table cellspacing="0">
658
658
659 <tr class="parity0">
659 <tr class="parity0">
660 <td class="age"><i>1970-01-01</i></td>
660 <td class="age"><i>1970-01-01</i></td>
661 <td><i>test</i></td>
661 <td><i>test</i></td>
662 <td>
662 <td>
663 <a class="list" href="/rev/1d22e65f027e?style=gitweb">
663 <a class="list" href="/rev/1d22e65f027e?style=gitweb">
664 <b>branch</b>
664 <b>branch</b>
665 <span class="logtags"><span class="branchtag" title="stable">stable</span> <span class="tagtag" title="tip">tip</span> </span>
665 <span class="logtags"><span class="branchtag" title="stable">stable</span> <span class="tagtag" title="tip">tip</span> </span>
666 </a>
666 </a>
667 </td>
667 </td>
668 <td class="link" nowrap>
668 <td class="link" nowrap>
669 <a href="/rev/1d22e65f027e?style=gitweb">changeset</a> |
669 <a href="/rev/1d22e65f027e?style=gitweb">changeset</a> |
670 <a href="/file/1d22e65f027e?style=gitweb">files</a>
670 <a href="/file/1d22e65f027e?style=gitweb">files</a>
671 </td>
671 </td>
672 </tr>
672 </tr>
673 <tr class="parity1">
673 <tr class="parity1">
674 <td class="age"><i>1970-01-01</i></td>
674 <td class="age"><i>1970-01-01</i></td>
675 <td><i>test</i></td>
675 <td><i>test</i></td>
676 <td>
676 <td>
677 <a class="list" href="/rev/a4f92ed23982?style=gitweb">
677 <a class="list" href="/rev/a4f92ed23982?style=gitweb">
678 <b>Added tag 1.0 for changeset 2ef0ac749a14</b>
678 <b>Added tag 1.0 for changeset 2ef0ac749a14</b>
679 <span class="logtags"><span class="branchtag" title="default">default</span> </span>
679 <span class="logtags"><span class="branchtag" title="default">default</span> </span>
680 </a>
680 </a>
681 </td>
681 </td>
682 <td class="link" nowrap>
682 <td class="link" nowrap>
683 <a href="/rev/a4f92ed23982?style=gitweb">changeset</a> |
683 <a href="/rev/a4f92ed23982?style=gitweb">changeset</a> |
684 <a href="/file/a4f92ed23982?style=gitweb">files</a>
684 <a href="/file/a4f92ed23982?style=gitweb">files</a>
685 </td>
685 </td>
686 </tr>
686 </tr>
687 <tr class="parity0">
687 <tr class="parity0">
688 <td class="age"><i>1970-01-01</i></td>
688 <td class="age"><i>1970-01-01</i></td>
689 <td><i>test</i></td>
689 <td><i>test</i></td>
690 <td>
690 <td>
691 <a class="list" href="/rev/2ef0ac749a14?style=gitweb">
691 <a class="list" href="/rev/2ef0ac749a14?style=gitweb">
692 <b>base</b>
692 <b>base</b>
693 <span class="logtags"><span class="tagtag" title="1.0">1.0</span> </span>
693 <span class="logtags"><span class="tagtag" title="1.0">1.0</span> </span>
694 </a>
694 </a>
695 </td>
695 </td>
696 <td class="link" nowrap>
696 <td class="link" nowrap>
697 <a href="/rev/2ef0ac749a14?style=gitweb">changeset</a> |
697 <a href="/rev/2ef0ac749a14?style=gitweb">changeset</a> |
698 <a href="/file/2ef0ac749a14?style=gitweb">files</a>
698 <a href="/file/2ef0ac749a14?style=gitweb">files</a>
699 </td>
699 </td>
700 </tr>
700 </tr>
701 <tr class="light"><td colspan="4"><a class="list" href="/shortlog?style=gitweb">...</a></td></tr>
701 <tr class="light"><td colspan="4"><a class="list" href="/shortlog?style=gitweb">...</a></td></tr>
702 </table>
702 </table>
703
703
704 <div><a class="title" href="/tags?style=gitweb">tags</a></div>
704 <div><a class="title" href="/tags?style=gitweb">tags</a></div>
705 <table cellspacing="0">
705 <table cellspacing="0">
706
706
707 <tr class="parity0">
707 <tr class="parity0">
708 <td class="age"><i>1970-01-01</i></td>
708 <td class="age"><i>1970-01-01</i></td>
709 <td><a class="list" href="/rev/2ef0ac749a14?style=gitweb"><b>1.0</b></a></td>
709 <td><a class="list" href="/rev/2ef0ac749a14?style=gitweb"><b>1.0</b></a></td>
710 <td class="link">
710 <td class="link">
711 <a href="/rev/2ef0ac749a14?style=gitweb">changeset</a> |
711 <a href="/rev/2ef0ac749a14?style=gitweb">changeset</a> |
712 <a href="/log/2ef0ac749a14?style=gitweb">changelog</a> |
712 <a href="/log/2ef0ac749a14?style=gitweb">changelog</a> |
713 <a href="/file/2ef0ac749a14?style=gitweb">files</a>
713 <a href="/file/2ef0ac749a14?style=gitweb">files</a>
714 </td>
714 </td>
715 </tr>
715 </tr>
716 <tr class="light"><td colspan="3"><a class="list" href="/tags?style=gitweb">...</a></td></tr>
716 <tr class="light"><td colspan="3"><a class="list" href="/tags?style=gitweb">...</a></td></tr>
717 </table>
717 </table>
718
718
719 <div><a class="title" href="#">branches</a></div>
719 <div><a class="title" href="#">branches</a></div>
720 <table cellspacing="0">
720 <table cellspacing="0">
721
721
722 <tr class="parity0">
722 <tr class="parity0">
723 <td class="age"><i>1970-01-01</i></td>
723 <td class="age"><i>1970-01-01</i></td>
724 <td><a class="list" href="/shortlog/1d22e65f027e?style=gitweb"><b>1d22e65f027e</b></a></td>
724 <td><a class="list" href="/shortlog/1d22e65f027e?style=gitweb"><b>1d22e65f027e</b></a></td>
725 <td class="">stable</td>
725 <td class="">stable</td>
726 <td class="link">
726 <td class="link">
727 <a href="/changeset/1d22e65f027e?style=gitweb">changeset</a> |
727 <a href="/changeset/1d22e65f027e?style=gitweb">changeset</a> |
728 <a href="/log/1d22e65f027e?style=gitweb">changelog</a> |
728 <a href="/log/1d22e65f027e?style=gitweb">changelog</a> |
729 <a href="/file/1d22e65f027e?style=gitweb">files</a>
729 <a href="/file/1d22e65f027e?style=gitweb">files</a>
730 </td>
730 </td>
731 </tr>
731 </tr>
732 <tr class="parity1">
732 <tr class="parity1">
733 <td class="age"><i>1970-01-01</i></td>
733 <td class="age"><i>1970-01-01</i></td>
734 <td><a class="list" href="/shortlog/a4f92ed23982?style=gitweb"><b>a4f92ed23982</b></a></td>
734 <td><a class="list" href="/shortlog/a4f92ed23982?style=gitweb"><b>a4f92ed23982</b></a></td>
735 <td class="">default</td>
735 <td class="">default</td>
736 <td class="link">
736 <td class="link">
737 <a href="/changeset/a4f92ed23982?style=gitweb">changeset</a> |
737 <a href="/changeset/a4f92ed23982?style=gitweb">changeset</a> |
738 <a href="/log/a4f92ed23982?style=gitweb">changelog</a> |
738 <a href="/log/a4f92ed23982?style=gitweb">changelog</a> |
739 <a href="/file/a4f92ed23982?style=gitweb">files</a>
739 <a href="/file/a4f92ed23982?style=gitweb">files</a>
740 </td>
740 </td>
741 </tr>
741 </tr>
742 <tr class="light">
742 <tr class="light">
743 <td colspan="4"><a class="list" href="#">...</a></td>
743 <td colspan="4"><a class="list" href="#">...</a></td>
744 </tr>
744 </tr>
745 </table>
745 </table>
746 <div class="page_footer">
746 <div class="page_footer">
747 <div class="page_footer_text">test</div>
747 <div class="page_footer_text">test</div>
748 <div class="rss_logo">
748 <div class="rss_logo">
749 <a href="/rss-log">RSS</a>
749 <a href="/rss-log">RSS</a>
750 <a href="/atom-log">Atom</a>
750 <a href="/atom-log">Atom</a>
751 </div>
751 </div>
752 <br />
752 <br />
753
753
754 </div>
754 </div>
755 </body>
755 </body>
756 </html>
756 </html>
757
757
758 $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/graph/?style=gitweb'
758 $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/graph/?style=gitweb'
759 200 Script output follows
759 200 Script output follows
760
760
761 <?xml version="1.0" encoding="ascii"?>
761 <?xml version="1.0" encoding="ascii"?>
762 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
762 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
763 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US" lang="en-US">
763 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US" lang="en-US">
764 <head>
764 <head>
765 <link rel="icon" href="/static/hgicon.png" type="image/png" />
765 <link rel="icon" href="/static/hgicon.png" type="image/png" />
766 <meta name="robots" content="index, nofollow"/>
766 <meta name="robots" content="index, nofollow"/>
767 <link rel="stylesheet" href="/static/style-gitweb.css" type="text/css" />
767 <link rel="stylesheet" href="/static/style-gitweb.css" type="text/css" />
768
768
769
769
770 <title>test: Graph</title>
770 <title>test: Graph</title>
771 <link rel="alternate" type="application/atom+xml"
771 <link rel="alternate" type="application/atom+xml"
772 href="/atom-log" title="Atom feed for test"/>
772 href="/atom-log" title="Atom feed for test"/>
773 <link rel="alternate" type="application/rss+xml"
773 <link rel="alternate" type="application/rss+xml"
774 href="/rss-log" title="RSS feed for test"/>
774 href="/rss-log" title="RSS feed for test"/>
775 <!--[if IE]><script type="text/javascript" src="/static/excanvas.js"></script><![endif]-->
775 <!--[if IE]><script type="text/javascript" src="/static/excanvas.js"></script><![endif]-->
776 </head>
776 </head>
777 <body>
777 <body>
778
778
779 <div class="page_header">
779 <div class="page_header">
780 <a href="http://mercurial.selenic.com/" title="Mercurial" style="float: right;">Mercurial</a><a href="/summary?style=gitweb">test</a> / graph
780 <a href="http://mercurial.selenic.com/" title="Mercurial" style="float: right;">Mercurial</a><a href="/summary?style=gitweb">test</a> / graph
781 </div>
781 </div>
782
782
783 <form action="/log">
783 <form action="/log">
784 <input type="hidden" name="style" value="gitweb" />
784 <input type="hidden" name="style" value="gitweb" />
785 <div class="search">
785 <div class="search">
786 <input type="text" name="rev" />
786 <input type="text" name="rev" />
787 </div>
787 </div>
788 </form>
788 </form>
789 <div class="page_nav">
789 <div class="page_nav">
790 <a href="/summary?style=gitweb">summary</a> |
790 <a href="/summary?style=gitweb">summary</a> |
791 <a href="/shortlog?style=gitweb">shortlog</a> |
791 <a href="/shortlog?style=gitweb">shortlog</a> |
792 <a href="/log/2?style=gitweb">changelog</a> |
792 <a href="/log/2?style=gitweb">changelog</a> |
793 graph |
793 graph |
794 <a href="/tags?style=gitweb">tags</a> |
794 <a href="/tags?style=gitweb">tags</a> |
795 <a href="/branches?style=gitweb">branches</a> |
795 <a href="/branches?style=gitweb">branches</a> |
796 <a href="/file/1d22e65f027e?style=gitweb">files</a> |
796 <a href="/file/1d22e65f027e?style=gitweb">files</a> |
797 <a href="/help?style=gitweb">help</a>
797 <a href="/help?style=gitweb">help</a>
798 <br/>
798 <br/>
799 <a href="/graph/2?style=gitweb&revcount=30">less</a>
799 <a href="/graph/2?style=gitweb&revcount=30">less</a>
800 <a href="/graph/2?style=gitweb&revcount=120">more</a>
800 <a href="/graph/2?style=gitweb&revcount=120">more</a>
801 | <a href="/graph/2ef0ac749a14?style=gitweb">(0)</a> <a href="/graph/2ef0ac749a14?style=gitweb">-2</a> <a href="/graph/tip?style=gitweb">tip</a> <br/>
801 | <a href="/graph/2ef0ac749a14?style=gitweb">(0)</a> <a href="/graph/2ef0ac749a14?style=gitweb">-2</a> <a href="/graph/tip?style=gitweb">tip</a> <br/>
802 </div>
802 </div>
803
803
804 <div class="title">&nbsp;</div>
804 <div class="title">&nbsp;</div>
805
805
806 <noscript>The revision graph only works with JavaScript-enabled browsers.</noscript>
806 <noscript>The revision graph only works with JavaScript-enabled browsers.</noscript>
807
807
808 <div id="wrapper">
808 <div id="wrapper">
809 <ul id="nodebgs"></ul>
809 <ul id="nodebgs"></ul>
810 <canvas id="graph" width="480" height="129"></canvas>
810 <canvas id="graph" width="480" height="129"></canvas>
811 <ul id="graphnodes"></ul>
811 <ul id="graphnodes"></ul>
812 </div>
812 </div>
813
813
814 <script type="text/javascript" src="/static/graph.js"></script>
814 <script type="text/javascript" src="/static/graph.js"></script>
815 <script>
815 <script>
816 <!-- hide script content
816 <!-- hide script content
817
817
818 var data = [["1d22e65f027e", [0, 1], [[0, 0, 1]], "branch", "test", "1970-01-01", ["stable", true], ["tip"], ["something"]], ["a4f92ed23982", [0, 1], [[0, 0, 1]], "Added tag 1.0 for changeset 2ef0ac749a14", "test", "1970-01-01", ["default", true], [], []], ["2ef0ac749a14", [0, 1], [], "base", "test", "1970-01-01", ["default", false], ["1.0"], []]];
818 var data = [["1d22e65f027e", [0, 1], [[0, 0, 1]], "branch", "test", "1970-01-01", ["stable", true], ["tip"], ["something"]], ["a4f92ed23982", [0, 1], [[0, 0, 1]], "Added tag 1.0 for changeset 2ef0ac749a14", "test", "1970-01-01", ["default", true], [], []], ["2ef0ac749a14", [0, 1], [], "base", "test", "1970-01-01", ["default", false], ["1.0"], []]];
819 var graph = new Graph();
819 var graph = new Graph();
820 graph.scale(39);
820 graph.scale(39);
821
821
822 graph.edge = function(x0, y0, x1, y1, color) {
822 graph.edge = function(x0, y0, x1, y1, color) {
823
823
824 this.setColor(color, 0.0, 0.65);
824 this.setColor(color, 0.0, 0.65);
825 this.ctx.beginPath();
825 this.ctx.beginPath();
826 this.ctx.moveTo(x0, y0);
826 this.ctx.moveTo(x0, y0);
827 this.ctx.lineTo(x1, y1);
827 this.ctx.lineTo(x1, y1);
828 this.ctx.stroke();
828 this.ctx.stroke();
829
829
830 }
830 }
831
831
832 var revlink = '<li style="_STYLE"><span class="desc">';
832 var revlink = '<li style="_STYLE"><span class="desc">';
833 revlink += '<a class="list" href="/rev/_NODEID?style=gitweb" title="_NODEID"><b>_DESC</b></a>';
833 revlink += '<a class="list" href="/rev/_NODEID?style=gitweb" title="_NODEID"><b>_DESC</b></a>';
834 revlink += '</span> _TAGS';
834 revlink += '</span> _TAGS';
835 revlink += '<span class="info">_DATE, by _USER</span></li>';
835 revlink += '<span class="info">_DATE, by _USER</span></li>';
836
836
837 graph.vertex = function(x, y, color, parity, cur) {
837 graph.vertex = function(x, y, color, parity, cur) {
838
838
839 this.ctx.beginPath();
839 this.ctx.beginPath();
840 color = this.setColor(color, 0.25, 0.75);
840 color = this.setColor(color, 0.25, 0.75);
841 this.ctx.arc(x, y, radius, 0, Math.PI * 2, true);
841 this.ctx.arc(x, y, radius, 0, Math.PI * 2, true);
842 this.ctx.fill();
842 this.ctx.fill();
843
843
844 var bg = '<li class="bg parity' + parity + '"></li>';
844 var bg = '<li class="bg parity' + parity + '"></li>';
845 var left = (this.columns + 1) * this.bg_height;
845 var left = (this.columns + 1) * this.bg_height;
846 var nstyle = 'padding-left: ' + left + 'px;';
846 var nstyle = 'padding-left: ' + left + 'px;';
847 var item = revlink.replace(/_STYLE/, nstyle);
847 var item = revlink.replace(/_STYLE/, nstyle);
848 item = item.replace(/_PARITY/, 'parity' + parity);
848 item = item.replace(/_PARITY/, 'parity' + parity);
849 item = item.replace(/_NODEID/, cur[0]);
849 item = item.replace(/_NODEID/, cur[0]);
850 item = item.replace(/_NODEID/, cur[0]);
850 item = item.replace(/_NODEID/, cur[0]);
851 item = item.replace(/_DESC/, cur[3]);
851 item = item.replace(/_DESC/, cur[3]);
852 item = item.replace(/_USER/, cur[4]);
852 item = item.replace(/_USER/, cur[4]);
853 item = item.replace(/_DATE/, cur[5]);
853 item = item.replace(/_DATE/, cur[5]);
854
854
855 var tagspan = '';
855 var tagspan = '';
856 if (cur[7].length || (cur[6][0] != 'default' || cur[6][1])) {
856 if (cur[7].length || (cur[6][0] != 'default' || cur[6][1])) {
857 tagspan = '<span class="logtags">';
857 tagspan = '<span class="logtags">';
858 if (cur[6][1]) {
858 if (cur[6][1]) {
859 tagspan += '<span class="branchtag" title="' + cur[6][0] + '">';
859 tagspan += '<span class="branchtag" title="' + cur[6][0] + '">';
860 tagspan += cur[6][0] + '</span> ';
860 tagspan += cur[6][0] + '</span> ';
861 } else if (!cur[6][1] && cur[6][0] != 'default') {
861 } else if (!cur[6][1] && cur[6][0] != 'default') {
862 tagspan += '<span class="inbranchtag" title="' + cur[6][0] + '">';
862 tagspan += '<span class="inbranchtag" title="' + cur[6][0] + '">';
863 tagspan += cur[6][0] + '</span> ';
863 tagspan += cur[6][0] + '</span> ';
864 }
864 }
865 if (cur[7].length) {
865 if (cur[7].length) {
866 for (var t in cur[7]) {
866 for (var t in cur[7]) {
867 var tag = cur[7][t];
867 var tag = cur[7][t];
868 tagspan += '<span class="tagtag">' + tag + '</span> ';
868 tagspan += '<span class="tagtag">' + tag + '</span> ';
869 }
869 }
870 }
870 }
871 tagspan += '</span>';
871 tagspan += '</span>';
872 }
872 }
873
873
874 item = item.replace(/_TAGS/, tagspan);
874 item = item.replace(/_TAGS/, tagspan);
875 return [bg, item];
875 return [bg, item];
876
876
877 }
877 }
878
878
879 graph.render(data);
879 graph.render(data);
880
880
881 // stop hiding script -->
881 // stop hiding script -->
882 </script>
882 </script>
883
883
884 <div class="page_nav">
884 <div class="page_nav">
885 <a href="/graph/2?style=gitweb&revcount=30">less</a>
885 <a href="/graph/2?style=gitweb&revcount=30">less</a>
886 <a href="/graph/2?style=gitweb&revcount=120">more</a>
886 <a href="/graph/2?style=gitweb&revcount=120">more</a>
887 | <a href="/graph/2ef0ac749a14?style=gitweb">(0)</a> <a href="/graph/2ef0ac749a14?style=gitweb">-2</a> <a href="/graph/tip?style=gitweb">tip</a>
887 | <a href="/graph/2ef0ac749a14?style=gitweb">(0)</a> <a href="/graph/2ef0ac749a14?style=gitweb">-2</a> <a href="/graph/tip?style=gitweb">tip</a>
888 </div>
888 </div>
889
889
890 <div class="page_footer">
890 <div class="page_footer">
891 <div class="page_footer_text">test</div>
891 <div class="page_footer_text">test</div>
892 <div class="rss_logo">
892 <div class="rss_logo">
893 <a href="/rss-log">RSS</a>
893 <a href="/rss-log">RSS</a>
894 <a href="/atom-log">Atom</a>
894 <a href="/atom-log">Atom</a>
895 </div>
895 </div>
896 <br />
896 <br />
897
897
898 </div>
898 </div>
899 </body>
899 </body>
900 </html>
900 </html>
901
901
902
902
903 capabilities
903 capabilities
904
904
905 $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '?cmd=capabilities'; echo
905 $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '?cmd=capabilities'; echo
906 200 Script output follows
906 200 Script output follows
907
907
908 lookup changegroupsubset branchmap pushkey unbundle=HG10GZ,HG10BZ,HG10UN
908 lookup changegroupsubset branchmap pushkey known unbundle=HG10GZ,HG10BZ,HG10UN
909
909
910 heads
910 heads
911
911
912 $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '?cmd=heads'
912 $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '?cmd=heads'
913 200 Script output follows
913 200 Script output follows
914
914
915 1d22e65f027e5a0609357e7d8e7508cd2ba5d2fe
915 1d22e65f027e5a0609357e7d8e7508cd2ba5d2fe
916
916
917 branches
917 branches
918
918
919 $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '?cmd=branches&nodes=0000000000000000000000000000000000000000'
919 $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '?cmd=branches&nodes=0000000000000000000000000000000000000000'
920 200 Script output follows
920 200 Script output follows
921
921
922 0000000000000000000000000000000000000000 0000000000000000000000000000000000000000 0000000000000000000000000000000000000000 0000000000000000000000000000000000000000
922 0000000000000000000000000000000000000000 0000000000000000000000000000000000000000 0000000000000000000000000000000000000000 0000000000000000000000000000000000000000
923
923
924 changegroup
924 changegroup
925
925
926 $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '?cmd=changegroup&roots=0000000000000000000000000000000000000000'
926 $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '?cmd=changegroup&roots=0000000000000000000000000000000000000000'
927 200 Script output follows
927 200 Script output follows
928
928
929 x\x9c\xbdTMHUA\x14\xbe\xa8\xf9\xec\xda&\x10\x11*\xb8\x88\x81\x99\xbef\xe6\xce\xbdw\xc6\xf2a\x16E\x1b\x11[%\x98\xcc\xaf\x8f\x8c\xf7\xc0\xf7\x82 (esc)
929 x\x9c\xbdTMHUA\x14\xbe\xa8\xf9\xec\xda&\x10\x11*\xb8\x88\x81\x99\xbef\xe6\xce\xbdw\xc6\xf2a\x16E\x1b\x11[%\x98\xcc\xaf\x8f\x8c\xf7\xc0\xf7\x82 (esc)
930 4\x11KP2m\x95\xad*\xabE\x05AP\xd0\xc22Z\x14\xf9\x03\xb9j\xa3\x9b$\xa4MJ\xb4\x90\xc0\x9a\x9bO0\x10\xdf\x13\xa2\x81\x0f\x869g\xe6|\xe7\x9c\xef\x8ceY\xf7\xa2KO\xd2\xb7K\x16~\\n\xe9\xad\x90w\x86\xab\x93W\x8e\xdf\xb0r\\Y\xee6(\xa2)\xf6\x95\xc6\x01\xe4\x1az\x80R\xe8kN\x98\xe7R\xa4\xa9K@\xe0!A\xb4k\xa7U*m\x03\x07\xd8\x92\x1d\xd2\xc9\xa4\x1d\xc2\xe6,\xa5\xcc+\x1f\xef\xafDgi\xef\xab\x1d\x1d\xb7\x9a\xe7[W\xfbc\x8f\xde-\xcd\xe7\xcaz\xb3\xbb\x19\xd3\x81\x10>c>\x08\x00"X\x11\xc2\x84@\xd2\xe7B*L\x00\x01P\x04R\xc3@\xbaB0\xdb8#\x83:\x83\xa2h\xbc=\xcd\xdaS\xe1Y,L\xd3\xa0\xf2\xa8\x94J:\xe6\xd8\x81Q\xe0\xe8d\xa7#\xe2,\xd1\xaeR*\xed \xa5\x01\x13\x01\xa6\x0cb\xe3;\xbe\xaf\xfcK[^wK\xe1N\xaf\xbbk\xe8B\xd1\xf4\xc1\x07\xb3\xab[\x10\xfdkmvwcB\xa6\xa4\xd4G\xc4D\xc2\x141\xad\x91\x10\x00\x08J\x81\xcb}\xee \xee+W\xba\x8a\x80\x90|\xd4\xa0\xd6\xa0\xd4T\xde\xe1\x9d,!\xe2\xb5\xa94\xe3\xe7\xd5\x9f\x06\x18\xcba\x03aP\xb8f\xcd\x04\x1a_\\9\xf1\xed\xe4\x9e\xe5\xa6\xd1\xd2\x9f\x03\xa7o\xae\x90H\xf3\xfb\xef\xffH3\xadk (esc)
930 4\x11KP2m\x95\xad*\xabE\x05AP\xd0\xc22Z\x14\xf9\x03\xb9j\xa3\x9b$\xa4MJ\xb4\x90\xc0\x9a\x9bO0\x10\xdf\x13\xa2\x81\x0f\x869g\xe6|\xe7\x9c\xef\x8ceY\xf7\xa2KO\xd2\xb7K\x16~\\n\xe9\xad\x90w\x86\xab\x93W\x8e\xdf\xb0r\\Y\xee6(\xa2)\xf6\x95\xc6\x01\xe4\x1az\x80R\xe8kN\x98\xe7R\xa4\xa9K@\xe0!A\xb4k\xa7U*m\x03\x07\xd8\x92\x1d\xd2\xc9\xa4\x1d\xc2\xe6,\xa5\xcc+\x1f\xef\xafDgi\xef\xab\x1d\x1d\xb7\x9a\xe7[W\xfbc\x8f\xde-\xcd\xe7\xcaz\xb3\xbb\x19\xd3\x81\x10>c>\x08\x00"X\x11\xc2\x84@\xd2\xe7B*L\x00\x01P\x04R\xc3@\xbaB0\xdb8#\x83:\x83\xa2h\xbc=\xcd\xdaS\xe1Y,L\xd3\xa0\xf2\xa8\x94J:\xe6\xd8\x81Q\xe0\xe8d\xa7#\xe2,\xd1\xaeR*\xed \xa5\x01\x13\x01\xa6\x0cb\xe3;\xbe\xaf\xfcK[^wK\xe1N\xaf\xbbk\xe8B\xd1\xf4\xc1\x07\xb3\xab[\x10\xfdkmvwcB\xa6\xa4\xd4G\xc4D\xc2\x141\xad\x91\x10\x00\x08J\x81\xcb}\xee \xee+W\xba\x8a\x80\x90|\xd4\xa0\xd6\xa0\xd4T\xde\xe1\x9d,!\xe2\xb5\xa94\xe3\xe7\xd5\x9f\x06\x18\xcba\x03aP\xb8f\xcd\x04\x1a_\\9\xf1\xed\xe4\x9e\xe5\xa6\xd1\xd2\x9f\x03\xa7o\xae\x90H\xf3\xfb\xef\xffH3\xadk (esc)
931 \xb0\x90\x92\x88\xb9\x14"\x068\xc2\x1e@\x00\xbb\x8a)\xd3'\x859 (esc)
931 \xb0\x90\x92\x88\xb9\x14"\x068\xc2\x1e@\x00\xbb\x8a)\xd3'\x859 (esc)
932 \xa8\x80\x84S \xa5\xbd-g\x13`\xe4\xdc\xc3H^\xdf\xe2\xc0TM\xc7\xf4BO\xcf\xde\xae\xe5\xae#\x1frM(K\x97`F\x19\x16s\x05GD\xb9\x01\xc1\x00+\x8c|\x9fp\xc11\xf0\x14\x00\x9cJ\x82<\xe0\x12\x9f\xc1\x90\xd0\xf5\xc8\x19>Pr\xaa\xeaW\xf5\xc4\xae\xd1\xfc\x17\xcf'\x13u\xb1\x9e\xcdHnC\x0e\xcc`\xc8\xa0&\xac\x0e\xf1|\x8c\x10$\xc4\x8c\xa2p\x05`\xdc\x08 \x80\xc4\xd7Rr-\x94\x10\x102\xedi;\xf3f\xf1z\x16\x86\xdb\xd8d\xe5\xe7\x8b\xf5\x8d\rzp\xb2\xfe\xac\xf5\xf2\xd3\xfe\xfckws\xedt\x96b\xd5l\x1c\x0b\x85\xb5\x170\x8f\x11\x84\xb0\x8f\x19\xa0\x00 _\x07\x1ac\xa2\xc3\x89Z\xe7\x96\xf9 \xccNFg\xc7F\xaa\x8a+\x9a\x9cc_\x17\x1b\x17\x9e]z38<\x97+\xb5,",\xc8\xc8?\\\x91\xff\x17.~U\x96\x97\xf5%\xdeN<\x8e\xf5\x97%\xe7^\xcfL\xed~\xda\x96k\xdc->\x86\x02\x83"\x96H\xa6\xe3\xaas=-\xeb7\xe5\xda\x8f\xbc (no-eol) (esc)
932 \xa8\x80\x84S \xa5\xbd-g\x13`\xe4\xdc\xc3H^\xdf\xe2\xc0TM\xc7\xf4BO\xcf\xde\xae\xe5\xae#\x1frM(K\x97`F\x19\x16s\x05GD\xb9\x01\xc1\x00+\x8c|\x9fp\xc11\xf0\x14\x00\x9cJ\x82<\xe0\x12\x9f\xc1\x90\xd0\xf5\xc8\x19>Pr\xaa\xeaW\xf5\xc4\xae\xd1\xfc\x17\xcf'\x13u\xb1\x9e\xcdHnC\x0e\xcc`\xc8\xa0&\xac\x0e\xf1|\x8c\x10$\xc4\x8c\xa2p\x05`\xdc\x08 \x80\xc4\xd7Rr-\x94\x10\x102\xedi;\xf3f\xf1z\x16\x86\xdb\xd8d\xe5\xe7\x8b\xf5\x8d\rzp\xb2\xfe\xac\xf5\xf2\xd3\xfe\xfckws\xedt\x96b\xd5l\x1c\x0b\x85\xb5\x170\x8f\x11\x84\xb0\x8f\x19\xa0\x00 _\x07\x1ac\xa2\xc3\x89Z\xe7\x96\xf9 \xccNFg\xc7F\xaa\x8a+\x9a\x9cc_\x17\x1b\x17\x9e]z38<\x97+\xb5,",\xc8\xc8?\\\x91\xff\x17.~U\x96\x97\xf5%\xdeN<\x8e\xf5\x97%\xe7^\xcfL\xed~\xda\x96k\xdc->\x86\x02\x83"\x96H\xa6\xe3\xaas=-\xeb7\xe5\xda\x8f\xbc (no-eol) (esc)
933
933
934 stream_out
934 stream_out
935
935
936 $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '?cmd=stream_out'
936 $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '?cmd=stream_out'
937 200 Script output follows
937 200 Script output follows
938
938
939 1
939 1
940
940
941 failing unbundle, requires POST request
941 failing unbundle, requires POST request
942
942
943 $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '?cmd=unbundle'
943 $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '?cmd=unbundle'
944 405 push requires POST request
944 405 push requires POST request
945
945
946 0
946 0
947 push requires POST request
947 push requires POST request
948 [1]
948 [1]
949
949
950 Static files
950 Static files
951
951
952 $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/static/style.css'
952 $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/static/style.css'
953 200 Script output follows
953 200 Script output follows
954
954
955 a { text-decoration:none; }
955 a { text-decoration:none; }
956 .age { white-space:nowrap; }
956 .age { white-space:nowrap; }
957 .date { white-space:nowrap; }
957 .date { white-space:nowrap; }
958 .indexlinks { white-space:nowrap; }
958 .indexlinks { white-space:nowrap; }
959 .parity0 { background-color: #ddd; }
959 .parity0 { background-color: #ddd; }
960 .parity1 { background-color: #eee; }
960 .parity1 { background-color: #eee; }
961 .lineno { width: 60px; color: #aaa; font-size: smaller;
961 .lineno { width: 60px; color: #aaa; font-size: smaller;
962 text-align: right; }
962 text-align: right; }
963 .plusline { color: green; }
963 .plusline { color: green; }
964 .minusline { color: red; }
964 .minusline { color: red; }
965 .atline { color: purple; }
965 .atline { color: purple; }
966 .annotate { font-size: smaller; text-align: right; padding-right: 1em; }
966 .annotate { font-size: smaller; text-align: right; padding-right: 1em; }
967 .buttons a {
967 .buttons a {
968 background-color: #666;
968 background-color: #666;
969 padding: 2pt;
969 padding: 2pt;
970 color: white;
970 color: white;
971 font-family: sans;
971 font-family: sans;
972 font-weight: bold;
972 font-weight: bold;
973 }
973 }
974 .navigate a {
974 .navigate a {
975 background-color: #ccc;
975 background-color: #ccc;
976 padding: 2pt;
976 padding: 2pt;
977 font-family: sans;
977 font-family: sans;
978 color: black;
978 color: black;
979 }
979 }
980
980
981 .metatag {
981 .metatag {
982 background-color: #888;
982 background-color: #888;
983 color: white;
983 color: white;
984 text-align: right;
984 text-align: right;
985 }
985 }
986
986
987 /* Common */
987 /* Common */
988 pre { margin: 0; }
988 pre { margin: 0; }
989
989
990 .logo {
990 .logo {
991 float: right;
991 float: right;
992 clear: right;
992 clear: right;
993 }
993 }
994
994
995 /* Changelog/Filelog entries */
995 /* Changelog/Filelog entries */
996 .logEntry { width: 100%; }
996 .logEntry { width: 100%; }
997 .logEntry .age { width: 15%; }
997 .logEntry .age { width: 15%; }
998 .logEntry th { font-weight: normal; text-align: right; vertical-align: top; }
998 .logEntry th { font-weight: normal; text-align: right; vertical-align: top; }
999 .logEntry th.age, .logEntry th.firstline { font-weight: bold; }
999 .logEntry th.age, .logEntry th.firstline { font-weight: bold; }
1000 .logEntry th.firstline { text-align: left; width: inherit; }
1000 .logEntry th.firstline { text-align: left; width: inherit; }
1001
1001
1002 /* Shortlog entries */
1002 /* Shortlog entries */
1003 .slogEntry { width: 100%; }
1003 .slogEntry { width: 100%; }
1004 .slogEntry .age { width: 8em; }
1004 .slogEntry .age { width: 8em; }
1005 .slogEntry td { font-weight: normal; text-align: left; vertical-align: top; }
1005 .slogEntry td { font-weight: normal; text-align: left; vertical-align: top; }
1006 .slogEntry td.author { width: 15em; }
1006 .slogEntry td.author { width: 15em; }
1007
1007
1008 /* Tag entries */
1008 /* Tag entries */
1009 #tagEntries { list-style: none; margin: 0; padding: 0; }
1009 #tagEntries { list-style: none; margin: 0; padding: 0; }
1010 #tagEntries .tagEntry { list-style: none; margin: 0; padding: 0; }
1010 #tagEntries .tagEntry { list-style: none; margin: 0; padding: 0; }
1011
1011
1012 /* Changeset entry */
1012 /* Changeset entry */
1013 #changesetEntry { }
1013 #changesetEntry { }
1014 #changesetEntry th { font-weight: normal; background-color: #888; color: #fff; text-align: right; }
1014 #changesetEntry th { font-weight: normal; background-color: #888; color: #fff; text-align: right; }
1015 #changesetEntry th.files, #changesetEntry th.description { vertical-align: top; }
1015 #changesetEntry th.files, #changesetEntry th.description { vertical-align: top; }
1016
1016
1017 /* File diff view */
1017 /* File diff view */
1018 #filediffEntry { }
1018 #filediffEntry { }
1019 #filediffEntry th { font-weight: normal; background-color: #888; color: #fff; text-align: right; }
1019 #filediffEntry th { font-weight: normal; background-color: #888; color: #fff; text-align: right; }
1020
1020
1021 /* Graph */
1021 /* Graph */
1022 div#wrapper {
1022 div#wrapper {
1023 position: relative;
1023 position: relative;
1024 margin: 0;
1024 margin: 0;
1025 padding: 0;
1025 padding: 0;
1026 }
1026 }
1027
1027
1028 canvas {
1028 canvas {
1029 position: absolute;
1029 position: absolute;
1030 z-index: 5;
1030 z-index: 5;
1031 top: -0.6em;
1031 top: -0.6em;
1032 margin: 0;
1032 margin: 0;
1033 }
1033 }
1034
1034
1035 ul#nodebgs {
1035 ul#nodebgs {
1036 list-style: none inside none;
1036 list-style: none inside none;
1037 padding: 0;
1037 padding: 0;
1038 margin: 0;
1038 margin: 0;
1039 top: -0.7em;
1039 top: -0.7em;
1040 }
1040 }
1041
1041
1042 ul#graphnodes li, ul#nodebgs li {
1042 ul#graphnodes li, ul#nodebgs li {
1043 height: 39px;
1043 height: 39px;
1044 }
1044 }
1045
1045
1046 ul#graphnodes {
1046 ul#graphnodes {
1047 position: absolute;
1047 position: absolute;
1048 z-index: 10;
1048 z-index: 10;
1049 top: -0.85em;
1049 top: -0.85em;
1050 list-style: none inside none;
1050 list-style: none inside none;
1051 padding: 0;
1051 padding: 0;
1052 }
1052 }
1053
1053
1054 ul#graphnodes li .info {
1054 ul#graphnodes li .info {
1055 display: block;
1055 display: block;
1056 font-size: 70%;
1056 font-size: 70%;
1057 position: relative;
1057 position: relative;
1058 top: -1px;
1058 top: -1px;
1059 }
1059 }
1060
1060
1061 Stop and restart with HGENCODING=cp932
1061 Stop and restart with HGENCODING=cp932
1062
1062
1063 $ "$TESTDIR/killdaemons.py"
1063 $ "$TESTDIR/killdaemons.py"
1064 $ HGENCODING=cp932 hg serve --config server.uncompressed=False -n test \
1064 $ HGENCODING=cp932 hg serve --config server.uncompressed=False -n test \
1065 > -p $HGPORT -d --pid-file=hg.pid -E errors.log
1065 > -p $HGPORT -d --pid-file=hg.pid -E errors.log
1066 $ cat hg.pid >> $DAEMON_PIDS
1066 $ cat hg.pid >> $DAEMON_PIDS
1067
1067
1068 commit message with Japanese Kanji 'Noh', which ends with '\x5c'
1068 commit message with Japanese Kanji 'Noh', which ends with '\x5c'
1069
1069
1070 $ echo foo >> foo
1070 $ echo foo >> foo
1071 $ HGENCODING=cp932 hg ci -m `python -c 'print("\x94\x5c")'`
1071 $ HGENCODING=cp932 hg ci -m `python -c 'print("\x94\x5c")'`
1072
1072
1073 Graph json escape of multibyte character
1073 Graph json escape of multibyte character
1074
1074
1075 $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/graph/' \
1075 $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/graph/' \
1076 > | grep '^var data ='
1076 > | grep '^var data ='
1077 var data = [["40b4d6888e92", [0, 1], [[0, 0, 1]], "\u80fd", "test", "1970-01-01", ["stable", true], ["tip"], ["something"]], ["1d22e65f027e", [0, 1], [[0, 0, 1]], "branch", "test", "1970-01-01", ["stable", false], [], []], ["a4f92ed23982", [0, 1], [[0, 0, 1]], "Added tag 1.0 for changeset 2ef0ac749a14", "test", "1970-01-01", ["default", true], [], []], ["2ef0ac749a14", [0, 1], [], "base", "test", "1970-01-01", ["default", false], ["1.0"], []]];
1077 var data = [["40b4d6888e92", [0, 1], [[0, 0, 1]], "\u80fd", "test", "1970-01-01", ["stable", true], ["tip"], ["something"]], ["1d22e65f027e", [0, 1], [[0, 0, 1]], "branch", "test", "1970-01-01", ["stable", false], [], []], ["a4f92ed23982", [0, 1], [[0, 0, 1]], "Added tag 1.0 for changeset 2ef0ac749a14", "test", "1970-01-01", ["default", true], [], []], ["2ef0ac749a14", [0, 1], [], "base", "test", "1970-01-01", ["default", false], ["1.0"], []]];
1078
1078
1079 ERRORS ENCOUNTERED
1079 ERRORS ENCOUNTERED
1080
1080
1081 $ cat errors.log
1081 $ cat errors.log
General Comments 0
You need to be logged in to leave comments. Login now