##// END OF EJS Templates
Extend/correct acc40572da5b regarding -qA and ignored files....
Thomas Arendsen Hein -
r6201:305d4450 default
parent child Browse files
Show More
@@ -1,3176 +1,3179 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
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 from node import *
8 from node import *
9 from i18n import _
9 from i18n import _
10 import os, re, sys, urllib
10 import os, re, sys, urllib
11 import hg, util, revlog, bundlerepo, extensions
11 import hg, util, revlog, bundlerepo, extensions
12 import difflib, patch, time, help, mdiff, tempfile
12 import difflib, patch, time, help, mdiff, tempfile
13 import errno, version, socket
13 import errno, version, socket
14 import archival, changegroup, cmdutil, hgweb.server, sshserver, hbisect
14 import archival, changegroup, cmdutil, hgweb.server, sshserver, hbisect
15
15
16 # Commands start here, listed alphabetically
16 # Commands start here, listed alphabetically
17
17
18 def add(ui, repo, *pats, **opts):
18 def add(ui, repo, *pats, **opts):
19 """add the specified files on the next commit
19 """add the specified files on the next commit
20
20
21 Schedule files to be version controlled and added to the repository.
21 Schedule files to be version controlled and added to the repository.
22
22
23 The files will be added to the repository at the next commit. To
23 The files will be added to the repository at the next commit. To
24 undo an add before that, see hg revert.
24 undo an add before that, see hg revert.
25
25
26 If no names are given, add all files in the repository.
26 If no names are given, add all files in the repository.
27 """
27 """
28
28
29 rejected = None
29 rejected = None
30 exacts = {}
30 exacts = {}
31 names = []
31 names = []
32 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
32 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
33 badmatch=util.always):
33 badmatch=util.always):
34 if exact:
34 if exact:
35 if ui.verbose:
35 if ui.verbose:
36 ui.status(_('adding %s\n') % rel)
36 ui.status(_('adding %s\n') % rel)
37 names.append(abs)
37 names.append(abs)
38 exacts[abs] = 1
38 exacts[abs] = 1
39 elif abs not in repo.dirstate:
39 elif abs not in repo.dirstate:
40 ui.status(_('adding %s\n') % rel)
40 ui.status(_('adding %s\n') % rel)
41 names.append(abs)
41 names.append(abs)
42 if not opts.get('dry_run'):
42 if not opts.get('dry_run'):
43 rejected = repo.add(names)
43 rejected = repo.add(names)
44 rejected = [p for p in rejected if p in exacts]
44 rejected = [p for p in rejected if p in exacts]
45 return rejected and 1 or 0
45 return rejected and 1 or 0
46
46
47 def addremove(ui, repo, *pats, **opts):
47 def addremove(ui, repo, *pats, **opts):
48 """add all new files, delete all missing files
48 """add all new files, delete all missing files
49
49
50 Add all new files and remove all missing files from the repository.
50 Add all new files and remove all missing files from the repository.
51
51
52 New files are ignored if they match any of the patterns in .hgignore. As
52 New files are ignored if they match any of the patterns in .hgignore. As
53 with add, these changes take effect at the next commit.
53 with add, these changes take effect at the next commit.
54
54
55 Use the -s option to detect renamed files. With a parameter > 0,
55 Use the -s option to detect renamed files. With a parameter > 0,
56 this compares every removed file with every added file and records
56 this compares every removed file with every added file and records
57 those similar enough as renames. This option takes a percentage
57 those similar enough as renames. This option takes a percentage
58 between 0 (disabled) and 100 (files must be identical) as its
58 between 0 (disabled) and 100 (files must be identical) as its
59 parameter. Detecting renamed files this way can be expensive.
59 parameter. Detecting renamed files this way can be expensive.
60 """
60 """
61 try:
61 try:
62 sim = float(opts.get('similarity') or 0)
62 sim = float(opts.get('similarity') or 0)
63 except ValueError:
63 except ValueError:
64 raise util.Abort(_('similarity must be a number'))
64 raise util.Abort(_('similarity must be a number'))
65 if sim < 0 or sim > 100:
65 if sim < 0 or sim > 100:
66 raise util.Abort(_('similarity must be between 0 and 100'))
66 raise util.Abort(_('similarity must be between 0 and 100'))
67 return cmdutil.addremove(repo, pats, opts, similarity=sim/100.)
67 return cmdutil.addremove(repo, pats, opts, similarity=sim/100.)
68
68
69 def annotate(ui, repo, *pats, **opts):
69 def annotate(ui, repo, *pats, **opts):
70 """show changeset information per file line
70 """show changeset information per file line
71
71
72 List changes in files, showing the revision id responsible for each line
72 List changes in files, showing the revision id responsible for each line
73
73
74 This command is useful to discover who did a change or when a change took
74 This command is useful to discover who did a change or when a change took
75 place.
75 place.
76
76
77 Without the -a option, annotate will avoid processing files it
77 Without the -a option, annotate will avoid processing files it
78 detects as binary. With -a, annotate will generate an annotation
78 detects as binary. With -a, annotate will generate an annotation
79 anyway, probably with undesirable results.
79 anyway, probably with undesirable results.
80 """
80 """
81 datefunc = ui.quiet and util.shortdate or util.datestr
81 datefunc = ui.quiet and util.shortdate or util.datestr
82 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
82 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
83
83
84 if not pats:
84 if not pats:
85 raise util.Abort(_('at least one file name or pattern required'))
85 raise util.Abort(_('at least one file name or pattern required'))
86
86
87 opmap = [('user', lambda x: ui.shortuser(x[0].user())),
87 opmap = [('user', lambda x: ui.shortuser(x[0].user())),
88 ('number', lambda x: str(x[0].rev())),
88 ('number', lambda x: str(x[0].rev())),
89 ('changeset', lambda x: short(x[0].node())),
89 ('changeset', lambda x: short(x[0].node())),
90 ('date', getdate),
90 ('date', getdate),
91 ('follow', lambda x: x[0].path()),
91 ('follow', lambda x: x[0].path()),
92 ]
92 ]
93
93
94 if (not opts['user'] and not opts['changeset'] and not opts['date']
94 if (not opts['user'] and not opts['changeset'] and not opts['date']
95 and not opts['follow']):
95 and not opts['follow']):
96 opts['number'] = 1
96 opts['number'] = 1
97
97
98 linenumber = opts.get('line_number') is not None
98 linenumber = opts.get('line_number') is not None
99 if (linenumber and (not opts['changeset']) and (not opts['number'])):
99 if (linenumber and (not opts['changeset']) and (not opts['number'])):
100 raise util.Abort(_('at least one of -n/-c is required for -l'))
100 raise util.Abort(_('at least one of -n/-c is required for -l'))
101
101
102 funcmap = [func for op, func in opmap if opts.get(op)]
102 funcmap = [func for op, func in opmap if opts.get(op)]
103 if linenumber:
103 if linenumber:
104 lastfunc = funcmap[-1]
104 lastfunc = funcmap[-1]
105 funcmap[-1] = lambda x: "%s:%s" % (lastfunc(x), x[1])
105 funcmap[-1] = lambda x: "%s:%s" % (lastfunc(x), x[1])
106
106
107 ctx = repo.changectx(opts['rev'])
107 ctx = repo.changectx(opts['rev'])
108
108
109 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
109 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
110 node=ctx.node()):
110 node=ctx.node()):
111 fctx = ctx.filectx(abs)
111 fctx = ctx.filectx(abs)
112 if not opts['text'] and util.binary(fctx.data()):
112 if not opts['text'] and util.binary(fctx.data()):
113 ui.write(_("%s: binary file\n") % ((pats and rel) or abs))
113 ui.write(_("%s: binary file\n") % ((pats and rel) or abs))
114 continue
114 continue
115
115
116 lines = fctx.annotate(follow=opts.get('follow'),
116 lines = fctx.annotate(follow=opts.get('follow'),
117 linenumber=linenumber)
117 linenumber=linenumber)
118 pieces = []
118 pieces = []
119
119
120 for f in funcmap:
120 for f in funcmap:
121 l = [f(n) for n, dummy in lines]
121 l = [f(n) for n, dummy in lines]
122 if l:
122 if l:
123 m = max(map(len, l))
123 m = max(map(len, l))
124 pieces.append(["%*s" % (m, x) for x in l])
124 pieces.append(["%*s" % (m, x) for x in l])
125
125
126 if pieces:
126 if pieces:
127 for p, l in zip(zip(*pieces), lines):
127 for p, l in zip(zip(*pieces), lines):
128 ui.write("%s: %s" % (" ".join(p), l[1]))
128 ui.write("%s: %s" % (" ".join(p), l[1]))
129
129
130 def archive(ui, repo, dest, **opts):
130 def archive(ui, repo, dest, **opts):
131 '''create unversioned archive of a repository revision
131 '''create unversioned archive of a repository revision
132
132
133 By default, the revision used is the parent of the working
133 By default, the revision used is the parent of the working
134 directory; use "-r" to specify a different revision.
134 directory; use "-r" to specify a different revision.
135
135
136 To specify the type of archive to create, use "-t". Valid
136 To specify the type of archive to create, use "-t". Valid
137 types are:
137 types are:
138
138
139 "files" (default): a directory full of files
139 "files" (default): a directory full of files
140 "tar": tar archive, uncompressed
140 "tar": tar archive, uncompressed
141 "tbz2": tar archive, compressed using bzip2
141 "tbz2": tar archive, compressed using bzip2
142 "tgz": tar archive, compressed using gzip
142 "tgz": tar archive, compressed using gzip
143 "uzip": zip archive, uncompressed
143 "uzip": zip archive, uncompressed
144 "zip": zip archive, compressed using deflate
144 "zip": zip archive, compressed using deflate
145
145
146 The exact name of the destination archive or directory is given
146 The exact name of the destination archive or directory is given
147 using a format string; see "hg help export" for details.
147 using a format string; see "hg help export" for details.
148
148
149 Each member added to an archive file has a directory prefix
149 Each member added to an archive file has a directory prefix
150 prepended. Use "-p" to specify a format string for the prefix.
150 prepended. Use "-p" to specify a format string for the prefix.
151 The default is the basename of the archive, with suffixes removed.
151 The default is the basename of the archive, with suffixes removed.
152 '''
152 '''
153
153
154 ctx = repo.changectx(opts['rev'])
154 ctx = repo.changectx(opts['rev'])
155 if not ctx:
155 if not ctx:
156 raise util.Abort(_('repository has no revisions'))
156 raise util.Abort(_('repository has no revisions'))
157 node = ctx.node()
157 node = ctx.node()
158 dest = cmdutil.make_filename(repo, dest, node)
158 dest = cmdutil.make_filename(repo, dest, node)
159 if os.path.realpath(dest) == repo.root:
159 if os.path.realpath(dest) == repo.root:
160 raise util.Abort(_('repository root cannot be destination'))
160 raise util.Abort(_('repository root cannot be destination'))
161 dummy, matchfn, dummy = cmdutil.matchpats(repo, [], opts)
161 dummy, matchfn, dummy = cmdutil.matchpats(repo, [], opts)
162 kind = opts.get('type') or 'files'
162 kind = opts.get('type') or 'files'
163 prefix = opts['prefix']
163 prefix = opts['prefix']
164 if dest == '-':
164 if dest == '-':
165 if kind == 'files':
165 if kind == 'files':
166 raise util.Abort(_('cannot archive plain files to stdout'))
166 raise util.Abort(_('cannot archive plain files to stdout'))
167 dest = sys.stdout
167 dest = sys.stdout
168 if not prefix: prefix = os.path.basename(repo.root) + '-%h'
168 if not prefix: prefix = os.path.basename(repo.root) + '-%h'
169 prefix = cmdutil.make_filename(repo, prefix, node)
169 prefix = cmdutil.make_filename(repo, prefix, node)
170 archival.archive(repo, dest, node, kind, not opts['no_decode'],
170 archival.archive(repo, dest, node, kind, not opts['no_decode'],
171 matchfn, prefix)
171 matchfn, prefix)
172
172
173 def backout(ui, repo, node=None, rev=None, **opts):
173 def backout(ui, repo, node=None, rev=None, **opts):
174 '''reverse effect of earlier changeset
174 '''reverse effect of earlier changeset
175
175
176 Commit the backed out changes as a new changeset. The new
176 Commit the backed out changes as a new changeset. The new
177 changeset is a child of the backed out changeset.
177 changeset is a child of the backed out changeset.
178
178
179 If you back out a changeset other than the tip, a new head is
179 If you back out a changeset other than the tip, a new head is
180 created. This head will be the new tip and you should merge this
180 created. This head will be the new tip and you should merge this
181 backout changeset with another head (current one by default).
181 backout changeset with another head (current one by default).
182
182
183 The --merge option remembers the parent of the working directory
183 The --merge option remembers the parent of the working directory
184 before starting the backout, then merges the new head with that
184 before starting the backout, then merges the new head with that
185 changeset afterwards. This saves you from doing the merge by
185 changeset afterwards. This saves you from doing the merge by
186 hand. The result of this merge is not committed, as for a normal
186 hand. The result of this merge is not committed, as for a normal
187 merge.
187 merge.
188
188
189 See 'hg help dates' for a list of formats valid for -d/--date.
189 See 'hg help dates' for a list of formats valid for -d/--date.
190 '''
190 '''
191 if rev and node:
191 if rev and node:
192 raise util.Abort(_("please specify just one revision"))
192 raise util.Abort(_("please specify just one revision"))
193
193
194 if not rev:
194 if not rev:
195 rev = node
195 rev = node
196
196
197 if not rev:
197 if not rev:
198 raise util.Abort(_("please specify a revision to backout"))
198 raise util.Abort(_("please specify a revision to backout"))
199
199
200 date = opts.get('date')
200 date = opts.get('date')
201 if date:
201 if date:
202 opts['date'] = util.parsedate(date)
202 opts['date'] = util.parsedate(date)
203
203
204 cmdutil.bail_if_changed(repo)
204 cmdutil.bail_if_changed(repo)
205 node = repo.lookup(rev)
205 node = repo.lookup(rev)
206
206
207 op1, op2 = repo.dirstate.parents()
207 op1, op2 = repo.dirstate.parents()
208 a = repo.changelog.ancestor(op1, node)
208 a = repo.changelog.ancestor(op1, node)
209 if a != node:
209 if a != node:
210 raise util.Abort(_('cannot back out change on a different branch'))
210 raise util.Abort(_('cannot back out change on a different branch'))
211
211
212 p1, p2 = repo.changelog.parents(node)
212 p1, p2 = repo.changelog.parents(node)
213 if p1 == nullid:
213 if p1 == nullid:
214 raise util.Abort(_('cannot back out a change with no parents'))
214 raise util.Abort(_('cannot back out a change with no parents'))
215 if p2 != nullid:
215 if p2 != nullid:
216 if not opts['parent']:
216 if not opts['parent']:
217 raise util.Abort(_('cannot back out a merge changeset without '
217 raise util.Abort(_('cannot back out a merge changeset without '
218 '--parent'))
218 '--parent'))
219 p = repo.lookup(opts['parent'])
219 p = repo.lookup(opts['parent'])
220 if p not in (p1, p2):
220 if p not in (p1, p2):
221 raise util.Abort(_('%s is not a parent of %s') %
221 raise util.Abort(_('%s is not a parent of %s') %
222 (short(p), short(node)))
222 (short(p), short(node)))
223 parent = p
223 parent = p
224 else:
224 else:
225 if opts['parent']:
225 if opts['parent']:
226 raise util.Abort(_('cannot use --parent on non-merge changeset'))
226 raise util.Abort(_('cannot use --parent on non-merge changeset'))
227 parent = p1
227 parent = p1
228
228
229 hg.clean(repo, node, show_stats=False)
229 hg.clean(repo, node, show_stats=False)
230 revert_opts = opts.copy()
230 revert_opts = opts.copy()
231 revert_opts['date'] = None
231 revert_opts['date'] = None
232 revert_opts['all'] = True
232 revert_opts['all'] = True
233 revert_opts['rev'] = hex(parent)
233 revert_opts['rev'] = hex(parent)
234 revert_opts['no_backup'] = None
234 revert_opts['no_backup'] = None
235 revert(ui, repo, **revert_opts)
235 revert(ui, repo, **revert_opts)
236 commit_opts = opts.copy()
236 commit_opts = opts.copy()
237 commit_opts['addremove'] = False
237 commit_opts['addremove'] = False
238 if not commit_opts['message'] and not commit_opts['logfile']:
238 if not commit_opts['message'] and not commit_opts['logfile']:
239 commit_opts['message'] = _("Backed out changeset %s") % (short(node))
239 commit_opts['message'] = _("Backed out changeset %s") % (short(node))
240 commit_opts['force_editor'] = True
240 commit_opts['force_editor'] = True
241 commit(ui, repo, **commit_opts)
241 commit(ui, repo, **commit_opts)
242 def nice(node):
242 def nice(node):
243 return '%d:%s' % (repo.changelog.rev(node), short(node))
243 return '%d:%s' % (repo.changelog.rev(node), short(node))
244 ui.status(_('changeset %s backs out changeset %s\n') %
244 ui.status(_('changeset %s backs out changeset %s\n') %
245 (nice(repo.changelog.tip()), nice(node)))
245 (nice(repo.changelog.tip()), nice(node)))
246 if op1 != node:
246 if op1 != node:
247 hg.clean(repo, op1, show_stats=False)
247 hg.clean(repo, op1, show_stats=False)
248 if opts['merge']:
248 if opts['merge']:
249 ui.status(_('merging with changeset %s\n') % nice(repo.changelog.tip()))
249 ui.status(_('merging with changeset %s\n') % nice(repo.changelog.tip()))
250 hg.merge(repo, hex(repo.changelog.tip()))
250 hg.merge(repo, hex(repo.changelog.tip()))
251 else:
251 else:
252 ui.status(_('the backout changeset is a new head - '
252 ui.status(_('the backout changeset is a new head - '
253 'do not forget to merge\n'))
253 'do not forget to merge\n'))
254 ui.status(_('(use "backout --merge" '
254 ui.status(_('(use "backout --merge" '
255 'if you want to auto-merge)\n'))
255 'if you want to auto-merge)\n'))
256
256
257 def bisect(ui, repo, rev=None, extra=None,
257 def bisect(ui, repo, rev=None, extra=None,
258 reset=None, good=None, bad=None, skip=None, noupdate=None):
258 reset=None, good=None, bad=None, skip=None, noupdate=None):
259 """subdivision search of changesets
259 """subdivision search of changesets
260
260
261 This command helps to find changesets which introduce problems.
261 This command helps to find changesets which introduce problems.
262 To use, mark the earliest changeset you know exhibits the problem
262 To use, mark the earliest changeset you know exhibits the problem
263 as bad, then mark the latest changeset which is free from the
263 as bad, then mark the latest changeset which is free from the
264 problem as good. Bisect will update your working directory to a
264 problem as good. Bisect will update your working directory to a
265 revision for testing. Once you have performed tests, mark the
265 revision for testing. Once you have performed tests, mark the
266 working directory as bad or good and bisect will either update to
266 working directory as bad or good and bisect will either update to
267 another candidate changeset or announce that it has found the bad
267 another candidate changeset or announce that it has found the bad
268 revision.
268 revision.
269 """
269 """
270 # backward compatibility
270 # backward compatibility
271 if rev in "good bad reset init".split():
271 if rev in "good bad reset init".split():
272 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
272 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
273 cmd, rev, extra = rev, extra, None
273 cmd, rev, extra = rev, extra, None
274 if cmd == "good":
274 if cmd == "good":
275 good = True
275 good = True
276 elif cmd == "bad":
276 elif cmd == "bad":
277 bad = True
277 bad = True
278 else:
278 else:
279 reset = True
279 reset = True
280 elif extra or good + bad + skip + reset > 1:
280 elif extra or good + bad + skip + reset > 1:
281 raise util.Abort("Incompatible arguments")
281 raise util.Abort("Incompatible arguments")
282
282
283 if reset:
283 if reset:
284 p = repo.join("bisect.state")
284 p = repo.join("bisect.state")
285 if os.path.exists(p):
285 if os.path.exists(p):
286 os.unlink(p)
286 os.unlink(p)
287 return
287 return
288
288
289 # load state
289 # load state
290 state = {'good': [], 'bad': [], 'skip': []}
290 state = {'good': [], 'bad': [], 'skip': []}
291 if os.path.exists(repo.join("bisect.state")):
291 if os.path.exists(repo.join("bisect.state")):
292 for l in repo.opener("bisect.state"):
292 for l in repo.opener("bisect.state"):
293 kind, node = l[:-1].split()
293 kind, node = l[:-1].split()
294 node = repo.lookup(node)
294 node = repo.lookup(node)
295 if kind not in state:
295 if kind not in state:
296 raise util.Abort(_("unknown bisect kind %s") % kind)
296 raise util.Abort(_("unknown bisect kind %s") % kind)
297 state[kind].append(node)
297 state[kind].append(node)
298
298
299 # update state
299 # update state
300 node = repo.lookup(rev or '.')
300 node = repo.lookup(rev or '.')
301 if good:
301 if good:
302 state['good'].append(node)
302 state['good'].append(node)
303 elif bad:
303 elif bad:
304 state['bad'].append(node)
304 state['bad'].append(node)
305 elif skip:
305 elif skip:
306 state['skip'].append(node)
306 state['skip'].append(node)
307
307
308 # save state
308 # save state
309 f = repo.opener("bisect.state", "w", atomictemp=True)
309 f = repo.opener("bisect.state", "w", atomictemp=True)
310 wlock = repo.wlock()
310 wlock = repo.wlock()
311 try:
311 try:
312 for kind in state:
312 for kind in state:
313 for node in state[kind]:
313 for node in state[kind]:
314 f.write("%s %s\n" % (kind, hg.hex(node)))
314 f.write("%s %s\n" % (kind, hg.hex(node)))
315 f.rename()
315 f.rename()
316 finally:
316 finally:
317 del wlock
317 del wlock
318
318
319 if not state['good'] or not state['bad']:
319 if not state['good'] or not state['bad']:
320 return
320 return
321
321
322 # actually bisect
322 # actually bisect
323 node, changesets, good = hbisect.bisect(repo.changelog, state)
323 node, changesets, good = hbisect.bisect(repo.changelog, state)
324 if changesets == 0:
324 if changesets == 0:
325 ui.write(_("The first %s revision is:\n") % (good and "good" or "bad"))
325 ui.write(_("The first %s revision is:\n") % (good and "good" or "bad"))
326 displayer = cmdutil.show_changeset(ui, repo, {})
326 displayer = cmdutil.show_changeset(ui, repo, {})
327 displayer.show(changenode=node)
327 displayer.show(changenode=node)
328 elif node is not None:
328 elif node is not None:
329 # compute the approximate number of remaining tests
329 # compute the approximate number of remaining tests
330 tests, size = 0, 2
330 tests, size = 0, 2
331 while size <= changesets:
331 while size <= changesets:
332 tests, size = tests + 1, size * 2
332 tests, size = tests + 1, size * 2
333 rev = repo.changelog.rev(node)
333 rev = repo.changelog.rev(node)
334 ui.write(_("Testing changeset %s:%s "
334 ui.write(_("Testing changeset %s:%s "
335 "(%s changesets remaining, ~%s tests)\n")
335 "(%s changesets remaining, ~%s tests)\n")
336 % (rev, hg.short(node), changesets, tests))
336 % (rev, hg.short(node), changesets, tests))
337 if not noupdate:
337 if not noupdate:
338 cmdutil.bail_if_changed(repo)
338 cmdutil.bail_if_changed(repo)
339 return hg.clean(repo, node)
339 return hg.clean(repo, node)
340
340
341 def branch(ui, repo, label=None, **opts):
341 def branch(ui, repo, label=None, **opts):
342 """set or show the current branch name
342 """set or show the current branch name
343
343
344 With no argument, show the current branch name. With one argument,
344 With no argument, show the current branch name. With one argument,
345 set the working directory branch name (the branch does not exist in
345 set the working directory branch name (the branch does not exist in
346 the repository until the next commit).
346 the repository until the next commit).
347
347
348 Unless --force is specified, branch will not let you set a
348 Unless --force is specified, branch will not let you set a
349 branch name that shadows an existing branch.
349 branch name that shadows an existing branch.
350
350
351 Use the command 'hg update' to switch to an existing branch.
351 Use the command 'hg update' to switch to an existing branch.
352 """
352 """
353
353
354 if label:
354 if label:
355 if not opts.get('force') and label in repo.branchtags():
355 if not opts.get('force') and label in repo.branchtags():
356 if label not in [p.branch() for p in repo.workingctx().parents()]:
356 if label not in [p.branch() for p in repo.workingctx().parents()]:
357 raise util.Abort(_('a branch of the same name already exists'
357 raise util.Abort(_('a branch of the same name already exists'
358 ' (use --force to override)'))
358 ' (use --force to override)'))
359 repo.dirstate.setbranch(util.fromlocal(label))
359 repo.dirstate.setbranch(util.fromlocal(label))
360 ui.status(_('marked working directory as branch %s\n') % label)
360 ui.status(_('marked working directory as branch %s\n') % label)
361 else:
361 else:
362 ui.write("%s\n" % util.tolocal(repo.dirstate.branch()))
362 ui.write("%s\n" % util.tolocal(repo.dirstate.branch()))
363
363
364 def branches(ui, repo, active=False):
364 def branches(ui, repo, active=False):
365 """list repository named branches
365 """list repository named branches
366
366
367 List the repository's named branches, indicating which ones are
367 List the repository's named branches, indicating which ones are
368 inactive. If active is specified, only show active branches.
368 inactive. If active is specified, only show active branches.
369
369
370 A branch is considered active if it contains unmerged heads.
370 A branch is considered active if it contains unmerged heads.
371
371
372 Use the command 'hg update' to switch to an existing branch.
372 Use the command 'hg update' to switch to an existing branch.
373 """
373 """
374 b = repo.branchtags()
374 b = repo.branchtags()
375 heads = dict.fromkeys(repo.heads(), 1)
375 heads = dict.fromkeys(repo.heads(), 1)
376 l = [((n in heads), repo.changelog.rev(n), n, t) for t, n in b.items()]
376 l = [((n in heads), repo.changelog.rev(n), n, t) for t, n in b.items()]
377 l.sort()
377 l.sort()
378 l.reverse()
378 l.reverse()
379 for ishead, r, n, t in l:
379 for ishead, r, n, t in l:
380 if active and not ishead:
380 if active and not ishead:
381 # If we're only displaying active branches, abort the loop on
381 # If we're only displaying active branches, abort the loop on
382 # encountering the first inactive head
382 # encountering the first inactive head
383 break
383 break
384 else:
384 else:
385 hexfunc = ui.debugflag and hex or short
385 hexfunc = ui.debugflag and hex or short
386 if ui.quiet:
386 if ui.quiet:
387 ui.write("%s\n" % t)
387 ui.write("%s\n" % t)
388 else:
388 else:
389 spaces = " " * (30 - util.locallen(t))
389 spaces = " " * (30 - util.locallen(t))
390 # The code only gets here if inactive branches are being
390 # The code only gets here if inactive branches are being
391 # displayed or the branch is active.
391 # displayed or the branch is active.
392 isinactive = ((not ishead) and " (inactive)") or ''
392 isinactive = ((not ishead) and " (inactive)") or ''
393 ui.write("%s%s %s:%s%s\n" % (t, spaces, r, hexfunc(n), isinactive))
393 ui.write("%s%s %s:%s%s\n" % (t, spaces, r, hexfunc(n), isinactive))
394
394
395 def bundle(ui, repo, fname, dest=None, **opts):
395 def bundle(ui, repo, fname, dest=None, **opts):
396 """create a changegroup file
396 """create a changegroup file
397
397
398 Generate a compressed changegroup file collecting changesets not
398 Generate a compressed changegroup file collecting changesets not
399 found in the other repository.
399 found in the other repository.
400
400
401 If no destination repository is specified the destination is
401 If no destination repository is specified the destination is
402 assumed to have all the nodes specified by one or more --base
402 assumed to have all the nodes specified by one or more --base
403 parameters. To create a bundle containing all changesets, use
403 parameters. To create a bundle containing all changesets, use
404 --all (or --base null).
404 --all (or --base null).
405
405
406 The bundle file can then be transferred using conventional means and
406 The bundle file can then be transferred using conventional means and
407 applied to another repository with the unbundle or pull command.
407 applied to another repository with the unbundle or pull command.
408 This is useful when direct push and pull are not available or when
408 This is useful when direct push and pull are not available or when
409 exporting an entire repository is undesirable.
409 exporting an entire repository is undesirable.
410
410
411 Applying bundles preserves all changeset contents including
411 Applying bundles preserves all changeset contents including
412 permissions, copy/rename information, and revision history.
412 permissions, copy/rename information, and revision history.
413 """
413 """
414 revs = opts.get('rev') or None
414 revs = opts.get('rev') or None
415 if revs:
415 if revs:
416 revs = [repo.lookup(rev) for rev in revs]
416 revs = [repo.lookup(rev) for rev in revs]
417 if opts.get('all'):
417 if opts.get('all'):
418 base = ['null']
418 base = ['null']
419 else:
419 else:
420 base = opts.get('base')
420 base = opts.get('base')
421 if base:
421 if base:
422 if dest:
422 if dest:
423 raise util.Abort(_("--base is incompatible with specifiying "
423 raise util.Abort(_("--base is incompatible with specifiying "
424 "a destination"))
424 "a destination"))
425 base = [repo.lookup(rev) for rev in base]
425 base = [repo.lookup(rev) for rev in base]
426 # create the right base
426 # create the right base
427 # XXX: nodesbetween / changegroup* should be "fixed" instead
427 # XXX: nodesbetween / changegroup* should be "fixed" instead
428 o = []
428 o = []
429 has = {nullid: None}
429 has = {nullid: None}
430 for n in base:
430 for n in base:
431 has.update(repo.changelog.reachable(n))
431 has.update(repo.changelog.reachable(n))
432 if revs:
432 if revs:
433 visit = list(revs)
433 visit = list(revs)
434 else:
434 else:
435 visit = repo.changelog.heads()
435 visit = repo.changelog.heads()
436 seen = {}
436 seen = {}
437 while visit:
437 while visit:
438 n = visit.pop(0)
438 n = visit.pop(0)
439 parents = [p for p in repo.changelog.parents(n) if p not in has]
439 parents = [p for p in repo.changelog.parents(n) if p not in has]
440 if len(parents) == 0:
440 if len(parents) == 0:
441 o.insert(0, n)
441 o.insert(0, n)
442 else:
442 else:
443 for p in parents:
443 for p in parents:
444 if p not in seen:
444 if p not in seen:
445 seen[p] = 1
445 seen[p] = 1
446 visit.append(p)
446 visit.append(p)
447 else:
447 else:
448 cmdutil.setremoteconfig(ui, opts)
448 cmdutil.setremoteconfig(ui, opts)
449 dest, revs, checkout = hg.parseurl(
449 dest, revs, checkout = hg.parseurl(
450 ui.expandpath(dest or 'default-push', dest or 'default'), revs)
450 ui.expandpath(dest or 'default-push', dest or 'default'), revs)
451 other = hg.repository(ui, dest)
451 other = hg.repository(ui, dest)
452 o = repo.findoutgoing(other, force=opts['force'])
452 o = repo.findoutgoing(other, force=opts['force'])
453
453
454 if revs:
454 if revs:
455 cg = repo.changegroupsubset(o, revs, 'bundle')
455 cg = repo.changegroupsubset(o, revs, 'bundle')
456 else:
456 else:
457 cg = repo.changegroup(o, 'bundle')
457 cg = repo.changegroup(o, 'bundle')
458 changegroup.writebundle(cg, fname, "HG10BZ")
458 changegroup.writebundle(cg, fname, "HG10BZ")
459
459
460 def cat(ui, repo, file1, *pats, **opts):
460 def cat(ui, repo, file1, *pats, **opts):
461 """output the current or given revision of files
461 """output the current or given revision of files
462
462
463 Print the specified files as they were at the given revision.
463 Print the specified files as they were at the given revision.
464 If no revision is given, the parent of the working directory is used,
464 If no revision is given, the parent of the working directory is used,
465 or tip if no revision is checked out.
465 or tip if no revision is checked out.
466
466
467 Output may be to a file, in which case the name of the file is
467 Output may be to a file, in which case the name of the file is
468 given using a format string. The formatting rules are the same as
468 given using a format string. The formatting rules are the same as
469 for the export command, with the following additions:
469 for the export command, with the following additions:
470
470
471 %s basename of file being printed
471 %s basename of file being printed
472 %d dirname of file being printed, or '.' if in repo root
472 %d dirname of file being printed, or '.' if in repo root
473 %p root-relative path name of file being printed
473 %p root-relative path name of file being printed
474 """
474 """
475 ctx = repo.changectx(opts['rev'])
475 ctx = repo.changectx(opts['rev'])
476 err = 1
476 err = 1
477 for src, abs, rel, exact in cmdutil.walk(repo, (file1,) + pats, opts,
477 for src, abs, rel, exact in cmdutil.walk(repo, (file1,) + pats, opts,
478 ctx.node()):
478 ctx.node()):
479 fp = cmdutil.make_file(repo, opts['output'], ctx.node(), pathname=abs)
479 fp = cmdutil.make_file(repo, opts['output'], ctx.node(), pathname=abs)
480 data = ctx.filectx(abs).data()
480 data = ctx.filectx(abs).data()
481 if opts.get('decode'):
481 if opts.get('decode'):
482 data = repo.wwritedata(abs, data)
482 data = repo.wwritedata(abs, data)
483 fp.write(data)
483 fp.write(data)
484 err = 0
484 err = 0
485 return err
485 return err
486
486
487 def clone(ui, source, dest=None, **opts):
487 def clone(ui, source, dest=None, **opts):
488 """make a copy of an existing repository
488 """make a copy of an existing repository
489
489
490 Create a copy of an existing repository in a new directory.
490 Create a copy of an existing repository in a new directory.
491
491
492 If no destination directory name is specified, it defaults to the
492 If no destination directory name is specified, it defaults to the
493 basename of the source.
493 basename of the source.
494
494
495 The location of the source is added to the new repository's
495 The location of the source is added to the new repository's
496 .hg/hgrc file, as the default to be used for future pulls.
496 .hg/hgrc file, as the default to be used for future pulls.
497
497
498 For efficiency, hardlinks are used for cloning whenever the source
498 For efficiency, hardlinks are used for cloning whenever the source
499 and destination are on the same filesystem (note this applies only
499 and destination are on the same filesystem (note this applies only
500 to the repository data, not to the checked out files). Some
500 to the repository data, not to the checked out files). Some
501 filesystems, such as AFS, implement hardlinking incorrectly, but
501 filesystems, such as AFS, implement hardlinking incorrectly, but
502 do not report errors. In these cases, use the --pull option to
502 do not report errors. In these cases, use the --pull option to
503 avoid hardlinking.
503 avoid hardlinking.
504
504
505 You can safely clone repositories and checked out files using full
505 You can safely clone repositories and checked out files using full
506 hardlinks with
506 hardlinks with
507
507
508 $ cp -al REPO REPOCLONE
508 $ cp -al REPO REPOCLONE
509
509
510 which is the fastest way to clone. However, the operation is not
510 which is the fastest way to clone. However, the operation is not
511 atomic (making sure REPO is not modified during the operation is
511 atomic (making sure REPO is not modified during the operation is
512 up to you) and you have to make sure your editor breaks hardlinks
512 up to you) and you have to make sure your editor breaks hardlinks
513 (Emacs and most Linux Kernel tools do so).
513 (Emacs and most Linux Kernel tools do so).
514
514
515 If you use the -r option to clone up to a specific revision, no
515 If you use the -r option to clone up to a specific revision, no
516 subsequent revisions will be present in the cloned repository.
516 subsequent revisions will be present in the cloned repository.
517 This option implies --pull, even on local repositories.
517 This option implies --pull, even on local repositories.
518
518
519 See pull for valid source format details.
519 See pull for valid source format details.
520
520
521 It is possible to specify an ssh:// URL as the destination, but no
521 It is possible to specify an ssh:// URL as the destination, but no
522 .hg/hgrc and working directory will be created on the remote side.
522 .hg/hgrc and working directory will be created on the remote side.
523 Look at the help text for the pull command for important details
523 Look at the help text for the pull command for important details
524 about ssh:// URLs.
524 about ssh:// URLs.
525 """
525 """
526 cmdutil.setremoteconfig(ui, opts)
526 cmdutil.setremoteconfig(ui, opts)
527 hg.clone(ui, source, dest,
527 hg.clone(ui, source, dest,
528 pull=opts['pull'],
528 pull=opts['pull'],
529 stream=opts['uncompressed'],
529 stream=opts['uncompressed'],
530 rev=opts['rev'],
530 rev=opts['rev'],
531 update=not opts['noupdate'])
531 update=not opts['noupdate'])
532
532
533 def commit(ui, repo, *pats, **opts):
533 def commit(ui, repo, *pats, **opts):
534 """commit the specified files or all outstanding changes
534 """commit the specified files or all outstanding changes
535
535
536 Commit changes to the given files into the repository.
536 Commit changes to the given files into the repository.
537
537
538 If a list of files is omitted, all changes reported by "hg status"
538 If a list of files is omitted, all changes reported by "hg status"
539 will be committed.
539 will be committed.
540
540
541 If no commit message is specified, the configured editor is started to
541 If no commit message is specified, the configured editor is started to
542 enter a message.
542 enter a message.
543
543
544 See 'hg help dates' for a list of formats valid for -d/--date.
544 See 'hg help dates' for a list of formats valid for -d/--date.
545 """
545 """
546 def commitfunc(ui, repo, files, message, match, opts):
546 def commitfunc(ui, repo, files, message, match, opts):
547 return repo.commit(files, message, opts['user'], opts['date'], match,
547 return repo.commit(files, message, opts['user'], opts['date'], match,
548 force_editor=opts.get('force_editor'))
548 force_editor=opts.get('force_editor'))
549 cmdutil.commit(ui, repo, commitfunc, pats, opts)
549 cmdutil.commit(ui, repo, commitfunc, pats, opts)
550
550
551 def copy(ui, repo, *pats, **opts):
551 def copy(ui, repo, *pats, **opts):
552 """mark files as copied for the next commit
552 """mark files as copied for the next commit
553
553
554 Mark dest as having copies of source files. If dest is a
554 Mark dest as having copies of source files. If dest is a
555 directory, copies are put in that directory. If dest is a file,
555 directory, copies are put in that directory. If dest is a file,
556 there can only be one source.
556 there can only be one source.
557
557
558 By default, this command copies the contents of files as they
558 By default, this command copies the contents of files as they
559 stand in the working directory. If invoked with --after, the
559 stand in the working directory. If invoked with --after, the
560 operation is recorded, but no copying is performed.
560 operation is recorded, but no copying is performed.
561
561
562 This command takes effect in the next commit. To undo a copy
562 This command takes effect in the next commit. To undo a copy
563 before that, see hg revert.
563 before that, see hg revert.
564 """
564 """
565 wlock = repo.wlock(False)
565 wlock = repo.wlock(False)
566 try:
566 try:
567 return cmdutil.copy(ui, repo, pats, opts)
567 return cmdutil.copy(ui, repo, pats, opts)
568 finally:
568 finally:
569 del wlock
569 del wlock
570
570
571 def debugancestor(ui, repo, *args):
571 def debugancestor(ui, repo, *args):
572 """find the ancestor revision of two revisions in a given index"""
572 """find the ancestor revision of two revisions in a given index"""
573 if len(args) == 3:
573 if len(args) == 3:
574 index, rev1, rev2 = args
574 index, rev1, rev2 = args
575 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index)
575 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index)
576 elif len(args) == 2:
576 elif len(args) == 2:
577 if not repo:
577 if not repo:
578 raise util.Abort(_("There is no Mercurial repository here "
578 raise util.Abort(_("There is no Mercurial repository here "
579 "(.hg not found)"))
579 "(.hg not found)"))
580 rev1, rev2 = args
580 rev1, rev2 = args
581 r = repo.changelog
581 r = repo.changelog
582 else:
582 else:
583 raise util.Abort(_('either two or three arguments required'))
583 raise util.Abort(_('either two or three arguments required'))
584 a = r.ancestor(r.lookup(rev1), r.lookup(rev2))
584 a = r.ancestor(r.lookup(rev1), r.lookup(rev2))
585 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
585 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
586
586
587 def debugcomplete(ui, cmd='', **opts):
587 def debugcomplete(ui, cmd='', **opts):
588 """returns the completion list associated with the given command"""
588 """returns the completion list associated with the given command"""
589
589
590 if opts['options']:
590 if opts['options']:
591 options = []
591 options = []
592 otables = [globalopts]
592 otables = [globalopts]
593 if cmd:
593 if cmd:
594 aliases, entry = cmdutil.findcmd(ui, cmd, table)
594 aliases, entry = cmdutil.findcmd(ui, cmd, table)
595 otables.append(entry[1])
595 otables.append(entry[1])
596 for t in otables:
596 for t in otables:
597 for o in t:
597 for o in t:
598 if o[0]:
598 if o[0]:
599 options.append('-%s' % o[0])
599 options.append('-%s' % o[0])
600 options.append('--%s' % o[1])
600 options.append('--%s' % o[1])
601 ui.write("%s\n" % "\n".join(options))
601 ui.write("%s\n" % "\n".join(options))
602 return
602 return
603
603
604 clist = cmdutil.findpossible(ui, cmd, table).keys()
604 clist = cmdutil.findpossible(ui, cmd, table).keys()
605 clist.sort()
605 clist.sort()
606 ui.write("%s\n" % "\n".join(clist))
606 ui.write("%s\n" % "\n".join(clist))
607
607
608 def debugfsinfo(ui, path = "."):
608 def debugfsinfo(ui, path = "."):
609 file('.debugfsinfo', 'w').write('')
609 file('.debugfsinfo', 'w').write('')
610 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
610 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
611 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
611 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
612 ui.write('case-sensitive: %s\n' % (util.checkfolding('.debugfsinfo')
612 ui.write('case-sensitive: %s\n' % (util.checkfolding('.debugfsinfo')
613 and 'yes' or 'no'))
613 and 'yes' or 'no'))
614 os.unlink('.debugfsinfo')
614 os.unlink('.debugfsinfo')
615
615
616 def debugrebuildstate(ui, repo, rev=""):
616 def debugrebuildstate(ui, repo, rev=""):
617 """rebuild the dirstate as it would look like for the given revision"""
617 """rebuild the dirstate as it would look like for the given revision"""
618 if rev == "":
618 if rev == "":
619 rev = repo.changelog.tip()
619 rev = repo.changelog.tip()
620 ctx = repo.changectx(rev)
620 ctx = repo.changectx(rev)
621 files = ctx.manifest()
621 files = ctx.manifest()
622 wlock = repo.wlock()
622 wlock = repo.wlock()
623 try:
623 try:
624 repo.dirstate.rebuild(rev, files)
624 repo.dirstate.rebuild(rev, files)
625 finally:
625 finally:
626 del wlock
626 del wlock
627
627
628 def debugcheckstate(ui, repo):
628 def debugcheckstate(ui, repo):
629 """validate the correctness of the current dirstate"""
629 """validate the correctness of the current dirstate"""
630 parent1, parent2 = repo.dirstate.parents()
630 parent1, parent2 = repo.dirstate.parents()
631 m1 = repo.changectx(parent1).manifest()
631 m1 = repo.changectx(parent1).manifest()
632 m2 = repo.changectx(parent2).manifest()
632 m2 = repo.changectx(parent2).manifest()
633 errors = 0
633 errors = 0
634 for f in repo.dirstate:
634 for f in repo.dirstate:
635 state = repo.dirstate[f]
635 state = repo.dirstate[f]
636 if state in "nr" and f not in m1:
636 if state in "nr" and f not in m1:
637 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
637 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
638 errors += 1
638 errors += 1
639 if state in "a" and f in m1:
639 if state in "a" and f in m1:
640 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
640 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
641 errors += 1
641 errors += 1
642 if state in "m" and f not in m1 and f not in m2:
642 if state in "m" and f not in m1 and f not in m2:
643 ui.warn(_("%s in state %s, but not in either manifest\n") %
643 ui.warn(_("%s in state %s, but not in either manifest\n") %
644 (f, state))
644 (f, state))
645 errors += 1
645 errors += 1
646 for f in m1:
646 for f in m1:
647 state = repo.dirstate[f]
647 state = repo.dirstate[f]
648 if state not in "nrm":
648 if state not in "nrm":
649 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
649 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
650 errors += 1
650 errors += 1
651 if errors:
651 if errors:
652 error = _(".hg/dirstate inconsistent with current parent's manifest")
652 error = _(".hg/dirstate inconsistent with current parent's manifest")
653 raise util.Abort(error)
653 raise util.Abort(error)
654
654
655 def showconfig(ui, repo, *values, **opts):
655 def showconfig(ui, repo, *values, **opts):
656 """show combined config settings from all hgrc files
656 """show combined config settings from all hgrc files
657
657
658 With no args, print names and values of all config items.
658 With no args, print names and values of all config items.
659
659
660 With one arg of the form section.name, print just the value of
660 With one arg of the form section.name, print just the value of
661 that config item.
661 that config item.
662
662
663 With multiple args, print names and values of all config items
663 With multiple args, print names and values of all config items
664 with matching section names."""
664 with matching section names."""
665
665
666 untrusted = bool(opts.get('untrusted'))
666 untrusted = bool(opts.get('untrusted'))
667 if values:
667 if values:
668 if len([v for v in values if '.' in v]) > 1:
668 if len([v for v in values if '.' in v]) > 1:
669 raise util.Abort(_('only one config item permitted'))
669 raise util.Abort(_('only one config item permitted'))
670 for section, name, value in ui.walkconfig(untrusted=untrusted):
670 for section, name, value in ui.walkconfig(untrusted=untrusted):
671 sectname = section + '.' + name
671 sectname = section + '.' + name
672 if values:
672 if values:
673 for v in values:
673 for v in values:
674 if v == section:
674 if v == section:
675 ui.write('%s=%s\n' % (sectname, value))
675 ui.write('%s=%s\n' % (sectname, value))
676 elif v == sectname:
676 elif v == sectname:
677 ui.write(value, '\n')
677 ui.write(value, '\n')
678 else:
678 else:
679 ui.write('%s=%s\n' % (sectname, value))
679 ui.write('%s=%s\n' % (sectname, value))
680
680
681 def debugsetparents(ui, repo, rev1, rev2=None):
681 def debugsetparents(ui, repo, rev1, rev2=None):
682 """manually set the parents of the current working directory
682 """manually set the parents of the current working directory
683
683
684 This is useful for writing repository conversion tools, but should
684 This is useful for writing repository conversion tools, but should
685 be used with care.
685 be used with care.
686 """
686 """
687
687
688 if not rev2:
688 if not rev2:
689 rev2 = hex(nullid)
689 rev2 = hex(nullid)
690
690
691 wlock = repo.wlock()
691 wlock = repo.wlock()
692 try:
692 try:
693 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
693 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
694 finally:
694 finally:
695 del wlock
695 del wlock
696
696
697 def debugstate(ui, repo):
697 def debugstate(ui, repo):
698 """show the contents of the current dirstate"""
698 """show the contents of the current dirstate"""
699 k = repo.dirstate._map.items()
699 k = repo.dirstate._map.items()
700 k.sort()
700 k.sort()
701 for file_, ent in k:
701 for file_, ent in k:
702 if ent[3] == -1:
702 if ent[3] == -1:
703 # Pad or slice to locale representation
703 # Pad or slice to locale representation
704 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(0)))
704 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(0)))
705 timestr = 'unset'
705 timestr = 'unset'
706 timestr = timestr[:locale_len] + ' '*(locale_len - len(timestr))
706 timestr = timestr[:locale_len] + ' '*(locale_len - len(timestr))
707 else:
707 else:
708 timestr = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(ent[3]))
708 timestr = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(ent[3]))
709 if ent[1] & 020000:
709 if ent[1] & 020000:
710 mode = 'lnk'
710 mode = 'lnk'
711 else:
711 else:
712 mode = '%3o' % (ent[1] & 0777)
712 mode = '%3o' % (ent[1] & 0777)
713 ui.write("%c %s %10d %s %s\n" % (ent[0], mode, ent[2], timestr, file_))
713 ui.write("%c %s %10d %s %s\n" % (ent[0], mode, ent[2], timestr, file_))
714 for f in repo.dirstate.copies():
714 for f in repo.dirstate.copies():
715 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
715 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
716
716
717 def debugdata(ui, file_, rev):
717 def debugdata(ui, file_, rev):
718 """dump the contents of a data file revision"""
718 """dump the contents of a data file revision"""
719 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_[:-2] + ".i")
719 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_[:-2] + ".i")
720 try:
720 try:
721 ui.write(r.revision(r.lookup(rev)))
721 ui.write(r.revision(r.lookup(rev)))
722 except KeyError:
722 except KeyError:
723 raise util.Abort(_('invalid revision identifier %s') % rev)
723 raise util.Abort(_('invalid revision identifier %s') % rev)
724
724
725 def debugdate(ui, date, range=None, **opts):
725 def debugdate(ui, date, range=None, **opts):
726 """parse and display a date"""
726 """parse and display a date"""
727 if opts["extended"]:
727 if opts["extended"]:
728 d = util.parsedate(date, util.extendeddateformats)
728 d = util.parsedate(date, util.extendeddateformats)
729 else:
729 else:
730 d = util.parsedate(date)
730 d = util.parsedate(date)
731 ui.write("internal: %s %s\n" % d)
731 ui.write("internal: %s %s\n" % d)
732 ui.write("standard: %s\n" % util.datestr(d))
732 ui.write("standard: %s\n" % util.datestr(d))
733 if range:
733 if range:
734 m = util.matchdate(range)
734 m = util.matchdate(range)
735 ui.write("match: %s\n" % m(d[0]))
735 ui.write("match: %s\n" % m(d[0]))
736
736
737 def debugindex(ui, file_):
737 def debugindex(ui, file_):
738 """dump the contents of an index file"""
738 """dump the contents of an index file"""
739 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
739 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
740 ui.write(" rev offset length base linkrev" +
740 ui.write(" rev offset length base linkrev" +
741 " nodeid p1 p2\n")
741 " nodeid p1 p2\n")
742 for i in xrange(r.count()):
742 for i in xrange(r.count()):
743 node = r.node(i)
743 node = r.node(i)
744 try:
744 try:
745 pp = r.parents(node)
745 pp = r.parents(node)
746 except:
746 except:
747 pp = [nullid, nullid]
747 pp = [nullid, nullid]
748 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
748 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
749 i, r.start(i), r.length(i), r.base(i), r.linkrev(node),
749 i, r.start(i), r.length(i), r.base(i), r.linkrev(node),
750 short(node), short(pp[0]), short(pp[1])))
750 short(node), short(pp[0]), short(pp[1])))
751
751
752 def debugindexdot(ui, file_):
752 def debugindexdot(ui, file_):
753 """dump an index DAG as a .dot file"""
753 """dump an index DAG as a .dot file"""
754 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
754 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
755 ui.write("digraph G {\n")
755 ui.write("digraph G {\n")
756 for i in xrange(r.count()):
756 for i in xrange(r.count()):
757 node = r.node(i)
757 node = r.node(i)
758 pp = r.parents(node)
758 pp = r.parents(node)
759 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
759 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
760 if pp[1] != nullid:
760 if pp[1] != nullid:
761 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
761 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
762 ui.write("}\n")
762 ui.write("}\n")
763
763
764 def debuginstall(ui):
764 def debuginstall(ui):
765 '''test Mercurial installation'''
765 '''test Mercurial installation'''
766
766
767 def writetemp(contents):
767 def writetemp(contents):
768 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
768 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
769 f = os.fdopen(fd, "wb")
769 f = os.fdopen(fd, "wb")
770 f.write(contents)
770 f.write(contents)
771 f.close()
771 f.close()
772 return name
772 return name
773
773
774 problems = 0
774 problems = 0
775
775
776 # encoding
776 # encoding
777 ui.status(_("Checking encoding (%s)...\n") % util._encoding)
777 ui.status(_("Checking encoding (%s)...\n") % util._encoding)
778 try:
778 try:
779 util.fromlocal("test")
779 util.fromlocal("test")
780 except util.Abort, inst:
780 except util.Abort, inst:
781 ui.write(" %s\n" % inst)
781 ui.write(" %s\n" % inst)
782 ui.write(_(" (check that your locale is properly set)\n"))
782 ui.write(_(" (check that your locale is properly set)\n"))
783 problems += 1
783 problems += 1
784
784
785 # compiled modules
785 # compiled modules
786 ui.status(_("Checking extensions...\n"))
786 ui.status(_("Checking extensions...\n"))
787 try:
787 try:
788 import bdiff, mpatch, base85
788 import bdiff, mpatch, base85
789 except Exception, inst:
789 except Exception, inst:
790 ui.write(" %s\n" % inst)
790 ui.write(" %s\n" % inst)
791 ui.write(_(" One or more extensions could not be found"))
791 ui.write(_(" One or more extensions could not be found"))
792 ui.write(_(" (check that you compiled the extensions)\n"))
792 ui.write(_(" (check that you compiled the extensions)\n"))
793 problems += 1
793 problems += 1
794
794
795 # templates
795 # templates
796 ui.status(_("Checking templates...\n"))
796 ui.status(_("Checking templates...\n"))
797 try:
797 try:
798 import templater
798 import templater
799 t = templater.templater(templater.templatepath("map-cmdline.default"))
799 t = templater.templater(templater.templatepath("map-cmdline.default"))
800 except Exception, inst:
800 except Exception, inst:
801 ui.write(" %s\n" % inst)
801 ui.write(" %s\n" % inst)
802 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
802 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
803 problems += 1
803 problems += 1
804
804
805 # patch
805 # patch
806 ui.status(_("Checking patch...\n"))
806 ui.status(_("Checking patch...\n"))
807 patchproblems = 0
807 patchproblems = 0
808 a = "1\n2\n3\n4\n"
808 a = "1\n2\n3\n4\n"
809 b = "1\n2\n3\ninsert\n4\n"
809 b = "1\n2\n3\ninsert\n4\n"
810 fa = writetemp(a)
810 fa = writetemp(a)
811 d = mdiff.unidiff(a, None, b, None, os.path.basename(fa),
811 d = mdiff.unidiff(a, None, b, None, os.path.basename(fa),
812 os.path.basename(fa))
812 os.path.basename(fa))
813 fd = writetemp(d)
813 fd = writetemp(d)
814
814
815 files = {}
815 files = {}
816 try:
816 try:
817 patch.patch(fd, ui, cwd=os.path.dirname(fa), files=files)
817 patch.patch(fd, ui, cwd=os.path.dirname(fa), files=files)
818 except util.Abort, e:
818 except util.Abort, e:
819 ui.write(_(" patch call failed:\n"))
819 ui.write(_(" patch call failed:\n"))
820 ui.write(" " + str(e) + "\n")
820 ui.write(" " + str(e) + "\n")
821 patchproblems += 1
821 patchproblems += 1
822 else:
822 else:
823 if list(files) != [os.path.basename(fa)]:
823 if list(files) != [os.path.basename(fa)]:
824 ui.write(_(" unexpected patch output!\n"))
824 ui.write(_(" unexpected patch output!\n"))
825 patchproblems += 1
825 patchproblems += 1
826 a = file(fa).read()
826 a = file(fa).read()
827 if a != b:
827 if a != b:
828 ui.write(_(" patch test failed!\n"))
828 ui.write(_(" patch test failed!\n"))
829 patchproblems += 1
829 patchproblems += 1
830
830
831 if patchproblems:
831 if patchproblems:
832 if ui.config('ui', 'patch'):
832 if ui.config('ui', 'patch'):
833 ui.write(_(" (Current patch tool may be incompatible with patch,"
833 ui.write(_(" (Current patch tool may be incompatible with patch,"
834 " or misconfigured. Please check your .hgrc file)\n"))
834 " or misconfigured. Please check your .hgrc file)\n"))
835 else:
835 else:
836 ui.write(_(" Internal patcher failure, please report this error"
836 ui.write(_(" Internal patcher failure, please report this error"
837 " to http://www.selenic.com/mercurial/bts\n"))
837 " to http://www.selenic.com/mercurial/bts\n"))
838 problems += patchproblems
838 problems += patchproblems
839
839
840 os.unlink(fa)
840 os.unlink(fa)
841 os.unlink(fd)
841 os.unlink(fd)
842
842
843 # editor
843 # editor
844 ui.status(_("Checking commit editor...\n"))
844 ui.status(_("Checking commit editor...\n"))
845 editor = ui.geteditor()
845 editor = ui.geteditor()
846 cmdpath = util.find_exe(editor) or util.find_exe(editor.split()[0])
846 cmdpath = util.find_exe(editor) or util.find_exe(editor.split()[0])
847 if not cmdpath:
847 if not cmdpath:
848 if editor == 'vi':
848 if editor == 'vi':
849 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
849 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
850 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
850 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
851 else:
851 else:
852 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
852 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
853 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
853 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
854 problems += 1
854 problems += 1
855
855
856 # check username
856 # check username
857 ui.status(_("Checking username...\n"))
857 ui.status(_("Checking username...\n"))
858 user = os.environ.get("HGUSER")
858 user = os.environ.get("HGUSER")
859 if user is None:
859 if user is None:
860 user = ui.config("ui", "username")
860 user = ui.config("ui", "username")
861 if user is None:
861 if user is None:
862 user = os.environ.get("EMAIL")
862 user = os.environ.get("EMAIL")
863 if not user:
863 if not user:
864 ui.warn(" ")
864 ui.warn(" ")
865 ui.username()
865 ui.username()
866 ui.write(_(" (specify a username in your .hgrc file)\n"))
866 ui.write(_(" (specify a username in your .hgrc file)\n"))
867
867
868 if not problems:
868 if not problems:
869 ui.status(_("No problems detected\n"))
869 ui.status(_("No problems detected\n"))
870 else:
870 else:
871 ui.write(_("%s problems detected,"
871 ui.write(_("%s problems detected,"
872 " please check your install!\n") % problems)
872 " please check your install!\n") % problems)
873
873
874 return problems
874 return problems
875
875
876 def debugrename(ui, repo, file1, *pats, **opts):
876 def debugrename(ui, repo, file1, *pats, **opts):
877 """dump rename information"""
877 """dump rename information"""
878
878
879 ctx = repo.changectx(opts.get('rev', 'tip'))
879 ctx = repo.changectx(opts.get('rev', 'tip'))
880 for src, abs, rel, exact in cmdutil.walk(repo, (file1,) + pats, opts,
880 for src, abs, rel, exact in cmdutil.walk(repo, (file1,) + pats, opts,
881 ctx.node()):
881 ctx.node()):
882 fctx = ctx.filectx(abs)
882 fctx = ctx.filectx(abs)
883 m = fctx.filelog().renamed(fctx.filenode())
883 m = fctx.filelog().renamed(fctx.filenode())
884 if m:
884 if m:
885 ui.write(_("%s renamed from %s:%s\n") % (rel, m[0], hex(m[1])))
885 ui.write(_("%s renamed from %s:%s\n") % (rel, m[0], hex(m[1])))
886 else:
886 else:
887 ui.write(_("%s not renamed\n") % rel)
887 ui.write(_("%s not renamed\n") % rel)
888
888
889 def debugwalk(ui, repo, *pats, **opts):
889 def debugwalk(ui, repo, *pats, **opts):
890 """show how files match on given patterns"""
890 """show how files match on given patterns"""
891 items = list(cmdutil.walk(repo, pats, opts))
891 items = list(cmdutil.walk(repo, pats, opts))
892 if not items:
892 if not items:
893 return
893 return
894 fmt = '%%s %%-%ds %%-%ds %%s' % (
894 fmt = '%%s %%-%ds %%-%ds %%s' % (
895 max([len(abs) for (src, abs, rel, exact) in items]),
895 max([len(abs) for (src, abs, rel, exact) in items]),
896 max([len(rel) for (src, abs, rel, exact) in items]))
896 max([len(rel) for (src, abs, rel, exact) in items]))
897 for src, abs, rel, exact in items:
897 for src, abs, rel, exact in items:
898 line = fmt % (src, abs, rel, exact and 'exact' or '')
898 line = fmt % (src, abs, rel, exact and 'exact' or '')
899 ui.write("%s\n" % line.rstrip())
899 ui.write("%s\n" % line.rstrip())
900
900
901 def diff(ui, repo, *pats, **opts):
901 def diff(ui, repo, *pats, **opts):
902 """diff repository (or selected files)
902 """diff repository (or selected files)
903
903
904 Show differences between revisions for the specified files.
904 Show differences between revisions for the specified files.
905
905
906 Differences between files are shown using the unified diff format.
906 Differences between files are shown using the unified diff format.
907
907
908 NOTE: diff may generate unexpected results for merges, as it will
908 NOTE: diff may generate unexpected results for merges, as it will
909 default to comparing against the working directory's first parent
909 default to comparing against the working directory's first parent
910 changeset if no revisions are specified.
910 changeset if no revisions are specified.
911
911
912 When two revision arguments are given, then changes are shown
912 When two revision arguments are given, then changes are shown
913 between those revisions. If only one revision is specified then
913 between those revisions. If only one revision is specified then
914 that revision is compared to the working directory, and, when no
914 that revision is compared to the working directory, and, when no
915 revisions are specified, the working directory files are compared
915 revisions are specified, the working directory files are compared
916 to its parent.
916 to its parent.
917
917
918 Without the -a option, diff will avoid generating diffs of files
918 Without the -a option, diff will avoid generating diffs of files
919 it detects as binary. With -a, diff will generate a diff anyway,
919 it detects as binary. With -a, diff will generate a diff anyway,
920 probably with undesirable results.
920 probably with undesirable results.
921 """
921 """
922 node1, node2 = cmdutil.revpair(repo, opts['rev'])
922 node1, node2 = cmdutil.revpair(repo, opts['rev'])
923
923
924 fns, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
924 fns, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
925
925
926 patch.diff(repo, node1, node2, fns, match=matchfn,
926 patch.diff(repo, node1, node2, fns, match=matchfn,
927 opts=patch.diffopts(ui, opts))
927 opts=patch.diffopts(ui, opts))
928
928
929 def export(ui, repo, *changesets, **opts):
929 def export(ui, repo, *changesets, **opts):
930 """dump the header and diffs for one or more changesets
930 """dump the header and diffs for one or more changesets
931
931
932 Print the changeset header and diffs for one or more revisions.
932 Print the changeset header and diffs for one or more revisions.
933
933
934 The information shown in the changeset header is: author,
934 The information shown in the changeset header is: author,
935 changeset hash, parent(s) and commit comment.
935 changeset hash, parent(s) and commit comment.
936
936
937 NOTE: export may generate unexpected diff output for merge changesets,
937 NOTE: export may generate unexpected diff output for merge changesets,
938 as it will compare the merge changeset against its first parent only.
938 as it will compare the merge changeset against its first parent only.
939
939
940 Output may be to a file, in which case the name of the file is
940 Output may be to a file, in which case the name of the file is
941 given using a format string. The formatting rules are as follows:
941 given using a format string. The formatting rules are as follows:
942
942
943 %% literal "%" character
943 %% literal "%" character
944 %H changeset hash (40 bytes of hexadecimal)
944 %H changeset hash (40 bytes of hexadecimal)
945 %N number of patches being generated
945 %N number of patches being generated
946 %R changeset revision number
946 %R changeset revision number
947 %b basename of the exporting repository
947 %b basename of the exporting repository
948 %h short-form changeset hash (12 bytes of hexadecimal)
948 %h short-form changeset hash (12 bytes of hexadecimal)
949 %n zero-padded sequence number, starting at 1
949 %n zero-padded sequence number, starting at 1
950 %r zero-padded changeset revision number
950 %r zero-padded changeset revision number
951
951
952 Without the -a option, export will avoid generating diffs of files
952 Without the -a option, export will avoid generating diffs of files
953 it detects as binary. With -a, export will generate a diff anyway,
953 it detects as binary. With -a, export will generate a diff anyway,
954 probably with undesirable results.
954 probably with undesirable results.
955
955
956 With the --switch-parent option, the diff will be against the second
956 With the --switch-parent option, the diff will be against the second
957 parent. It can be useful to review a merge.
957 parent. It can be useful to review a merge.
958 """
958 """
959 if not changesets:
959 if not changesets:
960 raise util.Abort(_("export requires at least one changeset"))
960 raise util.Abort(_("export requires at least one changeset"))
961 revs = cmdutil.revrange(repo, changesets)
961 revs = cmdutil.revrange(repo, changesets)
962 if len(revs) > 1:
962 if len(revs) > 1:
963 ui.note(_('exporting patches:\n'))
963 ui.note(_('exporting patches:\n'))
964 else:
964 else:
965 ui.note(_('exporting patch:\n'))
965 ui.note(_('exporting patch:\n'))
966 patch.export(repo, revs, template=opts['output'],
966 patch.export(repo, revs, template=opts['output'],
967 switch_parent=opts['switch_parent'],
967 switch_parent=opts['switch_parent'],
968 opts=patch.diffopts(ui, opts))
968 opts=patch.diffopts(ui, opts))
969
969
970 def grep(ui, repo, pattern, *pats, **opts):
970 def grep(ui, repo, pattern, *pats, **opts):
971 """search for a pattern in specified files and revisions
971 """search for a pattern in specified files and revisions
972
972
973 Search revisions of files for a regular expression.
973 Search revisions of files for a regular expression.
974
974
975 This command behaves differently than Unix grep. It only accepts
975 This command behaves differently than Unix grep. It only accepts
976 Python/Perl regexps. It searches repository history, not the
976 Python/Perl regexps. It searches repository history, not the
977 working directory. It always prints the revision number in which
977 working directory. It always prints the revision number in which
978 a match appears.
978 a match appears.
979
979
980 By default, grep only prints output for the first revision of a
980 By default, grep only prints output for the first revision of a
981 file in which it finds a match. To get it to print every revision
981 file in which it finds a match. To get it to print every revision
982 that contains a change in match status ("-" for a match that
982 that contains a change in match status ("-" for a match that
983 becomes a non-match, or "+" for a non-match that becomes a match),
983 becomes a non-match, or "+" for a non-match that becomes a match),
984 use the --all flag.
984 use the --all flag.
985 """
985 """
986 reflags = 0
986 reflags = 0
987 if opts['ignore_case']:
987 if opts['ignore_case']:
988 reflags |= re.I
988 reflags |= re.I
989 try:
989 try:
990 regexp = re.compile(pattern, reflags)
990 regexp = re.compile(pattern, reflags)
991 except Exception, inst:
991 except Exception, inst:
992 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
992 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
993 return None
993 return None
994 sep, eol = ':', '\n'
994 sep, eol = ':', '\n'
995 if opts['print0']:
995 if opts['print0']:
996 sep = eol = '\0'
996 sep = eol = '\0'
997
997
998 fcache = {}
998 fcache = {}
999 def getfile(fn):
999 def getfile(fn):
1000 if fn not in fcache:
1000 if fn not in fcache:
1001 fcache[fn] = repo.file(fn)
1001 fcache[fn] = repo.file(fn)
1002 return fcache[fn]
1002 return fcache[fn]
1003
1003
1004 def matchlines(body):
1004 def matchlines(body):
1005 begin = 0
1005 begin = 0
1006 linenum = 0
1006 linenum = 0
1007 while True:
1007 while True:
1008 match = regexp.search(body, begin)
1008 match = regexp.search(body, begin)
1009 if not match:
1009 if not match:
1010 break
1010 break
1011 mstart, mend = match.span()
1011 mstart, mend = match.span()
1012 linenum += body.count('\n', begin, mstart) + 1
1012 linenum += body.count('\n', begin, mstart) + 1
1013 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1013 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1014 lend = body.find('\n', mend)
1014 lend = body.find('\n', mend)
1015 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1015 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1016 begin = lend + 1
1016 begin = lend + 1
1017
1017
1018 class linestate(object):
1018 class linestate(object):
1019 def __init__(self, line, linenum, colstart, colend):
1019 def __init__(self, line, linenum, colstart, colend):
1020 self.line = line
1020 self.line = line
1021 self.linenum = linenum
1021 self.linenum = linenum
1022 self.colstart = colstart
1022 self.colstart = colstart
1023 self.colend = colend
1023 self.colend = colend
1024
1024
1025 def __eq__(self, other):
1025 def __eq__(self, other):
1026 return self.line == other.line
1026 return self.line == other.line
1027
1027
1028 matches = {}
1028 matches = {}
1029 copies = {}
1029 copies = {}
1030 def grepbody(fn, rev, body):
1030 def grepbody(fn, rev, body):
1031 matches[rev].setdefault(fn, [])
1031 matches[rev].setdefault(fn, [])
1032 m = matches[rev][fn]
1032 m = matches[rev][fn]
1033 for lnum, cstart, cend, line in matchlines(body):
1033 for lnum, cstart, cend, line in matchlines(body):
1034 s = linestate(line, lnum, cstart, cend)
1034 s = linestate(line, lnum, cstart, cend)
1035 m.append(s)
1035 m.append(s)
1036
1036
1037 def difflinestates(a, b):
1037 def difflinestates(a, b):
1038 sm = difflib.SequenceMatcher(None, a, b)
1038 sm = difflib.SequenceMatcher(None, a, b)
1039 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1039 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1040 if tag == 'insert':
1040 if tag == 'insert':
1041 for i in xrange(blo, bhi):
1041 for i in xrange(blo, bhi):
1042 yield ('+', b[i])
1042 yield ('+', b[i])
1043 elif tag == 'delete':
1043 elif tag == 'delete':
1044 for i in xrange(alo, ahi):
1044 for i in xrange(alo, ahi):
1045 yield ('-', a[i])
1045 yield ('-', a[i])
1046 elif tag == 'replace':
1046 elif tag == 'replace':
1047 for i in xrange(alo, ahi):
1047 for i in xrange(alo, ahi):
1048 yield ('-', a[i])
1048 yield ('-', a[i])
1049 for i in xrange(blo, bhi):
1049 for i in xrange(blo, bhi):
1050 yield ('+', b[i])
1050 yield ('+', b[i])
1051
1051
1052 prev = {}
1052 prev = {}
1053 def display(fn, rev, states, prevstates):
1053 def display(fn, rev, states, prevstates):
1054 datefunc = ui.quiet and util.shortdate or util.datestr
1054 datefunc = ui.quiet and util.shortdate or util.datestr
1055 found = False
1055 found = False
1056 filerevmatches = {}
1056 filerevmatches = {}
1057 r = prev.get(fn, -1)
1057 r = prev.get(fn, -1)
1058 if opts['all']:
1058 if opts['all']:
1059 iter = difflinestates(states, prevstates)
1059 iter = difflinestates(states, prevstates)
1060 else:
1060 else:
1061 iter = [('', l) for l in prevstates]
1061 iter = [('', l) for l in prevstates]
1062 for change, l in iter:
1062 for change, l in iter:
1063 cols = [fn, str(r)]
1063 cols = [fn, str(r)]
1064 if opts['line_number']:
1064 if opts['line_number']:
1065 cols.append(str(l.linenum))
1065 cols.append(str(l.linenum))
1066 if opts['all']:
1066 if opts['all']:
1067 cols.append(change)
1067 cols.append(change)
1068 if opts['user']:
1068 if opts['user']:
1069 cols.append(ui.shortuser(get(r)[1]))
1069 cols.append(ui.shortuser(get(r)[1]))
1070 if opts.get('date'):
1070 if opts.get('date'):
1071 cols.append(datefunc(get(r)[2]))
1071 cols.append(datefunc(get(r)[2]))
1072 if opts['files_with_matches']:
1072 if opts['files_with_matches']:
1073 c = (fn, r)
1073 c = (fn, r)
1074 if c in filerevmatches:
1074 if c in filerevmatches:
1075 continue
1075 continue
1076 filerevmatches[c] = 1
1076 filerevmatches[c] = 1
1077 else:
1077 else:
1078 cols.append(l.line)
1078 cols.append(l.line)
1079 ui.write(sep.join(cols), eol)
1079 ui.write(sep.join(cols), eol)
1080 found = True
1080 found = True
1081 return found
1081 return found
1082
1082
1083 fstate = {}
1083 fstate = {}
1084 skip = {}
1084 skip = {}
1085 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
1085 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
1086 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1086 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1087 found = False
1087 found = False
1088 follow = opts.get('follow')
1088 follow = opts.get('follow')
1089 for st, rev, fns in changeiter:
1089 for st, rev, fns in changeiter:
1090 if st == 'window':
1090 if st == 'window':
1091 matches.clear()
1091 matches.clear()
1092 elif st == 'add':
1092 elif st == 'add':
1093 ctx = repo.changectx(rev)
1093 ctx = repo.changectx(rev)
1094 matches[rev] = {}
1094 matches[rev] = {}
1095 for fn in fns:
1095 for fn in fns:
1096 if fn in skip:
1096 if fn in skip:
1097 continue
1097 continue
1098 try:
1098 try:
1099 grepbody(fn, rev, getfile(fn).read(ctx.filenode(fn)))
1099 grepbody(fn, rev, getfile(fn).read(ctx.filenode(fn)))
1100 fstate.setdefault(fn, [])
1100 fstate.setdefault(fn, [])
1101 if follow:
1101 if follow:
1102 copied = getfile(fn).renamed(ctx.filenode(fn))
1102 copied = getfile(fn).renamed(ctx.filenode(fn))
1103 if copied:
1103 if copied:
1104 copies.setdefault(rev, {})[fn] = copied[0]
1104 copies.setdefault(rev, {})[fn] = copied[0]
1105 except revlog.LookupError:
1105 except revlog.LookupError:
1106 pass
1106 pass
1107 elif st == 'iter':
1107 elif st == 'iter':
1108 states = matches[rev].items()
1108 states = matches[rev].items()
1109 states.sort()
1109 states.sort()
1110 for fn, m in states:
1110 for fn, m in states:
1111 copy = copies.get(rev, {}).get(fn)
1111 copy = copies.get(rev, {}).get(fn)
1112 if fn in skip:
1112 if fn in skip:
1113 if copy:
1113 if copy:
1114 skip[copy] = True
1114 skip[copy] = True
1115 continue
1115 continue
1116 if fn in prev or fstate[fn]:
1116 if fn in prev or fstate[fn]:
1117 r = display(fn, rev, m, fstate[fn])
1117 r = display(fn, rev, m, fstate[fn])
1118 found = found or r
1118 found = found or r
1119 if r and not opts['all']:
1119 if r and not opts['all']:
1120 skip[fn] = True
1120 skip[fn] = True
1121 if copy:
1121 if copy:
1122 skip[copy] = True
1122 skip[copy] = True
1123 fstate[fn] = m
1123 fstate[fn] = m
1124 if copy:
1124 if copy:
1125 fstate[copy] = m
1125 fstate[copy] = m
1126 prev[fn] = rev
1126 prev[fn] = rev
1127
1127
1128 fstate = fstate.items()
1128 fstate = fstate.items()
1129 fstate.sort()
1129 fstate.sort()
1130 for fn, state in fstate:
1130 for fn, state in fstate:
1131 if fn in skip:
1131 if fn in skip:
1132 continue
1132 continue
1133 if fn not in copies.get(prev[fn], {}):
1133 if fn not in copies.get(prev[fn], {}):
1134 found = display(fn, rev, {}, state) or found
1134 found = display(fn, rev, {}, state) or found
1135 return (not found and 1) or 0
1135 return (not found and 1) or 0
1136
1136
1137 def heads(ui, repo, *branchrevs, **opts):
1137 def heads(ui, repo, *branchrevs, **opts):
1138 """show current repository heads or show branch heads
1138 """show current repository heads or show branch heads
1139
1139
1140 With no arguments, show all repository head changesets.
1140 With no arguments, show all repository head changesets.
1141
1141
1142 If branch or revisions names are given this will show the heads of
1142 If branch or revisions names are given this will show the heads of
1143 the specified branches or the branches those revisions are tagged
1143 the specified branches or the branches those revisions are tagged
1144 with.
1144 with.
1145
1145
1146 Repository "heads" are changesets that don't have child
1146 Repository "heads" are changesets that don't have child
1147 changesets. They are where development generally takes place and
1147 changesets. They are where development generally takes place and
1148 are the usual targets for update and merge operations.
1148 are the usual targets for update and merge operations.
1149
1149
1150 Branch heads are changesets that have a given branch tag, but have
1150 Branch heads are changesets that have a given branch tag, but have
1151 no child changesets with that tag. They are usually where
1151 no child changesets with that tag. They are usually where
1152 development on the given branch takes place.
1152 development on the given branch takes place.
1153 """
1153 """
1154 if opts['rev']:
1154 if opts['rev']:
1155 start = repo.lookup(opts['rev'])
1155 start = repo.lookup(opts['rev'])
1156 else:
1156 else:
1157 start = None
1157 start = None
1158 if not branchrevs:
1158 if not branchrevs:
1159 # Assume we're looking repo-wide heads if no revs were specified.
1159 # Assume we're looking repo-wide heads if no revs were specified.
1160 heads = repo.heads(start)
1160 heads = repo.heads(start)
1161 else:
1161 else:
1162 heads = []
1162 heads = []
1163 visitedset = util.set()
1163 visitedset = util.set()
1164 for branchrev in branchrevs:
1164 for branchrev in branchrevs:
1165 branch = repo.changectx(branchrev).branch()
1165 branch = repo.changectx(branchrev).branch()
1166 if branch in visitedset:
1166 if branch in visitedset:
1167 continue
1167 continue
1168 visitedset.add(branch)
1168 visitedset.add(branch)
1169 bheads = repo.branchheads(branch, start)
1169 bheads = repo.branchheads(branch, start)
1170 if not bheads:
1170 if not bheads:
1171 if branch != branchrev:
1171 if branch != branchrev:
1172 ui.warn(_("no changes on branch %s containing %s are "
1172 ui.warn(_("no changes on branch %s containing %s are "
1173 "reachable from %s\n")
1173 "reachable from %s\n")
1174 % (branch, branchrev, opts['rev']))
1174 % (branch, branchrev, opts['rev']))
1175 else:
1175 else:
1176 ui.warn(_("no changes on branch %s are reachable from %s\n")
1176 ui.warn(_("no changes on branch %s are reachable from %s\n")
1177 % (branch, opts['rev']))
1177 % (branch, opts['rev']))
1178 heads.extend(bheads)
1178 heads.extend(bheads)
1179 if not heads:
1179 if not heads:
1180 return 1
1180 return 1
1181 displayer = cmdutil.show_changeset(ui, repo, opts)
1181 displayer = cmdutil.show_changeset(ui, repo, opts)
1182 for n in heads:
1182 for n in heads:
1183 displayer.show(changenode=n)
1183 displayer.show(changenode=n)
1184
1184
1185 def help_(ui, name=None, with_version=False):
1185 def help_(ui, name=None, with_version=False):
1186 """show help for a command, extension, or list of commands
1186 """show help for a command, extension, or list of commands
1187
1187
1188 With no arguments, print a list of commands and short help.
1188 With no arguments, print a list of commands and short help.
1189
1189
1190 Given a command name, print help for that command.
1190 Given a command name, print help for that command.
1191
1191
1192 Given an extension name, print help for that extension, and the
1192 Given an extension name, print help for that extension, and the
1193 commands it provides."""
1193 commands it provides."""
1194 option_lists = []
1194 option_lists = []
1195
1195
1196 def addglobalopts(aliases):
1196 def addglobalopts(aliases):
1197 if ui.verbose:
1197 if ui.verbose:
1198 option_lists.append((_("global options:"), globalopts))
1198 option_lists.append((_("global options:"), globalopts))
1199 if name == 'shortlist':
1199 if name == 'shortlist':
1200 option_lists.append((_('use "hg help" for the full list '
1200 option_lists.append((_('use "hg help" for the full list '
1201 'of commands'), ()))
1201 'of commands'), ()))
1202 else:
1202 else:
1203 if name == 'shortlist':
1203 if name == 'shortlist':
1204 msg = _('use "hg help" for the full list of commands '
1204 msg = _('use "hg help" for the full list of commands '
1205 'or "hg -v" for details')
1205 'or "hg -v" for details')
1206 elif aliases:
1206 elif aliases:
1207 msg = _('use "hg -v help%s" to show aliases and '
1207 msg = _('use "hg -v help%s" to show aliases and '
1208 'global options') % (name and " " + name or "")
1208 'global options') % (name and " " + name or "")
1209 else:
1209 else:
1210 msg = _('use "hg -v help %s" to show global options') % name
1210 msg = _('use "hg -v help %s" to show global options') % name
1211 option_lists.append((msg, ()))
1211 option_lists.append((msg, ()))
1212
1212
1213 def helpcmd(name):
1213 def helpcmd(name):
1214 if with_version:
1214 if with_version:
1215 version_(ui)
1215 version_(ui)
1216 ui.write('\n')
1216 ui.write('\n')
1217 aliases, i = cmdutil.findcmd(ui, name, table)
1217 aliases, i = cmdutil.findcmd(ui, name, table)
1218 # synopsis
1218 # synopsis
1219 ui.write("%s\n" % i[2])
1219 ui.write("%s\n" % i[2])
1220
1220
1221 # aliases
1221 # aliases
1222 if not ui.quiet and len(aliases) > 1:
1222 if not ui.quiet and len(aliases) > 1:
1223 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
1223 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
1224
1224
1225 # description
1225 # description
1226 doc = i[0].__doc__
1226 doc = i[0].__doc__
1227 if not doc:
1227 if not doc:
1228 doc = _("(No help text available)")
1228 doc = _("(No help text available)")
1229 if ui.quiet:
1229 if ui.quiet:
1230 doc = doc.splitlines(0)[0]
1230 doc = doc.splitlines(0)[0]
1231 ui.write("\n%s\n" % doc.rstrip())
1231 ui.write("\n%s\n" % doc.rstrip())
1232
1232
1233 if not ui.quiet:
1233 if not ui.quiet:
1234 # options
1234 # options
1235 if i[1]:
1235 if i[1]:
1236 option_lists.append((_("options:\n"), i[1]))
1236 option_lists.append((_("options:\n"), i[1]))
1237
1237
1238 addglobalopts(False)
1238 addglobalopts(False)
1239
1239
1240 def helplist(header, select=None):
1240 def helplist(header, select=None):
1241 h = {}
1241 h = {}
1242 cmds = {}
1242 cmds = {}
1243 for c, e in table.items():
1243 for c, e in table.items():
1244 f = c.split("|", 1)[0]
1244 f = c.split("|", 1)[0]
1245 if select and not select(f):
1245 if select and not select(f):
1246 continue
1246 continue
1247 if name == "shortlist" and not f.startswith("^"):
1247 if name == "shortlist" and not f.startswith("^"):
1248 continue
1248 continue
1249 f = f.lstrip("^")
1249 f = f.lstrip("^")
1250 if not ui.debugflag and f.startswith("debug"):
1250 if not ui.debugflag and f.startswith("debug"):
1251 continue
1251 continue
1252 doc = e[0].__doc__
1252 doc = e[0].__doc__
1253 if not doc:
1253 if not doc:
1254 doc = _("(No help text available)")
1254 doc = _("(No help text available)")
1255 h[f] = doc.splitlines(0)[0].rstrip()
1255 h[f] = doc.splitlines(0)[0].rstrip()
1256 cmds[f] = c.lstrip("^")
1256 cmds[f] = c.lstrip("^")
1257
1257
1258 if not h:
1258 if not h:
1259 ui.status(_('no commands defined\n'))
1259 ui.status(_('no commands defined\n'))
1260 return
1260 return
1261
1261
1262 ui.status(header)
1262 ui.status(header)
1263 fns = h.keys()
1263 fns = h.keys()
1264 fns.sort()
1264 fns.sort()
1265 m = max(map(len, fns))
1265 m = max(map(len, fns))
1266 for f in fns:
1266 for f in fns:
1267 if ui.verbose:
1267 if ui.verbose:
1268 commands = cmds[f].replace("|",", ")
1268 commands = cmds[f].replace("|",", ")
1269 ui.write(" %s:\n %s\n"%(commands, h[f]))
1269 ui.write(" %s:\n %s\n"%(commands, h[f]))
1270 else:
1270 else:
1271 ui.write(' %-*s %s\n' % (m, f, h[f]))
1271 ui.write(' %-*s %s\n' % (m, f, h[f]))
1272
1272
1273 if not ui.quiet:
1273 if not ui.quiet:
1274 addglobalopts(True)
1274 addglobalopts(True)
1275
1275
1276 def helptopic(name):
1276 def helptopic(name):
1277 v = None
1277 v = None
1278 for i in help.helptable:
1278 for i in help.helptable:
1279 l = i.split('|')
1279 l = i.split('|')
1280 if name in l:
1280 if name in l:
1281 v = i
1281 v = i
1282 header = l[-1]
1282 header = l[-1]
1283 if not v:
1283 if not v:
1284 raise cmdutil.UnknownCommand(name)
1284 raise cmdutil.UnknownCommand(name)
1285
1285
1286 # description
1286 # description
1287 doc = help.helptable[v]
1287 doc = help.helptable[v]
1288 if not doc:
1288 if not doc:
1289 doc = _("(No help text available)")
1289 doc = _("(No help text available)")
1290 if callable(doc):
1290 if callable(doc):
1291 doc = doc()
1291 doc = doc()
1292
1292
1293 ui.write("%s\n" % header)
1293 ui.write("%s\n" % header)
1294 ui.write("%s\n" % doc.rstrip())
1294 ui.write("%s\n" % doc.rstrip())
1295
1295
1296 def helpext(name):
1296 def helpext(name):
1297 try:
1297 try:
1298 mod = extensions.find(name)
1298 mod = extensions.find(name)
1299 except KeyError:
1299 except KeyError:
1300 raise cmdutil.UnknownCommand(name)
1300 raise cmdutil.UnknownCommand(name)
1301
1301
1302 doc = (mod.__doc__ or _('No help text available')).splitlines(0)
1302 doc = (mod.__doc__ or _('No help text available')).splitlines(0)
1303 ui.write(_('%s extension - %s\n') % (name.split('.')[-1], doc[0]))
1303 ui.write(_('%s extension - %s\n') % (name.split('.')[-1], doc[0]))
1304 for d in doc[1:]:
1304 for d in doc[1:]:
1305 ui.write(d, '\n')
1305 ui.write(d, '\n')
1306
1306
1307 ui.status('\n')
1307 ui.status('\n')
1308
1308
1309 try:
1309 try:
1310 ct = mod.cmdtable
1310 ct = mod.cmdtable
1311 except AttributeError:
1311 except AttributeError:
1312 ct = {}
1312 ct = {}
1313
1313
1314 modcmds = dict.fromkeys([c.split('|', 1)[0] for c in ct])
1314 modcmds = dict.fromkeys([c.split('|', 1)[0] for c in ct])
1315 helplist(_('list of commands:\n\n'), modcmds.has_key)
1315 helplist(_('list of commands:\n\n'), modcmds.has_key)
1316
1316
1317 if name and name != 'shortlist':
1317 if name and name != 'shortlist':
1318 i = None
1318 i = None
1319 for f in (helpcmd, helptopic, helpext):
1319 for f in (helpcmd, helptopic, helpext):
1320 try:
1320 try:
1321 f(name)
1321 f(name)
1322 i = None
1322 i = None
1323 break
1323 break
1324 except cmdutil.UnknownCommand, inst:
1324 except cmdutil.UnknownCommand, inst:
1325 i = inst
1325 i = inst
1326 if i:
1326 if i:
1327 raise i
1327 raise i
1328
1328
1329 else:
1329 else:
1330 # program name
1330 # program name
1331 if ui.verbose or with_version:
1331 if ui.verbose or with_version:
1332 version_(ui)
1332 version_(ui)
1333 else:
1333 else:
1334 ui.status(_("Mercurial Distributed SCM\n"))
1334 ui.status(_("Mercurial Distributed SCM\n"))
1335 ui.status('\n')
1335 ui.status('\n')
1336
1336
1337 # list of commands
1337 # list of commands
1338 if name == "shortlist":
1338 if name == "shortlist":
1339 header = _('basic commands:\n\n')
1339 header = _('basic commands:\n\n')
1340 else:
1340 else:
1341 header = _('list of commands:\n\n')
1341 header = _('list of commands:\n\n')
1342
1342
1343 helplist(header)
1343 helplist(header)
1344
1344
1345 # list all option lists
1345 # list all option lists
1346 opt_output = []
1346 opt_output = []
1347 for title, options in option_lists:
1347 for title, options in option_lists:
1348 opt_output.append(("\n%s" % title, None))
1348 opt_output.append(("\n%s" % title, None))
1349 for shortopt, longopt, default, desc in options:
1349 for shortopt, longopt, default, desc in options:
1350 if "DEPRECATED" in desc and not ui.verbose: continue
1350 if "DEPRECATED" in desc and not ui.verbose: continue
1351 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
1351 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
1352 longopt and " --%s" % longopt),
1352 longopt and " --%s" % longopt),
1353 "%s%s" % (desc,
1353 "%s%s" % (desc,
1354 default
1354 default
1355 and _(" (default: %s)") % default
1355 and _(" (default: %s)") % default
1356 or "")))
1356 or "")))
1357
1357
1358 if opt_output:
1358 if opt_output:
1359 opts_len = max([len(line[0]) for line in opt_output if line[1]] or [0])
1359 opts_len = max([len(line[0]) for line in opt_output if line[1]] or [0])
1360 for first, second in opt_output:
1360 for first, second in opt_output:
1361 if second:
1361 if second:
1362 ui.write(" %-*s %s\n" % (opts_len, first, second))
1362 ui.write(" %-*s %s\n" % (opts_len, first, second))
1363 else:
1363 else:
1364 ui.write("%s\n" % first)
1364 ui.write("%s\n" % first)
1365
1365
1366 def identify(ui, repo, source=None,
1366 def identify(ui, repo, source=None,
1367 rev=None, num=None, id=None, branch=None, tags=None):
1367 rev=None, num=None, id=None, branch=None, tags=None):
1368 """identify the working copy or specified revision
1368 """identify the working copy or specified revision
1369
1369
1370 With no revision, print a summary of the current state of the repo.
1370 With no revision, print a summary of the current state of the repo.
1371
1371
1372 With a path, do a lookup in another repository.
1372 With a path, do a lookup in another repository.
1373
1373
1374 This summary identifies the repository state using one or two parent
1374 This summary identifies the repository state using one or two parent
1375 hash identifiers, followed by a "+" if there are uncommitted changes
1375 hash identifiers, followed by a "+" if there are uncommitted changes
1376 in the working directory, a list of tags for this revision and a branch
1376 in the working directory, a list of tags for this revision and a branch
1377 name for non-default branches.
1377 name for non-default branches.
1378 """
1378 """
1379
1379
1380 if not repo and not source:
1380 if not repo and not source:
1381 raise util.Abort(_("There is no Mercurial repository here "
1381 raise util.Abort(_("There is no Mercurial repository here "
1382 "(.hg not found)"))
1382 "(.hg not found)"))
1383
1383
1384 hexfunc = ui.debugflag and hex or short
1384 hexfunc = ui.debugflag and hex or short
1385 default = not (num or id or branch or tags)
1385 default = not (num or id or branch or tags)
1386 output = []
1386 output = []
1387
1387
1388 if source:
1388 if source:
1389 source, revs, checkout = hg.parseurl(ui.expandpath(source), [])
1389 source, revs, checkout = hg.parseurl(ui.expandpath(source), [])
1390 srepo = hg.repository(ui, source)
1390 srepo = hg.repository(ui, source)
1391 if not rev and revs:
1391 if not rev and revs:
1392 rev = revs[0]
1392 rev = revs[0]
1393 if not rev:
1393 if not rev:
1394 rev = "tip"
1394 rev = "tip"
1395 if num or branch or tags:
1395 if num or branch or tags:
1396 raise util.Abort(
1396 raise util.Abort(
1397 "can't query remote revision number, branch, or tags")
1397 "can't query remote revision number, branch, or tags")
1398 output = [hexfunc(srepo.lookup(rev))]
1398 output = [hexfunc(srepo.lookup(rev))]
1399 elif not rev:
1399 elif not rev:
1400 ctx = repo.workingctx()
1400 ctx = repo.workingctx()
1401 parents = ctx.parents()
1401 parents = ctx.parents()
1402 changed = False
1402 changed = False
1403 if default or id or num:
1403 if default or id or num:
1404 changed = ctx.files() + ctx.deleted()
1404 changed = ctx.files() + ctx.deleted()
1405 if default or id:
1405 if default or id:
1406 output = ["%s%s" % ('+'.join([hexfunc(p.node()) for p in parents]),
1406 output = ["%s%s" % ('+'.join([hexfunc(p.node()) for p in parents]),
1407 (changed) and "+" or "")]
1407 (changed) and "+" or "")]
1408 if num:
1408 if num:
1409 output.append("%s%s" % ('+'.join([str(p.rev()) for p in parents]),
1409 output.append("%s%s" % ('+'.join([str(p.rev()) for p in parents]),
1410 (changed) and "+" or ""))
1410 (changed) and "+" or ""))
1411 else:
1411 else:
1412 ctx = repo.changectx(rev)
1412 ctx = repo.changectx(rev)
1413 if default or id:
1413 if default or id:
1414 output = [hexfunc(ctx.node())]
1414 output = [hexfunc(ctx.node())]
1415 if num:
1415 if num:
1416 output.append(str(ctx.rev()))
1416 output.append(str(ctx.rev()))
1417
1417
1418 if not source and default and not ui.quiet:
1418 if not source and default and not ui.quiet:
1419 b = util.tolocal(ctx.branch())
1419 b = util.tolocal(ctx.branch())
1420 if b != 'default':
1420 if b != 'default':
1421 output.append("(%s)" % b)
1421 output.append("(%s)" % b)
1422
1422
1423 # multiple tags for a single parent separated by '/'
1423 # multiple tags for a single parent separated by '/'
1424 t = "/".join(ctx.tags())
1424 t = "/".join(ctx.tags())
1425 if t:
1425 if t:
1426 output.append(t)
1426 output.append(t)
1427
1427
1428 if branch:
1428 if branch:
1429 output.append(util.tolocal(ctx.branch()))
1429 output.append(util.tolocal(ctx.branch()))
1430
1430
1431 if tags:
1431 if tags:
1432 output.extend(ctx.tags())
1432 output.extend(ctx.tags())
1433
1433
1434 ui.write("%s\n" % ' '.join(output))
1434 ui.write("%s\n" % ' '.join(output))
1435
1435
1436 def import_(ui, repo, patch1, *patches, **opts):
1436 def import_(ui, repo, patch1, *patches, **opts):
1437 """import an ordered set of patches
1437 """import an ordered set of patches
1438
1438
1439 Import a list of patches and commit them individually.
1439 Import a list of patches and commit them individually.
1440
1440
1441 If there are outstanding changes in the working directory, import
1441 If there are outstanding changes in the working directory, import
1442 will abort unless given the -f flag.
1442 will abort unless given the -f flag.
1443
1443
1444 You can import a patch straight from a mail message. Even patches
1444 You can import a patch straight from a mail message. Even patches
1445 as attachments work (body part must be type text/plain or
1445 as attachments work (body part must be type text/plain or
1446 text/x-patch to be used). From and Subject headers of email
1446 text/x-patch to be used). From and Subject headers of email
1447 message are used as default committer and commit message. All
1447 message are used as default committer and commit message. All
1448 text/plain body parts before first diff are added to commit
1448 text/plain body parts before first diff are added to commit
1449 message.
1449 message.
1450
1450
1451 If the imported patch was generated by hg export, user and description
1451 If the imported patch was generated by hg export, user and description
1452 from patch override values from message headers and body. Values
1452 from patch override values from message headers and body. Values
1453 given on command line with -m and -u override these.
1453 given on command line with -m and -u override these.
1454
1454
1455 If --exact is specified, import will set the working directory
1455 If --exact is specified, import will set the working directory
1456 to the parent of each patch before applying it, and will abort
1456 to the parent of each patch before applying it, and will abort
1457 if the resulting changeset has a different ID than the one
1457 if the resulting changeset has a different ID than the one
1458 recorded in the patch. This may happen due to character set
1458 recorded in the patch. This may happen due to character set
1459 problems or other deficiencies in the text patch format.
1459 problems or other deficiencies in the text patch format.
1460
1460
1461 To read a patch from standard input, use patch name "-".
1461 To read a patch from standard input, use patch name "-".
1462 See 'hg help dates' for a list of formats valid for -d/--date.
1462 See 'hg help dates' for a list of formats valid for -d/--date.
1463 """
1463 """
1464 patches = (patch1,) + patches
1464 patches = (patch1,) + patches
1465
1465
1466 date = opts.get('date')
1466 date = opts.get('date')
1467 if date:
1467 if date:
1468 opts['date'] = util.parsedate(date)
1468 opts['date'] = util.parsedate(date)
1469
1469
1470 if opts.get('exact') or not opts['force']:
1470 if opts.get('exact') or not opts['force']:
1471 cmdutil.bail_if_changed(repo)
1471 cmdutil.bail_if_changed(repo)
1472
1472
1473 d = opts["base"]
1473 d = opts["base"]
1474 strip = opts["strip"]
1474 strip = opts["strip"]
1475 wlock = lock = None
1475 wlock = lock = None
1476 try:
1476 try:
1477 wlock = repo.wlock()
1477 wlock = repo.wlock()
1478 lock = repo.lock()
1478 lock = repo.lock()
1479 for p in patches:
1479 for p in patches:
1480 pf = os.path.join(d, p)
1480 pf = os.path.join(d, p)
1481
1481
1482 if pf == '-':
1482 if pf == '-':
1483 ui.status(_("applying patch from stdin\n"))
1483 ui.status(_("applying patch from stdin\n"))
1484 data = patch.extract(ui, sys.stdin)
1484 data = patch.extract(ui, sys.stdin)
1485 else:
1485 else:
1486 ui.status(_("applying %s\n") % p)
1486 ui.status(_("applying %s\n") % p)
1487 if os.path.exists(pf):
1487 if os.path.exists(pf):
1488 data = patch.extract(ui, file(pf, 'rb'))
1488 data = patch.extract(ui, file(pf, 'rb'))
1489 else:
1489 else:
1490 data = patch.extract(ui, urllib.urlopen(pf))
1490 data = patch.extract(ui, urllib.urlopen(pf))
1491 tmpname, message, user, date, branch, nodeid, p1, p2 = data
1491 tmpname, message, user, date, branch, nodeid, p1, p2 = data
1492
1492
1493 if tmpname is None:
1493 if tmpname is None:
1494 raise util.Abort(_('no diffs found'))
1494 raise util.Abort(_('no diffs found'))
1495
1495
1496 try:
1496 try:
1497 cmdline_message = cmdutil.logmessage(opts)
1497 cmdline_message = cmdutil.logmessage(opts)
1498 if cmdline_message:
1498 if cmdline_message:
1499 # pickup the cmdline msg
1499 # pickup the cmdline msg
1500 message = cmdline_message
1500 message = cmdline_message
1501 elif message:
1501 elif message:
1502 # pickup the patch msg
1502 # pickup the patch msg
1503 message = message.strip()
1503 message = message.strip()
1504 else:
1504 else:
1505 # launch the editor
1505 # launch the editor
1506 message = None
1506 message = None
1507 ui.debug(_('message:\n%s\n') % message)
1507 ui.debug(_('message:\n%s\n') % message)
1508
1508
1509 wp = repo.workingctx().parents()
1509 wp = repo.workingctx().parents()
1510 if opts.get('exact'):
1510 if opts.get('exact'):
1511 if not nodeid or not p1:
1511 if not nodeid or not p1:
1512 raise util.Abort(_('not a mercurial patch'))
1512 raise util.Abort(_('not a mercurial patch'))
1513 p1 = repo.lookup(p1)
1513 p1 = repo.lookup(p1)
1514 p2 = repo.lookup(p2 or hex(nullid))
1514 p2 = repo.lookup(p2 or hex(nullid))
1515
1515
1516 if p1 != wp[0].node():
1516 if p1 != wp[0].node():
1517 hg.clean(repo, p1)
1517 hg.clean(repo, p1)
1518 repo.dirstate.setparents(p1, p2)
1518 repo.dirstate.setparents(p1, p2)
1519 elif p2:
1519 elif p2:
1520 try:
1520 try:
1521 p1 = repo.lookup(p1)
1521 p1 = repo.lookup(p1)
1522 p2 = repo.lookup(p2)
1522 p2 = repo.lookup(p2)
1523 if p1 == wp[0].node():
1523 if p1 == wp[0].node():
1524 repo.dirstate.setparents(p1, p2)
1524 repo.dirstate.setparents(p1, p2)
1525 except hg.RepoError:
1525 except hg.RepoError:
1526 pass
1526 pass
1527 if opts.get('exact') or opts.get('import_branch'):
1527 if opts.get('exact') or opts.get('import_branch'):
1528 repo.dirstate.setbranch(branch or 'default')
1528 repo.dirstate.setbranch(branch or 'default')
1529
1529
1530 files = {}
1530 files = {}
1531 try:
1531 try:
1532 fuzz = patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
1532 fuzz = patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
1533 files=files)
1533 files=files)
1534 finally:
1534 finally:
1535 files = patch.updatedir(ui, repo, files)
1535 files = patch.updatedir(ui, repo, files)
1536 if not opts.get('no_commit'):
1536 if not opts.get('no_commit'):
1537 n = repo.commit(files, message, opts.get('user') or user,
1537 n = repo.commit(files, message, opts.get('user') or user,
1538 opts.get('date') or date)
1538 opts.get('date') or date)
1539 if opts.get('exact'):
1539 if opts.get('exact'):
1540 if hex(n) != nodeid:
1540 if hex(n) != nodeid:
1541 repo.rollback()
1541 repo.rollback()
1542 raise util.Abort(_('patch is damaged'
1542 raise util.Abort(_('patch is damaged'
1543 ' or loses information'))
1543 ' or loses information'))
1544 # Force a dirstate write so that the next transaction
1544 # Force a dirstate write so that the next transaction
1545 # backups an up-do-date file.
1545 # backups an up-do-date file.
1546 repo.dirstate.write()
1546 repo.dirstate.write()
1547 finally:
1547 finally:
1548 os.unlink(tmpname)
1548 os.unlink(tmpname)
1549 finally:
1549 finally:
1550 del lock, wlock
1550 del lock, wlock
1551
1551
1552 def incoming(ui, repo, source="default", **opts):
1552 def incoming(ui, repo, source="default", **opts):
1553 """show new changesets found in source
1553 """show new changesets found in source
1554
1554
1555 Show new changesets found in the specified path/URL or the default
1555 Show new changesets found in the specified path/URL or the default
1556 pull location. These are the changesets that would be pulled if a pull
1556 pull location. These are the changesets that would be pulled if a pull
1557 was requested.
1557 was requested.
1558
1558
1559 For remote repository, using --bundle avoids downloading the changesets
1559 For remote repository, using --bundle avoids downloading the changesets
1560 twice if the incoming is followed by a pull.
1560 twice if the incoming is followed by a pull.
1561
1561
1562 See pull for valid source format details.
1562 See pull for valid source format details.
1563 """
1563 """
1564 limit = cmdutil.loglimit(opts)
1564 limit = cmdutil.loglimit(opts)
1565 source, revs, checkout = hg.parseurl(ui.expandpath(source), opts['rev'])
1565 source, revs, checkout = hg.parseurl(ui.expandpath(source), opts['rev'])
1566 cmdutil.setremoteconfig(ui, opts)
1566 cmdutil.setremoteconfig(ui, opts)
1567
1567
1568 other = hg.repository(ui, source)
1568 other = hg.repository(ui, source)
1569 ui.status(_('comparing with %s\n') % util.hidepassword(source))
1569 ui.status(_('comparing with %s\n') % util.hidepassword(source))
1570 if revs:
1570 if revs:
1571 revs = [other.lookup(rev) for rev in revs]
1571 revs = [other.lookup(rev) for rev in revs]
1572 incoming = repo.findincoming(other, heads=revs, force=opts["force"])
1572 incoming = repo.findincoming(other, heads=revs, force=opts["force"])
1573 if not incoming:
1573 if not incoming:
1574 try:
1574 try:
1575 os.unlink(opts["bundle"])
1575 os.unlink(opts["bundle"])
1576 except:
1576 except:
1577 pass
1577 pass
1578 ui.status(_("no changes found\n"))
1578 ui.status(_("no changes found\n"))
1579 return 1
1579 return 1
1580
1580
1581 cleanup = None
1581 cleanup = None
1582 try:
1582 try:
1583 fname = opts["bundle"]
1583 fname = opts["bundle"]
1584 if fname or not other.local():
1584 if fname or not other.local():
1585 # create a bundle (uncompressed if other repo is not local)
1585 # create a bundle (uncompressed if other repo is not local)
1586 if revs is None:
1586 if revs is None:
1587 cg = other.changegroup(incoming, "incoming")
1587 cg = other.changegroup(incoming, "incoming")
1588 else:
1588 else:
1589 cg = other.changegroupsubset(incoming, revs, 'incoming')
1589 cg = other.changegroupsubset(incoming, revs, 'incoming')
1590 bundletype = other.local() and "HG10BZ" or "HG10UN"
1590 bundletype = other.local() and "HG10BZ" or "HG10UN"
1591 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
1591 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
1592 # keep written bundle?
1592 # keep written bundle?
1593 if opts["bundle"]:
1593 if opts["bundle"]:
1594 cleanup = None
1594 cleanup = None
1595 if not other.local():
1595 if not other.local():
1596 # use the created uncompressed bundlerepo
1596 # use the created uncompressed bundlerepo
1597 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1597 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1598
1598
1599 o = other.changelog.nodesbetween(incoming, revs)[0]
1599 o = other.changelog.nodesbetween(incoming, revs)[0]
1600 if opts['newest_first']:
1600 if opts['newest_first']:
1601 o.reverse()
1601 o.reverse()
1602 displayer = cmdutil.show_changeset(ui, other, opts)
1602 displayer = cmdutil.show_changeset(ui, other, opts)
1603 count = 0
1603 count = 0
1604 for n in o:
1604 for n in o:
1605 if count >= limit:
1605 if count >= limit:
1606 break
1606 break
1607 parents = [p for p in other.changelog.parents(n) if p != nullid]
1607 parents = [p for p in other.changelog.parents(n) if p != nullid]
1608 if opts['no_merges'] and len(parents) == 2:
1608 if opts['no_merges'] and len(parents) == 2:
1609 continue
1609 continue
1610 count += 1
1610 count += 1
1611 displayer.show(changenode=n)
1611 displayer.show(changenode=n)
1612 finally:
1612 finally:
1613 if hasattr(other, 'close'):
1613 if hasattr(other, 'close'):
1614 other.close()
1614 other.close()
1615 if cleanup:
1615 if cleanup:
1616 os.unlink(cleanup)
1616 os.unlink(cleanup)
1617
1617
1618 def init(ui, dest=".", **opts):
1618 def init(ui, dest=".", **opts):
1619 """create a new repository in the given directory
1619 """create a new repository in the given directory
1620
1620
1621 Initialize a new repository in the given directory. If the given
1621 Initialize a new repository in the given directory. If the given
1622 directory does not exist, it is created.
1622 directory does not exist, it is created.
1623
1623
1624 If no directory is given, the current directory is used.
1624 If no directory is given, the current directory is used.
1625
1625
1626 It is possible to specify an ssh:// URL as the destination.
1626 It is possible to specify an ssh:// URL as the destination.
1627 Look at the help text for the pull command for important details
1627 Look at the help text for the pull command for important details
1628 about ssh:// URLs.
1628 about ssh:// URLs.
1629 """
1629 """
1630 cmdutil.setremoteconfig(ui, opts)
1630 cmdutil.setremoteconfig(ui, opts)
1631 hg.repository(ui, dest, create=1)
1631 hg.repository(ui, dest, create=1)
1632
1632
1633 def locate(ui, repo, *pats, **opts):
1633 def locate(ui, repo, *pats, **opts):
1634 """locate files matching specific patterns
1634 """locate files matching specific patterns
1635
1635
1636 Print all files under Mercurial control whose names match the
1636 Print all files under Mercurial control whose names match the
1637 given patterns.
1637 given patterns.
1638
1638
1639 This command searches the entire repository by default. To search
1639 This command searches the entire repository by default. To search
1640 just the current directory and its subdirectories, use
1640 just the current directory and its subdirectories, use
1641 "--include .".
1641 "--include .".
1642
1642
1643 If no patterns are given to match, this command prints all file
1643 If no patterns are given to match, this command prints all file
1644 names.
1644 names.
1645
1645
1646 If you want to feed the output of this command into the "xargs"
1646 If you want to feed the output of this command into the "xargs"
1647 command, use the "-0" option to both this command and "xargs".
1647 command, use the "-0" option to both this command and "xargs".
1648 This will avoid the problem of "xargs" treating single filenames
1648 This will avoid the problem of "xargs" treating single filenames
1649 that contain white space as multiple filenames.
1649 that contain white space as multiple filenames.
1650 """
1650 """
1651 end = opts['print0'] and '\0' or '\n'
1651 end = opts['print0'] and '\0' or '\n'
1652 rev = opts['rev']
1652 rev = opts['rev']
1653 if rev:
1653 if rev:
1654 node = repo.lookup(rev)
1654 node = repo.lookup(rev)
1655 else:
1655 else:
1656 node = None
1656 node = None
1657
1657
1658 ret = 1
1658 ret = 1
1659 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
1659 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
1660 badmatch=util.always,
1660 badmatch=util.always,
1661 default='relglob'):
1661 default='relglob'):
1662 if src == 'b':
1662 if src == 'b':
1663 continue
1663 continue
1664 if not node and abs not in repo.dirstate:
1664 if not node and abs not in repo.dirstate:
1665 continue
1665 continue
1666 if opts['fullpath']:
1666 if opts['fullpath']:
1667 ui.write(os.path.join(repo.root, abs), end)
1667 ui.write(os.path.join(repo.root, abs), end)
1668 else:
1668 else:
1669 ui.write(((pats and rel) or abs), end)
1669 ui.write(((pats and rel) or abs), end)
1670 ret = 0
1670 ret = 0
1671
1671
1672 return ret
1672 return ret
1673
1673
1674 def log(ui, repo, *pats, **opts):
1674 def log(ui, repo, *pats, **opts):
1675 """show revision history of entire repository or files
1675 """show revision history of entire repository or files
1676
1676
1677 Print the revision history of the specified files or the entire
1677 Print the revision history of the specified files or the entire
1678 project.
1678 project.
1679
1679
1680 File history is shown without following rename or copy history of
1680 File history is shown without following rename or copy history of
1681 files. Use -f/--follow with a file name to follow history across
1681 files. Use -f/--follow with a file name to follow history across
1682 renames and copies. --follow without a file name will only show
1682 renames and copies. --follow without a file name will only show
1683 ancestors or descendants of the starting revision. --follow-first
1683 ancestors or descendants of the starting revision. --follow-first
1684 only follows the first parent of merge revisions.
1684 only follows the first parent of merge revisions.
1685
1685
1686 If no revision range is specified, the default is tip:0 unless
1686 If no revision range is specified, the default is tip:0 unless
1687 --follow is set, in which case the working directory parent is
1687 --follow is set, in which case the working directory parent is
1688 used as the starting revision.
1688 used as the starting revision.
1689
1689
1690 See 'hg help dates' for a list of formats valid for -d/--date.
1690 See 'hg help dates' for a list of formats valid for -d/--date.
1691
1691
1692 By default this command outputs: changeset id and hash, tags,
1692 By default this command outputs: changeset id and hash, tags,
1693 non-trivial parents, user, date and time, and a summary for each
1693 non-trivial parents, user, date and time, and a summary for each
1694 commit. When the -v/--verbose switch is used, the list of changed
1694 commit. When the -v/--verbose switch is used, the list of changed
1695 files and full commit message is shown.
1695 files and full commit message is shown.
1696
1696
1697 NOTE: log -p may generate unexpected diff output for merge
1697 NOTE: log -p may generate unexpected diff output for merge
1698 changesets, as it will compare the merge changeset against its
1698 changesets, as it will compare the merge changeset against its
1699 first parent only. Also, the files: list will only reflect files
1699 first parent only. Also, the files: list will only reflect files
1700 that are different from BOTH parents.
1700 that are different from BOTH parents.
1701
1701
1702 """
1702 """
1703
1703
1704 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
1704 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
1705 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1705 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1706
1706
1707 limit = cmdutil.loglimit(opts)
1707 limit = cmdutil.loglimit(opts)
1708 count = 0
1708 count = 0
1709
1709
1710 if opts['copies'] and opts['rev']:
1710 if opts['copies'] and opts['rev']:
1711 endrev = max(cmdutil.revrange(repo, opts['rev'])) + 1
1711 endrev = max(cmdutil.revrange(repo, opts['rev'])) + 1
1712 else:
1712 else:
1713 endrev = repo.changelog.count()
1713 endrev = repo.changelog.count()
1714 rcache = {}
1714 rcache = {}
1715 ncache = {}
1715 ncache = {}
1716 def getrenamed(fn, rev):
1716 def getrenamed(fn, rev):
1717 '''looks up all renames for a file (up to endrev) the first
1717 '''looks up all renames for a file (up to endrev) the first
1718 time the file is given. It indexes on the changerev and only
1718 time the file is given. It indexes on the changerev and only
1719 parses the manifest if linkrev != changerev.
1719 parses the manifest if linkrev != changerev.
1720 Returns rename info for fn at changerev rev.'''
1720 Returns rename info for fn at changerev rev.'''
1721 if fn not in rcache:
1721 if fn not in rcache:
1722 rcache[fn] = {}
1722 rcache[fn] = {}
1723 ncache[fn] = {}
1723 ncache[fn] = {}
1724 fl = repo.file(fn)
1724 fl = repo.file(fn)
1725 for i in xrange(fl.count()):
1725 for i in xrange(fl.count()):
1726 node = fl.node(i)
1726 node = fl.node(i)
1727 lr = fl.linkrev(node)
1727 lr = fl.linkrev(node)
1728 renamed = fl.renamed(node)
1728 renamed = fl.renamed(node)
1729 rcache[fn][lr] = renamed
1729 rcache[fn][lr] = renamed
1730 if renamed:
1730 if renamed:
1731 ncache[fn][node] = renamed
1731 ncache[fn][node] = renamed
1732 if lr >= endrev:
1732 if lr >= endrev:
1733 break
1733 break
1734 if rev in rcache[fn]:
1734 if rev in rcache[fn]:
1735 return rcache[fn][rev]
1735 return rcache[fn][rev]
1736
1736
1737 # If linkrev != rev (i.e. rev not found in rcache) fallback to
1737 # If linkrev != rev (i.e. rev not found in rcache) fallback to
1738 # filectx logic.
1738 # filectx logic.
1739
1739
1740 try:
1740 try:
1741 return repo.changectx(rev).filectx(fn).renamed()
1741 return repo.changectx(rev).filectx(fn).renamed()
1742 except revlog.LookupError:
1742 except revlog.LookupError:
1743 pass
1743 pass
1744 return None
1744 return None
1745
1745
1746 df = False
1746 df = False
1747 if opts["date"]:
1747 if opts["date"]:
1748 df = util.matchdate(opts["date"])
1748 df = util.matchdate(opts["date"])
1749
1749
1750 only_branches = opts['only_branch']
1750 only_branches = opts['only_branch']
1751
1751
1752 displayer = cmdutil.show_changeset(ui, repo, opts, True, matchfn)
1752 displayer = cmdutil.show_changeset(ui, repo, opts, True, matchfn)
1753 for st, rev, fns in changeiter:
1753 for st, rev, fns in changeiter:
1754 if st == 'add':
1754 if st == 'add':
1755 changenode = repo.changelog.node(rev)
1755 changenode = repo.changelog.node(rev)
1756 parents = [p for p in repo.changelog.parentrevs(rev)
1756 parents = [p for p in repo.changelog.parentrevs(rev)
1757 if p != nullrev]
1757 if p != nullrev]
1758 if opts['no_merges'] and len(parents) == 2:
1758 if opts['no_merges'] and len(parents) == 2:
1759 continue
1759 continue
1760 if opts['only_merges'] and len(parents) != 2:
1760 if opts['only_merges'] and len(parents) != 2:
1761 continue
1761 continue
1762
1762
1763 if only_branches:
1763 if only_branches:
1764 revbranch = get(rev)[5]['branch']
1764 revbranch = get(rev)[5]['branch']
1765 if revbranch not in only_branches:
1765 if revbranch not in only_branches:
1766 continue
1766 continue
1767
1767
1768 if df:
1768 if df:
1769 changes = get(rev)
1769 changes = get(rev)
1770 if not df(changes[2][0]):
1770 if not df(changes[2][0]):
1771 continue
1771 continue
1772
1772
1773 if opts['keyword']:
1773 if opts['keyword']:
1774 changes = get(rev)
1774 changes = get(rev)
1775 miss = 0
1775 miss = 0
1776 for k in [kw.lower() for kw in opts['keyword']]:
1776 for k in [kw.lower() for kw in opts['keyword']]:
1777 if not (k in changes[1].lower() or
1777 if not (k in changes[1].lower() or
1778 k in changes[4].lower() or
1778 k in changes[4].lower() or
1779 k in " ".join(changes[3]).lower()):
1779 k in " ".join(changes[3]).lower()):
1780 miss = 1
1780 miss = 1
1781 break
1781 break
1782 if miss:
1782 if miss:
1783 continue
1783 continue
1784
1784
1785 copies = []
1785 copies = []
1786 if opts.get('copies') and rev:
1786 if opts.get('copies') and rev:
1787 for fn in get(rev)[3]:
1787 for fn in get(rev)[3]:
1788 rename = getrenamed(fn, rev)
1788 rename = getrenamed(fn, rev)
1789 if rename:
1789 if rename:
1790 copies.append((fn, rename[0]))
1790 copies.append((fn, rename[0]))
1791 displayer.show(rev, changenode, copies=copies)
1791 displayer.show(rev, changenode, copies=copies)
1792 elif st == 'iter':
1792 elif st == 'iter':
1793 if count == limit: break
1793 if count == limit: break
1794 if displayer.flush(rev):
1794 if displayer.flush(rev):
1795 count += 1
1795 count += 1
1796
1796
1797 def manifest(ui, repo, node=None, rev=None):
1797 def manifest(ui, repo, node=None, rev=None):
1798 """output the current or given revision of the project manifest
1798 """output the current or given revision of the project manifest
1799
1799
1800 Print a list of version controlled files for the given revision.
1800 Print a list of version controlled files for the given revision.
1801 If no revision is given, the parent of the working directory is used,
1801 If no revision is given, the parent of the working directory is used,
1802 or tip if no revision is checked out.
1802 or tip if no revision is checked out.
1803
1803
1804 The manifest is the list of files being version controlled. If no revision
1804 The manifest is the list of files being version controlled. If no revision
1805 is given then the first parent of the working directory is used.
1805 is given then the first parent of the working directory is used.
1806
1806
1807 With -v flag, print file permissions, symlink and executable bits. With
1807 With -v flag, print file permissions, symlink and executable bits. With
1808 --debug flag, print file revision hashes.
1808 --debug flag, print file revision hashes.
1809 """
1809 """
1810
1810
1811 if rev and node:
1811 if rev and node:
1812 raise util.Abort(_("please specify just one revision"))
1812 raise util.Abort(_("please specify just one revision"))
1813
1813
1814 if not node:
1814 if not node:
1815 node = rev
1815 node = rev
1816
1816
1817 m = repo.changectx(node).manifest()
1817 m = repo.changectx(node).manifest()
1818 files = m.keys()
1818 files = m.keys()
1819 files.sort()
1819 files.sort()
1820
1820
1821 for f in files:
1821 for f in files:
1822 if ui.debugflag:
1822 if ui.debugflag:
1823 ui.write("%40s " % hex(m[f]))
1823 ui.write("%40s " % hex(m[f]))
1824 if ui.verbose:
1824 if ui.verbose:
1825 type = m.execf(f) and "*" or m.linkf(f) and "@" or " "
1825 type = m.execf(f) and "*" or m.linkf(f) and "@" or " "
1826 perm = m.execf(f) and "755" or "644"
1826 perm = m.execf(f) and "755" or "644"
1827 ui.write("%3s %1s " % (perm, type))
1827 ui.write("%3s %1s " % (perm, type))
1828 ui.write("%s\n" % f)
1828 ui.write("%s\n" % f)
1829
1829
1830 def merge(ui, repo, node=None, force=None, rev=None):
1830 def merge(ui, repo, node=None, force=None, rev=None):
1831 """merge working directory with another revision
1831 """merge working directory with another revision
1832
1832
1833 Merge the contents of the current working directory and the
1833 Merge the contents of the current working directory and the
1834 requested revision. Files that changed between either parent are
1834 requested revision. Files that changed between either parent are
1835 marked as changed for the next commit and a commit must be
1835 marked as changed for the next commit and a commit must be
1836 performed before any further updates are allowed.
1836 performed before any further updates are allowed.
1837
1837
1838 If no revision is specified, the working directory's parent is a
1838 If no revision is specified, the working directory's parent is a
1839 head revision, and the repository contains exactly one other head,
1839 head revision, and the repository contains exactly one other head,
1840 the other head is merged with by default. Otherwise, an explicit
1840 the other head is merged with by default. Otherwise, an explicit
1841 revision to merge with must be provided.
1841 revision to merge with must be provided.
1842 """
1842 """
1843
1843
1844 if rev and node:
1844 if rev and node:
1845 raise util.Abort(_("please specify just one revision"))
1845 raise util.Abort(_("please specify just one revision"))
1846 if not node:
1846 if not node:
1847 node = rev
1847 node = rev
1848
1848
1849 if not node:
1849 if not node:
1850 heads = repo.heads()
1850 heads = repo.heads()
1851 if len(heads) > 2:
1851 if len(heads) > 2:
1852 raise util.Abort(_('repo has %d heads - '
1852 raise util.Abort(_('repo has %d heads - '
1853 'please merge with an explicit rev') %
1853 'please merge with an explicit rev') %
1854 len(heads))
1854 len(heads))
1855 parent = repo.dirstate.parents()[0]
1855 parent = repo.dirstate.parents()[0]
1856 if len(heads) == 1:
1856 if len(heads) == 1:
1857 msg = _('there is nothing to merge')
1857 msg = _('there is nothing to merge')
1858 if parent != repo.lookup(repo.workingctx().branch()):
1858 if parent != repo.lookup(repo.workingctx().branch()):
1859 msg = _('%s - use "hg update" instead') % msg
1859 msg = _('%s - use "hg update" instead') % msg
1860 raise util.Abort(msg)
1860 raise util.Abort(msg)
1861
1861
1862 if parent not in heads:
1862 if parent not in heads:
1863 raise util.Abort(_('working dir not at a head rev - '
1863 raise util.Abort(_('working dir not at a head rev - '
1864 'use "hg update" or merge with an explicit rev'))
1864 'use "hg update" or merge with an explicit rev'))
1865 node = parent == heads[0] and heads[-1] or heads[0]
1865 node = parent == heads[0] and heads[-1] or heads[0]
1866 return hg.merge(repo, node, force=force)
1866 return hg.merge(repo, node, force=force)
1867
1867
1868 def outgoing(ui, repo, dest=None, **opts):
1868 def outgoing(ui, repo, dest=None, **opts):
1869 """show changesets not found in destination
1869 """show changesets not found in destination
1870
1870
1871 Show changesets not found in the specified destination repository or
1871 Show changesets not found in the specified destination repository or
1872 the default push location. These are the changesets that would be pushed
1872 the default push location. These are the changesets that would be pushed
1873 if a push was requested.
1873 if a push was requested.
1874
1874
1875 See pull for valid destination format details.
1875 See pull for valid destination format details.
1876 """
1876 """
1877 limit = cmdutil.loglimit(opts)
1877 limit = cmdutil.loglimit(opts)
1878 dest, revs, checkout = hg.parseurl(
1878 dest, revs, checkout = hg.parseurl(
1879 ui.expandpath(dest or 'default-push', dest or 'default'), opts['rev'])
1879 ui.expandpath(dest or 'default-push', dest or 'default'), opts['rev'])
1880 cmdutil.setremoteconfig(ui, opts)
1880 cmdutil.setremoteconfig(ui, opts)
1881 if revs:
1881 if revs:
1882 revs = [repo.lookup(rev) for rev in revs]
1882 revs = [repo.lookup(rev) for rev in revs]
1883
1883
1884 other = hg.repository(ui, dest)
1884 other = hg.repository(ui, dest)
1885 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
1885 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
1886 o = repo.findoutgoing(other, force=opts['force'])
1886 o = repo.findoutgoing(other, force=opts['force'])
1887 if not o:
1887 if not o:
1888 ui.status(_("no changes found\n"))
1888 ui.status(_("no changes found\n"))
1889 return 1
1889 return 1
1890 o = repo.changelog.nodesbetween(o, revs)[0]
1890 o = repo.changelog.nodesbetween(o, revs)[0]
1891 if opts['newest_first']:
1891 if opts['newest_first']:
1892 o.reverse()
1892 o.reverse()
1893 displayer = cmdutil.show_changeset(ui, repo, opts)
1893 displayer = cmdutil.show_changeset(ui, repo, opts)
1894 count = 0
1894 count = 0
1895 for n in o:
1895 for n in o:
1896 if count >= limit:
1896 if count >= limit:
1897 break
1897 break
1898 parents = [p for p in repo.changelog.parents(n) if p != nullid]
1898 parents = [p for p in repo.changelog.parents(n) if p != nullid]
1899 if opts['no_merges'] and len(parents) == 2:
1899 if opts['no_merges'] and len(parents) == 2:
1900 continue
1900 continue
1901 count += 1
1901 count += 1
1902 displayer.show(changenode=n)
1902 displayer.show(changenode=n)
1903
1903
1904 def parents(ui, repo, file_=None, **opts):
1904 def parents(ui, repo, file_=None, **opts):
1905 """show the parents of the working dir or revision
1905 """show the parents of the working dir or revision
1906
1906
1907 Print the working directory's parent revisions. If a
1907 Print the working directory's parent revisions. If a
1908 revision is given via --rev, the parent of that revision
1908 revision is given via --rev, the parent of that revision
1909 will be printed. If a file argument is given, revision in
1909 will be printed. If a file argument is given, revision in
1910 which the file was last changed (before the working directory
1910 which the file was last changed (before the working directory
1911 revision or the argument to --rev if given) is printed.
1911 revision or the argument to --rev if given) is printed.
1912 """
1912 """
1913 rev = opts.get('rev')
1913 rev = opts.get('rev')
1914 if rev:
1914 if rev:
1915 ctx = repo.changectx(rev)
1915 ctx = repo.changectx(rev)
1916 else:
1916 else:
1917 ctx = repo.workingctx()
1917 ctx = repo.workingctx()
1918
1918
1919 if file_:
1919 if file_:
1920 files, match, anypats = cmdutil.matchpats(repo, (file_,), opts)
1920 files, match, anypats = cmdutil.matchpats(repo, (file_,), opts)
1921 if anypats or len(files) != 1:
1921 if anypats or len(files) != 1:
1922 raise util.Abort(_('can only specify an explicit file name'))
1922 raise util.Abort(_('can only specify an explicit file name'))
1923 file_ = files[0]
1923 file_ = files[0]
1924 filenodes = []
1924 filenodes = []
1925 for cp in ctx.parents():
1925 for cp in ctx.parents():
1926 if not cp:
1926 if not cp:
1927 continue
1927 continue
1928 try:
1928 try:
1929 filenodes.append(cp.filenode(file_))
1929 filenodes.append(cp.filenode(file_))
1930 except revlog.LookupError:
1930 except revlog.LookupError:
1931 pass
1931 pass
1932 if not filenodes:
1932 if not filenodes:
1933 raise util.Abort(_("'%s' not found in manifest!") % file_)
1933 raise util.Abort(_("'%s' not found in manifest!") % file_)
1934 fl = repo.file(file_)
1934 fl = repo.file(file_)
1935 p = [repo.lookup(fl.linkrev(fn)) for fn in filenodes]
1935 p = [repo.lookup(fl.linkrev(fn)) for fn in filenodes]
1936 else:
1936 else:
1937 p = [cp.node() for cp in ctx.parents()]
1937 p = [cp.node() for cp in ctx.parents()]
1938
1938
1939 displayer = cmdutil.show_changeset(ui, repo, opts)
1939 displayer = cmdutil.show_changeset(ui, repo, opts)
1940 for n in p:
1940 for n in p:
1941 if n != nullid:
1941 if n != nullid:
1942 displayer.show(changenode=n)
1942 displayer.show(changenode=n)
1943
1943
1944 def paths(ui, repo, search=None):
1944 def paths(ui, repo, search=None):
1945 """show definition of symbolic path names
1945 """show definition of symbolic path names
1946
1946
1947 Show definition of symbolic path name NAME. If no name is given, show
1947 Show definition of symbolic path name NAME. If no name is given, show
1948 definition of available names.
1948 definition of available names.
1949
1949
1950 Path names are defined in the [paths] section of /etc/mercurial/hgrc
1950 Path names are defined in the [paths] section of /etc/mercurial/hgrc
1951 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
1951 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
1952 """
1952 """
1953 if search:
1953 if search:
1954 for name, path in ui.configitems("paths"):
1954 for name, path in ui.configitems("paths"):
1955 if name == search:
1955 if name == search:
1956 ui.write("%s\n" % path)
1956 ui.write("%s\n" % path)
1957 return
1957 return
1958 ui.warn(_("not found!\n"))
1958 ui.warn(_("not found!\n"))
1959 return 1
1959 return 1
1960 else:
1960 else:
1961 for name, path in ui.configitems("paths"):
1961 for name, path in ui.configitems("paths"):
1962 ui.write("%s = %s\n" % (name, path))
1962 ui.write("%s = %s\n" % (name, path))
1963
1963
1964 def postincoming(ui, repo, modheads, optupdate, checkout):
1964 def postincoming(ui, repo, modheads, optupdate, checkout):
1965 if modheads == 0:
1965 if modheads == 0:
1966 return
1966 return
1967 if optupdate:
1967 if optupdate:
1968 if modheads <= 1 or checkout:
1968 if modheads <= 1 or checkout:
1969 return hg.update(repo, checkout)
1969 return hg.update(repo, checkout)
1970 else:
1970 else:
1971 ui.status(_("not updating, since new heads added\n"))
1971 ui.status(_("not updating, since new heads added\n"))
1972 if modheads > 1:
1972 if modheads > 1:
1973 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
1973 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
1974 else:
1974 else:
1975 ui.status(_("(run 'hg update' to get a working copy)\n"))
1975 ui.status(_("(run 'hg update' to get a working copy)\n"))
1976
1976
1977 def pull(ui, repo, source="default", **opts):
1977 def pull(ui, repo, source="default", **opts):
1978 """pull changes from the specified source
1978 """pull changes from the specified source
1979
1979
1980 Pull changes from a remote repository to a local one.
1980 Pull changes from a remote repository to a local one.
1981
1981
1982 This finds all changes from the repository at the specified path
1982 This finds all changes from the repository at the specified path
1983 or URL and adds them to the local repository. By default, this
1983 or URL and adds them to the local repository. By default, this
1984 does not update the copy of the project in the working directory.
1984 does not update the copy of the project in the working directory.
1985
1985
1986 Valid URLs are of the form:
1986 Valid URLs are of the form:
1987
1987
1988 local/filesystem/path (or file://local/filesystem/path)
1988 local/filesystem/path (or file://local/filesystem/path)
1989 http://[user@]host[:port]/[path]
1989 http://[user@]host[:port]/[path]
1990 https://[user@]host[:port]/[path]
1990 https://[user@]host[:port]/[path]
1991 ssh://[user@]host[:port]/[path]
1991 ssh://[user@]host[:port]/[path]
1992 static-http://host[:port]/[path]
1992 static-http://host[:port]/[path]
1993
1993
1994 Paths in the local filesystem can either point to Mercurial
1994 Paths in the local filesystem can either point to Mercurial
1995 repositories or to bundle files (as created by 'hg bundle' or
1995 repositories or to bundle files (as created by 'hg bundle' or
1996 'hg incoming --bundle'). The static-http:// protocol, albeit slow,
1996 'hg incoming --bundle'). The static-http:// protocol, albeit slow,
1997 allows access to a Mercurial repository where you simply use a web
1997 allows access to a Mercurial repository where you simply use a web
1998 server to publish the .hg directory as static content.
1998 server to publish the .hg directory as static content.
1999
1999
2000 An optional identifier after # indicates a particular branch, tag,
2000 An optional identifier after # indicates a particular branch, tag,
2001 or changeset to pull.
2001 or changeset to pull.
2002
2002
2003 Some notes about using SSH with Mercurial:
2003 Some notes about using SSH with Mercurial:
2004 - SSH requires an accessible shell account on the destination machine
2004 - SSH requires an accessible shell account on the destination machine
2005 and a copy of hg in the remote path or specified with as remotecmd.
2005 and a copy of hg in the remote path or specified with as remotecmd.
2006 - path is relative to the remote user's home directory by default.
2006 - path is relative to the remote user's home directory by default.
2007 Use an extra slash at the start of a path to specify an absolute path:
2007 Use an extra slash at the start of a path to specify an absolute path:
2008 ssh://example.com//tmp/repository
2008 ssh://example.com//tmp/repository
2009 - Mercurial doesn't use its own compression via SSH; the right thing
2009 - Mercurial doesn't use its own compression via SSH; the right thing
2010 to do is to configure it in your ~/.ssh/config, e.g.:
2010 to do is to configure it in your ~/.ssh/config, e.g.:
2011 Host *.mylocalnetwork.example.com
2011 Host *.mylocalnetwork.example.com
2012 Compression no
2012 Compression no
2013 Host *
2013 Host *
2014 Compression yes
2014 Compression yes
2015 Alternatively specify "ssh -C" as your ssh command in your hgrc or
2015 Alternatively specify "ssh -C" as your ssh command in your hgrc or
2016 with the --ssh command line option.
2016 with the --ssh command line option.
2017 """
2017 """
2018 source, revs, checkout = hg.parseurl(ui.expandpath(source), opts['rev'])
2018 source, revs, checkout = hg.parseurl(ui.expandpath(source), opts['rev'])
2019 cmdutil.setremoteconfig(ui, opts)
2019 cmdutil.setremoteconfig(ui, opts)
2020
2020
2021 other = hg.repository(ui, source)
2021 other = hg.repository(ui, source)
2022 ui.status(_('pulling from %s\n') % util.hidepassword(source))
2022 ui.status(_('pulling from %s\n') % util.hidepassword(source))
2023 if revs:
2023 if revs:
2024 try:
2024 try:
2025 revs = [other.lookup(rev) for rev in revs]
2025 revs = [other.lookup(rev) for rev in revs]
2026 except repo.NoCapability:
2026 except repo.NoCapability:
2027 error = _("Other repository doesn't support revision lookup, "
2027 error = _("Other repository doesn't support revision lookup, "
2028 "so a rev cannot be specified.")
2028 "so a rev cannot be specified.")
2029 raise util.Abort(error)
2029 raise util.Abort(error)
2030
2030
2031 modheads = repo.pull(other, heads=revs, force=opts['force'])
2031 modheads = repo.pull(other, heads=revs, force=opts['force'])
2032 return postincoming(ui, repo, modheads, opts['update'], checkout)
2032 return postincoming(ui, repo, modheads, opts['update'], checkout)
2033
2033
2034 def push(ui, repo, dest=None, **opts):
2034 def push(ui, repo, dest=None, **opts):
2035 """push changes to the specified destination
2035 """push changes to the specified destination
2036
2036
2037 Push changes from the local repository to the given destination.
2037 Push changes from the local repository to the given destination.
2038
2038
2039 This is the symmetrical operation for pull. It helps to move
2039 This is the symmetrical operation for pull. It helps to move
2040 changes from the current repository to a different one. If the
2040 changes from the current repository to a different one. If the
2041 destination is local this is identical to a pull in that directory
2041 destination is local this is identical to a pull in that directory
2042 from the current one.
2042 from the current one.
2043
2043
2044 By default, push will refuse to run if it detects the result would
2044 By default, push will refuse to run if it detects the result would
2045 increase the number of remote heads. This generally indicates the
2045 increase the number of remote heads. This generally indicates the
2046 the client has forgotten to sync and merge before pushing.
2046 the client has forgotten to sync and merge before pushing.
2047
2047
2048 Valid URLs are of the form:
2048 Valid URLs are of the form:
2049
2049
2050 local/filesystem/path (or file://local/filesystem/path)
2050 local/filesystem/path (or file://local/filesystem/path)
2051 ssh://[user@]host[:port]/[path]
2051 ssh://[user@]host[:port]/[path]
2052 http://[user@]host[:port]/[path]
2052 http://[user@]host[:port]/[path]
2053 https://[user@]host[:port]/[path]
2053 https://[user@]host[:port]/[path]
2054
2054
2055 An optional identifier after # indicates a particular branch, tag,
2055 An optional identifier after # indicates a particular branch, tag,
2056 or changeset to push.
2056 or changeset to push.
2057
2057
2058 Look at the help text for the pull command for important details
2058 Look at the help text for the pull command for important details
2059 about ssh:// URLs.
2059 about ssh:// URLs.
2060
2060
2061 Pushing to http:// and https:// URLs is only possible, if this
2061 Pushing to http:// and https:// URLs is only possible, if this
2062 feature is explicitly enabled on the remote Mercurial server.
2062 feature is explicitly enabled on the remote Mercurial server.
2063 """
2063 """
2064 dest, revs, checkout = hg.parseurl(
2064 dest, revs, checkout = hg.parseurl(
2065 ui.expandpath(dest or 'default-push', dest or 'default'), opts['rev'])
2065 ui.expandpath(dest or 'default-push', dest or 'default'), opts['rev'])
2066 cmdutil.setremoteconfig(ui, opts)
2066 cmdutil.setremoteconfig(ui, opts)
2067
2067
2068 other = hg.repository(ui, dest)
2068 other = hg.repository(ui, dest)
2069 ui.status('pushing to %s\n' % util.hidepassword(dest))
2069 ui.status('pushing to %s\n' % util.hidepassword(dest))
2070 if revs:
2070 if revs:
2071 revs = [repo.lookup(rev) for rev in revs]
2071 revs = [repo.lookup(rev) for rev in revs]
2072 r = repo.push(other, opts['force'], revs=revs)
2072 r = repo.push(other, opts['force'], revs=revs)
2073 return r == 0
2073 return r == 0
2074
2074
2075 def rawcommit(ui, repo, *pats, **opts):
2075 def rawcommit(ui, repo, *pats, **opts):
2076 """raw commit interface (DEPRECATED)
2076 """raw commit interface (DEPRECATED)
2077
2077
2078 (DEPRECATED)
2078 (DEPRECATED)
2079 Lowlevel commit, for use in helper scripts.
2079 Lowlevel commit, for use in helper scripts.
2080
2080
2081 This command is not intended to be used by normal users, as it is
2081 This command is not intended to be used by normal users, as it is
2082 primarily useful for importing from other SCMs.
2082 primarily useful for importing from other SCMs.
2083
2083
2084 This command is now deprecated and will be removed in a future
2084 This command is now deprecated and will be removed in a future
2085 release, please use debugsetparents and commit instead.
2085 release, please use debugsetparents and commit instead.
2086 """
2086 """
2087
2087
2088 ui.warn(_("(the rawcommit command is deprecated)\n"))
2088 ui.warn(_("(the rawcommit command is deprecated)\n"))
2089
2089
2090 message = cmdutil.logmessage(opts)
2090 message = cmdutil.logmessage(opts)
2091
2091
2092 files, match, anypats = cmdutil.matchpats(repo, pats, opts)
2092 files, match, anypats = cmdutil.matchpats(repo, pats, opts)
2093 if opts['files']:
2093 if opts['files']:
2094 files += open(opts['files']).read().splitlines()
2094 files += open(opts['files']).read().splitlines()
2095
2095
2096 parents = [repo.lookup(p) for p in opts['parent']]
2096 parents = [repo.lookup(p) for p in opts['parent']]
2097
2097
2098 try:
2098 try:
2099 repo.rawcommit(files, message, opts['user'], opts['date'], *parents)
2099 repo.rawcommit(files, message, opts['user'], opts['date'], *parents)
2100 except ValueError, inst:
2100 except ValueError, inst:
2101 raise util.Abort(str(inst))
2101 raise util.Abort(str(inst))
2102
2102
2103 def recover(ui, repo):
2103 def recover(ui, repo):
2104 """roll back an interrupted transaction
2104 """roll back an interrupted transaction
2105
2105
2106 Recover from an interrupted commit or pull.
2106 Recover from an interrupted commit or pull.
2107
2107
2108 This command tries to fix the repository status after an interrupted
2108 This command tries to fix the repository status after an interrupted
2109 operation. It should only be necessary when Mercurial suggests it.
2109 operation. It should only be necessary when Mercurial suggests it.
2110 """
2110 """
2111 if repo.recover():
2111 if repo.recover():
2112 return hg.verify(repo)
2112 return hg.verify(repo)
2113 return 1
2113 return 1
2114
2114
2115 def remove(ui, repo, *pats, **opts):
2115 def remove(ui, repo, *pats, **opts):
2116 """remove the specified files on the next commit
2116 """remove the specified files on the next commit
2117
2117
2118 Schedule the indicated files for removal from the repository.
2118 Schedule the indicated files for removal from the repository.
2119
2119
2120 This only removes files from the current branch, not from the
2120 This only removes files from the current branch, not from the
2121 entire project history. If the files still exist in the working
2121 entire project history. If the files still exist in the working
2122 directory, they will be deleted from it. If invoked with --after,
2122 directory, they will be deleted from it. If invoked with --after,
2123 files are marked as removed, but not actually unlinked unless --force
2123 files are marked as removed, but not actually unlinked unless --force
2124 is also given. Without exact file names, --after will only mark
2124 is also given. Without exact file names, --after will only mark
2125 files as removed if they are no longer in the working directory.
2125 files as removed if they are no longer in the working directory.
2126
2126
2127 This command schedules the files to be removed at the next commit.
2127 This command schedules the files to be removed at the next commit.
2128 To undo a remove before that, see hg revert.
2128 To undo a remove before that, see hg revert.
2129
2129
2130 Modified files and added files are not removed by default. To
2130 Modified files and added files are not removed by default. To
2131 remove them, use the -f/--force option.
2131 remove them, use the -f/--force option.
2132 """
2132 """
2133 if not opts['after'] and not pats:
2133 if not opts['after'] and not pats:
2134 raise util.Abort(_('no files specified'))
2134 raise util.Abort(_('no files specified'))
2135 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2135 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2136 exact = dict.fromkeys(files)
2136 exact = dict.fromkeys(files)
2137 mardu = map(dict.fromkeys, repo.status(files=files, match=matchfn))[:5]
2137 mardu = map(dict.fromkeys, repo.status(files=files, match=matchfn))[:5]
2138 modified, added, removed, deleted, unknown = mardu
2138 modified, added, removed, deleted, unknown = mardu
2139 remove, forget = [], []
2139 remove, forget = [], []
2140 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
2140 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
2141 reason = None
2141 reason = None
2142 if abs in modified and not opts['force']:
2142 if abs in modified and not opts['force']:
2143 reason = _('is modified (use -f to force removal)')
2143 reason = _('is modified (use -f to force removal)')
2144 elif abs in added:
2144 elif abs in added:
2145 if opts['force']:
2145 if opts['force']:
2146 forget.append(abs)
2146 forget.append(abs)
2147 continue
2147 continue
2148 reason = _('has been marked for add (use -f to force removal)')
2148 reason = _('has been marked for add (use -f to force removal)')
2149 exact = 1 # force the message
2149 exact = 1 # force the message
2150 elif abs not in repo.dirstate:
2150 elif abs not in repo.dirstate:
2151 reason = _('is not managed')
2151 reason = _('is not managed')
2152 elif opts['after'] and not exact and abs not in deleted:
2152 elif opts['after'] and not exact and abs not in deleted:
2153 continue
2153 continue
2154 elif abs in removed:
2154 elif abs in removed:
2155 continue
2155 continue
2156 if reason:
2156 if reason:
2157 if exact:
2157 if exact:
2158 ui.warn(_('not removing %s: file %s\n') % (rel, reason))
2158 ui.warn(_('not removing %s: file %s\n') % (rel, reason))
2159 else:
2159 else:
2160 if ui.verbose or not exact:
2160 if ui.verbose or not exact:
2161 ui.status(_('removing %s\n') % rel)
2161 ui.status(_('removing %s\n') % rel)
2162 remove.append(abs)
2162 remove.append(abs)
2163 repo.forget(forget)
2163 repo.forget(forget)
2164 repo.remove(remove, unlink=opts['force'] or not opts['after'])
2164 repo.remove(remove, unlink=opts['force'] or not opts['after'])
2165
2165
2166 def rename(ui, repo, *pats, **opts):
2166 def rename(ui, repo, *pats, **opts):
2167 """rename files; equivalent of copy + remove
2167 """rename files; equivalent of copy + remove
2168
2168
2169 Mark dest as copies of sources; mark sources for deletion. If
2169 Mark dest as copies of sources; mark sources for deletion. If
2170 dest is a directory, copies are put in that directory. If dest is
2170 dest is a directory, copies are put in that directory. If dest is
2171 a file, there can only be one source.
2171 a file, there can only be one source.
2172
2172
2173 By default, this command copies the contents of files as they
2173 By default, this command copies the contents of files as they
2174 stand in the working directory. If invoked with --after, the
2174 stand in the working directory. If invoked with --after, the
2175 operation is recorded, but no copying is performed.
2175 operation is recorded, but no copying is performed.
2176
2176
2177 This command takes effect in the next commit. To undo a rename
2177 This command takes effect in the next commit. To undo a rename
2178 before that, see hg revert.
2178 before that, see hg revert.
2179 """
2179 """
2180 wlock = repo.wlock(False)
2180 wlock = repo.wlock(False)
2181 try:
2181 try:
2182 return cmdutil.copy(ui, repo, pats, opts, rename=True)
2182 return cmdutil.copy(ui, repo, pats, opts, rename=True)
2183 finally:
2183 finally:
2184 del wlock
2184 del wlock
2185
2185
2186 def revert(ui, repo, *pats, **opts):
2186 def revert(ui, repo, *pats, **opts):
2187 """restore individual files or dirs to an earlier state
2187 """restore individual files or dirs to an earlier state
2188
2188
2189 (use update -r to check out earlier revisions, revert does not
2189 (use update -r to check out earlier revisions, revert does not
2190 change the working dir parents)
2190 change the working dir parents)
2191
2191
2192 With no revision specified, revert the named files or directories
2192 With no revision specified, revert the named files or directories
2193 to the contents they had in the parent of the working directory.
2193 to the contents they had in the parent of the working directory.
2194 This restores the contents of the affected files to an unmodified
2194 This restores the contents of the affected files to an unmodified
2195 state and unschedules adds, removes, copies, and renames. If the
2195 state and unschedules adds, removes, copies, and renames. If the
2196 working directory has two parents, you must explicitly specify the
2196 working directory has two parents, you must explicitly specify the
2197 revision to revert to.
2197 revision to revert to.
2198
2198
2199 Using the -r option, revert the given files or directories to their
2199 Using the -r option, revert the given files or directories to their
2200 contents as of a specific revision. This can be helpful to "roll
2200 contents as of a specific revision. This can be helpful to "roll
2201 back" some or all of an earlier change.
2201 back" some or all of an earlier change.
2202 See 'hg help dates' for a list of formats valid for -d/--date.
2202 See 'hg help dates' for a list of formats valid for -d/--date.
2203
2203
2204 Revert modifies the working directory. It does not commit any
2204 Revert modifies the working directory. It does not commit any
2205 changes, or change the parent of the working directory. If you
2205 changes, or change the parent of the working directory. If you
2206 revert to a revision other than the parent of the working
2206 revert to a revision other than the parent of the working
2207 directory, the reverted files will thus appear modified
2207 directory, the reverted files will thus appear modified
2208 afterwards.
2208 afterwards.
2209
2209
2210 If a file has been deleted, it is restored. If the executable
2210 If a file has been deleted, it is restored. If the executable
2211 mode of a file was changed, it is reset.
2211 mode of a file was changed, it is reset.
2212
2212
2213 If names are given, all files matching the names are reverted.
2213 If names are given, all files matching the names are reverted.
2214 If no arguments are given, no files are reverted.
2214 If no arguments are given, no files are reverted.
2215
2215
2216 Modified files are saved with a .orig suffix before reverting.
2216 Modified files are saved with a .orig suffix before reverting.
2217 To disable these backups, use --no-backup.
2217 To disable these backups, use --no-backup.
2218 """
2218 """
2219
2219
2220 if opts["date"]:
2220 if opts["date"]:
2221 if opts["rev"]:
2221 if opts["rev"]:
2222 raise util.Abort(_("you can't specify a revision and a date"))
2222 raise util.Abort(_("you can't specify a revision and a date"))
2223 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
2223 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
2224
2224
2225 if not pats and not opts['all']:
2225 if not pats and not opts['all']:
2226 raise util.Abort(_('no files or directories specified; '
2226 raise util.Abort(_('no files or directories specified; '
2227 'use --all to revert the whole repo'))
2227 'use --all to revert the whole repo'))
2228
2228
2229 parent, p2 = repo.dirstate.parents()
2229 parent, p2 = repo.dirstate.parents()
2230 if not opts['rev'] and p2 != nullid:
2230 if not opts['rev'] and p2 != nullid:
2231 raise util.Abort(_('uncommitted merge - please provide a '
2231 raise util.Abort(_('uncommitted merge - please provide a '
2232 'specific revision'))
2232 'specific revision'))
2233 ctx = repo.changectx(opts['rev'])
2233 ctx = repo.changectx(opts['rev'])
2234 node = ctx.node()
2234 node = ctx.node()
2235 mf = ctx.manifest()
2235 mf = ctx.manifest()
2236 if node == parent:
2236 if node == parent:
2237 pmf = mf
2237 pmf = mf
2238 else:
2238 else:
2239 pmf = None
2239 pmf = None
2240
2240
2241 # need all matching names in dirstate and manifest of target rev,
2241 # need all matching names in dirstate and manifest of target rev,
2242 # so have to walk both. do not print errors if files exist in one
2242 # so have to walk both. do not print errors if files exist in one
2243 # but not other.
2243 # but not other.
2244
2244
2245 names = {}
2245 names = {}
2246
2246
2247 wlock = repo.wlock()
2247 wlock = repo.wlock()
2248 try:
2248 try:
2249 # walk dirstate.
2249 # walk dirstate.
2250 files = []
2250 files = []
2251 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
2251 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
2252 badmatch=mf.has_key):
2252 badmatch=mf.has_key):
2253 names[abs] = (rel, exact)
2253 names[abs] = (rel, exact)
2254 if src != 'b':
2254 if src != 'b':
2255 files.append(abs)
2255 files.append(abs)
2256
2256
2257 # walk target manifest.
2257 # walk target manifest.
2258
2258
2259 def badmatch(path):
2259 def badmatch(path):
2260 if path in names:
2260 if path in names:
2261 return True
2261 return True
2262 path_ = path + '/'
2262 path_ = path + '/'
2263 for f in names:
2263 for f in names:
2264 if f.startswith(path_):
2264 if f.startswith(path_):
2265 return True
2265 return True
2266 return False
2266 return False
2267
2267
2268 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
2268 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
2269 badmatch=badmatch):
2269 badmatch=badmatch):
2270 if abs in names or src == 'b':
2270 if abs in names or src == 'b':
2271 continue
2271 continue
2272 names[abs] = (rel, exact)
2272 names[abs] = (rel, exact)
2273
2273
2274 changes = repo.status(files=files, match=names.has_key)[:4]
2274 changes = repo.status(files=files, match=names.has_key)[:4]
2275 modified, added, removed, deleted = map(dict.fromkeys, changes)
2275 modified, added, removed, deleted = map(dict.fromkeys, changes)
2276
2276
2277 # if f is a rename, also revert the source
2277 # if f is a rename, also revert the source
2278 cwd = repo.getcwd()
2278 cwd = repo.getcwd()
2279 for f in added:
2279 for f in added:
2280 src = repo.dirstate.copied(f)
2280 src = repo.dirstate.copied(f)
2281 if src and src not in names and repo.dirstate[src] == 'r':
2281 if src and src not in names and repo.dirstate[src] == 'r':
2282 removed[src] = None
2282 removed[src] = None
2283 names[src] = (repo.pathto(src, cwd), True)
2283 names[src] = (repo.pathto(src, cwd), True)
2284
2284
2285 def removeforget(abs):
2285 def removeforget(abs):
2286 if repo.dirstate[abs] == 'a':
2286 if repo.dirstate[abs] == 'a':
2287 return _('forgetting %s\n')
2287 return _('forgetting %s\n')
2288 return _('removing %s\n')
2288 return _('removing %s\n')
2289
2289
2290 revert = ([], _('reverting %s\n'))
2290 revert = ([], _('reverting %s\n'))
2291 add = ([], _('adding %s\n'))
2291 add = ([], _('adding %s\n'))
2292 remove = ([], removeforget)
2292 remove = ([], removeforget)
2293 undelete = ([], _('undeleting %s\n'))
2293 undelete = ([], _('undeleting %s\n'))
2294
2294
2295 disptable = (
2295 disptable = (
2296 # dispatch table:
2296 # dispatch table:
2297 # file state
2297 # file state
2298 # action if in target manifest
2298 # action if in target manifest
2299 # action if not in target manifest
2299 # action if not in target manifest
2300 # make backup if in target manifest
2300 # make backup if in target manifest
2301 # make backup if not in target manifest
2301 # make backup if not in target manifest
2302 (modified, revert, remove, True, True),
2302 (modified, revert, remove, True, True),
2303 (added, revert, remove, True, False),
2303 (added, revert, remove, True, False),
2304 (removed, undelete, None, False, False),
2304 (removed, undelete, None, False, False),
2305 (deleted, revert, remove, False, False),
2305 (deleted, revert, remove, False, False),
2306 )
2306 )
2307
2307
2308 entries = names.items()
2308 entries = names.items()
2309 entries.sort()
2309 entries.sort()
2310
2310
2311 for abs, (rel, exact) in entries:
2311 for abs, (rel, exact) in entries:
2312 mfentry = mf.get(abs)
2312 mfentry = mf.get(abs)
2313 target = repo.wjoin(abs)
2313 target = repo.wjoin(abs)
2314 def handle(xlist, dobackup):
2314 def handle(xlist, dobackup):
2315 xlist[0].append(abs)
2315 xlist[0].append(abs)
2316 if dobackup and not opts['no_backup'] and util.lexists(target):
2316 if dobackup and not opts['no_backup'] and util.lexists(target):
2317 bakname = "%s.orig" % rel
2317 bakname = "%s.orig" % rel
2318 ui.note(_('saving current version of %s as %s\n') %
2318 ui.note(_('saving current version of %s as %s\n') %
2319 (rel, bakname))
2319 (rel, bakname))
2320 if not opts.get('dry_run'):
2320 if not opts.get('dry_run'):
2321 util.copyfile(target, bakname)
2321 util.copyfile(target, bakname)
2322 if ui.verbose or not exact:
2322 if ui.verbose or not exact:
2323 msg = xlist[1]
2323 msg = xlist[1]
2324 if not isinstance(msg, basestring):
2324 if not isinstance(msg, basestring):
2325 msg = msg(abs)
2325 msg = msg(abs)
2326 ui.status(msg % rel)
2326 ui.status(msg % rel)
2327 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2327 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2328 if abs not in table: continue
2328 if abs not in table: continue
2329 # file has changed in dirstate
2329 # file has changed in dirstate
2330 if mfentry:
2330 if mfentry:
2331 handle(hitlist, backuphit)
2331 handle(hitlist, backuphit)
2332 elif misslist is not None:
2332 elif misslist is not None:
2333 handle(misslist, backupmiss)
2333 handle(misslist, backupmiss)
2334 break
2334 break
2335 else:
2335 else:
2336 if abs not in repo.dirstate:
2336 if abs not in repo.dirstate:
2337 if mfentry:
2337 if mfentry:
2338 handle(add, True)
2338 handle(add, True)
2339 elif exact:
2339 elif exact:
2340 ui.warn(_('file not managed: %s\n') % rel)
2340 ui.warn(_('file not managed: %s\n') % rel)
2341 continue
2341 continue
2342 # file has not changed in dirstate
2342 # file has not changed in dirstate
2343 if node == parent:
2343 if node == parent:
2344 if exact: ui.warn(_('no changes needed to %s\n') % rel)
2344 if exact: ui.warn(_('no changes needed to %s\n') % rel)
2345 continue
2345 continue
2346 if pmf is None:
2346 if pmf is None:
2347 # only need parent manifest in this unlikely case,
2347 # only need parent manifest in this unlikely case,
2348 # so do not read by default
2348 # so do not read by default
2349 pmf = repo.changectx(parent).manifest()
2349 pmf = repo.changectx(parent).manifest()
2350 if abs in pmf:
2350 if abs in pmf:
2351 if mfentry:
2351 if mfentry:
2352 # if version of file is same in parent and target
2352 # if version of file is same in parent and target
2353 # manifests, do nothing
2353 # manifests, do nothing
2354 if (pmf[abs] != mfentry or
2354 if (pmf[abs] != mfentry or
2355 pmf.flags(abs) != mf.flags(abs)):
2355 pmf.flags(abs) != mf.flags(abs)):
2356 handle(revert, False)
2356 handle(revert, False)
2357 else:
2357 else:
2358 handle(remove, False)
2358 handle(remove, False)
2359
2359
2360 if not opts.get('dry_run'):
2360 if not opts.get('dry_run'):
2361 def checkout(f):
2361 def checkout(f):
2362 fc = ctx[f]
2362 fc = ctx[f]
2363 repo.wwrite(f, fc.data(), fc.fileflags())
2363 repo.wwrite(f, fc.data(), fc.fileflags())
2364
2364
2365 audit_path = util.path_auditor(repo.root)
2365 audit_path = util.path_auditor(repo.root)
2366 for f in remove[0]:
2366 for f in remove[0]:
2367 if repo.dirstate[f] == 'a':
2367 if repo.dirstate[f] == 'a':
2368 repo.dirstate.forget(f)
2368 repo.dirstate.forget(f)
2369 continue
2369 continue
2370 audit_path(f)
2370 audit_path(f)
2371 try:
2371 try:
2372 util.unlink(repo.wjoin(f))
2372 util.unlink(repo.wjoin(f))
2373 except OSError:
2373 except OSError:
2374 pass
2374 pass
2375 repo.dirstate.remove(f)
2375 repo.dirstate.remove(f)
2376
2376
2377 for f in revert[0]:
2377 for f in revert[0]:
2378 checkout(f)
2378 checkout(f)
2379
2379
2380 for f in add[0]:
2380 for f in add[0]:
2381 checkout(f)
2381 checkout(f)
2382 repo.dirstate.add(f)
2382 repo.dirstate.add(f)
2383
2383
2384 normal = repo.dirstate.normallookup
2384 normal = repo.dirstate.normallookup
2385 if node == parent and p2 == nullid:
2385 if node == parent and p2 == nullid:
2386 normal = repo.dirstate.normal
2386 normal = repo.dirstate.normal
2387 for f in undelete[0]:
2387 for f in undelete[0]:
2388 checkout(f)
2388 checkout(f)
2389 normal(f)
2389 normal(f)
2390
2390
2391 finally:
2391 finally:
2392 del wlock
2392 del wlock
2393
2393
2394 def rollback(ui, repo):
2394 def rollback(ui, repo):
2395 """roll back the last transaction
2395 """roll back the last transaction
2396
2396
2397 This command should be used with care. There is only one level of
2397 This command should be used with care. There is only one level of
2398 rollback, and there is no way to undo a rollback. It will also
2398 rollback, and there is no way to undo a rollback. It will also
2399 restore the dirstate at the time of the last transaction, losing
2399 restore the dirstate at the time of the last transaction, losing
2400 any dirstate changes since that time.
2400 any dirstate changes since that time.
2401
2401
2402 Transactions are used to encapsulate the effects of all commands
2402 Transactions are used to encapsulate the effects of all commands
2403 that create new changesets or propagate existing changesets into a
2403 that create new changesets or propagate existing changesets into a
2404 repository. For example, the following commands are transactional,
2404 repository. For example, the following commands are transactional,
2405 and their effects can be rolled back:
2405 and their effects can be rolled back:
2406
2406
2407 commit
2407 commit
2408 import
2408 import
2409 pull
2409 pull
2410 push (with this repository as destination)
2410 push (with this repository as destination)
2411 unbundle
2411 unbundle
2412
2412
2413 This command is not intended for use on public repositories. Once
2413 This command is not intended for use on public repositories. Once
2414 changes are visible for pull by other users, rolling a transaction
2414 changes are visible for pull by other users, rolling a transaction
2415 back locally is ineffective (someone else may already have pulled
2415 back locally is ineffective (someone else may already have pulled
2416 the changes). Furthermore, a race is possible with readers of the
2416 the changes). Furthermore, a race is possible with readers of the
2417 repository; for example an in-progress pull from the repository
2417 repository; for example an in-progress pull from the repository
2418 may fail if a rollback is performed.
2418 may fail if a rollback is performed.
2419 """
2419 """
2420 repo.rollback()
2420 repo.rollback()
2421
2421
2422 def root(ui, repo):
2422 def root(ui, repo):
2423 """print the root (top) of the current working dir
2423 """print the root (top) of the current working dir
2424
2424
2425 Print the root directory of the current repository.
2425 Print the root directory of the current repository.
2426 """
2426 """
2427 ui.write(repo.root + "\n")
2427 ui.write(repo.root + "\n")
2428
2428
2429 def serve(ui, repo, **opts):
2429 def serve(ui, repo, **opts):
2430 """export the repository via HTTP
2430 """export the repository via HTTP
2431
2431
2432 Start a local HTTP repository browser and pull server.
2432 Start a local HTTP repository browser and pull server.
2433
2433
2434 By default, the server logs accesses to stdout and errors to
2434 By default, the server logs accesses to stdout and errors to
2435 stderr. Use the "-A" and "-E" options to log to files.
2435 stderr. Use the "-A" and "-E" options to log to files.
2436 """
2436 """
2437
2437
2438 if opts["stdio"]:
2438 if opts["stdio"]:
2439 if repo is None:
2439 if repo is None:
2440 raise hg.RepoError(_("There is no Mercurial repository here"
2440 raise hg.RepoError(_("There is no Mercurial repository here"
2441 " (.hg not found)"))
2441 " (.hg not found)"))
2442 s = sshserver.sshserver(ui, repo)
2442 s = sshserver.sshserver(ui, repo)
2443 s.serve_forever()
2443 s.serve_forever()
2444
2444
2445 parentui = ui.parentui or ui
2445 parentui = ui.parentui or ui
2446 optlist = ("name templates style address port prefix ipv6"
2446 optlist = ("name templates style address port prefix ipv6"
2447 " accesslog errorlog webdir_conf certificate")
2447 " accesslog errorlog webdir_conf certificate")
2448 for o in optlist.split():
2448 for o in optlist.split():
2449 if opts[o]:
2449 if opts[o]:
2450 parentui.setconfig("web", o, str(opts[o]))
2450 parentui.setconfig("web", o, str(opts[o]))
2451 if (repo is not None) and (repo.ui != parentui):
2451 if (repo is not None) and (repo.ui != parentui):
2452 repo.ui.setconfig("web", o, str(opts[o]))
2452 repo.ui.setconfig("web", o, str(opts[o]))
2453
2453
2454 if repo is None and not ui.config("web", "webdir_conf"):
2454 if repo is None and not ui.config("web", "webdir_conf"):
2455 raise hg.RepoError(_("There is no Mercurial repository here"
2455 raise hg.RepoError(_("There is no Mercurial repository here"
2456 " (.hg not found)"))
2456 " (.hg not found)"))
2457
2457
2458 class service:
2458 class service:
2459 def init(self):
2459 def init(self):
2460 util.set_signal_handler()
2460 util.set_signal_handler()
2461 try:
2461 try:
2462 self.httpd = hgweb.server.create_server(parentui, repo)
2462 self.httpd = hgweb.server.create_server(parentui, repo)
2463 except socket.error, inst:
2463 except socket.error, inst:
2464 raise util.Abort(_('cannot start server: ') + inst.args[1])
2464 raise util.Abort(_('cannot start server: ') + inst.args[1])
2465
2465
2466 if not ui.verbose: return
2466 if not ui.verbose: return
2467
2467
2468 if self.httpd.prefix:
2468 if self.httpd.prefix:
2469 prefix = self.httpd.prefix.strip('/') + '/'
2469 prefix = self.httpd.prefix.strip('/') + '/'
2470 else:
2470 else:
2471 prefix = ''
2471 prefix = ''
2472
2472
2473 if self.httpd.port != 80:
2473 if self.httpd.port != 80:
2474 ui.status(_('listening at http://%s:%d/%s\n') %
2474 ui.status(_('listening at http://%s:%d/%s\n') %
2475 (self.httpd.addr, self.httpd.port, prefix))
2475 (self.httpd.addr, self.httpd.port, prefix))
2476 else:
2476 else:
2477 ui.status(_('listening at http://%s/%s\n') %
2477 ui.status(_('listening at http://%s/%s\n') %
2478 (self.httpd.addr, prefix))
2478 (self.httpd.addr, prefix))
2479
2479
2480 def run(self):
2480 def run(self):
2481 self.httpd.serve_forever()
2481 self.httpd.serve_forever()
2482
2482
2483 service = service()
2483 service = service()
2484
2484
2485 cmdutil.service(opts, initfn=service.init, runfn=service.run)
2485 cmdutil.service(opts, initfn=service.init, runfn=service.run)
2486
2486
2487 def status(ui, repo, *pats, **opts):
2487 def status(ui, repo, *pats, **opts):
2488 """show changed files in the working directory
2488 """show changed files in the working directory
2489
2489
2490 Show status of files in the repository. If names are given, only
2490 Show status of files in the repository. If names are given, only
2491 files that match are shown. Files that are clean or ignored or
2491 files that match are shown. Files that are clean or ignored or
2492 source of a copy/move operation, are not listed unless -c (clean),
2492 source of a copy/move operation, are not listed unless -c (clean),
2493 -i (ignored), -C (copies) or -A is given. Unless options described
2493 -i (ignored), -C (copies) or -A is given. Unless options described
2494 with "show only ..." are given, the options -mardu are used.
2494 with "show only ..." are given, the options -mardu are used.
2495
2495
2496 Option -q/--quiet hides untracked files unless explicitly
2496 Option -q/--quiet hides untracked (unknown and ignored) files
2497 requested by -u.
2497 unless explicitly requested with -u/--unknown or -i/-ignored.
2498
2498
2499 NOTE: status may appear to disagree with diff if permissions have
2499 NOTE: status may appear to disagree with diff if permissions have
2500 changed or a merge has occurred. The standard diff format does not
2500 changed or a merge has occurred. The standard diff format does not
2501 report permission changes and diff only reports changes relative
2501 report permission changes and diff only reports changes relative
2502 to one merge parent.
2502 to one merge parent.
2503
2503
2504 If one revision is given, it is used as the base revision.
2504 If one revision is given, it is used as the base revision.
2505 If two revisions are given, the difference between them is shown.
2505 If two revisions are given, the difference between them is shown.
2506
2506
2507 The codes used to show the status of files are:
2507 The codes used to show the status of files are:
2508 M = modified
2508 M = modified
2509 A = added
2509 A = added
2510 R = removed
2510 R = removed
2511 C = clean
2511 C = clean
2512 ! = deleted, but still tracked
2512 ! = deleted, but still tracked
2513 ? = not tracked
2513 ? = not tracked
2514 I = ignored
2514 I = ignored
2515 = the previous added file was copied from here
2515 = the previous added file was copied from here
2516 """
2516 """
2517
2517
2518 all = opts['all']
2518 all = opts['all']
2519 node1, node2 = cmdutil.revpair(repo, opts.get('rev'))
2519 node1, node2 = cmdutil.revpair(repo, opts.get('rev'))
2520
2520
2521 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2521 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2522 cwd = (pats and repo.getcwd()) or ''
2522 cwd = (pats and repo.getcwd()) or ''
2523 modified, added, removed, deleted, unknown, ignored, clean = [
2523 modified, added, removed, deleted, unknown, ignored, clean = [
2524 n for n in repo.status(node1=node1, node2=node2, files=files,
2524 n for n in repo.status(node1=node1, node2=node2, files=files,
2525 match=matchfn,
2525 match=matchfn,
2526 list_ignored=all or opts['ignored'],
2526 list_ignored=opts['ignored']
2527 list_clean=all or opts['clean'])]
2527 or all and not ui.quiet,
2528 list_clean=opts['clean'] or all,
2529 list_unknown=opts['unknown']
2530 or not (ui.quiet or
2531 opts['modified'] or
2532 opts['added'] or
2533 opts['removed'] or
2534 opts['deleted'] or
2535 opts['ignored']))]
2528
2536
2529 changetypes = (('modified', 'M', modified),
2537 changetypes = (('modified', 'M', modified),
2530 ('added', 'A', added),
2538 ('added', 'A', added),
2531 ('removed', 'R', removed),
2539 ('removed', 'R', removed),
2532 ('deleted', '!', deleted),
2540 ('deleted', '!', deleted),
2533 ('unknown', '?', unknown),
2541 ('unknown', '?', unknown),
2534 ('ignored', 'I', ignored))
2542 ('ignored', 'I', ignored))
2535
2543
2536 explicit_changetypes = changetypes + (('clean', 'C', clean),)
2544 explicit_changetypes = changetypes + (('clean', 'C', clean),)
2537
2545
2538 end = opts['print0'] and '\0' or '\n'
2546 end = opts['print0'] and '\0' or '\n'
2539
2547
2540 for opt, char, changes in ([ct for ct in explicit_changetypes
2548 for opt, char, changes in ([ct for ct in explicit_changetypes
2541 if all or opts[ct[0]]]
2549 if all or opts[ct[0]]]
2542 or changetypes):
2550 or changetypes):
2543
2551
2544 # skip unknown files if -q, but -u and -A have priority over -q
2545 if (not opts['unknown']) and (not opts['all']):
2546 if opt == 'unknown' and ui.quiet:
2547 continue
2548
2549 if opts['no_status']:
2552 if opts['no_status']:
2550 format = "%%s%s" % end
2553 format = "%%s%s" % end
2551 else:
2554 else:
2552 format = "%s %%s%s" % (char, end)
2555 format = "%s %%s%s" % (char, end)
2553
2556
2554 for f in changes:
2557 for f in changes:
2555 ui.write(format % repo.pathto(f, cwd))
2558 ui.write(format % repo.pathto(f, cwd))
2556 if ((all or opts.get('copies')) and not opts.get('no_status')):
2559 if ((all or opts.get('copies')) and not opts.get('no_status')):
2557 copied = repo.dirstate.copied(f)
2560 copied = repo.dirstate.copied(f)
2558 if copied:
2561 if copied:
2559 ui.write(' %s%s' % (repo.pathto(copied, cwd), end))
2562 ui.write(' %s%s' % (repo.pathto(copied, cwd), end))
2560
2563
2561 def tag(ui, repo, name, rev_=None, **opts):
2564 def tag(ui, repo, name, rev_=None, **opts):
2562 """add a tag for the current or given revision
2565 """add a tag for the current or given revision
2563
2566
2564 Name a particular revision using <name>.
2567 Name a particular revision using <name>.
2565
2568
2566 Tags are used to name particular revisions of the repository and are
2569 Tags are used to name particular revisions of the repository and are
2567 very useful to compare different revision, to go back to significant
2570 very useful to compare different revision, to go back to significant
2568 earlier versions or to mark branch points as releases, etc.
2571 earlier versions or to mark branch points as releases, etc.
2569
2572
2570 If no revision is given, the parent of the working directory is used,
2573 If no revision is given, the parent of the working directory is used,
2571 or tip if no revision is checked out.
2574 or tip if no revision is checked out.
2572
2575
2573 To facilitate version control, distribution, and merging of tags,
2576 To facilitate version control, distribution, and merging of tags,
2574 they are stored as a file named ".hgtags" which is managed
2577 they are stored as a file named ".hgtags" which is managed
2575 similarly to other project files and can be hand-edited if
2578 similarly to other project files and can be hand-edited if
2576 necessary. The file '.hg/localtags' is used for local tags (not
2579 necessary. The file '.hg/localtags' is used for local tags (not
2577 shared among repositories).
2580 shared among repositories).
2578
2581
2579 See 'hg help dates' for a list of formats valid for -d/--date.
2582 See 'hg help dates' for a list of formats valid for -d/--date.
2580 """
2583 """
2581 if name in ['tip', '.', 'null']:
2584 if name in ['tip', '.', 'null']:
2582 raise util.Abort(_("the name '%s' is reserved") % name)
2585 raise util.Abort(_("the name '%s' is reserved") % name)
2583 if rev_ is not None:
2586 if rev_ is not None:
2584 ui.warn(_("use of 'hg tag NAME [REV]' is deprecated, "
2587 ui.warn(_("use of 'hg tag NAME [REV]' is deprecated, "
2585 "please use 'hg tag [-r REV] NAME' instead\n"))
2588 "please use 'hg tag [-r REV] NAME' instead\n"))
2586 if opts['rev']:
2589 if opts['rev']:
2587 raise util.Abort(_("use only one form to specify the revision"))
2590 raise util.Abort(_("use only one form to specify the revision"))
2588 if opts['rev'] and opts['remove']:
2591 if opts['rev'] and opts['remove']:
2589 raise util.Abort(_("--rev and --remove are incompatible"))
2592 raise util.Abort(_("--rev and --remove are incompatible"))
2590 if opts['rev']:
2593 if opts['rev']:
2591 rev_ = opts['rev']
2594 rev_ = opts['rev']
2592 message = opts['message']
2595 message = opts['message']
2593 if opts['remove']:
2596 if opts['remove']:
2594 tagtype = repo.tagtype(name)
2597 tagtype = repo.tagtype(name)
2595
2598
2596 if not tagtype:
2599 if not tagtype:
2597 raise util.Abort(_('tag %s does not exist') % name)
2600 raise util.Abort(_('tag %s does not exist') % name)
2598 if opts['local'] and tagtype == 'global':
2601 if opts['local'] and tagtype == 'global':
2599 raise util.Abort(_('%s tag is global') % name)
2602 raise util.Abort(_('%s tag is global') % name)
2600 if not opts['local'] and tagtype == 'local':
2603 if not opts['local'] and tagtype == 'local':
2601 raise util.Abort(_('%s tag is local') % name)
2604 raise util.Abort(_('%s tag is local') % name)
2602
2605
2603 rev_ = nullid
2606 rev_ = nullid
2604 if not message:
2607 if not message:
2605 message = _('Removed tag %s') % name
2608 message = _('Removed tag %s') % name
2606 elif name in repo.tags() and not opts['force']:
2609 elif name in repo.tags() and not opts['force']:
2607 raise util.Abort(_('a tag named %s already exists (use -f to force)')
2610 raise util.Abort(_('a tag named %s already exists (use -f to force)')
2608 % name)
2611 % name)
2609 if not rev_ and repo.dirstate.parents()[1] != nullid:
2612 if not rev_ and repo.dirstate.parents()[1] != nullid:
2610 raise util.Abort(_('uncommitted merge - please provide a '
2613 raise util.Abort(_('uncommitted merge - please provide a '
2611 'specific revision'))
2614 'specific revision'))
2612 r = repo.changectx(rev_).node()
2615 r = repo.changectx(rev_).node()
2613
2616
2614 if not message:
2617 if not message:
2615 message = _('Added tag %s for changeset %s') % (name, short(r))
2618 message = _('Added tag %s for changeset %s') % (name, short(r))
2616
2619
2617 repo.tag(name, r, message, opts['local'], opts['user'], opts['date'])
2620 repo.tag(name, r, message, opts['local'], opts['user'], opts['date'])
2618
2621
2619 def tags(ui, repo):
2622 def tags(ui, repo):
2620 """list repository tags
2623 """list repository tags
2621
2624
2622 List the repository tags.
2625 List the repository tags.
2623
2626
2624 This lists both regular and local tags. When the -v/--verbose switch
2627 This lists both regular and local tags. When the -v/--verbose switch
2625 is used, a third column "local" is printed for local tags.
2628 is used, a third column "local" is printed for local tags.
2626 """
2629 """
2627
2630
2628 l = repo.tagslist()
2631 l = repo.tagslist()
2629 l.reverse()
2632 l.reverse()
2630 hexfunc = ui.debugflag and hex or short
2633 hexfunc = ui.debugflag and hex or short
2631 tagtype = ""
2634 tagtype = ""
2632
2635
2633 for t, n in l:
2636 for t, n in l:
2634 if ui.quiet:
2637 if ui.quiet:
2635 ui.write("%s\n" % t)
2638 ui.write("%s\n" % t)
2636 continue
2639 continue
2637
2640
2638 try:
2641 try:
2639 hn = hexfunc(n)
2642 hn = hexfunc(n)
2640 r = "%5d:%s" % (repo.changelog.rev(n), hn)
2643 r = "%5d:%s" % (repo.changelog.rev(n), hn)
2641 except revlog.LookupError:
2644 except revlog.LookupError:
2642 r = " ?:%s" % hn
2645 r = " ?:%s" % hn
2643 else:
2646 else:
2644 spaces = " " * (30 - util.locallen(t))
2647 spaces = " " * (30 - util.locallen(t))
2645 if ui.verbose:
2648 if ui.verbose:
2646 if repo.tagtype(t) == 'local':
2649 if repo.tagtype(t) == 'local':
2647 tagtype = " local"
2650 tagtype = " local"
2648 else:
2651 else:
2649 tagtype = ""
2652 tagtype = ""
2650 ui.write("%s%s %s%s\n" % (t, spaces, r, tagtype))
2653 ui.write("%s%s %s%s\n" % (t, spaces, r, tagtype))
2651
2654
2652 def tip(ui, repo, **opts):
2655 def tip(ui, repo, **opts):
2653 """show the tip revision
2656 """show the tip revision
2654
2657
2655 Show the tip revision.
2658 Show the tip revision.
2656 """
2659 """
2657 cmdutil.show_changeset(ui, repo, opts).show(nullrev+repo.changelog.count())
2660 cmdutil.show_changeset(ui, repo, opts).show(nullrev+repo.changelog.count())
2658
2661
2659 def unbundle(ui, repo, fname1, *fnames, **opts):
2662 def unbundle(ui, repo, fname1, *fnames, **opts):
2660 """apply one or more changegroup files
2663 """apply one or more changegroup files
2661
2664
2662 Apply one or more compressed changegroup files generated by the
2665 Apply one or more compressed changegroup files generated by the
2663 bundle command.
2666 bundle command.
2664 """
2667 """
2665 fnames = (fname1,) + fnames
2668 fnames = (fname1,) + fnames
2666
2669
2667 lock = None
2670 lock = None
2668 try:
2671 try:
2669 lock = repo.lock()
2672 lock = repo.lock()
2670 for fname in fnames:
2673 for fname in fnames:
2671 if os.path.exists(fname):
2674 if os.path.exists(fname):
2672 f = open(fname, "rb")
2675 f = open(fname, "rb")
2673 else:
2676 else:
2674 f = urllib.urlopen(fname)
2677 f = urllib.urlopen(fname)
2675 gen = changegroup.readbundle(f, fname)
2678 gen = changegroup.readbundle(f, fname)
2676 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
2679 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
2677 finally:
2680 finally:
2678 del lock
2681 del lock
2679
2682
2680 return postincoming(ui, repo, modheads, opts['update'], None)
2683 return postincoming(ui, repo, modheads, opts['update'], None)
2681
2684
2682 def update(ui, repo, node=None, rev=None, clean=False, date=None):
2685 def update(ui, repo, node=None, rev=None, clean=False, date=None):
2683 """update working directory
2686 """update working directory
2684
2687
2685 Update the working directory to the specified revision, or the
2688 Update the working directory to the specified revision, or the
2686 tip of the current branch if none is specified.
2689 tip of the current branch if none is specified.
2687 See 'hg help dates' for a list of formats valid for -d/--date.
2690 See 'hg help dates' for a list of formats valid for -d/--date.
2688
2691
2689 If there are no outstanding changes in the working directory and
2692 If there are no outstanding changes in the working directory and
2690 there is a linear relationship between the current version and the
2693 there is a linear relationship between the current version and the
2691 requested version, the result is the requested version.
2694 requested version, the result is the requested version.
2692
2695
2693 To merge the working directory with another revision, use the
2696 To merge the working directory with another revision, use the
2694 merge command.
2697 merge command.
2695
2698
2696 By default, update will refuse to run if doing so would require
2699 By default, update will refuse to run if doing so would require
2697 discarding local changes.
2700 discarding local changes.
2698 """
2701 """
2699 if rev and node:
2702 if rev and node:
2700 raise util.Abort(_("please specify just one revision"))
2703 raise util.Abort(_("please specify just one revision"))
2701
2704
2702 if not rev:
2705 if not rev:
2703 rev = node
2706 rev = node
2704
2707
2705 if date:
2708 if date:
2706 if rev:
2709 if rev:
2707 raise util.Abort(_("you can't specify a revision and a date"))
2710 raise util.Abort(_("you can't specify a revision and a date"))
2708 rev = cmdutil.finddate(ui, repo, date)
2711 rev = cmdutil.finddate(ui, repo, date)
2709
2712
2710 if clean:
2713 if clean:
2711 return hg.clean(repo, rev)
2714 return hg.clean(repo, rev)
2712 else:
2715 else:
2713 return hg.update(repo, rev)
2716 return hg.update(repo, rev)
2714
2717
2715 def verify(ui, repo):
2718 def verify(ui, repo):
2716 """verify the integrity of the repository
2719 """verify the integrity of the repository
2717
2720
2718 Verify the integrity of the current repository.
2721 Verify the integrity of the current repository.
2719
2722
2720 This will perform an extensive check of the repository's
2723 This will perform an extensive check of the repository's
2721 integrity, validating the hashes and checksums of each entry in
2724 integrity, validating the hashes and checksums of each entry in
2722 the changelog, manifest, and tracked files, as well as the
2725 the changelog, manifest, and tracked files, as well as the
2723 integrity of their crosslinks and indices.
2726 integrity of their crosslinks and indices.
2724 """
2727 """
2725 return hg.verify(repo)
2728 return hg.verify(repo)
2726
2729
2727 def version_(ui):
2730 def version_(ui):
2728 """output version and copyright information"""
2731 """output version and copyright information"""
2729 ui.write(_("Mercurial Distributed SCM (version %s)\n")
2732 ui.write(_("Mercurial Distributed SCM (version %s)\n")
2730 % version.get_version())
2733 % version.get_version())
2731 ui.status(_(
2734 ui.status(_(
2732 "\nCopyright (C) 2005-2008 Matt Mackall <mpm@selenic.com> and others\n"
2735 "\nCopyright (C) 2005-2008 Matt Mackall <mpm@selenic.com> and others\n"
2733 "This is free software; see the source for copying conditions. "
2736 "This is free software; see the source for copying conditions. "
2734 "There is NO\nwarranty; "
2737 "There is NO\nwarranty; "
2735 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
2738 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
2736 ))
2739 ))
2737
2740
2738 # Command options and aliases are listed here, alphabetically
2741 # Command options and aliases are listed here, alphabetically
2739
2742
2740 globalopts = [
2743 globalopts = [
2741 ('R', 'repository', '',
2744 ('R', 'repository', '',
2742 _('repository root directory or symbolic path name')),
2745 _('repository root directory or symbolic path name')),
2743 ('', 'cwd', '', _('change working directory')),
2746 ('', 'cwd', '', _('change working directory')),
2744 ('y', 'noninteractive', None,
2747 ('y', 'noninteractive', None,
2745 _('do not prompt, assume \'yes\' for any required answers')),
2748 _('do not prompt, assume \'yes\' for any required answers')),
2746 ('q', 'quiet', None, _('suppress output')),
2749 ('q', 'quiet', None, _('suppress output')),
2747 ('v', 'verbose', None, _('enable additional output')),
2750 ('v', 'verbose', None, _('enable additional output')),
2748 ('', 'config', [], _('set/override config option')),
2751 ('', 'config', [], _('set/override config option')),
2749 ('', 'debug', None, _('enable debugging output')),
2752 ('', 'debug', None, _('enable debugging output')),
2750 ('', 'debugger', None, _('start debugger')),
2753 ('', 'debugger', None, _('start debugger')),
2751 ('', 'encoding', util._encoding, _('set the charset encoding')),
2754 ('', 'encoding', util._encoding, _('set the charset encoding')),
2752 ('', 'encodingmode', util._encodingmode, _('set the charset encoding mode')),
2755 ('', 'encodingmode', util._encodingmode, _('set the charset encoding mode')),
2753 ('', 'lsprof', None, _('print improved command execution profile')),
2756 ('', 'lsprof', None, _('print improved command execution profile')),
2754 ('', 'traceback', None, _('print traceback on exception')),
2757 ('', 'traceback', None, _('print traceback on exception')),
2755 ('', 'time', None, _('time how long the command takes')),
2758 ('', 'time', None, _('time how long the command takes')),
2756 ('', 'profile', None, _('print command execution profile')),
2759 ('', 'profile', None, _('print command execution profile')),
2757 ('', 'version', None, _('output version information and exit')),
2760 ('', 'version', None, _('output version information and exit')),
2758 ('h', 'help', None, _('display help and exit')),
2761 ('h', 'help', None, _('display help and exit')),
2759 ]
2762 ]
2760
2763
2761 dryrunopts = [('n', 'dry-run', None,
2764 dryrunopts = [('n', 'dry-run', None,
2762 _('do not perform actions, just print output'))]
2765 _('do not perform actions, just print output'))]
2763
2766
2764 remoteopts = [
2767 remoteopts = [
2765 ('e', 'ssh', '', _('specify ssh command to use')),
2768 ('e', 'ssh', '', _('specify ssh command to use')),
2766 ('', 'remotecmd', '', _('specify hg command to run on the remote side')),
2769 ('', 'remotecmd', '', _('specify hg command to run on the remote side')),
2767 ]
2770 ]
2768
2771
2769 walkopts = [
2772 walkopts = [
2770 ('I', 'include', [], _('include names matching the given patterns')),
2773 ('I', 'include', [], _('include names matching the given patterns')),
2771 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2774 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2772 ]
2775 ]
2773
2776
2774 commitopts = [
2777 commitopts = [
2775 ('m', 'message', '', _('use <text> as commit message')),
2778 ('m', 'message', '', _('use <text> as commit message')),
2776 ('l', 'logfile', '', _('read commit message from <file>')),
2779 ('l', 'logfile', '', _('read commit message from <file>')),
2777 ]
2780 ]
2778
2781
2779 commitopts2 = [
2782 commitopts2 = [
2780 ('d', 'date', '', _('record datecode as commit date')),
2783 ('d', 'date', '', _('record datecode as commit date')),
2781 ('u', 'user', '', _('record user as committer')),
2784 ('u', 'user', '', _('record user as committer')),
2782 ]
2785 ]
2783
2786
2784 templateopts = [
2787 templateopts = [
2785 ('', 'style', '', _('display using template map file')),
2788 ('', 'style', '', _('display using template map file')),
2786 ('', 'template', '', _('display with template')),
2789 ('', 'template', '', _('display with template')),
2787 ]
2790 ]
2788
2791
2789 logopts = [
2792 logopts = [
2790 ('p', 'patch', None, _('show patch')),
2793 ('p', 'patch', None, _('show patch')),
2791 ('l', 'limit', '', _('limit number of changes displayed')),
2794 ('l', 'limit', '', _('limit number of changes displayed')),
2792 ('M', 'no-merges', None, _('do not show merges')),
2795 ('M', 'no-merges', None, _('do not show merges')),
2793 ] + templateopts
2796 ] + templateopts
2794
2797
2795 table = {
2798 table = {
2796 "^add": (add, walkopts + dryrunopts, _('hg add [OPTION]... [FILE]...')),
2799 "^add": (add, walkopts + dryrunopts, _('hg add [OPTION]... [FILE]...')),
2797 "addremove":
2800 "addremove":
2798 (addremove,
2801 (addremove,
2799 [('s', 'similarity', '',
2802 [('s', 'similarity', '',
2800 _('guess renamed files by similarity (0<=s<=100)')),
2803 _('guess renamed files by similarity (0<=s<=100)')),
2801 ] + walkopts + dryrunopts,
2804 ] + walkopts + dryrunopts,
2802 _('hg addremove [OPTION]... [FILE]...')),
2805 _('hg addremove [OPTION]... [FILE]...')),
2803 "^annotate|blame":
2806 "^annotate|blame":
2804 (annotate,
2807 (annotate,
2805 [('r', 'rev', '', _('annotate the specified revision')),
2808 [('r', 'rev', '', _('annotate the specified revision')),
2806 ('f', 'follow', None, _('follow file copies and renames')),
2809 ('f', 'follow', None, _('follow file copies and renames')),
2807 ('a', 'text', None, _('treat all files as text')),
2810 ('a', 'text', None, _('treat all files as text')),
2808 ('u', 'user', None, _('list the author (long with -v)')),
2811 ('u', 'user', None, _('list the author (long with -v)')),
2809 ('d', 'date', None, _('list the date (short with -q)')),
2812 ('d', 'date', None, _('list the date (short with -q)')),
2810 ('n', 'number', None, _('list the revision number (default)')),
2813 ('n', 'number', None, _('list the revision number (default)')),
2811 ('c', 'changeset', None, _('list the changeset')),
2814 ('c', 'changeset', None, _('list the changeset')),
2812 ('l', 'line-number', None,
2815 ('l', 'line-number', None,
2813 _('show line number at the first appearance'))
2816 _('show line number at the first appearance'))
2814 ] + walkopts,
2817 ] + walkopts,
2815 _('hg annotate [-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...')),
2818 _('hg annotate [-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...')),
2816 "archive":
2819 "archive":
2817 (archive,
2820 (archive,
2818 [('', 'no-decode', None, _('do not pass files through decoders')),
2821 [('', 'no-decode', None, _('do not pass files through decoders')),
2819 ('p', 'prefix', '', _('directory prefix for files in archive')),
2822 ('p', 'prefix', '', _('directory prefix for files in archive')),
2820 ('r', 'rev', '', _('revision to distribute')),
2823 ('r', 'rev', '', _('revision to distribute')),
2821 ('t', 'type', '', _('type of distribution to create')),
2824 ('t', 'type', '', _('type of distribution to create')),
2822 ] + walkopts,
2825 ] + walkopts,
2823 _('hg archive [OPTION]... DEST')),
2826 _('hg archive [OPTION]... DEST')),
2824 "backout":
2827 "backout":
2825 (backout,
2828 (backout,
2826 [('', 'merge', None,
2829 [('', 'merge', None,
2827 _('merge with old dirstate parent after backout')),
2830 _('merge with old dirstate parent after backout')),
2828 ('', 'parent', '', _('parent to choose when backing out merge')),
2831 ('', 'parent', '', _('parent to choose when backing out merge')),
2829 ('r', 'rev', '', _('revision to backout')),
2832 ('r', 'rev', '', _('revision to backout')),
2830 ] + walkopts + commitopts + commitopts2,
2833 ] + walkopts + commitopts + commitopts2,
2831 _('hg backout [OPTION]... [-r] REV')),
2834 _('hg backout [OPTION]... [-r] REV')),
2832 "bisect":
2835 "bisect":
2833 (bisect,
2836 (bisect,
2834 [('r', 'reset', False, _('reset bisect state')),
2837 [('r', 'reset', False, _('reset bisect state')),
2835 ('g', 'good', False, _('mark changeset good')),
2838 ('g', 'good', False, _('mark changeset good')),
2836 ('b', 'bad', False, _('mark changeset bad')),
2839 ('b', 'bad', False, _('mark changeset bad')),
2837 ('s', 'skip', False, _('skip testing changeset')),
2840 ('s', 'skip', False, _('skip testing changeset')),
2838 ('U', 'noupdate', False, _('do not update to target'))],
2841 ('U', 'noupdate', False, _('do not update to target'))],
2839 _("hg bisect [-gbsr] [REV]")),
2842 _("hg bisect [-gbsr] [REV]")),
2840 "branch":
2843 "branch":
2841 (branch,
2844 (branch,
2842 [('f', 'force', None,
2845 [('f', 'force', None,
2843 _('set branch name even if it shadows an existing branch'))],
2846 _('set branch name even if it shadows an existing branch'))],
2844 _('hg branch [-f] [NAME]')),
2847 _('hg branch [-f] [NAME]')),
2845 "branches":
2848 "branches":
2846 (branches,
2849 (branches,
2847 [('a', 'active', False,
2850 [('a', 'active', False,
2848 _('show only branches that have unmerged heads'))],
2851 _('show only branches that have unmerged heads'))],
2849 _('hg branches [-a]')),
2852 _('hg branches [-a]')),
2850 "bundle":
2853 "bundle":
2851 (bundle,
2854 (bundle,
2852 [('f', 'force', None,
2855 [('f', 'force', None,
2853 _('run even when remote repository is unrelated')),
2856 _('run even when remote repository is unrelated')),
2854 ('r', 'rev', [],
2857 ('r', 'rev', [],
2855 _('a changeset you would like to bundle')),
2858 _('a changeset you would like to bundle')),
2856 ('', 'base', [],
2859 ('', 'base', [],
2857 _('a base changeset to specify instead of a destination')),
2860 _('a base changeset to specify instead of a destination')),
2858 ('a', 'all', None,
2861 ('a', 'all', None,
2859 _('bundle all changesets in the repository')),
2862 _('bundle all changesets in the repository')),
2860 ] + remoteopts,
2863 ] + remoteopts,
2861 _('hg bundle [-f] [-a] [-r REV]... [--base REV]... FILE [DEST]')),
2864 _('hg bundle [-f] [-a] [-r REV]... [--base REV]... FILE [DEST]')),
2862 "cat":
2865 "cat":
2863 (cat,
2866 (cat,
2864 [('o', 'output', '', _('print output to file with formatted name')),
2867 [('o', 'output', '', _('print output to file with formatted name')),
2865 ('r', 'rev', '', _('print the given revision')),
2868 ('r', 'rev', '', _('print the given revision')),
2866 ('', 'decode', None, _('apply any matching decode filter')),
2869 ('', 'decode', None, _('apply any matching decode filter')),
2867 ] + walkopts,
2870 ] + walkopts,
2868 _('hg cat [OPTION]... FILE...')),
2871 _('hg cat [OPTION]... FILE...')),
2869 "^clone":
2872 "^clone":
2870 (clone,
2873 (clone,
2871 [('U', 'noupdate', None, _('do not update the new working directory')),
2874 [('U', 'noupdate', None, _('do not update the new working directory')),
2872 ('r', 'rev', [],
2875 ('r', 'rev', [],
2873 _('a changeset you would like to have after cloning')),
2876 _('a changeset you would like to have after cloning')),
2874 ('', 'pull', None, _('use pull protocol to copy metadata')),
2877 ('', 'pull', None, _('use pull protocol to copy metadata')),
2875 ('', 'uncompressed', None,
2878 ('', 'uncompressed', None,
2876 _('use uncompressed transfer (fast over LAN)')),
2879 _('use uncompressed transfer (fast over LAN)')),
2877 ] + remoteopts,
2880 ] + remoteopts,
2878 _('hg clone [OPTION]... SOURCE [DEST]')),
2881 _('hg clone [OPTION]... SOURCE [DEST]')),
2879 "^commit|ci":
2882 "^commit|ci":
2880 (commit,
2883 (commit,
2881 [('A', 'addremove', None,
2884 [('A', 'addremove', None,
2882 _('mark new/missing files as added/removed before committing')),
2885 _('mark new/missing files as added/removed before committing')),
2883 ] + walkopts + commitopts + commitopts2,
2886 ] + walkopts + commitopts + commitopts2,
2884 _('hg commit [OPTION]... [FILE]...')),
2887 _('hg commit [OPTION]... [FILE]...')),
2885 "copy|cp":
2888 "copy|cp":
2886 (copy,
2889 (copy,
2887 [('A', 'after', None, _('record a copy that has already occurred')),
2890 [('A', 'after', None, _('record a copy that has already occurred')),
2888 ('f', 'force', None,
2891 ('f', 'force', None,
2889 _('forcibly copy over an existing managed file')),
2892 _('forcibly copy over an existing managed file')),
2890 ] + walkopts + dryrunopts,
2893 ] + walkopts + dryrunopts,
2891 _('hg copy [OPTION]... [SOURCE]... DEST')),
2894 _('hg copy [OPTION]... [SOURCE]... DEST')),
2892 "debugancestor": (debugancestor, [],
2895 "debugancestor": (debugancestor, [],
2893 _('hg debugancestor [INDEX] REV1 REV2')),
2896 _('hg debugancestor [INDEX] REV1 REV2')),
2894 "debugcheckstate": (debugcheckstate, [], _('hg debugcheckstate')),
2897 "debugcheckstate": (debugcheckstate, [], _('hg debugcheckstate')),
2895 "debugcomplete":
2898 "debugcomplete":
2896 (debugcomplete,
2899 (debugcomplete,
2897 [('o', 'options', None, _('show the command options'))],
2900 [('o', 'options', None, _('show the command options'))],
2898 _('hg debugcomplete [-o] CMD')),
2901 _('hg debugcomplete [-o] CMD')),
2899 "debugdate":
2902 "debugdate":
2900 (debugdate,
2903 (debugdate,
2901 [('e', 'extended', None, _('try extended date formats'))],
2904 [('e', 'extended', None, _('try extended date formats'))],
2902 _('hg debugdate [-e] DATE [RANGE]')),
2905 _('hg debugdate [-e] DATE [RANGE]')),
2903 "debugdata": (debugdata, [], _('hg debugdata FILE REV')),
2906 "debugdata": (debugdata, [], _('hg debugdata FILE REV')),
2904 "debugfsinfo": (debugfsinfo, [], _('hg debugfsinfo [PATH]')),
2907 "debugfsinfo": (debugfsinfo, [], _('hg debugfsinfo [PATH]')),
2905 "debugindex": (debugindex, [], _('hg debugindex FILE')),
2908 "debugindex": (debugindex, [], _('hg debugindex FILE')),
2906 "debugindexdot": (debugindexdot, [], _('hg debugindexdot FILE')),
2909 "debugindexdot": (debugindexdot, [], _('hg debugindexdot FILE')),
2907 "debuginstall": (debuginstall, [], _('hg debuginstall')),
2910 "debuginstall": (debuginstall, [], _('hg debuginstall')),
2908 "debugrawcommit|rawcommit":
2911 "debugrawcommit|rawcommit":
2909 (rawcommit,
2912 (rawcommit,
2910 [('p', 'parent', [], _('parent')),
2913 [('p', 'parent', [], _('parent')),
2911 ('F', 'files', '', _('file list'))
2914 ('F', 'files', '', _('file list'))
2912 ] + commitopts + commitopts2,
2915 ] + commitopts + commitopts2,
2913 _('hg debugrawcommit [OPTION]... [FILE]...')),
2916 _('hg debugrawcommit [OPTION]... [FILE]...')),
2914 "debugrebuildstate":
2917 "debugrebuildstate":
2915 (debugrebuildstate,
2918 (debugrebuildstate,
2916 [('r', 'rev', '', _('revision to rebuild to'))],
2919 [('r', 'rev', '', _('revision to rebuild to'))],
2917 _('hg debugrebuildstate [-r REV] [REV]')),
2920 _('hg debugrebuildstate [-r REV] [REV]')),
2918 "debugrename":
2921 "debugrename":
2919 (debugrename,
2922 (debugrename,
2920 [('r', 'rev', '', _('revision to debug'))],
2923 [('r', 'rev', '', _('revision to debug'))],
2921 _('hg debugrename [-r REV] FILE')),
2924 _('hg debugrename [-r REV] FILE')),
2922 "debugsetparents":
2925 "debugsetparents":
2923 (debugsetparents,
2926 (debugsetparents,
2924 [],
2927 [],
2925 _('hg debugsetparents REV1 [REV2]')),
2928 _('hg debugsetparents REV1 [REV2]')),
2926 "debugstate": (debugstate, [], _('hg debugstate')),
2929 "debugstate": (debugstate, [], _('hg debugstate')),
2927 "debugwalk": (debugwalk, walkopts, _('hg debugwalk [OPTION]... [FILE]...')),
2930 "debugwalk": (debugwalk, walkopts, _('hg debugwalk [OPTION]... [FILE]...')),
2928 "^diff":
2931 "^diff":
2929 (diff,
2932 (diff,
2930 [('r', 'rev', [], _('revision')),
2933 [('r', 'rev', [], _('revision')),
2931 ('a', 'text', None, _('treat all files as text')),
2934 ('a', 'text', None, _('treat all files as text')),
2932 ('p', 'show-function', None,
2935 ('p', 'show-function', None,
2933 _('show which function each change is in')),
2936 _('show which function each change is in')),
2934 ('g', 'git', None, _('use git extended diff format')),
2937 ('g', 'git', None, _('use git extended diff format')),
2935 ('', 'nodates', None, _("don't include dates in diff headers")),
2938 ('', 'nodates', None, _("don't include dates in diff headers")),
2936 ('w', 'ignore-all-space', None,
2939 ('w', 'ignore-all-space', None,
2937 _('ignore white space when comparing lines')),
2940 _('ignore white space when comparing lines')),
2938 ('b', 'ignore-space-change', None,
2941 ('b', 'ignore-space-change', None,
2939 _('ignore changes in the amount of white space')),
2942 _('ignore changes in the amount of white space')),
2940 ('B', 'ignore-blank-lines', None,
2943 ('B', 'ignore-blank-lines', None,
2941 _('ignore changes whose lines are all blank')),
2944 _('ignore changes whose lines are all blank')),
2942 ('U', 'unified', 3,
2945 ('U', 'unified', 3,
2943 _('number of lines of context to show'))
2946 _('number of lines of context to show'))
2944 ] + walkopts,
2947 ] + walkopts,
2945 _('hg diff [OPTION]... [-r REV1 [-r REV2]] [FILE]...')),
2948 _('hg diff [OPTION]... [-r REV1 [-r REV2]] [FILE]...')),
2946 "^export":
2949 "^export":
2947 (export,
2950 (export,
2948 [('o', 'output', '', _('print output to file with formatted name')),
2951 [('o', 'output', '', _('print output to file with formatted name')),
2949 ('a', 'text', None, _('treat all files as text')),
2952 ('a', 'text', None, _('treat all files as text')),
2950 ('g', 'git', None, _('use git extended diff format')),
2953 ('g', 'git', None, _('use git extended diff format')),
2951 ('', 'nodates', None, _("don't include dates in diff headers")),
2954 ('', 'nodates', None, _("don't include dates in diff headers")),
2952 ('', 'switch-parent', None, _('diff against the second parent'))],
2955 ('', 'switch-parent', None, _('diff against the second parent'))],
2953 _('hg export [OPTION]... [-o OUTFILESPEC] REV...')),
2956 _('hg export [OPTION]... [-o OUTFILESPEC] REV...')),
2954 "grep":
2957 "grep":
2955 (grep,
2958 (grep,
2956 [('0', 'print0', None, _('end fields with NUL')),
2959 [('0', 'print0', None, _('end fields with NUL')),
2957 ('', 'all', None, _('print all revisions that match')),
2960 ('', 'all', None, _('print all revisions that match')),
2958 ('f', 'follow', None,
2961 ('f', 'follow', None,
2959 _('follow changeset history, or file history across copies and renames')),
2962 _('follow changeset history, or file history across copies and renames')),
2960 ('i', 'ignore-case', None, _('ignore case when matching')),
2963 ('i', 'ignore-case', None, _('ignore case when matching')),
2961 ('l', 'files-with-matches', None,
2964 ('l', 'files-with-matches', None,
2962 _('print only filenames and revs that match')),
2965 _('print only filenames and revs that match')),
2963 ('n', 'line-number', None, _('print matching line numbers')),
2966 ('n', 'line-number', None, _('print matching line numbers')),
2964 ('r', 'rev', [], _('search in given revision range')),
2967 ('r', 'rev', [], _('search in given revision range')),
2965 ('u', 'user', None, _('list the author (long with -v)')),
2968 ('u', 'user', None, _('list the author (long with -v)')),
2966 ('d', 'date', None, _('list the date (short with -q)')),
2969 ('d', 'date', None, _('list the date (short with -q)')),
2967 ] + walkopts,
2970 ] + walkopts,
2968 _('hg grep [OPTION]... PATTERN [FILE]...')),
2971 _('hg grep [OPTION]... PATTERN [FILE]...')),
2969 "heads":
2972 "heads":
2970 (heads,
2973 (heads,
2971 [('r', 'rev', '', _('show only heads which are descendants of rev')),
2974 [('r', 'rev', '', _('show only heads which are descendants of rev')),
2972 ] + templateopts,
2975 ] + templateopts,
2973 _('hg heads [-r REV] [REV]...')),
2976 _('hg heads [-r REV] [REV]...')),
2974 "help": (help_, [], _('hg help [COMMAND]')),
2977 "help": (help_, [], _('hg help [COMMAND]')),
2975 "identify|id":
2978 "identify|id":
2976 (identify,
2979 (identify,
2977 [('r', 'rev', '', _('identify the specified rev')),
2980 [('r', 'rev', '', _('identify the specified rev')),
2978 ('n', 'num', None, _('show local revision number')),
2981 ('n', 'num', None, _('show local revision number')),
2979 ('i', 'id', None, _('show global revision id')),
2982 ('i', 'id', None, _('show global revision id')),
2980 ('b', 'branch', None, _('show branch')),
2983 ('b', 'branch', None, _('show branch')),
2981 ('t', 'tags', None, _('show tags'))],
2984 ('t', 'tags', None, _('show tags'))],
2982 _('hg identify [-nibt] [-r REV] [SOURCE]')),
2985 _('hg identify [-nibt] [-r REV] [SOURCE]')),
2983 "import|patch":
2986 "import|patch":
2984 (import_,
2987 (import_,
2985 [('p', 'strip', 1,
2988 [('p', 'strip', 1,
2986 _('directory strip option for patch. This has the same\n'
2989 _('directory strip option for patch. This has the same\n'
2987 'meaning as the corresponding patch option')),
2990 'meaning as the corresponding patch option')),
2988 ('b', 'base', '', _('base path')),
2991 ('b', 'base', '', _('base path')),
2989 ('f', 'force', None,
2992 ('f', 'force', None,
2990 _('skip check for outstanding uncommitted changes')),
2993 _('skip check for outstanding uncommitted changes')),
2991 ('', 'no-commit', None, _("don't commit, just update the working directory")),
2994 ('', 'no-commit', None, _("don't commit, just update the working directory")),
2992 ('', 'exact', None,
2995 ('', 'exact', None,
2993 _('apply patch to the nodes from which it was generated')),
2996 _('apply patch to the nodes from which it was generated')),
2994 ('', 'import-branch', None,
2997 ('', 'import-branch', None,
2995 _('Use any branch information in patch (implied by --exact)'))] +
2998 _('Use any branch information in patch (implied by --exact)'))] +
2996 commitopts + commitopts2,
2999 commitopts + commitopts2,
2997 _('hg import [OPTION]... PATCH...')),
3000 _('hg import [OPTION]... PATCH...')),
2998 "incoming|in":
3001 "incoming|in":
2999 (incoming,
3002 (incoming,
3000 [('f', 'force', None,
3003 [('f', 'force', None,
3001 _('run even when remote repository is unrelated')),
3004 _('run even when remote repository is unrelated')),
3002 ('n', 'newest-first', None, _('show newest record first')),
3005 ('n', 'newest-first', None, _('show newest record first')),
3003 ('', 'bundle', '', _('file to store the bundles into')),
3006 ('', 'bundle', '', _('file to store the bundles into')),
3004 ('r', 'rev', [], _('a specific revision up to which you would like to pull')),
3007 ('r', 'rev', [], _('a specific revision up to which you would like to pull')),
3005 ] + logopts + remoteopts,
3008 ] + logopts + remoteopts,
3006 _('hg incoming [-p] [-n] [-M] [-f] [-r REV]...'
3009 _('hg incoming [-p] [-n] [-M] [-f] [-r REV]...'
3007 ' [--bundle FILENAME] [SOURCE]')),
3010 ' [--bundle FILENAME] [SOURCE]')),
3008 "^init":
3011 "^init":
3009 (init,
3012 (init,
3010 remoteopts,
3013 remoteopts,
3011 _('hg init [-e CMD] [--remotecmd CMD] [DEST]')),
3014 _('hg init [-e CMD] [--remotecmd CMD] [DEST]')),
3012 "locate":
3015 "locate":
3013 (locate,
3016 (locate,
3014 [('r', 'rev', '', _('search the repository as it stood at rev')),
3017 [('r', 'rev', '', _('search the repository as it stood at rev')),
3015 ('0', 'print0', None,
3018 ('0', 'print0', None,
3016 _('end filenames with NUL, for use with xargs')),
3019 _('end filenames with NUL, for use with xargs')),
3017 ('f', 'fullpath', None,
3020 ('f', 'fullpath', None,
3018 _('print complete paths from the filesystem root')),
3021 _('print complete paths from the filesystem root')),
3019 ] + walkopts,
3022 ] + walkopts,
3020 _('hg locate [OPTION]... [PATTERN]...')),
3023 _('hg locate [OPTION]... [PATTERN]...')),
3021 "^log|history":
3024 "^log|history":
3022 (log,
3025 (log,
3023 [('f', 'follow', None,
3026 [('f', 'follow', None,
3024 _('follow changeset history, or file history across copies and renames')),
3027 _('follow changeset history, or file history across copies and renames')),
3025 ('', 'follow-first', None,
3028 ('', 'follow-first', None,
3026 _('only follow the first parent of merge changesets')),
3029 _('only follow the first parent of merge changesets')),
3027 ('d', 'date', '', _('show revs matching date spec')),
3030 ('d', 'date', '', _('show revs matching date spec')),
3028 ('C', 'copies', None, _('show copied files')),
3031 ('C', 'copies', None, _('show copied files')),
3029 ('k', 'keyword', [], _('do case-insensitive search for a keyword')),
3032 ('k', 'keyword', [], _('do case-insensitive search for a keyword')),
3030 ('r', 'rev', [], _('show the specified revision or range')),
3033 ('r', 'rev', [], _('show the specified revision or range')),
3031 ('', 'removed', None, _('include revs where files were removed')),
3034 ('', 'removed', None, _('include revs where files were removed')),
3032 ('m', 'only-merges', None, _('show only merges')),
3035 ('m', 'only-merges', None, _('show only merges')),
3033 ('b', 'only-branch', [],
3036 ('b', 'only-branch', [],
3034 _('show only changesets within the given named branch')),
3037 _('show only changesets within the given named branch')),
3035 ('P', 'prune', [], _('do not display revision or any of its ancestors')),
3038 ('P', 'prune', [], _('do not display revision or any of its ancestors')),
3036 ] + logopts + walkopts,
3039 ] + logopts + walkopts,
3037 _('hg log [OPTION]... [FILE]')),
3040 _('hg log [OPTION]... [FILE]')),
3038 "manifest":
3041 "manifest":
3039 (manifest,
3042 (manifest,
3040 [('r', 'rev', '', _('revision to display'))],
3043 [('r', 'rev', '', _('revision to display'))],
3041 _('hg manifest [-r REV]')),
3044 _('hg manifest [-r REV]')),
3042 "^merge":
3045 "^merge":
3043 (merge,
3046 (merge,
3044 [('f', 'force', None, _('force a merge with outstanding changes')),
3047 [('f', 'force', None, _('force a merge with outstanding changes')),
3045 ('r', 'rev', '', _('revision to merge')),
3048 ('r', 'rev', '', _('revision to merge')),
3046 ],
3049 ],
3047 _('hg merge [-f] [[-r] REV]')),
3050 _('hg merge [-f] [[-r] REV]')),
3048 "outgoing|out":
3051 "outgoing|out":
3049 (outgoing,
3052 (outgoing,
3050 [('f', 'force', None,
3053 [('f', 'force', None,
3051 _('run even when remote repository is unrelated')),
3054 _('run even when remote repository is unrelated')),
3052 ('r', 'rev', [], _('a specific revision you would like to push')),
3055 ('r', 'rev', [], _('a specific revision you would like to push')),
3053 ('n', 'newest-first', None, _('show newest record first')),
3056 ('n', 'newest-first', None, _('show newest record first')),
3054 ] + logopts + remoteopts,
3057 ] + logopts + remoteopts,
3055 _('hg outgoing [-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
3058 _('hg outgoing [-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
3056 "^parents":
3059 "^parents":
3057 (parents,
3060 (parents,
3058 [('r', 'rev', '', _('show parents from the specified rev')),
3061 [('r', 'rev', '', _('show parents from the specified rev')),
3059 ] + templateopts,
3062 ] + templateopts,
3060 _('hg parents [-r REV] [FILE]')),
3063 _('hg parents [-r REV] [FILE]')),
3061 "paths": (paths, [], _('hg paths [NAME]')),
3064 "paths": (paths, [], _('hg paths [NAME]')),
3062 "^pull":
3065 "^pull":
3063 (pull,
3066 (pull,
3064 [('u', 'update', None,
3067 [('u', 'update', None,
3065 _('update to new tip if changesets were pulled')),
3068 _('update to new tip if changesets were pulled')),
3066 ('f', 'force', None,
3069 ('f', 'force', None,
3067 _('run even when remote repository is unrelated')),
3070 _('run even when remote repository is unrelated')),
3068 ('r', 'rev', [],
3071 ('r', 'rev', [],
3069 _('a specific revision up to which you would like to pull')),
3072 _('a specific revision up to which you would like to pull')),
3070 ] + remoteopts,
3073 ] + remoteopts,
3071 _('hg pull [-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
3074 _('hg pull [-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
3072 "^push":
3075 "^push":
3073 (push,
3076 (push,
3074 [('f', 'force', None, _('force push')),
3077 [('f', 'force', None, _('force push')),
3075 ('r', 'rev', [], _('a specific revision you would like to push')),
3078 ('r', 'rev', [], _('a specific revision you would like to push')),
3076 ] + remoteopts,
3079 ] + remoteopts,
3077 _('hg push [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
3080 _('hg push [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
3078 "recover": (recover, [], _('hg recover')),
3081 "recover": (recover, [], _('hg recover')),
3079 "^remove|rm":
3082 "^remove|rm":
3080 (remove,
3083 (remove,
3081 [('A', 'after', None, _('record remove without deleting')),
3084 [('A', 'after', None, _('record remove without deleting')),
3082 ('f', 'force', None, _('remove file even if modified')),
3085 ('f', 'force', None, _('remove file even if modified')),
3083 ] + walkopts,
3086 ] + walkopts,
3084 _('hg remove [OPTION]... FILE...')),
3087 _('hg remove [OPTION]... FILE...')),
3085 "rename|mv":
3088 "rename|mv":
3086 (rename,
3089 (rename,
3087 [('A', 'after', None, _('record a rename that has already occurred')),
3090 [('A', 'after', None, _('record a rename that has already occurred')),
3088 ('f', 'force', None,
3091 ('f', 'force', None,
3089 _('forcibly copy over an existing managed file')),
3092 _('forcibly copy over an existing managed file')),
3090 ] + walkopts + dryrunopts,
3093 ] + walkopts + dryrunopts,
3091 _('hg rename [OPTION]... SOURCE... DEST')),
3094 _('hg rename [OPTION]... SOURCE... DEST')),
3092 "revert":
3095 "revert":
3093 (revert,
3096 (revert,
3094 [('a', 'all', None, _('revert all changes when no arguments given')),
3097 [('a', 'all', None, _('revert all changes when no arguments given')),
3095 ('d', 'date', '', _('tipmost revision matching date')),
3098 ('d', 'date', '', _('tipmost revision matching date')),
3096 ('r', 'rev', '', _('revision to revert to')),
3099 ('r', 'rev', '', _('revision to revert to')),
3097 ('', 'no-backup', None, _('do not save backup copies of files')),
3100 ('', 'no-backup', None, _('do not save backup copies of files')),
3098 ] + walkopts + dryrunopts,
3101 ] + walkopts + dryrunopts,
3099 _('hg revert [OPTION]... [-r REV] [NAME]...')),
3102 _('hg revert [OPTION]... [-r REV] [NAME]...')),
3100 "rollback": (rollback, [], _('hg rollback')),
3103 "rollback": (rollback, [], _('hg rollback')),
3101 "root": (root, [], _('hg root')),
3104 "root": (root, [], _('hg root')),
3102 "^serve":
3105 "^serve":
3103 (serve,
3106 (serve,
3104 [('A', 'accesslog', '', _('name of access log file to write to')),
3107 [('A', 'accesslog', '', _('name of access log file to write to')),
3105 ('d', 'daemon', None, _('run server in background')),
3108 ('d', 'daemon', None, _('run server in background')),
3106 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
3109 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
3107 ('E', 'errorlog', '', _('name of error log file to write to')),
3110 ('E', 'errorlog', '', _('name of error log file to write to')),
3108 ('p', 'port', 0, _('port to use (default: 8000)')),
3111 ('p', 'port', 0, _('port to use (default: 8000)')),
3109 ('a', 'address', '', _('address to use')),
3112 ('a', 'address', '', _('address to use')),
3110 ('', 'prefix', '', _('prefix path to serve from (default: server root)')),
3113 ('', 'prefix', '', _('prefix path to serve from (default: server root)')),
3111 ('n', 'name', '',
3114 ('n', 'name', '',
3112 _('name to show in web pages (default: working dir)')),
3115 _('name to show in web pages (default: working dir)')),
3113 ('', 'webdir-conf', '', _('name of the webdir config file'
3116 ('', 'webdir-conf', '', _('name of the webdir config file'
3114 ' (serve more than one repo)')),
3117 ' (serve more than one repo)')),
3115 ('', 'pid-file', '', _('name of file to write process ID to')),
3118 ('', 'pid-file', '', _('name of file to write process ID to')),
3116 ('', 'stdio', None, _('for remote clients')),
3119 ('', 'stdio', None, _('for remote clients')),
3117 ('t', 'templates', '', _('web templates to use')),
3120 ('t', 'templates', '', _('web templates to use')),
3118 ('', 'style', '', _('template style to use')),
3121 ('', 'style', '', _('template style to use')),
3119 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
3122 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
3120 ('', 'certificate', '', _('SSL certificate file'))],
3123 ('', 'certificate', '', _('SSL certificate file'))],
3121 _('hg serve [OPTION]...')),
3124 _('hg serve [OPTION]...')),
3122 "showconfig|debugconfig":
3125 "showconfig|debugconfig":
3123 (showconfig,
3126 (showconfig,
3124 [('u', 'untrusted', None, _('show untrusted configuration options'))],
3127 [('u', 'untrusted', None, _('show untrusted configuration options'))],
3125 _('hg showconfig [-u] [NAME]...')),
3128 _('hg showconfig [-u] [NAME]...')),
3126 "^status|st":
3129 "^status|st":
3127 (status,
3130 (status,
3128 [('A', 'all', None, _('show status of all files')),
3131 [('A', 'all', None, _('show status of all files')),
3129 ('m', 'modified', None, _('show only modified files')),
3132 ('m', 'modified', None, _('show only modified files')),
3130 ('a', 'added', None, _('show only added files')),
3133 ('a', 'added', None, _('show only added files')),
3131 ('r', 'removed', None, _('show only removed files')),
3134 ('r', 'removed', None, _('show only removed files')),
3132 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
3135 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
3133 ('c', 'clean', None, _('show only files without changes')),
3136 ('c', 'clean', None, _('show only files without changes')),
3134 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
3137 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
3135 ('i', 'ignored', None, _('show only ignored files')),
3138 ('i', 'ignored', None, _('show only ignored files')),
3136 ('n', 'no-status', None, _('hide status prefix')),
3139 ('n', 'no-status', None, _('hide status prefix')),
3137 ('C', 'copies', None, _('show source of copied files')),
3140 ('C', 'copies', None, _('show source of copied files')),
3138 ('0', 'print0', None,
3141 ('0', 'print0', None,
3139 _('end filenames with NUL, for use with xargs')),
3142 _('end filenames with NUL, for use with xargs')),
3140 ('', 'rev', [], _('show difference from revision')),
3143 ('', 'rev', [], _('show difference from revision')),
3141 ] + walkopts,
3144 ] + walkopts,
3142 _('hg status [OPTION]... [FILE]...')),
3145 _('hg status [OPTION]... [FILE]...')),
3143 "tag":
3146 "tag":
3144 (tag,
3147 (tag,
3145 [('f', 'force', None, _('replace existing tag')),
3148 [('f', 'force', None, _('replace existing tag')),
3146 ('l', 'local', None, _('make the tag local')),
3149 ('l', 'local', None, _('make the tag local')),
3147 ('r', 'rev', '', _('revision to tag')),
3150 ('r', 'rev', '', _('revision to tag')),
3148 ('', 'remove', None, _('remove a tag')),
3151 ('', 'remove', None, _('remove a tag')),
3149 # -l/--local is already there, commitopts cannot be used
3152 # -l/--local is already there, commitopts cannot be used
3150 ('m', 'message', '', _('use <text> as commit message')),
3153 ('m', 'message', '', _('use <text> as commit message')),
3151 ] + commitopts2,
3154 ] + commitopts2,
3152 _('hg tag [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME')),
3155 _('hg tag [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME')),
3153 "tags": (tags, [], _('hg tags')),
3156 "tags": (tags, [], _('hg tags')),
3154 "tip":
3157 "tip":
3155 (tip,
3158 (tip,
3156 [('p', 'patch', None, _('show patch')),
3159 [('p', 'patch', None, _('show patch')),
3157 ] + templateopts,
3160 ] + templateopts,
3158 _('hg tip [-p]')),
3161 _('hg tip [-p]')),
3159 "unbundle":
3162 "unbundle":
3160 (unbundle,
3163 (unbundle,
3161 [('u', 'update', None,
3164 [('u', 'update', None,
3162 _('update to new tip if changesets were unbundled'))],
3165 _('update to new tip if changesets were unbundled'))],
3163 _('hg unbundle [-u] FILE...')),
3166 _('hg unbundle [-u] FILE...')),
3164 "^update|up|checkout|co":
3167 "^update|up|checkout|co":
3165 (update,
3168 (update,
3166 [('C', 'clean', None, _('overwrite locally modified files')),
3169 [('C', 'clean', None, _('overwrite locally modified files')),
3167 ('d', 'date', '', _('tipmost revision matching date')),
3170 ('d', 'date', '', _('tipmost revision matching date')),
3168 ('r', 'rev', '', _('revision'))],
3171 ('r', 'rev', '', _('revision'))],
3169 _('hg update [-C] [-d DATE] [[-r] REV]')),
3172 _('hg update [-C] [-d DATE] [[-r] REV]')),
3170 "verify": (verify, [], _('hg verify')),
3173 "verify": (verify, [], _('hg verify')),
3171 "version": (version_, [], _('hg version')),
3174 "version": (version_, [], _('hg version')),
3172 }
3175 }
3173
3176
3174 norepo = ("clone init version help debugcomplete debugdata"
3177 norepo = ("clone init version help debugcomplete debugdata"
3175 " debugindex debugindexdot debugdate debuginstall debugfsinfo")
3178 " debugindex debugindexdot debugdate debuginstall debugfsinfo")
3176 optionalrepo = ("identify paths serve showconfig debugancestor")
3179 optionalrepo = ("identify paths serve showconfig debugancestor")
@@ -1,596 +1,598 b''
1 """
1 """
2 dirstate.py - working directory tracking for mercurial
2 dirstate.py - working directory tracking for mercurial
3
3
4 Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
5
5
6 This software may be used and distributed according to the terms
6 This software may be used and distributed according to the terms
7 of the GNU General Public License, incorporated herein by reference.
7 of the GNU General Public License, incorporated herein by reference.
8 """
8 """
9
9
10 from node import *
10 from node import *
11 from i18n import _
11 from i18n import _
12 import struct, os, time, bisect, stat, strutil, util, re, errno, ignore
12 import struct, os, time, bisect, stat, strutil, util, re, errno, ignore
13 import cStringIO, osutil
13 import cStringIO, osutil
14
14
15 _unknown = ('?', 0, 0, 0)
15 _unknown = ('?', 0, 0, 0)
16 _format = ">cllll"
16 _format = ">cllll"
17
17
18 class dirstate(object):
18 class dirstate(object):
19
19
20 def __init__(self, opener, ui, root):
20 def __init__(self, opener, ui, root):
21 self._opener = opener
21 self._opener = opener
22 self._root = root
22 self._root = root
23 self._dirty = False
23 self._dirty = False
24 self._dirtypl = False
24 self._dirtypl = False
25 self._ui = ui
25 self._ui = ui
26
26
27 def __getattr__(self, name):
27 def __getattr__(self, name):
28 if name == '_map':
28 if name == '_map':
29 self._read()
29 self._read()
30 return self._map
30 return self._map
31 elif name == '_copymap':
31 elif name == '_copymap':
32 self._read()
32 self._read()
33 return self._copymap
33 return self._copymap
34 elif name == '_branch':
34 elif name == '_branch':
35 try:
35 try:
36 self._branch = (self._opener("branch").read().strip()
36 self._branch = (self._opener("branch").read().strip()
37 or "default")
37 or "default")
38 except IOError:
38 except IOError:
39 self._branch = "default"
39 self._branch = "default"
40 return self._branch
40 return self._branch
41 elif name == '_pl':
41 elif name == '_pl':
42 self._pl = [nullid, nullid]
42 self._pl = [nullid, nullid]
43 try:
43 try:
44 st = self._opener("dirstate").read(40)
44 st = self._opener("dirstate").read(40)
45 if len(st) == 40:
45 if len(st) == 40:
46 self._pl = st[:20], st[20:40]
46 self._pl = st[:20], st[20:40]
47 except IOError, err:
47 except IOError, err:
48 if err.errno != errno.ENOENT: raise
48 if err.errno != errno.ENOENT: raise
49 return self._pl
49 return self._pl
50 elif name == '_dirs':
50 elif name == '_dirs':
51 self._dirs = {}
51 self._dirs = {}
52 for f in self._map:
52 for f in self._map:
53 if self[f] != 'r':
53 if self[f] != 'r':
54 self._incpath(f)
54 self._incpath(f)
55 return self._dirs
55 return self._dirs
56 elif name == '_ignore':
56 elif name == '_ignore':
57 files = [self._join('.hgignore')]
57 files = [self._join('.hgignore')]
58 for name, path in self._ui.configitems("ui"):
58 for name, path in self._ui.configitems("ui"):
59 if name == 'ignore' or name.startswith('ignore.'):
59 if name == 'ignore' or name.startswith('ignore.'):
60 files.append(os.path.expanduser(path))
60 files.append(os.path.expanduser(path))
61 self._ignore = ignore.ignore(self._root, files, self._ui.warn)
61 self._ignore = ignore.ignore(self._root, files, self._ui.warn)
62 return self._ignore
62 return self._ignore
63 elif name == '_slash':
63 elif name == '_slash':
64 self._slash = self._ui.configbool('ui', 'slash') and os.sep != '/'
64 self._slash = self._ui.configbool('ui', 'slash') and os.sep != '/'
65 return self._slash
65 return self._slash
66 else:
66 else:
67 raise AttributeError, name
67 raise AttributeError, name
68
68
69 def _join(self, f):
69 def _join(self, f):
70 return os.path.join(self._root, f)
70 return os.path.join(self._root, f)
71
71
72 def getcwd(self):
72 def getcwd(self):
73 cwd = os.getcwd()
73 cwd = os.getcwd()
74 if cwd == self._root: return ''
74 if cwd == self._root: return ''
75 # self._root ends with a path separator if self._root is '/' or 'C:\'
75 # self._root ends with a path separator if self._root is '/' or 'C:\'
76 rootsep = self._root
76 rootsep = self._root
77 if not util.endswithsep(rootsep):
77 if not util.endswithsep(rootsep):
78 rootsep += os.sep
78 rootsep += os.sep
79 if cwd.startswith(rootsep):
79 if cwd.startswith(rootsep):
80 return cwd[len(rootsep):]
80 return cwd[len(rootsep):]
81 else:
81 else:
82 # we're outside the repo. return an absolute path.
82 # we're outside the repo. return an absolute path.
83 return cwd
83 return cwd
84
84
85 def pathto(self, f, cwd=None):
85 def pathto(self, f, cwd=None):
86 if cwd is None:
86 if cwd is None:
87 cwd = self.getcwd()
87 cwd = self.getcwd()
88 path = util.pathto(self._root, cwd, f)
88 path = util.pathto(self._root, cwd, f)
89 if self._slash:
89 if self._slash:
90 return util.normpath(path)
90 return util.normpath(path)
91 return path
91 return path
92
92
93 def __getitem__(self, key):
93 def __getitem__(self, key):
94 ''' current states:
94 ''' current states:
95 n normal
95 n normal
96 m needs merging
96 m needs merging
97 r marked for removal
97 r marked for removal
98 a marked for addition
98 a marked for addition
99 ? not tracked'''
99 ? not tracked'''
100 return self._map.get(key, ("?",))[0]
100 return self._map.get(key, ("?",))[0]
101
101
102 def __contains__(self, key):
102 def __contains__(self, key):
103 return key in self._map
103 return key in self._map
104
104
105 def __iter__(self):
105 def __iter__(self):
106 a = self._map.keys()
106 a = self._map.keys()
107 a.sort()
107 a.sort()
108 for x in a:
108 for x in a:
109 yield x
109 yield x
110
110
111 def parents(self):
111 def parents(self):
112 return self._pl
112 return self._pl
113
113
114 def branch(self):
114 def branch(self):
115 return self._branch
115 return self._branch
116
116
117 def setparents(self, p1, p2=nullid):
117 def setparents(self, p1, p2=nullid):
118 self._dirty = self._dirtypl = True
118 self._dirty = self._dirtypl = True
119 self._pl = p1, p2
119 self._pl = p1, p2
120
120
121 def setbranch(self, branch):
121 def setbranch(self, branch):
122 self._branch = branch
122 self._branch = branch
123 self._opener("branch", "w").write(branch + '\n')
123 self._opener("branch", "w").write(branch + '\n')
124
124
125 def _read(self):
125 def _read(self):
126 self._map = {}
126 self._map = {}
127 self._copymap = {}
127 self._copymap = {}
128 if not self._dirtypl:
128 if not self._dirtypl:
129 self._pl = [nullid, nullid]
129 self._pl = [nullid, nullid]
130 try:
130 try:
131 st = self._opener("dirstate").read()
131 st = self._opener("dirstate").read()
132 except IOError, err:
132 except IOError, err:
133 if err.errno != errno.ENOENT: raise
133 if err.errno != errno.ENOENT: raise
134 return
134 return
135 if not st:
135 if not st:
136 return
136 return
137
137
138 if not self._dirtypl:
138 if not self._dirtypl:
139 self._pl = [st[:20], st[20: 40]]
139 self._pl = [st[:20], st[20: 40]]
140
140
141 # deref fields so they will be local in loop
141 # deref fields so they will be local in loop
142 dmap = self._map
142 dmap = self._map
143 copymap = self._copymap
143 copymap = self._copymap
144 unpack = struct.unpack
144 unpack = struct.unpack
145 e_size = struct.calcsize(_format)
145 e_size = struct.calcsize(_format)
146 pos1 = 40
146 pos1 = 40
147 l = len(st)
147 l = len(st)
148
148
149 # the inner loop
149 # the inner loop
150 while pos1 < l:
150 while pos1 < l:
151 pos2 = pos1 + e_size
151 pos2 = pos1 + e_size
152 e = unpack(">cllll", st[pos1:pos2]) # a literal here is faster
152 e = unpack(">cllll", st[pos1:pos2]) # a literal here is faster
153 pos1 = pos2 + e[4]
153 pos1 = pos2 + e[4]
154 f = st[pos2:pos1]
154 f = st[pos2:pos1]
155 if '\0' in f:
155 if '\0' in f:
156 f, c = f.split('\0')
156 f, c = f.split('\0')
157 copymap[f] = c
157 copymap[f] = c
158 dmap[f] = e # we hold onto e[4] because making a subtuple is slow
158 dmap[f] = e # we hold onto e[4] because making a subtuple is slow
159
159
160 def invalidate(self):
160 def invalidate(self):
161 for a in "_map _copymap _branch _pl _dirs _ignore".split():
161 for a in "_map _copymap _branch _pl _dirs _ignore".split():
162 if a in self.__dict__:
162 if a in self.__dict__:
163 delattr(self, a)
163 delattr(self, a)
164 self._dirty = False
164 self._dirty = False
165
165
166 def copy(self, source, dest):
166 def copy(self, source, dest):
167 self._dirty = True
167 self._dirty = True
168 self._copymap[dest] = source
168 self._copymap[dest] = source
169
169
170 def copied(self, file):
170 def copied(self, file):
171 return self._copymap.get(file, None)
171 return self._copymap.get(file, None)
172
172
173 def copies(self):
173 def copies(self):
174 return self._copymap
174 return self._copymap
175
175
176 def _incpath(self, path):
176 def _incpath(self, path):
177 c = path.rfind('/')
177 c = path.rfind('/')
178 if c >= 0:
178 if c >= 0:
179 dirs = self._dirs
179 dirs = self._dirs
180 base = path[:c]
180 base = path[:c]
181 if base not in dirs:
181 if base not in dirs:
182 self._incpath(base)
182 self._incpath(base)
183 dirs[base] = 1
183 dirs[base] = 1
184 else:
184 else:
185 dirs[base] += 1
185 dirs[base] += 1
186
186
187 def _decpath(self, path):
187 def _decpath(self, path):
188 c = path.rfind('/')
188 c = path.rfind('/')
189 if c >= 0:
189 if c >= 0:
190 base = path[:c]
190 base = path[:c]
191 dirs = self._dirs
191 dirs = self._dirs
192 if dirs[base] == 1:
192 if dirs[base] == 1:
193 del dirs[base]
193 del dirs[base]
194 self._decpath(base)
194 self._decpath(base)
195 else:
195 else:
196 dirs[base] -= 1
196 dirs[base] -= 1
197
197
198 def _incpathcheck(self, f):
198 def _incpathcheck(self, f):
199 if '\r' in f or '\n' in f:
199 if '\r' in f or '\n' in f:
200 raise util.Abort(_("'\\n' and '\\r' disallowed in filenames: %r")
200 raise util.Abort(_("'\\n' and '\\r' disallowed in filenames: %r")
201 % f)
201 % f)
202 # shadows
202 # shadows
203 if f in self._dirs:
203 if f in self._dirs:
204 raise util.Abort(_('directory %r already in dirstate') % f)
204 raise util.Abort(_('directory %r already in dirstate') % f)
205 for c in strutil.rfindall(f, '/'):
205 for c in strutil.rfindall(f, '/'):
206 d = f[:c]
206 d = f[:c]
207 if d in self._dirs:
207 if d in self._dirs:
208 break
208 break
209 if d in self._map and self[d] != 'r':
209 if d in self._map and self[d] != 'r':
210 raise util.Abort(_('file %r in dirstate clashes with %r') %
210 raise util.Abort(_('file %r in dirstate clashes with %r') %
211 (d, f))
211 (d, f))
212 self._incpath(f)
212 self._incpath(f)
213
213
214 def _changepath(self, f, newstate, relaxed=False):
214 def _changepath(self, f, newstate, relaxed=False):
215 # handle upcoming path changes
215 # handle upcoming path changes
216 oldstate = self[f]
216 oldstate = self[f]
217 if oldstate not in "?r" and newstate in "?r":
217 if oldstate not in "?r" and newstate in "?r":
218 if "_dirs" in self.__dict__:
218 if "_dirs" in self.__dict__:
219 self._decpath(f)
219 self._decpath(f)
220 return
220 return
221 if oldstate in "?r" and newstate not in "?r":
221 if oldstate in "?r" and newstate not in "?r":
222 if relaxed and oldstate == '?':
222 if relaxed and oldstate == '?':
223 # XXX
223 # XXX
224 # in relaxed mode we assume the caller knows
224 # in relaxed mode we assume the caller knows
225 # what it is doing, workaround for updating
225 # what it is doing, workaround for updating
226 # dir-to-file revisions
226 # dir-to-file revisions
227 if "_dirs" in self.__dict__:
227 if "_dirs" in self.__dict__:
228 self._incpath(f)
228 self._incpath(f)
229 return
229 return
230 self._incpathcheck(f)
230 self._incpathcheck(f)
231 return
231 return
232
232
233 def normal(self, f):
233 def normal(self, f):
234 'mark a file normal and clean'
234 'mark a file normal and clean'
235 self._dirty = True
235 self._dirty = True
236 self._changepath(f, 'n', True)
236 self._changepath(f, 'n', True)
237 s = os.lstat(self._join(f))
237 s = os.lstat(self._join(f))
238 self._map[f] = ('n', s.st_mode, s.st_size, s.st_mtime, 0)
238 self._map[f] = ('n', s.st_mode, s.st_size, s.st_mtime, 0)
239 if f in self._copymap:
239 if f in self._copymap:
240 del self._copymap[f]
240 del self._copymap[f]
241
241
242 def normallookup(self, f):
242 def normallookup(self, f):
243 'mark a file normal, but possibly dirty'
243 'mark a file normal, but possibly dirty'
244 self._dirty = True
244 self._dirty = True
245 self._changepath(f, 'n', True)
245 self._changepath(f, 'n', True)
246 self._map[f] = ('n', 0, -1, -1, 0)
246 self._map[f] = ('n', 0, -1, -1, 0)
247 if f in self._copymap:
247 if f in self._copymap:
248 del self._copymap[f]
248 del self._copymap[f]
249
249
250 def normaldirty(self, f):
250 def normaldirty(self, f):
251 'mark a file normal, but dirty'
251 'mark a file normal, but dirty'
252 self._dirty = True
252 self._dirty = True
253 self._changepath(f, 'n', True)
253 self._changepath(f, 'n', True)
254 self._map[f] = ('n', 0, -2, -1, 0)
254 self._map[f] = ('n', 0, -2, -1, 0)
255 if f in self._copymap:
255 if f in self._copymap:
256 del self._copymap[f]
256 del self._copymap[f]
257
257
258 def add(self, f):
258 def add(self, f):
259 'mark a file added'
259 'mark a file added'
260 self._dirty = True
260 self._dirty = True
261 self._changepath(f, 'a')
261 self._changepath(f, 'a')
262 self._map[f] = ('a', 0, -1, -1, 0)
262 self._map[f] = ('a', 0, -1, -1, 0)
263 if f in self._copymap:
263 if f in self._copymap:
264 del self._copymap[f]
264 del self._copymap[f]
265
265
266 def remove(self, f):
266 def remove(self, f):
267 'mark a file removed'
267 'mark a file removed'
268 self._dirty = True
268 self._dirty = True
269 self._changepath(f, 'r')
269 self._changepath(f, 'r')
270 self._map[f] = ('r', 0, 0, 0, 0)
270 self._map[f] = ('r', 0, 0, 0, 0)
271 if f in self._copymap:
271 if f in self._copymap:
272 del self._copymap[f]
272 del self._copymap[f]
273
273
274 def merge(self, f):
274 def merge(self, f):
275 'mark a file merged'
275 'mark a file merged'
276 self._dirty = True
276 self._dirty = True
277 s = os.lstat(self._join(f))
277 s = os.lstat(self._join(f))
278 self._changepath(f, 'm', True)
278 self._changepath(f, 'm', True)
279 self._map[f] = ('m', s.st_mode, s.st_size, s.st_mtime, 0)
279 self._map[f] = ('m', s.st_mode, s.st_size, s.st_mtime, 0)
280 if f in self._copymap:
280 if f in self._copymap:
281 del self._copymap[f]
281 del self._copymap[f]
282
282
283 def forget(self, f):
283 def forget(self, f):
284 'forget a file'
284 'forget a file'
285 self._dirty = True
285 self._dirty = True
286 try:
286 try:
287 self._changepath(f, '?')
287 self._changepath(f, '?')
288 del self._map[f]
288 del self._map[f]
289 except KeyError:
289 except KeyError:
290 self._ui.warn(_("not in dirstate: %s\n") % f)
290 self._ui.warn(_("not in dirstate: %s\n") % f)
291
291
292 def clear(self):
292 def clear(self):
293 self._map = {}
293 self._map = {}
294 if "_dirs" in self.__dict__:
294 if "_dirs" in self.__dict__:
295 delattr(self, "_dirs");
295 delattr(self, "_dirs");
296 self._copymap = {}
296 self._copymap = {}
297 self._pl = [nullid, nullid]
297 self._pl = [nullid, nullid]
298 self._dirty = True
298 self._dirty = True
299
299
300 def rebuild(self, parent, files):
300 def rebuild(self, parent, files):
301 self.clear()
301 self.clear()
302 for f in files:
302 for f in files:
303 if files.execf(f):
303 if files.execf(f):
304 self._map[f] = ('n', 0777, -1, 0, 0)
304 self._map[f] = ('n', 0777, -1, 0, 0)
305 else:
305 else:
306 self._map[f] = ('n', 0666, -1, 0, 0)
306 self._map[f] = ('n', 0666, -1, 0, 0)
307 self._pl = (parent, nullid)
307 self._pl = (parent, nullid)
308 self._dirty = True
308 self._dirty = True
309
309
310 def write(self):
310 def write(self):
311 if not self._dirty:
311 if not self._dirty:
312 return
312 return
313 cs = cStringIO.StringIO()
313 cs = cStringIO.StringIO()
314 copymap = self._copymap
314 copymap = self._copymap
315 pack = struct.pack
315 pack = struct.pack
316 write = cs.write
316 write = cs.write
317 write("".join(self._pl))
317 write("".join(self._pl))
318 for f, e in self._map.iteritems():
318 for f, e in self._map.iteritems():
319 if f in copymap:
319 if f in copymap:
320 f = "%s\0%s" % (f, copymap[f])
320 f = "%s\0%s" % (f, copymap[f])
321 e = pack(_format, e[0], e[1], e[2], e[3], len(f))
321 e = pack(_format, e[0], e[1], e[2], e[3], len(f))
322 write(e)
322 write(e)
323 write(f)
323 write(f)
324 st = self._opener("dirstate", "w", atomictemp=True)
324 st = self._opener("dirstate", "w", atomictemp=True)
325 st.write(cs.getvalue())
325 st.write(cs.getvalue())
326 st.rename()
326 st.rename()
327 self._dirty = self._dirtypl = False
327 self._dirty = self._dirtypl = False
328
328
329 def _filter(self, files):
329 def _filter(self, files):
330 ret = {}
330 ret = {}
331 unknown = []
331 unknown = []
332
332
333 for x in files:
333 for x in files:
334 if x == '.':
334 if x == '.':
335 return self._map.copy()
335 return self._map.copy()
336 if x not in self._map:
336 if x not in self._map:
337 unknown.append(x)
337 unknown.append(x)
338 else:
338 else:
339 ret[x] = self._map[x]
339 ret[x] = self._map[x]
340
340
341 if not unknown:
341 if not unknown:
342 return ret
342 return ret
343
343
344 b = self._map.keys()
344 b = self._map.keys()
345 b.sort()
345 b.sort()
346 blen = len(b)
346 blen = len(b)
347
347
348 for x in unknown:
348 for x in unknown:
349 bs = bisect.bisect(b, "%s%s" % (x, '/'))
349 bs = bisect.bisect(b, "%s%s" % (x, '/'))
350 while bs < blen:
350 while bs < blen:
351 s = b[bs]
351 s = b[bs]
352 if len(s) > len(x) and s.startswith(x):
352 if len(s) > len(x) and s.startswith(x):
353 ret[s] = self._map[s]
353 ret[s] = self._map[s]
354 else:
354 else:
355 break
355 break
356 bs += 1
356 bs += 1
357 return ret
357 return ret
358
358
359 def _supported(self, f, mode, verbose=False):
359 def _supported(self, f, mode, verbose=False):
360 if stat.S_ISREG(mode) or stat.S_ISLNK(mode):
360 if stat.S_ISREG(mode) or stat.S_ISLNK(mode):
361 return True
361 return True
362 if verbose:
362 if verbose:
363 kind = 'unknown'
363 kind = 'unknown'
364 if stat.S_ISCHR(mode): kind = _('character device')
364 if stat.S_ISCHR(mode): kind = _('character device')
365 elif stat.S_ISBLK(mode): kind = _('block device')
365 elif stat.S_ISBLK(mode): kind = _('block device')
366 elif stat.S_ISFIFO(mode): kind = _('fifo')
366 elif stat.S_ISFIFO(mode): kind = _('fifo')
367 elif stat.S_ISSOCK(mode): kind = _('socket')
367 elif stat.S_ISSOCK(mode): kind = _('socket')
368 elif stat.S_ISDIR(mode): kind = _('directory')
368 elif stat.S_ISDIR(mode): kind = _('directory')
369 self._ui.warn(_('%s: unsupported file type (type is %s)\n')
369 self._ui.warn(_('%s: unsupported file type (type is %s)\n')
370 % (self.pathto(f), kind))
370 % (self.pathto(f), kind))
371 return False
371 return False
372
372
373 def _dirignore(self, f):
373 def _dirignore(self, f):
374 if self._ignore(f):
374 if self._ignore(f):
375 return True
375 return True
376 for c in strutil.findall(f, '/'):
376 for c in strutil.findall(f, '/'):
377 if self._ignore(f[:c]):
377 if self._ignore(f[:c]):
378 return True
378 return True
379 return False
379 return False
380
380
381 def walk(self, files=None, match=util.always, badmatch=None):
381 def walk(self, files=None, match=util.always, badmatch=None):
382 # filter out the stat
382 # filter out the stat
383 for src, f, st in self.statwalk(files, match, badmatch=badmatch):
383 for src, f, st in self.statwalk(files, match, badmatch=badmatch):
384 yield src, f
384 yield src, f
385
385
386 def statwalk(self, files=None, match=util.always, ignored=False,
386 def statwalk(self, files=None, match=util.always, unknown=True,
387 badmatch=None, directories=False):
387 ignored=False, badmatch=None, directories=False):
388 '''
388 '''
389 walk recursively through the directory tree, finding all files
389 walk recursively through the directory tree, finding all files
390 matched by the match function
390 matched by the match function
391
391
392 results are yielded in a tuple (src, filename, st), where src
392 results are yielded in a tuple (src, filename, st), where src
393 is one of:
393 is one of:
394 'f' the file was found in the directory tree
394 'f' the file was found in the directory tree
395 'd' the file is a directory of the tree
395 'd' the file is a directory of the tree
396 'm' the file was only in the dirstate and not in the tree
396 'm' the file was only in the dirstate and not in the tree
397 'b' file was not found and matched badmatch
397 'b' file was not found and matched badmatch
398
398
399 and st is the stat result if the file was found in the directory.
399 and st is the stat result if the file was found in the directory.
400 '''
400 '''
401
401
402 # walk all files by default
402 # walk all files by default
403 if not files:
403 if not files:
404 files = ['.']
404 files = ['.']
405 dc = self._map.copy()
405 dc = self._map.copy()
406 else:
406 else:
407 files = util.unique(files)
407 files = util.unique(files)
408 dc = self._filter(files)
408 dc = self._filter(files)
409
409
410 def imatch(file_):
410 def imatch(file_):
411 if file_ not in dc and self._ignore(file_):
411 if file_ not in dc and self._ignore(file_):
412 return False
412 return False
413 return match(file_)
413 return match(file_)
414
414
415 # TODO: don't walk unknown directories if unknown and ignored are False
415 ignore = self._ignore
416 ignore = self._ignore
416 dirignore = self._dirignore
417 dirignore = self._dirignore
417 if ignored:
418 if ignored:
418 imatch = match
419 imatch = match
419 ignore = util.never
420 ignore = util.never
420 dirignore = util.never
421 dirignore = util.never
421
422
422 # self._root may end with a path separator when self._root == '/'
423 # self._root may end with a path separator when self._root == '/'
423 common_prefix_len = len(self._root)
424 common_prefix_len = len(self._root)
424 if not util.endswithsep(self._root):
425 if not util.endswithsep(self._root):
425 common_prefix_len += 1
426 common_prefix_len += 1
426
427
427 normpath = util.normpath
428 normpath = util.normpath
428 listdir = osutil.listdir
429 listdir = osutil.listdir
429 lstat = os.lstat
430 lstat = os.lstat
430 bisect_left = bisect.bisect_left
431 bisect_left = bisect.bisect_left
431 isdir = os.path.isdir
432 isdir = os.path.isdir
432 pconvert = util.pconvert
433 pconvert = util.pconvert
433 join = os.path.join
434 join = os.path.join
434 s_isdir = stat.S_ISDIR
435 s_isdir = stat.S_ISDIR
435 supported = self._supported
436 supported = self._supported
436 _join = self._join
437 _join = self._join
437 known = {'.hg': 1}
438 known = {'.hg': 1}
438
439
439 # recursion free walker, faster than os.walk.
440 # recursion free walker, faster than os.walk.
440 def findfiles(s):
441 def findfiles(s):
441 work = [s]
442 work = [s]
442 wadd = work.append
443 wadd = work.append
443 found = []
444 found = []
444 add = found.append
445 add = found.append
445 if directories:
446 if directories:
446 add((normpath(s[common_prefix_len:]), 'd', lstat(s)))
447 add((normpath(s[common_prefix_len:]), 'd', lstat(s)))
447 while work:
448 while work:
448 top = work.pop()
449 top = work.pop()
449 entries = listdir(top, stat=True)
450 entries = listdir(top, stat=True)
450 # nd is the top of the repository dir tree
451 # nd is the top of the repository dir tree
451 nd = normpath(top[common_prefix_len:])
452 nd = normpath(top[common_prefix_len:])
452 if nd == '.':
453 if nd == '.':
453 nd = ''
454 nd = ''
454 else:
455 else:
455 # do not recurse into a repo contained in this
456 # do not recurse into a repo contained in this
456 # one. use bisect to find .hg directory so speed
457 # one. use bisect to find .hg directory so speed
457 # is good on big directory.
458 # is good on big directory.
458 names = [e[0] for e in entries]
459 names = [e[0] for e in entries]
459 hg = bisect_left(names, '.hg')
460 hg = bisect_left(names, '.hg')
460 if hg < len(names) and names[hg] == '.hg':
461 if hg < len(names) and names[hg] == '.hg':
461 if isdir(join(top, '.hg')):
462 if isdir(join(top, '.hg')):
462 continue
463 continue
463 for f, kind, st in entries:
464 for f, kind, st in entries:
464 np = pconvert(join(nd, f))
465 np = pconvert(join(nd, f))
465 if np in known:
466 if np in known:
466 continue
467 continue
467 known[np] = 1
468 known[np] = 1
468 p = join(top, f)
469 p = join(top, f)
469 # don't trip over symlinks
470 # don't trip over symlinks
470 if kind == stat.S_IFDIR:
471 if kind == stat.S_IFDIR:
471 if not ignore(np):
472 if not ignore(np):
472 wadd(p)
473 wadd(p)
473 if directories:
474 if directories:
474 add((np, 'd', st))
475 add((np, 'd', st))
475 if np in dc and match(np):
476 if np in dc and match(np):
476 add((np, 'm', st))
477 add((np, 'm', st))
477 elif imatch(np):
478 elif imatch(np):
478 if supported(np, st.st_mode):
479 if supported(np, st.st_mode):
479 add((np, 'f', st))
480 add((np, 'f', st))
480 elif np in dc:
481 elif np in dc:
481 add((np, 'm', st))
482 add((np, 'm', st))
482 found.sort()
483 found.sort()
483 return found
484 return found
484
485
485 # step one, find all files that match our criteria
486 # step one, find all files that match our criteria
486 files.sort()
487 files.sort()
487 for ff in files:
488 for ff in files:
488 nf = normpath(ff)
489 nf = normpath(ff)
489 f = _join(ff)
490 f = _join(ff)
490 try:
491 try:
491 st = lstat(f)
492 st = lstat(f)
492 except OSError, inst:
493 except OSError, inst:
493 found = False
494 found = False
494 for fn in dc:
495 for fn in dc:
495 if nf == fn or (fn.startswith(nf) and fn[len(nf)] == '/'):
496 if nf == fn or (fn.startswith(nf) and fn[len(nf)] == '/'):
496 found = True
497 found = True
497 break
498 break
498 if not found:
499 if not found:
499 if inst.errno != errno.ENOENT or not badmatch:
500 if inst.errno != errno.ENOENT or not badmatch:
500 self._ui.warn('%s: %s\n' %
501 self._ui.warn('%s: %s\n' %
501 (self.pathto(ff), inst.strerror))
502 (self.pathto(ff), inst.strerror))
502 elif badmatch and badmatch(ff) and imatch(nf):
503 elif badmatch and badmatch(ff) and imatch(nf):
503 yield 'b', ff, None
504 yield 'b', ff, None
504 continue
505 continue
505 if s_isdir(st.st_mode):
506 if s_isdir(st.st_mode):
506 if not dirignore(nf):
507 if not dirignore(nf):
507 for f, src, st in findfiles(f):
508 for f, src, st in findfiles(f):
508 yield src, f, st
509 yield src, f, st
509 else:
510 else:
510 if nf in known:
511 if nf in known:
511 continue
512 continue
512 known[nf] = 1
513 known[nf] = 1
513 if match(nf):
514 if match(nf):
514 if supported(ff, st.st_mode, verbose=True):
515 if supported(ff, st.st_mode, verbose=True):
515 yield 'f', nf, st
516 yield 'f', nf, st
516 elif ff in dc:
517 elif ff in dc:
517 yield 'm', nf, st
518 yield 'm', nf, st
518
519
519 # step two run through anything left in the dc hash and yield
520 # step two run through anything left in the dc hash and yield
520 # if we haven't already seen it
521 # if we haven't already seen it
521 ks = dc.keys()
522 ks = dc.keys()
522 ks.sort()
523 ks.sort()
523 for k in ks:
524 for k in ks:
524 if k in known:
525 if k in known:
525 continue
526 continue
526 known[k] = 1
527 known[k] = 1
527 if imatch(k):
528 if imatch(k):
528 yield 'm', k, None
529 yield 'm', k, None
529
530
530 def status(self, files, match, list_ignored, list_clean):
531 def status(self, files, match, list_ignored, list_clean, list_unknown=True):
531 lookup, modified, added, unknown, ignored = [], [], [], [], []
532 lookup, modified, added, unknown, ignored = [], [], [], [], []
532 removed, deleted, clean = [], [], []
533 removed, deleted, clean = [], [], []
533
534
534 files = files or []
535 files = files or []
535 _join = self._join
536 _join = self._join
536 lstat = os.lstat
537 lstat = os.lstat
537 cmap = self._copymap
538 cmap = self._copymap
538 dmap = self._map
539 dmap = self._map
539 ladd = lookup.append
540 ladd = lookup.append
540 madd = modified.append
541 madd = modified.append
541 aadd = added.append
542 aadd = added.append
542 uadd = unknown.append
543 uadd = unknown.append
543 iadd = ignored.append
544 iadd = ignored.append
544 radd = removed.append
545 radd = removed.append
545 dadd = deleted.append
546 dadd = deleted.append
546 cadd = clean.append
547 cadd = clean.append
547
548
548 for src, fn, st in self.statwalk(files, match, ignored=list_ignored):
549 for src, fn, st in self.statwalk(files, match, unknown=list_unknown,
550 ignored=list_ignored):
549 if fn in dmap:
551 if fn in dmap:
550 type_, mode, size, time, foo = dmap[fn]
552 type_, mode, size, time, foo = dmap[fn]
551 else:
553 else:
552 if (list_ignored or fn in files) and self._dirignore(fn):
554 if (list_ignored or fn in files) and self._dirignore(fn):
553 if list_ignored:
555 if list_ignored:
554 iadd(fn)
556 iadd(fn)
555 else:
557 elif list_unknown:
556 uadd(fn)
558 uadd(fn)
557 continue
559 continue
558 if src == 'm':
560 if src == 'm':
559 nonexistent = True
561 nonexistent = True
560 if not st:
562 if not st:
561 try:
563 try:
562 st = lstat(_join(fn))
564 st = lstat(_join(fn))
563 except OSError, inst:
565 except OSError, inst:
564 if inst.errno not in (errno.ENOENT, errno.ENOTDIR):
566 if inst.errno not in (errno.ENOENT, errno.ENOTDIR):
565 raise
567 raise
566 st = None
568 st = None
567 # We need to re-check that it is a valid file
569 # We need to re-check that it is a valid file
568 if st and self._supported(fn, st.st_mode):
570 if st and self._supported(fn, st.st_mode):
569 nonexistent = False
571 nonexistent = False
570 # XXX: what to do with file no longer present in the fs
572 # XXX: what to do with file no longer present in the fs
571 # who are not removed in the dirstate ?
573 # who are not removed in the dirstate ?
572 if nonexistent and type_ in "nma":
574 if nonexistent and type_ in "nma":
573 dadd(fn)
575 dadd(fn)
574 continue
576 continue
575 # check the common case first
577 # check the common case first
576 if type_ == 'n':
578 if type_ == 'n':
577 if not st:
579 if not st:
578 st = lstat(_join(fn))
580 st = lstat(_join(fn))
579 if (size >= 0 and (size != st.st_size
581 if (size >= 0 and (size != st.st_size
580 or (mode ^ st.st_mode) & 0100)
582 or (mode ^ st.st_mode) & 0100)
581 or size == -2
583 or size == -2
582 or fn in self._copymap):
584 or fn in self._copymap):
583 madd(fn)
585 madd(fn)
584 elif time != int(st.st_mtime):
586 elif time != int(st.st_mtime):
585 ladd(fn)
587 ladd(fn)
586 elif list_clean:
588 elif list_clean:
587 cadd(fn)
589 cadd(fn)
588 elif type_ == 'm':
590 elif type_ == 'm':
589 madd(fn)
591 madd(fn)
590 elif type_ == 'a':
592 elif type_ == 'a':
591 aadd(fn)
593 aadd(fn)
592 elif type_ == 'r':
594 elif type_ == 'r':
593 radd(fn)
595 radd(fn)
594
596
595 return (lookup, modified, added, removed, deleted, unknown, ignored,
597 return (lookup, modified, added, removed, deleted, unknown, ignored,
596 clean)
598 clean)
@@ -1,2125 +1,2126 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
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 from node import *
8 from node import *
9 from i18n import _
9 from i18n import _
10 import repo, changegroup
10 import repo, changegroup
11 import changelog, dirstate, filelog, manifest, context, weakref
11 import changelog, dirstate, filelog, manifest, context, weakref
12 import re, lock, transaction, tempfile, stat, errno, ui
12 import re, lock, transaction, tempfile, stat, errno, ui
13 import os, revlog, time, util, extensions, hook, inspect
13 import os, revlog, time, util, extensions, hook, inspect
14
14
15 class localrepository(repo.repository):
15 class localrepository(repo.repository):
16 capabilities = util.set(('lookup', 'changegroupsubset'))
16 capabilities = util.set(('lookup', 'changegroupsubset'))
17 supported = ('revlogv1', 'store')
17 supported = ('revlogv1', 'store')
18
18
19 def __init__(self, parentui, path=None, create=0):
19 def __init__(self, parentui, path=None, create=0):
20 repo.repository.__init__(self)
20 repo.repository.__init__(self)
21 self.root = os.path.realpath(path)
21 self.root = os.path.realpath(path)
22 self.path = os.path.join(self.root, ".hg")
22 self.path = os.path.join(self.root, ".hg")
23 self.origroot = path
23 self.origroot = path
24 self.opener = util.opener(self.path)
24 self.opener = util.opener(self.path)
25 self.wopener = util.opener(self.root)
25 self.wopener = util.opener(self.root)
26
26
27 if not os.path.isdir(self.path):
27 if not os.path.isdir(self.path):
28 if create:
28 if create:
29 if not os.path.exists(path):
29 if not os.path.exists(path):
30 os.mkdir(path)
30 os.mkdir(path)
31 os.mkdir(self.path)
31 os.mkdir(self.path)
32 requirements = ["revlogv1"]
32 requirements = ["revlogv1"]
33 if parentui.configbool('format', 'usestore', True):
33 if parentui.configbool('format', 'usestore', True):
34 os.mkdir(os.path.join(self.path, "store"))
34 os.mkdir(os.path.join(self.path, "store"))
35 requirements.append("store")
35 requirements.append("store")
36 # create an invalid changelog
36 # create an invalid changelog
37 self.opener("00changelog.i", "a").write(
37 self.opener("00changelog.i", "a").write(
38 '\0\0\0\2' # represents revlogv2
38 '\0\0\0\2' # represents revlogv2
39 ' dummy changelog to prevent using the old repo layout'
39 ' dummy changelog to prevent using the old repo layout'
40 )
40 )
41 reqfile = self.opener("requires", "w")
41 reqfile = self.opener("requires", "w")
42 for r in requirements:
42 for r in requirements:
43 reqfile.write("%s\n" % r)
43 reqfile.write("%s\n" % r)
44 reqfile.close()
44 reqfile.close()
45 else:
45 else:
46 raise repo.RepoError(_("repository %s not found") % path)
46 raise repo.RepoError(_("repository %s not found") % path)
47 elif create:
47 elif create:
48 raise repo.RepoError(_("repository %s already exists") % path)
48 raise repo.RepoError(_("repository %s already exists") % path)
49 else:
49 else:
50 # find requirements
50 # find requirements
51 try:
51 try:
52 requirements = self.opener("requires").read().splitlines()
52 requirements = self.opener("requires").read().splitlines()
53 except IOError, inst:
53 except IOError, inst:
54 if inst.errno != errno.ENOENT:
54 if inst.errno != errno.ENOENT:
55 raise
55 raise
56 requirements = []
56 requirements = []
57 # check them
57 # check them
58 for r in requirements:
58 for r in requirements:
59 if r not in self.supported:
59 if r not in self.supported:
60 raise repo.RepoError(_("requirement '%s' not supported") % r)
60 raise repo.RepoError(_("requirement '%s' not supported") % r)
61
61
62 # setup store
62 # setup store
63 if "store" in requirements:
63 if "store" in requirements:
64 self.encodefn = util.encodefilename
64 self.encodefn = util.encodefilename
65 self.decodefn = util.decodefilename
65 self.decodefn = util.decodefilename
66 self.spath = os.path.join(self.path, "store")
66 self.spath = os.path.join(self.path, "store")
67 else:
67 else:
68 self.encodefn = lambda x: x
68 self.encodefn = lambda x: x
69 self.decodefn = lambda x: x
69 self.decodefn = lambda x: x
70 self.spath = self.path
70 self.spath = self.path
71
71
72 try:
72 try:
73 # files in .hg/ will be created using this mode
73 # files in .hg/ will be created using this mode
74 mode = os.stat(self.spath).st_mode
74 mode = os.stat(self.spath).st_mode
75 # avoid some useless chmods
75 # avoid some useless chmods
76 if (0777 & ~util._umask) == (0777 & mode):
76 if (0777 & ~util._umask) == (0777 & mode):
77 mode = None
77 mode = None
78 except OSError:
78 except OSError:
79 mode = None
79 mode = None
80
80
81 self._createmode = mode
81 self._createmode = mode
82 self.opener.createmode = mode
82 self.opener.createmode = mode
83 sopener = util.opener(self.spath)
83 sopener = util.opener(self.spath)
84 sopener.createmode = mode
84 sopener.createmode = mode
85 self.sopener = util.encodedopener(sopener, self.encodefn)
85 self.sopener = util.encodedopener(sopener, self.encodefn)
86
86
87 self.ui = ui.ui(parentui=parentui)
87 self.ui = ui.ui(parentui=parentui)
88 try:
88 try:
89 self.ui.readconfig(self.join("hgrc"), self.root)
89 self.ui.readconfig(self.join("hgrc"), self.root)
90 extensions.loadall(self.ui)
90 extensions.loadall(self.ui)
91 except IOError:
91 except IOError:
92 pass
92 pass
93
93
94 self.tagscache = None
94 self.tagscache = None
95 self._tagstypecache = None
95 self._tagstypecache = None
96 self.branchcache = None
96 self.branchcache = None
97 self._ubranchcache = None # UTF-8 version of branchcache
97 self._ubranchcache = None # UTF-8 version of branchcache
98 self._branchcachetip = None
98 self._branchcachetip = None
99 self.nodetagscache = None
99 self.nodetagscache = None
100 self.filterpats = {}
100 self.filterpats = {}
101 self._datafilters = {}
101 self._datafilters = {}
102 self._transref = self._lockref = self._wlockref = None
102 self._transref = self._lockref = self._wlockref = None
103
103
104 def __getattr__(self, name):
104 def __getattr__(self, name):
105 if name == 'changelog':
105 if name == 'changelog':
106 self.changelog = changelog.changelog(self.sopener)
106 self.changelog = changelog.changelog(self.sopener)
107 self.sopener.defversion = self.changelog.version
107 self.sopener.defversion = self.changelog.version
108 return self.changelog
108 return self.changelog
109 if name == 'manifest':
109 if name == 'manifest':
110 self.changelog
110 self.changelog
111 self.manifest = manifest.manifest(self.sopener)
111 self.manifest = manifest.manifest(self.sopener)
112 return self.manifest
112 return self.manifest
113 if name == 'dirstate':
113 if name == 'dirstate':
114 self.dirstate = dirstate.dirstate(self.opener, self.ui, self.root)
114 self.dirstate = dirstate.dirstate(self.opener, self.ui, self.root)
115 return self.dirstate
115 return self.dirstate
116 else:
116 else:
117 raise AttributeError, name
117 raise AttributeError, name
118
118
119 def url(self):
119 def url(self):
120 return 'file:' + self.root
120 return 'file:' + self.root
121
121
122 def hook(self, name, throw=False, **args):
122 def hook(self, name, throw=False, **args):
123 return hook.hook(self.ui, self, name, throw, **args)
123 return hook.hook(self.ui, self, name, throw, **args)
124
124
125 tag_disallowed = ':\r\n'
125 tag_disallowed = ':\r\n'
126
126
127 def _tag(self, name, node, message, local, user, date, parent=None,
127 def _tag(self, name, node, message, local, user, date, parent=None,
128 extra={}):
128 extra={}):
129 use_dirstate = parent is None
129 use_dirstate = parent is None
130
130
131 for c in self.tag_disallowed:
131 for c in self.tag_disallowed:
132 if c in name:
132 if c in name:
133 raise util.Abort(_('%r cannot be used in a tag name') % c)
133 raise util.Abort(_('%r cannot be used in a tag name') % c)
134
134
135 self.hook('pretag', throw=True, node=hex(node), tag=name, local=local)
135 self.hook('pretag', throw=True, node=hex(node), tag=name, local=local)
136
136
137 def writetag(fp, name, munge, prevtags):
137 def writetag(fp, name, munge, prevtags):
138 fp.seek(0, 2)
138 fp.seek(0, 2)
139 if prevtags and prevtags[-1] != '\n':
139 if prevtags and prevtags[-1] != '\n':
140 fp.write('\n')
140 fp.write('\n')
141 fp.write('%s %s\n' % (hex(node), munge and munge(name) or name))
141 fp.write('%s %s\n' % (hex(node), munge and munge(name) or name))
142 fp.close()
142 fp.close()
143
143
144 prevtags = ''
144 prevtags = ''
145 if local:
145 if local:
146 try:
146 try:
147 fp = self.opener('localtags', 'r+')
147 fp = self.opener('localtags', 'r+')
148 except IOError, err:
148 except IOError, err:
149 fp = self.opener('localtags', 'a')
149 fp = self.opener('localtags', 'a')
150 else:
150 else:
151 prevtags = fp.read()
151 prevtags = fp.read()
152
152
153 # local tags are stored in the current charset
153 # local tags are stored in the current charset
154 writetag(fp, name, None, prevtags)
154 writetag(fp, name, None, prevtags)
155 self.hook('tag', node=hex(node), tag=name, local=local)
155 self.hook('tag', node=hex(node), tag=name, local=local)
156 return
156 return
157
157
158 if use_dirstate:
158 if use_dirstate:
159 try:
159 try:
160 fp = self.wfile('.hgtags', 'rb+')
160 fp = self.wfile('.hgtags', 'rb+')
161 except IOError, err:
161 except IOError, err:
162 fp = self.wfile('.hgtags', 'ab')
162 fp = self.wfile('.hgtags', 'ab')
163 else:
163 else:
164 prevtags = fp.read()
164 prevtags = fp.read()
165 else:
165 else:
166 try:
166 try:
167 prevtags = self.filectx('.hgtags', parent).data()
167 prevtags = self.filectx('.hgtags', parent).data()
168 except revlog.LookupError:
168 except revlog.LookupError:
169 pass
169 pass
170 fp = self.wfile('.hgtags', 'wb')
170 fp = self.wfile('.hgtags', 'wb')
171 if prevtags:
171 if prevtags:
172 fp.write(prevtags)
172 fp.write(prevtags)
173
173
174 # committed tags are stored in UTF-8
174 # committed tags are stored in UTF-8
175 writetag(fp, name, util.fromlocal, prevtags)
175 writetag(fp, name, util.fromlocal, prevtags)
176
176
177 if use_dirstate and '.hgtags' not in self.dirstate:
177 if use_dirstate and '.hgtags' not in self.dirstate:
178 self.add(['.hgtags'])
178 self.add(['.hgtags'])
179
179
180 tagnode = self.commit(['.hgtags'], message, user, date, p1=parent,
180 tagnode = self.commit(['.hgtags'], message, user, date, p1=parent,
181 extra=extra)
181 extra=extra)
182
182
183 self.hook('tag', node=hex(node), tag=name, local=local)
183 self.hook('tag', node=hex(node), tag=name, local=local)
184
184
185 return tagnode
185 return tagnode
186
186
187 def tag(self, name, node, message, local, user, date):
187 def tag(self, name, node, message, local, user, date):
188 '''tag a revision with a symbolic name.
188 '''tag a revision with a symbolic name.
189
189
190 if local is True, the tag is stored in a per-repository file.
190 if local is True, the tag is stored in a per-repository file.
191 otherwise, it is stored in the .hgtags file, and a new
191 otherwise, it is stored in the .hgtags file, and a new
192 changeset is committed with the change.
192 changeset is committed with the change.
193
193
194 keyword arguments:
194 keyword arguments:
195
195
196 local: whether to store tag in non-version-controlled file
196 local: whether to store tag in non-version-controlled file
197 (default False)
197 (default False)
198
198
199 message: commit message to use if committing
199 message: commit message to use if committing
200
200
201 user: name of user to use if committing
201 user: name of user to use if committing
202
202
203 date: date tuple to use if committing'''
203 date: date tuple to use if committing'''
204
204
205 date = util.parsedate(date)
205 date = util.parsedate(date)
206 for x in self.status()[:5]:
206 for x in self.status()[:5]:
207 if '.hgtags' in x:
207 if '.hgtags' in x:
208 raise util.Abort(_('working copy of .hgtags is changed '
208 raise util.Abort(_('working copy of .hgtags is changed '
209 '(please commit .hgtags manually)'))
209 '(please commit .hgtags manually)'))
210
210
211
211
212 self._tag(name, node, message, local, user, date)
212 self._tag(name, node, message, local, user, date)
213
213
214 def tags(self):
214 def tags(self):
215 '''return a mapping of tag to node'''
215 '''return a mapping of tag to node'''
216 if self.tagscache:
216 if self.tagscache:
217 return self.tagscache
217 return self.tagscache
218
218
219 globaltags = {}
219 globaltags = {}
220 tagtypes = {}
220 tagtypes = {}
221
221
222 def readtags(lines, fn, tagtype):
222 def readtags(lines, fn, tagtype):
223 filetags = {}
223 filetags = {}
224 count = 0
224 count = 0
225
225
226 def warn(msg):
226 def warn(msg):
227 self.ui.warn(_("%s, line %s: %s\n") % (fn, count, msg))
227 self.ui.warn(_("%s, line %s: %s\n") % (fn, count, msg))
228
228
229 for l in lines:
229 for l in lines:
230 count += 1
230 count += 1
231 if not l:
231 if not l:
232 continue
232 continue
233 s = l.split(" ", 1)
233 s = l.split(" ", 1)
234 if len(s) != 2:
234 if len(s) != 2:
235 warn(_("cannot parse entry"))
235 warn(_("cannot parse entry"))
236 continue
236 continue
237 node, key = s
237 node, key = s
238 key = util.tolocal(key.strip()) # stored in UTF-8
238 key = util.tolocal(key.strip()) # stored in UTF-8
239 try:
239 try:
240 bin_n = bin(node)
240 bin_n = bin(node)
241 except TypeError:
241 except TypeError:
242 warn(_("node '%s' is not well formed") % node)
242 warn(_("node '%s' is not well formed") % node)
243 continue
243 continue
244 if bin_n not in self.changelog.nodemap:
244 if bin_n not in self.changelog.nodemap:
245 warn(_("tag '%s' refers to unknown node") % key)
245 warn(_("tag '%s' refers to unknown node") % key)
246 continue
246 continue
247
247
248 h = []
248 h = []
249 if key in filetags:
249 if key in filetags:
250 n, h = filetags[key]
250 n, h = filetags[key]
251 h.append(n)
251 h.append(n)
252 filetags[key] = (bin_n, h)
252 filetags[key] = (bin_n, h)
253
253
254 for k, nh in filetags.items():
254 for k, nh in filetags.items():
255 if k not in globaltags:
255 if k not in globaltags:
256 globaltags[k] = nh
256 globaltags[k] = nh
257 tagtypes[k] = tagtype
257 tagtypes[k] = tagtype
258 continue
258 continue
259
259
260 # we prefer the global tag if:
260 # we prefer the global tag if:
261 # it supercedes us OR
261 # it supercedes us OR
262 # mutual supercedes and it has a higher rank
262 # mutual supercedes and it has a higher rank
263 # otherwise we win because we're tip-most
263 # otherwise we win because we're tip-most
264 an, ah = nh
264 an, ah = nh
265 bn, bh = globaltags[k]
265 bn, bh = globaltags[k]
266 if (bn != an and an in bh and
266 if (bn != an and an in bh and
267 (bn not in ah or len(bh) > len(ah))):
267 (bn not in ah or len(bh) > len(ah))):
268 an = bn
268 an = bn
269 ah.extend([n for n in bh if n not in ah])
269 ah.extend([n for n in bh if n not in ah])
270 globaltags[k] = an, ah
270 globaltags[k] = an, ah
271 tagtypes[k] = tagtype
271 tagtypes[k] = tagtype
272
272
273 # read the tags file from each head, ending with the tip
273 # read the tags file from each head, ending with the tip
274 f = None
274 f = None
275 for rev, node, fnode in self._hgtagsnodes():
275 for rev, node, fnode in self._hgtagsnodes():
276 f = (f and f.filectx(fnode) or
276 f = (f and f.filectx(fnode) or
277 self.filectx('.hgtags', fileid=fnode))
277 self.filectx('.hgtags', fileid=fnode))
278 readtags(f.data().splitlines(), f, "global")
278 readtags(f.data().splitlines(), f, "global")
279
279
280 try:
280 try:
281 data = util.fromlocal(self.opener("localtags").read())
281 data = util.fromlocal(self.opener("localtags").read())
282 # localtags are stored in the local character set
282 # localtags are stored in the local character set
283 # while the internal tag table is stored in UTF-8
283 # while the internal tag table is stored in UTF-8
284 readtags(data.splitlines(), "localtags", "local")
284 readtags(data.splitlines(), "localtags", "local")
285 except IOError:
285 except IOError:
286 pass
286 pass
287
287
288 self.tagscache = {}
288 self.tagscache = {}
289 self._tagstypecache = {}
289 self._tagstypecache = {}
290 for k,nh in globaltags.items():
290 for k,nh in globaltags.items():
291 n = nh[0]
291 n = nh[0]
292 if n != nullid:
292 if n != nullid:
293 self.tagscache[k] = n
293 self.tagscache[k] = n
294 self._tagstypecache[k] = tagtypes[k]
294 self._tagstypecache[k] = tagtypes[k]
295 self.tagscache['tip'] = self.changelog.tip()
295 self.tagscache['tip'] = self.changelog.tip()
296
296
297 return self.tagscache
297 return self.tagscache
298
298
299 def tagtype(self, tagname):
299 def tagtype(self, tagname):
300 '''
300 '''
301 return the type of the given tag. result can be:
301 return the type of the given tag. result can be:
302
302
303 'local' : a local tag
303 'local' : a local tag
304 'global' : a global tag
304 'global' : a global tag
305 None : tag does not exist
305 None : tag does not exist
306 '''
306 '''
307
307
308 self.tags()
308 self.tags()
309
309
310 return self._tagstypecache.get(tagname)
310 return self._tagstypecache.get(tagname)
311
311
312 def _hgtagsnodes(self):
312 def _hgtagsnodes(self):
313 heads = self.heads()
313 heads = self.heads()
314 heads.reverse()
314 heads.reverse()
315 last = {}
315 last = {}
316 ret = []
316 ret = []
317 for node in heads:
317 for node in heads:
318 c = self.changectx(node)
318 c = self.changectx(node)
319 rev = c.rev()
319 rev = c.rev()
320 try:
320 try:
321 fnode = c.filenode('.hgtags')
321 fnode = c.filenode('.hgtags')
322 except revlog.LookupError:
322 except revlog.LookupError:
323 continue
323 continue
324 ret.append((rev, node, fnode))
324 ret.append((rev, node, fnode))
325 if fnode in last:
325 if fnode in last:
326 ret[last[fnode]] = None
326 ret[last[fnode]] = None
327 last[fnode] = len(ret) - 1
327 last[fnode] = len(ret) - 1
328 return [item for item in ret if item]
328 return [item for item in ret if item]
329
329
330 def tagslist(self):
330 def tagslist(self):
331 '''return a list of tags ordered by revision'''
331 '''return a list of tags ordered by revision'''
332 l = []
332 l = []
333 for t, n in self.tags().items():
333 for t, n in self.tags().items():
334 try:
334 try:
335 r = self.changelog.rev(n)
335 r = self.changelog.rev(n)
336 except:
336 except:
337 r = -2 # sort to the beginning of the list if unknown
337 r = -2 # sort to the beginning of the list if unknown
338 l.append((r, t, n))
338 l.append((r, t, n))
339 l.sort()
339 l.sort()
340 return [(t, n) for r, t, n in l]
340 return [(t, n) for r, t, n in l]
341
341
342 def nodetags(self, node):
342 def nodetags(self, node):
343 '''return the tags associated with a node'''
343 '''return the tags associated with a node'''
344 if not self.nodetagscache:
344 if not self.nodetagscache:
345 self.nodetagscache = {}
345 self.nodetagscache = {}
346 for t, n in self.tags().items():
346 for t, n in self.tags().items():
347 self.nodetagscache.setdefault(n, []).append(t)
347 self.nodetagscache.setdefault(n, []).append(t)
348 return self.nodetagscache.get(node, [])
348 return self.nodetagscache.get(node, [])
349
349
350 def _branchtags(self, partial, lrev):
350 def _branchtags(self, partial, lrev):
351 tiprev = self.changelog.count() - 1
351 tiprev = self.changelog.count() - 1
352 if lrev != tiprev:
352 if lrev != tiprev:
353 self._updatebranchcache(partial, lrev+1, tiprev+1)
353 self._updatebranchcache(partial, lrev+1, tiprev+1)
354 self._writebranchcache(partial, self.changelog.tip(), tiprev)
354 self._writebranchcache(partial, self.changelog.tip(), tiprev)
355
355
356 return partial
356 return partial
357
357
358 def branchtags(self):
358 def branchtags(self):
359 tip = self.changelog.tip()
359 tip = self.changelog.tip()
360 if self.branchcache is not None and self._branchcachetip == tip:
360 if self.branchcache is not None and self._branchcachetip == tip:
361 return self.branchcache
361 return self.branchcache
362
362
363 oldtip = self._branchcachetip
363 oldtip = self._branchcachetip
364 self._branchcachetip = tip
364 self._branchcachetip = tip
365 if self.branchcache is None:
365 if self.branchcache is None:
366 self.branchcache = {} # avoid recursion in changectx
366 self.branchcache = {} # avoid recursion in changectx
367 else:
367 else:
368 self.branchcache.clear() # keep using the same dict
368 self.branchcache.clear() # keep using the same dict
369 if oldtip is None or oldtip not in self.changelog.nodemap:
369 if oldtip is None or oldtip not in self.changelog.nodemap:
370 partial, last, lrev = self._readbranchcache()
370 partial, last, lrev = self._readbranchcache()
371 else:
371 else:
372 lrev = self.changelog.rev(oldtip)
372 lrev = self.changelog.rev(oldtip)
373 partial = self._ubranchcache
373 partial = self._ubranchcache
374
374
375 self._branchtags(partial, lrev)
375 self._branchtags(partial, lrev)
376
376
377 # the branch cache is stored on disk as UTF-8, but in the local
377 # the branch cache is stored on disk as UTF-8, but in the local
378 # charset internally
378 # charset internally
379 for k, v in partial.items():
379 for k, v in partial.items():
380 self.branchcache[util.tolocal(k)] = v
380 self.branchcache[util.tolocal(k)] = v
381 self._ubranchcache = partial
381 self._ubranchcache = partial
382 return self.branchcache
382 return self.branchcache
383
383
384 def _readbranchcache(self):
384 def _readbranchcache(self):
385 partial = {}
385 partial = {}
386 try:
386 try:
387 f = self.opener("branch.cache")
387 f = self.opener("branch.cache")
388 lines = f.read().split('\n')
388 lines = f.read().split('\n')
389 f.close()
389 f.close()
390 except (IOError, OSError):
390 except (IOError, OSError):
391 return {}, nullid, nullrev
391 return {}, nullid, nullrev
392
392
393 try:
393 try:
394 last, lrev = lines.pop(0).split(" ", 1)
394 last, lrev = lines.pop(0).split(" ", 1)
395 last, lrev = bin(last), int(lrev)
395 last, lrev = bin(last), int(lrev)
396 if not (lrev < self.changelog.count() and
396 if not (lrev < self.changelog.count() and
397 self.changelog.node(lrev) == last): # sanity check
397 self.changelog.node(lrev) == last): # sanity check
398 # invalidate the cache
398 # invalidate the cache
399 raise ValueError('invalidating branch cache (tip differs)')
399 raise ValueError('invalidating branch cache (tip differs)')
400 for l in lines:
400 for l in lines:
401 if not l: continue
401 if not l: continue
402 node, label = l.split(" ", 1)
402 node, label = l.split(" ", 1)
403 partial[label.strip()] = bin(node)
403 partial[label.strip()] = bin(node)
404 except (KeyboardInterrupt, util.SignalInterrupt):
404 except (KeyboardInterrupt, util.SignalInterrupt):
405 raise
405 raise
406 except Exception, inst:
406 except Exception, inst:
407 if self.ui.debugflag:
407 if self.ui.debugflag:
408 self.ui.warn(str(inst), '\n')
408 self.ui.warn(str(inst), '\n')
409 partial, last, lrev = {}, nullid, nullrev
409 partial, last, lrev = {}, nullid, nullrev
410 return partial, last, lrev
410 return partial, last, lrev
411
411
412 def _writebranchcache(self, branches, tip, tiprev):
412 def _writebranchcache(self, branches, tip, tiprev):
413 try:
413 try:
414 f = self.opener("branch.cache", "w", atomictemp=True)
414 f = self.opener("branch.cache", "w", atomictemp=True)
415 f.write("%s %s\n" % (hex(tip), tiprev))
415 f.write("%s %s\n" % (hex(tip), tiprev))
416 for label, node in branches.iteritems():
416 for label, node in branches.iteritems():
417 f.write("%s %s\n" % (hex(node), label))
417 f.write("%s %s\n" % (hex(node), label))
418 f.rename()
418 f.rename()
419 except (IOError, OSError):
419 except (IOError, OSError):
420 pass
420 pass
421
421
422 def _updatebranchcache(self, partial, start, end):
422 def _updatebranchcache(self, partial, start, end):
423 for r in xrange(start, end):
423 for r in xrange(start, end):
424 c = self.changectx(r)
424 c = self.changectx(r)
425 b = c.branch()
425 b = c.branch()
426 partial[b] = c.node()
426 partial[b] = c.node()
427
427
428 def lookup(self, key):
428 def lookup(self, key):
429 if key == '.':
429 if key == '.':
430 key, second = self.dirstate.parents()
430 key, second = self.dirstate.parents()
431 if key == nullid:
431 if key == nullid:
432 raise repo.RepoError(_("no revision checked out"))
432 raise repo.RepoError(_("no revision checked out"))
433 if second != nullid:
433 if second != nullid:
434 self.ui.warn(_("warning: working directory has two parents, "
434 self.ui.warn(_("warning: working directory has two parents, "
435 "tag '.' uses the first\n"))
435 "tag '.' uses the first\n"))
436 elif key == 'null':
436 elif key == 'null':
437 return nullid
437 return nullid
438 n = self.changelog._match(key)
438 n = self.changelog._match(key)
439 if n:
439 if n:
440 return n
440 return n
441 if key in self.tags():
441 if key in self.tags():
442 return self.tags()[key]
442 return self.tags()[key]
443 if key in self.branchtags():
443 if key in self.branchtags():
444 return self.branchtags()[key]
444 return self.branchtags()[key]
445 n = self.changelog._partialmatch(key)
445 n = self.changelog._partialmatch(key)
446 if n:
446 if n:
447 return n
447 return n
448 try:
448 try:
449 if len(key) == 20:
449 if len(key) == 20:
450 key = hex(key)
450 key = hex(key)
451 except:
451 except:
452 pass
452 pass
453 raise repo.RepoError(_("unknown revision '%s'") % key)
453 raise repo.RepoError(_("unknown revision '%s'") % key)
454
454
455 def dev(self):
455 def dev(self):
456 return os.lstat(self.path).st_dev
456 return os.lstat(self.path).st_dev
457
457
458 def local(self):
458 def local(self):
459 return True
459 return True
460
460
461 def join(self, f):
461 def join(self, f):
462 return os.path.join(self.path, f)
462 return os.path.join(self.path, f)
463
463
464 def sjoin(self, f):
464 def sjoin(self, f):
465 f = self.encodefn(f)
465 f = self.encodefn(f)
466 return os.path.join(self.spath, f)
466 return os.path.join(self.spath, f)
467
467
468 def wjoin(self, f):
468 def wjoin(self, f):
469 return os.path.join(self.root, f)
469 return os.path.join(self.root, f)
470
470
471 def file(self, f):
471 def file(self, f):
472 if f[0] == '/':
472 if f[0] == '/':
473 f = f[1:]
473 f = f[1:]
474 return filelog.filelog(self.sopener, f)
474 return filelog.filelog(self.sopener, f)
475
475
476 def changectx(self, changeid=None):
476 def changectx(self, changeid=None):
477 return context.changectx(self, changeid)
477 return context.changectx(self, changeid)
478
478
479 def workingctx(self):
479 def workingctx(self):
480 return context.workingctx(self)
480 return context.workingctx(self)
481
481
482 def parents(self, changeid=None):
482 def parents(self, changeid=None):
483 '''
483 '''
484 get list of changectxs for parents of changeid or working directory
484 get list of changectxs for parents of changeid or working directory
485 '''
485 '''
486 if changeid is None:
486 if changeid is None:
487 pl = self.dirstate.parents()
487 pl = self.dirstate.parents()
488 else:
488 else:
489 n = self.changelog.lookup(changeid)
489 n = self.changelog.lookup(changeid)
490 pl = self.changelog.parents(n)
490 pl = self.changelog.parents(n)
491 if pl[1] == nullid:
491 if pl[1] == nullid:
492 return [self.changectx(pl[0])]
492 return [self.changectx(pl[0])]
493 return [self.changectx(pl[0]), self.changectx(pl[1])]
493 return [self.changectx(pl[0]), self.changectx(pl[1])]
494
494
495 def filectx(self, path, changeid=None, fileid=None):
495 def filectx(self, path, changeid=None, fileid=None):
496 """changeid can be a changeset revision, node, or tag.
496 """changeid can be a changeset revision, node, or tag.
497 fileid can be a file revision or node."""
497 fileid can be a file revision or node."""
498 return context.filectx(self, path, changeid, fileid)
498 return context.filectx(self, path, changeid, fileid)
499
499
500 def getcwd(self):
500 def getcwd(self):
501 return self.dirstate.getcwd()
501 return self.dirstate.getcwd()
502
502
503 def pathto(self, f, cwd=None):
503 def pathto(self, f, cwd=None):
504 return self.dirstate.pathto(f, cwd)
504 return self.dirstate.pathto(f, cwd)
505
505
506 def wfile(self, f, mode='r'):
506 def wfile(self, f, mode='r'):
507 return self.wopener(f, mode)
507 return self.wopener(f, mode)
508
508
509 def _link(self, f):
509 def _link(self, f):
510 return os.path.islink(self.wjoin(f))
510 return os.path.islink(self.wjoin(f))
511
511
512 def _filter(self, filter, filename, data):
512 def _filter(self, filter, filename, data):
513 if filter not in self.filterpats:
513 if filter not in self.filterpats:
514 l = []
514 l = []
515 for pat, cmd in self.ui.configitems(filter):
515 for pat, cmd in self.ui.configitems(filter):
516 mf = util.matcher(self.root, "", [pat], [], [])[1]
516 mf = util.matcher(self.root, "", [pat], [], [])[1]
517 fn = None
517 fn = None
518 params = cmd
518 params = cmd
519 for name, filterfn in self._datafilters.iteritems():
519 for name, filterfn in self._datafilters.iteritems():
520 if cmd.startswith(name):
520 if cmd.startswith(name):
521 fn = filterfn
521 fn = filterfn
522 params = cmd[len(name):].lstrip()
522 params = cmd[len(name):].lstrip()
523 break
523 break
524 if not fn:
524 if not fn:
525 fn = lambda s, c, **kwargs: util.filter(s, c)
525 fn = lambda s, c, **kwargs: util.filter(s, c)
526 # Wrap old filters not supporting keyword arguments
526 # Wrap old filters not supporting keyword arguments
527 if not inspect.getargspec(fn)[2]:
527 if not inspect.getargspec(fn)[2]:
528 oldfn = fn
528 oldfn = fn
529 fn = lambda s, c, **kwargs: oldfn(s, c)
529 fn = lambda s, c, **kwargs: oldfn(s, c)
530 l.append((mf, fn, params))
530 l.append((mf, fn, params))
531 self.filterpats[filter] = l
531 self.filterpats[filter] = l
532
532
533 for mf, fn, cmd in self.filterpats[filter]:
533 for mf, fn, cmd in self.filterpats[filter]:
534 if mf(filename):
534 if mf(filename):
535 self.ui.debug(_("filtering %s through %s\n") % (filename, cmd))
535 self.ui.debug(_("filtering %s through %s\n") % (filename, cmd))
536 data = fn(data, cmd, ui=self.ui, repo=self, filename=filename)
536 data = fn(data, cmd, ui=self.ui, repo=self, filename=filename)
537 break
537 break
538
538
539 return data
539 return data
540
540
541 def adddatafilter(self, name, filter):
541 def adddatafilter(self, name, filter):
542 self._datafilters[name] = filter
542 self._datafilters[name] = filter
543
543
544 def wread(self, filename):
544 def wread(self, filename):
545 if self._link(filename):
545 if self._link(filename):
546 data = os.readlink(self.wjoin(filename))
546 data = os.readlink(self.wjoin(filename))
547 else:
547 else:
548 data = self.wopener(filename, 'r').read()
548 data = self.wopener(filename, 'r').read()
549 return self._filter("encode", filename, data)
549 return self._filter("encode", filename, data)
550
550
551 def wwrite(self, filename, data, flags):
551 def wwrite(self, filename, data, flags):
552 data = self._filter("decode", filename, data)
552 data = self._filter("decode", filename, data)
553 try:
553 try:
554 os.unlink(self.wjoin(filename))
554 os.unlink(self.wjoin(filename))
555 except OSError:
555 except OSError:
556 pass
556 pass
557 self.wopener(filename, 'w').write(data)
557 self.wopener(filename, 'w').write(data)
558 util.set_flags(self.wjoin(filename), flags)
558 util.set_flags(self.wjoin(filename), flags)
559
559
560 def wwritedata(self, filename, data):
560 def wwritedata(self, filename, data):
561 return self._filter("decode", filename, data)
561 return self._filter("decode", filename, data)
562
562
563 def transaction(self):
563 def transaction(self):
564 if self._transref and self._transref():
564 if self._transref and self._transref():
565 return self._transref().nest()
565 return self._transref().nest()
566
566
567 # abort here if the journal already exists
567 # abort here if the journal already exists
568 if os.path.exists(self.sjoin("journal")):
568 if os.path.exists(self.sjoin("journal")):
569 raise repo.RepoError(_("journal already exists - run hg recover"))
569 raise repo.RepoError(_("journal already exists - run hg recover"))
570
570
571 # save dirstate for rollback
571 # save dirstate for rollback
572 try:
572 try:
573 ds = self.opener("dirstate").read()
573 ds = self.opener("dirstate").read()
574 except IOError:
574 except IOError:
575 ds = ""
575 ds = ""
576 self.opener("journal.dirstate", "w").write(ds)
576 self.opener("journal.dirstate", "w").write(ds)
577 self.opener("journal.branch", "w").write(self.dirstate.branch())
577 self.opener("journal.branch", "w").write(self.dirstate.branch())
578
578
579 renames = [(self.sjoin("journal"), self.sjoin("undo")),
579 renames = [(self.sjoin("journal"), self.sjoin("undo")),
580 (self.join("journal.dirstate"), self.join("undo.dirstate")),
580 (self.join("journal.dirstate"), self.join("undo.dirstate")),
581 (self.join("journal.branch"), self.join("undo.branch"))]
581 (self.join("journal.branch"), self.join("undo.branch"))]
582 tr = transaction.transaction(self.ui.warn, self.sopener,
582 tr = transaction.transaction(self.ui.warn, self.sopener,
583 self.sjoin("journal"),
583 self.sjoin("journal"),
584 aftertrans(renames),
584 aftertrans(renames),
585 self._createmode)
585 self._createmode)
586 self._transref = weakref.ref(tr)
586 self._transref = weakref.ref(tr)
587 return tr
587 return tr
588
588
589 def recover(self):
589 def recover(self):
590 l = self.lock()
590 l = self.lock()
591 try:
591 try:
592 if os.path.exists(self.sjoin("journal")):
592 if os.path.exists(self.sjoin("journal")):
593 self.ui.status(_("rolling back interrupted transaction\n"))
593 self.ui.status(_("rolling back interrupted transaction\n"))
594 transaction.rollback(self.sopener, self.sjoin("journal"))
594 transaction.rollback(self.sopener, self.sjoin("journal"))
595 self.invalidate()
595 self.invalidate()
596 return True
596 return True
597 else:
597 else:
598 self.ui.warn(_("no interrupted transaction available\n"))
598 self.ui.warn(_("no interrupted transaction available\n"))
599 return False
599 return False
600 finally:
600 finally:
601 del l
601 del l
602
602
603 def rollback(self):
603 def rollback(self):
604 wlock = lock = None
604 wlock = lock = None
605 try:
605 try:
606 wlock = self.wlock()
606 wlock = self.wlock()
607 lock = self.lock()
607 lock = self.lock()
608 if os.path.exists(self.sjoin("undo")):
608 if os.path.exists(self.sjoin("undo")):
609 self.ui.status(_("rolling back last transaction\n"))
609 self.ui.status(_("rolling back last transaction\n"))
610 transaction.rollback(self.sopener, self.sjoin("undo"))
610 transaction.rollback(self.sopener, self.sjoin("undo"))
611 util.rename(self.join("undo.dirstate"), self.join("dirstate"))
611 util.rename(self.join("undo.dirstate"), self.join("dirstate"))
612 try:
612 try:
613 branch = self.opener("undo.branch").read()
613 branch = self.opener("undo.branch").read()
614 self.dirstate.setbranch(branch)
614 self.dirstate.setbranch(branch)
615 except IOError:
615 except IOError:
616 self.ui.warn(_("Named branch could not be reset, "
616 self.ui.warn(_("Named branch could not be reset, "
617 "current branch still is: %s\n")
617 "current branch still is: %s\n")
618 % util.tolocal(self.dirstate.branch()))
618 % util.tolocal(self.dirstate.branch()))
619 self.invalidate()
619 self.invalidate()
620 self.dirstate.invalidate()
620 self.dirstate.invalidate()
621 else:
621 else:
622 self.ui.warn(_("no rollback information available\n"))
622 self.ui.warn(_("no rollback information available\n"))
623 finally:
623 finally:
624 del lock, wlock
624 del lock, wlock
625
625
626 def invalidate(self):
626 def invalidate(self):
627 for a in "changelog manifest".split():
627 for a in "changelog manifest".split():
628 if hasattr(self, a):
628 if hasattr(self, a):
629 self.__delattr__(a)
629 self.__delattr__(a)
630 self.tagscache = None
630 self.tagscache = None
631 self._tagstypecache = None
631 self._tagstypecache = None
632 self.nodetagscache = None
632 self.nodetagscache = None
633 self.branchcache = None
633 self.branchcache = None
634 self._ubranchcache = None
634 self._ubranchcache = None
635 self._branchcachetip = None
635 self._branchcachetip = None
636
636
637 def _lock(self, lockname, wait, releasefn, acquirefn, desc):
637 def _lock(self, lockname, wait, releasefn, acquirefn, desc):
638 try:
638 try:
639 l = lock.lock(lockname, 0, releasefn, desc=desc)
639 l = lock.lock(lockname, 0, releasefn, desc=desc)
640 except lock.LockHeld, inst:
640 except lock.LockHeld, inst:
641 if not wait:
641 if not wait:
642 raise
642 raise
643 self.ui.warn(_("waiting for lock on %s held by %r\n") %
643 self.ui.warn(_("waiting for lock on %s held by %r\n") %
644 (desc, inst.locker))
644 (desc, inst.locker))
645 # default to 600 seconds timeout
645 # default to 600 seconds timeout
646 l = lock.lock(lockname, int(self.ui.config("ui", "timeout", "600")),
646 l = lock.lock(lockname, int(self.ui.config("ui", "timeout", "600")),
647 releasefn, desc=desc)
647 releasefn, desc=desc)
648 if acquirefn:
648 if acquirefn:
649 acquirefn()
649 acquirefn()
650 return l
650 return l
651
651
652 def lock(self, wait=True):
652 def lock(self, wait=True):
653 if self._lockref and self._lockref():
653 if self._lockref and self._lockref():
654 return self._lockref()
654 return self._lockref()
655
655
656 l = self._lock(self.sjoin("lock"), wait, None, self.invalidate,
656 l = self._lock(self.sjoin("lock"), wait, None, self.invalidate,
657 _('repository %s') % self.origroot)
657 _('repository %s') % self.origroot)
658 self._lockref = weakref.ref(l)
658 self._lockref = weakref.ref(l)
659 return l
659 return l
660
660
661 def wlock(self, wait=True):
661 def wlock(self, wait=True):
662 if self._wlockref and self._wlockref():
662 if self._wlockref and self._wlockref():
663 return self._wlockref()
663 return self._wlockref()
664
664
665 l = self._lock(self.join("wlock"), wait, self.dirstate.write,
665 l = self._lock(self.join("wlock"), wait, self.dirstate.write,
666 self.dirstate.invalidate, _('working directory of %s') %
666 self.dirstate.invalidate, _('working directory of %s') %
667 self.origroot)
667 self.origroot)
668 self._wlockref = weakref.ref(l)
668 self._wlockref = weakref.ref(l)
669 return l
669 return l
670
670
671 def filecommit(self, fn, manifest1, manifest2, linkrev, tr, changelist):
671 def filecommit(self, fn, manifest1, manifest2, linkrev, tr, changelist):
672 """
672 """
673 commit an individual file as part of a larger transaction
673 commit an individual file as part of a larger transaction
674 """
674 """
675
675
676 t = self.wread(fn)
676 t = self.wread(fn)
677 fl = self.file(fn)
677 fl = self.file(fn)
678 fp1 = manifest1.get(fn, nullid)
678 fp1 = manifest1.get(fn, nullid)
679 fp2 = manifest2.get(fn, nullid)
679 fp2 = manifest2.get(fn, nullid)
680
680
681 meta = {}
681 meta = {}
682 cp = self.dirstate.copied(fn)
682 cp = self.dirstate.copied(fn)
683 if cp:
683 if cp:
684 # Mark the new revision of this file as a copy of another
684 # Mark the new revision of this file as a copy of another
685 # file. This copy data will effectively act as a parent
685 # file. This copy data will effectively act as a parent
686 # of this new revision. If this is a merge, the first
686 # of this new revision. If this is a merge, the first
687 # parent will be the nullid (meaning "look up the copy data")
687 # parent will be the nullid (meaning "look up the copy data")
688 # and the second one will be the other parent. For example:
688 # and the second one will be the other parent. For example:
689 #
689 #
690 # 0 --- 1 --- 3 rev1 changes file foo
690 # 0 --- 1 --- 3 rev1 changes file foo
691 # \ / rev2 renames foo to bar and changes it
691 # \ / rev2 renames foo to bar and changes it
692 # \- 2 -/ rev3 should have bar with all changes and
692 # \- 2 -/ rev3 should have bar with all changes and
693 # should record that bar descends from
693 # should record that bar descends from
694 # bar in rev2 and foo in rev1
694 # bar in rev2 and foo in rev1
695 #
695 #
696 # this allows this merge to succeed:
696 # this allows this merge to succeed:
697 #
697 #
698 # 0 --- 1 --- 3 rev4 reverts the content change from rev2
698 # 0 --- 1 --- 3 rev4 reverts the content change from rev2
699 # \ / merging rev3 and rev4 should use bar@rev2
699 # \ / merging rev3 and rev4 should use bar@rev2
700 # \- 2 --- 4 as the merge base
700 # \- 2 --- 4 as the merge base
701 #
701 #
702 meta["copy"] = cp
702 meta["copy"] = cp
703 if not manifest2: # not a branch merge
703 if not manifest2: # not a branch merge
704 meta["copyrev"] = hex(manifest1.get(cp, nullid))
704 meta["copyrev"] = hex(manifest1.get(cp, nullid))
705 fp2 = nullid
705 fp2 = nullid
706 elif fp2 != nullid: # copied on remote side
706 elif fp2 != nullid: # copied on remote side
707 meta["copyrev"] = hex(manifest1.get(cp, nullid))
707 meta["copyrev"] = hex(manifest1.get(cp, nullid))
708 elif fp1 != nullid: # copied on local side, reversed
708 elif fp1 != nullid: # copied on local side, reversed
709 meta["copyrev"] = hex(manifest2.get(cp))
709 meta["copyrev"] = hex(manifest2.get(cp))
710 fp2 = fp1
710 fp2 = fp1
711 elif cp in manifest2: # directory rename on local side
711 elif cp in manifest2: # directory rename on local side
712 meta["copyrev"] = hex(manifest2[cp])
712 meta["copyrev"] = hex(manifest2[cp])
713 else: # directory rename on remote side
713 else: # directory rename on remote side
714 meta["copyrev"] = hex(manifest1.get(cp, nullid))
714 meta["copyrev"] = hex(manifest1.get(cp, nullid))
715 self.ui.debug(_(" %s: copy %s:%s\n") %
715 self.ui.debug(_(" %s: copy %s:%s\n") %
716 (fn, cp, meta["copyrev"]))
716 (fn, cp, meta["copyrev"]))
717 fp1 = nullid
717 fp1 = nullid
718 elif fp2 != nullid:
718 elif fp2 != nullid:
719 # is one parent an ancestor of the other?
719 # is one parent an ancestor of the other?
720 fpa = fl.ancestor(fp1, fp2)
720 fpa = fl.ancestor(fp1, fp2)
721 if fpa == fp1:
721 if fpa == fp1:
722 fp1, fp2 = fp2, nullid
722 fp1, fp2 = fp2, nullid
723 elif fpa == fp2:
723 elif fpa == fp2:
724 fp2 = nullid
724 fp2 = nullid
725
725
726 # is the file unmodified from the parent? report existing entry
726 # is the file unmodified from the parent? report existing entry
727 if fp2 == nullid and not fl.cmp(fp1, t) and not meta:
727 if fp2 == nullid and not fl.cmp(fp1, t) and not meta:
728 return fp1
728 return fp1
729
729
730 changelist.append(fn)
730 changelist.append(fn)
731 return fl.add(t, meta, tr, linkrev, fp1, fp2)
731 return fl.add(t, meta, tr, linkrev, fp1, fp2)
732
732
733 def rawcommit(self, files, text, user, date, p1=None, p2=None, extra={}):
733 def rawcommit(self, files, text, user, date, p1=None, p2=None, extra={}):
734 if p1 is None:
734 if p1 is None:
735 p1, p2 = self.dirstate.parents()
735 p1, p2 = self.dirstate.parents()
736 return self.commit(files=files, text=text, user=user, date=date,
736 return self.commit(files=files, text=text, user=user, date=date,
737 p1=p1, p2=p2, extra=extra, empty_ok=True)
737 p1=p1, p2=p2, extra=extra, empty_ok=True)
738
738
739 def commit(self, files=None, text="", user=None, date=None,
739 def commit(self, files=None, text="", user=None, date=None,
740 match=util.always, force=False, force_editor=False,
740 match=util.always, force=False, force_editor=False,
741 p1=None, p2=None, extra={}, empty_ok=False):
741 p1=None, p2=None, extra={}, empty_ok=False):
742 wlock = lock = tr = None
742 wlock = lock = tr = None
743 valid = 0 # don't save the dirstate if this isn't set
743 valid = 0 # don't save the dirstate if this isn't set
744 if files:
744 if files:
745 files = util.unique(files)
745 files = util.unique(files)
746 try:
746 try:
747 commit = []
747 commit = []
748 remove = []
748 remove = []
749 changed = []
749 changed = []
750 use_dirstate = (p1 is None) # not rawcommit
750 use_dirstate = (p1 is None) # not rawcommit
751 extra = extra.copy()
751 extra = extra.copy()
752
752
753 if use_dirstate:
753 if use_dirstate:
754 if files:
754 if files:
755 for f in files:
755 for f in files:
756 s = self.dirstate[f]
756 s = self.dirstate[f]
757 if s in 'nma':
757 if s in 'nma':
758 commit.append(f)
758 commit.append(f)
759 elif s == 'r':
759 elif s == 'r':
760 remove.append(f)
760 remove.append(f)
761 else:
761 else:
762 self.ui.warn(_("%s not tracked!\n") % f)
762 self.ui.warn(_("%s not tracked!\n") % f)
763 else:
763 else:
764 changes = self.status(match=match)[:5]
764 changes = self.status(match=match)[:5]
765 modified, added, removed, deleted, unknown = changes
765 modified, added, removed, deleted, unknown = changes
766 commit = modified + added
766 commit = modified + added
767 remove = removed
767 remove = removed
768 else:
768 else:
769 commit = files
769 commit = files
770
770
771 if use_dirstate:
771 if use_dirstate:
772 p1, p2 = self.dirstate.parents()
772 p1, p2 = self.dirstate.parents()
773 update_dirstate = True
773 update_dirstate = True
774 else:
774 else:
775 p1, p2 = p1, p2 or nullid
775 p1, p2 = p1, p2 or nullid
776 update_dirstate = (self.dirstate.parents()[0] == p1)
776 update_dirstate = (self.dirstate.parents()[0] == p1)
777
777
778 c1 = self.changelog.read(p1)
778 c1 = self.changelog.read(p1)
779 c2 = self.changelog.read(p2)
779 c2 = self.changelog.read(p2)
780 m1 = self.manifest.read(c1[0]).copy()
780 m1 = self.manifest.read(c1[0]).copy()
781 m2 = self.manifest.read(c2[0])
781 m2 = self.manifest.read(c2[0])
782
782
783 if use_dirstate:
783 if use_dirstate:
784 branchname = self.workingctx().branch()
784 branchname = self.workingctx().branch()
785 try:
785 try:
786 branchname = branchname.decode('UTF-8').encode('UTF-8')
786 branchname = branchname.decode('UTF-8').encode('UTF-8')
787 except UnicodeDecodeError:
787 except UnicodeDecodeError:
788 raise util.Abort(_('branch name not in UTF-8!'))
788 raise util.Abort(_('branch name not in UTF-8!'))
789 else:
789 else:
790 branchname = ""
790 branchname = ""
791
791
792 if use_dirstate:
792 if use_dirstate:
793 oldname = c1[5].get("branch") # stored in UTF-8
793 oldname = c1[5].get("branch") # stored in UTF-8
794 if (not commit and not remove and not force and p2 == nullid
794 if (not commit and not remove and not force and p2 == nullid
795 and branchname == oldname):
795 and branchname == oldname):
796 self.ui.status(_("nothing changed\n"))
796 self.ui.status(_("nothing changed\n"))
797 return None
797 return None
798
798
799 xp1 = hex(p1)
799 xp1 = hex(p1)
800 if p2 == nullid: xp2 = ''
800 if p2 == nullid: xp2 = ''
801 else: xp2 = hex(p2)
801 else: xp2 = hex(p2)
802
802
803 self.hook("precommit", throw=True, parent1=xp1, parent2=xp2)
803 self.hook("precommit", throw=True, parent1=xp1, parent2=xp2)
804
804
805 wlock = self.wlock()
805 wlock = self.wlock()
806 lock = self.lock()
806 lock = self.lock()
807 tr = self.transaction()
807 tr = self.transaction()
808 trp = weakref.proxy(tr)
808 trp = weakref.proxy(tr)
809
809
810 # check in files
810 # check in files
811 new = {}
811 new = {}
812 linkrev = self.changelog.count()
812 linkrev = self.changelog.count()
813 commit.sort()
813 commit.sort()
814 is_exec = util.execfunc(self.root, m1.execf)
814 is_exec = util.execfunc(self.root, m1.execf)
815 is_link = util.linkfunc(self.root, m1.linkf)
815 is_link = util.linkfunc(self.root, m1.linkf)
816 for f in commit:
816 for f in commit:
817 self.ui.note(f + "\n")
817 self.ui.note(f + "\n")
818 try:
818 try:
819 new[f] = self.filecommit(f, m1, m2, linkrev, trp, changed)
819 new[f] = self.filecommit(f, m1, m2, linkrev, trp, changed)
820 new_exec = is_exec(f)
820 new_exec = is_exec(f)
821 new_link = is_link(f)
821 new_link = is_link(f)
822 if ((not changed or changed[-1] != f) and
822 if ((not changed or changed[-1] != f) and
823 m2.get(f) != new[f]):
823 m2.get(f) != new[f]):
824 # mention the file in the changelog if some
824 # mention the file in the changelog if some
825 # flag changed, even if there was no content
825 # flag changed, even if there was no content
826 # change.
826 # change.
827 old_exec = m1.execf(f)
827 old_exec = m1.execf(f)
828 old_link = m1.linkf(f)
828 old_link = m1.linkf(f)
829 if old_exec != new_exec or old_link != new_link:
829 if old_exec != new_exec or old_link != new_link:
830 changed.append(f)
830 changed.append(f)
831 m1.set(f, new_exec, new_link)
831 m1.set(f, new_exec, new_link)
832 if use_dirstate:
832 if use_dirstate:
833 self.dirstate.normal(f)
833 self.dirstate.normal(f)
834
834
835 except (OSError, IOError):
835 except (OSError, IOError):
836 if use_dirstate:
836 if use_dirstate:
837 self.ui.warn(_("trouble committing %s!\n") % f)
837 self.ui.warn(_("trouble committing %s!\n") % f)
838 raise
838 raise
839 else:
839 else:
840 remove.append(f)
840 remove.append(f)
841
841
842 # update manifest
842 # update manifest
843 m1.update(new)
843 m1.update(new)
844 remove.sort()
844 remove.sort()
845 removed = []
845 removed = []
846
846
847 for f in remove:
847 for f in remove:
848 if f in m1:
848 if f in m1:
849 del m1[f]
849 del m1[f]
850 removed.append(f)
850 removed.append(f)
851 elif f in m2:
851 elif f in m2:
852 removed.append(f)
852 removed.append(f)
853 mn = self.manifest.add(m1, trp, linkrev, c1[0], c2[0],
853 mn = self.manifest.add(m1, trp, linkrev, c1[0], c2[0],
854 (new, removed))
854 (new, removed))
855
855
856 # add changeset
856 # add changeset
857 new = new.keys()
857 new = new.keys()
858 new.sort()
858 new.sort()
859
859
860 user = user or self.ui.username()
860 user = user or self.ui.username()
861 if (not empty_ok and not text) or force_editor:
861 if (not empty_ok and not text) or force_editor:
862 edittext = []
862 edittext = []
863 if text:
863 if text:
864 edittext.append(text)
864 edittext.append(text)
865 edittext.append("")
865 edittext.append("")
866 edittext.append(_("HG: Enter commit message."
866 edittext.append(_("HG: Enter commit message."
867 " Lines beginning with 'HG:' are removed."))
867 " Lines beginning with 'HG:' are removed."))
868 edittext.append("HG: --")
868 edittext.append("HG: --")
869 edittext.append("HG: user: %s" % user)
869 edittext.append("HG: user: %s" % user)
870 if p2 != nullid:
870 if p2 != nullid:
871 edittext.append("HG: branch merge")
871 edittext.append("HG: branch merge")
872 if branchname:
872 if branchname:
873 edittext.append("HG: branch '%s'" % util.tolocal(branchname))
873 edittext.append("HG: branch '%s'" % util.tolocal(branchname))
874 edittext.extend(["HG: changed %s" % f for f in changed])
874 edittext.extend(["HG: changed %s" % f for f in changed])
875 edittext.extend(["HG: removed %s" % f for f in removed])
875 edittext.extend(["HG: removed %s" % f for f in removed])
876 if not changed and not remove:
876 if not changed and not remove:
877 edittext.append("HG: no files changed")
877 edittext.append("HG: no files changed")
878 edittext.append("")
878 edittext.append("")
879 # run editor in the repository root
879 # run editor in the repository root
880 olddir = os.getcwd()
880 olddir = os.getcwd()
881 os.chdir(self.root)
881 os.chdir(self.root)
882 text = self.ui.edit("\n".join(edittext), user)
882 text = self.ui.edit("\n".join(edittext), user)
883 os.chdir(olddir)
883 os.chdir(olddir)
884
884
885 if branchname:
885 if branchname:
886 extra["branch"] = branchname
886 extra["branch"] = branchname
887
887
888 if use_dirstate:
888 if use_dirstate:
889 lines = [line.rstrip() for line in text.rstrip().splitlines()]
889 lines = [line.rstrip() for line in text.rstrip().splitlines()]
890 while lines and not lines[0]:
890 while lines and not lines[0]:
891 del lines[0]
891 del lines[0]
892 if not lines:
892 if not lines:
893 raise util.Abort(_("empty commit message"))
893 raise util.Abort(_("empty commit message"))
894 text = '\n'.join(lines)
894 text = '\n'.join(lines)
895
895
896 n = self.changelog.add(mn, changed + removed, text, trp, p1, p2,
896 n = self.changelog.add(mn, changed + removed, text, trp, p1, p2,
897 user, date, extra)
897 user, date, extra)
898 self.hook('pretxncommit', throw=True, node=hex(n), parent1=xp1,
898 self.hook('pretxncommit', throw=True, node=hex(n), parent1=xp1,
899 parent2=xp2)
899 parent2=xp2)
900 tr.close()
900 tr.close()
901
901
902 if self.branchcache:
902 if self.branchcache:
903 self.branchtags()
903 self.branchtags()
904
904
905 if use_dirstate or update_dirstate:
905 if use_dirstate or update_dirstate:
906 self.dirstate.setparents(n)
906 self.dirstate.setparents(n)
907 if use_dirstate:
907 if use_dirstate:
908 for f in removed:
908 for f in removed:
909 self.dirstate.forget(f)
909 self.dirstate.forget(f)
910 valid = 1 # our dirstate updates are complete
910 valid = 1 # our dirstate updates are complete
911
911
912 self.hook("commit", node=hex(n), parent1=xp1, parent2=xp2)
912 self.hook("commit", node=hex(n), parent1=xp1, parent2=xp2)
913 return n
913 return n
914 finally:
914 finally:
915 if not valid: # don't save our updated dirstate
915 if not valid: # don't save our updated dirstate
916 self.dirstate.invalidate()
916 self.dirstate.invalidate()
917 del tr, lock, wlock
917 del tr, lock, wlock
918
918
919 def walk(self, node=None, files=[], match=util.always, badmatch=None):
919 def walk(self, node=None, files=[], match=util.always, badmatch=None):
920 '''
920 '''
921 walk recursively through the directory tree or a given
921 walk recursively through the directory tree or a given
922 changeset, finding all files matched by the match
922 changeset, finding all files matched by the match
923 function
923 function
924
924
925 results are yielded in a tuple (src, filename), where src
925 results are yielded in a tuple (src, filename), where src
926 is one of:
926 is one of:
927 'f' the file was found in the directory tree
927 'f' the file was found in the directory tree
928 'm' the file was only in the dirstate and not in the tree
928 'm' the file was only in the dirstate and not in the tree
929 'b' file was not found and matched badmatch
929 'b' file was not found and matched badmatch
930 '''
930 '''
931
931
932 if node:
932 if node:
933 fdict = dict.fromkeys(files)
933 fdict = dict.fromkeys(files)
934 # for dirstate.walk, files=['.'] means "walk the whole tree".
934 # for dirstate.walk, files=['.'] means "walk the whole tree".
935 # follow that here, too
935 # follow that here, too
936 fdict.pop('.', None)
936 fdict.pop('.', None)
937 mdict = self.manifest.read(self.changelog.read(node)[0])
937 mdict = self.manifest.read(self.changelog.read(node)[0])
938 mfiles = mdict.keys()
938 mfiles = mdict.keys()
939 mfiles.sort()
939 mfiles.sort()
940 for fn in mfiles:
940 for fn in mfiles:
941 for ffn in fdict:
941 for ffn in fdict:
942 # match if the file is the exact name or a directory
942 # match if the file is the exact name or a directory
943 if ffn == fn or fn.startswith("%s/" % ffn):
943 if ffn == fn or fn.startswith("%s/" % ffn):
944 del fdict[ffn]
944 del fdict[ffn]
945 break
945 break
946 if match(fn):
946 if match(fn):
947 yield 'm', fn
947 yield 'm', fn
948 ffiles = fdict.keys()
948 ffiles = fdict.keys()
949 ffiles.sort()
949 ffiles.sort()
950 for fn in ffiles:
950 for fn in ffiles:
951 if badmatch and badmatch(fn):
951 if badmatch and badmatch(fn):
952 if match(fn):
952 if match(fn):
953 yield 'b', fn
953 yield 'b', fn
954 else:
954 else:
955 self.ui.warn(_('%s: No such file in rev %s\n')
955 self.ui.warn(_('%s: No such file in rev %s\n')
956 % (self.pathto(fn), short(node)))
956 % (self.pathto(fn), short(node)))
957 else:
957 else:
958 for src, fn in self.dirstate.walk(files, match, badmatch=badmatch):
958 for src, fn in self.dirstate.walk(files, match, badmatch=badmatch):
959 yield src, fn
959 yield src, fn
960
960
961 def status(self, node1=None, node2=None, files=[], match=util.always,
961 def status(self, node1=None, node2=None, files=[], match=util.always,
962 list_ignored=False, list_clean=False):
962 list_ignored=False, list_clean=False, list_unknown=True):
963 """return status of files between two nodes or node and working directory
963 """return status of files between two nodes or node and working directory
964
964
965 If node1 is None, use the first dirstate parent instead.
965 If node1 is None, use the first dirstate parent instead.
966 If node2 is None, compare node1 with working directory.
966 If node2 is None, compare node1 with working directory.
967 """
967 """
968
968
969 def fcmp(fn, getnode):
969 def fcmp(fn, getnode):
970 t1 = self.wread(fn)
970 t1 = self.wread(fn)
971 return self.file(fn).cmp(getnode(fn), t1)
971 return self.file(fn).cmp(getnode(fn), t1)
972
972
973 def mfmatches(node):
973 def mfmatches(node):
974 change = self.changelog.read(node)
974 change = self.changelog.read(node)
975 mf = self.manifest.read(change[0]).copy()
975 mf = self.manifest.read(change[0]).copy()
976 for fn in mf.keys():
976 for fn in mf.keys():
977 if not match(fn):
977 if not match(fn):
978 del mf[fn]
978 del mf[fn]
979 return mf
979 return mf
980
980
981 modified, added, removed, deleted, unknown = [], [], [], [], []
981 modified, added, removed, deleted, unknown = [], [], [], [], []
982 ignored, clean = [], []
982 ignored, clean = [], []
983
983
984 compareworking = False
984 compareworking = False
985 if not node1 or (not node2 and node1 == self.dirstate.parents()[0]):
985 if not node1 or (not node2 and node1 == self.dirstate.parents()[0]):
986 compareworking = True
986 compareworking = True
987
987
988 if not compareworking:
988 if not compareworking:
989 # read the manifest from node1 before the manifest from node2,
989 # read the manifest from node1 before the manifest from node2,
990 # so that we'll hit the manifest cache if we're going through
990 # so that we'll hit the manifest cache if we're going through
991 # all the revisions in parent->child order.
991 # all the revisions in parent->child order.
992 mf1 = mfmatches(node1)
992 mf1 = mfmatches(node1)
993
993
994 # are we comparing the working directory?
994 # are we comparing the working directory?
995 if not node2:
995 if not node2:
996 (lookup, modified, added, removed, deleted, unknown,
996 (lookup, modified, added, removed, deleted, unknown,
997 ignored, clean) = self.dirstate.status(files, match,
997 ignored, clean) = self.dirstate.status(files, match,
998 list_ignored, list_clean)
998 list_ignored, list_clean,
999 list_unknown)
999
1000
1000 # are we comparing working dir against its parent?
1001 # are we comparing working dir against its parent?
1001 if compareworking:
1002 if compareworking:
1002 if lookup:
1003 if lookup:
1003 fixup = []
1004 fixup = []
1004 # do a full compare of any files that might have changed
1005 # do a full compare of any files that might have changed
1005 ctx = self.changectx()
1006 ctx = self.changectx()
1006 mexec = lambda f: 'x' in ctx.fileflags(f)
1007 mexec = lambda f: 'x' in ctx.fileflags(f)
1007 mlink = lambda f: 'l' in ctx.fileflags(f)
1008 mlink = lambda f: 'l' in ctx.fileflags(f)
1008 is_exec = util.execfunc(self.root, mexec)
1009 is_exec = util.execfunc(self.root, mexec)
1009 is_link = util.linkfunc(self.root, mlink)
1010 is_link = util.linkfunc(self.root, mlink)
1010 def flags(f):
1011 def flags(f):
1011 return is_link(f) and 'l' or is_exec(f) and 'x' or ''
1012 return is_link(f) and 'l' or is_exec(f) and 'x' or ''
1012 for f in lookup:
1013 for f in lookup:
1013 if (f not in ctx or flags(f) != ctx.fileflags(f)
1014 if (f not in ctx or flags(f) != ctx.fileflags(f)
1014 or ctx[f].cmp(self.wread(f))):
1015 or ctx[f].cmp(self.wread(f))):
1015 modified.append(f)
1016 modified.append(f)
1016 else:
1017 else:
1017 fixup.append(f)
1018 fixup.append(f)
1018 if list_clean:
1019 if list_clean:
1019 clean.append(f)
1020 clean.append(f)
1020
1021
1021 # update dirstate for files that are actually clean
1022 # update dirstate for files that are actually clean
1022 if fixup:
1023 if fixup:
1023 wlock = None
1024 wlock = None
1024 try:
1025 try:
1025 try:
1026 try:
1026 wlock = self.wlock(False)
1027 wlock = self.wlock(False)
1027 except lock.LockException:
1028 except lock.LockException:
1028 pass
1029 pass
1029 if wlock:
1030 if wlock:
1030 for f in fixup:
1031 for f in fixup:
1031 self.dirstate.normal(f)
1032 self.dirstate.normal(f)
1032 finally:
1033 finally:
1033 del wlock
1034 del wlock
1034 else:
1035 else:
1035 # we are comparing working dir against non-parent
1036 # we are comparing working dir against non-parent
1036 # generate a pseudo-manifest for the working dir
1037 # generate a pseudo-manifest for the working dir
1037 # XXX: create it in dirstate.py ?
1038 # XXX: create it in dirstate.py ?
1038 mf2 = mfmatches(self.dirstate.parents()[0])
1039 mf2 = mfmatches(self.dirstate.parents()[0])
1039 is_exec = util.execfunc(self.root, mf2.execf)
1040 is_exec = util.execfunc(self.root, mf2.execf)
1040 is_link = util.linkfunc(self.root, mf2.linkf)
1041 is_link = util.linkfunc(self.root, mf2.linkf)
1041 for f in lookup + modified + added:
1042 for f in lookup + modified + added:
1042 mf2[f] = ""
1043 mf2[f] = ""
1043 mf2.set(f, is_exec(f), is_link(f))
1044 mf2.set(f, is_exec(f), is_link(f))
1044 for f in removed:
1045 for f in removed:
1045 if f in mf2:
1046 if f in mf2:
1046 del mf2[f]
1047 del mf2[f]
1047
1048
1048 else:
1049 else:
1049 # we are comparing two revisions
1050 # we are comparing two revisions
1050 mf2 = mfmatches(node2)
1051 mf2 = mfmatches(node2)
1051
1052
1052 if not compareworking:
1053 if not compareworking:
1053 # flush lists from dirstate before comparing manifests
1054 # flush lists from dirstate before comparing manifests
1054 modified, added, clean = [], [], []
1055 modified, added, clean = [], [], []
1055
1056
1056 # make sure to sort the files so we talk to the disk in a
1057 # make sure to sort the files so we talk to the disk in a
1057 # reasonable order
1058 # reasonable order
1058 mf2keys = mf2.keys()
1059 mf2keys = mf2.keys()
1059 mf2keys.sort()
1060 mf2keys.sort()
1060 getnode = lambda fn: mf1.get(fn, nullid)
1061 getnode = lambda fn: mf1.get(fn, nullid)
1061 for fn in mf2keys:
1062 for fn in mf2keys:
1062 if fn in mf1:
1063 if fn in mf1:
1063 if (mf1.flags(fn) != mf2.flags(fn) or
1064 if (mf1.flags(fn) != mf2.flags(fn) or
1064 (mf1[fn] != mf2[fn] and
1065 (mf1[fn] != mf2[fn] and
1065 (mf2[fn] != "" or fcmp(fn, getnode)))):
1066 (mf2[fn] != "" or fcmp(fn, getnode)))):
1066 modified.append(fn)
1067 modified.append(fn)
1067 elif list_clean:
1068 elif list_clean:
1068 clean.append(fn)
1069 clean.append(fn)
1069 del mf1[fn]
1070 del mf1[fn]
1070 else:
1071 else:
1071 added.append(fn)
1072 added.append(fn)
1072
1073
1073 removed = mf1.keys()
1074 removed = mf1.keys()
1074
1075
1075 # sort and return results:
1076 # sort and return results:
1076 for l in modified, added, removed, deleted, unknown, ignored, clean:
1077 for l in modified, added, removed, deleted, unknown, ignored, clean:
1077 l.sort()
1078 l.sort()
1078 return (modified, added, removed, deleted, unknown, ignored, clean)
1079 return (modified, added, removed, deleted, unknown, ignored, clean)
1079
1080
1080 def add(self, list):
1081 def add(self, list):
1081 wlock = self.wlock()
1082 wlock = self.wlock()
1082 try:
1083 try:
1083 rejected = []
1084 rejected = []
1084 for f in list:
1085 for f in list:
1085 p = self.wjoin(f)
1086 p = self.wjoin(f)
1086 try:
1087 try:
1087 st = os.lstat(p)
1088 st = os.lstat(p)
1088 except:
1089 except:
1089 self.ui.warn(_("%s does not exist!\n") % f)
1090 self.ui.warn(_("%s does not exist!\n") % f)
1090 rejected.append(f)
1091 rejected.append(f)
1091 continue
1092 continue
1092 if st.st_size > 10000000:
1093 if st.st_size > 10000000:
1093 self.ui.warn(_("%s: files over 10MB may cause memory and"
1094 self.ui.warn(_("%s: files over 10MB may cause memory and"
1094 " performance problems\n"
1095 " performance problems\n"
1095 "(use 'hg revert %s' to unadd the file)\n")
1096 "(use 'hg revert %s' to unadd the file)\n")
1096 % (f, f))
1097 % (f, f))
1097 if not (stat.S_ISREG(st.st_mode) or stat.S_ISLNK(st.st_mode)):
1098 if not (stat.S_ISREG(st.st_mode) or stat.S_ISLNK(st.st_mode)):
1098 self.ui.warn(_("%s not added: only files and symlinks "
1099 self.ui.warn(_("%s not added: only files and symlinks "
1099 "supported currently\n") % f)
1100 "supported currently\n") % f)
1100 rejected.append(p)
1101 rejected.append(p)
1101 elif self.dirstate[f] in 'amn':
1102 elif self.dirstate[f] in 'amn':
1102 self.ui.warn(_("%s already tracked!\n") % f)
1103 self.ui.warn(_("%s already tracked!\n") % f)
1103 elif self.dirstate[f] == 'r':
1104 elif self.dirstate[f] == 'r':
1104 self.dirstate.normallookup(f)
1105 self.dirstate.normallookup(f)
1105 else:
1106 else:
1106 self.dirstate.add(f)
1107 self.dirstate.add(f)
1107 return rejected
1108 return rejected
1108 finally:
1109 finally:
1109 del wlock
1110 del wlock
1110
1111
1111 def forget(self, list):
1112 def forget(self, list):
1112 wlock = self.wlock()
1113 wlock = self.wlock()
1113 try:
1114 try:
1114 for f in list:
1115 for f in list:
1115 if self.dirstate[f] != 'a':
1116 if self.dirstate[f] != 'a':
1116 self.ui.warn(_("%s not added!\n") % f)
1117 self.ui.warn(_("%s not added!\n") % f)
1117 else:
1118 else:
1118 self.dirstate.forget(f)
1119 self.dirstate.forget(f)
1119 finally:
1120 finally:
1120 del wlock
1121 del wlock
1121
1122
1122 def remove(self, list, unlink=False):
1123 def remove(self, list, unlink=False):
1123 wlock = None
1124 wlock = None
1124 try:
1125 try:
1125 if unlink:
1126 if unlink:
1126 for f in list:
1127 for f in list:
1127 try:
1128 try:
1128 util.unlink(self.wjoin(f))
1129 util.unlink(self.wjoin(f))
1129 except OSError, inst:
1130 except OSError, inst:
1130 if inst.errno != errno.ENOENT:
1131 if inst.errno != errno.ENOENT:
1131 raise
1132 raise
1132 wlock = self.wlock()
1133 wlock = self.wlock()
1133 for f in list:
1134 for f in list:
1134 if unlink and os.path.exists(self.wjoin(f)):
1135 if unlink and os.path.exists(self.wjoin(f)):
1135 self.ui.warn(_("%s still exists!\n") % f)
1136 self.ui.warn(_("%s still exists!\n") % f)
1136 elif self.dirstate[f] == 'a':
1137 elif self.dirstate[f] == 'a':
1137 self.dirstate.forget(f)
1138 self.dirstate.forget(f)
1138 elif f not in self.dirstate:
1139 elif f not in self.dirstate:
1139 self.ui.warn(_("%s not tracked!\n") % f)
1140 self.ui.warn(_("%s not tracked!\n") % f)
1140 else:
1141 else:
1141 self.dirstate.remove(f)
1142 self.dirstate.remove(f)
1142 finally:
1143 finally:
1143 del wlock
1144 del wlock
1144
1145
1145 def undelete(self, list):
1146 def undelete(self, list):
1146 wlock = None
1147 wlock = None
1147 try:
1148 try:
1148 manifests = [self.manifest.read(self.changelog.read(p)[0])
1149 manifests = [self.manifest.read(self.changelog.read(p)[0])
1149 for p in self.dirstate.parents() if p != nullid]
1150 for p in self.dirstate.parents() if p != nullid]
1150 wlock = self.wlock()
1151 wlock = self.wlock()
1151 for f in list:
1152 for f in list:
1152 if self.dirstate[f] != 'r':
1153 if self.dirstate[f] != 'r':
1153 self.ui.warn("%s not removed!\n" % f)
1154 self.ui.warn("%s not removed!\n" % f)
1154 else:
1155 else:
1155 m = f in manifests[0] and manifests[0] or manifests[1]
1156 m = f in manifests[0] and manifests[0] or manifests[1]
1156 t = self.file(f).read(m[f])
1157 t = self.file(f).read(m[f])
1157 self.wwrite(f, t, m.flags(f))
1158 self.wwrite(f, t, m.flags(f))
1158 self.dirstate.normal(f)
1159 self.dirstate.normal(f)
1159 finally:
1160 finally:
1160 del wlock
1161 del wlock
1161
1162
1162 def copy(self, source, dest):
1163 def copy(self, source, dest):
1163 wlock = None
1164 wlock = None
1164 try:
1165 try:
1165 p = self.wjoin(dest)
1166 p = self.wjoin(dest)
1166 if not (os.path.exists(p) or os.path.islink(p)):
1167 if not (os.path.exists(p) or os.path.islink(p)):
1167 self.ui.warn(_("%s does not exist!\n") % dest)
1168 self.ui.warn(_("%s does not exist!\n") % dest)
1168 elif not (os.path.isfile(p) or os.path.islink(p)):
1169 elif not (os.path.isfile(p) or os.path.islink(p)):
1169 self.ui.warn(_("copy failed: %s is not a file or a "
1170 self.ui.warn(_("copy failed: %s is not a file or a "
1170 "symbolic link\n") % dest)
1171 "symbolic link\n") % dest)
1171 else:
1172 else:
1172 wlock = self.wlock()
1173 wlock = self.wlock()
1173 if dest not in self.dirstate:
1174 if dest not in self.dirstate:
1174 self.dirstate.add(dest)
1175 self.dirstate.add(dest)
1175 self.dirstate.copy(source, dest)
1176 self.dirstate.copy(source, dest)
1176 finally:
1177 finally:
1177 del wlock
1178 del wlock
1178
1179
1179 def heads(self, start=None):
1180 def heads(self, start=None):
1180 heads = self.changelog.heads(start)
1181 heads = self.changelog.heads(start)
1181 # sort the output in rev descending order
1182 # sort the output in rev descending order
1182 heads = [(-self.changelog.rev(h), h) for h in heads]
1183 heads = [(-self.changelog.rev(h), h) for h in heads]
1183 heads.sort()
1184 heads.sort()
1184 return [n for (r, n) in heads]
1185 return [n for (r, n) in heads]
1185
1186
1186 def branchheads(self, branch, start=None):
1187 def branchheads(self, branch, start=None):
1187 branches = self.branchtags()
1188 branches = self.branchtags()
1188 if branch not in branches:
1189 if branch not in branches:
1189 return []
1190 return []
1190 # The basic algorithm is this:
1191 # The basic algorithm is this:
1191 #
1192 #
1192 # Start from the branch tip since there are no later revisions that can
1193 # Start from the branch tip since there are no later revisions that can
1193 # possibly be in this branch, and the tip is a guaranteed head.
1194 # possibly be in this branch, and the tip is a guaranteed head.
1194 #
1195 #
1195 # Remember the tip's parents as the first ancestors, since these by
1196 # Remember the tip's parents as the first ancestors, since these by
1196 # definition are not heads.
1197 # definition are not heads.
1197 #
1198 #
1198 # Step backwards from the brach tip through all the revisions. We are
1199 # Step backwards from the brach tip through all the revisions. We are
1199 # guaranteed by the rules of Mercurial that we will now be visiting the
1200 # guaranteed by the rules of Mercurial that we will now be visiting the
1200 # nodes in reverse topological order (children before parents).
1201 # nodes in reverse topological order (children before parents).
1201 #
1202 #
1202 # If a revision is one of the ancestors of a head then we can toss it
1203 # If a revision is one of the ancestors of a head then we can toss it
1203 # out of the ancestors set (we've already found it and won't be
1204 # out of the ancestors set (we've already found it and won't be
1204 # visiting it again) and put its parents in the ancestors set.
1205 # visiting it again) and put its parents in the ancestors set.
1205 #
1206 #
1206 # Otherwise, if a revision is in the branch it's another head, since it
1207 # Otherwise, if a revision is in the branch it's another head, since it
1207 # wasn't in the ancestor list of an existing head. So add it to the
1208 # wasn't in the ancestor list of an existing head. So add it to the
1208 # head list, and add its parents to the ancestor list.
1209 # head list, and add its parents to the ancestor list.
1209 #
1210 #
1210 # If it is not in the branch ignore it.
1211 # If it is not in the branch ignore it.
1211 #
1212 #
1212 # Once we have a list of heads, use nodesbetween to filter out all the
1213 # Once we have a list of heads, use nodesbetween to filter out all the
1213 # heads that cannot be reached from startrev. There may be a more
1214 # heads that cannot be reached from startrev. There may be a more
1214 # efficient way to do this as part of the previous algorithm.
1215 # efficient way to do this as part of the previous algorithm.
1215
1216
1216 set = util.set
1217 set = util.set
1217 heads = [self.changelog.rev(branches[branch])]
1218 heads = [self.changelog.rev(branches[branch])]
1218 # Don't care if ancestors contains nullrev or not.
1219 # Don't care if ancestors contains nullrev or not.
1219 ancestors = set(self.changelog.parentrevs(heads[0]))
1220 ancestors = set(self.changelog.parentrevs(heads[0]))
1220 for rev in xrange(heads[0] - 1, nullrev, -1):
1221 for rev in xrange(heads[0] - 1, nullrev, -1):
1221 if rev in ancestors:
1222 if rev in ancestors:
1222 ancestors.update(self.changelog.parentrevs(rev))
1223 ancestors.update(self.changelog.parentrevs(rev))
1223 ancestors.remove(rev)
1224 ancestors.remove(rev)
1224 elif self.changectx(rev).branch() == branch:
1225 elif self.changectx(rev).branch() == branch:
1225 heads.append(rev)
1226 heads.append(rev)
1226 ancestors.update(self.changelog.parentrevs(rev))
1227 ancestors.update(self.changelog.parentrevs(rev))
1227 heads = [self.changelog.node(rev) for rev in heads]
1228 heads = [self.changelog.node(rev) for rev in heads]
1228 if start is not None:
1229 if start is not None:
1229 heads = self.changelog.nodesbetween([start], heads)[2]
1230 heads = self.changelog.nodesbetween([start], heads)[2]
1230 return heads
1231 return heads
1231
1232
1232 def branches(self, nodes):
1233 def branches(self, nodes):
1233 if not nodes:
1234 if not nodes:
1234 nodes = [self.changelog.tip()]
1235 nodes = [self.changelog.tip()]
1235 b = []
1236 b = []
1236 for n in nodes:
1237 for n in nodes:
1237 t = n
1238 t = n
1238 while 1:
1239 while 1:
1239 p = self.changelog.parents(n)
1240 p = self.changelog.parents(n)
1240 if p[1] != nullid or p[0] == nullid:
1241 if p[1] != nullid or p[0] == nullid:
1241 b.append((t, n, p[0], p[1]))
1242 b.append((t, n, p[0], p[1]))
1242 break
1243 break
1243 n = p[0]
1244 n = p[0]
1244 return b
1245 return b
1245
1246
1246 def between(self, pairs):
1247 def between(self, pairs):
1247 r = []
1248 r = []
1248
1249
1249 for top, bottom in pairs:
1250 for top, bottom in pairs:
1250 n, l, i = top, [], 0
1251 n, l, i = top, [], 0
1251 f = 1
1252 f = 1
1252
1253
1253 while n != bottom:
1254 while n != bottom:
1254 p = self.changelog.parents(n)[0]
1255 p = self.changelog.parents(n)[0]
1255 if i == f:
1256 if i == f:
1256 l.append(n)
1257 l.append(n)
1257 f = f * 2
1258 f = f * 2
1258 n = p
1259 n = p
1259 i += 1
1260 i += 1
1260
1261
1261 r.append(l)
1262 r.append(l)
1262
1263
1263 return r
1264 return r
1264
1265
1265 def findincoming(self, remote, base=None, heads=None, force=False):
1266 def findincoming(self, remote, base=None, heads=None, force=False):
1266 """Return list of roots of the subsets of missing nodes from remote
1267 """Return list of roots of the subsets of missing nodes from remote
1267
1268
1268 If base dict is specified, assume that these nodes and their parents
1269 If base dict is specified, assume that these nodes and their parents
1269 exist on the remote side and that no child of a node of base exists
1270 exist on the remote side and that no child of a node of base exists
1270 in both remote and self.
1271 in both remote and self.
1271 Furthermore base will be updated to include the nodes that exists
1272 Furthermore base will be updated to include the nodes that exists
1272 in self and remote but no children exists in self and remote.
1273 in self and remote but no children exists in self and remote.
1273 If a list of heads is specified, return only nodes which are heads
1274 If a list of heads is specified, return only nodes which are heads
1274 or ancestors of these heads.
1275 or ancestors of these heads.
1275
1276
1276 All the ancestors of base are in self and in remote.
1277 All the ancestors of base are in self and in remote.
1277 All the descendants of the list returned are missing in self.
1278 All the descendants of the list returned are missing in self.
1278 (and so we know that the rest of the nodes are missing in remote, see
1279 (and so we know that the rest of the nodes are missing in remote, see
1279 outgoing)
1280 outgoing)
1280 """
1281 """
1281 m = self.changelog.nodemap
1282 m = self.changelog.nodemap
1282 search = []
1283 search = []
1283 fetch = {}
1284 fetch = {}
1284 seen = {}
1285 seen = {}
1285 seenbranch = {}
1286 seenbranch = {}
1286 if base == None:
1287 if base == None:
1287 base = {}
1288 base = {}
1288
1289
1289 if not heads:
1290 if not heads:
1290 heads = remote.heads()
1291 heads = remote.heads()
1291
1292
1292 if self.changelog.tip() == nullid:
1293 if self.changelog.tip() == nullid:
1293 base[nullid] = 1
1294 base[nullid] = 1
1294 if heads != [nullid]:
1295 if heads != [nullid]:
1295 return [nullid]
1296 return [nullid]
1296 return []
1297 return []
1297
1298
1298 # assume we're closer to the tip than the root
1299 # assume we're closer to the tip than the root
1299 # and start by examining the heads
1300 # and start by examining the heads
1300 self.ui.status(_("searching for changes\n"))
1301 self.ui.status(_("searching for changes\n"))
1301
1302
1302 unknown = []
1303 unknown = []
1303 for h in heads:
1304 for h in heads:
1304 if h not in m:
1305 if h not in m:
1305 unknown.append(h)
1306 unknown.append(h)
1306 else:
1307 else:
1307 base[h] = 1
1308 base[h] = 1
1308
1309
1309 if not unknown:
1310 if not unknown:
1310 return []
1311 return []
1311
1312
1312 req = dict.fromkeys(unknown)
1313 req = dict.fromkeys(unknown)
1313 reqcnt = 0
1314 reqcnt = 0
1314
1315
1315 # search through remote branches
1316 # search through remote branches
1316 # a 'branch' here is a linear segment of history, with four parts:
1317 # a 'branch' here is a linear segment of history, with four parts:
1317 # head, root, first parent, second parent
1318 # head, root, first parent, second parent
1318 # (a branch always has two parents (or none) by definition)
1319 # (a branch always has two parents (or none) by definition)
1319 unknown = remote.branches(unknown)
1320 unknown = remote.branches(unknown)
1320 while unknown:
1321 while unknown:
1321 r = []
1322 r = []
1322 while unknown:
1323 while unknown:
1323 n = unknown.pop(0)
1324 n = unknown.pop(0)
1324 if n[0] in seen:
1325 if n[0] in seen:
1325 continue
1326 continue
1326
1327
1327 self.ui.debug(_("examining %s:%s\n")
1328 self.ui.debug(_("examining %s:%s\n")
1328 % (short(n[0]), short(n[1])))
1329 % (short(n[0]), short(n[1])))
1329 if n[0] == nullid: # found the end of the branch
1330 if n[0] == nullid: # found the end of the branch
1330 pass
1331 pass
1331 elif n in seenbranch:
1332 elif n in seenbranch:
1332 self.ui.debug(_("branch already found\n"))
1333 self.ui.debug(_("branch already found\n"))
1333 continue
1334 continue
1334 elif n[1] and n[1] in m: # do we know the base?
1335 elif n[1] and n[1] in m: # do we know the base?
1335 self.ui.debug(_("found incomplete branch %s:%s\n")
1336 self.ui.debug(_("found incomplete branch %s:%s\n")
1336 % (short(n[0]), short(n[1])))
1337 % (short(n[0]), short(n[1])))
1337 search.append(n) # schedule branch range for scanning
1338 search.append(n) # schedule branch range for scanning
1338 seenbranch[n] = 1
1339 seenbranch[n] = 1
1339 else:
1340 else:
1340 if n[1] not in seen and n[1] not in fetch:
1341 if n[1] not in seen and n[1] not in fetch:
1341 if n[2] in m and n[3] in m:
1342 if n[2] in m and n[3] in m:
1342 self.ui.debug(_("found new changeset %s\n") %
1343 self.ui.debug(_("found new changeset %s\n") %
1343 short(n[1]))
1344 short(n[1]))
1344 fetch[n[1]] = 1 # earliest unknown
1345 fetch[n[1]] = 1 # earliest unknown
1345 for p in n[2:4]:
1346 for p in n[2:4]:
1346 if p in m:
1347 if p in m:
1347 base[p] = 1 # latest known
1348 base[p] = 1 # latest known
1348
1349
1349 for p in n[2:4]:
1350 for p in n[2:4]:
1350 if p not in req and p not in m:
1351 if p not in req and p not in m:
1351 r.append(p)
1352 r.append(p)
1352 req[p] = 1
1353 req[p] = 1
1353 seen[n[0]] = 1
1354 seen[n[0]] = 1
1354
1355
1355 if r:
1356 if r:
1356 reqcnt += 1
1357 reqcnt += 1
1357 self.ui.debug(_("request %d: %s\n") %
1358 self.ui.debug(_("request %d: %s\n") %
1358 (reqcnt, " ".join(map(short, r))))
1359 (reqcnt, " ".join(map(short, r))))
1359 for p in xrange(0, len(r), 10):
1360 for p in xrange(0, len(r), 10):
1360 for b in remote.branches(r[p:p+10]):
1361 for b in remote.branches(r[p:p+10]):
1361 self.ui.debug(_("received %s:%s\n") %
1362 self.ui.debug(_("received %s:%s\n") %
1362 (short(b[0]), short(b[1])))
1363 (short(b[0]), short(b[1])))
1363 unknown.append(b)
1364 unknown.append(b)
1364
1365
1365 # do binary search on the branches we found
1366 # do binary search on the branches we found
1366 while search:
1367 while search:
1367 n = search.pop(0)
1368 n = search.pop(0)
1368 reqcnt += 1
1369 reqcnt += 1
1369 l = remote.between([(n[0], n[1])])[0]
1370 l = remote.between([(n[0], n[1])])[0]
1370 l.append(n[1])
1371 l.append(n[1])
1371 p = n[0]
1372 p = n[0]
1372 f = 1
1373 f = 1
1373 for i in l:
1374 for i in l:
1374 self.ui.debug(_("narrowing %d:%d %s\n") % (f, len(l), short(i)))
1375 self.ui.debug(_("narrowing %d:%d %s\n") % (f, len(l), short(i)))
1375 if i in m:
1376 if i in m:
1376 if f <= 2:
1377 if f <= 2:
1377 self.ui.debug(_("found new branch changeset %s\n") %
1378 self.ui.debug(_("found new branch changeset %s\n") %
1378 short(p))
1379 short(p))
1379 fetch[p] = 1
1380 fetch[p] = 1
1380 base[i] = 1
1381 base[i] = 1
1381 else:
1382 else:
1382 self.ui.debug(_("narrowed branch search to %s:%s\n")
1383 self.ui.debug(_("narrowed branch search to %s:%s\n")
1383 % (short(p), short(i)))
1384 % (short(p), short(i)))
1384 search.append((p, i))
1385 search.append((p, i))
1385 break
1386 break
1386 p, f = i, f * 2
1387 p, f = i, f * 2
1387
1388
1388 # sanity check our fetch list
1389 # sanity check our fetch list
1389 for f in fetch.keys():
1390 for f in fetch.keys():
1390 if f in m:
1391 if f in m:
1391 raise repo.RepoError(_("already have changeset ") + short(f[:4]))
1392 raise repo.RepoError(_("already have changeset ") + short(f[:4]))
1392
1393
1393 if base.keys() == [nullid]:
1394 if base.keys() == [nullid]:
1394 if force:
1395 if force:
1395 self.ui.warn(_("warning: repository is unrelated\n"))
1396 self.ui.warn(_("warning: repository is unrelated\n"))
1396 else:
1397 else:
1397 raise util.Abort(_("repository is unrelated"))
1398 raise util.Abort(_("repository is unrelated"))
1398
1399
1399 self.ui.debug(_("found new changesets starting at ") +
1400 self.ui.debug(_("found new changesets starting at ") +
1400 " ".join([short(f) for f in fetch]) + "\n")
1401 " ".join([short(f) for f in fetch]) + "\n")
1401
1402
1402 self.ui.debug(_("%d total queries\n") % reqcnt)
1403 self.ui.debug(_("%d total queries\n") % reqcnt)
1403
1404
1404 return fetch.keys()
1405 return fetch.keys()
1405
1406
1406 def findoutgoing(self, remote, base=None, heads=None, force=False):
1407 def findoutgoing(self, remote, base=None, heads=None, force=False):
1407 """Return list of nodes that are roots of subsets not in remote
1408 """Return list of nodes that are roots of subsets not in remote
1408
1409
1409 If base dict is specified, assume that these nodes and their parents
1410 If base dict is specified, assume that these nodes and their parents
1410 exist on the remote side.
1411 exist on the remote side.
1411 If a list of heads is specified, return only nodes which are heads
1412 If a list of heads is specified, return only nodes which are heads
1412 or ancestors of these heads, and return a second element which
1413 or ancestors of these heads, and return a second element which
1413 contains all remote heads which get new children.
1414 contains all remote heads which get new children.
1414 """
1415 """
1415 if base == None:
1416 if base == None:
1416 base = {}
1417 base = {}
1417 self.findincoming(remote, base, heads, force=force)
1418 self.findincoming(remote, base, heads, force=force)
1418
1419
1419 self.ui.debug(_("common changesets up to ")
1420 self.ui.debug(_("common changesets up to ")
1420 + " ".join(map(short, base.keys())) + "\n")
1421 + " ".join(map(short, base.keys())) + "\n")
1421
1422
1422 remain = dict.fromkeys(self.changelog.nodemap)
1423 remain = dict.fromkeys(self.changelog.nodemap)
1423
1424
1424 # prune everything remote has from the tree
1425 # prune everything remote has from the tree
1425 del remain[nullid]
1426 del remain[nullid]
1426 remove = base.keys()
1427 remove = base.keys()
1427 while remove:
1428 while remove:
1428 n = remove.pop(0)
1429 n = remove.pop(0)
1429 if n in remain:
1430 if n in remain:
1430 del remain[n]
1431 del remain[n]
1431 for p in self.changelog.parents(n):
1432 for p in self.changelog.parents(n):
1432 remove.append(p)
1433 remove.append(p)
1433
1434
1434 # find every node whose parents have been pruned
1435 # find every node whose parents have been pruned
1435 subset = []
1436 subset = []
1436 # find every remote head that will get new children
1437 # find every remote head that will get new children
1437 updated_heads = {}
1438 updated_heads = {}
1438 for n in remain:
1439 for n in remain:
1439 p1, p2 = self.changelog.parents(n)
1440 p1, p2 = self.changelog.parents(n)
1440 if p1 not in remain and p2 not in remain:
1441 if p1 not in remain and p2 not in remain:
1441 subset.append(n)
1442 subset.append(n)
1442 if heads:
1443 if heads:
1443 if p1 in heads:
1444 if p1 in heads:
1444 updated_heads[p1] = True
1445 updated_heads[p1] = True
1445 if p2 in heads:
1446 if p2 in heads:
1446 updated_heads[p2] = True
1447 updated_heads[p2] = True
1447
1448
1448 # this is the set of all roots we have to push
1449 # this is the set of all roots we have to push
1449 if heads:
1450 if heads:
1450 return subset, updated_heads.keys()
1451 return subset, updated_heads.keys()
1451 else:
1452 else:
1452 return subset
1453 return subset
1453
1454
1454 def pull(self, remote, heads=None, force=False):
1455 def pull(self, remote, heads=None, force=False):
1455 lock = self.lock()
1456 lock = self.lock()
1456 try:
1457 try:
1457 fetch = self.findincoming(remote, heads=heads, force=force)
1458 fetch = self.findincoming(remote, heads=heads, force=force)
1458 if fetch == [nullid]:
1459 if fetch == [nullid]:
1459 self.ui.status(_("requesting all changes\n"))
1460 self.ui.status(_("requesting all changes\n"))
1460
1461
1461 if not fetch:
1462 if not fetch:
1462 self.ui.status(_("no changes found\n"))
1463 self.ui.status(_("no changes found\n"))
1463 return 0
1464 return 0
1464
1465
1465 if heads is None:
1466 if heads is None:
1466 cg = remote.changegroup(fetch, 'pull')
1467 cg = remote.changegroup(fetch, 'pull')
1467 else:
1468 else:
1468 if 'changegroupsubset' not in remote.capabilities:
1469 if 'changegroupsubset' not in remote.capabilities:
1469 raise util.Abort(_("Partial pull cannot be done because other repository doesn't support changegroupsubset."))
1470 raise util.Abort(_("Partial pull cannot be done because other repository doesn't support changegroupsubset."))
1470 cg = remote.changegroupsubset(fetch, heads, 'pull')
1471 cg = remote.changegroupsubset(fetch, heads, 'pull')
1471 return self.addchangegroup(cg, 'pull', remote.url())
1472 return self.addchangegroup(cg, 'pull', remote.url())
1472 finally:
1473 finally:
1473 del lock
1474 del lock
1474
1475
1475 def push(self, remote, force=False, revs=None):
1476 def push(self, remote, force=False, revs=None):
1476 # there are two ways to push to remote repo:
1477 # there are two ways to push to remote repo:
1477 #
1478 #
1478 # addchangegroup assumes local user can lock remote
1479 # addchangegroup assumes local user can lock remote
1479 # repo (local filesystem, old ssh servers).
1480 # repo (local filesystem, old ssh servers).
1480 #
1481 #
1481 # unbundle assumes local user cannot lock remote repo (new ssh
1482 # unbundle assumes local user cannot lock remote repo (new ssh
1482 # servers, http servers).
1483 # servers, http servers).
1483
1484
1484 if remote.capable('unbundle'):
1485 if remote.capable('unbundle'):
1485 return self.push_unbundle(remote, force, revs)
1486 return self.push_unbundle(remote, force, revs)
1486 return self.push_addchangegroup(remote, force, revs)
1487 return self.push_addchangegroup(remote, force, revs)
1487
1488
1488 def prepush(self, remote, force, revs):
1489 def prepush(self, remote, force, revs):
1489 base = {}
1490 base = {}
1490 remote_heads = remote.heads()
1491 remote_heads = remote.heads()
1491 inc = self.findincoming(remote, base, remote_heads, force=force)
1492 inc = self.findincoming(remote, base, remote_heads, force=force)
1492
1493
1493 update, updated_heads = self.findoutgoing(remote, base, remote_heads)
1494 update, updated_heads = self.findoutgoing(remote, base, remote_heads)
1494 if revs is not None:
1495 if revs is not None:
1495 msng_cl, bases, heads = self.changelog.nodesbetween(update, revs)
1496 msng_cl, bases, heads = self.changelog.nodesbetween(update, revs)
1496 else:
1497 else:
1497 bases, heads = update, self.changelog.heads()
1498 bases, heads = update, self.changelog.heads()
1498
1499
1499 if not bases:
1500 if not bases:
1500 self.ui.status(_("no changes found\n"))
1501 self.ui.status(_("no changes found\n"))
1501 return None, 1
1502 return None, 1
1502 elif not force:
1503 elif not force:
1503 # check if we're creating new remote heads
1504 # check if we're creating new remote heads
1504 # to be a remote head after push, node must be either
1505 # to be a remote head after push, node must be either
1505 # - unknown locally
1506 # - unknown locally
1506 # - a local outgoing head descended from update
1507 # - a local outgoing head descended from update
1507 # - a remote head that's known locally and not
1508 # - a remote head that's known locally and not
1508 # ancestral to an outgoing head
1509 # ancestral to an outgoing head
1509
1510
1510 warn = 0
1511 warn = 0
1511
1512
1512 if remote_heads == [nullid]:
1513 if remote_heads == [nullid]:
1513 warn = 0
1514 warn = 0
1514 elif not revs and len(heads) > len(remote_heads):
1515 elif not revs and len(heads) > len(remote_heads):
1515 warn = 1
1516 warn = 1
1516 else:
1517 else:
1517 newheads = list(heads)
1518 newheads = list(heads)
1518 for r in remote_heads:
1519 for r in remote_heads:
1519 if r in self.changelog.nodemap:
1520 if r in self.changelog.nodemap:
1520 desc = self.changelog.heads(r, heads)
1521 desc = self.changelog.heads(r, heads)
1521 l = [h for h in heads if h in desc]
1522 l = [h for h in heads if h in desc]
1522 if not l:
1523 if not l:
1523 newheads.append(r)
1524 newheads.append(r)
1524 else:
1525 else:
1525 newheads.append(r)
1526 newheads.append(r)
1526 if len(newheads) > len(remote_heads):
1527 if len(newheads) > len(remote_heads):
1527 warn = 1
1528 warn = 1
1528
1529
1529 if warn:
1530 if warn:
1530 self.ui.warn(_("abort: push creates new remote heads!\n"))
1531 self.ui.warn(_("abort: push creates new remote heads!\n"))
1531 self.ui.status(_("(did you forget to merge?"
1532 self.ui.status(_("(did you forget to merge?"
1532 " use push -f to force)\n"))
1533 " use push -f to force)\n"))
1533 return None, 0
1534 return None, 0
1534 elif inc:
1535 elif inc:
1535 self.ui.warn(_("note: unsynced remote changes!\n"))
1536 self.ui.warn(_("note: unsynced remote changes!\n"))
1536
1537
1537
1538
1538 if revs is None:
1539 if revs is None:
1539 cg = self.changegroup(update, 'push')
1540 cg = self.changegroup(update, 'push')
1540 else:
1541 else:
1541 cg = self.changegroupsubset(update, revs, 'push')
1542 cg = self.changegroupsubset(update, revs, 'push')
1542 return cg, remote_heads
1543 return cg, remote_heads
1543
1544
1544 def push_addchangegroup(self, remote, force, revs):
1545 def push_addchangegroup(self, remote, force, revs):
1545 lock = remote.lock()
1546 lock = remote.lock()
1546 try:
1547 try:
1547 ret = self.prepush(remote, force, revs)
1548 ret = self.prepush(remote, force, revs)
1548 if ret[0] is not None:
1549 if ret[0] is not None:
1549 cg, remote_heads = ret
1550 cg, remote_heads = ret
1550 return remote.addchangegroup(cg, 'push', self.url())
1551 return remote.addchangegroup(cg, 'push', self.url())
1551 return ret[1]
1552 return ret[1]
1552 finally:
1553 finally:
1553 del lock
1554 del lock
1554
1555
1555 def push_unbundle(self, remote, force, revs):
1556 def push_unbundle(self, remote, force, revs):
1556 # local repo finds heads on server, finds out what revs it
1557 # local repo finds heads on server, finds out what revs it
1557 # must push. once revs transferred, if server finds it has
1558 # must push. once revs transferred, if server finds it has
1558 # different heads (someone else won commit/push race), server
1559 # different heads (someone else won commit/push race), server
1559 # aborts.
1560 # aborts.
1560
1561
1561 ret = self.prepush(remote, force, revs)
1562 ret = self.prepush(remote, force, revs)
1562 if ret[0] is not None:
1563 if ret[0] is not None:
1563 cg, remote_heads = ret
1564 cg, remote_heads = ret
1564 if force: remote_heads = ['force']
1565 if force: remote_heads = ['force']
1565 return remote.unbundle(cg, remote_heads, 'push')
1566 return remote.unbundle(cg, remote_heads, 'push')
1566 return ret[1]
1567 return ret[1]
1567
1568
1568 def changegroupinfo(self, nodes, source):
1569 def changegroupinfo(self, nodes, source):
1569 if self.ui.verbose or source == 'bundle':
1570 if self.ui.verbose or source == 'bundle':
1570 self.ui.status(_("%d changesets found\n") % len(nodes))
1571 self.ui.status(_("%d changesets found\n") % len(nodes))
1571 if self.ui.debugflag:
1572 if self.ui.debugflag:
1572 self.ui.debug(_("List of changesets:\n"))
1573 self.ui.debug(_("List of changesets:\n"))
1573 for node in nodes:
1574 for node in nodes:
1574 self.ui.debug("%s\n" % hex(node))
1575 self.ui.debug("%s\n" % hex(node))
1575
1576
1576 def changegroupsubset(self, bases, heads, source, extranodes=None):
1577 def changegroupsubset(self, bases, heads, source, extranodes=None):
1577 """This function generates a changegroup consisting of all the nodes
1578 """This function generates a changegroup consisting of all the nodes
1578 that are descendents of any of the bases, and ancestors of any of
1579 that are descendents of any of the bases, and ancestors of any of
1579 the heads.
1580 the heads.
1580
1581
1581 It is fairly complex as determining which filenodes and which
1582 It is fairly complex as determining which filenodes and which
1582 manifest nodes need to be included for the changeset to be complete
1583 manifest nodes need to be included for the changeset to be complete
1583 is non-trivial.
1584 is non-trivial.
1584
1585
1585 Another wrinkle is doing the reverse, figuring out which changeset in
1586 Another wrinkle is doing the reverse, figuring out which changeset in
1586 the changegroup a particular filenode or manifestnode belongs to.
1587 the changegroup a particular filenode or manifestnode belongs to.
1587
1588
1588 The caller can specify some nodes that must be included in the
1589 The caller can specify some nodes that must be included in the
1589 changegroup using the extranodes argument. It should be a dict
1590 changegroup using the extranodes argument. It should be a dict
1590 where the keys are the filenames (or 1 for the manifest), and the
1591 where the keys are the filenames (or 1 for the manifest), and the
1591 values are lists of (node, linknode) tuples, where node is a wanted
1592 values are lists of (node, linknode) tuples, where node is a wanted
1592 node and linknode is the changelog node that should be transmitted as
1593 node and linknode is the changelog node that should be transmitted as
1593 the linkrev.
1594 the linkrev.
1594 """
1595 """
1595
1596
1596 self.hook('preoutgoing', throw=True, source=source)
1597 self.hook('preoutgoing', throw=True, source=source)
1597
1598
1598 # Set up some initial variables
1599 # Set up some initial variables
1599 # Make it easy to refer to self.changelog
1600 # Make it easy to refer to self.changelog
1600 cl = self.changelog
1601 cl = self.changelog
1601 # msng is short for missing - compute the list of changesets in this
1602 # msng is short for missing - compute the list of changesets in this
1602 # changegroup.
1603 # changegroup.
1603 msng_cl_lst, bases, heads = cl.nodesbetween(bases, heads)
1604 msng_cl_lst, bases, heads = cl.nodesbetween(bases, heads)
1604 self.changegroupinfo(msng_cl_lst, source)
1605 self.changegroupinfo(msng_cl_lst, source)
1605 # Some bases may turn out to be superfluous, and some heads may be
1606 # Some bases may turn out to be superfluous, and some heads may be
1606 # too. nodesbetween will return the minimal set of bases and heads
1607 # too. nodesbetween will return the minimal set of bases and heads
1607 # necessary to re-create the changegroup.
1608 # necessary to re-create the changegroup.
1608
1609
1609 # Known heads are the list of heads that it is assumed the recipient
1610 # Known heads are the list of heads that it is assumed the recipient
1610 # of this changegroup will know about.
1611 # of this changegroup will know about.
1611 knownheads = {}
1612 knownheads = {}
1612 # We assume that all parents of bases are known heads.
1613 # We assume that all parents of bases are known heads.
1613 for n in bases:
1614 for n in bases:
1614 for p in cl.parents(n):
1615 for p in cl.parents(n):
1615 if p != nullid:
1616 if p != nullid:
1616 knownheads[p] = 1
1617 knownheads[p] = 1
1617 knownheads = knownheads.keys()
1618 knownheads = knownheads.keys()
1618 if knownheads:
1619 if knownheads:
1619 # Now that we know what heads are known, we can compute which
1620 # Now that we know what heads are known, we can compute which
1620 # changesets are known. The recipient must know about all
1621 # changesets are known. The recipient must know about all
1621 # changesets required to reach the known heads from the null
1622 # changesets required to reach the known heads from the null
1622 # changeset.
1623 # changeset.
1623 has_cl_set, junk, junk = cl.nodesbetween(None, knownheads)
1624 has_cl_set, junk, junk = cl.nodesbetween(None, knownheads)
1624 junk = None
1625 junk = None
1625 # Transform the list into an ersatz set.
1626 # Transform the list into an ersatz set.
1626 has_cl_set = dict.fromkeys(has_cl_set)
1627 has_cl_set = dict.fromkeys(has_cl_set)
1627 else:
1628 else:
1628 # If there were no known heads, the recipient cannot be assumed to
1629 # If there were no known heads, the recipient cannot be assumed to
1629 # know about any changesets.
1630 # know about any changesets.
1630 has_cl_set = {}
1631 has_cl_set = {}
1631
1632
1632 # Make it easy to refer to self.manifest
1633 # Make it easy to refer to self.manifest
1633 mnfst = self.manifest
1634 mnfst = self.manifest
1634 # We don't know which manifests are missing yet
1635 # We don't know which manifests are missing yet
1635 msng_mnfst_set = {}
1636 msng_mnfst_set = {}
1636 # Nor do we know which filenodes are missing.
1637 # Nor do we know which filenodes are missing.
1637 msng_filenode_set = {}
1638 msng_filenode_set = {}
1638
1639
1639 junk = mnfst.index[mnfst.count() - 1] # Get around a bug in lazyindex
1640 junk = mnfst.index[mnfst.count() - 1] # Get around a bug in lazyindex
1640 junk = None
1641 junk = None
1641
1642
1642 # A changeset always belongs to itself, so the changenode lookup
1643 # A changeset always belongs to itself, so the changenode lookup
1643 # function for a changenode is identity.
1644 # function for a changenode is identity.
1644 def identity(x):
1645 def identity(x):
1645 return x
1646 return x
1646
1647
1647 # A function generating function. Sets up an environment for the
1648 # A function generating function. Sets up an environment for the
1648 # inner function.
1649 # inner function.
1649 def cmp_by_rev_func(revlog):
1650 def cmp_by_rev_func(revlog):
1650 # Compare two nodes by their revision number in the environment's
1651 # Compare two nodes by their revision number in the environment's
1651 # revision history. Since the revision number both represents the
1652 # revision history. Since the revision number both represents the
1652 # most efficient order to read the nodes in, and represents a
1653 # most efficient order to read the nodes in, and represents a
1653 # topological sorting of the nodes, this function is often useful.
1654 # topological sorting of the nodes, this function is often useful.
1654 def cmp_by_rev(a, b):
1655 def cmp_by_rev(a, b):
1655 return cmp(revlog.rev(a), revlog.rev(b))
1656 return cmp(revlog.rev(a), revlog.rev(b))
1656 return cmp_by_rev
1657 return cmp_by_rev
1657
1658
1658 # If we determine that a particular file or manifest node must be a
1659 # If we determine that a particular file or manifest node must be a
1659 # node that the recipient of the changegroup will already have, we can
1660 # node that the recipient of the changegroup will already have, we can
1660 # also assume the recipient will have all the parents. This function
1661 # also assume the recipient will have all the parents. This function
1661 # prunes them from the set of missing nodes.
1662 # prunes them from the set of missing nodes.
1662 def prune_parents(revlog, hasset, msngset):
1663 def prune_parents(revlog, hasset, msngset):
1663 haslst = hasset.keys()
1664 haslst = hasset.keys()
1664 haslst.sort(cmp_by_rev_func(revlog))
1665 haslst.sort(cmp_by_rev_func(revlog))
1665 for node in haslst:
1666 for node in haslst:
1666 parentlst = [p for p in revlog.parents(node) if p != nullid]
1667 parentlst = [p for p in revlog.parents(node) if p != nullid]
1667 while parentlst:
1668 while parentlst:
1668 n = parentlst.pop()
1669 n = parentlst.pop()
1669 if n not in hasset:
1670 if n not in hasset:
1670 hasset[n] = 1
1671 hasset[n] = 1
1671 p = [p for p in revlog.parents(n) if p != nullid]
1672 p = [p for p in revlog.parents(n) if p != nullid]
1672 parentlst.extend(p)
1673 parentlst.extend(p)
1673 for n in hasset:
1674 for n in hasset:
1674 msngset.pop(n, None)
1675 msngset.pop(n, None)
1675
1676
1676 # This is a function generating function used to set up an environment
1677 # This is a function generating function used to set up an environment
1677 # for the inner function to execute in.
1678 # for the inner function to execute in.
1678 def manifest_and_file_collector(changedfileset):
1679 def manifest_and_file_collector(changedfileset):
1679 # This is an information gathering function that gathers
1680 # This is an information gathering function that gathers
1680 # information from each changeset node that goes out as part of
1681 # information from each changeset node that goes out as part of
1681 # the changegroup. The information gathered is a list of which
1682 # the changegroup. The information gathered is a list of which
1682 # manifest nodes are potentially required (the recipient may
1683 # manifest nodes are potentially required (the recipient may
1683 # already have them) and total list of all files which were
1684 # already have them) and total list of all files which were
1684 # changed in any changeset in the changegroup.
1685 # changed in any changeset in the changegroup.
1685 #
1686 #
1686 # We also remember the first changenode we saw any manifest
1687 # We also remember the first changenode we saw any manifest
1687 # referenced by so we can later determine which changenode 'owns'
1688 # referenced by so we can later determine which changenode 'owns'
1688 # the manifest.
1689 # the manifest.
1689 def collect_manifests_and_files(clnode):
1690 def collect_manifests_and_files(clnode):
1690 c = cl.read(clnode)
1691 c = cl.read(clnode)
1691 for f in c[3]:
1692 for f in c[3]:
1692 # This is to make sure we only have one instance of each
1693 # This is to make sure we only have one instance of each
1693 # filename string for each filename.
1694 # filename string for each filename.
1694 changedfileset.setdefault(f, f)
1695 changedfileset.setdefault(f, f)
1695 msng_mnfst_set.setdefault(c[0], clnode)
1696 msng_mnfst_set.setdefault(c[0], clnode)
1696 return collect_manifests_and_files
1697 return collect_manifests_and_files
1697
1698
1698 # Figure out which manifest nodes (of the ones we think might be part
1699 # Figure out which manifest nodes (of the ones we think might be part
1699 # of the changegroup) the recipient must know about and remove them
1700 # of the changegroup) the recipient must know about and remove them
1700 # from the changegroup.
1701 # from the changegroup.
1701 def prune_manifests():
1702 def prune_manifests():
1702 has_mnfst_set = {}
1703 has_mnfst_set = {}
1703 for n in msng_mnfst_set:
1704 for n in msng_mnfst_set:
1704 # If a 'missing' manifest thinks it belongs to a changenode
1705 # If a 'missing' manifest thinks it belongs to a changenode
1705 # the recipient is assumed to have, obviously the recipient
1706 # the recipient is assumed to have, obviously the recipient
1706 # must have that manifest.
1707 # must have that manifest.
1707 linknode = cl.node(mnfst.linkrev(n))
1708 linknode = cl.node(mnfst.linkrev(n))
1708 if linknode in has_cl_set:
1709 if linknode in has_cl_set:
1709 has_mnfst_set[n] = 1
1710 has_mnfst_set[n] = 1
1710 prune_parents(mnfst, has_mnfst_set, msng_mnfst_set)
1711 prune_parents(mnfst, has_mnfst_set, msng_mnfst_set)
1711
1712
1712 # Use the information collected in collect_manifests_and_files to say
1713 # Use the information collected in collect_manifests_and_files to say
1713 # which changenode any manifestnode belongs to.
1714 # which changenode any manifestnode belongs to.
1714 def lookup_manifest_link(mnfstnode):
1715 def lookup_manifest_link(mnfstnode):
1715 return msng_mnfst_set[mnfstnode]
1716 return msng_mnfst_set[mnfstnode]
1716
1717
1717 # A function generating function that sets up the initial environment
1718 # A function generating function that sets up the initial environment
1718 # the inner function.
1719 # the inner function.
1719 def filenode_collector(changedfiles):
1720 def filenode_collector(changedfiles):
1720 next_rev = [0]
1721 next_rev = [0]
1721 # This gathers information from each manifestnode included in the
1722 # This gathers information from each manifestnode included in the
1722 # changegroup about which filenodes the manifest node references
1723 # changegroup about which filenodes the manifest node references
1723 # so we can include those in the changegroup too.
1724 # so we can include those in the changegroup too.
1724 #
1725 #
1725 # It also remembers which changenode each filenode belongs to. It
1726 # It also remembers which changenode each filenode belongs to. It
1726 # does this by assuming the a filenode belongs to the changenode
1727 # does this by assuming the a filenode belongs to the changenode
1727 # the first manifest that references it belongs to.
1728 # the first manifest that references it belongs to.
1728 def collect_msng_filenodes(mnfstnode):
1729 def collect_msng_filenodes(mnfstnode):
1729 r = mnfst.rev(mnfstnode)
1730 r = mnfst.rev(mnfstnode)
1730 if r == next_rev[0]:
1731 if r == next_rev[0]:
1731 # If the last rev we looked at was the one just previous,
1732 # If the last rev we looked at was the one just previous,
1732 # we only need to see a diff.
1733 # we only need to see a diff.
1733 deltamf = mnfst.readdelta(mnfstnode)
1734 deltamf = mnfst.readdelta(mnfstnode)
1734 # For each line in the delta
1735 # For each line in the delta
1735 for f, fnode in deltamf.items():
1736 for f, fnode in deltamf.items():
1736 f = changedfiles.get(f, None)
1737 f = changedfiles.get(f, None)
1737 # And if the file is in the list of files we care
1738 # And if the file is in the list of files we care
1738 # about.
1739 # about.
1739 if f is not None:
1740 if f is not None:
1740 # Get the changenode this manifest belongs to
1741 # Get the changenode this manifest belongs to
1741 clnode = msng_mnfst_set[mnfstnode]
1742 clnode = msng_mnfst_set[mnfstnode]
1742 # Create the set of filenodes for the file if
1743 # Create the set of filenodes for the file if
1743 # there isn't one already.
1744 # there isn't one already.
1744 ndset = msng_filenode_set.setdefault(f, {})
1745 ndset = msng_filenode_set.setdefault(f, {})
1745 # And set the filenode's changelog node to the
1746 # And set the filenode's changelog node to the
1746 # manifest's if it hasn't been set already.
1747 # manifest's if it hasn't been set already.
1747 ndset.setdefault(fnode, clnode)
1748 ndset.setdefault(fnode, clnode)
1748 else:
1749 else:
1749 # Otherwise we need a full manifest.
1750 # Otherwise we need a full manifest.
1750 m = mnfst.read(mnfstnode)
1751 m = mnfst.read(mnfstnode)
1751 # For every file in we care about.
1752 # For every file in we care about.
1752 for f in changedfiles:
1753 for f in changedfiles:
1753 fnode = m.get(f, None)
1754 fnode = m.get(f, None)
1754 # If it's in the manifest
1755 # If it's in the manifest
1755 if fnode is not None:
1756 if fnode is not None:
1756 # See comments above.
1757 # See comments above.
1757 clnode = msng_mnfst_set[mnfstnode]
1758 clnode = msng_mnfst_set[mnfstnode]
1758 ndset = msng_filenode_set.setdefault(f, {})
1759 ndset = msng_filenode_set.setdefault(f, {})
1759 ndset.setdefault(fnode, clnode)
1760 ndset.setdefault(fnode, clnode)
1760 # Remember the revision we hope to see next.
1761 # Remember the revision we hope to see next.
1761 next_rev[0] = r + 1
1762 next_rev[0] = r + 1
1762 return collect_msng_filenodes
1763 return collect_msng_filenodes
1763
1764
1764 # We have a list of filenodes we think we need for a file, lets remove
1765 # We have a list of filenodes we think we need for a file, lets remove
1765 # all those we now the recipient must have.
1766 # all those we now the recipient must have.
1766 def prune_filenodes(f, filerevlog):
1767 def prune_filenodes(f, filerevlog):
1767 msngset = msng_filenode_set[f]
1768 msngset = msng_filenode_set[f]
1768 hasset = {}
1769 hasset = {}
1769 # If a 'missing' filenode thinks it belongs to a changenode we
1770 # If a 'missing' filenode thinks it belongs to a changenode we
1770 # assume the recipient must have, then the recipient must have
1771 # assume the recipient must have, then the recipient must have
1771 # that filenode.
1772 # that filenode.
1772 for n in msngset:
1773 for n in msngset:
1773 clnode = cl.node(filerevlog.linkrev(n))
1774 clnode = cl.node(filerevlog.linkrev(n))
1774 if clnode in has_cl_set:
1775 if clnode in has_cl_set:
1775 hasset[n] = 1
1776 hasset[n] = 1
1776 prune_parents(filerevlog, hasset, msngset)
1777 prune_parents(filerevlog, hasset, msngset)
1777
1778
1778 # A function generator function that sets up the a context for the
1779 # A function generator function that sets up the a context for the
1779 # inner function.
1780 # inner function.
1780 def lookup_filenode_link_func(fname):
1781 def lookup_filenode_link_func(fname):
1781 msngset = msng_filenode_set[fname]
1782 msngset = msng_filenode_set[fname]
1782 # Lookup the changenode the filenode belongs to.
1783 # Lookup the changenode the filenode belongs to.
1783 def lookup_filenode_link(fnode):
1784 def lookup_filenode_link(fnode):
1784 return msngset[fnode]
1785 return msngset[fnode]
1785 return lookup_filenode_link
1786 return lookup_filenode_link
1786
1787
1787 # Add the nodes that were explicitly requested.
1788 # Add the nodes that were explicitly requested.
1788 def add_extra_nodes(name, nodes):
1789 def add_extra_nodes(name, nodes):
1789 if not extranodes or name not in extranodes:
1790 if not extranodes or name not in extranodes:
1790 return
1791 return
1791
1792
1792 for node, linknode in extranodes[name]:
1793 for node, linknode in extranodes[name]:
1793 if node not in nodes:
1794 if node not in nodes:
1794 nodes[node] = linknode
1795 nodes[node] = linknode
1795
1796
1796 # Now that we have all theses utility functions to help out and
1797 # Now that we have all theses utility functions to help out and
1797 # logically divide up the task, generate the group.
1798 # logically divide up the task, generate the group.
1798 def gengroup():
1799 def gengroup():
1799 # The set of changed files starts empty.
1800 # The set of changed files starts empty.
1800 changedfiles = {}
1801 changedfiles = {}
1801 # Create a changenode group generator that will call our functions
1802 # Create a changenode group generator that will call our functions
1802 # back to lookup the owning changenode and collect information.
1803 # back to lookup the owning changenode and collect information.
1803 group = cl.group(msng_cl_lst, identity,
1804 group = cl.group(msng_cl_lst, identity,
1804 manifest_and_file_collector(changedfiles))
1805 manifest_and_file_collector(changedfiles))
1805 for chnk in group:
1806 for chnk in group:
1806 yield chnk
1807 yield chnk
1807
1808
1808 # The list of manifests has been collected by the generator
1809 # The list of manifests has been collected by the generator
1809 # calling our functions back.
1810 # calling our functions back.
1810 prune_manifests()
1811 prune_manifests()
1811 add_extra_nodes(1, msng_mnfst_set)
1812 add_extra_nodes(1, msng_mnfst_set)
1812 msng_mnfst_lst = msng_mnfst_set.keys()
1813 msng_mnfst_lst = msng_mnfst_set.keys()
1813 # Sort the manifestnodes by revision number.
1814 # Sort the manifestnodes by revision number.
1814 msng_mnfst_lst.sort(cmp_by_rev_func(mnfst))
1815 msng_mnfst_lst.sort(cmp_by_rev_func(mnfst))
1815 # Create a generator for the manifestnodes that calls our lookup
1816 # Create a generator for the manifestnodes that calls our lookup
1816 # and data collection functions back.
1817 # and data collection functions back.
1817 group = mnfst.group(msng_mnfst_lst, lookup_manifest_link,
1818 group = mnfst.group(msng_mnfst_lst, lookup_manifest_link,
1818 filenode_collector(changedfiles))
1819 filenode_collector(changedfiles))
1819 for chnk in group:
1820 for chnk in group:
1820 yield chnk
1821 yield chnk
1821
1822
1822 # These are no longer needed, dereference and toss the memory for
1823 # These are no longer needed, dereference and toss the memory for
1823 # them.
1824 # them.
1824 msng_mnfst_lst = None
1825 msng_mnfst_lst = None
1825 msng_mnfst_set.clear()
1826 msng_mnfst_set.clear()
1826
1827
1827 if extranodes:
1828 if extranodes:
1828 for fname in extranodes:
1829 for fname in extranodes:
1829 if isinstance(fname, int):
1830 if isinstance(fname, int):
1830 continue
1831 continue
1831 add_extra_nodes(fname,
1832 add_extra_nodes(fname,
1832 msng_filenode_set.setdefault(fname, {}))
1833 msng_filenode_set.setdefault(fname, {}))
1833 changedfiles[fname] = 1
1834 changedfiles[fname] = 1
1834 changedfiles = changedfiles.keys()
1835 changedfiles = changedfiles.keys()
1835 changedfiles.sort()
1836 changedfiles.sort()
1836 # Go through all our files in order sorted by name.
1837 # Go through all our files in order sorted by name.
1837 for fname in changedfiles:
1838 for fname in changedfiles:
1838 filerevlog = self.file(fname)
1839 filerevlog = self.file(fname)
1839 if filerevlog.count() == 0:
1840 if filerevlog.count() == 0:
1840 raise util.Abort(_("empty or missing revlog for %s") % fname)
1841 raise util.Abort(_("empty or missing revlog for %s") % fname)
1841 # Toss out the filenodes that the recipient isn't really
1842 # Toss out the filenodes that the recipient isn't really
1842 # missing.
1843 # missing.
1843 if fname in msng_filenode_set:
1844 if fname in msng_filenode_set:
1844 prune_filenodes(fname, filerevlog)
1845 prune_filenodes(fname, filerevlog)
1845 msng_filenode_lst = msng_filenode_set[fname].keys()
1846 msng_filenode_lst = msng_filenode_set[fname].keys()
1846 else:
1847 else:
1847 msng_filenode_lst = []
1848 msng_filenode_lst = []
1848 # If any filenodes are left, generate the group for them,
1849 # If any filenodes are left, generate the group for them,
1849 # otherwise don't bother.
1850 # otherwise don't bother.
1850 if len(msng_filenode_lst) > 0:
1851 if len(msng_filenode_lst) > 0:
1851 yield changegroup.chunkheader(len(fname))
1852 yield changegroup.chunkheader(len(fname))
1852 yield fname
1853 yield fname
1853 # Sort the filenodes by their revision #
1854 # Sort the filenodes by their revision #
1854 msng_filenode_lst.sort(cmp_by_rev_func(filerevlog))
1855 msng_filenode_lst.sort(cmp_by_rev_func(filerevlog))
1855 # Create a group generator and only pass in a changenode
1856 # Create a group generator and only pass in a changenode
1856 # lookup function as we need to collect no information
1857 # lookup function as we need to collect no information
1857 # from filenodes.
1858 # from filenodes.
1858 group = filerevlog.group(msng_filenode_lst,
1859 group = filerevlog.group(msng_filenode_lst,
1859 lookup_filenode_link_func(fname))
1860 lookup_filenode_link_func(fname))
1860 for chnk in group:
1861 for chnk in group:
1861 yield chnk
1862 yield chnk
1862 if fname in msng_filenode_set:
1863 if fname in msng_filenode_set:
1863 # Don't need this anymore, toss it to free memory.
1864 # Don't need this anymore, toss it to free memory.
1864 del msng_filenode_set[fname]
1865 del msng_filenode_set[fname]
1865 # Signal that no more groups are left.
1866 # Signal that no more groups are left.
1866 yield changegroup.closechunk()
1867 yield changegroup.closechunk()
1867
1868
1868 if msng_cl_lst:
1869 if msng_cl_lst:
1869 self.hook('outgoing', node=hex(msng_cl_lst[0]), source=source)
1870 self.hook('outgoing', node=hex(msng_cl_lst[0]), source=source)
1870
1871
1871 return util.chunkbuffer(gengroup())
1872 return util.chunkbuffer(gengroup())
1872
1873
1873 def changegroup(self, basenodes, source):
1874 def changegroup(self, basenodes, source):
1874 """Generate a changegroup of all nodes that we have that a recipient
1875 """Generate a changegroup of all nodes that we have that a recipient
1875 doesn't.
1876 doesn't.
1876
1877
1877 This is much easier than the previous function as we can assume that
1878 This is much easier than the previous function as we can assume that
1878 the recipient has any changenode we aren't sending them."""
1879 the recipient has any changenode we aren't sending them."""
1879
1880
1880 self.hook('preoutgoing', throw=True, source=source)
1881 self.hook('preoutgoing', throw=True, source=source)
1881
1882
1882 cl = self.changelog
1883 cl = self.changelog
1883 nodes = cl.nodesbetween(basenodes, None)[0]
1884 nodes = cl.nodesbetween(basenodes, None)[0]
1884 revset = dict.fromkeys([cl.rev(n) for n in nodes])
1885 revset = dict.fromkeys([cl.rev(n) for n in nodes])
1885 self.changegroupinfo(nodes, source)
1886 self.changegroupinfo(nodes, source)
1886
1887
1887 def identity(x):
1888 def identity(x):
1888 return x
1889 return x
1889
1890
1890 def gennodelst(revlog):
1891 def gennodelst(revlog):
1891 for r in xrange(0, revlog.count()):
1892 for r in xrange(0, revlog.count()):
1892 n = revlog.node(r)
1893 n = revlog.node(r)
1893 if revlog.linkrev(n) in revset:
1894 if revlog.linkrev(n) in revset:
1894 yield n
1895 yield n
1895
1896
1896 def changed_file_collector(changedfileset):
1897 def changed_file_collector(changedfileset):
1897 def collect_changed_files(clnode):
1898 def collect_changed_files(clnode):
1898 c = cl.read(clnode)
1899 c = cl.read(clnode)
1899 for fname in c[3]:
1900 for fname in c[3]:
1900 changedfileset[fname] = 1
1901 changedfileset[fname] = 1
1901 return collect_changed_files
1902 return collect_changed_files
1902
1903
1903 def lookuprevlink_func(revlog):
1904 def lookuprevlink_func(revlog):
1904 def lookuprevlink(n):
1905 def lookuprevlink(n):
1905 return cl.node(revlog.linkrev(n))
1906 return cl.node(revlog.linkrev(n))
1906 return lookuprevlink
1907 return lookuprevlink
1907
1908
1908 def gengroup():
1909 def gengroup():
1909 # construct a list of all changed files
1910 # construct a list of all changed files
1910 changedfiles = {}
1911 changedfiles = {}
1911
1912
1912 for chnk in cl.group(nodes, identity,
1913 for chnk in cl.group(nodes, identity,
1913 changed_file_collector(changedfiles)):
1914 changed_file_collector(changedfiles)):
1914 yield chnk
1915 yield chnk
1915 changedfiles = changedfiles.keys()
1916 changedfiles = changedfiles.keys()
1916 changedfiles.sort()
1917 changedfiles.sort()
1917
1918
1918 mnfst = self.manifest
1919 mnfst = self.manifest
1919 nodeiter = gennodelst(mnfst)
1920 nodeiter = gennodelst(mnfst)
1920 for chnk in mnfst.group(nodeiter, lookuprevlink_func(mnfst)):
1921 for chnk in mnfst.group(nodeiter, lookuprevlink_func(mnfst)):
1921 yield chnk
1922 yield chnk
1922
1923
1923 for fname in changedfiles:
1924 for fname in changedfiles:
1924 filerevlog = self.file(fname)
1925 filerevlog = self.file(fname)
1925 if filerevlog.count() == 0:
1926 if filerevlog.count() == 0:
1926 raise util.Abort(_("empty or missing revlog for %s") % fname)
1927 raise util.Abort(_("empty or missing revlog for %s") % fname)
1927 nodeiter = gennodelst(filerevlog)
1928 nodeiter = gennodelst(filerevlog)
1928 nodeiter = list(nodeiter)
1929 nodeiter = list(nodeiter)
1929 if nodeiter:
1930 if nodeiter:
1930 yield changegroup.chunkheader(len(fname))
1931 yield changegroup.chunkheader(len(fname))
1931 yield fname
1932 yield fname
1932 lookup = lookuprevlink_func(filerevlog)
1933 lookup = lookuprevlink_func(filerevlog)
1933 for chnk in filerevlog.group(nodeiter, lookup):
1934 for chnk in filerevlog.group(nodeiter, lookup):
1934 yield chnk
1935 yield chnk
1935
1936
1936 yield changegroup.closechunk()
1937 yield changegroup.closechunk()
1937
1938
1938 if nodes:
1939 if nodes:
1939 self.hook('outgoing', node=hex(nodes[0]), source=source)
1940 self.hook('outgoing', node=hex(nodes[0]), source=source)
1940
1941
1941 return util.chunkbuffer(gengroup())
1942 return util.chunkbuffer(gengroup())
1942
1943
1943 def addchangegroup(self, source, srctype, url, emptyok=False):
1944 def addchangegroup(self, source, srctype, url, emptyok=False):
1944 """add changegroup to repo.
1945 """add changegroup to repo.
1945
1946
1946 return values:
1947 return values:
1947 - nothing changed or no source: 0
1948 - nothing changed or no source: 0
1948 - more heads than before: 1+added heads (2..n)
1949 - more heads than before: 1+added heads (2..n)
1949 - less heads than before: -1-removed heads (-2..-n)
1950 - less heads than before: -1-removed heads (-2..-n)
1950 - number of heads stays the same: 1
1951 - number of heads stays the same: 1
1951 """
1952 """
1952 def csmap(x):
1953 def csmap(x):
1953 self.ui.debug(_("add changeset %s\n") % short(x))
1954 self.ui.debug(_("add changeset %s\n") % short(x))
1954 return cl.count()
1955 return cl.count()
1955
1956
1956 def revmap(x):
1957 def revmap(x):
1957 return cl.rev(x)
1958 return cl.rev(x)
1958
1959
1959 if not source:
1960 if not source:
1960 return 0
1961 return 0
1961
1962
1962 self.hook('prechangegroup', throw=True, source=srctype, url=url)
1963 self.hook('prechangegroup', throw=True, source=srctype, url=url)
1963
1964
1964 changesets = files = revisions = 0
1965 changesets = files = revisions = 0
1965
1966
1966 # write changelog data to temp files so concurrent readers will not see
1967 # write changelog data to temp files so concurrent readers will not see
1967 # inconsistent view
1968 # inconsistent view
1968 cl = self.changelog
1969 cl = self.changelog
1969 cl.delayupdate()
1970 cl.delayupdate()
1970 oldheads = len(cl.heads())
1971 oldheads = len(cl.heads())
1971
1972
1972 tr = self.transaction()
1973 tr = self.transaction()
1973 try:
1974 try:
1974 trp = weakref.proxy(tr)
1975 trp = weakref.proxy(tr)
1975 # pull off the changeset group
1976 # pull off the changeset group
1976 self.ui.status(_("adding changesets\n"))
1977 self.ui.status(_("adding changesets\n"))
1977 cor = cl.count() - 1
1978 cor = cl.count() - 1
1978 chunkiter = changegroup.chunkiter(source)
1979 chunkiter = changegroup.chunkiter(source)
1979 if cl.addgroup(chunkiter, csmap, trp, 1) is None and not emptyok:
1980 if cl.addgroup(chunkiter, csmap, trp, 1) is None and not emptyok:
1980 raise util.Abort(_("received changelog group is empty"))
1981 raise util.Abort(_("received changelog group is empty"))
1981 cnr = cl.count() - 1
1982 cnr = cl.count() - 1
1982 changesets = cnr - cor
1983 changesets = cnr - cor
1983
1984
1984 # pull off the manifest group
1985 # pull off the manifest group
1985 self.ui.status(_("adding manifests\n"))
1986 self.ui.status(_("adding manifests\n"))
1986 chunkiter = changegroup.chunkiter(source)
1987 chunkiter = changegroup.chunkiter(source)
1987 # no need to check for empty manifest group here:
1988 # no need to check for empty manifest group here:
1988 # if the result of the merge of 1 and 2 is the same in 3 and 4,
1989 # if the result of the merge of 1 and 2 is the same in 3 and 4,
1989 # no new manifest will be created and the manifest group will
1990 # no new manifest will be created and the manifest group will
1990 # be empty during the pull
1991 # be empty during the pull
1991 self.manifest.addgroup(chunkiter, revmap, trp)
1992 self.manifest.addgroup(chunkiter, revmap, trp)
1992
1993
1993 # process the files
1994 # process the files
1994 self.ui.status(_("adding file changes\n"))
1995 self.ui.status(_("adding file changes\n"))
1995 while 1:
1996 while 1:
1996 f = changegroup.getchunk(source)
1997 f = changegroup.getchunk(source)
1997 if not f:
1998 if not f:
1998 break
1999 break
1999 self.ui.debug(_("adding %s revisions\n") % f)
2000 self.ui.debug(_("adding %s revisions\n") % f)
2000 fl = self.file(f)
2001 fl = self.file(f)
2001 o = fl.count()
2002 o = fl.count()
2002 chunkiter = changegroup.chunkiter(source)
2003 chunkiter = changegroup.chunkiter(source)
2003 if fl.addgroup(chunkiter, revmap, trp) is None:
2004 if fl.addgroup(chunkiter, revmap, trp) is None:
2004 raise util.Abort(_("received file revlog group is empty"))
2005 raise util.Abort(_("received file revlog group is empty"))
2005 revisions += fl.count() - o
2006 revisions += fl.count() - o
2006 files += 1
2007 files += 1
2007
2008
2008 # make changelog see real files again
2009 # make changelog see real files again
2009 cl.finalize(trp)
2010 cl.finalize(trp)
2010
2011
2011 newheads = len(self.changelog.heads())
2012 newheads = len(self.changelog.heads())
2012 heads = ""
2013 heads = ""
2013 if oldheads and newheads != oldheads:
2014 if oldheads and newheads != oldheads:
2014 heads = _(" (%+d heads)") % (newheads - oldheads)
2015 heads = _(" (%+d heads)") % (newheads - oldheads)
2015
2016
2016 self.ui.status(_("added %d changesets"
2017 self.ui.status(_("added %d changesets"
2017 " with %d changes to %d files%s\n")
2018 " with %d changes to %d files%s\n")
2018 % (changesets, revisions, files, heads))
2019 % (changesets, revisions, files, heads))
2019
2020
2020 if changesets > 0:
2021 if changesets > 0:
2021 self.hook('pretxnchangegroup', throw=True,
2022 self.hook('pretxnchangegroup', throw=True,
2022 node=hex(self.changelog.node(cor+1)), source=srctype,
2023 node=hex(self.changelog.node(cor+1)), source=srctype,
2023 url=url)
2024 url=url)
2024
2025
2025 tr.close()
2026 tr.close()
2026 finally:
2027 finally:
2027 del tr
2028 del tr
2028
2029
2029 if changesets > 0:
2030 if changesets > 0:
2030 # forcefully update the on-disk branch cache
2031 # forcefully update the on-disk branch cache
2031 self.ui.debug(_("updating the branch cache\n"))
2032 self.ui.debug(_("updating the branch cache\n"))
2032 self.branchtags()
2033 self.branchtags()
2033 self.hook("changegroup", node=hex(self.changelog.node(cor+1)),
2034 self.hook("changegroup", node=hex(self.changelog.node(cor+1)),
2034 source=srctype, url=url)
2035 source=srctype, url=url)
2035
2036
2036 for i in xrange(cor + 1, cnr + 1):
2037 for i in xrange(cor + 1, cnr + 1):
2037 self.hook("incoming", node=hex(self.changelog.node(i)),
2038 self.hook("incoming", node=hex(self.changelog.node(i)),
2038 source=srctype, url=url)
2039 source=srctype, url=url)
2039
2040
2040 # never return 0 here:
2041 # never return 0 here:
2041 if newheads < oldheads:
2042 if newheads < oldheads:
2042 return newheads - oldheads - 1
2043 return newheads - oldheads - 1
2043 else:
2044 else:
2044 return newheads - oldheads + 1
2045 return newheads - oldheads + 1
2045
2046
2046
2047
2047 def stream_in(self, remote):
2048 def stream_in(self, remote):
2048 fp = remote.stream_out()
2049 fp = remote.stream_out()
2049 l = fp.readline()
2050 l = fp.readline()
2050 try:
2051 try:
2051 resp = int(l)
2052 resp = int(l)
2052 except ValueError:
2053 except ValueError:
2053 raise util.UnexpectedOutput(
2054 raise util.UnexpectedOutput(
2054 _('Unexpected response from remote server:'), l)
2055 _('Unexpected response from remote server:'), l)
2055 if resp == 1:
2056 if resp == 1:
2056 raise util.Abort(_('operation forbidden by server'))
2057 raise util.Abort(_('operation forbidden by server'))
2057 elif resp == 2:
2058 elif resp == 2:
2058 raise util.Abort(_('locking the remote repository failed'))
2059 raise util.Abort(_('locking the remote repository failed'))
2059 elif resp != 0:
2060 elif resp != 0:
2060 raise util.Abort(_('the server sent an unknown error code'))
2061 raise util.Abort(_('the server sent an unknown error code'))
2061 self.ui.status(_('streaming all changes\n'))
2062 self.ui.status(_('streaming all changes\n'))
2062 l = fp.readline()
2063 l = fp.readline()
2063 try:
2064 try:
2064 total_files, total_bytes = map(int, l.split(' ', 1))
2065 total_files, total_bytes = map(int, l.split(' ', 1))
2065 except ValueError, TypeError:
2066 except ValueError, TypeError:
2066 raise util.UnexpectedOutput(
2067 raise util.UnexpectedOutput(
2067 _('Unexpected response from remote server:'), l)
2068 _('Unexpected response from remote server:'), l)
2068 self.ui.status(_('%d files to transfer, %s of data\n') %
2069 self.ui.status(_('%d files to transfer, %s of data\n') %
2069 (total_files, util.bytecount(total_bytes)))
2070 (total_files, util.bytecount(total_bytes)))
2070 start = time.time()
2071 start = time.time()
2071 for i in xrange(total_files):
2072 for i in xrange(total_files):
2072 # XXX doesn't support '\n' or '\r' in filenames
2073 # XXX doesn't support '\n' or '\r' in filenames
2073 l = fp.readline()
2074 l = fp.readline()
2074 try:
2075 try:
2075 name, size = l.split('\0', 1)
2076 name, size = l.split('\0', 1)
2076 size = int(size)
2077 size = int(size)
2077 except ValueError, TypeError:
2078 except ValueError, TypeError:
2078 raise util.UnexpectedOutput(
2079 raise util.UnexpectedOutput(
2079 _('Unexpected response from remote server:'), l)
2080 _('Unexpected response from remote server:'), l)
2080 self.ui.debug('adding %s (%s)\n' % (name, util.bytecount(size)))
2081 self.ui.debug('adding %s (%s)\n' % (name, util.bytecount(size)))
2081 ofp = self.sopener(name, 'w')
2082 ofp = self.sopener(name, 'w')
2082 for chunk in util.filechunkiter(fp, limit=size):
2083 for chunk in util.filechunkiter(fp, limit=size):
2083 ofp.write(chunk)
2084 ofp.write(chunk)
2084 ofp.close()
2085 ofp.close()
2085 elapsed = time.time() - start
2086 elapsed = time.time() - start
2086 if elapsed <= 0:
2087 if elapsed <= 0:
2087 elapsed = 0.001
2088 elapsed = 0.001
2088 self.ui.status(_('transferred %s in %.1f seconds (%s/sec)\n') %
2089 self.ui.status(_('transferred %s in %.1f seconds (%s/sec)\n') %
2089 (util.bytecount(total_bytes), elapsed,
2090 (util.bytecount(total_bytes), elapsed,
2090 util.bytecount(total_bytes / elapsed)))
2091 util.bytecount(total_bytes / elapsed)))
2091 self.invalidate()
2092 self.invalidate()
2092 return len(self.heads()) + 1
2093 return len(self.heads()) + 1
2093
2094
2094 def clone(self, remote, heads=[], stream=False):
2095 def clone(self, remote, heads=[], stream=False):
2095 '''clone remote repository.
2096 '''clone remote repository.
2096
2097
2097 keyword arguments:
2098 keyword arguments:
2098 heads: list of revs to clone (forces use of pull)
2099 heads: list of revs to clone (forces use of pull)
2099 stream: use streaming clone if possible'''
2100 stream: use streaming clone if possible'''
2100
2101
2101 # now, all clients that can request uncompressed clones can
2102 # now, all clients that can request uncompressed clones can
2102 # read repo formats supported by all servers that can serve
2103 # read repo formats supported by all servers that can serve
2103 # them.
2104 # them.
2104
2105
2105 # if revlog format changes, client will have to check version
2106 # if revlog format changes, client will have to check version
2106 # and format flags on "stream" capability, and use
2107 # and format flags on "stream" capability, and use
2107 # uncompressed only if compatible.
2108 # uncompressed only if compatible.
2108
2109
2109 if stream and not heads and remote.capable('stream'):
2110 if stream and not heads and remote.capable('stream'):
2110 return self.stream_in(remote)
2111 return self.stream_in(remote)
2111 return self.pull(remote, heads)
2112 return self.pull(remote, heads)
2112
2113
2113 # used to avoid circular references so destructors work
2114 # used to avoid circular references so destructors work
2114 def aftertrans(files):
2115 def aftertrans(files):
2115 renamefiles = [tuple(t) for t in files]
2116 renamefiles = [tuple(t) for t in files]
2116 def a():
2117 def a():
2117 for src, dest in renamefiles:
2118 for src, dest in renamefiles:
2118 util.rename(src, dest)
2119 util.rename(src, dest)
2119 return a
2120 return a
2120
2121
2121 def instance(ui, path, create):
2122 def instance(ui, path, create):
2122 return localrepository(ui, util.drop_scheme('file', path), create)
2123 return localrepository(ui, util.drop_scheme('file', path), create)
2123
2124
2124 def islocal(path):
2125 def islocal(path):
2125 return True
2126 return True
@@ -1,312 +1,312 b''
1 Mercurial Distributed SCM
1 Mercurial Distributed SCM
2
2
3 basic commands:
3 basic commands:
4
4
5 add add the specified files on the next commit
5 add add the specified files on the next commit
6 annotate show changeset information per file line
6 annotate show changeset information per file line
7 clone make a copy of an existing repository
7 clone make a copy of an existing repository
8 commit commit the specified files or all outstanding changes
8 commit commit the specified files or all outstanding changes
9 diff diff repository (or selected files)
9 diff diff repository (or selected files)
10 export dump the header and diffs for one or more changesets
10 export dump the header and diffs for one or more changesets
11 init create a new repository in the given directory
11 init create a new repository in the given directory
12 log show revision history of entire repository or files
12 log show revision history of entire repository or files
13 merge merge working directory with another revision
13 merge merge working directory with another revision
14 parents show the parents of the working dir or revision
14 parents show the parents of the working dir or revision
15 pull pull changes from the specified source
15 pull pull changes from the specified source
16 push push changes to the specified destination
16 push push changes to the specified destination
17 remove remove the specified files on the next commit
17 remove remove the specified files on the next commit
18 serve export the repository via HTTP
18 serve export the repository via HTTP
19 status show changed files in the working directory
19 status show changed files in the working directory
20 update update working directory
20 update update working directory
21
21
22 use "hg help" for the full list of commands or "hg -v" for details
22 use "hg help" for the full list of commands or "hg -v" for details
23 add add the specified files on the next commit
23 add add the specified files on the next commit
24 annotate show changeset information per file line
24 annotate show changeset information per file line
25 clone make a copy of an existing repository
25 clone make a copy of an existing repository
26 commit commit the specified files or all outstanding changes
26 commit commit the specified files or all outstanding changes
27 diff diff repository (or selected files)
27 diff diff repository (or selected files)
28 export dump the header and diffs for one or more changesets
28 export dump the header and diffs for one or more changesets
29 init create a new repository in the given directory
29 init create a new repository in the given directory
30 log show revision history of entire repository or files
30 log show revision history of entire repository or files
31 merge merge working directory with another revision
31 merge merge working directory with another revision
32 parents show the parents of the working dir or revision
32 parents show the parents of the working dir or revision
33 pull pull changes from the specified source
33 pull pull changes from the specified source
34 push push changes to the specified destination
34 push push changes to the specified destination
35 remove remove the specified files on the next commit
35 remove remove the specified files on the next commit
36 serve export the repository via HTTP
36 serve export the repository via HTTP
37 status show changed files in the working directory
37 status show changed files in the working directory
38 update update working directory
38 update update working directory
39 Mercurial Distributed SCM
39 Mercurial Distributed SCM
40
40
41 list of commands:
41 list of commands:
42
42
43 add add the specified files on the next commit
43 add add the specified files on the next commit
44 addremove add all new files, delete all missing files
44 addremove add all new files, delete all missing files
45 annotate show changeset information per file line
45 annotate show changeset information per file line
46 archive create unversioned archive of a repository revision
46 archive create unversioned archive of a repository revision
47 backout reverse effect of earlier changeset
47 backout reverse effect of earlier changeset
48 bisect subdivision search of changesets
48 bisect subdivision search of changesets
49 branch set or show the current branch name
49 branch set or show the current branch name
50 branches list repository named branches
50 branches list repository named branches
51 bundle create a changegroup file
51 bundle create a changegroup file
52 cat output the current or given revision of files
52 cat output the current or given revision of files
53 clone make a copy of an existing repository
53 clone make a copy of an existing repository
54 commit commit the specified files or all outstanding changes
54 commit commit the specified files or all outstanding changes
55 copy mark files as copied for the next commit
55 copy mark files as copied for the next commit
56 diff diff repository (or selected files)
56 diff diff repository (or selected files)
57 export dump the header and diffs for one or more changesets
57 export dump the header and diffs for one or more changesets
58 grep search for a pattern in specified files and revisions
58 grep search for a pattern in specified files and revisions
59 heads show current repository heads or show branch heads
59 heads show current repository heads or show branch heads
60 help show help for a command, extension, or list of commands
60 help show help for a command, extension, or list of commands
61 identify identify the working copy or specified revision
61 identify identify the working copy or specified revision
62 import import an ordered set of patches
62 import import an ordered set of patches
63 incoming show new changesets found in source
63 incoming show new changesets found in source
64 init create a new repository in the given directory
64 init create a new repository in the given directory
65 locate locate files matching specific patterns
65 locate locate files matching specific patterns
66 log show revision history of entire repository or files
66 log show revision history of entire repository or files
67 manifest output the current or given revision of the project manifest
67 manifest output the current or given revision of the project manifest
68 merge merge working directory with another revision
68 merge merge working directory with another revision
69 outgoing show changesets not found in destination
69 outgoing show changesets not found in destination
70 parents show the parents of the working dir or revision
70 parents show the parents of the working dir or revision
71 paths show definition of symbolic path names
71 paths show definition of symbolic path names
72 pull pull changes from the specified source
72 pull pull changes from the specified source
73 push push changes to the specified destination
73 push push changes to the specified destination
74 recover roll back an interrupted transaction
74 recover roll back an interrupted transaction
75 remove remove the specified files on the next commit
75 remove remove the specified files on the next commit
76 rename rename files; equivalent of copy + remove
76 rename rename files; equivalent of copy + remove
77 revert restore individual files or dirs to an earlier state
77 revert restore individual files or dirs to an earlier state
78 rollback roll back the last transaction
78 rollback roll back the last transaction
79 root print the root (top) of the current working dir
79 root print the root (top) of the current working dir
80 serve export the repository via HTTP
80 serve export the repository via HTTP
81 showconfig show combined config settings from all hgrc files
81 showconfig show combined config settings from all hgrc files
82 status show changed files in the working directory
82 status show changed files in the working directory
83 tag add a tag for the current or given revision
83 tag add a tag for the current or given revision
84 tags list repository tags
84 tags list repository tags
85 tip show the tip revision
85 tip show the tip revision
86 unbundle apply one or more changegroup files
86 unbundle apply one or more changegroup files
87 update update working directory
87 update update working directory
88 verify verify the integrity of the repository
88 verify verify the integrity of the repository
89 version output version and copyright information
89 version output version and copyright information
90
90
91 use "hg -v help" to show aliases and global options
91 use "hg -v help" to show aliases and global options
92 add add the specified files on the next commit
92 add add the specified files on the next commit
93 addremove add all new files, delete all missing files
93 addremove add all new files, delete all missing files
94 annotate show changeset information per file line
94 annotate show changeset information per file line
95 archive create unversioned archive of a repository revision
95 archive create unversioned archive of a repository revision
96 backout reverse effect of earlier changeset
96 backout reverse effect of earlier changeset
97 bisect subdivision search of changesets
97 bisect subdivision search of changesets
98 branch set or show the current branch name
98 branch set or show the current branch name
99 branches list repository named branches
99 branches list repository named branches
100 bundle create a changegroup file
100 bundle create a changegroup file
101 cat output the current or given revision of files
101 cat output the current or given revision of files
102 clone make a copy of an existing repository
102 clone make a copy of an existing repository
103 commit commit the specified files or all outstanding changes
103 commit commit the specified files or all outstanding changes
104 copy mark files as copied for the next commit
104 copy mark files as copied for the next commit
105 diff diff repository (or selected files)
105 diff diff repository (or selected files)
106 export dump the header and diffs for one or more changesets
106 export dump the header and diffs for one or more changesets
107 grep search for a pattern in specified files and revisions
107 grep search for a pattern in specified files and revisions
108 heads show current repository heads or show branch heads
108 heads show current repository heads or show branch heads
109 help show help for a command, extension, or list of commands
109 help show help for a command, extension, or list of commands
110 identify identify the working copy or specified revision
110 identify identify the working copy or specified revision
111 import import an ordered set of patches
111 import import an ordered set of patches
112 incoming show new changesets found in source
112 incoming show new changesets found in source
113 init create a new repository in the given directory
113 init create a new repository in the given directory
114 locate locate files matching specific patterns
114 locate locate files matching specific patterns
115 log show revision history of entire repository or files
115 log show revision history of entire repository or files
116 manifest output the current or given revision of the project manifest
116 manifest output the current or given revision of the project manifest
117 merge merge working directory with another revision
117 merge merge working directory with another revision
118 outgoing show changesets not found in destination
118 outgoing show changesets not found in destination
119 parents show the parents of the working dir or revision
119 parents show the parents of the working dir or revision
120 paths show definition of symbolic path names
120 paths show definition of symbolic path names
121 pull pull changes from the specified source
121 pull pull changes from the specified source
122 push push changes to the specified destination
122 push push changes to the specified destination
123 recover roll back an interrupted transaction
123 recover roll back an interrupted transaction
124 remove remove the specified files on the next commit
124 remove remove the specified files on the next commit
125 rename rename files; equivalent of copy + remove
125 rename rename files; equivalent of copy + remove
126 revert restore individual files or dirs to an earlier state
126 revert restore individual files or dirs to an earlier state
127 rollback roll back the last transaction
127 rollback roll back the last transaction
128 root print the root (top) of the current working dir
128 root print the root (top) of the current working dir
129 serve export the repository via HTTP
129 serve export the repository via HTTP
130 showconfig show combined config settings from all hgrc files
130 showconfig show combined config settings from all hgrc files
131 status show changed files in the working directory
131 status show changed files in the working directory
132 tag add a tag for the current or given revision
132 tag add a tag for the current or given revision
133 tags list repository tags
133 tags list repository tags
134 tip show the tip revision
134 tip show the tip revision
135 unbundle apply one or more changegroup files
135 unbundle apply one or more changegroup files
136 update update working directory
136 update update working directory
137 verify verify the integrity of the repository
137 verify verify the integrity of the repository
138 version output version and copyright information
138 version output version and copyright information
139 hg add [OPTION]... [FILE]...
139 hg add [OPTION]... [FILE]...
140
140
141 add the specified files on the next commit
141 add the specified files on the next commit
142
142
143 Schedule files to be version controlled and added to the repository.
143 Schedule files to be version controlled and added to the repository.
144
144
145 The files will be added to the repository at the next commit. To
145 The files will be added to the repository at the next commit. To
146 undo an add before that, see hg revert.
146 undo an add before that, see hg revert.
147
147
148 If no names are given, add all files in the repository.
148 If no names are given, add all files in the repository.
149
149
150 options:
150 options:
151
151
152 -I --include include names matching the given patterns
152 -I --include include names matching the given patterns
153 -X --exclude exclude names matching the given patterns
153 -X --exclude exclude names matching the given patterns
154 -n --dry-run do not perform actions, just print output
154 -n --dry-run do not perform actions, just print output
155
155
156 use "hg -v help add" to show global options
156 use "hg -v help add" to show global options
157 hg add: option --skjdfks not recognized
157 hg add: option --skjdfks not recognized
158 hg add [OPTION]... [FILE]...
158 hg add [OPTION]... [FILE]...
159
159
160 add the specified files on the next commit
160 add the specified files on the next commit
161
161
162 Schedule files to be version controlled and added to the repository.
162 Schedule files to be version controlled and added to the repository.
163
163
164 The files will be added to the repository at the next commit. To
164 The files will be added to the repository at the next commit. To
165 undo an add before that, see hg revert.
165 undo an add before that, see hg revert.
166
166
167 If no names are given, add all files in the repository.
167 If no names are given, add all files in the repository.
168
168
169 options:
169 options:
170
170
171 -I --include include names matching the given patterns
171 -I --include include names matching the given patterns
172 -X --exclude exclude names matching the given patterns
172 -X --exclude exclude names matching the given patterns
173 -n --dry-run do not perform actions, just print output
173 -n --dry-run do not perform actions, just print output
174
174
175 use "hg -v help add" to show global options
175 use "hg -v help add" to show global options
176 hg diff [OPTION]... [-r REV1 [-r REV2]] [FILE]...
176 hg diff [OPTION]... [-r REV1 [-r REV2]] [FILE]...
177
177
178 diff repository (or selected files)
178 diff repository (or selected files)
179
179
180 Show differences between revisions for the specified files.
180 Show differences between revisions for the specified files.
181
181
182 Differences between files are shown using the unified diff format.
182 Differences between files are shown using the unified diff format.
183
183
184 NOTE: diff may generate unexpected results for merges, as it will
184 NOTE: diff may generate unexpected results for merges, as it will
185 default to comparing against the working directory's first parent
185 default to comparing against the working directory's first parent
186 changeset if no revisions are specified.
186 changeset if no revisions are specified.
187
187
188 When two revision arguments are given, then changes are shown
188 When two revision arguments are given, then changes are shown
189 between those revisions. If only one revision is specified then
189 between those revisions. If only one revision is specified then
190 that revision is compared to the working directory, and, when no
190 that revision is compared to the working directory, and, when no
191 revisions are specified, the working directory files are compared
191 revisions are specified, the working directory files are compared
192 to its parent.
192 to its parent.
193
193
194 Without the -a option, diff will avoid generating diffs of files
194 Without the -a option, diff will avoid generating diffs of files
195 it detects as binary. With -a, diff will generate a diff anyway,
195 it detects as binary. With -a, diff will generate a diff anyway,
196 probably with undesirable results.
196 probably with undesirable results.
197
197
198 options:
198 options:
199
199
200 -r --rev revision
200 -r --rev revision
201 -a --text treat all files as text
201 -a --text treat all files as text
202 -p --show-function show which function each change is in
202 -p --show-function show which function each change is in
203 -g --git use git extended diff format
203 -g --git use git extended diff format
204 --nodates don't include dates in diff headers
204 --nodates don't include dates in diff headers
205 -w --ignore-all-space ignore white space when comparing lines
205 -w --ignore-all-space ignore white space when comparing lines
206 -b --ignore-space-change ignore changes in the amount of white space
206 -b --ignore-space-change ignore changes in the amount of white space
207 -B --ignore-blank-lines ignore changes whose lines are all blank
207 -B --ignore-blank-lines ignore changes whose lines are all blank
208 -U --unified number of lines of context to show (default: 3)
208 -U --unified number of lines of context to show (default: 3)
209 -I --include include names matching the given patterns
209 -I --include include names matching the given patterns
210 -X --exclude exclude names matching the given patterns
210 -X --exclude exclude names matching the given patterns
211
211
212 use "hg -v help diff" to show global options
212 use "hg -v help diff" to show global options
213 hg status [OPTION]... [FILE]...
213 hg status [OPTION]... [FILE]...
214
214
215 aliases: st
215 aliases: st
216
216
217 show changed files in the working directory
217 show changed files in the working directory
218
218
219 Show status of files in the repository. If names are given, only
219 Show status of files in the repository. If names are given, only
220 files that match are shown. Files that are clean or ignored or
220 files that match are shown. Files that are clean or ignored or
221 source of a copy/move operation, are not listed unless -c (clean),
221 source of a copy/move operation, are not listed unless -c (clean),
222 -i (ignored), -C (copies) or -A is given. Unless options described
222 -i (ignored), -C (copies) or -A is given. Unless options described
223 with "show only ..." are given, the options -mardu are used.
223 with "show only ..." are given, the options -mardu are used.
224
224
225 Option -q/--quiet hides untracked files unless explicitly
225 Option -q/--quiet hides untracked (unknown and ignored) files
226 requested by -u.
226 unless explicitly requested with -u/--unknown or -i/-ignored.
227
227
228 NOTE: status may appear to disagree with diff if permissions have
228 NOTE: status may appear to disagree with diff if permissions have
229 changed or a merge has occurred. The standard diff format does not
229 changed or a merge has occurred. The standard diff format does not
230 report permission changes and diff only reports changes relative
230 report permission changes and diff only reports changes relative
231 to one merge parent.
231 to one merge parent.
232
232
233 If one revision is given, it is used as the base revision.
233 If one revision is given, it is used as the base revision.
234 If two revisions are given, the difference between them is shown.
234 If two revisions are given, the difference between them is shown.
235
235
236 The codes used to show the status of files are:
236 The codes used to show the status of files are:
237 M = modified
237 M = modified
238 A = added
238 A = added
239 R = removed
239 R = removed
240 C = clean
240 C = clean
241 ! = deleted, but still tracked
241 ! = deleted, but still tracked
242 ? = not tracked
242 ? = not tracked
243 I = ignored
243 I = ignored
244 = the previous added file was copied from here
244 = the previous added file was copied from here
245
245
246 options:
246 options:
247
247
248 -A --all show status of all files
248 -A --all show status of all files
249 -m --modified show only modified files
249 -m --modified show only modified files
250 -a --added show only added files
250 -a --added show only added files
251 -r --removed show only removed files
251 -r --removed show only removed files
252 -d --deleted show only deleted (but tracked) files
252 -d --deleted show only deleted (but tracked) files
253 -c --clean show only files without changes
253 -c --clean show only files without changes
254 -u --unknown show only unknown (not tracked) files
254 -u --unknown show only unknown (not tracked) files
255 -i --ignored show only ignored files
255 -i --ignored show only ignored files
256 -n --no-status hide status prefix
256 -n --no-status hide status prefix
257 -C --copies show source of copied files
257 -C --copies show source of copied files
258 -0 --print0 end filenames with NUL, for use with xargs
258 -0 --print0 end filenames with NUL, for use with xargs
259 --rev show difference from revision
259 --rev show difference from revision
260 -I --include include names matching the given patterns
260 -I --include include names matching the given patterns
261 -X --exclude exclude names matching the given patterns
261 -X --exclude exclude names matching the given patterns
262
262
263 use "hg -v help status" to show global options
263 use "hg -v help status" to show global options
264 hg status [OPTION]... [FILE]...
264 hg status [OPTION]... [FILE]...
265
265
266 show changed files in the working directory
266 show changed files in the working directory
267 hg: unknown command 'foo'
267 hg: unknown command 'foo'
268 Mercurial Distributed SCM
268 Mercurial Distributed SCM
269
269
270 basic commands:
270 basic commands:
271
271
272 add add the specified files on the next commit
272 add add the specified files on the next commit
273 annotate show changeset information per file line
273 annotate show changeset information per file line
274 clone make a copy of an existing repository
274 clone make a copy of an existing repository
275 commit commit the specified files or all outstanding changes
275 commit commit the specified files or all outstanding changes
276 diff diff repository (or selected files)
276 diff diff repository (or selected files)
277 export dump the header and diffs for one or more changesets
277 export dump the header and diffs for one or more changesets
278 init create a new repository in the given directory
278 init create a new repository in the given directory
279 log show revision history of entire repository or files
279 log show revision history of entire repository or files
280 merge merge working directory with another revision
280 merge merge working directory with another revision
281 parents show the parents of the working dir or revision
281 parents show the parents of the working dir or revision
282 pull pull changes from the specified source
282 pull pull changes from the specified source
283 push push changes to the specified destination
283 push push changes to the specified destination
284 remove remove the specified files on the next commit
284 remove remove the specified files on the next commit
285 serve export the repository via HTTP
285 serve export the repository via HTTP
286 status show changed files in the working directory
286 status show changed files in the working directory
287 update update working directory
287 update update working directory
288
288
289 use "hg help" for the full list of commands or "hg -v" for details
289 use "hg help" for the full list of commands or "hg -v" for details
290 hg: unknown command 'skjdfks'
290 hg: unknown command 'skjdfks'
291 Mercurial Distributed SCM
291 Mercurial Distributed SCM
292
292
293 basic commands:
293 basic commands:
294
294
295 add add the specified files on the next commit
295 add add the specified files on the next commit
296 annotate show changeset information per file line
296 annotate show changeset information per file line
297 clone make a copy of an existing repository
297 clone make a copy of an existing repository
298 commit commit the specified files or all outstanding changes
298 commit commit the specified files or all outstanding changes
299 diff diff repository (or selected files)
299 diff diff repository (or selected files)
300 export dump the header and diffs for one or more changesets
300 export dump the header and diffs for one or more changesets
301 init create a new repository in the given directory
301 init create a new repository in the given directory
302 log show revision history of entire repository or files
302 log show revision history of entire repository or files
303 merge merge working directory with another revision
303 merge merge working directory with another revision
304 parents show the parents of the working dir or revision
304 parents show the parents of the working dir or revision
305 pull pull changes from the specified source
305 pull pull changes from the specified source
306 push push changes to the specified destination
306 push push changes to the specified destination
307 remove remove the specified files on the next commit
307 remove remove the specified files on the next commit
308 serve export the repository via HTTP
308 serve export the repository via HTTP
309 status show changed files in the working directory
309 status show changed files in the working directory
310 update update working directory
310 update update working directory
311
311
312 use "hg help" for the full list of commands or "hg -v" for details
312 use "hg help" for the full list of commands or "hg -v" for details
@@ -1,94 +1,95 b''
1 #!/bin/sh
1 #!/bin/sh
2
2
3 hg init repo1
3 hg init repo1
4 cd repo1
4 cd repo1
5 mkdir a b a/1 b/1 b/2
5 mkdir a b a/1 b/1 b/2
6 touch in_root a/in_a b/in_b a/1/in_a_1 b/1/in_b_1 b/2/in_b_2
6 touch in_root a/in_a b/in_b a/1/in_a_1 b/1/in_b_1 b/2/in_b_2
7 echo "hg status in repo root:"
7 echo "hg status in repo root:"
8 hg status
8 hg status
9 echo "hg status . in repo root:"
9 echo "hg status . in repo root:"
10 hg status .
10 hg status .
11 for dir in a b a/1 b/1 b/2; do
11 for dir in a b a/1 b/1 b/2; do
12 echo "hg status in $dir:"
12 echo "hg status in $dir:"
13 hg status --cwd "$dir"
13 hg status --cwd "$dir"
14 echo "hg status . in $dir:"
14 echo "hg status . in $dir:"
15 hg status --cwd "$dir" .
15 hg status --cwd "$dir" .
16 echo "hg status .. in $dir:"
16 echo "hg status .. in $dir:"
17 hg status --cwd "$dir" ..
17 hg status --cwd "$dir" ..
18 done
18 done
19 cd ..
19 cd ..
20
20
21 hg init repo2
21 hg init repo2
22 cd repo2
22 cd repo2
23 touch modified removed deleted ignored
23 touch modified removed deleted ignored
24 echo "^ignored$" > .hgignore
24 echo "^ignored$" > .hgignore
25 hg ci -A -m 'initial checkin' -d "1000000 0"
25 hg ci -A -m 'initial checkin' -d "1000000 0"
26 sleep 1 # make sure mtime is changed
26 sleep 1 # make sure mtime is changed
27 touch modified added unknown ignored
27 touch modified added unknown ignored
28 hg add added
28 hg add added
29 hg remove removed
29 hg remove removed
30 rm deleted
30 rm deleted
31 echo "hg status:"
31 echo "hg status:"
32 hg status
32 hg status
33 echo "hg status modified added removed deleted unknown never-existed ignored:"
33 echo "hg status modified added removed deleted unknown never-existed ignored:"
34 hg status modified added removed deleted unknown never-existed ignored
34 hg status modified added removed deleted unknown never-existed ignored
35 hg copy modified copied
35 hg copy modified copied
36 echo "hg status -C:"
36 echo "hg status -C:"
37 hg status -C
37 hg status -C
38 echo "hg status -A:"
38 echo "hg status -A:"
39 hg status -A
39 hg status -A
40 echo "^ignoreddir$" > .hgignore
40 echo "^ignoreddir$" > .hgignore
41 mkdir ignoreddir
41 mkdir ignoreddir
42 touch ignoreddir/file
42 touch ignoreddir/file
43 echo "hg status ignoreddir/file:"
43 echo "hg status ignoreddir/file:"
44 hg status ignoreddir/file
44 hg status ignoreddir/file
45 echo "hg status -i ignoreddir/file:"
45 echo "hg status -i ignoreddir/file:"
46 hg status -i ignoreddir/file
46 hg status -i ignoreddir/file
47 cd ..
47 cd ..
48
48
49 # check 'status -q' and some combinations
49 # check 'status -q' and some combinations
50 hg init repo3
50 hg init repo3
51 cd repo3
51 cd repo3
52 touch modified removed deleted ignored
52 touch modified removed deleted ignored
53 echo "^ignored$" > .hgignore
53 echo "^ignored$" > .hgignore
54 hg commit -A -m 'initial checkin'
54 hg commit -A -m 'initial checkin'
55 touch added unknown ignored
55 touch added unknown ignored
56 hg add added
56 hg add added
57 echo "test" >> modified
57 echo "test" >> modified
58 hg remove removed
58 hg remove removed
59 rm deleted
59 rm deleted
60 hg copy modified copied
60 hg copy modified copied
61
61
62 # Run status with 2 different flags.
62 # Run status with 2 different flags.
63 # Check if result is the same or different.
63 # Check if result is the same or different.
64 # If result is not as expected, raise error
64 # If result is not as expected, raise error
65 function assert {
65 function assert {
66 hg status $1 > ../a
66 hg status $1 > ../a
67 hg status $2 > ../b
67 hg status $2 > ../b
68 out=`diff ../a ../b`
68 out=`diff ../a ../b`
69 if [ $? -ne 0 ]; then
69 if [ $? -ne 0 ]; then
70 out=1
70 out=1
71 else
71 else
72 out=0
72 out=0
73 fi
73 fi
74 if [ $3 -eq 0 ]; then
74 if [ $3 -eq 0 ]; then
75 df="same"
75 df="same"
76 else
76 else
77 df="different"
77 df="different"
78 fi
78 fi
79 if [ $out -ne $3 ]; then
79 if [ $out -ne $3 ]; then
80 echo "Error on $1 and $2, should be $df."
80 echo "Error on $1 and $2, should be $df."
81 fi
81 fi
82 }
82 }
83
83
84 # assert flag1 flag2 [0-same | 1-different]
84 # assert flag1 flag2 [0-same | 1-different]
85 assert "-q" "-mard" 0
85 assert "-q" "-mard" 0
86 assert "-A" "-mardicCu" 0
86 assert "-A" "-marduicC" 0
87 assert "-qA" "-mardicCu" 0
87 assert "-qA" "-mardcC" 0
88 assert "-qAu" "-A" 0
88 assert "-qAui" "-A" 0
89 assert "-qA" "-A" 0
89 assert "-qAu" "-marducC" 0
90 assert "-qu" "-u" 0
90 assert "-qAi" "-mardicC" 0
91 assert "-q" "-u" 1
91 assert "-qu" "-u" 0
92 assert "-m" "-a" 1
92 assert "-q" "-u" 1
93 assert "-r" "-d" 1
93 assert "-m" "-a" 1
94 assert "-r" "-d" 1
94
95
General Comments 0
You need to be logged in to leave comments. Login now