##// END OF EJS Templates
docopy: deal with globs on windows in a better way
Alexis S. L. Carvalho -
r4055:e37786b2 default
parent child Browse files
Show More
@@ -1,773 +1,775 b''
1 # cmdutil.py - help for command processing in mercurial
1 # cmdutil.py - help for command processing in mercurial
2 #
2 #
3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 from demandload import demandload
8 from demandload import demandload
9 from node import *
9 from node import *
10 from i18n import gettext as _
10 from i18n import gettext as _
11 demandload(globals(), 'os sys')
11 demandload(globals(), 'os sys')
12 demandload(globals(), 'mdiff util templater patch')
12 demandload(globals(), 'mdiff util templater patch')
13
13
14 revrangesep = ':'
14 revrangesep = ':'
15
15
16 def revpair(repo, revs):
16 def revpair(repo, revs):
17 '''return pair of nodes, given list of revisions. second item can
17 '''return pair of nodes, given list of revisions. second item can
18 be None, meaning use working dir.'''
18 be None, meaning use working dir.'''
19
19
20 def revfix(repo, val, defval):
20 def revfix(repo, val, defval):
21 if not val and val != 0 and defval is not None:
21 if not val and val != 0 and defval is not None:
22 val = defval
22 val = defval
23 return repo.lookup(val)
23 return repo.lookup(val)
24
24
25 if not revs:
25 if not revs:
26 return repo.dirstate.parents()[0], None
26 return repo.dirstate.parents()[0], None
27 end = None
27 end = None
28 if len(revs) == 1:
28 if len(revs) == 1:
29 if revrangesep in revs[0]:
29 if revrangesep in revs[0]:
30 start, end = revs[0].split(revrangesep, 1)
30 start, end = revs[0].split(revrangesep, 1)
31 start = revfix(repo, start, 0)
31 start = revfix(repo, start, 0)
32 end = revfix(repo, end, repo.changelog.count() - 1)
32 end = revfix(repo, end, repo.changelog.count() - 1)
33 else:
33 else:
34 start = revfix(repo, revs[0], None)
34 start = revfix(repo, revs[0], None)
35 elif len(revs) == 2:
35 elif len(revs) == 2:
36 if revrangesep in revs[0] or revrangesep in revs[1]:
36 if revrangesep in revs[0] or revrangesep in revs[1]:
37 raise util.Abort(_('too many revisions specified'))
37 raise util.Abort(_('too many revisions specified'))
38 start = revfix(repo, revs[0], None)
38 start = revfix(repo, revs[0], None)
39 end = revfix(repo, revs[1], None)
39 end = revfix(repo, revs[1], None)
40 else:
40 else:
41 raise util.Abort(_('too many revisions specified'))
41 raise util.Abort(_('too many revisions specified'))
42 return start, end
42 return start, end
43
43
44 def revrange(repo, revs):
44 def revrange(repo, revs):
45 """Yield revision as strings from a list of revision specifications."""
45 """Yield revision as strings from a list of revision specifications."""
46
46
47 def revfix(repo, val, defval):
47 def revfix(repo, val, defval):
48 if not val and val != 0 and defval is not None:
48 if not val and val != 0 and defval is not None:
49 return defval
49 return defval
50 return repo.changelog.rev(repo.lookup(val))
50 return repo.changelog.rev(repo.lookup(val))
51
51
52 seen, l = {}, []
52 seen, l = {}, []
53 for spec in revs:
53 for spec in revs:
54 if revrangesep in spec:
54 if revrangesep in spec:
55 start, end = spec.split(revrangesep, 1)
55 start, end = spec.split(revrangesep, 1)
56 start = revfix(repo, start, 0)
56 start = revfix(repo, start, 0)
57 end = revfix(repo, end, repo.changelog.count() - 1)
57 end = revfix(repo, end, repo.changelog.count() - 1)
58 step = start > end and -1 or 1
58 step = start > end and -1 or 1
59 for rev in xrange(start, end+step, step):
59 for rev in xrange(start, end+step, step):
60 if rev in seen:
60 if rev in seen:
61 continue
61 continue
62 seen[rev] = 1
62 seen[rev] = 1
63 l.append(rev)
63 l.append(rev)
64 else:
64 else:
65 rev = revfix(repo, spec, None)
65 rev = revfix(repo, spec, None)
66 if rev in seen:
66 if rev in seen:
67 continue
67 continue
68 seen[rev] = 1
68 seen[rev] = 1
69 l.append(rev)
69 l.append(rev)
70
70
71 return l
71 return l
72
72
73 def make_filename(repo, pat, node,
73 def make_filename(repo, pat, node,
74 total=None, seqno=None, revwidth=None, pathname=None):
74 total=None, seqno=None, revwidth=None, pathname=None):
75 node_expander = {
75 node_expander = {
76 'H': lambda: hex(node),
76 'H': lambda: hex(node),
77 'R': lambda: str(repo.changelog.rev(node)),
77 'R': lambda: str(repo.changelog.rev(node)),
78 'h': lambda: short(node),
78 'h': lambda: short(node),
79 }
79 }
80 expander = {
80 expander = {
81 '%': lambda: '%',
81 '%': lambda: '%',
82 'b': lambda: os.path.basename(repo.root),
82 'b': lambda: os.path.basename(repo.root),
83 }
83 }
84
84
85 try:
85 try:
86 if node:
86 if node:
87 expander.update(node_expander)
87 expander.update(node_expander)
88 if node and revwidth is not None:
88 if node and revwidth is not None:
89 expander['r'] = (lambda:
89 expander['r'] = (lambda:
90 str(repo.changelog.rev(node)).zfill(revwidth))
90 str(repo.changelog.rev(node)).zfill(revwidth))
91 if total is not None:
91 if total is not None:
92 expander['N'] = lambda: str(total)
92 expander['N'] = lambda: str(total)
93 if seqno is not None:
93 if seqno is not None:
94 expander['n'] = lambda: str(seqno)
94 expander['n'] = lambda: str(seqno)
95 if total is not None and seqno is not None:
95 if total is not None and seqno is not None:
96 expander['n'] = lambda: str(seqno).zfill(len(str(total)))
96 expander['n'] = lambda: str(seqno).zfill(len(str(total)))
97 if pathname is not None:
97 if pathname is not None:
98 expander['s'] = lambda: os.path.basename(pathname)
98 expander['s'] = lambda: os.path.basename(pathname)
99 expander['d'] = lambda: os.path.dirname(pathname) or '.'
99 expander['d'] = lambda: os.path.dirname(pathname) or '.'
100 expander['p'] = lambda: pathname
100 expander['p'] = lambda: pathname
101
101
102 newname = []
102 newname = []
103 patlen = len(pat)
103 patlen = len(pat)
104 i = 0
104 i = 0
105 while i < patlen:
105 while i < patlen:
106 c = pat[i]
106 c = pat[i]
107 if c == '%':
107 if c == '%':
108 i += 1
108 i += 1
109 c = pat[i]
109 c = pat[i]
110 c = expander[c]()
110 c = expander[c]()
111 newname.append(c)
111 newname.append(c)
112 i += 1
112 i += 1
113 return ''.join(newname)
113 return ''.join(newname)
114 except KeyError, inst:
114 except KeyError, inst:
115 raise util.Abort(_("invalid format spec '%%%s' in output file name") %
115 raise util.Abort(_("invalid format spec '%%%s' in output file name") %
116 inst.args[0])
116 inst.args[0])
117
117
118 def make_file(repo, pat, node=None,
118 def make_file(repo, pat, node=None,
119 total=None, seqno=None, revwidth=None, mode='wb', pathname=None):
119 total=None, seqno=None, revwidth=None, mode='wb', pathname=None):
120 if not pat or pat == '-':
120 if not pat or pat == '-':
121 return 'w' in mode and sys.stdout or sys.stdin
121 return 'w' in mode and sys.stdout or sys.stdin
122 if hasattr(pat, 'write') and 'w' in mode:
122 if hasattr(pat, 'write') and 'w' in mode:
123 return pat
123 return pat
124 if hasattr(pat, 'read') and 'r' in mode:
124 if hasattr(pat, 'read') and 'r' in mode:
125 return pat
125 return pat
126 return open(make_filename(repo, pat, node, total, seqno, revwidth,
126 return open(make_filename(repo, pat, node, total, seqno, revwidth,
127 pathname),
127 pathname),
128 mode)
128 mode)
129
129
130 def matchpats(repo, pats=[], opts={}, head=''):
130 def matchpats(repo, pats=[], opts={}, head='', globbed=False):
131 cwd = repo.getcwd()
131 cwd = repo.getcwd()
132 if not pats and cwd:
132 if not pats and cwd:
133 opts['include'] = [os.path.join(cwd, i)
133 opts['include'] = [os.path.join(cwd, i)
134 for i in opts.get('include', [])]
134 for i in opts.get('include', [])]
135 opts['exclude'] = [os.path.join(cwd, x)
135 opts['exclude'] = [os.path.join(cwd, x)
136 for x in opts.get('exclude', [])]
136 for x in opts.get('exclude', [])]
137 cwd = ''
137 cwd = ''
138 return util.cmdmatcher(repo.root, cwd, pats or ['.'], opts.get('include'),
138 return util.cmdmatcher(repo.root, cwd, pats or ['.'], opts.get('include'),
139 opts.get('exclude'), head)
139 opts.get('exclude'), head, globbed=globbed)
140
140
141 def walk(repo, pats=[], opts={}, node=None, head='', badmatch=None):
141 def walk(repo, pats=[], opts={}, node=None, head='', badmatch=None,
142 files, matchfn, anypats = matchpats(repo, pats, opts, head)
142 globbed=False):
143 files, matchfn, anypats = matchpats(repo, pats, opts, head,
144 globbed=globbed)
143 exact = dict.fromkeys(files)
145 exact = dict.fromkeys(files)
144 for src, fn in repo.walk(node=node, files=files, match=matchfn,
146 for src, fn in repo.walk(node=node, files=files, match=matchfn,
145 badmatch=badmatch):
147 badmatch=badmatch):
146 yield src, fn, util.pathto(repo.getcwd(), fn), fn in exact
148 yield src, fn, util.pathto(repo.getcwd(), fn), fn in exact
147
149
148 def findrenames(repo, added=None, removed=None, threshold=0.5):
150 def findrenames(repo, added=None, removed=None, threshold=0.5):
149 if added is None or removed is None:
151 if added is None or removed is None:
150 added, removed = repo.status()[1:3]
152 added, removed = repo.status()[1:3]
151 changes = repo.changelog.read(repo.dirstate.parents()[0])
153 changes = repo.changelog.read(repo.dirstate.parents()[0])
152 mf = repo.manifest.read(changes[0])
154 mf = repo.manifest.read(changes[0])
153 for a in added:
155 for a in added:
154 aa = repo.wread(a)
156 aa = repo.wread(a)
155 bestscore, bestname = None, None
157 bestscore, bestname = None, None
156 for r in removed:
158 for r in removed:
157 rr = repo.file(r).read(mf[r])
159 rr = repo.file(r).read(mf[r])
158 delta = mdiff.textdiff(aa, rr)
160 delta = mdiff.textdiff(aa, rr)
159 if len(delta) < len(aa):
161 if len(delta) < len(aa):
160 myscore = 1.0 - (float(len(delta)) / len(aa))
162 myscore = 1.0 - (float(len(delta)) / len(aa))
161 if bestscore is None or myscore > bestscore:
163 if bestscore is None or myscore > bestscore:
162 bestscore, bestname = myscore, r
164 bestscore, bestname = myscore, r
163 if bestname and bestscore >= threshold:
165 if bestname and bestscore >= threshold:
164 yield bestname, a, bestscore
166 yield bestname, a, bestscore
165
167
166 def addremove(repo, pats=[], opts={}, wlock=None, dry_run=None,
168 def addremove(repo, pats=[], opts={}, wlock=None, dry_run=None,
167 similarity=None):
169 similarity=None):
168 if dry_run is None:
170 if dry_run is None:
169 dry_run = opts.get('dry_run')
171 dry_run = opts.get('dry_run')
170 if similarity is None:
172 if similarity is None:
171 similarity = float(opts.get('similarity') or 0)
173 similarity = float(opts.get('similarity') or 0)
172 add, remove = [], []
174 add, remove = [], []
173 mapping = {}
175 mapping = {}
174 for src, abs, rel, exact in walk(repo, pats, opts):
176 for src, abs, rel, exact in walk(repo, pats, opts):
175 if src == 'f' and repo.dirstate.state(abs) == '?':
177 if src == 'f' and repo.dirstate.state(abs) == '?':
176 add.append(abs)
178 add.append(abs)
177 mapping[abs] = rel, exact
179 mapping[abs] = rel, exact
178 if repo.ui.verbose or not exact:
180 if repo.ui.verbose or not exact:
179 repo.ui.status(_('adding %s\n') % ((pats and rel) or abs))
181 repo.ui.status(_('adding %s\n') % ((pats and rel) or abs))
180 if repo.dirstate.state(abs) != 'r' and not os.path.exists(rel):
182 if repo.dirstate.state(abs) != 'r' and not os.path.exists(rel):
181 remove.append(abs)
183 remove.append(abs)
182 mapping[abs] = rel, exact
184 mapping[abs] = rel, exact
183 if repo.ui.verbose or not exact:
185 if repo.ui.verbose or not exact:
184 repo.ui.status(_('removing %s\n') % ((pats and rel) or abs))
186 repo.ui.status(_('removing %s\n') % ((pats and rel) or abs))
185 if not dry_run:
187 if not dry_run:
186 repo.add(add, wlock=wlock)
188 repo.add(add, wlock=wlock)
187 repo.remove(remove, wlock=wlock)
189 repo.remove(remove, wlock=wlock)
188 if similarity > 0:
190 if similarity > 0:
189 for old, new, score in findrenames(repo, add, remove, similarity):
191 for old, new, score in findrenames(repo, add, remove, similarity):
190 oldrel, oldexact = mapping[old]
192 oldrel, oldexact = mapping[old]
191 newrel, newexact = mapping[new]
193 newrel, newexact = mapping[new]
192 if repo.ui.verbose or not oldexact or not newexact:
194 if repo.ui.verbose or not oldexact or not newexact:
193 repo.ui.status(_('recording removal of %s as rename to %s '
195 repo.ui.status(_('recording removal of %s as rename to %s '
194 '(%d%% similar)\n') %
196 '(%d%% similar)\n') %
195 (oldrel, newrel, score * 100))
197 (oldrel, newrel, score * 100))
196 if not dry_run:
198 if not dry_run:
197 repo.copy(old, new, wlock=wlock)
199 repo.copy(old, new, wlock=wlock)
198
200
199 class changeset_printer(object):
201 class changeset_printer(object):
200 '''show changeset information when templating not requested.'''
202 '''show changeset information when templating not requested.'''
201
203
202 def __init__(self, ui, repo, patch, brinfo, buffered):
204 def __init__(self, ui, repo, patch, brinfo, buffered):
203 self.ui = ui
205 self.ui = ui
204 self.repo = repo
206 self.repo = repo
205 self.buffered = buffered
207 self.buffered = buffered
206 self.patch = patch
208 self.patch = patch
207 self.brinfo = brinfo
209 self.brinfo = brinfo
208 self.header = {}
210 self.header = {}
209 self.hunk = {}
211 self.hunk = {}
210 self.lastheader = None
212 self.lastheader = None
211
213
212 def flush(self, rev):
214 def flush(self, rev):
213 if rev in self.header:
215 if rev in self.header:
214 h = self.header[rev]
216 h = self.header[rev]
215 if h != self.lastheader:
217 if h != self.lastheader:
216 self.lastheader = h
218 self.lastheader = h
217 self.ui.write(h)
219 self.ui.write(h)
218 del self.header[rev]
220 del self.header[rev]
219 if rev in self.hunk:
221 if rev in self.hunk:
220 self.ui.write(self.hunk[rev])
222 self.ui.write(self.hunk[rev])
221 del self.hunk[rev]
223 del self.hunk[rev]
222 return 1
224 return 1
223 return 0
225 return 0
224
226
225 def show(self, rev=0, changenode=None, copies=None, **props):
227 def show(self, rev=0, changenode=None, copies=None, **props):
226 if self.buffered:
228 if self.buffered:
227 self.ui.pushbuffer()
229 self.ui.pushbuffer()
228 self._show(rev, changenode, copies, props)
230 self._show(rev, changenode, copies, props)
229 self.hunk[rev] = self.ui.popbuffer()
231 self.hunk[rev] = self.ui.popbuffer()
230 else:
232 else:
231 self._show(rev, changenode, copies, props)
233 self._show(rev, changenode, copies, props)
232
234
233 def _show(self, rev, changenode, copies, props):
235 def _show(self, rev, changenode, copies, props):
234 '''show a single changeset or file revision'''
236 '''show a single changeset or file revision'''
235 log = self.repo.changelog
237 log = self.repo.changelog
236 if changenode is None:
238 if changenode is None:
237 changenode = log.node(rev)
239 changenode = log.node(rev)
238 elif not rev:
240 elif not rev:
239 rev = log.rev(changenode)
241 rev = log.rev(changenode)
240
242
241 if self.ui.quiet:
243 if self.ui.quiet:
242 self.ui.write("%d:%s\n" % (rev, short(changenode)))
244 self.ui.write("%d:%s\n" % (rev, short(changenode)))
243 return
245 return
244
246
245 changes = log.read(changenode)
247 changes = log.read(changenode)
246 date = util.datestr(changes[2])
248 date = util.datestr(changes[2])
247 extra = changes[5]
249 extra = changes[5]
248 branch = extra.get("branch")
250 branch = extra.get("branch")
249
251
250 hexfunc = self.ui.debugflag and hex or short
252 hexfunc = self.ui.debugflag and hex or short
251
253
252 parents = log.parentrevs(rev)
254 parents = log.parentrevs(rev)
253 if not self.ui.debugflag:
255 if not self.ui.debugflag:
254 if parents[1] == nullrev:
256 if parents[1] == nullrev:
255 if parents[0] >= rev - 1:
257 if parents[0] >= rev - 1:
256 parents = []
258 parents = []
257 else:
259 else:
258 parents = [parents[0]]
260 parents = [parents[0]]
259 parents = [(p, hexfunc(log.node(p))) for p in parents]
261 parents = [(p, hexfunc(log.node(p))) for p in parents]
260
262
261 self.ui.write(_("changeset: %d:%s\n") % (rev, hexfunc(changenode)))
263 self.ui.write(_("changeset: %d:%s\n") % (rev, hexfunc(changenode)))
262
264
263 if branch:
265 if branch:
264 branch = util.tolocal(branch)
266 branch = util.tolocal(branch)
265 self.ui.write(_("branch: %s\n") % branch)
267 self.ui.write(_("branch: %s\n") % branch)
266 for tag in self.repo.nodetags(changenode):
268 for tag in self.repo.nodetags(changenode):
267 self.ui.write(_("tag: %s\n") % tag)
269 self.ui.write(_("tag: %s\n") % tag)
268 for parent in parents:
270 for parent in parents:
269 self.ui.write(_("parent: %d:%s\n") % parent)
271 self.ui.write(_("parent: %d:%s\n") % parent)
270
272
271 if self.brinfo:
273 if self.brinfo:
272 br = self.repo.branchlookup([changenode])
274 br = self.repo.branchlookup([changenode])
273 if br:
275 if br:
274 self.ui.write(_("branch: %s\n") % " ".join(br[changenode]))
276 self.ui.write(_("branch: %s\n") % " ".join(br[changenode]))
275
277
276 if self.ui.debugflag:
278 if self.ui.debugflag:
277 self.ui.write(_("manifest: %d:%s\n") %
279 self.ui.write(_("manifest: %d:%s\n") %
278 (self.repo.manifest.rev(changes[0]), hex(changes[0])))
280 (self.repo.manifest.rev(changes[0]), hex(changes[0])))
279 self.ui.write(_("user: %s\n") % changes[1])
281 self.ui.write(_("user: %s\n") % changes[1])
280 self.ui.write(_("date: %s\n") % date)
282 self.ui.write(_("date: %s\n") % date)
281
283
282 if self.ui.debugflag:
284 if self.ui.debugflag:
283 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
285 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
284 for key, value in zip([_("files:"), _("files+:"), _("files-:")],
286 for key, value in zip([_("files:"), _("files+:"), _("files-:")],
285 files):
287 files):
286 if value:
288 if value:
287 self.ui.write("%-12s %s\n" % (key, " ".join(value)))
289 self.ui.write("%-12s %s\n" % (key, " ".join(value)))
288 elif changes[3] and self.ui.verbose:
290 elif changes[3] and self.ui.verbose:
289 self.ui.write(_("files: %s\n") % " ".join(changes[3]))
291 self.ui.write(_("files: %s\n") % " ".join(changes[3]))
290 if copies and self.ui.verbose:
292 if copies and self.ui.verbose:
291 copies = ['%s (%s)' % c for c in copies]
293 copies = ['%s (%s)' % c for c in copies]
292 self.ui.write(_("copies: %s\n") % ' '.join(copies))
294 self.ui.write(_("copies: %s\n") % ' '.join(copies))
293
295
294 if extra and self.ui.debugflag:
296 if extra and self.ui.debugflag:
295 extraitems = extra.items()
297 extraitems = extra.items()
296 extraitems.sort()
298 extraitems.sort()
297 for key, value in extraitems:
299 for key, value in extraitems:
298 self.ui.write(_("extra: %s=%s\n")
300 self.ui.write(_("extra: %s=%s\n")
299 % (key, value.encode('string_escape')))
301 % (key, value.encode('string_escape')))
300
302
301 description = changes[4].strip()
303 description = changes[4].strip()
302 if description:
304 if description:
303 if self.ui.verbose:
305 if self.ui.verbose:
304 self.ui.write(_("description:\n"))
306 self.ui.write(_("description:\n"))
305 self.ui.write(description)
307 self.ui.write(description)
306 self.ui.write("\n\n")
308 self.ui.write("\n\n")
307 else:
309 else:
308 self.ui.write(_("summary: %s\n") %
310 self.ui.write(_("summary: %s\n") %
309 description.splitlines()[0])
311 description.splitlines()[0])
310 self.ui.write("\n")
312 self.ui.write("\n")
311
313
312 self.showpatch(changenode)
314 self.showpatch(changenode)
313
315
314 def showpatch(self, node):
316 def showpatch(self, node):
315 if self.patch:
317 if self.patch:
316 prev = self.repo.changelog.parents(node)[0]
318 prev = self.repo.changelog.parents(node)[0]
317 patch.diff(self.repo, prev, node, match=self.patch, fp=self.ui)
319 patch.diff(self.repo, prev, node, match=self.patch, fp=self.ui)
318 self.ui.write("\n")
320 self.ui.write("\n")
319
321
320 class changeset_templater(changeset_printer):
322 class changeset_templater(changeset_printer):
321 '''format changeset information.'''
323 '''format changeset information.'''
322
324
323 def __init__(self, ui, repo, patch, brinfo, mapfile, buffered):
325 def __init__(self, ui, repo, patch, brinfo, mapfile, buffered):
324 changeset_printer.__init__(self, ui, repo, patch, brinfo, buffered)
326 changeset_printer.__init__(self, ui, repo, patch, brinfo, buffered)
325 self.t = templater.templater(mapfile, templater.common_filters,
327 self.t = templater.templater(mapfile, templater.common_filters,
326 cache={'parent': '{rev}:{node|short} ',
328 cache={'parent': '{rev}:{node|short} ',
327 'manifest': '{rev}:{node|short}',
329 'manifest': '{rev}:{node|short}',
328 'filecopy': '{name} ({source})'})
330 'filecopy': '{name} ({source})'})
329
331
330 def use_template(self, t):
332 def use_template(self, t):
331 '''set template string to use'''
333 '''set template string to use'''
332 self.t.cache['changeset'] = t
334 self.t.cache['changeset'] = t
333
335
334 def _show(self, rev, changenode, copies, props):
336 def _show(self, rev, changenode, copies, props):
335 '''show a single changeset or file revision'''
337 '''show a single changeset or file revision'''
336 log = self.repo.changelog
338 log = self.repo.changelog
337 if changenode is None:
339 if changenode is None:
338 changenode = log.node(rev)
340 changenode = log.node(rev)
339 elif not rev:
341 elif not rev:
340 rev = log.rev(changenode)
342 rev = log.rev(changenode)
341
343
342 changes = log.read(changenode)
344 changes = log.read(changenode)
343
345
344 def showlist(name, values, plural=None, **args):
346 def showlist(name, values, plural=None, **args):
345 '''expand set of values.
347 '''expand set of values.
346 name is name of key in template map.
348 name is name of key in template map.
347 values is list of strings or dicts.
349 values is list of strings or dicts.
348 plural is plural of name, if not simply name + 's'.
350 plural is plural of name, if not simply name + 's'.
349
351
350 expansion works like this, given name 'foo'.
352 expansion works like this, given name 'foo'.
351
353
352 if values is empty, expand 'no_foos'.
354 if values is empty, expand 'no_foos'.
353
355
354 if 'foo' not in template map, return values as a string,
356 if 'foo' not in template map, return values as a string,
355 joined by space.
357 joined by space.
356
358
357 expand 'start_foos'.
359 expand 'start_foos'.
358
360
359 for each value, expand 'foo'. if 'last_foo' in template
361 for each value, expand 'foo'. if 'last_foo' in template
360 map, expand it instead of 'foo' for last key.
362 map, expand it instead of 'foo' for last key.
361
363
362 expand 'end_foos'.
364 expand 'end_foos'.
363 '''
365 '''
364 if plural: names = plural
366 if plural: names = plural
365 else: names = name + 's'
367 else: names = name + 's'
366 if not values:
368 if not values:
367 noname = 'no_' + names
369 noname = 'no_' + names
368 if noname in self.t:
370 if noname in self.t:
369 yield self.t(noname, **args)
371 yield self.t(noname, **args)
370 return
372 return
371 if name not in self.t:
373 if name not in self.t:
372 if isinstance(values[0], str):
374 if isinstance(values[0], str):
373 yield ' '.join(values)
375 yield ' '.join(values)
374 else:
376 else:
375 for v in values:
377 for v in values:
376 yield dict(v, **args)
378 yield dict(v, **args)
377 return
379 return
378 startname = 'start_' + names
380 startname = 'start_' + names
379 if startname in self.t:
381 if startname in self.t:
380 yield self.t(startname, **args)
382 yield self.t(startname, **args)
381 vargs = args.copy()
383 vargs = args.copy()
382 def one(v, tag=name):
384 def one(v, tag=name):
383 try:
385 try:
384 vargs.update(v)
386 vargs.update(v)
385 except (AttributeError, ValueError):
387 except (AttributeError, ValueError):
386 try:
388 try:
387 for a, b in v:
389 for a, b in v:
388 vargs[a] = b
390 vargs[a] = b
389 except ValueError:
391 except ValueError:
390 vargs[name] = v
392 vargs[name] = v
391 return self.t(tag, **vargs)
393 return self.t(tag, **vargs)
392 lastname = 'last_' + name
394 lastname = 'last_' + name
393 if lastname in self.t:
395 if lastname in self.t:
394 last = values.pop()
396 last = values.pop()
395 else:
397 else:
396 last = None
398 last = None
397 for v in values:
399 for v in values:
398 yield one(v)
400 yield one(v)
399 if last is not None:
401 if last is not None:
400 yield one(last, tag=lastname)
402 yield one(last, tag=lastname)
401 endname = 'end_' + names
403 endname = 'end_' + names
402 if endname in self.t:
404 if endname in self.t:
403 yield self.t(endname, **args)
405 yield self.t(endname, **args)
404
406
405 def showbranches(**args):
407 def showbranches(**args):
406 branch = changes[5].get("branch")
408 branch = changes[5].get("branch")
407 if branch:
409 if branch:
408 branch = util.tolocal(branch)
410 branch = util.tolocal(branch)
409 return showlist('branch', [branch], plural='branches', **args)
411 return showlist('branch', [branch], plural='branches', **args)
410 # add old style branches if requested
412 # add old style branches if requested
411 if self.brinfo:
413 if self.brinfo:
412 br = self.repo.branchlookup([changenode])
414 br = self.repo.branchlookup([changenode])
413 if changenode in br:
415 if changenode in br:
414 return showlist('branch', br[changenode],
416 return showlist('branch', br[changenode],
415 plural='branches', **args)
417 plural='branches', **args)
416
418
417 def showparents(**args):
419 def showparents(**args):
418 parents = [[('rev', log.rev(p)), ('node', hex(p))]
420 parents = [[('rev', log.rev(p)), ('node', hex(p))]
419 for p in log.parents(changenode)
421 for p in log.parents(changenode)
420 if self.ui.debugflag or p != nullid]
422 if self.ui.debugflag or p != nullid]
421 if (not self.ui.debugflag and len(parents) == 1 and
423 if (not self.ui.debugflag and len(parents) == 1 and
422 parents[0][0][1] == rev - 1):
424 parents[0][0][1] == rev - 1):
423 return
425 return
424 return showlist('parent', parents, **args)
426 return showlist('parent', parents, **args)
425
427
426 def showtags(**args):
428 def showtags(**args):
427 return showlist('tag', self.repo.nodetags(changenode), **args)
429 return showlist('tag', self.repo.nodetags(changenode), **args)
428
430
429 def showextras(**args):
431 def showextras(**args):
430 extras = changes[5].items()
432 extras = changes[5].items()
431 extras.sort()
433 extras.sort()
432 for key, value in extras:
434 for key, value in extras:
433 args = args.copy()
435 args = args.copy()
434 args.update(dict(key=key, value=value))
436 args.update(dict(key=key, value=value))
435 yield self.t('extra', **args)
437 yield self.t('extra', **args)
436
438
437 def showcopies(**args):
439 def showcopies(**args):
438 c = [{'name': x[0], 'source': x[1]} for x in copies]
440 c = [{'name': x[0], 'source': x[1]} for x in copies]
439 return showlist('file_copy', c, plural='file_copies', **args)
441 return showlist('file_copy', c, plural='file_copies', **args)
440
442
441 if self.ui.debugflag:
443 if self.ui.debugflag:
442 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
444 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
443 def showfiles(**args):
445 def showfiles(**args):
444 return showlist('file', files[0], **args)
446 return showlist('file', files[0], **args)
445 def showadds(**args):
447 def showadds(**args):
446 return showlist('file_add', files[1], **args)
448 return showlist('file_add', files[1], **args)
447 def showdels(**args):
449 def showdels(**args):
448 return showlist('file_del', files[2], **args)
450 return showlist('file_del', files[2], **args)
449 def showmanifest(**args):
451 def showmanifest(**args):
450 args = args.copy()
452 args = args.copy()
451 args.update(dict(rev=self.repo.manifest.rev(changes[0]),
453 args.update(dict(rev=self.repo.manifest.rev(changes[0]),
452 node=hex(changes[0])))
454 node=hex(changes[0])))
453 return self.t('manifest', **args)
455 return self.t('manifest', **args)
454 else:
456 else:
455 def showfiles(**args):
457 def showfiles(**args):
456 return showlist('file', changes[3], **args)
458 return showlist('file', changes[3], **args)
457 showadds = ''
459 showadds = ''
458 showdels = ''
460 showdels = ''
459 showmanifest = ''
461 showmanifest = ''
460
462
461 defprops = {
463 defprops = {
462 'author': changes[1],
464 'author': changes[1],
463 'branches': showbranches,
465 'branches': showbranches,
464 'date': changes[2],
466 'date': changes[2],
465 'desc': changes[4],
467 'desc': changes[4],
466 'file_adds': showadds,
468 'file_adds': showadds,
467 'file_dels': showdels,
469 'file_dels': showdels,
468 'files': showfiles,
470 'files': showfiles,
469 'file_copies': showcopies,
471 'file_copies': showcopies,
470 'manifest': showmanifest,
472 'manifest': showmanifest,
471 'node': hex(changenode),
473 'node': hex(changenode),
472 'parents': showparents,
474 'parents': showparents,
473 'rev': rev,
475 'rev': rev,
474 'tags': showtags,
476 'tags': showtags,
475 'extras': showextras,
477 'extras': showextras,
476 }
478 }
477 props = props.copy()
479 props = props.copy()
478 props.update(defprops)
480 props.update(defprops)
479
481
480 try:
482 try:
481 if self.ui.debugflag and 'header_debug' in self.t:
483 if self.ui.debugflag and 'header_debug' in self.t:
482 key = 'header_debug'
484 key = 'header_debug'
483 elif self.ui.quiet and 'header_quiet' in self.t:
485 elif self.ui.quiet and 'header_quiet' in self.t:
484 key = 'header_quiet'
486 key = 'header_quiet'
485 elif self.ui.verbose and 'header_verbose' in self.t:
487 elif self.ui.verbose and 'header_verbose' in self.t:
486 key = 'header_verbose'
488 key = 'header_verbose'
487 elif 'header' in self.t:
489 elif 'header' in self.t:
488 key = 'header'
490 key = 'header'
489 else:
491 else:
490 key = ''
492 key = ''
491 if key:
493 if key:
492 h = templater.stringify(self.t(key, **props))
494 h = templater.stringify(self.t(key, **props))
493 if self.buffered:
495 if self.buffered:
494 self.header[rev] = h
496 self.header[rev] = h
495 else:
497 else:
496 self.ui.write(h)
498 self.ui.write(h)
497 if self.ui.debugflag and 'changeset_debug' in self.t:
499 if self.ui.debugflag and 'changeset_debug' in self.t:
498 key = 'changeset_debug'
500 key = 'changeset_debug'
499 elif self.ui.quiet and 'changeset_quiet' in self.t:
501 elif self.ui.quiet and 'changeset_quiet' in self.t:
500 key = 'changeset_quiet'
502 key = 'changeset_quiet'
501 elif self.ui.verbose and 'changeset_verbose' in self.t:
503 elif self.ui.verbose and 'changeset_verbose' in self.t:
502 key = 'changeset_verbose'
504 key = 'changeset_verbose'
503 else:
505 else:
504 key = 'changeset'
506 key = 'changeset'
505 self.ui.write(templater.stringify(self.t(key, **props)))
507 self.ui.write(templater.stringify(self.t(key, **props)))
506 self.showpatch(changenode)
508 self.showpatch(changenode)
507 except KeyError, inst:
509 except KeyError, inst:
508 raise util.Abort(_("%s: no key named '%s'") % (self.t.mapfile,
510 raise util.Abort(_("%s: no key named '%s'") % (self.t.mapfile,
509 inst.args[0]))
511 inst.args[0]))
510 except SyntaxError, inst:
512 except SyntaxError, inst:
511 raise util.Abort(_('%s: %s') % (self.t.mapfile, inst.args[0]))
513 raise util.Abort(_('%s: %s') % (self.t.mapfile, inst.args[0]))
512
514
513 def show_changeset(ui, repo, opts, buffered=False, matchfn=False):
515 def show_changeset(ui, repo, opts, buffered=False, matchfn=False):
514 """show one changeset using template or regular display.
516 """show one changeset using template or regular display.
515
517
516 Display format will be the first non-empty hit of:
518 Display format will be the first non-empty hit of:
517 1. option 'template'
519 1. option 'template'
518 2. option 'style'
520 2. option 'style'
519 3. [ui] setting 'logtemplate'
521 3. [ui] setting 'logtemplate'
520 4. [ui] setting 'style'
522 4. [ui] setting 'style'
521 If all of these values are either the unset or the empty string,
523 If all of these values are either the unset or the empty string,
522 regular display via changeset_printer() is done.
524 regular display via changeset_printer() is done.
523 """
525 """
524 # options
526 # options
525 patch = False
527 patch = False
526 if opts.get('patch'):
528 if opts.get('patch'):
527 patch = matchfn or util.always
529 patch = matchfn or util.always
528
530
529 br = None
531 br = None
530 if opts.get('branches'):
532 if opts.get('branches'):
531 ui.warn(_("the --branches option is deprecated, "
533 ui.warn(_("the --branches option is deprecated, "
532 "please use 'hg branches' instead\n"))
534 "please use 'hg branches' instead\n"))
533 br = True
535 br = True
534 tmpl = opts.get('template')
536 tmpl = opts.get('template')
535 mapfile = None
537 mapfile = None
536 if tmpl:
538 if tmpl:
537 tmpl = templater.parsestring(tmpl, quoted=False)
539 tmpl = templater.parsestring(tmpl, quoted=False)
538 else:
540 else:
539 mapfile = opts.get('style')
541 mapfile = opts.get('style')
540 # ui settings
542 # ui settings
541 if not mapfile:
543 if not mapfile:
542 tmpl = ui.config('ui', 'logtemplate')
544 tmpl = ui.config('ui', 'logtemplate')
543 if tmpl:
545 if tmpl:
544 tmpl = templater.parsestring(tmpl)
546 tmpl = templater.parsestring(tmpl)
545 else:
547 else:
546 mapfile = ui.config('ui', 'style')
548 mapfile = ui.config('ui', 'style')
547
549
548 if tmpl or mapfile:
550 if tmpl or mapfile:
549 if mapfile:
551 if mapfile:
550 if not os.path.split(mapfile)[0]:
552 if not os.path.split(mapfile)[0]:
551 mapname = (templater.templatepath('map-cmdline.' + mapfile)
553 mapname = (templater.templatepath('map-cmdline.' + mapfile)
552 or templater.templatepath(mapfile))
554 or templater.templatepath(mapfile))
553 if mapname: mapfile = mapname
555 if mapname: mapfile = mapname
554 try:
556 try:
555 t = changeset_templater(ui, repo, patch, br, mapfile, buffered)
557 t = changeset_templater(ui, repo, patch, br, mapfile, buffered)
556 except SyntaxError, inst:
558 except SyntaxError, inst:
557 raise util.Abort(inst.args[0])
559 raise util.Abort(inst.args[0])
558 if tmpl: t.use_template(tmpl)
560 if tmpl: t.use_template(tmpl)
559 return t
561 return t
560 return changeset_printer(ui, repo, patch, br, buffered)
562 return changeset_printer(ui, repo, patch, br, buffered)
561
563
562 def finddate(ui, repo, date):
564 def finddate(ui, repo, date):
563 """Find the tipmost changeset that matches the given date spec"""
565 """Find the tipmost changeset that matches the given date spec"""
564 df = util.matchdate(date + " to " + date)
566 df = util.matchdate(date + " to " + date)
565 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
567 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
566 changeiter, matchfn = walkchangerevs(ui, repo, [], get, {'rev':None})
568 changeiter, matchfn = walkchangerevs(ui, repo, [], get, {'rev':None})
567 results = {}
569 results = {}
568 for st, rev, fns in changeiter:
570 for st, rev, fns in changeiter:
569 if st == 'add':
571 if st == 'add':
570 d = get(rev)[2]
572 d = get(rev)[2]
571 if df(d[0]):
573 if df(d[0]):
572 results[rev] = d
574 results[rev] = d
573 elif st == 'iter':
575 elif st == 'iter':
574 if rev in results:
576 if rev in results:
575 ui.status("Found revision %s from %s\n" %
577 ui.status("Found revision %s from %s\n" %
576 (rev, util.datestr(results[rev])))
578 (rev, util.datestr(results[rev])))
577 return str(rev)
579 return str(rev)
578
580
579 raise util.Abort(_("revision matching date not found"))
581 raise util.Abort(_("revision matching date not found"))
580
582
581 def walkchangerevs(ui, repo, pats, change, opts):
583 def walkchangerevs(ui, repo, pats, change, opts):
582 '''Iterate over files and the revs they changed in.
584 '''Iterate over files and the revs they changed in.
583
585
584 Callers most commonly need to iterate backwards over the history
586 Callers most commonly need to iterate backwards over the history
585 it is interested in. Doing so has awful (quadratic-looking)
587 it is interested in. Doing so has awful (quadratic-looking)
586 performance, so we use iterators in a "windowed" way.
588 performance, so we use iterators in a "windowed" way.
587
589
588 We walk a window of revisions in the desired order. Within the
590 We walk a window of revisions in the desired order. Within the
589 window, we first walk forwards to gather data, then in the desired
591 window, we first walk forwards to gather data, then in the desired
590 order (usually backwards) to display it.
592 order (usually backwards) to display it.
591
593
592 This function returns an (iterator, matchfn) tuple. The iterator
594 This function returns an (iterator, matchfn) tuple. The iterator
593 yields 3-tuples. They will be of one of the following forms:
595 yields 3-tuples. They will be of one of the following forms:
594
596
595 "window", incrementing, lastrev: stepping through a window,
597 "window", incrementing, lastrev: stepping through a window,
596 positive if walking forwards through revs, last rev in the
598 positive if walking forwards through revs, last rev in the
597 sequence iterated over - use to reset state for the current window
599 sequence iterated over - use to reset state for the current window
598
600
599 "add", rev, fns: out-of-order traversal of the given file names
601 "add", rev, fns: out-of-order traversal of the given file names
600 fns, which changed during revision rev - use to gather data for
602 fns, which changed during revision rev - use to gather data for
601 possible display
603 possible display
602
604
603 "iter", rev, None: in-order traversal of the revs earlier iterated
605 "iter", rev, None: in-order traversal of the revs earlier iterated
604 over with "add" - use to display data'''
606 over with "add" - use to display data'''
605
607
606 def increasing_windows(start, end, windowsize=8, sizelimit=512):
608 def increasing_windows(start, end, windowsize=8, sizelimit=512):
607 if start < end:
609 if start < end:
608 while start < end:
610 while start < end:
609 yield start, min(windowsize, end-start)
611 yield start, min(windowsize, end-start)
610 start += windowsize
612 start += windowsize
611 if windowsize < sizelimit:
613 if windowsize < sizelimit:
612 windowsize *= 2
614 windowsize *= 2
613 else:
615 else:
614 while start > end:
616 while start > end:
615 yield start, min(windowsize, start-end-1)
617 yield start, min(windowsize, start-end-1)
616 start -= windowsize
618 start -= windowsize
617 if windowsize < sizelimit:
619 if windowsize < sizelimit:
618 windowsize *= 2
620 windowsize *= 2
619
621
620 files, matchfn, anypats = matchpats(repo, pats, opts)
622 files, matchfn, anypats = matchpats(repo, pats, opts)
621 follow = opts.get('follow') or opts.get('follow_first')
623 follow = opts.get('follow') or opts.get('follow_first')
622
624
623 if repo.changelog.count() == 0:
625 if repo.changelog.count() == 0:
624 return [], matchfn
626 return [], matchfn
625
627
626 if follow:
628 if follow:
627 defrange = '%s:0' % repo.changectx().rev()
629 defrange = '%s:0' % repo.changectx().rev()
628 else:
630 else:
629 defrange = 'tip:0'
631 defrange = 'tip:0'
630 revs = revrange(repo, opts['rev'] or [defrange])
632 revs = revrange(repo, opts['rev'] or [defrange])
631 wanted = {}
633 wanted = {}
632 slowpath = anypats or opts.get('removed')
634 slowpath = anypats or opts.get('removed')
633 fncache = {}
635 fncache = {}
634
636
635 if not slowpath and not files:
637 if not slowpath and not files:
636 # No files, no patterns. Display all revs.
638 # No files, no patterns. Display all revs.
637 wanted = dict.fromkeys(revs)
639 wanted = dict.fromkeys(revs)
638 copies = []
640 copies = []
639 if not slowpath:
641 if not slowpath:
640 # Only files, no patterns. Check the history of each file.
642 # Only files, no patterns. Check the history of each file.
641 def filerevgen(filelog, node):
643 def filerevgen(filelog, node):
642 cl_count = repo.changelog.count()
644 cl_count = repo.changelog.count()
643 if node is None:
645 if node is None:
644 last = filelog.count() - 1
646 last = filelog.count() - 1
645 else:
647 else:
646 last = filelog.rev(node)
648 last = filelog.rev(node)
647 for i, window in increasing_windows(last, nullrev):
649 for i, window in increasing_windows(last, nullrev):
648 revs = []
650 revs = []
649 for j in xrange(i - window, i + 1):
651 for j in xrange(i - window, i + 1):
650 n = filelog.node(j)
652 n = filelog.node(j)
651 revs.append((filelog.linkrev(n),
653 revs.append((filelog.linkrev(n),
652 follow and filelog.renamed(n)))
654 follow and filelog.renamed(n)))
653 revs.reverse()
655 revs.reverse()
654 for rev in revs:
656 for rev in revs:
655 # only yield rev for which we have the changelog, it can
657 # only yield rev for which we have the changelog, it can
656 # happen while doing "hg log" during a pull or commit
658 # happen while doing "hg log" during a pull or commit
657 if rev[0] < cl_count:
659 if rev[0] < cl_count:
658 yield rev
660 yield rev
659 def iterfiles():
661 def iterfiles():
660 for filename in files:
662 for filename in files:
661 yield filename, None
663 yield filename, None
662 for filename_node in copies:
664 for filename_node in copies:
663 yield filename_node
665 yield filename_node
664 minrev, maxrev = min(revs), max(revs)
666 minrev, maxrev = min(revs), max(revs)
665 for file_, node in iterfiles():
667 for file_, node in iterfiles():
666 filelog = repo.file(file_)
668 filelog = repo.file(file_)
667 # A zero count may be a directory or deleted file, so
669 # A zero count may be a directory or deleted file, so
668 # try to find matching entries on the slow path.
670 # try to find matching entries on the slow path.
669 if filelog.count() == 0:
671 if filelog.count() == 0:
670 slowpath = True
672 slowpath = True
671 break
673 break
672 for rev, copied in filerevgen(filelog, node):
674 for rev, copied in filerevgen(filelog, node):
673 if rev <= maxrev:
675 if rev <= maxrev:
674 if rev < minrev:
676 if rev < minrev:
675 break
677 break
676 fncache.setdefault(rev, [])
678 fncache.setdefault(rev, [])
677 fncache[rev].append(file_)
679 fncache[rev].append(file_)
678 wanted[rev] = 1
680 wanted[rev] = 1
679 if follow and copied:
681 if follow and copied:
680 copies.append(copied)
682 copies.append(copied)
681 if slowpath:
683 if slowpath:
682 if follow:
684 if follow:
683 raise util.Abort(_('can only follow copies/renames for explicit '
685 raise util.Abort(_('can only follow copies/renames for explicit '
684 'file names'))
686 'file names'))
685
687
686 # The slow path checks files modified in every changeset.
688 # The slow path checks files modified in every changeset.
687 def changerevgen():
689 def changerevgen():
688 for i, window in increasing_windows(repo.changelog.count()-1,
690 for i, window in increasing_windows(repo.changelog.count()-1,
689 nullrev):
691 nullrev):
690 for j in xrange(i - window, i + 1):
692 for j in xrange(i - window, i + 1):
691 yield j, change(j)[3]
693 yield j, change(j)[3]
692
694
693 for rev, changefiles in changerevgen():
695 for rev, changefiles in changerevgen():
694 matches = filter(matchfn, changefiles)
696 matches = filter(matchfn, changefiles)
695 if matches:
697 if matches:
696 fncache[rev] = matches
698 fncache[rev] = matches
697 wanted[rev] = 1
699 wanted[rev] = 1
698
700
699 class followfilter:
701 class followfilter:
700 def __init__(self, onlyfirst=False):
702 def __init__(self, onlyfirst=False):
701 self.startrev = nullrev
703 self.startrev = nullrev
702 self.roots = []
704 self.roots = []
703 self.onlyfirst = onlyfirst
705 self.onlyfirst = onlyfirst
704
706
705 def match(self, rev):
707 def match(self, rev):
706 def realparents(rev):
708 def realparents(rev):
707 if self.onlyfirst:
709 if self.onlyfirst:
708 return repo.changelog.parentrevs(rev)[0:1]
710 return repo.changelog.parentrevs(rev)[0:1]
709 else:
711 else:
710 return filter(lambda x: x != nullrev,
712 return filter(lambda x: x != nullrev,
711 repo.changelog.parentrevs(rev))
713 repo.changelog.parentrevs(rev))
712
714
713 if self.startrev == nullrev:
715 if self.startrev == nullrev:
714 self.startrev = rev
716 self.startrev = rev
715 return True
717 return True
716
718
717 if rev > self.startrev:
719 if rev > self.startrev:
718 # forward: all descendants
720 # forward: all descendants
719 if not self.roots:
721 if not self.roots:
720 self.roots.append(self.startrev)
722 self.roots.append(self.startrev)
721 for parent in realparents(rev):
723 for parent in realparents(rev):
722 if parent in self.roots:
724 if parent in self.roots:
723 self.roots.append(rev)
725 self.roots.append(rev)
724 return True
726 return True
725 else:
727 else:
726 # backwards: all parents
728 # backwards: all parents
727 if not self.roots:
729 if not self.roots:
728 self.roots.extend(realparents(self.startrev))
730 self.roots.extend(realparents(self.startrev))
729 if rev in self.roots:
731 if rev in self.roots:
730 self.roots.remove(rev)
732 self.roots.remove(rev)
731 self.roots.extend(realparents(rev))
733 self.roots.extend(realparents(rev))
732 return True
734 return True
733
735
734 return False
736 return False
735
737
736 # it might be worthwhile to do this in the iterator if the rev range
738 # it might be worthwhile to do this in the iterator if the rev range
737 # is descending and the prune args are all within that range
739 # is descending and the prune args are all within that range
738 for rev in opts.get('prune', ()):
740 for rev in opts.get('prune', ()):
739 rev = repo.changelog.rev(repo.lookup(rev))
741 rev = repo.changelog.rev(repo.lookup(rev))
740 ff = followfilter()
742 ff = followfilter()
741 stop = min(revs[0], revs[-1])
743 stop = min(revs[0], revs[-1])
742 for x in xrange(rev, stop-1, -1):
744 for x in xrange(rev, stop-1, -1):
743 if ff.match(x) and x in wanted:
745 if ff.match(x) and x in wanted:
744 del wanted[x]
746 del wanted[x]
745
747
746 def iterate():
748 def iterate():
747 if follow and not files:
749 if follow and not files:
748 ff = followfilter(onlyfirst=opts.get('follow_first'))
750 ff = followfilter(onlyfirst=opts.get('follow_first'))
749 def want(rev):
751 def want(rev):
750 if ff.match(rev) and rev in wanted:
752 if ff.match(rev) and rev in wanted:
751 return True
753 return True
752 return False
754 return False
753 else:
755 else:
754 def want(rev):
756 def want(rev):
755 return rev in wanted
757 return rev in wanted
756
758
757 for i, window in increasing_windows(0, len(revs)):
759 for i, window in increasing_windows(0, len(revs)):
758 yield 'window', revs[0] < revs[-1], revs[-1]
760 yield 'window', revs[0] < revs[-1], revs[-1]
759 nrevs = [rev for rev in revs[i:i+window] if want(rev)]
761 nrevs = [rev for rev in revs[i:i+window] if want(rev)]
760 srevs = list(nrevs)
762 srevs = list(nrevs)
761 srevs.sort()
763 srevs.sort()
762 for rev in srevs:
764 for rev in srevs:
763 fns = fncache.get(rev)
765 fns = fncache.get(rev)
764 if not fns:
766 if not fns:
765 def fns_generator():
767 def fns_generator():
766 for f in change(rev)[3]:
768 for f in change(rev)[3]:
767 if matchfn(f):
769 if matchfn(f):
768 yield f
770 yield f
769 fns = fns_generator()
771 fns = fns_generator()
770 yield 'add', rev, fns
772 yield 'add', rev, fns
771 for rev in nrevs:
773 for rev in nrevs:
772 yield 'iter', rev, None
774 yield 'iter', rev, None
773 return iterate(), matchfn
775 return iterate(), matchfn
@@ -1,3338 +1,3339 b''
1 # commands.py - command processing for mercurial
1 # commands.py - command processing for mercurial
2 #
2 #
3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 from demandload import demandload
8 from demandload import demandload
9 from node import *
9 from node import *
10 from i18n import gettext as _
10 from i18n import gettext as _
11 demandload(globals(), "bisect os re sys signal imp urllib pdb shlex stat")
11 demandload(globals(), "bisect os re sys signal imp urllib pdb shlex stat")
12 demandload(globals(), "fancyopts ui hg util lock revlog bundlerepo")
12 demandload(globals(), "fancyopts ui hg util lock revlog bundlerepo")
13 demandload(globals(), "difflib patch time help mdiff tempfile")
13 demandload(globals(), "difflib patch time help mdiff tempfile")
14 demandload(globals(), "traceback errno version atexit socket")
14 demandload(globals(), "traceback errno version atexit socket")
15 demandload(globals(), "archival changegroup cmdutil hgweb.server sshserver")
15 demandload(globals(), "archival changegroup cmdutil hgweb.server sshserver")
16
16
17 class UnknownCommand(Exception):
17 class UnknownCommand(Exception):
18 """Exception raised if command is not in the command table."""
18 """Exception raised if command is not in the command table."""
19 class AmbiguousCommand(Exception):
19 class AmbiguousCommand(Exception):
20 """Exception raised if command shortcut matches more than one command."""
20 """Exception raised if command shortcut matches more than one command."""
21
21
22 def bail_if_changed(repo):
22 def bail_if_changed(repo):
23 modified, added, removed, deleted = repo.status()[:4]
23 modified, added, removed, deleted = repo.status()[:4]
24 if modified or added or removed or deleted:
24 if modified or added or removed or deleted:
25 raise util.Abort(_("outstanding uncommitted changes"))
25 raise util.Abort(_("outstanding uncommitted changes"))
26
26
27 def logmessage(opts):
27 def logmessage(opts):
28 """ get the log message according to -m and -l option """
28 """ get the log message according to -m and -l option """
29 message = opts['message']
29 message = opts['message']
30 logfile = opts['logfile']
30 logfile = opts['logfile']
31
31
32 if message and logfile:
32 if message and logfile:
33 raise util.Abort(_('options --message and --logfile are mutually '
33 raise util.Abort(_('options --message and --logfile are mutually '
34 'exclusive'))
34 'exclusive'))
35 if not message and logfile:
35 if not message and logfile:
36 try:
36 try:
37 if logfile == '-':
37 if logfile == '-':
38 message = sys.stdin.read()
38 message = sys.stdin.read()
39 else:
39 else:
40 message = open(logfile).read()
40 message = open(logfile).read()
41 except IOError, inst:
41 except IOError, inst:
42 raise util.Abort(_("can't read commit message '%s': %s") %
42 raise util.Abort(_("can't read commit message '%s': %s") %
43 (logfile, inst.strerror))
43 (logfile, inst.strerror))
44 return message
44 return message
45
45
46 def setremoteconfig(ui, opts):
46 def setremoteconfig(ui, opts):
47 "copy remote options to ui tree"
47 "copy remote options to ui tree"
48 if opts.get('ssh'):
48 if opts.get('ssh'):
49 ui.setconfig("ui", "ssh", opts['ssh'])
49 ui.setconfig("ui", "ssh", opts['ssh'])
50 if opts.get('remotecmd'):
50 if opts.get('remotecmd'):
51 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
51 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
52
52
53 # Commands start here, listed alphabetically
53 # Commands start here, listed alphabetically
54
54
55 def add(ui, repo, *pats, **opts):
55 def add(ui, repo, *pats, **opts):
56 """add the specified files on the next commit
56 """add the specified files on the next commit
57
57
58 Schedule files to be version controlled and added to the repository.
58 Schedule files to be version controlled and added to the repository.
59
59
60 The files will be added to the repository at the next commit. To
60 The files will be added to the repository at the next commit. To
61 undo an add before that, see hg revert.
61 undo an add before that, see hg revert.
62
62
63 If no names are given, add all files in the repository.
63 If no names are given, add all files in the repository.
64 """
64 """
65
65
66 names = []
66 names = []
67 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
67 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
68 if exact:
68 if exact:
69 if ui.verbose:
69 if ui.verbose:
70 ui.status(_('adding %s\n') % rel)
70 ui.status(_('adding %s\n') % rel)
71 names.append(abs)
71 names.append(abs)
72 elif repo.dirstate.state(abs) == '?':
72 elif repo.dirstate.state(abs) == '?':
73 ui.status(_('adding %s\n') % rel)
73 ui.status(_('adding %s\n') % rel)
74 names.append(abs)
74 names.append(abs)
75 if not opts.get('dry_run'):
75 if not opts.get('dry_run'):
76 repo.add(names)
76 repo.add(names)
77
77
78 def addremove(ui, repo, *pats, **opts):
78 def addremove(ui, repo, *pats, **opts):
79 """add all new files, delete all missing files
79 """add all new files, delete all missing files
80
80
81 Add all new files and remove all missing files from the repository.
81 Add all new files and remove all missing files from the repository.
82
82
83 New files are ignored if they match any of the patterns in .hgignore. As
83 New files are ignored if they match any of the patterns in .hgignore. As
84 with add, these changes take effect at the next commit.
84 with add, these changes take effect at the next commit.
85
85
86 Use the -s option to detect renamed files. With a parameter > 0,
86 Use the -s option to detect renamed files. With a parameter > 0,
87 this compares every removed file with every added file and records
87 this compares every removed file with every added file and records
88 those similar enough as renames. This option takes a percentage
88 those similar enough as renames. This option takes a percentage
89 between 0 (disabled) and 100 (files must be identical) as its
89 between 0 (disabled) and 100 (files must be identical) as its
90 parameter. Detecting renamed files this way can be expensive.
90 parameter. Detecting renamed files this way can be expensive.
91 """
91 """
92 sim = float(opts.get('similarity') or 0)
92 sim = float(opts.get('similarity') or 0)
93 if sim < 0 or sim > 100:
93 if sim < 0 or sim > 100:
94 raise util.Abort(_('similarity must be between 0 and 100'))
94 raise util.Abort(_('similarity must be between 0 and 100'))
95 return cmdutil.addremove(repo, pats, opts, similarity=sim/100.)
95 return cmdutil.addremove(repo, pats, opts, similarity=sim/100.)
96
96
97 def annotate(ui, repo, *pats, **opts):
97 def annotate(ui, repo, *pats, **opts):
98 """show changeset information per file line
98 """show changeset information per file line
99
99
100 List changes in files, showing the revision id responsible for each line
100 List changes in files, showing the revision id responsible for each line
101
101
102 This command is useful to discover who did a change or when a change took
102 This command is useful to discover who did a change or when a change took
103 place.
103 place.
104
104
105 Without the -a option, annotate will avoid processing files it
105 Without the -a option, annotate will avoid processing files it
106 detects as binary. With -a, annotate will generate an annotation
106 detects as binary. With -a, annotate will generate an annotation
107 anyway, probably with undesirable results.
107 anyway, probably with undesirable results.
108 """
108 """
109 getdate = util.cachefunc(lambda x: util.datestr(x.date()))
109 getdate = util.cachefunc(lambda x: util.datestr(x.date()))
110
110
111 if not pats:
111 if not pats:
112 raise util.Abort(_('at least one file name or pattern required'))
112 raise util.Abort(_('at least one file name or pattern required'))
113
113
114 opmap = [['user', lambda x: ui.shortuser(x.user())],
114 opmap = [['user', lambda x: ui.shortuser(x.user())],
115 ['number', lambda x: str(x.rev())],
115 ['number', lambda x: str(x.rev())],
116 ['changeset', lambda x: short(x.node())],
116 ['changeset', lambda x: short(x.node())],
117 ['date', getdate], ['follow', lambda x: x.path()]]
117 ['date', getdate], ['follow', lambda x: x.path()]]
118 if (not opts['user'] and not opts['changeset'] and not opts['date']
118 if (not opts['user'] and not opts['changeset'] and not opts['date']
119 and not opts['follow']):
119 and not opts['follow']):
120 opts['number'] = 1
120 opts['number'] = 1
121
121
122 ctx = repo.changectx(opts['rev'])
122 ctx = repo.changectx(opts['rev'])
123
123
124 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
124 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
125 node=ctx.node()):
125 node=ctx.node()):
126 fctx = ctx.filectx(abs)
126 fctx = ctx.filectx(abs)
127 if not opts['text'] and util.binary(fctx.data()):
127 if not opts['text'] and util.binary(fctx.data()):
128 ui.write(_("%s: binary file\n") % ((pats and rel) or abs))
128 ui.write(_("%s: binary file\n") % ((pats and rel) or abs))
129 continue
129 continue
130
130
131 lines = fctx.annotate(follow=opts.get('follow'))
131 lines = fctx.annotate(follow=opts.get('follow'))
132 pieces = []
132 pieces = []
133
133
134 for o, f in opmap:
134 for o, f in opmap:
135 if opts[o]:
135 if opts[o]:
136 l = [f(n) for n, dummy in lines]
136 l = [f(n) for n, dummy in lines]
137 if l:
137 if l:
138 m = max(map(len, l))
138 m = max(map(len, l))
139 pieces.append(["%*s" % (m, x) for x in l])
139 pieces.append(["%*s" % (m, x) for x in l])
140
140
141 if pieces:
141 if pieces:
142 for p, l in zip(zip(*pieces), lines):
142 for p, l in zip(zip(*pieces), lines):
143 ui.write("%s: %s" % (" ".join(p), l[1]))
143 ui.write("%s: %s" % (" ".join(p), l[1]))
144
144
145 def archive(ui, repo, dest, **opts):
145 def archive(ui, repo, dest, **opts):
146 '''create unversioned archive of a repository revision
146 '''create unversioned archive of a repository revision
147
147
148 By default, the revision used is the parent of the working
148 By default, the revision used is the parent of the working
149 directory; use "-r" to specify a different revision.
149 directory; use "-r" to specify a different revision.
150
150
151 To specify the type of archive to create, use "-t". Valid
151 To specify the type of archive to create, use "-t". Valid
152 types are:
152 types are:
153
153
154 "files" (default): a directory full of files
154 "files" (default): a directory full of files
155 "tar": tar archive, uncompressed
155 "tar": tar archive, uncompressed
156 "tbz2": tar archive, compressed using bzip2
156 "tbz2": tar archive, compressed using bzip2
157 "tgz": tar archive, compressed using gzip
157 "tgz": tar archive, compressed using gzip
158 "uzip": zip archive, uncompressed
158 "uzip": zip archive, uncompressed
159 "zip": zip archive, compressed using deflate
159 "zip": zip archive, compressed using deflate
160
160
161 The exact name of the destination archive or directory is given
161 The exact name of the destination archive or directory is given
162 using a format string; see "hg help export" for details.
162 using a format string; see "hg help export" for details.
163
163
164 Each member added to an archive file has a directory prefix
164 Each member added to an archive file has a directory prefix
165 prepended. Use "-p" to specify a format string for the prefix.
165 prepended. Use "-p" to specify a format string for the prefix.
166 The default is the basename of the archive, with suffixes removed.
166 The default is the basename of the archive, with suffixes removed.
167 '''
167 '''
168
168
169 node = repo.changectx(opts['rev']).node()
169 node = repo.changectx(opts['rev']).node()
170 dest = cmdutil.make_filename(repo, dest, node)
170 dest = cmdutil.make_filename(repo, dest, node)
171 if os.path.realpath(dest) == repo.root:
171 if os.path.realpath(dest) == repo.root:
172 raise util.Abort(_('repository root cannot be destination'))
172 raise util.Abort(_('repository root cannot be destination'))
173 dummy, matchfn, dummy = cmdutil.matchpats(repo, [], opts)
173 dummy, matchfn, dummy = cmdutil.matchpats(repo, [], opts)
174 kind = opts.get('type') or 'files'
174 kind = opts.get('type') or 'files'
175 prefix = opts['prefix']
175 prefix = opts['prefix']
176 if dest == '-':
176 if dest == '-':
177 if kind == 'files':
177 if kind == 'files':
178 raise util.Abort(_('cannot archive plain files to stdout'))
178 raise util.Abort(_('cannot archive plain files to stdout'))
179 dest = sys.stdout
179 dest = sys.stdout
180 if not prefix: prefix = os.path.basename(repo.root) + '-%h'
180 if not prefix: prefix = os.path.basename(repo.root) + '-%h'
181 prefix = cmdutil.make_filename(repo, prefix, node)
181 prefix = cmdutil.make_filename(repo, prefix, node)
182 archival.archive(repo, dest, node, kind, not opts['no_decode'],
182 archival.archive(repo, dest, node, kind, not opts['no_decode'],
183 matchfn, prefix)
183 matchfn, prefix)
184
184
185 def backout(ui, repo, rev, **opts):
185 def backout(ui, repo, rev, **opts):
186 '''reverse effect of earlier changeset
186 '''reverse effect of earlier changeset
187
187
188 Commit the backed out changes as a new changeset. The new
188 Commit the backed out changes as a new changeset. The new
189 changeset is a child of the backed out changeset.
189 changeset is a child of the backed out changeset.
190
190
191 If you back out a changeset other than the tip, a new head is
191 If you back out a changeset other than the tip, a new head is
192 created. This head is the parent of the working directory. If
192 created. This head is the parent of the working directory. If
193 you back out an old changeset, your working directory will appear
193 you back out an old changeset, your working directory will appear
194 old after the backout. You should merge the backout changeset
194 old after the backout. You should merge the backout changeset
195 with another head.
195 with another head.
196
196
197 The --merge option remembers the parent of the working directory
197 The --merge option remembers the parent of the working directory
198 before starting the backout, then merges the new head with that
198 before starting the backout, then merges the new head with that
199 changeset afterwards. This saves you from doing the merge by
199 changeset afterwards. This saves you from doing the merge by
200 hand. The result of this merge is not committed, as for a normal
200 hand. The result of this merge is not committed, as for a normal
201 merge.'''
201 merge.'''
202
202
203 bail_if_changed(repo)
203 bail_if_changed(repo)
204 op1, op2 = repo.dirstate.parents()
204 op1, op2 = repo.dirstate.parents()
205 if op2 != nullid:
205 if op2 != nullid:
206 raise util.Abort(_('outstanding uncommitted merge'))
206 raise util.Abort(_('outstanding uncommitted merge'))
207 node = repo.lookup(rev)
207 node = repo.lookup(rev)
208 p1, p2 = repo.changelog.parents(node)
208 p1, p2 = repo.changelog.parents(node)
209 if p1 == nullid:
209 if p1 == nullid:
210 raise util.Abort(_('cannot back out a change with no parents'))
210 raise util.Abort(_('cannot back out a change with no parents'))
211 if p2 != nullid:
211 if p2 != nullid:
212 if not opts['parent']:
212 if not opts['parent']:
213 raise util.Abort(_('cannot back out a merge changeset without '
213 raise util.Abort(_('cannot back out a merge changeset without '
214 '--parent'))
214 '--parent'))
215 p = repo.lookup(opts['parent'])
215 p = repo.lookup(opts['parent'])
216 if p not in (p1, p2):
216 if p not in (p1, p2):
217 raise util.Abort(_('%s is not a parent of %s') %
217 raise util.Abort(_('%s is not a parent of %s') %
218 (short(p), short(node)))
218 (short(p), short(node)))
219 parent = p
219 parent = p
220 else:
220 else:
221 if opts['parent']:
221 if opts['parent']:
222 raise util.Abort(_('cannot use --parent on non-merge changeset'))
222 raise util.Abort(_('cannot use --parent on non-merge changeset'))
223 parent = p1
223 parent = p1
224 hg.clean(repo, node, show_stats=False)
224 hg.clean(repo, node, show_stats=False)
225 revert_opts = opts.copy()
225 revert_opts = opts.copy()
226 revert_opts['date'] = None
226 revert_opts['date'] = None
227 revert_opts['all'] = True
227 revert_opts['all'] = True
228 revert_opts['rev'] = hex(parent)
228 revert_opts['rev'] = hex(parent)
229 revert(ui, repo, **revert_opts)
229 revert(ui, repo, **revert_opts)
230 commit_opts = opts.copy()
230 commit_opts = opts.copy()
231 commit_opts['addremove'] = False
231 commit_opts['addremove'] = False
232 if not commit_opts['message'] and not commit_opts['logfile']:
232 if not commit_opts['message'] and not commit_opts['logfile']:
233 commit_opts['message'] = _("Backed out changeset %s") % (hex(node))
233 commit_opts['message'] = _("Backed out changeset %s") % (hex(node))
234 commit_opts['force_editor'] = True
234 commit_opts['force_editor'] = True
235 commit(ui, repo, **commit_opts)
235 commit(ui, repo, **commit_opts)
236 def nice(node):
236 def nice(node):
237 return '%d:%s' % (repo.changelog.rev(node), short(node))
237 return '%d:%s' % (repo.changelog.rev(node), short(node))
238 ui.status(_('changeset %s backs out changeset %s\n') %
238 ui.status(_('changeset %s backs out changeset %s\n') %
239 (nice(repo.changelog.tip()), nice(node)))
239 (nice(repo.changelog.tip()), nice(node)))
240 if op1 != node:
240 if op1 != node:
241 if opts['merge']:
241 if opts['merge']:
242 ui.status(_('merging with changeset %s\n') % nice(op1))
242 ui.status(_('merging with changeset %s\n') % nice(op1))
243 n = _lookup(repo, hex(op1))
243 n = _lookup(repo, hex(op1))
244 hg.merge(repo, n)
244 hg.merge(repo, n)
245 else:
245 else:
246 ui.status(_('the backout changeset is a new head - '
246 ui.status(_('the backout changeset is a new head - '
247 'do not forget to merge\n'))
247 'do not forget to merge\n'))
248 ui.status(_('(use "backout --merge" '
248 ui.status(_('(use "backout --merge" '
249 'if you want to auto-merge)\n'))
249 'if you want to auto-merge)\n'))
250
250
251 def branch(ui, repo, label=None):
251 def branch(ui, repo, label=None):
252 """set or show the current branch name
252 """set or show the current branch name
253
253
254 With <name>, set the current branch name. Otherwise, show the
254 With <name>, set the current branch name. Otherwise, show the
255 current branch name.
255 current branch name.
256 """
256 """
257
257
258 if label is not None:
258 if label is not None:
259 repo.opener("branch", "w").write(util.fromlocal(label) + '\n')
259 repo.opener("branch", "w").write(util.fromlocal(label) + '\n')
260 else:
260 else:
261 b = util.tolocal(repo.workingctx().branch())
261 b = util.tolocal(repo.workingctx().branch())
262 if b:
262 if b:
263 ui.write("%s\n" % b)
263 ui.write("%s\n" % b)
264
264
265 def branches(ui, repo):
265 def branches(ui, repo):
266 """list repository named branches
266 """list repository named branches
267
267
268 List the repository's named branches.
268 List the repository's named branches.
269 """
269 """
270 b = repo.branchtags()
270 b = repo.branchtags()
271 l = [(-repo.changelog.rev(n), n, t) for t, n in b.items()]
271 l = [(-repo.changelog.rev(n), n, t) for t, n in b.items()]
272 l.sort()
272 l.sort()
273 for r, n, t in l:
273 for r, n, t in l:
274 hexfunc = ui.debugflag and hex or short
274 hexfunc = ui.debugflag and hex or short
275 if ui.quiet:
275 if ui.quiet:
276 ui.write("%s\n" % t)
276 ui.write("%s\n" % t)
277 else:
277 else:
278 t = util.localsub(t, 30)
278 t = util.localsub(t, 30)
279 t += " " * (30 - util.locallen(t))
279 t += " " * (30 - util.locallen(t))
280 ui.write("%s %s:%s\n" % (t, -r, hexfunc(n)))
280 ui.write("%s %s:%s\n" % (t, -r, hexfunc(n)))
281
281
282 def bundle(ui, repo, fname, dest=None, **opts):
282 def bundle(ui, repo, fname, dest=None, **opts):
283 """create a changegroup file
283 """create a changegroup file
284
284
285 Generate a compressed changegroup file collecting changesets not
285 Generate a compressed changegroup file collecting changesets not
286 found in the other repository.
286 found in the other repository.
287
287
288 If no destination repository is specified the destination is assumed
288 If no destination repository is specified the destination is assumed
289 to have all the nodes specified by one or more --base parameters.
289 to have all the nodes specified by one or more --base parameters.
290
290
291 The bundle file can then be transferred using conventional means and
291 The bundle file can then be transferred using conventional means and
292 applied to another repository with the unbundle or pull command.
292 applied to another repository with the unbundle or pull command.
293 This is useful when direct push and pull are not available or when
293 This is useful when direct push and pull are not available or when
294 exporting an entire repository is undesirable.
294 exporting an entire repository is undesirable.
295
295
296 Applying bundles preserves all changeset contents including
296 Applying bundles preserves all changeset contents including
297 permissions, copy/rename information, and revision history.
297 permissions, copy/rename information, and revision history.
298 """
298 """
299 revs = opts.get('rev') or None
299 revs = opts.get('rev') or None
300 if revs:
300 if revs:
301 revs = [repo.lookup(rev) for rev in revs]
301 revs = [repo.lookup(rev) for rev in revs]
302 base = opts.get('base')
302 base = opts.get('base')
303 if base:
303 if base:
304 if dest:
304 if dest:
305 raise util.Abort(_("--base is incompatible with specifiying "
305 raise util.Abort(_("--base is incompatible with specifiying "
306 "a destination"))
306 "a destination"))
307 base = [repo.lookup(rev) for rev in base]
307 base = [repo.lookup(rev) for rev in base]
308 # create the right base
308 # create the right base
309 # XXX: nodesbetween / changegroup* should be "fixed" instead
309 # XXX: nodesbetween / changegroup* should be "fixed" instead
310 o = []
310 o = []
311 has = {nullid: None}
311 has = {nullid: None}
312 for n in base:
312 for n in base:
313 has.update(repo.changelog.reachable(n))
313 has.update(repo.changelog.reachable(n))
314 if revs:
314 if revs:
315 visit = list(revs)
315 visit = list(revs)
316 else:
316 else:
317 visit = repo.changelog.heads()
317 visit = repo.changelog.heads()
318 seen = {}
318 seen = {}
319 while visit:
319 while visit:
320 n = visit.pop(0)
320 n = visit.pop(0)
321 parents = [p for p in repo.changelog.parents(n) if p not in has]
321 parents = [p for p in repo.changelog.parents(n) if p not in has]
322 if len(parents) == 0:
322 if len(parents) == 0:
323 o.insert(0, n)
323 o.insert(0, n)
324 else:
324 else:
325 for p in parents:
325 for p in parents:
326 if p not in seen:
326 if p not in seen:
327 seen[p] = 1
327 seen[p] = 1
328 visit.append(p)
328 visit.append(p)
329 else:
329 else:
330 setremoteconfig(ui, opts)
330 setremoteconfig(ui, opts)
331 dest = ui.expandpath(dest or 'default-push', dest or 'default')
331 dest = ui.expandpath(dest or 'default-push', dest or 'default')
332 other = hg.repository(ui, dest)
332 other = hg.repository(ui, dest)
333 o = repo.findoutgoing(other, force=opts['force'])
333 o = repo.findoutgoing(other, force=opts['force'])
334
334
335 if revs:
335 if revs:
336 cg = repo.changegroupsubset(o, revs, 'bundle')
336 cg = repo.changegroupsubset(o, revs, 'bundle')
337 else:
337 else:
338 cg = repo.changegroup(o, 'bundle')
338 cg = repo.changegroup(o, 'bundle')
339 changegroup.writebundle(cg, fname, "HG10BZ")
339 changegroup.writebundle(cg, fname, "HG10BZ")
340
340
341 def cat(ui, repo, file1, *pats, **opts):
341 def cat(ui, repo, file1, *pats, **opts):
342 """output the current or given revision of files
342 """output the current or given revision of files
343
343
344 Print the specified files as they were at the given revision.
344 Print the specified files as they were at the given revision.
345 If no revision is given, the parent of the working directory is used,
345 If no revision is given, the parent of the working directory is used,
346 or tip if no revision is checked out.
346 or tip if no revision is checked out.
347
347
348 Output may be to a file, in which case the name of the file is
348 Output may be to a file, in which case the name of the file is
349 given using a format string. The formatting rules are the same as
349 given using a format string. The formatting rules are the same as
350 for the export command, with the following additions:
350 for the export command, with the following additions:
351
351
352 %s basename of file being printed
352 %s basename of file being printed
353 %d dirname of file being printed, or '.' if in repo root
353 %d dirname of file being printed, or '.' if in repo root
354 %p root-relative path name of file being printed
354 %p root-relative path name of file being printed
355 """
355 """
356 ctx = repo.changectx(opts['rev'])
356 ctx = repo.changectx(opts['rev'])
357 for src, abs, rel, exact in cmdutil.walk(repo, (file1,) + pats, opts,
357 for src, abs, rel, exact in cmdutil.walk(repo, (file1,) + pats, opts,
358 ctx.node()):
358 ctx.node()):
359 fp = cmdutil.make_file(repo, opts['output'], ctx.node(), pathname=abs)
359 fp = cmdutil.make_file(repo, opts['output'], ctx.node(), pathname=abs)
360 fp.write(ctx.filectx(abs).data())
360 fp.write(ctx.filectx(abs).data())
361
361
362 def clone(ui, source, dest=None, **opts):
362 def clone(ui, source, dest=None, **opts):
363 """make a copy of an existing repository
363 """make a copy of an existing repository
364
364
365 Create a copy of an existing repository in a new directory.
365 Create a copy of an existing repository in a new directory.
366
366
367 If no destination directory name is specified, it defaults to the
367 If no destination directory name is specified, it defaults to the
368 basename of the source.
368 basename of the source.
369
369
370 The location of the source is added to the new repository's
370 The location of the source is added to the new repository's
371 .hg/hgrc file, as the default to be used for future pulls.
371 .hg/hgrc file, as the default to be used for future pulls.
372
372
373 For efficiency, hardlinks are used for cloning whenever the source
373 For efficiency, hardlinks are used for cloning whenever the source
374 and destination are on the same filesystem (note this applies only
374 and destination are on the same filesystem (note this applies only
375 to the repository data, not to the checked out files). Some
375 to the repository data, not to the checked out files). Some
376 filesystems, such as AFS, implement hardlinking incorrectly, but
376 filesystems, such as AFS, implement hardlinking incorrectly, but
377 do not report errors. In these cases, use the --pull option to
377 do not report errors. In these cases, use the --pull option to
378 avoid hardlinking.
378 avoid hardlinking.
379
379
380 You can safely clone repositories and checked out files using full
380 You can safely clone repositories and checked out files using full
381 hardlinks with
381 hardlinks with
382
382
383 $ cp -al REPO REPOCLONE
383 $ cp -al REPO REPOCLONE
384
384
385 which is the fastest way to clone. However, the operation is not
385 which is the fastest way to clone. However, the operation is not
386 atomic (making sure REPO is not modified during the operation is
386 atomic (making sure REPO is not modified during the operation is
387 up to you) and you have to make sure your editor breaks hardlinks
387 up to you) and you have to make sure your editor breaks hardlinks
388 (Emacs and most Linux Kernel tools do so).
388 (Emacs and most Linux Kernel tools do so).
389
389
390 If you use the -r option to clone up to a specific revision, no
390 If you use the -r option to clone up to a specific revision, no
391 subsequent revisions will be present in the cloned repository.
391 subsequent revisions will be present in the cloned repository.
392 This option implies --pull, even on local repositories.
392 This option implies --pull, even on local repositories.
393
393
394 See pull for valid source format details.
394 See pull for valid source format details.
395
395
396 It is possible to specify an ssh:// URL as the destination, but no
396 It is possible to specify an ssh:// URL as the destination, but no
397 .hg/hgrc and working directory will be created on the remote side.
397 .hg/hgrc and working directory will be created on the remote side.
398 Look at the help text for the pull command for important details
398 Look at the help text for the pull command for important details
399 about ssh:// URLs.
399 about ssh:// URLs.
400 """
400 """
401 setremoteconfig(ui, opts)
401 setremoteconfig(ui, opts)
402 hg.clone(ui, ui.expandpath(source), dest,
402 hg.clone(ui, ui.expandpath(source), dest,
403 pull=opts['pull'],
403 pull=opts['pull'],
404 stream=opts['uncompressed'],
404 stream=opts['uncompressed'],
405 rev=opts['rev'],
405 rev=opts['rev'],
406 update=not opts['noupdate'])
406 update=not opts['noupdate'])
407
407
408 def commit(ui, repo, *pats, **opts):
408 def commit(ui, repo, *pats, **opts):
409 """commit the specified files or all outstanding changes
409 """commit the specified files or all outstanding changes
410
410
411 Commit changes to the given files into the repository.
411 Commit changes to the given files into the repository.
412
412
413 If a list of files is omitted, all changes reported by "hg status"
413 If a list of files is omitted, all changes reported by "hg status"
414 will be committed.
414 will be committed.
415
415
416 If no commit message is specified, the editor configured in your hgrc
416 If no commit message is specified, the editor configured in your hgrc
417 or in the EDITOR environment variable is started to enter a message.
417 or in the EDITOR environment variable is started to enter a message.
418 """
418 """
419 message = logmessage(opts)
419 message = logmessage(opts)
420
420
421 if opts['addremove']:
421 if opts['addremove']:
422 cmdutil.addremove(repo, pats, opts)
422 cmdutil.addremove(repo, pats, opts)
423 fns, match, anypats = cmdutil.matchpats(repo, pats, opts)
423 fns, match, anypats = cmdutil.matchpats(repo, pats, opts)
424 if pats:
424 if pats:
425 status = repo.status(files=fns, match=match)
425 status = repo.status(files=fns, match=match)
426 modified, added, removed, deleted, unknown = status[:5]
426 modified, added, removed, deleted, unknown = status[:5]
427 files = modified + added + removed
427 files = modified + added + removed
428 slist = None
428 slist = None
429 for f in fns:
429 for f in fns:
430 if f not in files:
430 if f not in files:
431 rf = repo.wjoin(f)
431 rf = repo.wjoin(f)
432 if f in unknown:
432 if f in unknown:
433 raise util.Abort(_("file %s not tracked!") % rf)
433 raise util.Abort(_("file %s not tracked!") % rf)
434 try:
434 try:
435 mode = os.lstat(rf)[stat.ST_MODE]
435 mode = os.lstat(rf)[stat.ST_MODE]
436 except OSError:
436 except OSError:
437 raise util.Abort(_("file %s not found!") % rf)
437 raise util.Abort(_("file %s not found!") % rf)
438 if stat.S_ISDIR(mode):
438 if stat.S_ISDIR(mode):
439 name = f + '/'
439 name = f + '/'
440 if slist is None:
440 if slist is None:
441 slist = list(files)
441 slist = list(files)
442 slist.sort()
442 slist.sort()
443 i = bisect.bisect(slist, name)
443 i = bisect.bisect(slist, name)
444 if i >= len(slist) or not slist[i].startswith(name):
444 if i >= len(slist) or not slist[i].startswith(name):
445 raise util.Abort(_("no match under directory %s!")
445 raise util.Abort(_("no match under directory %s!")
446 % rf)
446 % rf)
447 elif not stat.S_ISREG(mode):
447 elif not stat.S_ISREG(mode):
448 raise util.Abort(_("can't commit %s: "
448 raise util.Abort(_("can't commit %s: "
449 "unsupported file type!") % rf)
449 "unsupported file type!") % rf)
450 else:
450 else:
451 files = []
451 files = []
452 try:
452 try:
453 repo.commit(files, message, opts['user'], opts['date'], match,
453 repo.commit(files, message, opts['user'], opts['date'], match,
454 force_editor=opts.get('force_editor'))
454 force_editor=opts.get('force_editor'))
455 except ValueError, inst:
455 except ValueError, inst:
456 raise util.Abort(str(inst))
456 raise util.Abort(str(inst))
457
457
458 def docopy(ui, repo, pats, opts, wlock):
458 def docopy(ui, repo, pats, opts, wlock):
459 # called with the repo lock held
459 # called with the repo lock held
460 #
460 #
461 # hgsep => pathname that uses "/" to separate directories
461 # hgsep => pathname that uses "/" to separate directories
462 # ossep => pathname that uses os.sep to separate directories
462 # ossep => pathname that uses os.sep to separate directories
463 cwd = repo.getcwd()
463 cwd = repo.getcwd()
464 errors = 0
464 errors = 0
465 copied = []
465 copied = []
466 targets = {}
466 targets = {}
467
467
468 # abs: hgsep
468 # abs: hgsep
469 # rel: ossep
469 # rel: ossep
470 # return: hgsep
470 # return: hgsep
471 def okaytocopy(abs, rel, exact):
471 def okaytocopy(abs, rel, exact):
472 reasons = {'?': _('is not managed'),
472 reasons = {'?': _('is not managed'),
473 'a': _('has been marked for add'),
473 'a': _('has been marked for add'),
474 'r': _('has been marked for remove')}
474 'r': _('has been marked for remove')}
475 state = repo.dirstate.state(abs)
475 state = repo.dirstate.state(abs)
476 reason = reasons.get(state)
476 reason = reasons.get(state)
477 if reason:
477 if reason:
478 if state == 'a':
478 if state == 'a':
479 origsrc = repo.dirstate.copied(abs)
479 origsrc = repo.dirstate.copied(abs)
480 if origsrc is not None:
480 if origsrc is not None:
481 return origsrc
481 return origsrc
482 if exact:
482 if exact:
483 ui.warn(_('%s: not copying - file %s\n') % (rel, reason))
483 ui.warn(_('%s: not copying - file %s\n') % (rel, reason))
484 else:
484 else:
485 return abs
485 return abs
486
486
487 # origsrc: hgsep
487 # origsrc: hgsep
488 # abssrc: hgsep
488 # abssrc: hgsep
489 # relsrc: ossep
489 # relsrc: ossep
490 # target: ossep
490 # target: ossep
491 def copy(origsrc, abssrc, relsrc, target, exact):
491 def copy(origsrc, abssrc, relsrc, target, exact):
492 abstarget = util.canonpath(repo.root, cwd, target)
492 abstarget = util.canonpath(repo.root, cwd, target)
493 reltarget = util.pathto(cwd, abstarget)
493 reltarget = util.pathto(cwd, abstarget)
494 prevsrc = targets.get(abstarget)
494 prevsrc = targets.get(abstarget)
495 if prevsrc is not None:
495 if prevsrc is not None:
496 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
496 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
497 (reltarget, util.localpath(abssrc),
497 (reltarget, util.localpath(abssrc),
498 util.localpath(prevsrc)))
498 util.localpath(prevsrc)))
499 return
499 return
500 if (not opts['after'] and os.path.exists(reltarget) or
500 if (not opts['after'] and os.path.exists(reltarget) or
501 opts['after'] and repo.dirstate.state(abstarget) not in '?r'):
501 opts['after'] and repo.dirstate.state(abstarget) not in '?r'):
502 if not opts['force']:
502 if not opts['force']:
503 ui.warn(_('%s: not overwriting - file exists\n') %
503 ui.warn(_('%s: not overwriting - file exists\n') %
504 reltarget)
504 reltarget)
505 return
505 return
506 if not opts['after'] and not opts.get('dry_run'):
506 if not opts['after'] and not opts.get('dry_run'):
507 os.unlink(reltarget)
507 os.unlink(reltarget)
508 if opts['after']:
508 if opts['after']:
509 if not os.path.exists(reltarget):
509 if not os.path.exists(reltarget):
510 return
510 return
511 else:
511 else:
512 targetdir = os.path.dirname(reltarget) or '.'
512 targetdir = os.path.dirname(reltarget) or '.'
513 if not os.path.isdir(targetdir) and not opts.get('dry_run'):
513 if not os.path.isdir(targetdir) and not opts.get('dry_run'):
514 os.makedirs(targetdir)
514 os.makedirs(targetdir)
515 try:
515 try:
516 restore = repo.dirstate.state(abstarget) == 'r'
516 restore = repo.dirstate.state(abstarget) == 'r'
517 if restore and not opts.get('dry_run'):
517 if restore and not opts.get('dry_run'):
518 repo.undelete([abstarget], wlock)
518 repo.undelete([abstarget], wlock)
519 try:
519 try:
520 if not opts.get('dry_run'):
520 if not opts.get('dry_run'):
521 util.copyfile(relsrc, reltarget)
521 util.copyfile(relsrc, reltarget)
522 restore = False
522 restore = False
523 finally:
523 finally:
524 if restore:
524 if restore:
525 repo.remove([abstarget], wlock)
525 repo.remove([abstarget], wlock)
526 except IOError, inst:
526 except IOError, inst:
527 if inst.errno == errno.ENOENT:
527 if inst.errno == errno.ENOENT:
528 ui.warn(_('%s: deleted in working copy\n') % relsrc)
528 ui.warn(_('%s: deleted in working copy\n') % relsrc)
529 else:
529 else:
530 ui.warn(_('%s: cannot copy - %s\n') %
530 ui.warn(_('%s: cannot copy - %s\n') %
531 (relsrc, inst.strerror))
531 (relsrc, inst.strerror))
532 errors += 1
532 errors += 1
533 return
533 return
534 if ui.verbose or not exact:
534 if ui.verbose or not exact:
535 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
535 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
536 targets[abstarget] = abssrc
536 targets[abstarget] = abssrc
537 if abstarget != origsrc and not opts.get('dry_run'):
537 if abstarget != origsrc and not opts.get('dry_run'):
538 repo.copy(origsrc, abstarget, wlock)
538 repo.copy(origsrc, abstarget, wlock)
539 copied.append((abssrc, relsrc, exact))
539 copied.append((abssrc, relsrc, exact))
540
540
541 # pat: ossep
541 # pat: ossep
542 # dest ossep
542 # dest ossep
543 # srcs: list of (hgsep, hgsep, ossep, bool)
543 # srcs: list of (hgsep, hgsep, ossep, bool)
544 # return: function that takes hgsep and returns ossep
544 # return: function that takes hgsep and returns ossep
545 def targetpathfn(pat, dest, srcs):
545 def targetpathfn(pat, dest, srcs):
546 if os.path.isdir(pat):
546 if os.path.isdir(pat):
547 abspfx = util.canonpath(repo.root, cwd, pat)
547 abspfx = util.canonpath(repo.root, cwd, pat)
548 abspfx = util.localpath(abspfx)
548 abspfx = util.localpath(abspfx)
549 if destdirexists:
549 if destdirexists:
550 striplen = len(os.path.split(abspfx)[0])
550 striplen = len(os.path.split(abspfx)[0])
551 else:
551 else:
552 striplen = len(abspfx)
552 striplen = len(abspfx)
553 if striplen:
553 if striplen:
554 striplen += len(os.sep)
554 striplen += len(os.sep)
555 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
555 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
556 elif destdirexists:
556 elif destdirexists:
557 res = lambda p: os.path.join(dest,
557 res = lambda p: os.path.join(dest,
558 os.path.basename(util.localpath(p)))
558 os.path.basename(util.localpath(p)))
559 else:
559 else:
560 res = lambda p: dest
560 res = lambda p: dest
561 return res
561 return res
562
562
563 # pat: ossep
563 # pat: ossep
564 # dest ossep
564 # dest ossep
565 # srcs: list of (hgsep, hgsep, ossep, bool)
565 # srcs: list of (hgsep, hgsep, ossep, bool)
566 # return: function that takes hgsep and returns ossep
566 # return: function that takes hgsep and returns ossep
567 def targetpathafterfn(pat, dest, srcs):
567 def targetpathafterfn(pat, dest, srcs):
568 if util.patkind(pat, None)[0]:
568 if util.patkind(pat, None)[0]:
569 # a mercurial pattern
569 # a mercurial pattern
570 res = lambda p: os.path.join(dest,
570 res = lambda p: os.path.join(dest,
571 os.path.basename(util.localpath(p)))
571 os.path.basename(util.localpath(p)))
572 else:
572 else:
573 abspfx = util.canonpath(repo.root, cwd, pat)
573 abspfx = util.canonpath(repo.root, cwd, pat)
574 if len(abspfx) < len(srcs[0][0]):
574 if len(abspfx) < len(srcs[0][0]):
575 # A directory. Either the target path contains the last
575 # A directory. Either the target path contains the last
576 # component of the source path or it does not.
576 # component of the source path or it does not.
577 def evalpath(striplen):
577 def evalpath(striplen):
578 score = 0
578 score = 0
579 for s in srcs:
579 for s in srcs:
580 t = os.path.join(dest, util.localpath(s[0])[striplen:])
580 t = os.path.join(dest, util.localpath(s[0])[striplen:])
581 if os.path.exists(t):
581 if os.path.exists(t):
582 score += 1
582 score += 1
583 return score
583 return score
584
584
585 abspfx = util.localpath(abspfx)
585 abspfx = util.localpath(abspfx)
586 striplen = len(abspfx)
586 striplen = len(abspfx)
587 if striplen:
587 if striplen:
588 striplen += len(os.sep)
588 striplen += len(os.sep)
589 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
589 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
590 score = evalpath(striplen)
590 score = evalpath(striplen)
591 striplen1 = len(os.path.split(abspfx)[0])
591 striplen1 = len(os.path.split(abspfx)[0])
592 if striplen1:
592 if striplen1:
593 striplen1 += len(os.sep)
593 striplen1 += len(os.sep)
594 if evalpath(striplen1) > score:
594 if evalpath(striplen1) > score:
595 striplen = striplen1
595 striplen = striplen1
596 res = lambda p: os.path.join(dest,
596 res = lambda p: os.path.join(dest,
597 util.localpath(p)[striplen:])
597 util.localpath(p)[striplen:])
598 else:
598 else:
599 # a file
599 # a file
600 if destdirexists:
600 if destdirexists:
601 res = lambda p: os.path.join(dest,
601 res = lambda p: os.path.join(dest,
602 os.path.basename(util.localpath(p)))
602 os.path.basename(util.localpath(p)))
603 else:
603 else:
604 res = lambda p: dest
604 res = lambda p: dest
605 return res
605 return res
606
606
607
607
608 pats = list(pats)
608 pats = util.expand_glob(pats)
609 if not pats:
609 if not pats:
610 raise util.Abort(_('no source or destination specified'))
610 raise util.Abort(_('no source or destination specified'))
611 if len(pats) == 1:
611 if len(pats) == 1:
612 raise util.Abort(_('no destination specified'))
612 raise util.Abort(_('no destination specified'))
613 dest = pats.pop()
613 dest = pats.pop()
614 destdirexists = os.path.isdir(dest)
614 destdirexists = os.path.isdir(dest)
615 if (len(pats) > 1 or util.patkind(pats[0], None)[0]) and not destdirexists:
615 if (len(pats) > 1 or util.patkind(pats[0], None)[0]) and not destdirexists:
616 raise util.Abort(_('with multiple sources, destination must be an '
616 raise util.Abort(_('with multiple sources, destination must be an '
617 'existing directory'))
617 'existing directory'))
618 if opts['after']:
618 if opts['after']:
619 tfn = targetpathafterfn
619 tfn = targetpathafterfn
620 else:
620 else:
621 tfn = targetpathfn
621 tfn = targetpathfn
622 copylist = []
622 copylist = []
623 for pat in pats:
623 for pat in pats:
624 srcs = []
624 srcs = []
625 for tag, abssrc, relsrc, exact in cmdutil.walk(repo, [pat], opts):
625 for tag, abssrc, relsrc, exact in cmdutil.walk(repo, [pat], opts,
626 globbed=True):
626 origsrc = okaytocopy(abssrc, relsrc, exact)
627 origsrc = okaytocopy(abssrc, relsrc, exact)
627 if origsrc:
628 if origsrc:
628 srcs.append((origsrc, abssrc, relsrc, exact))
629 srcs.append((origsrc, abssrc, relsrc, exact))
629 if not srcs:
630 if not srcs:
630 continue
631 continue
631 copylist.append((tfn(pat, dest, srcs), srcs))
632 copylist.append((tfn(pat, dest, srcs), srcs))
632 if not copylist:
633 if not copylist:
633 raise util.Abort(_('no files to copy'))
634 raise util.Abort(_('no files to copy'))
634
635
635 for targetpath, srcs in copylist:
636 for targetpath, srcs in copylist:
636 for origsrc, abssrc, relsrc, exact in srcs:
637 for origsrc, abssrc, relsrc, exact in srcs:
637 copy(origsrc, abssrc, relsrc, targetpath(abssrc), exact)
638 copy(origsrc, abssrc, relsrc, targetpath(abssrc), exact)
638
639
639 if errors:
640 if errors:
640 ui.warn(_('(consider using --after)\n'))
641 ui.warn(_('(consider using --after)\n'))
641 return errors, copied
642 return errors, copied
642
643
643 def copy(ui, repo, *pats, **opts):
644 def copy(ui, repo, *pats, **opts):
644 """mark files as copied for the next commit
645 """mark files as copied for the next commit
645
646
646 Mark dest as having copies of source files. If dest is a
647 Mark dest as having copies of source files. If dest is a
647 directory, copies are put in that directory. If dest is a file,
648 directory, copies are put in that directory. If dest is a file,
648 there can only be one source.
649 there can only be one source.
649
650
650 By default, this command copies the contents of files as they
651 By default, this command copies the contents of files as they
651 stand in the working directory. If invoked with --after, the
652 stand in the working directory. If invoked with --after, the
652 operation is recorded, but no copying is performed.
653 operation is recorded, but no copying is performed.
653
654
654 This command takes effect in the next commit. To undo a copy
655 This command takes effect in the next commit. To undo a copy
655 before that, see hg revert.
656 before that, see hg revert.
656 """
657 """
657 wlock = repo.wlock(0)
658 wlock = repo.wlock(0)
658 errs, copied = docopy(ui, repo, pats, opts, wlock)
659 errs, copied = docopy(ui, repo, pats, opts, wlock)
659 return errs
660 return errs
660
661
661 def debugancestor(ui, index, rev1, rev2):
662 def debugancestor(ui, index, rev1, rev2):
662 """find the ancestor revision of two revisions in a given index"""
663 """find the ancestor revision of two revisions in a given index"""
663 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index, "", 0)
664 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index, "", 0)
664 a = r.ancestor(r.lookup(rev1), r.lookup(rev2))
665 a = r.ancestor(r.lookup(rev1), r.lookup(rev2))
665 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
666 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
666
667
667 def debugcomplete(ui, cmd='', **opts):
668 def debugcomplete(ui, cmd='', **opts):
668 """returns the completion list associated with the given command"""
669 """returns the completion list associated with the given command"""
669
670
670 if opts['options']:
671 if opts['options']:
671 options = []
672 options = []
672 otables = [globalopts]
673 otables = [globalopts]
673 if cmd:
674 if cmd:
674 aliases, entry = findcmd(ui, cmd)
675 aliases, entry = findcmd(ui, cmd)
675 otables.append(entry[1])
676 otables.append(entry[1])
676 for t in otables:
677 for t in otables:
677 for o in t:
678 for o in t:
678 if o[0]:
679 if o[0]:
679 options.append('-%s' % o[0])
680 options.append('-%s' % o[0])
680 options.append('--%s' % o[1])
681 options.append('--%s' % o[1])
681 ui.write("%s\n" % "\n".join(options))
682 ui.write("%s\n" % "\n".join(options))
682 return
683 return
683
684
684 clist = findpossible(ui, cmd).keys()
685 clist = findpossible(ui, cmd).keys()
685 clist.sort()
686 clist.sort()
686 ui.write("%s\n" % "\n".join(clist))
687 ui.write("%s\n" % "\n".join(clist))
687
688
688 def debugrebuildstate(ui, repo, rev=None):
689 def debugrebuildstate(ui, repo, rev=None):
689 """rebuild the dirstate as it would look like for the given revision"""
690 """rebuild the dirstate as it would look like for the given revision"""
690 if not rev:
691 if not rev:
691 rev = repo.changelog.tip()
692 rev = repo.changelog.tip()
692 else:
693 else:
693 rev = repo.lookup(rev)
694 rev = repo.lookup(rev)
694 change = repo.changelog.read(rev)
695 change = repo.changelog.read(rev)
695 n = change[0]
696 n = change[0]
696 files = repo.manifest.read(n)
697 files = repo.manifest.read(n)
697 wlock = repo.wlock()
698 wlock = repo.wlock()
698 repo.dirstate.rebuild(rev, files)
699 repo.dirstate.rebuild(rev, files)
699
700
700 def debugcheckstate(ui, repo):
701 def debugcheckstate(ui, repo):
701 """validate the correctness of the current dirstate"""
702 """validate the correctness of the current dirstate"""
702 parent1, parent2 = repo.dirstate.parents()
703 parent1, parent2 = repo.dirstate.parents()
703 repo.dirstate.read()
704 repo.dirstate.read()
704 dc = repo.dirstate.map
705 dc = repo.dirstate.map
705 keys = dc.keys()
706 keys = dc.keys()
706 keys.sort()
707 keys.sort()
707 m1n = repo.changelog.read(parent1)[0]
708 m1n = repo.changelog.read(parent1)[0]
708 m2n = repo.changelog.read(parent2)[0]
709 m2n = repo.changelog.read(parent2)[0]
709 m1 = repo.manifest.read(m1n)
710 m1 = repo.manifest.read(m1n)
710 m2 = repo.manifest.read(m2n)
711 m2 = repo.manifest.read(m2n)
711 errors = 0
712 errors = 0
712 for f in dc:
713 for f in dc:
713 state = repo.dirstate.state(f)
714 state = repo.dirstate.state(f)
714 if state in "nr" and f not in m1:
715 if state in "nr" and f not in m1:
715 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
716 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
716 errors += 1
717 errors += 1
717 if state in "a" and f in m1:
718 if state in "a" and f in m1:
718 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
719 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
719 errors += 1
720 errors += 1
720 if state in "m" and f not in m1 and f not in m2:
721 if state in "m" and f not in m1 and f not in m2:
721 ui.warn(_("%s in state %s, but not in either manifest\n") %
722 ui.warn(_("%s in state %s, but not in either manifest\n") %
722 (f, state))
723 (f, state))
723 errors += 1
724 errors += 1
724 for f in m1:
725 for f in m1:
725 state = repo.dirstate.state(f)
726 state = repo.dirstate.state(f)
726 if state not in "nrm":
727 if state not in "nrm":
727 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
728 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
728 errors += 1
729 errors += 1
729 if errors:
730 if errors:
730 error = _(".hg/dirstate inconsistent with current parent's manifest")
731 error = _(".hg/dirstate inconsistent with current parent's manifest")
731 raise util.Abort(error)
732 raise util.Abort(error)
732
733
733 def showconfig(ui, repo, *values, **opts):
734 def showconfig(ui, repo, *values, **opts):
734 """show combined config settings from all hgrc files
735 """show combined config settings from all hgrc files
735
736
736 With no args, print names and values of all config items.
737 With no args, print names and values of all config items.
737
738
738 With one arg of the form section.name, print just the value of
739 With one arg of the form section.name, print just the value of
739 that config item.
740 that config item.
740
741
741 With multiple args, print names and values of all config items
742 With multiple args, print names and values of all config items
742 with matching section names."""
743 with matching section names."""
743
744
744 untrusted = bool(opts.get('untrusted'))
745 untrusted = bool(opts.get('untrusted'))
745 if values:
746 if values:
746 if len([v for v in values if '.' in v]) > 1:
747 if len([v for v in values if '.' in v]) > 1:
747 raise util.Abort(_('only one config item permitted'))
748 raise util.Abort(_('only one config item permitted'))
748 for section, name, value in ui.walkconfig(untrusted=untrusted):
749 for section, name, value in ui.walkconfig(untrusted=untrusted):
749 sectname = section + '.' + name
750 sectname = section + '.' + name
750 if values:
751 if values:
751 for v in values:
752 for v in values:
752 if v == section:
753 if v == section:
753 ui.write('%s=%s\n' % (sectname, value))
754 ui.write('%s=%s\n' % (sectname, value))
754 elif v == sectname:
755 elif v == sectname:
755 ui.write(value, '\n')
756 ui.write(value, '\n')
756 else:
757 else:
757 ui.write('%s=%s\n' % (sectname, value))
758 ui.write('%s=%s\n' % (sectname, value))
758
759
759 def debugsetparents(ui, repo, rev1, rev2=None):
760 def debugsetparents(ui, repo, rev1, rev2=None):
760 """manually set the parents of the current working directory
761 """manually set the parents of the current working directory
761
762
762 This is useful for writing repository conversion tools, but should
763 This is useful for writing repository conversion tools, but should
763 be used with care.
764 be used with care.
764 """
765 """
765
766
766 if not rev2:
767 if not rev2:
767 rev2 = hex(nullid)
768 rev2 = hex(nullid)
768
769
769 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
770 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
770
771
771 def debugstate(ui, repo):
772 def debugstate(ui, repo):
772 """show the contents of the current dirstate"""
773 """show the contents of the current dirstate"""
773 repo.dirstate.read()
774 repo.dirstate.read()
774 dc = repo.dirstate.map
775 dc = repo.dirstate.map
775 keys = dc.keys()
776 keys = dc.keys()
776 keys.sort()
777 keys.sort()
777 for file_ in keys:
778 for file_ in keys:
778 if dc[file_][3] == -1:
779 if dc[file_][3] == -1:
779 # Pad or slice to locale representation
780 # Pad or slice to locale representation
780 locale_len = len(time.strftime("%x %X", time.localtime(0)))
781 locale_len = len(time.strftime("%x %X", time.localtime(0)))
781 timestr = 'unset'
782 timestr = 'unset'
782 timestr = timestr[:locale_len] + ' '*(locale_len - len(timestr))
783 timestr = timestr[:locale_len] + ' '*(locale_len - len(timestr))
783 else:
784 else:
784 timestr = time.strftime("%x %X", time.localtime(dc[file_][3]))
785 timestr = time.strftime("%x %X", time.localtime(dc[file_][3]))
785 ui.write("%c %3o %10d %s %s\n"
786 ui.write("%c %3o %10d %s %s\n"
786 % (dc[file_][0], dc[file_][1] & 0777, dc[file_][2],
787 % (dc[file_][0], dc[file_][1] & 0777, dc[file_][2],
787 timestr, file_))
788 timestr, file_))
788 for f in repo.dirstate.copies():
789 for f in repo.dirstate.copies():
789 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
790 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
790
791
791 def debugdata(ui, file_, rev):
792 def debugdata(ui, file_, rev):
792 """dump the contents of an data file revision"""
793 """dump the contents of an data file revision"""
793 r = revlog.revlog(util.opener(os.getcwd(), audit=False),
794 r = revlog.revlog(util.opener(os.getcwd(), audit=False),
794 file_[:-2] + ".i", file_, 0)
795 file_[:-2] + ".i", file_, 0)
795 try:
796 try:
796 ui.write(r.revision(r.lookup(rev)))
797 ui.write(r.revision(r.lookup(rev)))
797 except KeyError:
798 except KeyError:
798 raise util.Abort(_('invalid revision identifier %s') % rev)
799 raise util.Abort(_('invalid revision identifier %s') % rev)
799
800
800 def debugdate(ui, date, range=None, **opts):
801 def debugdate(ui, date, range=None, **opts):
801 """parse and display a date"""
802 """parse and display a date"""
802 if opts["extended"]:
803 if opts["extended"]:
803 d = util.parsedate(date, util.extendeddateformats)
804 d = util.parsedate(date, util.extendeddateformats)
804 else:
805 else:
805 d = util.parsedate(date)
806 d = util.parsedate(date)
806 ui.write("internal: %s %s\n" % d)
807 ui.write("internal: %s %s\n" % d)
807 ui.write("standard: %s\n" % util.datestr(d))
808 ui.write("standard: %s\n" % util.datestr(d))
808 if range:
809 if range:
809 m = util.matchdate(range)
810 m = util.matchdate(range)
810 ui.write("match: %s\n" % m(d[0]))
811 ui.write("match: %s\n" % m(d[0]))
811
812
812 def debugindex(ui, file_):
813 def debugindex(ui, file_):
813 """dump the contents of an index file"""
814 """dump the contents of an index file"""
814 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "", 0)
815 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "", 0)
815 ui.write(" rev offset length base linkrev" +
816 ui.write(" rev offset length base linkrev" +
816 " nodeid p1 p2\n")
817 " nodeid p1 p2\n")
817 for i in xrange(r.count()):
818 for i in xrange(r.count()):
818 node = r.node(i)
819 node = r.node(i)
819 pp = r.parents(node)
820 pp = r.parents(node)
820 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
821 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
821 i, r.start(i), r.length(i), r.base(i), r.linkrev(node),
822 i, r.start(i), r.length(i), r.base(i), r.linkrev(node),
822 short(node), short(pp[0]), short(pp[1])))
823 short(node), short(pp[0]), short(pp[1])))
823
824
824 def debugindexdot(ui, file_):
825 def debugindexdot(ui, file_):
825 """dump an index DAG as a .dot file"""
826 """dump an index DAG as a .dot file"""
826 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "", 0)
827 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "", 0)
827 ui.write("digraph G {\n")
828 ui.write("digraph G {\n")
828 for i in xrange(r.count()):
829 for i in xrange(r.count()):
829 node = r.node(i)
830 node = r.node(i)
830 pp = r.parents(node)
831 pp = r.parents(node)
831 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
832 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
832 if pp[1] != nullid:
833 if pp[1] != nullid:
833 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
834 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
834 ui.write("}\n")
835 ui.write("}\n")
835
836
836 def debuginstall(ui):
837 def debuginstall(ui):
837 '''test Mercurial installation'''
838 '''test Mercurial installation'''
838
839
839 def writetemp(contents):
840 def writetemp(contents):
840 (fd, name) = tempfile.mkstemp()
841 (fd, name) = tempfile.mkstemp()
841 f = os.fdopen(fd, "wb")
842 f = os.fdopen(fd, "wb")
842 f.write(contents)
843 f.write(contents)
843 f.close()
844 f.close()
844 return name
845 return name
845
846
846 problems = 0
847 problems = 0
847
848
848 # encoding
849 # encoding
849 ui.status(_("Checking encoding (%s)...\n") % util._encoding)
850 ui.status(_("Checking encoding (%s)...\n") % util._encoding)
850 try:
851 try:
851 util.fromlocal("test")
852 util.fromlocal("test")
852 except util.Abort, inst:
853 except util.Abort, inst:
853 ui.write(" %s\n" % inst)
854 ui.write(" %s\n" % inst)
854 ui.write(_(" (check that your locale is properly set)\n"))
855 ui.write(_(" (check that your locale is properly set)\n"))
855 problems += 1
856 problems += 1
856
857
857 # compiled modules
858 # compiled modules
858 ui.status(_("Checking extensions...\n"))
859 ui.status(_("Checking extensions...\n"))
859 try:
860 try:
860 import bdiff, mpatch, base85
861 import bdiff, mpatch, base85
861 except Exception, inst:
862 except Exception, inst:
862 ui.write(" %s\n" % inst)
863 ui.write(" %s\n" % inst)
863 ui.write(_(" One or more extensions could not be found"))
864 ui.write(_(" One or more extensions could not be found"))
864 ui.write(_(" (check that you compiled the extensions)\n"))
865 ui.write(_(" (check that you compiled the extensions)\n"))
865 problems += 1
866 problems += 1
866
867
867 # templates
868 # templates
868 ui.status(_("Checking templates...\n"))
869 ui.status(_("Checking templates...\n"))
869 try:
870 try:
870 import templater
871 import templater
871 t = templater.templater(templater.templatepath("map-cmdline.default"))
872 t = templater.templater(templater.templatepath("map-cmdline.default"))
872 except Exception, inst:
873 except Exception, inst:
873 ui.write(" %s\n" % inst)
874 ui.write(" %s\n" % inst)
874 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
875 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
875 problems += 1
876 problems += 1
876
877
877 # patch
878 # patch
878 ui.status(_("Checking patch...\n"))
879 ui.status(_("Checking patch...\n"))
879 path = os.environ.get('PATH', '')
880 path = os.environ.get('PATH', '')
880 patcher = util.find_in_path('gpatch', path,
881 patcher = util.find_in_path('gpatch', path,
881 util.find_in_path('patch', path, None))
882 util.find_in_path('patch', path, None))
882 if not patcher:
883 if not patcher:
883 ui.write(_(" Can't find patch or gpatch in PATH\n"))
884 ui.write(_(" Can't find patch or gpatch in PATH\n"))
884 ui.write(_(" (specify a patch utility in your .hgrc file)\n"))
885 ui.write(_(" (specify a patch utility in your .hgrc file)\n"))
885 problems += 1
886 problems += 1
886 else:
887 else:
887 # actually attempt a patch here
888 # actually attempt a patch here
888 a = "1\n2\n3\n4\n"
889 a = "1\n2\n3\n4\n"
889 b = "1\n2\n3\ninsert\n4\n"
890 b = "1\n2\n3\ninsert\n4\n"
890 d = mdiff.unidiff(a, None, b, None, "a")
891 d = mdiff.unidiff(a, None, b, None, "a")
891 fa = writetemp(a)
892 fa = writetemp(a)
892 fd = writetemp(d)
893 fd = writetemp(d)
893 fp = os.popen('%s %s %s' % (patcher, fa, fd))
894 fp = os.popen('%s %s %s' % (patcher, fa, fd))
894 files = []
895 files = []
895 output = ""
896 output = ""
896 for line in fp:
897 for line in fp:
897 output += line
898 output += line
898 if line.startswith('patching file '):
899 if line.startswith('patching file '):
899 pf = util.parse_patch_output(line.rstrip())
900 pf = util.parse_patch_output(line.rstrip())
900 files.append(pf)
901 files.append(pf)
901 if files != [fa]:
902 if files != [fa]:
902 ui.write(_(" unexpected patch output!"))
903 ui.write(_(" unexpected patch output!"))
903 ui.write(_(" (you may have an incompatible version of patch)\n"))
904 ui.write(_(" (you may have an incompatible version of patch)\n"))
904 ui.write(output)
905 ui.write(output)
905 problems += 1
906 problems += 1
906 a = file(fa).read()
907 a = file(fa).read()
907 if a != b:
908 if a != b:
908 ui.write(_(" patch test failed!"))
909 ui.write(_(" patch test failed!"))
909 ui.write(_(" (you may have an incompatible version of patch)\n"))
910 ui.write(_(" (you may have an incompatible version of patch)\n"))
910 problems += 1
911 problems += 1
911 os.unlink(fa)
912 os.unlink(fa)
912 os.unlink(fd)
913 os.unlink(fd)
913
914
914 # merge helper
915 # merge helper
915 ui.status(_("Checking merge helper...\n"))
916 ui.status(_("Checking merge helper...\n"))
916 cmd = (os.environ.get("HGMERGE") or ui.config("ui", "merge")
917 cmd = (os.environ.get("HGMERGE") or ui.config("ui", "merge")
917 or "hgmerge")
918 or "hgmerge")
918 cmdpath = util.find_in_path(cmd, path)
919 cmdpath = util.find_in_path(cmd, path)
919 if not cmdpath:
920 if not cmdpath:
920 cmdpath = util.find_in_path(cmd.split()[0], path)
921 cmdpath = util.find_in_path(cmd.split()[0], path)
921 if not cmdpath:
922 if not cmdpath:
922 if cmd == 'hgmerge':
923 if cmd == 'hgmerge':
923 ui.write(_(" No merge helper set and can't find default"
924 ui.write(_(" No merge helper set and can't find default"
924 " hgmerge script in PATH\n"))
925 " hgmerge script in PATH\n"))
925 ui.write(_(" (specify a merge helper in your .hgrc file)\n"))
926 ui.write(_(" (specify a merge helper in your .hgrc file)\n"))
926 else:
927 else:
927 ui.write(_(" Can't find merge helper '%s' in PATH\n") % cmd)
928 ui.write(_(" Can't find merge helper '%s' in PATH\n") % cmd)
928 ui.write(_(" (specify a merge helper in your .hgrc file)\n"))
929 ui.write(_(" (specify a merge helper in your .hgrc file)\n"))
929 problems += 1
930 problems += 1
930 else:
931 else:
931 # actually attempt a patch here
932 # actually attempt a patch here
932 fa = writetemp("1\n2\n3\n4\n")
933 fa = writetemp("1\n2\n3\n4\n")
933 fl = writetemp("1\n2\n3\ninsert\n4\n")
934 fl = writetemp("1\n2\n3\ninsert\n4\n")
934 fr = writetemp("begin\n1\n2\n3\n4\n")
935 fr = writetemp("begin\n1\n2\n3\n4\n")
935 r = os.system('%s %s %s %s' % (cmd, fl, fa, fr))
936 r = os.system('%s %s %s %s' % (cmd, fl, fa, fr))
936 if r:
937 if r:
937 ui.write(_(" got unexpected merge error %d!") % r)
938 ui.write(_(" got unexpected merge error %d!") % r)
938 problems += 1
939 problems += 1
939 m = file(fl).read()
940 m = file(fl).read()
940 if m != "begin\n1\n2\n3\ninsert\n4\n":
941 if m != "begin\n1\n2\n3\ninsert\n4\n":
941 ui.write(_(" got unexpected merge results!") % r)
942 ui.write(_(" got unexpected merge results!") % r)
942 ui.write(_(" (your merge helper may have the"
943 ui.write(_(" (your merge helper may have the"
943 " wrong argument order)\n"))
944 " wrong argument order)\n"))
944 ui.write(m)
945 ui.write(m)
945 os.unlink(fa)
946 os.unlink(fa)
946 os.unlink(fl)
947 os.unlink(fl)
947 os.unlink(fr)
948 os.unlink(fr)
948
949
949 # editor
950 # editor
950 ui.status(_("Checking commit editor...\n"))
951 ui.status(_("Checking commit editor...\n"))
951 editor = (os.environ.get("HGEDITOR") or
952 editor = (os.environ.get("HGEDITOR") or
952 ui.config("ui", "editor") or
953 ui.config("ui", "editor") or
953 os.environ.get("EDITOR", "vi"))
954 os.environ.get("EDITOR", "vi"))
954 cmdpath = util.find_in_path(editor, path)
955 cmdpath = util.find_in_path(editor, path)
955 if not cmdpath:
956 if not cmdpath:
956 cmdpath = util.find_in_path(editor.split()[0], path)
957 cmdpath = util.find_in_path(editor.split()[0], path)
957 if not cmdpath:
958 if not cmdpath:
958 if editor == 'vi':
959 if editor == 'vi':
959 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
960 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
960 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
961 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
961 else:
962 else:
962 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
963 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
963 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
964 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
964 problems += 1
965 problems += 1
965
966
966 # check username
967 # check username
967 ui.status(_("Checking username...\n"))
968 ui.status(_("Checking username...\n"))
968 user = os.environ.get("HGUSER")
969 user = os.environ.get("HGUSER")
969 if user is None:
970 if user is None:
970 user = ui.config("ui", "username")
971 user = ui.config("ui", "username")
971 if user is None:
972 if user is None:
972 user = os.environ.get("EMAIL")
973 user = os.environ.get("EMAIL")
973 if not user:
974 if not user:
974 ui.warn(" ")
975 ui.warn(" ")
975 ui.username()
976 ui.username()
976 ui.write(_(" (specify a username in your .hgrc file)\n"))
977 ui.write(_(" (specify a username in your .hgrc file)\n"))
977
978
978 if not problems:
979 if not problems:
979 ui.status(_("No problems detected\n"))
980 ui.status(_("No problems detected\n"))
980 else:
981 else:
981 ui.write(_("%s problems detected,"
982 ui.write(_("%s problems detected,"
982 " please check your install!\n") % problems)
983 " please check your install!\n") % problems)
983
984
984 return problems
985 return problems
985
986
986 def debugrename(ui, repo, file1, *pats, **opts):
987 def debugrename(ui, repo, file1, *pats, **opts):
987 """dump rename information"""
988 """dump rename information"""
988
989
989 ctx = repo.changectx(opts.get('rev', 'tip'))
990 ctx = repo.changectx(opts.get('rev', 'tip'))
990 for src, abs, rel, exact in cmdutil.walk(repo, (file1,) + pats, opts,
991 for src, abs, rel, exact in cmdutil.walk(repo, (file1,) + pats, opts,
991 ctx.node()):
992 ctx.node()):
992 m = ctx.filectx(abs).renamed()
993 m = ctx.filectx(abs).renamed()
993 if m:
994 if m:
994 ui.write(_("%s renamed from %s:%s\n") % (rel, m[0], hex(m[1])))
995 ui.write(_("%s renamed from %s:%s\n") % (rel, m[0], hex(m[1])))
995 else:
996 else:
996 ui.write(_("%s not renamed\n") % rel)
997 ui.write(_("%s not renamed\n") % rel)
997
998
998 def debugwalk(ui, repo, *pats, **opts):
999 def debugwalk(ui, repo, *pats, **opts):
999 """show how files match on given patterns"""
1000 """show how files match on given patterns"""
1000 items = list(cmdutil.walk(repo, pats, opts))
1001 items = list(cmdutil.walk(repo, pats, opts))
1001 if not items:
1002 if not items:
1002 return
1003 return
1003 fmt = '%%s %%-%ds %%-%ds %%s' % (
1004 fmt = '%%s %%-%ds %%-%ds %%s' % (
1004 max([len(abs) for (src, abs, rel, exact) in items]),
1005 max([len(abs) for (src, abs, rel, exact) in items]),
1005 max([len(rel) for (src, abs, rel, exact) in items]))
1006 max([len(rel) for (src, abs, rel, exact) in items]))
1006 for src, abs, rel, exact in items:
1007 for src, abs, rel, exact in items:
1007 line = fmt % (src, abs, rel, exact and 'exact' or '')
1008 line = fmt % (src, abs, rel, exact and 'exact' or '')
1008 ui.write("%s\n" % line.rstrip())
1009 ui.write("%s\n" % line.rstrip())
1009
1010
1010 def diff(ui, repo, *pats, **opts):
1011 def diff(ui, repo, *pats, **opts):
1011 """diff repository (or selected files)
1012 """diff repository (or selected files)
1012
1013
1013 Show differences between revisions for the specified files.
1014 Show differences between revisions for the specified files.
1014
1015
1015 Differences between files are shown using the unified diff format.
1016 Differences between files are shown using the unified diff format.
1016
1017
1017 NOTE: diff may generate unexpected results for merges, as it will
1018 NOTE: diff may generate unexpected results for merges, as it will
1018 default to comparing against the working directory's first parent
1019 default to comparing against the working directory's first parent
1019 changeset if no revisions are specified.
1020 changeset if no revisions are specified.
1020
1021
1021 When two revision arguments are given, then changes are shown
1022 When two revision arguments are given, then changes are shown
1022 between those revisions. If only one revision is specified then
1023 between those revisions. If only one revision is specified then
1023 that revision is compared to the working directory, and, when no
1024 that revision is compared to the working directory, and, when no
1024 revisions are specified, the working directory files are compared
1025 revisions are specified, the working directory files are compared
1025 to its parent.
1026 to its parent.
1026
1027
1027 Without the -a option, diff will avoid generating diffs of files
1028 Without the -a option, diff will avoid generating diffs of files
1028 it detects as binary. With -a, diff will generate a diff anyway,
1029 it detects as binary. With -a, diff will generate a diff anyway,
1029 probably with undesirable results.
1030 probably with undesirable results.
1030 """
1031 """
1031 node1, node2 = cmdutil.revpair(repo, opts['rev'])
1032 node1, node2 = cmdutil.revpair(repo, opts['rev'])
1032
1033
1033 fns, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
1034 fns, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
1034
1035
1035 patch.diff(repo, node1, node2, fns, match=matchfn,
1036 patch.diff(repo, node1, node2, fns, match=matchfn,
1036 opts=patch.diffopts(ui, opts))
1037 opts=patch.diffopts(ui, opts))
1037
1038
1038 def export(ui, repo, *changesets, **opts):
1039 def export(ui, repo, *changesets, **opts):
1039 """dump the header and diffs for one or more changesets
1040 """dump the header and diffs for one or more changesets
1040
1041
1041 Print the changeset header and diffs for one or more revisions.
1042 Print the changeset header and diffs for one or more revisions.
1042
1043
1043 The information shown in the changeset header is: author,
1044 The information shown in the changeset header is: author,
1044 changeset hash, parent(s) and commit comment.
1045 changeset hash, parent(s) and commit comment.
1045
1046
1046 NOTE: export may generate unexpected diff output for merge changesets,
1047 NOTE: export may generate unexpected diff output for merge changesets,
1047 as it will compare the merge changeset against its first parent only.
1048 as it will compare the merge changeset against its first parent only.
1048
1049
1049 Output may be to a file, in which case the name of the file is
1050 Output may be to a file, in which case the name of the file is
1050 given using a format string. The formatting rules are as follows:
1051 given using a format string. The formatting rules are as follows:
1051
1052
1052 %% literal "%" character
1053 %% literal "%" character
1053 %H changeset hash (40 bytes of hexadecimal)
1054 %H changeset hash (40 bytes of hexadecimal)
1054 %N number of patches being generated
1055 %N number of patches being generated
1055 %R changeset revision number
1056 %R changeset revision number
1056 %b basename of the exporting repository
1057 %b basename of the exporting repository
1057 %h short-form changeset hash (12 bytes of hexadecimal)
1058 %h short-form changeset hash (12 bytes of hexadecimal)
1058 %n zero-padded sequence number, starting at 1
1059 %n zero-padded sequence number, starting at 1
1059 %r zero-padded changeset revision number
1060 %r zero-padded changeset revision number
1060
1061
1061 Without the -a option, export will avoid generating diffs of files
1062 Without the -a option, export will avoid generating diffs of files
1062 it detects as binary. With -a, export will generate a diff anyway,
1063 it detects as binary. With -a, export will generate a diff anyway,
1063 probably with undesirable results.
1064 probably with undesirable results.
1064
1065
1065 With the --switch-parent option, the diff will be against the second
1066 With the --switch-parent option, the diff will be against the second
1066 parent. It can be useful to review a merge.
1067 parent. It can be useful to review a merge.
1067 """
1068 """
1068 if not changesets:
1069 if not changesets:
1069 raise util.Abort(_("export requires at least one changeset"))
1070 raise util.Abort(_("export requires at least one changeset"))
1070 revs = cmdutil.revrange(repo, changesets)
1071 revs = cmdutil.revrange(repo, changesets)
1071 if len(revs) > 1:
1072 if len(revs) > 1:
1072 ui.note(_('exporting patches:\n'))
1073 ui.note(_('exporting patches:\n'))
1073 else:
1074 else:
1074 ui.note(_('exporting patch:\n'))
1075 ui.note(_('exporting patch:\n'))
1075 patch.export(repo, revs, template=opts['output'],
1076 patch.export(repo, revs, template=opts['output'],
1076 switch_parent=opts['switch_parent'],
1077 switch_parent=opts['switch_parent'],
1077 opts=patch.diffopts(ui, opts))
1078 opts=patch.diffopts(ui, opts))
1078
1079
1079 def grep(ui, repo, pattern, *pats, **opts):
1080 def grep(ui, repo, pattern, *pats, **opts):
1080 """search for a pattern in specified files and revisions
1081 """search for a pattern in specified files and revisions
1081
1082
1082 Search revisions of files for a regular expression.
1083 Search revisions of files for a regular expression.
1083
1084
1084 This command behaves differently than Unix grep. It only accepts
1085 This command behaves differently than Unix grep. It only accepts
1085 Python/Perl regexps. It searches repository history, not the
1086 Python/Perl regexps. It searches repository history, not the
1086 working directory. It always prints the revision number in which
1087 working directory. It always prints the revision number in which
1087 a match appears.
1088 a match appears.
1088
1089
1089 By default, grep only prints output for the first revision of a
1090 By default, grep only prints output for the first revision of a
1090 file in which it finds a match. To get it to print every revision
1091 file in which it finds a match. To get it to print every revision
1091 that contains a change in match status ("-" for a match that
1092 that contains a change in match status ("-" for a match that
1092 becomes a non-match, or "+" for a non-match that becomes a match),
1093 becomes a non-match, or "+" for a non-match that becomes a match),
1093 use the --all flag.
1094 use the --all flag.
1094 """
1095 """
1095 reflags = 0
1096 reflags = 0
1096 if opts['ignore_case']:
1097 if opts['ignore_case']:
1097 reflags |= re.I
1098 reflags |= re.I
1098 regexp = re.compile(pattern, reflags)
1099 regexp = re.compile(pattern, reflags)
1099 sep, eol = ':', '\n'
1100 sep, eol = ':', '\n'
1100 if opts['print0']:
1101 if opts['print0']:
1101 sep = eol = '\0'
1102 sep = eol = '\0'
1102
1103
1103 fcache = {}
1104 fcache = {}
1104 def getfile(fn):
1105 def getfile(fn):
1105 if fn not in fcache:
1106 if fn not in fcache:
1106 fcache[fn] = repo.file(fn)
1107 fcache[fn] = repo.file(fn)
1107 return fcache[fn]
1108 return fcache[fn]
1108
1109
1109 def matchlines(body):
1110 def matchlines(body):
1110 begin = 0
1111 begin = 0
1111 linenum = 0
1112 linenum = 0
1112 while True:
1113 while True:
1113 match = regexp.search(body, begin)
1114 match = regexp.search(body, begin)
1114 if not match:
1115 if not match:
1115 break
1116 break
1116 mstart, mend = match.span()
1117 mstart, mend = match.span()
1117 linenum += body.count('\n', begin, mstart) + 1
1118 linenum += body.count('\n', begin, mstart) + 1
1118 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1119 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1119 lend = body.find('\n', mend)
1120 lend = body.find('\n', mend)
1120 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1121 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1121 begin = lend + 1
1122 begin = lend + 1
1122
1123
1123 class linestate(object):
1124 class linestate(object):
1124 def __init__(self, line, linenum, colstart, colend):
1125 def __init__(self, line, linenum, colstart, colend):
1125 self.line = line
1126 self.line = line
1126 self.linenum = linenum
1127 self.linenum = linenum
1127 self.colstart = colstart
1128 self.colstart = colstart
1128 self.colend = colend
1129 self.colend = colend
1129
1130
1130 def __eq__(self, other):
1131 def __eq__(self, other):
1131 return self.line == other.line
1132 return self.line == other.line
1132
1133
1133 matches = {}
1134 matches = {}
1134 copies = {}
1135 copies = {}
1135 def grepbody(fn, rev, body):
1136 def grepbody(fn, rev, body):
1136 matches[rev].setdefault(fn, [])
1137 matches[rev].setdefault(fn, [])
1137 m = matches[rev][fn]
1138 m = matches[rev][fn]
1138 for lnum, cstart, cend, line in matchlines(body):
1139 for lnum, cstart, cend, line in matchlines(body):
1139 s = linestate(line, lnum, cstart, cend)
1140 s = linestate(line, lnum, cstart, cend)
1140 m.append(s)
1141 m.append(s)
1141
1142
1142 def difflinestates(a, b):
1143 def difflinestates(a, b):
1143 sm = difflib.SequenceMatcher(None, a, b)
1144 sm = difflib.SequenceMatcher(None, a, b)
1144 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1145 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1145 if tag == 'insert':
1146 if tag == 'insert':
1146 for i in xrange(blo, bhi):
1147 for i in xrange(blo, bhi):
1147 yield ('+', b[i])
1148 yield ('+', b[i])
1148 elif tag == 'delete':
1149 elif tag == 'delete':
1149 for i in xrange(alo, ahi):
1150 for i in xrange(alo, ahi):
1150 yield ('-', a[i])
1151 yield ('-', a[i])
1151 elif tag == 'replace':
1152 elif tag == 'replace':
1152 for i in xrange(alo, ahi):
1153 for i in xrange(alo, ahi):
1153 yield ('-', a[i])
1154 yield ('-', a[i])
1154 for i in xrange(blo, bhi):
1155 for i in xrange(blo, bhi):
1155 yield ('+', b[i])
1156 yield ('+', b[i])
1156
1157
1157 prev = {}
1158 prev = {}
1158 def display(fn, rev, states, prevstates):
1159 def display(fn, rev, states, prevstates):
1159 counts = {'-': 0, '+': 0}
1160 counts = {'-': 0, '+': 0}
1160 filerevmatches = {}
1161 filerevmatches = {}
1161 if incrementing or not opts['all']:
1162 if incrementing or not opts['all']:
1162 a, b, r = prevstates, states, rev
1163 a, b, r = prevstates, states, rev
1163 else:
1164 else:
1164 a, b, r = states, prevstates, prev.get(fn, -1)
1165 a, b, r = states, prevstates, prev.get(fn, -1)
1165 for change, l in difflinestates(a, b):
1166 for change, l in difflinestates(a, b):
1166 cols = [fn, str(r)]
1167 cols = [fn, str(r)]
1167 if opts['line_number']:
1168 if opts['line_number']:
1168 cols.append(str(l.linenum))
1169 cols.append(str(l.linenum))
1169 if opts['all']:
1170 if opts['all']:
1170 cols.append(change)
1171 cols.append(change)
1171 if opts['user']:
1172 if opts['user']:
1172 cols.append(ui.shortuser(get(r)[1]))
1173 cols.append(ui.shortuser(get(r)[1]))
1173 if opts['files_with_matches']:
1174 if opts['files_with_matches']:
1174 c = (fn, r)
1175 c = (fn, r)
1175 if c in filerevmatches:
1176 if c in filerevmatches:
1176 continue
1177 continue
1177 filerevmatches[c] = 1
1178 filerevmatches[c] = 1
1178 else:
1179 else:
1179 cols.append(l.line)
1180 cols.append(l.line)
1180 ui.write(sep.join(cols), eol)
1181 ui.write(sep.join(cols), eol)
1181 counts[change] += 1
1182 counts[change] += 1
1182 return counts['+'], counts['-']
1183 return counts['+'], counts['-']
1183
1184
1184 fstate = {}
1185 fstate = {}
1185 skip = {}
1186 skip = {}
1186 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
1187 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
1187 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1188 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1188 count = 0
1189 count = 0
1189 incrementing = False
1190 incrementing = False
1190 follow = opts.get('follow')
1191 follow = opts.get('follow')
1191 for st, rev, fns in changeiter:
1192 for st, rev, fns in changeiter:
1192 if st == 'window':
1193 if st == 'window':
1193 incrementing = rev
1194 incrementing = rev
1194 matches.clear()
1195 matches.clear()
1195 elif st == 'add':
1196 elif st == 'add':
1196 mf = repo.changectx(rev).manifest()
1197 mf = repo.changectx(rev).manifest()
1197 matches[rev] = {}
1198 matches[rev] = {}
1198 for fn in fns:
1199 for fn in fns:
1199 if fn in skip:
1200 if fn in skip:
1200 continue
1201 continue
1201 fstate.setdefault(fn, {})
1202 fstate.setdefault(fn, {})
1202 try:
1203 try:
1203 grepbody(fn, rev, getfile(fn).read(mf[fn]))
1204 grepbody(fn, rev, getfile(fn).read(mf[fn]))
1204 if follow:
1205 if follow:
1205 copied = getfile(fn).renamed(mf[fn])
1206 copied = getfile(fn).renamed(mf[fn])
1206 if copied:
1207 if copied:
1207 copies.setdefault(rev, {})[fn] = copied[0]
1208 copies.setdefault(rev, {})[fn] = copied[0]
1208 except KeyError:
1209 except KeyError:
1209 pass
1210 pass
1210 elif st == 'iter':
1211 elif st == 'iter':
1211 states = matches[rev].items()
1212 states = matches[rev].items()
1212 states.sort()
1213 states.sort()
1213 for fn, m in states:
1214 for fn, m in states:
1214 copy = copies.get(rev, {}).get(fn)
1215 copy = copies.get(rev, {}).get(fn)
1215 if fn in skip:
1216 if fn in skip:
1216 if copy:
1217 if copy:
1217 skip[copy] = True
1218 skip[copy] = True
1218 continue
1219 continue
1219 if incrementing or not opts['all'] or fstate[fn]:
1220 if incrementing or not opts['all'] or fstate[fn]:
1220 pos, neg = display(fn, rev, m, fstate[fn])
1221 pos, neg = display(fn, rev, m, fstate[fn])
1221 count += pos + neg
1222 count += pos + neg
1222 if pos and not opts['all']:
1223 if pos and not opts['all']:
1223 skip[fn] = True
1224 skip[fn] = True
1224 if copy:
1225 if copy:
1225 skip[copy] = True
1226 skip[copy] = True
1226 fstate[fn] = m
1227 fstate[fn] = m
1227 if copy:
1228 if copy:
1228 fstate[copy] = m
1229 fstate[copy] = m
1229 prev[fn] = rev
1230 prev[fn] = rev
1230
1231
1231 if not incrementing:
1232 if not incrementing:
1232 fstate = fstate.items()
1233 fstate = fstate.items()
1233 fstate.sort()
1234 fstate.sort()
1234 for fn, state in fstate:
1235 for fn, state in fstate:
1235 if fn in skip:
1236 if fn in skip:
1236 continue
1237 continue
1237 if fn not in copies.get(prev[fn], {}):
1238 if fn not in copies.get(prev[fn], {}):
1238 display(fn, rev, {}, state)
1239 display(fn, rev, {}, state)
1239 return (count == 0 and 1) or 0
1240 return (count == 0 and 1) or 0
1240
1241
1241 def heads(ui, repo, **opts):
1242 def heads(ui, repo, **opts):
1242 """show current repository heads
1243 """show current repository heads
1243
1244
1244 Show all repository head changesets.
1245 Show all repository head changesets.
1245
1246
1246 Repository "heads" are changesets that don't have children
1247 Repository "heads" are changesets that don't have children
1247 changesets. They are where development generally takes place and
1248 changesets. They are where development generally takes place and
1248 are the usual targets for update and merge operations.
1249 are the usual targets for update and merge operations.
1249 """
1250 """
1250 if opts['rev']:
1251 if opts['rev']:
1251 heads = repo.heads(repo.lookup(opts['rev']))
1252 heads = repo.heads(repo.lookup(opts['rev']))
1252 else:
1253 else:
1253 heads = repo.heads()
1254 heads = repo.heads()
1254 displayer = cmdutil.show_changeset(ui, repo, opts)
1255 displayer = cmdutil.show_changeset(ui, repo, opts)
1255 for n in heads:
1256 for n in heads:
1256 displayer.show(changenode=n)
1257 displayer.show(changenode=n)
1257
1258
1258 def help_(ui, name=None, with_version=False):
1259 def help_(ui, name=None, with_version=False):
1259 """show help for a command, extension, or list of commands
1260 """show help for a command, extension, or list of commands
1260
1261
1261 With no arguments, print a list of commands and short help.
1262 With no arguments, print a list of commands and short help.
1262
1263
1263 Given a command name, print help for that command.
1264 Given a command name, print help for that command.
1264
1265
1265 Given an extension name, print help for that extension, and the
1266 Given an extension name, print help for that extension, and the
1266 commands it provides."""
1267 commands it provides."""
1267 option_lists = []
1268 option_lists = []
1268
1269
1269 def helpcmd(name):
1270 def helpcmd(name):
1270 if with_version:
1271 if with_version:
1271 version_(ui)
1272 version_(ui)
1272 ui.write('\n')
1273 ui.write('\n')
1273 aliases, i = findcmd(ui, name)
1274 aliases, i = findcmd(ui, name)
1274 # synopsis
1275 # synopsis
1275 ui.write("%s\n\n" % i[2])
1276 ui.write("%s\n\n" % i[2])
1276
1277
1277 # description
1278 # description
1278 doc = i[0].__doc__
1279 doc = i[0].__doc__
1279 if not doc:
1280 if not doc:
1280 doc = _("(No help text available)")
1281 doc = _("(No help text available)")
1281 if ui.quiet:
1282 if ui.quiet:
1282 doc = doc.splitlines(0)[0]
1283 doc = doc.splitlines(0)[0]
1283 ui.write("%s\n" % doc.rstrip())
1284 ui.write("%s\n" % doc.rstrip())
1284
1285
1285 if not ui.quiet:
1286 if not ui.quiet:
1286 # aliases
1287 # aliases
1287 if len(aliases) > 1:
1288 if len(aliases) > 1:
1288 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
1289 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
1289
1290
1290 # options
1291 # options
1291 if i[1]:
1292 if i[1]:
1292 option_lists.append(("options", i[1]))
1293 option_lists.append(("options", i[1]))
1293
1294
1294 def helplist(select=None):
1295 def helplist(select=None):
1295 h = {}
1296 h = {}
1296 cmds = {}
1297 cmds = {}
1297 for c, e in table.items():
1298 for c, e in table.items():
1298 f = c.split("|", 1)[0]
1299 f = c.split("|", 1)[0]
1299 if select and not select(f):
1300 if select and not select(f):
1300 continue
1301 continue
1301 if name == "shortlist" and not f.startswith("^"):
1302 if name == "shortlist" and not f.startswith("^"):
1302 continue
1303 continue
1303 f = f.lstrip("^")
1304 f = f.lstrip("^")
1304 if not ui.debugflag and f.startswith("debug"):
1305 if not ui.debugflag and f.startswith("debug"):
1305 continue
1306 continue
1306 doc = e[0].__doc__
1307 doc = e[0].__doc__
1307 if not doc:
1308 if not doc:
1308 doc = _("(No help text available)")
1309 doc = _("(No help text available)")
1309 h[f] = doc.splitlines(0)[0].rstrip()
1310 h[f] = doc.splitlines(0)[0].rstrip()
1310 cmds[f] = c.lstrip("^")
1311 cmds[f] = c.lstrip("^")
1311
1312
1312 fns = h.keys()
1313 fns = h.keys()
1313 fns.sort()
1314 fns.sort()
1314 m = max(map(len, fns))
1315 m = max(map(len, fns))
1315 for f in fns:
1316 for f in fns:
1316 if ui.verbose:
1317 if ui.verbose:
1317 commands = cmds[f].replace("|",", ")
1318 commands = cmds[f].replace("|",", ")
1318 ui.write(" %s:\n %s\n"%(commands, h[f]))
1319 ui.write(" %s:\n %s\n"%(commands, h[f]))
1319 else:
1320 else:
1320 ui.write(' %-*s %s\n' % (m, f, h[f]))
1321 ui.write(' %-*s %s\n' % (m, f, h[f]))
1321
1322
1322 def helptopic(name):
1323 def helptopic(name):
1323 v = None
1324 v = None
1324 for i in help.helptable:
1325 for i in help.helptable:
1325 l = i.split('|')
1326 l = i.split('|')
1326 if name in l:
1327 if name in l:
1327 v = i
1328 v = i
1328 header = l[-1]
1329 header = l[-1]
1329 if not v:
1330 if not v:
1330 raise UnknownCommand(name)
1331 raise UnknownCommand(name)
1331
1332
1332 # description
1333 # description
1333 doc = help.helptable[v]
1334 doc = help.helptable[v]
1334 if not doc:
1335 if not doc:
1335 doc = _("(No help text available)")
1336 doc = _("(No help text available)")
1336 if callable(doc):
1337 if callable(doc):
1337 doc = doc()
1338 doc = doc()
1338
1339
1339 ui.write("%s\n" % header)
1340 ui.write("%s\n" % header)
1340 ui.write("%s\n" % doc.rstrip())
1341 ui.write("%s\n" % doc.rstrip())
1341
1342
1342 def helpext(name):
1343 def helpext(name):
1343 try:
1344 try:
1344 mod = findext(name)
1345 mod = findext(name)
1345 except KeyError:
1346 except KeyError:
1346 raise UnknownCommand(name)
1347 raise UnknownCommand(name)
1347
1348
1348 doc = (mod.__doc__ or _('No help text available')).splitlines(0)
1349 doc = (mod.__doc__ or _('No help text available')).splitlines(0)
1349 ui.write(_('%s extension - %s\n') % (name.split('.')[-1], doc[0]))
1350 ui.write(_('%s extension - %s\n') % (name.split('.')[-1], doc[0]))
1350 for d in doc[1:]:
1351 for d in doc[1:]:
1351 ui.write(d, '\n')
1352 ui.write(d, '\n')
1352
1353
1353 ui.status('\n')
1354 ui.status('\n')
1354
1355
1355 try:
1356 try:
1356 ct = mod.cmdtable
1357 ct = mod.cmdtable
1357 except AttributeError:
1358 except AttributeError:
1358 ui.status(_('no commands defined\n'))
1359 ui.status(_('no commands defined\n'))
1359 return
1360 return
1360
1361
1361 if ui.verbose:
1362 if ui.verbose:
1362 ui.status(_('list of commands:\n\n'))
1363 ui.status(_('list of commands:\n\n'))
1363 else:
1364 else:
1364 ui.status(_('list of commands (use "hg help -v %s" '
1365 ui.status(_('list of commands (use "hg help -v %s" '
1365 'to show aliases and global options):\n\n') % name)
1366 'to show aliases and global options):\n\n') % name)
1366
1367
1367 modcmds = dict.fromkeys([c.split('|', 1)[0] for c in ct])
1368 modcmds = dict.fromkeys([c.split('|', 1)[0] for c in ct])
1368 helplist(modcmds.has_key)
1369 helplist(modcmds.has_key)
1369
1370
1370 if name and name != 'shortlist':
1371 if name and name != 'shortlist':
1371 i = None
1372 i = None
1372 for f in (helpcmd, helptopic, helpext):
1373 for f in (helpcmd, helptopic, helpext):
1373 try:
1374 try:
1374 f(name)
1375 f(name)
1375 i = None
1376 i = None
1376 break
1377 break
1377 except UnknownCommand, inst:
1378 except UnknownCommand, inst:
1378 i = inst
1379 i = inst
1379 if i:
1380 if i:
1380 raise i
1381 raise i
1381
1382
1382 else:
1383 else:
1383 # program name
1384 # program name
1384 if ui.verbose or with_version:
1385 if ui.verbose or with_version:
1385 version_(ui)
1386 version_(ui)
1386 else:
1387 else:
1387 ui.status(_("Mercurial Distributed SCM\n"))
1388 ui.status(_("Mercurial Distributed SCM\n"))
1388 ui.status('\n')
1389 ui.status('\n')
1389
1390
1390 # list of commands
1391 # list of commands
1391 if name == "shortlist":
1392 if name == "shortlist":
1392 ui.status(_('basic commands (use "hg help" '
1393 ui.status(_('basic commands (use "hg help" '
1393 'for the full list or option "-v" for details):\n\n'))
1394 'for the full list or option "-v" for details):\n\n'))
1394 elif ui.verbose:
1395 elif ui.verbose:
1395 ui.status(_('list of commands:\n\n'))
1396 ui.status(_('list of commands:\n\n'))
1396 else:
1397 else:
1397 ui.status(_('list of commands (use "hg help -v" '
1398 ui.status(_('list of commands (use "hg help -v" '
1398 'to show aliases and global options):\n\n'))
1399 'to show aliases and global options):\n\n'))
1399
1400
1400 helplist()
1401 helplist()
1401
1402
1402 # global options
1403 # global options
1403 if ui.verbose:
1404 if ui.verbose:
1404 option_lists.append(("global options", globalopts))
1405 option_lists.append(("global options", globalopts))
1405
1406
1406 # list all option lists
1407 # list all option lists
1407 opt_output = []
1408 opt_output = []
1408 for title, options in option_lists:
1409 for title, options in option_lists:
1409 opt_output.append(("\n%s:\n" % title, None))
1410 opt_output.append(("\n%s:\n" % title, None))
1410 for shortopt, longopt, default, desc in options:
1411 for shortopt, longopt, default, desc in options:
1411 if "DEPRECATED" in desc and not ui.verbose: continue
1412 if "DEPRECATED" in desc and not ui.verbose: continue
1412 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
1413 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
1413 longopt and " --%s" % longopt),
1414 longopt and " --%s" % longopt),
1414 "%s%s" % (desc,
1415 "%s%s" % (desc,
1415 default
1416 default
1416 and _(" (default: %s)") % default
1417 and _(" (default: %s)") % default
1417 or "")))
1418 or "")))
1418
1419
1419 if opt_output:
1420 if opt_output:
1420 opts_len = max([len(line[0]) for line in opt_output if line[1]])
1421 opts_len = max([len(line[0]) for line in opt_output if line[1]])
1421 for first, second in opt_output:
1422 for first, second in opt_output:
1422 if second:
1423 if second:
1423 ui.write(" %-*s %s\n" % (opts_len, first, second))
1424 ui.write(" %-*s %s\n" % (opts_len, first, second))
1424 else:
1425 else:
1425 ui.write("%s\n" % first)
1426 ui.write("%s\n" % first)
1426
1427
1427 def identify(ui, repo):
1428 def identify(ui, repo):
1428 """print information about the working copy
1429 """print information about the working copy
1429
1430
1430 Print a short summary of the current state of the repo.
1431 Print a short summary of the current state of the repo.
1431
1432
1432 This summary identifies the repository state using one or two parent
1433 This summary identifies the repository state using one or two parent
1433 hash identifiers, followed by a "+" if there are uncommitted changes
1434 hash identifiers, followed by a "+" if there are uncommitted changes
1434 in the working directory, followed by a list of tags for this revision.
1435 in the working directory, followed by a list of tags for this revision.
1435 """
1436 """
1436 parents = [p for p in repo.dirstate.parents() if p != nullid]
1437 parents = [p for p in repo.dirstate.parents() if p != nullid]
1437 if not parents:
1438 if not parents:
1438 ui.write(_("unknown\n"))
1439 ui.write(_("unknown\n"))
1439 return
1440 return
1440
1441
1441 hexfunc = ui.debugflag and hex or short
1442 hexfunc = ui.debugflag and hex or short
1442 modified, added, removed, deleted = repo.status()[:4]
1443 modified, added, removed, deleted = repo.status()[:4]
1443 output = ["%s%s" %
1444 output = ["%s%s" %
1444 ('+'.join([hexfunc(parent) for parent in parents]),
1445 ('+'.join([hexfunc(parent) for parent in parents]),
1445 (modified or added or removed or deleted) and "+" or "")]
1446 (modified or added or removed or deleted) and "+" or "")]
1446
1447
1447 if not ui.quiet:
1448 if not ui.quiet:
1448
1449
1449 branch = util.tolocal(repo.workingctx().branch())
1450 branch = util.tolocal(repo.workingctx().branch())
1450 if branch:
1451 if branch:
1451 output.append("(%s)" % branch)
1452 output.append("(%s)" % branch)
1452
1453
1453 # multiple tags for a single parent separated by '/'
1454 # multiple tags for a single parent separated by '/'
1454 parenttags = ['/'.join(tags)
1455 parenttags = ['/'.join(tags)
1455 for tags in map(repo.nodetags, parents) if tags]
1456 for tags in map(repo.nodetags, parents) if tags]
1456 # tags for multiple parents separated by ' + '
1457 # tags for multiple parents separated by ' + '
1457 if parenttags:
1458 if parenttags:
1458 output.append(' + '.join(parenttags))
1459 output.append(' + '.join(parenttags))
1459
1460
1460 ui.write("%s\n" % ' '.join(output))
1461 ui.write("%s\n" % ' '.join(output))
1461
1462
1462 def import_(ui, repo, patch1, *patches, **opts):
1463 def import_(ui, repo, patch1, *patches, **opts):
1463 """import an ordered set of patches
1464 """import an ordered set of patches
1464
1465
1465 Import a list of patches and commit them individually.
1466 Import a list of patches and commit them individually.
1466
1467
1467 If there are outstanding changes in the working directory, import
1468 If there are outstanding changes in the working directory, import
1468 will abort unless given the -f flag.
1469 will abort unless given the -f flag.
1469
1470
1470 You can import a patch straight from a mail message. Even patches
1471 You can import a patch straight from a mail message. Even patches
1471 as attachments work (body part must be type text/plain or
1472 as attachments work (body part must be type text/plain or
1472 text/x-patch to be used). From and Subject headers of email
1473 text/x-patch to be used). From and Subject headers of email
1473 message are used as default committer and commit message. All
1474 message are used as default committer and commit message. All
1474 text/plain body parts before first diff are added to commit
1475 text/plain body parts before first diff are added to commit
1475 message.
1476 message.
1476
1477
1477 If imported patch was generated by hg export, user and description
1478 If imported patch was generated by hg export, user and description
1478 from patch override values from message headers and body. Values
1479 from patch override values from message headers and body. Values
1479 given on command line with -m and -u override these.
1480 given on command line with -m and -u override these.
1480
1481
1481 To read a patch from standard input, use patch name "-".
1482 To read a patch from standard input, use patch name "-".
1482 """
1483 """
1483 patches = (patch1,) + patches
1484 patches = (patch1,) + patches
1484
1485
1485 if not opts['force']:
1486 if not opts['force']:
1486 bail_if_changed(repo)
1487 bail_if_changed(repo)
1487
1488
1488 d = opts["base"]
1489 d = opts["base"]
1489 strip = opts["strip"]
1490 strip = opts["strip"]
1490
1491
1491 wlock = repo.wlock()
1492 wlock = repo.wlock()
1492 lock = repo.lock()
1493 lock = repo.lock()
1493
1494
1494 for p in patches:
1495 for p in patches:
1495 pf = os.path.join(d, p)
1496 pf = os.path.join(d, p)
1496
1497
1497 if pf == '-':
1498 if pf == '-':
1498 ui.status(_("applying patch from stdin\n"))
1499 ui.status(_("applying patch from stdin\n"))
1499 tmpname, message, user, date = patch.extract(ui, sys.stdin)
1500 tmpname, message, user, date = patch.extract(ui, sys.stdin)
1500 else:
1501 else:
1501 ui.status(_("applying %s\n") % p)
1502 ui.status(_("applying %s\n") % p)
1502 tmpname, message, user, date = patch.extract(ui, file(pf))
1503 tmpname, message, user, date = patch.extract(ui, file(pf))
1503
1504
1504 if tmpname is None:
1505 if tmpname is None:
1505 raise util.Abort(_('no diffs found'))
1506 raise util.Abort(_('no diffs found'))
1506
1507
1507 try:
1508 try:
1508 cmdline_message = logmessage(opts)
1509 cmdline_message = logmessage(opts)
1509 if cmdline_message:
1510 if cmdline_message:
1510 # pickup the cmdline msg
1511 # pickup the cmdline msg
1511 message = cmdline_message
1512 message = cmdline_message
1512 elif message:
1513 elif message:
1513 # pickup the patch msg
1514 # pickup the patch msg
1514 message = message.strip()
1515 message = message.strip()
1515 else:
1516 else:
1516 # launch the editor
1517 # launch the editor
1517 message = None
1518 message = None
1518 ui.debug(_('message:\n%s\n') % message)
1519 ui.debug(_('message:\n%s\n') % message)
1519
1520
1520 files = {}
1521 files = {}
1521 try:
1522 try:
1522 fuzz = patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
1523 fuzz = patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
1523 files=files)
1524 files=files)
1524 finally:
1525 finally:
1525 files = patch.updatedir(ui, repo, files, wlock=wlock)
1526 files = patch.updatedir(ui, repo, files, wlock=wlock)
1526 repo.commit(files, message, user, date, wlock=wlock, lock=lock)
1527 repo.commit(files, message, user, date, wlock=wlock, lock=lock)
1527 finally:
1528 finally:
1528 os.unlink(tmpname)
1529 os.unlink(tmpname)
1529
1530
1530 def incoming(ui, repo, source="default", **opts):
1531 def incoming(ui, repo, source="default", **opts):
1531 """show new changesets found in source
1532 """show new changesets found in source
1532
1533
1533 Show new changesets found in the specified path/URL or the default
1534 Show new changesets found in the specified path/URL or the default
1534 pull location. These are the changesets that would be pulled if a pull
1535 pull location. These are the changesets that would be pulled if a pull
1535 was requested.
1536 was requested.
1536
1537
1537 For remote repository, using --bundle avoids downloading the changesets
1538 For remote repository, using --bundle avoids downloading the changesets
1538 twice if the incoming is followed by a pull.
1539 twice if the incoming is followed by a pull.
1539
1540
1540 See pull for valid source format details.
1541 See pull for valid source format details.
1541 """
1542 """
1542 source = ui.expandpath(source)
1543 source = ui.expandpath(source)
1543 setremoteconfig(ui, opts)
1544 setremoteconfig(ui, opts)
1544
1545
1545 other = hg.repository(ui, source)
1546 other = hg.repository(ui, source)
1546 incoming = repo.findincoming(other, force=opts["force"])
1547 incoming = repo.findincoming(other, force=opts["force"])
1547 if not incoming:
1548 if not incoming:
1548 ui.status(_("no changes found\n"))
1549 ui.status(_("no changes found\n"))
1549 return
1550 return
1550
1551
1551 cleanup = None
1552 cleanup = None
1552 try:
1553 try:
1553 fname = opts["bundle"]
1554 fname = opts["bundle"]
1554 if fname or not other.local():
1555 if fname or not other.local():
1555 # create a bundle (uncompressed if other repo is not local)
1556 # create a bundle (uncompressed if other repo is not local)
1556 cg = other.changegroup(incoming, "incoming")
1557 cg = other.changegroup(incoming, "incoming")
1557 bundletype = other.local() and "HG10BZ" or "HG10UN"
1558 bundletype = other.local() and "HG10BZ" or "HG10UN"
1558 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
1559 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
1559 # keep written bundle?
1560 # keep written bundle?
1560 if opts["bundle"]:
1561 if opts["bundle"]:
1561 cleanup = None
1562 cleanup = None
1562 if not other.local():
1563 if not other.local():
1563 # use the created uncompressed bundlerepo
1564 # use the created uncompressed bundlerepo
1564 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1565 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1565
1566
1566 revs = None
1567 revs = None
1567 if opts['rev']:
1568 if opts['rev']:
1568 revs = [other.lookup(rev) for rev in opts['rev']]
1569 revs = [other.lookup(rev) for rev in opts['rev']]
1569 o = other.changelog.nodesbetween(incoming, revs)[0]
1570 o = other.changelog.nodesbetween(incoming, revs)[0]
1570 if opts['newest_first']:
1571 if opts['newest_first']:
1571 o.reverse()
1572 o.reverse()
1572 displayer = cmdutil.show_changeset(ui, other, opts)
1573 displayer = cmdutil.show_changeset(ui, other, opts)
1573 for n in o:
1574 for n in o:
1574 parents = [p for p in other.changelog.parents(n) if p != nullid]
1575 parents = [p for p in other.changelog.parents(n) if p != nullid]
1575 if opts['no_merges'] and len(parents) == 2:
1576 if opts['no_merges'] and len(parents) == 2:
1576 continue
1577 continue
1577 displayer.show(changenode=n)
1578 displayer.show(changenode=n)
1578 finally:
1579 finally:
1579 if hasattr(other, 'close'):
1580 if hasattr(other, 'close'):
1580 other.close()
1581 other.close()
1581 if cleanup:
1582 if cleanup:
1582 os.unlink(cleanup)
1583 os.unlink(cleanup)
1583
1584
1584 def init(ui, dest=".", **opts):
1585 def init(ui, dest=".", **opts):
1585 """create a new repository in the given directory
1586 """create a new repository in the given directory
1586
1587
1587 Initialize a new repository in the given directory. If the given
1588 Initialize a new repository in the given directory. If the given
1588 directory does not exist, it is created.
1589 directory does not exist, it is created.
1589
1590
1590 If no directory is given, the current directory is used.
1591 If no directory is given, the current directory is used.
1591
1592
1592 It is possible to specify an ssh:// URL as the destination.
1593 It is possible to specify an ssh:// URL as the destination.
1593 Look at the help text for the pull command for important details
1594 Look at the help text for the pull command for important details
1594 about ssh:// URLs.
1595 about ssh:// URLs.
1595 """
1596 """
1596 setremoteconfig(ui, opts)
1597 setremoteconfig(ui, opts)
1597 hg.repository(ui, dest, create=1)
1598 hg.repository(ui, dest, create=1)
1598
1599
1599 def locate(ui, repo, *pats, **opts):
1600 def locate(ui, repo, *pats, **opts):
1600 """locate files matching specific patterns
1601 """locate files matching specific patterns
1601
1602
1602 Print all files under Mercurial control whose names match the
1603 Print all files under Mercurial control whose names match the
1603 given patterns.
1604 given patterns.
1604
1605
1605 This command searches the current directory and its
1606 This command searches the current directory and its
1606 subdirectories. To search an entire repository, move to the root
1607 subdirectories. To search an entire repository, move to the root
1607 of the repository.
1608 of the repository.
1608
1609
1609 If no patterns are given to match, this command prints all file
1610 If no patterns are given to match, this command prints all file
1610 names.
1611 names.
1611
1612
1612 If you want to feed the output of this command into the "xargs"
1613 If you want to feed the output of this command into the "xargs"
1613 command, use the "-0" option to both this command and "xargs".
1614 command, use the "-0" option to both this command and "xargs".
1614 This will avoid the problem of "xargs" treating single filenames
1615 This will avoid the problem of "xargs" treating single filenames
1615 that contain white space as multiple filenames.
1616 that contain white space as multiple filenames.
1616 """
1617 """
1617 end = opts['print0'] and '\0' or '\n'
1618 end = opts['print0'] and '\0' or '\n'
1618 rev = opts['rev']
1619 rev = opts['rev']
1619 if rev:
1620 if rev:
1620 node = repo.lookup(rev)
1621 node = repo.lookup(rev)
1621 else:
1622 else:
1622 node = None
1623 node = None
1623
1624
1624 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
1625 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
1625 head='(?:.*/|)'):
1626 head='(?:.*/|)'):
1626 if not node and repo.dirstate.state(abs) == '?':
1627 if not node and repo.dirstate.state(abs) == '?':
1627 continue
1628 continue
1628 if opts['fullpath']:
1629 if opts['fullpath']:
1629 ui.write(os.path.join(repo.root, abs), end)
1630 ui.write(os.path.join(repo.root, abs), end)
1630 else:
1631 else:
1631 ui.write(((pats and rel) or abs), end)
1632 ui.write(((pats and rel) or abs), end)
1632
1633
1633 def log(ui, repo, *pats, **opts):
1634 def log(ui, repo, *pats, **opts):
1634 """show revision history of entire repository or files
1635 """show revision history of entire repository or files
1635
1636
1636 Print the revision history of the specified files or the entire
1637 Print the revision history of the specified files or the entire
1637 project.
1638 project.
1638
1639
1639 File history is shown without following rename or copy history of
1640 File history is shown without following rename or copy history of
1640 files. Use -f/--follow with a file name to follow history across
1641 files. Use -f/--follow with a file name to follow history across
1641 renames and copies. --follow without a file name will only show
1642 renames and copies. --follow without a file name will only show
1642 ancestors or descendants of the starting revision. --follow-first
1643 ancestors or descendants of the starting revision. --follow-first
1643 only follows the first parent of merge revisions.
1644 only follows the first parent of merge revisions.
1644
1645
1645 If no revision range is specified, the default is tip:0 unless
1646 If no revision range is specified, the default is tip:0 unless
1646 --follow is set, in which case the working directory parent is
1647 --follow is set, in which case the working directory parent is
1647 used as the starting revision.
1648 used as the starting revision.
1648
1649
1649 By default this command outputs: changeset id and hash, tags,
1650 By default this command outputs: changeset id and hash, tags,
1650 non-trivial parents, user, date and time, and a summary for each
1651 non-trivial parents, user, date and time, and a summary for each
1651 commit. When the -v/--verbose switch is used, the list of changed
1652 commit. When the -v/--verbose switch is used, the list of changed
1652 files and full commit message is shown.
1653 files and full commit message is shown.
1653
1654
1654 NOTE: log -p may generate unexpected diff output for merge
1655 NOTE: log -p may generate unexpected diff output for merge
1655 changesets, as it will compare the merge changeset against its
1656 changesets, as it will compare the merge changeset against its
1656 first parent only. Also, the files: list will only reflect files
1657 first parent only. Also, the files: list will only reflect files
1657 that are different from BOTH parents.
1658 that are different from BOTH parents.
1658
1659
1659 """
1660 """
1660
1661
1661 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
1662 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
1662 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1663 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1663
1664
1664 if opts['limit']:
1665 if opts['limit']:
1665 try:
1666 try:
1666 limit = int(opts['limit'])
1667 limit = int(opts['limit'])
1667 except ValueError:
1668 except ValueError:
1668 raise util.Abort(_('limit must be a positive integer'))
1669 raise util.Abort(_('limit must be a positive integer'))
1669 if limit <= 0: raise util.Abort(_('limit must be positive'))
1670 if limit <= 0: raise util.Abort(_('limit must be positive'))
1670 else:
1671 else:
1671 limit = sys.maxint
1672 limit = sys.maxint
1672 count = 0
1673 count = 0
1673
1674
1674 if opts['copies'] and opts['rev']:
1675 if opts['copies'] and opts['rev']:
1675 endrev = max(cmdutil.revrange(repo, opts['rev'])) + 1
1676 endrev = max(cmdutil.revrange(repo, opts['rev'])) + 1
1676 else:
1677 else:
1677 endrev = repo.changelog.count()
1678 endrev = repo.changelog.count()
1678 rcache = {}
1679 rcache = {}
1679 ncache = {}
1680 ncache = {}
1680 dcache = []
1681 dcache = []
1681 def getrenamed(fn, rev, man):
1682 def getrenamed(fn, rev, man):
1682 '''looks up all renames for a file (up to endrev) the first
1683 '''looks up all renames for a file (up to endrev) the first
1683 time the file is given. It indexes on the changerev and only
1684 time the file is given. It indexes on the changerev and only
1684 parses the manifest if linkrev != changerev.
1685 parses the manifest if linkrev != changerev.
1685 Returns rename info for fn at changerev rev.'''
1686 Returns rename info for fn at changerev rev.'''
1686 if fn not in rcache:
1687 if fn not in rcache:
1687 rcache[fn] = {}
1688 rcache[fn] = {}
1688 ncache[fn] = {}
1689 ncache[fn] = {}
1689 fl = repo.file(fn)
1690 fl = repo.file(fn)
1690 for i in xrange(fl.count()):
1691 for i in xrange(fl.count()):
1691 node = fl.node(i)
1692 node = fl.node(i)
1692 lr = fl.linkrev(node)
1693 lr = fl.linkrev(node)
1693 renamed = fl.renamed(node)
1694 renamed = fl.renamed(node)
1694 rcache[fn][lr] = renamed
1695 rcache[fn][lr] = renamed
1695 if renamed:
1696 if renamed:
1696 ncache[fn][node] = renamed
1697 ncache[fn][node] = renamed
1697 if lr >= endrev:
1698 if lr >= endrev:
1698 break
1699 break
1699 if rev in rcache[fn]:
1700 if rev in rcache[fn]:
1700 return rcache[fn][rev]
1701 return rcache[fn][rev]
1701 mr = repo.manifest.rev(man)
1702 mr = repo.manifest.rev(man)
1702 if repo.manifest.parentrevs(mr) != (mr - 1, nullrev):
1703 if repo.manifest.parentrevs(mr) != (mr - 1, nullrev):
1703 return ncache[fn].get(repo.manifest.find(man, fn)[0])
1704 return ncache[fn].get(repo.manifest.find(man, fn)[0])
1704 if not dcache or dcache[0] != man:
1705 if not dcache or dcache[0] != man:
1705 dcache[:] = [man, repo.manifest.readdelta(man)]
1706 dcache[:] = [man, repo.manifest.readdelta(man)]
1706 if fn in dcache[1]:
1707 if fn in dcache[1]:
1707 return ncache[fn].get(dcache[1][fn])
1708 return ncache[fn].get(dcache[1][fn])
1708 return None
1709 return None
1709
1710
1710 df = False
1711 df = False
1711 if opts["date"]:
1712 if opts["date"]:
1712 df = util.matchdate(opts["date"])
1713 df = util.matchdate(opts["date"])
1713
1714
1714
1715
1715 displayer = cmdutil.show_changeset(ui, repo, opts, True, matchfn)
1716 displayer = cmdutil.show_changeset(ui, repo, opts, True, matchfn)
1716 for st, rev, fns in changeiter:
1717 for st, rev, fns in changeiter:
1717 if st == 'add':
1718 if st == 'add':
1718 changenode = repo.changelog.node(rev)
1719 changenode = repo.changelog.node(rev)
1719 parents = [p for p in repo.changelog.parentrevs(rev)
1720 parents = [p for p in repo.changelog.parentrevs(rev)
1720 if p != nullrev]
1721 if p != nullrev]
1721 if opts['no_merges'] and len(parents) == 2:
1722 if opts['no_merges'] and len(parents) == 2:
1722 continue
1723 continue
1723 if opts['only_merges'] and len(parents) != 2:
1724 if opts['only_merges'] and len(parents) != 2:
1724 continue
1725 continue
1725
1726
1726 if df:
1727 if df:
1727 changes = get(rev)
1728 changes = get(rev)
1728 if not df(changes[2][0]):
1729 if not df(changes[2][0]):
1729 continue
1730 continue
1730
1731
1731 if opts['keyword']:
1732 if opts['keyword']:
1732 changes = get(rev)
1733 changes = get(rev)
1733 miss = 0
1734 miss = 0
1734 for k in [kw.lower() for kw in opts['keyword']]:
1735 for k in [kw.lower() for kw in opts['keyword']]:
1735 if not (k in changes[1].lower() or
1736 if not (k in changes[1].lower() or
1736 k in changes[4].lower() or
1737 k in changes[4].lower() or
1737 k in " ".join(changes[3][:20]).lower()):
1738 k in " ".join(changes[3][:20]).lower()):
1738 miss = 1
1739 miss = 1
1739 break
1740 break
1740 if miss:
1741 if miss:
1741 continue
1742 continue
1742
1743
1743 copies = []
1744 copies = []
1744 if opts.get('copies') and rev:
1745 if opts.get('copies') and rev:
1745 mf = get(rev)[0]
1746 mf = get(rev)[0]
1746 for fn in get(rev)[3]:
1747 for fn in get(rev)[3]:
1747 rename = getrenamed(fn, rev, mf)
1748 rename = getrenamed(fn, rev, mf)
1748 if rename:
1749 if rename:
1749 copies.append((fn, rename[0]))
1750 copies.append((fn, rename[0]))
1750 displayer.show(rev, changenode, copies=copies)
1751 displayer.show(rev, changenode, copies=copies)
1751 elif st == 'iter':
1752 elif st == 'iter':
1752 if count == limit: break
1753 if count == limit: break
1753 if displayer.flush(rev):
1754 if displayer.flush(rev):
1754 count += 1
1755 count += 1
1755
1756
1756 def manifest(ui, repo, rev=None):
1757 def manifest(ui, repo, rev=None):
1757 """output the current or given revision of the project manifest
1758 """output the current or given revision of the project manifest
1758
1759
1759 Print a list of version controlled files for the given revision.
1760 Print a list of version controlled files for the given revision.
1760 If no revision is given, the parent of the working directory is used,
1761 If no revision is given, the parent of the working directory is used,
1761 or tip if no revision is checked out.
1762 or tip if no revision is checked out.
1762
1763
1763 The manifest is the list of files being version controlled. If no revision
1764 The manifest is the list of files being version controlled. If no revision
1764 is given then the first parent of the working directory is used.
1765 is given then the first parent of the working directory is used.
1765
1766
1766 With -v flag, print file permissions. With --debug flag, print
1767 With -v flag, print file permissions. With --debug flag, print
1767 file revision hashes.
1768 file revision hashes.
1768 """
1769 """
1769
1770
1770 m = repo.changectx(rev).manifest()
1771 m = repo.changectx(rev).manifest()
1771 files = m.keys()
1772 files = m.keys()
1772 files.sort()
1773 files.sort()
1773
1774
1774 for f in files:
1775 for f in files:
1775 if ui.debugflag:
1776 if ui.debugflag:
1776 ui.write("%40s " % hex(m[f]))
1777 ui.write("%40s " % hex(m[f]))
1777 if ui.verbose:
1778 if ui.verbose:
1778 ui.write("%3s " % (m.execf(f) and "755" or "644"))
1779 ui.write("%3s " % (m.execf(f) and "755" or "644"))
1779 ui.write("%s\n" % f)
1780 ui.write("%s\n" % f)
1780
1781
1781 def merge(ui, repo, node=None, force=None, branch=None):
1782 def merge(ui, repo, node=None, force=None, branch=None):
1782 """merge working directory with another revision
1783 """merge working directory with another revision
1783
1784
1784 Merge the contents of the current working directory and the
1785 Merge the contents of the current working directory and the
1785 requested revision. Files that changed between either parent are
1786 requested revision. Files that changed between either parent are
1786 marked as changed for the next commit and a commit must be
1787 marked as changed for the next commit and a commit must be
1787 performed before any further updates are allowed.
1788 performed before any further updates are allowed.
1788
1789
1789 If no revision is specified, the working directory's parent is a
1790 If no revision is specified, the working directory's parent is a
1790 head revision, and the repository contains exactly one other head,
1791 head revision, and the repository contains exactly one other head,
1791 the other head is merged with by default. Otherwise, an explicit
1792 the other head is merged with by default. Otherwise, an explicit
1792 revision to merge with must be provided.
1793 revision to merge with must be provided.
1793 """
1794 """
1794
1795
1795 if node or branch:
1796 if node or branch:
1796 node = _lookup(repo, node, branch)
1797 node = _lookup(repo, node, branch)
1797 else:
1798 else:
1798 heads = repo.heads()
1799 heads = repo.heads()
1799 if len(heads) > 2:
1800 if len(heads) > 2:
1800 raise util.Abort(_('repo has %d heads - '
1801 raise util.Abort(_('repo has %d heads - '
1801 'please merge with an explicit rev') %
1802 'please merge with an explicit rev') %
1802 len(heads))
1803 len(heads))
1803 if len(heads) == 1:
1804 if len(heads) == 1:
1804 raise util.Abort(_('there is nothing to merge - '
1805 raise util.Abort(_('there is nothing to merge - '
1805 'use "hg update" instead'))
1806 'use "hg update" instead'))
1806 parent = repo.dirstate.parents()[0]
1807 parent = repo.dirstate.parents()[0]
1807 if parent not in heads:
1808 if parent not in heads:
1808 raise util.Abort(_('working dir not at a head rev - '
1809 raise util.Abort(_('working dir not at a head rev - '
1809 'use "hg update" or merge with an explicit rev'))
1810 'use "hg update" or merge with an explicit rev'))
1810 node = parent == heads[0] and heads[-1] or heads[0]
1811 node = parent == heads[0] and heads[-1] or heads[0]
1811 return hg.merge(repo, node, force=force)
1812 return hg.merge(repo, node, force=force)
1812
1813
1813 def outgoing(ui, repo, dest=None, **opts):
1814 def outgoing(ui, repo, dest=None, **opts):
1814 """show changesets not found in destination
1815 """show changesets not found in destination
1815
1816
1816 Show changesets not found in the specified destination repository or
1817 Show changesets not found in the specified destination repository or
1817 the default push location. These are the changesets that would be pushed
1818 the default push location. These are the changesets that would be pushed
1818 if a push was requested.
1819 if a push was requested.
1819
1820
1820 See pull for valid destination format details.
1821 See pull for valid destination format details.
1821 """
1822 """
1822 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1823 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1823 setremoteconfig(ui, opts)
1824 setremoteconfig(ui, opts)
1824 revs = None
1825 revs = None
1825 if opts['rev']:
1826 if opts['rev']:
1826 revs = [repo.lookup(rev) for rev in opts['rev']]
1827 revs = [repo.lookup(rev) for rev in opts['rev']]
1827
1828
1828 other = hg.repository(ui, dest)
1829 other = hg.repository(ui, dest)
1829 o = repo.findoutgoing(other, force=opts['force'])
1830 o = repo.findoutgoing(other, force=opts['force'])
1830 if not o:
1831 if not o:
1831 ui.status(_("no changes found\n"))
1832 ui.status(_("no changes found\n"))
1832 return
1833 return
1833 o = repo.changelog.nodesbetween(o, revs)[0]
1834 o = repo.changelog.nodesbetween(o, revs)[0]
1834 if opts['newest_first']:
1835 if opts['newest_first']:
1835 o.reverse()
1836 o.reverse()
1836 displayer = cmdutil.show_changeset(ui, repo, opts)
1837 displayer = cmdutil.show_changeset(ui, repo, opts)
1837 for n in o:
1838 for n in o:
1838 parents = [p for p in repo.changelog.parents(n) if p != nullid]
1839 parents = [p for p in repo.changelog.parents(n) if p != nullid]
1839 if opts['no_merges'] and len(parents) == 2:
1840 if opts['no_merges'] and len(parents) == 2:
1840 continue
1841 continue
1841 displayer.show(changenode=n)
1842 displayer.show(changenode=n)
1842
1843
1843 def parents(ui, repo, file_=None, **opts):
1844 def parents(ui, repo, file_=None, **opts):
1844 """show the parents of the working dir or revision
1845 """show the parents of the working dir or revision
1845
1846
1846 Print the working directory's parent revisions.
1847 Print the working directory's parent revisions.
1847 """
1848 """
1848 rev = opts.get('rev')
1849 rev = opts.get('rev')
1849 if rev:
1850 if rev:
1850 if file_:
1851 if file_:
1851 ctx = repo.filectx(file_, changeid=rev)
1852 ctx = repo.filectx(file_, changeid=rev)
1852 else:
1853 else:
1853 ctx = repo.changectx(rev)
1854 ctx = repo.changectx(rev)
1854 p = [cp.node() for cp in ctx.parents()]
1855 p = [cp.node() for cp in ctx.parents()]
1855 else:
1856 else:
1856 p = repo.dirstate.parents()
1857 p = repo.dirstate.parents()
1857
1858
1858 displayer = cmdutil.show_changeset(ui, repo, opts)
1859 displayer = cmdutil.show_changeset(ui, repo, opts)
1859 for n in p:
1860 for n in p:
1860 if n != nullid:
1861 if n != nullid:
1861 displayer.show(changenode=n)
1862 displayer.show(changenode=n)
1862
1863
1863 def paths(ui, repo, search=None):
1864 def paths(ui, repo, search=None):
1864 """show definition of symbolic path names
1865 """show definition of symbolic path names
1865
1866
1866 Show definition of symbolic path name NAME. If no name is given, show
1867 Show definition of symbolic path name NAME. If no name is given, show
1867 definition of available names.
1868 definition of available names.
1868
1869
1869 Path names are defined in the [paths] section of /etc/mercurial/hgrc
1870 Path names are defined in the [paths] section of /etc/mercurial/hgrc
1870 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
1871 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
1871 """
1872 """
1872 if search:
1873 if search:
1873 for name, path in ui.configitems("paths"):
1874 for name, path in ui.configitems("paths"):
1874 if name == search:
1875 if name == search:
1875 ui.write("%s\n" % path)
1876 ui.write("%s\n" % path)
1876 return
1877 return
1877 ui.warn(_("not found!\n"))
1878 ui.warn(_("not found!\n"))
1878 return 1
1879 return 1
1879 else:
1880 else:
1880 for name, path in ui.configitems("paths"):
1881 for name, path in ui.configitems("paths"):
1881 ui.write("%s = %s\n" % (name, path))
1882 ui.write("%s = %s\n" % (name, path))
1882
1883
1883 def postincoming(ui, repo, modheads, optupdate):
1884 def postincoming(ui, repo, modheads, optupdate):
1884 if modheads == 0:
1885 if modheads == 0:
1885 return
1886 return
1886 if optupdate:
1887 if optupdate:
1887 if modheads == 1:
1888 if modheads == 1:
1888 return hg.update(repo, repo.changelog.tip()) # update
1889 return hg.update(repo, repo.changelog.tip()) # update
1889 else:
1890 else:
1890 ui.status(_("not updating, since new heads added\n"))
1891 ui.status(_("not updating, since new heads added\n"))
1891 if modheads > 1:
1892 if modheads > 1:
1892 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
1893 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
1893 else:
1894 else:
1894 ui.status(_("(run 'hg update' to get a working copy)\n"))
1895 ui.status(_("(run 'hg update' to get a working copy)\n"))
1895
1896
1896 def pull(ui, repo, source="default", **opts):
1897 def pull(ui, repo, source="default", **opts):
1897 """pull changes from the specified source
1898 """pull changes from the specified source
1898
1899
1899 Pull changes from a remote repository to a local one.
1900 Pull changes from a remote repository to a local one.
1900
1901
1901 This finds all changes from the repository at the specified path
1902 This finds all changes from the repository at the specified path
1902 or URL and adds them to the local repository. By default, this
1903 or URL and adds them to the local repository. By default, this
1903 does not update the copy of the project in the working directory.
1904 does not update the copy of the project in the working directory.
1904
1905
1905 Valid URLs are of the form:
1906 Valid URLs are of the form:
1906
1907
1907 local/filesystem/path (or file://local/filesystem/path)
1908 local/filesystem/path (or file://local/filesystem/path)
1908 http://[user@]host[:port]/[path]
1909 http://[user@]host[:port]/[path]
1909 https://[user@]host[:port]/[path]
1910 https://[user@]host[:port]/[path]
1910 ssh://[user@]host[:port]/[path]
1911 ssh://[user@]host[:port]/[path]
1911 static-http://host[:port]/[path]
1912 static-http://host[:port]/[path]
1912
1913
1913 Paths in the local filesystem can either point to Mercurial
1914 Paths in the local filesystem can either point to Mercurial
1914 repositories or to bundle files (as created by 'hg bundle' or
1915 repositories or to bundle files (as created by 'hg bundle' or
1915 'hg incoming --bundle'). The static-http:// protocol, albeit slow,
1916 'hg incoming --bundle'). The static-http:// protocol, albeit slow,
1916 allows access to a Mercurial repository where you simply use a web
1917 allows access to a Mercurial repository where you simply use a web
1917 server to publish the .hg directory as static content.
1918 server to publish the .hg directory as static content.
1918
1919
1919 Some notes about using SSH with Mercurial:
1920 Some notes about using SSH with Mercurial:
1920 - SSH requires an accessible shell account on the destination machine
1921 - SSH requires an accessible shell account on the destination machine
1921 and a copy of hg in the remote path or specified with as remotecmd.
1922 and a copy of hg in the remote path or specified with as remotecmd.
1922 - path is relative to the remote user's home directory by default.
1923 - path is relative to the remote user's home directory by default.
1923 Use an extra slash at the start of a path to specify an absolute path:
1924 Use an extra slash at the start of a path to specify an absolute path:
1924 ssh://example.com//tmp/repository
1925 ssh://example.com//tmp/repository
1925 - Mercurial doesn't use its own compression via SSH; the right thing
1926 - Mercurial doesn't use its own compression via SSH; the right thing
1926 to do is to configure it in your ~/.ssh/config, e.g.:
1927 to do is to configure it in your ~/.ssh/config, e.g.:
1927 Host *.mylocalnetwork.example.com
1928 Host *.mylocalnetwork.example.com
1928 Compression no
1929 Compression no
1929 Host *
1930 Host *
1930 Compression yes
1931 Compression yes
1931 Alternatively specify "ssh -C" as your ssh command in your hgrc or
1932 Alternatively specify "ssh -C" as your ssh command in your hgrc or
1932 with the --ssh command line option.
1933 with the --ssh command line option.
1933 """
1934 """
1934 source = ui.expandpath(source)
1935 source = ui.expandpath(source)
1935 setremoteconfig(ui, opts)
1936 setremoteconfig(ui, opts)
1936
1937
1937 other = hg.repository(ui, source)
1938 other = hg.repository(ui, source)
1938 ui.status(_('pulling from %s\n') % (source))
1939 ui.status(_('pulling from %s\n') % (source))
1939 revs = None
1940 revs = None
1940 if opts['rev']:
1941 if opts['rev']:
1941 if 'lookup' in other.capabilities:
1942 if 'lookup' in other.capabilities:
1942 revs = [other.lookup(rev) for rev in opts['rev']]
1943 revs = [other.lookup(rev) for rev in opts['rev']]
1943 else:
1944 else:
1944 error = _("Other repository doesn't support revision lookup, so a rev cannot be specified.")
1945 error = _("Other repository doesn't support revision lookup, so a rev cannot be specified.")
1945 raise util.Abort(error)
1946 raise util.Abort(error)
1946 modheads = repo.pull(other, heads=revs, force=opts['force'])
1947 modheads = repo.pull(other, heads=revs, force=opts['force'])
1947 return postincoming(ui, repo, modheads, opts['update'])
1948 return postincoming(ui, repo, modheads, opts['update'])
1948
1949
1949 def push(ui, repo, dest=None, **opts):
1950 def push(ui, repo, dest=None, **opts):
1950 """push changes to the specified destination
1951 """push changes to the specified destination
1951
1952
1952 Push changes from the local repository to the given destination.
1953 Push changes from the local repository to the given destination.
1953
1954
1954 This is the symmetrical operation for pull. It helps to move
1955 This is the symmetrical operation for pull. It helps to move
1955 changes from the current repository to a different one. If the
1956 changes from the current repository to a different one. If the
1956 destination is local this is identical to a pull in that directory
1957 destination is local this is identical to a pull in that directory
1957 from the current one.
1958 from the current one.
1958
1959
1959 By default, push will refuse to run if it detects the result would
1960 By default, push will refuse to run if it detects the result would
1960 increase the number of remote heads. This generally indicates the
1961 increase the number of remote heads. This generally indicates the
1961 the client has forgotten to sync and merge before pushing.
1962 the client has forgotten to sync and merge before pushing.
1962
1963
1963 Valid URLs are of the form:
1964 Valid URLs are of the form:
1964
1965
1965 local/filesystem/path (or file://local/filesystem/path)
1966 local/filesystem/path (or file://local/filesystem/path)
1966 ssh://[user@]host[:port]/[path]
1967 ssh://[user@]host[:port]/[path]
1967 http://[user@]host[:port]/[path]
1968 http://[user@]host[:port]/[path]
1968 https://[user@]host[:port]/[path]
1969 https://[user@]host[:port]/[path]
1969
1970
1970 Look at the help text for the pull command for important details
1971 Look at the help text for the pull command for important details
1971 about ssh:// URLs.
1972 about ssh:// URLs.
1972
1973
1973 Pushing to http:// and https:// URLs is only possible, if this
1974 Pushing to http:// and https:// URLs is only possible, if this
1974 feature is explicitly enabled on the remote Mercurial server.
1975 feature is explicitly enabled on the remote Mercurial server.
1975 """
1976 """
1976 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1977 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1977 setremoteconfig(ui, opts)
1978 setremoteconfig(ui, opts)
1978
1979
1979 other = hg.repository(ui, dest)
1980 other = hg.repository(ui, dest)
1980 ui.status('pushing to %s\n' % (dest))
1981 ui.status('pushing to %s\n' % (dest))
1981 revs = None
1982 revs = None
1982 if opts['rev']:
1983 if opts['rev']:
1983 revs = [repo.lookup(rev) for rev in opts['rev']]
1984 revs = [repo.lookup(rev) for rev in opts['rev']]
1984 r = repo.push(other, opts['force'], revs=revs)
1985 r = repo.push(other, opts['force'], revs=revs)
1985 return r == 0
1986 return r == 0
1986
1987
1987 def rawcommit(ui, repo, *pats, **opts):
1988 def rawcommit(ui, repo, *pats, **opts):
1988 """raw commit interface (DEPRECATED)
1989 """raw commit interface (DEPRECATED)
1989
1990
1990 (DEPRECATED)
1991 (DEPRECATED)
1991 Lowlevel commit, for use in helper scripts.
1992 Lowlevel commit, for use in helper scripts.
1992
1993
1993 This command is not intended to be used by normal users, as it is
1994 This command is not intended to be used by normal users, as it is
1994 primarily useful for importing from other SCMs.
1995 primarily useful for importing from other SCMs.
1995
1996
1996 This command is now deprecated and will be removed in a future
1997 This command is now deprecated and will be removed in a future
1997 release, please use debugsetparents and commit instead.
1998 release, please use debugsetparents and commit instead.
1998 """
1999 """
1999
2000
2000 ui.warn(_("(the rawcommit command is deprecated)\n"))
2001 ui.warn(_("(the rawcommit command is deprecated)\n"))
2001
2002
2002 message = logmessage(opts)
2003 message = logmessage(opts)
2003
2004
2004 files, match, anypats = cmdutil.matchpats(repo, pats, opts)
2005 files, match, anypats = cmdutil.matchpats(repo, pats, opts)
2005 if opts['files']:
2006 if opts['files']:
2006 files += open(opts['files']).read().splitlines()
2007 files += open(opts['files']).read().splitlines()
2007
2008
2008 parents = [repo.lookup(p) for p in opts['parent']]
2009 parents = [repo.lookup(p) for p in opts['parent']]
2009
2010
2010 try:
2011 try:
2011 repo.rawcommit(files, message, opts['user'], opts['date'], *parents)
2012 repo.rawcommit(files, message, opts['user'], opts['date'], *parents)
2012 except ValueError, inst:
2013 except ValueError, inst:
2013 raise util.Abort(str(inst))
2014 raise util.Abort(str(inst))
2014
2015
2015 def recover(ui, repo):
2016 def recover(ui, repo):
2016 """roll back an interrupted transaction
2017 """roll back an interrupted transaction
2017
2018
2018 Recover from an interrupted commit or pull.
2019 Recover from an interrupted commit or pull.
2019
2020
2020 This command tries to fix the repository status after an interrupted
2021 This command tries to fix the repository status after an interrupted
2021 operation. It should only be necessary when Mercurial suggests it.
2022 operation. It should only be necessary when Mercurial suggests it.
2022 """
2023 """
2023 if repo.recover():
2024 if repo.recover():
2024 return hg.verify(repo)
2025 return hg.verify(repo)
2025 return 1
2026 return 1
2026
2027
2027 def remove(ui, repo, *pats, **opts):
2028 def remove(ui, repo, *pats, **opts):
2028 """remove the specified files on the next commit
2029 """remove the specified files on the next commit
2029
2030
2030 Schedule the indicated files for removal from the repository.
2031 Schedule the indicated files for removal from the repository.
2031
2032
2032 This only removes files from the current branch, not from the
2033 This only removes files from the current branch, not from the
2033 entire project history. If the files still exist in the working
2034 entire project history. If the files still exist in the working
2034 directory, they will be deleted from it. If invoked with --after,
2035 directory, they will be deleted from it. If invoked with --after,
2035 files that have been manually deleted are marked as removed.
2036 files that have been manually deleted are marked as removed.
2036
2037
2037 This command schedules the files to be removed at the next commit.
2038 This command schedules the files to be removed at the next commit.
2038 To undo a remove before that, see hg revert.
2039 To undo a remove before that, see hg revert.
2039
2040
2040 Modified files and added files are not removed by default. To
2041 Modified files and added files are not removed by default. To
2041 remove them, use the -f/--force option.
2042 remove them, use the -f/--force option.
2042 """
2043 """
2043 names = []
2044 names = []
2044 if not opts['after'] and not pats:
2045 if not opts['after'] and not pats:
2045 raise util.Abort(_('no files specified'))
2046 raise util.Abort(_('no files specified'))
2046 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2047 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2047 exact = dict.fromkeys(files)
2048 exact = dict.fromkeys(files)
2048 mardu = map(dict.fromkeys, repo.status(files=files, match=matchfn))[:5]
2049 mardu = map(dict.fromkeys, repo.status(files=files, match=matchfn))[:5]
2049 modified, added, removed, deleted, unknown = mardu
2050 modified, added, removed, deleted, unknown = mardu
2050 remove, forget = [], []
2051 remove, forget = [], []
2051 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
2052 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
2052 reason = None
2053 reason = None
2053 if abs not in deleted and opts['after']:
2054 if abs not in deleted and opts['after']:
2054 reason = _('is still present')
2055 reason = _('is still present')
2055 elif abs in modified and not opts['force']:
2056 elif abs in modified and not opts['force']:
2056 reason = _('is modified (use -f to force removal)')
2057 reason = _('is modified (use -f to force removal)')
2057 elif abs in added:
2058 elif abs in added:
2058 if opts['force']:
2059 if opts['force']:
2059 forget.append(abs)
2060 forget.append(abs)
2060 continue
2061 continue
2061 reason = _('has been marked for add (use -f to force removal)')
2062 reason = _('has been marked for add (use -f to force removal)')
2062 elif abs in unknown:
2063 elif abs in unknown:
2063 reason = _('is not managed')
2064 reason = _('is not managed')
2064 elif abs in removed:
2065 elif abs in removed:
2065 continue
2066 continue
2066 if reason:
2067 if reason:
2067 if exact:
2068 if exact:
2068 ui.warn(_('not removing %s: file %s\n') % (rel, reason))
2069 ui.warn(_('not removing %s: file %s\n') % (rel, reason))
2069 else:
2070 else:
2070 if ui.verbose or not exact:
2071 if ui.verbose or not exact:
2071 ui.status(_('removing %s\n') % rel)
2072 ui.status(_('removing %s\n') % rel)
2072 remove.append(abs)
2073 remove.append(abs)
2073 repo.forget(forget)
2074 repo.forget(forget)
2074 repo.remove(remove, unlink=not opts['after'])
2075 repo.remove(remove, unlink=not opts['after'])
2075
2076
2076 def rename(ui, repo, *pats, **opts):
2077 def rename(ui, repo, *pats, **opts):
2077 """rename files; equivalent of copy + remove
2078 """rename files; equivalent of copy + remove
2078
2079
2079 Mark dest as copies of sources; mark sources for deletion. If
2080 Mark dest as copies of sources; mark sources for deletion. If
2080 dest is a directory, copies are put in that directory. If dest is
2081 dest is a directory, copies are put in that directory. If dest is
2081 a file, there can only be one source.
2082 a file, there can only be one source.
2082
2083
2083 By default, this command copies the contents of files as they
2084 By default, this command copies the contents of files as they
2084 stand in the working directory. If invoked with --after, the
2085 stand in the working directory. If invoked with --after, the
2085 operation is recorded, but no copying is performed.
2086 operation is recorded, but no copying is performed.
2086
2087
2087 This command takes effect in the next commit. To undo a rename
2088 This command takes effect in the next commit. To undo a rename
2088 before that, see hg revert.
2089 before that, see hg revert.
2089 """
2090 """
2090 wlock = repo.wlock(0)
2091 wlock = repo.wlock(0)
2091 errs, copied = docopy(ui, repo, pats, opts, wlock)
2092 errs, copied = docopy(ui, repo, pats, opts, wlock)
2092 names = []
2093 names = []
2093 for abs, rel, exact in copied:
2094 for abs, rel, exact in copied:
2094 if ui.verbose or not exact:
2095 if ui.verbose or not exact:
2095 ui.status(_('removing %s\n') % rel)
2096 ui.status(_('removing %s\n') % rel)
2096 names.append(abs)
2097 names.append(abs)
2097 if not opts.get('dry_run'):
2098 if not opts.get('dry_run'):
2098 repo.remove(names, True, wlock)
2099 repo.remove(names, True, wlock)
2099 return errs
2100 return errs
2100
2101
2101 def revert(ui, repo, *pats, **opts):
2102 def revert(ui, repo, *pats, **opts):
2102 """revert files or dirs to their states as of some revision
2103 """revert files or dirs to their states as of some revision
2103
2104
2104 With no revision specified, revert the named files or directories
2105 With no revision specified, revert the named files or directories
2105 to the contents they had in the parent of the working directory.
2106 to the contents they had in the parent of the working directory.
2106 This restores the contents of the affected files to an unmodified
2107 This restores the contents of the affected files to an unmodified
2107 state and unschedules adds, removes, copies, and renames. If the
2108 state and unschedules adds, removes, copies, and renames. If the
2108 working directory has two parents, you must explicitly specify the
2109 working directory has two parents, you must explicitly specify the
2109 revision to revert to.
2110 revision to revert to.
2110
2111
2111 Modified files are saved with a .orig suffix before reverting.
2112 Modified files are saved with a .orig suffix before reverting.
2112 To disable these backups, use --no-backup.
2113 To disable these backups, use --no-backup.
2113
2114
2114 Using the -r option, revert the given files or directories to their
2115 Using the -r option, revert the given files or directories to their
2115 contents as of a specific revision. This can be helpful to "roll
2116 contents as of a specific revision. This can be helpful to "roll
2116 back" some or all of a change that should not have been committed.
2117 back" some or all of a change that should not have been committed.
2117
2118
2118 Revert modifies the working directory. It does not commit any
2119 Revert modifies the working directory. It does not commit any
2119 changes, or change the parent of the working directory. If you
2120 changes, or change the parent of the working directory. If you
2120 revert to a revision other than the parent of the working
2121 revert to a revision other than the parent of the working
2121 directory, the reverted files will thus appear modified
2122 directory, the reverted files will thus appear modified
2122 afterwards.
2123 afterwards.
2123
2124
2124 If a file has been deleted, it is recreated. If the executable
2125 If a file has been deleted, it is recreated. If the executable
2125 mode of a file was changed, it is reset.
2126 mode of a file was changed, it is reset.
2126
2127
2127 If names are given, all files matching the names are reverted.
2128 If names are given, all files matching the names are reverted.
2128
2129
2129 If no arguments are given, no files are reverted.
2130 If no arguments are given, no files are reverted.
2130 """
2131 """
2131
2132
2132 if opts["date"]:
2133 if opts["date"]:
2133 if opts["rev"]:
2134 if opts["rev"]:
2134 raise util.Abort(_("you can't specify a revision and a date"))
2135 raise util.Abort(_("you can't specify a revision and a date"))
2135 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
2136 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
2136
2137
2137 if not pats and not opts['all']:
2138 if not pats and not opts['all']:
2138 raise util.Abort(_('no files or directories specified; '
2139 raise util.Abort(_('no files or directories specified; '
2139 'use --all to revert the whole repo'))
2140 'use --all to revert the whole repo'))
2140
2141
2141 parent, p2 = repo.dirstate.parents()
2142 parent, p2 = repo.dirstate.parents()
2142 if not opts['rev'] and p2 != nullid:
2143 if not opts['rev'] and p2 != nullid:
2143 raise util.Abort(_('uncommitted merge - please provide a '
2144 raise util.Abort(_('uncommitted merge - please provide a '
2144 'specific revision'))
2145 'specific revision'))
2145 node = repo.changectx(opts['rev']).node()
2146 node = repo.changectx(opts['rev']).node()
2146 mf = repo.manifest.read(repo.changelog.read(node)[0])
2147 mf = repo.manifest.read(repo.changelog.read(node)[0])
2147 if node == parent:
2148 if node == parent:
2148 pmf = mf
2149 pmf = mf
2149 else:
2150 else:
2150 pmf = None
2151 pmf = None
2151
2152
2152 wlock = repo.wlock()
2153 wlock = repo.wlock()
2153
2154
2154 # need all matching names in dirstate and manifest of target rev,
2155 # need all matching names in dirstate and manifest of target rev,
2155 # so have to walk both. do not print errors if files exist in one
2156 # so have to walk both. do not print errors if files exist in one
2156 # but not other.
2157 # but not other.
2157
2158
2158 names = {}
2159 names = {}
2159 target_only = {}
2160 target_only = {}
2160
2161
2161 # walk dirstate.
2162 # walk dirstate.
2162
2163
2163 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
2164 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
2164 badmatch=mf.has_key):
2165 badmatch=mf.has_key):
2165 names[abs] = (rel, exact)
2166 names[abs] = (rel, exact)
2166 if src == 'b':
2167 if src == 'b':
2167 target_only[abs] = True
2168 target_only[abs] = True
2168
2169
2169 # walk target manifest.
2170 # walk target manifest.
2170
2171
2171 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
2172 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
2172 badmatch=names.has_key):
2173 badmatch=names.has_key):
2173 if abs in names: continue
2174 if abs in names: continue
2174 names[abs] = (rel, exact)
2175 names[abs] = (rel, exact)
2175 target_only[abs] = True
2176 target_only[abs] = True
2176
2177
2177 changes = repo.status(match=names.has_key, wlock=wlock)[:5]
2178 changes = repo.status(match=names.has_key, wlock=wlock)[:5]
2178 modified, added, removed, deleted, unknown = map(dict.fromkeys, changes)
2179 modified, added, removed, deleted, unknown = map(dict.fromkeys, changes)
2179
2180
2180 revert = ([], _('reverting %s\n'))
2181 revert = ([], _('reverting %s\n'))
2181 add = ([], _('adding %s\n'))
2182 add = ([], _('adding %s\n'))
2182 remove = ([], _('removing %s\n'))
2183 remove = ([], _('removing %s\n'))
2183 forget = ([], _('forgetting %s\n'))
2184 forget = ([], _('forgetting %s\n'))
2184 undelete = ([], _('undeleting %s\n'))
2185 undelete = ([], _('undeleting %s\n'))
2185 update = {}
2186 update = {}
2186
2187
2187 disptable = (
2188 disptable = (
2188 # dispatch table:
2189 # dispatch table:
2189 # file state
2190 # file state
2190 # action if in target manifest
2191 # action if in target manifest
2191 # action if not in target manifest
2192 # action if not in target manifest
2192 # make backup if in target manifest
2193 # make backup if in target manifest
2193 # make backup if not in target manifest
2194 # make backup if not in target manifest
2194 (modified, revert, remove, True, True),
2195 (modified, revert, remove, True, True),
2195 (added, revert, forget, True, False),
2196 (added, revert, forget, True, False),
2196 (removed, undelete, None, False, False),
2197 (removed, undelete, None, False, False),
2197 (deleted, revert, remove, False, False),
2198 (deleted, revert, remove, False, False),
2198 (unknown, add, None, True, False),
2199 (unknown, add, None, True, False),
2199 (target_only, add, None, False, False),
2200 (target_only, add, None, False, False),
2200 )
2201 )
2201
2202
2202 entries = names.items()
2203 entries = names.items()
2203 entries.sort()
2204 entries.sort()
2204
2205
2205 for abs, (rel, exact) in entries:
2206 for abs, (rel, exact) in entries:
2206 mfentry = mf.get(abs)
2207 mfentry = mf.get(abs)
2207 def handle(xlist, dobackup):
2208 def handle(xlist, dobackup):
2208 xlist[0].append(abs)
2209 xlist[0].append(abs)
2209 update[abs] = 1
2210 update[abs] = 1
2210 if dobackup and not opts['no_backup'] and os.path.exists(rel):
2211 if dobackup and not opts['no_backup'] and os.path.exists(rel):
2211 bakname = "%s.orig" % rel
2212 bakname = "%s.orig" % rel
2212 ui.note(_('saving current version of %s as %s\n') %
2213 ui.note(_('saving current version of %s as %s\n') %
2213 (rel, bakname))
2214 (rel, bakname))
2214 if not opts.get('dry_run'):
2215 if not opts.get('dry_run'):
2215 util.copyfile(rel, bakname)
2216 util.copyfile(rel, bakname)
2216 if ui.verbose or not exact:
2217 if ui.verbose or not exact:
2217 ui.status(xlist[1] % rel)
2218 ui.status(xlist[1] % rel)
2218 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2219 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2219 if abs not in table: continue
2220 if abs not in table: continue
2220 # file has changed in dirstate
2221 # file has changed in dirstate
2221 if mfentry:
2222 if mfentry:
2222 handle(hitlist, backuphit)
2223 handle(hitlist, backuphit)
2223 elif misslist is not None:
2224 elif misslist is not None:
2224 handle(misslist, backupmiss)
2225 handle(misslist, backupmiss)
2225 else:
2226 else:
2226 if exact: ui.warn(_('file not managed: %s\n') % rel)
2227 if exact: ui.warn(_('file not managed: %s\n') % rel)
2227 break
2228 break
2228 else:
2229 else:
2229 # file has not changed in dirstate
2230 # file has not changed in dirstate
2230 if node == parent:
2231 if node == parent:
2231 if exact: ui.warn(_('no changes needed to %s\n') % rel)
2232 if exact: ui.warn(_('no changes needed to %s\n') % rel)
2232 continue
2233 continue
2233 if pmf is None:
2234 if pmf is None:
2234 # only need parent manifest in this unlikely case,
2235 # only need parent manifest in this unlikely case,
2235 # so do not read by default
2236 # so do not read by default
2236 pmf = repo.manifest.read(repo.changelog.read(parent)[0])
2237 pmf = repo.manifest.read(repo.changelog.read(parent)[0])
2237 if abs in pmf:
2238 if abs in pmf:
2238 if mfentry:
2239 if mfentry:
2239 # if version of file is same in parent and target
2240 # if version of file is same in parent and target
2240 # manifests, do nothing
2241 # manifests, do nothing
2241 if pmf[abs] != mfentry:
2242 if pmf[abs] != mfentry:
2242 handle(revert, False)
2243 handle(revert, False)
2243 else:
2244 else:
2244 handle(remove, False)
2245 handle(remove, False)
2245
2246
2246 if not opts.get('dry_run'):
2247 if not opts.get('dry_run'):
2247 repo.dirstate.forget(forget[0])
2248 repo.dirstate.forget(forget[0])
2248 r = hg.revert(repo, node, update.has_key, wlock)
2249 r = hg.revert(repo, node, update.has_key, wlock)
2249 repo.dirstate.update(add[0], 'a')
2250 repo.dirstate.update(add[0], 'a')
2250 repo.dirstate.update(undelete[0], 'n')
2251 repo.dirstate.update(undelete[0], 'n')
2251 repo.dirstate.update(remove[0], 'r')
2252 repo.dirstate.update(remove[0], 'r')
2252 return r
2253 return r
2253
2254
2254 def rollback(ui, repo):
2255 def rollback(ui, repo):
2255 """roll back the last transaction in this repository
2256 """roll back the last transaction in this repository
2256
2257
2257 Roll back the last transaction in this repository, restoring the
2258 Roll back the last transaction in this repository, restoring the
2258 project to its state prior to the transaction.
2259 project to its state prior to the transaction.
2259
2260
2260 Transactions are used to encapsulate the effects of all commands
2261 Transactions are used to encapsulate the effects of all commands
2261 that create new changesets or propagate existing changesets into a
2262 that create new changesets or propagate existing changesets into a
2262 repository. For example, the following commands are transactional,
2263 repository. For example, the following commands are transactional,
2263 and their effects can be rolled back:
2264 and their effects can be rolled back:
2264
2265
2265 commit
2266 commit
2266 import
2267 import
2267 pull
2268 pull
2268 push (with this repository as destination)
2269 push (with this repository as destination)
2269 unbundle
2270 unbundle
2270
2271
2271 This command should be used with care. There is only one level of
2272 This command should be used with care. There is only one level of
2272 rollback, and there is no way to undo a rollback.
2273 rollback, and there is no way to undo a rollback.
2273
2274
2274 This command is not intended for use on public repositories. Once
2275 This command is not intended for use on public repositories. Once
2275 changes are visible for pull by other users, rolling a transaction
2276 changes are visible for pull by other users, rolling a transaction
2276 back locally is ineffective (someone else may already have pulled
2277 back locally is ineffective (someone else may already have pulled
2277 the changes). Furthermore, a race is possible with readers of the
2278 the changes). Furthermore, a race is possible with readers of the
2278 repository; for example an in-progress pull from the repository
2279 repository; for example an in-progress pull from the repository
2279 may fail if a rollback is performed.
2280 may fail if a rollback is performed.
2280 """
2281 """
2281 repo.rollback()
2282 repo.rollback()
2282
2283
2283 def root(ui, repo):
2284 def root(ui, repo):
2284 """print the root (top) of the current working dir
2285 """print the root (top) of the current working dir
2285
2286
2286 Print the root directory of the current repository.
2287 Print the root directory of the current repository.
2287 """
2288 """
2288 ui.write(repo.root + "\n")
2289 ui.write(repo.root + "\n")
2289
2290
2290 def serve(ui, repo, **opts):
2291 def serve(ui, repo, **opts):
2291 """export the repository via HTTP
2292 """export the repository via HTTP
2292
2293
2293 Start a local HTTP repository browser and pull server.
2294 Start a local HTTP repository browser and pull server.
2294
2295
2295 By default, the server logs accesses to stdout and errors to
2296 By default, the server logs accesses to stdout and errors to
2296 stderr. Use the "-A" and "-E" options to log to files.
2297 stderr. Use the "-A" and "-E" options to log to files.
2297 """
2298 """
2298
2299
2299 if opts["stdio"]:
2300 if opts["stdio"]:
2300 if repo is None:
2301 if repo is None:
2301 raise hg.RepoError(_("There is no Mercurial repository here"
2302 raise hg.RepoError(_("There is no Mercurial repository here"
2302 " (.hg not found)"))
2303 " (.hg not found)"))
2303 s = sshserver.sshserver(ui, repo)
2304 s = sshserver.sshserver(ui, repo)
2304 s.serve_forever()
2305 s.serve_forever()
2305
2306
2306 optlist = ("name templates style address port ipv6"
2307 optlist = ("name templates style address port ipv6"
2307 " accesslog errorlog webdir_conf")
2308 " accesslog errorlog webdir_conf")
2308 for o in optlist.split():
2309 for o in optlist.split():
2309 if opts[o]:
2310 if opts[o]:
2310 ui.setconfig("web", o, str(opts[o]))
2311 ui.setconfig("web", o, str(opts[o]))
2311
2312
2312 if repo is None and not ui.config("web", "webdir_conf"):
2313 if repo is None and not ui.config("web", "webdir_conf"):
2313 raise hg.RepoError(_("There is no Mercurial repository here"
2314 raise hg.RepoError(_("There is no Mercurial repository here"
2314 " (.hg not found)"))
2315 " (.hg not found)"))
2315
2316
2316 if opts['daemon'] and not opts['daemon_pipefds']:
2317 if opts['daemon'] and not opts['daemon_pipefds']:
2317 rfd, wfd = os.pipe()
2318 rfd, wfd = os.pipe()
2318 args = sys.argv[:]
2319 args = sys.argv[:]
2319 args.append('--daemon-pipefds=%d,%d' % (rfd, wfd))
2320 args.append('--daemon-pipefds=%d,%d' % (rfd, wfd))
2320 pid = os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
2321 pid = os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
2321 args[0], args)
2322 args[0], args)
2322 os.close(wfd)
2323 os.close(wfd)
2323 os.read(rfd, 1)
2324 os.read(rfd, 1)
2324 os._exit(0)
2325 os._exit(0)
2325
2326
2326 httpd = hgweb.server.create_server(ui, repo)
2327 httpd = hgweb.server.create_server(ui, repo)
2327
2328
2328 if ui.verbose:
2329 if ui.verbose:
2329 if httpd.port != 80:
2330 if httpd.port != 80:
2330 ui.status(_('listening at http://%s:%d/\n') %
2331 ui.status(_('listening at http://%s:%d/\n') %
2331 (httpd.addr, httpd.port))
2332 (httpd.addr, httpd.port))
2332 else:
2333 else:
2333 ui.status(_('listening at http://%s/\n') % httpd.addr)
2334 ui.status(_('listening at http://%s/\n') % httpd.addr)
2334
2335
2335 if opts['pid_file']:
2336 if opts['pid_file']:
2336 fp = open(opts['pid_file'], 'w')
2337 fp = open(opts['pid_file'], 'w')
2337 fp.write(str(os.getpid()) + '\n')
2338 fp.write(str(os.getpid()) + '\n')
2338 fp.close()
2339 fp.close()
2339
2340
2340 if opts['daemon_pipefds']:
2341 if opts['daemon_pipefds']:
2341 rfd, wfd = [int(x) for x in opts['daemon_pipefds'].split(',')]
2342 rfd, wfd = [int(x) for x in opts['daemon_pipefds'].split(',')]
2342 os.close(rfd)
2343 os.close(rfd)
2343 os.write(wfd, 'y')
2344 os.write(wfd, 'y')
2344 os.close(wfd)
2345 os.close(wfd)
2345 sys.stdout.flush()
2346 sys.stdout.flush()
2346 sys.stderr.flush()
2347 sys.stderr.flush()
2347 fd = os.open(util.nulldev, os.O_RDWR)
2348 fd = os.open(util.nulldev, os.O_RDWR)
2348 if fd != 0: os.dup2(fd, 0)
2349 if fd != 0: os.dup2(fd, 0)
2349 if fd != 1: os.dup2(fd, 1)
2350 if fd != 1: os.dup2(fd, 1)
2350 if fd != 2: os.dup2(fd, 2)
2351 if fd != 2: os.dup2(fd, 2)
2351 if fd not in (0, 1, 2): os.close(fd)
2352 if fd not in (0, 1, 2): os.close(fd)
2352
2353
2353 httpd.serve_forever()
2354 httpd.serve_forever()
2354
2355
2355 def status(ui, repo, *pats, **opts):
2356 def status(ui, repo, *pats, **opts):
2356 """show changed files in the working directory
2357 """show changed files in the working directory
2357
2358
2358 Show status of files in the repository. If names are given, only
2359 Show status of files in the repository. If names are given, only
2359 files that match are shown. Files that are clean or ignored, are
2360 files that match are shown. Files that are clean or ignored, are
2360 not listed unless -c (clean), -i (ignored) or -A is given.
2361 not listed unless -c (clean), -i (ignored) or -A is given.
2361
2362
2362 NOTE: status may appear to disagree with diff if permissions have
2363 NOTE: status may appear to disagree with diff if permissions have
2363 changed or a merge has occurred. The standard diff format does not
2364 changed or a merge has occurred. The standard diff format does not
2364 report permission changes and diff only reports changes relative
2365 report permission changes and diff only reports changes relative
2365 to one merge parent.
2366 to one merge parent.
2366
2367
2367 If one revision is given, it is used as the base revision.
2368 If one revision is given, it is used as the base revision.
2368 If two revisions are given, the difference between them is shown.
2369 If two revisions are given, the difference between them is shown.
2369
2370
2370 The codes used to show the status of files are:
2371 The codes used to show the status of files are:
2371 M = modified
2372 M = modified
2372 A = added
2373 A = added
2373 R = removed
2374 R = removed
2374 C = clean
2375 C = clean
2375 ! = deleted, but still tracked
2376 ! = deleted, but still tracked
2376 ? = not tracked
2377 ? = not tracked
2377 I = ignored (not shown by default)
2378 I = ignored (not shown by default)
2378 = the previous added file was copied from here
2379 = the previous added file was copied from here
2379 """
2380 """
2380
2381
2381 all = opts['all']
2382 all = opts['all']
2382 node1, node2 = cmdutil.revpair(repo, opts.get('rev'))
2383 node1, node2 = cmdutil.revpair(repo, opts.get('rev'))
2383
2384
2384 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2385 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2385 cwd = (pats and repo.getcwd()) or ''
2386 cwd = (pats and repo.getcwd()) or ''
2386 modified, added, removed, deleted, unknown, ignored, clean = [
2387 modified, added, removed, deleted, unknown, ignored, clean = [
2387 n for n in repo.status(node1=node1, node2=node2, files=files,
2388 n for n in repo.status(node1=node1, node2=node2, files=files,
2388 match=matchfn,
2389 match=matchfn,
2389 list_ignored=all or opts['ignored'],
2390 list_ignored=all or opts['ignored'],
2390 list_clean=all or opts['clean'])]
2391 list_clean=all or opts['clean'])]
2391
2392
2392 changetypes = (('modified', 'M', modified),
2393 changetypes = (('modified', 'M', modified),
2393 ('added', 'A', added),
2394 ('added', 'A', added),
2394 ('removed', 'R', removed),
2395 ('removed', 'R', removed),
2395 ('deleted', '!', deleted),
2396 ('deleted', '!', deleted),
2396 ('unknown', '?', unknown),
2397 ('unknown', '?', unknown),
2397 ('ignored', 'I', ignored))
2398 ('ignored', 'I', ignored))
2398
2399
2399 explicit_changetypes = changetypes + (('clean', 'C', clean),)
2400 explicit_changetypes = changetypes + (('clean', 'C', clean),)
2400
2401
2401 end = opts['print0'] and '\0' or '\n'
2402 end = opts['print0'] and '\0' or '\n'
2402
2403
2403 for opt, char, changes in ([ct for ct in explicit_changetypes
2404 for opt, char, changes in ([ct for ct in explicit_changetypes
2404 if all or opts[ct[0]]]
2405 if all or opts[ct[0]]]
2405 or changetypes):
2406 or changetypes):
2406 if opts['no_status']:
2407 if opts['no_status']:
2407 format = "%%s%s" % end
2408 format = "%%s%s" % end
2408 else:
2409 else:
2409 format = "%s %%s%s" % (char, end)
2410 format = "%s %%s%s" % (char, end)
2410
2411
2411 for f in changes:
2412 for f in changes:
2412 ui.write(format % util.pathto(cwd, f))
2413 ui.write(format % util.pathto(cwd, f))
2413 if ((all or opts.get('copies')) and not opts.get('no_status')):
2414 if ((all or opts.get('copies')) and not opts.get('no_status')):
2414 copied = repo.dirstate.copied(f)
2415 copied = repo.dirstate.copied(f)
2415 if copied:
2416 if copied:
2416 ui.write(' %s%s' % (util.pathto(cwd, copied), end))
2417 ui.write(' %s%s' % (util.pathto(cwd, copied), end))
2417
2418
2418 def tag(ui, repo, name, rev_=None, **opts):
2419 def tag(ui, repo, name, rev_=None, **opts):
2419 """add a tag for the current or given revision
2420 """add a tag for the current or given revision
2420
2421
2421 Name a particular revision using <name>.
2422 Name a particular revision using <name>.
2422
2423
2423 Tags are used to name particular revisions of the repository and are
2424 Tags are used to name particular revisions of the repository and are
2424 very useful to compare different revision, to go back to significant
2425 very useful to compare different revision, to go back to significant
2425 earlier versions or to mark branch points as releases, etc.
2426 earlier versions or to mark branch points as releases, etc.
2426
2427
2427 If no revision is given, the parent of the working directory is used,
2428 If no revision is given, the parent of the working directory is used,
2428 or tip if no revision is checked out.
2429 or tip if no revision is checked out.
2429
2430
2430 To facilitate version control, distribution, and merging of tags,
2431 To facilitate version control, distribution, and merging of tags,
2431 they are stored as a file named ".hgtags" which is managed
2432 they are stored as a file named ".hgtags" which is managed
2432 similarly to other project files and can be hand-edited if
2433 similarly to other project files and can be hand-edited if
2433 necessary. The file '.hg/localtags' is used for local tags (not
2434 necessary. The file '.hg/localtags' is used for local tags (not
2434 shared among repositories).
2435 shared among repositories).
2435 """
2436 """
2436 if name in ['tip', '.', 'null']:
2437 if name in ['tip', '.', 'null']:
2437 raise util.Abort(_("the name '%s' is reserved") % name)
2438 raise util.Abort(_("the name '%s' is reserved") % name)
2438 if rev_ is not None:
2439 if rev_ is not None:
2439 ui.warn(_("use of 'hg tag NAME [REV]' is deprecated, "
2440 ui.warn(_("use of 'hg tag NAME [REV]' is deprecated, "
2440 "please use 'hg tag [-r REV] NAME' instead\n"))
2441 "please use 'hg tag [-r REV] NAME' instead\n"))
2441 if opts['rev']:
2442 if opts['rev']:
2442 raise util.Abort(_("use only one form to specify the revision"))
2443 raise util.Abort(_("use only one form to specify the revision"))
2443 if opts['rev']:
2444 if opts['rev']:
2444 rev_ = opts['rev']
2445 rev_ = opts['rev']
2445 if not rev_ and repo.dirstate.parents()[1] != nullid:
2446 if not rev_ and repo.dirstate.parents()[1] != nullid:
2446 raise util.Abort(_('uncommitted merge - please provide a '
2447 raise util.Abort(_('uncommitted merge - please provide a '
2447 'specific revision'))
2448 'specific revision'))
2448 r = repo.changectx(rev_).node()
2449 r = repo.changectx(rev_).node()
2449
2450
2450 message = opts['message']
2451 message = opts['message']
2451 if not message:
2452 if not message:
2452 message = _('Added tag %s for changeset %s') % (name, short(r))
2453 message = _('Added tag %s for changeset %s') % (name, short(r))
2453
2454
2454 repo.tag(name, r, message, opts['local'], opts['user'], opts['date'])
2455 repo.tag(name, r, message, opts['local'], opts['user'], opts['date'])
2455
2456
2456 def tags(ui, repo):
2457 def tags(ui, repo):
2457 """list repository tags
2458 """list repository tags
2458
2459
2459 List the repository tags.
2460 List the repository tags.
2460
2461
2461 This lists both regular and local tags.
2462 This lists both regular and local tags.
2462 """
2463 """
2463
2464
2464 l = repo.tagslist()
2465 l = repo.tagslist()
2465 l.reverse()
2466 l.reverse()
2466 hexfunc = ui.debugflag and hex or short
2467 hexfunc = ui.debugflag and hex or short
2467 for t, n in l:
2468 for t, n in l:
2468 try:
2469 try:
2469 r = "%5d:%s" % (repo.changelog.rev(n), hexfunc(n))
2470 r = "%5d:%s" % (repo.changelog.rev(n), hexfunc(n))
2470 except KeyError:
2471 except KeyError:
2471 r = " ?:?"
2472 r = " ?:?"
2472 if ui.quiet:
2473 if ui.quiet:
2473 ui.write("%s\n" % t)
2474 ui.write("%s\n" % t)
2474 else:
2475 else:
2475 t = util.localsub(t, 30)
2476 t = util.localsub(t, 30)
2476 t += " " * (30 - util.locallen(t))
2477 t += " " * (30 - util.locallen(t))
2477 ui.write("%s %s\n" % (t, r))
2478 ui.write("%s %s\n" % (t, r))
2478
2479
2479 def tip(ui, repo, **opts):
2480 def tip(ui, repo, **opts):
2480 """show the tip revision
2481 """show the tip revision
2481
2482
2482 Show the tip revision.
2483 Show the tip revision.
2483 """
2484 """
2484 cmdutil.show_changeset(ui, repo, opts).show(nullrev+repo.changelog.count())
2485 cmdutil.show_changeset(ui, repo, opts).show(nullrev+repo.changelog.count())
2485
2486
2486 def unbundle(ui, repo, fname, **opts):
2487 def unbundle(ui, repo, fname, **opts):
2487 """apply a changegroup file
2488 """apply a changegroup file
2488
2489
2489 Apply a compressed changegroup file generated by the bundle
2490 Apply a compressed changegroup file generated by the bundle
2490 command.
2491 command.
2491 """
2492 """
2492 gen = changegroup.readbundle(urllib.urlopen(fname), fname)
2493 gen = changegroup.readbundle(urllib.urlopen(fname), fname)
2493 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
2494 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
2494 return postincoming(ui, repo, modheads, opts['update'])
2495 return postincoming(ui, repo, modheads, opts['update'])
2495
2496
2496 def update(ui, repo, node=None, clean=False, branch=None, date=None):
2497 def update(ui, repo, node=None, clean=False, branch=None, date=None):
2497 """update working directory
2498 """update working directory
2498
2499
2499 Update the working directory to the specified revision.
2500 Update the working directory to the specified revision.
2500
2501
2501 If there are no outstanding changes in the working directory and
2502 If there are no outstanding changes in the working directory and
2502 there is a linear relationship between the current version and the
2503 there is a linear relationship between the current version and the
2503 requested version, the result is the requested version.
2504 requested version, the result is the requested version.
2504
2505
2505 To merge the working directory with another revision, use the
2506 To merge the working directory with another revision, use the
2506 merge command.
2507 merge command.
2507
2508
2508 By default, update will refuse to run if doing so would require
2509 By default, update will refuse to run if doing so would require
2509 discarding local changes.
2510 discarding local changes.
2510 """
2511 """
2511 if date:
2512 if date:
2512 if node:
2513 if node:
2513 raise util.Abort(_("you can't specify a revision and a date"))
2514 raise util.Abort(_("you can't specify a revision and a date"))
2514 node = cmdutil.finddate(ui, repo, date)
2515 node = cmdutil.finddate(ui, repo, date)
2515
2516
2516 node = _lookup(repo, node, branch)
2517 node = _lookup(repo, node, branch)
2517 if clean:
2518 if clean:
2518 return hg.clean(repo, node)
2519 return hg.clean(repo, node)
2519 else:
2520 else:
2520 return hg.update(repo, node)
2521 return hg.update(repo, node)
2521
2522
2522 def _lookup(repo, node, branch=None):
2523 def _lookup(repo, node, branch=None):
2523 if branch:
2524 if branch:
2524 repo.ui.warn(_("the --branch option is deprecated, "
2525 repo.ui.warn(_("the --branch option is deprecated, "
2525 "please use 'hg branch' instead\n"))
2526 "please use 'hg branch' instead\n"))
2526 br = repo.branchlookup(branch=branch)
2527 br = repo.branchlookup(branch=branch)
2527 found = []
2528 found = []
2528 for x in br:
2529 for x in br:
2529 if branch in br[x]:
2530 if branch in br[x]:
2530 found.append(x)
2531 found.append(x)
2531 if len(found) > 1:
2532 if len(found) > 1:
2532 repo.ui.warn(_("Found multiple heads for %s\n") % branch)
2533 repo.ui.warn(_("Found multiple heads for %s\n") % branch)
2533 for x in found:
2534 for x in found:
2534 cmdutil.show_changeset(ui, repo, {}).show(changenode=x)
2535 cmdutil.show_changeset(ui, repo, {}).show(changenode=x)
2535 raise util.Abort("")
2536 raise util.Abort("")
2536 if len(found) == 1:
2537 if len(found) == 1:
2537 node = found[0]
2538 node = found[0]
2538 repo.ui.warn(_("Using head %s for branch %s\n")
2539 repo.ui.warn(_("Using head %s for branch %s\n")
2539 % (short(node), branch))
2540 % (short(node), branch))
2540 else:
2541 else:
2541 raise util.Abort(_("branch %s not found") % branch)
2542 raise util.Abort(_("branch %s not found") % branch)
2542 else:
2543 else:
2543 node = node and repo.lookup(node) or repo.changelog.tip()
2544 node = node and repo.lookup(node) or repo.changelog.tip()
2544 return node
2545 return node
2545
2546
2546 def verify(ui, repo):
2547 def verify(ui, repo):
2547 """verify the integrity of the repository
2548 """verify the integrity of the repository
2548
2549
2549 Verify the integrity of the current repository.
2550 Verify the integrity of the current repository.
2550
2551
2551 This will perform an extensive check of the repository's
2552 This will perform an extensive check of the repository's
2552 integrity, validating the hashes and checksums of each entry in
2553 integrity, validating the hashes and checksums of each entry in
2553 the changelog, manifest, and tracked files, as well as the
2554 the changelog, manifest, and tracked files, as well as the
2554 integrity of their crosslinks and indices.
2555 integrity of their crosslinks and indices.
2555 """
2556 """
2556 return hg.verify(repo)
2557 return hg.verify(repo)
2557
2558
2558 def version_(ui):
2559 def version_(ui):
2559 """output version and copyright information"""
2560 """output version and copyright information"""
2560 ui.write(_("Mercurial Distributed SCM (version %s)\n")
2561 ui.write(_("Mercurial Distributed SCM (version %s)\n")
2561 % version.get_version())
2562 % version.get_version())
2562 ui.status(_(
2563 ui.status(_(
2563 "\nCopyright (C) 2005, 2006 Matt Mackall <mpm@selenic.com>\n"
2564 "\nCopyright (C) 2005, 2006 Matt Mackall <mpm@selenic.com>\n"
2564 "This is free software; see the source for copying conditions. "
2565 "This is free software; see the source for copying conditions. "
2565 "There is NO\nwarranty; "
2566 "There is NO\nwarranty; "
2566 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
2567 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
2567 ))
2568 ))
2568
2569
2569 # Command options and aliases are listed here, alphabetically
2570 # Command options and aliases are listed here, alphabetically
2570
2571
2571 globalopts = [
2572 globalopts = [
2572 ('R', 'repository', '',
2573 ('R', 'repository', '',
2573 _('repository root directory or symbolic path name')),
2574 _('repository root directory or symbolic path name')),
2574 ('', 'cwd', '', _('change working directory')),
2575 ('', 'cwd', '', _('change working directory')),
2575 ('y', 'noninteractive', None,
2576 ('y', 'noninteractive', None,
2576 _('do not prompt, assume \'yes\' for any required answers')),
2577 _('do not prompt, assume \'yes\' for any required answers')),
2577 ('q', 'quiet', None, _('suppress output')),
2578 ('q', 'quiet', None, _('suppress output')),
2578 ('v', 'verbose', None, _('enable additional output')),
2579 ('v', 'verbose', None, _('enable additional output')),
2579 ('', 'config', [], _('set/override config option')),
2580 ('', 'config', [], _('set/override config option')),
2580 ('', 'debug', None, _('enable debugging output')),
2581 ('', 'debug', None, _('enable debugging output')),
2581 ('', 'debugger', None, _('start debugger')),
2582 ('', 'debugger', None, _('start debugger')),
2582 ('', 'encoding', util._encoding, _('set the charset encoding')),
2583 ('', 'encoding', util._encoding, _('set the charset encoding')),
2583 ('', 'encodingmode', util._encodingmode, _('set the charset encoding mode')),
2584 ('', 'encodingmode', util._encodingmode, _('set the charset encoding mode')),
2584 ('', 'lsprof', None, _('print improved command execution profile')),
2585 ('', 'lsprof', None, _('print improved command execution profile')),
2585 ('', 'traceback', None, _('print traceback on exception')),
2586 ('', 'traceback', None, _('print traceback on exception')),
2586 ('', 'time', None, _('time how long the command takes')),
2587 ('', 'time', None, _('time how long the command takes')),
2587 ('', 'profile', None, _('print command execution profile')),
2588 ('', 'profile', None, _('print command execution profile')),
2588 ('', 'version', None, _('output version information and exit')),
2589 ('', 'version', None, _('output version information and exit')),
2589 ('h', 'help', None, _('display help and exit')),
2590 ('h', 'help', None, _('display help and exit')),
2590 ]
2591 ]
2591
2592
2592 dryrunopts = [('n', 'dry-run', None,
2593 dryrunopts = [('n', 'dry-run', None,
2593 _('do not perform actions, just print output'))]
2594 _('do not perform actions, just print output'))]
2594
2595
2595 remoteopts = [
2596 remoteopts = [
2596 ('e', 'ssh', '', _('specify ssh command to use')),
2597 ('e', 'ssh', '', _('specify ssh command to use')),
2597 ('', 'remotecmd', '', _('specify hg command to run on the remote side')),
2598 ('', 'remotecmd', '', _('specify hg command to run on the remote side')),
2598 ]
2599 ]
2599
2600
2600 walkopts = [
2601 walkopts = [
2601 ('I', 'include', [], _('include names matching the given patterns')),
2602 ('I', 'include', [], _('include names matching the given patterns')),
2602 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2603 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2603 ]
2604 ]
2604
2605
2605 commitopts = [
2606 commitopts = [
2606 ('m', 'message', '', _('use <text> as commit message')),
2607 ('m', 'message', '', _('use <text> as commit message')),
2607 ('l', 'logfile', '', _('read commit message from <file>')),
2608 ('l', 'logfile', '', _('read commit message from <file>')),
2608 ]
2609 ]
2609
2610
2610 table = {
2611 table = {
2611 "^add": (add, walkopts + dryrunopts, _('hg add [OPTION]... [FILE]...')),
2612 "^add": (add, walkopts + dryrunopts, _('hg add [OPTION]... [FILE]...')),
2612 "addremove":
2613 "addremove":
2613 (addremove,
2614 (addremove,
2614 [('s', 'similarity', '',
2615 [('s', 'similarity', '',
2615 _('guess renamed files by similarity (0<=s<=100)')),
2616 _('guess renamed files by similarity (0<=s<=100)')),
2616 ] + walkopts + dryrunopts,
2617 ] + walkopts + dryrunopts,
2617 _('hg addremove [OPTION]... [FILE]...')),
2618 _('hg addremove [OPTION]... [FILE]...')),
2618 "^annotate":
2619 "^annotate":
2619 (annotate,
2620 (annotate,
2620 [('r', 'rev', '', _('annotate the specified revision')),
2621 [('r', 'rev', '', _('annotate the specified revision')),
2621 ('f', 'follow', None, _('follow file copies and renames')),
2622 ('f', 'follow', None, _('follow file copies and renames')),
2622 ('a', 'text', None, _('treat all files as text')),
2623 ('a', 'text', None, _('treat all files as text')),
2623 ('u', 'user', None, _('list the author')),
2624 ('u', 'user', None, _('list the author')),
2624 ('d', 'date', None, _('list the date')),
2625 ('d', 'date', None, _('list the date')),
2625 ('n', 'number', None, _('list the revision number (default)')),
2626 ('n', 'number', None, _('list the revision number (default)')),
2626 ('c', 'changeset', None, _('list the changeset')),
2627 ('c', 'changeset', None, _('list the changeset')),
2627 ] + walkopts,
2628 ] + walkopts,
2628 _('hg annotate [-r REV] [-f] [-a] [-u] [-d] [-n] [-c] FILE...')),
2629 _('hg annotate [-r REV] [-f] [-a] [-u] [-d] [-n] [-c] FILE...')),
2629 "archive":
2630 "archive":
2630 (archive,
2631 (archive,
2631 [('', 'no-decode', None, _('do not pass files through decoders')),
2632 [('', 'no-decode', None, _('do not pass files through decoders')),
2632 ('p', 'prefix', '', _('directory prefix for files in archive')),
2633 ('p', 'prefix', '', _('directory prefix for files in archive')),
2633 ('r', 'rev', '', _('revision to distribute')),
2634 ('r', 'rev', '', _('revision to distribute')),
2634 ('t', 'type', '', _('type of distribution to create')),
2635 ('t', 'type', '', _('type of distribution to create')),
2635 ] + walkopts,
2636 ] + walkopts,
2636 _('hg archive [OPTION]... DEST')),
2637 _('hg archive [OPTION]... DEST')),
2637 "backout":
2638 "backout":
2638 (backout,
2639 (backout,
2639 [('', 'merge', None,
2640 [('', 'merge', None,
2640 _('merge with old dirstate parent after backout')),
2641 _('merge with old dirstate parent after backout')),
2641 ('d', 'date', '', _('record datecode as commit date')),
2642 ('d', 'date', '', _('record datecode as commit date')),
2642 ('', 'parent', '', _('parent to choose when backing out merge')),
2643 ('', 'parent', '', _('parent to choose when backing out merge')),
2643 ('u', 'user', '', _('record user as committer')),
2644 ('u', 'user', '', _('record user as committer')),
2644 ] + walkopts + commitopts,
2645 ] + walkopts + commitopts,
2645 _('hg backout [OPTION]... REV')),
2646 _('hg backout [OPTION]... REV')),
2646 "branch": (branch, [], _('hg branch [NAME]')),
2647 "branch": (branch, [], _('hg branch [NAME]')),
2647 "branches": (branches, [], _('hg branches')),
2648 "branches": (branches, [], _('hg branches')),
2648 "bundle":
2649 "bundle":
2649 (bundle,
2650 (bundle,
2650 [('f', 'force', None,
2651 [('f', 'force', None,
2651 _('run even when remote repository is unrelated')),
2652 _('run even when remote repository is unrelated')),
2652 ('r', 'rev', [],
2653 ('r', 'rev', [],
2653 _('a changeset you would like to bundle')),
2654 _('a changeset you would like to bundle')),
2654 ('', 'base', [],
2655 ('', 'base', [],
2655 _('a base changeset to specify instead of a destination')),
2656 _('a base changeset to specify instead of a destination')),
2656 ] + remoteopts,
2657 ] + remoteopts,
2657 _('hg bundle [-f] [-r REV]... [--base REV]... FILE [DEST]')),
2658 _('hg bundle [-f] [-r REV]... [--base REV]... FILE [DEST]')),
2658 "cat":
2659 "cat":
2659 (cat,
2660 (cat,
2660 [('o', 'output', '', _('print output to file with formatted name')),
2661 [('o', 'output', '', _('print output to file with formatted name')),
2661 ('r', 'rev', '', _('print the given revision')),
2662 ('r', 'rev', '', _('print the given revision')),
2662 ] + walkopts,
2663 ] + walkopts,
2663 _('hg cat [OPTION]... FILE...')),
2664 _('hg cat [OPTION]... FILE...')),
2664 "^clone":
2665 "^clone":
2665 (clone,
2666 (clone,
2666 [('U', 'noupdate', None, _('do not update the new working directory')),
2667 [('U', 'noupdate', None, _('do not update the new working directory')),
2667 ('r', 'rev', [],
2668 ('r', 'rev', [],
2668 _('a changeset you would like to have after cloning')),
2669 _('a changeset you would like to have after cloning')),
2669 ('', 'pull', None, _('use pull protocol to copy metadata')),
2670 ('', 'pull', None, _('use pull protocol to copy metadata')),
2670 ('', 'uncompressed', None,
2671 ('', 'uncompressed', None,
2671 _('use uncompressed transfer (fast over LAN)')),
2672 _('use uncompressed transfer (fast over LAN)')),
2672 ] + remoteopts,
2673 ] + remoteopts,
2673 _('hg clone [OPTION]... SOURCE [DEST]')),
2674 _('hg clone [OPTION]... SOURCE [DEST]')),
2674 "^commit|ci":
2675 "^commit|ci":
2675 (commit,
2676 (commit,
2676 [('A', 'addremove', None,
2677 [('A', 'addremove', None,
2677 _('mark new/missing files as added/removed before committing')),
2678 _('mark new/missing files as added/removed before committing')),
2678 ('d', 'date', '', _('record datecode as commit date')),
2679 ('d', 'date', '', _('record datecode as commit date')),
2679 ('u', 'user', '', _('record user as commiter')),
2680 ('u', 'user', '', _('record user as commiter')),
2680 ] + walkopts + commitopts,
2681 ] + walkopts + commitopts,
2681 _('hg commit [OPTION]... [FILE]...')),
2682 _('hg commit [OPTION]... [FILE]...')),
2682 "copy|cp":
2683 "copy|cp":
2683 (copy,
2684 (copy,
2684 [('A', 'after', None, _('record a copy that has already occurred')),
2685 [('A', 'after', None, _('record a copy that has already occurred')),
2685 ('f', 'force', None,
2686 ('f', 'force', None,
2686 _('forcibly copy over an existing managed file')),
2687 _('forcibly copy over an existing managed file')),
2687 ] + walkopts + dryrunopts,
2688 ] + walkopts + dryrunopts,
2688 _('hg copy [OPTION]... [SOURCE]... DEST')),
2689 _('hg copy [OPTION]... [SOURCE]... DEST')),
2689 "debugancestor": (debugancestor, [], _('debugancestor INDEX REV1 REV2')),
2690 "debugancestor": (debugancestor, [], _('debugancestor INDEX REV1 REV2')),
2690 "debugcomplete":
2691 "debugcomplete":
2691 (debugcomplete,
2692 (debugcomplete,
2692 [('o', 'options', None, _('show the command options'))],
2693 [('o', 'options', None, _('show the command options'))],
2693 _('debugcomplete [-o] CMD')),
2694 _('debugcomplete [-o] CMD')),
2694 "debuginstall": (debuginstall, [], _('debuginstall')),
2695 "debuginstall": (debuginstall, [], _('debuginstall')),
2695 "debugrebuildstate":
2696 "debugrebuildstate":
2696 (debugrebuildstate,
2697 (debugrebuildstate,
2697 [('r', 'rev', '', _('revision to rebuild to'))],
2698 [('r', 'rev', '', _('revision to rebuild to'))],
2698 _('debugrebuildstate [-r REV] [REV]')),
2699 _('debugrebuildstate [-r REV] [REV]')),
2699 "debugcheckstate": (debugcheckstate, [], _('debugcheckstate')),
2700 "debugcheckstate": (debugcheckstate, [], _('debugcheckstate')),
2700 "debugsetparents": (debugsetparents, [], _('debugsetparents REV1 [REV2]')),
2701 "debugsetparents": (debugsetparents, [], _('debugsetparents REV1 [REV2]')),
2701 "debugstate": (debugstate, [], _('debugstate')),
2702 "debugstate": (debugstate, [], _('debugstate')),
2702 "debugdate":
2703 "debugdate":
2703 (debugdate,
2704 (debugdate,
2704 [('e', 'extended', None, _('try extended date formats'))],
2705 [('e', 'extended', None, _('try extended date formats'))],
2705 _('debugdate [-e] DATE [RANGE]')),
2706 _('debugdate [-e] DATE [RANGE]')),
2706 "debugdata": (debugdata, [], _('debugdata FILE REV')),
2707 "debugdata": (debugdata, [], _('debugdata FILE REV')),
2707 "debugindex": (debugindex, [], _('debugindex FILE')),
2708 "debugindex": (debugindex, [], _('debugindex FILE')),
2708 "debugindexdot": (debugindexdot, [], _('debugindexdot FILE')),
2709 "debugindexdot": (debugindexdot, [], _('debugindexdot FILE')),
2709 "debugrename": (debugrename, [], _('debugrename FILE [REV]')),
2710 "debugrename": (debugrename, [], _('debugrename FILE [REV]')),
2710 "debugwalk": (debugwalk, walkopts, _('debugwalk [OPTION]... [FILE]...')),
2711 "debugwalk": (debugwalk, walkopts, _('debugwalk [OPTION]... [FILE]...')),
2711 "^diff":
2712 "^diff":
2712 (diff,
2713 (diff,
2713 [('r', 'rev', [], _('revision')),
2714 [('r', 'rev', [], _('revision')),
2714 ('a', 'text', None, _('treat all files as text')),
2715 ('a', 'text', None, _('treat all files as text')),
2715 ('p', 'show-function', None,
2716 ('p', 'show-function', None,
2716 _('show which function each change is in')),
2717 _('show which function each change is in')),
2717 ('g', 'git', None, _('use git extended diff format')),
2718 ('g', 'git', None, _('use git extended diff format')),
2718 ('', 'nodates', None, _("don't include dates in diff headers")),
2719 ('', 'nodates', None, _("don't include dates in diff headers")),
2719 ('w', 'ignore-all-space', None,
2720 ('w', 'ignore-all-space', None,
2720 _('ignore white space when comparing lines')),
2721 _('ignore white space when comparing lines')),
2721 ('b', 'ignore-space-change', None,
2722 ('b', 'ignore-space-change', None,
2722 _('ignore changes in the amount of white space')),
2723 _('ignore changes in the amount of white space')),
2723 ('B', 'ignore-blank-lines', None,
2724 ('B', 'ignore-blank-lines', None,
2724 _('ignore changes whose lines are all blank')),
2725 _('ignore changes whose lines are all blank')),
2725 ] + walkopts,
2726 ] + walkopts,
2726 _('hg diff [OPTION]... [-r REV1 [-r REV2]] [FILE]...')),
2727 _('hg diff [OPTION]... [-r REV1 [-r REV2]] [FILE]...')),
2727 "^export":
2728 "^export":
2728 (export,
2729 (export,
2729 [('o', 'output', '', _('print output to file with formatted name')),
2730 [('o', 'output', '', _('print output to file with formatted name')),
2730 ('a', 'text', None, _('treat all files as text')),
2731 ('a', 'text', None, _('treat all files as text')),
2731 ('g', 'git', None, _('use git extended diff format')),
2732 ('g', 'git', None, _('use git extended diff format')),
2732 ('', 'nodates', None, _("don't include dates in diff headers")),
2733 ('', 'nodates', None, _("don't include dates in diff headers")),
2733 ('', 'switch-parent', None, _('diff against the second parent'))],
2734 ('', 'switch-parent', None, _('diff against the second parent'))],
2734 _('hg export [OPTION]... [-o OUTFILESPEC] REV...')),
2735 _('hg export [OPTION]... [-o OUTFILESPEC] REV...')),
2735 "grep":
2736 "grep":
2736 (grep,
2737 (grep,
2737 [('0', 'print0', None, _('end fields with NUL')),
2738 [('0', 'print0', None, _('end fields with NUL')),
2738 ('', 'all', None, _('print all revisions that match')),
2739 ('', 'all', None, _('print all revisions that match')),
2739 ('f', 'follow', None,
2740 ('f', 'follow', None,
2740 _('follow changeset history, or file history across copies and renames')),
2741 _('follow changeset history, or file history across copies and renames')),
2741 ('i', 'ignore-case', None, _('ignore case when matching')),
2742 ('i', 'ignore-case', None, _('ignore case when matching')),
2742 ('l', 'files-with-matches', None,
2743 ('l', 'files-with-matches', None,
2743 _('print only filenames and revs that match')),
2744 _('print only filenames and revs that match')),
2744 ('n', 'line-number', None, _('print matching line numbers')),
2745 ('n', 'line-number', None, _('print matching line numbers')),
2745 ('r', 'rev', [], _('search in given revision range')),
2746 ('r', 'rev', [], _('search in given revision range')),
2746 ('u', 'user', None, _('print user who committed change')),
2747 ('u', 'user', None, _('print user who committed change')),
2747 ] + walkopts,
2748 ] + walkopts,
2748 _('hg grep [OPTION]... PATTERN [FILE]...')),
2749 _('hg grep [OPTION]... PATTERN [FILE]...')),
2749 "heads":
2750 "heads":
2750 (heads,
2751 (heads,
2751 [('b', 'branches', None, _('show branches (DEPRECATED)')),
2752 [('b', 'branches', None, _('show branches (DEPRECATED)')),
2752 ('', 'style', '', _('display using template map file')),
2753 ('', 'style', '', _('display using template map file')),
2753 ('r', 'rev', '', _('show only heads which are descendants of rev')),
2754 ('r', 'rev', '', _('show only heads which are descendants of rev')),
2754 ('', 'template', '', _('display with template'))],
2755 ('', 'template', '', _('display with template'))],
2755 _('hg heads [-r REV]')),
2756 _('hg heads [-r REV]')),
2756 "help": (help_, [], _('hg help [COMMAND]')),
2757 "help": (help_, [], _('hg help [COMMAND]')),
2757 "identify|id": (identify, [], _('hg identify')),
2758 "identify|id": (identify, [], _('hg identify')),
2758 "import|patch":
2759 "import|patch":
2759 (import_,
2760 (import_,
2760 [('p', 'strip', 1,
2761 [('p', 'strip', 1,
2761 _('directory strip option for patch. This has the same\n'
2762 _('directory strip option for patch. This has the same\n'
2762 'meaning as the corresponding patch option')),
2763 'meaning as the corresponding patch option')),
2763 ('b', 'base', '', _('base path (DEPRECATED)')),
2764 ('b', 'base', '', _('base path (DEPRECATED)')),
2764 ('f', 'force', None,
2765 ('f', 'force', None,
2765 _('skip check for outstanding uncommitted changes'))] + commitopts,
2766 _('skip check for outstanding uncommitted changes'))] + commitopts,
2766 _('hg import [-p NUM] [-m MESSAGE] [-f] PATCH...')),
2767 _('hg import [-p NUM] [-m MESSAGE] [-f] PATCH...')),
2767 "incoming|in": (incoming,
2768 "incoming|in": (incoming,
2768 [('M', 'no-merges', None, _('do not show merges')),
2769 [('M', 'no-merges', None, _('do not show merges')),
2769 ('f', 'force', None,
2770 ('f', 'force', None,
2770 _('run even when remote repository is unrelated')),
2771 _('run even when remote repository is unrelated')),
2771 ('', 'style', '', _('display using template map file')),
2772 ('', 'style', '', _('display using template map file')),
2772 ('n', 'newest-first', None, _('show newest record first')),
2773 ('n', 'newest-first', None, _('show newest record first')),
2773 ('', 'bundle', '', _('file to store the bundles into')),
2774 ('', 'bundle', '', _('file to store the bundles into')),
2774 ('p', 'patch', None, _('show patch')),
2775 ('p', 'patch', None, _('show patch')),
2775 ('r', 'rev', [], _('a specific revision up to which you would like to pull')),
2776 ('r', 'rev', [], _('a specific revision up to which you would like to pull')),
2776 ('', 'template', '', _('display with template')),
2777 ('', 'template', '', _('display with template')),
2777 ] + remoteopts,
2778 ] + remoteopts,
2778 _('hg incoming [-p] [-n] [-M] [-f] [-r REV]...'
2779 _('hg incoming [-p] [-n] [-M] [-f] [-r REV]...'
2779 ' [--bundle FILENAME] [SOURCE]')),
2780 ' [--bundle FILENAME] [SOURCE]')),
2780 "^init":
2781 "^init":
2781 (init,
2782 (init,
2782 remoteopts,
2783 remoteopts,
2783 _('hg init [-e CMD] [--remotecmd CMD] [DEST]')),
2784 _('hg init [-e CMD] [--remotecmd CMD] [DEST]')),
2784 "locate":
2785 "locate":
2785 (locate,
2786 (locate,
2786 [('r', 'rev', '', _('search the repository as it stood at rev')),
2787 [('r', 'rev', '', _('search the repository as it stood at rev')),
2787 ('0', 'print0', None,
2788 ('0', 'print0', None,
2788 _('end filenames with NUL, for use with xargs')),
2789 _('end filenames with NUL, for use with xargs')),
2789 ('f', 'fullpath', None,
2790 ('f', 'fullpath', None,
2790 _('print complete paths from the filesystem root')),
2791 _('print complete paths from the filesystem root')),
2791 ] + walkopts,
2792 ] + walkopts,
2792 _('hg locate [OPTION]... [PATTERN]...')),
2793 _('hg locate [OPTION]... [PATTERN]...')),
2793 "^log|history":
2794 "^log|history":
2794 (log,
2795 (log,
2795 [('b', 'branches', None, _('show branches (DEPRECATED)')),
2796 [('b', 'branches', None, _('show branches (DEPRECATED)')),
2796 ('f', 'follow', None,
2797 ('f', 'follow', None,
2797 _('follow changeset history, or file history across copies and renames')),
2798 _('follow changeset history, or file history across copies and renames')),
2798 ('', 'follow-first', None,
2799 ('', 'follow-first', None,
2799 _('only follow the first parent of merge changesets')),
2800 _('only follow the first parent of merge changesets')),
2800 ('d', 'date', '', _('show revs matching date spec')),
2801 ('d', 'date', '', _('show revs matching date spec')),
2801 ('C', 'copies', None, _('show copied files')),
2802 ('C', 'copies', None, _('show copied files')),
2802 ('k', 'keyword', [], _('search for a keyword')),
2803 ('k', 'keyword', [], _('search for a keyword')),
2803 ('l', 'limit', '', _('limit number of changes displayed')),
2804 ('l', 'limit', '', _('limit number of changes displayed')),
2804 ('r', 'rev', [], _('show the specified revision or range')),
2805 ('r', 'rev', [], _('show the specified revision or range')),
2805 ('', 'removed', None, _('include revs where files were removed')),
2806 ('', 'removed', None, _('include revs where files were removed')),
2806 ('M', 'no-merges', None, _('do not show merges')),
2807 ('M', 'no-merges', None, _('do not show merges')),
2807 ('', 'style', '', _('display using template map file')),
2808 ('', 'style', '', _('display using template map file')),
2808 ('m', 'only-merges', None, _('show only merges')),
2809 ('m', 'only-merges', None, _('show only merges')),
2809 ('p', 'patch', None, _('show patch')),
2810 ('p', 'patch', None, _('show patch')),
2810 ('P', 'prune', [], _('do not display revision or any of its ancestors')),
2811 ('P', 'prune', [], _('do not display revision or any of its ancestors')),
2811 ('', 'template', '', _('display with template')),
2812 ('', 'template', '', _('display with template')),
2812 ] + walkopts,
2813 ] + walkopts,
2813 _('hg log [OPTION]... [FILE]')),
2814 _('hg log [OPTION]... [FILE]')),
2814 "manifest": (manifest, [], _('hg manifest [REV]')),
2815 "manifest": (manifest, [], _('hg manifest [REV]')),
2815 "^merge":
2816 "^merge":
2816 (merge,
2817 (merge,
2817 [('b', 'branch', '', _('merge with head of a specific branch (DEPRECATED)')),
2818 [('b', 'branch', '', _('merge with head of a specific branch (DEPRECATED)')),
2818 ('f', 'force', None, _('force a merge with outstanding changes'))],
2819 ('f', 'force', None, _('force a merge with outstanding changes'))],
2819 _('hg merge [-f] [REV]')),
2820 _('hg merge [-f] [REV]')),
2820 "outgoing|out": (outgoing,
2821 "outgoing|out": (outgoing,
2821 [('M', 'no-merges', None, _('do not show merges')),
2822 [('M', 'no-merges', None, _('do not show merges')),
2822 ('f', 'force', None,
2823 ('f', 'force', None,
2823 _('run even when remote repository is unrelated')),
2824 _('run even when remote repository is unrelated')),
2824 ('p', 'patch', None, _('show patch')),
2825 ('p', 'patch', None, _('show patch')),
2825 ('', 'style', '', _('display using template map file')),
2826 ('', 'style', '', _('display using template map file')),
2826 ('r', 'rev', [], _('a specific revision you would like to push')),
2827 ('r', 'rev', [], _('a specific revision you would like to push')),
2827 ('n', 'newest-first', None, _('show newest record first')),
2828 ('n', 'newest-first', None, _('show newest record first')),
2828 ('', 'template', '', _('display with template')),
2829 ('', 'template', '', _('display with template')),
2829 ] + remoteopts,
2830 ] + remoteopts,
2830 _('hg outgoing [-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
2831 _('hg outgoing [-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
2831 "^parents":
2832 "^parents":
2832 (parents,
2833 (parents,
2833 [('b', 'branches', None, _('show branches (DEPRECATED)')),
2834 [('b', 'branches', None, _('show branches (DEPRECATED)')),
2834 ('r', 'rev', '', _('show parents from the specified rev')),
2835 ('r', 'rev', '', _('show parents from the specified rev')),
2835 ('', 'style', '', _('display using template map file')),
2836 ('', 'style', '', _('display using template map file')),
2836 ('', 'template', '', _('display with template'))],
2837 ('', 'template', '', _('display with template'))],
2837 _('hg parents [-r REV] [FILE]')),
2838 _('hg parents [-r REV] [FILE]')),
2838 "paths": (paths, [], _('hg paths [NAME]')),
2839 "paths": (paths, [], _('hg paths [NAME]')),
2839 "^pull":
2840 "^pull":
2840 (pull,
2841 (pull,
2841 [('u', 'update', None,
2842 [('u', 'update', None,
2842 _('update to new tip if changesets were pulled')),
2843 _('update to new tip if changesets were pulled')),
2843 ('f', 'force', None,
2844 ('f', 'force', None,
2844 _('run even when remote repository is unrelated')),
2845 _('run even when remote repository is unrelated')),
2845 ('r', 'rev', [],
2846 ('r', 'rev', [],
2846 _('a specific revision up to which you would like to pull')),
2847 _('a specific revision up to which you would like to pull')),
2847 ] + remoteopts,
2848 ] + remoteopts,
2848 _('hg pull [-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
2849 _('hg pull [-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
2849 "^push":
2850 "^push":
2850 (push,
2851 (push,
2851 [('f', 'force', None, _('force push')),
2852 [('f', 'force', None, _('force push')),
2852 ('r', 'rev', [], _('a specific revision you would like to push')),
2853 ('r', 'rev', [], _('a specific revision you would like to push')),
2853 ] + remoteopts,
2854 ] + remoteopts,
2854 _('hg push [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
2855 _('hg push [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
2855 "debugrawcommit|rawcommit":
2856 "debugrawcommit|rawcommit":
2856 (rawcommit,
2857 (rawcommit,
2857 [('p', 'parent', [], _('parent')),
2858 [('p', 'parent', [], _('parent')),
2858 ('d', 'date', '', _('date code')),
2859 ('d', 'date', '', _('date code')),
2859 ('u', 'user', '', _('user')),
2860 ('u', 'user', '', _('user')),
2860 ('F', 'files', '', _('file list'))
2861 ('F', 'files', '', _('file list'))
2861 ] + commitopts,
2862 ] + commitopts,
2862 _('hg debugrawcommit [OPTION]... [FILE]...')),
2863 _('hg debugrawcommit [OPTION]... [FILE]...')),
2863 "recover": (recover, [], _('hg recover')),
2864 "recover": (recover, [], _('hg recover')),
2864 "^remove|rm":
2865 "^remove|rm":
2865 (remove,
2866 (remove,
2866 [('A', 'after', None, _('record remove that has already occurred')),
2867 [('A', 'after', None, _('record remove that has already occurred')),
2867 ('f', 'force', None, _('remove file even if modified')),
2868 ('f', 'force', None, _('remove file even if modified')),
2868 ] + walkopts,
2869 ] + walkopts,
2869 _('hg remove [OPTION]... FILE...')),
2870 _('hg remove [OPTION]... FILE...')),
2870 "rename|mv":
2871 "rename|mv":
2871 (rename,
2872 (rename,
2872 [('A', 'after', None, _('record a rename that has already occurred')),
2873 [('A', 'after', None, _('record a rename that has already occurred')),
2873 ('f', 'force', None,
2874 ('f', 'force', None,
2874 _('forcibly copy over an existing managed file')),
2875 _('forcibly copy over an existing managed file')),
2875 ] + walkopts + dryrunopts,
2876 ] + walkopts + dryrunopts,
2876 _('hg rename [OPTION]... SOURCE... DEST')),
2877 _('hg rename [OPTION]... SOURCE... DEST')),
2877 "^revert":
2878 "^revert":
2878 (revert,
2879 (revert,
2879 [('a', 'all', None, _('revert all changes when no arguments given')),
2880 [('a', 'all', None, _('revert all changes when no arguments given')),
2880 ('d', 'date', '', _('tipmost revision matching date')),
2881 ('d', 'date', '', _('tipmost revision matching date')),
2881 ('r', 'rev', '', _('revision to revert to')),
2882 ('r', 'rev', '', _('revision to revert to')),
2882 ('', 'no-backup', None, _('do not save backup copies of files')),
2883 ('', 'no-backup', None, _('do not save backup copies of files')),
2883 ] + walkopts + dryrunopts,
2884 ] + walkopts + dryrunopts,
2884 _('hg revert [OPTION]... [-r REV] [NAME]...')),
2885 _('hg revert [OPTION]... [-r REV] [NAME]...')),
2885 "rollback": (rollback, [], _('hg rollback')),
2886 "rollback": (rollback, [], _('hg rollback')),
2886 "root": (root, [], _('hg root')),
2887 "root": (root, [], _('hg root')),
2887 "showconfig|debugconfig":
2888 "showconfig|debugconfig":
2888 (showconfig,
2889 (showconfig,
2889 [('u', 'untrusted', None, _('show untrusted configuration options'))],
2890 [('u', 'untrusted', None, _('show untrusted configuration options'))],
2890 _('showconfig [-u] [NAME]...')),
2891 _('showconfig [-u] [NAME]...')),
2891 "^serve":
2892 "^serve":
2892 (serve,
2893 (serve,
2893 [('A', 'accesslog', '', _('name of access log file to write to')),
2894 [('A', 'accesslog', '', _('name of access log file to write to')),
2894 ('d', 'daemon', None, _('run server in background')),
2895 ('d', 'daemon', None, _('run server in background')),
2895 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
2896 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
2896 ('E', 'errorlog', '', _('name of error log file to write to')),
2897 ('E', 'errorlog', '', _('name of error log file to write to')),
2897 ('p', 'port', 0, _('port to use (default: 8000)')),
2898 ('p', 'port', 0, _('port to use (default: 8000)')),
2898 ('a', 'address', '', _('address to use')),
2899 ('a', 'address', '', _('address to use')),
2899 ('n', 'name', '',
2900 ('n', 'name', '',
2900 _('name to show in web pages (default: working dir)')),
2901 _('name to show in web pages (default: working dir)')),
2901 ('', 'webdir-conf', '', _('name of the webdir config file'
2902 ('', 'webdir-conf', '', _('name of the webdir config file'
2902 ' (serve more than one repo)')),
2903 ' (serve more than one repo)')),
2903 ('', 'pid-file', '', _('name of file to write process ID to')),
2904 ('', 'pid-file', '', _('name of file to write process ID to')),
2904 ('', 'stdio', None, _('for remote clients')),
2905 ('', 'stdio', None, _('for remote clients')),
2905 ('t', 'templates', '', _('web templates to use')),
2906 ('t', 'templates', '', _('web templates to use')),
2906 ('', 'style', '', _('template style to use')),
2907 ('', 'style', '', _('template style to use')),
2907 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4'))],
2908 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4'))],
2908 _('hg serve [OPTION]...')),
2909 _('hg serve [OPTION]...')),
2909 "^status|st":
2910 "^status|st":
2910 (status,
2911 (status,
2911 [('A', 'all', None, _('show status of all files')),
2912 [('A', 'all', None, _('show status of all files')),
2912 ('m', 'modified', None, _('show only modified files')),
2913 ('m', 'modified', None, _('show only modified files')),
2913 ('a', 'added', None, _('show only added files')),
2914 ('a', 'added', None, _('show only added files')),
2914 ('r', 'removed', None, _('show only removed files')),
2915 ('r', 'removed', None, _('show only removed files')),
2915 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
2916 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
2916 ('c', 'clean', None, _('show only files without changes')),
2917 ('c', 'clean', None, _('show only files without changes')),
2917 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
2918 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
2918 ('i', 'ignored', None, _('show ignored files')),
2919 ('i', 'ignored', None, _('show ignored files')),
2919 ('n', 'no-status', None, _('hide status prefix')),
2920 ('n', 'no-status', None, _('hide status prefix')),
2920 ('C', 'copies', None, _('show source of copied files')),
2921 ('C', 'copies', None, _('show source of copied files')),
2921 ('0', 'print0', None,
2922 ('0', 'print0', None,
2922 _('end filenames with NUL, for use with xargs')),
2923 _('end filenames with NUL, for use with xargs')),
2923 ('', 'rev', [], _('show difference from revision')),
2924 ('', 'rev', [], _('show difference from revision')),
2924 ] + walkopts,
2925 ] + walkopts,
2925 _('hg status [OPTION]... [FILE]...')),
2926 _('hg status [OPTION]... [FILE]...')),
2926 "tag":
2927 "tag":
2927 (tag,
2928 (tag,
2928 [('l', 'local', None, _('make the tag local')),
2929 [('l', 'local', None, _('make the tag local')),
2929 ('m', 'message', '', _('message for tag commit log entry')),
2930 ('m', 'message', '', _('message for tag commit log entry')),
2930 ('d', 'date', '', _('record datecode as commit date')),
2931 ('d', 'date', '', _('record datecode as commit date')),
2931 ('u', 'user', '', _('record user as commiter')),
2932 ('u', 'user', '', _('record user as commiter')),
2932 ('r', 'rev', '', _('revision to tag'))],
2933 ('r', 'rev', '', _('revision to tag'))],
2933 _('hg tag [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME')),
2934 _('hg tag [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME')),
2934 "tags": (tags, [], _('hg tags')),
2935 "tags": (tags, [], _('hg tags')),
2935 "tip":
2936 "tip":
2936 (tip,
2937 (tip,
2937 [('b', 'branches', None, _('show branches (DEPRECATED)')),
2938 [('b', 'branches', None, _('show branches (DEPRECATED)')),
2938 ('', 'style', '', _('display using template map file')),
2939 ('', 'style', '', _('display using template map file')),
2939 ('p', 'patch', None, _('show patch')),
2940 ('p', 'patch', None, _('show patch')),
2940 ('', 'template', '', _('display with template'))],
2941 ('', 'template', '', _('display with template'))],
2941 _('hg tip [-p]')),
2942 _('hg tip [-p]')),
2942 "unbundle":
2943 "unbundle":
2943 (unbundle,
2944 (unbundle,
2944 [('u', 'update', None,
2945 [('u', 'update', None,
2945 _('update to new tip if changesets were unbundled'))],
2946 _('update to new tip if changesets were unbundled'))],
2946 _('hg unbundle [-u] FILE')),
2947 _('hg unbundle [-u] FILE')),
2947 "^update|up|checkout|co":
2948 "^update|up|checkout|co":
2948 (update,
2949 (update,
2949 [('b', 'branch', '',
2950 [('b', 'branch', '',
2950 _('checkout the head of a specific branch (DEPRECATED)')),
2951 _('checkout the head of a specific branch (DEPRECATED)')),
2951 ('C', 'clean', None, _('overwrite locally modified files')),
2952 ('C', 'clean', None, _('overwrite locally modified files')),
2952 ('d', 'date', '', _('tipmost revision matching date'))],
2953 ('d', 'date', '', _('tipmost revision matching date'))],
2953 _('hg update [-C] [-d DATE] [REV]')),
2954 _('hg update [-C] [-d DATE] [REV]')),
2954 "verify": (verify, [], _('hg verify')),
2955 "verify": (verify, [], _('hg verify')),
2955 "version": (version_, [], _('hg version')),
2956 "version": (version_, [], _('hg version')),
2956 }
2957 }
2957
2958
2958 norepo = ("clone init version help debugancestor debugcomplete debugdata"
2959 norepo = ("clone init version help debugancestor debugcomplete debugdata"
2959 " debugindex debugindexdot debugdate debuginstall")
2960 " debugindex debugindexdot debugdate debuginstall")
2960 optionalrepo = ("paths serve showconfig")
2961 optionalrepo = ("paths serve showconfig")
2961
2962
2962 def findpossible(ui, cmd):
2963 def findpossible(ui, cmd):
2963 """
2964 """
2964 Return cmd -> (aliases, command table entry)
2965 Return cmd -> (aliases, command table entry)
2965 for each matching command.
2966 for each matching command.
2966 Return debug commands (or their aliases) only if no normal command matches.
2967 Return debug commands (or their aliases) only if no normal command matches.
2967 """
2968 """
2968 choice = {}
2969 choice = {}
2969 debugchoice = {}
2970 debugchoice = {}
2970 for e in table.keys():
2971 for e in table.keys():
2971 aliases = e.lstrip("^").split("|")
2972 aliases = e.lstrip("^").split("|")
2972 found = None
2973 found = None
2973 if cmd in aliases:
2974 if cmd in aliases:
2974 found = cmd
2975 found = cmd
2975 elif not ui.config("ui", "strict"):
2976 elif not ui.config("ui", "strict"):
2976 for a in aliases:
2977 for a in aliases:
2977 if a.startswith(cmd):
2978 if a.startswith(cmd):
2978 found = a
2979 found = a
2979 break
2980 break
2980 if found is not None:
2981 if found is not None:
2981 if aliases[0].startswith("debug") or found.startswith("debug"):
2982 if aliases[0].startswith("debug") or found.startswith("debug"):
2982 debugchoice[found] = (aliases, table[e])
2983 debugchoice[found] = (aliases, table[e])
2983 else:
2984 else:
2984 choice[found] = (aliases, table[e])
2985 choice[found] = (aliases, table[e])
2985
2986
2986 if not choice and debugchoice:
2987 if not choice and debugchoice:
2987 choice = debugchoice
2988 choice = debugchoice
2988
2989
2989 return choice
2990 return choice
2990
2991
2991 def findcmd(ui, cmd):
2992 def findcmd(ui, cmd):
2992 """Return (aliases, command table entry) for command string."""
2993 """Return (aliases, command table entry) for command string."""
2993 choice = findpossible(ui, cmd)
2994 choice = findpossible(ui, cmd)
2994
2995
2995 if choice.has_key(cmd):
2996 if choice.has_key(cmd):
2996 return choice[cmd]
2997 return choice[cmd]
2997
2998
2998 if len(choice) > 1:
2999 if len(choice) > 1:
2999 clist = choice.keys()
3000 clist = choice.keys()
3000 clist.sort()
3001 clist.sort()
3001 raise AmbiguousCommand(cmd, clist)
3002 raise AmbiguousCommand(cmd, clist)
3002
3003
3003 if choice:
3004 if choice:
3004 return choice.values()[0]
3005 return choice.values()[0]
3005
3006
3006 raise UnknownCommand(cmd)
3007 raise UnknownCommand(cmd)
3007
3008
3008 def catchterm(*args):
3009 def catchterm(*args):
3009 raise util.SignalInterrupt
3010 raise util.SignalInterrupt
3010
3011
3011 def run():
3012 def run():
3012 sys.exit(dispatch(sys.argv[1:]))
3013 sys.exit(dispatch(sys.argv[1:]))
3013
3014
3014 class ParseError(Exception):
3015 class ParseError(Exception):
3015 """Exception raised on errors in parsing the command line."""
3016 """Exception raised on errors in parsing the command line."""
3016
3017
3017 def parse(ui, args):
3018 def parse(ui, args):
3018 options = {}
3019 options = {}
3019 cmdoptions = {}
3020 cmdoptions = {}
3020
3021
3021 try:
3022 try:
3022 args = fancyopts.fancyopts(args, globalopts, options)
3023 args = fancyopts.fancyopts(args, globalopts, options)
3023 except fancyopts.getopt.GetoptError, inst:
3024 except fancyopts.getopt.GetoptError, inst:
3024 raise ParseError(None, inst)
3025 raise ParseError(None, inst)
3025
3026
3026 if args:
3027 if args:
3027 cmd, args = args[0], args[1:]
3028 cmd, args = args[0], args[1:]
3028 aliases, i = findcmd(ui, cmd)
3029 aliases, i = findcmd(ui, cmd)
3029 cmd = aliases[0]
3030 cmd = aliases[0]
3030 defaults = ui.config("defaults", cmd)
3031 defaults = ui.config("defaults", cmd)
3031 if defaults:
3032 if defaults:
3032 args = shlex.split(defaults) + args
3033 args = shlex.split(defaults) + args
3033 c = list(i[1])
3034 c = list(i[1])
3034 else:
3035 else:
3035 cmd = None
3036 cmd = None
3036 c = []
3037 c = []
3037
3038
3038 # combine global options into local
3039 # combine global options into local
3039 for o in globalopts:
3040 for o in globalopts:
3040 c.append((o[0], o[1], options[o[1]], o[3]))
3041 c.append((o[0], o[1], options[o[1]], o[3]))
3041
3042
3042 try:
3043 try:
3043 args = fancyopts.fancyopts(args, c, cmdoptions)
3044 args = fancyopts.fancyopts(args, c, cmdoptions)
3044 except fancyopts.getopt.GetoptError, inst:
3045 except fancyopts.getopt.GetoptError, inst:
3045 raise ParseError(cmd, inst)
3046 raise ParseError(cmd, inst)
3046
3047
3047 # separate global options back out
3048 # separate global options back out
3048 for o in globalopts:
3049 for o in globalopts:
3049 n = o[1]
3050 n = o[1]
3050 options[n] = cmdoptions[n]
3051 options[n] = cmdoptions[n]
3051 del cmdoptions[n]
3052 del cmdoptions[n]
3052
3053
3053 return (cmd, cmd and i[0] or None, args, options, cmdoptions)
3054 return (cmd, cmd and i[0] or None, args, options, cmdoptions)
3054
3055
3055 external = {}
3056 external = {}
3056
3057
3057 def findext(name):
3058 def findext(name):
3058 '''return module with given extension name'''
3059 '''return module with given extension name'''
3059 try:
3060 try:
3060 return sys.modules[external[name]]
3061 return sys.modules[external[name]]
3061 except KeyError:
3062 except KeyError:
3062 for k, v in external.iteritems():
3063 for k, v in external.iteritems():
3063 if k.endswith('.' + name) or k.endswith('/' + name) or v == name:
3064 if k.endswith('.' + name) or k.endswith('/' + name) or v == name:
3064 return sys.modules[v]
3065 return sys.modules[v]
3065 raise KeyError(name)
3066 raise KeyError(name)
3066
3067
3067 def load_extensions(ui):
3068 def load_extensions(ui):
3068 added = []
3069 added = []
3069 for ext_name, load_from_name in ui.extensions():
3070 for ext_name, load_from_name in ui.extensions():
3070 if ext_name in external:
3071 if ext_name in external:
3071 continue
3072 continue
3072 try:
3073 try:
3073 if load_from_name:
3074 if load_from_name:
3074 # the module will be loaded in sys.modules
3075 # the module will be loaded in sys.modules
3075 # choose an unique name so that it doesn't
3076 # choose an unique name so that it doesn't
3076 # conflicts with other modules
3077 # conflicts with other modules
3077 module_name = "hgext_%s" % ext_name.replace('.', '_')
3078 module_name = "hgext_%s" % ext_name.replace('.', '_')
3078 mod = imp.load_source(module_name, load_from_name)
3079 mod = imp.load_source(module_name, load_from_name)
3079 else:
3080 else:
3080 def importh(name):
3081 def importh(name):
3081 mod = __import__(name)
3082 mod = __import__(name)
3082 components = name.split('.')
3083 components = name.split('.')
3083 for comp in components[1:]:
3084 for comp in components[1:]:
3084 mod = getattr(mod, comp)
3085 mod = getattr(mod, comp)
3085 return mod
3086 return mod
3086 try:
3087 try:
3087 mod = importh("hgext.%s" % ext_name)
3088 mod = importh("hgext.%s" % ext_name)
3088 except ImportError:
3089 except ImportError:
3089 mod = importh(ext_name)
3090 mod = importh(ext_name)
3090 external[ext_name] = mod.__name__
3091 external[ext_name] = mod.__name__
3091 added.append((mod, ext_name))
3092 added.append((mod, ext_name))
3092 except (util.SignalInterrupt, KeyboardInterrupt):
3093 except (util.SignalInterrupt, KeyboardInterrupt):
3093 raise
3094 raise
3094 except Exception, inst:
3095 except Exception, inst:
3095 ui.warn(_("*** failed to import extension %s: %s\n") %
3096 ui.warn(_("*** failed to import extension %s: %s\n") %
3096 (ext_name, inst))
3097 (ext_name, inst))
3097 if ui.print_exc():
3098 if ui.print_exc():
3098 return 1
3099 return 1
3099
3100
3100 for mod, name in added:
3101 for mod, name in added:
3101 uisetup = getattr(mod, 'uisetup', None)
3102 uisetup = getattr(mod, 'uisetup', None)
3102 if uisetup:
3103 if uisetup:
3103 uisetup(ui)
3104 uisetup(ui)
3104 cmdtable = getattr(mod, 'cmdtable', {})
3105 cmdtable = getattr(mod, 'cmdtable', {})
3105 for t in cmdtable:
3106 for t in cmdtable:
3106 if t in table:
3107 if t in table:
3107 ui.warn(_("module %s overrides %s\n") % (name, t))
3108 ui.warn(_("module %s overrides %s\n") % (name, t))
3108 table.update(cmdtable)
3109 table.update(cmdtable)
3109
3110
3110 def parseconfig(config):
3111 def parseconfig(config):
3111 """parse the --config options from the command line"""
3112 """parse the --config options from the command line"""
3112 parsed = []
3113 parsed = []
3113 for cfg in config:
3114 for cfg in config:
3114 try:
3115 try:
3115 name, value = cfg.split('=', 1)
3116 name, value = cfg.split('=', 1)
3116 section, name = name.split('.', 1)
3117 section, name = name.split('.', 1)
3117 if not section or not name:
3118 if not section or not name:
3118 raise IndexError
3119 raise IndexError
3119 parsed.append((section, name, value))
3120 parsed.append((section, name, value))
3120 except (IndexError, ValueError):
3121 except (IndexError, ValueError):
3121 raise util.Abort(_('malformed --config option: %s') % cfg)
3122 raise util.Abort(_('malformed --config option: %s') % cfg)
3122 return parsed
3123 return parsed
3123
3124
3124 def dispatch(args):
3125 def dispatch(args):
3125 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
3126 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
3126 num = getattr(signal, name, None)
3127 num = getattr(signal, name, None)
3127 if num: signal.signal(num, catchterm)
3128 if num: signal.signal(num, catchterm)
3128
3129
3129 try:
3130 try:
3130 u = ui.ui(traceback='--traceback' in sys.argv[1:])
3131 u = ui.ui(traceback='--traceback' in sys.argv[1:])
3131 except util.Abort, inst:
3132 except util.Abort, inst:
3132 sys.stderr.write(_("abort: %s\n") % inst)
3133 sys.stderr.write(_("abort: %s\n") % inst)
3133 return -1
3134 return -1
3134
3135
3135 load_extensions(u)
3136 load_extensions(u)
3136 u.addreadhook(load_extensions)
3137 u.addreadhook(load_extensions)
3137
3138
3138 try:
3139 try:
3139 cmd, func, args, options, cmdoptions = parse(u, args)
3140 cmd, func, args, options, cmdoptions = parse(u, args)
3140 if options["encoding"]:
3141 if options["encoding"]:
3141 util._encoding = options["encoding"]
3142 util._encoding = options["encoding"]
3142 if options["encodingmode"]:
3143 if options["encodingmode"]:
3143 util._encodingmode = options["encodingmode"]
3144 util._encodingmode = options["encodingmode"]
3144 if options["time"]:
3145 if options["time"]:
3145 def get_times():
3146 def get_times():
3146 t = os.times()
3147 t = os.times()
3147 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
3148 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
3148 t = (t[0], t[1], t[2], t[3], time.clock())
3149 t = (t[0], t[1], t[2], t[3], time.clock())
3149 return t
3150 return t
3150 s = get_times()
3151 s = get_times()
3151 def print_time():
3152 def print_time():
3152 t = get_times()
3153 t = get_times()
3153 u.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
3154 u.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
3154 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
3155 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
3155 atexit.register(print_time)
3156 atexit.register(print_time)
3156
3157
3157 # enter the debugger before command execution
3158 # enter the debugger before command execution
3158 if options['debugger']:
3159 if options['debugger']:
3159 pdb.set_trace()
3160 pdb.set_trace()
3160
3161
3161 try:
3162 try:
3162 if options['cwd']:
3163 if options['cwd']:
3163 os.chdir(options['cwd'])
3164 os.chdir(options['cwd'])
3164
3165
3165 u.updateopts(options["verbose"], options["debug"], options["quiet"],
3166 u.updateopts(options["verbose"], options["debug"], options["quiet"],
3166 not options["noninteractive"], options["traceback"],
3167 not options["noninteractive"], options["traceback"],
3167 parseconfig(options["config"]))
3168 parseconfig(options["config"]))
3168
3169
3169 path = u.expandpath(options["repository"]) or ""
3170 path = u.expandpath(options["repository"]) or ""
3170 repo = path and hg.repository(u, path=path) or None
3171 repo = path and hg.repository(u, path=path) or None
3171 if repo and not repo.local():
3172 if repo and not repo.local():
3172 raise util.Abort(_("repository '%s' is not local") % path)
3173 raise util.Abort(_("repository '%s' is not local") % path)
3173
3174
3174 if options['help']:
3175 if options['help']:
3175 return help_(u, cmd, options['version'])
3176 return help_(u, cmd, options['version'])
3176 elif options['version']:
3177 elif options['version']:
3177 return version_(u)
3178 return version_(u)
3178 elif not cmd:
3179 elif not cmd:
3179 return help_(u, 'shortlist')
3180 return help_(u, 'shortlist')
3180
3181
3181 if cmd not in norepo.split():
3182 if cmd not in norepo.split():
3182 try:
3183 try:
3183 if not repo:
3184 if not repo:
3184 repo = hg.repository(u, path=path)
3185 repo = hg.repository(u, path=path)
3185 u = repo.ui
3186 u = repo.ui
3186 for name in external.itervalues():
3187 for name in external.itervalues():
3187 mod = sys.modules[name]
3188 mod = sys.modules[name]
3188 if hasattr(mod, 'reposetup'):
3189 if hasattr(mod, 'reposetup'):
3189 mod.reposetup(u, repo)
3190 mod.reposetup(u, repo)
3190 hg.repo_setup_hooks.append(mod.reposetup)
3191 hg.repo_setup_hooks.append(mod.reposetup)
3191 except hg.RepoError:
3192 except hg.RepoError:
3192 if cmd not in optionalrepo.split():
3193 if cmd not in optionalrepo.split():
3193 raise
3194 raise
3194 d = lambda: func(u, repo, *args, **cmdoptions)
3195 d = lambda: func(u, repo, *args, **cmdoptions)
3195 else:
3196 else:
3196 d = lambda: func(u, *args, **cmdoptions)
3197 d = lambda: func(u, *args, **cmdoptions)
3197
3198
3198 try:
3199 try:
3199 if options['profile']:
3200 if options['profile']:
3200 import hotshot, hotshot.stats
3201 import hotshot, hotshot.stats
3201 prof = hotshot.Profile("hg.prof")
3202 prof = hotshot.Profile("hg.prof")
3202 try:
3203 try:
3203 try:
3204 try:
3204 return prof.runcall(d)
3205 return prof.runcall(d)
3205 except:
3206 except:
3206 try:
3207 try:
3207 u.warn(_('exception raised - generating '
3208 u.warn(_('exception raised - generating '
3208 'profile anyway\n'))
3209 'profile anyway\n'))
3209 except:
3210 except:
3210 pass
3211 pass
3211 raise
3212 raise
3212 finally:
3213 finally:
3213 prof.close()
3214 prof.close()
3214 stats = hotshot.stats.load("hg.prof")
3215 stats = hotshot.stats.load("hg.prof")
3215 stats.strip_dirs()
3216 stats.strip_dirs()
3216 stats.sort_stats('time', 'calls')
3217 stats.sort_stats('time', 'calls')
3217 stats.print_stats(40)
3218 stats.print_stats(40)
3218 elif options['lsprof']:
3219 elif options['lsprof']:
3219 try:
3220 try:
3220 from mercurial import lsprof
3221 from mercurial import lsprof
3221 except ImportError:
3222 except ImportError:
3222 raise util.Abort(_(
3223 raise util.Abort(_(
3223 'lsprof not available - install from '
3224 'lsprof not available - install from '
3224 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
3225 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
3225 p = lsprof.Profiler()
3226 p = lsprof.Profiler()
3226 p.enable(subcalls=True)
3227 p.enable(subcalls=True)
3227 try:
3228 try:
3228 return d()
3229 return d()
3229 finally:
3230 finally:
3230 p.disable()
3231 p.disable()
3231 stats = lsprof.Stats(p.getstats())
3232 stats = lsprof.Stats(p.getstats())
3232 stats.sort()
3233 stats.sort()
3233 stats.pprint(top=10, file=sys.stderr, climit=5)
3234 stats.pprint(top=10, file=sys.stderr, climit=5)
3234 else:
3235 else:
3235 return d()
3236 return d()
3236 finally:
3237 finally:
3237 u.flush()
3238 u.flush()
3238 except:
3239 except:
3239 # enter the debugger when we hit an exception
3240 # enter the debugger when we hit an exception
3240 if options['debugger']:
3241 if options['debugger']:
3241 pdb.post_mortem(sys.exc_info()[2])
3242 pdb.post_mortem(sys.exc_info()[2])
3242 u.print_exc()
3243 u.print_exc()
3243 raise
3244 raise
3244 except ParseError, inst:
3245 except ParseError, inst:
3245 if inst.args[0]:
3246 if inst.args[0]:
3246 u.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
3247 u.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
3247 help_(u, inst.args[0])
3248 help_(u, inst.args[0])
3248 else:
3249 else:
3249 u.warn(_("hg: %s\n") % inst.args[1])
3250 u.warn(_("hg: %s\n") % inst.args[1])
3250 help_(u, 'shortlist')
3251 help_(u, 'shortlist')
3251 except AmbiguousCommand, inst:
3252 except AmbiguousCommand, inst:
3252 u.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
3253 u.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
3253 (inst.args[0], " ".join(inst.args[1])))
3254 (inst.args[0], " ".join(inst.args[1])))
3254 except UnknownCommand, inst:
3255 except UnknownCommand, inst:
3255 u.warn(_("hg: unknown command '%s'\n") % inst.args[0])
3256 u.warn(_("hg: unknown command '%s'\n") % inst.args[0])
3256 help_(u, 'shortlist')
3257 help_(u, 'shortlist')
3257 except hg.RepoError, inst:
3258 except hg.RepoError, inst:
3258 u.warn(_("abort: %s!\n") % inst)
3259 u.warn(_("abort: %s!\n") % inst)
3259 except lock.LockHeld, inst:
3260 except lock.LockHeld, inst:
3260 if inst.errno == errno.ETIMEDOUT:
3261 if inst.errno == errno.ETIMEDOUT:
3261 reason = _('timed out waiting for lock held by %s') % inst.locker
3262 reason = _('timed out waiting for lock held by %s') % inst.locker
3262 else:
3263 else:
3263 reason = _('lock held by %s') % inst.locker
3264 reason = _('lock held by %s') % inst.locker
3264 u.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
3265 u.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
3265 except lock.LockUnavailable, inst:
3266 except lock.LockUnavailable, inst:
3266 u.warn(_("abort: could not lock %s: %s\n") %
3267 u.warn(_("abort: could not lock %s: %s\n") %
3267 (inst.desc or inst.filename, inst.strerror))
3268 (inst.desc or inst.filename, inst.strerror))
3268 except revlog.RevlogError, inst:
3269 except revlog.RevlogError, inst:
3269 u.warn(_("abort: %s!\n") % inst)
3270 u.warn(_("abort: %s!\n") % inst)
3270 except util.SignalInterrupt:
3271 except util.SignalInterrupt:
3271 u.warn(_("killed!\n"))
3272 u.warn(_("killed!\n"))
3272 except KeyboardInterrupt:
3273 except KeyboardInterrupt:
3273 try:
3274 try:
3274 u.warn(_("interrupted!\n"))
3275 u.warn(_("interrupted!\n"))
3275 except IOError, inst:
3276 except IOError, inst:
3276 if inst.errno == errno.EPIPE:
3277 if inst.errno == errno.EPIPE:
3277 if u.debugflag:
3278 if u.debugflag:
3278 u.warn(_("\nbroken pipe\n"))
3279 u.warn(_("\nbroken pipe\n"))
3279 else:
3280 else:
3280 raise
3281 raise
3281 except socket.error, inst:
3282 except socket.error, inst:
3282 u.warn(_("abort: %s\n") % inst[1])
3283 u.warn(_("abort: %s\n") % inst[1])
3283 except IOError, inst:
3284 except IOError, inst:
3284 if hasattr(inst, "code"):
3285 if hasattr(inst, "code"):
3285 u.warn(_("abort: %s\n") % inst)
3286 u.warn(_("abort: %s\n") % inst)
3286 elif hasattr(inst, "reason"):
3287 elif hasattr(inst, "reason"):
3287 try: # usually it is in the form (errno, strerror)
3288 try: # usually it is in the form (errno, strerror)
3288 reason = inst.reason.args[1]
3289 reason = inst.reason.args[1]
3289 except: # it might be anything, for example a string
3290 except: # it might be anything, for example a string
3290 reason = inst.reason
3291 reason = inst.reason
3291 u.warn(_("abort: error: %s\n") % reason)
3292 u.warn(_("abort: error: %s\n") % reason)
3292 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
3293 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
3293 if u.debugflag:
3294 if u.debugflag:
3294 u.warn(_("broken pipe\n"))
3295 u.warn(_("broken pipe\n"))
3295 elif getattr(inst, "strerror", None):
3296 elif getattr(inst, "strerror", None):
3296 if getattr(inst, "filename", None):
3297 if getattr(inst, "filename", None):
3297 u.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
3298 u.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
3298 else:
3299 else:
3299 u.warn(_("abort: %s\n") % inst.strerror)
3300 u.warn(_("abort: %s\n") % inst.strerror)
3300 else:
3301 else:
3301 raise
3302 raise
3302 except OSError, inst:
3303 except OSError, inst:
3303 if getattr(inst, "filename", None):
3304 if getattr(inst, "filename", None):
3304 u.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
3305 u.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
3305 else:
3306 else:
3306 u.warn(_("abort: %s\n") % inst.strerror)
3307 u.warn(_("abort: %s\n") % inst.strerror)
3307 except util.UnexpectedOutput, inst:
3308 except util.UnexpectedOutput, inst:
3308 u.warn(_("abort: %s") % inst[0])
3309 u.warn(_("abort: %s") % inst[0])
3309 if not isinstance(inst[1], basestring):
3310 if not isinstance(inst[1], basestring):
3310 u.warn(" %r\n" % (inst[1],))
3311 u.warn(" %r\n" % (inst[1],))
3311 elif not inst[1]:
3312 elif not inst[1]:
3312 u.warn(_(" empty string\n"))
3313 u.warn(_(" empty string\n"))
3313 else:
3314 else:
3314 u.warn("\n%r\n" % util.ellipsis(inst[1]))
3315 u.warn("\n%r\n" % util.ellipsis(inst[1]))
3315 except util.Abort, inst:
3316 except util.Abort, inst:
3316 u.warn(_("abort: %s\n") % inst)
3317 u.warn(_("abort: %s\n") % inst)
3317 except TypeError, inst:
3318 except TypeError, inst:
3318 # was this an argument error?
3319 # was this an argument error?
3319 tb = traceback.extract_tb(sys.exc_info()[2])
3320 tb = traceback.extract_tb(sys.exc_info()[2])
3320 if len(tb) > 2: # no
3321 if len(tb) > 2: # no
3321 raise
3322 raise
3322 u.debug(inst, "\n")
3323 u.debug(inst, "\n")
3323 u.warn(_("%s: invalid arguments\n") % cmd)
3324 u.warn(_("%s: invalid arguments\n") % cmd)
3324 help_(u, cmd)
3325 help_(u, cmd)
3325 except SystemExit, inst:
3326 except SystemExit, inst:
3326 # Commands shouldn't sys.exit directly, but give a return code.
3327 # Commands shouldn't sys.exit directly, but give a return code.
3327 # Just in case catch this and and pass exit code to caller.
3328 # Just in case catch this and and pass exit code to caller.
3328 return inst.code
3329 return inst.code
3329 except:
3330 except:
3330 u.warn(_("** unknown exception encountered, details follow\n"))
3331 u.warn(_("** unknown exception encountered, details follow\n"))
3331 u.warn(_("** report bug details to "
3332 u.warn(_("** report bug details to "
3332 "http://www.selenic.com/mercurial/bts\n"))
3333 "http://www.selenic.com/mercurial/bts\n"))
3333 u.warn(_("** or mercurial@selenic.com\n"))
3334 u.warn(_("** or mercurial@selenic.com\n"))
3334 u.warn(_("** Mercurial Distributed SCM (version %s)\n")
3335 u.warn(_("** Mercurial Distributed SCM (version %s)\n")
3335 % version.get_version())
3336 % version.get_version())
3336 raise
3337 raise
3337
3338
3338 return -1
3339 return -1
@@ -1,1338 +1,1340 b''
1 """
1 """
2 util.py - Mercurial utility functions and platform specfic implementations
2 util.py - Mercurial utility functions and platform specfic implementations
3
3
4 Copyright 2005 K. Thananchayan <thananck@yahoo.com>
4 Copyright 2005 K. Thananchayan <thananck@yahoo.com>
5 Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
5 Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
6 Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
6 Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
7
7
8 This software may be used and distributed according to the terms
8 This software may be used and distributed according to the terms
9 of the GNU General Public License, incorporated herein by reference.
9 of the GNU General Public License, incorporated herein by reference.
10
10
11 This contains helper routines that are independent of the SCM core and hide
11 This contains helper routines that are independent of the SCM core and hide
12 platform-specific details from the core.
12 platform-specific details from the core.
13 """
13 """
14
14
15 from i18n import gettext as _
15 from i18n import gettext as _
16 from demandload import *
16 from demandload import *
17 demandload(globals(), "cStringIO errno getpass popen2 re shutil sys tempfile")
17 demandload(globals(), "cStringIO errno getpass popen2 re shutil sys tempfile")
18 demandload(globals(), "os threading time calendar ConfigParser locale glob")
18 demandload(globals(), "os threading time calendar ConfigParser locale glob")
19
19
20 _encoding = os.environ.get("HGENCODING") or locale.getpreferredencoding() \
20 _encoding = os.environ.get("HGENCODING") or locale.getpreferredencoding() \
21 or "ascii"
21 or "ascii"
22 _encodingmode = os.environ.get("HGENCODINGMODE", "strict")
22 _encodingmode = os.environ.get("HGENCODINGMODE", "strict")
23 _fallbackencoding = 'ISO-8859-1'
23 _fallbackencoding = 'ISO-8859-1'
24
24
25 def tolocal(s):
25 def tolocal(s):
26 """
26 """
27 Convert a string from internal UTF-8 to local encoding
27 Convert a string from internal UTF-8 to local encoding
28
28
29 All internal strings should be UTF-8 but some repos before the
29 All internal strings should be UTF-8 but some repos before the
30 implementation of locale support may contain latin1 or possibly
30 implementation of locale support may contain latin1 or possibly
31 other character sets. We attempt to decode everything strictly
31 other character sets. We attempt to decode everything strictly
32 using UTF-8, then Latin-1, and failing that, we use UTF-8 and
32 using UTF-8, then Latin-1, and failing that, we use UTF-8 and
33 replace unknown characters.
33 replace unknown characters.
34 """
34 """
35 for e in ('UTF-8', _fallbackencoding):
35 for e in ('UTF-8', _fallbackencoding):
36 try:
36 try:
37 u = s.decode(e) # attempt strict decoding
37 u = s.decode(e) # attempt strict decoding
38 return u.encode(_encoding, "replace")
38 return u.encode(_encoding, "replace")
39 except LookupError, k:
39 except LookupError, k:
40 raise Abort(_("%s, please check your locale settings") % k)
40 raise Abort(_("%s, please check your locale settings") % k)
41 except UnicodeDecodeError:
41 except UnicodeDecodeError:
42 pass
42 pass
43 u = s.decode("utf-8", "replace") # last ditch
43 u = s.decode("utf-8", "replace") # last ditch
44 return u.encode(_encoding, "replace")
44 return u.encode(_encoding, "replace")
45
45
46 def fromlocal(s):
46 def fromlocal(s):
47 """
47 """
48 Convert a string from the local character encoding to UTF-8
48 Convert a string from the local character encoding to UTF-8
49
49
50 We attempt to decode strings using the encoding mode set by
50 We attempt to decode strings using the encoding mode set by
51 HG_ENCODINGMODE, which defaults to 'strict'. In this mode, unknown
51 HG_ENCODINGMODE, which defaults to 'strict'. In this mode, unknown
52 characters will cause an error message. Other modes include
52 characters will cause an error message. Other modes include
53 'replace', which replaces unknown characters with a special
53 'replace', which replaces unknown characters with a special
54 Unicode character, and 'ignore', which drops the character.
54 Unicode character, and 'ignore', which drops the character.
55 """
55 """
56 try:
56 try:
57 return s.decode(_encoding, _encodingmode).encode("utf-8")
57 return s.decode(_encoding, _encodingmode).encode("utf-8")
58 except UnicodeDecodeError, inst:
58 except UnicodeDecodeError, inst:
59 sub = s[max(0, inst.start-10):inst.start+10]
59 sub = s[max(0, inst.start-10):inst.start+10]
60 raise Abort("decoding near '%s': %s!" % (sub, inst))
60 raise Abort("decoding near '%s': %s!" % (sub, inst))
61 except LookupError, k:
61 except LookupError, k:
62 raise Abort(_("%s, please check your locale settings") % k)
62 raise Abort(_("%s, please check your locale settings") % k)
63
63
64 def locallen(s):
64 def locallen(s):
65 """Find the length in characters of a local string"""
65 """Find the length in characters of a local string"""
66 return len(s.decode(_encoding, "replace"))
66 return len(s.decode(_encoding, "replace"))
67
67
68 def localsub(s, a, b=None):
68 def localsub(s, a, b=None):
69 try:
69 try:
70 u = s.decode(_encoding, _encodingmode)
70 u = s.decode(_encoding, _encodingmode)
71 if b is not None:
71 if b is not None:
72 u = u[a:b]
72 u = u[a:b]
73 else:
73 else:
74 u = u[:a]
74 u = u[:a]
75 return u.encode(_encoding, _encodingmode)
75 return u.encode(_encoding, _encodingmode)
76 except UnicodeDecodeError, inst:
76 except UnicodeDecodeError, inst:
77 sub = s[max(0, inst.start-10), inst.start+10]
77 sub = s[max(0, inst.start-10), inst.start+10]
78 raise Abort(_("decoding near '%s': %s!\n") % (sub, inst))
78 raise Abort(_("decoding near '%s': %s!\n") % (sub, inst))
79
79
80 # used by parsedate
80 # used by parsedate
81 defaultdateformats = (
81 defaultdateformats = (
82 '%Y-%m-%d %H:%M:%S',
82 '%Y-%m-%d %H:%M:%S',
83 '%Y-%m-%d %I:%M:%S%p',
83 '%Y-%m-%d %I:%M:%S%p',
84 '%Y-%m-%d %H:%M',
84 '%Y-%m-%d %H:%M',
85 '%Y-%m-%d %I:%M%p',
85 '%Y-%m-%d %I:%M%p',
86 '%Y-%m-%d',
86 '%Y-%m-%d',
87 '%m-%d',
87 '%m-%d',
88 '%m/%d',
88 '%m/%d',
89 '%m/%d/%y',
89 '%m/%d/%y',
90 '%m/%d/%Y',
90 '%m/%d/%Y',
91 '%a %b %d %H:%M:%S %Y',
91 '%a %b %d %H:%M:%S %Y',
92 '%a %b %d %I:%M:%S%p %Y',
92 '%a %b %d %I:%M:%S%p %Y',
93 '%b %d %H:%M:%S %Y',
93 '%b %d %H:%M:%S %Y',
94 '%b %d %I:%M:%S%p %Y',
94 '%b %d %I:%M:%S%p %Y',
95 '%b %d %H:%M:%S',
95 '%b %d %H:%M:%S',
96 '%b %d %I:%M:%S%p',
96 '%b %d %I:%M:%S%p',
97 '%b %d %H:%M',
97 '%b %d %H:%M',
98 '%b %d %I:%M%p',
98 '%b %d %I:%M%p',
99 '%b %d %Y',
99 '%b %d %Y',
100 '%b %d',
100 '%b %d',
101 '%H:%M:%S',
101 '%H:%M:%S',
102 '%I:%M:%SP',
102 '%I:%M:%SP',
103 '%H:%M',
103 '%H:%M',
104 '%I:%M%p',
104 '%I:%M%p',
105 )
105 )
106
106
107 extendeddateformats = defaultdateformats + (
107 extendeddateformats = defaultdateformats + (
108 "%Y",
108 "%Y",
109 "%Y-%m",
109 "%Y-%m",
110 "%b",
110 "%b",
111 "%b %Y",
111 "%b %Y",
112 )
112 )
113
113
114 class SignalInterrupt(Exception):
114 class SignalInterrupt(Exception):
115 """Exception raised on SIGTERM and SIGHUP."""
115 """Exception raised on SIGTERM and SIGHUP."""
116
116
117 # like SafeConfigParser but with case-sensitive keys
117 # like SafeConfigParser but with case-sensitive keys
118 class configparser(ConfigParser.SafeConfigParser):
118 class configparser(ConfigParser.SafeConfigParser):
119 def optionxform(self, optionstr):
119 def optionxform(self, optionstr):
120 return optionstr
120 return optionstr
121
121
122 def cachefunc(func):
122 def cachefunc(func):
123 '''cache the result of function calls'''
123 '''cache the result of function calls'''
124 # XXX doesn't handle keywords args
124 # XXX doesn't handle keywords args
125 cache = {}
125 cache = {}
126 if func.func_code.co_argcount == 1:
126 if func.func_code.co_argcount == 1:
127 # we gain a small amount of time because
127 # we gain a small amount of time because
128 # we don't need to pack/unpack the list
128 # we don't need to pack/unpack the list
129 def f(arg):
129 def f(arg):
130 if arg not in cache:
130 if arg not in cache:
131 cache[arg] = func(arg)
131 cache[arg] = func(arg)
132 return cache[arg]
132 return cache[arg]
133 else:
133 else:
134 def f(*args):
134 def f(*args):
135 if args not in cache:
135 if args not in cache:
136 cache[args] = func(*args)
136 cache[args] = func(*args)
137 return cache[args]
137 return cache[args]
138
138
139 return f
139 return f
140
140
141 def pipefilter(s, cmd):
141 def pipefilter(s, cmd):
142 '''filter string S through command CMD, returning its output'''
142 '''filter string S through command CMD, returning its output'''
143 (pout, pin) = popen2.popen2(cmd, -1, 'b')
143 (pout, pin) = popen2.popen2(cmd, -1, 'b')
144 def writer():
144 def writer():
145 try:
145 try:
146 pin.write(s)
146 pin.write(s)
147 pin.close()
147 pin.close()
148 except IOError, inst:
148 except IOError, inst:
149 if inst.errno != errno.EPIPE:
149 if inst.errno != errno.EPIPE:
150 raise
150 raise
151
151
152 # we should use select instead on UNIX, but this will work on most
152 # we should use select instead on UNIX, but this will work on most
153 # systems, including Windows
153 # systems, including Windows
154 w = threading.Thread(target=writer)
154 w = threading.Thread(target=writer)
155 w.start()
155 w.start()
156 f = pout.read()
156 f = pout.read()
157 pout.close()
157 pout.close()
158 w.join()
158 w.join()
159 return f
159 return f
160
160
161 def tempfilter(s, cmd):
161 def tempfilter(s, cmd):
162 '''filter string S through a pair of temporary files with CMD.
162 '''filter string S through a pair of temporary files with CMD.
163 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,
164 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
165 the temporary files generated.'''
165 the temporary files generated.'''
166 inname, outname = None, None
166 inname, outname = None, None
167 try:
167 try:
168 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
168 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
169 fp = os.fdopen(infd, 'wb')
169 fp = os.fdopen(infd, 'wb')
170 fp.write(s)
170 fp.write(s)
171 fp.close()
171 fp.close()
172 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
172 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
173 os.close(outfd)
173 os.close(outfd)
174 cmd = cmd.replace('INFILE', inname)
174 cmd = cmd.replace('INFILE', inname)
175 cmd = cmd.replace('OUTFILE', outname)
175 cmd = cmd.replace('OUTFILE', outname)
176 code = os.system(cmd)
176 code = os.system(cmd)
177 if code: raise Abort(_("command '%s' failed: %s") %
177 if code: raise Abort(_("command '%s' failed: %s") %
178 (cmd, explain_exit(code)))
178 (cmd, explain_exit(code)))
179 return open(outname, 'rb').read()
179 return open(outname, 'rb').read()
180 finally:
180 finally:
181 try:
181 try:
182 if inname: os.unlink(inname)
182 if inname: os.unlink(inname)
183 except: pass
183 except: pass
184 try:
184 try:
185 if outname: os.unlink(outname)
185 if outname: os.unlink(outname)
186 except: pass
186 except: pass
187
187
188 filtertable = {
188 filtertable = {
189 'tempfile:': tempfilter,
189 'tempfile:': tempfilter,
190 'pipe:': pipefilter,
190 'pipe:': pipefilter,
191 }
191 }
192
192
193 def filter(s, cmd):
193 def filter(s, cmd):
194 "filter a string through a command that transforms its input to its output"
194 "filter a string through a command that transforms its input to its output"
195 for name, fn in filtertable.iteritems():
195 for name, fn in filtertable.iteritems():
196 if cmd.startswith(name):
196 if cmd.startswith(name):
197 return fn(s, cmd[len(name):].lstrip())
197 return fn(s, cmd[len(name):].lstrip())
198 return pipefilter(s, cmd)
198 return pipefilter(s, cmd)
199
199
200 def find_in_path(name, path, default=None):
200 def find_in_path(name, path, default=None):
201 '''find name in search path. path can be string (will be split
201 '''find name in search path. path can be string (will be split
202 with os.pathsep), or iterable thing that returns strings. if name
202 with os.pathsep), or iterable thing that returns strings. if name
203 found, return path to name. else return default.'''
203 found, return path to name. else return default.'''
204 if isinstance(path, str):
204 if isinstance(path, str):
205 path = path.split(os.pathsep)
205 path = path.split(os.pathsep)
206 for p in path:
206 for p in path:
207 p_name = os.path.join(p, name)
207 p_name = os.path.join(p, name)
208 if os.path.exists(p_name):
208 if os.path.exists(p_name):
209 return p_name
209 return p_name
210 return default
210 return default
211
211
212 def binary(s):
212 def binary(s):
213 """return true if a string is binary data using diff's heuristic"""
213 """return true if a string is binary data using diff's heuristic"""
214 if s and '\0' in s[:4096]:
214 if s and '\0' in s[:4096]:
215 return True
215 return True
216 return False
216 return False
217
217
218 def unique(g):
218 def unique(g):
219 """return the uniq elements of iterable g"""
219 """return the uniq elements of iterable g"""
220 seen = {}
220 seen = {}
221 l = []
221 l = []
222 for f in g:
222 for f in g:
223 if f not in seen:
223 if f not in seen:
224 seen[f] = 1
224 seen[f] = 1
225 l.append(f)
225 l.append(f)
226 return l
226 return l
227
227
228 class Abort(Exception):
228 class Abort(Exception):
229 """Raised if a command needs to print an error and exit."""
229 """Raised if a command needs to print an error and exit."""
230
230
231 class UnexpectedOutput(Abort):
231 class UnexpectedOutput(Abort):
232 """Raised to print an error with part of output and exit."""
232 """Raised to print an error with part of output and exit."""
233
233
234 def always(fn): return True
234 def always(fn): return True
235 def never(fn): return False
235 def never(fn): return False
236
236
237 def expand_glob(pats):
237 def expand_glob(pats):
238 '''On Windows, expand the implicit globs in a list of patterns'''
238 '''On Windows, expand the implicit globs in a list of patterns'''
239 if os.name != 'nt':
239 if os.name != 'nt':
240 return list(pats)
240 return list(pats)
241 ret = []
241 ret = []
242 for p in pats:
242 for p in pats:
243 kind, name = patkind(p, None)
243 kind, name = patkind(p, None)
244 if kind is None:
244 if kind is None:
245 globbed = glob.glob(name)
245 globbed = glob.glob(name)
246 if globbed:
246 if globbed:
247 ret.extend(globbed)
247 ret.extend(globbed)
248 continue
248 continue
249 # if we couldn't expand the glob, just keep it around
249 # if we couldn't expand the glob, just keep it around
250 ret.append(p)
250 ret.append(p)
251 return ret
251 return ret
252
252
253 def patkind(name, dflt_pat='glob'):
253 def patkind(name, dflt_pat='glob'):
254 """Split a string into an optional pattern kind prefix and the
254 """Split a string into an optional pattern kind prefix and the
255 actual pattern."""
255 actual pattern."""
256 for prefix in 're', 'glob', 'path', 'relglob', 'relpath', 'relre':
256 for prefix in 're', 'glob', 'path', 'relglob', 'relpath', 'relre':
257 if name.startswith(prefix + ':'): return name.split(':', 1)
257 if name.startswith(prefix + ':'): return name.split(':', 1)
258 return dflt_pat, name
258 return dflt_pat, name
259
259
260 def globre(pat, head='^', tail='$'):
260 def globre(pat, head='^', tail='$'):
261 "convert a glob pattern into a regexp"
261 "convert a glob pattern into a regexp"
262 i, n = 0, len(pat)
262 i, n = 0, len(pat)
263 res = ''
263 res = ''
264 group = False
264 group = False
265 def peek(): return i < n and pat[i]
265 def peek(): return i < n and pat[i]
266 while i < n:
266 while i < n:
267 c = pat[i]
267 c = pat[i]
268 i = i+1
268 i = i+1
269 if c == '*':
269 if c == '*':
270 if peek() == '*':
270 if peek() == '*':
271 i += 1
271 i += 1
272 res += '.*'
272 res += '.*'
273 else:
273 else:
274 res += '[^/]*'
274 res += '[^/]*'
275 elif c == '?':
275 elif c == '?':
276 res += '.'
276 res += '.'
277 elif c == '[':
277 elif c == '[':
278 j = i
278 j = i
279 if j < n and pat[j] in '!]':
279 if j < n and pat[j] in '!]':
280 j += 1
280 j += 1
281 while j < n and pat[j] != ']':
281 while j < n and pat[j] != ']':
282 j += 1
282 j += 1
283 if j >= n:
283 if j >= n:
284 res += '\\['
284 res += '\\['
285 else:
285 else:
286 stuff = pat[i:j].replace('\\','\\\\')
286 stuff = pat[i:j].replace('\\','\\\\')
287 i = j + 1
287 i = j + 1
288 if stuff[0] == '!':
288 if stuff[0] == '!':
289 stuff = '^' + stuff[1:]
289 stuff = '^' + stuff[1:]
290 elif stuff[0] == '^':
290 elif stuff[0] == '^':
291 stuff = '\\' + stuff
291 stuff = '\\' + stuff
292 res = '%s[%s]' % (res, stuff)
292 res = '%s[%s]' % (res, stuff)
293 elif c == '{':
293 elif c == '{':
294 group = True
294 group = True
295 res += '(?:'
295 res += '(?:'
296 elif c == '}' and group:
296 elif c == '}' and group:
297 res += ')'
297 res += ')'
298 group = False
298 group = False
299 elif c == ',' and group:
299 elif c == ',' and group:
300 res += '|'
300 res += '|'
301 elif c == '\\':
301 elif c == '\\':
302 p = peek()
302 p = peek()
303 if p:
303 if p:
304 i += 1
304 i += 1
305 res += re.escape(p)
305 res += re.escape(p)
306 else:
306 else:
307 res += re.escape(c)
307 res += re.escape(c)
308 else:
308 else:
309 res += re.escape(c)
309 res += re.escape(c)
310 return head + res + tail
310 return head + res + tail
311
311
312 _globchars = {'[': 1, '{': 1, '*': 1, '?': 1}
312 _globchars = {'[': 1, '{': 1, '*': 1, '?': 1}
313
313
314 def pathto(n1, n2):
314 def pathto(n1, n2):
315 '''return the relative path from one place to another.
315 '''return the relative path from one place to another.
316 n1 should use os.sep to separate directories
316 n1 should use os.sep to separate directories
317 n2 should use "/" to separate directories
317 n2 should use "/" to separate directories
318 returns an os.sep-separated path.
318 returns an os.sep-separated path.
319 '''
319 '''
320 if not n1: return localpath(n2)
320 if not n1: return localpath(n2)
321 a, b = n1.split(os.sep), n2.split('/')
321 a, b = n1.split(os.sep), n2.split('/')
322 a.reverse()
322 a.reverse()
323 b.reverse()
323 b.reverse()
324 while a and b and a[-1] == b[-1]:
324 while a and b and a[-1] == b[-1]:
325 a.pop()
325 a.pop()
326 b.pop()
326 b.pop()
327 b.reverse()
327 b.reverse()
328 return os.sep.join((['..'] * len(a)) + b)
328 return os.sep.join((['..'] * len(a)) + b)
329
329
330 def canonpath(root, cwd, myname):
330 def canonpath(root, cwd, myname):
331 """return the canonical path of myname, given cwd and root"""
331 """return the canonical path of myname, given cwd and root"""
332 if root == os.sep:
332 if root == os.sep:
333 rootsep = os.sep
333 rootsep = os.sep
334 elif root.endswith(os.sep):
334 elif root.endswith(os.sep):
335 rootsep = root
335 rootsep = root
336 else:
336 else:
337 rootsep = root + os.sep
337 rootsep = root + os.sep
338 name = myname
338 name = myname
339 if not os.path.isabs(name):
339 if not os.path.isabs(name):
340 name = os.path.join(root, cwd, name)
340 name = os.path.join(root, cwd, name)
341 name = os.path.normpath(name)
341 name = os.path.normpath(name)
342 if name != rootsep and name.startswith(rootsep):
342 if name != rootsep and name.startswith(rootsep):
343 name = name[len(rootsep):]
343 name = name[len(rootsep):]
344 audit_path(name)
344 audit_path(name)
345 return pconvert(name)
345 return pconvert(name)
346 elif name == root:
346 elif name == root:
347 return ''
347 return ''
348 else:
348 else:
349 # Determine whether `name' is in the hierarchy at or beneath `root',
349 # Determine whether `name' is in the hierarchy at or beneath `root',
350 # by iterating name=dirname(name) until that causes no change (can't
350 # by iterating name=dirname(name) until that causes no change (can't
351 # check name == '/', because that doesn't work on windows). For each
351 # check name == '/', because that doesn't work on windows). For each
352 # `name', compare dev/inode numbers. If they match, the list `rel'
352 # `name', compare dev/inode numbers. If they match, the list `rel'
353 # holds the reversed list of components making up the relative file
353 # holds the reversed list of components making up the relative file
354 # name we want.
354 # name we want.
355 root_st = os.stat(root)
355 root_st = os.stat(root)
356 rel = []
356 rel = []
357 while True:
357 while True:
358 try:
358 try:
359 name_st = os.stat(name)
359 name_st = os.stat(name)
360 except OSError:
360 except OSError:
361 break
361 break
362 if samestat(name_st, root_st):
362 if samestat(name_st, root_st):
363 rel.reverse()
363 rel.reverse()
364 name = os.path.join(*rel)
364 name = os.path.join(*rel)
365 audit_path(name)
365 audit_path(name)
366 return pconvert(name)
366 return pconvert(name)
367 dirname, basename = os.path.split(name)
367 dirname, basename = os.path.split(name)
368 rel.append(basename)
368 rel.append(basename)
369 if dirname == name:
369 if dirname == name:
370 break
370 break
371 name = dirname
371 name = dirname
372
372
373 raise Abort('%s not under root' % myname)
373 raise Abort('%s not under root' % myname)
374
374
375 def matcher(canonroot, cwd='', names=['.'], inc=[], exc=[], head='', src=None):
375 def matcher(canonroot, cwd='', names=['.'], inc=[], exc=[], head='', src=None):
376 return _matcher(canonroot, cwd, names, inc, exc, head, 'glob', src)
376 return _matcher(canonroot, cwd, names, inc, exc, head, 'glob', src)
377
377
378 def cmdmatcher(canonroot, cwd='', names=['.'], inc=[], exc=[], head='', src=None):
378 def cmdmatcher(canonroot, cwd='', names=['.'], inc=[], exc=[], head='',
379 names = expand_glob(names)
379 src=None, globbed=False):
380 if not globbed:
381 names = expand_glob(names)
380 return _matcher(canonroot, cwd, names, inc, exc, head, 'relpath', src)
382 return _matcher(canonroot, cwd, names, inc, exc, head, 'relpath', src)
381
383
382 def _matcher(canonroot, cwd, names, inc, exc, head, dflt_pat, src):
384 def _matcher(canonroot, cwd, names, inc, exc, head, dflt_pat, src):
383 """build a function to match a set of file patterns
385 """build a function to match a set of file patterns
384
386
385 arguments:
387 arguments:
386 canonroot - the canonical root of the tree you're matching against
388 canonroot - the canonical root of the tree you're matching against
387 cwd - the current working directory, if relevant
389 cwd - the current working directory, if relevant
388 names - patterns to find
390 names - patterns to find
389 inc - patterns to include
391 inc - patterns to include
390 exc - patterns to exclude
392 exc - patterns to exclude
391 head - a regex to prepend to patterns to control whether a match is rooted
393 head - a regex to prepend to patterns to control whether a match is rooted
392
394
393 a pattern is one of:
395 a pattern is one of:
394 'glob:<rooted glob>'
396 'glob:<rooted glob>'
395 're:<rooted regexp>'
397 're:<rooted regexp>'
396 'path:<rooted path>'
398 'path:<rooted path>'
397 'relglob:<relative glob>'
399 'relglob:<relative glob>'
398 'relpath:<relative path>'
400 'relpath:<relative path>'
399 'relre:<relative regexp>'
401 'relre:<relative regexp>'
400 '<rooted path or regexp>'
402 '<rooted path or regexp>'
401
403
402 returns:
404 returns:
403 a 3-tuple containing
405 a 3-tuple containing
404 - list of explicit non-pattern names passed in
406 - list of explicit non-pattern names passed in
405 - a bool match(filename) function
407 - a bool match(filename) function
406 - a bool indicating if any patterns were passed in
408 - a bool indicating if any patterns were passed in
407
409
408 todo:
410 todo:
409 make head regex a rooted bool
411 make head regex a rooted bool
410 """
412 """
411
413
412 def contains_glob(name):
414 def contains_glob(name):
413 for c in name:
415 for c in name:
414 if c in _globchars: return True
416 if c in _globchars: return True
415 return False
417 return False
416
418
417 def regex(kind, name, tail):
419 def regex(kind, name, tail):
418 '''convert a pattern into a regular expression'''
420 '''convert a pattern into a regular expression'''
419 if kind == 're':
421 if kind == 're':
420 return name
422 return name
421 elif kind == 'path':
423 elif kind == 'path':
422 return '^' + re.escape(name) + '(?:/|$)'
424 return '^' + re.escape(name) + '(?:/|$)'
423 elif kind == 'relglob':
425 elif kind == 'relglob':
424 return head + globre(name, '(?:|.*/)', tail)
426 return head + globre(name, '(?:|.*/)', tail)
425 elif kind == 'relpath':
427 elif kind == 'relpath':
426 return head + re.escape(name) + tail
428 return head + re.escape(name) + tail
427 elif kind == 'relre':
429 elif kind == 'relre':
428 if name.startswith('^'):
430 if name.startswith('^'):
429 return name
431 return name
430 return '.*' + name
432 return '.*' + name
431 return head + globre(name, '', tail)
433 return head + globre(name, '', tail)
432
434
433 def matchfn(pats, tail):
435 def matchfn(pats, tail):
434 """build a matching function from a set of patterns"""
436 """build a matching function from a set of patterns"""
435 if not pats:
437 if not pats:
436 return
438 return
437 matches = []
439 matches = []
438 for k, p in pats:
440 for k, p in pats:
439 try:
441 try:
440 pat = '(?:%s)' % regex(k, p, tail)
442 pat = '(?:%s)' % regex(k, p, tail)
441 matches.append(re.compile(pat).match)
443 matches.append(re.compile(pat).match)
442 except re.error:
444 except re.error:
443 if src: raise Abort("%s: invalid pattern (%s): %s" % (src, k, p))
445 if src: raise Abort("%s: invalid pattern (%s): %s" % (src, k, p))
444 else: raise Abort("invalid pattern (%s): %s" % (k, p))
446 else: raise Abort("invalid pattern (%s): %s" % (k, p))
445
447
446 def buildfn(text):
448 def buildfn(text):
447 for m in matches:
449 for m in matches:
448 r = m(text)
450 r = m(text)
449 if r:
451 if r:
450 return r
452 return r
451
453
452 return buildfn
454 return buildfn
453
455
454 def globprefix(pat):
456 def globprefix(pat):
455 '''return the non-glob prefix of a path, e.g. foo/* -> foo'''
457 '''return the non-glob prefix of a path, e.g. foo/* -> foo'''
456 root = []
458 root = []
457 for p in pat.split(os.sep):
459 for p in pat.split(os.sep):
458 if contains_glob(p): break
460 if contains_glob(p): break
459 root.append(p)
461 root.append(p)
460 return '/'.join(root)
462 return '/'.join(root)
461
463
462 pats = []
464 pats = []
463 files = []
465 files = []
464 roots = []
466 roots = []
465 for kind, name in [patkind(p, dflt_pat) for p in names]:
467 for kind, name in [patkind(p, dflt_pat) for p in names]:
466 if kind in ('glob', 'relpath'):
468 if kind in ('glob', 'relpath'):
467 name = canonpath(canonroot, cwd, name)
469 name = canonpath(canonroot, cwd, name)
468 if name == '':
470 if name == '':
469 kind, name = 'glob', '**'
471 kind, name = 'glob', '**'
470 if kind in ('glob', 'path', 're'):
472 if kind in ('glob', 'path', 're'):
471 pats.append((kind, name))
473 pats.append((kind, name))
472 if kind == 'glob':
474 if kind == 'glob':
473 root = globprefix(name)
475 root = globprefix(name)
474 if root: roots.append(root)
476 if root: roots.append(root)
475 elif kind == 'relpath':
477 elif kind == 'relpath':
476 files.append((kind, name))
478 files.append((kind, name))
477 roots.append(name)
479 roots.append(name)
478
480
479 patmatch = matchfn(pats, '$') or always
481 patmatch = matchfn(pats, '$') or always
480 filematch = matchfn(files, '(?:/|$)') or always
482 filematch = matchfn(files, '(?:/|$)') or always
481 incmatch = always
483 incmatch = always
482 if inc:
484 if inc:
483 inckinds = [patkind(canonpath(canonroot, cwd, i)) for i in inc]
485 inckinds = [patkind(canonpath(canonroot, cwd, i)) for i in inc]
484 incmatch = matchfn(inckinds, '(?:/|$)')
486 incmatch = matchfn(inckinds, '(?:/|$)')
485 excmatch = lambda fn: False
487 excmatch = lambda fn: False
486 if exc:
488 if exc:
487 exckinds = [patkind(canonpath(canonroot, cwd, x)) for x in exc]
489 exckinds = [patkind(canonpath(canonroot, cwd, x)) for x in exc]
488 excmatch = matchfn(exckinds, '(?:/|$)')
490 excmatch = matchfn(exckinds, '(?:/|$)')
489
491
490 return (roots,
492 return (roots,
491 lambda fn: (incmatch(fn) and not excmatch(fn) and
493 lambda fn: (incmatch(fn) and not excmatch(fn) and
492 (fn.endswith('/') or
494 (fn.endswith('/') or
493 (not pats and not files) or
495 (not pats and not files) or
494 (pats and patmatch(fn)) or
496 (pats and patmatch(fn)) or
495 (files and filematch(fn)))),
497 (files and filematch(fn)))),
496 (inc or exc or (pats and pats != [('glob', '**')])) and True)
498 (inc or exc or (pats and pats != [('glob', '**')])) and True)
497
499
498 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None):
500 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None):
499 '''enhanced shell command execution.
501 '''enhanced shell command execution.
500 run with environment maybe modified, maybe in different dir.
502 run with environment maybe modified, maybe in different dir.
501
503
502 if command fails and onerr is None, return status. if ui object,
504 if command fails and onerr is None, return status. if ui object,
503 print error message and return status, else raise onerr object as
505 print error message and return status, else raise onerr object as
504 exception.'''
506 exception.'''
505 def py2shell(val):
507 def py2shell(val):
506 'convert python object into string that is useful to shell'
508 'convert python object into string that is useful to shell'
507 if val in (None, False):
509 if val in (None, False):
508 return '0'
510 return '0'
509 if val == True:
511 if val == True:
510 return '1'
512 return '1'
511 return str(val)
513 return str(val)
512 oldenv = {}
514 oldenv = {}
513 for k in environ:
515 for k in environ:
514 oldenv[k] = os.environ.get(k)
516 oldenv[k] = os.environ.get(k)
515 if cwd is not None:
517 if cwd is not None:
516 oldcwd = os.getcwd()
518 oldcwd = os.getcwd()
517 origcmd = cmd
519 origcmd = cmd
518 if os.name == 'nt':
520 if os.name == 'nt':
519 cmd = '"%s"' % cmd
521 cmd = '"%s"' % cmd
520 try:
522 try:
521 for k, v in environ.iteritems():
523 for k, v in environ.iteritems():
522 os.environ[k] = py2shell(v)
524 os.environ[k] = py2shell(v)
523 if cwd is not None and oldcwd != cwd:
525 if cwd is not None and oldcwd != cwd:
524 os.chdir(cwd)
526 os.chdir(cwd)
525 rc = os.system(cmd)
527 rc = os.system(cmd)
526 if rc and onerr:
528 if rc and onerr:
527 errmsg = '%s %s' % (os.path.basename(origcmd.split(None, 1)[0]),
529 errmsg = '%s %s' % (os.path.basename(origcmd.split(None, 1)[0]),
528 explain_exit(rc)[0])
530 explain_exit(rc)[0])
529 if errprefix:
531 if errprefix:
530 errmsg = '%s: %s' % (errprefix, errmsg)
532 errmsg = '%s: %s' % (errprefix, errmsg)
531 try:
533 try:
532 onerr.warn(errmsg + '\n')
534 onerr.warn(errmsg + '\n')
533 except AttributeError:
535 except AttributeError:
534 raise onerr(errmsg)
536 raise onerr(errmsg)
535 return rc
537 return rc
536 finally:
538 finally:
537 for k, v in oldenv.iteritems():
539 for k, v in oldenv.iteritems():
538 if v is None:
540 if v is None:
539 del os.environ[k]
541 del os.environ[k]
540 else:
542 else:
541 os.environ[k] = v
543 os.environ[k] = v
542 if cwd is not None and oldcwd != cwd:
544 if cwd is not None and oldcwd != cwd:
543 os.chdir(oldcwd)
545 os.chdir(oldcwd)
544
546
545 def rename(src, dst):
547 def rename(src, dst):
546 """forcibly rename a file"""
548 """forcibly rename a file"""
547 try:
549 try:
548 os.rename(src, dst)
550 os.rename(src, dst)
549 except OSError, err:
551 except OSError, err:
550 # on windows, rename to existing file is not allowed, so we
552 # on windows, rename to existing file is not allowed, so we
551 # must delete destination first. but if file is open, unlink
553 # must delete destination first. but if file is open, unlink
552 # schedules it for delete but does not delete it. rename
554 # schedules it for delete but does not delete it. rename
553 # happens immediately even for open files, so we create
555 # happens immediately even for open files, so we create
554 # temporary file, delete it, rename destination to that name,
556 # temporary file, delete it, rename destination to that name,
555 # then delete that. then rename is safe to do.
557 # then delete that. then rename is safe to do.
556 fd, temp = tempfile.mkstemp(dir=os.path.dirname(dst) or '.')
558 fd, temp = tempfile.mkstemp(dir=os.path.dirname(dst) or '.')
557 os.close(fd)
559 os.close(fd)
558 os.unlink(temp)
560 os.unlink(temp)
559 os.rename(dst, temp)
561 os.rename(dst, temp)
560 os.unlink(temp)
562 os.unlink(temp)
561 os.rename(src, dst)
563 os.rename(src, dst)
562
564
563 def unlink(f):
565 def unlink(f):
564 """unlink and remove the directory if it is empty"""
566 """unlink and remove the directory if it is empty"""
565 os.unlink(f)
567 os.unlink(f)
566 # try removing directories that might now be empty
568 # try removing directories that might now be empty
567 try:
569 try:
568 os.removedirs(os.path.dirname(f))
570 os.removedirs(os.path.dirname(f))
569 except OSError:
571 except OSError:
570 pass
572 pass
571
573
572 def copyfile(src, dest):
574 def copyfile(src, dest):
573 "copy a file, preserving mode"
575 "copy a file, preserving mode"
574 try:
576 try:
575 shutil.copyfile(src, dest)
577 shutil.copyfile(src, dest)
576 shutil.copymode(src, dest)
578 shutil.copymode(src, dest)
577 except shutil.Error, inst:
579 except shutil.Error, inst:
578 raise util.Abort(str(inst))
580 raise util.Abort(str(inst))
579
581
580 def copyfiles(src, dst, hardlink=None):
582 def copyfiles(src, dst, hardlink=None):
581 """Copy a directory tree using hardlinks if possible"""
583 """Copy a directory tree using hardlinks if possible"""
582
584
583 if hardlink is None:
585 if hardlink is None:
584 hardlink = (os.stat(src).st_dev ==
586 hardlink = (os.stat(src).st_dev ==
585 os.stat(os.path.dirname(dst)).st_dev)
587 os.stat(os.path.dirname(dst)).st_dev)
586
588
587 if os.path.isdir(src):
589 if os.path.isdir(src):
588 os.mkdir(dst)
590 os.mkdir(dst)
589 for name in os.listdir(src):
591 for name in os.listdir(src):
590 srcname = os.path.join(src, name)
592 srcname = os.path.join(src, name)
591 dstname = os.path.join(dst, name)
593 dstname = os.path.join(dst, name)
592 copyfiles(srcname, dstname, hardlink)
594 copyfiles(srcname, dstname, hardlink)
593 else:
595 else:
594 if hardlink:
596 if hardlink:
595 try:
597 try:
596 os_link(src, dst)
598 os_link(src, dst)
597 except (IOError, OSError):
599 except (IOError, OSError):
598 hardlink = False
600 hardlink = False
599 shutil.copy(src, dst)
601 shutil.copy(src, dst)
600 else:
602 else:
601 shutil.copy(src, dst)
603 shutil.copy(src, dst)
602
604
603 def audit_path(path):
605 def audit_path(path):
604 """Abort if path contains dangerous components"""
606 """Abort if path contains dangerous components"""
605 parts = os.path.normcase(path).split(os.sep)
607 parts = os.path.normcase(path).split(os.sep)
606 if (os.path.splitdrive(path)[0] or parts[0] in ('.hg', '')
608 if (os.path.splitdrive(path)[0] or parts[0] in ('.hg', '')
607 or os.pardir in parts):
609 or os.pardir in parts):
608 raise Abort(_("path contains illegal component: %s\n") % path)
610 raise Abort(_("path contains illegal component: %s\n") % path)
609
611
610 def _makelock_file(info, pathname):
612 def _makelock_file(info, pathname):
611 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
613 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
612 os.write(ld, info)
614 os.write(ld, info)
613 os.close(ld)
615 os.close(ld)
614
616
615 def _readlock_file(pathname):
617 def _readlock_file(pathname):
616 return posixfile(pathname).read()
618 return posixfile(pathname).read()
617
619
618 def nlinks(pathname):
620 def nlinks(pathname):
619 """Return number of hardlinks for the given file."""
621 """Return number of hardlinks for the given file."""
620 return os.lstat(pathname).st_nlink
622 return os.lstat(pathname).st_nlink
621
623
622 if hasattr(os, 'link'):
624 if hasattr(os, 'link'):
623 os_link = os.link
625 os_link = os.link
624 else:
626 else:
625 def os_link(src, dst):
627 def os_link(src, dst):
626 raise OSError(0, _("Hardlinks not supported"))
628 raise OSError(0, _("Hardlinks not supported"))
627
629
628 def fstat(fp):
630 def fstat(fp):
629 '''stat file object that may not have fileno method.'''
631 '''stat file object that may not have fileno method.'''
630 try:
632 try:
631 return os.fstat(fp.fileno())
633 return os.fstat(fp.fileno())
632 except AttributeError:
634 except AttributeError:
633 return os.stat(fp.name)
635 return os.stat(fp.name)
634
636
635 posixfile = file
637 posixfile = file
636
638
637 def is_win_9x():
639 def is_win_9x():
638 '''return true if run on windows 95, 98 or me.'''
640 '''return true if run on windows 95, 98 or me.'''
639 try:
641 try:
640 return sys.getwindowsversion()[3] == 1
642 return sys.getwindowsversion()[3] == 1
641 except AttributeError:
643 except AttributeError:
642 return os.name == 'nt' and 'command' in os.environ.get('comspec', '')
644 return os.name == 'nt' and 'command' in os.environ.get('comspec', '')
643
645
644 getuser_fallback = None
646 getuser_fallback = None
645
647
646 def getuser():
648 def getuser():
647 '''return name of current user'''
649 '''return name of current user'''
648 try:
650 try:
649 return getpass.getuser()
651 return getpass.getuser()
650 except ImportError:
652 except ImportError:
651 # import of pwd will fail on windows - try fallback
653 # import of pwd will fail on windows - try fallback
652 if getuser_fallback:
654 if getuser_fallback:
653 return getuser_fallback()
655 return getuser_fallback()
654 # raised if win32api not available
656 # raised if win32api not available
655 raise Abort(_('user name not available - set USERNAME '
657 raise Abort(_('user name not available - set USERNAME '
656 'environment variable'))
658 'environment variable'))
657
659
658 def username(uid=None):
660 def username(uid=None):
659 """Return the name of the user with the given uid.
661 """Return the name of the user with the given uid.
660
662
661 If uid is None, return the name of the current user."""
663 If uid is None, return the name of the current user."""
662 try:
664 try:
663 import pwd
665 import pwd
664 if uid is None:
666 if uid is None:
665 uid = os.getuid()
667 uid = os.getuid()
666 try:
668 try:
667 return pwd.getpwuid(uid)[0]
669 return pwd.getpwuid(uid)[0]
668 except KeyError:
670 except KeyError:
669 return str(uid)
671 return str(uid)
670 except ImportError:
672 except ImportError:
671 return None
673 return None
672
674
673 def groupname(gid=None):
675 def groupname(gid=None):
674 """Return the name of the group with the given gid.
676 """Return the name of the group with the given gid.
675
677
676 If gid is None, return the name of the current group."""
678 If gid is None, return the name of the current group."""
677 try:
679 try:
678 import grp
680 import grp
679 if gid is None:
681 if gid is None:
680 gid = os.getgid()
682 gid = os.getgid()
681 try:
683 try:
682 return grp.getgrgid(gid)[0]
684 return grp.getgrgid(gid)[0]
683 except KeyError:
685 except KeyError:
684 return str(gid)
686 return str(gid)
685 except ImportError:
687 except ImportError:
686 return None
688 return None
687
689
688 # File system features
690 # File system features
689
691
690 def checkfolding(path):
692 def checkfolding(path):
691 """
693 """
692 Check whether the given path is on a case-sensitive filesystem
694 Check whether the given path is on a case-sensitive filesystem
693
695
694 Requires a path (like /foo/.hg) ending with a foldable final
696 Requires a path (like /foo/.hg) ending with a foldable final
695 directory component.
697 directory component.
696 """
698 """
697 s1 = os.stat(path)
699 s1 = os.stat(path)
698 d, b = os.path.split(path)
700 d, b = os.path.split(path)
699 p2 = os.path.join(d, b.upper())
701 p2 = os.path.join(d, b.upper())
700 if path == p2:
702 if path == p2:
701 p2 = os.path.join(d, b.lower())
703 p2 = os.path.join(d, b.lower())
702 try:
704 try:
703 s2 = os.stat(p2)
705 s2 = os.stat(p2)
704 if s2 == s1:
706 if s2 == s1:
705 return False
707 return False
706 return True
708 return True
707 except:
709 except:
708 return True
710 return True
709
711
710 # Platform specific variants
712 # Platform specific variants
711 if os.name == 'nt':
713 if os.name == 'nt':
712 demandload(globals(), "msvcrt")
714 demandload(globals(), "msvcrt")
713 nulldev = 'NUL:'
715 nulldev = 'NUL:'
714
716
715 class winstdout:
717 class winstdout:
716 '''stdout on windows misbehaves if sent through a pipe'''
718 '''stdout on windows misbehaves if sent through a pipe'''
717
719
718 def __init__(self, fp):
720 def __init__(self, fp):
719 self.fp = fp
721 self.fp = fp
720
722
721 def __getattr__(self, key):
723 def __getattr__(self, key):
722 return getattr(self.fp, key)
724 return getattr(self.fp, key)
723
725
724 def close(self):
726 def close(self):
725 try:
727 try:
726 self.fp.close()
728 self.fp.close()
727 except: pass
729 except: pass
728
730
729 def write(self, s):
731 def write(self, s):
730 try:
732 try:
731 return self.fp.write(s)
733 return self.fp.write(s)
732 except IOError, inst:
734 except IOError, inst:
733 if inst.errno != 0: raise
735 if inst.errno != 0: raise
734 self.close()
736 self.close()
735 raise IOError(errno.EPIPE, 'Broken pipe')
737 raise IOError(errno.EPIPE, 'Broken pipe')
736
738
737 sys.stdout = winstdout(sys.stdout)
739 sys.stdout = winstdout(sys.stdout)
738
740
739 def system_rcpath():
741 def system_rcpath():
740 try:
742 try:
741 return system_rcpath_win32()
743 return system_rcpath_win32()
742 except:
744 except:
743 return [r'c:\mercurial\mercurial.ini']
745 return [r'c:\mercurial\mercurial.ini']
744
746
745 def os_rcpath():
747 def os_rcpath():
746 '''return default os-specific hgrc search path'''
748 '''return default os-specific hgrc search path'''
747 path = system_rcpath()
749 path = system_rcpath()
748 path.append(user_rcpath())
750 path.append(user_rcpath())
749 userprofile = os.environ.get('USERPROFILE')
751 userprofile = os.environ.get('USERPROFILE')
750 if userprofile:
752 if userprofile:
751 path.append(os.path.join(userprofile, 'mercurial.ini'))
753 path.append(os.path.join(userprofile, 'mercurial.ini'))
752 return path
754 return path
753
755
754 def user_rcpath():
756 def user_rcpath():
755 '''return os-specific hgrc search path to the user dir'''
757 '''return os-specific hgrc search path to the user dir'''
756 return os.path.join(os.path.expanduser('~'), 'mercurial.ini')
758 return os.path.join(os.path.expanduser('~'), 'mercurial.ini')
757
759
758 def parse_patch_output(output_line):
760 def parse_patch_output(output_line):
759 """parses the output produced by patch and returns the file name"""
761 """parses the output produced by patch and returns the file name"""
760 pf = output_line[14:]
762 pf = output_line[14:]
761 if pf[0] == '`':
763 if pf[0] == '`':
762 pf = pf[1:-1] # Remove the quotes
764 pf = pf[1:-1] # Remove the quotes
763 return pf
765 return pf
764
766
765 def testpid(pid):
767 def testpid(pid):
766 '''return False if pid dead, True if running or not known'''
768 '''return False if pid dead, True if running or not known'''
767 return True
769 return True
768
770
769 def is_exec(f, last):
771 def is_exec(f, last):
770 return last
772 return last
771
773
772 def set_exec(f, mode):
774 def set_exec(f, mode):
773 pass
775 pass
774
776
775 def set_binary(fd):
777 def set_binary(fd):
776 msvcrt.setmode(fd.fileno(), os.O_BINARY)
778 msvcrt.setmode(fd.fileno(), os.O_BINARY)
777
779
778 def pconvert(path):
780 def pconvert(path):
779 return path.replace("\\", "/")
781 return path.replace("\\", "/")
780
782
781 def localpath(path):
783 def localpath(path):
782 return path.replace('/', '\\')
784 return path.replace('/', '\\')
783
785
784 def normpath(path):
786 def normpath(path):
785 return pconvert(os.path.normpath(path))
787 return pconvert(os.path.normpath(path))
786
788
787 makelock = _makelock_file
789 makelock = _makelock_file
788 readlock = _readlock_file
790 readlock = _readlock_file
789
791
790 def samestat(s1, s2):
792 def samestat(s1, s2):
791 return False
793 return False
792
794
793 def shellquote(s):
795 def shellquote(s):
794 return '"%s"' % s.replace('"', '\\"')
796 return '"%s"' % s.replace('"', '\\"')
795
797
796 def explain_exit(code):
798 def explain_exit(code):
797 return _("exited with status %d") % code, code
799 return _("exited with status %d") % code, code
798
800
799 # if you change this stub into a real check, please try to implement the
801 # if you change this stub into a real check, please try to implement the
800 # username and groupname functions above, too.
802 # username and groupname functions above, too.
801 def isowner(fp, st=None):
803 def isowner(fp, st=None):
802 return True
804 return True
803
805
804 try:
806 try:
805 # override functions with win32 versions if possible
807 # override functions with win32 versions if possible
806 from util_win32 import *
808 from util_win32 import *
807 if not is_win_9x():
809 if not is_win_9x():
808 posixfile = posixfile_nt
810 posixfile = posixfile_nt
809 except ImportError:
811 except ImportError:
810 pass
812 pass
811
813
812 else:
814 else:
813 nulldev = '/dev/null'
815 nulldev = '/dev/null'
814
816
815 def rcfiles(path):
817 def rcfiles(path):
816 rcs = [os.path.join(path, 'hgrc')]
818 rcs = [os.path.join(path, 'hgrc')]
817 rcdir = os.path.join(path, 'hgrc.d')
819 rcdir = os.path.join(path, 'hgrc.d')
818 try:
820 try:
819 rcs.extend([os.path.join(rcdir, f) for f in os.listdir(rcdir)
821 rcs.extend([os.path.join(rcdir, f) for f in os.listdir(rcdir)
820 if f.endswith(".rc")])
822 if f.endswith(".rc")])
821 except OSError:
823 except OSError:
822 pass
824 pass
823 return rcs
825 return rcs
824
826
825 def os_rcpath():
827 def os_rcpath():
826 '''return default os-specific hgrc search path'''
828 '''return default os-specific hgrc search path'''
827 path = []
829 path = []
828 # old mod_python does not set sys.argv
830 # old mod_python does not set sys.argv
829 if len(getattr(sys, 'argv', [])) > 0:
831 if len(getattr(sys, 'argv', [])) > 0:
830 path.extend(rcfiles(os.path.dirname(sys.argv[0]) +
832 path.extend(rcfiles(os.path.dirname(sys.argv[0]) +
831 '/../etc/mercurial'))
833 '/../etc/mercurial'))
832 path.extend(rcfiles('/etc/mercurial'))
834 path.extend(rcfiles('/etc/mercurial'))
833 path.append(os.path.expanduser('~/.hgrc'))
835 path.append(os.path.expanduser('~/.hgrc'))
834 path = [os.path.normpath(f) for f in path]
836 path = [os.path.normpath(f) for f in path]
835 return path
837 return path
836
838
837 def parse_patch_output(output_line):
839 def parse_patch_output(output_line):
838 """parses the output produced by patch and returns the file name"""
840 """parses the output produced by patch and returns the file name"""
839 pf = output_line[14:]
841 pf = output_line[14:]
840 if pf.startswith("'") and pf.endswith("'") and " " in pf:
842 if pf.startswith("'") and pf.endswith("'") and " " in pf:
841 pf = pf[1:-1] # Remove the quotes
843 pf = pf[1:-1] # Remove the quotes
842 return pf
844 return pf
843
845
844 def is_exec(f, last):
846 def is_exec(f, last):
845 """check whether a file is executable"""
847 """check whether a file is executable"""
846 return (os.lstat(f).st_mode & 0100 != 0)
848 return (os.lstat(f).st_mode & 0100 != 0)
847
849
848 def set_exec(f, mode):
850 def set_exec(f, mode):
849 s = os.lstat(f).st_mode
851 s = os.lstat(f).st_mode
850 if (s & 0100 != 0) == mode:
852 if (s & 0100 != 0) == mode:
851 return
853 return
852 if mode:
854 if mode:
853 # Turn on +x for every +r bit when making a file executable
855 # Turn on +x for every +r bit when making a file executable
854 # and obey umask.
856 # and obey umask.
855 umask = os.umask(0)
857 umask = os.umask(0)
856 os.umask(umask)
858 os.umask(umask)
857 os.chmod(f, s | (s & 0444) >> 2 & ~umask)
859 os.chmod(f, s | (s & 0444) >> 2 & ~umask)
858 else:
860 else:
859 os.chmod(f, s & 0666)
861 os.chmod(f, s & 0666)
860
862
861 def set_binary(fd):
863 def set_binary(fd):
862 pass
864 pass
863
865
864 def pconvert(path):
866 def pconvert(path):
865 return path
867 return path
866
868
867 def localpath(path):
869 def localpath(path):
868 return path
870 return path
869
871
870 normpath = os.path.normpath
872 normpath = os.path.normpath
871 samestat = os.path.samestat
873 samestat = os.path.samestat
872
874
873 def makelock(info, pathname):
875 def makelock(info, pathname):
874 try:
876 try:
875 os.symlink(info, pathname)
877 os.symlink(info, pathname)
876 except OSError, why:
878 except OSError, why:
877 if why.errno == errno.EEXIST:
879 if why.errno == errno.EEXIST:
878 raise
880 raise
879 else:
881 else:
880 _makelock_file(info, pathname)
882 _makelock_file(info, pathname)
881
883
882 def readlock(pathname):
884 def readlock(pathname):
883 try:
885 try:
884 return os.readlink(pathname)
886 return os.readlink(pathname)
885 except OSError, why:
887 except OSError, why:
886 if why.errno == errno.EINVAL:
888 if why.errno == errno.EINVAL:
887 return _readlock_file(pathname)
889 return _readlock_file(pathname)
888 else:
890 else:
889 raise
891 raise
890
892
891 def shellquote(s):
893 def shellquote(s):
892 return "'%s'" % s.replace("'", "'\\''")
894 return "'%s'" % s.replace("'", "'\\''")
893
895
894 def testpid(pid):
896 def testpid(pid):
895 '''return False if pid dead, True if running or not sure'''
897 '''return False if pid dead, True if running or not sure'''
896 try:
898 try:
897 os.kill(pid, 0)
899 os.kill(pid, 0)
898 return True
900 return True
899 except OSError, inst:
901 except OSError, inst:
900 return inst.errno != errno.ESRCH
902 return inst.errno != errno.ESRCH
901
903
902 def explain_exit(code):
904 def explain_exit(code):
903 """return a 2-tuple (desc, code) describing a process's status"""
905 """return a 2-tuple (desc, code) describing a process's status"""
904 if os.WIFEXITED(code):
906 if os.WIFEXITED(code):
905 val = os.WEXITSTATUS(code)
907 val = os.WEXITSTATUS(code)
906 return _("exited with status %d") % val, val
908 return _("exited with status %d") % val, val
907 elif os.WIFSIGNALED(code):
909 elif os.WIFSIGNALED(code):
908 val = os.WTERMSIG(code)
910 val = os.WTERMSIG(code)
909 return _("killed by signal %d") % val, val
911 return _("killed by signal %d") % val, val
910 elif os.WIFSTOPPED(code):
912 elif os.WIFSTOPPED(code):
911 val = os.WSTOPSIG(code)
913 val = os.WSTOPSIG(code)
912 return _("stopped by signal %d") % val, val
914 return _("stopped by signal %d") % val, val
913 raise ValueError(_("invalid exit code"))
915 raise ValueError(_("invalid exit code"))
914
916
915 def isowner(fp, st=None):
917 def isowner(fp, st=None):
916 """Return True if the file object f belongs to the current user.
918 """Return True if the file object f belongs to the current user.
917
919
918 The return value of a util.fstat(f) may be passed as the st argument.
920 The return value of a util.fstat(f) may be passed as the st argument.
919 """
921 """
920 if st is None:
922 if st is None:
921 st = fstat(fp)
923 st = fstat(fp)
922 return st.st_uid == os.getuid()
924 return st.st_uid == os.getuid()
923
925
924 def _buildencodefun():
926 def _buildencodefun():
925 e = '_'
927 e = '_'
926 win_reserved = [ord(x) for x in '\\:*?"<>|']
928 win_reserved = [ord(x) for x in '\\:*?"<>|']
927 cmap = dict([ (chr(x), chr(x)) for x in xrange(127) ])
929 cmap = dict([ (chr(x), chr(x)) for x in xrange(127) ])
928 for x in (range(32) + range(126, 256) + win_reserved):
930 for x in (range(32) + range(126, 256) + win_reserved):
929 cmap[chr(x)] = "~%02x" % x
931 cmap[chr(x)] = "~%02x" % x
930 for x in range(ord("A"), ord("Z")+1) + [ord(e)]:
932 for x in range(ord("A"), ord("Z")+1) + [ord(e)]:
931 cmap[chr(x)] = e + chr(x).lower()
933 cmap[chr(x)] = e + chr(x).lower()
932 dmap = {}
934 dmap = {}
933 for k, v in cmap.iteritems():
935 for k, v in cmap.iteritems():
934 dmap[v] = k
936 dmap[v] = k
935 def decode(s):
937 def decode(s):
936 i = 0
938 i = 0
937 while i < len(s):
939 while i < len(s):
938 for l in xrange(1, 4):
940 for l in xrange(1, 4):
939 try:
941 try:
940 yield dmap[s[i:i+l]]
942 yield dmap[s[i:i+l]]
941 i += l
943 i += l
942 break
944 break
943 except KeyError:
945 except KeyError:
944 pass
946 pass
945 else:
947 else:
946 raise KeyError
948 raise KeyError
947 return (lambda s: "".join([cmap[c] for c in s]),
949 return (lambda s: "".join([cmap[c] for c in s]),
948 lambda s: "".join(list(decode(s))))
950 lambda s: "".join(list(decode(s))))
949
951
950 encodefilename, decodefilename = _buildencodefun()
952 encodefilename, decodefilename = _buildencodefun()
951
953
952 def encodedopener(openerfn, fn):
954 def encodedopener(openerfn, fn):
953 def o(path, *args, **kw):
955 def o(path, *args, **kw):
954 return openerfn(fn(path), *args, **kw)
956 return openerfn(fn(path), *args, **kw)
955 return o
957 return o
956
958
957 def opener(base, audit=True):
959 def opener(base, audit=True):
958 """
960 """
959 return a function that opens files relative to base
961 return a function that opens files relative to base
960
962
961 this function is used to hide the details of COW semantics and
963 this function is used to hide the details of COW semantics and
962 remote file access from higher level code.
964 remote file access from higher level code.
963 """
965 """
964 p = base
966 p = base
965 audit_p = audit
967 audit_p = audit
966
968
967 def mktempcopy(name):
969 def mktempcopy(name):
968 d, fn = os.path.split(name)
970 d, fn = os.path.split(name)
969 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
971 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
970 os.close(fd)
972 os.close(fd)
971 ofp = posixfile(temp, "wb")
973 ofp = posixfile(temp, "wb")
972 try:
974 try:
973 try:
975 try:
974 ifp = posixfile(name, "rb")
976 ifp = posixfile(name, "rb")
975 except IOError, inst:
977 except IOError, inst:
976 if not getattr(inst, 'filename', None):
978 if not getattr(inst, 'filename', None):
977 inst.filename = name
979 inst.filename = name
978 raise
980 raise
979 for chunk in filechunkiter(ifp):
981 for chunk in filechunkiter(ifp):
980 ofp.write(chunk)
982 ofp.write(chunk)
981 ifp.close()
983 ifp.close()
982 ofp.close()
984 ofp.close()
983 except:
985 except:
984 try: os.unlink(temp)
986 try: os.unlink(temp)
985 except: pass
987 except: pass
986 raise
988 raise
987 st = os.lstat(name)
989 st = os.lstat(name)
988 os.chmod(temp, st.st_mode)
990 os.chmod(temp, st.st_mode)
989 return temp
991 return temp
990
992
991 class atomictempfile(posixfile):
993 class atomictempfile(posixfile):
992 """the file will only be copied when rename is called"""
994 """the file will only be copied when rename is called"""
993 def __init__(self, name, mode):
995 def __init__(self, name, mode):
994 self.__name = name
996 self.__name = name
995 self.temp = mktempcopy(name)
997 self.temp = mktempcopy(name)
996 posixfile.__init__(self, self.temp, mode)
998 posixfile.__init__(self, self.temp, mode)
997 def rename(self):
999 def rename(self):
998 if not self.closed:
1000 if not self.closed:
999 posixfile.close(self)
1001 posixfile.close(self)
1000 rename(self.temp, localpath(self.__name))
1002 rename(self.temp, localpath(self.__name))
1001 def __del__(self):
1003 def __del__(self):
1002 if not self.closed:
1004 if not self.closed:
1003 try:
1005 try:
1004 os.unlink(self.temp)
1006 os.unlink(self.temp)
1005 except: pass
1007 except: pass
1006 posixfile.close(self)
1008 posixfile.close(self)
1007
1009
1008 class atomicfile(atomictempfile):
1010 class atomicfile(atomictempfile):
1009 """the file will only be copied on close"""
1011 """the file will only be copied on close"""
1010 def __init__(self, name, mode):
1012 def __init__(self, name, mode):
1011 atomictempfile.__init__(self, name, mode)
1013 atomictempfile.__init__(self, name, mode)
1012 def close(self):
1014 def close(self):
1013 self.rename()
1015 self.rename()
1014 def __del__(self):
1016 def __del__(self):
1015 self.rename()
1017 self.rename()
1016
1018
1017 def o(path, mode="r", text=False, atomic=False, atomictemp=False):
1019 def o(path, mode="r", text=False, atomic=False, atomictemp=False):
1018 if audit_p:
1020 if audit_p:
1019 audit_path(path)
1021 audit_path(path)
1020 f = os.path.join(p, path)
1022 f = os.path.join(p, path)
1021
1023
1022 if not text:
1024 if not text:
1023 mode += "b" # for that other OS
1025 mode += "b" # for that other OS
1024
1026
1025 if mode[0] != "r":
1027 if mode[0] != "r":
1026 try:
1028 try:
1027 nlink = nlinks(f)
1029 nlink = nlinks(f)
1028 except OSError:
1030 except OSError:
1029 d = os.path.dirname(f)
1031 d = os.path.dirname(f)
1030 if not os.path.isdir(d):
1032 if not os.path.isdir(d):
1031 os.makedirs(d)
1033 os.makedirs(d)
1032 else:
1034 else:
1033 if atomic:
1035 if atomic:
1034 return atomicfile(f, mode)
1036 return atomicfile(f, mode)
1035 elif atomictemp:
1037 elif atomictemp:
1036 return atomictempfile(f, mode)
1038 return atomictempfile(f, mode)
1037 if nlink > 1:
1039 if nlink > 1:
1038 rename(mktempcopy(f), f)
1040 rename(mktempcopy(f), f)
1039 return posixfile(f, mode)
1041 return posixfile(f, mode)
1040
1042
1041 return o
1043 return o
1042
1044
1043 class chunkbuffer(object):
1045 class chunkbuffer(object):
1044 """Allow arbitrary sized chunks of data to be efficiently read from an
1046 """Allow arbitrary sized chunks of data to be efficiently read from an
1045 iterator over chunks of arbitrary size."""
1047 iterator over chunks of arbitrary size."""
1046
1048
1047 def __init__(self, in_iter, targetsize = 2**16):
1049 def __init__(self, in_iter, targetsize = 2**16):
1048 """in_iter is the iterator that's iterating over the input chunks.
1050 """in_iter is the iterator that's iterating over the input chunks.
1049 targetsize is how big a buffer to try to maintain."""
1051 targetsize is how big a buffer to try to maintain."""
1050 self.in_iter = iter(in_iter)
1052 self.in_iter = iter(in_iter)
1051 self.buf = ''
1053 self.buf = ''
1052 self.targetsize = int(targetsize)
1054 self.targetsize = int(targetsize)
1053 if self.targetsize <= 0:
1055 if self.targetsize <= 0:
1054 raise ValueError(_("targetsize must be greater than 0, was %d") %
1056 raise ValueError(_("targetsize must be greater than 0, was %d") %
1055 targetsize)
1057 targetsize)
1056 self.iterempty = False
1058 self.iterempty = False
1057
1059
1058 def fillbuf(self):
1060 def fillbuf(self):
1059 """Ignore target size; read every chunk from iterator until empty."""
1061 """Ignore target size; read every chunk from iterator until empty."""
1060 if not self.iterempty:
1062 if not self.iterempty:
1061 collector = cStringIO.StringIO()
1063 collector = cStringIO.StringIO()
1062 collector.write(self.buf)
1064 collector.write(self.buf)
1063 for ch in self.in_iter:
1065 for ch in self.in_iter:
1064 collector.write(ch)
1066 collector.write(ch)
1065 self.buf = collector.getvalue()
1067 self.buf = collector.getvalue()
1066 self.iterempty = True
1068 self.iterempty = True
1067
1069
1068 def read(self, l):
1070 def read(self, l):
1069 """Read L bytes of data from the iterator of chunks of data.
1071 """Read L bytes of data from the iterator of chunks of data.
1070 Returns less than L bytes if the iterator runs dry."""
1072 Returns less than L bytes if the iterator runs dry."""
1071 if l > len(self.buf) and not self.iterempty:
1073 if l > len(self.buf) and not self.iterempty:
1072 # Clamp to a multiple of self.targetsize
1074 # Clamp to a multiple of self.targetsize
1073 targetsize = self.targetsize * ((l // self.targetsize) + 1)
1075 targetsize = self.targetsize * ((l // self.targetsize) + 1)
1074 collector = cStringIO.StringIO()
1076 collector = cStringIO.StringIO()
1075 collector.write(self.buf)
1077 collector.write(self.buf)
1076 collected = len(self.buf)
1078 collected = len(self.buf)
1077 for chunk in self.in_iter:
1079 for chunk in self.in_iter:
1078 collector.write(chunk)
1080 collector.write(chunk)
1079 collected += len(chunk)
1081 collected += len(chunk)
1080 if collected >= targetsize:
1082 if collected >= targetsize:
1081 break
1083 break
1082 if collected < targetsize:
1084 if collected < targetsize:
1083 self.iterempty = True
1085 self.iterempty = True
1084 self.buf = collector.getvalue()
1086 self.buf = collector.getvalue()
1085 s, self.buf = self.buf[:l], buffer(self.buf, l)
1087 s, self.buf = self.buf[:l], buffer(self.buf, l)
1086 return s
1088 return s
1087
1089
1088 def filechunkiter(f, size=65536, limit=None):
1090 def filechunkiter(f, size=65536, limit=None):
1089 """Create a generator that produces the data in the file size
1091 """Create a generator that produces the data in the file size
1090 (default 65536) bytes at a time, up to optional limit (default is
1092 (default 65536) bytes at a time, up to optional limit (default is
1091 to read all data). Chunks may be less than size bytes if the
1093 to read all data). Chunks may be less than size bytes if the
1092 chunk is the last chunk in the file, or the file is a socket or
1094 chunk is the last chunk in the file, or the file is a socket or
1093 some other type of file that sometimes reads less data than is
1095 some other type of file that sometimes reads less data than is
1094 requested."""
1096 requested."""
1095 assert size >= 0
1097 assert size >= 0
1096 assert limit is None or limit >= 0
1098 assert limit is None or limit >= 0
1097 while True:
1099 while True:
1098 if limit is None: nbytes = size
1100 if limit is None: nbytes = size
1099 else: nbytes = min(limit, size)
1101 else: nbytes = min(limit, size)
1100 s = nbytes and f.read(nbytes)
1102 s = nbytes and f.read(nbytes)
1101 if not s: break
1103 if not s: break
1102 if limit: limit -= len(s)
1104 if limit: limit -= len(s)
1103 yield s
1105 yield s
1104
1106
1105 def makedate():
1107 def makedate():
1106 lt = time.localtime()
1108 lt = time.localtime()
1107 if lt[8] == 1 and time.daylight:
1109 if lt[8] == 1 and time.daylight:
1108 tz = time.altzone
1110 tz = time.altzone
1109 else:
1111 else:
1110 tz = time.timezone
1112 tz = time.timezone
1111 return time.mktime(lt), tz
1113 return time.mktime(lt), tz
1112
1114
1113 def datestr(date=None, format='%a %b %d %H:%M:%S %Y', timezone=True):
1115 def datestr(date=None, format='%a %b %d %H:%M:%S %Y', timezone=True):
1114 """represent a (unixtime, offset) tuple as a localized time.
1116 """represent a (unixtime, offset) tuple as a localized time.
1115 unixtime is seconds since the epoch, and offset is the time zone's
1117 unixtime is seconds since the epoch, and offset is the time zone's
1116 number of seconds away from UTC. if timezone is false, do not
1118 number of seconds away from UTC. if timezone is false, do not
1117 append time zone to string."""
1119 append time zone to string."""
1118 t, tz = date or makedate()
1120 t, tz = date or makedate()
1119 s = time.strftime(format, time.gmtime(float(t) - tz))
1121 s = time.strftime(format, time.gmtime(float(t) - tz))
1120 if timezone:
1122 if timezone:
1121 s += " %+03d%02d" % (-tz / 3600, ((-tz % 3600) / 60))
1123 s += " %+03d%02d" % (-tz / 3600, ((-tz % 3600) / 60))
1122 return s
1124 return s
1123
1125
1124 def strdate(string, format, defaults):
1126 def strdate(string, format, defaults):
1125 """parse a localized time string and return a (unixtime, offset) tuple.
1127 """parse a localized time string and return a (unixtime, offset) tuple.
1126 if the string cannot be parsed, ValueError is raised."""
1128 if the string cannot be parsed, ValueError is raised."""
1127 def timezone(string):
1129 def timezone(string):
1128 tz = string.split()[-1]
1130 tz = string.split()[-1]
1129 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
1131 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
1130 tz = int(tz)
1132 tz = int(tz)
1131 offset = - 3600 * (tz / 100) - 60 * (tz % 100)
1133 offset = - 3600 * (tz / 100) - 60 * (tz % 100)
1132 return offset
1134 return offset
1133 if tz == "GMT" or tz == "UTC":
1135 if tz == "GMT" or tz == "UTC":
1134 return 0
1136 return 0
1135 return None
1137 return None
1136
1138
1137 # NOTE: unixtime = localunixtime + offset
1139 # NOTE: unixtime = localunixtime + offset
1138 offset, date = timezone(string), string
1140 offset, date = timezone(string), string
1139 if offset != None:
1141 if offset != None:
1140 date = " ".join(string.split()[:-1])
1142 date = " ".join(string.split()[:-1])
1141
1143
1142 # add missing elements from defaults
1144 # add missing elements from defaults
1143 for part in defaults:
1145 for part in defaults:
1144 found = [True for p in part if ("%"+p) in format]
1146 found = [True for p in part if ("%"+p) in format]
1145 if not found:
1147 if not found:
1146 date += "@" + defaults[part]
1148 date += "@" + defaults[part]
1147 format += "@%" + part[0]
1149 format += "@%" + part[0]
1148
1150
1149 timetuple = time.strptime(date, format)
1151 timetuple = time.strptime(date, format)
1150 localunixtime = int(calendar.timegm(timetuple))
1152 localunixtime = int(calendar.timegm(timetuple))
1151 if offset is None:
1153 if offset is None:
1152 # local timezone
1154 # local timezone
1153 unixtime = int(time.mktime(timetuple))
1155 unixtime = int(time.mktime(timetuple))
1154 offset = unixtime - localunixtime
1156 offset = unixtime - localunixtime
1155 else:
1157 else:
1156 unixtime = localunixtime + offset
1158 unixtime = localunixtime + offset
1157 return unixtime, offset
1159 return unixtime, offset
1158
1160
1159 def parsedate(string, formats=None, defaults=None):
1161 def parsedate(string, formats=None, defaults=None):
1160 """parse a localized time string and return a (unixtime, offset) tuple.
1162 """parse a localized time string and return a (unixtime, offset) tuple.
1161 The date may be a "unixtime offset" string or in one of the specified
1163 The date may be a "unixtime offset" string or in one of the specified
1162 formats."""
1164 formats."""
1163 if not string:
1165 if not string:
1164 return 0, 0
1166 return 0, 0
1165 if not formats:
1167 if not formats:
1166 formats = defaultdateformats
1168 formats = defaultdateformats
1167 string = string.strip()
1169 string = string.strip()
1168 try:
1170 try:
1169 when, offset = map(int, string.split(' '))
1171 when, offset = map(int, string.split(' '))
1170 except ValueError:
1172 except ValueError:
1171 # fill out defaults
1173 # fill out defaults
1172 if not defaults:
1174 if not defaults:
1173 defaults = {}
1175 defaults = {}
1174 now = makedate()
1176 now = makedate()
1175 for part in "d mb yY HI M S".split():
1177 for part in "d mb yY HI M S".split():
1176 if part not in defaults:
1178 if part not in defaults:
1177 if part[0] in "HMS":
1179 if part[0] in "HMS":
1178 defaults[part] = "00"
1180 defaults[part] = "00"
1179 elif part[0] in "dm":
1181 elif part[0] in "dm":
1180 defaults[part] = "1"
1182 defaults[part] = "1"
1181 else:
1183 else:
1182 defaults[part] = datestr(now, "%" + part[0], False)
1184 defaults[part] = datestr(now, "%" + part[0], False)
1183
1185
1184 for format in formats:
1186 for format in formats:
1185 try:
1187 try:
1186 when, offset = strdate(string, format, defaults)
1188 when, offset = strdate(string, format, defaults)
1187 except ValueError:
1189 except ValueError:
1188 pass
1190 pass
1189 else:
1191 else:
1190 break
1192 break
1191 else:
1193 else:
1192 raise Abort(_('invalid date: %r ') % string)
1194 raise Abort(_('invalid date: %r ') % string)
1193 # validate explicit (probably user-specified) date and
1195 # validate explicit (probably user-specified) date and
1194 # time zone offset. values must fit in signed 32 bits for
1196 # time zone offset. values must fit in signed 32 bits for
1195 # current 32-bit linux runtimes. timezones go from UTC-12
1197 # current 32-bit linux runtimes. timezones go from UTC-12
1196 # to UTC+14
1198 # to UTC+14
1197 if abs(when) > 0x7fffffff:
1199 if abs(when) > 0x7fffffff:
1198 raise Abort(_('date exceeds 32 bits: %d') % when)
1200 raise Abort(_('date exceeds 32 bits: %d') % when)
1199 if offset < -50400 or offset > 43200:
1201 if offset < -50400 or offset > 43200:
1200 raise Abort(_('impossible time zone offset: %d') % offset)
1202 raise Abort(_('impossible time zone offset: %d') % offset)
1201 return when, offset
1203 return when, offset
1202
1204
1203 def matchdate(date):
1205 def matchdate(date):
1204 """Return a function that matches a given date match specifier
1206 """Return a function that matches a given date match specifier
1205
1207
1206 Formats include:
1208 Formats include:
1207
1209
1208 '{date}' match a given date to the accuracy provided
1210 '{date}' match a given date to the accuracy provided
1209
1211
1210 '<{date}' on or before a given date
1212 '<{date}' on or before a given date
1211
1213
1212 '>{date}' on or after a given date
1214 '>{date}' on or after a given date
1213
1215
1214 """
1216 """
1215
1217
1216 def lower(date):
1218 def lower(date):
1217 return parsedate(date, extendeddateformats)[0]
1219 return parsedate(date, extendeddateformats)[0]
1218
1220
1219 def upper(date):
1221 def upper(date):
1220 d = dict(mb="12", HI="23", M="59", S="59")
1222 d = dict(mb="12", HI="23", M="59", S="59")
1221 for days in "31 30 29".split():
1223 for days in "31 30 29".split():
1222 try:
1224 try:
1223 d["d"] = days
1225 d["d"] = days
1224 return parsedate(date, extendeddateformats, d)[0]
1226 return parsedate(date, extendeddateformats, d)[0]
1225 except:
1227 except:
1226 pass
1228 pass
1227 d["d"] = "28"
1229 d["d"] = "28"
1228 return parsedate(date, extendeddateformats, d)[0]
1230 return parsedate(date, extendeddateformats, d)[0]
1229
1231
1230 if date[0] == "<":
1232 if date[0] == "<":
1231 when = upper(date[1:])
1233 when = upper(date[1:])
1232 return lambda x: x <= when
1234 return lambda x: x <= when
1233 elif date[0] == ">":
1235 elif date[0] == ">":
1234 when = lower(date[1:])
1236 when = lower(date[1:])
1235 return lambda x: x >= when
1237 return lambda x: x >= when
1236 elif date[0] == "-":
1238 elif date[0] == "-":
1237 try:
1239 try:
1238 days = int(date[1:])
1240 days = int(date[1:])
1239 except ValueError:
1241 except ValueError:
1240 raise Abort(_("invalid day spec: %s") % date[1:])
1242 raise Abort(_("invalid day spec: %s") % date[1:])
1241 when = makedate()[0] - days * 3600 * 24
1243 when = makedate()[0] - days * 3600 * 24
1242 return lambda x: x >= when
1244 return lambda x: x >= when
1243 elif " to " in date:
1245 elif " to " in date:
1244 a, b = date.split(" to ")
1246 a, b = date.split(" to ")
1245 start, stop = lower(a), upper(b)
1247 start, stop = lower(a), upper(b)
1246 return lambda x: x >= start and x <= stop
1248 return lambda x: x >= start and x <= stop
1247 else:
1249 else:
1248 start, stop = lower(date), upper(date)
1250 start, stop = lower(date), upper(date)
1249 return lambda x: x >= start and x <= stop
1251 return lambda x: x >= start and x <= stop
1250
1252
1251 def shortuser(user):
1253 def shortuser(user):
1252 """Return a short representation of a user name or email address."""
1254 """Return a short representation of a user name or email address."""
1253 f = user.find('@')
1255 f = user.find('@')
1254 if f >= 0:
1256 if f >= 0:
1255 user = user[:f]
1257 user = user[:f]
1256 f = user.find('<')
1258 f = user.find('<')
1257 if f >= 0:
1259 if f >= 0:
1258 user = user[f+1:]
1260 user = user[f+1:]
1259 f = user.find(' ')
1261 f = user.find(' ')
1260 if f >= 0:
1262 if f >= 0:
1261 user = user[:f]
1263 user = user[:f]
1262 f = user.find('.')
1264 f = user.find('.')
1263 if f >= 0:
1265 if f >= 0:
1264 user = user[:f]
1266 user = user[:f]
1265 return user
1267 return user
1266
1268
1267 def ellipsis(text, maxlength=400):
1269 def ellipsis(text, maxlength=400):
1268 """Trim string to at most maxlength (default: 400) characters."""
1270 """Trim string to at most maxlength (default: 400) characters."""
1269 if len(text) <= maxlength:
1271 if len(text) <= maxlength:
1270 return text
1272 return text
1271 else:
1273 else:
1272 return "%s..." % (text[:maxlength-3])
1274 return "%s..." % (text[:maxlength-3])
1273
1275
1274 def walkrepos(path):
1276 def walkrepos(path):
1275 '''yield every hg repository under path, recursively.'''
1277 '''yield every hg repository under path, recursively.'''
1276 def errhandler(err):
1278 def errhandler(err):
1277 if err.filename == path:
1279 if err.filename == path:
1278 raise err
1280 raise err
1279
1281
1280 for root, dirs, files in os.walk(path, onerror=errhandler):
1282 for root, dirs, files in os.walk(path, onerror=errhandler):
1281 for d in dirs:
1283 for d in dirs:
1282 if d == '.hg':
1284 if d == '.hg':
1283 yield root
1285 yield root
1284 dirs[:] = []
1286 dirs[:] = []
1285 break
1287 break
1286
1288
1287 _rcpath = None
1289 _rcpath = None
1288
1290
1289 def rcpath():
1291 def rcpath():
1290 '''return hgrc search path. if env var HGRCPATH is set, use it.
1292 '''return hgrc search path. if env var HGRCPATH is set, use it.
1291 for each item in path, if directory, use files ending in .rc,
1293 for each item in path, if directory, use files ending in .rc,
1292 else use item.
1294 else use item.
1293 make HGRCPATH empty to only look in .hg/hgrc of current repo.
1295 make HGRCPATH empty to only look in .hg/hgrc of current repo.
1294 if no HGRCPATH, use default os-specific path.'''
1296 if no HGRCPATH, use default os-specific path.'''
1295 global _rcpath
1297 global _rcpath
1296 if _rcpath is None:
1298 if _rcpath is None:
1297 if 'HGRCPATH' in os.environ:
1299 if 'HGRCPATH' in os.environ:
1298 _rcpath = []
1300 _rcpath = []
1299 for p in os.environ['HGRCPATH'].split(os.pathsep):
1301 for p in os.environ['HGRCPATH'].split(os.pathsep):
1300 if not p: continue
1302 if not p: continue
1301 if os.path.isdir(p):
1303 if os.path.isdir(p):
1302 for f in os.listdir(p):
1304 for f in os.listdir(p):
1303 if f.endswith('.rc'):
1305 if f.endswith('.rc'):
1304 _rcpath.append(os.path.join(p, f))
1306 _rcpath.append(os.path.join(p, f))
1305 else:
1307 else:
1306 _rcpath.append(p)
1308 _rcpath.append(p)
1307 else:
1309 else:
1308 _rcpath = os_rcpath()
1310 _rcpath = os_rcpath()
1309 return _rcpath
1311 return _rcpath
1310
1312
1311 def bytecount(nbytes):
1313 def bytecount(nbytes):
1312 '''return byte count formatted as readable string, with units'''
1314 '''return byte count formatted as readable string, with units'''
1313
1315
1314 units = (
1316 units = (
1315 (100, 1<<30, _('%.0f GB')),
1317 (100, 1<<30, _('%.0f GB')),
1316 (10, 1<<30, _('%.1f GB')),
1318 (10, 1<<30, _('%.1f GB')),
1317 (1, 1<<30, _('%.2f GB')),
1319 (1, 1<<30, _('%.2f GB')),
1318 (100, 1<<20, _('%.0f MB')),
1320 (100, 1<<20, _('%.0f MB')),
1319 (10, 1<<20, _('%.1f MB')),
1321 (10, 1<<20, _('%.1f MB')),
1320 (1, 1<<20, _('%.2f MB')),
1322 (1, 1<<20, _('%.2f MB')),
1321 (100, 1<<10, _('%.0f KB')),
1323 (100, 1<<10, _('%.0f KB')),
1322 (10, 1<<10, _('%.1f KB')),
1324 (10, 1<<10, _('%.1f KB')),
1323 (1, 1<<10, _('%.2f KB')),
1325 (1, 1<<10, _('%.2f KB')),
1324 (1, 1, _('%.0f bytes')),
1326 (1, 1, _('%.0f bytes')),
1325 )
1327 )
1326
1328
1327 for multiplier, divisor, format in units:
1329 for multiplier, divisor, format in units:
1328 if nbytes >= divisor * multiplier:
1330 if nbytes >= divisor * multiplier:
1329 return format % (nbytes / float(divisor))
1331 return format % (nbytes / float(divisor))
1330 return units[-1][2] % nbytes
1332 return units[-1][2] % nbytes
1331
1333
1332 def drop_scheme(scheme, path):
1334 def drop_scheme(scheme, path):
1333 sc = scheme + ':'
1335 sc = scheme + ':'
1334 if path.startswith(sc):
1336 if path.startswith(sc):
1335 path = path[len(sc):]
1337 path = path[len(sc):]
1336 if path.startswith('//'):
1338 if path.startswith('//'):
1337 path = path[2:]
1339 path = path[2:]
1338 return path
1340 return path
General Comments 0
You need to be logged in to leave comments. Login now