##// END OF EJS Templates
fix memory usage of revlog caches by limiting cache size [issue1639]
Matt Mackall -
r9097:431462bd default
parent child Browse files
Show More
@@ -1,3558 +1,3547 b''
1 # commands.py - command processing for mercurial
1 # commands.py - command processing for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2, incorporated herein by reference.
6 # GNU General Public License version 2, incorporated herein by reference.
7
7
8 from node import hex, nullid, nullrev, short
8 from node import hex, nullid, nullrev, short
9 from lock import release
9 from lock import release
10 from i18n import _, gettext
10 from i18n import _, gettext
11 import os, re, sys, subprocess, difflib, time
11 import os, re, sys, subprocess, difflib, time
12 import hg, util, revlog, bundlerepo, extensions, copies, context, error
12 import hg, util, revlog, bundlerepo, extensions, copies, context, error
13 import patch, help, mdiff, tempfile, url, encoding
13 import patch, help, mdiff, tempfile, url, encoding
14 import archival, changegroup, cmdutil, sshserver, hbisect
14 import archival, changegroup, cmdutil, sshserver, hbisect
15 from hgweb import server
15 from hgweb import server
16 import merge as merge_
16 import merge as merge_
17
17
18 # Commands start here, listed alphabetically
18 # Commands start here, listed alphabetically
19
19
20 def add(ui, repo, *pats, **opts):
20 def add(ui, repo, *pats, **opts):
21 """add the specified files on the next commit
21 """add the specified files on the next commit
22
22
23 Schedule files to be version controlled and added to the
23 Schedule files to be version controlled and added to the
24 repository.
24 repository.
25
25
26 The files will be added to the repository at the next commit. To
26 The files will be added to the repository at the next commit. To
27 undo an add before that, see hg forget.
27 undo an add before that, see hg forget.
28
28
29 If no names are given, add all files to the repository.
29 If no names are given, add all files to the repository.
30 """
30 """
31
31
32 bad = []
32 bad = []
33 exacts = {}
33 exacts = {}
34 names = []
34 names = []
35 m = cmdutil.match(repo, pats, opts)
35 m = cmdutil.match(repo, pats, opts)
36 oldbad = m.bad
36 oldbad = m.bad
37 m.bad = lambda x,y: bad.append(x) or oldbad(x,y)
37 m.bad = lambda x,y: bad.append(x) or oldbad(x,y)
38
38
39 for f in repo.walk(m):
39 for f in repo.walk(m):
40 exact = m.exact(f)
40 exact = m.exact(f)
41 if exact or f not in repo.dirstate:
41 if exact or f not in repo.dirstate:
42 names.append(f)
42 names.append(f)
43 if ui.verbose or not exact:
43 if ui.verbose or not exact:
44 ui.status(_('adding %s\n') % m.rel(f))
44 ui.status(_('adding %s\n') % m.rel(f))
45 if not opts.get('dry_run'):
45 if not opts.get('dry_run'):
46 bad += [f for f in repo.add(names) if f in m.files()]
46 bad += [f for f in repo.add(names) if f in m.files()]
47 return bad and 1 or 0
47 return bad and 1 or 0
48
48
49 def addremove(ui, repo, *pats, **opts):
49 def addremove(ui, repo, *pats, **opts):
50 """add all new files, delete all missing files
50 """add all new files, delete all missing files
51
51
52 Add all new files and remove all missing files from the
52 Add all new files and remove all missing files from the
53 repository.
53 repository.
54
54
55 New files are ignored if they match any of the patterns in
55 New files are ignored if they match any of the patterns in
56 .hgignore. As with add, these changes take effect at the next
56 .hgignore. As with add, these changes take effect at the next
57 commit.
57 commit.
58
58
59 Use the -s/--similarity option to detect renamed files. With a
59 Use the -s/--similarity option to detect renamed files. With a
60 parameter > 0, this compares every removed file with every added
60 parameter > 0, this compares every removed file with every added
61 file and records those similar enough as renames. This option
61 file and records those similar enough as renames. This option
62 takes a percentage between 0 (disabled) and 100 (files must be
62 takes a percentage between 0 (disabled) and 100 (files must be
63 identical) as its parameter. Detecting renamed files this way can
63 identical) as its parameter. Detecting renamed files this way can
64 be expensive.
64 be expensive.
65 """
65 """
66 try:
66 try:
67 sim = float(opts.get('similarity') or 0)
67 sim = float(opts.get('similarity') or 0)
68 except ValueError:
68 except ValueError:
69 raise util.Abort(_('similarity must be a number'))
69 raise util.Abort(_('similarity must be a number'))
70 if sim < 0 or sim > 100:
70 if sim < 0 or sim > 100:
71 raise util.Abort(_('similarity must be between 0 and 100'))
71 raise util.Abort(_('similarity must be between 0 and 100'))
72 return cmdutil.addremove(repo, pats, opts, similarity=sim/100.)
72 return cmdutil.addremove(repo, pats, opts, similarity=sim/100.)
73
73
74 def annotate(ui, repo, *pats, **opts):
74 def annotate(ui, repo, *pats, **opts):
75 """show changeset information by line for each file
75 """show changeset information by line for each file
76
76
77 List changes in files, showing the revision id responsible for
77 List changes in files, showing the revision id responsible for
78 each line
78 each line
79
79
80 This command is useful for discovering when a change was made and
80 This command is useful for discovering when a change was made and
81 by whom.
81 by whom.
82
82
83 Without the -a/--text option, annotate will avoid processing files
83 Without the -a/--text option, annotate will avoid processing files
84 it detects as binary. With -a, annotate will annotate the file
84 it detects as binary. With -a, annotate will annotate the file
85 anyway, although the results will probably be neither useful
85 anyway, although the results will probably be neither useful
86 nor desirable.
86 nor desirable.
87 """
87 """
88 datefunc = ui.quiet and util.shortdate or util.datestr
88 datefunc = ui.quiet and util.shortdate or util.datestr
89 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
89 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
90
90
91 if not pats:
91 if not pats:
92 raise util.Abort(_('at least one filename or pattern is required'))
92 raise util.Abort(_('at least one filename or pattern is required'))
93
93
94 opmap = [('user', lambda x: ui.shortuser(x[0].user())),
94 opmap = [('user', lambda x: ui.shortuser(x[0].user())),
95 ('number', lambda x: str(x[0].rev())),
95 ('number', lambda x: str(x[0].rev())),
96 ('changeset', lambda x: short(x[0].node())),
96 ('changeset', lambda x: short(x[0].node())),
97 ('date', getdate),
97 ('date', getdate),
98 ('follow', lambda x: x[0].path()),
98 ('follow', lambda x: x[0].path()),
99 ]
99 ]
100
100
101 if (not opts.get('user') and not opts.get('changeset') and not opts.get('date')
101 if (not opts.get('user') and not opts.get('changeset') and not opts.get('date')
102 and not opts.get('follow')):
102 and not opts.get('follow')):
103 opts['number'] = 1
103 opts['number'] = 1
104
104
105 linenumber = opts.get('line_number') is not None
105 linenumber = opts.get('line_number') is not None
106 if (linenumber and (not opts.get('changeset')) and (not opts.get('number'))):
106 if (linenumber and (not opts.get('changeset')) and (not opts.get('number'))):
107 raise util.Abort(_('at least one of -n/-c is required for -l'))
107 raise util.Abort(_('at least one of -n/-c is required for -l'))
108
108
109 funcmap = [func for op, func in opmap if opts.get(op)]
109 funcmap = [func for op, func in opmap if opts.get(op)]
110 if linenumber:
110 if linenumber:
111 lastfunc = funcmap[-1]
111 lastfunc = funcmap[-1]
112 funcmap[-1] = lambda x: "%s:%s" % (lastfunc(x), x[1])
112 funcmap[-1] = lambda x: "%s:%s" % (lastfunc(x), x[1])
113
113
114 ctx = repo[opts.get('rev')]
114 ctx = repo[opts.get('rev')]
115
115
116 m = cmdutil.match(repo, pats, opts)
116 m = cmdutil.match(repo, pats, opts)
117 for abs in ctx.walk(m):
117 for abs in ctx.walk(m):
118 fctx = ctx[abs]
118 fctx = ctx[abs]
119 if not opts.get('text') and util.binary(fctx.data()):
119 if not opts.get('text') and util.binary(fctx.data()):
120 ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
120 ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
121 continue
121 continue
122
122
123 lines = fctx.annotate(follow=opts.get('follow'),
123 lines = fctx.annotate(follow=opts.get('follow'),
124 linenumber=linenumber)
124 linenumber=linenumber)
125 pieces = []
125 pieces = []
126
126
127 for f in funcmap:
127 for f in funcmap:
128 l = [f(n) for n, dummy in lines]
128 l = [f(n) for n, dummy in lines]
129 if l:
129 if l:
130 ml = max(map(len, l))
130 ml = max(map(len, l))
131 pieces.append(["%*s" % (ml, x) for x in l])
131 pieces.append(["%*s" % (ml, x) for x in l])
132
132
133 if pieces:
133 if pieces:
134 for p, l in zip(zip(*pieces), lines):
134 for p, l in zip(zip(*pieces), lines):
135 ui.write("%s: %s" % (" ".join(p), l[1]))
135 ui.write("%s: %s" % (" ".join(p), l[1]))
136
136
137 def archive(ui, repo, dest, **opts):
137 def archive(ui, repo, dest, **opts):
138 '''create an unversioned archive of a repository revision
138 '''create an unversioned archive of a repository revision
139
139
140 By default, the revision used is the parent of the working
140 By default, the revision used is the parent of the working
141 directory; use -r/--rev to specify a different revision.
141 directory; use -r/--rev to specify a different revision.
142
142
143 To specify the type of archive to create, use -t/--type. Valid
143 To specify the type of archive to create, use -t/--type. Valid
144 types are:
144 types are:
145
145
146 "files" (default): a directory full of files
146 "files" (default): a directory full of files
147 "tar": tar archive, uncompressed
147 "tar": tar archive, uncompressed
148 "tbz2": tar archive, compressed using bzip2
148 "tbz2": tar archive, compressed using bzip2
149 "tgz": tar archive, compressed using gzip
149 "tgz": tar archive, compressed using gzip
150 "uzip": zip archive, uncompressed
150 "uzip": zip archive, uncompressed
151 "zip": zip archive, compressed using deflate
151 "zip": zip archive, compressed using deflate
152
152
153 The exact name of the destination archive or directory is given
153 The exact name of the destination archive or directory is given
154 using a format string; see 'hg help export' for details.
154 using a format string; see 'hg help export' for details.
155
155
156 Each member added to an archive file has a directory prefix
156 Each member added to an archive file has a directory prefix
157 prepended. Use -p/--prefix to specify a format string for the
157 prepended. Use -p/--prefix to specify a format string for the
158 prefix. The default is the basename of the archive, with suffixes
158 prefix. The default is the basename of the archive, with suffixes
159 removed.
159 removed.
160 '''
160 '''
161
161
162 ctx = repo[opts.get('rev')]
162 ctx = repo[opts.get('rev')]
163 if not ctx:
163 if not ctx:
164 raise util.Abort(_('no working directory: please specify a revision'))
164 raise util.Abort(_('no working directory: please specify a revision'))
165 node = ctx.node()
165 node = ctx.node()
166 dest = cmdutil.make_filename(repo, dest, node)
166 dest = cmdutil.make_filename(repo, dest, node)
167 if os.path.realpath(dest) == repo.root:
167 if os.path.realpath(dest) == repo.root:
168 raise util.Abort(_('repository root cannot be destination'))
168 raise util.Abort(_('repository root cannot be destination'))
169 matchfn = cmdutil.match(repo, [], opts)
169 matchfn = cmdutil.match(repo, [], opts)
170 kind = opts.get('type') or 'files'
170 kind = opts.get('type') or 'files'
171 prefix = opts.get('prefix')
171 prefix = opts.get('prefix')
172 if dest == '-':
172 if dest == '-':
173 if kind == 'files':
173 if kind == 'files':
174 raise util.Abort(_('cannot archive plain files to stdout'))
174 raise util.Abort(_('cannot archive plain files to stdout'))
175 dest = sys.stdout
175 dest = sys.stdout
176 if not prefix: prefix = os.path.basename(repo.root) + '-%h'
176 if not prefix: prefix = os.path.basename(repo.root) + '-%h'
177 prefix = cmdutil.make_filename(repo, prefix, node)
177 prefix = cmdutil.make_filename(repo, prefix, node)
178 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
178 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
179 matchfn, prefix)
179 matchfn, prefix)
180
180
181 def backout(ui, repo, node=None, rev=None, **opts):
181 def backout(ui, repo, node=None, rev=None, **opts):
182 '''reverse effect of earlier changeset
182 '''reverse effect of earlier changeset
183
183
184 Commit the backed out changes as a new changeset. The new
184 Commit the backed out changes as a new changeset. The new
185 changeset is a child of the backed out changeset.
185 changeset is a child of the backed out changeset.
186
186
187 If you backout a changeset other than the tip, a new head is
187 If you backout a changeset other than the tip, a new head is
188 created. This head will be the new tip and you should merge this
188 created. This head will be the new tip and you should merge this
189 backout changeset with another head.
189 backout changeset with another head.
190
190
191 The --merge option remembers the parent of the working directory
191 The --merge option remembers the parent of the working directory
192 before starting the backout, then merges the new head with that
192 before starting the backout, then merges the new head with that
193 changeset afterwards. This saves you from doing the merge by hand.
193 changeset afterwards. This saves you from doing the merge by hand.
194 The result of this merge is not committed, as with a normal merge.
194 The result of this merge is not committed, as with a normal merge.
195
195
196 See 'hg help dates' for a list of formats valid for -d/--date.
196 See 'hg help dates' for a list of formats valid for -d/--date.
197 '''
197 '''
198 if rev and node:
198 if rev and node:
199 raise util.Abort(_("please specify just one revision"))
199 raise util.Abort(_("please specify just one revision"))
200
200
201 if not rev:
201 if not rev:
202 rev = node
202 rev = node
203
203
204 if not rev:
204 if not rev:
205 raise util.Abort(_("please specify a revision to backout"))
205 raise util.Abort(_("please specify a revision to backout"))
206
206
207 date = opts.get('date')
207 date = opts.get('date')
208 if date:
208 if date:
209 opts['date'] = util.parsedate(date)
209 opts['date'] = util.parsedate(date)
210
210
211 cmdutil.bail_if_changed(repo)
211 cmdutil.bail_if_changed(repo)
212 node = repo.lookup(rev)
212 node = repo.lookup(rev)
213
213
214 op1, op2 = repo.dirstate.parents()
214 op1, op2 = repo.dirstate.parents()
215 a = repo.changelog.ancestor(op1, node)
215 a = repo.changelog.ancestor(op1, node)
216 if a != node:
216 if a != node:
217 raise util.Abort(_('cannot backout change on a different branch'))
217 raise util.Abort(_('cannot backout change on a different branch'))
218
218
219 p1, p2 = repo.changelog.parents(node)
219 p1, p2 = repo.changelog.parents(node)
220 if p1 == nullid:
220 if p1 == nullid:
221 raise util.Abort(_('cannot backout a change with no parents'))
221 raise util.Abort(_('cannot backout a change with no parents'))
222 if p2 != nullid:
222 if p2 != nullid:
223 if not opts.get('parent'):
223 if not opts.get('parent'):
224 raise util.Abort(_('cannot backout a merge changeset without '
224 raise util.Abort(_('cannot backout a merge changeset without '
225 '--parent'))
225 '--parent'))
226 p = repo.lookup(opts['parent'])
226 p = repo.lookup(opts['parent'])
227 if p not in (p1, p2):
227 if p not in (p1, p2):
228 raise util.Abort(_('%s is not a parent of %s') %
228 raise util.Abort(_('%s is not a parent of %s') %
229 (short(p), short(node)))
229 (short(p), short(node)))
230 parent = p
230 parent = p
231 else:
231 else:
232 if opts.get('parent'):
232 if opts.get('parent'):
233 raise util.Abort(_('cannot use --parent on non-merge changeset'))
233 raise util.Abort(_('cannot use --parent on non-merge changeset'))
234 parent = p1
234 parent = p1
235
235
236 # the backout should appear on the same branch
236 # the backout should appear on the same branch
237 branch = repo.dirstate.branch()
237 branch = repo.dirstate.branch()
238 hg.clean(repo, node, show_stats=False)
238 hg.clean(repo, node, show_stats=False)
239 repo.dirstate.setbranch(branch)
239 repo.dirstate.setbranch(branch)
240 revert_opts = opts.copy()
240 revert_opts = opts.copy()
241 revert_opts['date'] = None
241 revert_opts['date'] = None
242 revert_opts['all'] = True
242 revert_opts['all'] = True
243 revert_opts['rev'] = hex(parent)
243 revert_opts['rev'] = hex(parent)
244 revert_opts['no_backup'] = None
244 revert_opts['no_backup'] = None
245 revert(ui, repo, **revert_opts)
245 revert(ui, repo, **revert_opts)
246 commit_opts = opts.copy()
246 commit_opts = opts.copy()
247 commit_opts['addremove'] = False
247 commit_opts['addremove'] = False
248 if not commit_opts['message'] and not commit_opts['logfile']:
248 if not commit_opts['message'] and not commit_opts['logfile']:
249 commit_opts['message'] = _("Backed out changeset %s") % (short(node))
249 commit_opts['message'] = _("Backed out changeset %s") % (short(node))
250 commit_opts['force_editor'] = True
250 commit_opts['force_editor'] = True
251 commit(ui, repo, **commit_opts)
251 commit(ui, repo, **commit_opts)
252 def nice(node):
252 def nice(node):
253 return '%d:%s' % (repo.changelog.rev(node), short(node))
253 return '%d:%s' % (repo.changelog.rev(node), short(node))
254 ui.status(_('changeset %s backs out changeset %s\n') %
254 ui.status(_('changeset %s backs out changeset %s\n') %
255 (nice(repo.changelog.tip()), nice(node)))
255 (nice(repo.changelog.tip()), nice(node)))
256 if op1 != node:
256 if op1 != node:
257 hg.clean(repo, op1, show_stats=False)
257 hg.clean(repo, op1, show_stats=False)
258 if opts.get('merge'):
258 if opts.get('merge'):
259 ui.status(_('merging with changeset %s\n') % nice(repo.changelog.tip()))
259 ui.status(_('merging with changeset %s\n') % nice(repo.changelog.tip()))
260 hg.merge(repo, hex(repo.changelog.tip()))
260 hg.merge(repo, hex(repo.changelog.tip()))
261 else:
261 else:
262 ui.status(_('the backout changeset is a new head - '
262 ui.status(_('the backout changeset is a new head - '
263 'do not forget to merge\n'))
263 'do not forget to merge\n'))
264 ui.status(_('(use "backout --merge" '
264 ui.status(_('(use "backout --merge" '
265 'if you want to auto-merge)\n'))
265 'if you want to auto-merge)\n'))
266
266
267 def bisect(ui, repo, rev=None, extra=None, command=None,
267 def bisect(ui, repo, rev=None, extra=None, command=None,
268 reset=None, good=None, bad=None, skip=None, noupdate=None):
268 reset=None, good=None, bad=None, skip=None, noupdate=None):
269 """subdivision search of changesets
269 """subdivision search of changesets
270
270
271 This command helps to find changesets which introduce problems. To
271 This command helps to find changesets which introduce problems. To
272 use, mark the earliest changeset you know exhibits the problem as
272 use, mark the earliest changeset you know exhibits the problem as
273 bad, then mark the latest changeset which is free from the problem
273 bad, then mark the latest changeset which is free from the problem
274 as good. Bisect will update your working directory to a revision
274 as good. Bisect will update your working directory to a revision
275 for testing (unless the -U/--noupdate option is specified). Once
275 for testing (unless the -U/--noupdate option is specified). Once
276 you have performed tests, mark the working directory as good or
276 you have performed tests, mark the working directory as good or
277 bad, and bisect will either update to another candidate changeset
277 bad, and bisect will either update to another candidate changeset
278 or announce that it has found the bad revision.
278 or announce that it has found the bad revision.
279
279
280 As a shortcut, you can also use the revision argument to mark a
280 As a shortcut, you can also use the revision argument to mark a
281 revision as good or bad without checking it out first.
281 revision as good or bad without checking it out first.
282
282
283 If you supply a command, it will be used for automatic bisection.
283 If you supply a command, it will be used for automatic bisection.
284 Its exit status will be used to mark revisions as good or bad:
284 Its exit status will be used to mark revisions as good or bad:
285 status 0 means good, 125 means to skip the revision, 127
285 status 0 means good, 125 means to skip the revision, 127
286 (command not found) will abort the bisection, and any other
286 (command not found) will abort the bisection, and any other
287 non-zero exit status means the revision is bad.
287 non-zero exit status means the revision is bad.
288 """
288 """
289 def print_result(nodes, good):
289 def print_result(nodes, good):
290 displayer = cmdutil.show_changeset(ui, repo, {})
290 displayer = cmdutil.show_changeset(ui, repo, {})
291 if len(nodes) == 1:
291 if len(nodes) == 1:
292 # narrowed it down to a single revision
292 # narrowed it down to a single revision
293 if good:
293 if good:
294 ui.write(_("The first good revision is:\n"))
294 ui.write(_("The first good revision is:\n"))
295 else:
295 else:
296 ui.write(_("The first bad revision is:\n"))
296 ui.write(_("The first bad revision is:\n"))
297 displayer.show(repo[nodes[0]])
297 displayer.show(repo[nodes[0]])
298 else:
298 else:
299 # multiple possible revisions
299 # multiple possible revisions
300 if good:
300 if good:
301 ui.write(_("Due to skipped revisions, the first "
301 ui.write(_("Due to skipped revisions, the first "
302 "good revision could be any of:\n"))
302 "good revision could be any of:\n"))
303 else:
303 else:
304 ui.write(_("Due to skipped revisions, the first "
304 ui.write(_("Due to skipped revisions, the first "
305 "bad revision could be any of:\n"))
305 "bad revision could be any of:\n"))
306 for n in nodes:
306 for n in nodes:
307 displayer.show(repo[n])
307 displayer.show(repo[n])
308
308
309 def check_state(state, interactive=True):
309 def check_state(state, interactive=True):
310 if not state['good'] or not state['bad']:
310 if not state['good'] or not state['bad']:
311 if (good or bad or skip or reset) and interactive:
311 if (good or bad or skip or reset) and interactive:
312 return
312 return
313 if not state['good']:
313 if not state['good']:
314 raise util.Abort(_('cannot bisect (no known good revisions)'))
314 raise util.Abort(_('cannot bisect (no known good revisions)'))
315 else:
315 else:
316 raise util.Abort(_('cannot bisect (no known bad revisions)'))
316 raise util.Abort(_('cannot bisect (no known bad revisions)'))
317 return True
317 return True
318
318
319 # backward compatibility
319 # backward compatibility
320 if rev in "good bad reset init".split():
320 if rev in "good bad reset init".split():
321 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
321 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
322 cmd, rev, extra = rev, extra, None
322 cmd, rev, extra = rev, extra, None
323 if cmd == "good":
323 if cmd == "good":
324 good = True
324 good = True
325 elif cmd == "bad":
325 elif cmd == "bad":
326 bad = True
326 bad = True
327 else:
327 else:
328 reset = True
328 reset = True
329 elif extra or good + bad + skip + reset + bool(command) > 1:
329 elif extra or good + bad + skip + reset + bool(command) > 1:
330 raise util.Abort(_('incompatible arguments'))
330 raise util.Abort(_('incompatible arguments'))
331
331
332 if reset:
332 if reset:
333 p = repo.join("bisect.state")
333 p = repo.join("bisect.state")
334 if os.path.exists(p):
334 if os.path.exists(p):
335 os.unlink(p)
335 os.unlink(p)
336 return
336 return
337
337
338 state = hbisect.load_state(repo)
338 state = hbisect.load_state(repo)
339
339
340 if command:
340 if command:
341 commandpath = util.find_exe(command)
341 commandpath = util.find_exe(command)
342 if commandpath is None:
342 if commandpath is None:
343 raise util.Abort(_("cannot find executable: %s") % command)
343 raise util.Abort(_("cannot find executable: %s") % command)
344 changesets = 1
344 changesets = 1
345 try:
345 try:
346 while changesets:
346 while changesets:
347 # update state
347 # update state
348 status = subprocess.call([commandpath])
348 status = subprocess.call([commandpath])
349 if status == 125:
349 if status == 125:
350 transition = "skip"
350 transition = "skip"
351 elif status == 0:
351 elif status == 0:
352 transition = "good"
352 transition = "good"
353 # status < 0 means process was killed
353 # status < 0 means process was killed
354 elif status == 127:
354 elif status == 127:
355 raise util.Abort(_("failed to execute %s") % command)
355 raise util.Abort(_("failed to execute %s") % command)
356 elif status < 0:
356 elif status < 0:
357 raise util.Abort(_("%s killed") % command)
357 raise util.Abort(_("%s killed") % command)
358 else:
358 else:
359 transition = "bad"
359 transition = "bad"
360 ctx = repo[rev or '.']
360 ctx = repo[rev or '.']
361 state[transition].append(ctx.node())
361 state[transition].append(ctx.node())
362 ui.status(_('Changeset %d:%s: %s\n') % (ctx, ctx, transition))
362 ui.status(_('Changeset %d:%s: %s\n') % (ctx, ctx, transition))
363 check_state(state, interactive=False)
363 check_state(state, interactive=False)
364 # bisect
364 # bisect
365 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
365 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
366 # update to next check
366 # update to next check
367 cmdutil.bail_if_changed(repo)
367 cmdutil.bail_if_changed(repo)
368 hg.clean(repo, nodes[0], show_stats=False)
368 hg.clean(repo, nodes[0], show_stats=False)
369 finally:
369 finally:
370 hbisect.save_state(repo, state)
370 hbisect.save_state(repo, state)
371 return print_result(nodes, not status)
371 return print_result(nodes, not status)
372
372
373 # update state
373 # update state
374 node = repo.lookup(rev or '.')
374 node = repo.lookup(rev or '.')
375 if good:
375 if good:
376 state['good'].append(node)
376 state['good'].append(node)
377 elif bad:
377 elif bad:
378 state['bad'].append(node)
378 state['bad'].append(node)
379 elif skip:
379 elif skip:
380 state['skip'].append(node)
380 state['skip'].append(node)
381
381
382 hbisect.save_state(repo, state)
382 hbisect.save_state(repo, state)
383
383
384 if not check_state(state):
384 if not check_state(state):
385 return
385 return
386
386
387 # actually bisect
387 # actually bisect
388 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
388 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
389 if changesets == 0:
389 if changesets == 0:
390 print_result(nodes, good)
390 print_result(nodes, good)
391 else:
391 else:
392 assert len(nodes) == 1 # only a single node can be tested next
392 assert len(nodes) == 1 # only a single node can be tested next
393 node = nodes[0]
393 node = nodes[0]
394 # compute the approximate number of remaining tests
394 # compute the approximate number of remaining tests
395 tests, size = 0, 2
395 tests, size = 0, 2
396 while size <= changesets:
396 while size <= changesets:
397 tests, size = tests + 1, size * 2
397 tests, size = tests + 1, size * 2
398 rev = repo.changelog.rev(node)
398 rev = repo.changelog.rev(node)
399 ui.write(_("Testing changeset %s:%s "
399 ui.write(_("Testing changeset %s:%s "
400 "(%s changesets remaining, ~%s tests)\n")
400 "(%s changesets remaining, ~%s tests)\n")
401 % (rev, short(node), changesets, tests))
401 % (rev, short(node), changesets, tests))
402 if not noupdate:
402 if not noupdate:
403 cmdutil.bail_if_changed(repo)
403 cmdutil.bail_if_changed(repo)
404 return hg.clean(repo, node)
404 return hg.clean(repo, node)
405
405
406 def branch(ui, repo, label=None, **opts):
406 def branch(ui, repo, label=None, **opts):
407 """set or show the current branch name
407 """set or show the current branch name
408
408
409 With no argument, show the current branch name. With one argument,
409 With no argument, show the current branch name. With one argument,
410 set the working directory branch name (the branch will not exist
410 set the working directory branch name (the branch will not exist
411 in the repository until the next commit). Standard practice
411 in the repository until the next commit). Standard practice
412 recommends that primary development take place on the 'default'
412 recommends that primary development take place on the 'default'
413 branch.
413 branch.
414
414
415 Unless -f/--force is specified, branch will not let you set a
415 Unless -f/--force is specified, branch will not let you set a
416 branch name that already exists, even if it's inactive.
416 branch name that already exists, even if it's inactive.
417
417
418 Use -C/--clean to reset the working directory branch to that of
418 Use -C/--clean to reset the working directory branch to that of
419 the parent of the working directory, negating a previous branch
419 the parent of the working directory, negating a previous branch
420 change.
420 change.
421
421
422 Use the command 'hg update' to switch to an existing branch. Use
422 Use the command 'hg update' to switch to an existing branch. Use
423 'hg commit --close-branch' to mark this branch as closed.
423 'hg commit --close-branch' to mark this branch as closed.
424 """
424 """
425
425
426 if opts.get('clean'):
426 if opts.get('clean'):
427 label = repo[None].parents()[0].branch()
427 label = repo[None].parents()[0].branch()
428 repo.dirstate.setbranch(label)
428 repo.dirstate.setbranch(label)
429 ui.status(_('reset working directory to branch %s\n') % label)
429 ui.status(_('reset working directory to branch %s\n') % label)
430 elif label:
430 elif label:
431 if not opts.get('force') and label in repo.branchtags():
431 if not opts.get('force') and label in repo.branchtags():
432 if label not in [p.branch() for p in repo.parents()]:
432 if label not in [p.branch() for p in repo.parents()]:
433 raise util.Abort(_('a branch of the same name already exists'
433 raise util.Abort(_('a branch of the same name already exists'
434 ' (use --force to override)'))
434 ' (use --force to override)'))
435 repo.dirstate.setbranch(encoding.fromlocal(label))
435 repo.dirstate.setbranch(encoding.fromlocal(label))
436 ui.status(_('marked working directory as branch %s\n') % label)
436 ui.status(_('marked working directory as branch %s\n') % label)
437 else:
437 else:
438 ui.write("%s\n" % encoding.tolocal(repo.dirstate.branch()))
438 ui.write("%s\n" % encoding.tolocal(repo.dirstate.branch()))
439
439
440 def branches(ui, repo, active=False, closed=False):
440 def branches(ui, repo, active=False, closed=False):
441 """list repository named branches
441 """list repository named branches
442
442
443 List the repository's named branches, indicating which ones are
443 List the repository's named branches, indicating which ones are
444 inactive. If -c/--closed is specified, also list branches which have
444 inactive. If -c/--closed is specified, also list branches which have
445 been marked closed (see hg commit --close-branch).
445 been marked closed (see hg commit --close-branch).
446
446
447 If -a/--active is specified, only show active branches. A branch
447 If -a/--active is specified, only show active branches. A branch
448 is considered active if it contains repository heads.
448 is considered active if it contains repository heads.
449
449
450 Use the command 'hg update' to switch to an existing branch.
450 Use the command 'hg update' to switch to an existing branch.
451 """
451 """
452
452
453 hexfunc = ui.debugflag and hex or short
453 hexfunc = ui.debugflag and hex or short
454 activebranches = [encoding.tolocal(repo[n].branch())
454 activebranches = [encoding.tolocal(repo[n].branch())
455 for n in repo.heads()]
455 for n in repo.heads()]
456 def testactive(tag, node):
456 def testactive(tag, node):
457 realhead = tag in activebranches
457 realhead = tag in activebranches
458 open = node in repo.branchheads(tag, closed=False)
458 open = node in repo.branchheads(tag, closed=False)
459 return realhead and open
459 return realhead and open
460 branches = sorted([(testactive(tag, node), repo.changelog.rev(node), tag)
460 branches = sorted([(testactive(tag, node), repo.changelog.rev(node), tag)
461 for tag, node in repo.branchtags().items()],
461 for tag, node in repo.branchtags().items()],
462 reverse=True)
462 reverse=True)
463
463
464 for isactive, node, tag in branches:
464 for isactive, node, tag in branches:
465 if (not active) or isactive:
465 if (not active) or isactive:
466 if ui.quiet:
466 if ui.quiet:
467 ui.write("%s\n" % tag)
467 ui.write("%s\n" % tag)
468 else:
468 else:
469 hn = repo.lookup(node)
469 hn = repo.lookup(node)
470 if isactive:
470 if isactive:
471 notice = ''
471 notice = ''
472 elif hn not in repo.branchheads(tag, closed=False):
472 elif hn not in repo.branchheads(tag, closed=False):
473 if not closed:
473 if not closed:
474 continue
474 continue
475 notice = ' (closed)'
475 notice = ' (closed)'
476 else:
476 else:
477 notice = ' (inactive)'
477 notice = ' (inactive)'
478 rev = str(node).rjust(31 - encoding.colwidth(tag))
478 rev = str(node).rjust(31 - encoding.colwidth(tag))
479 data = tag, rev, hexfunc(hn), notice
479 data = tag, rev, hexfunc(hn), notice
480 ui.write("%s %s:%s%s\n" % data)
480 ui.write("%s %s:%s%s\n" % data)
481
481
482 def bundle(ui, repo, fname, dest=None, **opts):
482 def bundle(ui, repo, fname, dest=None, **opts):
483 """create a changegroup file
483 """create a changegroup file
484
484
485 Generate a compressed changegroup file collecting changesets not
485 Generate a compressed changegroup file collecting changesets not
486 known to be in another repository.
486 known to be in another repository.
487
487
488 If no destination repository is specified the destination is
488 If no destination repository is specified the destination is
489 assumed to have all the nodes specified by one or more --base
489 assumed to have all the nodes specified by one or more --base
490 parameters. To create a bundle containing all changesets, use
490 parameters. To create a bundle containing all changesets, use
491 -a/--all (or --base null).
491 -a/--all (or --base null).
492
492
493 You can change compression method with the -t/--type option.
493 You can change compression method with the -t/--type option.
494 The available compression methods are: none, bzip2, and
494 The available compression methods are: none, bzip2, and
495 gzip (by default, bundles are compressed using bzip2).
495 gzip (by default, bundles are compressed using bzip2).
496
496
497 The bundle file can then be transferred using conventional means
497 The bundle file can then be transferred using conventional means
498 and applied to another repository with the unbundle or pull
498 and applied to another repository with the unbundle or pull
499 command. This is useful when direct push and pull are not
499 command. This is useful when direct push and pull are not
500 available or when exporting an entire repository is undesirable.
500 available or when exporting an entire repository is undesirable.
501
501
502 Applying bundles preserves all changeset contents including
502 Applying bundles preserves all changeset contents including
503 permissions, copy/rename information, and revision history.
503 permissions, copy/rename information, and revision history.
504 """
504 """
505 revs = opts.get('rev') or None
505 revs = opts.get('rev') or None
506 if revs:
506 if revs:
507 revs = [repo.lookup(rev) for rev in revs]
507 revs = [repo.lookup(rev) for rev in revs]
508 if opts.get('all'):
508 if opts.get('all'):
509 base = ['null']
509 base = ['null']
510 else:
510 else:
511 base = opts.get('base')
511 base = opts.get('base')
512 if base:
512 if base:
513 if dest:
513 if dest:
514 raise util.Abort(_("--base is incompatible with specifying "
514 raise util.Abort(_("--base is incompatible with specifying "
515 "a destination"))
515 "a destination"))
516 base = [repo.lookup(rev) for rev in base]
516 base = [repo.lookup(rev) for rev in base]
517 # create the right base
517 # create the right base
518 # XXX: nodesbetween / changegroup* should be "fixed" instead
518 # XXX: nodesbetween / changegroup* should be "fixed" instead
519 o = []
519 o = []
520 has = set((nullid,))
520 has = set((nullid,))
521 for n in base:
521 for n in base:
522 has.update(repo.changelog.reachable(n))
522 has.update(repo.changelog.reachable(n))
523 if revs:
523 if revs:
524 visit = list(revs)
524 visit = list(revs)
525 else:
525 else:
526 visit = repo.changelog.heads()
526 visit = repo.changelog.heads()
527 seen = {}
527 seen = {}
528 while visit:
528 while visit:
529 n = visit.pop(0)
529 n = visit.pop(0)
530 parents = [p for p in repo.changelog.parents(n) if p not in has]
530 parents = [p for p in repo.changelog.parents(n) if p not in has]
531 if len(parents) == 0:
531 if len(parents) == 0:
532 o.insert(0, n)
532 o.insert(0, n)
533 else:
533 else:
534 for p in parents:
534 for p in parents:
535 if p not in seen:
535 if p not in seen:
536 seen[p] = 1
536 seen[p] = 1
537 visit.append(p)
537 visit.append(p)
538 else:
538 else:
539 dest, revs, checkout = hg.parseurl(
539 dest, revs, checkout = hg.parseurl(
540 ui.expandpath(dest or 'default-push', dest or 'default'), revs)
540 ui.expandpath(dest or 'default-push', dest or 'default'), revs)
541 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
541 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
542 o = repo.findoutgoing(other, force=opts.get('force'))
542 o = repo.findoutgoing(other, force=opts.get('force'))
543
543
544 if revs:
544 if revs:
545 cg = repo.changegroupsubset(o, revs, 'bundle')
545 cg = repo.changegroupsubset(o, revs, 'bundle')
546 else:
546 else:
547 cg = repo.changegroup(o, 'bundle')
547 cg = repo.changegroup(o, 'bundle')
548
548
549 bundletype = opts.get('type', 'bzip2').lower()
549 bundletype = opts.get('type', 'bzip2').lower()
550 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
550 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
551 bundletype = btypes.get(bundletype)
551 bundletype = btypes.get(bundletype)
552 if bundletype not in changegroup.bundletypes:
552 if bundletype not in changegroup.bundletypes:
553 raise util.Abort(_('unknown bundle type specified with --type'))
553 raise util.Abort(_('unknown bundle type specified with --type'))
554
554
555 changegroup.writebundle(cg, fname, bundletype)
555 changegroup.writebundle(cg, fname, bundletype)
556
556
557 def cat(ui, repo, file1, *pats, **opts):
557 def cat(ui, repo, file1, *pats, **opts):
558 """output the current or given revision of files
558 """output the current or given revision of files
559
559
560 Print the specified files as they were at the given revision. If
560 Print the specified files as they were at the given revision. If
561 no revision is given, the parent of the working directory is used,
561 no revision is given, the parent of the working directory is used,
562 or tip if no revision is checked out.
562 or tip if no revision is checked out.
563
563
564 Output may be to a file, in which case the name of the file is
564 Output may be to a file, in which case the name of the file is
565 given using a format string. The formatting rules are the same as
565 given using a format string. The formatting rules are the same as
566 for the export command, with the following additions:
566 for the export command, with the following additions:
567
567
568 %s basename of file being printed
568 %s basename of file being printed
569 %d dirname of file being printed, or '.' if in repository root
569 %d dirname of file being printed, or '.' if in repository root
570 %p root-relative path name of file being printed
570 %p root-relative path name of file being printed
571 """
571 """
572 ctx = repo[opts.get('rev')]
572 ctx = repo[opts.get('rev')]
573 err = 1
573 err = 1
574 m = cmdutil.match(repo, (file1,) + pats, opts)
574 m = cmdutil.match(repo, (file1,) + pats, opts)
575 for abs in ctx.walk(m):
575 for abs in ctx.walk(m):
576 fp = cmdutil.make_file(repo, opts.get('output'), ctx.node(), pathname=abs)
576 fp = cmdutil.make_file(repo, opts.get('output'), ctx.node(), pathname=abs)
577 data = ctx[abs].data()
577 data = ctx[abs].data()
578 if opts.get('decode'):
578 if opts.get('decode'):
579 data = repo.wwritedata(abs, data)
579 data = repo.wwritedata(abs, data)
580 fp.write(data)
580 fp.write(data)
581 err = 0
581 err = 0
582 return err
582 return err
583
583
584 def clone(ui, source, dest=None, **opts):
584 def clone(ui, source, dest=None, **opts):
585 """make a copy of an existing repository
585 """make a copy of an existing repository
586
586
587 Create a copy of an existing repository in a new directory.
587 Create a copy of an existing repository in a new directory.
588
588
589 If no destination directory name is specified, it defaults to the
589 If no destination directory name is specified, it defaults to the
590 basename of the source.
590 basename of the source.
591
591
592 The location of the source is added to the new repository's
592 The location of the source is added to the new repository's
593 .hg/hgrc file, as the default to be used for future pulls.
593 .hg/hgrc file, as the default to be used for future pulls.
594
594
595 If you use the -r/--rev option to clone up to a specific revision,
595 If you use the -r/--rev option to clone up to a specific revision,
596 no subsequent revisions (including subsequent tags) will be
596 no subsequent revisions (including subsequent tags) will be
597 present in the cloned repository. This option implies --pull, even
597 present in the cloned repository. This option implies --pull, even
598 on local repositories.
598 on local repositories.
599
599
600 By default, clone will check out the head of the 'default' branch.
600 By default, clone will check out the head of the 'default' branch.
601 If the -U/--noupdate option is used, the new clone will contain
601 If the -U/--noupdate option is used, the new clone will contain
602 only a repository (.hg) and no working copy (the working copy
602 only a repository (.hg) and no working copy (the working copy
603 parent is the null revision).
603 parent is the null revision).
604
604
605 See 'hg help urls' for valid source format details.
605 See 'hg help urls' for valid source format details.
606
606
607 It is possible to specify an ssh:// URL as the destination, but no
607 It is possible to specify an ssh:// URL as the destination, but no
608 .hg/hgrc and working directory will be created on the remote side.
608 .hg/hgrc and working directory will be created on the remote side.
609 Please see 'hg help urls' for important details about ssh:// URLs.
609 Please see 'hg help urls' for important details about ssh:// URLs.
610
610
611 For efficiency, hardlinks are used for cloning whenever the source
611 For efficiency, hardlinks are used for cloning whenever the source
612 and destination are on the same filesystem (note this applies only
612 and destination are on the same filesystem (note this applies only
613 to the repository data, not to the checked out files). Some
613 to the repository data, not to the checked out files). Some
614 filesystems, such as AFS, implement hardlinking incorrectly, but
614 filesystems, such as AFS, implement hardlinking incorrectly, but
615 do not report errors. In these cases, use the --pull option to
615 do not report errors. In these cases, use the --pull option to
616 avoid hardlinking.
616 avoid hardlinking.
617
617
618 In some cases, you can clone repositories and checked out files
618 In some cases, you can clone repositories and checked out files
619 using full hardlinks with
619 using full hardlinks with
620
620
621 $ cp -al REPO REPOCLONE
621 $ cp -al REPO REPOCLONE
622
622
623 This is the fastest way to clone, but it is not always safe. The
623 This is the fastest way to clone, but it is not always safe. The
624 operation is not atomic (making sure REPO is not modified during
624 operation is not atomic (making sure REPO is not modified during
625 the operation is up to you) and you have to make sure your editor
625 the operation is up to you) and you have to make sure your editor
626 breaks hardlinks (Emacs and most Linux Kernel tools do so). Also,
626 breaks hardlinks (Emacs and most Linux Kernel tools do so). Also,
627 this is not compatible with certain extensions that place their
627 this is not compatible with certain extensions that place their
628 metadata under the .hg directory, such as mq.
628 metadata under the .hg directory, such as mq.
629
629
630 """
630 """
631 hg.clone(cmdutil.remoteui(ui, opts), source, dest,
631 hg.clone(cmdutil.remoteui(ui, opts), source, dest,
632 pull=opts.get('pull'),
632 pull=opts.get('pull'),
633 stream=opts.get('uncompressed'),
633 stream=opts.get('uncompressed'),
634 rev=opts.get('rev'),
634 rev=opts.get('rev'),
635 update=not opts.get('noupdate'))
635 update=not opts.get('noupdate'))
636
636
637 def commit(ui, repo, *pats, **opts):
637 def commit(ui, repo, *pats, **opts):
638 """commit the specified files or all outstanding changes
638 """commit the specified files or all outstanding changes
639
639
640 Commit changes to the given files into the repository. Unlike a
640 Commit changes to the given files into the repository. Unlike a
641 centralized RCS, this operation is a local operation. See hg push
641 centralized RCS, this operation is a local operation. See hg push
642 for a way to actively distribute your changes.
642 for a way to actively distribute your changes.
643
643
644 If a list of files is omitted, all changes reported by "hg status"
644 If a list of files is omitted, all changes reported by "hg status"
645 will be committed.
645 will be committed.
646
646
647 If you are committing the result of a merge, do not provide any
647 If you are committing the result of a merge, do not provide any
648 filenames or -I/-X filters.
648 filenames or -I/-X filters.
649
649
650 If no commit message is specified, the configured editor is
650 If no commit message is specified, the configured editor is
651 started to prompt you for a message.
651 started to prompt you for a message.
652
652
653 See 'hg help dates' for a list of formats valid for -d/--date.
653 See 'hg help dates' for a list of formats valid for -d/--date.
654 """
654 """
655 extra = {}
655 extra = {}
656 if opts.get('close_branch'):
656 if opts.get('close_branch'):
657 extra['close'] = 1
657 extra['close'] = 1
658 e = cmdutil.commiteditor
658 e = cmdutil.commiteditor
659 if opts.get('force_editor'):
659 if opts.get('force_editor'):
660 e = cmdutil.commitforceeditor
660 e = cmdutil.commitforceeditor
661
661
662 def commitfunc(ui, repo, message, match, opts):
662 def commitfunc(ui, repo, message, match, opts):
663 return repo.commit(message, opts.get('user'), opts.get('date'), match,
663 return repo.commit(message, opts.get('user'), opts.get('date'), match,
664 editor=e, extra=extra)
664 editor=e, extra=extra)
665
665
666 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
666 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
667 if not node:
667 if not node:
668 ui.status(_("nothing changed\n"))
668 ui.status(_("nothing changed\n"))
669 return
669 return
670 cl = repo.changelog
670 cl = repo.changelog
671 rev = cl.rev(node)
671 rev = cl.rev(node)
672 parents = cl.parentrevs(rev)
672 parents = cl.parentrevs(rev)
673 if rev - 1 in parents:
673 if rev - 1 in parents:
674 # one of the parents was the old tip
674 # one of the parents was the old tip
675 pass
675 pass
676 elif (parents == (nullrev, nullrev) or
676 elif (parents == (nullrev, nullrev) or
677 len(cl.heads(cl.node(parents[0]))) > 1 and
677 len(cl.heads(cl.node(parents[0]))) > 1 and
678 (parents[1] == nullrev or len(cl.heads(cl.node(parents[1]))) > 1)):
678 (parents[1] == nullrev or len(cl.heads(cl.node(parents[1]))) > 1)):
679 ui.status(_('created new head\n'))
679 ui.status(_('created new head\n'))
680
680
681 if ui.debugflag:
681 if ui.debugflag:
682 ui.write(_('committed changeset %d:%s\n') % (rev,hex(node)))
682 ui.write(_('committed changeset %d:%s\n') % (rev,hex(node)))
683 elif ui.verbose:
683 elif ui.verbose:
684 ui.write(_('committed changeset %d:%s\n') % (rev,short(node)))
684 ui.write(_('committed changeset %d:%s\n') % (rev,short(node)))
685
685
686 def copy(ui, repo, *pats, **opts):
686 def copy(ui, repo, *pats, **opts):
687 """mark files as copied for the next commit
687 """mark files as copied for the next commit
688
688
689 Mark dest as having copies of source files. If dest is a
689 Mark dest as having copies of source files. If dest is a
690 directory, copies are put in that directory. If dest is a file,
690 directory, copies are put in that directory. If dest is a file,
691 the source must be a single file.
691 the source must be a single file.
692
692
693 By default, this command copies the contents of files as they
693 By default, this command copies the contents of files as they
694 exist in the working directory. If invoked with -A/--after, the
694 exist in the working directory. If invoked with -A/--after, the
695 operation is recorded, but no copying is performed.
695 operation is recorded, but no copying is performed.
696
696
697 This command takes effect with the next commit. To undo a copy
697 This command takes effect with the next commit. To undo a copy
698 before that, see hg revert.
698 before that, see hg revert.
699 """
699 """
700 wlock = repo.wlock(False)
700 wlock = repo.wlock(False)
701 try:
701 try:
702 return cmdutil.copy(ui, repo, pats, opts)
702 return cmdutil.copy(ui, repo, pats, opts)
703 finally:
703 finally:
704 wlock.release()
704 wlock.release()
705
705
706 def debugancestor(ui, repo, *args):
706 def debugancestor(ui, repo, *args):
707 """find the ancestor revision of two revisions in a given index"""
707 """find the ancestor revision of two revisions in a given index"""
708 if len(args) == 3:
708 if len(args) == 3:
709 index, rev1, rev2 = args
709 index, rev1, rev2 = args
710 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index)
710 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index)
711 lookup = r.lookup
711 lookup = r.lookup
712 elif len(args) == 2:
712 elif len(args) == 2:
713 if not repo:
713 if not repo:
714 raise util.Abort(_("There is no Mercurial repository here "
714 raise util.Abort(_("There is no Mercurial repository here "
715 "(.hg not found)"))
715 "(.hg not found)"))
716 rev1, rev2 = args
716 rev1, rev2 = args
717 r = repo.changelog
717 r = repo.changelog
718 lookup = repo.lookup
718 lookup = repo.lookup
719 else:
719 else:
720 raise util.Abort(_('either two or three arguments required'))
720 raise util.Abort(_('either two or three arguments required'))
721 a = r.ancestor(lookup(rev1), lookup(rev2))
721 a = r.ancestor(lookup(rev1), lookup(rev2))
722 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
722 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
723
723
724 def debugcommands(ui, cmd='', *args):
724 def debugcommands(ui, cmd='', *args):
725 for cmd, vals in sorted(table.iteritems()):
725 for cmd, vals in sorted(table.iteritems()):
726 cmd = cmd.split('|')[0].strip('^')
726 cmd = cmd.split('|')[0].strip('^')
727 opts = ', '.join([i[1] for i in vals[1]])
727 opts = ', '.join([i[1] for i in vals[1]])
728 ui.write('%s: %s\n' % (cmd, opts))
728 ui.write('%s: %s\n' % (cmd, opts))
729
729
730 def debugcomplete(ui, cmd='', **opts):
730 def debugcomplete(ui, cmd='', **opts):
731 """returns the completion list associated with the given command"""
731 """returns the completion list associated with the given command"""
732
732
733 if opts.get('options'):
733 if opts.get('options'):
734 options = []
734 options = []
735 otables = [globalopts]
735 otables = [globalopts]
736 if cmd:
736 if cmd:
737 aliases, entry = cmdutil.findcmd(cmd, table, False)
737 aliases, entry = cmdutil.findcmd(cmd, table, False)
738 otables.append(entry[1])
738 otables.append(entry[1])
739 for t in otables:
739 for t in otables:
740 for o in t:
740 for o in t:
741 if o[0]:
741 if o[0]:
742 options.append('-%s' % o[0])
742 options.append('-%s' % o[0])
743 options.append('--%s' % o[1])
743 options.append('--%s' % o[1])
744 ui.write("%s\n" % "\n".join(options))
744 ui.write("%s\n" % "\n".join(options))
745 return
745 return
746
746
747 cmdlist = cmdutil.findpossible(cmd, table)
747 cmdlist = cmdutil.findpossible(cmd, table)
748 if ui.verbose:
748 if ui.verbose:
749 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
749 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
750 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
750 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
751
751
752 def debugfsinfo(ui, path = "."):
752 def debugfsinfo(ui, path = "."):
753 file('.debugfsinfo', 'w').write('')
753 file('.debugfsinfo', 'w').write('')
754 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
754 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
755 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
755 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
756 ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
756 ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
757 and 'yes' or 'no'))
757 and 'yes' or 'no'))
758 os.unlink('.debugfsinfo')
758 os.unlink('.debugfsinfo')
759
759
760 def debugrebuildstate(ui, repo, rev="tip"):
760 def debugrebuildstate(ui, repo, rev="tip"):
761 """rebuild the dirstate as it would look like for the given revision"""
761 """rebuild the dirstate as it would look like for the given revision"""
762 ctx = repo[rev]
762 ctx = repo[rev]
763 wlock = repo.wlock()
763 wlock = repo.wlock()
764 try:
764 try:
765 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
765 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
766 finally:
766 finally:
767 wlock.release()
767 wlock.release()
768
768
769 def debugcheckstate(ui, repo):
769 def debugcheckstate(ui, repo):
770 """validate the correctness of the current dirstate"""
770 """validate the correctness of the current dirstate"""
771 parent1, parent2 = repo.dirstate.parents()
771 parent1, parent2 = repo.dirstate.parents()
772 m1 = repo[parent1].manifest()
772 m1 = repo[parent1].manifest()
773 m2 = repo[parent2].manifest()
773 m2 = repo[parent2].manifest()
774 errors = 0
774 errors = 0
775 for f in repo.dirstate:
775 for f in repo.dirstate:
776 state = repo.dirstate[f]
776 state = repo.dirstate[f]
777 if state in "nr" and f not in m1:
777 if state in "nr" and f not in m1:
778 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
778 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
779 errors += 1
779 errors += 1
780 if state in "a" and f in m1:
780 if state in "a" and f in m1:
781 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
781 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
782 errors += 1
782 errors += 1
783 if state in "m" and f not in m1 and f not in m2:
783 if state in "m" and f not in m1 and f not in m2:
784 ui.warn(_("%s in state %s, but not in either manifest\n") %
784 ui.warn(_("%s in state %s, but not in either manifest\n") %
785 (f, state))
785 (f, state))
786 errors += 1
786 errors += 1
787 for f in m1:
787 for f in m1:
788 state = repo.dirstate[f]
788 state = repo.dirstate[f]
789 if state not in "nrm":
789 if state not in "nrm":
790 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
790 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
791 errors += 1
791 errors += 1
792 if errors:
792 if errors:
793 error = _(".hg/dirstate inconsistent with current parent's manifest")
793 error = _(".hg/dirstate inconsistent with current parent's manifest")
794 raise util.Abort(error)
794 raise util.Abort(error)
795
795
796 def showconfig(ui, repo, *values, **opts):
796 def showconfig(ui, repo, *values, **opts):
797 """show combined config settings from all hgrc files
797 """show combined config settings from all hgrc files
798
798
799 With no arguments, print names and values of all config items.
799 With no arguments, print names and values of all config items.
800
800
801 With one argument of the form section.name, print just the value
801 With one argument of the form section.name, print just the value
802 of that config item.
802 of that config item.
803
803
804 With multiple arguments, print names and values of all config
804 With multiple arguments, print names and values of all config
805 items with matching section names.
805 items with matching section names.
806
806
807 With --debug, the source (filename and line number) is printed
807 With --debug, the source (filename and line number) is printed
808 for each config item.
808 for each config item.
809 """
809 """
810
810
811 untrusted = bool(opts.get('untrusted'))
811 untrusted = bool(opts.get('untrusted'))
812 if values:
812 if values:
813 if len([v for v in values if '.' in v]) > 1:
813 if len([v for v in values if '.' in v]) > 1:
814 raise util.Abort(_('only one config item permitted'))
814 raise util.Abort(_('only one config item permitted'))
815 for section, name, value in ui.walkconfig(untrusted=untrusted):
815 for section, name, value in ui.walkconfig(untrusted=untrusted):
816 sectname = section + '.' + name
816 sectname = section + '.' + name
817 if values:
817 if values:
818 for v in values:
818 for v in values:
819 if v == section:
819 if v == section:
820 ui.debug('%s: ' %
820 ui.debug('%s: ' %
821 ui.configsource(section, name, untrusted))
821 ui.configsource(section, name, untrusted))
822 ui.write('%s=%s\n' % (sectname, value))
822 ui.write('%s=%s\n' % (sectname, value))
823 elif v == sectname:
823 elif v == sectname:
824 ui.debug('%s: ' %
824 ui.debug('%s: ' %
825 ui.configsource(section, name, untrusted))
825 ui.configsource(section, name, untrusted))
826 ui.write(value, '\n')
826 ui.write(value, '\n')
827 else:
827 else:
828 ui.debug('%s: ' %
828 ui.debug('%s: ' %
829 ui.configsource(section, name, untrusted))
829 ui.configsource(section, name, untrusted))
830 ui.write('%s=%s\n' % (sectname, value))
830 ui.write('%s=%s\n' % (sectname, value))
831
831
832 def debugsetparents(ui, repo, rev1, rev2=None):
832 def debugsetparents(ui, repo, rev1, rev2=None):
833 """manually set the parents of the current working directory
833 """manually set the parents of the current working directory
834
834
835 This is useful for writing repository conversion tools, but should
835 This is useful for writing repository conversion tools, but should
836 be used with care.
836 be used with care.
837 """
837 """
838
838
839 if not rev2:
839 if not rev2:
840 rev2 = hex(nullid)
840 rev2 = hex(nullid)
841
841
842 wlock = repo.wlock()
842 wlock = repo.wlock()
843 try:
843 try:
844 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
844 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
845 finally:
845 finally:
846 wlock.release()
846 wlock.release()
847
847
848 def debugstate(ui, repo, nodates=None):
848 def debugstate(ui, repo, nodates=None):
849 """show the contents of the current dirstate"""
849 """show the contents of the current dirstate"""
850 timestr = ""
850 timestr = ""
851 showdate = not nodates
851 showdate = not nodates
852 for file_, ent in sorted(repo.dirstate._map.iteritems()):
852 for file_, ent in sorted(repo.dirstate._map.iteritems()):
853 if showdate:
853 if showdate:
854 if ent[3] == -1:
854 if ent[3] == -1:
855 # Pad or slice to locale representation
855 # Pad or slice to locale representation
856 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ", time.localtime(0)))
856 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ", time.localtime(0)))
857 timestr = 'unset'
857 timestr = 'unset'
858 timestr = timestr[:locale_len] + ' '*(locale_len - len(timestr))
858 timestr = timestr[:locale_len] + ' '*(locale_len - len(timestr))
859 else:
859 else:
860 timestr = time.strftime("%Y-%m-%d %H:%M:%S ", time.localtime(ent[3]))
860 timestr = time.strftime("%Y-%m-%d %H:%M:%S ", time.localtime(ent[3]))
861 if ent[1] & 020000:
861 if ent[1] & 020000:
862 mode = 'lnk'
862 mode = 'lnk'
863 else:
863 else:
864 mode = '%3o' % (ent[1] & 0777)
864 mode = '%3o' % (ent[1] & 0777)
865 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
865 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
866 for f in repo.dirstate.copies():
866 for f in repo.dirstate.copies():
867 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
867 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
868
868
869 def debugsub(ui, repo, rev=None):
869 def debugsub(ui, repo, rev=None):
870 if rev == '':
870 if rev == '':
871 rev = None
871 rev = None
872 for k,v in sorted(repo[rev].substate.items()):
872 for k,v in sorted(repo[rev].substate.items()):
873 ui.write('path %s\n' % k)
873 ui.write('path %s\n' % k)
874 ui.write(' source %s\n' % v[0])
874 ui.write(' source %s\n' % v[0])
875 ui.write(' revision %s\n' % v[1])
875 ui.write(' revision %s\n' % v[1])
876
876
877 def debugdata(ui, file_, rev):
877 def debugdata(ui, file_, rev):
878 """dump the contents of a data file revision"""
878 """dump the contents of a data file revision"""
879 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_[:-2] + ".i")
879 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_[:-2] + ".i")
880 try:
880 try:
881 ui.write(r.revision(r.lookup(rev)))
881 ui.write(r.revision(r.lookup(rev)))
882 except KeyError:
882 except KeyError:
883 raise util.Abort(_('invalid revision identifier %s') % rev)
883 raise util.Abort(_('invalid revision identifier %s') % rev)
884
884
885 def debugdate(ui, date, range=None, **opts):
885 def debugdate(ui, date, range=None, **opts):
886 """parse and display a date"""
886 """parse and display a date"""
887 if opts["extended"]:
887 if opts["extended"]:
888 d = util.parsedate(date, util.extendeddateformats)
888 d = util.parsedate(date, util.extendeddateformats)
889 else:
889 else:
890 d = util.parsedate(date)
890 d = util.parsedate(date)
891 ui.write("internal: %s %s\n" % d)
891 ui.write("internal: %s %s\n" % d)
892 ui.write("standard: %s\n" % util.datestr(d))
892 ui.write("standard: %s\n" % util.datestr(d))
893 if range:
893 if range:
894 m = util.matchdate(range)
894 m = util.matchdate(range)
895 ui.write("match: %s\n" % m(d[0]))
895 ui.write("match: %s\n" % m(d[0]))
896
896
897 def debugindex(ui, file_):
897 def debugindex(ui, file_):
898 """dump the contents of an index file"""
898 """dump the contents of an index file"""
899 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
899 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
900 ui.write(" rev offset length base linkrev"
900 ui.write(" rev offset length base linkrev"
901 " nodeid p1 p2\n")
901 " nodeid p1 p2\n")
902 for i in r:
902 for i in r:
903 node = r.node(i)
903 node = r.node(i)
904 try:
904 try:
905 pp = r.parents(node)
905 pp = r.parents(node)
906 except:
906 except:
907 pp = [nullid, nullid]
907 pp = [nullid, nullid]
908 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
908 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
909 i, r.start(i), r.length(i), r.base(i), r.linkrev(i),
909 i, r.start(i), r.length(i), r.base(i), r.linkrev(i),
910 short(node), short(pp[0]), short(pp[1])))
910 short(node), short(pp[0]), short(pp[1])))
911
911
912 def debugindexdot(ui, file_):
912 def debugindexdot(ui, file_):
913 """dump an index DAG as a graphviz dot file"""
913 """dump an index DAG as a graphviz dot file"""
914 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
914 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
915 ui.write("digraph G {\n")
915 ui.write("digraph G {\n")
916 for i in r:
916 for i in r:
917 node = r.node(i)
917 node = r.node(i)
918 pp = r.parents(node)
918 pp = r.parents(node)
919 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
919 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
920 if pp[1] != nullid:
920 if pp[1] != nullid:
921 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
921 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
922 ui.write("}\n")
922 ui.write("}\n")
923
923
924 def debuginstall(ui):
924 def debuginstall(ui):
925 '''test Mercurial installation'''
925 '''test Mercurial installation'''
926
926
927 def writetemp(contents):
927 def writetemp(contents):
928 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
928 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
929 f = os.fdopen(fd, "wb")
929 f = os.fdopen(fd, "wb")
930 f.write(contents)
930 f.write(contents)
931 f.close()
931 f.close()
932 return name
932 return name
933
933
934 problems = 0
934 problems = 0
935
935
936 # encoding
936 # encoding
937 ui.status(_("Checking encoding (%s)...\n") % encoding.encoding)
937 ui.status(_("Checking encoding (%s)...\n") % encoding.encoding)
938 try:
938 try:
939 encoding.fromlocal("test")
939 encoding.fromlocal("test")
940 except util.Abort, inst:
940 except util.Abort, inst:
941 ui.write(" %s\n" % inst)
941 ui.write(" %s\n" % inst)
942 ui.write(_(" (check that your locale is properly set)\n"))
942 ui.write(_(" (check that your locale is properly set)\n"))
943 problems += 1
943 problems += 1
944
944
945 # compiled modules
945 # compiled modules
946 ui.status(_("Checking extensions...\n"))
946 ui.status(_("Checking extensions...\n"))
947 try:
947 try:
948 import bdiff, mpatch, base85
948 import bdiff, mpatch, base85
949 except Exception, inst:
949 except Exception, inst:
950 ui.write(" %s\n" % inst)
950 ui.write(" %s\n" % inst)
951 ui.write(_(" One or more extensions could not be found"))
951 ui.write(_(" One or more extensions could not be found"))
952 ui.write(_(" (check that you compiled the extensions)\n"))
952 ui.write(_(" (check that you compiled the extensions)\n"))
953 problems += 1
953 problems += 1
954
954
955 # templates
955 # templates
956 ui.status(_("Checking templates...\n"))
956 ui.status(_("Checking templates...\n"))
957 try:
957 try:
958 import templater
958 import templater
959 templater.templater(templater.templatepath("map-cmdline.default"))
959 templater.templater(templater.templatepath("map-cmdline.default"))
960 except Exception, inst:
960 except Exception, inst:
961 ui.write(" %s\n" % inst)
961 ui.write(" %s\n" % inst)
962 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
962 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
963 problems += 1
963 problems += 1
964
964
965 # patch
965 # patch
966 ui.status(_("Checking patch...\n"))
966 ui.status(_("Checking patch...\n"))
967 patchproblems = 0
967 patchproblems = 0
968 a = "1\n2\n3\n4\n"
968 a = "1\n2\n3\n4\n"
969 b = "1\n2\n3\ninsert\n4\n"
969 b = "1\n2\n3\ninsert\n4\n"
970 fa = writetemp(a)
970 fa = writetemp(a)
971 d = mdiff.unidiff(a, None, b, None, os.path.basename(fa),
971 d = mdiff.unidiff(a, None, b, None, os.path.basename(fa),
972 os.path.basename(fa))
972 os.path.basename(fa))
973 fd = writetemp(d)
973 fd = writetemp(d)
974
974
975 files = {}
975 files = {}
976 try:
976 try:
977 patch.patch(fd, ui, cwd=os.path.dirname(fa), files=files)
977 patch.patch(fd, ui, cwd=os.path.dirname(fa), files=files)
978 except util.Abort, e:
978 except util.Abort, e:
979 ui.write(_(" patch call failed:\n"))
979 ui.write(_(" patch call failed:\n"))
980 ui.write(" " + str(e) + "\n")
980 ui.write(" " + str(e) + "\n")
981 patchproblems += 1
981 patchproblems += 1
982 else:
982 else:
983 if list(files) != [os.path.basename(fa)]:
983 if list(files) != [os.path.basename(fa)]:
984 ui.write(_(" unexpected patch output!\n"))
984 ui.write(_(" unexpected patch output!\n"))
985 patchproblems += 1
985 patchproblems += 1
986 a = file(fa).read()
986 a = file(fa).read()
987 if a != b:
987 if a != b:
988 ui.write(_(" patch test failed!\n"))
988 ui.write(_(" patch test failed!\n"))
989 patchproblems += 1
989 patchproblems += 1
990
990
991 if patchproblems:
991 if patchproblems:
992 if ui.config('ui', 'patch'):
992 if ui.config('ui', 'patch'):
993 ui.write(_(" (Current patch tool may be incompatible with patch,"
993 ui.write(_(" (Current patch tool may be incompatible with patch,"
994 " or misconfigured. Please check your .hgrc file)\n"))
994 " or misconfigured. Please check your .hgrc file)\n"))
995 else:
995 else:
996 ui.write(_(" Internal patcher failure, please report this error"
996 ui.write(_(" Internal patcher failure, please report this error"
997 " to http://mercurial.selenic.com/bts/\n"))
997 " to http://mercurial.selenic.com/bts/\n"))
998 problems += patchproblems
998 problems += patchproblems
999
999
1000 os.unlink(fa)
1000 os.unlink(fa)
1001 os.unlink(fd)
1001 os.unlink(fd)
1002
1002
1003 # editor
1003 # editor
1004 ui.status(_("Checking commit editor...\n"))
1004 ui.status(_("Checking commit editor...\n"))
1005 editor = ui.geteditor()
1005 editor = ui.geteditor()
1006 cmdpath = util.find_exe(editor) or util.find_exe(editor.split()[0])
1006 cmdpath = util.find_exe(editor) or util.find_exe(editor.split()[0])
1007 if not cmdpath:
1007 if not cmdpath:
1008 if editor == 'vi':
1008 if editor == 'vi':
1009 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
1009 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
1010 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
1010 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
1011 else:
1011 else:
1012 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
1012 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
1013 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
1013 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
1014 problems += 1
1014 problems += 1
1015
1015
1016 # check username
1016 # check username
1017 ui.status(_("Checking username...\n"))
1017 ui.status(_("Checking username...\n"))
1018 user = os.environ.get("HGUSER")
1018 user = os.environ.get("HGUSER")
1019 if user is None:
1019 if user is None:
1020 user = ui.config("ui", "username")
1020 user = ui.config("ui", "username")
1021 if user is None:
1021 if user is None:
1022 user = os.environ.get("EMAIL")
1022 user = os.environ.get("EMAIL")
1023 if not user:
1023 if not user:
1024 ui.warn(" ")
1024 ui.warn(" ")
1025 ui.username()
1025 ui.username()
1026 ui.write(_(" (specify a username in your .hgrc file)\n"))
1026 ui.write(_(" (specify a username in your .hgrc file)\n"))
1027
1027
1028 if not problems:
1028 if not problems:
1029 ui.status(_("No problems detected\n"))
1029 ui.status(_("No problems detected\n"))
1030 else:
1030 else:
1031 ui.write(_("%s problems detected,"
1031 ui.write(_("%s problems detected,"
1032 " please check your install!\n") % problems)
1032 " please check your install!\n") % problems)
1033
1033
1034 return problems
1034 return problems
1035
1035
1036 def debugrename(ui, repo, file1, *pats, **opts):
1036 def debugrename(ui, repo, file1, *pats, **opts):
1037 """dump rename information"""
1037 """dump rename information"""
1038
1038
1039 ctx = repo[opts.get('rev')]
1039 ctx = repo[opts.get('rev')]
1040 m = cmdutil.match(repo, (file1,) + pats, opts)
1040 m = cmdutil.match(repo, (file1,) + pats, opts)
1041 for abs in ctx.walk(m):
1041 for abs in ctx.walk(m):
1042 fctx = ctx[abs]
1042 fctx = ctx[abs]
1043 o = fctx.filelog().renamed(fctx.filenode())
1043 o = fctx.filelog().renamed(fctx.filenode())
1044 rel = m.rel(abs)
1044 rel = m.rel(abs)
1045 if o:
1045 if o:
1046 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
1046 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
1047 else:
1047 else:
1048 ui.write(_("%s not renamed\n") % rel)
1048 ui.write(_("%s not renamed\n") % rel)
1049
1049
1050 def debugwalk(ui, repo, *pats, **opts):
1050 def debugwalk(ui, repo, *pats, **opts):
1051 """show how files match on given patterns"""
1051 """show how files match on given patterns"""
1052 m = cmdutil.match(repo, pats, opts)
1052 m = cmdutil.match(repo, pats, opts)
1053 items = list(repo.walk(m))
1053 items = list(repo.walk(m))
1054 if not items:
1054 if not items:
1055 return
1055 return
1056 fmt = 'f %%-%ds %%-%ds %%s' % (
1056 fmt = 'f %%-%ds %%-%ds %%s' % (
1057 max([len(abs) for abs in items]),
1057 max([len(abs) for abs in items]),
1058 max([len(m.rel(abs)) for abs in items]))
1058 max([len(m.rel(abs)) for abs in items]))
1059 for abs in items:
1059 for abs in items:
1060 line = fmt % (abs, m.rel(abs), m.exact(abs) and 'exact' or '')
1060 line = fmt % (abs, m.rel(abs), m.exact(abs) and 'exact' or '')
1061 ui.write("%s\n" % line.rstrip())
1061 ui.write("%s\n" % line.rstrip())
1062
1062
1063 def diff(ui, repo, *pats, **opts):
1063 def diff(ui, repo, *pats, **opts):
1064 """diff repository (or selected files)
1064 """diff repository (or selected files)
1065
1065
1066 Show differences between revisions for the specified files.
1066 Show differences between revisions for the specified files.
1067
1067
1068 Differences between files are shown using the unified diff format.
1068 Differences between files are shown using the unified diff format.
1069
1069
1070 NOTE: diff may generate unexpected results for merges, as it will
1070 NOTE: diff may generate unexpected results for merges, as it will
1071 default to comparing against the working directory's first parent
1071 default to comparing against the working directory's first parent
1072 changeset if no revisions are specified.
1072 changeset if no revisions are specified.
1073
1073
1074 When two revision arguments are given, then changes are shown
1074 When two revision arguments are given, then changes are shown
1075 between those revisions. If only one revision is specified then
1075 between those revisions. If only one revision is specified then
1076 that revision is compared to the working directory, and, when no
1076 that revision is compared to the working directory, and, when no
1077 revisions are specified, the working directory files are compared
1077 revisions are specified, the working directory files are compared
1078 to its parent.
1078 to its parent.
1079
1079
1080 Without the -a/--text option, diff will avoid generating diffs of
1080 Without the -a/--text option, diff will avoid generating diffs of
1081 files it detects as binary. With -a, diff will generate a diff
1081 files it detects as binary. With -a, diff will generate a diff
1082 anyway, probably with undesirable results.
1082 anyway, probably with undesirable results.
1083
1083
1084 Use the -g/--git option to generate diffs in the git extended diff
1084 Use the -g/--git option to generate diffs in the git extended diff
1085 format. For more information, read 'hg help diffs'.
1085 format. For more information, read 'hg help diffs'.
1086 """
1086 """
1087
1087
1088 revs = opts.get('rev')
1088 revs = opts.get('rev')
1089 change = opts.get('change')
1089 change = opts.get('change')
1090
1090
1091 if revs and change:
1091 if revs and change:
1092 msg = _('cannot specify --rev and --change at the same time')
1092 msg = _('cannot specify --rev and --change at the same time')
1093 raise util.Abort(msg)
1093 raise util.Abort(msg)
1094 elif change:
1094 elif change:
1095 node2 = repo.lookup(change)
1095 node2 = repo.lookup(change)
1096 node1 = repo[node2].parents()[0].node()
1096 node1 = repo[node2].parents()[0].node()
1097 else:
1097 else:
1098 node1, node2 = cmdutil.revpair(repo, revs)
1098 node1, node2 = cmdutil.revpair(repo, revs)
1099
1099
1100 m = cmdutil.match(repo, pats, opts)
1100 m = cmdutil.match(repo, pats, opts)
1101 it = patch.diff(repo, node1, node2, match=m, opts=patch.diffopts(ui, opts))
1101 it = patch.diff(repo, node1, node2, match=m, opts=patch.diffopts(ui, opts))
1102 for chunk in it:
1102 for chunk in it:
1103 ui.write(chunk)
1103 ui.write(chunk)
1104
1104
1105 def export(ui, repo, *changesets, **opts):
1105 def export(ui, repo, *changesets, **opts):
1106 """dump the header and diffs for one or more changesets
1106 """dump the header and diffs for one or more changesets
1107
1107
1108 Print the changeset header and diffs for one or more revisions.
1108 Print the changeset header and diffs for one or more revisions.
1109
1109
1110 The information shown in the changeset header is: author,
1110 The information shown in the changeset header is: author,
1111 changeset hash, parent(s) and commit comment.
1111 changeset hash, parent(s) and commit comment.
1112
1112
1113 NOTE: export may generate unexpected diff output for merge
1113 NOTE: export may generate unexpected diff output for merge
1114 changesets, as it will compare the merge changeset against its
1114 changesets, as it will compare the merge changeset against its
1115 first parent only.
1115 first parent only.
1116
1116
1117 Output may be to a file, in which case the name of the file is
1117 Output may be to a file, in which case the name of the file is
1118 given using a format string. The formatting rules are as follows:
1118 given using a format string. The formatting rules are as follows:
1119
1119
1120 %% literal "%" character
1120 %% literal "%" character
1121 %H changeset hash (40 bytes of hexadecimal)
1121 %H changeset hash (40 bytes of hexadecimal)
1122 %N number of patches being generated
1122 %N number of patches being generated
1123 %R changeset revision number
1123 %R changeset revision number
1124 %b basename of the exporting repository
1124 %b basename of the exporting repository
1125 %h short-form changeset hash (12 bytes of hexadecimal)
1125 %h short-form changeset hash (12 bytes of hexadecimal)
1126 %n zero-padded sequence number, starting at 1
1126 %n zero-padded sequence number, starting at 1
1127 %r zero-padded changeset revision number
1127 %r zero-padded changeset revision number
1128
1128
1129 Without the -a/--text option, export will avoid generating diffs
1129 Without the -a/--text option, export will avoid generating diffs
1130 of files it detects as binary. With -a, export will generate a
1130 of files it detects as binary. With -a, export will generate a
1131 diff anyway, probably with undesirable results.
1131 diff anyway, probably with undesirable results.
1132
1132
1133 Use the -g/--git option to generate diffs in the git extended diff
1133 Use the -g/--git option to generate diffs in the git extended diff
1134 format. See 'hg help diffs' for more information.
1134 format. See 'hg help diffs' for more information.
1135
1135
1136 With the --switch-parent option, the diff will be against the
1136 With the --switch-parent option, the diff will be against the
1137 second parent. It can be useful to review a merge.
1137 second parent. It can be useful to review a merge.
1138 """
1138 """
1139 if not changesets:
1139 if not changesets:
1140 raise util.Abort(_("export requires at least one changeset"))
1140 raise util.Abort(_("export requires at least one changeset"))
1141 revs = cmdutil.revrange(repo, changesets)
1141 revs = cmdutil.revrange(repo, changesets)
1142 if len(revs) > 1:
1142 if len(revs) > 1:
1143 ui.note(_('exporting patches:\n'))
1143 ui.note(_('exporting patches:\n'))
1144 else:
1144 else:
1145 ui.note(_('exporting patch:\n'))
1145 ui.note(_('exporting patch:\n'))
1146 patch.export(repo, revs, template=opts.get('output'),
1146 patch.export(repo, revs, template=opts.get('output'),
1147 switch_parent=opts.get('switch_parent'),
1147 switch_parent=opts.get('switch_parent'),
1148 opts=patch.diffopts(ui, opts))
1148 opts=patch.diffopts(ui, opts))
1149
1149
1150 def forget(ui, repo, *pats, **opts):
1150 def forget(ui, repo, *pats, **opts):
1151 """forget the specified files on the next commit
1151 """forget the specified files on the next commit
1152
1152
1153 Mark the specified files so they will no longer be tracked
1153 Mark the specified files so they will no longer be tracked
1154 after the next commit.
1154 after the next commit.
1155
1155
1156 This only removes files from the current branch, not from the
1156 This only removes files from the current branch, not from the
1157 entire project history, and it does not delete them from the
1157 entire project history, and it does not delete them from the
1158 working directory.
1158 working directory.
1159
1159
1160 To undo a forget before the next commit, see hg add.
1160 To undo a forget before the next commit, see hg add.
1161 """
1161 """
1162
1162
1163 if not pats:
1163 if not pats:
1164 raise util.Abort(_('no files specified'))
1164 raise util.Abort(_('no files specified'))
1165
1165
1166 m = cmdutil.match(repo, pats, opts)
1166 m = cmdutil.match(repo, pats, opts)
1167 s = repo.status(match=m, clean=True)
1167 s = repo.status(match=m, clean=True)
1168 forget = sorted(s[0] + s[1] + s[3] + s[6])
1168 forget = sorted(s[0] + s[1] + s[3] + s[6])
1169
1169
1170 for f in m.files():
1170 for f in m.files():
1171 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
1171 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
1172 ui.warn(_('not removing %s: file is already untracked\n')
1172 ui.warn(_('not removing %s: file is already untracked\n')
1173 % m.rel(f))
1173 % m.rel(f))
1174
1174
1175 for f in forget:
1175 for f in forget:
1176 if ui.verbose or not m.exact(f):
1176 if ui.verbose or not m.exact(f):
1177 ui.status(_('removing %s\n') % m.rel(f))
1177 ui.status(_('removing %s\n') % m.rel(f))
1178
1178
1179 repo.remove(forget, unlink=False)
1179 repo.remove(forget, unlink=False)
1180
1180
1181 def grep(ui, repo, pattern, *pats, **opts):
1181 def grep(ui, repo, pattern, *pats, **opts):
1182 """search for a pattern in specified files and revisions
1182 """search for a pattern in specified files and revisions
1183
1183
1184 Search revisions of files for a regular expression.
1184 Search revisions of files for a regular expression.
1185
1185
1186 This command behaves differently than Unix grep. It only accepts
1186 This command behaves differently than Unix grep. It only accepts
1187 Python/Perl regexps. It searches repository history, not the
1187 Python/Perl regexps. It searches repository history, not the
1188 working directory. It always prints the revision number in which a
1188 working directory. It always prints the revision number in which a
1189 match appears.
1189 match appears.
1190
1190
1191 By default, grep only prints output for the first revision of a
1191 By default, grep only prints output for the first revision of a
1192 file in which it finds a match. To get it to print every revision
1192 file in which it finds a match. To get it to print every revision
1193 that contains a change in match status ("-" for a match that
1193 that contains a change in match status ("-" for a match that
1194 becomes a non-match, or "+" for a non-match that becomes a match),
1194 becomes a non-match, or "+" for a non-match that becomes a match),
1195 use the --all flag.
1195 use the --all flag.
1196 """
1196 """
1197 reflags = 0
1197 reflags = 0
1198 if opts.get('ignore_case'):
1198 if opts.get('ignore_case'):
1199 reflags |= re.I
1199 reflags |= re.I
1200 try:
1200 try:
1201 regexp = re.compile(pattern, reflags)
1201 regexp = re.compile(pattern, reflags)
1202 except Exception, inst:
1202 except Exception, inst:
1203 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
1203 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
1204 return None
1204 return None
1205 sep, eol = ':', '\n'
1205 sep, eol = ':', '\n'
1206 if opts.get('print0'):
1206 if opts.get('print0'):
1207 sep = eol = '\0'
1207 sep = eol = '\0'
1208
1208
1209 fcache = {}
1209 getfile = util.lrucachefunc(repo.file)
1210 forder = []
1211 def getfile(fn):
1212 if fn not in fcache:
1213 if len(fcache) > 20:
1214 del fcache[forder.pop(0)]
1215 fcache[fn] = repo.file(fn)
1216 else:
1217 forder.remove(fn)
1218
1219 forder.append(fn)
1220 return fcache[fn]
1221
1210
1222 def matchlines(body):
1211 def matchlines(body):
1223 begin = 0
1212 begin = 0
1224 linenum = 0
1213 linenum = 0
1225 while True:
1214 while True:
1226 match = regexp.search(body, begin)
1215 match = regexp.search(body, begin)
1227 if not match:
1216 if not match:
1228 break
1217 break
1229 mstart, mend = match.span()
1218 mstart, mend = match.span()
1230 linenum += body.count('\n', begin, mstart) + 1
1219 linenum += body.count('\n', begin, mstart) + 1
1231 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1220 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1232 begin = body.find('\n', mend) + 1 or len(body)
1221 begin = body.find('\n', mend) + 1 or len(body)
1233 lend = begin - 1
1222 lend = begin - 1
1234 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1223 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1235
1224
1236 class linestate(object):
1225 class linestate(object):
1237 def __init__(self, line, linenum, colstart, colend):
1226 def __init__(self, line, linenum, colstart, colend):
1238 self.line = line
1227 self.line = line
1239 self.linenum = linenum
1228 self.linenum = linenum
1240 self.colstart = colstart
1229 self.colstart = colstart
1241 self.colend = colend
1230 self.colend = colend
1242
1231
1243 def __hash__(self):
1232 def __hash__(self):
1244 return hash((self.linenum, self.line))
1233 return hash((self.linenum, self.line))
1245
1234
1246 def __eq__(self, other):
1235 def __eq__(self, other):
1247 return self.line == other.line
1236 return self.line == other.line
1248
1237
1249 matches = {}
1238 matches = {}
1250 copies = {}
1239 copies = {}
1251 def grepbody(fn, rev, body):
1240 def grepbody(fn, rev, body):
1252 matches[rev].setdefault(fn, [])
1241 matches[rev].setdefault(fn, [])
1253 m = matches[rev][fn]
1242 m = matches[rev][fn]
1254 for lnum, cstart, cend, line in matchlines(body):
1243 for lnum, cstart, cend, line in matchlines(body):
1255 s = linestate(line, lnum, cstart, cend)
1244 s = linestate(line, lnum, cstart, cend)
1256 m.append(s)
1245 m.append(s)
1257
1246
1258 def difflinestates(a, b):
1247 def difflinestates(a, b):
1259 sm = difflib.SequenceMatcher(None, a, b)
1248 sm = difflib.SequenceMatcher(None, a, b)
1260 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1249 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1261 if tag == 'insert':
1250 if tag == 'insert':
1262 for i in xrange(blo, bhi):
1251 for i in xrange(blo, bhi):
1263 yield ('+', b[i])
1252 yield ('+', b[i])
1264 elif tag == 'delete':
1253 elif tag == 'delete':
1265 for i in xrange(alo, ahi):
1254 for i in xrange(alo, ahi):
1266 yield ('-', a[i])
1255 yield ('-', a[i])
1267 elif tag == 'replace':
1256 elif tag == 'replace':
1268 for i in xrange(alo, ahi):
1257 for i in xrange(alo, ahi):
1269 yield ('-', a[i])
1258 yield ('-', a[i])
1270 for i in xrange(blo, bhi):
1259 for i in xrange(blo, bhi):
1271 yield ('+', b[i])
1260 yield ('+', b[i])
1272
1261
1273 def display(fn, r, pstates, states):
1262 def display(fn, r, pstates, states):
1274 datefunc = ui.quiet and util.shortdate or util.datestr
1263 datefunc = ui.quiet and util.shortdate or util.datestr
1275 found = False
1264 found = False
1276 filerevmatches = {}
1265 filerevmatches = {}
1277 if opts.get('all'):
1266 if opts.get('all'):
1278 iter = difflinestates(pstates, states)
1267 iter = difflinestates(pstates, states)
1279 else:
1268 else:
1280 iter = [('', l) for l in states]
1269 iter = [('', l) for l in states]
1281 for change, l in iter:
1270 for change, l in iter:
1282 cols = [fn, str(r)]
1271 cols = [fn, str(r)]
1283 if opts.get('line_number'):
1272 if opts.get('line_number'):
1284 cols.append(str(l.linenum))
1273 cols.append(str(l.linenum))
1285 if opts.get('all'):
1274 if opts.get('all'):
1286 cols.append(change)
1275 cols.append(change)
1287 if opts.get('user'):
1276 if opts.get('user'):
1288 cols.append(ui.shortuser(get(r)[1]))
1277 cols.append(ui.shortuser(get(r)[1]))
1289 if opts.get('date'):
1278 if opts.get('date'):
1290 cols.append(datefunc(get(r)[2]))
1279 cols.append(datefunc(get(r)[2]))
1291 if opts.get('files_with_matches'):
1280 if opts.get('files_with_matches'):
1292 c = (fn, r)
1281 c = (fn, r)
1293 if c in filerevmatches:
1282 if c in filerevmatches:
1294 continue
1283 continue
1295 filerevmatches[c] = 1
1284 filerevmatches[c] = 1
1296 else:
1285 else:
1297 cols.append(l.line)
1286 cols.append(l.line)
1298 ui.write(sep.join(cols), eol)
1287 ui.write(sep.join(cols), eol)
1299 found = True
1288 found = True
1300 return found
1289 return found
1301
1290
1302 skip = {}
1291 skip = {}
1303 revfiles = {}
1292 revfiles = {}
1304 get = util.cachefunc(lambda r: repo[r].changeset())
1293 get = util.cachefunc(lambda r: repo[r].changeset())
1305 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1294 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1306 found = False
1295 found = False
1307 follow = opts.get('follow')
1296 follow = opts.get('follow')
1308 for st, rev, fns in changeiter:
1297 for st, rev, fns in changeiter:
1309 if st == 'window':
1298 if st == 'window':
1310 matches.clear()
1299 matches.clear()
1311 revfiles.clear()
1300 revfiles.clear()
1312 elif st == 'add':
1301 elif st == 'add':
1313 ctx = repo[rev]
1302 ctx = repo[rev]
1314 pctx = ctx.parents()[0]
1303 pctx = ctx.parents()[0]
1315 parent = pctx.rev()
1304 parent = pctx.rev()
1316 matches.setdefault(rev, {})
1305 matches.setdefault(rev, {})
1317 matches.setdefault(parent, {})
1306 matches.setdefault(parent, {})
1318 files = revfiles.setdefault(rev, [])
1307 files = revfiles.setdefault(rev, [])
1319 for fn in fns:
1308 for fn in fns:
1320 flog = getfile(fn)
1309 flog = getfile(fn)
1321 try:
1310 try:
1322 fnode = ctx.filenode(fn)
1311 fnode = ctx.filenode(fn)
1323 except error.LookupError:
1312 except error.LookupError:
1324 continue
1313 continue
1325
1314
1326 copied = flog.renamed(fnode)
1315 copied = flog.renamed(fnode)
1327 copy = follow and copied and copied[0]
1316 copy = follow and copied and copied[0]
1328 if copy:
1317 if copy:
1329 copies.setdefault(rev, {})[fn] = copy
1318 copies.setdefault(rev, {})[fn] = copy
1330 if fn in skip:
1319 if fn in skip:
1331 if copy:
1320 if copy:
1332 skip[copy] = True
1321 skip[copy] = True
1333 continue
1322 continue
1334 files.append(fn)
1323 files.append(fn)
1335
1324
1336 if not matches[rev].has_key(fn):
1325 if not matches[rev].has_key(fn):
1337 grepbody(fn, rev, flog.read(fnode))
1326 grepbody(fn, rev, flog.read(fnode))
1338
1327
1339 pfn = copy or fn
1328 pfn = copy or fn
1340 if not matches[parent].has_key(pfn):
1329 if not matches[parent].has_key(pfn):
1341 try:
1330 try:
1342 fnode = pctx.filenode(pfn)
1331 fnode = pctx.filenode(pfn)
1343 grepbody(pfn, parent, flog.read(fnode))
1332 grepbody(pfn, parent, flog.read(fnode))
1344 except error.LookupError:
1333 except error.LookupError:
1345 pass
1334 pass
1346 elif st == 'iter':
1335 elif st == 'iter':
1347 parent = repo[rev].parents()[0].rev()
1336 parent = repo[rev].parents()[0].rev()
1348 for fn in sorted(revfiles.get(rev, [])):
1337 for fn in sorted(revfiles.get(rev, [])):
1349 states = matches[rev][fn]
1338 states = matches[rev][fn]
1350 copy = copies.get(rev, {}).get(fn)
1339 copy = copies.get(rev, {}).get(fn)
1351 if fn in skip:
1340 if fn in skip:
1352 if copy:
1341 if copy:
1353 skip[copy] = True
1342 skip[copy] = True
1354 continue
1343 continue
1355 pstates = matches.get(parent, {}).get(copy or fn, [])
1344 pstates = matches.get(parent, {}).get(copy or fn, [])
1356 if pstates or states:
1345 if pstates or states:
1357 r = display(fn, rev, pstates, states)
1346 r = display(fn, rev, pstates, states)
1358 found = found or r
1347 found = found or r
1359 if r and not opts.get('all'):
1348 if r and not opts.get('all'):
1360 skip[fn] = True
1349 skip[fn] = True
1361 if copy:
1350 if copy:
1362 skip[copy] = True
1351 skip[copy] = True
1363
1352
1364 def heads(ui, repo, *branchrevs, **opts):
1353 def heads(ui, repo, *branchrevs, **opts):
1365 """show current repository heads or show branch heads
1354 """show current repository heads or show branch heads
1366
1355
1367 With no arguments, show all repository head changesets.
1356 With no arguments, show all repository head changesets.
1368
1357
1369 Repository "heads" are changesets that don't have child
1358 Repository "heads" are changesets that don't have child
1370 changesets. They are where development generally takes place and
1359 changesets. They are where development generally takes place and
1371 are the usual targets for update and merge operations.
1360 are the usual targets for update and merge operations.
1372
1361
1373 If one or more REV is given, the "branch heads" will be shown for
1362 If one or more REV is given, the "branch heads" will be shown for
1374 the named branch associated with that revision. The name of the
1363 the named branch associated with that revision. The name of the
1375 branch is called the revision's branch tag.
1364 branch is called the revision's branch tag.
1376
1365
1377 Branch heads are revisions on a given named branch that do not have
1366 Branch heads are revisions on a given named branch that do not have
1378 any descendants on the same branch. A branch head could be a true head
1367 any descendants on the same branch. A branch head could be a true head
1379 or it could be the last changeset on a branch before a new branch
1368 or it could be the last changeset on a branch before a new branch
1380 was created. If none of the branch heads are true heads, the branch
1369 was created. If none of the branch heads are true heads, the branch
1381 is considered inactive. If -c/--closed is specified, also show branch
1370 is considered inactive. If -c/--closed is specified, also show branch
1382 heads marked closed (see hg commit --close-branch).
1371 heads marked closed (see hg commit --close-branch).
1383
1372
1384 If STARTREV is specified only those heads (or branch heads) that
1373 If STARTREV is specified only those heads (or branch heads) that
1385 are descendants of STARTREV will be displayed.
1374 are descendants of STARTREV will be displayed.
1386 """
1375 """
1387 if opts.get('rev'):
1376 if opts.get('rev'):
1388 start = repo.lookup(opts['rev'])
1377 start = repo.lookup(opts['rev'])
1389 else:
1378 else:
1390 start = None
1379 start = None
1391 closed = opts.get('closed')
1380 closed = opts.get('closed')
1392 hideinactive, _heads = opts.get('active'), None
1381 hideinactive, _heads = opts.get('active'), None
1393 if not branchrevs:
1382 if not branchrevs:
1394 # Assume we're looking repo-wide heads if no revs were specified.
1383 # Assume we're looking repo-wide heads if no revs were specified.
1395 heads = repo.heads(start)
1384 heads = repo.heads(start)
1396 else:
1385 else:
1397 if hideinactive:
1386 if hideinactive:
1398 _heads = repo.heads(start)
1387 _heads = repo.heads(start)
1399 heads = []
1388 heads = []
1400 visitedset = set()
1389 visitedset = set()
1401 for branchrev in branchrevs:
1390 for branchrev in branchrevs:
1402 branch = repo[branchrev].branch()
1391 branch = repo[branchrev].branch()
1403 if branch in visitedset:
1392 if branch in visitedset:
1404 continue
1393 continue
1405 visitedset.add(branch)
1394 visitedset.add(branch)
1406 bheads = repo.branchheads(branch, start, closed=closed)
1395 bheads = repo.branchheads(branch, start, closed=closed)
1407 if not bheads:
1396 if not bheads:
1408 if not opts.get('rev'):
1397 if not opts.get('rev'):
1409 ui.warn(_("no open branch heads on branch %s\n") % branch)
1398 ui.warn(_("no open branch heads on branch %s\n") % branch)
1410 elif branch != branchrev:
1399 elif branch != branchrev:
1411 ui.warn(_("no changes on branch %s containing %s are "
1400 ui.warn(_("no changes on branch %s containing %s are "
1412 "reachable from %s\n")
1401 "reachable from %s\n")
1413 % (branch, branchrev, opts.get('rev')))
1402 % (branch, branchrev, opts.get('rev')))
1414 else:
1403 else:
1415 ui.warn(_("no changes on branch %s are reachable from %s\n")
1404 ui.warn(_("no changes on branch %s are reachable from %s\n")
1416 % (branch, opts.get('rev')))
1405 % (branch, opts.get('rev')))
1417 if hideinactive:
1406 if hideinactive:
1418 bheads = [bhead for bhead in bheads if bhead in _heads]
1407 bheads = [bhead for bhead in bheads if bhead in _heads]
1419 heads.extend(bheads)
1408 heads.extend(bheads)
1420 if not heads:
1409 if not heads:
1421 return 1
1410 return 1
1422 displayer = cmdutil.show_changeset(ui, repo, opts)
1411 displayer = cmdutil.show_changeset(ui, repo, opts)
1423 for n in heads:
1412 for n in heads:
1424 displayer.show(repo[n])
1413 displayer.show(repo[n])
1425
1414
1426 def help_(ui, name=None, with_version=False):
1415 def help_(ui, name=None, with_version=False):
1427 """show help for a given topic or a help overview
1416 """show help for a given topic or a help overview
1428
1417
1429 With no arguments, print a list of commands with short help messages.
1418 With no arguments, print a list of commands with short help messages.
1430
1419
1431 Given a topic, extension, or command name, print help for that
1420 Given a topic, extension, or command name, print help for that
1432 topic."""
1421 topic."""
1433 option_lists = []
1422 option_lists = []
1434
1423
1435 def addglobalopts(aliases):
1424 def addglobalopts(aliases):
1436 if ui.verbose:
1425 if ui.verbose:
1437 option_lists.append((_("global options:"), globalopts))
1426 option_lists.append((_("global options:"), globalopts))
1438 if name == 'shortlist':
1427 if name == 'shortlist':
1439 option_lists.append((_('use "hg help" for the full list '
1428 option_lists.append((_('use "hg help" for the full list '
1440 'of commands'), ()))
1429 'of commands'), ()))
1441 else:
1430 else:
1442 if name == 'shortlist':
1431 if name == 'shortlist':
1443 msg = _('use "hg help" for the full list of commands '
1432 msg = _('use "hg help" for the full list of commands '
1444 'or "hg -v" for details')
1433 'or "hg -v" for details')
1445 elif aliases:
1434 elif aliases:
1446 msg = _('use "hg -v help%s" to show aliases and '
1435 msg = _('use "hg -v help%s" to show aliases and '
1447 'global options') % (name and " " + name or "")
1436 'global options') % (name and " " + name or "")
1448 else:
1437 else:
1449 msg = _('use "hg -v help %s" to show global options') % name
1438 msg = _('use "hg -v help %s" to show global options') % name
1450 option_lists.append((msg, ()))
1439 option_lists.append((msg, ()))
1451
1440
1452 def helpcmd(name):
1441 def helpcmd(name):
1453 if with_version:
1442 if with_version:
1454 version_(ui)
1443 version_(ui)
1455 ui.write('\n')
1444 ui.write('\n')
1456
1445
1457 try:
1446 try:
1458 aliases, i = cmdutil.findcmd(name, table, False)
1447 aliases, i = cmdutil.findcmd(name, table, False)
1459 except error.AmbiguousCommand, inst:
1448 except error.AmbiguousCommand, inst:
1460 select = lambda c: c.lstrip('^').startswith(inst.args[0])
1449 select = lambda c: c.lstrip('^').startswith(inst.args[0])
1461 helplist(_('list of commands:\n\n'), select)
1450 helplist(_('list of commands:\n\n'), select)
1462 return
1451 return
1463
1452
1464 # synopsis
1453 # synopsis
1465 if len(i) > 2:
1454 if len(i) > 2:
1466 if i[2].startswith('hg'):
1455 if i[2].startswith('hg'):
1467 ui.write("%s\n" % i[2])
1456 ui.write("%s\n" % i[2])
1468 else:
1457 else:
1469 ui.write('hg %s %s\n' % (aliases[0], i[2]))
1458 ui.write('hg %s %s\n' % (aliases[0], i[2]))
1470 else:
1459 else:
1471 ui.write('hg %s\n' % aliases[0])
1460 ui.write('hg %s\n' % aliases[0])
1472
1461
1473 # aliases
1462 # aliases
1474 if not ui.quiet and len(aliases) > 1:
1463 if not ui.quiet and len(aliases) > 1:
1475 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
1464 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
1476
1465
1477 # description
1466 # description
1478 doc = gettext(i[0].__doc__)
1467 doc = gettext(i[0].__doc__)
1479 if not doc:
1468 if not doc:
1480 doc = _("(no help text available)")
1469 doc = _("(no help text available)")
1481 if ui.quiet:
1470 if ui.quiet:
1482 doc = doc.splitlines(0)[0]
1471 doc = doc.splitlines(0)[0]
1483 ui.write("\n%s\n" % doc.rstrip())
1472 ui.write("\n%s\n" % doc.rstrip())
1484
1473
1485 if not ui.quiet:
1474 if not ui.quiet:
1486 # options
1475 # options
1487 if i[1]:
1476 if i[1]:
1488 option_lists.append((_("options:\n"), i[1]))
1477 option_lists.append((_("options:\n"), i[1]))
1489
1478
1490 addglobalopts(False)
1479 addglobalopts(False)
1491
1480
1492 def helplist(header, select=None):
1481 def helplist(header, select=None):
1493 h = {}
1482 h = {}
1494 cmds = {}
1483 cmds = {}
1495 for c, e in table.iteritems():
1484 for c, e in table.iteritems():
1496 f = c.split("|", 1)[0]
1485 f = c.split("|", 1)[0]
1497 if select and not select(f):
1486 if select and not select(f):
1498 continue
1487 continue
1499 if (not select and name != 'shortlist' and
1488 if (not select and name != 'shortlist' and
1500 e[0].__module__ != __name__):
1489 e[0].__module__ != __name__):
1501 continue
1490 continue
1502 if name == "shortlist" and not f.startswith("^"):
1491 if name == "shortlist" and not f.startswith("^"):
1503 continue
1492 continue
1504 f = f.lstrip("^")
1493 f = f.lstrip("^")
1505 if not ui.debugflag and f.startswith("debug"):
1494 if not ui.debugflag and f.startswith("debug"):
1506 continue
1495 continue
1507 doc = gettext(e[0].__doc__)
1496 doc = gettext(e[0].__doc__)
1508 if not doc:
1497 if not doc:
1509 doc = _("(no help text available)")
1498 doc = _("(no help text available)")
1510 h[f] = doc.splitlines(0)[0].rstrip()
1499 h[f] = doc.splitlines(0)[0].rstrip()
1511 cmds[f] = c.lstrip("^")
1500 cmds[f] = c.lstrip("^")
1512
1501
1513 if not h:
1502 if not h:
1514 ui.status(_('no commands defined\n'))
1503 ui.status(_('no commands defined\n'))
1515 return
1504 return
1516
1505
1517 ui.status(header)
1506 ui.status(header)
1518 fns = sorted(h)
1507 fns = sorted(h)
1519 m = max(map(len, fns))
1508 m = max(map(len, fns))
1520 for f in fns:
1509 for f in fns:
1521 if ui.verbose:
1510 if ui.verbose:
1522 commands = cmds[f].replace("|",", ")
1511 commands = cmds[f].replace("|",", ")
1523 ui.write(" %s:\n %s\n"%(commands, h[f]))
1512 ui.write(" %s:\n %s\n"%(commands, h[f]))
1524 else:
1513 else:
1525 ui.write(' %-*s %s\n' % (m, f, util.wrap(h[f], m + 4)))
1514 ui.write(' %-*s %s\n' % (m, f, util.wrap(h[f], m + 4)))
1526
1515
1527 if name != 'shortlist':
1516 if name != 'shortlist':
1528 exts, maxlength = extensions.enabled()
1517 exts, maxlength = extensions.enabled()
1529 ui.write(help.listexts(_('enabled extensions:'), exts, maxlength))
1518 ui.write(help.listexts(_('enabled extensions:'), exts, maxlength))
1530
1519
1531 if not ui.quiet:
1520 if not ui.quiet:
1532 addglobalopts(True)
1521 addglobalopts(True)
1533
1522
1534 def helptopic(name):
1523 def helptopic(name):
1535 for names, header, doc in help.helptable:
1524 for names, header, doc in help.helptable:
1536 if name in names:
1525 if name in names:
1537 break
1526 break
1538 else:
1527 else:
1539 raise error.UnknownCommand(name)
1528 raise error.UnknownCommand(name)
1540
1529
1541 # description
1530 # description
1542 if not doc:
1531 if not doc:
1543 doc = _("(no help text available)")
1532 doc = _("(no help text available)")
1544 if hasattr(doc, '__call__'):
1533 if hasattr(doc, '__call__'):
1545 doc = doc()
1534 doc = doc()
1546
1535
1547 ui.write("%s\n" % header)
1536 ui.write("%s\n" % header)
1548 ui.write("%s\n" % doc.rstrip())
1537 ui.write("%s\n" % doc.rstrip())
1549
1538
1550 def helpext(name):
1539 def helpext(name):
1551 try:
1540 try:
1552 mod = extensions.find(name)
1541 mod = extensions.find(name)
1553 except KeyError:
1542 except KeyError:
1554 raise error.UnknownCommand(name)
1543 raise error.UnknownCommand(name)
1555
1544
1556 doc = gettext(mod.__doc__) or _('no help text available')
1545 doc = gettext(mod.__doc__) or _('no help text available')
1557 doc = doc.splitlines(0)
1546 doc = doc.splitlines(0)
1558 ui.write(_('%s extension - %s\n') % (name.split('.')[-1], doc[0]))
1547 ui.write(_('%s extension - %s\n') % (name.split('.')[-1], doc[0]))
1559 for d in doc[1:]:
1548 for d in doc[1:]:
1560 ui.write(d, '\n')
1549 ui.write(d, '\n')
1561
1550
1562 ui.status('\n')
1551 ui.status('\n')
1563
1552
1564 try:
1553 try:
1565 ct = mod.cmdtable
1554 ct = mod.cmdtable
1566 except AttributeError:
1555 except AttributeError:
1567 ct = {}
1556 ct = {}
1568
1557
1569 modcmds = set([c.split('|', 1)[0] for c in ct])
1558 modcmds = set([c.split('|', 1)[0] for c in ct])
1570 helplist(_('list of commands:\n\n'), modcmds.__contains__)
1559 helplist(_('list of commands:\n\n'), modcmds.__contains__)
1571
1560
1572 if name and name != 'shortlist':
1561 if name and name != 'shortlist':
1573 i = None
1562 i = None
1574 for f in (helptopic, helpcmd, helpext):
1563 for f in (helptopic, helpcmd, helpext):
1575 try:
1564 try:
1576 f(name)
1565 f(name)
1577 i = None
1566 i = None
1578 break
1567 break
1579 except error.UnknownCommand, inst:
1568 except error.UnknownCommand, inst:
1580 i = inst
1569 i = inst
1581 if i:
1570 if i:
1582 raise i
1571 raise i
1583
1572
1584 else:
1573 else:
1585 # program name
1574 # program name
1586 if ui.verbose or with_version:
1575 if ui.verbose or with_version:
1587 version_(ui)
1576 version_(ui)
1588 else:
1577 else:
1589 ui.status(_("Mercurial Distributed SCM\n"))
1578 ui.status(_("Mercurial Distributed SCM\n"))
1590 ui.status('\n')
1579 ui.status('\n')
1591
1580
1592 # list of commands
1581 # list of commands
1593 if name == "shortlist":
1582 if name == "shortlist":
1594 header = _('basic commands:\n\n')
1583 header = _('basic commands:\n\n')
1595 else:
1584 else:
1596 header = _('list of commands:\n\n')
1585 header = _('list of commands:\n\n')
1597
1586
1598 helplist(header)
1587 helplist(header)
1599
1588
1600 # list all option lists
1589 # list all option lists
1601 opt_output = []
1590 opt_output = []
1602 for title, options in option_lists:
1591 for title, options in option_lists:
1603 opt_output.append(("\n%s" % title, None))
1592 opt_output.append(("\n%s" % title, None))
1604 for shortopt, longopt, default, desc in options:
1593 for shortopt, longopt, default, desc in options:
1605 if "DEPRECATED" in desc and not ui.verbose: continue
1594 if "DEPRECATED" in desc and not ui.verbose: continue
1606 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
1595 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
1607 longopt and " --%s" % longopt),
1596 longopt and " --%s" % longopt),
1608 "%s%s" % (desc,
1597 "%s%s" % (desc,
1609 default
1598 default
1610 and _(" (default: %s)") % default
1599 and _(" (default: %s)") % default
1611 or "")))
1600 or "")))
1612
1601
1613 if not name:
1602 if not name:
1614 ui.write(_("\nadditional help topics:\n\n"))
1603 ui.write(_("\nadditional help topics:\n\n"))
1615 topics = []
1604 topics = []
1616 for names, header, doc in help.helptable:
1605 for names, header, doc in help.helptable:
1617 names = [(-len(name), name) for name in names]
1606 names = [(-len(name), name) for name in names]
1618 names.sort()
1607 names.sort()
1619 topics.append((names[0][1], header))
1608 topics.append((names[0][1], header))
1620 topics_len = max([len(s[0]) for s in topics])
1609 topics_len = max([len(s[0]) for s in topics])
1621 for t, desc in topics:
1610 for t, desc in topics:
1622 ui.write(" %-*s %s\n" % (topics_len, t, desc))
1611 ui.write(" %-*s %s\n" % (topics_len, t, desc))
1623
1612
1624 if opt_output:
1613 if opt_output:
1625 opts_len = max([len(line[0]) for line in opt_output if line[1]] or [0])
1614 opts_len = max([len(line[0]) for line in opt_output if line[1]] or [0])
1626 for first, second in opt_output:
1615 for first, second in opt_output:
1627 if second:
1616 if second:
1628 second = util.wrap(second, opts_len + 3)
1617 second = util.wrap(second, opts_len + 3)
1629 ui.write(" %-*s %s\n" % (opts_len, first, second))
1618 ui.write(" %-*s %s\n" % (opts_len, first, second))
1630 else:
1619 else:
1631 ui.write("%s\n" % first)
1620 ui.write("%s\n" % first)
1632
1621
1633 def identify(ui, repo, source=None,
1622 def identify(ui, repo, source=None,
1634 rev=None, num=None, id=None, branch=None, tags=None):
1623 rev=None, num=None, id=None, branch=None, tags=None):
1635 """identify the working copy or specified revision
1624 """identify the working copy or specified revision
1636
1625
1637 With no revision, print a summary of the current state of the
1626 With no revision, print a summary of the current state of the
1638 repository.
1627 repository.
1639
1628
1640 Specifying a path to a repository root or Mercurial bundle will
1629 Specifying a path to a repository root or Mercurial bundle will
1641 cause lookup to operate on that repository/bundle.
1630 cause lookup to operate on that repository/bundle.
1642
1631
1643 This summary identifies the repository state using one or two
1632 This summary identifies the repository state using one or two
1644 parent hash identifiers, followed by a "+" if there are
1633 parent hash identifiers, followed by a "+" if there are
1645 uncommitted changes in the working directory, a list of tags for
1634 uncommitted changes in the working directory, a list of tags for
1646 this revision and a branch name for non-default branches.
1635 this revision and a branch name for non-default branches.
1647 """
1636 """
1648
1637
1649 if not repo and not source:
1638 if not repo and not source:
1650 raise util.Abort(_("There is no Mercurial repository here "
1639 raise util.Abort(_("There is no Mercurial repository here "
1651 "(.hg not found)"))
1640 "(.hg not found)"))
1652
1641
1653 hexfunc = ui.debugflag and hex or short
1642 hexfunc = ui.debugflag and hex or short
1654 default = not (num or id or branch or tags)
1643 default = not (num or id or branch or tags)
1655 output = []
1644 output = []
1656
1645
1657 revs = []
1646 revs = []
1658 if source:
1647 if source:
1659 source, revs, checkout = hg.parseurl(ui.expandpath(source), [])
1648 source, revs, checkout = hg.parseurl(ui.expandpath(source), [])
1660 repo = hg.repository(ui, source)
1649 repo = hg.repository(ui, source)
1661
1650
1662 if not repo.local():
1651 if not repo.local():
1663 if not rev and revs:
1652 if not rev and revs:
1664 rev = revs[0]
1653 rev = revs[0]
1665 if not rev:
1654 if not rev:
1666 rev = "tip"
1655 rev = "tip"
1667 if num or branch or tags:
1656 if num or branch or tags:
1668 raise util.Abort(
1657 raise util.Abort(
1669 "can't query remote revision number, branch, or tags")
1658 "can't query remote revision number, branch, or tags")
1670 output = [hexfunc(repo.lookup(rev))]
1659 output = [hexfunc(repo.lookup(rev))]
1671 elif not rev:
1660 elif not rev:
1672 ctx = repo[None]
1661 ctx = repo[None]
1673 parents = ctx.parents()
1662 parents = ctx.parents()
1674 changed = False
1663 changed = False
1675 if default or id or num:
1664 if default or id or num:
1676 changed = ctx.files() + ctx.deleted()
1665 changed = ctx.files() + ctx.deleted()
1677 if default or id:
1666 if default or id:
1678 output = ["%s%s" % ('+'.join([hexfunc(p.node()) for p in parents]),
1667 output = ["%s%s" % ('+'.join([hexfunc(p.node()) for p in parents]),
1679 (changed) and "+" or "")]
1668 (changed) and "+" or "")]
1680 if num:
1669 if num:
1681 output.append("%s%s" % ('+'.join([str(p.rev()) for p in parents]),
1670 output.append("%s%s" % ('+'.join([str(p.rev()) for p in parents]),
1682 (changed) and "+" or ""))
1671 (changed) and "+" or ""))
1683 else:
1672 else:
1684 ctx = repo[rev]
1673 ctx = repo[rev]
1685 if default or id:
1674 if default or id:
1686 output = [hexfunc(ctx.node())]
1675 output = [hexfunc(ctx.node())]
1687 if num:
1676 if num:
1688 output.append(str(ctx.rev()))
1677 output.append(str(ctx.rev()))
1689
1678
1690 if repo.local() and default and not ui.quiet:
1679 if repo.local() and default and not ui.quiet:
1691 b = encoding.tolocal(ctx.branch())
1680 b = encoding.tolocal(ctx.branch())
1692 if b != 'default':
1681 if b != 'default':
1693 output.append("(%s)" % b)
1682 output.append("(%s)" % b)
1694
1683
1695 # multiple tags for a single parent separated by '/'
1684 # multiple tags for a single parent separated by '/'
1696 t = "/".join(ctx.tags())
1685 t = "/".join(ctx.tags())
1697 if t:
1686 if t:
1698 output.append(t)
1687 output.append(t)
1699
1688
1700 if branch:
1689 if branch:
1701 output.append(encoding.tolocal(ctx.branch()))
1690 output.append(encoding.tolocal(ctx.branch()))
1702
1691
1703 if tags:
1692 if tags:
1704 output.extend(ctx.tags())
1693 output.extend(ctx.tags())
1705
1694
1706 ui.write("%s\n" % ' '.join(output))
1695 ui.write("%s\n" % ' '.join(output))
1707
1696
1708 def import_(ui, repo, patch1, *patches, **opts):
1697 def import_(ui, repo, patch1, *patches, **opts):
1709 """import an ordered set of patches
1698 """import an ordered set of patches
1710
1699
1711 Import a list of patches and commit them individually.
1700 Import a list of patches and commit them individually.
1712
1701
1713 If there are outstanding changes in the working directory, import
1702 If there are outstanding changes in the working directory, import
1714 will abort unless given the -f/--force flag.
1703 will abort unless given the -f/--force flag.
1715
1704
1716 You can import a patch straight from a mail message. Even patches
1705 You can import a patch straight from a mail message. Even patches
1717 as attachments work (to use the body part, it must have type
1706 as attachments work (to use the body part, it must have type
1718 text/plain or text/x-patch). From and Subject headers of email
1707 text/plain or text/x-patch). From and Subject headers of email
1719 message are used as default committer and commit message. All
1708 message are used as default committer and commit message. All
1720 text/plain body parts before first diff are added to commit
1709 text/plain body parts before first diff are added to commit
1721 message.
1710 message.
1722
1711
1723 If the imported patch was generated by hg export, user and
1712 If the imported patch was generated by hg export, user and
1724 description from patch override values from message headers and
1713 description from patch override values from message headers and
1725 body. Values given on command line with -m/--message and -u/--user
1714 body. Values given on command line with -m/--message and -u/--user
1726 override these.
1715 override these.
1727
1716
1728 If --exact is specified, import will set the working directory to
1717 If --exact is specified, import will set the working directory to
1729 the parent of each patch before applying it, and will abort if the
1718 the parent of each patch before applying it, and will abort if the
1730 resulting changeset has a different ID than the one recorded in
1719 resulting changeset has a different ID than the one recorded in
1731 the patch. This may happen due to character set problems or other
1720 the patch. This may happen due to character set problems or other
1732 deficiencies in the text patch format.
1721 deficiencies in the text patch format.
1733
1722
1734 With -s/--similarity, hg will attempt to discover renames and
1723 With -s/--similarity, hg will attempt to discover renames and
1735 copies in the patch in the same way as 'addremove'.
1724 copies in the patch in the same way as 'addremove'.
1736
1725
1737 To read a patch from standard input, use "-" as the patch name. If
1726 To read a patch from standard input, use "-" as the patch name. If
1738 a URL is specified, the patch will be downloaded from it.
1727 a URL is specified, the patch will be downloaded from it.
1739 See 'hg help dates' for a list of formats valid for -d/--date.
1728 See 'hg help dates' for a list of formats valid for -d/--date.
1740 """
1729 """
1741 patches = (patch1,) + patches
1730 patches = (patch1,) + patches
1742
1731
1743 date = opts.get('date')
1732 date = opts.get('date')
1744 if date:
1733 if date:
1745 opts['date'] = util.parsedate(date)
1734 opts['date'] = util.parsedate(date)
1746
1735
1747 try:
1736 try:
1748 sim = float(opts.get('similarity') or 0)
1737 sim = float(opts.get('similarity') or 0)
1749 except ValueError:
1738 except ValueError:
1750 raise util.Abort(_('similarity must be a number'))
1739 raise util.Abort(_('similarity must be a number'))
1751 if sim < 0 or sim > 100:
1740 if sim < 0 or sim > 100:
1752 raise util.Abort(_('similarity must be between 0 and 100'))
1741 raise util.Abort(_('similarity must be between 0 and 100'))
1753
1742
1754 if opts.get('exact') or not opts.get('force'):
1743 if opts.get('exact') or not opts.get('force'):
1755 cmdutil.bail_if_changed(repo)
1744 cmdutil.bail_if_changed(repo)
1756
1745
1757 d = opts["base"]
1746 d = opts["base"]
1758 strip = opts["strip"]
1747 strip = opts["strip"]
1759 wlock = lock = None
1748 wlock = lock = None
1760 try:
1749 try:
1761 wlock = repo.wlock()
1750 wlock = repo.wlock()
1762 lock = repo.lock()
1751 lock = repo.lock()
1763 for p in patches:
1752 for p in patches:
1764 pf = os.path.join(d, p)
1753 pf = os.path.join(d, p)
1765
1754
1766 if pf == '-':
1755 if pf == '-':
1767 ui.status(_("applying patch from stdin\n"))
1756 ui.status(_("applying patch from stdin\n"))
1768 pf = sys.stdin
1757 pf = sys.stdin
1769 else:
1758 else:
1770 ui.status(_("applying %s\n") % p)
1759 ui.status(_("applying %s\n") % p)
1771 pf = url.open(ui, pf)
1760 pf = url.open(ui, pf)
1772 data = patch.extract(ui, pf)
1761 data = patch.extract(ui, pf)
1773 tmpname, message, user, date, branch, nodeid, p1, p2 = data
1762 tmpname, message, user, date, branch, nodeid, p1, p2 = data
1774
1763
1775 if tmpname is None:
1764 if tmpname is None:
1776 raise util.Abort(_('no diffs found'))
1765 raise util.Abort(_('no diffs found'))
1777
1766
1778 try:
1767 try:
1779 cmdline_message = cmdutil.logmessage(opts)
1768 cmdline_message = cmdutil.logmessage(opts)
1780 if cmdline_message:
1769 if cmdline_message:
1781 # pickup the cmdline msg
1770 # pickup the cmdline msg
1782 message = cmdline_message
1771 message = cmdline_message
1783 elif message:
1772 elif message:
1784 # pickup the patch msg
1773 # pickup the patch msg
1785 message = message.strip()
1774 message = message.strip()
1786 else:
1775 else:
1787 # launch the editor
1776 # launch the editor
1788 message = None
1777 message = None
1789 ui.debug(_('message:\n%s\n') % message)
1778 ui.debug(_('message:\n%s\n') % message)
1790
1779
1791 wp = repo.parents()
1780 wp = repo.parents()
1792 if opts.get('exact'):
1781 if opts.get('exact'):
1793 if not nodeid or not p1:
1782 if not nodeid or not p1:
1794 raise util.Abort(_('not a Mercurial patch'))
1783 raise util.Abort(_('not a Mercurial patch'))
1795 p1 = repo.lookup(p1)
1784 p1 = repo.lookup(p1)
1796 p2 = repo.lookup(p2 or hex(nullid))
1785 p2 = repo.lookup(p2 or hex(nullid))
1797
1786
1798 if p1 != wp[0].node():
1787 if p1 != wp[0].node():
1799 hg.clean(repo, p1)
1788 hg.clean(repo, p1)
1800 repo.dirstate.setparents(p1, p2)
1789 repo.dirstate.setparents(p1, p2)
1801 elif p2:
1790 elif p2:
1802 try:
1791 try:
1803 p1 = repo.lookup(p1)
1792 p1 = repo.lookup(p1)
1804 p2 = repo.lookup(p2)
1793 p2 = repo.lookup(p2)
1805 if p1 == wp[0].node():
1794 if p1 == wp[0].node():
1806 repo.dirstate.setparents(p1, p2)
1795 repo.dirstate.setparents(p1, p2)
1807 except error.RepoError:
1796 except error.RepoError:
1808 pass
1797 pass
1809 if opts.get('exact') or opts.get('import_branch'):
1798 if opts.get('exact') or opts.get('import_branch'):
1810 repo.dirstate.setbranch(branch or 'default')
1799 repo.dirstate.setbranch(branch or 'default')
1811
1800
1812 files = {}
1801 files = {}
1813 try:
1802 try:
1814 patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
1803 patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
1815 files=files, eolmode=None)
1804 files=files, eolmode=None)
1816 finally:
1805 finally:
1817 files = patch.updatedir(ui, repo, files, similarity=sim/100.)
1806 files = patch.updatedir(ui, repo, files, similarity=sim/100.)
1818 if not opts.get('no_commit'):
1807 if not opts.get('no_commit'):
1819 m = cmdutil.matchfiles(repo, files or [])
1808 m = cmdutil.matchfiles(repo, files or [])
1820 n = repo.commit(message, opts.get('user') or user,
1809 n = repo.commit(message, opts.get('user') or user,
1821 opts.get('date') or date, match=m,
1810 opts.get('date') or date, match=m,
1822 editor=cmdutil.commiteditor)
1811 editor=cmdutil.commiteditor)
1823 if opts.get('exact'):
1812 if opts.get('exact'):
1824 if hex(n) != nodeid:
1813 if hex(n) != nodeid:
1825 repo.rollback()
1814 repo.rollback()
1826 raise util.Abort(_('patch is damaged'
1815 raise util.Abort(_('patch is damaged'
1827 ' or loses information'))
1816 ' or loses information'))
1828 # Force a dirstate write so that the next transaction
1817 # Force a dirstate write so that the next transaction
1829 # backups an up-do-date file.
1818 # backups an up-do-date file.
1830 repo.dirstate.write()
1819 repo.dirstate.write()
1831 finally:
1820 finally:
1832 os.unlink(tmpname)
1821 os.unlink(tmpname)
1833 finally:
1822 finally:
1834 release(lock, wlock)
1823 release(lock, wlock)
1835
1824
1836 def incoming(ui, repo, source="default", **opts):
1825 def incoming(ui, repo, source="default", **opts):
1837 """show new changesets found in source
1826 """show new changesets found in source
1838
1827
1839 Show new changesets found in the specified path/URL or the default
1828 Show new changesets found in the specified path/URL or the default
1840 pull location. These are the changesets that would have been pulled
1829 pull location. These are the changesets that would have been pulled
1841 if a pull at the time you issued this command.
1830 if a pull at the time you issued this command.
1842
1831
1843 For remote repository, using --bundle avoids downloading the
1832 For remote repository, using --bundle avoids downloading the
1844 changesets twice if the incoming is followed by a pull.
1833 changesets twice if the incoming is followed by a pull.
1845
1834
1846 See pull for valid source format details.
1835 See pull for valid source format details.
1847 """
1836 """
1848 limit = cmdutil.loglimit(opts)
1837 limit = cmdutil.loglimit(opts)
1849 source, revs, checkout = hg.parseurl(ui.expandpath(source), opts.get('rev'))
1838 source, revs, checkout = hg.parseurl(ui.expandpath(source), opts.get('rev'))
1850 other = hg.repository(cmdutil.remoteui(repo, opts), source)
1839 other = hg.repository(cmdutil.remoteui(repo, opts), source)
1851 ui.status(_('comparing with %s\n') % url.hidepassword(source))
1840 ui.status(_('comparing with %s\n') % url.hidepassword(source))
1852 if revs:
1841 if revs:
1853 revs = [other.lookup(rev) for rev in revs]
1842 revs = [other.lookup(rev) for rev in revs]
1854 common, incoming, rheads = repo.findcommonincoming(other, heads=revs,
1843 common, incoming, rheads = repo.findcommonincoming(other, heads=revs,
1855 force=opts["force"])
1844 force=opts["force"])
1856 if not incoming:
1845 if not incoming:
1857 try:
1846 try:
1858 os.unlink(opts["bundle"])
1847 os.unlink(opts["bundle"])
1859 except:
1848 except:
1860 pass
1849 pass
1861 ui.status(_("no changes found\n"))
1850 ui.status(_("no changes found\n"))
1862 return 1
1851 return 1
1863
1852
1864 cleanup = None
1853 cleanup = None
1865 try:
1854 try:
1866 fname = opts["bundle"]
1855 fname = opts["bundle"]
1867 if fname or not other.local():
1856 if fname or not other.local():
1868 # create a bundle (uncompressed if other repo is not local)
1857 # create a bundle (uncompressed if other repo is not local)
1869
1858
1870 if revs is None and other.capable('changegroupsubset'):
1859 if revs is None and other.capable('changegroupsubset'):
1871 revs = rheads
1860 revs = rheads
1872
1861
1873 if revs is None:
1862 if revs is None:
1874 cg = other.changegroup(incoming, "incoming")
1863 cg = other.changegroup(incoming, "incoming")
1875 else:
1864 else:
1876 cg = other.changegroupsubset(incoming, revs, 'incoming')
1865 cg = other.changegroupsubset(incoming, revs, 'incoming')
1877 bundletype = other.local() and "HG10BZ" or "HG10UN"
1866 bundletype = other.local() and "HG10BZ" or "HG10UN"
1878 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
1867 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
1879 # keep written bundle?
1868 # keep written bundle?
1880 if opts["bundle"]:
1869 if opts["bundle"]:
1881 cleanup = None
1870 cleanup = None
1882 if not other.local():
1871 if not other.local():
1883 # use the created uncompressed bundlerepo
1872 # use the created uncompressed bundlerepo
1884 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1873 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1885
1874
1886 o = other.changelog.nodesbetween(incoming, revs)[0]
1875 o = other.changelog.nodesbetween(incoming, revs)[0]
1887 if opts.get('newest_first'):
1876 if opts.get('newest_first'):
1888 o.reverse()
1877 o.reverse()
1889 displayer = cmdutil.show_changeset(ui, other, opts)
1878 displayer = cmdutil.show_changeset(ui, other, opts)
1890 count = 0
1879 count = 0
1891 for n in o:
1880 for n in o:
1892 if count >= limit:
1881 if count >= limit:
1893 break
1882 break
1894 parents = [p for p in other.changelog.parents(n) if p != nullid]
1883 parents = [p for p in other.changelog.parents(n) if p != nullid]
1895 if opts.get('no_merges') and len(parents) == 2:
1884 if opts.get('no_merges') and len(parents) == 2:
1896 continue
1885 continue
1897 count += 1
1886 count += 1
1898 displayer.show(other[n])
1887 displayer.show(other[n])
1899 finally:
1888 finally:
1900 if hasattr(other, 'close'):
1889 if hasattr(other, 'close'):
1901 other.close()
1890 other.close()
1902 if cleanup:
1891 if cleanup:
1903 os.unlink(cleanup)
1892 os.unlink(cleanup)
1904
1893
1905 def init(ui, dest=".", **opts):
1894 def init(ui, dest=".", **opts):
1906 """create a new repository in the given directory
1895 """create a new repository in the given directory
1907
1896
1908 Initialize a new repository in the given directory. If the given
1897 Initialize a new repository in the given directory. If the given
1909 directory does not exist, it will be created.
1898 directory does not exist, it will be created.
1910
1899
1911 If no directory is given, the current directory is used.
1900 If no directory is given, the current directory is used.
1912
1901
1913 It is possible to specify an ssh:// URL as the destination.
1902 It is possible to specify an ssh:// URL as the destination.
1914 See 'hg help urls' for more information.
1903 See 'hg help urls' for more information.
1915 """
1904 """
1916 hg.repository(cmdutil.remoteui(ui, opts), dest, create=1)
1905 hg.repository(cmdutil.remoteui(ui, opts), dest, create=1)
1917
1906
1918 def locate(ui, repo, *pats, **opts):
1907 def locate(ui, repo, *pats, **opts):
1919 """locate files matching specific patterns
1908 """locate files matching specific patterns
1920
1909
1921 Print files under Mercurial control in the working directory whose
1910 Print files under Mercurial control in the working directory whose
1922 names match the given patterns.
1911 names match the given patterns.
1923
1912
1924 By default, this command searches all directories in the working
1913 By default, this command searches all directories in the working
1925 directory. To search just the current directory and its
1914 directory. To search just the current directory and its
1926 subdirectories, use "--include .".
1915 subdirectories, use "--include .".
1927
1916
1928 If no patterns are given to match, this command prints the names
1917 If no patterns are given to match, this command prints the names
1929 of all files under Mercurial control in the working directory.
1918 of all files under Mercurial control in the working directory.
1930
1919
1931 If you want to feed the output of this command into the "xargs"
1920 If you want to feed the output of this command into the "xargs"
1932 command, use the -0 option to both this command and "xargs". This
1921 command, use the -0 option to both this command and "xargs". This
1933 will avoid the problem of "xargs" treating single filenames that
1922 will avoid the problem of "xargs" treating single filenames that
1934 contain whitespace as multiple filenames.
1923 contain whitespace as multiple filenames.
1935 """
1924 """
1936 end = opts.get('print0') and '\0' or '\n'
1925 end = opts.get('print0') and '\0' or '\n'
1937 rev = opts.get('rev') or None
1926 rev = opts.get('rev') or None
1938
1927
1939 ret = 1
1928 ret = 1
1940 m = cmdutil.match(repo, pats, opts, default='relglob')
1929 m = cmdutil.match(repo, pats, opts, default='relglob')
1941 m.bad = lambda x,y: False
1930 m.bad = lambda x,y: False
1942 for abs in repo[rev].walk(m):
1931 for abs in repo[rev].walk(m):
1943 if not rev and abs not in repo.dirstate:
1932 if not rev and abs not in repo.dirstate:
1944 continue
1933 continue
1945 if opts.get('fullpath'):
1934 if opts.get('fullpath'):
1946 ui.write(repo.wjoin(abs), end)
1935 ui.write(repo.wjoin(abs), end)
1947 else:
1936 else:
1948 ui.write(((pats and m.rel(abs)) or abs), end)
1937 ui.write(((pats and m.rel(abs)) or abs), end)
1949 ret = 0
1938 ret = 0
1950
1939
1951 return ret
1940 return ret
1952
1941
1953 def log(ui, repo, *pats, **opts):
1942 def log(ui, repo, *pats, **opts):
1954 """show revision history of entire repository or files
1943 """show revision history of entire repository or files
1955
1944
1956 Print the revision history of the specified files or the entire
1945 Print the revision history of the specified files or the entire
1957 project.
1946 project.
1958
1947
1959 File history is shown without following rename or copy history of
1948 File history is shown without following rename or copy history of
1960 files. Use -f/--follow with a filename to follow history across
1949 files. Use -f/--follow with a filename to follow history across
1961 renames and copies. --follow without a filename will only show
1950 renames and copies. --follow without a filename will only show
1962 ancestors or descendants of the starting revision. --follow-first
1951 ancestors or descendants of the starting revision. --follow-first
1963 only follows the first parent of merge revisions.
1952 only follows the first parent of merge revisions.
1964
1953
1965 If no revision range is specified, the default is tip:0 unless
1954 If no revision range is specified, the default is tip:0 unless
1966 --follow is set, in which case the working directory parent is
1955 --follow is set, in which case the working directory parent is
1967 used as the starting revision.
1956 used as the starting revision.
1968
1957
1969 See 'hg help dates' for a list of formats valid for -d/--date.
1958 See 'hg help dates' for a list of formats valid for -d/--date.
1970
1959
1971 By default this command prints revision number and changeset id,
1960 By default this command prints revision number and changeset id,
1972 tags, non-trivial parents, user, date and time, and a summary for
1961 tags, non-trivial parents, user, date and time, and a summary for
1973 each commit. When the -v/--verbose switch is used, the list of
1962 each commit. When the -v/--verbose switch is used, the list of
1974 changed files and full commit message are shown.
1963 changed files and full commit message are shown.
1975
1964
1976 NOTE: log -p/--patch may generate unexpected diff output for merge
1965 NOTE: log -p/--patch may generate unexpected diff output for merge
1977 changesets, as it will only compare the merge changeset against
1966 changesets, as it will only compare the merge changeset against
1978 its first parent. Also, only files different from BOTH parents
1967 its first parent. Also, only files different from BOTH parents
1979 will appear in files:.
1968 will appear in files:.
1980 """
1969 """
1981
1970
1982 get = util.cachefunc(lambda r: repo[r].changeset())
1971 get = util.cachefunc(lambda r: repo[r].changeset())
1983 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1972 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1984
1973
1985 limit = cmdutil.loglimit(opts)
1974 limit = cmdutil.loglimit(opts)
1986 count = 0
1975 count = 0
1987
1976
1988 if opts.get('copies') and opts.get('rev'):
1977 if opts.get('copies') and opts.get('rev'):
1989 endrev = max(cmdutil.revrange(repo, opts.get('rev'))) + 1
1978 endrev = max(cmdutil.revrange(repo, opts.get('rev'))) + 1
1990 else:
1979 else:
1991 endrev = len(repo)
1980 endrev = len(repo)
1992 rcache = {}
1981 rcache = {}
1993 ncache = {}
1982 ncache = {}
1994 def getrenamed(fn, rev):
1983 def getrenamed(fn, rev):
1995 '''looks up all renames for a file (up to endrev) the first
1984 '''looks up all renames for a file (up to endrev) the first
1996 time the file is given. It indexes on the changerev and only
1985 time the file is given. It indexes on the changerev and only
1997 parses the manifest if linkrev != changerev.
1986 parses the manifest if linkrev != changerev.
1998 Returns rename info for fn at changerev rev.'''
1987 Returns rename info for fn at changerev rev.'''
1999 if fn not in rcache:
1988 if fn not in rcache:
2000 rcache[fn] = {}
1989 rcache[fn] = {}
2001 ncache[fn] = {}
1990 ncache[fn] = {}
2002 fl = repo.file(fn)
1991 fl = repo.file(fn)
2003 for i in fl:
1992 for i in fl:
2004 node = fl.node(i)
1993 node = fl.node(i)
2005 lr = fl.linkrev(i)
1994 lr = fl.linkrev(i)
2006 renamed = fl.renamed(node)
1995 renamed = fl.renamed(node)
2007 rcache[fn][lr] = renamed
1996 rcache[fn][lr] = renamed
2008 if renamed:
1997 if renamed:
2009 ncache[fn][node] = renamed
1998 ncache[fn][node] = renamed
2010 if lr >= endrev:
1999 if lr >= endrev:
2011 break
2000 break
2012 if rev in rcache[fn]:
2001 if rev in rcache[fn]:
2013 return rcache[fn][rev]
2002 return rcache[fn][rev]
2014
2003
2015 # If linkrev != rev (i.e. rev not found in rcache) fallback to
2004 # If linkrev != rev (i.e. rev not found in rcache) fallback to
2016 # filectx logic.
2005 # filectx logic.
2017
2006
2018 try:
2007 try:
2019 return repo[rev][fn].renamed()
2008 return repo[rev][fn].renamed()
2020 except error.LookupError:
2009 except error.LookupError:
2021 pass
2010 pass
2022 return None
2011 return None
2023
2012
2024 df = False
2013 df = False
2025 if opts["date"]:
2014 if opts["date"]:
2026 df = util.matchdate(opts["date"])
2015 df = util.matchdate(opts["date"])
2027
2016
2028 only_branches = opts.get('only_branch')
2017 only_branches = opts.get('only_branch')
2029
2018
2030 displayer = cmdutil.show_changeset(ui, repo, opts, True, matchfn)
2019 displayer = cmdutil.show_changeset(ui, repo, opts, True, matchfn)
2031 for st, rev, fns in changeiter:
2020 for st, rev, fns in changeiter:
2032 if st == 'add':
2021 if st == 'add':
2033 parents = [p for p in repo.changelog.parentrevs(rev)
2022 parents = [p for p in repo.changelog.parentrevs(rev)
2034 if p != nullrev]
2023 if p != nullrev]
2035 if opts.get('no_merges') and len(parents) == 2:
2024 if opts.get('no_merges') and len(parents) == 2:
2036 continue
2025 continue
2037 if opts.get('only_merges') and len(parents) != 2:
2026 if opts.get('only_merges') and len(parents) != 2:
2038 continue
2027 continue
2039
2028
2040 if only_branches:
2029 if only_branches:
2041 revbranch = get(rev)[5]['branch']
2030 revbranch = get(rev)[5]['branch']
2042 if revbranch not in only_branches:
2031 if revbranch not in only_branches:
2043 continue
2032 continue
2044
2033
2045 if df:
2034 if df:
2046 changes = get(rev)
2035 changes = get(rev)
2047 if not df(changes[2][0]):
2036 if not df(changes[2][0]):
2048 continue
2037 continue
2049
2038
2050 if opts.get('keyword'):
2039 if opts.get('keyword'):
2051 changes = get(rev)
2040 changes = get(rev)
2052 miss = 0
2041 miss = 0
2053 for k in [kw.lower() for kw in opts['keyword']]:
2042 for k in [kw.lower() for kw in opts['keyword']]:
2054 if not (k in changes[1].lower() or
2043 if not (k in changes[1].lower() or
2055 k in changes[4].lower() or
2044 k in changes[4].lower() or
2056 k in " ".join(changes[3]).lower()):
2045 k in " ".join(changes[3]).lower()):
2057 miss = 1
2046 miss = 1
2058 break
2047 break
2059 if miss:
2048 if miss:
2060 continue
2049 continue
2061
2050
2062 if opts['user']:
2051 if opts['user']:
2063 changes = get(rev)
2052 changes = get(rev)
2064 if not [k for k in opts['user'] if k in changes[1]]:
2053 if not [k for k in opts['user'] if k in changes[1]]:
2065 continue
2054 continue
2066
2055
2067 copies = []
2056 copies = []
2068 if opts.get('copies') and rev:
2057 if opts.get('copies') and rev:
2069 for fn in get(rev)[3]:
2058 for fn in get(rev)[3]:
2070 rename = getrenamed(fn, rev)
2059 rename = getrenamed(fn, rev)
2071 if rename:
2060 if rename:
2072 copies.append((fn, rename[0]))
2061 copies.append((fn, rename[0]))
2073 displayer.show(context.changectx(repo, rev), copies=copies)
2062 displayer.show(context.changectx(repo, rev), copies=copies)
2074 elif st == 'iter':
2063 elif st == 'iter':
2075 if count == limit: break
2064 if count == limit: break
2076 if displayer.flush(rev):
2065 if displayer.flush(rev):
2077 count += 1
2066 count += 1
2078
2067
2079 def manifest(ui, repo, node=None, rev=None):
2068 def manifest(ui, repo, node=None, rev=None):
2080 """output the current or given revision of the project manifest
2069 """output the current or given revision of the project manifest
2081
2070
2082 Print a list of version controlled files for the given revision.
2071 Print a list of version controlled files for the given revision.
2083 If no revision is given, the first parent of the working directory
2072 If no revision is given, the first parent of the working directory
2084 is used, or the null revision if no revision is checked out.
2073 is used, or the null revision if no revision is checked out.
2085
2074
2086 With -v, print file permissions, symlink and executable bits.
2075 With -v, print file permissions, symlink and executable bits.
2087 With --debug, print file revision hashes.
2076 With --debug, print file revision hashes.
2088 """
2077 """
2089
2078
2090 if rev and node:
2079 if rev and node:
2091 raise util.Abort(_("please specify just one revision"))
2080 raise util.Abort(_("please specify just one revision"))
2092
2081
2093 if not node:
2082 if not node:
2094 node = rev
2083 node = rev
2095
2084
2096 decor = {'l':'644 @ ', 'x':'755 * ', '':'644 '}
2085 decor = {'l':'644 @ ', 'x':'755 * ', '':'644 '}
2097 ctx = repo[node]
2086 ctx = repo[node]
2098 for f in ctx:
2087 for f in ctx:
2099 if ui.debugflag:
2088 if ui.debugflag:
2100 ui.write("%40s " % hex(ctx.manifest()[f]))
2089 ui.write("%40s " % hex(ctx.manifest()[f]))
2101 if ui.verbose:
2090 if ui.verbose:
2102 ui.write(decor[ctx.flags(f)])
2091 ui.write(decor[ctx.flags(f)])
2103 ui.write("%s\n" % f)
2092 ui.write("%s\n" % f)
2104
2093
2105 def merge(ui, repo, node=None, **opts):
2094 def merge(ui, repo, node=None, **opts):
2106 """merge working directory with another revision
2095 """merge working directory with another revision
2107
2096
2108 The current working directory is updated with all changes made in
2097 The current working directory is updated with all changes made in
2109 the requested revision since the last common predecessor revision.
2098 the requested revision since the last common predecessor revision.
2110
2099
2111 Files that changed between either parent are marked as changed for
2100 Files that changed between either parent are marked as changed for
2112 the next commit and a commit must be performed before any further
2101 the next commit and a commit must be performed before any further
2113 updates to the repository are allowed. The next commit will have
2102 updates to the repository are allowed. The next commit will have
2114 two parents.
2103 two parents.
2115
2104
2116 If no revision is specified, the working directory's parent is a
2105 If no revision is specified, the working directory's parent is a
2117 head revision, and the current branch contains exactly one other
2106 head revision, and the current branch contains exactly one other
2118 head, the other head is merged with by default. Otherwise, an
2107 head, the other head is merged with by default. Otherwise, an
2119 explicit revision with which to merge with must be provided.
2108 explicit revision with which to merge with must be provided.
2120 """
2109 """
2121
2110
2122 if opts.get('rev') and node:
2111 if opts.get('rev') and node:
2123 raise util.Abort(_("please specify just one revision"))
2112 raise util.Abort(_("please specify just one revision"))
2124 if not node:
2113 if not node:
2125 node = opts.get('rev')
2114 node = opts.get('rev')
2126
2115
2127 if not node:
2116 if not node:
2128 branch = repo.changectx(None).branch()
2117 branch = repo.changectx(None).branch()
2129 bheads = repo.branchheads(branch)
2118 bheads = repo.branchheads(branch)
2130 if len(bheads) > 2:
2119 if len(bheads) > 2:
2131 raise util.Abort(_("branch '%s' has %d heads - "
2120 raise util.Abort(_("branch '%s' has %d heads - "
2132 "please merge with an explicit rev") %
2121 "please merge with an explicit rev") %
2133 (branch, len(bheads)))
2122 (branch, len(bheads)))
2134
2123
2135 parent = repo.dirstate.parents()[0]
2124 parent = repo.dirstate.parents()[0]
2136 if len(bheads) == 1:
2125 if len(bheads) == 1:
2137 if len(repo.heads()) > 1:
2126 if len(repo.heads()) > 1:
2138 raise util.Abort(_("branch '%s' has one head - "
2127 raise util.Abort(_("branch '%s' has one head - "
2139 "please merge with an explicit rev") %
2128 "please merge with an explicit rev") %
2140 branch)
2129 branch)
2141 msg = _('there is nothing to merge')
2130 msg = _('there is nothing to merge')
2142 if parent != repo.lookup(repo[None].branch()):
2131 if parent != repo.lookup(repo[None].branch()):
2143 msg = _('%s - use "hg update" instead') % msg
2132 msg = _('%s - use "hg update" instead') % msg
2144 raise util.Abort(msg)
2133 raise util.Abort(msg)
2145
2134
2146 if parent not in bheads:
2135 if parent not in bheads:
2147 raise util.Abort(_('working dir not at a head rev - '
2136 raise util.Abort(_('working dir not at a head rev - '
2148 'use "hg update" or merge with an explicit rev'))
2137 'use "hg update" or merge with an explicit rev'))
2149 node = parent == bheads[0] and bheads[-1] or bheads[0]
2138 node = parent == bheads[0] and bheads[-1] or bheads[0]
2150
2139
2151 if opts.get('preview'):
2140 if opts.get('preview'):
2152 p1 = repo['.']
2141 p1 = repo['.']
2153 p2 = repo[node]
2142 p2 = repo[node]
2154 common = p1.ancestor(p2)
2143 common = p1.ancestor(p2)
2155 roots, heads = [common.node()], [p2.node()]
2144 roots, heads = [common.node()], [p2.node()]
2156 displayer = cmdutil.show_changeset(ui, repo, opts)
2145 displayer = cmdutil.show_changeset(ui, repo, opts)
2157 for node in repo.changelog.nodesbetween(roots=roots, heads=heads)[0]:
2146 for node in repo.changelog.nodesbetween(roots=roots, heads=heads)[0]:
2158 displayer.show(repo[node])
2147 displayer.show(repo[node])
2159 return 0
2148 return 0
2160
2149
2161 return hg.merge(repo, node, force=opts.get('force'))
2150 return hg.merge(repo, node, force=opts.get('force'))
2162
2151
2163 def outgoing(ui, repo, dest=None, **opts):
2152 def outgoing(ui, repo, dest=None, **opts):
2164 """show changesets not found in destination
2153 """show changesets not found in destination
2165
2154
2166 Show changesets not found in the specified destination repository
2155 Show changesets not found in the specified destination repository
2167 or the default push location. These are the changesets that would
2156 or the default push location. These are the changesets that would
2168 be pushed if a push was requested.
2157 be pushed if a push was requested.
2169
2158
2170 See pull for valid destination format details.
2159 See pull for valid destination format details.
2171 """
2160 """
2172 limit = cmdutil.loglimit(opts)
2161 limit = cmdutil.loglimit(opts)
2173 dest, revs, checkout = hg.parseurl(
2162 dest, revs, checkout = hg.parseurl(
2174 ui.expandpath(dest or 'default-push', dest or 'default'), opts.get('rev'))
2163 ui.expandpath(dest or 'default-push', dest or 'default'), opts.get('rev'))
2175 if revs:
2164 if revs:
2176 revs = [repo.lookup(rev) for rev in revs]
2165 revs = [repo.lookup(rev) for rev in revs]
2177
2166
2178 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
2167 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
2179 ui.status(_('comparing with %s\n') % url.hidepassword(dest))
2168 ui.status(_('comparing with %s\n') % url.hidepassword(dest))
2180 o = repo.findoutgoing(other, force=opts.get('force'))
2169 o = repo.findoutgoing(other, force=opts.get('force'))
2181 if not o:
2170 if not o:
2182 ui.status(_("no changes found\n"))
2171 ui.status(_("no changes found\n"))
2183 return 1
2172 return 1
2184 o = repo.changelog.nodesbetween(o, revs)[0]
2173 o = repo.changelog.nodesbetween(o, revs)[0]
2185 if opts.get('newest_first'):
2174 if opts.get('newest_first'):
2186 o.reverse()
2175 o.reverse()
2187 displayer = cmdutil.show_changeset(ui, repo, opts)
2176 displayer = cmdutil.show_changeset(ui, repo, opts)
2188 count = 0
2177 count = 0
2189 for n in o:
2178 for n in o:
2190 if count >= limit:
2179 if count >= limit:
2191 break
2180 break
2192 parents = [p for p in repo.changelog.parents(n) if p != nullid]
2181 parents = [p for p in repo.changelog.parents(n) if p != nullid]
2193 if opts.get('no_merges') and len(parents) == 2:
2182 if opts.get('no_merges') and len(parents) == 2:
2194 continue
2183 continue
2195 count += 1
2184 count += 1
2196 displayer.show(repo[n])
2185 displayer.show(repo[n])
2197
2186
2198 def parents(ui, repo, file_=None, **opts):
2187 def parents(ui, repo, file_=None, **opts):
2199 """show the parents of the working directory or revision
2188 """show the parents of the working directory or revision
2200
2189
2201 Print the working directory's parent revisions. If a revision is
2190 Print the working directory's parent revisions. If a revision is
2202 given via -r/--rev, the parent of that revision will be printed.
2191 given via -r/--rev, the parent of that revision will be printed.
2203 If a file argument is given, the revision in which the file was
2192 If a file argument is given, the revision in which the file was
2204 last changed (before the working directory revision or the
2193 last changed (before the working directory revision or the
2205 argument to --rev if given) is printed.
2194 argument to --rev if given) is printed.
2206 """
2195 """
2207 rev = opts.get('rev')
2196 rev = opts.get('rev')
2208 if rev:
2197 if rev:
2209 ctx = repo[rev]
2198 ctx = repo[rev]
2210 else:
2199 else:
2211 ctx = repo[None]
2200 ctx = repo[None]
2212
2201
2213 if file_:
2202 if file_:
2214 m = cmdutil.match(repo, (file_,), opts)
2203 m = cmdutil.match(repo, (file_,), opts)
2215 if m.anypats() or len(m.files()) != 1:
2204 if m.anypats() or len(m.files()) != 1:
2216 raise util.Abort(_('can only specify an explicit filename'))
2205 raise util.Abort(_('can only specify an explicit filename'))
2217 file_ = m.files()[0]
2206 file_ = m.files()[0]
2218 filenodes = []
2207 filenodes = []
2219 for cp in ctx.parents():
2208 for cp in ctx.parents():
2220 if not cp:
2209 if not cp:
2221 continue
2210 continue
2222 try:
2211 try:
2223 filenodes.append(cp.filenode(file_))
2212 filenodes.append(cp.filenode(file_))
2224 except error.LookupError:
2213 except error.LookupError:
2225 pass
2214 pass
2226 if not filenodes:
2215 if not filenodes:
2227 raise util.Abort(_("'%s' not found in manifest!") % file_)
2216 raise util.Abort(_("'%s' not found in manifest!") % file_)
2228 fl = repo.file(file_)
2217 fl = repo.file(file_)
2229 p = [repo.lookup(fl.linkrev(fl.rev(fn))) for fn in filenodes]
2218 p = [repo.lookup(fl.linkrev(fl.rev(fn))) for fn in filenodes]
2230 else:
2219 else:
2231 p = [cp.node() for cp in ctx.parents()]
2220 p = [cp.node() for cp in ctx.parents()]
2232
2221
2233 displayer = cmdutil.show_changeset(ui, repo, opts)
2222 displayer = cmdutil.show_changeset(ui, repo, opts)
2234 for n in p:
2223 for n in p:
2235 if n != nullid:
2224 if n != nullid:
2236 displayer.show(repo[n])
2225 displayer.show(repo[n])
2237
2226
2238 def paths(ui, repo, search=None):
2227 def paths(ui, repo, search=None):
2239 """show aliases for remote repositories
2228 """show aliases for remote repositories
2240
2229
2241 Show definition of symbolic path name NAME. If no name is given,
2230 Show definition of symbolic path name NAME. If no name is given,
2242 show definition of all available names.
2231 show definition of all available names.
2243
2232
2244 Path names are defined in the [paths] section of /etc/mercurial/hgrc
2233 Path names are defined in the [paths] section of /etc/mercurial/hgrc
2245 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
2234 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
2246
2235
2247 See 'hg help urls' for more information.
2236 See 'hg help urls' for more information.
2248 """
2237 """
2249 if search:
2238 if search:
2250 for name, path in ui.configitems("paths"):
2239 for name, path in ui.configitems("paths"):
2251 if name == search:
2240 if name == search:
2252 ui.write("%s\n" % url.hidepassword(path))
2241 ui.write("%s\n" % url.hidepassword(path))
2253 return
2242 return
2254 ui.warn(_("not found!\n"))
2243 ui.warn(_("not found!\n"))
2255 return 1
2244 return 1
2256 else:
2245 else:
2257 for name, path in ui.configitems("paths"):
2246 for name, path in ui.configitems("paths"):
2258 ui.write("%s = %s\n" % (name, url.hidepassword(path)))
2247 ui.write("%s = %s\n" % (name, url.hidepassword(path)))
2259
2248
2260 def postincoming(ui, repo, modheads, optupdate, checkout):
2249 def postincoming(ui, repo, modheads, optupdate, checkout):
2261 if modheads == 0:
2250 if modheads == 0:
2262 return
2251 return
2263 if optupdate:
2252 if optupdate:
2264 if (modheads <= 1 or len(repo.branchheads()) == 1) or checkout:
2253 if (modheads <= 1 or len(repo.branchheads()) == 1) or checkout:
2265 return hg.update(repo, checkout)
2254 return hg.update(repo, checkout)
2266 else:
2255 else:
2267 ui.status(_("not updating, since new heads added\n"))
2256 ui.status(_("not updating, since new heads added\n"))
2268 if modheads > 1:
2257 if modheads > 1:
2269 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2258 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2270 else:
2259 else:
2271 ui.status(_("(run 'hg update' to get a working copy)\n"))
2260 ui.status(_("(run 'hg update' to get a working copy)\n"))
2272
2261
2273 def pull(ui, repo, source="default", **opts):
2262 def pull(ui, repo, source="default", **opts):
2274 """pull changes from the specified source
2263 """pull changes from the specified source
2275
2264
2276 Pull changes from a remote repository to a local one.
2265 Pull changes from a remote repository to a local one.
2277
2266
2278 This finds all changes from the repository at the specified path
2267 This finds all changes from the repository at the specified path
2279 or URL and adds them to a local repository (the current one unless
2268 or URL and adds them to a local repository (the current one unless
2280 -R is specified). By default, this does not update the copy of the
2269 -R is specified). By default, this does not update the copy of the
2281 project in the working directory.
2270 project in the working directory.
2282
2271
2283 Use hg incoming if you want to see what would have been added by a
2272 Use hg incoming if you want to see what would have been added by a
2284 pull at the time you issued this command. If you then decide to
2273 pull at the time you issued this command. If you then decide to
2285 added those changes to the repository, you should use pull -r X
2274 added those changes to the repository, you should use pull -r X
2286 where X is the last changeset listed by hg incoming.
2275 where X is the last changeset listed by hg incoming.
2287
2276
2288 If SOURCE is omitted, the 'default' path will be used.
2277 If SOURCE is omitted, the 'default' path will be used.
2289 See 'hg help urls' for more information.
2278 See 'hg help urls' for more information.
2290 """
2279 """
2291 source, revs, checkout = hg.parseurl(ui.expandpath(source), opts.get('rev'))
2280 source, revs, checkout = hg.parseurl(ui.expandpath(source), opts.get('rev'))
2292 other = hg.repository(cmdutil.remoteui(repo, opts), source)
2281 other = hg.repository(cmdutil.remoteui(repo, opts), source)
2293 ui.status(_('pulling from %s\n') % url.hidepassword(source))
2282 ui.status(_('pulling from %s\n') % url.hidepassword(source))
2294 if revs:
2283 if revs:
2295 try:
2284 try:
2296 revs = [other.lookup(rev) for rev in revs]
2285 revs = [other.lookup(rev) for rev in revs]
2297 except error.CapabilityError:
2286 except error.CapabilityError:
2298 err = _("Other repository doesn't support revision lookup, "
2287 err = _("Other repository doesn't support revision lookup, "
2299 "so a rev cannot be specified.")
2288 "so a rev cannot be specified.")
2300 raise util.Abort(err)
2289 raise util.Abort(err)
2301
2290
2302 modheads = repo.pull(other, heads=revs, force=opts.get('force'))
2291 modheads = repo.pull(other, heads=revs, force=opts.get('force'))
2303 return postincoming(ui, repo, modheads, opts.get('update'), checkout)
2292 return postincoming(ui, repo, modheads, opts.get('update'), checkout)
2304
2293
2305 def push(ui, repo, dest=None, **opts):
2294 def push(ui, repo, dest=None, **opts):
2306 """push changes to the specified destination
2295 """push changes to the specified destination
2307
2296
2308 Push changes from the local repository to the given destination.
2297 Push changes from the local repository to the given destination.
2309
2298
2310 This is the symmetrical operation for pull. It moves changes from
2299 This is the symmetrical operation for pull. It moves changes from
2311 the current repository to a different one. If the destination is
2300 the current repository to a different one. If the destination is
2312 local this is identical to a pull in that directory from the
2301 local this is identical to a pull in that directory from the
2313 current one.
2302 current one.
2314
2303
2315 By default, push will refuse to run if it detects the result would
2304 By default, push will refuse to run if it detects the result would
2316 increase the number of remote heads. This generally indicates the
2305 increase the number of remote heads. This generally indicates the
2317 user forgot to pull and merge before pushing.
2306 user forgot to pull and merge before pushing.
2318
2307
2319 If -r/--rev is used, the named revision and all its ancestors will
2308 If -r/--rev is used, the named revision and all its ancestors will
2320 be pushed to the remote repository.
2309 be pushed to the remote repository.
2321
2310
2322 Please see 'hg help urls' for important details about ssh://
2311 Please see 'hg help urls' for important details about ssh://
2323 URLs. If DESTINATION is omitted, a default path will be used.
2312 URLs. If DESTINATION is omitted, a default path will be used.
2324 """
2313 """
2325 dest, revs, checkout = hg.parseurl(
2314 dest, revs, checkout = hg.parseurl(
2326 ui.expandpath(dest or 'default-push', dest or 'default'), opts.get('rev'))
2315 ui.expandpath(dest or 'default-push', dest or 'default'), opts.get('rev'))
2327 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
2316 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
2328 ui.status(_('pushing to %s\n') % url.hidepassword(dest))
2317 ui.status(_('pushing to %s\n') % url.hidepassword(dest))
2329 if revs:
2318 if revs:
2330 revs = [repo.lookup(rev) for rev in revs]
2319 revs = [repo.lookup(rev) for rev in revs]
2331
2320
2332 # push subrepos depth-first for coherent ordering
2321 # push subrepos depth-first for coherent ordering
2333 c = repo['']
2322 c = repo['']
2334 subs = c.substate # only repos that are committed
2323 subs = c.substate # only repos that are committed
2335 for s in sorted(subs):
2324 for s in sorted(subs):
2336 c.sub(s).push(opts.get('force'))
2325 c.sub(s).push(opts.get('force'))
2337
2326
2338 r = repo.push(other, opts.get('force'), revs=revs)
2327 r = repo.push(other, opts.get('force'), revs=revs)
2339 return r == 0
2328 return r == 0
2340
2329
2341 def recover(ui, repo):
2330 def recover(ui, repo):
2342 """roll back an interrupted transaction
2331 """roll back an interrupted transaction
2343
2332
2344 Recover from an interrupted commit or pull.
2333 Recover from an interrupted commit or pull.
2345
2334
2346 This command tries to fix the repository status after an
2335 This command tries to fix the repository status after an
2347 interrupted operation. It should only be necessary when Mercurial
2336 interrupted operation. It should only be necessary when Mercurial
2348 suggests it.
2337 suggests it.
2349 """
2338 """
2350 if repo.recover():
2339 if repo.recover():
2351 return hg.verify(repo)
2340 return hg.verify(repo)
2352 return 1
2341 return 1
2353
2342
2354 def remove(ui, repo, *pats, **opts):
2343 def remove(ui, repo, *pats, **opts):
2355 """remove the specified files on the next commit
2344 """remove the specified files on the next commit
2356
2345
2357 Schedule the indicated files for removal from the repository.
2346 Schedule the indicated files for removal from the repository.
2358
2347
2359 This only removes files from the current branch, not from the
2348 This only removes files from the current branch, not from the
2360 entire project history. -A/--after can be used to remove only
2349 entire project history. -A/--after can be used to remove only
2361 files that have already been deleted, -f/--force can be used to
2350 files that have already been deleted, -f/--force can be used to
2362 force deletion, and -Af can be used to remove files from the next
2351 force deletion, and -Af can be used to remove files from the next
2363 revision without deleting them from the working directory.
2352 revision without deleting them from the working directory.
2364
2353
2365 The following table details the behavior of remove for different
2354 The following table details the behavior of remove for different
2366 file states (columns) and option combinations (rows). The file
2355 file states (columns) and option combinations (rows). The file
2367 states are Added [A], Clean [C], Modified [M] and Missing [!]
2356 states are Added [A], Clean [C], Modified [M] and Missing [!]
2368 (as reported by hg status). The actions are Warn, Remove (from
2357 (as reported by hg status). The actions are Warn, Remove (from
2369 branch) and Delete (from disk).
2358 branch) and Delete (from disk).
2370
2359
2371 A C M !
2360 A C M !
2372 none W RD W R
2361 none W RD W R
2373 -f R RD RD R
2362 -f R RD RD R
2374 -A W W W R
2363 -A W W W R
2375 -Af R R R R
2364 -Af R R R R
2376
2365
2377 This command schedules the files to be removed at the next commit.
2366 This command schedules the files to be removed at the next commit.
2378 To undo a remove before that, see hg revert.
2367 To undo a remove before that, see hg revert.
2379 """
2368 """
2380
2369
2381 after, force = opts.get('after'), opts.get('force')
2370 after, force = opts.get('after'), opts.get('force')
2382 if not pats and not after:
2371 if not pats and not after:
2383 raise util.Abort(_('no files specified'))
2372 raise util.Abort(_('no files specified'))
2384
2373
2385 m = cmdutil.match(repo, pats, opts)
2374 m = cmdutil.match(repo, pats, opts)
2386 s = repo.status(match=m, clean=True)
2375 s = repo.status(match=m, clean=True)
2387 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
2376 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
2388
2377
2389 for f in m.files():
2378 for f in m.files():
2390 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
2379 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
2391 ui.warn(_('not removing %s: file is untracked\n') % m.rel(f))
2380 ui.warn(_('not removing %s: file is untracked\n') % m.rel(f))
2392
2381
2393 def warn(files, reason):
2382 def warn(files, reason):
2394 for f in files:
2383 for f in files:
2395 ui.warn(_('not removing %s: file %s (use -f to force removal)\n')
2384 ui.warn(_('not removing %s: file %s (use -f to force removal)\n')
2396 % (m.rel(f), reason))
2385 % (m.rel(f), reason))
2397
2386
2398 if force:
2387 if force:
2399 remove, forget = modified + deleted + clean, added
2388 remove, forget = modified + deleted + clean, added
2400 elif after:
2389 elif after:
2401 remove, forget = deleted, []
2390 remove, forget = deleted, []
2402 warn(modified + added + clean, _('still exists'))
2391 warn(modified + added + clean, _('still exists'))
2403 else:
2392 else:
2404 remove, forget = deleted + clean, []
2393 remove, forget = deleted + clean, []
2405 warn(modified, _('is modified'))
2394 warn(modified, _('is modified'))
2406 warn(added, _('has been marked for add'))
2395 warn(added, _('has been marked for add'))
2407
2396
2408 for f in sorted(remove + forget):
2397 for f in sorted(remove + forget):
2409 if ui.verbose or not m.exact(f):
2398 if ui.verbose or not m.exact(f):
2410 ui.status(_('removing %s\n') % m.rel(f))
2399 ui.status(_('removing %s\n') % m.rel(f))
2411
2400
2412 repo.forget(forget)
2401 repo.forget(forget)
2413 repo.remove(remove, unlink=not after)
2402 repo.remove(remove, unlink=not after)
2414
2403
2415 def rename(ui, repo, *pats, **opts):
2404 def rename(ui, repo, *pats, **opts):
2416 """rename files; equivalent of copy + remove
2405 """rename files; equivalent of copy + remove
2417
2406
2418 Mark dest as copies of sources; mark sources for deletion. If dest
2407 Mark dest as copies of sources; mark sources for deletion. If dest
2419 is a directory, copies are put in that directory. If dest is a
2408 is a directory, copies are put in that directory. If dest is a
2420 file, there can only be one source.
2409 file, there can only be one source.
2421
2410
2422 By default, this command copies the contents of files as they
2411 By default, this command copies the contents of files as they
2423 exist in the working directory. If invoked with -A/--after, the
2412 exist in the working directory. If invoked with -A/--after, the
2424 operation is recorded, but no copying is performed.
2413 operation is recorded, but no copying is performed.
2425
2414
2426 This command takes effect at the next commit. To undo a rename
2415 This command takes effect at the next commit. To undo a rename
2427 before that, see hg revert.
2416 before that, see hg revert.
2428 """
2417 """
2429 wlock = repo.wlock(False)
2418 wlock = repo.wlock(False)
2430 try:
2419 try:
2431 return cmdutil.copy(ui, repo, pats, opts, rename=True)
2420 return cmdutil.copy(ui, repo, pats, opts, rename=True)
2432 finally:
2421 finally:
2433 wlock.release()
2422 wlock.release()
2434
2423
2435 def resolve(ui, repo, *pats, **opts):
2424 def resolve(ui, repo, *pats, **opts):
2436 """retry file merges from a merge or update
2425 """retry file merges from a merge or update
2437
2426
2438 This command will cleanly retry unresolved file merges using file
2427 This command will cleanly retry unresolved file merges using file
2439 revisions preserved from the last update or merge. To attempt to
2428 revisions preserved from the last update or merge. To attempt to
2440 resolve all unresolved files, use the -a/--all switch.
2429 resolve all unresolved files, use the -a/--all switch.
2441
2430
2442 If a conflict is resolved manually, please note that the changes
2431 If a conflict is resolved manually, please note that the changes
2443 will be overwritten if the merge is retried with resolve. The
2432 will be overwritten if the merge is retried with resolve. The
2444 -m/--mark switch should be used to mark the file as resolved.
2433 -m/--mark switch should be used to mark the file as resolved.
2445
2434
2446 This command also allows listing resolved files and manually
2435 This command also allows listing resolved files and manually
2447 indicating whether or not files are resolved. All files must be
2436 indicating whether or not files are resolved. All files must be
2448 marked as resolved before a commit is permitted.
2437 marked as resolved before a commit is permitted.
2449
2438
2450 The codes used to show the status of files are:
2439 The codes used to show the status of files are:
2451 U = unresolved
2440 U = unresolved
2452 R = resolved
2441 R = resolved
2453 """
2442 """
2454
2443
2455 all, mark, unmark, show = [opts.get(o) for o in 'all mark unmark list'.split()]
2444 all, mark, unmark, show = [opts.get(o) for o in 'all mark unmark list'.split()]
2456
2445
2457 if (show and (mark or unmark)) or (mark and unmark):
2446 if (show and (mark or unmark)) or (mark and unmark):
2458 raise util.Abort(_("too many options specified"))
2447 raise util.Abort(_("too many options specified"))
2459 if pats and all:
2448 if pats and all:
2460 raise util.Abort(_("can't specify --all and patterns"))
2449 raise util.Abort(_("can't specify --all and patterns"))
2461 if not (all or pats or show or mark or unmark):
2450 if not (all or pats or show or mark or unmark):
2462 raise util.Abort(_('no files or directories specified; '
2451 raise util.Abort(_('no files or directories specified; '
2463 'use --all to remerge all files'))
2452 'use --all to remerge all files'))
2464
2453
2465 ms = merge_.mergestate(repo)
2454 ms = merge_.mergestate(repo)
2466 m = cmdutil.match(repo, pats, opts)
2455 m = cmdutil.match(repo, pats, opts)
2467
2456
2468 for f in ms:
2457 for f in ms:
2469 if m(f):
2458 if m(f):
2470 if show:
2459 if show:
2471 ui.write("%s %s\n" % (ms[f].upper(), f))
2460 ui.write("%s %s\n" % (ms[f].upper(), f))
2472 elif mark:
2461 elif mark:
2473 ms.mark(f, "r")
2462 ms.mark(f, "r")
2474 elif unmark:
2463 elif unmark:
2475 ms.mark(f, "u")
2464 ms.mark(f, "u")
2476 else:
2465 else:
2477 wctx = repo[None]
2466 wctx = repo[None]
2478 mctx = wctx.parents()[-1]
2467 mctx = wctx.parents()[-1]
2479
2468
2480 # backup pre-resolve (merge uses .orig for its own purposes)
2469 # backup pre-resolve (merge uses .orig for its own purposes)
2481 a = repo.wjoin(f)
2470 a = repo.wjoin(f)
2482 util.copyfile(a, a + ".resolve")
2471 util.copyfile(a, a + ".resolve")
2483
2472
2484 # resolve file
2473 # resolve file
2485 ms.resolve(f, wctx, mctx)
2474 ms.resolve(f, wctx, mctx)
2486
2475
2487 # replace filemerge's .orig file with our resolve file
2476 # replace filemerge's .orig file with our resolve file
2488 util.rename(a + ".resolve", a + ".orig")
2477 util.rename(a + ".resolve", a + ".orig")
2489
2478
2490 def revert(ui, repo, *pats, **opts):
2479 def revert(ui, repo, *pats, **opts):
2491 """restore individual files or directories to an earlier state
2480 """restore individual files or directories to an earlier state
2492
2481
2493 (Use update -r to check out earlier revisions, revert does not
2482 (Use update -r to check out earlier revisions, revert does not
2494 change the working directory parents.)
2483 change the working directory parents.)
2495
2484
2496 With no revision specified, revert the named files or directories
2485 With no revision specified, revert the named files or directories
2497 to the contents they had in the parent of the working directory.
2486 to the contents they had in the parent of the working directory.
2498 This restores the contents of the affected files to an unmodified
2487 This restores the contents of the affected files to an unmodified
2499 state and unschedules adds, removes, copies, and renames. If the
2488 state and unschedules adds, removes, copies, and renames. If the
2500 working directory has two parents, you must explicitly specify the
2489 working directory has two parents, you must explicitly specify the
2501 revision to revert to.
2490 revision to revert to.
2502
2491
2503 Using the -r/--rev option, revert the given files or directories
2492 Using the -r/--rev option, revert the given files or directories
2504 to their contents as of a specific revision. This can be helpful
2493 to their contents as of a specific revision. This can be helpful
2505 to "roll back" some or all of an earlier change. See 'hg help
2494 to "roll back" some or all of an earlier change. See 'hg help
2506 dates' for a list of formats valid for -d/--date.
2495 dates' for a list of formats valid for -d/--date.
2507
2496
2508 Revert modifies the working directory. It does not commit any
2497 Revert modifies the working directory. It does not commit any
2509 changes, or change the parent of the working directory. If you
2498 changes, or change the parent of the working directory. If you
2510 revert to a revision other than the parent of the working
2499 revert to a revision other than the parent of the working
2511 directory, the reverted files will thus appear modified
2500 directory, the reverted files will thus appear modified
2512 afterwards.
2501 afterwards.
2513
2502
2514 If a file has been deleted, it is restored. If the executable mode
2503 If a file has been deleted, it is restored. If the executable mode
2515 of a file was changed, it is reset.
2504 of a file was changed, it is reset.
2516
2505
2517 If names are given, all files matching the names are reverted.
2506 If names are given, all files matching the names are reverted.
2518 If no arguments are given, no files are reverted.
2507 If no arguments are given, no files are reverted.
2519
2508
2520 Modified files are saved with a .orig suffix before reverting.
2509 Modified files are saved with a .orig suffix before reverting.
2521 To disable these backups, use --no-backup.
2510 To disable these backups, use --no-backup.
2522 """
2511 """
2523
2512
2524 if opts["date"]:
2513 if opts["date"]:
2525 if opts["rev"]:
2514 if opts["rev"]:
2526 raise util.Abort(_("you can't specify a revision and a date"))
2515 raise util.Abort(_("you can't specify a revision and a date"))
2527 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
2516 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
2528
2517
2529 if not pats and not opts.get('all'):
2518 if not pats and not opts.get('all'):
2530 raise util.Abort(_('no files or directories specified; '
2519 raise util.Abort(_('no files or directories specified; '
2531 'use --all to revert the whole repo'))
2520 'use --all to revert the whole repo'))
2532
2521
2533 parent, p2 = repo.dirstate.parents()
2522 parent, p2 = repo.dirstate.parents()
2534 if not opts.get('rev') and p2 != nullid:
2523 if not opts.get('rev') and p2 != nullid:
2535 raise util.Abort(_('uncommitted merge - please provide a '
2524 raise util.Abort(_('uncommitted merge - please provide a '
2536 'specific revision'))
2525 'specific revision'))
2537 ctx = repo[opts.get('rev')]
2526 ctx = repo[opts.get('rev')]
2538 node = ctx.node()
2527 node = ctx.node()
2539 mf = ctx.manifest()
2528 mf = ctx.manifest()
2540 if node == parent:
2529 if node == parent:
2541 pmf = mf
2530 pmf = mf
2542 else:
2531 else:
2543 pmf = None
2532 pmf = None
2544
2533
2545 # need all matching names in dirstate and manifest of target rev,
2534 # need all matching names in dirstate and manifest of target rev,
2546 # so have to walk both. do not print errors if files exist in one
2535 # so have to walk both. do not print errors if files exist in one
2547 # but not other.
2536 # but not other.
2548
2537
2549 names = {}
2538 names = {}
2550
2539
2551 wlock = repo.wlock()
2540 wlock = repo.wlock()
2552 try:
2541 try:
2553 # walk dirstate.
2542 # walk dirstate.
2554
2543
2555 m = cmdutil.match(repo, pats, opts)
2544 m = cmdutil.match(repo, pats, opts)
2556 m.bad = lambda x,y: False
2545 m.bad = lambda x,y: False
2557 for abs in repo.walk(m):
2546 for abs in repo.walk(m):
2558 names[abs] = m.rel(abs), m.exact(abs)
2547 names[abs] = m.rel(abs), m.exact(abs)
2559
2548
2560 # walk target manifest.
2549 # walk target manifest.
2561
2550
2562 def badfn(path, msg):
2551 def badfn(path, msg):
2563 if path in names:
2552 if path in names:
2564 return
2553 return
2565 path_ = path + '/'
2554 path_ = path + '/'
2566 for f in names:
2555 for f in names:
2567 if f.startswith(path_):
2556 if f.startswith(path_):
2568 return
2557 return
2569 ui.warn("%s: %s\n" % (m.rel(path), msg))
2558 ui.warn("%s: %s\n" % (m.rel(path), msg))
2570
2559
2571 m = cmdutil.match(repo, pats, opts)
2560 m = cmdutil.match(repo, pats, opts)
2572 m.bad = badfn
2561 m.bad = badfn
2573 for abs in repo[node].walk(m):
2562 for abs in repo[node].walk(m):
2574 if abs not in names:
2563 if abs not in names:
2575 names[abs] = m.rel(abs), m.exact(abs)
2564 names[abs] = m.rel(abs), m.exact(abs)
2576
2565
2577 m = cmdutil.matchfiles(repo, names)
2566 m = cmdutil.matchfiles(repo, names)
2578 changes = repo.status(match=m)[:4]
2567 changes = repo.status(match=m)[:4]
2579 modified, added, removed, deleted = map(set, changes)
2568 modified, added, removed, deleted = map(set, changes)
2580
2569
2581 # if f is a rename, also revert the source
2570 # if f is a rename, also revert the source
2582 cwd = repo.getcwd()
2571 cwd = repo.getcwd()
2583 for f in added:
2572 for f in added:
2584 src = repo.dirstate.copied(f)
2573 src = repo.dirstate.copied(f)
2585 if src and src not in names and repo.dirstate[src] == 'r':
2574 if src and src not in names and repo.dirstate[src] == 'r':
2586 removed.add(src)
2575 removed.add(src)
2587 names[src] = (repo.pathto(src, cwd), True)
2576 names[src] = (repo.pathto(src, cwd), True)
2588
2577
2589 def removeforget(abs):
2578 def removeforget(abs):
2590 if repo.dirstate[abs] == 'a':
2579 if repo.dirstate[abs] == 'a':
2591 return _('forgetting %s\n')
2580 return _('forgetting %s\n')
2592 return _('removing %s\n')
2581 return _('removing %s\n')
2593
2582
2594 revert = ([], _('reverting %s\n'))
2583 revert = ([], _('reverting %s\n'))
2595 add = ([], _('adding %s\n'))
2584 add = ([], _('adding %s\n'))
2596 remove = ([], removeforget)
2585 remove = ([], removeforget)
2597 undelete = ([], _('undeleting %s\n'))
2586 undelete = ([], _('undeleting %s\n'))
2598
2587
2599 disptable = (
2588 disptable = (
2600 # dispatch table:
2589 # dispatch table:
2601 # file state
2590 # file state
2602 # action if in target manifest
2591 # action if in target manifest
2603 # action if not in target manifest
2592 # action if not in target manifest
2604 # make backup if in target manifest
2593 # make backup if in target manifest
2605 # make backup if not in target manifest
2594 # make backup if not in target manifest
2606 (modified, revert, remove, True, True),
2595 (modified, revert, remove, True, True),
2607 (added, revert, remove, True, False),
2596 (added, revert, remove, True, False),
2608 (removed, undelete, None, False, False),
2597 (removed, undelete, None, False, False),
2609 (deleted, revert, remove, False, False),
2598 (deleted, revert, remove, False, False),
2610 )
2599 )
2611
2600
2612 for abs, (rel, exact) in sorted(names.items()):
2601 for abs, (rel, exact) in sorted(names.items()):
2613 mfentry = mf.get(abs)
2602 mfentry = mf.get(abs)
2614 target = repo.wjoin(abs)
2603 target = repo.wjoin(abs)
2615 def handle(xlist, dobackup):
2604 def handle(xlist, dobackup):
2616 xlist[0].append(abs)
2605 xlist[0].append(abs)
2617 if dobackup and not opts.get('no_backup') and util.lexists(target):
2606 if dobackup and not opts.get('no_backup') and util.lexists(target):
2618 bakname = "%s.orig" % rel
2607 bakname = "%s.orig" % rel
2619 ui.note(_('saving current version of %s as %s\n') %
2608 ui.note(_('saving current version of %s as %s\n') %
2620 (rel, bakname))
2609 (rel, bakname))
2621 if not opts.get('dry_run'):
2610 if not opts.get('dry_run'):
2622 util.copyfile(target, bakname)
2611 util.copyfile(target, bakname)
2623 if ui.verbose or not exact:
2612 if ui.verbose or not exact:
2624 msg = xlist[1]
2613 msg = xlist[1]
2625 if not isinstance(msg, basestring):
2614 if not isinstance(msg, basestring):
2626 msg = msg(abs)
2615 msg = msg(abs)
2627 ui.status(msg % rel)
2616 ui.status(msg % rel)
2628 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2617 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2629 if abs not in table: continue
2618 if abs not in table: continue
2630 # file has changed in dirstate
2619 # file has changed in dirstate
2631 if mfentry:
2620 if mfentry:
2632 handle(hitlist, backuphit)
2621 handle(hitlist, backuphit)
2633 elif misslist is not None:
2622 elif misslist is not None:
2634 handle(misslist, backupmiss)
2623 handle(misslist, backupmiss)
2635 break
2624 break
2636 else:
2625 else:
2637 if abs not in repo.dirstate:
2626 if abs not in repo.dirstate:
2638 if mfentry:
2627 if mfentry:
2639 handle(add, True)
2628 handle(add, True)
2640 elif exact:
2629 elif exact:
2641 ui.warn(_('file not managed: %s\n') % rel)
2630 ui.warn(_('file not managed: %s\n') % rel)
2642 continue
2631 continue
2643 # file has not changed in dirstate
2632 # file has not changed in dirstate
2644 if node == parent:
2633 if node == parent:
2645 if exact: ui.warn(_('no changes needed to %s\n') % rel)
2634 if exact: ui.warn(_('no changes needed to %s\n') % rel)
2646 continue
2635 continue
2647 if pmf is None:
2636 if pmf is None:
2648 # only need parent manifest in this unlikely case,
2637 # only need parent manifest in this unlikely case,
2649 # so do not read by default
2638 # so do not read by default
2650 pmf = repo[parent].manifest()
2639 pmf = repo[parent].manifest()
2651 if abs in pmf:
2640 if abs in pmf:
2652 if mfentry:
2641 if mfentry:
2653 # if version of file is same in parent and target
2642 # if version of file is same in parent and target
2654 # manifests, do nothing
2643 # manifests, do nothing
2655 if (pmf[abs] != mfentry or
2644 if (pmf[abs] != mfentry or
2656 pmf.flags(abs) != mf.flags(abs)):
2645 pmf.flags(abs) != mf.flags(abs)):
2657 handle(revert, False)
2646 handle(revert, False)
2658 else:
2647 else:
2659 handle(remove, False)
2648 handle(remove, False)
2660
2649
2661 if not opts.get('dry_run'):
2650 if not opts.get('dry_run'):
2662 def checkout(f):
2651 def checkout(f):
2663 fc = ctx[f]
2652 fc = ctx[f]
2664 repo.wwrite(f, fc.data(), fc.flags())
2653 repo.wwrite(f, fc.data(), fc.flags())
2665
2654
2666 audit_path = util.path_auditor(repo.root)
2655 audit_path = util.path_auditor(repo.root)
2667 for f in remove[0]:
2656 for f in remove[0]:
2668 if repo.dirstate[f] == 'a':
2657 if repo.dirstate[f] == 'a':
2669 repo.dirstate.forget(f)
2658 repo.dirstate.forget(f)
2670 continue
2659 continue
2671 audit_path(f)
2660 audit_path(f)
2672 try:
2661 try:
2673 util.unlink(repo.wjoin(f))
2662 util.unlink(repo.wjoin(f))
2674 except OSError:
2663 except OSError:
2675 pass
2664 pass
2676 repo.dirstate.remove(f)
2665 repo.dirstate.remove(f)
2677
2666
2678 normal = None
2667 normal = None
2679 if node == parent:
2668 if node == parent:
2680 # We're reverting to our parent. If possible, we'd like status
2669 # We're reverting to our parent. If possible, we'd like status
2681 # to report the file as clean. We have to use normallookup for
2670 # to report the file as clean. We have to use normallookup for
2682 # merges to avoid losing information about merged/dirty files.
2671 # merges to avoid losing information about merged/dirty files.
2683 if p2 != nullid:
2672 if p2 != nullid:
2684 normal = repo.dirstate.normallookup
2673 normal = repo.dirstate.normallookup
2685 else:
2674 else:
2686 normal = repo.dirstate.normal
2675 normal = repo.dirstate.normal
2687 for f in revert[0]:
2676 for f in revert[0]:
2688 checkout(f)
2677 checkout(f)
2689 if normal:
2678 if normal:
2690 normal(f)
2679 normal(f)
2691
2680
2692 for f in add[0]:
2681 for f in add[0]:
2693 checkout(f)
2682 checkout(f)
2694 repo.dirstate.add(f)
2683 repo.dirstate.add(f)
2695
2684
2696 normal = repo.dirstate.normallookup
2685 normal = repo.dirstate.normallookup
2697 if node == parent and p2 == nullid:
2686 if node == parent and p2 == nullid:
2698 normal = repo.dirstate.normal
2687 normal = repo.dirstate.normal
2699 for f in undelete[0]:
2688 for f in undelete[0]:
2700 checkout(f)
2689 checkout(f)
2701 normal(f)
2690 normal(f)
2702
2691
2703 finally:
2692 finally:
2704 wlock.release()
2693 wlock.release()
2705
2694
2706 def rollback(ui, repo):
2695 def rollback(ui, repo):
2707 """roll back the last transaction
2696 """roll back the last transaction
2708
2697
2709 This command should be used with care. There is only one level of
2698 This command should be used with care. There is only one level of
2710 rollback, and there is no way to undo a rollback. It will also
2699 rollback, and there is no way to undo a rollback. It will also
2711 restore the dirstate at the time of the last transaction, losing
2700 restore the dirstate at the time of the last transaction, losing
2712 any dirstate changes since that time. This command does not alter
2701 any dirstate changes since that time. This command does not alter
2713 the working directory.
2702 the working directory.
2714
2703
2715 Transactions are used to encapsulate the effects of all commands
2704 Transactions are used to encapsulate the effects of all commands
2716 that create new changesets or propagate existing changesets into a
2705 that create new changesets or propagate existing changesets into a
2717 repository. For example, the following commands are transactional,
2706 repository. For example, the following commands are transactional,
2718 and their effects can be rolled back:
2707 and their effects can be rolled back:
2719
2708
2720 commit
2709 commit
2721 import
2710 import
2722 pull
2711 pull
2723 push (with this repository as destination)
2712 push (with this repository as destination)
2724 unbundle
2713 unbundle
2725
2714
2726 This command is not intended for use on public repositories. Once
2715 This command is not intended for use on public repositories. Once
2727 changes are visible for pull by other users, rolling a transaction
2716 changes are visible for pull by other users, rolling a transaction
2728 back locally is ineffective (someone else may already have pulled
2717 back locally is ineffective (someone else may already have pulled
2729 the changes). Furthermore, a race is possible with readers of the
2718 the changes). Furthermore, a race is possible with readers of the
2730 repository; for example an in-progress pull from the repository
2719 repository; for example an in-progress pull from the repository
2731 may fail if a rollback is performed.
2720 may fail if a rollback is performed.
2732 """
2721 """
2733 repo.rollback()
2722 repo.rollback()
2734
2723
2735 def root(ui, repo):
2724 def root(ui, repo):
2736 """print the root (top) of the current working directory
2725 """print the root (top) of the current working directory
2737
2726
2738 Print the root directory of the current repository.
2727 Print the root directory of the current repository.
2739 """
2728 """
2740 ui.write(repo.root + "\n")
2729 ui.write(repo.root + "\n")
2741
2730
2742 def serve(ui, repo, **opts):
2731 def serve(ui, repo, **opts):
2743 """export the repository via HTTP
2732 """export the repository via HTTP
2744
2733
2745 Start a local HTTP repository browser and pull server.
2734 Start a local HTTP repository browser and pull server.
2746
2735
2747 By default, the server logs accesses to stdout and errors to
2736 By default, the server logs accesses to stdout and errors to
2748 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
2737 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
2749 files.
2738 files.
2750 """
2739 """
2751
2740
2752 if opts["stdio"]:
2741 if opts["stdio"]:
2753 if repo is None:
2742 if repo is None:
2754 raise error.RepoError(_("There is no Mercurial repository here"
2743 raise error.RepoError(_("There is no Mercurial repository here"
2755 " (.hg not found)"))
2744 " (.hg not found)"))
2756 s = sshserver.sshserver(ui, repo)
2745 s = sshserver.sshserver(ui, repo)
2757 s.serve_forever()
2746 s.serve_forever()
2758
2747
2759 baseui = repo and repo.baseui or ui
2748 baseui = repo and repo.baseui or ui
2760 optlist = ("name templates style address port prefix ipv6"
2749 optlist = ("name templates style address port prefix ipv6"
2761 " accesslog errorlog webdir_conf certificate encoding")
2750 " accesslog errorlog webdir_conf certificate encoding")
2762 for o in optlist.split():
2751 for o in optlist.split():
2763 if opts.get(o, None):
2752 if opts.get(o, None):
2764 baseui.setconfig("web", o, str(opts[o]))
2753 baseui.setconfig("web", o, str(opts[o]))
2765 if (repo is not None) and (repo.ui != baseui):
2754 if (repo is not None) and (repo.ui != baseui):
2766 repo.ui.setconfig("web", o, str(opts[o]))
2755 repo.ui.setconfig("web", o, str(opts[o]))
2767
2756
2768 if repo is None and not ui.config("web", "webdir_conf"):
2757 if repo is None and not ui.config("web", "webdir_conf"):
2769 raise error.RepoError(_("There is no Mercurial repository here"
2758 raise error.RepoError(_("There is no Mercurial repository here"
2770 " (.hg not found)"))
2759 " (.hg not found)"))
2771
2760
2772 class service(object):
2761 class service(object):
2773 def init(self):
2762 def init(self):
2774 util.set_signal_handler()
2763 util.set_signal_handler()
2775 self.httpd = server.create_server(baseui, repo)
2764 self.httpd = server.create_server(baseui, repo)
2776
2765
2777 if not ui.verbose: return
2766 if not ui.verbose: return
2778
2767
2779 if self.httpd.prefix:
2768 if self.httpd.prefix:
2780 prefix = self.httpd.prefix.strip('/') + '/'
2769 prefix = self.httpd.prefix.strip('/') + '/'
2781 else:
2770 else:
2782 prefix = ''
2771 prefix = ''
2783
2772
2784 port = ':%d' % self.httpd.port
2773 port = ':%d' % self.httpd.port
2785 if port == ':80':
2774 if port == ':80':
2786 port = ''
2775 port = ''
2787
2776
2788 bindaddr = self.httpd.addr
2777 bindaddr = self.httpd.addr
2789 if bindaddr == '0.0.0.0':
2778 if bindaddr == '0.0.0.0':
2790 bindaddr = '*'
2779 bindaddr = '*'
2791 elif ':' in bindaddr: # IPv6
2780 elif ':' in bindaddr: # IPv6
2792 bindaddr = '[%s]' % bindaddr
2781 bindaddr = '[%s]' % bindaddr
2793
2782
2794 fqaddr = self.httpd.fqaddr
2783 fqaddr = self.httpd.fqaddr
2795 if ':' in fqaddr:
2784 if ':' in fqaddr:
2796 fqaddr = '[%s]' % fqaddr
2785 fqaddr = '[%s]' % fqaddr
2797 ui.status(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
2786 ui.status(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
2798 (fqaddr, port, prefix, bindaddr, self.httpd.port))
2787 (fqaddr, port, prefix, bindaddr, self.httpd.port))
2799
2788
2800 def run(self):
2789 def run(self):
2801 self.httpd.serve_forever()
2790 self.httpd.serve_forever()
2802
2791
2803 service = service()
2792 service = service()
2804
2793
2805 cmdutil.service(opts, initfn=service.init, runfn=service.run)
2794 cmdutil.service(opts, initfn=service.init, runfn=service.run)
2806
2795
2807 def status(ui, repo, *pats, **opts):
2796 def status(ui, repo, *pats, **opts):
2808 """show changed files in the working directory
2797 """show changed files in the working directory
2809
2798
2810 Show status of files in the repository. If names are given, only
2799 Show status of files in the repository. If names are given, only
2811 files that match are shown. Files that are clean or ignored or
2800 files that match are shown. Files that are clean or ignored or
2812 the source of a copy/move operation, are not listed unless
2801 the source of a copy/move operation, are not listed unless
2813 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
2802 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
2814 Unless options described with "show only ..." are given, the
2803 Unless options described with "show only ..." are given, the
2815 options -mardu are used.
2804 options -mardu are used.
2816
2805
2817 Option -q/--quiet hides untracked (unknown and ignored) files
2806 Option -q/--quiet hides untracked (unknown and ignored) files
2818 unless explicitly requested with -u/--unknown or -i/--ignored.
2807 unless explicitly requested with -u/--unknown or -i/--ignored.
2819
2808
2820 NOTE: status may appear to disagree with diff if permissions have
2809 NOTE: status may appear to disagree with diff if permissions have
2821 changed or a merge has occurred. The standard diff format does not
2810 changed or a merge has occurred. The standard diff format does not
2822 report permission changes and diff only reports changes relative
2811 report permission changes and diff only reports changes relative
2823 to one merge parent.
2812 to one merge parent.
2824
2813
2825 If one revision is given, it is used as the base revision.
2814 If one revision is given, it is used as the base revision.
2826 If two revisions are given, the differences between them are
2815 If two revisions are given, the differences between them are
2827 shown.
2816 shown.
2828
2817
2829 The codes used to show the status of files are:
2818 The codes used to show the status of files are:
2830 M = modified
2819 M = modified
2831 A = added
2820 A = added
2832 R = removed
2821 R = removed
2833 C = clean
2822 C = clean
2834 ! = missing (deleted by non-hg command, but still tracked)
2823 ! = missing (deleted by non-hg command, but still tracked)
2835 ? = not tracked
2824 ? = not tracked
2836 I = ignored
2825 I = ignored
2837 = origin of the previous file listed as A (added)
2826 = origin of the previous file listed as A (added)
2838 """
2827 """
2839
2828
2840 node1, node2 = cmdutil.revpair(repo, opts.get('rev'))
2829 node1, node2 = cmdutil.revpair(repo, opts.get('rev'))
2841 cwd = (pats and repo.getcwd()) or ''
2830 cwd = (pats and repo.getcwd()) or ''
2842 end = opts.get('print0') and '\0' or '\n'
2831 end = opts.get('print0') and '\0' or '\n'
2843 copy = {}
2832 copy = {}
2844 states = 'modified added removed deleted unknown ignored clean'.split()
2833 states = 'modified added removed deleted unknown ignored clean'.split()
2845 show = [k for k in states if opts.get(k)]
2834 show = [k for k in states if opts.get(k)]
2846 if opts.get('all'):
2835 if opts.get('all'):
2847 show += ui.quiet and (states[:4] + ['clean']) or states
2836 show += ui.quiet and (states[:4] + ['clean']) or states
2848 if not show:
2837 if not show:
2849 show = ui.quiet and states[:4] or states[:5]
2838 show = ui.quiet and states[:4] or states[:5]
2850
2839
2851 stat = repo.status(node1, node2, cmdutil.match(repo, pats, opts),
2840 stat = repo.status(node1, node2, cmdutil.match(repo, pats, opts),
2852 'ignored' in show, 'clean' in show, 'unknown' in show)
2841 'ignored' in show, 'clean' in show, 'unknown' in show)
2853 changestates = zip(states, 'MAR!?IC', stat)
2842 changestates = zip(states, 'MAR!?IC', stat)
2854
2843
2855 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
2844 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
2856 ctxn = repo[nullid]
2845 ctxn = repo[nullid]
2857 ctx1 = repo[node1]
2846 ctx1 = repo[node1]
2858 ctx2 = repo[node2]
2847 ctx2 = repo[node2]
2859 added = stat[1]
2848 added = stat[1]
2860 if node2 is None:
2849 if node2 is None:
2861 added = stat[0] + stat[1] # merged?
2850 added = stat[0] + stat[1] # merged?
2862
2851
2863 for k, v in copies.copies(repo, ctx1, ctx2, ctxn)[0].iteritems():
2852 for k, v in copies.copies(repo, ctx1, ctx2, ctxn)[0].iteritems():
2864 if k in added:
2853 if k in added:
2865 copy[k] = v
2854 copy[k] = v
2866 elif v in added:
2855 elif v in added:
2867 copy[v] = k
2856 copy[v] = k
2868
2857
2869 for state, char, files in changestates:
2858 for state, char, files in changestates:
2870 if state in show:
2859 if state in show:
2871 format = "%s %%s%s" % (char, end)
2860 format = "%s %%s%s" % (char, end)
2872 if opts.get('no_status'):
2861 if opts.get('no_status'):
2873 format = "%%s%s" % end
2862 format = "%%s%s" % end
2874
2863
2875 for f in files:
2864 for f in files:
2876 ui.write(format % repo.pathto(f, cwd))
2865 ui.write(format % repo.pathto(f, cwd))
2877 if f in copy:
2866 if f in copy:
2878 ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end))
2867 ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end))
2879
2868
2880 def tag(ui, repo, name1, *names, **opts):
2869 def tag(ui, repo, name1, *names, **opts):
2881 """add one or more tags for the current or given revision
2870 """add one or more tags for the current or given revision
2882
2871
2883 Name a particular revision using <name>.
2872 Name a particular revision using <name>.
2884
2873
2885 Tags are used to name particular revisions of the repository and are
2874 Tags are used to name particular revisions of the repository and are
2886 very useful to compare different revisions, to go back to significant
2875 very useful to compare different revisions, to go back to significant
2887 earlier versions or to mark branch points as releases, etc.
2876 earlier versions or to mark branch points as releases, etc.
2888
2877
2889 If no revision is given, the parent of the working directory is
2878 If no revision is given, the parent of the working directory is
2890 used, or tip if no revision is checked out.
2879 used, or tip if no revision is checked out.
2891
2880
2892 To facilitate version control, distribution, and merging of tags,
2881 To facilitate version control, distribution, and merging of tags,
2893 they are stored as a file named ".hgtags" which is managed
2882 they are stored as a file named ".hgtags" which is managed
2894 similarly to other project files and can be hand-edited if
2883 similarly to other project files and can be hand-edited if
2895 necessary. The file '.hg/localtags' is used for local tags (not
2884 necessary. The file '.hg/localtags' is used for local tags (not
2896 shared among repositories).
2885 shared among repositories).
2897
2886
2898 See 'hg help dates' for a list of formats valid for -d/--date.
2887 See 'hg help dates' for a list of formats valid for -d/--date.
2899 """
2888 """
2900
2889
2901 rev_ = "."
2890 rev_ = "."
2902 names = (name1,) + names
2891 names = (name1,) + names
2903 if len(names) != len(set(names)):
2892 if len(names) != len(set(names)):
2904 raise util.Abort(_('tag names must be unique'))
2893 raise util.Abort(_('tag names must be unique'))
2905 for n in names:
2894 for n in names:
2906 if n in ['tip', '.', 'null']:
2895 if n in ['tip', '.', 'null']:
2907 raise util.Abort(_('the name \'%s\' is reserved') % n)
2896 raise util.Abort(_('the name \'%s\' is reserved') % n)
2908 if opts.get('rev') and opts.get('remove'):
2897 if opts.get('rev') and opts.get('remove'):
2909 raise util.Abort(_("--rev and --remove are incompatible"))
2898 raise util.Abort(_("--rev and --remove are incompatible"))
2910 if opts.get('rev'):
2899 if opts.get('rev'):
2911 rev_ = opts['rev']
2900 rev_ = opts['rev']
2912 message = opts.get('message')
2901 message = opts.get('message')
2913 if opts.get('remove'):
2902 if opts.get('remove'):
2914 expectedtype = opts.get('local') and 'local' or 'global'
2903 expectedtype = opts.get('local') and 'local' or 'global'
2915 for n in names:
2904 for n in names:
2916 if not repo.tagtype(n):
2905 if not repo.tagtype(n):
2917 raise util.Abort(_('tag \'%s\' does not exist') % n)
2906 raise util.Abort(_('tag \'%s\' does not exist') % n)
2918 if repo.tagtype(n) != expectedtype:
2907 if repo.tagtype(n) != expectedtype:
2919 if expectedtype == 'global':
2908 if expectedtype == 'global':
2920 raise util.Abort(_('tag \'%s\' is not a global tag') % n)
2909 raise util.Abort(_('tag \'%s\' is not a global tag') % n)
2921 else:
2910 else:
2922 raise util.Abort(_('tag \'%s\' is not a local tag') % n)
2911 raise util.Abort(_('tag \'%s\' is not a local tag') % n)
2923 rev_ = nullid
2912 rev_ = nullid
2924 if not message:
2913 if not message:
2925 message = _('Removed tag %s') % ', '.join(names)
2914 message = _('Removed tag %s') % ', '.join(names)
2926 elif not opts.get('force'):
2915 elif not opts.get('force'):
2927 for n in names:
2916 for n in names:
2928 if n in repo.tags():
2917 if n in repo.tags():
2929 raise util.Abort(_('tag \'%s\' already exists '
2918 raise util.Abort(_('tag \'%s\' already exists '
2930 '(use -f to force)') % n)
2919 '(use -f to force)') % n)
2931 if not rev_ and repo.dirstate.parents()[1] != nullid:
2920 if not rev_ and repo.dirstate.parents()[1] != nullid:
2932 raise util.Abort(_('uncommitted merge - please provide a '
2921 raise util.Abort(_('uncommitted merge - please provide a '
2933 'specific revision'))
2922 'specific revision'))
2934 r = repo[rev_].node()
2923 r = repo[rev_].node()
2935
2924
2936 if not message:
2925 if not message:
2937 message = (_('Added tag %s for changeset %s') %
2926 message = (_('Added tag %s for changeset %s') %
2938 (', '.join(names), short(r)))
2927 (', '.join(names), short(r)))
2939
2928
2940 date = opts.get('date')
2929 date = opts.get('date')
2941 if date:
2930 if date:
2942 date = util.parsedate(date)
2931 date = util.parsedate(date)
2943
2932
2944 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date)
2933 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date)
2945
2934
2946 def tags(ui, repo):
2935 def tags(ui, repo):
2947 """list repository tags
2936 """list repository tags
2948
2937
2949 This lists both regular and local tags. When the -v/--verbose
2938 This lists both regular and local tags. When the -v/--verbose
2950 switch is used, a third column "local" is printed for local tags.
2939 switch is used, a third column "local" is printed for local tags.
2951 """
2940 """
2952
2941
2953 hexfunc = ui.debugflag and hex or short
2942 hexfunc = ui.debugflag and hex or short
2954 tagtype = ""
2943 tagtype = ""
2955
2944
2956 for t, n in reversed(repo.tagslist()):
2945 for t, n in reversed(repo.tagslist()):
2957 if ui.quiet:
2946 if ui.quiet:
2958 ui.write("%s\n" % t)
2947 ui.write("%s\n" % t)
2959 continue
2948 continue
2960
2949
2961 try:
2950 try:
2962 hn = hexfunc(n)
2951 hn = hexfunc(n)
2963 r = "%5d:%s" % (repo.changelog.rev(n), hn)
2952 r = "%5d:%s" % (repo.changelog.rev(n), hn)
2964 except error.LookupError:
2953 except error.LookupError:
2965 r = " ?:%s" % hn
2954 r = " ?:%s" % hn
2966 else:
2955 else:
2967 spaces = " " * (30 - encoding.colwidth(t))
2956 spaces = " " * (30 - encoding.colwidth(t))
2968 if ui.verbose:
2957 if ui.verbose:
2969 if repo.tagtype(t) == 'local':
2958 if repo.tagtype(t) == 'local':
2970 tagtype = " local"
2959 tagtype = " local"
2971 else:
2960 else:
2972 tagtype = ""
2961 tagtype = ""
2973 ui.write("%s%s %s%s\n" % (t, spaces, r, tagtype))
2962 ui.write("%s%s %s%s\n" % (t, spaces, r, tagtype))
2974
2963
2975 def tip(ui, repo, **opts):
2964 def tip(ui, repo, **opts):
2976 """show the tip revision
2965 """show the tip revision
2977
2966
2978 The tip revision (usually just called the tip) is the changeset
2967 The tip revision (usually just called the tip) is the changeset
2979 most recently added to the repository (and therefore the most
2968 most recently added to the repository (and therefore the most
2980 recently changed head).
2969 recently changed head).
2981
2970
2982 If you have just made a commit, that commit will be the tip. If
2971 If you have just made a commit, that commit will be the tip. If
2983 you have just pulled changes from another repository, the tip of
2972 you have just pulled changes from another repository, the tip of
2984 that repository becomes the current tip. The "tip" tag is special
2973 that repository becomes the current tip. The "tip" tag is special
2985 and cannot be renamed or assigned to a different changeset.
2974 and cannot be renamed or assigned to a different changeset.
2986 """
2975 """
2987 cmdutil.show_changeset(ui, repo, opts).show(repo[len(repo) - 1])
2976 cmdutil.show_changeset(ui, repo, opts).show(repo[len(repo) - 1])
2988
2977
2989 def unbundle(ui, repo, fname1, *fnames, **opts):
2978 def unbundle(ui, repo, fname1, *fnames, **opts):
2990 """apply one or more changegroup files
2979 """apply one or more changegroup files
2991
2980
2992 Apply one or more compressed changegroup files generated by the
2981 Apply one or more compressed changegroup files generated by the
2993 bundle command.
2982 bundle command.
2994 """
2983 """
2995 fnames = (fname1,) + fnames
2984 fnames = (fname1,) + fnames
2996
2985
2997 lock = repo.lock()
2986 lock = repo.lock()
2998 try:
2987 try:
2999 for fname in fnames:
2988 for fname in fnames:
3000 f = url.open(ui, fname)
2989 f = url.open(ui, fname)
3001 gen = changegroup.readbundle(f, fname)
2990 gen = changegroup.readbundle(f, fname)
3002 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
2991 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
3003 finally:
2992 finally:
3004 lock.release()
2993 lock.release()
3005
2994
3006 return postincoming(ui, repo, modheads, opts.get('update'), None)
2995 return postincoming(ui, repo, modheads, opts.get('update'), None)
3007
2996
3008 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False):
2997 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False):
3009 """update working directory
2998 """update working directory
3010
2999
3011 Update the repository's working directory to the specified
3000 Update the repository's working directory to the specified
3012 revision, or the tip of the current branch if none is specified.
3001 revision, or the tip of the current branch if none is specified.
3013 Use null as the revision to remove the working copy (like 'hg
3002 Use null as the revision to remove the working copy (like 'hg
3014 clone -U').
3003 clone -U').
3015
3004
3016 When the working directory contains no uncommitted changes, it
3005 When the working directory contains no uncommitted changes, it
3017 will be replaced by the state of the requested revision from the
3006 will be replaced by the state of the requested revision from the
3018 repository. When the requested revision is on a different branch,
3007 repository. When the requested revision is on a different branch,
3019 the working directory will additionally be switched to that
3008 the working directory will additionally be switched to that
3020 branch.
3009 branch.
3021
3010
3022 When there are uncommitted changes, use option -C/--clean to
3011 When there are uncommitted changes, use option -C/--clean to
3023 discard them, forcibly replacing the state of the working
3012 discard them, forcibly replacing the state of the working
3024 directory with the requested revision. Alternately, use -c/--check
3013 directory with the requested revision. Alternately, use -c/--check
3025 to abort.
3014 to abort.
3026
3015
3027 When there are uncommitted changes and option -C/--clean is not
3016 When there are uncommitted changes and option -C/--clean is not
3028 used, and the parent revision and requested revision are on the
3017 used, and the parent revision and requested revision are on the
3029 same branch, and one of them is an ancestor of the other, then the
3018 same branch, and one of them is an ancestor of the other, then the
3030 new working directory will contain the requested revision merged
3019 new working directory will contain the requested revision merged
3031 with the uncommitted changes. Otherwise, the update will fail with
3020 with the uncommitted changes. Otherwise, the update will fail with
3032 a suggestion to use 'merge' or 'update -C' instead.
3021 a suggestion to use 'merge' or 'update -C' instead.
3033
3022
3034 If you want to update just one file to an older revision, use
3023 If you want to update just one file to an older revision, use
3035 revert.
3024 revert.
3036
3025
3037 See 'hg help dates' for a list of formats valid for -d/--date.
3026 See 'hg help dates' for a list of formats valid for -d/--date.
3038 """
3027 """
3039 if rev and node:
3028 if rev and node:
3040 raise util.Abort(_("please specify just one revision"))
3029 raise util.Abort(_("please specify just one revision"))
3041
3030
3042 if not rev:
3031 if not rev:
3043 rev = node
3032 rev = node
3044
3033
3045 if not clean and check:
3034 if not clean and check:
3046 # we could use dirty() but we can ignore merge and branch trivia
3035 # we could use dirty() but we can ignore merge and branch trivia
3047 c = repo[None]
3036 c = repo[None]
3048 if c.modified() or c.added() or c.removed():
3037 if c.modified() or c.added() or c.removed():
3049 raise util.Abort(_("uncommitted local changes"))
3038 raise util.Abort(_("uncommitted local changes"))
3050
3039
3051 if date:
3040 if date:
3052 if rev:
3041 if rev:
3053 raise util.Abort(_("you can't specify a revision and a date"))
3042 raise util.Abort(_("you can't specify a revision and a date"))
3054 rev = cmdutil.finddate(ui, repo, date)
3043 rev = cmdutil.finddate(ui, repo, date)
3055
3044
3056 if clean:
3045 if clean:
3057 return hg.clean(repo, rev)
3046 return hg.clean(repo, rev)
3058 else:
3047 else:
3059 return hg.update(repo, rev)
3048 return hg.update(repo, rev)
3060
3049
3061 def verify(ui, repo):
3050 def verify(ui, repo):
3062 """verify the integrity of the repository
3051 """verify the integrity of the repository
3063
3052
3064 Verify the integrity of the current repository.
3053 Verify the integrity of the current repository.
3065
3054
3066 This will perform an extensive check of the repository's
3055 This will perform an extensive check of the repository's
3067 integrity, validating the hashes and checksums of each entry in
3056 integrity, validating the hashes and checksums of each entry in
3068 the changelog, manifest, and tracked files, as well as the
3057 the changelog, manifest, and tracked files, as well as the
3069 integrity of their crosslinks and indices.
3058 integrity of their crosslinks and indices.
3070 """
3059 """
3071 return hg.verify(repo)
3060 return hg.verify(repo)
3072
3061
3073 def version_(ui):
3062 def version_(ui):
3074 """output version and copyright information"""
3063 """output version and copyright information"""
3075 ui.write(_("Mercurial Distributed SCM (version %s)\n")
3064 ui.write(_("Mercurial Distributed SCM (version %s)\n")
3076 % util.version())
3065 % util.version())
3077 ui.status(_(
3066 ui.status(_(
3078 "\nCopyright (C) 2005-2009 Matt Mackall <mpm@selenic.com> and others\n"
3067 "\nCopyright (C) 2005-2009 Matt Mackall <mpm@selenic.com> and others\n"
3079 "This is free software; see the source for copying conditions. "
3068 "This is free software; see the source for copying conditions. "
3080 "There is NO\nwarranty; "
3069 "There is NO\nwarranty; "
3081 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
3070 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
3082 ))
3071 ))
3083
3072
3084 # Command options and aliases are listed here, alphabetically
3073 # Command options and aliases are listed here, alphabetically
3085
3074
3086 globalopts = [
3075 globalopts = [
3087 ('R', 'repository', '',
3076 ('R', 'repository', '',
3088 _('repository root directory or symbolic path name')),
3077 _('repository root directory or symbolic path name')),
3089 ('', 'cwd', '', _('change working directory')),
3078 ('', 'cwd', '', _('change working directory')),
3090 ('y', 'noninteractive', None,
3079 ('y', 'noninteractive', None,
3091 _('do not prompt, assume \'yes\' for any required answers')),
3080 _('do not prompt, assume \'yes\' for any required answers')),
3092 ('q', 'quiet', None, _('suppress output')),
3081 ('q', 'quiet', None, _('suppress output')),
3093 ('v', 'verbose', None, _('enable additional output')),
3082 ('v', 'verbose', None, _('enable additional output')),
3094 ('', 'config', [], _('set/override config option')),
3083 ('', 'config', [], _('set/override config option')),
3095 ('', 'debug', None, _('enable debugging output')),
3084 ('', 'debug', None, _('enable debugging output')),
3096 ('', 'debugger', None, _('start debugger')),
3085 ('', 'debugger', None, _('start debugger')),
3097 ('', 'encoding', encoding.encoding, _('set the charset encoding')),
3086 ('', 'encoding', encoding.encoding, _('set the charset encoding')),
3098 ('', 'encodingmode', encoding.encodingmode,
3087 ('', 'encodingmode', encoding.encodingmode,
3099 _('set the charset encoding mode')),
3088 _('set the charset encoding mode')),
3100 ('', 'traceback', None, _('print traceback on exception')),
3089 ('', 'traceback', None, _('print traceback on exception')),
3101 ('', 'time', None, _('time how long the command takes')),
3090 ('', 'time', None, _('time how long the command takes')),
3102 ('', 'profile', None, _('print command execution profile')),
3091 ('', 'profile', None, _('print command execution profile')),
3103 ('', 'version', None, _('output version information and exit')),
3092 ('', 'version', None, _('output version information and exit')),
3104 ('h', 'help', None, _('display help and exit')),
3093 ('h', 'help', None, _('display help and exit')),
3105 ]
3094 ]
3106
3095
3107 dryrunopts = [('n', 'dry-run', None,
3096 dryrunopts = [('n', 'dry-run', None,
3108 _('do not perform actions, just print output'))]
3097 _('do not perform actions, just print output'))]
3109
3098
3110 remoteopts = [
3099 remoteopts = [
3111 ('e', 'ssh', '', _('specify ssh command to use')),
3100 ('e', 'ssh', '', _('specify ssh command to use')),
3112 ('', 'remotecmd', '', _('specify hg command to run on the remote side')),
3101 ('', 'remotecmd', '', _('specify hg command to run on the remote side')),
3113 ]
3102 ]
3114
3103
3115 walkopts = [
3104 walkopts = [
3116 ('I', 'include', [], _('include names matching the given patterns')),
3105 ('I', 'include', [], _('include names matching the given patterns')),
3117 ('X', 'exclude', [], _('exclude names matching the given patterns')),
3106 ('X', 'exclude', [], _('exclude names matching the given patterns')),
3118 ]
3107 ]
3119
3108
3120 commitopts = [
3109 commitopts = [
3121 ('m', 'message', '', _('use <text> as commit message')),
3110 ('m', 'message', '', _('use <text> as commit message')),
3122 ('l', 'logfile', '', _('read commit message from <file>')),
3111 ('l', 'logfile', '', _('read commit message from <file>')),
3123 ]
3112 ]
3124
3113
3125 commitopts2 = [
3114 commitopts2 = [
3126 ('d', 'date', '', _('record datecode as commit date')),
3115 ('d', 'date', '', _('record datecode as commit date')),
3127 ('u', 'user', '', _('record the specified user as committer')),
3116 ('u', 'user', '', _('record the specified user as committer')),
3128 ]
3117 ]
3129
3118
3130 templateopts = [
3119 templateopts = [
3131 ('', 'style', '', _('display using template map file')),
3120 ('', 'style', '', _('display using template map file')),
3132 ('', 'template', '', _('display with template')),
3121 ('', 'template', '', _('display with template')),
3133 ]
3122 ]
3134
3123
3135 logopts = [
3124 logopts = [
3136 ('p', 'patch', None, _('show patch')),
3125 ('p', 'patch', None, _('show patch')),
3137 ('g', 'git', None, _('use git extended diff format')),
3126 ('g', 'git', None, _('use git extended diff format')),
3138 ('l', 'limit', '', _('limit number of changes displayed')),
3127 ('l', 'limit', '', _('limit number of changes displayed')),
3139 ('M', 'no-merges', None, _('do not show merges')),
3128 ('M', 'no-merges', None, _('do not show merges')),
3140 ] + templateopts
3129 ] + templateopts
3141
3130
3142 diffopts = [
3131 diffopts = [
3143 ('a', 'text', None, _('treat all files as text')),
3132 ('a', 'text', None, _('treat all files as text')),
3144 ('g', 'git', None, _('use git extended diff format')),
3133 ('g', 'git', None, _('use git extended diff format')),
3145 ('', 'nodates', None, _("don't include dates in diff headers"))
3134 ('', 'nodates', None, _("don't include dates in diff headers"))
3146 ]
3135 ]
3147
3136
3148 diffopts2 = [
3137 diffopts2 = [
3149 ('p', 'show-function', None, _('show which function each change is in')),
3138 ('p', 'show-function', None, _('show which function each change is in')),
3150 ('w', 'ignore-all-space', None,
3139 ('w', 'ignore-all-space', None,
3151 _('ignore white space when comparing lines')),
3140 _('ignore white space when comparing lines')),
3152 ('b', 'ignore-space-change', None,
3141 ('b', 'ignore-space-change', None,
3153 _('ignore changes in the amount of white space')),
3142 _('ignore changes in the amount of white space')),
3154 ('B', 'ignore-blank-lines', None,
3143 ('B', 'ignore-blank-lines', None,
3155 _('ignore changes whose lines are all blank')),
3144 _('ignore changes whose lines are all blank')),
3156 ('U', 'unified', '', _('number of lines of context to show'))
3145 ('U', 'unified', '', _('number of lines of context to show'))
3157 ]
3146 ]
3158
3147
3159 similarityopts = [
3148 similarityopts = [
3160 ('s', 'similarity', '',
3149 ('s', 'similarity', '',
3161 _('guess renamed files by similarity (0<=s<=100)'))
3150 _('guess renamed files by similarity (0<=s<=100)'))
3162 ]
3151 ]
3163
3152
3164 table = {
3153 table = {
3165 "^add": (add, walkopts + dryrunopts, _('[OPTION]... [FILE]...')),
3154 "^add": (add, walkopts + dryrunopts, _('[OPTION]... [FILE]...')),
3166 "addremove":
3155 "addremove":
3167 (addremove, similarityopts + walkopts + dryrunopts,
3156 (addremove, similarityopts + walkopts + dryrunopts,
3168 _('[OPTION]... [FILE]...')),
3157 _('[OPTION]... [FILE]...')),
3169 "^annotate|blame":
3158 "^annotate|blame":
3170 (annotate,
3159 (annotate,
3171 [('r', 'rev', '', _('annotate the specified revision')),
3160 [('r', 'rev', '', _('annotate the specified revision')),
3172 ('f', 'follow', None, _('follow file copies and renames')),
3161 ('f', 'follow', None, _('follow file copies and renames')),
3173 ('a', 'text', None, _('treat all files as text')),
3162 ('a', 'text', None, _('treat all files as text')),
3174 ('u', 'user', None, _('list the author (long with -v)')),
3163 ('u', 'user', None, _('list the author (long with -v)')),
3175 ('d', 'date', None, _('list the date (short with -q)')),
3164 ('d', 'date', None, _('list the date (short with -q)')),
3176 ('n', 'number', None, _('list the revision number (default)')),
3165 ('n', 'number', None, _('list the revision number (default)')),
3177 ('c', 'changeset', None, _('list the changeset')),
3166 ('c', 'changeset', None, _('list the changeset')),
3178 ('l', 'line-number', None,
3167 ('l', 'line-number', None,
3179 _('show line number at the first appearance'))
3168 _('show line number at the first appearance'))
3180 ] + walkopts,
3169 ] + walkopts,
3181 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...')),
3170 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...')),
3182 "archive":
3171 "archive":
3183 (archive,
3172 (archive,
3184 [('', 'no-decode', None, _('do not pass files through decoders')),
3173 [('', 'no-decode', None, _('do not pass files through decoders')),
3185 ('p', 'prefix', '', _('directory prefix for files in archive')),
3174 ('p', 'prefix', '', _('directory prefix for files in archive')),
3186 ('r', 'rev', '', _('revision to distribute')),
3175 ('r', 'rev', '', _('revision to distribute')),
3187 ('t', 'type', '', _('type of distribution to create')),
3176 ('t', 'type', '', _('type of distribution to create')),
3188 ] + walkopts,
3177 ] + walkopts,
3189 _('[OPTION]... DEST')),
3178 _('[OPTION]... DEST')),
3190 "backout":
3179 "backout":
3191 (backout,
3180 (backout,
3192 [('', 'merge', None,
3181 [('', 'merge', None,
3193 _('merge with old dirstate parent after backout')),
3182 _('merge with old dirstate parent after backout')),
3194 ('', 'parent', '', _('parent to choose when backing out merge')),
3183 ('', 'parent', '', _('parent to choose when backing out merge')),
3195 ('r', 'rev', '', _('revision to backout')),
3184 ('r', 'rev', '', _('revision to backout')),
3196 ] + walkopts + commitopts + commitopts2,
3185 ] + walkopts + commitopts + commitopts2,
3197 _('[OPTION]... [-r] REV')),
3186 _('[OPTION]... [-r] REV')),
3198 "bisect":
3187 "bisect":
3199 (bisect,
3188 (bisect,
3200 [('r', 'reset', False, _('reset bisect state')),
3189 [('r', 'reset', False, _('reset bisect state')),
3201 ('g', 'good', False, _('mark changeset good')),
3190 ('g', 'good', False, _('mark changeset good')),
3202 ('b', 'bad', False, _('mark changeset bad')),
3191 ('b', 'bad', False, _('mark changeset bad')),
3203 ('s', 'skip', False, _('skip testing changeset')),
3192 ('s', 'skip', False, _('skip testing changeset')),
3204 ('c', 'command', '', _('use command to check changeset state')),
3193 ('c', 'command', '', _('use command to check changeset state')),
3205 ('U', 'noupdate', False, _('do not update to target'))],
3194 ('U', 'noupdate', False, _('do not update to target'))],
3206 _("[-gbsr] [-c CMD] [REV]")),
3195 _("[-gbsr] [-c CMD] [REV]")),
3207 "branch":
3196 "branch":
3208 (branch,
3197 (branch,
3209 [('f', 'force', None,
3198 [('f', 'force', None,
3210 _('set branch name even if it shadows an existing branch')),
3199 _('set branch name even if it shadows an existing branch')),
3211 ('C', 'clean', None, _('reset branch name to parent branch name'))],
3200 ('C', 'clean', None, _('reset branch name to parent branch name'))],
3212 _('[-fC] [NAME]')),
3201 _('[-fC] [NAME]')),
3213 "branches":
3202 "branches":
3214 (branches,
3203 (branches,
3215 [('a', 'active', False,
3204 [('a', 'active', False,
3216 _('show only branches that have unmerged heads')),
3205 _('show only branches that have unmerged heads')),
3217 ('c', 'closed', False,
3206 ('c', 'closed', False,
3218 _('show normal and closed heads'))],
3207 _('show normal and closed heads'))],
3219 _('[-a]')),
3208 _('[-a]')),
3220 "bundle":
3209 "bundle":
3221 (bundle,
3210 (bundle,
3222 [('f', 'force', None,
3211 [('f', 'force', None,
3223 _('run even when remote repository is unrelated')),
3212 _('run even when remote repository is unrelated')),
3224 ('r', 'rev', [],
3213 ('r', 'rev', [],
3225 _('a changeset up to which you would like to bundle')),
3214 _('a changeset up to which you would like to bundle')),
3226 ('', 'base', [],
3215 ('', 'base', [],
3227 _('a base changeset to specify instead of a destination')),
3216 _('a base changeset to specify instead of a destination')),
3228 ('a', 'all', None, _('bundle all changesets in the repository')),
3217 ('a', 'all', None, _('bundle all changesets in the repository')),
3229 ('t', 'type', 'bzip2', _('bundle compression type to use')),
3218 ('t', 'type', 'bzip2', _('bundle compression type to use')),
3230 ] + remoteopts,
3219 ] + remoteopts,
3231 _('[-f] [-a] [-r REV]... [--base REV]... FILE [DEST]')),
3220 _('[-f] [-a] [-r REV]... [--base REV]... FILE [DEST]')),
3232 "cat":
3221 "cat":
3233 (cat,
3222 (cat,
3234 [('o', 'output', '', _('print output to file with formatted name')),
3223 [('o', 'output', '', _('print output to file with formatted name')),
3235 ('r', 'rev', '', _('print the given revision')),
3224 ('r', 'rev', '', _('print the given revision')),
3236 ('', 'decode', None, _('apply any matching decode filter')),
3225 ('', 'decode', None, _('apply any matching decode filter')),
3237 ] + walkopts,
3226 ] + walkopts,
3238 _('[OPTION]... FILE...')),
3227 _('[OPTION]... FILE...')),
3239 "^clone":
3228 "^clone":
3240 (clone,
3229 (clone,
3241 [('U', 'noupdate', None,
3230 [('U', 'noupdate', None,
3242 _('the clone will only contain a repository (no working copy)')),
3231 _('the clone will only contain a repository (no working copy)')),
3243 ('r', 'rev', [],
3232 ('r', 'rev', [],
3244 _('a changeset you would like to have after cloning')),
3233 _('a changeset you would like to have after cloning')),
3245 ('', 'pull', None, _('use pull protocol to copy metadata')),
3234 ('', 'pull', None, _('use pull protocol to copy metadata')),
3246 ('', 'uncompressed', None,
3235 ('', 'uncompressed', None,
3247 _('use uncompressed transfer (fast over LAN)')),
3236 _('use uncompressed transfer (fast over LAN)')),
3248 ] + remoteopts,
3237 ] + remoteopts,
3249 _('[OPTION]... SOURCE [DEST]')),
3238 _('[OPTION]... SOURCE [DEST]')),
3250 "^commit|ci":
3239 "^commit|ci":
3251 (commit,
3240 (commit,
3252 [('A', 'addremove', None,
3241 [('A', 'addremove', None,
3253 _('mark new/missing files as added/removed before committing')),
3242 _('mark new/missing files as added/removed before committing')),
3254 ('', 'close-branch', None,
3243 ('', 'close-branch', None,
3255 _('mark a branch as closed, hiding it from the branch list')),
3244 _('mark a branch as closed, hiding it from the branch list')),
3256 ] + walkopts + commitopts + commitopts2,
3245 ] + walkopts + commitopts + commitopts2,
3257 _('[OPTION]... [FILE]...')),
3246 _('[OPTION]... [FILE]...')),
3258 "copy|cp":
3247 "copy|cp":
3259 (copy,
3248 (copy,
3260 [('A', 'after', None, _('record a copy that has already occurred')),
3249 [('A', 'after', None, _('record a copy that has already occurred')),
3261 ('f', 'force', None,
3250 ('f', 'force', None,
3262 _('forcibly copy over an existing managed file')),
3251 _('forcibly copy over an existing managed file')),
3263 ] + walkopts + dryrunopts,
3252 ] + walkopts + dryrunopts,
3264 _('[OPTION]... [SOURCE]... DEST')),
3253 _('[OPTION]... [SOURCE]... DEST')),
3265 "debugancestor": (debugancestor, [], _('[INDEX] REV1 REV2')),
3254 "debugancestor": (debugancestor, [], _('[INDEX] REV1 REV2')),
3266 "debugcheckstate": (debugcheckstate, []),
3255 "debugcheckstate": (debugcheckstate, []),
3267 "debugcommands": (debugcommands, [], _('[COMMAND]')),
3256 "debugcommands": (debugcommands, [], _('[COMMAND]')),
3268 "debugcomplete":
3257 "debugcomplete":
3269 (debugcomplete,
3258 (debugcomplete,
3270 [('o', 'options', None, _('show the command options'))],
3259 [('o', 'options', None, _('show the command options'))],
3271 _('[-o] CMD')),
3260 _('[-o] CMD')),
3272 "debugdate":
3261 "debugdate":
3273 (debugdate,
3262 (debugdate,
3274 [('e', 'extended', None, _('try extended date formats'))],
3263 [('e', 'extended', None, _('try extended date formats'))],
3275 _('[-e] DATE [RANGE]')),
3264 _('[-e] DATE [RANGE]')),
3276 "debugdata": (debugdata, [], _('FILE REV')),
3265 "debugdata": (debugdata, [], _('FILE REV')),
3277 "debugfsinfo": (debugfsinfo, [], _('[PATH]')),
3266 "debugfsinfo": (debugfsinfo, [], _('[PATH]')),
3278 "debugindex": (debugindex, [], _('FILE')),
3267 "debugindex": (debugindex, [], _('FILE')),
3279 "debugindexdot": (debugindexdot, [], _('FILE')),
3268 "debugindexdot": (debugindexdot, [], _('FILE')),
3280 "debuginstall": (debuginstall, []),
3269 "debuginstall": (debuginstall, []),
3281 "debugrebuildstate":
3270 "debugrebuildstate":
3282 (debugrebuildstate,
3271 (debugrebuildstate,
3283 [('r', 'rev', '', _('revision to rebuild to'))],
3272 [('r', 'rev', '', _('revision to rebuild to'))],
3284 _('[-r REV] [REV]')),
3273 _('[-r REV] [REV]')),
3285 "debugrename":
3274 "debugrename":
3286 (debugrename,
3275 (debugrename,
3287 [('r', 'rev', '', _('revision to debug'))],
3276 [('r', 'rev', '', _('revision to debug'))],
3288 _('[-r REV] FILE')),
3277 _('[-r REV] FILE')),
3289 "debugsetparents":
3278 "debugsetparents":
3290 (debugsetparents, [], _('REV1 [REV2]')),
3279 (debugsetparents, [], _('REV1 [REV2]')),
3291 "debugstate":
3280 "debugstate":
3292 (debugstate,
3281 (debugstate,
3293 [('', 'nodates', None, _('do not display the saved mtime'))],
3282 [('', 'nodates', None, _('do not display the saved mtime'))],
3294 _('[OPTION]...')),
3283 _('[OPTION]...')),
3295 "debugsub":
3284 "debugsub":
3296 (debugsub,
3285 (debugsub,
3297 [('r', 'rev', '', _('revision to check'))],
3286 [('r', 'rev', '', _('revision to check'))],
3298 _('[-r REV] [REV]')),
3287 _('[-r REV] [REV]')),
3299 "debugwalk": (debugwalk, walkopts, _('[OPTION]... [FILE]...')),
3288 "debugwalk": (debugwalk, walkopts, _('[OPTION]... [FILE]...')),
3300 "^diff":
3289 "^diff":
3301 (diff,
3290 (diff,
3302 [('r', 'rev', [], _('revision')),
3291 [('r', 'rev', [], _('revision')),
3303 ('c', 'change', '', _('change made by revision'))
3292 ('c', 'change', '', _('change made by revision'))
3304 ] + diffopts + diffopts2 + walkopts,
3293 ] + diffopts + diffopts2 + walkopts,
3305 _('[OPTION]... [-r REV1 [-r REV2]] [FILE]...')),
3294 _('[OPTION]... [-r REV1 [-r REV2]] [FILE]...')),
3306 "^export":
3295 "^export":
3307 (export,
3296 (export,
3308 [('o', 'output', '', _('print output to file with formatted name')),
3297 [('o', 'output', '', _('print output to file with formatted name')),
3309 ('', 'switch-parent', None, _('diff against the second parent'))
3298 ('', 'switch-parent', None, _('diff against the second parent'))
3310 ] + diffopts,
3299 ] + diffopts,
3311 _('[OPTION]... [-o OUTFILESPEC] REV...')),
3300 _('[OPTION]... [-o OUTFILESPEC] REV...')),
3312 "^forget":
3301 "^forget":
3313 (forget,
3302 (forget,
3314 [] + walkopts,
3303 [] + walkopts,
3315 _('[OPTION]... FILE...')),
3304 _('[OPTION]... FILE...')),
3316 "grep":
3305 "grep":
3317 (grep,
3306 (grep,
3318 [('0', 'print0', None, _('end fields with NUL')),
3307 [('0', 'print0', None, _('end fields with NUL')),
3319 ('', 'all', None, _('print all revisions that match')),
3308 ('', 'all', None, _('print all revisions that match')),
3320 ('f', 'follow', None,
3309 ('f', 'follow', None,
3321 _('follow changeset history, or file history across copies and renames')),
3310 _('follow changeset history, or file history across copies and renames')),
3322 ('i', 'ignore-case', None, _('ignore case when matching')),
3311 ('i', 'ignore-case', None, _('ignore case when matching')),
3323 ('l', 'files-with-matches', None,
3312 ('l', 'files-with-matches', None,
3324 _('print only filenames and revisions that match')),
3313 _('print only filenames and revisions that match')),
3325 ('n', 'line-number', None, _('print matching line numbers')),
3314 ('n', 'line-number', None, _('print matching line numbers')),
3326 ('r', 'rev', [], _('search in given revision range')),
3315 ('r', 'rev', [], _('search in given revision range')),
3327 ('u', 'user', None, _('list the author (long with -v)')),
3316 ('u', 'user', None, _('list the author (long with -v)')),
3328 ('d', 'date', None, _('list the date (short with -q)')),
3317 ('d', 'date', None, _('list the date (short with -q)')),
3329 ] + walkopts,
3318 ] + walkopts,
3330 _('[OPTION]... PATTERN [FILE]...')),
3319 _('[OPTION]... PATTERN [FILE]...')),
3331 "heads":
3320 "heads":
3332 (heads,
3321 (heads,
3333 [('r', 'rev', '', _('show only heads which are descendants of REV')),
3322 [('r', 'rev', '', _('show only heads which are descendants of REV')),
3334 ('a', 'active', False,
3323 ('a', 'active', False,
3335 _('show only the active heads from open branches')),
3324 _('show only the active heads from open branches')),
3336 ('c', 'closed', False,
3325 ('c', 'closed', False,
3337 _('show normal and closed heads')),
3326 _('show normal and closed heads')),
3338 ] + templateopts,
3327 ] + templateopts,
3339 _('[-r STARTREV] [REV]...')),
3328 _('[-r STARTREV] [REV]...')),
3340 "help": (help_, [], _('[TOPIC]')),
3329 "help": (help_, [], _('[TOPIC]')),
3341 "identify|id":
3330 "identify|id":
3342 (identify,
3331 (identify,
3343 [('r', 'rev', '', _('identify the specified revision')),
3332 [('r', 'rev', '', _('identify the specified revision')),
3344 ('n', 'num', None, _('show local revision number')),
3333 ('n', 'num', None, _('show local revision number')),
3345 ('i', 'id', None, _('show global revision id')),
3334 ('i', 'id', None, _('show global revision id')),
3346 ('b', 'branch', None, _('show branch')),
3335 ('b', 'branch', None, _('show branch')),
3347 ('t', 'tags', None, _('show tags'))],
3336 ('t', 'tags', None, _('show tags'))],
3348 _('[-nibt] [-r REV] [SOURCE]')),
3337 _('[-nibt] [-r REV] [SOURCE]')),
3349 "import|patch":
3338 "import|patch":
3350 (import_,
3339 (import_,
3351 [('p', 'strip', 1,
3340 [('p', 'strip', 1,
3352 _('directory strip option for patch. This has the same '
3341 _('directory strip option for patch. This has the same '
3353 'meaning as the corresponding patch option')),
3342 'meaning as the corresponding patch option')),
3354 ('b', 'base', '', _('base path')),
3343 ('b', 'base', '', _('base path')),
3355 ('f', 'force', None,
3344 ('f', 'force', None,
3356 _('skip check for outstanding uncommitted changes')),
3345 _('skip check for outstanding uncommitted changes')),
3357 ('', 'no-commit', None, _("don't commit, just update the working directory")),
3346 ('', 'no-commit', None, _("don't commit, just update the working directory")),
3358 ('', 'exact', None,
3347 ('', 'exact', None,
3359 _('apply patch to the nodes from which it was generated')),
3348 _('apply patch to the nodes from which it was generated')),
3360 ('', 'import-branch', None,
3349 ('', 'import-branch', None,
3361 _('use any branch information in patch (implied by --exact)'))] +
3350 _('use any branch information in patch (implied by --exact)'))] +
3362 commitopts + commitopts2 + similarityopts,
3351 commitopts + commitopts2 + similarityopts,
3363 _('[OPTION]... PATCH...')),
3352 _('[OPTION]... PATCH...')),
3364 "incoming|in":
3353 "incoming|in":
3365 (incoming,
3354 (incoming,
3366 [('f', 'force', None,
3355 [('f', 'force', None,
3367 _('run even when remote repository is unrelated')),
3356 _('run even when remote repository is unrelated')),
3368 ('n', 'newest-first', None, _('show newest record first')),
3357 ('n', 'newest-first', None, _('show newest record first')),
3369 ('', 'bundle', '', _('file to store the bundles into')),
3358 ('', 'bundle', '', _('file to store the bundles into')),
3370 ('r', 'rev', [],
3359 ('r', 'rev', [],
3371 _('a specific revision up to which you would like to pull')),
3360 _('a specific revision up to which you would like to pull')),
3372 ] + logopts + remoteopts,
3361 ] + logopts + remoteopts,
3373 _('[-p] [-n] [-M] [-f] [-r REV]...'
3362 _('[-p] [-n] [-M] [-f] [-r REV]...'
3374 ' [--bundle FILENAME] [SOURCE]')),
3363 ' [--bundle FILENAME] [SOURCE]')),
3375 "^init":
3364 "^init":
3376 (init,
3365 (init,
3377 remoteopts,
3366 remoteopts,
3378 _('[-e CMD] [--remotecmd CMD] [DEST]')),
3367 _('[-e CMD] [--remotecmd CMD] [DEST]')),
3379 "locate":
3368 "locate":
3380 (locate,
3369 (locate,
3381 [('r', 'rev', '', _('search the repository as it stood at REV')),
3370 [('r', 'rev', '', _('search the repository as it stood at REV')),
3382 ('0', 'print0', None,
3371 ('0', 'print0', None,
3383 _('end filenames with NUL, for use with xargs')),
3372 _('end filenames with NUL, for use with xargs')),
3384 ('f', 'fullpath', None,
3373 ('f', 'fullpath', None,
3385 _('print complete paths from the filesystem root')),
3374 _('print complete paths from the filesystem root')),
3386 ] + walkopts,
3375 ] + walkopts,
3387 _('[OPTION]... [PATTERN]...')),
3376 _('[OPTION]... [PATTERN]...')),
3388 "^log|history":
3377 "^log|history":
3389 (log,
3378 (log,
3390 [('f', 'follow', None,
3379 [('f', 'follow', None,
3391 _('follow changeset history, or file history across copies and renames')),
3380 _('follow changeset history, or file history across copies and renames')),
3392 ('', 'follow-first', None,
3381 ('', 'follow-first', None,
3393 _('only follow the first parent of merge changesets')),
3382 _('only follow the first parent of merge changesets')),
3394 ('d', 'date', '', _('show revisions matching date spec')),
3383 ('d', 'date', '', _('show revisions matching date spec')),
3395 ('C', 'copies', None, _('show copied files')),
3384 ('C', 'copies', None, _('show copied files')),
3396 ('k', 'keyword', [], _('do case-insensitive search for a keyword')),
3385 ('k', 'keyword', [], _('do case-insensitive search for a keyword')),
3397 ('r', 'rev', [], _('show the specified revision or range')),
3386 ('r', 'rev', [], _('show the specified revision or range')),
3398 ('', 'removed', None, _('include revisions where files were removed')),
3387 ('', 'removed', None, _('include revisions where files were removed')),
3399 ('m', 'only-merges', None, _('show only merges')),
3388 ('m', 'only-merges', None, _('show only merges')),
3400 ('u', 'user', [], _('revisions committed by user')),
3389 ('u', 'user', [], _('revisions committed by user')),
3401 ('b', 'only-branch', [],
3390 ('b', 'only-branch', [],
3402 _('show only changesets within the given named branch')),
3391 _('show only changesets within the given named branch')),
3403 ('P', 'prune', [], _('do not display revision or any of its ancestors')),
3392 ('P', 'prune', [], _('do not display revision or any of its ancestors')),
3404 ] + logopts + walkopts,
3393 ] + logopts + walkopts,
3405 _('[OPTION]... [FILE]')),
3394 _('[OPTION]... [FILE]')),
3406 "manifest":
3395 "manifest":
3407 (manifest,
3396 (manifest,
3408 [('r', 'rev', '', _('revision to display'))],
3397 [('r', 'rev', '', _('revision to display'))],
3409 _('[-r REV]')),
3398 _('[-r REV]')),
3410 "^merge":
3399 "^merge":
3411 (merge,
3400 (merge,
3412 [('f', 'force', None, _('force a merge with outstanding changes')),
3401 [('f', 'force', None, _('force a merge with outstanding changes')),
3413 ('r', 'rev', '', _('revision to merge')),
3402 ('r', 'rev', '', _('revision to merge')),
3414 ('P', 'preview', None,
3403 ('P', 'preview', None,
3415 _('review revisions to merge (no merge is performed)'))],
3404 _('review revisions to merge (no merge is performed)'))],
3416 _('[-f] [[-r] REV]')),
3405 _('[-f] [[-r] REV]')),
3417 "outgoing|out":
3406 "outgoing|out":
3418 (outgoing,
3407 (outgoing,
3419 [('f', 'force', None,
3408 [('f', 'force', None,
3420 _('run even when remote repository is unrelated')),
3409 _('run even when remote repository is unrelated')),
3421 ('r', 'rev', [],
3410 ('r', 'rev', [],
3422 _('a specific revision up to which you would like to push')),
3411 _('a specific revision up to which you would like to push')),
3423 ('n', 'newest-first', None, _('show newest record first')),
3412 ('n', 'newest-first', None, _('show newest record first')),
3424 ] + logopts + remoteopts,
3413 ] + logopts + remoteopts,
3425 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
3414 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
3426 "^parents":
3415 "^parents":
3427 (parents,
3416 (parents,
3428 [('r', 'rev', '', _('show parents from the specified revision')),
3417 [('r', 'rev', '', _('show parents from the specified revision')),
3429 ] + templateopts,
3418 ] + templateopts,
3430 _('[-r REV] [FILE]')),
3419 _('[-r REV] [FILE]')),
3431 "paths": (paths, [], _('[NAME]')),
3420 "paths": (paths, [], _('[NAME]')),
3432 "^pull":
3421 "^pull":
3433 (pull,
3422 (pull,
3434 [('u', 'update', None,
3423 [('u', 'update', None,
3435 _('update to new tip if changesets were pulled')),
3424 _('update to new tip if changesets were pulled')),
3436 ('f', 'force', None,
3425 ('f', 'force', None,
3437 _('run even when remote repository is unrelated')),
3426 _('run even when remote repository is unrelated')),
3438 ('r', 'rev', [],
3427 ('r', 'rev', [],
3439 _('a specific revision up to which you would like to pull')),
3428 _('a specific revision up to which you would like to pull')),
3440 ] + remoteopts,
3429 ] + remoteopts,
3441 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
3430 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
3442 "^push":
3431 "^push":
3443 (push,
3432 (push,
3444 [('f', 'force', None, _('force push')),
3433 [('f', 'force', None, _('force push')),
3445 ('r', 'rev', [],
3434 ('r', 'rev', [],
3446 _('a specific revision up to which you would like to push')),
3435 _('a specific revision up to which you would like to push')),
3447 ] + remoteopts,
3436 ] + remoteopts,
3448 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
3437 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
3449 "recover": (recover, []),
3438 "recover": (recover, []),
3450 "^remove|rm":
3439 "^remove|rm":
3451 (remove,
3440 (remove,
3452 [('A', 'after', None, _('record delete for missing files')),
3441 [('A', 'after', None, _('record delete for missing files')),
3453 ('f', 'force', None,
3442 ('f', 'force', None,
3454 _('remove (and delete) file even if added or modified')),
3443 _('remove (and delete) file even if added or modified')),
3455 ] + walkopts,
3444 ] + walkopts,
3456 _('[OPTION]... FILE...')),
3445 _('[OPTION]... FILE...')),
3457 "rename|mv":
3446 "rename|mv":
3458 (rename,
3447 (rename,
3459 [('A', 'after', None, _('record a rename that has already occurred')),
3448 [('A', 'after', None, _('record a rename that has already occurred')),
3460 ('f', 'force', None,
3449 ('f', 'force', None,
3461 _('forcibly copy over an existing managed file')),
3450 _('forcibly copy over an existing managed file')),
3462 ] + walkopts + dryrunopts,
3451 ] + walkopts + dryrunopts,
3463 _('[OPTION]... SOURCE... DEST')),
3452 _('[OPTION]... SOURCE... DEST')),
3464 "resolve":
3453 "resolve":
3465 (resolve,
3454 (resolve,
3466 [('a', 'all', None, _('remerge all unresolved files')),
3455 [('a', 'all', None, _('remerge all unresolved files')),
3467 ('l', 'list', None, _('list state of files needing merge')),
3456 ('l', 'list', None, _('list state of files needing merge')),
3468 ('m', 'mark', None, _('mark files as resolved')),
3457 ('m', 'mark', None, _('mark files as resolved')),
3469 ('u', 'unmark', None, _('unmark files as resolved'))]
3458 ('u', 'unmark', None, _('unmark files as resolved'))]
3470 + walkopts,
3459 + walkopts,
3471 _('[OPTION]... [FILE]...')),
3460 _('[OPTION]... [FILE]...')),
3472 "revert":
3461 "revert":
3473 (revert,
3462 (revert,
3474 [('a', 'all', None, _('revert all changes when no arguments given')),
3463 [('a', 'all', None, _('revert all changes when no arguments given')),
3475 ('d', 'date', '', _('tipmost revision matching date')),
3464 ('d', 'date', '', _('tipmost revision matching date')),
3476 ('r', 'rev', '', _('revision to revert to')),
3465 ('r', 'rev', '', _('revision to revert to')),
3477 ('', 'no-backup', None, _('do not save backup copies of files')),
3466 ('', 'no-backup', None, _('do not save backup copies of files')),
3478 ] + walkopts + dryrunopts,
3467 ] + walkopts + dryrunopts,
3479 _('[OPTION]... [-r REV] [NAME]...')),
3468 _('[OPTION]... [-r REV] [NAME]...')),
3480 "rollback": (rollback, []),
3469 "rollback": (rollback, []),
3481 "root": (root, []),
3470 "root": (root, []),
3482 "^serve":
3471 "^serve":
3483 (serve,
3472 (serve,
3484 [('A', 'accesslog', '', _('name of access log file to write to')),
3473 [('A', 'accesslog', '', _('name of access log file to write to')),
3485 ('d', 'daemon', None, _('run server in background')),
3474 ('d', 'daemon', None, _('run server in background')),
3486 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
3475 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
3487 ('E', 'errorlog', '', _('name of error log file to write to')),
3476 ('E', 'errorlog', '', _('name of error log file to write to')),
3488 ('p', 'port', 0, _('port to listen on (default: 8000)')),
3477 ('p', 'port', 0, _('port to listen on (default: 8000)')),
3489 ('a', 'address', '', _('address to listen on (default: all interfaces)')),
3478 ('a', 'address', '', _('address to listen on (default: all interfaces)')),
3490 ('', 'prefix', '', _('prefix path to serve from (default: server root)')),
3479 ('', 'prefix', '', _('prefix path to serve from (default: server root)')),
3491 ('n', 'name', '',
3480 ('n', 'name', '',
3492 _('name to show in web pages (default: working directory)')),
3481 _('name to show in web pages (default: working directory)')),
3493 ('', 'webdir-conf', '', _('name of the webdir config file'
3482 ('', 'webdir-conf', '', _('name of the webdir config file'
3494 ' (serve more than one repository)')),
3483 ' (serve more than one repository)')),
3495 ('', 'pid-file', '', _('name of file to write process ID to')),
3484 ('', 'pid-file', '', _('name of file to write process ID to')),
3496 ('', 'stdio', None, _('for remote clients')),
3485 ('', 'stdio', None, _('for remote clients')),
3497 ('t', 'templates', '', _('web templates to use')),
3486 ('t', 'templates', '', _('web templates to use')),
3498 ('', 'style', '', _('template style to use')),
3487 ('', 'style', '', _('template style to use')),
3499 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
3488 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
3500 ('', 'certificate', '', _('SSL certificate file'))],
3489 ('', 'certificate', '', _('SSL certificate file'))],
3501 _('[OPTION]...')),
3490 _('[OPTION]...')),
3502 "showconfig|debugconfig":
3491 "showconfig|debugconfig":
3503 (showconfig,
3492 (showconfig,
3504 [('u', 'untrusted', None, _('show untrusted configuration options'))],
3493 [('u', 'untrusted', None, _('show untrusted configuration options'))],
3505 _('[-u] [NAME]...')),
3494 _('[-u] [NAME]...')),
3506 "^status|st":
3495 "^status|st":
3507 (status,
3496 (status,
3508 [('A', 'all', None, _('show status of all files')),
3497 [('A', 'all', None, _('show status of all files')),
3509 ('m', 'modified', None, _('show only modified files')),
3498 ('m', 'modified', None, _('show only modified files')),
3510 ('a', 'added', None, _('show only added files')),
3499 ('a', 'added', None, _('show only added files')),
3511 ('r', 'removed', None, _('show only removed files')),
3500 ('r', 'removed', None, _('show only removed files')),
3512 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
3501 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
3513 ('c', 'clean', None, _('show only files without changes')),
3502 ('c', 'clean', None, _('show only files without changes')),
3514 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
3503 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
3515 ('i', 'ignored', None, _('show only ignored files')),
3504 ('i', 'ignored', None, _('show only ignored files')),
3516 ('n', 'no-status', None, _('hide status prefix')),
3505 ('n', 'no-status', None, _('hide status prefix')),
3517 ('C', 'copies', None, _('show source of copied files')),
3506 ('C', 'copies', None, _('show source of copied files')),
3518 ('0', 'print0', None,
3507 ('0', 'print0', None,
3519 _('end filenames with NUL, for use with xargs')),
3508 _('end filenames with NUL, for use with xargs')),
3520 ('', 'rev', [], _('show difference from revision')),
3509 ('', 'rev', [], _('show difference from revision')),
3521 ] + walkopts,
3510 ] + walkopts,
3522 _('[OPTION]... [FILE]...')),
3511 _('[OPTION]... [FILE]...')),
3523 "tag":
3512 "tag":
3524 (tag,
3513 (tag,
3525 [('f', 'force', None, _('replace existing tag')),
3514 [('f', 'force', None, _('replace existing tag')),
3526 ('l', 'local', None, _('make the tag local')),
3515 ('l', 'local', None, _('make the tag local')),
3527 ('r', 'rev', '', _('revision to tag')),
3516 ('r', 'rev', '', _('revision to tag')),
3528 ('', 'remove', None, _('remove a tag')),
3517 ('', 'remove', None, _('remove a tag')),
3529 # -l/--local is already there, commitopts cannot be used
3518 # -l/--local is already there, commitopts cannot be used
3530 ('m', 'message', '', _('use <text> as commit message')),
3519 ('m', 'message', '', _('use <text> as commit message')),
3531 ] + commitopts2,
3520 ] + commitopts2,
3532 _('[-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...')),
3521 _('[-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...')),
3533 "tags": (tags, []),
3522 "tags": (tags, []),
3534 "tip":
3523 "tip":
3535 (tip,
3524 (tip,
3536 [('p', 'patch', None, _('show patch')),
3525 [('p', 'patch', None, _('show patch')),
3537 ('g', 'git', None, _('use git extended diff format')),
3526 ('g', 'git', None, _('use git extended diff format')),
3538 ] + templateopts,
3527 ] + templateopts,
3539 _('[-p]')),
3528 _('[-p]')),
3540 "unbundle":
3529 "unbundle":
3541 (unbundle,
3530 (unbundle,
3542 [('u', 'update', None,
3531 [('u', 'update', None,
3543 _('update to new tip if changesets were unbundled'))],
3532 _('update to new tip if changesets were unbundled'))],
3544 _('[-u] FILE...')),
3533 _('[-u] FILE...')),
3545 "^update|up|checkout|co":
3534 "^update|up|checkout|co":
3546 (update,
3535 (update,
3547 [('C', 'clean', None, _('overwrite locally modified files (no backup)')),
3536 [('C', 'clean', None, _('overwrite locally modified files (no backup)')),
3548 ('c', 'check', None, _('check for uncommitted changes')),
3537 ('c', 'check', None, _('check for uncommitted changes')),
3549 ('d', 'date', '', _('tipmost revision matching date')),
3538 ('d', 'date', '', _('tipmost revision matching date')),
3550 ('r', 'rev', '', _('revision'))],
3539 ('r', 'rev', '', _('revision'))],
3551 _('[-C] [-d DATE] [[-r] REV]')),
3540 _('[-C] [-d DATE] [[-r] REV]')),
3552 "verify": (verify, []),
3541 "verify": (verify, []),
3553 "version": (version_, []),
3542 "version": (version_, []),
3554 }
3543 }
3555
3544
3556 norepo = ("clone init version help debugcommands debugcomplete debugdata"
3545 norepo = ("clone init version help debugcommands debugcomplete debugdata"
3557 " debugindex debugindexdot debugdate debuginstall debugfsinfo")
3546 " debugindex debugindexdot debugdate debuginstall debugfsinfo")
3558 optionalrepo = ("identify paths serve showconfig debugancestor")
3547 optionalrepo = ("identify paths serve showconfig debugancestor")
@@ -1,815 +1,815 b''
1 # context.py - changeset and file context objects for mercurial
1 # context.py - changeset and file context objects for mercurial
2 #
2 #
3 # Copyright 2006, 2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2006, 2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2, incorporated herein by reference.
6 # GNU General Public License version 2, incorporated herein by reference.
7
7
8 from node import nullid, nullrev, short, hex
8 from node import nullid, nullrev, short, hex
9 from i18n import _
9 from i18n import _
10 import ancestor, bdiff, error, util, subrepo
10 import ancestor, bdiff, error, util, subrepo
11 import os, errno
11 import os, errno
12
12
13 propertycache = util.propertycache
13 propertycache = util.propertycache
14
14
15 class changectx(object):
15 class changectx(object):
16 """A changecontext object makes access to data related to a particular
16 """A changecontext object makes access to data related to a particular
17 changeset convenient."""
17 changeset convenient."""
18 def __init__(self, repo, changeid=''):
18 def __init__(self, repo, changeid=''):
19 """changeid is a revision number, node, or tag"""
19 """changeid is a revision number, node, or tag"""
20 if changeid == '':
20 if changeid == '':
21 changeid = '.'
21 changeid = '.'
22 self._repo = repo
22 self._repo = repo
23 if isinstance(changeid, (long, int)):
23 if isinstance(changeid, (long, int)):
24 self._rev = changeid
24 self._rev = changeid
25 self._node = self._repo.changelog.node(changeid)
25 self._node = self._repo.changelog.node(changeid)
26 else:
26 else:
27 self._node = self._repo.lookup(changeid)
27 self._node = self._repo.lookup(changeid)
28 self._rev = self._repo.changelog.rev(self._node)
28 self._rev = self._repo.changelog.rev(self._node)
29
29
30 def __str__(self):
30 def __str__(self):
31 return short(self.node())
31 return short(self.node())
32
32
33 def __int__(self):
33 def __int__(self):
34 return self.rev()
34 return self.rev()
35
35
36 def __repr__(self):
36 def __repr__(self):
37 return "<changectx %s>" % str(self)
37 return "<changectx %s>" % str(self)
38
38
39 def __hash__(self):
39 def __hash__(self):
40 try:
40 try:
41 return hash(self._rev)
41 return hash(self._rev)
42 except AttributeError:
42 except AttributeError:
43 return id(self)
43 return id(self)
44
44
45 def __eq__(self, other):
45 def __eq__(self, other):
46 try:
46 try:
47 return self._rev == other._rev
47 return self._rev == other._rev
48 except AttributeError:
48 except AttributeError:
49 return False
49 return False
50
50
51 def __ne__(self, other):
51 def __ne__(self, other):
52 return not (self == other)
52 return not (self == other)
53
53
54 def __nonzero__(self):
54 def __nonzero__(self):
55 return self._rev != nullrev
55 return self._rev != nullrev
56
56
57 @propertycache
57 @propertycache
58 def _changeset(self):
58 def _changeset(self):
59 return self._repo.changelog.read(self.node())
59 return self._repo.changelog.read(self.node())
60
60
61 @propertycache
61 @propertycache
62 def _manifest(self):
62 def _manifest(self):
63 return self._repo.manifest.read(self._changeset[0])
63 return self._repo.manifest.read(self._changeset[0])
64
64
65 @propertycache
65 @propertycache
66 def _manifestdelta(self):
66 def _manifestdelta(self):
67 return self._repo.manifest.readdelta(self._changeset[0])
67 return self._repo.manifest.readdelta(self._changeset[0])
68
68
69 @propertycache
69 @propertycache
70 def _parents(self):
70 def _parents(self):
71 p = self._repo.changelog.parentrevs(self._rev)
71 p = self._repo.changelog.parentrevs(self._rev)
72 if p[1] == nullrev:
72 if p[1] == nullrev:
73 p = p[:-1]
73 p = p[:-1]
74 return [changectx(self._repo, x) for x in p]
74 return [changectx(self._repo, x) for x in p]
75
75
76 @propertycache
76 @propertycache
77 def substate(self):
77 def substate(self):
78 return subrepo.state(self)
78 return subrepo.state(self)
79
79
80 def __contains__(self, key):
80 def __contains__(self, key):
81 return key in self._manifest
81 return key in self._manifest
82
82
83 def __getitem__(self, key):
83 def __getitem__(self, key):
84 return self.filectx(key)
84 return self.filectx(key)
85
85
86 def __iter__(self):
86 def __iter__(self):
87 for f in sorted(self._manifest):
87 for f in sorted(self._manifest):
88 yield f
88 yield f
89
89
90 def changeset(self): return self._changeset
90 def changeset(self): return self._changeset
91 def manifest(self): return self._manifest
91 def manifest(self): return self._manifest
92 def manifestnode(self): return self._changeset[0]
92 def manifestnode(self): return self._changeset[0]
93
93
94 def rev(self): return self._rev
94 def rev(self): return self._rev
95 def node(self): return self._node
95 def node(self): return self._node
96 def hex(self): return hex(self._node)
96 def hex(self): return hex(self._node)
97 def user(self): return self._changeset[1]
97 def user(self): return self._changeset[1]
98 def date(self): return self._changeset[2]
98 def date(self): return self._changeset[2]
99 def files(self): return self._changeset[3]
99 def files(self): return self._changeset[3]
100 def description(self): return self._changeset[4]
100 def description(self): return self._changeset[4]
101 def branch(self): return self._changeset[5].get("branch")
101 def branch(self): return self._changeset[5].get("branch")
102 def extra(self): return self._changeset[5]
102 def extra(self): return self._changeset[5]
103 def tags(self): return self._repo.nodetags(self._node)
103 def tags(self): return self._repo.nodetags(self._node)
104
104
105 def parents(self):
105 def parents(self):
106 """return contexts for each parent changeset"""
106 """return contexts for each parent changeset"""
107 return self._parents
107 return self._parents
108
108
109 def p1(self):
109 def p1(self):
110 return self._parents[0]
110 return self._parents[0]
111
111
112 def p2(self):
112 def p2(self):
113 if len(self._parents) == 2:
113 if len(self._parents) == 2:
114 return self._parents[1]
114 return self._parents[1]
115 return changectx(self._repo, -1)
115 return changectx(self._repo, -1)
116
116
117 def children(self):
117 def children(self):
118 """return contexts for each child changeset"""
118 """return contexts for each child changeset"""
119 c = self._repo.changelog.children(self._node)
119 c = self._repo.changelog.children(self._node)
120 return [changectx(self._repo, x) for x in c]
120 return [changectx(self._repo, x) for x in c]
121
121
122 def ancestors(self):
122 def ancestors(self):
123 for a in self._repo.changelog.ancestors(self._rev):
123 for a in self._repo.changelog.ancestors(self._rev):
124 yield changectx(self._repo, a)
124 yield changectx(self._repo, a)
125
125
126 def descendants(self):
126 def descendants(self):
127 for d in self._repo.changelog.descendants(self._rev):
127 for d in self._repo.changelog.descendants(self._rev):
128 yield changectx(self._repo, d)
128 yield changectx(self._repo, d)
129
129
130 def _fileinfo(self, path):
130 def _fileinfo(self, path):
131 if '_manifest' in self.__dict__:
131 if '_manifest' in self.__dict__:
132 try:
132 try:
133 return self._manifest[path], self._manifest.flags(path)
133 return self._manifest[path], self._manifest.flags(path)
134 except KeyError:
134 except KeyError:
135 raise error.LookupError(self._node, path,
135 raise error.LookupError(self._node, path,
136 _('not found in manifest'))
136 _('not found in manifest'))
137 if '_manifestdelta' in self.__dict__ or path in self.files():
137 if '_manifestdelta' in self.__dict__ or path in self.files():
138 if path in self._manifestdelta:
138 if path in self._manifestdelta:
139 return self._manifestdelta[path], self._manifestdelta.flags(path)
139 return self._manifestdelta[path], self._manifestdelta.flags(path)
140 node, flag = self._repo.manifest.find(self._changeset[0], path)
140 node, flag = self._repo.manifest.find(self._changeset[0], path)
141 if not node:
141 if not node:
142 raise error.LookupError(self._node, path,
142 raise error.LookupError(self._node, path,
143 _('not found in manifest'))
143 _('not found in manifest'))
144
144
145 return node, flag
145 return node, flag
146
146
147 def filenode(self, path):
147 def filenode(self, path):
148 return self._fileinfo(path)[0]
148 return self._fileinfo(path)[0]
149
149
150 def flags(self, path):
150 def flags(self, path):
151 try:
151 try:
152 return self._fileinfo(path)[1]
152 return self._fileinfo(path)[1]
153 except error.LookupError:
153 except error.LookupError:
154 return ''
154 return ''
155
155
156 def filectx(self, path, fileid=None, filelog=None):
156 def filectx(self, path, fileid=None, filelog=None):
157 """get a file context from this changeset"""
157 """get a file context from this changeset"""
158 if fileid is None:
158 if fileid is None:
159 fileid = self.filenode(path)
159 fileid = self.filenode(path)
160 return filectx(self._repo, path, fileid=fileid,
160 return filectx(self._repo, path, fileid=fileid,
161 changectx=self, filelog=filelog)
161 changectx=self, filelog=filelog)
162
162
163 def ancestor(self, c2):
163 def ancestor(self, c2):
164 """
164 """
165 return the ancestor context of self and c2
165 return the ancestor context of self and c2
166 """
166 """
167 n = self._repo.changelog.ancestor(self._node, c2._node)
167 n = self._repo.changelog.ancestor(self._node, c2._node)
168 return changectx(self._repo, n)
168 return changectx(self._repo, n)
169
169
170 def walk(self, match):
170 def walk(self, match):
171 fset = set(match.files())
171 fset = set(match.files())
172 # for dirstate.walk, files=['.'] means "walk the whole tree".
172 # for dirstate.walk, files=['.'] means "walk the whole tree".
173 # follow that here, too
173 # follow that here, too
174 fset.discard('.')
174 fset.discard('.')
175 for fn in self:
175 for fn in self:
176 for ffn in fset:
176 for ffn in fset:
177 # match if the file is the exact name or a directory
177 # match if the file is the exact name or a directory
178 if ffn == fn or fn.startswith("%s/" % ffn):
178 if ffn == fn or fn.startswith("%s/" % ffn):
179 fset.remove(ffn)
179 fset.remove(ffn)
180 break
180 break
181 if match(fn):
181 if match(fn):
182 yield fn
182 yield fn
183 for fn in sorted(fset):
183 for fn in sorted(fset):
184 if match.bad(fn, 'No such file in rev ' + str(self)) and match(fn):
184 if match.bad(fn, 'No such file in rev ' + str(self)) and match(fn):
185 yield fn
185 yield fn
186
186
187 def sub(self, path):
187 def sub(self, path):
188 return subrepo.subrepo(self, path)
188 return subrepo.subrepo(self, path)
189
189
190 class filectx(object):
190 class filectx(object):
191 """A filecontext object makes access to data related to a particular
191 """A filecontext object makes access to data related to a particular
192 filerevision convenient."""
192 filerevision convenient."""
193 def __init__(self, repo, path, changeid=None, fileid=None,
193 def __init__(self, repo, path, changeid=None, fileid=None,
194 filelog=None, changectx=None):
194 filelog=None, changectx=None):
195 """changeid can be a changeset revision, node, or tag.
195 """changeid can be a changeset revision, node, or tag.
196 fileid can be a file revision or node."""
196 fileid can be a file revision or node."""
197 self._repo = repo
197 self._repo = repo
198 self._path = path
198 self._path = path
199
199
200 assert (changeid is not None
200 assert (changeid is not None
201 or fileid is not None
201 or fileid is not None
202 or changectx is not None)
202 or changectx is not None)
203
203
204 if filelog:
204 if filelog:
205 self._filelog = filelog
205 self._filelog = filelog
206
206
207 if changeid is not None:
207 if changeid is not None:
208 self._changeid = changeid
208 self._changeid = changeid
209 if changectx is not None:
209 if changectx is not None:
210 self._changectx = changectx
210 self._changectx = changectx
211 if fileid is not None:
211 if fileid is not None:
212 self._fileid = fileid
212 self._fileid = fileid
213
213
214 @propertycache
214 @propertycache
215 def _changectx(self):
215 def _changectx(self):
216 return changectx(self._repo, self._changeid)
216 return changectx(self._repo, self._changeid)
217
217
218 @propertycache
218 @propertycache
219 def _filelog(self):
219 def _filelog(self):
220 return self._repo.file(self._path)
220 return self._repo.file(self._path)
221
221
222 @propertycache
222 @propertycache
223 def _changeid(self):
223 def _changeid(self):
224 if '_changectx' in self.__dict__:
224 if '_changectx' in self.__dict__:
225 return self._changectx.rev()
225 return self._changectx.rev()
226 else:
226 else:
227 return self._filelog.linkrev(self._filerev)
227 return self._filelog.linkrev(self._filerev)
228
228
229 @propertycache
229 @propertycache
230 def _filenode(self):
230 def _filenode(self):
231 if '_fileid' in self.__dict__:
231 if '_fileid' in self.__dict__:
232 return self._filelog.lookup(self._fileid)
232 return self._filelog.lookup(self._fileid)
233 else:
233 else:
234 return self._changectx.filenode(self._path)
234 return self._changectx.filenode(self._path)
235
235
236 @propertycache
236 @propertycache
237 def _filerev(self):
237 def _filerev(self):
238 return self._filelog.rev(self._filenode)
238 return self._filelog.rev(self._filenode)
239
239
240 @propertycache
240 @propertycache
241 def _repopath(self):
241 def _repopath(self):
242 return self._path
242 return self._path
243
243
244 def __nonzero__(self):
244 def __nonzero__(self):
245 try:
245 try:
246 self._filenode
246 self._filenode
247 return True
247 return True
248 except error.LookupError:
248 except error.LookupError:
249 # file is missing
249 # file is missing
250 return False
250 return False
251
251
252 def __str__(self):
252 def __str__(self):
253 return "%s@%s" % (self.path(), short(self.node()))
253 return "%s@%s" % (self.path(), short(self.node()))
254
254
255 def __repr__(self):
255 def __repr__(self):
256 return "<filectx %s>" % str(self)
256 return "<filectx %s>" % str(self)
257
257
258 def __hash__(self):
258 def __hash__(self):
259 try:
259 try:
260 return hash((self._path, self._fileid))
260 return hash((self._path, self._fileid))
261 except AttributeError:
261 except AttributeError:
262 return id(self)
262 return id(self)
263
263
264 def __eq__(self, other):
264 def __eq__(self, other):
265 try:
265 try:
266 return (self._path == other._path
266 return (self._path == other._path
267 and self._fileid == other._fileid)
267 and self._fileid == other._fileid)
268 except AttributeError:
268 except AttributeError:
269 return False
269 return False
270
270
271 def __ne__(self, other):
271 def __ne__(self, other):
272 return not (self == other)
272 return not (self == other)
273
273
274 def filectx(self, fileid):
274 def filectx(self, fileid):
275 '''opens an arbitrary revision of the file without
275 '''opens an arbitrary revision of the file without
276 opening a new filelog'''
276 opening a new filelog'''
277 return filectx(self._repo, self._path, fileid=fileid,
277 return filectx(self._repo, self._path, fileid=fileid,
278 filelog=self._filelog)
278 filelog=self._filelog)
279
279
280 def filerev(self): return self._filerev
280 def filerev(self): return self._filerev
281 def filenode(self): return self._filenode
281 def filenode(self): return self._filenode
282 def flags(self): return self._changectx.flags(self._path)
282 def flags(self): return self._changectx.flags(self._path)
283 def filelog(self): return self._filelog
283 def filelog(self): return self._filelog
284
284
285 def rev(self):
285 def rev(self):
286 if '_changectx' in self.__dict__:
286 if '_changectx' in self.__dict__:
287 return self._changectx.rev()
287 return self._changectx.rev()
288 if '_changeid' in self.__dict__:
288 if '_changeid' in self.__dict__:
289 return self._changectx.rev()
289 return self._changectx.rev()
290 return self._filelog.linkrev(self._filerev)
290 return self._filelog.linkrev(self._filerev)
291
291
292 def linkrev(self): return self._filelog.linkrev(self._filerev)
292 def linkrev(self): return self._filelog.linkrev(self._filerev)
293 def node(self): return self._changectx.node()
293 def node(self): return self._changectx.node()
294 def user(self): return self._changectx.user()
294 def user(self): return self._changectx.user()
295 def date(self): return self._changectx.date()
295 def date(self): return self._changectx.date()
296 def files(self): return self._changectx.files()
296 def files(self): return self._changectx.files()
297 def description(self): return self._changectx.description()
297 def description(self): return self._changectx.description()
298 def branch(self): return self._changectx.branch()
298 def branch(self): return self._changectx.branch()
299 def manifest(self): return self._changectx.manifest()
299 def manifest(self): return self._changectx.manifest()
300 def changectx(self): return self._changectx
300 def changectx(self): return self._changectx
301
301
302 def data(self): return self._filelog.read(self._filenode)
302 def data(self): return self._filelog.read(self._filenode)
303 def path(self): return self._path
303 def path(self): return self._path
304 def size(self): return self._filelog.size(self._filerev)
304 def size(self): return self._filelog.size(self._filerev)
305
305
306 def cmp(self, text): return self._filelog.cmp(self._filenode, text)
306 def cmp(self, text): return self._filelog.cmp(self._filenode, text)
307
307
308 def renamed(self):
308 def renamed(self):
309 """check if file was actually renamed in this changeset revision
309 """check if file was actually renamed in this changeset revision
310
310
311 If rename logged in file revision, we report copy for changeset only
311 If rename logged in file revision, we report copy for changeset only
312 if file revisions linkrev points back to the changeset in question
312 if file revisions linkrev points back to the changeset in question
313 or both changeset parents contain different file revisions.
313 or both changeset parents contain different file revisions.
314 """
314 """
315
315
316 renamed = self._filelog.renamed(self._filenode)
316 renamed = self._filelog.renamed(self._filenode)
317 if not renamed:
317 if not renamed:
318 return renamed
318 return renamed
319
319
320 if self.rev() == self.linkrev():
320 if self.rev() == self.linkrev():
321 return renamed
321 return renamed
322
322
323 name = self.path()
323 name = self.path()
324 fnode = self._filenode
324 fnode = self._filenode
325 for p in self._changectx.parents():
325 for p in self._changectx.parents():
326 try:
326 try:
327 if fnode == p.filenode(name):
327 if fnode == p.filenode(name):
328 return None
328 return None
329 except error.LookupError:
329 except error.LookupError:
330 pass
330 pass
331 return renamed
331 return renamed
332
332
333 def parents(self):
333 def parents(self):
334 p = self._path
334 p = self._path
335 fl = self._filelog
335 fl = self._filelog
336 pl = [(p, n, fl) for n in self._filelog.parents(self._filenode)]
336 pl = [(p, n, fl) for n in self._filelog.parents(self._filenode)]
337
337
338 r = self._filelog.renamed(self._filenode)
338 r = self._filelog.renamed(self._filenode)
339 if r:
339 if r:
340 pl[0] = (r[0], r[1], None)
340 pl[0] = (r[0], r[1], None)
341
341
342 return [filectx(self._repo, p, fileid=n, filelog=l)
342 return [filectx(self._repo, p, fileid=n, filelog=l)
343 for p,n,l in pl if n != nullid]
343 for p,n,l in pl if n != nullid]
344
344
345 def children(self):
345 def children(self):
346 # hard for renames
346 # hard for renames
347 c = self._filelog.children(self._filenode)
347 c = self._filelog.children(self._filenode)
348 return [filectx(self._repo, self._path, fileid=x,
348 return [filectx(self._repo, self._path, fileid=x,
349 filelog=self._filelog) for x in c]
349 filelog=self._filelog) for x in c]
350
350
351 def annotate(self, follow=False, linenumber=None):
351 def annotate(self, follow=False, linenumber=None):
352 '''returns a list of tuples of (ctx, line) for each line
352 '''returns a list of tuples of (ctx, line) for each line
353 in the file, where ctx is the filectx of the node where
353 in the file, where ctx is the filectx of the node where
354 that line was last changed.
354 that line was last changed.
355 This returns tuples of ((ctx, linenumber), line) for each line,
355 This returns tuples of ((ctx, linenumber), line) for each line,
356 if "linenumber" parameter is NOT "None".
356 if "linenumber" parameter is NOT "None".
357 In such tuples, linenumber means one at the first appearance
357 In such tuples, linenumber means one at the first appearance
358 in the managed file.
358 in the managed file.
359 To reduce annotation cost,
359 To reduce annotation cost,
360 this returns fixed value(False is used) as linenumber,
360 this returns fixed value(False is used) as linenumber,
361 if "linenumber" parameter is "False".'''
361 if "linenumber" parameter is "False".'''
362
362
363 def decorate_compat(text, rev):
363 def decorate_compat(text, rev):
364 return ([rev] * len(text.splitlines()), text)
364 return ([rev] * len(text.splitlines()), text)
365
365
366 def without_linenumber(text, rev):
366 def without_linenumber(text, rev):
367 return ([(rev, False)] * len(text.splitlines()), text)
367 return ([(rev, False)] * len(text.splitlines()), text)
368
368
369 def with_linenumber(text, rev):
369 def with_linenumber(text, rev):
370 size = len(text.splitlines())
370 size = len(text.splitlines())
371 return ([(rev, i) for i in xrange(1, size + 1)], text)
371 return ([(rev, i) for i in xrange(1, size + 1)], text)
372
372
373 decorate = (((linenumber is None) and decorate_compat) or
373 decorate = (((linenumber is None) and decorate_compat) or
374 (linenumber and with_linenumber) or
374 (linenumber and with_linenumber) or
375 without_linenumber)
375 without_linenumber)
376
376
377 def pair(parent, child):
377 def pair(parent, child):
378 for a1, a2, b1, b2 in bdiff.blocks(parent[1], child[1]):
378 for a1, a2, b1, b2 in bdiff.blocks(parent[1], child[1]):
379 child[0][b1:b2] = parent[0][a1:a2]
379 child[0][b1:b2] = parent[0][a1:a2]
380 return child
380 return child
381
381
382 getlog = util.cachefunc(lambda x: self._repo.file(x))
382 getlog = util.lrucachefunc(lambda x: self._repo.file(x))
383 def getctx(path, fileid):
383 def getctx(path, fileid):
384 log = path == self._path and self._filelog or getlog(path)
384 log = path == self._path and self._filelog or getlog(path)
385 return filectx(self._repo, path, fileid=fileid, filelog=log)
385 return filectx(self._repo, path, fileid=fileid, filelog=log)
386 getctx = util.cachefunc(getctx)
386 getctx = util.lrucachefunc(getctx)
387
387
388 def parents(f):
388 def parents(f):
389 # we want to reuse filectx objects as much as possible
389 # we want to reuse filectx objects as much as possible
390 p = f._path
390 p = f._path
391 if f._filerev is None: # working dir
391 if f._filerev is None: # working dir
392 pl = [(n.path(), n.filerev()) for n in f.parents()]
392 pl = [(n.path(), n.filerev()) for n in f.parents()]
393 else:
393 else:
394 pl = [(p, n) for n in f._filelog.parentrevs(f._filerev)]
394 pl = [(p, n) for n in f._filelog.parentrevs(f._filerev)]
395
395
396 if follow:
396 if follow:
397 r = f.renamed()
397 r = f.renamed()
398 if r:
398 if r:
399 pl[0] = (r[0], getlog(r[0]).rev(r[1]))
399 pl[0] = (r[0], getlog(r[0]).rev(r[1]))
400
400
401 return [getctx(p, n) for p, n in pl if n != nullrev]
401 return [getctx(p, n) for p, n in pl if n != nullrev]
402
402
403 # use linkrev to find the first changeset where self appeared
403 # use linkrev to find the first changeset where self appeared
404 if self.rev() != self.linkrev():
404 if self.rev() != self.linkrev():
405 base = self.filectx(self.filerev())
405 base = self.filectx(self.filerev())
406 else:
406 else:
407 base = self
407 base = self
408
408
409 # find all ancestors
409 # find all ancestors
410 needed = {base: 1}
410 needed = {base: 1}
411 visit = [base]
411 visit = [base]
412 files = [base._path]
412 files = [base._path]
413 while visit:
413 while visit:
414 f = visit.pop(0)
414 f = visit.pop(0)
415 for p in parents(f):
415 for p in parents(f):
416 if p not in needed:
416 if p not in needed:
417 needed[p] = 1
417 needed[p] = 1
418 visit.append(p)
418 visit.append(p)
419 if p._path not in files:
419 if p._path not in files:
420 files.append(p._path)
420 files.append(p._path)
421 else:
421 else:
422 # count how many times we'll use this
422 # count how many times we'll use this
423 needed[p] += 1
423 needed[p] += 1
424
424
425 # sort by revision (per file) which is a topological order
425 # sort by revision (per file) which is a topological order
426 visit = []
426 visit = []
427 for f in files:
427 for f in files:
428 fn = [(n.rev(), n) for n in needed if n._path == f]
428 fn = [(n.rev(), n) for n in needed if n._path == f]
429 visit.extend(fn)
429 visit.extend(fn)
430
430
431 hist = {}
431 hist = {}
432 for r, f in sorted(visit):
432 for r, f in sorted(visit):
433 curr = decorate(f.data(), f)
433 curr = decorate(f.data(), f)
434 for p in parents(f):
434 for p in parents(f):
435 if p != nullid:
435 if p != nullid:
436 curr = pair(hist[p], curr)
436 curr = pair(hist[p], curr)
437 # trim the history of unneeded revs
437 # trim the history of unneeded revs
438 needed[p] -= 1
438 needed[p] -= 1
439 if not needed[p]:
439 if not needed[p]:
440 del hist[p]
440 del hist[p]
441 hist[f] = curr
441 hist[f] = curr
442
442
443 return zip(hist[f][0], hist[f][1].splitlines(1))
443 return zip(hist[f][0], hist[f][1].splitlines(1))
444
444
445 def ancestor(self, fc2):
445 def ancestor(self, fc2):
446 """
446 """
447 find the common ancestor file context, if any, of self, and fc2
447 find the common ancestor file context, if any, of self, and fc2
448 """
448 """
449
449
450 acache = {}
450 acache = {}
451
451
452 # prime the ancestor cache for the working directory
452 # prime the ancestor cache for the working directory
453 for c in (self, fc2):
453 for c in (self, fc2):
454 if c._filerev is None:
454 if c._filerev is None:
455 pl = [(n.path(), n.filenode()) for n in c.parents()]
455 pl = [(n.path(), n.filenode()) for n in c.parents()]
456 acache[(c._path, None)] = pl
456 acache[(c._path, None)] = pl
457
457
458 flcache = {self._repopath:self._filelog, fc2._repopath:fc2._filelog}
458 flcache = {self._repopath:self._filelog, fc2._repopath:fc2._filelog}
459 def parents(vertex):
459 def parents(vertex):
460 if vertex in acache:
460 if vertex in acache:
461 return acache[vertex]
461 return acache[vertex]
462 f, n = vertex
462 f, n = vertex
463 if f not in flcache:
463 if f not in flcache:
464 flcache[f] = self._repo.file(f)
464 flcache[f] = self._repo.file(f)
465 fl = flcache[f]
465 fl = flcache[f]
466 pl = [(f, p) for p in fl.parents(n) if p != nullid]
466 pl = [(f, p) for p in fl.parents(n) if p != nullid]
467 re = fl.renamed(n)
467 re = fl.renamed(n)
468 if re:
468 if re:
469 pl.append(re)
469 pl.append(re)
470 acache[vertex] = pl
470 acache[vertex] = pl
471 return pl
471 return pl
472
472
473 a, b = (self._path, self._filenode), (fc2._path, fc2._filenode)
473 a, b = (self._path, self._filenode), (fc2._path, fc2._filenode)
474 v = ancestor.ancestor(a, b, parents)
474 v = ancestor.ancestor(a, b, parents)
475 if v:
475 if v:
476 f, n = v
476 f, n = v
477 return filectx(self._repo, f, fileid=n, filelog=flcache[f])
477 return filectx(self._repo, f, fileid=n, filelog=flcache[f])
478
478
479 return None
479 return None
480
480
481 class workingctx(changectx):
481 class workingctx(changectx):
482 """A workingctx object makes access to data related to
482 """A workingctx object makes access to data related to
483 the current working directory convenient.
483 the current working directory convenient.
484 parents - a pair of parent nodeids, or None to use the dirstate.
484 parents - a pair of parent nodeids, or None to use the dirstate.
485 date - any valid date string or (unixtime, offset), or None.
485 date - any valid date string or (unixtime, offset), or None.
486 user - username string, or None.
486 user - username string, or None.
487 extra - a dictionary of extra values, or None.
487 extra - a dictionary of extra values, or None.
488 changes - a list of file lists as returned by localrepo.status()
488 changes - a list of file lists as returned by localrepo.status()
489 or None to use the repository status.
489 or None to use the repository status.
490 """
490 """
491 def __init__(self, repo, parents=None, text="", user=None, date=None,
491 def __init__(self, repo, parents=None, text="", user=None, date=None,
492 extra=None, changes=None):
492 extra=None, changes=None):
493 self._repo = repo
493 self._repo = repo
494 self._rev = None
494 self._rev = None
495 self._node = None
495 self._node = None
496 self._text = text
496 self._text = text
497 if date:
497 if date:
498 self._date = util.parsedate(date)
498 self._date = util.parsedate(date)
499 if user:
499 if user:
500 self._user = user
500 self._user = user
501 if parents:
501 if parents:
502 self._parents = [changectx(self._repo, p) for p in parents]
502 self._parents = [changectx(self._repo, p) for p in parents]
503 if changes:
503 if changes:
504 self._status = list(changes)
504 self._status = list(changes)
505
505
506 self._extra = {}
506 self._extra = {}
507 if extra:
507 if extra:
508 self._extra = extra.copy()
508 self._extra = extra.copy()
509 if 'branch' not in self._extra:
509 if 'branch' not in self._extra:
510 branch = self._repo.dirstate.branch()
510 branch = self._repo.dirstate.branch()
511 try:
511 try:
512 branch = branch.decode('UTF-8').encode('UTF-8')
512 branch = branch.decode('UTF-8').encode('UTF-8')
513 except UnicodeDecodeError:
513 except UnicodeDecodeError:
514 raise util.Abort(_('branch name not in UTF-8!'))
514 raise util.Abort(_('branch name not in UTF-8!'))
515 self._extra['branch'] = branch
515 self._extra['branch'] = branch
516 if self._extra['branch'] == '':
516 if self._extra['branch'] == '':
517 self._extra['branch'] = 'default'
517 self._extra['branch'] = 'default'
518
518
519 def __str__(self):
519 def __str__(self):
520 return str(self._parents[0]) + "+"
520 return str(self._parents[0]) + "+"
521
521
522 def __nonzero__(self):
522 def __nonzero__(self):
523 return True
523 return True
524
524
525 def __contains__(self, key):
525 def __contains__(self, key):
526 return self._repo.dirstate[key] not in "?r"
526 return self._repo.dirstate[key] not in "?r"
527
527
528 @propertycache
528 @propertycache
529 def _manifest(self):
529 def _manifest(self):
530 """generate a manifest corresponding to the working directory"""
530 """generate a manifest corresponding to the working directory"""
531
531
532 man = self._parents[0].manifest().copy()
532 man = self._parents[0].manifest().copy()
533 copied = self._repo.dirstate.copies()
533 copied = self._repo.dirstate.copies()
534 cf = lambda x: man.flags(copied.get(x, x))
534 cf = lambda x: man.flags(copied.get(x, x))
535 ff = self._repo.dirstate.flagfunc(cf)
535 ff = self._repo.dirstate.flagfunc(cf)
536 modified, added, removed, deleted, unknown = self._status[:5]
536 modified, added, removed, deleted, unknown = self._status[:5]
537 for i, l in (("a", added), ("m", modified), ("u", unknown)):
537 for i, l in (("a", added), ("m", modified), ("u", unknown)):
538 for f in l:
538 for f in l:
539 man[f] = man.get(copied.get(f, f), nullid) + i
539 man[f] = man.get(copied.get(f, f), nullid) + i
540 try:
540 try:
541 man.set(f, ff(f))
541 man.set(f, ff(f))
542 except OSError:
542 except OSError:
543 pass
543 pass
544
544
545 for f in deleted + removed:
545 for f in deleted + removed:
546 if f in man:
546 if f in man:
547 del man[f]
547 del man[f]
548
548
549 return man
549 return man
550
550
551 @propertycache
551 @propertycache
552 def _status(self):
552 def _status(self):
553 return self._repo.status(unknown=True)
553 return self._repo.status(unknown=True)
554
554
555 @propertycache
555 @propertycache
556 def _user(self):
556 def _user(self):
557 return self._repo.ui.username()
557 return self._repo.ui.username()
558
558
559 @propertycache
559 @propertycache
560 def _date(self):
560 def _date(self):
561 return util.makedate()
561 return util.makedate()
562
562
563 @propertycache
563 @propertycache
564 def _parents(self):
564 def _parents(self):
565 p = self._repo.dirstate.parents()
565 p = self._repo.dirstate.parents()
566 if p[1] == nullid:
566 if p[1] == nullid:
567 p = p[:-1]
567 p = p[:-1]
568 self._parents = [changectx(self._repo, x) for x in p]
568 self._parents = [changectx(self._repo, x) for x in p]
569 return self._parents
569 return self._parents
570
570
571 def manifest(self): return self._manifest
571 def manifest(self): return self._manifest
572
572
573 def user(self): return self._user or self._repo.ui.username()
573 def user(self): return self._user or self._repo.ui.username()
574 def date(self): return self._date
574 def date(self): return self._date
575 def description(self): return self._text
575 def description(self): return self._text
576 def files(self):
576 def files(self):
577 return sorted(self._status[0] + self._status[1] + self._status[2])
577 return sorted(self._status[0] + self._status[1] + self._status[2])
578
578
579 def modified(self): return self._status[0]
579 def modified(self): return self._status[0]
580 def added(self): return self._status[1]
580 def added(self): return self._status[1]
581 def removed(self): return self._status[2]
581 def removed(self): return self._status[2]
582 def deleted(self): return self._status[3]
582 def deleted(self): return self._status[3]
583 def unknown(self): return self._status[4]
583 def unknown(self): return self._status[4]
584 def clean(self): return self._status[5]
584 def clean(self): return self._status[5]
585 def branch(self): return self._extra['branch']
585 def branch(self): return self._extra['branch']
586 def extra(self): return self._extra
586 def extra(self): return self._extra
587
587
588 def tags(self):
588 def tags(self):
589 t = []
589 t = []
590 [t.extend(p.tags()) for p in self.parents()]
590 [t.extend(p.tags()) for p in self.parents()]
591 return t
591 return t
592
592
593 def children(self):
593 def children(self):
594 return []
594 return []
595
595
596 def flags(self, path):
596 def flags(self, path):
597 if '_manifest' in self.__dict__:
597 if '_manifest' in self.__dict__:
598 try:
598 try:
599 return self._manifest.flags(path)
599 return self._manifest.flags(path)
600 except KeyError:
600 except KeyError:
601 return ''
601 return ''
602
602
603 pnode = self._parents[0].changeset()[0]
603 pnode = self._parents[0].changeset()[0]
604 orig = self._repo.dirstate.copies().get(path, path)
604 orig = self._repo.dirstate.copies().get(path, path)
605 node, flag = self._repo.manifest.find(pnode, orig)
605 node, flag = self._repo.manifest.find(pnode, orig)
606 try:
606 try:
607 ff = self._repo.dirstate.flagfunc(lambda x: flag or '')
607 ff = self._repo.dirstate.flagfunc(lambda x: flag or '')
608 return ff(path)
608 return ff(path)
609 except OSError:
609 except OSError:
610 pass
610 pass
611
611
612 if not node or path in self.deleted() or path in self.removed():
612 if not node or path in self.deleted() or path in self.removed():
613 return ''
613 return ''
614 return flag
614 return flag
615
615
616 def filectx(self, path, filelog=None):
616 def filectx(self, path, filelog=None):
617 """get a file context from the working directory"""
617 """get a file context from the working directory"""
618 return workingfilectx(self._repo, path, workingctx=self,
618 return workingfilectx(self._repo, path, workingctx=self,
619 filelog=filelog)
619 filelog=filelog)
620
620
621 def ancestor(self, c2):
621 def ancestor(self, c2):
622 """return the ancestor context of self and c2"""
622 """return the ancestor context of self and c2"""
623 return self._parents[0].ancestor(c2) # punt on two parents for now
623 return self._parents[0].ancestor(c2) # punt on two parents for now
624
624
625 def walk(self, match):
625 def walk(self, match):
626 return sorted(self._repo.dirstate.walk(match, True, False))
626 return sorted(self._repo.dirstate.walk(match, True, False))
627
627
628 def dirty(self, missing=False):
628 def dirty(self, missing=False):
629 "check whether a working directory is modified"
629 "check whether a working directory is modified"
630
630
631 return (self.p2() or self.branch() != self.p1().branch() or
631 return (self.p2() or self.branch() != self.p1().branch() or
632 self.modified() or self.added() or self.removed() or
632 self.modified() or self.added() or self.removed() or
633 (missing and self.deleted()))
633 (missing and self.deleted()))
634
634
635 class workingfilectx(filectx):
635 class workingfilectx(filectx):
636 """A workingfilectx object makes access to data related to a particular
636 """A workingfilectx object makes access to data related to a particular
637 file in the working directory convenient."""
637 file in the working directory convenient."""
638 def __init__(self, repo, path, filelog=None, workingctx=None):
638 def __init__(self, repo, path, filelog=None, workingctx=None):
639 """changeid can be a changeset revision, node, or tag.
639 """changeid can be a changeset revision, node, or tag.
640 fileid can be a file revision or node."""
640 fileid can be a file revision or node."""
641 self._repo = repo
641 self._repo = repo
642 self._path = path
642 self._path = path
643 self._changeid = None
643 self._changeid = None
644 self._filerev = self._filenode = None
644 self._filerev = self._filenode = None
645
645
646 if filelog:
646 if filelog:
647 self._filelog = filelog
647 self._filelog = filelog
648 if workingctx:
648 if workingctx:
649 self._changectx = workingctx
649 self._changectx = workingctx
650
650
651 @propertycache
651 @propertycache
652 def _changectx(self):
652 def _changectx(self):
653 return workingctx(self._repo)
653 return workingctx(self._repo)
654
654
655 def __nonzero__(self):
655 def __nonzero__(self):
656 return True
656 return True
657
657
658 def __str__(self):
658 def __str__(self):
659 return "%s@%s" % (self.path(), self._changectx)
659 return "%s@%s" % (self.path(), self._changectx)
660
660
661 def data(self): return self._repo.wread(self._path)
661 def data(self): return self._repo.wread(self._path)
662 def renamed(self):
662 def renamed(self):
663 rp = self._repo.dirstate.copied(self._path)
663 rp = self._repo.dirstate.copied(self._path)
664 if not rp:
664 if not rp:
665 return None
665 return None
666 return rp, self._changectx._parents[0]._manifest.get(rp, nullid)
666 return rp, self._changectx._parents[0]._manifest.get(rp, nullid)
667
667
668 def parents(self):
668 def parents(self):
669 '''return parent filectxs, following copies if necessary'''
669 '''return parent filectxs, following copies if necessary'''
670 def filenode(ctx, path):
670 def filenode(ctx, path):
671 return ctx._manifest.get(path, nullid)
671 return ctx._manifest.get(path, nullid)
672
672
673 path = self._path
673 path = self._path
674 fl = self._filelog
674 fl = self._filelog
675 pcl = self._changectx._parents
675 pcl = self._changectx._parents
676 renamed = self.renamed()
676 renamed = self.renamed()
677
677
678 if renamed:
678 if renamed:
679 pl = [renamed + (None,)]
679 pl = [renamed + (None,)]
680 else:
680 else:
681 pl = [(path, filenode(pcl[0], path), fl)]
681 pl = [(path, filenode(pcl[0], path), fl)]
682
682
683 for pc in pcl[1:]:
683 for pc in pcl[1:]:
684 pl.append((path, filenode(pc, path), fl))
684 pl.append((path, filenode(pc, path), fl))
685
685
686 return [filectx(self._repo, p, fileid=n, filelog=l)
686 return [filectx(self._repo, p, fileid=n, filelog=l)
687 for p,n,l in pl if n != nullid]
687 for p,n,l in pl if n != nullid]
688
688
689 def children(self):
689 def children(self):
690 return []
690 return []
691
691
692 def size(self): return os.stat(self._repo.wjoin(self._path)).st_size
692 def size(self): return os.stat(self._repo.wjoin(self._path)).st_size
693 def date(self):
693 def date(self):
694 t, tz = self._changectx.date()
694 t, tz = self._changectx.date()
695 try:
695 try:
696 return (int(os.lstat(self._repo.wjoin(self._path)).st_mtime), tz)
696 return (int(os.lstat(self._repo.wjoin(self._path)).st_mtime), tz)
697 except OSError, err:
697 except OSError, err:
698 if err.errno != errno.ENOENT: raise
698 if err.errno != errno.ENOENT: raise
699 return (t, tz)
699 return (t, tz)
700
700
701 def cmp(self, text): return self._repo.wread(self._path) == text
701 def cmp(self, text): return self._repo.wread(self._path) == text
702
702
703 class memctx(object):
703 class memctx(object):
704 """Use memctx to perform in-memory commits via localrepo.commitctx().
704 """Use memctx to perform in-memory commits via localrepo.commitctx().
705
705
706 Revision information is supplied at initialization time while
706 Revision information is supplied at initialization time while
707 related files data and is made available through a callback
707 related files data and is made available through a callback
708 mechanism. 'repo' is the current localrepo, 'parents' is a
708 mechanism. 'repo' is the current localrepo, 'parents' is a
709 sequence of two parent revisions identifiers (pass None for every
709 sequence of two parent revisions identifiers (pass None for every
710 missing parent), 'text' is the commit message and 'files' lists
710 missing parent), 'text' is the commit message and 'files' lists
711 names of files touched by the revision (normalized and relative to
711 names of files touched by the revision (normalized and relative to
712 repository root).
712 repository root).
713
713
714 filectxfn(repo, memctx, path) is a callable receiving the
714 filectxfn(repo, memctx, path) is a callable receiving the
715 repository, the current memctx object and the normalized path of
715 repository, the current memctx object and the normalized path of
716 requested file, relative to repository root. It is fired by the
716 requested file, relative to repository root. It is fired by the
717 commit function for every file in 'files', but calls order is
717 commit function for every file in 'files', but calls order is
718 undefined. If the file is available in the revision being
718 undefined. If the file is available in the revision being
719 committed (updated or added), filectxfn returns a memfilectx
719 committed (updated or added), filectxfn returns a memfilectx
720 object. If the file was removed, filectxfn raises an
720 object. If the file was removed, filectxfn raises an
721 IOError. Moved files are represented by marking the source file
721 IOError. Moved files are represented by marking the source file
722 removed and the new file added with copy information (see
722 removed and the new file added with copy information (see
723 memfilectx).
723 memfilectx).
724
724
725 user receives the committer name and defaults to current
725 user receives the committer name and defaults to current
726 repository username, date is the commit date in any format
726 repository username, date is the commit date in any format
727 supported by util.parsedate() and defaults to current date, extra
727 supported by util.parsedate() and defaults to current date, extra
728 is a dictionary of metadata or is left empty.
728 is a dictionary of metadata or is left empty.
729 """
729 """
730 def __init__(self, repo, parents, text, files, filectxfn, user=None,
730 def __init__(self, repo, parents, text, files, filectxfn, user=None,
731 date=None, extra=None):
731 date=None, extra=None):
732 self._repo = repo
732 self._repo = repo
733 self._rev = None
733 self._rev = None
734 self._node = None
734 self._node = None
735 self._text = text
735 self._text = text
736 self._date = date and util.parsedate(date) or util.makedate()
736 self._date = date and util.parsedate(date) or util.makedate()
737 self._user = user
737 self._user = user
738 parents = [(p or nullid) for p in parents]
738 parents = [(p or nullid) for p in parents]
739 p1, p2 = parents
739 p1, p2 = parents
740 self._parents = [changectx(self._repo, p) for p in (p1, p2)]
740 self._parents = [changectx(self._repo, p) for p in (p1, p2)]
741 files = sorted(set(files))
741 files = sorted(set(files))
742 self._status = [files, [], [], [], []]
742 self._status = [files, [], [], [], []]
743 self._filectxfn = filectxfn
743 self._filectxfn = filectxfn
744
744
745 self._extra = extra and extra.copy() or {}
745 self._extra = extra and extra.copy() or {}
746 if 'branch' not in self._extra:
746 if 'branch' not in self._extra:
747 self._extra['branch'] = 'default'
747 self._extra['branch'] = 'default'
748 elif self._extra.get('branch') == '':
748 elif self._extra.get('branch') == '':
749 self._extra['branch'] = 'default'
749 self._extra['branch'] = 'default'
750
750
751 def __str__(self):
751 def __str__(self):
752 return str(self._parents[0]) + "+"
752 return str(self._parents[0]) + "+"
753
753
754 def __int__(self):
754 def __int__(self):
755 return self._rev
755 return self._rev
756
756
757 def __nonzero__(self):
757 def __nonzero__(self):
758 return True
758 return True
759
759
760 def __getitem__(self, key):
760 def __getitem__(self, key):
761 return self.filectx(key)
761 return self.filectx(key)
762
762
763 def p1(self): return self._parents[0]
763 def p1(self): return self._parents[0]
764 def p2(self): return self._parents[1]
764 def p2(self): return self._parents[1]
765
765
766 def user(self): return self._user or self._repo.ui.username()
766 def user(self): return self._user or self._repo.ui.username()
767 def date(self): return self._date
767 def date(self): return self._date
768 def description(self): return self._text
768 def description(self): return self._text
769 def files(self): return self.modified()
769 def files(self): return self.modified()
770 def modified(self): return self._status[0]
770 def modified(self): return self._status[0]
771 def added(self): return self._status[1]
771 def added(self): return self._status[1]
772 def removed(self): return self._status[2]
772 def removed(self): return self._status[2]
773 def deleted(self): return self._status[3]
773 def deleted(self): return self._status[3]
774 def unknown(self): return self._status[4]
774 def unknown(self): return self._status[4]
775 def clean(self): return self._status[5]
775 def clean(self): return self._status[5]
776 def branch(self): return self._extra['branch']
776 def branch(self): return self._extra['branch']
777 def extra(self): return self._extra
777 def extra(self): return self._extra
778 def flags(self, f): return self[f].flags()
778 def flags(self, f): return self[f].flags()
779
779
780 def parents(self):
780 def parents(self):
781 """return contexts for each parent changeset"""
781 """return contexts for each parent changeset"""
782 return self._parents
782 return self._parents
783
783
784 def filectx(self, path, filelog=None):
784 def filectx(self, path, filelog=None):
785 """get a file context from the working directory"""
785 """get a file context from the working directory"""
786 return self._filectxfn(self._repo, self, path)
786 return self._filectxfn(self._repo, self, path)
787
787
788 class memfilectx(object):
788 class memfilectx(object):
789 """memfilectx represents an in-memory file to commit.
789 """memfilectx represents an in-memory file to commit.
790
790
791 See memctx for more details.
791 See memctx for more details.
792 """
792 """
793 def __init__(self, path, data, islink, isexec, copied):
793 def __init__(self, path, data, islink, isexec, copied):
794 """
794 """
795 path is the normalized file path relative to repository root.
795 path is the normalized file path relative to repository root.
796 data is the file content as a string.
796 data is the file content as a string.
797 islink is True if the file is a symbolic link.
797 islink is True if the file is a symbolic link.
798 isexec is True if the file is executable.
798 isexec is True if the file is executable.
799 copied is the source file path if current file was copied in the
799 copied is the source file path if current file was copied in the
800 revision being committed, or None."""
800 revision being committed, or None."""
801 self._path = path
801 self._path = path
802 self._data = data
802 self._data = data
803 self._flags = (islink and 'l' or '') + (isexec and 'x' or '')
803 self._flags = (islink and 'l' or '') + (isexec and 'x' or '')
804 self._copied = None
804 self._copied = None
805 if copied:
805 if copied:
806 self._copied = (copied, nullid)
806 self._copied = (copied, nullid)
807
807
808 def __nonzero__(self): return True
808 def __nonzero__(self): return True
809 def __str__(self): return "%s@%s" % (self.path(), self._changectx)
809 def __str__(self): return "%s@%s" % (self.path(), self._changectx)
810 def path(self): return self._path
810 def path(self): return self._path
811 def data(self): return self._data
811 def data(self): return self._data
812 def flags(self): return self._flags
812 def flags(self): return self._flags
813 def isexec(self): return 'x' in self._flags
813 def isexec(self): return 'x' in self._flags
814 def islink(self): return 'l' in self._flags
814 def islink(self): return 'l' in self._flags
815 def renamed(self): return self._copied
815 def renamed(self): return self._copied
@@ -1,233 +1,233 b''
1 # copies.py - copy detection for Mercurial
1 # copies.py - copy detection for Mercurial
2 #
2 #
3 # Copyright 2008 Matt Mackall <mpm@selenic.com>
3 # Copyright 2008 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2, incorporated herein by reference.
6 # GNU General Public License version 2, incorporated herein by reference.
7
7
8 from i18n import _
8 from i18n import _
9 import util
9 import util
10 import heapq
10 import heapq
11
11
12 def _nonoverlap(d1, d2, d3):
12 def _nonoverlap(d1, d2, d3):
13 "Return list of elements in d1 not in d2 or d3"
13 "Return list of elements in d1 not in d2 or d3"
14 return sorted([d for d in d1 if d not in d3 and d not in d2])
14 return sorted([d for d in d1 if d not in d3 and d not in d2])
15
15
16 def _dirname(f):
16 def _dirname(f):
17 s = f.rfind("/")
17 s = f.rfind("/")
18 if s == -1:
18 if s == -1:
19 return ""
19 return ""
20 return f[:s]
20 return f[:s]
21
21
22 def _dirs(files):
22 def _dirs(files):
23 d = set()
23 d = set()
24 for f in files:
24 for f in files:
25 f = _dirname(f)
25 f = _dirname(f)
26 while f not in d:
26 while f not in d:
27 d.add(f)
27 d.add(f)
28 f = _dirname(f)
28 f = _dirname(f)
29 return d
29 return d
30
30
31 def _findoldnames(fctx, limit):
31 def _findoldnames(fctx, limit):
32 "find files that path was copied from, back to linkrev limit"
32 "find files that path was copied from, back to linkrev limit"
33 old = {}
33 old = {}
34 seen = set()
34 seen = set()
35 orig = fctx.path()
35 orig = fctx.path()
36 visit = [(fctx, 0)]
36 visit = [(fctx, 0)]
37 while visit:
37 while visit:
38 fc, depth = visit.pop()
38 fc, depth = visit.pop()
39 s = str(fc)
39 s = str(fc)
40 if s in seen:
40 if s in seen:
41 continue
41 continue
42 seen.add(s)
42 seen.add(s)
43 if fc.path() != orig and fc.path() not in old:
43 if fc.path() != orig and fc.path() not in old:
44 old[fc.path()] = (depth, fc.path()) # remember depth
44 old[fc.path()] = (depth, fc.path()) # remember depth
45 if fc.rev() < limit and fc.rev() is not None:
45 if fc.rev() < limit and fc.rev() is not None:
46 continue
46 continue
47 visit += [(p, depth - 1) for p in fc.parents()]
47 visit += [(p, depth - 1) for p in fc.parents()]
48
48
49 # return old names sorted by depth
49 # return old names sorted by depth
50 return [o[1] for o in sorted(old.values())]
50 return [o[1] for o in sorted(old.values())]
51
51
52 def _findlimit(repo, a, b):
52 def _findlimit(repo, a, b):
53 "find the earliest revision that's an ancestor of a or b but not both"
53 "find the earliest revision that's an ancestor of a or b but not both"
54 # basic idea:
54 # basic idea:
55 # - mark a and b with different sides
55 # - mark a and b with different sides
56 # - if a parent's children are all on the same side, the parent is
56 # - if a parent's children are all on the same side, the parent is
57 # on that side, otherwise it is on no side
57 # on that side, otherwise it is on no side
58 # - walk the graph in topological order with the help of a heap;
58 # - walk the graph in topological order with the help of a heap;
59 # - add unseen parents to side map
59 # - add unseen parents to side map
60 # - clear side of any parent that has children on different sides
60 # - clear side of any parent that has children on different sides
61 # - track number of interesting revs that might still be on a side
61 # - track number of interesting revs that might still be on a side
62 # - track the lowest interesting rev seen
62 # - track the lowest interesting rev seen
63 # - quit when interesting revs is zero
63 # - quit when interesting revs is zero
64
64
65 cl = repo.changelog
65 cl = repo.changelog
66 working = len(cl) # pseudo rev for the working directory
66 working = len(cl) # pseudo rev for the working directory
67 if a is None:
67 if a is None:
68 a = working
68 a = working
69 if b is None:
69 if b is None:
70 b = working
70 b = working
71
71
72 side = {a: -1, b: 1}
72 side = {a: -1, b: 1}
73 visit = [-a, -b]
73 visit = [-a, -b]
74 heapq.heapify(visit)
74 heapq.heapify(visit)
75 interesting = len(visit)
75 interesting = len(visit)
76 limit = working
76 limit = working
77
77
78 while interesting:
78 while interesting:
79 r = -heapq.heappop(visit)
79 r = -heapq.heappop(visit)
80 if r == working:
80 if r == working:
81 parents = [cl.rev(p) for p in repo.dirstate.parents()]
81 parents = [cl.rev(p) for p in repo.dirstate.parents()]
82 else:
82 else:
83 parents = cl.parentrevs(r)
83 parents = cl.parentrevs(r)
84 for p in parents:
84 for p in parents:
85 if p not in side:
85 if p not in side:
86 # first time we see p; add it to visit
86 # first time we see p; add it to visit
87 side[p] = side[r]
87 side[p] = side[r]
88 if side[p]:
88 if side[p]:
89 interesting += 1
89 interesting += 1
90 heapq.heappush(visit, -p)
90 heapq.heappush(visit, -p)
91 elif side[p] and side[p] != side[r]:
91 elif side[p] and side[p] != side[r]:
92 # p was interesting but now we know better
92 # p was interesting but now we know better
93 side[p] = 0
93 side[p] = 0
94 interesting -= 1
94 interesting -= 1
95 if side[r]:
95 if side[r]:
96 limit = r # lowest rev visited
96 limit = r # lowest rev visited
97 interesting -= 1
97 interesting -= 1
98 return limit
98 return limit
99
99
100 def copies(repo, c1, c2, ca, checkdirs=False):
100 def copies(repo, c1, c2, ca, checkdirs=False):
101 """
101 """
102 Find moves and copies between context c1 and c2
102 Find moves and copies between context c1 and c2
103 """
103 """
104 # avoid silly behavior for update from empty dir
104 # avoid silly behavior for update from empty dir
105 if not c1 or not c2 or c1 == c2:
105 if not c1 or not c2 or c1 == c2:
106 return {}, {}
106 return {}, {}
107
107
108 # avoid silly behavior for parent -> working dir
108 # avoid silly behavior for parent -> working dir
109 if c2.node() is None and c1.node() == repo.dirstate.parents()[0]:
109 if c2.node() is None and c1.node() == repo.dirstate.parents()[0]:
110 return repo.dirstate.copies(), {}
110 return repo.dirstate.copies(), {}
111
111
112 limit = _findlimit(repo, c1.rev(), c2.rev())
112 limit = _findlimit(repo, c1.rev(), c2.rev())
113 m1 = c1.manifest()
113 m1 = c1.manifest()
114 m2 = c2.manifest()
114 m2 = c2.manifest()
115 ma = ca.manifest()
115 ma = ca.manifest()
116
116
117 def makectx(f, n):
117 def makectx(f, n):
118 if len(n) != 20: # in a working context?
118 if len(n) != 20: # in a working context?
119 if c1.rev() is None:
119 if c1.rev() is None:
120 return c1.filectx(f)
120 return c1.filectx(f)
121 return c2.filectx(f)
121 return c2.filectx(f)
122 return repo.filectx(f, fileid=n)
122 return repo.filectx(f, fileid=n)
123 ctx = util.cachefunc(makectx)
124
123
124 ctx = util.lrucachefunc(makectx)
125 copy = {}
125 copy = {}
126 fullcopy = {}
126 fullcopy = {}
127 diverge = {}
127 diverge = {}
128
128
129 def checkcopies(f, m1, m2):
129 def checkcopies(f, m1, m2):
130 '''check possible copies of f from m1 to m2'''
130 '''check possible copies of f from m1 to m2'''
131 c1 = ctx(f, m1[f])
131 c1 = ctx(f, m1[f])
132 for of in _findoldnames(c1, limit):
132 for of in _findoldnames(c1, limit):
133 fullcopy[f] = of # remember for dir rename detection
133 fullcopy[f] = of # remember for dir rename detection
134 if of in m2: # original file not in other manifest?
134 if of in m2: # original file not in other manifest?
135 # if the original file is unchanged on the other branch,
135 # if the original file is unchanged on the other branch,
136 # no merge needed
136 # no merge needed
137 if m2[of] != ma.get(of):
137 if m2[of] != ma.get(of):
138 c2 = ctx(of, m2[of])
138 c2 = ctx(of, m2[of])
139 ca = c1.ancestor(c2)
139 ca = c1.ancestor(c2)
140 # related and named changed on only one side?
140 # related and named changed on only one side?
141 if ca and (ca.path() == f or ca.path() == c2.path()):
141 if ca and (ca.path() == f or ca.path() == c2.path()):
142 if c1 != ca or c2 != ca: # merge needed?
142 if c1 != ca or c2 != ca: # merge needed?
143 copy[f] = of
143 copy[f] = of
144 elif of in ma:
144 elif of in ma:
145 diverge.setdefault(of, []).append(f)
145 diverge.setdefault(of, []).append(f)
146
146
147 repo.ui.debug(_(" searching for copies back to rev %d\n") % limit)
147 repo.ui.debug(_(" searching for copies back to rev %d\n") % limit)
148
148
149 u1 = _nonoverlap(m1, m2, ma)
149 u1 = _nonoverlap(m1, m2, ma)
150 u2 = _nonoverlap(m2, m1, ma)
150 u2 = _nonoverlap(m2, m1, ma)
151
151
152 if u1:
152 if u1:
153 repo.ui.debug(_(" unmatched files in local:\n %s\n")
153 repo.ui.debug(_(" unmatched files in local:\n %s\n")
154 % "\n ".join(u1))
154 % "\n ".join(u1))
155 if u2:
155 if u2:
156 repo.ui.debug(_(" unmatched files in other:\n %s\n")
156 repo.ui.debug(_(" unmatched files in other:\n %s\n")
157 % "\n ".join(u2))
157 % "\n ".join(u2))
158
158
159 for f in u1:
159 for f in u1:
160 checkcopies(f, m1, m2)
160 checkcopies(f, m1, m2)
161 for f in u2:
161 for f in u2:
162 checkcopies(f, m2, m1)
162 checkcopies(f, m2, m1)
163
163
164 diverge2 = set()
164 diverge2 = set()
165 for of, fl in diverge.items():
165 for of, fl in diverge.items():
166 if len(fl) == 1:
166 if len(fl) == 1:
167 del diverge[of] # not actually divergent
167 del diverge[of] # not actually divergent
168 else:
168 else:
169 diverge2.update(fl) # reverse map for below
169 diverge2.update(fl) # reverse map for below
170
170
171 if fullcopy:
171 if fullcopy:
172 repo.ui.debug(_(" all copies found (* = to merge, ! = divergent):\n"))
172 repo.ui.debug(_(" all copies found (* = to merge, ! = divergent):\n"))
173 for f in fullcopy:
173 for f in fullcopy:
174 note = ""
174 note = ""
175 if f in copy: note += "*"
175 if f in copy: note += "*"
176 if f in diverge2: note += "!"
176 if f in diverge2: note += "!"
177 repo.ui.debug(" %s -> %s %s\n" % (f, fullcopy[f], note))
177 repo.ui.debug(" %s -> %s %s\n" % (f, fullcopy[f], note))
178 del diverge2
178 del diverge2
179
179
180 if not fullcopy or not checkdirs:
180 if not fullcopy or not checkdirs:
181 return copy, diverge
181 return copy, diverge
182
182
183 repo.ui.debug(_(" checking for directory renames\n"))
183 repo.ui.debug(_(" checking for directory renames\n"))
184
184
185 # generate a directory move map
185 # generate a directory move map
186 d1, d2 = _dirs(m1), _dirs(m2)
186 d1, d2 = _dirs(m1), _dirs(m2)
187 invalid = set()
187 invalid = set()
188 dirmove = {}
188 dirmove = {}
189
189
190 # examine each file copy for a potential directory move, which is
190 # examine each file copy for a potential directory move, which is
191 # when all the files in a directory are moved to a new directory
191 # when all the files in a directory are moved to a new directory
192 for dst, src in fullcopy.iteritems():
192 for dst, src in fullcopy.iteritems():
193 dsrc, ddst = _dirname(src), _dirname(dst)
193 dsrc, ddst = _dirname(src), _dirname(dst)
194 if dsrc in invalid:
194 if dsrc in invalid:
195 # already seen to be uninteresting
195 # already seen to be uninteresting
196 continue
196 continue
197 elif dsrc in d1 and ddst in d1:
197 elif dsrc in d1 and ddst in d1:
198 # directory wasn't entirely moved locally
198 # directory wasn't entirely moved locally
199 invalid.add(dsrc)
199 invalid.add(dsrc)
200 elif dsrc in d2 and ddst in d2:
200 elif dsrc in d2 and ddst in d2:
201 # directory wasn't entirely moved remotely
201 # directory wasn't entirely moved remotely
202 invalid.add(dsrc)
202 invalid.add(dsrc)
203 elif dsrc in dirmove and dirmove[dsrc] != ddst:
203 elif dsrc in dirmove and dirmove[dsrc] != ddst:
204 # files from the same directory moved to two different places
204 # files from the same directory moved to two different places
205 invalid.add(dsrc)
205 invalid.add(dsrc)
206 else:
206 else:
207 # looks good so far
207 # looks good so far
208 dirmove[dsrc + "/"] = ddst + "/"
208 dirmove[dsrc + "/"] = ddst + "/"
209
209
210 for i in invalid:
210 for i in invalid:
211 if i in dirmove:
211 if i in dirmove:
212 del dirmove[i]
212 del dirmove[i]
213 del d1, d2, invalid
213 del d1, d2, invalid
214
214
215 if not dirmove:
215 if not dirmove:
216 return copy, diverge
216 return copy, diverge
217
217
218 for d in dirmove:
218 for d in dirmove:
219 repo.ui.debug(_(" dir %s -> %s\n") % (d, dirmove[d]))
219 repo.ui.debug(_(" dir %s -> %s\n") % (d, dirmove[d]))
220
220
221 # check unaccounted nonoverlapping files against directory moves
221 # check unaccounted nonoverlapping files against directory moves
222 for f in u1 + u2:
222 for f in u1 + u2:
223 if f not in fullcopy:
223 if f not in fullcopy:
224 for d in dirmove:
224 for d in dirmove:
225 if f.startswith(d):
225 if f.startswith(d):
226 # new file added in a directory that was moved, move it
226 # new file added in a directory that was moved, move it
227 df = dirmove[d] + f[len(d):]
227 df = dirmove[d] + f[len(d):]
228 if df not in copy:
228 if df not in copy:
229 copy[f] = df
229 copy[f] = df
230 repo.ui.debug(_(" file %s -> %s\n") % (f, copy[f]))
230 repo.ui.debug(_(" file %s -> %s\n") % (f, copy[f]))
231 break
231 break
232
232
233 return copy, diverge
233 return copy, diverge
@@ -1,1257 +1,1284 b''
1 # util.py - Mercurial utility functions and platform specfic implementations
1 # util.py - Mercurial utility functions and platform specfic implementations
2 #
2 #
3 # Copyright 2005 K. Thananchayan <thananck@yahoo.com>
3 # Copyright 2005 K. Thananchayan <thananck@yahoo.com>
4 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
5 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
5 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
6 #
6 #
7 # This software may be used and distributed according to the terms of the
7 # This software may be used and distributed according to the terms of the
8 # GNU General Public License version 2, incorporated herein by reference.
8 # GNU General Public License version 2, incorporated herein by reference.
9
9
10 """Mercurial utility functions and platform specfic implementations.
10 """Mercurial utility functions and platform specfic implementations.
11
11
12 This contains helper routines that are independent of the SCM core and
12 This contains helper routines that are independent of the SCM core and
13 hide platform-specific details from the core.
13 hide platform-specific details from the core.
14 """
14 """
15
15
16 from i18n import _
16 from i18n import _
17 import error, osutil
17 import error, osutil
18 import cStringIO, errno, re, shutil, sys, tempfile, traceback
18 import cStringIO, errno, re, shutil, sys, tempfile, traceback
19 import os, stat, time, calendar, random, textwrap
19 import os, stat, time, calendar, random, textwrap
20 import imp
20 import imp
21
21
22 # Python compatibility
22 # Python compatibility
23
23
24 def sha1(s):
24 def sha1(s):
25 return _fastsha1(s)
25 return _fastsha1(s)
26
26
27 def _fastsha1(s):
27 def _fastsha1(s):
28 # This function will import sha1 from hashlib or sha (whichever is
28 # This function will import sha1 from hashlib or sha (whichever is
29 # available) and overwrite itself with it on the first call.
29 # available) and overwrite itself with it on the first call.
30 # Subsequent calls will go directly to the imported function.
30 # Subsequent calls will go directly to the imported function.
31 try:
31 try:
32 from hashlib import sha1 as _sha1
32 from hashlib import sha1 as _sha1
33 except ImportError:
33 except ImportError:
34 from sha import sha as _sha1
34 from sha import sha as _sha1
35 global _fastsha1, sha1
35 global _fastsha1, sha1
36 _fastsha1 = sha1 = _sha1
36 _fastsha1 = sha1 = _sha1
37 return _sha1(s)
37 return _sha1(s)
38
38
39 import subprocess
39 import subprocess
40 closefds = os.name == 'posix'
40 closefds = os.name == 'posix'
41 def popen2(cmd):
41 def popen2(cmd):
42 # Setting bufsize to -1 lets the system decide the buffer size.
42 # Setting bufsize to -1 lets the system decide the buffer size.
43 # The default for bufsize is 0, meaning unbuffered. This leads to
43 # The default for bufsize is 0, meaning unbuffered. This leads to
44 # poor performance on Mac OS X: http://bugs.python.org/issue4194
44 # poor performance on Mac OS X: http://bugs.python.org/issue4194
45 p = subprocess.Popen(cmd, shell=True, bufsize=-1,
45 p = subprocess.Popen(cmd, shell=True, bufsize=-1,
46 close_fds=closefds,
46 close_fds=closefds,
47 stdin=subprocess.PIPE, stdout=subprocess.PIPE)
47 stdin=subprocess.PIPE, stdout=subprocess.PIPE)
48 return p.stdin, p.stdout
48 return p.stdin, p.stdout
49 def popen3(cmd):
49 def popen3(cmd):
50 p = subprocess.Popen(cmd, shell=True, bufsize=-1,
50 p = subprocess.Popen(cmd, shell=True, bufsize=-1,
51 close_fds=closefds,
51 close_fds=closefds,
52 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
52 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
53 stderr=subprocess.PIPE)
53 stderr=subprocess.PIPE)
54 return p.stdin, p.stdout, p.stderr
54 return p.stdin, p.stdout, p.stderr
55
55
56 def version():
56 def version():
57 """Return version information if available."""
57 """Return version information if available."""
58 try:
58 try:
59 import __version__
59 import __version__
60 return __version__.version
60 return __version__.version
61 except ImportError:
61 except ImportError:
62 return 'unknown'
62 return 'unknown'
63
63
64 # used by parsedate
64 # used by parsedate
65 defaultdateformats = (
65 defaultdateformats = (
66 '%Y-%m-%d %H:%M:%S',
66 '%Y-%m-%d %H:%M:%S',
67 '%Y-%m-%d %I:%M:%S%p',
67 '%Y-%m-%d %I:%M:%S%p',
68 '%Y-%m-%d %H:%M',
68 '%Y-%m-%d %H:%M',
69 '%Y-%m-%d %I:%M%p',
69 '%Y-%m-%d %I:%M%p',
70 '%Y-%m-%d',
70 '%Y-%m-%d',
71 '%m-%d',
71 '%m-%d',
72 '%m/%d',
72 '%m/%d',
73 '%m/%d/%y',
73 '%m/%d/%y',
74 '%m/%d/%Y',
74 '%m/%d/%Y',
75 '%a %b %d %H:%M:%S %Y',
75 '%a %b %d %H:%M:%S %Y',
76 '%a %b %d %I:%M:%S%p %Y',
76 '%a %b %d %I:%M:%S%p %Y',
77 '%a, %d %b %Y %H:%M:%S', # GNU coreutils "/bin/date --rfc-2822"
77 '%a, %d %b %Y %H:%M:%S', # GNU coreutils "/bin/date --rfc-2822"
78 '%b %d %H:%M:%S %Y',
78 '%b %d %H:%M:%S %Y',
79 '%b %d %I:%M:%S%p %Y',
79 '%b %d %I:%M:%S%p %Y',
80 '%b %d %H:%M:%S',
80 '%b %d %H:%M:%S',
81 '%b %d %I:%M:%S%p',
81 '%b %d %I:%M:%S%p',
82 '%b %d %H:%M',
82 '%b %d %H:%M',
83 '%b %d %I:%M%p',
83 '%b %d %I:%M%p',
84 '%b %d %Y',
84 '%b %d %Y',
85 '%b %d',
85 '%b %d',
86 '%H:%M:%S',
86 '%H:%M:%S',
87 '%I:%M:%SP',
87 '%I:%M:%SP',
88 '%H:%M',
88 '%H:%M',
89 '%I:%M%p',
89 '%I:%M%p',
90 )
90 )
91
91
92 extendeddateformats = defaultdateformats + (
92 extendeddateformats = defaultdateformats + (
93 "%Y",
93 "%Y",
94 "%Y-%m",
94 "%Y-%m",
95 "%b",
95 "%b",
96 "%b %Y",
96 "%b %Y",
97 )
97 )
98
98
99 def cachefunc(func):
99 def cachefunc(func):
100 '''cache the result of function calls'''
100 '''cache the result of function calls'''
101 # XXX doesn't handle keywords args
101 # XXX doesn't handle keywords args
102 cache = {}
102 cache = {}
103 if func.func_code.co_argcount == 1:
103 if func.func_code.co_argcount == 1:
104 # we gain a small amount of time because
104 # we gain a small amount of time because
105 # we don't need to pack/unpack the list
105 # we don't need to pack/unpack the list
106 def f(arg):
106 def f(arg):
107 if arg not in cache:
107 if arg not in cache:
108 cache[arg] = func(arg)
108 cache[arg] = func(arg)
109 return cache[arg]
109 return cache[arg]
110 else:
110 else:
111 def f(*args):
111 def f(*args):
112 if args not in cache:
112 if args not in cache:
113 cache[args] = func(*args)
113 cache[args] = func(*args)
114 return cache[args]
114 return cache[args]
115
115
116 return f
116 return f
117
117
118 def lrucachefunc(func):
119 '''cache most recent results of function calls'''
120 cache = {}
121 order = []
122 if func.func_code.co_argcount == 1:
123 def f(arg):
124 if arg not in cache:
125 if len(cache) > 20:
126 del cache[order.pop(0)]
127 cache[arg] = func(arg)
128 else:
129 order.remove(arg)
130 order.append(arg)
131 return cache[arg]
132 else:
133 def f(*args):
134 if args not in cache:
135 if len(cache) > 20:
136 del cache[order.pop(0)]
137 cache[args] = func(*args)
138 else:
139 order.remove(args)
140 order.append(args)
141 return cache[args]
142
143 return f
144
118 class propertycache(object):
145 class propertycache(object):
119 def __init__(self, func):
146 def __init__(self, func):
120 self.func = func
147 self.func = func
121 self.name = func.__name__
148 self.name = func.__name__
122 def __get__(self, obj, type=None):
149 def __get__(self, obj, type=None):
123 result = self.func(obj)
150 result = self.func(obj)
124 setattr(obj, self.name, result)
151 setattr(obj, self.name, result)
125 return result
152 return result
126
153
127 def pipefilter(s, cmd):
154 def pipefilter(s, cmd):
128 '''filter string S through command CMD, returning its output'''
155 '''filter string S through command CMD, returning its output'''
129 p = subprocess.Popen(cmd, shell=True, close_fds=closefds,
156 p = subprocess.Popen(cmd, shell=True, close_fds=closefds,
130 stdin=subprocess.PIPE, stdout=subprocess.PIPE)
157 stdin=subprocess.PIPE, stdout=subprocess.PIPE)
131 pout, perr = p.communicate(s)
158 pout, perr = p.communicate(s)
132 return pout
159 return pout
133
160
134 def tempfilter(s, cmd):
161 def tempfilter(s, cmd):
135 '''filter string S through a pair of temporary files with CMD.
162 '''filter string S through a pair of temporary files with CMD.
136 CMD is used as a template to create the real command to be run,
163 CMD is used as a template to create the real command to be run,
137 with the strings INFILE and OUTFILE replaced by the real names of
164 with the strings INFILE and OUTFILE replaced by the real names of
138 the temporary files generated.'''
165 the temporary files generated.'''
139 inname, outname = None, None
166 inname, outname = None, None
140 try:
167 try:
141 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
168 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
142 fp = os.fdopen(infd, 'wb')
169 fp = os.fdopen(infd, 'wb')
143 fp.write(s)
170 fp.write(s)
144 fp.close()
171 fp.close()
145 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
172 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
146 os.close(outfd)
173 os.close(outfd)
147 cmd = cmd.replace('INFILE', inname)
174 cmd = cmd.replace('INFILE', inname)
148 cmd = cmd.replace('OUTFILE', outname)
175 cmd = cmd.replace('OUTFILE', outname)
149 code = os.system(cmd)
176 code = os.system(cmd)
150 if sys.platform == 'OpenVMS' and code & 1:
177 if sys.platform == 'OpenVMS' and code & 1:
151 code = 0
178 code = 0
152 if code: raise Abort(_("command '%s' failed: %s") %
179 if code: raise Abort(_("command '%s' failed: %s") %
153 (cmd, explain_exit(code)))
180 (cmd, explain_exit(code)))
154 return open(outname, 'rb').read()
181 return open(outname, 'rb').read()
155 finally:
182 finally:
156 try:
183 try:
157 if inname: os.unlink(inname)
184 if inname: os.unlink(inname)
158 except: pass
185 except: pass
159 try:
186 try:
160 if outname: os.unlink(outname)
187 if outname: os.unlink(outname)
161 except: pass
188 except: pass
162
189
163 filtertable = {
190 filtertable = {
164 'tempfile:': tempfilter,
191 'tempfile:': tempfilter,
165 'pipe:': pipefilter,
192 'pipe:': pipefilter,
166 }
193 }
167
194
168 def filter(s, cmd):
195 def filter(s, cmd):
169 "filter a string through a command that transforms its input to its output"
196 "filter a string through a command that transforms its input to its output"
170 for name, fn in filtertable.iteritems():
197 for name, fn in filtertable.iteritems():
171 if cmd.startswith(name):
198 if cmd.startswith(name):
172 return fn(s, cmd[len(name):].lstrip())
199 return fn(s, cmd[len(name):].lstrip())
173 return pipefilter(s, cmd)
200 return pipefilter(s, cmd)
174
201
175 def binary(s):
202 def binary(s):
176 """return true if a string is binary data"""
203 """return true if a string is binary data"""
177 return bool(s and '\0' in s)
204 return bool(s and '\0' in s)
178
205
179 def increasingchunks(source, min=1024, max=65536):
206 def increasingchunks(source, min=1024, max=65536):
180 '''return no less than min bytes per chunk while data remains,
207 '''return no less than min bytes per chunk while data remains,
181 doubling min after each chunk until it reaches max'''
208 doubling min after each chunk until it reaches max'''
182 def log2(x):
209 def log2(x):
183 if not x:
210 if not x:
184 return 0
211 return 0
185 i = 0
212 i = 0
186 while x:
213 while x:
187 x >>= 1
214 x >>= 1
188 i += 1
215 i += 1
189 return i - 1
216 return i - 1
190
217
191 buf = []
218 buf = []
192 blen = 0
219 blen = 0
193 for chunk in source:
220 for chunk in source:
194 buf.append(chunk)
221 buf.append(chunk)
195 blen += len(chunk)
222 blen += len(chunk)
196 if blen >= min:
223 if blen >= min:
197 if min < max:
224 if min < max:
198 min = min << 1
225 min = min << 1
199 nmin = 1 << log2(blen)
226 nmin = 1 << log2(blen)
200 if nmin > min:
227 if nmin > min:
201 min = nmin
228 min = nmin
202 if min > max:
229 if min > max:
203 min = max
230 min = max
204 yield ''.join(buf)
231 yield ''.join(buf)
205 blen = 0
232 blen = 0
206 buf = []
233 buf = []
207 if buf:
234 if buf:
208 yield ''.join(buf)
235 yield ''.join(buf)
209
236
210 Abort = error.Abort
237 Abort = error.Abort
211
238
212 def always(fn): return True
239 def always(fn): return True
213 def never(fn): return False
240 def never(fn): return False
214
241
215 def pathto(root, n1, n2):
242 def pathto(root, n1, n2):
216 '''return the relative path from one place to another.
243 '''return the relative path from one place to another.
217 root should use os.sep to separate directories
244 root should use os.sep to separate directories
218 n1 should use os.sep to separate directories
245 n1 should use os.sep to separate directories
219 n2 should use "/" to separate directories
246 n2 should use "/" to separate directories
220 returns an os.sep-separated path.
247 returns an os.sep-separated path.
221
248
222 If n1 is a relative path, it's assumed it's
249 If n1 is a relative path, it's assumed it's
223 relative to root.
250 relative to root.
224 n2 should always be relative to root.
251 n2 should always be relative to root.
225 '''
252 '''
226 if not n1: return localpath(n2)
253 if not n1: return localpath(n2)
227 if os.path.isabs(n1):
254 if os.path.isabs(n1):
228 if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]:
255 if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]:
229 return os.path.join(root, localpath(n2))
256 return os.path.join(root, localpath(n2))
230 n2 = '/'.join((pconvert(root), n2))
257 n2 = '/'.join((pconvert(root), n2))
231 a, b = splitpath(n1), n2.split('/')
258 a, b = splitpath(n1), n2.split('/')
232 a.reverse()
259 a.reverse()
233 b.reverse()
260 b.reverse()
234 while a and b and a[-1] == b[-1]:
261 while a and b and a[-1] == b[-1]:
235 a.pop()
262 a.pop()
236 b.pop()
263 b.pop()
237 b.reverse()
264 b.reverse()
238 return os.sep.join((['..'] * len(a)) + b) or '.'
265 return os.sep.join((['..'] * len(a)) + b) or '.'
239
266
240 def canonpath(root, cwd, myname):
267 def canonpath(root, cwd, myname):
241 """return the canonical path of myname, given cwd and root"""
268 """return the canonical path of myname, given cwd and root"""
242 if root == os.sep:
269 if root == os.sep:
243 rootsep = os.sep
270 rootsep = os.sep
244 elif endswithsep(root):
271 elif endswithsep(root):
245 rootsep = root
272 rootsep = root
246 else:
273 else:
247 rootsep = root + os.sep
274 rootsep = root + os.sep
248 name = myname
275 name = myname
249 if not os.path.isabs(name):
276 if not os.path.isabs(name):
250 name = os.path.join(root, cwd, name)
277 name = os.path.join(root, cwd, name)
251 name = os.path.normpath(name)
278 name = os.path.normpath(name)
252 audit_path = path_auditor(root)
279 audit_path = path_auditor(root)
253 if name != rootsep and name.startswith(rootsep):
280 if name != rootsep and name.startswith(rootsep):
254 name = name[len(rootsep):]
281 name = name[len(rootsep):]
255 audit_path(name)
282 audit_path(name)
256 return pconvert(name)
283 return pconvert(name)
257 elif name == root:
284 elif name == root:
258 return ''
285 return ''
259 else:
286 else:
260 # Determine whether `name' is in the hierarchy at or beneath `root',
287 # Determine whether `name' is in the hierarchy at or beneath `root',
261 # by iterating name=dirname(name) until that causes no change (can't
288 # by iterating name=dirname(name) until that causes no change (can't
262 # check name == '/', because that doesn't work on windows). For each
289 # check name == '/', because that doesn't work on windows). For each
263 # `name', compare dev/inode numbers. If they match, the list `rel'
290 # `name', compare dev/inode numbers. If they match, the list `rel'
264 # holds the reversed list of components making up the relative file
291 # holds the reversed list of components making up the relative file
265 # name we want.
292 # name we want.
266 root_st = os.stat(root)
293 root_st = os.stat(root)
267 rel = []
294 rel = []
268 while True:
295 while True:
269 try:
296 try:
270 name_st = os.stat(name)
297 name_st = os.stat(name)
271 except OSError:
298 except OSError:
272 break
299 break
273 if samestat(name_st, root_st):
300 if samestat(name_st, root_st):
274 if not rel:
301 if not rel:
275 # name was actually the same as root (maybe a symlink)
302 # name was actually the same as root (maybe a symlink)
276 return ''
303 return ''
277 rel.reverse()
304 rel.reverse()
278 name = os.path.join(*rel)
305 name = os.path.join(*rel)
279 audit_path(name)
306 audit_path(name)
280 return pconvert(name)
307 return pconvert(name)
281 dirname, basename = os.path.split(name)
308 dirname, basename = os.path.split(name)
282 rel.append(basename)
309 rel.append(basename)
283 if dirname == name:
310 if dirname == name:
284 break
311 break
285 name = dirname
312 name = dirname
286
313
287 raise Abort('%s not under root' % myname)
314 raise Abort('%s not under root' % myname)
288
315
289 _hgexecutable = None
316 _hgexecutable = None
290
317
291 def main_is_frozen():
318 def main_is_frozen():
292 """return True if we are a frozen executable.
319 """return True if we are a frozen executable.
293
320
294 The code supports py2exe (most common, Windows only) and tools/freeze
321 The code supports py2exe (most common, Windows only) and tools/freeze
295 (portable, not much used).
322 (portable, not much used).
296 """
323 """
297 return (hasattr(sys, "frozen") or # new py2exe
324 return (hasattr(sys, "frozen") or # new py2exe
298 hasattr(sys, "importers") or # old py2exe
325 hasattr(sys, "importers") or # old py2exe
299 imp.is_frozen("__main__")) # tools/freeze
326 imp.is_frozen("__main__")) # tools/freeze
300
327
301 def hgexecutable():
328 def hgexecutable():
302 """return location of the 'hg' executable.
329 """return location of the 'hg' executable.
303
330
304 Defaults to $HG or 'hg' in the search path.
331 Defaults to $HG or 'hg' in the search path.
305 """
332 """
306 if _hgexecutable is None:
333 if _hgexecutable is None:
307 hg = os.environ.get('HG')
334 hg = os.environ.get('HG')
308 if hg:
335 if hg:
309 set_hgexecutable(hg)
336 set_hgexecutable(hg)
310 elif main_is_frozen():
337 elif main_is_frozen():
311 set_hgexecutable(sys.executable)
338 set_hgexecutable(sys.executable)
312 else:
339 else:
313 set_hgexecutable(find_exe('hg') or 'hg')
340 set_hgexecutable(find_exe('hg') or 'hg')
314 return _hgexecutable
341 return _hgexecutable
315
342
316 def set_hgexecutable(path):
343 def set_hgexecutable(path):
317 """set location of the 'hg' executable"""
344 """set location of the 'hg' executable"""
318 global _hgexecutable
345 global _hgexecutable
319 _hgexecutable = path
346 _hgexecutable = path
320
347
321 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None):
348 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None):
322 '''enhanced shell command execution.
349 '''enhanced shell command execution.
323 run with environment maybe modified, maybe in different dir.
350 run with environment maybe modified, maybe in different dir.
324
351
325 if command fails and onerr is None, return status. if ui object,
352 if command fails and onerr is None, return status. if ui object,
326 print error message and return status, else raise onerr object as
353 print error message and return status, else raise onerr object as
327 exception.'''
354 exception.'''
328 def py2shell(val):
355 def py2shell(val):
329 'convert python object into string that is useful to shell'
356 'convert python object into string that is useful to shell'
330 if val is None or val is False:
357 if val is None or val is False:
331 return '0'
358 return '0'
332 if val is True:
359 if val is True:
333 return '1'
360 return '1'
334 return str(val)
361 return str(val)
335 oldenv = {}
362 oldenv = {}
336 for k in environ:
363 for k in environ:
337 oldenv[k] = os.environ.get(k)
364 oldenv[k] = os.environ.get(k)
338 if cwd is not None:
365 if cwd is not None:
339 oldcwd = os.getcwd()
366 oldcwd = os.getcwd()
340 origcmd = cmd
367 origcmd = cmd
341 if os.name == 'nt':
368 if os.name == 'nt':
342 cmd = '"%s"' % cmd
369 cmd = '"%s"' % cmd
343 try:
370 try:
344 for k, v in environ.iteritems():
371 for k, v in environ.iteritems():
345 os.environ[k] = py2shell(v)
372 os.environ[k] = py2shell(v)
346 os.environ['HG'] = hgexecutable()
373 os.environ['HG'] = hgexecutable()
347 if cwd is not None and oldcwd != cwd:
374 if cwd is not None and oldcwd != cwd:
348 os.chdir(cwd)
375 os.chdir(cwd)
349 rc = os.system(cmd)
376 rc = os.system(cmd)
350 if sys.platform == 'OpenVMS' and rc & 1:
377 if sys.platform == 'OpenVMS' and rc & 1:
351 rc = 0
378 rc = 0
352 if rc and onerr:
379 if rc and onerr:
353 errmsg = '%s %s' % (os.path.basename(origcmd.split(None, 1)[0]),
380 errmsg = '%s %s' % (os.path.basename(origcmd.split(None, 1)[0]),
354 explain_exit(rc)[0])
381 explain_exit(rc)[0])
355 if errprefix:
382 if errprefix:
356 errmsg = '%s: %s' % (errprefix, errmsg)
383 errmsg = '%s: %s' % (errprefix, errmsg)
357 try:
384 try:
358 onerr.warn(errmsg + '\n')
385 onerr.warn(errmsg + '\n')
359 except AttributeError:
386 except AttributeError:
360 raise onerr(errmsg)
387 raise onerr(errmsg)
361 return rc
388 return rc
362 finally:
389 finally:
363 for k, v in oldenv.iteritems():
390 for k, v in oldenv.iteritems():
364 if v is None:
391 if v is None:
365 del os.environ[k]
392 del os.environ[k]
366 else:
393 else:
367 os.environ[k] = v
394 os.environ[k] = v
368 if cwd is not None and oldcwd != cwd:
395 if cwd is not None and oldcwd != cwd:
369 os.chdir(oldcwd)
396 os.chdir(oldcwd)
370
397
371 def checksignature(func):
398 def checksignature(func):
372 '''wrap a function with code to check for calling errors'''
399 '''wrap a function with code to check for calling errors'''
373 def check(*args, **kwargs):
400 def check(*args, **kwargs):
374 try:
401 try:
375 return func(*args, **kwargs)
402 return func(*args, **kwargs)
376 except TypeError:
403 except TypeError:
377 if len(traceback.extract_tb(sys.exc_info()[2])) == 1:
404 if len(traceback.extract_tb(sys.exc_info()[2])) == 1:
378 raise error.SignatureError
405 raise error.SignatureError
379 raise
406 raise
380
407
381 return check
408 return check
382
409
383 # os.path.lexists is not available on python2.3
410 # os.path.lexists is not available on python2.3
384 def lexists(filename):
411 def lexists(filename):
385 "test whether a file with this name exists. does not follow symlinks"
412 "test whether a file with this name exists. does not follow symlinks"
386 try:
413 try:
387 os.lstat(filename)
414 os.lstat(filename)
388 except:
415 except:
389 return False
416 return False
390 return True
417 return True
391
418
392 def rename(src, dst):
419 def rename(src, dst):
393 """forcibly rename a file"""
420 """forcibly rename a file"""
394 try:
421 try:
395 os.rename(src, dst)
422 os.rename(src, dst)
396 except OSError, err: # FIXME: check err (EEXIST ?)
423 except OSError, err: # FIXME: check err (EEXIST ?)
397
424
398 # On windows, rename to existing file is not allowed, so we
425 # On windows, rename to existing file is not allowed, so we
399 # must delete destination first. But if a file is open, unlink
426 # must delete destination first. But if a file is open, unlink
400 # schedules it for delete but does not delete it. Rename
427 # schedules it for delete but does not delete it. Rename
401 # happens immediately even for open files, so we rename
428 # happens immediately even for open files, so we rename
402 # destination to a temporary name, then delete that. Then
429 # destination to a temporary name, then delete that. Then
403 # rename is safe to do.
430 # rename is safe to do.
404 # The temporary name is chosen at random to avoid the situation
431 # The temporary name is chosen at random to avoid the situation
405 # where a file is left lying around from a previous aborted run.
432 # where a file is left lying around from a previous aborted run.
406 # The usual race condition this introduces can't be avoided as
433 # The usual race condition this introduces can't be avoided as
407 # we need the name to rename into, and not the file itself. Due
434 # we need the name to rename into, and not the file itself. Due
408 # to the nature of the operation however, any races will at worst
435 # to the nature of the operation however, any races will at worst
409 # lead to the rename failing and the current operation aborting.
436 # lead to the rename failing and the current operation aborting.
410
437
411 def tempname(prefix):
438 def tempname(prefix):
412 for tries in xrange(10):
439 for tries in xrange(10):
413 temp = '%s-%08x' % (prefix, random.randint(0, 0xffffffff))
440 temp = '%s-%08x' % (prefix, random.randint(0, 0xffffffff))
414 if not os.path.exists(temp):
441 if not os.path.exists(temp):
415 return temp
442 return temp
416 raise IOError, (errno.EEXIST, "No usable temporary filename found")
443 raise IOError, (errno.EEXIST, "No usable temporary filename found")
417
444
418 temp = tempname(dst)
445 temp = tempname(dst)
419 os.rename(dst, temp)
446 os.rename(dst, temp)
420 os.unlink(temp)
447 os.unlink(temp)
421 os.rename(src, dst)
448 os.rename(src, dst)
422
449
423 def unlink(f):
450 def unlink(f):
424 """unlink and remove the directory if it is empty"""
451 """unlink and remove the directory if it is empty"""
425 os.unlink(f)
452 os.unlink(f)
426 # try removing directories that might now be empty
453 # try removing directories that might now be empty
427 try:
454 try:
428 os.removedirs(os.path.dirname(f))
455 os.removedirs(os.path.dirname(f))
429 except OSError:
456 except OSError:
430 pass
457 pass
431
458
432 def copyfile(src, dest):
459 def copyfile(src, dest):
433 "copy a file, preserving mode and atime/mtime"
460 "copy a file, preserving mode and atime/mtime"
434 if os.path.islink(src):
461 if os.path.islink(src):
435 try:
462 try:
436 os.unlink(dest)
463 os.unlink(dest)
437 except:
464 except:
438 pass
465 pass
439 os.symlink(os.readlink(src), dest)
466 os.symlink(os.readlink(src), dest)
440 else:
467 else:
441 try:
468 try:
442 shutil.copyfile(src, dest)
469 shutil.copyfile(src, dest)
443 shutil.copystat(src, dest)
470 shutil.copystat(src, dest)
444 except shutil.Error, inst:
471 except shutil.Error, inst:
445 raise Abort(str(inst))
472 raise Abort(str(inst))
446
473
447 def copyfiles(src, dst, hardlink=None):
474 def copyfiles(src, dst, hardlink=None):
448 """Copy a directory tree using hardlinks if possible"""
475 """Copy a directory tree using hardlinks if possible"""
449
476
450 if hardlink is None:
477 if hardlink is None:
451 hardlink = (os.stat(src).st_dev ==
478 hardlink = (os.stat(src).st_dev ==
452 os.stat(os.path.dirname(dst)).st_dev)
479 os.stat(os.path.dirname(dst)).st_dev)
453
480
454 if os.path.isdir(src):
481 if os.path.isdir(src):
455 os.mkdir(dst)
482 os.mkdir(dst)
456 for name, kind in osutil.listdir(src):
483 for name, kind in osutil.listdir(src):
457 srcname = os.path.join(src, name)
484 srcname = os.path.join(src, name)
458 dstname = os.path.join(dst, name)
485 dstname = os.path.join(dst, name)
459 copyfiles(srcname, dstname, hardlink)
486 copyfiles(srcname, dstname, hardlink)
460 else:
487 else:
461 if hardlink:
488 if hardlink:
462 try:
489 try:
463 os_link(src, dst)
490 os_link(src, dst)
464 except (IOError, OSError):
491 except (IOError, OSError):
465 hardlink = False
492 hardlink = False
466 shutil.copy(src, dst)
493 shutil.copy(src, dst)
467 else:
494 else:
468 shutil.copy(src, dst)
495 shutil.copy(src, dst)
469
496
470 class path_auditor(object):
497 class path_auditor(object):
471 '''ensure that a filesystem path contains no banned components.
498 '''ensure that a filesystem path contains no banned components.
472 the following properties of a path are checked:
499 the following properties of a path are checked:
473
500
474 - under top-level .hg
501 - under top-level .hg
475 - starts at the root of a windows drive
502 - starts at the root of a windows drive
476 - contains ".."
503 - contains ".."
477 - traverses a symlink (e.g. a/symlink_here/b)
504 - traverses a symlink (e.g. a/symlink_here/b)
478 - inside a nested repository'''
505 - inside a nested repository'''
479
506
480 def __init__(self, root):
507 def __init__(self, root):
481 self.audited = set()
508 self.audited = set()
482 self.auditeddir = set()
509 self.auditeddir = set()
483 self.root = root
510 self.root = root
484
511
485 def __call__(self, path):
512 def __call__(self, path):
486 if path in self.audited:
513 if path in self.audited:
487 return
514 return
488 normpath = os.path.normcase(path)
515 normpath = os.path.normcase(path)
489 parts = splitpath(normpath)
516 parts = splitpath(normpath)
490 if (os.path.splitdrive(path)[0]
517 if (os.path.splitdrive(path)[0]
491 or parts[0].lower() in ('.hg', '.hg.', '')
518 or parts[0].lower() in ('.hg', '.hg.', '')
492 or os.pardir in parts):
519 or os.pardir in parts):
493 raise Abort(_("path contains illegal component: %s") % path)
520 raise Abort(_("path contains illegal component: %s") % path)
494 if '.hg' in path.lower():
521 if '.hg' in path.lower():
495 lparts = [p.lower() for p in parts]
522 lparts = [p.lower() for p in parts]
496 for p in '.hg', '.hg.':
523 for p in '.hg', '.hg.':
497 if p in lparts[1:]:
524 if p in lparts[1:]:
498 pos = lparts.index(p)
525 pos = lparts.index(p)
499 base = os.path.join(*parts[:pos])
526 base = os.path.join(*parts[:pos])
500 raise Abort(_('path %r is inside repo %r') % (path, base))
527 raise Abort(_('path %r is inside repo %r') % (path, base))
501 def check(prefix):
528 def check(prefix):
502 curpath = os.path.join(self.root, prefix)
529 curpath = os.path.join(self.root, prefix)
503 try:
530 try:
504 st = os.lstat(curpath)
531 st = os.lstat(curpath)
505 except OSError, err:
532 except OSError, err:
506 # EINVAL can be raised as invalid path syntax under win32.
533 # EINVAL can be raised as invalid path syntax under win32.
507 # They must be ignored for patterns can be checked too.
534 # They must be ignored for patterns can be checked too.
508 if err.errno not in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL):
535 if err.errno not in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL):
509 raise
536 raise
510 else:
537 else:
511 if stat.S_ISLNK(st.st_mode):
538 if stat.S_ISLNK(st.st_mode):
512 raise Abort(_('path %r traverses symbolic link %r') %
539 raise Abort(_('path %r traverses symbolic link %r') %
513 (path, prefix))
540 (path, prefix))
514 elif (stat.S_ISDIR(st.st_mode) and
541 elif (stat.S_ISDIR(st.st_mode) and
515 os.path.isdir(os.path.join(curpath, '.hg'))):
542 os.path.isdir(os.path.join(curpath, '.hg'))):
516 raise Abort(_('path %r is inside repo %r') %
543 raise Abort(_('path %r is inside repo %r') %
517 (path, prefix))
544 (path, prefix))
518 parts.pop()
545 parts.pop()
519 prefixes = []
546 prefixes = []
520 while parts:
547 while parts:
521 prefix = os.sep.join(parts)
548 prefix = os.sep.join(parts)
522 if prefix in self.auditeddir:
549 if prefix in self.auditeddir:
523 break
550 break
524 check(prefix)
551 check(prefix)
525 prefixes.append(prefix)
552 prefixes.append(prefix)
526 parts.pop()
553 parts.pop()
527
554
528 self.audited.add(path)
555 self.audited.add(path)
529 # only add prefixes to the cache after checking everything: we don't
556 # only add prefixes to the cache after checking everything: we don't
530 # want to add "foo/bar/baz" before checking if there's a "foo/.hg"
557 # want to add "foo/bar/baz" before checking if there's a "foo/.hg"
531 self.auditeddir.update(prefixes)
558 self.auditeddir.update(prefixes)
532
559
533 def nlinks(pathname):
560 def nlinks(pathname):
534 """Return number of hardlinks for the given file."""
561 """Return number of hardlinks for the given file."""
535 return os.lstat(pathname).st_nlink
562 return os.lstat(pathname).st_nlink
536
563
537 if hasattr(os, 'link'):
564 if hasattr(os, 'link'):
538 os_link = os.link
565 os_link = os.link
539 else:
566 else:
540 def os_link(src, dst):
567 def os_link(src, dst):
541 raise OSError(0, _("Hardlinks not supported"))
568 raise OSError(0, _("Hardlinks not supported"))
542
569
543 def lookup_reg(key, name=None, scope=None):
570 def lookup_reg(key, name=None, scope=None):
544 return None
571 return None
545
572
546 if os.name == 'nt':
573 if os.name == 'nt':
547 from windows import *
574 from windows import *
548 else:
575 else:
549 from posix import *
576 from posix import *
550
577
551 def makelock(info, pathname):
578 def makelock(info, pathname):
552 try:
579 try:
553 return os.symlink(info, pathname)
580 return os.symlink(info, pathname)
554 except OSError, why:
581 except OSError, why:
555 if why.errno == errno.EEXIST:
582 if why.errno == errno.EEXIST:
556 raise
583 raise
557 except AttributeError: # no symlink in os
584 except AttributeError: # no symlink in os
558 pass
585 pass
559
586
560 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
587 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
561 os.write(ld, info)
588 os.write(ld, info)
562 os.close(ld)
589 os.close(ld)
563
590
564 def readlock(pathname):
591 def readlock(pathname):
565 try:
592 try:
566 return os.readlink(pathname)
593 return os.readlink(pathname)
567 except OSError, why:
594 except OSError, why:
568 if why.errno not in (errno.EINVAL, errno.ENOSYS):
595 if why.errno not in (errno.EINVAL, errno.ENOSYS):
569 raise
596 raise
570 except AttributeError: # no symlink in os
597 except AttributeError: # no symlink in os
571 pass
598 pass
572 return posixfile(pathname).read()
599 return posixfile(pathname).read()
573
600
574 def fstat(fp):
601 def fstat(fp):
575 '''stat file object that may not have fileno method.'''
602 '''stat file object that may not have fileno method.'''
576 try:
603 try:
577 return os.fstat(fp.fileno())
604 return os.fstat(fp.fileno())
578 except AttributeError:
605 except AttributeError:
579 return os.stat(fp.name)
606 return os.stat(fp.name)
580
607
581 # File system features
608 # File system features
582
609
583 def checkcase(path):
610 def checkcase(path):
584 """
611 """
585 Check whether the given path is on a case-sensitive filesystem
612 Check whether the given path is on a case-sensitive filesystem
586
613
587 Requires a path (like /foo/.hg) ending with a foldable final
614 Requires a path (like /foo/.hg) ending with a foldable final
588 directory component.
615 directory component.
589 """
616 """
590 s1 = os.stat(path)
617 s1 = os.stat(path)
591 d, b = os.path.split(path)
618 d, b = os.path.split(path)
592 p2 = os.path.join(d, b.upper())
619 p2 = os.path.join(d, b.upper())
593 if path == p2:
620 if path == p2:
594 p2 = os.path.join(d, b.lower())
621 p2 = os.path.join(d, b.lower())
595 try:
622 try:
596 s2 = os.stat(p2)
623 s2 = os.stat(p2)
597 if s2 == s1:
624 if s2 == s1:
598 return False
625 return False
599 return True
626 return True
600 except:
627 except:
601 return True
628 return True
602
629
603 _fspathcache = {}
630 _fspathcache = {}
604 def fspath(name, root):
631 def fspath(name, root):
605 '''Get name in the case stored in the filesystem
632 '''Get name in the case stored in the filesystem
606
633
607 The name is either relative to root, or it is an absolute path starting
634 The name is either relative to root, or it is an absolute path starting
608 with root. Note that this function is unnecessary, and should not be
635 with root. Note that this function is unnecessary, and should not be
609 called, for case-sensitive filesystems (simply because it's expensive).
636 called, for case-sensitive filesystems (simply because it's expensive).
610 '''
637 '''
611 # If name is absolute, make it relative
638 # If name is absolute, make it relative
612 if name.lower().startswith(root.lower()):
639 if name.lower().startswith(root.lower()):
613 l = len(root)
640 l = len(root)
614 if name[l] == os.sep or name[l] == os.altsep:
641 if name[l] == os.sep or name[l] == os.altsep:
615 l = l + 1
642 l = l + 1
616 name = name[l:]
643 name = name[l:]
617
644
618 if not os.path.exists(os.path.join(root, name)):
645 if not os.path.exists(os.path.join(root, name)):
619 return None
646 return None
620
647
621 seps = os.sep
648 seps = os.sep
622 if os.altsep:
649 if os.altsep:
623 seps = seps + os.altsep
650 seps = seps + os.altsep
624 # Protect backslashes. This gets silly very quickly.
651 # Protect backslashes. This gets silly very quickly.
625 seps.replace('\\','\\\\')
652 seps.replace('\\','\\\\')
626 pattern = re.compile(r'([^%s]+)|([%s]+)' % (seps, seps))
653 pattern = re.compile(r'([^%s]+)|([%s]+)' % (seps, seps))
627 dir = os.path.normcase(os.path.normpath(root))
654 dir = os.path.normcase(os.path.normpath(root))
628 result = []
655 result = []
629 for part, sep in pattern.findall(name):
656 for part, sep in pattern.findall(name):
630 if sep:
657 if sep:
631 result.append(sep)
658 result.append(sep)
632 continue
659 continue
633
660
634 if dir not in _fspathcache:
661 if dir not in _fspathcache:
635 _fspathcache[dir] = os.listdir(dir)
662 _fspathcache[dir] = os.listdir(dir)
636 contents = _fspathcache[dir]
663 contents = _fspathcache[dir]
637
664
638 lpart = part.lower()
665 lpart = part.lower()
639 for n in contents:
666 for n in contents:
640 if n.lower() == lpart:
667 if n.lower() == lpart:
641 result.append(n)
668 result.append(n)
642 break
669 break
643 else:
670 else:
644 # Cannot happen, as the file exists!
671 # Cannot happen, as the file exists!
645 result.append(part)
672 result.append(part)
646 dir = os.path.join(dir, lpart)
673 dir = os.path.join(dir, lpart)
647
674
648 return ''.join(result)
675 return ''.join(result)
649
676
650 def checkexec(path):
677 def checkexec(path):
651 """
678 """
652 Check whether the given path is on a filesystem with UNIX-like exec flags
679 Check whether the given path is on a filesystem with UNIX-like exec flags
653
680
654 Requires a directory (like /foo/.hg)
681 Requires a directory (like /foo/.hg)
655 """
682 """
656
683
657 # VFAT on some Linux versions can flip mode but it doesn't persist
684 # VFAT on some Linux versions can flip mode but it doesn't persist
658 # a FS remount. Frequently we can detect it if files are created
685 # a FS remount. Frequently we can detect it if files are created
659 # with exec bit on.
686 # with exec bit on.
660
687
661 try:
688 try:
662 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
689 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
663 fh, fn = tempfile.mkstemp("", "", path)
690 fh, fn = tempfile.mkstemp("", "", path)
664 try:
691 try:
665 os.close(fh)
692 os.close(fh)
666 m = os.stat(fn).st_mode & 0777
693 m = os.stat(fn).st_mode & 0777
667 new_file_has_exec = m & EXECFLAGS
694 new_file_has_exec = m & EXECFLAGS
668 os.chmod(fn, m ^ EXECFLAGS)
695 os.chmod(fn, m ^ EXECFLAGS)
669 exec_flags_cannot_flip = ((os.stat(fn).st_mode & 0777) == m)
696 exec_flags_cannot_flip = ((os.stat(fn).st_mode & 0777) == m)
670 finally:
697 finally:
671 os.unlink(fn)
698 os.unlink(fn)
672 except (IOError, OSError):
699 except (IOError, OSError):
673 # we don't care, the user probably won't be able to commit anyway
700 # we don't care, the user probably won't be able to commit anyway
674 return False
701 return False
675 return not (new_file_has_exec or exec_flags_cannot_flip)
702 return not (new_file_has_exec or exec_flags_cannot_flip)
676
703
677 def checklink(path):
704 def checklink(path):
678 """check whether the given path is on a symlink-capable filesystem"""
705 """check whether the given path is on a symlink-capable filesystem"""
679 # mktemp is not racy because symlink creation will fail if the
706 # mktemp is not racy because symlink creation will fail if the
680 # file already exists
707 # file already exists
681 name = tempfile.mktemp(dir=path)
708 name = tempfile.mktemp(dir=path)
682 try:
709 try:
683 os.symlink(".", name)
710 os.symlink(".", name)
684 os.unlink(name)
711 os.unlink(name)
685 return True
712 return True
686 except (OSError, AttributeError):
713 except (OSError, AttributeError):
687 return False
714 return False
688
715
689 def needbinarypatch():
716 def needbinarypatch():
690 """return True if patches should be applied in binary mode by default."""
717 """return True if patches should be applied in binary mode by default."""
691 return os.name == 'nt'
718 return os.name == 'nt'
692
719
693 def endswithsep(path):
720 def endswithsep(path):
694 '''Check path ends with os.sep or os.altsep.'''
721 '''Check path ends with os.sep or os.altsep.'''
695 return path.endswith(os.sep) or os.altsep and path.endswith(os.altsep)
722 return path.endswith(os.sep) or os.altsep and path.endswith(os.altsep)
696
723
697 def splitpath(path):
724 def splitpath(path):
698 '''Split path by os.sep.
725 '''Split path by os.sep.
699 Note that this function does not use os.altsep because this is
726 Note that this function does not use os.altsep because this is
700 an alternative of simple "xxx.split(os.sep)".
727 an alternative of simple "xxx.split(os.sep)".
701 It is recommended to use os.path.normpath() before using this
728 It is recommended to use os.path.normpath() before using this
702 function if need.'''
729 function if need.'''
703 return path.split(os.sep)
730 return path.split(os.sep)
704
731
705 def gui():
732 def gui():
706 '''Are we running in a GUI?'''
733 '''Are we running in a GUI?'''
707 return os.name == "nt" or os.name == "mac" or os.environ.get("DISPLAY")
734 return os.name == "nt" or os.name == "mac" or os.environ.get("DISPLAY")
708
735
709 def mktempcopy(name, emptyok=False, createmode=None):
736 def mktempcopy(name, emptyok=False, createmode=None):
710 """Create a temporary file with the same contents from name
737 """Create a temporary file with the same contents from name
711
738
712 The permission bits are copied from the original file.
739 The permission bits are copied from the original file.
713
740
714 If the temporary file is going to be truncated immediately, you
741 If the temporary file is going to be truncated immediately, you
715 can use emptyok=True as an optimization.
742 can use emptyok=True as an optimization.
716
743
717 Returns the name of the temporary file.
744 Returns the name of the temporary file.
718 """
745 """
719 d, fn = os.path.split(name)
746 d, fn = os.path.split(name)
720 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
747 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
721 os.close(fd)
748 os.close(fd)
722 # Temporary files are created with mode 0600, which is usually not
749 # Temporary files are created with mode 0600, which is usually not
723 # what we want. If the original file already exists, just copy
750 # what we want. If the original file already exists, just copy
724 # its mode. Otherwise, manually obey umask.
751 # its mode. Otherwise, manually obey umask.
725 try:
752 try:
726 st_mode = os.lstat(name).st_mode & 0777
753 st_mode = os.lstat(name).st_mode & 0777
727 except OSError, inst:
754 except OSError, inst:
728 if inst.errno != errno.ENOENT:
755 if inst.errno != errno.ENOENT:
729 raise
756 raise
730 st_mode = createmode
757 st_mode = createmode
731 if st_mode is None:
758 if st_mode is None:
732 st_mode = ~umask
759 st_mode = ~umask
733 st_mode &= 0666
760 st_mode &= 0666
734 os.chmod(temp, st_mode)
761 os.chmod(temp, st_mode)
735 if emptyok:
762 if emptyok:
736 return temp
763 return temp
737 try:
764 try:
738 try:
765 try:
739 ifp = posixfile(name, "rb")
766 ifp = posixfile(name, "rb")
740 except IOError, inst:
767 except IOError, inst:
741 if inst.errno == errno.ENOENT:
768 if inst.errno == errno.ENOENT:
742 return temp
769 return temp
743 if not getattr(inst, 'filename', None):
770 if not getattr(inst, 'filename', None):
744 inst.filename = name
771 inst.filename = name
745 raise
772 raise
746 ofp = posixfile(temp, "wb")
773 ofp = posixfile(temp, "wb")
747 for chunk in filechunkiter(ifp):
774 for chunk in filechunkiter(ifp):
748 ofp.write(chunk)
775 ofp.write(chunk)
749 ifp.close()
776 ifp.close()
750 ofp.close()
777 ofp.close()
751 except:
778 except:
752 try: os.unlink(temp)
779 try: os.unlink(temp)
753 except: pass
780 except: pass
754 raise
781 raise
755 return temp
782 return temp
756
783
757 class atomictempfile(object):
784 class atomictempfile(object):
758 """file-like object that atomically updates a file
785 """file-like object that atomically updates a file
759
786
760 All writes will be redirected to a temporary copy of the original
787 All writes will be redirected to a temporary copy of the original
761 file. When rename is called, the copy is renamed to the original
788 file. When rename is called, the copy is renamed to the original
762 name, making the changes visible.
789 name, making the changes visible.
763 """
790 """
764 def __init__(self, name, mode, createmode):
791 def __init__(self, name, mode, createmode):
765 self.__name = name
792 self.__name = name
766 self._fp = None
793 self._fp = None
767 self.temp = mktempcopy(name, emptyok=('w' in mode),
794 self.temp = mktempcopy(name, emptyok=('w' in mode),
768 createmode=createmode)
795 createmode=createmode)
769 self._fp = posixfile(self.temp, mode)
796 self._fp = posixfile(self.temp, mode)
770
797
771 def __getattr__(self, name):
798 def __getattr__(self, name):
772 return getattr(self._fp, name)
799 return getattr(self._fp, name)
773
800
774 def rename(self):
801 def rename(self):
775 if not self._fp.closed:
802 if not self._fp.closed:
776 self._fp.close()
803 self._fp.close()
777 rename(self.temp, localpath(self.__name))
804 rename(self.temp, localpath(self.__name))
778
805
779 def __del__(self):
806 def __del__(self):
780 if not self._fp:
807 if not self._fp:
781 return
808 return
782 if not self._fp.closed:
809 if not self._fp.closed:
783 try:
810 try:
784 os.unlink(self.temp)
811 os.unlink(self.temp)
785 except: pass
812 except: pass
786 self._fp.close()
813 self._fp.close()
787
814
788 def makedirs(name, mode=None):
815 def makedirs(name, mode=None):
789 """recursive directory creation with parent mode inheritance"""
816 """recursive directory creation with parent mode inheritance"""
790 try:
817 try:
791 os.mkdir(name)
818 os.mkdir(name)
792 if mode is not None:
819 if mode is not None:
793 os.chmod(name, mode)
820 os.chmod(name, mode)
794 return
821 return
795 except OSError, err:
822 except OSError, err:
796 if err.errno == errno.EEXIST:
823 if err.errno == errno.EEXIST:
797 return
824 return
798 if err.errno != errno.ENOENT:
825 if err.errno != errno.ENOENT:
799 raise
826 raise
800 parent = os.path.abspath(os.path.dirname(name))
827 parent = os.path.abspath(os.path.dirname(name))
801 makedirs(parent, mode)
828 makedirs(parent, mode)
802 makedirs(name, mode)
829 makedirs(name, mode)
803
830
804 class opener(object):
831 class opener(object):
805 """Open files relative to a base directory
832 """Open files relative to a base directory
806
833
807 This class is used to hide the details of COW semantics and
834 This class is used to hide the details of COW semantics and
808 remote file access from higher level code.
835 remote file access from higher level code.
809 """
836 """
810 def __init__(self, base, audit=True):
837 def __init__(self, base, audit=True):
811 self.base = base
838 self.base = base
812 if audit:
839 if audit:
813 self.audit_path = path_auditor(base)
840 self.audit_path = path_auditor(base)
814 else:
841 else:
815 self.audit_path = always
842 self.audit_path = always
816 self.createmode = None
843 self.createmode = None
817
844
818 def __getattr__(self, name):
845 def __getattr__(self, name):
819 if name == '_can_symlink':
846 if name == '_can_symlink':
820 self._can_symlink = checklink(self.base)
847 self._can_symlink = checklink(self.base)
821 return self._can_symlink
848 return self._can_symlink
822 raise AttributeError(name)
849 raise AttributeError(name)
823
850
824 def _fixfilemode(self, name):
851 def _fixfilemode(self, name):
825 if self.createmode is None:
852 if self.createmode is None:
826 return
853 return
827 os.chmod(name, self.createmode & 0666)
854 os.chmod(name, self.createmode & 0666)
828
855
829 def __call__(self, path, mode="r", text=False, atomictemp=False):
856 def __call__(self, path, mode="r", text=False, atomictemp=False):
830 self.audit_path(path)
857 self.audit_path(path)
831 f = os.path.join(self.base, path)
858 f = os.path.join(self.base, path)
832
859
833 if not text and "b" not in mode:
860 if not text and "b" not in mode:
834 mode += "b" # for that other OS
861 mode += "b" # for that other OS
835
862
836 nlink = -1
863 nlink = -1
837 if mode not in ("r", "rb"):
864 if mode not in ("r", "rb"):
838 try:
865 try:
839 nlink = nlinks(f)
866 nlink = nlinks(f)
840 except OSError:
867 except OSError:
841 nlink = 0
868 nlink = 0
842 d = os.path.dirname(f)
869 d = os.path.dirname(f)
843 if not os.path.isdir(d):
870 if not os.path.isdir(d):
844 makedirs(d, self.createmode)
871 makedirs(d, self.createmode)
845 if atomictemp:
872 if atomictemp:
846 return atomictempfile(f, mode, self.createmode)
873 return atomictempfile(f, mode, self.createmode)
847 if nlink > 1:
874 if nlink > 1:
848 rename(mktempcopy(f), f)
875 rename(mktempcopy(f), f)
849 fp = posixfile(f, mode)
876 fp = posixfile(f, mode)
850 if nlink == 0:
877 if nlink == 0:
851 self._fixfilemode(f)
878 self._fixfilemode(f)
852 return fp
879 return fp
853
880
854 def symlink(self, src, dst):
881 def symlink(self, src, dst):
855 self.audit_path(dst)
882 self.audit_path(dst)
856 linkname = os.path.join(self.base, dst)
883 linkname = os.path.join(self.base, dst)
857 try:
884 try:
858 os.unlink(linkname)
885 os.unlink(linkname)
859 except OSError:
886 except OSError:
860 pass
887 pass
861
888
862 dirname = os.path.dirname(linkname)
889 dirname = os.path.dirname(linkname)
863 if not os.path.exists(dirname):
890 if not os.path.exists(dirname):
864 makedirs(dirname, self.createmode)
891 makedirs(dirname, self.createmode)
865
892
866 if self._can_symlink:
893 if self._can_symlink:
867 try:
894 try:
868 os.symlink(src, linkname)
895 os.symlink(src, linkname)
869 except OSError, err:
896 except OSError, err:
870 raise OSError(err.errno, _('could not symlink to %r: %s') %
897 raise OSError(err.errno, _('could not symlink to %r: %s') %
871 (src, err.strerror), linkname)
898 (src, err.strerror), linkname)
872 else:
899 else:
873 f = self(dst, "w")
900 f = self(dst, "w")
874 f.write(src)
901 f.write(src)
875 f.close()
902 f.close()
876 self._fixfilemode(dst)
903 self._fixfilemode(dst)
877
904
878 class chunkbuffer(object):
905 class chunkbuffer(object):
879 """Allow arbitrary sized chunks of data to be efficiently read from an
906 """Allow arbitrary sized chunks of data to be efficiently read from an
880 iterator over chunks of arbitrary size."""
907 iterator over chunks of arbitrary size."""
881
908
882 def __init__(self, in_iter):
909 def __init__(self, in_iter):
883 """in_iter is the iterator that's iterating over the input chunks.
910 """in_iter is the iterator that's iterating over the input chunks.
884 targetsize is how big a buffer to try to maintain."""
911 targetsize is how big a buffer to try to maintain."""
885 self.iter = iter(in_iter)
912 self.iter = iter(in_iter)
886 self.buf = ''
913 self.buf = ''
887 self.targetsize = 2**16
914 self.targetsize = 2**16
888
915
889 def read(self, l):
916 def read(self, l):
890 """Read L bytes of data from the iterator of chunks of data.
917 """Read L bytes of data from the iterator of chunks of data.
891 Returns less than L bytes if the iterator runs dry."""
918 Returns less than L bytes if the iterator runs dry."""
892 if l > len(self.buf) and self.iter:
919 if l > len(self.buf) and self.iter:
893 # Clamp to a multiple of self.targetsize
920 # Clamp to a multiple of self.targetsize
894 targetsize = max(l, self.targetsize)
921 targetsize = max(l, self.targetsize)
895 collector = cStringIO.StringIO()
922 collector = cStringIO.StringIO()
896 collector.write(self.buf)
923 collector.write(self.buf)
897 collected = len(self.buf)
924 collected = len(self.buf)
898 for chunk in self.iter:
925 for chunk in self.iter:
899 collector.write(chunk)
926 collector.write(chunk)
900 collected += len(chunk)
927 collected += len(chunk)
901 if collected >= targetsize:
928 if collected >= targetsize:
902 break
929 break
903 if collected < targetsize:
930 if collected < targetsize:
904 self.iter = False
931 self.iter = False
905 self.buf = collector.getvalue()
932 self.buf = collector.getvalue()
906 if len(self.buf) == l:
933 if len(self.buf) == l:
907 s, self.buf = str(self.buf), ''
934 s, self.buf = str(self.buf), ''
908 else:
935 else:
909 s, self.buf = self.buf[:l], buffer(self.buf, l)
936 s, self.buf = self.buf[:l], buffer(self.buf, l)
910 return s
937 return s
911
938
912 def filechunkiter(f, size=65536, limit=None):
939 def filechunkiter(f, size=65536, limit=None):
913 """Create a generator that produces the data in the file size
940 """Create a generator that produces the data in the file size
914 (default 65536) bytes at a time, up to optional limit (default is
941 (default 65536) bytes at a time, up to optional limit (default is
915 to read all data). Chunks may be less than size bytes if the
942 to read all data). Chunks may be less than size bytes if the
916 chunk is the last chunk in the file, or the file is a socket or
943 chunk is the last chunk in the file, or the file is a socket or
917 some other type of file that sometimes reads less data than is
944 some other type of file that sometimes reads less data than is
918 requested."""
945 requested."""
919 assert size >= 0
946 assert size >= 0
920 assert limit is None or limit >= 0
947 assert limit is None or limit >= 0
921 while True:
948 while True:
922 if limit is None: nbytes = size
949 if limit is None: nbytes = size
923 else: nbytes = min(limit, size)
950 else: nbytes = min(limit, size)
924 s = nbytes and f.read(nbytes)
951 s = nbytes and f.read(nbytes)
925 if not s: break
952 if not s: break
926 if limit: limit -= len(s)
953 if limit: limit -= len(s)
927 yield s
954 yield s
928
955
929 def makedate():
956 def makedate():
930 lt = time.localtime()
957 lt = time.localtime()
931 if lt[8] == 1 and time.daylight:
958 if lt[8] == 1 and time.daylight:
932 tz = time.altzone
959 tz = time.altzone
933 else:
960 else:
934 tz = time.timezone
961 tz = time.timezone
935 return time.mktime(lt), tz
962 return time.mktime(lt), tz
936
963
937 def datestr(date=None, format='%a %b %d %H:%M:%S %Y %1%2'):
964 def datestr(date=None, format='%a %b %d %H:%M:%S %Y %1%2'):
938 """represent a (unixtime, offset) tuple as a localized time.
965 """represent a (unixtime, offset) tuple as a localized time.
939 unixtime is seconds since the epoch, and offset is the time zone's
966 unixtime is seconds since the epoch, and offset is the time zone's
940 number of seconds away from UTC. if timezone is false, do not
967 number of seconds away from UTC. if timezone is false, do not
941 append time zone to string."""
968 append time zone to string."""
942 t, tz = date or makedate()
969 t, tz = date or makedate()
943 if "%1" in format or "%2" in format:
970 if "%1" in format or "%2" in format:
944 sign = (tz > 0) and "-" or "+"
971 sign = (tz > 0) and "-" or "+"
945 minutes = abs(tz) / 60
972 minutes = abs(tz) / 60
946 format = format.replace("%1", "%c%02d" % (sign, minutes / 60))
973 format = format.replace("%1", "%c%02d" % (sign, minutes / 60))
947 format = format.replace("%2", "%02d" % (minutes % 60))
974 format = format.replace("%2", "%02d" % (minutes % 60))
948 s = time.strftime(format, time.gmtime(float(t) - tz))
975 s = time.strftime(format, time.gmtime(float(t) - tz))
949 return s
976 return s
950
977
951 def shortdate(date=None):
978 def shortdate(date=None):
952 """turn (timestamp, tzoff) tuple into iso 8631 date."""
979 """turn (timestamp, tzoff) tuple into iso 8631 date."""
953 return datestr(date, format='%Y-%m-%d')
980 return datestr(date, format='%Y-%m-%d')
954
981
955 def strdate(string, format, defaults=[]):
982 def strdate(string, format, defaults=[]):
956 """parse a localized time string and return a (unixtime, offset) tuple.
983 """parse a localized time string and return a (unixtime, offset) tuple.
957 if the string cannot be parsed, ValueError is raised."""
984 if the string cannot be parsed, ValueError is raised."""
958 def timezone(string):
985 def timezone(string):
959 tz = string.split()[-1]
986 tz = string.split()[-1]
960 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
987 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
961 sign = (tz[0] == "+") and 1 or -1
988 sign = (tz[0] == "+") and 1 or -1
962 hours = int(tz[1:3])
989 hours = int(tz[1:3])
963 minutes = int(tz[3:5])
990 minutes = int(tz[3:5])
964 return -sign * (hours * 60 + minutes) * 60
991 return -sign * (hours * 60 + minutes) * 60
965 if tz == "GMT" or tz == "UTC":
992 if tz == "GMT" or tz == "UTC":
966 return 0
993 return 0
967 return None
994 return None
968
995
969 # NOTE: unixtime = localunixtime + offset
996 # NOTE: unixtime = localunixtime + offset
970 offset, date = timezone(string), string
997 offset, date = timezone(string), string
971 if offset != None:
998 if offset != None:
972 date = " ".join(string.split()[:-1])
999 date = " ".join(string.split()[:-1])
973
1000
974 # add missing elements from defaults
1001 # add missing elements from defaults
975 for part in defaults:
1002 for part in defaults:
976 found = [True for p in part if ("%"+p) in format]
1003 found = [True for p in part if ("%"+p) in format]
977 if not found:
1004 if not found:
978 date += "@" + defaults[part]
1005 date += "@" + defaults[part]
979 format += "@%" + part[0]
1006 format += "@%" + part[0]
980
1007
981 timetuple = time.strptime(date, format)
1008 timetuple = time.strptime(date, format)
982 localunixtime = int(calendar.timegm(timetuple))
1009 localunixtime = int(calendar.timegm(timetuple))
983 if offset is None:
1010 if offset is None:
984 # local timezone
1011 # local timezone
985 unixtime = int(time.mktime(timetuple))
1012 unixtime = int(time.mktime(timetuple))
986 offset = unixtime - localunixtime
1013 offset = unixtime - localunixtime
987 else:
1014 else:
988 unixtime = localunixtime + offset
1015 unixtime = localunixtime + offset
989 return unixtime, offset
1016 return unixtime, offset
990
1017
991 def parsedate(date, formats=None, defaults=None):
1018 def parsedate(date, formats=None, defaults=None):
992 """parse a localized date/time string and return a (unixtime, offset) tuple.
1019 """parse a localized date/time string and return a (unixtime, offset) tuple.
993
1020
994 The date may be a "unixtime offset" string or in one of the specified
1021 The date may be a "unixtime offset" string or in one of the specified
995 formats. If the date already is a (unixtime, offset) tuple, it is returned.
1022 formats. If the date already is a (unixtime, offset) tuple, it is returned.
996 """
1023 """
997 if not date:
1024 if not date:
998 return 0, 0
1025 return 0, 0
999 if isinstance(date, tuple) and len(date) == 2:
1026 if isinstance(date, tuple) and len(date) == 2:
1000 return date
1027 return date
1001 if not formats:
1028 if not formats:
1002 formats = defaultdateformats
1029 formats = defaultdateformats
1003 date = date.strip()
1030 date = date.strip()
1004 try:
1031 try:
1005 when, offset = map(int, date.split(' '))
1032 when, offset = map(int, date.split(' '))
1006 except ValueError:
1033 except ValueError:
1007 # fill out defaults
1034 # fill out defaults
1008 if not defaults:
1035 if not defaults:
1009 defaults = {}
1036 defaults = {}
1010 now = makedate()
1037 now = makedate()
1011 for part in "d mb yY HI M S".split():
1038 for part in "d mb yY HI M S".split():
1012 if part not in defaults:
1039 if part not in defaults:
1013 if part[0] in "HMS":
1040 if part[0] in "HMS":
1014 defaults[part] = "00"
1041 defaults[part] = "00"
1015 else:
1042 else:
1016 defaults[part] = datestr(now, "%" + part[0])
1043 defaults[part] = datestr(now, "%" + part[0])
1017
1044
1018 for format in formats:
1045 for format in formats:
1019 try:
1046 try:
1020 when, offset = strdate(date, format, defaults)
1047 when, offset = strdate(date, format, defaults)
1021 except (ValueError, OverflowError):
1048 except (ValueError, OverflowError):
1022 pass
1049 pass
1023 else:
1050 else:
1024 break
1051 break
1025 else:
1052 else:
1026 raise Abort(_('invalid date: %r ') % date)
1053 raise Abort(_('invalid date: %r ') % date)
1027 # validate explicit (probably user-specified) date and
1054 # validate explicit (probably user-specified) date and
1028 # time zone offset. values must fit in signed 32 bits for
1055 # time zone offset. values must fit in signed 32 bits for
1029 # current 32-bit linux runtimes. timezones go from UTC-12
1056 # current 32-bit linux runtimes. timezones go from UTC-12
1030 # to UTC+14
1057 # to UTC+14
1031 if abs(when) > 0x7fffffff:
1058 if abs(when) > 0x7fffffff:
1032 raise Abort(_('date exceeds 32 bits: %d') % when)
1059 raise Abort(_('date exceeds 32 bits: %d') % when)
1033 if offset < -50400 or offset > 43200:
1060 if offset < -50400 or offset > 43200:
1034 raise Abort(_('impossible time zone offset: %d') % offset)
1061 raise Abort(_('impossible time zone offset: %d') % offset)
1035 return when, offset
1062 return when, offset
1036
1063
1037 def matchdate(date):
1064 def matchdate(date):
1038 """Return a function that matches a given date match specifier
1065 """Return a function that matches a given date match specifier
1039
1066
1040 Formats include:
1067 Formats include:
1041
1068
1042 '{date}' match a given date to the accuracy provided
1069 '{date}' match a given date to the accuracy provided
1043
1070
1044 '<{date}' on or before a given date
1071 '<{date}' on or before a given date
1045
1072
1046 '>{date}' on or after a given date
1073 '>{date}' on or after a given date
1047
1074
1048 """
1075 """
1049
1076
1050 def lower(date):
1077 def lower(date):
1051 d = dict(mb="1", d="1")
1078 d = dict(mb="1", d="1")
1052 return parsedate(date, extendeddateformats, d)[0]
1079 return parsedate(date, extendeddateformats, d)[0]
1053
1080
1054 def upper(date):
1081 def upper(date):
1055 d = dict(mb="12", HI="23", M="59", S="59")
1082 d = dict(mb="12", HI="23", M="59", S="59")
1056 for days in "31 30 29".split():
1083 for days in "31 30 29".split():
1057 try:
1084 try:
1058 d["d"] = days
1085 d["d"] = days
1059 return parsedate(date, extendeddateformats, d)[0]
1086 return parsedate(date, extendeddateformats, d)[0]
1060 except:
1087 except:
1061 pass
1088 pass
1062 d["d"] = "28"
1089 d["d"] = "28"
1063 return parsedate(date, extendeddateformats, d)[0]
1090 return parsedate(date, extendeddateformats, d)[0]
1064
1091
1065 date = date.strip()
1092 date = date.strip()
1066 if date[0] == "<":
1093 if date[0] == "<":
1067 when = upper(date[1:])
1094 when = upper(date[1:])
1068 return lambda x: x <= when
1095 return lambda x: x <= when
1069 elif date[0] == ">":
1096 elif date[0] == ">":
1070 when = lower(date[1:])
1097 when = lower(date[1:])
1071 return lambda x: x >= when
1098 return lambda x: x >= when
1072 elif date[0] == "-":
1099 elif date[0] == "-":
1073 try:
1100 try:
1074 days = int(date[1:])
1101 days = int(date[1:])
1075 except ValueError:
1102 except ValueError:
1076 raise Abort(_("invalid day spec: %s") % date[1:])
1103 raise Abort(_("invalid day spec: %s") % date[1:])
1077 when = makedate()[0] - days * 3600 * 24
1104 when = makedate()[0] - days * 3600 * 24
1078 return lambda x: x >= when
1105 return lambda x: x >= when
1079 elif " to " in date:
1106 elif " to " in date:
1080 a, b = date.split(" to ")
1107 a, b = date.split(" to ")
1081 start, stop = lower(a), upper(b)
1108 start, stop = lower(a), upper(b)
1082 return lambda x: x >= start and x <= stop
1109 return lambda x: x >= start and x <= stop
1083 else:
1110 else:
1084 start, stop = lower(date), upper(date)
1111 start, stop = lower(date), upper(date)
1085 return lambda x: x >= start and x <= stop
1112 return lambda x: x >= start and x <= stop
1086
1113
1087 def shortuser(user):
1114 def shortuser(user):
1088 """Return a short representation of a user name or email address."""
1115 """Return a short representation of a user name or email address."""
1089 f = user.find('@')
1116 f = user.find('@')
1090 if f >= 0:
1117 if f >= 0:
1091 user = user[:f]
1118 user = user[:f]
1092 f = user.find('<')
1119 f = user.find('<')
1093 if f >= 0:
1120 if f >= 0:
1094 user = user[f+1:]
1121 user = user[f+1:]
1095 f = user.find(' ')
1122 f = user.find(' ')
1096 if f >= 0:
1123 if f >= 0:
1097 user = user[:f]
1124 user = user[:f]
1098 f = user.find('.')
1125 f = user.find('.')
1099 if f >= 0:
1126 if f >= 0:
1100 user = user[:f]
1127 user = user[:f]
1101 return user
1128 return user
1102
1129
1103 def email(author):
1130 def email(author):
1104 '''get email of author.'''
1131 '''get email of author.'''
1105 r = author.find('>')
1132 r = author.find('>')
1106 if r == -1: r = None
1133 if r == -1: r = None
1107 return author[author.find('<')+1:r]
1134 return author[author.find('<')+1:r]
1108
1135
1109 def ellipsis(text, maxlength=400):
1136 def ellipsis(text, maxlength=400):
1110 """Trim string to at most maxlength (default: 400) characters."""
1137 """Trim string to at most maxlength (default: 400) characters."""
1111 if len(text) <= maxlength:
1138 if len(text) <= maxlength:
1112 return text
1139 return text
1113 else:
1140 else:
1114 return "%s..." % (text[:maxlength-3])
1141 return "%s..." % (text[:maxlength-3])
1115
1142
1116 def walkrepos(path, followsym=False, seen_dirs=None, recurse=False):
1143 def walkrepos(path, followsym=False, seen_dirs=None, recurse=False):
1117 '''yield every hg repository under path, recursively.'''
1144 '''yield every hg repository under path, recursively.'''
1118 def errhandler(err):
1145 def errhandler(err):
1119 if err.filename == path:
1146 if err.filename == path:
1120 raise err
1147 raise err
1121 if followsym and hasattr(os.path, 'samestat'):
1148 if followsym and hasattr(os.path, 'samestat'):
1122 def _add_dir_if_not_there(dirlst, dirname):
1149 def _add_dir_if_not_there(dirlst, dirname):
1123 match = False
1150 match = False
1124 samestat = os.path.samestat
1151 samestat = os.path.samestat
1125 dirstat = os.stat(dirname)
1152 dirstat = os.stat(dirname)
1126 for lstdirstat in dirlst:
1153 for lstdirstat in dirlst:
1127 if samestat(dirstat, lstdirstat):
1154 if samestat(dirstat, lstdirstat):
1128 match = True
1155 match = True
1129 break
1156 break
1130 if not match:
1157 if not match:
1131 dirlst.append(dirstat)
1158 dirlst.append(dirstat)
1132 return not match
1159 return not match
1133 else:
1160 else:
1134 followsym = False
1161 followsym = False
1135
1162
1136 if (seen_dirs is None) and followsym:
1163 if (seen_dirs is None) and followsym:
1137 seen_dirs = []
1164 seen_dirs = []
1138 _add_dir_if_not_there(seen_dirs, path)
1165 _add_dir_if_not_there(seen_dirs, path)
1139 for root, dirs, files in os.walk(path, topdown=True, onerror=errhandler):
1166 for root, dirs, files in os.walk(path, topdown=True, onerror=errhandler):
1140 if '.hg' in dirs:
1167 if '.hg' in dirs:
1141 yield root # found a repository
1168 yield root # found a repository
1142 qroot = os.path.join(root, '.hg', 'patches')
1169 qroot = os.path.join(root, '.hg', 'patches')
1143 if os.path.isdir(os.path.join(qroot, '.hg')):
1170 if os.path.isdir(os.path.join(qroot, '.hg')):
1144 yield qroot # we have a patch queue repo here
1171 yield qroot # we have a patch queue repo here
1145 if recurse:
1172 if recurse:
1146 # avoid recursing inside the .hg directory
1173 # avoid recursing inside the .hg directory
1147 dirs.remove('.hg')
1174 dirs.remove('.hg')
1148 else:
1175 else:
1149 dirs[:] = [] # don't descend further
1176 dirs[:] = [] # don't descend further
1150 elif followsym:
1177 elif followsym:
1151 newdirs = []
1178 newdirs = []
1152 for d in dirs:
1179 for d in dirs:
1153 fname = os.path.join(root, d)
1180 fname = os.path.join(root, d)
1154 if _add_dir_if_not_there(seen_dirs, fname):
1181 if _add_dir_if_not_there(seen_dirs, fname):
1155 if os.path.islink(fname):
1182 if os.path.islink(fname):
1156 for hgname in walkrepos(fname, True, seen_dirs):
1183 for hgname in walkrepos(fname, True, seen_dirs):
1157 yield hgname
1184 yield hgname
1158 else:
1185 else:
1159 newdirs.append(d)
1186 newdirs.append(d)
1160 dirs[:] = newdirs
1187 dirs[:] = newdirs
1161
1188
1162 _rcpath = None
1189 _rcpath = None
1163
1190
1164 def os_rcpath():
1191 def os_rcpath():
1165 '''return default os-specific hgrc search path'''
1192 '''return default os-specific hgrc search path'''
1166 path = system_rcpath()
1193 path = system_rcpath()
1167 path.extend(user_rcpath())
1194 path.extend(user_rcpath())
1168 path = [os.path.normpath(f) for f in path]
1195 path = [os.path.normpath(f) for f in path]
1169 return path
1196 return path
1170
1197
1171 def rcpath():
1198 def rcpath():
1172 '''return hgrc search path. if env var HGRCPATH is set, use it.
1199 '''return hgrc search path. if env var HGRCPATH is set, use it.
1173 for each item in path, if directory, use files ending in .rc,
1200 for each item in path, if directory, use files ending in .rc,
1174 else use item.
1201 else use item.
1175 make HGRCPATH empty to only look in .hg/hgrc of current repo.
1202 make HGRCPATH empty to only look in .hg/hgrc of current repo.
1176 if no HGRCPATH, use default os-specific path.'''
1203 if no HGRCPATH, use default os-specific path.'''
1177 global _rcpath
1204 global _rcpath
1178 if _rcpath is None:
1205 if _rcpath is None:
1179 if 'HGRCPATH' in os.environ:
1206 if 'HGRCPATH' in os.environ:
1180 _rcpath = []
1207 _rcpath = []
1181 for p in os.environ['HGRCPATH'].split(os.pathsep):
1208 for p in os.environ['HGRCPATH'].split(os.pathsep):
1182 if not p: continue
1209 if not p: continue
1183 if os.path.isdir(p):
1210 if os.path.isdir(p):
1184 for f, kind in osutil.listdir(p):
1211 for f, kind in osutil.listdir(p):
1185 if f.endswith('.rc'):
1212 if f.endswith('.rc'):
1186 _rcpath.append(os.path.join(p, f))
1213 _rcpath.append(os.path.join(p, f))
1187 else:
1214 else:
1188 _rcpath.append(p)
1215 _rcpath.append(p)
1189 else:
1216 else:
1190 _rcpath = os_rcpath()
1217 _rcpath = os_rcpath()
1191 return _rcpath
1218 return _rcpath
1192
1219
1193 def bytecount(nbytes):
1220 def bytecount(nbytes):
1194 '''return byte count formatted as readable string, with units'''
1221 '''return byte count formatted as readable string, with units'''
1195
1222
1196 units = (
1223 units = (
1197 (100, 1<<30, _('%.0f GB')),
1224 (100, 1<<30, _('%.0f GB')),
1198 (10, 1<<30, _('%.1f GB')),
1225 (10, 1<<30, _('%.1f GB')),
1199 (1, 1<<30, _('%.2f GB')),
1226 (1, 1<<30, _('%.2f GB')),
1200 (100, 1<<20, _('%.0f MB')),
1227 (100, 1<<20, _('%.0f MB')),
1201 (10, 1<<20, _('%.1f MB')),
1228 (10, 1<<20, _('%.1f MB')),
1202 (1, 1<<20, _('%.2f MB')),
1229 (1, 1<<20, _('%.2f MB')),
1203 (100, 1<<10, _('%.0f KB')),
1230 (100, 1<<10, _('%.0f KB')),
1204 (10, 1<<10, _('%.1f KB')),
1231 (10, 1<<10, _('%.1f KB')),
1205 (1, 1<<10, _('%.2f KB')),
1232 (1, 1<<10, _('%.2f KB')),
1206 (1, 1, _('%.0f bytes')),
1233 (1, 1, _('%.0f bytes')),
1207 )
1234 )
1208
1235
1209 for multiplier, divisor, format in units:
1236 for multiplier, divisor, format in units:
1210 if nbytes >= divisor * multiplier:
1237 if nbytes >= divisor * multiplier:
1211 return format % (nbytes / float(divisor))
1238 return format % (nbytes / float(divisor))
1212 return units[-1][2] % nbytes
1239 return units[-1][2] % nbytes
1213
1240
1214 def drop_scheme(scheme, path):
1241 def drop_scheme(scheme, path):
1215 sc = scheme + ':'
1242 sc = scheme + ':'
1216 if path.startswith(sc):
1243 if path.startswith(sc):
1217 path = path[len(sc):]
1244 path = path[len(sc):]
1218 if path.startswith('//'):
1245 if path.startswith('//'):
1219 path = path[2:]
1246 path = path[2:]
1220 return path
1247 return path
1221
1248
1222 def uirepr(s):
1249 def uirepr(s):
1223 # Avoid double backslash in Windows path repr()
1250 # Avoid double backslash in Windows path repr()
1224 return repr(s).replace('\\\\', '\\')
1251 return repr(s).replace('\\\\', '\\')
1225
1252
1226 def termwidth():
1253 def termwidth():
1227 if 'COLUMNS' in os.environ:
1254 if 'COLUMNS' in os.environ:
1228 try:
1255 try:
1229 return int(os.environ['COLUMNS'])
1256 return int(os.environ['COLUMNS'])
1230 except ValueError:
1257 except ValueError:
1231 pass
1258 pass
1232 try:
1259 try:
1233 import termios, array, fcntl
1260 import termios, array, fcntl
1234 for dev in (sys.stdout, sys.stdin):
1261 for dev in (sys.stdout, sys.stdin):
1235 try:
1262 try:
1236 try:
1263 try:
1237 fd = dev.fileno()
1264 fd = dev.fileno()
1238 except AttributeError:
1265 except AttributeError:
1239 continue
1266 continue
1240 if not os.isatty(fd):
1267 if not os.isatty(fd):
1241 continue
1268 continue
1242 arri = fcntl.ioctl(fd, termios.TIOCGWINSZ, '\0' * 8)
1269 arri = fcntl.ioctl(fd, termios.TIOCGWINSZ, '\0' * 8)
1243 return array.array('h', arri)[1]
1270 return array.array('h', arri)[1]
1244 except ValueError:
1271 except ValueError:
1245 pass
1272 pass
1246 except ImportError:
1273 except ImportError:
1247 pass
1274 pass
1248 return 80
1275 return 80
1249
1276
1250 def wrap(line, hangindent, width=78):
1277 def wrap(line, hangindent, width=78):
1251 padding = '\n' + ' ' * hangindent
1278 padding = '\n' + ' ' * hangindent
1252 return padding.join(textwrap.wrap(line, width=width - hangindent))
1279 return padding.join(textwrap.wrap(line, width=width - hangindent))
1253
1280
1254 def iterlines(iterator):
1281 def iterlines(iterator):
1255 for chunk in iterator:
1282 for chunk in iterator:
1256 for line in chunk.splitlines():
1283 for line in chunk.splitlines():
1257 yield line
1284 yield line
General Comments 0
You need to be logged in to leave comments. Login now