##// END OF EJS Templates
Move opener to utils...
mpm@selenic.com -
r1090:1bca39b8 default
parent child Browse files
Show More
@@ -1,1879 +1,1879
1 # commands.py - command processing for mercurial
1 # commands.py - command processing for mercurial
2 #
2 #
3 # Copyright 2005 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005 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 demandload(globals(), "os re sys signal shutil imp")
9 demandload(globals(), "os re sys signal shutil imp")
10 demandload(globals(), "fancyopts ui hg util lock")
10 demandload(globals(), "fancyopts ui hg util lock")
11 demandload(globals(), "fnmatch hgweb mdiff random signal time traceback")
11 demandload(globals(), "fnmatch hgweb mdiff random signal time traceback")
12 demandload(globals(), "errno socket version struct atexit sets")
12 demandload(globals(), "errno socket version struct atexit sets")
13
13
14 class UnknownCommand(Exception):
14 class UnknownCommand(Exception):
15 """Exception raised if command is not in the command table."""
15 """Exception raised if command is not in the command table."""
16
16
17 def filterfiles(filters, files):
17 def filterfiles(filters, files):
18 l = [x for x in files if x in filters]
18 l = [x for x in files if x in filters]
19
19
20 for t in filters:
20 for t in filters:
21 if t and t[-1] != "/":
21 if t and t[-1] != "/":
22 t += "/"
22 t += "/"
23 l += [x for x in files if x.startswith(t)]
23 l += [x for x in files if x.startswith(t)]
24 return l
24 return l
25
25
26 def relpath(repo, args):
26 def relpath(repo, args):
27 cwd = repo.getcwd()
27 cwd = repo.getcwd()
28 if cwd:
28 if cwd:
29 return [util.normpath(os.path.join(cwd, x)) for x in args]
29 return [util.normpath(os.path.join(cwd, x)) for x in args]
30 return args
30 return args
31
31
32 def matchpats(repo, cwd, pats=[], opts={}, head=''):
32 def matchpats(repo, cwd, pats=[], opts={}, head=''):
33 return util.matcher(repo.root, cwd, pats or ['.'], opts.get('include'),
33 return util.matcher(repo.root, cwd, pats or ['.'], opts.get('include'),
34 opts.get('exclude'), head)
34 opts.get('exclude'), head)
35
35
36 def makewalk(repo, pats, opts, head=''):
36 def makewalk(repo, pats, opts, head=''):
37 cwd = repo.getcwd()
37 cwd = repo.getcwd()
38 files, matchfn, anypats = matchpats(repo, cwd, pats, opts, head)
38 files, matchfn, anypats = matchpats(repo, cwd, pats, opts, head)
39 exact = dict(zip(files, files))
39 exact = dict(zip(files, files))
40 def walk():
40 def walk():
41 for src, fn in repo.walk(files=files, match=matchfn):
41 for src, fn in repo.walk(files=files, match=matchfn):
42 yield src, fn, util.pathto(cwd, fn), fn in exact
42 yield src, fn, util.pathto(cwd, fn), fn in exact
43 return files, matchfn, walk()
43 return files, matchfn, walk()
44
44
45 def walk(repo, pats, opts, head=''):
45 def walk(repo, pats, opts, head=''):
46 files, matchfn, results = makewalk(repo, pats, opts, head)
46 files, matchfn, results = makewalk(repo, pats, opts, head)
47 for r in results:
47 for r in results:
48 yield r
48 yield r
49
49
50 def walkchangerevs(ui, repo, cwd, pats, opts):
50 def walkchangerevs(ui, repo, cwd, pats, opts):
51 # This code most commonly needs to iterate backwards over the
51 # This code most commonly needs to iterate backwards over the
52 # history it is interested in. Doing so has awful
52 # history it is interested in. Doing so has awful
53 # (quadratic-looking) performance, so we use iterators in a
53 # (quadratic-looking) performance, so we use iterators in a
54 # "windowed" way. Walk forwards through a window of revisions,
54 # "windowed" way. Walk forwards through a window of revisions,
55 # yielding them in the desired order, and walk the windows
55 # yielding them in the desired order, and walk the windows
56 # themselves backwards.
56 # themselves backwards.
57 cwd = repo.getcwd()
57 cwd = repo.getcwd()
58 if not pats and cwd:
58 if not pats and cwd:
59 opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
59 opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
60 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
60 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
61 files, matchfn, anypats = matchpats(repo, (pats and cwd) or '',
61 files, matchfn, anypats = matchpats(repo, (pats and cwd) or '',
62 pats, opts)
62 pats, opts)
63 revs = map(int, revrange(ui, repo, opts['rev'] or ['tip:0']))
63 revs = map(int, revrange(ui, repo, opts['rev'] or ['tip:0']))
64 wanted = {}
64 wanted = {}
65 slowpath = anypats
65 slowpath = anypats
66 window = 300
66 window = 300
67 fncache = {}
67 fncache = {}
68 if not slowpath and not files:
68 if not slowpath and not files:
69 # No files, no patterns. Display all revs.
69 # No files, no patterns. Display all revs.
70 wanted = dict(zip(revs, revs))
70 wanted = dict(zip(revs, revs))
71 if not slowpath:
71 if not slowpath:
72 # Only files, no patterns. Check the history of each file.
72 # Only files, no patterns. Check the history of each file.
73 def filerevgen(filelog):
73 def filerevgen(filelog):
74 for i in xrange(filelog.count() - 1, -1, -window):
74 for i in xrange(filelog.count() - 1, -1, -window):
75 revs = []
75 revs = []
76 for j in xrange(max(0, i - window), i + 1):
76 for j in xrange(max(0, i - window), i + 1):
77 revs.append(filelog.linkrev(filelog.node(j)))
77 revs.append(filelog.linkrev(filelog.node(j)))
78 revs.reverse()
78 revs.reverse()
79 for rev in revs:
79 for rev in revs:
80 yield rev
80 yield rev
81
81
82 minrev, maxrev = min(revs), max(revs)
82 minrev, maxrev = min(revs), max(revs)
83 for file in files:
83 for file in files:
84 filelog = repo.file(file)
84 filelog = repo.file(file)
85 # A zero count may be a directory or deleted file, so
85 # A zero count may be a directory or deleted file, so
86 # try to find matching entries on the slow path.
86 # try to find matching entries on the slow path.
87 if filelog.count() == 0:
87 if filelog.count() == 0:
88 slowpath = True
88 slowpath = True
89 break
89 break
90 for rev in filerevgen(filelog):
90 for rev in filerevgen(filelog):
91 if rev <= maxrev:
91 if rev <= maxrev:
92 if rev < minrev:
92 if rev < minrev:
93 break
93 break
94 fncache.setdefault(rev, [])
94 fncache.setdefault(rev, [])
95 fncache[rev].append(file)
95 fncache[rev].append(file)
96 wanted[rev] = 1
96 wanted[rev] = 1
97 if slowpath:
97 if slowpath:
98 # The slow path checks files modified in every changeset.
98 # The slow path checks files modified in every changeset.
99 def changerevgen():
99 def changerevgen():
100 for i in xrange(repo.changelog.count() - 1, -1, -window):
100 for i in xrange(repo.changelog.count() - 1, -1, -window):
101 for j in xrange(max(0, i - window), i + 1):
101 for j in xrange(max(0, i - window), i + 1):
102 yield j, repo.changelog.read(repo.lookup(str(j)))[3]
102 yield j, repo.changelog.read(repo.lookup(str(j)))[3]
103
103
104 for rev, changefiles in changerevgen():
104 for rev, changefiles in changerevgen():
105 matches = filter(matchfn, changefiles)
105 matches = filter(matchfn, changefiles)
106 if matches:
106 if matches:
107 fncache[rev] = matches
107 fncache[rev] = matches
108 wanted[rev] = 1
108 wanted[rev] = 1
109
109
110 for i in xrange(0, len(revs), window):
110 for i in xrange(0, len(revs), window):
111 yield 'window', revs[0] < revs[-1], revs[-1]
111 yield 'window', revs[0] < revs[-1], revs[-1]
112 nrevs = [rev for rev in revs[i:min(i+window, len(revs))]
112 nrevs = [rev for rev in revs[i:min(i+window, len(revs))]
113 if rev in wanted]
113 if rev in wanted]
114 srevs = list(nrevs)
114 srevs = list(nrevs)
115 srevs.sort()
115 srevs.sort()
116 for rev in srevs:
116 for rev in srevs:
117 fns = fncache.get(rev)
117 fns = fncache.get(rev)
118 if not fns:
118 if not fns:
119 fns = repo.changelog.read(repo.lookup(str(rev)))[3]
119 fns = repo.changelog.read(repo.lookup(str(rev)))[3]
120 fns = filter(matchfn, fns)
120 fns = filter(matchfn, fns)
121 yield 'add', rev, fns
121 yield 'add', rev, fns
122 for rev in nrevs:
122 for rev in nrevs:
123 yield 'iter', rev, None
123 yield 'iter', rev, None
124
124
125 revrangesep = ':'
125 revrangesep = ':'
126
126
127 def revrange(ui, repo, revs, revlog=None):
127 def revrange(ui, repo, revs, revlog=None):
128 """Yield revision as strings from a list of revision specifications."""
128 """Yield revision as strings from a list of revision specifications."""
129 if revlog is None:
129 if revlog is None:
130 revlog = repo.changelog
130 revlog = repo.changelog
131 revcount = revlog.count()
131 revcount = revlog.count()
132 def fix(val, defval):
132 def fix(val, defval):
133 if not val:
133 if not val:
134 return defval
134 return defval
135 try:
135 try:
136 num = int(val)
136 num = int(val)
137 if str(num) != val:
137 if str(num) != val:
138 raise ValueError
138 raise ValueError
139 if num < 0:
139 if num < 0:
140 num += revcount
140 num += revcount
141 if not (0 <= num < revcount):
141 if not (0 <= num < revcount):
142 raise ValueError
142 raise ValueError
143 except ValueError:
143 except ValueError:
144 try:
144 try:
145 num = repo.changelog.rev(repo.lookup(val))
145 num = repo.changelog.rev(repo.lookup(val))
146 except KeyError:
146 except KeyError:
147 try:
147 try:
148 num = revlog.rev(revlog.lookup(val))
148 num = revlog.rev(revlog.lookup(val))
149 except KeyError:
149 except KeyError:
150 raise util.Abort('invalid revision identifier %s', val)
150 raise util.Abort('invalid revision identifier %s', val)
151 return num
151 return num
152 for spec in revs:
152 for spec in revs:
153 if spec.find(revrangesep) >= 0:
153 if spec.find(revrangesep) >= 0:
154 start, end = spec.split(revrangesep, 1)
154 start, end = spec.split(revrangesep, 1)
155 start = fix(start, 0)
155 start = fix(start, 0)
156 end = fix(end, revcount - 1)
156 end = fix(end, revcount - 1)
157 step = start > end and -1 or 1
157 step = start > end and -1 or 1
158 for rev in xrange(start, end+step, step):
158 for rev in xrange(start, end+step, step):
159 yield str(rev)
159 yield str(rev)
160 else:
160 else:
161 yield str(fix(spec, None))
161 yield str(fix(spec, None))
162
162
163 def make_filename(repo, r, pat, node=None,
163 def make_filename(repo, r, pat, node=None,
164 total=None, seqno=None, revwidth=None):
164 total=None, seqno=None, revwidth=None):
165 node_expander = {
165 node_expander = {
166 'H': lambda: hg.hex(node),
166 'H': lambda: hg.hex(node),
167 'R': lambda: str(r.rev(node)),
167 'R': lambda: str(r.rev(node)),
168 'h': lambda: hg.short(node),
168 'h': lambda: hg.short(node),
169 }
169 }
170 expander = {
170 expander = {
171 '%': lambda: '%',
171 '%': lambda: '%',
172 'b': lambda: os.path.basename(repo.root),
172 'b': lambda: os.path.basename(repo.root),
173 }
173 }
174
174
175 try:
175 try:
176 if node:
176 if node:
177 expander.update(node_expander)
177 expander.update(node_expander)
178 if node and revwidth is not None:
178 if node and revwidth is not None:
179 expander['r'] = lambda: str(r.rev(node)).zfill(revwidth)
179 expander['r'] = lambda: str(r.rev(node)).zfill(revwidth)
180 if total is not None:
180 if total is not None:
181 expander['N'] = lambda: str(total)
181 expander['N'] = lambda: str(total)
182 if seqno is not None:
182 if seqno is not None:
183 expander['n'] = lambda: str(seqno)
183 expander['n'] = lambda: str(seqno)
184 if total is not None and seqno is not None:
184 if total is not None and seqno is not None:
185 expander['n'] = lambda:str(seqno).zfill(len(str(total)))
185 expander['n'] = lambda:str(seqno).zfill(len(str(total)))
186
186
187 newname = []
187 newname = []
188 patlen = len(pat)
188 patlen = len(pat)
189 i = 0
189 i = 0
190 while i < patlen:
190 while i < patlen:
191 c = pat[i]
191 c = pat[i]
192 if c == '%':
192 if c == '%':
193 i += 1
193 i += 1
194 c = pat[i]
194 c = pat[i]
195 c = expander[c]()
195 c = expander[c]()
196 newname.append(c)
196 newname.append(c)
197 i += 1
197 i += 1
198 return ''.join(newname)
198 return ''.join(newname)
199 except KeyError, inst:
199 except KeyError, inst:
200 raise util.Abort("invalid format spec '%%%s' in output file name",
200 raise util.Abort("invalid format spec '%%%s' in output file name",
201 inst.args[0])
201 inst.args[0])
202
202
203 def make_file(repo, r, pat, node=None,
203 def make_file(repo, r, pat, node=None,
204 total=None, seqno=None, revwidth=None, mode='wb'):
204 total=None, seqno=None, revwidth=None, mode='wb'):
205 if not pat or pat == '-':
205 if not pat or pat == '-':
206 return 'w' in mode and sys.stdout or sys.stdin
206 return 'w' in mode and sys.stdout or sys.stdin
207 if hasattr(pat, 'write') and 'w' in mode:
207 if hasattr(pat, 'write') and 'w' in mode:
208 return pat
208 return pat
209 if hasattr(pat, 'read') and 'r' in mode:
209 if hasattr(pat, 'read') and 'r' in mode:
210 return pat
210 return pat
211 return open(make_filename(repo, r, pat, node, total, seqno, revwidth),
211 return open(make_filename(repo, r, pat, node, total, seqno, revwidth),
212 mode)
212 mode)
213
213
214 def dodiff(fp, ui, repo, node1, node2, files=None, match=util.always,
214 def dodiff(fp, ui, repo, node1, node2, files=None, match=util.always,
215 changes=None, text=False):
215 changes=None, text=False):
216 def date(c):
216 def date(c):
217 return time.asctime(time.gmtime(float(c[2].split(' ')[0])))
217 return time.asctime(time.gmtime(float(c[2].split(' ')[0])))
218
218
219 if not changes:
219 if not changes:
220 (c, a, d, u) = repo.changes(node1, node2, files, match=match)
220 (c, a, d, u) = repo.changes(node1, node2, files, match=match)
221 else:
221 else:
222 (c, a, d, u) = changes
222 (c, a, d, u) = changes
223 if files:
223 if files:
224 c, a, d = map(lambda x: filterfiles(files, x), (c, a, d))
224 c, a, d = map(lambda x: filterfiles(files, x), (c, a, d))
225
225
226 if not c and not a and not d:
226 if not c and not a and not d:
227 return
227 return
228
228
229 if node2:
229 if node2:
230 change = repo.changelog.read(node2)
230 change = repo.changelog.read(node2)
231 mmap2 = repo.manifest.read(change[0])
231 mmap2 = repo.manifest.read(change[0])
232 date2 = date(change)
232 date2 = date(change)
233 def read(f):
233 def read(f):
234 return repo.file(f).read(mmap2[f])
234 return repo.file(f).read(mmap2[f])
235 else:
235 else:
236 date2 = time.asctime()
236 date2 = time.asctime()
237 if not node1:
237 if not node1:
238 node1 = repo.dirstate.parents()[0]
238 node1 = repo.dirstate.parents()[0]
239 def read(f):
239 def read(f):
240 return repo.wfile(f).read()
240 return repo.wfile(f).read()
241
241
242 if ui.quiet:
242 if ui.quiet:
243 r = None
243 r = None
244 else:
244 else:
245 hexfunc = ui.verbose and hg.hex or hg.short
245 hexfunc = ui.verbose and hg.hex or hg.short
246 r = [hexfunc(node) for node in [node1, node2] if node]
246 r = [hexfunc(node) for node in [node1, node2] if node]
247
247
248 change = repo.changelog.read(node1)
248 change = repo.changelog.read(node1)
249 mmap = repo.manifest.read(change[0])
249 mmap = repo.manifest.read(change[0])
250 date1 = date(change)
250 date1 = date(change)
251
251
252 for f in c:
252 for f in c:
253 to = None
253 to = None
254 if f in mmap:
254 if f in mmap:
255 to = repo.file(f).read(mmap[f])
255 to = repo.file(f).read(mmap[f])
256 tn = read(f)
256 tn = read(f)
257 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text))
257 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text))
258 for f in a:
258 for f in a:
259 to = None
259 to = None
260 tn = read(f)
260 tn = read(f)
261 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text))
261 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text))
262 for f in d:
262 for f in d:
263 to = repo.file(f).read(mmap[f])
263 to = repo.file(f).read(mmap[f])
264 tn = None
264 tn = None
265 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text))
265 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text))
266
266
267 def show_changeset(ui, repo, rev=0, changenode=None, brinfo=None):
267 def show_changeset(ui, repo, rev=0, changenode=None, brinfo=None):
268 """show a single changeset or file revision"""
268 """show a single changeset or file revision"""
269 log = repo.changelog
269 log = repo.changelog
270 if changenode is None:
270 if changenode is None:
271 changenode = log.node(rev)
271 changenode = log.node(rev)
272 elif not rev:
272 elif not rev:
273 rev = log.rev(changenode)
273 rev = log.rev(changenode)
274
274
275 if ui.quiet:
275 if ui.quiet:
276 ui.write("%d:%s\n" % (rev, hg.short(changenode)))
276 ui.write("%d:%s\n" % (rev, hg.short(changenode)))
277 return
277 return
278
278
279 changes = log.read(changenode)
279 changes = log.read(changenode)
280
280
281 t, tz = changes[2].split(' ')
281 t, tz = changes[2].split(' ')
282 # a conversion tool was sticking non-integer offsets into repos
282 # a conversion tool was sticking non-integer offsets into repos
283 try:
283 try:
284 tz = int(tz)
284 tz = int(tz)
285 except ValueError:
285 except ValueError:
286 tz = 0
286 tz = 0
287 date = time.asctime(time.localtime(float(t))) + " %+05d" % (int(tz)/-36)
287 date = time.asctime(time.localtime(float(t))) + " %+05d" % (int(tz)/-36)
288
288
289 parents = [(log.rev(p), ui.verbose and hg.hex(p) or hg.short(p))
289 parents = [(log.rev(p), ui.verbose and hg.hex(p) or hg.short(p))
290 for p in log.parents(changenode)
290 for p in log.parents(changenode)
291 if ui.debugflag or p != hg.nullid]
291 if ui.debugflag or p != hg.nullid]
292 if not ui.debugflag and len(parents) == 1 and parents[0][0] == rev-1:
292 if not ui.debugflag and len(parents) == 1 and parents[0][0] == rev-1:
293 parents = []
293 parents = []
294
294
295 if ui.verbose:
295 if ui.verbose:
296 ui.write("changeset: %d:%s\n" % (rev, hg.hex(changenode)))
296 ui.write("changeset: %d:%s\n" % (rev, hg.hex(changenode)))
297 else:
297 else:
298 ui.write("changeset: %d:%s\n" % (rev, hg.short(changenode)))
298 ui.write("changeset: %d:%s\n" % (rev, hg.short(changenode)))
299
299
300 for tag in repo.nodetags(changenode):
300 for tag in repo.nodetags(changenode):
301 ui.status("tag: %s\n" % tag)
301 ui.status("tag: %s\n" % tag)
302 for parent in parents:
302 for parent in parents:
303 ui.write("parent: %d:%s\n" % parent)
303 ui.write("parent: %d:%s\n" % parent)
304
304
305 if brinfo and changenode in brinfo:
305 if brinfo and changenode in brinfo:
306 br = brinfo[changenode]
306 br = brinfo[changenode]
307 ui.write("branch: %s\n" % " ".join(br))
307 ui.write("branch: %s\n" % " ".join(br))
308
308
309 ui.debug("manifest: %d:%s\n" % (repo.manifest.rev(changes[0]),
309 ui.debug("manifest: %d:%s\n" % (repo.manifest.rev(changes[0]),
310 hg.hex(changes[0])))
310 hg.hex(changes[0])))
311 ui.status("user: %s\n" % changes[1])
311 ui.status("user: %s\n" % changes[1])
312 ui.status("date: %s\n" % date)
312 ui.status("date: %s\n" % date)
313
313
314 if ui.debugflag:
314 if ui.debugflag:
315 files = repo.changes(log.parents(changenode)[0], changenode)
315 files = repo.changes(log.parents(changenode)[0], changenode)
316 for key, value in zip(["files:", "files+:", "files-:"], files):
316 for key, value in zip(["files:", "files+:", "files-:"], files):
317 if value:
317 if value:
318 ui.note("%-12s %s\n" % (key, " ".join(value)))
318 ui.note("%-12s %s\n" % (key, " ".join(value)))
319 else:
319 else:
320 ui.note("files: %s\n" % " ".join(changes[3]))
320 ui.note("files: %s\n" % " ".join(changes[3]))
321
321
322 description = changes[4].strip()
322 description = changes[4].strip()
323 if description:
323 if description:
324 if ui.verbose:
324 if ui.verbose:
325 ui.status("description:\n")
325 ui.status("description:\n")
326 ui.status(description)
326 ui.status(description)
327 ui.status("\n\n")
327 ui.status("\n\n")
328 else:
328 else:
329 ui.status("summary: %s\n" % description.splitlines()[0])
329 ui.status("summary: %s\n" % description.splitlines()[0])
330 ui.status("\n")
330 ui.status("\n")
331
331
332 def show_version(ui):
332 def show_version(ui):
333 """output version and copyright information"""
333 """output version and copyright information"""
334 ui.write("Mercurial Distributed SCM (version %s)\n"
334 ui.write("Mercurial Distributed SCM (version %s)\n"
335 % version.get_version())
335 % version.get_version())
336 ui.status(
336 ui.status(
337 "\nCopyright (C) 2005 Matt Mackall <mpm@selenic.com>\n"
337 "\nCopyright (C) 2005 Matt Mackall <mpm@selenic.com>\n"
338 "This is free software; see the source for copying conditions. "
338 "This is free software; see the source for copying conditions. "
339 "There is NO\nwarranty; "
339 "There is NO\nwarranty; "
340 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
340 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
341 )
341 )
342
342
343 def help_(ui, cmd=None, with_version=False):
343 def help_(ui, cmd=None, with_version=False):
344 """show help for a given command or all commands"""
344 """show help for a given command or all commands"""
345 option_lists = []
345 option_lists = []
346 if cmd and cmd != 'shortlist':
346 if cmd and cmd != 'shortlist':
347 if with_version:
347 if with_version:
348 show_version(ui)
348 show_version(ui)
349 ui.write('\n')
349 ui.write('\n')
350 key, i = find(cmd)
350 key, i = find(cmd)
351 # synopsis
351 # synopsis
352 ui.write("%s\n\n" % i[2])
352 ui.write("%s\n\n" % i[2])
353
353
354 # description
354 # description
355 doc = i[0].__doc__
355 doc = i[0].__doc__
356 if ui.quiet:
356 if ui.quiet:
357 doc = doc.splitlines(0)[0]
357 doc = doc.splitlines(0)[0]
358 ui.write("%s\n" % doc.rstrip())
358 ui.write("%s\n" % doc.rstrip())
359
359
360 if not ui.quiet:
360 if not ui.quiet:
361 # aliases
361 # aliases
362 aliases = ', '.join(key.split('|')[1:])
362 aliases = ', '.join(key.split('|')[1:])
363 if aliases:
363 if aliases:
364 ui.write("\naliases: %s\n" % aliases)
364 ui.write("\naliases: %s\n" % aliases)
365
365
366 # options
366 # options
367 if i[1]:
367 if i[1]:
368 option_lists.append(("options", i[1]))
368 option_lists.append(("options", i[1]))
369
369
370 else:
370 else:
371 # program name
371 # program name
372 if ui.verbose or with_version:
372 if ui.verbose or with_version:
373 show_version(ui)
373 show_version(ui)
374 else:
374 else:
375 ui.status("Mercurial Distributed SCM\n")
375 ui.status("Mercurial Distributed SCM\n")
376 ui.status('\n')
376 ui.status('\n')
377
377
378 # list of commands
378 # list of commands
379 if cmd == "shortlist":
379 if cmd == "shortlist":
380 ui.status('basic commands (use "hg help" '
380 ui.status('basic commands (use "hg help" '
381 'for the full list or option "-v" for details):\n\n')
381 'for the full list or option "-v" for details):\n\n')
382 elif ui.verbose:
382 elif ui.verbose:
383 ui.status('list of commands:\n\n')
383 ui.status('list of commands:\n\n')
384 else:
384 else:
385 ui.status('list of commands (use "hg help -v" '
385 ui.status('list of commands (use "hg help -v" '
386 'to show aliases and global options):\n\n')
386 'to show aliases and global options):\n\n')
387
387
388 h = {}
388 h = {}
389 cmds = {}
389 cmds = {}
390 for c, e in table.items():
390 for c, e in table.items():
391 f = c.split("|")[0]
391 f = c.split("|")[0]
392 if cmd == "shortlist" and not f.startswith("^"):
392 if cmd == "shortlist" and not f.startswith("^"):
393 continue
393 continue
394 f = f.lstrip("^")
394 f = f.lstrip("^")
395 if not ui.debugflag and f.startswith("debug"):
395 if not ui.debugflag and f.startswith("debug"):
396 continue
396 continue
397 d = ""
397 d = ""
398 if e[0].__doc__:
398 if e[0].__doc__:
399 d = e[0].__doc__.splitlines(0)[0].rstrip()
399 d = e[0].__doc__.splitlines(0)[0].rstrip()
400 h[f] = d
400 h[f] = d
401 cmds[f]=c.lstrip("^")
401 cmds[f]=c.lstrip("^")
402
402
403 fns = h.keys()
403 fns = h.keys()
404 fns.sort()
404 fns.sort()
405 m = max(map(len, fns))
405 m = max(map(len, fns))
406 for f in fns:
406 for f in fns:
407 if ui.verbose:
407 if ui.verbose:
408 commands = cmds[f].replace("|",", ")
408 commands = cmds[f].replace("|",", ")
409 ui.write(" %s:\n %s\n"%(commands,h[f]))
409 ui.write(" %s:\n %s\n"%(commands,h[f]))
410 else:
410 else:
411 ui.write(' %-*s %s\n' % (m, f, h[f]))
411 ui.write(' %-*s %s\n' % (m, f, h[f]))
412
412
413 # global options
413 # global options
414 if ui.verbose:
414 if ui.verbose:
415 option_lists.append(("global options", globalopts))
415 option_lists.append(("global options", globalopts))
416
416
417 # list all option lists
417 # list all option lists
418 opt_output = []
418 opt_output = []
419 for title, options in option_lists:
419 for title, options in option_lists:
420 opt_output.append(("\n%s:\n" % title, None))
420 opt_output.append(("\n%s:\n" % title, None))
421 for shortopt, longopt, default, desc in options:
421 for shortopt, longopt, default, desc in options:
422 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
422 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
423 longopt and " --%s" % longopt),
423 longopt and " --%s" % longopt),
424 "%s%s" % (desc,
424 "%s%s" % (desc,
425 default and " (default: %s)" % default
425 default and " (default: %s)" % default
426 or "")))
426 or "")))
427
427
428 if opt_output:
428 if opt_output:
429 opts_len = max([len(line[0]) for line in opt_output if line[1]])
429 opts_len = max([len(line[0]) for line in opt_output if line[1]])
430 for first, second in opt_output:
430 for first, second in opt_output:
431 if second:
431 if second:
432 ui.write(" %-*s %s\n" % (opts_len, first, second))
432 ui.write(" %-*s %s\n" % (opts_len, first, second))
433 else:
433 else:
434 ui.write("%s\n" % first)
434 ui.write("%s\n" % first)
435
435
436 # Commands start here, listed alphabetically
436 # Commands start here, listed alphabetically
437
437
438 def add(ui, repo, *pats, **opts):
438 def add(ui, repo, *pats, **opts):
439 '''add the specified files on the next commit'''
439 '''add the specified files on the next commit'''
440 names = []
440 names = []
441 for src, abs, rel, exact in walk(repo, pats, opts):
441 for src, abs, rel, exact in walk(repo, pats, opts):
442 if exact:
442 if exact:
443 names.append(abs)
443 names.append(abs)
444 elif repo.dirstate.state(abs) == '?':
444 elif repo.dirstate.state(abs) == '?':
445 ui.status('adding %s\n' % rel)
445 ui.status('adding %s\n' % rel)
446 names.append(abs)
446 names.append(abs)
447 repo.add(names)
447 repo.add(names)
448
448
449 def addremove(ui, repo, *pats, **opts):
449 def addremove(ui, repo, *pats, **opts):
450 """add all new files, delete all missing files"""
450 """add all new files, delete all missing files"""
451 add, remove = [], []
451 add, remove = [], []
452 for src, abs, rel, exact in walk(repo, pats, opts):
452 for src, abs, rel, exact in walk(repo, pats, opts):
453 if src == 'f' and repo.dirstate.state(abs) == '?':
453 if src == 'f' and repo.dirstate.state(abs) == '?':
454 add.append(abs)
454 add.append(abs)
455 if not exact:
455 if not exact:
456 ui.status('adding ', rel, '\n')
456 ui.status('adding ', rel, '\n')
457 if repo.dirstate.state(abs) != 'r' and not os.path.exists(rel):
457 if repo.dirstate.state(abs) != 'r' and not os.path.exists(rel):
458 remove.append(abs)
458 remove.append(abs)
459 if not exact:
459 if not exact:
460 ui.status('removing ', rel, '\n')
460 ui.status('removing ', rel, '\n')
461 repo.add(add)
461 repo.add(add)
462 repo.remove(remove)
462 repo.remove(remove)
463
463
464 def annotate(ui, repo, *pats, **opts):
464 def annotate(ui, repo, *pats, **opts):
465 """show changeset information per file line"""
465 """show changeset information per file line"""
466 def getnode(rev):
466 def getnode(rev):
467 return hg.short(repo.changelog.node(rev))
467 return hg.short(repo.changelog.node(rev))
468
468
469 def getname(rev):
469 def getname(rev):
470 try:
470 try:
471 return bcache[rev]
471 return bcache[rev]
472 except KeyError:
472 except KeyError:
473 cl = repo.changelog.read(repo.changelog.node(rev))
473 cl = repo.changelog.read(repo.changelog.node(rev))
474 name = cl[1]
474 name = cl[1]
475 f = name.find('@')
475 f = name.find('@')
476 if f >= 0:
476 if f >= 0:
477 name = name[:f]
477 name = name[:f]
478 f = name.find('<')
478 f = name.find('<')
479 if f >= 0:
479 if f >= 0:
480 name = name[f+1:]
480 name = name[f+1:]
481 bcache[rev] = name
481 bcache[rev] = name
482 return name
482 return name
483
483
484 if not pats:
484 if not pats:
485 raise util.Abort('at least one file name or pattern required')
485 raise util.Abort('at least one file name or pattern required')
486
486
487 bcache = {}
487 bcache = {}
488 opmap = [['user', getname], ['number', str], ['changeset', getnode]]
488 opmap = [['user', getname], ['number', str], ['changeset', getnode]]
489 if not opts['user'] and not opts['changeset']:
489 if not opts['user'] and not opts['changeset']:
490 opts['number'] = 1
490 opts['number'] = 1
491
491
492 if opts['rev']:
492 if opts['rev']:
493 node = repo.changelog.lookup(opts['rev'])
493 node = repo.changelog.lookup(opts['rev'])
494 else:
494 else:
495 node = repo.dirstate.parents()[0]
495 node = repo.dirstate.parents()[0]
496 change = repo.changelog.read(node)
496 change = repo.changelog.read(node)
497 mmap = repo.manifest.read(change[0])
497 mmap = repo.manifest.read(change[0])
498
498
499 for src, abs, rel, exact in walk(repo, pats, opts):
499 for src, abs, rel, exact in walk(repo, pats, opts):
500 if abs not in mmap:
500 if abs not in mmap:
501 ui.warn("warning: %s is not in the repository!\n" % rel)
501 ui.warn("warning: %s is not in the repository!\n" % rel)
502 continue
502 continue
503
503
504 f = repo.file(abs)
504 f = repo.file(abs)
505 if not opts['text'] and util.binary(f.read(mmap[abs])):
505 if not opts['text'] and util.binary(f.read(mmap[abs])):
506 ui.write("%s: binary file\n" % rel)
506 ui.write("%s: binary file\n" % rel)
507 continue
507 continue
508
508
509 lines = f.annotate(mmap[abs])
509 lines = f.annotate(mmap[abs])
510 pieces = []
510 pieces = []
511
511
512 for o, f in opmap:
512 for o, f in opmap:
513 if opts[o]:
513 if opts[o]:
514 l = [f(n) for n, dummy in lines]
514 l = [f(n) for n, dummy in lines]
515 if l:
515 if l:
516 m = max(map(len, l))
516 m = max(map(len, l))
517 pieces.append(["%*s" % (m, x) for x in l])
517 pieces.append(["%*s" % (m, x) for x in l])
518
518
519 if pieces:
519 if pieces:
520 for p, l in zip(zip(*pieces), lines):
520 for p, l in zip(zip(*pieces), lines):
521 ui.write("%s: %s" % (" ".join(p), l[1]))
521 ui.write("%s: %s" % (" ".join(p), l[1]))
522
522
523 def cat(ui, repo, file1, rev=None, **opts):
523 def cat(ui, repo, file1, rev=None, **opts):
524 """output the latest or given revision of a file"""
524 """output the latest or given revision of a file"""
525 r = repo.file(relpath(repo, [file1])[0])
525 r = repo.file(relpath(repo, [file1])[0])
526 if rev:
526 if rev:
527 try:
527 try:
528 # assume all revision numbers are for changesets
528 # assume all revision numbers are for changesets
529 n = repo.lookup(rev)
529 n = repo.lookup(rev)
530 change = repo.changelog.read(n)
530 change = repo.changelog.read(n)
531 m = repo.manifest.read(change[0])
531 m = repo.manifest.read(change[0])
532 n = m[relpath(repo, [file1])[0]]
532 n = m[relpath(repo, [file1])[0]]
533 except hg.RepoError, KeyError:
533 except hg.RepoError, KeyError:
534 n = r.lookup(rev)
534 n = r.lookup(rev)
535 else:
535 else:
536 n = r.tip()
536 n = r.tip()
537 fp = make_file(repo, r, opts['output'], node=n)
537 fp = make_file(repo, r, opts['output'], node=n)
538 fp.write(r.read(n))
538 fp.write(r.read(n))
539
539
540 def clone(ui, source, dest=None, **opts):
540 def clone(ui, source, dest=None, **opts):
541 """make a copy of an existing repository"""
541 """make a copy of an existing repository"""
542 if dest is None:
542 if dest is None:
543 dest = os.path.basename(os.path.normpath(source))
543 dest = os.path.basename(os.path.normpath(source))
544
544
545 if os.path.exists(dest):
545 if os.path.exists(dest):
546 ui.warn("abort: destination '%s' already exists\n" % dest)
546 ui.warn("abort: destination '%s' already exists\n" % dest)
547 return 1
547 return 1
548
548
549 dest = os.path.realpath(dest)
549 dest = os.path.realpath(dest)
550
550
551 class Dircleanup:
551 class Dircleanup:
552 def __init__(self, dir_):
552 def __init__(self, dir_):
553 self.rmtree = shutil.rmtree
553 self.rmtree = shutil.rmtree
554 self.dir_ = dir_
554 self.dir_ = dir_
555 os.mkdir(dir_)
555 os.mkdir(dir_)
556 def close(self):
556 def close(self):
557 self.dir_ = None
557 self.dir_ = None
558 def __del__(self):
558 def __del__(self):
559 if self.dir_:
559 if self.dir_:
560 self.rmtree(self.dir_, True)
560 self.rmtree(self.dir_, True)
561
561
562 if opts['ssh']:
562 if opts['ssh']:
563 ui.setconfig("ui", "ssh", opts['ssh'])
563 ui.setconfig("ui", "ssh", opts['ssh'])
564 if opts['remotecmd']:
564 if opts['remotecmd']:
565 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
565 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
566
566
567 d = Dircleanup(dest)
567 d = Dircleanup(dest)
568 source = ui.expandpath(source)
568 source = ui.expandpath(source)
569 abspath = source
569 abspath = source
570 other = hg.repository(ui, source)
570 other = hg.repository(ui, source)
571
571
572 if other.dev() != -1:
572 if other.dev() != -1:
573 abspath = os.path.abspath(source)
573 abspath = os.path.abspath(source)
574 copyfile = (os.stat(dest).st_dev == other.dev()
574 copyfile = (os.stat(dest).st_dev == other.dev()
575 and getattr(os, 'link', None) or shutil.copy2)
575 and getattr(os, 'link', None) or shutil.copy2)
576 if copyfile is not shutil.copy2:
576 if copyfile is not shutil.copy2:
577 ui.note("cloning by hardlink\n")
577 ui.note("cloning by hardlink\n")
578 # we use a lock here because because we're not nicely ordered
578 # we use a lock here because because we're not nicely ordered
579 l = lock.lock(os.path.join(source, ".hg", "lock"))
579 l = lock.lock(os.path.join(source, ".hg", "lock"))
580
580
581 util.copytree(os.path.join(source, ".hg"), os.path.join(dest, ".hg"),
581 util.copytree(os.path.join(source, ".hg"), os.path.join(dest, ".hg"),
582 copyfile)
582 copyfile)
583 try:
583 try:
584 os.unlink(os.path.join(dest, ".hg", "dirstate"))
584 os.unlink(os.path.join(dest, ".hg", "dirstate"))
585 except OSError:
585 except OSError:
586 pass
586 pass
587
587
588 repo = hg.repository(ui, dest)
588 repo = hg.repository(ui, dest)
589
589
590 else:
590 else:
591 repo = hg.repository(ui, dest, create=1)
591 repo = hg.repository(ui, dest, create=1)
592 repo.pull(other)
592 repo.pull(other)
593
593
594 f = repo.opener("hgrc", "w")
594 f = repo.opener("hgrc", "w")
595 f.write("[paths]\n")
595 f.write("[paths]\n")
596 f.write("default = %s\n" % abspath)
596 f.write("default = %s\n" % abspath)
597
597
598 if not opts['noupdate']:
598 if not opts['noupdate']:
599 update(ui, repo)
599 update(ui, repo)
600
600
601 d.close()
601 d.close()
602
602
603 def commit(ui, repo, *pats, **opts):
603 def commit(ui, repo, *pats, **opts):
604 """commit the specified files or all outstanding changes"""
604 """commit the specified files or all outstanding changes"""
605 if opts['text']:
605 if opts['text']:
606 ui.warn("Warning: -t and --text is deprecated,"
606 ui.warn("Warning: -t and --text is deprecated,"
607 " please use -m or --message instead.\n")
607 " please use -m or --message instead.\n")
608 message = opts['message'] or opts['text']
608 message = opts['message'] or opts['text']
609 logfile = opts['logfile']
609 logfile = opts['logfile']
610 if not message and logfile:
610 if not message and logfile:
611 try:
611 try:
612 if logfile == '-':
612 if logfile == '-':
613 message = sys.stdin.read()
613 message = sys.stdin.read()
614 else:
614 else:
615 message = open(logfile).read()
615 message = open(logfile).read()
616 except IOError, why:
616 except IOError, why:
617 ui.warn("Can't read commit message %s: %s\n" % (logfile, why))
617 ui.warn("Can't read commit message %s: %s\n" % (logfile, why))
618
618
619 if opts['addremove']:
619 if opts['addremove']:
620 addremove(ui, repo, *pats, **opts)
620 addremove(ui, repo, *pats, **opts)
621 cwd = repo.getcwd()
621 cwd = repo.getcwd()
622 if not pats and cwd:
622 if not pats and cwd:
623 opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
623 opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
624 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
624 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
625 fns, match, anypats = matchpats(repo, (pats and repo.getcwd()) or '',
625 fns, match, anypats = matchpats(repo, (pats and repo.getcwd()) or '',
626 pats, opts)
626 pats, opts)
627 if pats:
627 if pats:
628 c, a, d, u = repo.changes(files=fns, match=match)
628 c, a, d, u = repo.changes(files=fns, match=match)
629 files = c + a + [fn for fn in d if repo.dirstate.state(fn) == 'r']
629 files = c + a + [fn for fn in d if repo.dirstate.state(fn) == 'r']
630 else:
630 else:
631 files = []
631 files = []
632 repo.commit(files, message, opts['user'], opts['date'], match)
632 repo.commit(files, message, opts['user'], opts['date'], match)
633
633
634 def copy(ui, repo, source, dest):
634 def copy(ui, repo, source, dest):
635 """mark a file as copied or renamed for the next commit"""
635 """mark a file as copied or renamed for the next commit"""
636 return repo.copy(*relpath(repo, (source, dest)))
636 return repo.copy(*relpath(repo, (source, dest)))
637
637
638 def debugcheckstate(ui, repo):
638 def debugcheckstate(ui, repo):
639 """validate the correctness of the current dirstate"""
639 """validate the correctness of the current dirstate"""
640 parent1, parent2 = repo.dirstate.parents()
640 parent1, parent2 = repo.dirstate.parents()
641 repo.dirstate.read()
641 repo.dirstate.read()
642 dc = repo.dirstate.map
642 dc = repo.dirstate.map
643 keys = dc.keys()
643 keys = dc.keys()
644 keys.sort()
644 keys.sort()
645 m1n = repo.changelog.read(parent1)[0]
645 m1n = repo.changelog.read(parent1)[0]
646 m2n = repo.changelog.read(parent2)[0]
646 m2n = repo.changelog.read(parent2)[0]
647 m1 = repo.manifest.read(m1n)
647 m1 = repo.manifest.read(m1n)
648 m2 = repo.manifest.read(m2n)
648 m2 = repo.manifest.read(m2n)
649 errors = 0
649 errors = 0
650 for f in dc:
650 for f in dc:
651 state = repo.dirstate.state(f)
651 state = repo.dirstate.state(f)
652 if state in "nr" and f not in m1:
652 if state in "nr" and f not in m1:
653 ui.warn("%s in state %s, but not in manifest1\n" % (f, state))
653 ui.warn("%s in state %s, but not in manifest1\n" % (f, state))
654 errors += 1
654 errors += 1
655 if state in "a" and f in m1:
655 if state in "a" and f in m1:
656 ui.warn("%s in state %s, but also in manifest1\n" % (f, state))
656 ui.warn("%s in state %s, but also in manifest1\n" % (f, state))
657 errors += 1
657 errors += 1
658 if state in "m" and f not in m1 and f not in m2:
658 if state in "m" and f not in m1 and f not in m2:
659 ui.warn("%s in state %s, but not in either manifest\n" %
659 ui.warn("%s in state %s, but not in either manifest\n" %
660 (f, state))
660 (f, state))
661 errors += 1
661 errors += 1
662 for f in m1:
662 for f in m1:
663 state = repo.dirstate.state(f)
663 state = repo.dirstate.state(f)
664 if state not in "nrm":
664 if state not in "nrm":
665 ui.warn("%s in manifest1, but listed as state %s" % (f, state))
665 ui.warn("%s in manifest1, but listed as state %s" % (f, state))
666 errors += 1
666 errors += 1
667 if errors:
667 if errors:
668 raise util.Abort(".hg/dirstate inconsistent with current parent's manifest")
668 raise util.Abort(".hg/dirstate inconsistent with current parent's manifest")
669
669
670 def debugconfig(ui):
670 def debugconfig(ui):
671 """show combined config settings from all hgrc files"""
671 """show combined config settings from all hgrc files"""
672 try:
672 try:
673 repo = hg.repository(ui)
673 repo = hg.repository(ui)
674 except hg.RepoError:
674 except hg.RepoError:
675 pass
675 pass
676 for section, name, value in ui.walkconfig():
676 for section, name, value in ui.walkconfig():
677 ui.write('%s.%s=%s\n' % (section, name, value))
677 ui.write('%s.%s=%s\n' % (section, name, value))
678
678
679 def debugstate(ui, repo):
679 def debugstate(ui, repo):
680 """show the contents of the current dirstate"""
680 """show the contents of the current dirstate"""
681 repo.dirstate.read()
681 repo.dirstate.read()
682 dc = repo.dirstate.map
682 dc = repo.dirstate.map
683 keys = dc.keys()
683 keys = dc.keys()
684 keys.sort()
684 keys.sort()
685 for file_ in keys:
685 for file_ in keys:
686 ui.write("%c %3o %10d %s %s\n"
686 ui.write("%c %3o %10d %s %s\n"
687 % (dc[file_][0], dc[file_][1] & 0777, dc[file_][2],
687 % (dc[file_][0], dc[file_][1] & 0777, dc[file_][2],
688 time.strftime("%x %X",
688 time.strftime("%x %X",
689 time.localtime(dc[file_][3])), file_))
689 time.localtime(dc[file_][3])), file_))
690
690
691 def debugdata(ui, file_, rev):
691 def debugdata(ui, file_, rev):
692 """dump the contents of an data file revision"""
692 """dump the contents of an data file revision"""
693 r = hg.revlog(hg.opener(""), file_[:-2] + ".i", file_)
693 r = hg.revlog(file, file_[:-2] + ".i", file_)
694 ui.write(r.revision(r.lookup(rev)))
694 ui.write(r.revision(r.lookup(rev)))
695
695
696 def debugindex(ui, file_):
696 def debugindex(ui, file_):
697 """dump the contents of an index file"""
697 """dump the contents of an index file"""
698 r = hg.revlog(hg.opener(""), file_, "")
698 r = hg.revlog(file, file_, "")
699 ui.write(" rev offset length base linkrev" +
699 ui.write(" rev offset length base linkrev" +
700 " nodeid p1 p2\n")
700 " nodeid p1 p2\n")
701 for i in range(r.count()):
701 for i in range(r.count()):
702 e = r.index[i]
702 e = r.index[i]
703 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
703 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
704 i, e[0], e[1], e[2], e[3],
704 i, e[0], e[1], e[2], e[3],
705 hg.short(e[6]), hg.short(e[4]), hg.short(e[5])))
705 hg.short(e[6]), hg.short(e[4]), hg.short(e[5])))
706
706
707 def debugindexdot(ui, file_):
707 def debugindexdot(ui, file_):
708 """dump an index DAG as a .dot file"""
708 """dump an index DAG as a .dot file"""
709 r = hg.revlog(hg.opener(""), file_, "")
709 r = hg.revlog(file, file_, "")
710 ui.write("digraph G {\n")
710 ui.write("digraph G {\n")
711 for i in range(r.count()):
711 for i in range(r.count()):
712 e = r.index[i]
712 e = r.index[i]
713 ui.write("\t%d -> %d\n" % (r.rev(e[4]), i))
713 ui.write("\t%d -> %d\n" % (r.rev(e[4]), i))
714 if e[5] != hg.nullid:
714 if e[5] != hg.nullid:
715 ui.write("\t%d -> %d\n" % (r.rev(e[5]), i))
715 ui.write("\t%d -> %d\n" % (r.rev(e[5]), i))
716 ui.write("}\n")
716 ui.write("}\n")
717
717
718 def debugwalk(ui, repo, *pats, **opts):
718 def debugwalk(ui, repo, *pats, **opts):
719 """show how files match on given patterns"""
719 """show how files match on given patterns"""
720 items = list(walk(repo, pats, opts))
720 items = list(walk(repo, pats, opts))
721 if not items:
721 if not items:
722 return
722 return
723 fmt = '%%s %%-%ds %%-%ds %%s\n' % (
723 fmt = '%%s %%-%ds %%-%ds %%s\n' % (
724 max([len(abs) for (src, abs, rel, exact) in items]),
724 max([len(abs) for (src, abs, rel, exact) in items]),
725 max([len(rel) for (src, abs, rel, exact) in items]))
725 max([len(rel) for (src, abs, rel, exact) in items]))
726 for src, abs, rel, exact in items:
726 for src, abs, rel, exact in items:
727 ui.write(fmt % (src, abs, rel, exact and 'exact' or ''))
727 ui.write(fmt % (src, abs, rel, exact and 'exact' or ''))
728
728
729 def diff(ui, repo, *pats, **opts):
729 def diff(ui, repo, *pats, **opts):
730 """diff working directory (or selected files)"""
730 """diff working directory (or selected files)"""
731 node1, node2 = None, None
731 node1, node2 = None, None
732 revs = [repo.lookup(x) for x in opts['rev']]
732 revs = [repo.lookup(x) for x in opts['rev']]
733
733
734 if len(revs) > 0:
734 if len(revs) > 0:
735 node1 = revs[0]
735 node1 = revs[0]
736 if len(revs) > 1:
736 if len(revs) > 1:
737 node2 = revs[1]
737 node2 = revs[1]
738 if len(revs) > 2:
738 if len(revs) > 2:
739 raise util.Abort("too many revisions to diff")
739 raise util.Abort("too many revisions to diff")
740
740
741 files = []
741 files = []
742 match = util.always
742 match = util.always
743 if pats:
743 if pats:
744 roots, match, results = makewalk(repo, pats, opts)
744 roots, match, results = makewalk(repo, pats, opts)
745 for src, abs, rel, exact in results:
745 for src, abs, rel, exact in results:
746 files.append(abs)
746 files.append(abs)
747
747
748 dodiff(sys.stdout, ui, repo, node1, node2, files, match=match,
748 dodiff(sys.stdout, ui, repo, node1, node2, files, match=match,
749 text=opts['text'])
749 text=opts['text'])
750
750
751 def doexport(ui, repo, changeset, seqno, total, revwidth, opts):
751 def doexport(ui, repo, changeset, seqno, total, revwidth, opts):
752 node = repo.lookup(changeset)
752 node = repo.lookup(changeset)
753 prev, other = repo.changelog.parents(node)
753 prev, other = repo.changelog.parents(node)
754 change = repo.changelog.read(node)
754 change = repo.changelog.read(node)
755
755
756 fp = make_file(repo, repo.changelog, opts['output'],
756 fp = make_file(repo, repo.changelog, opts['output'],
757 node=node, total=total, seqno=seqno,
757 node=node, total=total, seqno=seqno,
758 revwidth=revwidth)
758 revwidth=revwidth)
759 if fp != sys.stdout:
759 if fp != sys.stdout:
760 ui.note("%s\n" % fp.name)
760 ui.note("%s\n" % fp.name)
761
761
762 fp.write("# HG changeset patch\n")
762 fp.write("# HG changeset patch\n")
763 fp.write("# User %s\n" % change[1])
763 fp.write("# User %s\n" % change[1])
764 fp.write("# Node ID %s\n" % hg.hex(node))
764 fp.write("# Node ID %s\n" % hg.hex(node))
765 fp.write("# Parent %s\n" % hg.hex(prev))
765 fp.write("# Parent %s\n" % hg.hex(prev))
766 if other != hg.nullid:
766 if other != hg.nullid:
767 fp.write("# Parent %s\n" % hg.hex(other))
767 fp.write("# Parent %s\n" % hg.hex(other))
768 fp.write(change[4].rstrip())
768 fp.write(change[4].rstrip())
769 fp.write("\n\n")
769 fp.write("\n\n")
770
770
771 dodiff(fp, ui, repo, prev, node, text=opts['text'])
771 dodiff(fp, ui, repo, prev, node, text=opts['text'])
772 if fp != sys.stdout:
772 if fp != sys.stdout:
773 fp.close()
773 fp.close()
774
774
775 def export(ui, repo, *changesets, **opts):
775 def export(ui, repo, *changesets, **opts):
776 """dump the header and diffs for one or more changesets"""
776 """dump the header and diffs for one or more changesets"""
777 if not changesets:
777 if not changesets:
778 raise util.Abort("export requires at least one changeset")
778 raise util.Abort("export requires at least one changeset")
779 seqno = 0
779 seqno = 0
780 revs = list(revrange(ui, repo, changesets))
780 revs = list(revrange(ui, repo, changesets))
781 total = len(revs)
781 total = len(revs)
782 revwidth = max(map(len, revs))
782 revwidth = max(map(len, revs))
783 ui.note(len(revs) > 1 and "Exporting patches:\n" or "Exporting patch:\n")
783 ui.note(len(revs) > 1 and "Exporting patches:\n" or "Exporting patch:\n")
784 for cset in revs:
784 for cset in revs:
785 seqno += 1
785 seqno += 1
786 doexport(ui, repo, cset, seqno, total, revwidth, opts)
786 doexport(ui, repo, cset, seqno, total, revwidth, opts)
787
787
788 def forget(ui, repo, *pats, **opts):
788 def forget(ui, repo, *pats, **opts):
789 """don't add the specified files on the next commit"""
789 """don't add the specified files on the next commit"""
790 forget = []
790 forget = []
791 for src, abs, rel, exact in walk(repo, pats, opts):
791 for src, abs, rel, exact in walk(repo, pats, opts):
792 if repo.dirstate.state(abs) == 'a':
792 if repo.dirstate.state(abs) == 'a':
793 forget.append(abs)
793 forget.append(abs)
794 if not exact:
794 if not exact:
795 ui.status('forgetting ', rel, '\n')
795 ui.status('forgetting ', rel, '\n')
796 repo.forget(forget)
796 repo.forget(forget)
797
797
798 def grep(ui, repo, pattern=None, *pats, **opts):
798 def grep(ui, repo, pattern=None, *pats, **opts):
799 """search for a pattern in specified files and revisions"""
799 """search for a pattern in specified files and revisions"""
800 if pattern is None:
800 if pattern is None:
801 pattern = opts['regexp']
801 pattern = opts['regexp']
802 if not pattern:
802 if not pattern:
803 raise util.Abort('no pattern to search for')
803 raise util.Abort('no pattern to search for')
804 reflags = 0
804 reflags = 0
805 if opts['ignore_case']:
805 if opts['ignore_case']:
806 reflags |= re.I
806 reflags |= re.I
807 regexp = re.compile(pattern, reflags)
807 regexp = re.compile(pattern, reflags)
808 sep, end = ':', '\n'
808 sep, end = ':', '\n'
809 if opts['null'] or opts['print0']:
809 if opts['null'] or opts['print0']:
810 sep = end = '\0'
810 sep = end = '\0'
811
811
812 fcache = {}
812 fcache = {}
813 def getfile(fn):
813 def getfile(fn):
814 if fn not in fcache:
814 if fn not in fcache:
815 fcache[fn] = repo.file(fn)
815 fcache[fn] = repo.file(fn)
816 return fcache[fn]
816 return fcache[fn]
817
817
818 def matchlines(body):
818 def matchlines(body):
819 begin = 0
819 begin = 0
820 linenum = 0
820 linenum = 0
821 while True:
821 while True:
822 match = regexp.search(body, begin)
822 match = regexp.search(body, begin)
823 if not match:
823 if not match:
824 break
824 break
825 mstart, mend = match.span()
825 mstart, mend = match.span()
826 linenum += body.count('\n', begin, mstart) + 1
826 linenum += body.count('\n', begin, mstart) + 1
827 lstart = body.rfind('\n', begin, mstart) + 1 or begin
827 lstart = body.rfind('\n', begin, mstart) + 1 or begin
828 lend = body.find('\n', mend)
828 lend = body.find('\n', mend)
829 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
829 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
830 begin = lend + 1
830 begin = lend + 1
831
831
832 class linestate:
832 class linestate:
833 def __init__(self, line, linenum, colstart, colend):
833 def __init__(self, line, linenum, colstart, colend):
834 self.line = line
834 self.line = line
835 self.linenum = linenum
835 self.linenum = linenum
836 self.colstart = colstart
836 self.colstart = colstart
837 self.colend = colend
837 self.colend = colend
838 def __eq__(self, other):
838 def __eq__(self, other):
839 return self.line == other.line
839 return self.line == other.line
840 def __hash__(self):
840 def __hash__(self):
841 return hash(self.line)
841 return hash(self.line)
842
842
843 matches = {}
843 matches = {}
844 def grepbody(fn, rev, body):
844 def grepbody(fn, rev, body):
845 matches[rev].setdefault(fn, {})
845 matches[rev].setdefault(fn, {})
846 m = matches[rev][fn]
846 m = matches[rev][fn]
847 for lnum, cstart, cend, line in matchlines(body):
847 for lnum, cstart, cend, line in matchlines(body):
848 s = linestate(line, lnum, cstart, cend)
848 s = linestate(line, lnum, cstart, cend)
849 m[s] = s
849 m[s] = s
850
850
851 prev = {}
851 prev = {}
852 def display(fn, rev, states, prevstates):
852 def display(fn, rev, states, prevstates):
853 diff = list(sets.Set(states).symmetric_difference(sets.Set(prevstates)))
853 diff = list(sets.Set(states).symmetric_difference(sets.Set(prevstates)))
854 diff.sort(lambda x, y: cmp(x.linenum, y.linenum))
854 diff.sort(lambda x, y: cmp(x.linenum, y.linenum))
855 for l in diff:
855 for l in diff:
856 if incrementing:
856 if incrementing:
857 change = ((l in prevstates) and '-') or '+'
857 change = ((l in prevstates) and '-') or '+'
858 r = rev
858 r = rev
859 else:
859 else:
860 change = ((l in states) and '-') or '+'
860 change = ((l in states) and '-') or '+'
861 r = prev[fn]
861 r = prev[fn]
862 ui.write('%s:%s:%s:%s%s\n' % (fn, r, l.linenum, change, l.line))
862 ui.write('%s:%s:%s:%s%s\n' % (fn, r, l.linenum, change, l.line))
863
863
864 fstate = {}
864 fstate = {}
865 for st, rev, fns in walkchangerevs(ui, repo, repo.getcwd(), pats, opts):
865 for st, rev, fns in walkchangerevs(ui, repo, repo.getcwd(), pats, opts):
866 if st == 'window':
866 if st == 'window':
867 incrementing = rev
867 incrementing = rev
868 matches.clear()
868 matches.clear()
869 elif st == 'add':
869 elif st == 'add':
870 change = repo.changelog.read(repo.lookup(str(rev)))
870 change = repo.changelog.read(repo.lookup(str(rev)))
871 mf = repo.manifest.read(change[0])
871 mf = repo.manifest.read(change[0])
872 matches[rev] = {}
872 matches[rev] = {}
873 for fn in fns:
873 for fn in fns:
874 fstate.setdefault(fn, {})
874 fstate.setdefault(fn, {})
875 try:
875 try:
876 grepbody(fn, rev, getfile(fn).read(mf[fn]))
876 grepbody(fn, rev, getfile(fn).read(mf[fn]))
877 except KeyError:
877 except KeyError:
878 pass
878 pass
879 elif st == 'iter':
879 elif st == 'iter':
880 states = matches[rev].items()
880 states = matches[rev].items()
881 states.sort()
881 states.sort()
882 for fn, m in states:
882 for fn, m in states:
883 if incrementing or fstate[fn]:
883 if incrementing or fstate[fn]:
884 display(fn, rev, m, fstate[fn])
884 display(fn, rev, m, fstate[fn])
885 fstate[fn] = m
885 fstate[fn] = m
886 prev[fn] = rev
886 prev[fn] = rev
887
887
888 if not incrementing:
888 if not incrementing:
889 fstate = fstate.items()
889 fstate = fstate.items()
890 fstate.sort()
890 fstate.sort()
891 for fn, state in fstate:
891 for fn, state in fstate:
892 display(fn, rev, {}, state)
892 display(fn, rev, {}, state)
893
893
894 def heads(ui, repo, **opts):
894 def heads(ui, repo, **opts):
895 """show current repository heads"""
895 """show current repository heads"""
896 heads = repo.changelog.heads()
896 heads = repo.changelog.heads()
897 br = None
897 br = None
898 if opts['branches']:
898 if opts['branches']:
899 br = repo.branchlookup(heads)
899 br = repo.branchlookup(heads)
900 for n in repo.changelog.heads():
900 for n in repo.changelog.heads():
901 show_changeset(ui, repo, changenode=n, brinfo=br)
901 show_changeset(ui, repo, changenode=n, brinfo=br)
902
902
903 def identify(ui, repo):
903 def identify(ui, repo):
904 """print information about the working copy"""
904 """print information about the working copy"""
905 parents = [p for p in repo.dirstate.parents() if p != hg.nullid]
905 parents = [p for p in repo.dirstate.parents() if p != hg.nullid]
906 if not parents:
906 if not parents:
907 ui.write("unknown\n")
907 ui.write("unknown\n")
908 return
908 return
909
909
910 hexfunc = ui.verbose and hg.hex or hg.short
910 hexfunc = ui.verbose and hg.hex or hg.short
911 (c, a, d, u) = repo.changes()
911 (c, a, d, u) = repo.changes()
912 output = ["%s%s" % ('+'.join([hexfunc(parent) for parent in parents]),
912 output = ["%s%s" % ('+'.join([hexfunc(parent) for parent in parents]),
913 (c or a or d) and "+" or "")]
913 (c or a or d) and "+" or "")]
914
914
915 if not ui.quiet:
915 if not ui.quiet:
916 # multiple tags for a single parent separated by '/'
916 # multiple tags for a single parent separated by '/'
917 parenttags = ['/'.join(tags)
917 parenttags = ['/'.join(tags)
918 for tags in map(repo.nodetags, parents) if tags]
918 for tags in map(repo.nodetags, parents) if tags]
919 # tags for multiple parents separated by ' + '
919 # tags for multiple parents separated by ' + '
920 if parenttags:
920 if parenttags:
921 output.append(' + '.join(parenttags))
921 output.append(' + '.join(parenttags))
922
922
923 ui.write("%s\n" % ' '.join(output))
923 ui.write("%s\n" % ' '.join(output))
924
924
925 def import_(ui, repo, patch1, *patches, **opts):
925 def import_(ui, repo, patch1, *patches, **opts):
926 """import an ordered set of patches"""
926 """import an ordered set of patches"""
927 patches = (patch1,) + patches
927 patches = (patch1,) + patches
928
928
929 if not opts['force']:
929 if not opts['force']:
930 (c, a, d, u) = repo.changes()
930 (c, a, d, u) = repo.changes()
931 if c or a or d:
931 if c or a or d:
932 ui.warn("abort: outstanding uncommitted changes!\n")
932 ui.warn("abort: outstanding uncommitted changes!\n")
933 return 1
933 return 1
934
934
935 d = opts["base"]
935 d = opts["base"]
936 strip = opts["strip"]
936 strip = opts["strip"]
937
937
938 for patch in patches:
938 for patch in patches:
939 ui.status("applying %s\n" % patch)
939 ui.status("applying %s\n" % patch)
940 pf = os.path.join(d, patch)
940 pf = os.path.join(d, patch)
941
941
942 message = []
942 message = []
943 user = None
943 user = None
944 hgpatch = False
944 hgpatch = False
945 for line in file(pf):
945 for line in file(pf):
946 line = line.rstrip()
946 line = line.rstrip()
947 if line.startswith("--- ") or line.startswith("diff -r"):
947 if line.startswith("--- ") or line.startswith("diff -r"):
948 break
948 break
949 elif hgpatch:
949 elif hgpatch:
950 # parse values when importing the result of an hg export
950 # parse values when importing the result of an hg export
951 if line.startswith("# User "):
951 if line.startswith("# User "):
952 user = line[7:]
952 user = line[7:]
953 ui.debug('User: %s\n' % user)
953 ui.debug('User: %s\n' % user)
954 elif not line.startswith("# ") and line:
954 elif not line.startswith("# ") and line:
955 message.append(line)
955 message.append(line)
956 hgpatch = False
956 hgpatch = False
957 elif line == '# HG changeset patch':
957 elif line == '# HG changeset patch':
958 hgpatch = True
958 hgpatch = True
959 message = [] # We may have collected garbage
959 message = [] # We may have collected garbage
960 else:
960 else:
961 message.append(line)
961 message.append(line)
962
962
963 # make sure message isn't empty
963 # make sure message isn't empty
964 if not message:
964 if not message:
965 message = "imported patch %s\n" % patch
965 message = "imported patch %s\n" % patch
966 else:
966 else:
967 message = "%s\n" % '\n'.join(message)
967 message = "%s\n" % '\n'.join(message)
968 ui.debug('message:\n%s\n' % message)
968 ui.debug('message:\n%s\n' % message)
969
969
970 f = os.popen("patch -p%d < '%s'" % (strip, pf))
970 f = os.popen("patch -p%d < '%s'" % (strip, pf))
971 files = []
971 files = []
972 for l in f.read().splitlines():
972 for l in f.read().splitlines():
973 l.rstrip('\r\n');
973 l.rstrip('\r\n');
974 ui.status("%s\n" % l)
974 ui.status("%s\n" % l)
975 if l.startswith('patching file '):
975 if l.startswith('patching file '):
976 pf = l[14:]
976 pf = l[14:]
977 if pf not in files:
977 if pf not in files:
978 files.append(pf)
978 files.append(pf)
979 patcherr = f.close()
979 patcherr = f.close()
980 if patcherr:
980 if patcherr:
981 raise util.Abort("patch failed")
981 raise util.Abort("patch failed")
982
982
983 if len(files) > 0:
983 if len(files) > 0:
984 addremove(ui, repo, *files)
984 addremove(ui, repo, *files)
985 repo.commit(files, message, user)
985 repo.commit(files, message, user)
986
986
987 def incoming(ui, repo, source="default"):
987 def incoming(ui, repo, source="default"):
988 """show new changesets found in source"""
988 """show new changesets found in source"""
989 source = ui.expandpath(source)
989 source = ui.expandpath(source)
990 other = hg.repository(ui, source)
990 other = hg.repository(ui, source)
991 if not other.local():
991 if not other.local():
992 ui.warn("abort: incoming doesn't work for remote"
992 ui.warn("abort: incoming doesn't work for remote"
993 + " repositories yet, sorry!\n")
993 + " repositories yet, sorry!\n")
994 return 1
994 return 1
995 o = repo.findincoming(other)
995 o = repo.findincoming(other)
996 if not o:
996 if not o:
997 return
997 return
998 o = other.newer(o)
998 o = other.newer(o)
999 o.reverse()
999 o.reverse()
1000 for n in o:
1000 for n in o:
1001 show_changeset(ui, other, changenode=n)
1001 show_changeset(ui, other, changenode=n)
1002
1002
1003 def init(ui, dest="."):
1003 def init(ui, dest="."):
1004 """create a new repository in the given directory"""
1004 """create a new repository in the given directory"""
1005 if not os.path.exists(dest):
1005 if not os.path.exists(dest):
1006 os.mkdir(dest)
1006 os.mkdir(dest)
1007 hg.repository(ui, dest, create=1)
1007 hg.repository(ui, dest, create=1)
1008
1008
1009 def locate(ui, repo, *pats, **opts):
1009 def locate(ui, repo, *pats, **opts):
1010 """locate files matching specific patterns"""
1010 """locate files matching specific patterns"""
1011 end = opts['print0'] and '\0' or '\n'
1011 end = opts['print0'] and '\0' or '\n'
1012
1012
1013 for src, abs, rel, exact in walk(repo, pats, opts, '(?:.*/|)'):
1013 for src, abs, rel, exact in walk(repo, pats, opts, '(?:.*/|)'):
1014 if repo.dirstate.state(abs) == '?':
1014 if repo.dirstate.state(abs) == '?':
1015 continue
1015 continue
1016 if opts['fullpath']:
1016 if opts['fullpath']:
1017 ui.write(os.path.join(repo.root, abs), end)
1017 ui.write(os.path.join(repo.root, abs), end)
1018 else:
1018 else:
1019 ui.write(rel, end)
1019 ui.write(rel, end)
1020
1020
1021 def log(ui, repo, *pats, **opts):
1021 def log(ui, repo, *pats, **opts):
1022 """show revision history of entire repository or files"""
1022 """show revision history of entire repository or files"""
1023 class dui:
1023 class dui:
1024 # Implement and delegate some ui protocol. Save hunks of
1024 # Implement and delegate some ui protocol. Save hunks of
1025 # output for later display in the desired order.
1025 # output for later display in the desired order.
1026 def __init__(self, ui):
1026 def __init__(self, ui):
1027 self.ui = ui
1027 self.ui = ui
1028 self.hunk = {}
1028 self.hunk = {}
1029 def bump(self, rev):
1029 def bump(self, rev):
1030 self.rev = rev
1030 self.rev = rev
1031 self.hunk[rev] = []
1031 self.hunk[rev] = []
1032 def note(self, *args):
1032 def note(self, *args):
1033 if self.verbose:
1033 if self.verbose:
1034 self.write(*args)
1034 self.write(*args)
1035 def status(self, *args):
1035 def status(self, *args):
1036 if not self.quiet:
1036 if not self.quiet:
1037 self.write(*args)
1037 self.write(*args)
1038 def write(self, *args):
1038 def write(self, *args):
1039 self.hunk[self.rev].append(args)
1039 self.hunk[self.rev].append(args)
1040 def __getattr__(self, key):
1040 def __getattr__(self, key):
1041 return getattr(self.ui, key)
1041 return getattr(self.ui, key)
1042 cwd = repo.getcwd()
1042 cwd = repo.getcwd()
1043 if not pats and cwd:
1043 if not pats and cwd:
1044 opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
1044 opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
1045 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
1045 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
1046 for st, rev, fns in walkchangerevs(ui, repo, (pats and cwd) or '', pats,
1046 for st, rev, fns in walkchangerevs(ui, repo, (pats and cwd) or '', pats,
1047 opts):
1047 opts):
1048 if st == 'window':
1048 if st == 'window':
1049 du = dui(ui)
1049 du = dui(ui)
1050 elif st == 'add':
1050 elif st == 'add':
1051 du.bump(rev)
1051 du.bump(rev)
1052 show_changeset(du, repo, rev)
1052 show_changeset(du, repo, rev)
1053 if opts['patch']:
1053 if opts['patch']:
1054 changenode = repo.changelog.node(rev)
1054 changenode = repo.changelog.node(rev)
1055 prev, other = repo.changelog.parents(changenode)
1055 prev, other = repo.changelog.parents(changenode)
1056 dodiff(du, du, repo, prev, changenode, fns)
1056 dodiff(du, du, repo, prev, changenode, fns)
1057 du.write("\n\n")
1057 du.write("\n\n")
1058 elif st == 'iter':
1058 elif st == 'iter':
1059 for args in du.hunk[rev]:
1059 for args in du.hunk[rev]:
1060 ui.write(*args)
1060 ui.write(*args)
1061
1061
1062 def manifest(ui, repo, rev=None):
1062 def manifest(ui, repo, rev=None):
1063 """output the latest or given revision of the project manifest"""
1063 """output the latest or given revision of the project manifest"""
1064 if rev:
1064 if rev:
1065 try:
1065 try:
1066 # assume all revision numbers are for changesets
1066 # assume all revision numbers are for changesets
1067 n = repo.lookup(rev)
1067 n = repo.lookup(rev)
1068 change = repo.changelog.read(n)
1068 change = repo.changelog.read(n)
1069 n = change[0]
1069 n = change[0]
1070 except hg.RepoError:
1070 except hg.RepoError:
1071 n = repo.manifest.lookup(rev)
1071 n = repo.manifest.lookup(rev)
1072 else:
1072 else:
1073 n = repo.manifest.tip()
1073 n = repo.manifest.tip()
1074 m = repo.manifest.read(n)
1074 m = repo.manifest.read(n)
1075 mf = repo.manifest.readflags(n)
1075 mf = repo.manifest.readflags(n)
1076 files = m.keys()
1076 files = m.keys()
1077 files.sort()
1077 files.sort()
1078
1078
1079 for f in files:
1079 for f in files:
1080 ui.write("%40s %3s %s\n" % (hg.hex(m[f]), mf[f] and "755" or "644", f))
1080 ui.write("%40s %3s %s\n" % (hg.hex(m[f]), mf[f] and "755" or "644", f))
1081
1081
1082 def outgoing(ui, repo, dest="default-push"):
1082 def outgoing(ui, repo, dest="default-push"):
1083 """show changesets not found in destination"""
1083 """show changesets not found in destination"""
1084 dest = ui.expandpath(dest)
1084 dest = ui.expandpath(dest)
1085 other = hg.repository(ui, dest)
1085 other = hg.repository(ui, dest)
1086 o = repo.findoutgoing(other)
1086 o = repo.findoutgoing(other)
1087 o = repo.newer(o)
1087 o = repo.newer(o)
1088 o.reverse()
1088 o.reverse()
1089 for n in o:
1089 for n in o:
1090 show_changeset(ui, repo, changenode=n)
1090 show_changeset(ui, repo, changenode=n)
1091
1091
1092 def parents(ui, repo, rev=None):
1092 def parents(ui, repo, rev=None):
1093 """show the parents of the working dir or revision"""
1093 """show the parents of the working dir or revision"""
1094 if rev:
1094 if rev:
1095 p = repo.changelog.parents(repo.lookup(rev))
1095 p = repo.changelog.parents(repo.lookup(rev))
1096 else:
1096 else:
1097 p = repo.dirstate.parents()
1097 p = repo.dirstate.parents()
1098
1098
1099 for n in p:
1099 for n in p:
1100 if n != hg.nullid:
1100 if n != hg.nullid:
1101 show_changeset(ui, repo, changenode=n)
1101 show_changeset(ui, repo, changenode=n)
1102
1102
1103 def paths(ui, search=None):
1103 def paths(ui, search=None):
1104 """show definition of symbolic path names"""
1104 """show definition of symbolic path names"""
1105 try:
1105 try:
1106 repo = hg.repository(ui=ui)
1106 repo = hg.repository(ui=ui)
1107 except hg.RepoError:
1107 except hg.RepoError:
1108 pass
1108 pass
1109
1109
1110 if search:
1110 if search:
1111 for name, path in ui.configitems("paths"):
1111 for name, path in ui.configitems("paths"):
1112 if name == search:
1112 if name == search:
1113 ui.write("%s\n" % path)
1113 ui.write("%s\n" % path)
1114 return
1114 return
1115 ui.warn("not found!\n")
1115 ui.warn("not found!\n")
1116 return 1
1116 return 1
1117 else:
1117 else:
1118 for name, path in ui.configitems("paths"):
1118 for name, path in ui.configitems("paths"):
1119 ui.write("%s = %s\n" % (name, path))
1119 ui.write("%s = %s\n" % (name, path))
1120
1120
1121 def pull(ui, repo, source="default", **opts):
1121 def pull(ui, repo, source="default", **opts):
1122 """pull changes from the specified source"""
1122 """pull changes from the specified source"""
1123 source = ui.expandpath(source)
1123 source = ui.expandpath(source)
1124 ui.status('pulling from %s\n' % (source))
1124 ui.status('pulling from %s\n' % (source))
1125
1125
1126 if opts['ssh']:
1126 if opts['ssh']:
1127 ui.setconfig("ui", "ssh", opts['ssh'])
1127 ui.setconfig("ui", "ssh", opts['ssh'])
1128 if opts['remotecmd']:
1128 if opts['remotecmd']:
1129 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
1129 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
1130
1130
1131 other = hg.repository(ui, source)
1131 other = hg.repository(ui, source)
1132 r = repo.pull(other)
1132 r = repo.pull(other)
1133 if not r:
1133 if not r:
1134 if opts['update']:
1134 if opts['update']:
1135 return update(ui, repo)
1135 return update(ui, repo)
1136 else:
1136 else:
1137 ui.status("(run 'hg update' to get a working copy)\n")
1137 ui.status("(run 'hg update' to get a working copy)\n")
1138
1138
1139 return r
1139 return r
1140
1140
1141 def push(ui, repo, dest="default-push", force=False, ssh=None, remotecmd=None):
1141 def push(ui, repo, dest="default-push", force=False, ssh=None, remotecmd=None):
1142 """push changes to the specified destination"""
1142 """push changes to the specified destination"""
1143 dest = ui.expandpath(dest)
1143 dest = ui.expandpath(dest)
1144 ui.status('pushing to %s\n' % (dest))
1144 ui.status('pushing to %s\n' % (dest))
1145
1145
1146 if ssh:
1146 if ssh:
1147 ui.setconfig("ui", "ssh", ssh)
1147 ui.setconfig("ui", "ssh", ssh)
1148 if remotecmd:
1148 if remotecmd:
1149 ui.setconfig("ui", "remotecmd", remotecmd)
1149 ui.setconfig("ui", "remotecmd", remotecmd)
1150
1150
1151 other = hg.repository(ui, dest)
1151 other = hg.repository(ui, dest)
1152 r = repo.push(other, force)
1152 r = repo.push(other, force)
1153 return r
1153 return r
1154
1154
1155 def rawcommit(ui, repo, *flist, **rc):
1155 def rawcommit(ui, repo, *flist, **rc):
1156 "raw commit interface"
1156 "raw commit interface"
1157 if rc['text']:
1157 if rc['text']:
1158 ui.warn("Warning: -t and --text is deprecated,"
1158 ui.warn("Warning: -t and --text is deprecated,"
1159 " please use -m or --message instead.\n")
1159 " please use -m or --message instead.\n")
1160 message = rc['message'] or rc['text']
1160 message = rc['message'] or rc['text']
1161 if not message and rc['logfile']:
1161 if not message and rc['logfile']:
1162 try:
1162 try:
1163 message = open(rc['logfile']).read()
1163 message = open(rc['logfile']).read()
1164 except IOError:
1164 except IOError:
1165 pass
1165 pass
1166 if not message and not rc['logfile']:
1166 if not message and not rc['logfile']:
1167 ui.warn("abort: missing commit message\n")
1167 ui.warn("abort: missing commit message\n")
1168 return 1
1168 return 1
1169
1169
1170 files = relpath(repo, list(flist))
1170 files = relpath(repo, list(flist))
1171 if rc['files']:
1171 if rc['files']:
1172 files += open(rc['files']).read().splitlines()
1172 files += open(rc['files']).read().splitlines()
1173
1173
1174 rc['parent'] = map(repo.lookup, rc['parent'])
1174 rc['parent'] = map(repo.lookup, rc['parent'])
1175
1175
1176 repo.rawcommit(files, message, rc['user'], rc['date'], *rc['parent'])
1176 repo.rawcommit(files, message, rc['user'], rc['date'], *rc['parent'])
1177
1177
1178 def recover(ui, repo):
1178 def recover(ui, repo):
1179 """roll back an interrupted transaction"""
1179 """roll back an interrupted transaction"""
1180 repo.recover()
1180 repo.recover()
1181
1181
1182 def remove(ui, repo, file1, *files):
1182 def remove(ui, repo, file1, *files):
1183 """remove the specified files on the next commit"""
1183 """remove the specified files on the next commit"""
1184 repo.remove(relpath(repo, (file1,) + files))
1184 repo.remove(relpath(repo, (file1,) + files))
1185
1185
1186 def revert(ui, repo, *names, **opts):
1186 def revert(ui, repo, *names, **opts):
1187 """revert modified files or dirs back to their unmodified states"""
1187 """revert modified files or dirs back to their unmodified states"""
1188 node = opts['rev'] and repo.lookup(opts['rev']) or \
1188 node = opts['rev'] and repo.lookup(opts['rev']) or \
1189 repo.dirstate.parents()[0]
1189 repo.dirstate.parents()[0]
1190 root = os.path.realpath(repo.root)
1190 root = os.path.realpath(repo.root)
1191
1191
1192 def trimpath(p):
1192 def trimpath(p):
1193 p = os.path.realpath(p)
1193 p = os.path.realpath(p)
1194 if p.startswith(root):
1194 if p.startswith(root):
1195 rest = p[len(root):]
1195 rest = p[len(root):]
1196 if not rest:
1196 if not rest:
1197 return rest
1197 return rest
1198 if p.startswith(os.sep):
1198 if p.startswith(os.sep):
1199 return rest[1:]
1199 return rest[1:]
1200 return p
1200 return p
1201
1201
1202 relnames = map(trimpath, names or [os.getcwd()])
1202 relnames = map(trimpath, names or [os.getcwd()])
1203 chosen = {}
1203 chosen = {}
1204
1204
1205 def choose(name):
1205 def choose(name):
1206 def body(name):
1206 def body(name):
1207 for r in relnames:
1207 for r in relnames:
1208 if not name.startswith(r):
1208 if not name.startswith(r):
1209 continue
1209 continue
1210 rest = name[len(r):]
1210 rest = name[len(r):]
1211 if not rest:
1211 if not rest:
1212 return r, True
1212 return r, True
1213 depth = rest.count(os.sep)
1213 depth = rest.count(os.sep)
1214 if not r:
1214 if not r:
1215 if depth == 0 or not opts['nonrecursive']:
1215 if depth == 0 or not opts['nonrecursive']:
1216 return r, True
1216 return r, True
1217 elif rest[0] == os.sep:
1217 elif rest[0] == os.sep:
1218 if depth == 1 or not opts['nonrecursive']:
1218 if depth == 1 or not opts['nonrecursive']:
1219 return r, True
1219 return r, True
1220 return None, False
1220 return None, False
1221 relname, ret = body(name)
1221 relname, ret = body(name)
1222 if ret:
1222 if ret:
1223 chosen[relname] = 1
1223 chosen[relname] = 1
1224 return ret
1224 return ret
1225
1225
1226 r = repo.update(node, False, True, choose, False)
1226 r = repo.update(node, False, True, choose, False)
1227 for n in relnames:
1227 for n in relnames:
1228 if n not in chosen:
1228 if n not in chosen:
1229 ui.warn('error: no matches for %s\n' % n)
1229 ui.warn('error: no matches for %s\n' % n)
1230 r = 1
1230 r = 1
1231 sys.stdout.flush()
1231 sys.stdout.flush()
1232 return r
1232 return r
1233
1233
1234 def root(ui, repo):
1234 def root(ui, repo):
1235 """print the root (top) of the current working dir"""
1235 """print the root (top) of the current working dir"""
1236 ui.write(repo.root + "\n")
1236 ui.write(repo.root + "\n")
1237
1237
1238 def serve(ui, repo, **opts):
1238 def serve(ui, repo, **opts):
1239 """export the repository via HTTP"""
1239 """export the repository via HTTP"""
1240
1240
1241 if opts["stdio"]:
1241 if opts["stdio"]:
1242 fin, fout = sys.stdin, sys.stdout
1242 fin, fout = sys.stdin, sys.stdout
1243 sys.stdout = sys.stderr
1243 sys.stdout = sys.stderr
1244
1244
1245 def getarg():
1245 def getarg():
1246 argline = fin.readline()[:-1]
1246 argline = fin.readline()[:-1]
1247 arg, l = argline.split()
1247 arg, l = argline.split()
1248 val = fin.read(int(l))
1248 val = fin.read(int(l))
1249 return arg, val
1249 return arg, val
1250 def respond(v):
1250 def respond(v):
1251 fout.write("%d\n" % len(v))
1251 fout.write("%d\n" % len(v))
1252 fout.write(v)
1252 fout.write(v)
1253 fout.flush()
1253 fout.flush()
1254
1254
1255 lock = None
1255 lock = None
1256
1256
1257 while 1:
1257 while 1:
1258 cmd = fin.readline()[:-1]
1258 cmd = fin.readline()[:-1]
1259 if cmd == '':
1259 if cmd == '':
1260 return
1260 return
1261 if cmd == "heads":
1261 if cmd == "heads":
1262 h = repo.heads()
1262 h = repo.heads()
1263 respond(" ".join(map(hg.hex, h)) + "\n")
1263 respond(" ".join(map(hg.hex, h)) + "\n")
1264 if cmd == "lock":
1264 if cmd == "lock":
1265 lock = repo.lock()
1265 lock = repo.lock()
1266 respond("")
1266 respond("")
1267 if cmd == "unlock":
1267 if cmd == "unlock":
1268 if lock:
1268 if lock:
1269 lock.release()
1269 lock.release()
1270 lock = None
1270 lock = None
1271 respond("")
1271 respond("")
1272 elif cmd == "branches":
1272 elif cmd == "branches":
1273 arg, nodes = getarg()
1273 arg, nodes = getarg()
1274 nodes = map(hg.bin, nodes.split(" "))
1274 nodes = map(hg.bin, nodes.split(" "))
1275 r = []
1275 r = []
1276 for b in repo.branches(nodes):
1276 for b in repo.branches(nodes):
1277 r.append(" ".join(map(hg.hex, b)) + "\n")
1277 r.append(" ".join(map(hg.hex, b)) + "\n")
1278 respond("".join(r))
1278 respond("".join(r))
1279 elif cmd == "between":
1279 elif cmd == "between":
1280 arg, pairs = getarg()
1280 arg, pairs = getarg()
1281 pairs = [map(hg.bin, p.split("-")) for p in pairs.split(" ")]
1281 pairs = [map(hg.bin, p.split("-")) for p in pairs.split(" ")]
1282 r = []
1282 r = []
1283 for b in repo.between(pairs):
1283 for b in repo.between(pairs):
1284 r.append(" ".join(map(hg.hex, b)) + "\n")
1284 r.append(" ".join(map(hg.hex, b)) + "\n")
1285 respond("".join(r))
1285 respond("".join(r))
1286 elif cmd == "changegroup":
1286 elif cmd == "changegroup":
1287 nodes = []
1287 nodes = []
1288 arg, roots = getarg()
1288 arg, roots = getarg()
1289 nodes = map(hg.bin, roots.split(" "))
1289 nodes = map(hg.bin, roots.split(" "))
1290
1290
1291 cg = repo.changegroup(nodes)
1291 cg = repo.changegroup(nodes)
1292 while 1:
1292 while 1:
1293 d = cg.read(4096)
1293 d = cg.read(4096)
1294 if not d:
1294 if not d:
1295 break
1295 break
1296 fout.write(d)
1296 fout.write(d)
1297
1297
1298 fout.flush()
1298 fout.flush()
1299
1299
1300 elif cmd == "addchangegroup":
1300 elif cmd == "addchangegroup":
1301 if not lock:
1301 if not lock:
1302 respond("not locked")
1302 respond("not locked")
1303 continue
1303 continue
1304 respond("")
1304 respond("")
1305
1305
1306 r = repo.addchangegroup(fin)
1306 r = repo.addchangegroup(fin)
1307 respond("")
1307 respond("")
1308
1308
1309 optlist = "name templates style address port ipv6 accesslog errorlog"
1309 optlist = "name templates style address port ipv6 accesslog errorlog"
1310 for o in optlist.split():
1310 for o in optlist.split():
1311 if opts[o]:
1311 if opts[o]:
1312 ui.setconfig("web", o, opts[o])
1312 ui.setconfig("web", o, opts[o])
1313
1313
1314 httpd = hgweb.create_server(repo)
1314 httpd = hgweb.create_server(repo)
1315
1315
1316 if ui.verbose:
1316 if ui.verbose:
1317 addr, port = httpd.socket.getsockname()
1317 addr, port = httpd.socket.getsockname()
1318 if addr == '0.0.0.0':
1318 if addr == '0.0.0.0':
1319 addr = socket.gethostname()
1319 addr = socket.gethostname()
1320 else:
1320 else:
1321 try:
1321 try:
1322 addr = socket.gethostbyaddr(addr)[0]
1322 addr = socket.gethostbyaddr(addr)[0]
1323 except socket.error:
1323 except socket.error:
1324 pass
1324 pass
1325 if port != 80:
1325 if port != 80:
1326 ui.status('listening at http://%s:%d/\n' % (addr, port))
1326 ui.status('listening at http://%s:%d/\n' % (addr, port))
1327 else:
1327 else:
1328 ui.status('listening at http://%s/\n' % addr)
1328 ui.status('listening at http://%s/\n' % addr)
1329 httpd.serve_forever()
1329 httpd.serve_forever()
1330
1330
1331 def status(ui, repo, *pats, **opts):
1331 def status(ui, repo, *pats, **opts):
1332 '''show changed files in the working directory
1332 '''show changed files in the working directory
1333
1333
1334 M = modified
1334 M = modified
1335 A = added
1335 A = added
1336 R = removed
1336 R = removed
1337 ? = not tracked
1337 ? = not tracked
1338 '''
1338 '''
1339
1339
1340 cwd = repo.getcwd()
1340 cwd = repo.getcwd()
1341 files, matchfn, anypats = matchpats(repo, cwd, pats, opts)
1341 files, matchfn, anypats = matchpats(repo, cwd, pats, opts)
1342 (c, a, d, u) = [[util.pathto(cwd, x) for x in n]
1342 (c, a, d, u) = [[util.pathto(cwd, x) for x in n]
1343 for n in repo.changes(files=files, match=matchfn)]
1343 for n in repo.changes(files=files, match=matchfn)]
1344
1344
1345 changetypes = [('modified', 'M', c),
1345 changetypes = [('modified', 'M', c),
1346 ('added', 'A', a),
1346 ('added', 'A', a),
1347 ('removed', 'R', d),
1347 ('removed', 'R', d),
1348 ('unknown', '?', u)]
1348 ('unknown', '?', u)]
1349
1349
1350 end = opts['print0'] and '\0' or '\n'
1350 end = opts['print0'] and '\0' or '\n'
1351
1351
1352 for opt, char, changes in ([ct for ct in changetypes if opts[ct[0]]]
1352 for opt, char, changes in ([ct for ct in changetypes if opts[ct[0]]]
1353 or changetypes):
1353 or changetypes):
1354 if opts['strip']:
1354 if opts['strip']:
1355 format = "%%s%s" % end
1355 format = "%%s%s" % end
1356 else:
1356 else:
1357 format = "%s %%s%s" % (char, end);
1357 format = "%s %%s%s" % (char, end);
1358
1358
1359 for f in changes:
1359 for f in changes:
1360 ui.write(format % f)
1360 ui.write(format % f)
1361
1361
1362 def tag(ui, repo, name, rev=None, **opts):
1362 def tag(ui, repo, name, rev=None, **opts):
1363 """add a tag for the current tip or a given revision"""
1363 """add a tag for the current tip or a given revision"""
1364 if opts['text']:
1364 if opts['text']:
1365 ui.warn("Warning: -t and --text is deprecated,"
1365 ui.warn("Warning: -t and --text is deprecated,"
1366 " please use -m or --message instead.\n")
1366 " please use -m or --message instead.\n")
1367 if name == "tip":
1367 if name == "tip":
1368 ui.warn("abort: 'tip' is a reserved name!\n")
1368 ui.warn("abort: 'tip' is a reserved name!\n")
1369 return -1
1369 return -1
1370 if rev:
1370 if rev:
1371 r = hg.hex(repo.lookup(rev))
1371 r = hg.hex(repo.lookup(rev))
1372 else:
1372 else:
1373 r = hg.hex(repo.changelog.tip())
1373 r = hg.hex(repo.changelog.tip())
1374
1374
1375 if name.find(revrangesep) >= 0:
1375 if name.find(revrangesep) >= 0:
1376 ui.warn("abort: '%s' cannot be used in a tag name\n" % revrangesep)
1376 ui.warn("abort: '%s' cannot be used in a tag name\n" % revrangesep)
1377 return -1
1377 return -1
1378
1378
1379 if opts['local']:
1379 if opts['local']:
1380 repo.opener("localtags", "a").write("%s %s\n" % (r, name))
1380 repo.opener("localtags", "a").write("%s %s\n" % (r, name))
1381 return
1381 return
1382
1382
1383 (c, a, d, u) = repo.changes()
1383 (c, a, d, u) = repo.changes()
1384 for x in (c, a, d, u):
1384 for x in (c, a, d, u):
1385 if ".hgtags" in x:
1385 if ".hgtags" in x:
1386 ui.warn("abort: working copy of .hgtags is changed!\n")
1386 ui.warn("abort: working copy of .hgtags is changed!\n")
1387 ui.status("(please commit .hgtags manually)\n")
1387 ui.status("(please commit .hgtags manually)\n")
1388 return -1
1388 return -1
1389
1389
1390 repo.wfile(".hgtags", "ab").write("%s %s\n" % (r, name))
1390 repo.wfile(".hgtags", "ab").write("%s %s\n" % (r, name))
1391 if repo.dirstate.state(".hgtags") == '?':
1391 if repo.dirstate.state(".hgtags") == '?':
1392 repo.add([".hgtags"])
1392 repo.add([".hgtags"])
1393
1393
1394 message = (opts['message'] or opts['text'] or
1394 message = (opts['message'] or opts['text'] or
1395 "Added tag %s for changeset %s" % (name, r))
1395 "Added tag %s for changeset %s" % (name, r))
1396 repo.commit([".hgtags"], message, opts['user'], opts['date'])
1396 repo.commit([".hgtags"], message, opts['user'], opts['date'])
1397
1397
1398 def tags(ui, repo):
1398 def tags(ui, repo):
1399 """list repository tags"""
1399 """list repository tags"""
1400
1400
1401 l = repo.tagslist()
1401 l = repo.tagslist()
1402 l.reverse()
1402 l.reverse()
1403 for t, n in l:
1403 for t, n in l:
1404 try:
1404 try:
1405 r = "%5d:%s" % (repo.changelog.rev(n), hg.hex(n))
1405 r = "%5d:%s" % (repo.changelog.rev(n), hg.hex(n))
1406 except KeyError:
1406 except KeyError:
1407 r = " ?:?"
1407 r = " ?:?"
1408 ui.write("%-30s %s\n" % (t, r))
1408 ui.write("%-30s %s\n" % (t, r))
1409
1409
1410 def tip(ui, repo):
1410 def tip(ui, repo):
1411 """show the tip revision"""
1411 """show the tip revision"""
1412 n = repo.changelog.tip()
1412 n = repo.changelog.tip()
1413 show_changeset(ui, repo, changenode=n)
1413 show_changeset(ui, repo, changenode=n)
1414
1414
1415 def undo(ui, repo):
1415 def undo(ui, repo):
1416 """undo the last commit or pull
1416 """undo the last commit or pull
1417
1417
1418 Roll back the last pull or commit transaction on the
1418 Roll back the last pull or commit transaction on the
1419 repository, restoring the project to its earlier state.
1419 repository, restoring the project to its earlier state.
1420
1420
1421 This command should be used with care. There is only one level of
1421 This command should be used with care. There is only one level of
1422 undo and there is no redo.
1422 undo and there is no redo.
1423
1423
1424 This command is not intended for use on public repositories. Once
1424 This command is not intended for use on public repositories. Once
1425 a change is visible for pull by other users, undoing it locally is
1425 a change is visible for pull by other users, undoing it locally is
1426 ineffective.
1426 ineffective.
1427 """
1427 """
1428 repo.undo()
1428 repo.undo()
1429
1429
1430 def update(ui, repo, node=None, merge=False, clean=False, branch=None):
1430 def update(ui, repo, node=None, merge=False, clean=False, branch=None):
1431 '''update or merge working directory
1431 '''update or merge working directory
1432
1432
1433 If there are no outstanding changes in the working directory and
1433 If there are no outstanding changes in the working directory and
1434 there is a linear relationship between the current version and the
1434 there is a linear relationship between the current version and the
1435 requested version, the result is the requested version.
1435 requested version, the result is the requested version.
1436
1436
1437 Otherwise the result is a merge between the contents of the
1437 Otherwise the result is a merge between the contents of the
1438 current working directory and the requested version. Files that
1438 current working directory and the requested version. Files that
1439 changed between either parent are marked as changed for the next
1439 changed between either parent are marked as changed for the next
1440 commit and a commit must be performed before any further updates
1440 commit and a commit must be performed before any further updates
1441 are allowed.
1441 are allowed.
1442 '''
1442 '''
1443 if branch:
1443 if branch:
1444 br = repo.branchlookup(branch=branch)
1444 br = repo.branchlookup(branch=branch)
1445 found = []
1445 found = []
1446 for x in br:
1446 for x in br:
1447 if branch in br[x]:
1447 if branch in br[x]:
1448 found.append(x)
1448 found.append(x)
1449 if len(found) > 1:
1449 if len(found) > 1:
1450 ui.warn("Found multiple heads for %s\n" % branch)
1450 ui.warn("Found multiple heads for %s\n" % branch)
1451 for x in found:
1451 for x in found:
1452 show_changeset(ui, repo, changenode=x, brinfo=br)
1452 show_changeset(ui, repo, changenode=x, brinfo=br)
1453 return 1
1453 return 1
1454 if len(found) == 1:
1454 if len(found) == 1:
1455 node = found[0]
1455 node = found[0]
1456 ui.warn("Using head %s for branch %s\n" % (hg.short(node), branch))
1456 ui.warn("Using head %s for branch %s\n" % (hg.short(node), branch))
1457 else:
1457 else:
1458 ui.warn("branch %s not found\n" % (branch))
1458 ui.warn("branch %s not found\n" % (branch))
1459 return 1
1459 return 1
1460 else:
1460 else:
1461 node = node and repo.lookup(node) or repo.changelog.tip()
1461 node = node and repo.lookup(node) or repo.changelog.tip()
1462 return repo.update(node, allow=merge, force=clean)
1462 return repo.update(node, allow=merge, force=clean)
1463
1463
1464 def verify(ui, repo):
1464 def verify(ui, repo):
1465 """verify the integrity of the repository"""
1465 """verify the integrity of the repository"""
1466 return repo.verify()
1466 return repo.verify()
1467
1467
1468 # Command options and aliases are listed here, alphabetically
1468 # Command options and aliases are listed here, alphabetically
1469
1469
1470 table = {
1470 table = {
1471 "^add":
1471 "^add":
1472 (add,
1472 (add,
1473 [('I', 'include', [], 'include path in search'),
1473 [('I', 'include', [], 'include path in search'),
1474 ('X', 'exclude', [], 'exclude path from search')],
1474 ('X', 'exclude', [], 'exclude path from search')],
1475 "hg add [OPTION]... [FILE]..."),
1475 "hg add [OPTION]... [FILE]..."),
1476 "addremove":
1476 "addremove":
1477 (addremove,
1477 (addremove,
1478 [('I', 'include', [], 'include path in search'),
1478 [('I', 'include', [], 'include path in search'),
1479 ('X', 'exclude', [], 'exclude path from search')],
1479 ('X', 'exclude', [], 'exclude path from search')],
1480 "hg addremove [OPTION]... [FILE]..."),
1480 "hg addremove [OPTION]... [FILE]..."),
1481 "^annotate":
1481 "^annotate":
1482 (annotate,
1482 (annotate,
1483 [('r', 'rev', '', 'revision'),
1483 [('r', 'rev', '', 'revision'),
1484 ('a', 'text', None, 'treat all files as text'),
1484 ('a', 'text', None, 'treat all files as text'),
1485 ('u', 'user', None, 'show user'),
1485 ('u', 'user', None, 'show user'),
1486 ('n', 'number', None, 'show revision number'),
1486 ('n', 'number', None, 'show revision number'),
1487 ('c', 'changeset', None, 'show changeset'),
1487 ('c', 'changeset', None, 'show changeset'),
1488 ('I', 'include', [], 'include path in search'),
1488 ('I', 'include', [], 'include path in search'),
1489 ('X', 'exclude', [], 'exclude path from search')],
1489 ('X', 'exclude', [], 'exclude path from search')],
1490 'hg annotate [OPTION]... FILE...'),
1490 'hg annotate [OPTION]... FILE...'),
1491 "cat":
1491 "cat":
1492 (cat,
1492 (cat,
1493 [('o', 'output', "", 'output to file')],
1493 [('o', 'output', "", 'output to file')],
1494 'hg cat [-o OUTFILE] FILE [REV]'),
1494 'hg cat [-o OUTFILE] FILE [REV]'),
1495 "^clone":
1495 "^clone":
1496 (clone,
1496 (clone,
1497 [('U', 'noupdate', None, 'skip update after cloning'),
1497 [('U', 'noupdate', None, 'skip update after cloning'),
1498 ('e', 'ssh', "", 'ssh command'),
1498 ('e', 'ssh', "", 'ssh command'),
1499 ('', 'remotecmd', "", 'remote hg command')],
1499 ('', 'remotecmd', "", 'remote hg command')],
1500 'hg clone [OPTION]... SOURCE [DEST]'),
1500 'hg clone [OPTION]... SOURCE [DEST]'),
1501 "^commit|ci":
1501 "^commit|ci":
1502 (commit,
1502 (commit,
1503 [('A', 'addremove', None, 'run add/remove during commit'),
1503 [('A', 'addremove', None, 'run add/remove during commit'),
1504 ('I', 'include', [], 'include path in search'),
1504 ('I', 'include', [], 'include path in search'),
1505 ('X', 'exclude', [], 'exclude path from search'),
1505 ('X', 'exclude', [], 'exclude path from search'),
1506 ('m', 'message', "", 'commit message'),
1506 ('m', 'message', "", 'commit message'),
1507 ('t', 'text', "", 'commit message (deprecated: use -m)'),
1507 ('t', 'text', "", 'commit message (deprecated: use -m)'),
1508 ('l', 'logfile', "", 'commit message file'),
1508 ('l', 'logfile', "", 'commit message file'),
1509 ('d', 'date', "", 'date code'),
1509 ('d', 'date', "", 'date code'),
1510 ('u', 'user', "", 'user')],
1510 ('u', 'user', "", 'user')],
1511 'hg commit [OPTION]... [FILE]...'),
1511 'hg commit [OPTION]... [FILE]...'),
1512 "copy": (copy, [], 'hg copy SOURCE DEST'),
1512 "copy": (copy, [], 'hg copy SOURCE DEST'),
1513 "debugcheckstate": (debugcheckstate, [], 'debugcheckstate'),
1513 "debugcheckstate": (debugcheckstate, [], 'debugcheckstate'),
1514 "debugconfig": (debugconfig, [], 'debugconfig'),
1514 "debugconfig": (debugconfig, [], 'debugconfig'),
1515 "debugstate": (debugstate, [], 'debugstate'),
1515 "debugstate": (debugstate, [], 'debugstate'),
1516 "debugdata": (debugdata, [], 'debugdata FILE REV'),
1516 "debugdata": (debugdata, [], 'debugdata FILE REV'),
1517 "debugindex": (debugindex, [], 'debugindex FILE'),
1517 "debugindex": (debugindex, [], 'debugindex FILE'),
1518 "debugindexdot": (debugindexdot, [], 'debugindexdot FILE'),
1518 "debugindexdot": (debugindexdot, [], 'debugindexdot FILE'),
1519 "debugwalk":
1519 "debugwalk":
1520 (debugwalk,
1520 (debugwalk,
1521 [('I', 'include', [], 'include path in search'),
1521 [('I', 'include', [], 'include path in search'),
1522 ('X', 'exclude', [], 'exclude path from search')],
1522 ('X', 'exclude', [], 'exclude path from search')],
1523 'debugwalk [OPTION]... [FILE]...'),
1523 'debugwalk [OPTION]... [FILE]...'),
1524 "^diff":
1524 "^diff":
1525 (diff,
1525 (diff,
1526 [('r', 'rev', [], 'revision'),
1526 [('r', 'rev', [], 'revision'),
1527 ('a', 'text', None, 'treat all files as text'),
1527 ('a', 'text', None, 'treat all files as text'),
1528 ('I', 'include', [], 'include path in search'),
1528 ('I', 'include', [], 'include path in search'),
1529 ('X', 'exclude', [], 'exclude path from search')],
1529 ('X', 'exclude', [], 'exclude path from search')],
1530 'hg diff [-a] [-I] [-X] [-r REV1 [-r REV2]] [FILE]...'),
1530 'hg diff [-a] [-I] [-X] [-r REV1 [-r REV2]] [FILE]...'),
1531 "^export":
1531 "^export":
1532 (export,
1532 (export,
1533 [('o', 'output', "", 'output to file'),
1533 [('o', 'output', "", 'output to file'),
1534 ('a', 'text', None, 'treat all files as text')],
1534 ('a', 'text', None, 'treat all files as text')],
1535 "hg export [-a] [-o OUTFILE] REV..."),
1535 "hg export [-a] [-o OUTFILE] REV..."),
1536 "forget":
1536 "forget":
1537 (forget,
1537 (forget,
1538 [('I', 'include', [], 'include path in search'),
1538 [('I', 'include', [], 'include path in search'),
1539 ('X', 'exclude', [], 'exclude path from search')],
1539 ('X', 'exclude', [], 'exclude path from search')],
1540 "hg forget [OPTION]... FILE..."),
1540 "hg forget [OPTION]... FILE..."),
1541 "grep":
1541 "grep":
1542 (grep,
1542 (grep,
1543 [('0', 'print0', None, 'terminate file names with NUL'),
1543 [('0', 'print0', None, 'terminate file names with NUL'),
1544 ('I', 'include', [], 'include path in search'),
1544 ('I', 'include', [], 'include path in search'),
1545 ('X', 'exclude', [], 'include path in search'),
1545 ('X', 'exclude', [], 'include path in search'),
1546 ('Z', 'null', None, 'terminate file names with NUL'),
1546 ('Z', 'null', None, 'terminate file names with NUL'),
1547 ('a', 'all-revs', '', 'search all revs'),
1547 ('a', 'all-revs', '', 'search all revs'),
1548 ('e', 'regexp', '', 'pattern to search for'),
1548 ('e', 'regexp', '', 'pattern to search for'),
1549 ('f', 'full-path', None, 'print complete paths'),
1549 ('f', 'full-path', None, 'print complete paths'),
1550 ('i', 'ignore-case', None, 'ignore case when matching'),
1550 ('i', 'ignore-case', None, 'ignore case when matching'),
1551 ('l', 'files-with-matches', None, 'print names of files with matches'),
1551 ('l', 'files-with-matches', None, 'print names of files with matches'),
1552 ('n', 'line-number', '', 'print line numbers'),
1552 ('n', 'line-number', '', 'print line numbers'),
1553 ('r', 'rev', [], 'search in revision rev'),
1553 ('r', 'rev', [], 'search in revision rev'),
1554 ('s', 'no-messages', None, 'do not print error messages'),
1554 ('s', 'no-messages', None, 'do not print error messages'),
1555 ('v', 'invert-match', None, 'select non-matching lines')],
1555 ('v', 'invert-match', None, 'select non-matching lines')],
1556 "hg grep [OPTION]... [PATTERN] [FILE]..."),
1556 "hg grep [OPTION]... [PATTERN] [FILE]..."),
1557 "heads":
1557 "heads":
1558 (heads,
1558 (heads,
1559 [('b', 'branches', None, 'find branch info')],
1559 [('b', 'branches', None, 'find branch info')],
1560 'hg heads [-b]'),
1560 'hg heads [-b]'),
1561 "help": (help_, [], 'hg help [COMMAND]'),
1561 "help": (help_, [], 'hg help [COMMAND]'),
1562 "identify|id": (identify, [], 'hg identify'),
1562 "identify|id": (identify, [], 'hg identify'),
1563 "import|patch":
1563 "import|patch":
1564 (import_,
1564 (import_,
1565 [('p', 'strip', 1, 'path strip'),
1565 [('p', 'strip', 1, 'path strip'),
1566 ('f', 'force', None, 'skip check for outstanding changes'),
1566 ('f', 'force', None, 'skip check for outstanding changes'),
1567 ('b', 'base', "", 'base path')],
1567 ('b', 'base', "", 'base path')],
1568 "hg import [-f] [-p NUM] [-b BASE] PATCH..."),
1568 "hg import [-f] [-p NUM] [-b BASE] PATCH..."),
1569 "incoming|in": (incoming, [], 'hg incoming [SOURCE]'),
1569 "incoming|in": (incoming, [], 'hg incoming [SOURCE]'),
1570 "^init": (init, [], 'hg init [DEST]'),
1570 "^init": (init, [], 'hg init [DEST]'),
1571 "locate":
1571 "locate":
1572 (locate,
1572 (locate,
1573 [('r', 'rev', '', 'revision'),
1573 [('r', 'rev', '', 'revision'),
1574 ('0', 'print0', None, 'end records with NUL'),
1574 ('0', 'print0', None, 'end records with NUL'),
1575 ('f', 'fullpath', None, 'print complete paths'),
1575 ('f', 'fullpath', None, 'print complete paths'),
1576 ('I', 'include', [], 'include path in search'),
1576 ('I', 'include', [], 'include path in search'),
1577 ('X', 'exclude', [], 'exclude path from search')],
1577 ('X', 'exclude', [], 'exclude path from search')],
1578 'hg locate [OPTION]... [PATTERN]...'),
1578 'hg locate [OPTION]... [PATTERN]...'),
1579 "^log|history":
1579 "^log|history":
1580 (log,
1580 (log,
1581 [('I', 'include', [], 'include path in search'),
1581 [('I', 'include', [], 'include path in search'),
1582 ('X', 'exclude', [], 'exclude path from search'),
1582 ('X', 'exclude', [], 'exclude path from search'),
1583 ('r', 'rev', [], 'revision'),
1583 ('r', 'rev', [], 'revision'),
1584 ('p', 'patch', None, 'show patch')],
1584 ('p', 'patch', None, 'show patch')],
1585 'hg log [-I] [-X] [-r REV]... [-p] [FILE]'),
1585 'hg log [-I] [-X] [-r REV]... [-p] [FILE]'),
1586 "manifest": (manifest, [], 'hg manifest [REV]'),
1586 "manifest": (manifest, [], 'hg manifest [REV]'),
1587 "outgoing|out": (outgoing, [], 'hg outgoing [DEST]'),
1587 "outgoing|out": (outgoing, [], 'hg outgoing [DEST]'),
1588 "parents": (parents, [], 'hg parents [REV]'),
1588 "parents": (parents, [], 'hg parents [REV]'),
1589 "paths": (paths, [], 'hg paths [NAME]'),
1589 "paths": (paths, [], 'hg paths [NAME]'),
1590 "^pull":
1590 "^pull":
1591 (pull,
1591 (pull,
1592 [('u', 'update', None, 'update working directory'),
1592 [('u', 'update', None, 'update working directory'),
1593 ('e', 'ssh', "", 'ssh command'),
1593 ('e', 'ssh', "", 'ssh command'),
1594 ('', 'remotecmd', "", 'remote hg command')],
1594 ('', 'remotecmd', "", 'remote hg command')],
1595 'hg pull [-u] [-e FILE] [--remotecmd FILE] [SOURCE]'),
1595 'hg pull [-u] [-e FILE] [--remotecmd FILE] [SOURCE]'),
1596 "^push":
1596 "^push":
1597 (push,
1597 (push,
1598 [('f', 'force', None, 'force push'),
1598 [('f', 'force', None, 'force push'),
1599 ('e', 'ssh', "", 'ssh command'),
1599 ('e', 'ssh', "", 'ssh command'),
1600 ('', 'remotecmd', "", 'remote hg command')],
1600 ('', 'remotecmd', "", 'remote hg command')],
1601 'hg push [-f] [-e FILE] [--remotecmd FILE] [DEST]'),
1601 'hg push [-f] [-e FILE] [--remotecmd FILE] [DEST]'),
1602 "rawcommit":
1602 "rawcommit":
1603 (rawcommit,
1603 (rawcommit,
1604 [('p', 'parent', [], 'parent'),
1604 [('p', 'parent', [], 'parent'),
1605 ('d', 'date', "", 'date code'),
1605 ('d', 'date', "", 'date code'),
1606 ('u', 'user', "", 'user'),
1606 ('u', 'user', "", 'user'),
1607 ('F', 'files', "", 'file list'),
1607 ('F', 'files', "", 'file list'),
1608 ('m', 'message', "", 'commit message'),
1608 ('m', 'message', "", 'commit message'),
1609 ('t', 'text', "", 'commit message (deprecated: use -m)'),
1609 ('t', 'text', "", 'commit message (deprecated: use -m)'),
1610 ('l', 'logfile', "", 'commit message file')],
1610 ('l', 'logfile', "", 'commit message file')],
1611 'hg rawcommit [OPTION]... [FILE]...'),
1611 'hg rawcommit [OPTION]... [FILE]...'),
1612 "recover": (recover, [], "hg recover"),
1612 "recover": (recover, [], "hg recover"),
1613 "^remove|rm": (remove, [], "hg remove FILE..."),
1613 "^remove|rm": (remove, [], "hg remove FILE..."),
1614 "^revert":
1614 "^revert":
1615 (revert,
1615 (revert,
1616 [("n", "nonrecursive", None, "don't recurse into subdirs"),
1616 [("n", "nonrecursive", None, "don't recurse into subdirs"),
1617 ("r", "rev", "", "revision")],
1617 ("r", "rev", "", "revision")],
1618 "hg revert [-n] [-r REV] [NAME]..."),
1618 "hg revert [-n] [-r REV] [NAME]..."),
1619 "root": (root, [], "hg root"),
1619 "root": (root, [], "hg root"),
1620 "^serve":
1620 "^serve":
1621 (serve,
1621 (serve,
1622 [('A', 'accesslog', '', 'access log file'),
1622 [('A', 'accesslog', '', 'access log file'),
1623 ('E', 'errorlog', '', 'error log file'),
1623 ('E', 'errorlog', '', 'error log file'),
1624 ('p', 'port', 0, 'listen port'),
1624 ('p', 'port', 0, 'listen port'),
1625 ('a', 'address', '', 'interface address'),
1625 ('a', 'address', '', 'interface address'),
1626 ('n', 'name', "", 'repository name'),
1626 ('n', 'name', "", 'repository name'),
1627 ('', 'stdio', None, 'for remote clients'),
1627 ('', 'stdio', None, 'for remote clients'),
1628 ('t', 'templates', "", 'template directory'),
1628 ('t', 'templates', "", 'template directory'),
1629 ('', 'style', "", 'template style'),
1629 ('', 'style', "", 'template style'),
1630 ('6', 'ipv6', None, 'use IPv6 in addition to IPv4')],
1630 ('6', 'ipv6', None, 'use IPv6 in addition to IPv4')],
1631 "hg serve [OPTION]..."),
1631 "hg serve [OPTION]..."),
1632 "^status":
1632 "^status":
1633 (status,
1633 (status,
1634 [('m', 'modified', None, 'show only modified files'),
1634 [('m', 'modified', None, 'show only modified files'),
1635 ('a', 'added', None, 'show only added files'),
1635 ('a', 'added', None, 'show only added files'),
1636 ('r', 'removed', None, 'show only removed files'),
1636 ('r', 'removed', None, 'show only removed files'),
1637 ('u', 'unknown', None, 'show only unknown (not tracked) files'),
1637 ('u', 'unknown', None, 'show only unknown (not tracked) files'),
1638 ('p', 'strip', None, 'strip status prefix'),
1638 ('p', 'strip', None, 'strip status prefix'),
1639 ('0', 'print0', None, 'end records with NUL'),
1639 ('0', 'print0', None, 'end records with NUL'),
1640 ('I', 'include', [], 'include path in search'),
1640 ('I', 'include', [], 'include path in search'),
1641 ('X', 'exclude', [], 'exclude path from search')],
1641 ('X', 'exclude', [], 'exclude path from search')],
1642 "hg status [OPTION]... [FILE]..."),
1642 "hg status [OPTION]... [FILE]..."),
1643 "tag":
1643 "tag":
1644 (tag,
1644 (tag,
1645 [('l', 'local', None, 'make the tag local'),
1645 [('l', 'local', None, 'make the tag local'),
1646 ('m', 'message', "", 'commit message'),
1646 ('m', 'message', "", 'commit message'),
1647 ('t', 'text', "", 'commit message (deprecated: use -m)'),
1647 ('t', 'text', "", 'commit message (deprecated: use -m)'),
1648 ('d', 'date', "", 'date code'),
1648 ('d', 'date', "", 'date code'),
1649 ('u', 'user', "", 'user')],
1649 ('u', 'user', "", 'user')],
1650 'hg tag [OPTION]... NAME [REV]'),
1650 'hg tag [OPTION]... NAME [REV]'),
1651 "tags": (tags, [], 'hg tags'),
1651 "tags": (tags, [], 'hg tags'),
1652 "tip": (tip, [], 'hg tip'),
1652 "tip": (tip, [], 'hg tip'),
1653 "undo": (undo, [], 'hg undo'),
1653 "undo": (undo, [], 'hg undo'),
1654 "^update|up|checkout|co":
1654 "^update|up|checkout|co":
1655 (update,
1655 (update,
1656 [('b', 'branch', "", 'checkout the head of a specific branch'),
1656 [('b', 'branch', "", 'checkout the head of a specific branch'),
1657 ('m', 'merge', None, 'allow merging of conflicts'),
1657 ('m', 'merge', None, 'allow merging of conflicts'),
1658 ('C', 'clean', None, 'overwrite locally modified files')],
1658 ('C', 'clean', None, 'overwrite locally modified files')],
1659 'hg update [-b TAG] [-m] [-C] [REV]'),
1659 'hg update [-b TAG] [-m] [-C] [REV]'),
1660 "verify": (verify, [], 'hg verify'),
1660 "verify": (verify, [], 'hg verify'),
1661 "version": (show_version, [], 'hg version'),
1661 "version": (show_version, [], 'hg version'),
1662 }
1662 }
1663
1663
1664 globalopts = [
1664 globalopts = [
1665 ('R', 'repository', "", 'repository root directory'),
1665 ('R', 'repository', "", 'repository root directory'),
1666 ('', 'cwd', '', 'change working directory'),
1666 ('', 'cwd', '', 'change working directory'),
1667 ('y', 'noninteractive', None, 'run non-interactively'),
1667 ('y', 'noninteractive', None, 'run non-interactively'),
1668 ('q', 'quiet', None, 'quiet mode'),
1668 ('q', 'quiet', None, 'quiet mode'),
1669 ('v', 'verbose', None, 'verbose mode'),
1669 ('v', 'verbose', None, 'verbose mode'),
1670 ('', 'debug', None, 'debug mode'),
1670 ('', 'debug', None, 'debug mode'),
1671 ('', 'traceback', None, 'print traceback on exception'),
1671 ('', 'traceback', None, 'print traceback on exception'),
1672 ('', 'time', None, 'time how long the command takes'),
1672 ('', 'time', None, 'time how long the command takes'),
1673 ('', 'profile', None, 'profile'),
1673 ('', 'profile', None, 'profile'),
1674 ('', 'version', None, 'output version information and exit'),
1674 ('', 'version', None, 'output version information and exit'),
1675 ('h', 'help', None, 'display help and exit'),
1675 ('h', 'help', None, 'display help and exit'),
1676 ]
1676 ]
1677
1677
1678 norepo = ("clone init version help debugconfig debugdata"
1678 norepo = ("clone init version help debugconfig debugdata"
1679 " debugindex debugindexdot paths")
1679 " debugindex debugindexdot paths")
1680
1680
1681 def find(cmd):
1681 def find(cmd):
1682 for e in table.keys():
1682 for e in table.keys():
1683 if re.match("(%s)$" % e, cmd):
1683 if re.match("(%s)$" % e, cmd):
1684 return e, table[e]
1684 return e, table[e]
1685
1685
1686 raise UnknownCommand(cmd)
1686 raise UnknownCommand(cmd)
1687
1687
1688 class SignalInterrupt(Exception):
1688 class SignalInterrupt(Exception):
1689 """Exception raised on SIGTERM and SIGHUP."""
1689 """Exception raised on SIGTERM and SIGHUP."""
1690
1690
1691 def catchterm(*args):
1691 def catchterm(*args):
1692 raise SignalInterrupt
1692 raise SignalInterrupt
1693
1693
1694 def run():
1694 def run():
1695 sys.exit(dispatch(sys.argv[1:]))
1695 sys.exit(dispatch(sys.argv[1:]))
1696
1696
1697 class ParseError(Exception):
1697 class ParseError(Exception):
1698 """Exception raised on errors in parsing the command line."""
1698 """Exception raised on errors in parsing the command line."""
1699
1699
1700 def parse(args):
1700 def parse(args):
1701 options = {}
1701 options = {}
1702 cmdoptions = {}
1702 cmdoptions = {}
1703
1703
1704 try:
1704 try:
1705 args = fancyopts.fancyopts(args, globalopts, options)
1705 args = fancyopts.fancyopts(args, globalopts, options)
1706 except fancyopts.getopt.GetoptError, inst:
1706 except fancyopts.getopt.GetoptError, inst:
1707 raise ParseError(None, inst)
1707 raise ParseError(None, inst)
1708
1708
1709 if args:
1709 if args:
1710 cmd, args = args[0], args[1:]
1710 cmd, args = args[0], args[1:]
1711 i = find(cmd)[1]
1711 i = find(cmd)[1]
1712 c = list(i[1])
1712 c = list(i[1])
1713 else:
1713 else:
1714 cmd = None
1714 cmd = None
1715 c = []
1715 c = []
1716
1716
1717 # combine global options into local
1717 # combine global options into local
1718 for o in globalopts:
1718 for o in globalopts:
1719 c.append((o[0], o[1], options[o[1]], o[3]))
1719 c.append((o[0], o[1], options[o[1]], o[3]))
1720
1720
1721 try:
1721 try:
1722 args = fancyopts.fancyopts(args, c, cmdoptions)
1722 args = fancyopts.fancyopts(args, c, cmdoptions)
1723 except fancyopts.getopt.GetoptError, inst:
1723 except fancyopts.getopt.GetoptError, inst:
1724 raise ParseError(cmd, inst)
1724 raise ParseError(cmd, inst)
1725
1725
1726 # separate global options back out
1726 # separate global options back out
1727 for o in globalopts:
1727 for o in globalopts:
1728 n = o[1]
1728 n = o[1]
1729 options[n] = cmdoptions[n]
1729 options[n] = cmdoptions[n]
1730 del cmdoptions[n]
1730 del cmdoptions[n]
1731
1731
1732 return (cmd, cmd and i[0] or None, args, options, cmdoptions)
1732 return (cmd, cmd and i[0] or None, args, options, cmdoptions)
1733
1733
1734 def dispatch(args):
1734 def dispatch(args):
1735 signal.signal(signal.SIGTERM, catchterm)
1735 signal.signal(signal.SIGTERM, catchterm)
1736 try:
1736 try:
1737 signal.signal(signal.SIGHUP, catchterm)
1737 signal.signal(signal.SIGHUP, catchterm)
1738 except AttributeError:
1738 except AttributeError:
1739 pass
1739 pass
1740
1740
1741 u = ui.ui()
1741 u = ui.ui()
1742 external = []
1742 external = []
1743 for x in u.extensions():
1743 for x in u.extensions():
1744 if x[1]:
1744 if x[1]:
1745 mod = imp.load_source(x[0], x[1])
1745 mod = imp.load_source(x[0], x[1])
1746 else:
1746 else:
1747 def importh(name):
1747 def importh(name):
1748 mod = __import__(name)
1748 mod = __import__(name)
1749 components = name.split('.')
1749 components = name.split('.')
1750 for comp in components[1:]:
1750 for comp in components[1:]:
1751 mod = getattr(mod, comp)
1751 mod = getattr(mod, comp)
1752 return mod
1752 return mod
1753 mod = importh(x[0])
1753 mod = importh(x[0])
1754 external.append(mod)
1754 external.append(mod)
1755 for x in external:
1755 for x in external:
1756 for t in x.cmdtable:
1756 for t in x.cmdtable:
1757 if t in table:
1757 if t in table:
1758 u.warn("module %s override %s\n" % (x.__name__, t))
1758 u.warn("module %s override %s\n" % (x.__name__, t))
1759 table.update(x.cmdtable)
1759 table.update(x.cmdtable)
1760
1760
1761 try:
1761 try:
1762 cmd, func, args, options, cmdoptions = parse(args)
1762 cmd, func, args, options, cmdoptions = parse(args)
1763 except ParseError, inst:
1763 except ParseError, inst:
1764 if inst.args[0]:
1764 if inst.args[0]:
1765 u.warn("hg %s: %s\n" % (inst.args[0], inst.args[1]))
1765 u.warn("hg %s: %s\n" % (inst.args[0], inst.args[1]))
1766 help_(u, inst.args[0])
1766 help_(u, inst.args[0])
1767 else:
1767 else:
1768 u.warn("hg: %s\n" % inst.args[1])
1768 u.warn("hg: %s\n" % inst.args[1])
1769 help_(u, 'shortlist')
1769 help_(u, 'shortlist')
1770 sys.exit(-1)
1770 sys.exit(-1)
1771 except UnknownCommand, inst:
1771 except UnknownCommand, inst:
1772 u.warn("hg: unknown command '%s'\n" % inst.args[0])
1772 u.warn("hg: unknown command '%s'\n" % inst.args[0])
1773 help_(u, 'shortlist')
1773 help_(u, 'shortlist')
1774 sys.exit(1)
1774 sys.exit(1)
1775
1775
1776 if options["time"]:
1776 if options["time"]:
1777 def get_times():
1777 def get_times():
1778 t = os.times()
1778 t = os.times()
1779 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
1779 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
1780 t = (t[0], t[1], t[2], t[3], time.clock())
1780 t = (t[0], t[1], t[2], t[3], time.clock())
1781 return t
1781 return t
1782 s = get_times()
1782 s = get_times()
1783 def print_time():
1783 def print_time():
1784 t = get_times()
1784 t = get_times()
1785 u.warn("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n" %
1785 u.warn("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n" %
1786 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
1786 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
1787 atexit.register(print_time)
1787 atexit.register(print_time)
1788
1788
1789 u.updateopts(options["verbose"], options["debug"], options["quiet"],
1789 u.updateopts(options["verbose"], options["debug"], options["quiet"],
1790 not options["noninteractive"])
1790 not options["noninteractive"])
1791
1791
1792 try:
1792 try:
1793 try:
1793 try:
1794 if options['help']:
1794 if options['help']:
1795 help_(u, cmd, options['version'])
1795 help_(u, cmd, options['version'])
1796 sys.exit(0)
1796 sys.exit(0)
1797 elif options['version']:
1797 elif options['version']:
1798 show_version(u)
1798 show_version(u)
1799 sys.exit(0)
1799 sys.exit(0)
1800 elif not cmd:
1800 elif not cmd:
1801 help_(u, 'shortlist')
1801 help_(u, 'shortlist')
1802 sys.exit(0)
1802 sys.exit(0)
1803
1803
1804 if options['cwd']:
1804 if options['cwd']:
1805 try:
1805 try:
1806 os.chdir(options['cwd'])
1806 os.chdir(options['cwd'])
1807 except OSError, inst:
1807 except OSError, inst:
1808 u.warn('abort: %s: %s\n' % (options['cwd'], inst.strerror))
1808 u.warn('abort: %s: %s\n' % (options['cwd'], inst.strerror))
1809 sys.exit(1)
1809 sys.exit(1)
1810
1810
1811 if cmd not in norepo.split():
1811 if cmd not in norepo.split():
1812 path = options["repository"] or ""
1812 path = options["repository"] or ""
1813 repo = hg.repository(ui=u, path=path)
1813 repo = hg.repository(ui=u, path=path)
1814 for x in external:
1814 for x in external:
1815 x.reposetup(u, repo)
1815 x.reposetup(u, repo)
1816 d = lambda: func(u, repo, *args, **cmdoptions)
1816 d = lambda: func(u, repo, *args, **cmdoptions)
1817 else:
1817 else:
1818 d = lambda: func(u, *args, **cmdoptions)
1818 d = lambda: func(u, *args, **cmdoptions)
1819
1819
1820 if options['profile']:
1820 if options['profile']:
1821 import hotshot, hotshot.stats
1821 import hotshot, hotshot.stats
1822 prof = hotshot.Profile("hg.prof")
1822 prof = hotshot.Profile("hg.prof")
1823 r = prof.runcall(d)
1823 r = prof.runcall(d)
1824 prof.close()
1824 prof.close()
1825 stats = hotshot.stats.load("hg.prof")
1825 stats = hotshot.stats.load("hg.prof")
1826 stats.strip_dirs()
1826 stats.strip_dirs()
1827 stats.sort_stats('time', 'calls')
1827 stats.sort_stats('time', 'calls')
1828 stats.print_stats(40)
1828 stats.print_stats(40)
1829 return r
1829 return r
1830 else:
1830 else:
1831 return d()
1831 return d()
1832 except:
1832 except:
1833 if options['traceback']:
1833 if options['traceback']:
1834 traceback.print_exc()
1834 traceback.print_exc()
1835 raise
1835 raise
1836 except hg.RepoError, inst:
1836 except hg.RepoError, inst:
1837 u.warn("abort: ", inst, "!\n")
1837 u.warn("abort: ", inst, "!\n")
1838 except SignalInterrupt:
1838 except SignalInterrupt:
1839 u.warn("killed!\n")
1839 u.warn("killed!\n")
1840 except KeyboardInterrupt:
1840 except KeyboardInterrupt:
1841 try:
1841 try:
1842 u.warn("interrupted!\n")
1842 u.warn("interrupted!\n")
1843 except IOError, inst:
1843 except IOError, inst:
1844 if inst.errno == errno.EPIPE:
1844 if inst.errno == errno.EPIPE:
1845 if u.debugflag:
1845 if u.debugflag:
1846 u.warn("\nbroken pipe\n")
1846 u.warn("\nbroken pipe\n")
1847 else:
1847 else:
1848 raise
1848 raise
1849 except IOError, inst:
1849 except IOError, inst:
1850 if hasattr(inst, "code"):
1850 if hasattr(inst, "code"):
1851 u.warn("abort: %s\n" % inst)
1851 u.warn("abort: %s\n" % inst)
1852 elif hasattr(inst, "reason"):
1852 elif hasattr(inst, "reason"):
1853 u.warn("abort: error: %s\n" % inst.reason[1])
1853 u.warn("abort: error: %s\n" % inst.reason[1])
1854 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
1854 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
1855 if u.debugflag:
1855 if u.debugflag:
1856 u.warn("broken pipe\n")
1856 u.warn("broken pipe\n")
1857 else:
1857 else:
1858 raise
1858 raise
1859 except OSError, inst:
1859 except OSError, inst:
1860 if hasattr(inst, "filename"):
1860 if hasattr(inst, "filename"):
1861 u.warn("abort: %s: %s\n" % (inst.strerror, inst.filename))
1861 u.warn("abort: %s: %s\n" % (inst.strerror, inst.filename))
1862 else:
1862 else:
1863 u.warn("abort: %s\n" % inst.strerror)
1863 u.warn("abort: %s\n" % inst.strerror)
1864 except util.Abort, inst:
1864 except util.Abort, inst:
1865 u.warn('abort: ', inst.args[0] % inst.args[1:], '\n')
1865 u.warn('abort: ', inst.args[0] % inst.args[1:], '\n')
1866 sys.exit(1)
1866 sys.exit(1)
1867 except TypeError, inst:
1867 except TypeError, inst:
1868 # was this an argument error?
1868 # was this an argument error?
1869 tb = traceback.extract_tb(sys.exc_info()[2])
1869 tb = traceback.extract_tb(sys.exc_info()[2])
1870 if len(tb) > 2: # no
1870 if len(tb) > 2: # no
1871 raise
1871 raise
1872 u.debug(inst, "\n")
1872 u.debug(inst, "\n")
1873 u.warn("%s: invalid arguments\n" % cmd)
1873 u.warn("%s: invalid arguments\n" % cmd)
1874 help_(u, cmd)
1874 help_(u, cmd)
1875 except UnknownCommand, inst:
1875 except UnknownCommand, inst:
1876 u.warn("hg: unknown command '%s'\n" % inst.args[0])
1876 u.warn("hg: unknown command '%s'\n" % inst.args[0])
1877 help_(u, 'shortlist')
1877 help_(u, 'shortlist')
1878
1878
1879 sys.exit(-1)
1879 sys.exit(-1)
@@ -1,59 +1,31
1 # hg.py - repository classes for mercurial
1 # hg.py - repository classes for mercurial
2 #
2 #
3 # Copyright 2005 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005 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 import os
8 import os
9 import util
9 import util
10 from node import *
10 from node import *
11 from revlog import *
11 from revlog import *
12 from repo import *
12 from repo import *
13 from demandload import *
13 from demandload import *
14 demandload(globals(), "localrepo httprepo sshrepo")
14 demandload(globals(), "localrepo httprepo sshrepo")
15
15
16 # used to avoid circular references so destructors work
17 def opener(base):
18 p = base
19 def o(path, mode="r"):
20 if p.startswith("http://"):
21 f = os.path.join(p, urllib.quote(path))
22 return httprangereader.httprangereader(f)
23
24 f = os.path.join(p, path)
25
26 mode += "b" # for that other OS
27
28 if mode[0] != "r":
29 try:
30 s = os.stat(f)
31 except OSError:
32 d = os.path.dirname(f)
33 if not os.path.isdir(d):
34 os.makedirs(d)
35 else:
36 if s.st_nlink > 1:
37 file(f + ".tmp", "wb").write(file(f, "rb").read())
38 util.rename(f+".tmp", f)
39
40 return file(f, mode)
41
42 return o
43
44 def repository(ui, path=None, create=0):
16 def repository(ui, path=None, create=0):
45 if path:
17 if path:
46 if path.startswith("http://"):
18 if path.startswith("http://"):
47 return httprepo.httprepository(ui, path)
19 return httprepo.httprepository(ui, path)
48 if path.startswith("https://"):
20 if path.startswith("https://"):
49 return httprepo.httpsrepository(ui, path)
21 return httprepo.httpsrepository(ui, path)
50 if path.startswith("hg://"):
22 if path.startswith("hg://"):
51 return httprepo.httprepository(
23 return httprepo.httprepository(
52 ui, path.replace("hg://", "http://"))
24 ui, path.replace("hg://", "http://"))
53 if path.startswith("old-http://"):
25 if path.startswith("old-http://"):
54 return localrepo.localrepository(
26 return localrepo.localrepository(
55 ui, opener, path.replace("old-http://", "http://"))
27 ui, util.opener, path.replace("old-http://", "http://"))
56 if path.startswith("ssh://"):
28 if path.startswith("ssh://"):
57 return sshrepo.sshrepository(ui, path)
29 return sshrepo.sshrepository(ui, path)
58
30
59 return localrepo.localrepository(ui, opener, path, create)
31 return localrepo.localrepository(ui, util.opener, path, create)
@@ -1,327 +1,362
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
5
6 This software may be used and distributed according to the terms
6 This software may be used and distributed according to the terms
7 of the GNU General Public License, incorporated herein by reference.
7 of the GNU General Public License, incorporated herein by reference.
8
8
9 This contains helper routines that are independent of the SCM core and hide
9 This contains helper routines that are independent of the SCM core and hide
10 platform-specific details from the core.
10 platform-specific details from the core.
11 """
11 """
12
12
13 import os, errno
13 import os, errno
14 from demandload import *
14 from demandload import *
15 demandload(globals(), "re")
15 demandload(globals(), "re")
16
16
17 def binary(s):
17 def binary(s):
18 """return true if a string is binary data using diff's heuristic"""
18 """return true if a string is binary data using diff's heuristic"""
19 if s and '\0' in s[:4096]:
19 if s and '\0' in s[:4096]:
20 return True
20 return True
21 return False
21 return False
22
22
23 def unique(g):
23 def unique(g):
24 """return the uniq elements of iterable g"""
24 """return the uniq elements of iterable g"""
25 seen = {}
25 seen = {}
26 for f in g:
26 for f in g:
27 if f not in seen:
27 if f not in seen:
28 seen[f] = 1
28 seen[f] = 1
29 yield f
29 yield f
30
30
31 class Abort(Exception):
31 class Abort(Exception):
32 """Raised if a command needs to print an error and exit."""
32 """Raised if a command needs to print an error and exit."""
33
33
34 def always(fn): return True
34 def always(fn): return True
35 def never(fn): return False
35 def never(fn): return False
36
36
37 def globre(pat, head='^', tail='$'):
37 def globre(pat, head='^', tail='$'):
38 "convert a glob pattern into a regexp"
38 "convert a glob pattern into a regexp"
39 i, n = 0, len(pat)
39 i, n = 0, len(pat)
40 res = ''
40 res = ''
41 group = False
41 group = False
42 def peek(): return i < n and pat[i]
42 def peek(): return i < n and pat[i]
43 while i < n:
43 while i < n:
44 c = pat[i]
44 c = pat[i]
45 i = i+1
45 i = i+1
46 if c == '*':
46 if c == '*':
47 if peek() == '*':
47 if peek() == '*':
48 i += 1
48 i += 1
49 res += '.*'
49 res += '.*'
50 else:
50 else:
51 res += '[^/]*'
51 res += '[^/]*'
52 elif c == '?':
52 elif c == '?':
53 res += '.'
53 res += '.'
54 elif c == '[':
54 elif c == '[':
55 j = i
55 j = i
56 if j < n and pat[j] in '!]':
56 if j < n and pat[j] in '!]':
57 j += 1
57 j += 1
58 while j < n and pat[j] != ']':
58 while j < n and pat[j] != ']':
59 j += 1
59 j += 1
60 if j >= n:
60 if j >= n:
61 res += '\\['
61 res += '\\['
62 else:
62 else:
63 stuff = pat[i:j].replace('\\','\\\\')
63 stuff = pat[i:j].replace('\\','\\\\')
64 i = j + 1
64 i = j + 1
65 if stuff[0] == '!':
65 if stuff[0] == '!':
66 stuff = '^' + stuff[1:]
66 stuff = '^' + stuff[1:]
67 elif stuff[0] == '^':
67 elif stuff[0] == '^':
68 stuff = '\\' + stuff
68 stuff = '\\' + stuff
69 res = '%s[%s]' % (res, stuff)
69 res = '%s[%s]' % (res, stuff)
70 elif c == '{':
70 elif c == '{':
71 group = True
71 group = True
72 res += '(?:'
72 res += '(?:'
73 elif c == '}' and group:
73 elif c == '}' and group:
74 res += ')'
74 res += ')'
75 group = False
75 group = False
76 elif c == ',' and group:
76 elif c == ',' and group:
77 res += '|'
77 res += '|'
78 else:
78 else:
79 res += re.escape(c)
79 res += re.escape(c)
80 return head + res + tail
80 return head + res + tail
81
81
82 _globchars = {'[': 1, '{': 1, '*': 1, '?': 1}
82 _globchars = {'[': 1, '{': 1, '*': 1, '?': 1}
83
83
84 def pathto(n1, n2):
84 def pathto(n1, n2):
85 '''return the relative path from one place to another.
85 '''return the relative path from one place to another.
86 this returns a path in the form used by the local filesystem, not hg.'''
86 this returns a path in the form used by the local filesystem, not hg.'''
87 if not n1: return localpath(n2)
87 if not n1: return localpath(n2)
88 a, b = n1.split('/'), n2.split('/')
88 a, b = n1.split('/'), n2.split('/')
89 a.reverse(), b.reverse()
89 a.reverse(), b.reverse()
90 while a and b and a[-1] == b[-1]:
90 while a and b and a[-1] == b[-1]:
91 a.pop(), b.pop()
91 a.pop(), b.pop()
92 b.reverse()
92 b.reverse()
93 return os.sep.join((['..'] * len(a)) + b)
93 return os.sep.join((['..'] * len(a)) + b)
94
94
95 def canonpath(root, cwd, myname):
95 def canonpath(root, cwd, myname):
96 """return the canonical path of myname, given cwd and root"""
96 """return the canonical path of myname, given cwd and root"""
97 rootsep = root + os.sep
97 rootsep = root + os.sep
98 name = myname
98 name = myname
99 if not name.startswith(os.sep):
99 if not name.startswith(os.sep):
100 name = os.path.join(root, cwd, name)
100 name = os.path.join(root, cwd, name)
101 name = os.path.normpath(name)
101 name = os.path.normpath(name)
102 if name.startswith(rootsep):
102 if name.startswith(rootsep):
103 return pconvert(name[len(rootsep):])
103 return pconvert(name[len(rootsep):])
104 elif name == root:
104 elif name == root:
105 return ''
105 return ''
106 else:
106 else:
107 raise Abort('%s not under root' % myname)
107 raise Abort('%s not under root' % myname)
108
108
109 def matcher(canonroot, cwd, names, inc, exc, head=''):
109 def matcher(canonroot, cwd, names, inc, exc, head=''):
110 """build a function to match a set of file patterns
110 """build a function to match a set of file patterns
111
111
112 arguments:
112 arguments:
113 canonroot - the canonical root of the tree you're matching against
113 canonroot - the canonical root of the tree you're matching against
114 cwd - the current working directory, if relevant
114 cwd - the current working directory, if relevant
115 names - patterns to find
115 names - patterns to find
116 inc - patterns to include
116 inc - patterns to include
117 exc - patterns to exclude
117 exc - patterns to exclude
118 head - a regex to prepend to patterns to control whether a match is rooted
118 head - a regex to prepend to patterns to control whether a match is rooted
119
119
120 a pattern is one of:
120 a pattern is one of:
121 're:<regex>'
121 're:<regex>'
122 'glob:<shellglob>'
122 'glob:<shellglob>'
123 'path:<explicit path>'
123 'path:<explicit path>'
124 'relpath:<relative path>'
124 'relpath:<relative path>'
125 '<relative path>'
125 '<relative path>'
126
126
127 returns:
127 returns:
128 a 3-tuple containing
128 a 3-tuple containing
129 - list of explicit non-pattern names passed in
129 - list of explicit non-pattern names passed in
130 - a bool match(filename) function
130 - a bool match(filename) function
131 - a bool indicating if any patterns were passed in
131 - a bool indicating if any patterns were passed in
132
132
133 todo:
133 todo:
134 make head regex a rooted bool
134 make head regex a rooted bool
135 """
135 """
136
136
137 def patkind(name):
137 def patkind(name):
138 for prefix in 're:', 'glob:', 'path:', 'relpath:':
138 for prefix in 're:', 'glob:', 'path:', 'relpath:':
139 if name.startswith(prefix): return name.split(':', 1)
139 if name.startswith(prefix): return name.split(':', 1)
140 for c in name:
140 for c in name:
141 if c in _globchars: return 'glob', name
141 if c in _globchars: return 'glob', name
142 return 'relpath', name
142 return 'relpath', name
143
143
144 def regex(kind, name, tail):
144 def regex(kind, name, tail):
145 '''convert a pattern into a regular expression'''
145 '''convert a pattern into a regular expression'''
146 if kind == 're':
146 if kind == 're':
147 return name
147 return name
148 elif kind == 'path':
148 elif kind == 'path':
149 return '^' + re.escape(name) + '(?:/|$)'
149 return '^' + re.escape(name) + '(?:/|$)'
150 elif kind == 'relpath':
150 elif kind == 'relpath':
151 return head + re.escape(name) + tail
151 return head + re.escape(name) + tail
152 return head + globre(name, '', tail)
152 return head + globre(name, '', tail)
153
153
154 def matchfn(pats, tail):
154 def matchfn(pats, tail):
155 """build a matching function from a set of patterns"""
155 """build a matching function from a set of patterns"""
156 if pats:
156 if pats:
157 pat = '(?:%s)' % '|'.join([regex(k, p, tail) for (k, p) in pats])
157 pat = '(?:%s)' % '|'.join([regex(k, p, tail) for (k, p) in pats])
158 return re.compile(pat).match
158 return re.compile(pat).match
159
159
160 def globprefix(pat):
160 def globprefix(pat):
161 '''return the non-glob prefix of a path, e.g. foo/* -> foo'''
161 '''return the non-glob prefix of a path, e.g. foo/* -> foo'''
162 root = []
162 root = []
163 for p in pat.split(os.sep):
163 for p in pat.split(os.sep):
164 if patkind(p)[0] == 'glob': break
164 if patkind(p)[0] == 'glob': break
165 root.append(p)
165 root.append(p)
166 return '/'.join(root)
166 return '/'.join(root)
167
167
168 pats = []
168 pats = []
169 files = []
169 files = []
170 roots = []
170 roots = []
171 for kind, name in map(patkind, names):
171 for kind, name in map(patkind, names):
172 if kind in ('glob', 'relpath'):
172 if kind in ('glob', 'relpath'):
173 name = canonpath(canonroot, cwd, name)
173 name = canonpath(canonroot, cwd, name)
174 if name == '':
174 if name == '':
175 kind, name = 'glob', '**'
175 kind, name = 'glob', '**'
176 if kind in ('glob', 'path', 're'):
176 if kind in ('glob', 'path', 're'):
177 pats.append((kind, name))
177 pats.append((kind, name))
178 if kind == 'glob':
178 if kind == 'glob':
179 root = globprefix(name)
179 root = globprefix(name)
180 if root: roots.append(root)
180 if root: roots.append(root)
181 elif kind == 'relpath':
181 elif kind == 'relpath':
182 files.append((kind, name))
182 files.append((kind, name))
183 roots.append(name)
183 roots.append(name)
184
184
185 patmatch = matchfn(pats, '$') or always
185 patmatch = matchfn(pats, '$') or always
186 filematch = matchfn(files, '(?:/|$)') or always
186 filematch = matchfn(files, '(?:/|$)') or always
187 incmatch = always
187 incmatch = always
188 if inc:
188 if inc:
189 incmatch = matchfn(map(patkind, inc), '(?:/|$)')
189 incmatch = matchfn(map(patkind, inc), '(?:/|$)')
190 excmatch = lambda fn: False
190 excmatch = lambda fn: False
191 if exc:
191 if exc:
192 excmatch = matchfn(map(patkind, exc), '(?:/|$)')
192 excmatch = matchfn(map(patkind, exc), '(?:/|$)')
193
193
194 return (roots,
194 return (roots,
195 lambda fn: (incmatch(fn) and not excmatch(fn) and
195 lambda fn: (incmatch(fn) and not excmatch(fn) and
196 (fn.endswith('/') or
196 (fn.endswith('/') or
197 (not pats and not files) or
197 (not pats and not files) or
198 (pats and patmatch(fn)) or
198 (pats and patmatch(fn)) or
199 (files and filematch(fn)))),
199 (files and filematch(fn)))),
200 (inc or exc or (pats and pats != [('glob', '**')])) and True)
200 (inc or exc or (pats and pats != [('glob', '**')])) and True)
201
201
202 def system(cmd, errprefix=None):
202 def system(cmd, errprefix=None):
203 """execute a shell command that must succeed"""
203 """execute a shell command that must succeed"""
204 rc = os.system(cmd)
204 rc = os.system(cmd)
205 if rc:
205 if rc:
206 errmsg = "%s %s" % (os.path.basename(cmd.split(None, 1)[0]),
206 errmsg = "%s %s" % (os.path.basename(cmd.split(None, 1)[0]),
207 explain_exit(rc)[0])
207 explain_exit(rc)[0])
208 if errprefix:
208 if errprefix:
209 errmsg = "%s: %s" % (errprefix, errmsg)
209 errmsg = "%s: %s" % (errprefix, errmsg)
210 raise Abort(errmsg)
210 raise Abort(errmsg)
211
211
212 def rename(src, dst):
212 def rename(src, dst):
213 """forcibly rename a file"""
213 """forcibly rename a file"""
214 try:
214 try:
215 os.rename(src, dst)
215 os.rename(src, dst)
216 except:
216 except:
217 os.unlink(dst)
217 os.unlink(dst)
218 os.rename(src, dst)
218 os.rename(src, dst)
219
219
220 def copytree(src, dst, copyfile):
220 def copytree(src, dst, copyfile):
221 """Copy a directory tree, files are copied using 'copyfile'."""
221 """Copy a directory tree, files are copied using 'copyfile'."""
222 names = os.listdir(src)
222 names = os.listdir(src)
223 os.mkdir(dst)
223 os.mkdir(dst)
224
224
225 for name in names:
225 for name in names:
226 srcname = os.path.join(src, name)
226 srcname = os.path.join(src, name)
227 dstname = os.path.join(dst, name)
227 dstname = os.path.join(dst, name)
228 if os.path.isdir(srcname):
228 if os.path.isdir(srcname):
229 copytree(srcname, dstname, copyfile)
229 copytree(srcname, dstname, copyfile)
230 elif os.path.isfile(srcname):
230 elif os.path.isfile(srcname):
231 copyfile(srcname, dstname)
231 copyfile(srcname, dstname)
232 else:
232 else:
233 pass
233 pass
234
234
235 def opener(base):
236 """
237 return a function that opens files relative to base
238
239 this function is used to hide the details of COW semantics and
240 remote file access from higher level code.
241
242 todo: separate remote file access into a separate function
243 """
244 p = base
245 def o(path, mode="r"):
246 if p.startswith("http://"):
247 f = os.path.join(p, urllib.quote(path))
248 return httprangereader.httprangereader(f)
249
250 f = os.path.join(p, path)
251
252 mode += "b" # for that other OS
253
254 if mode[0] != "r":
255 try:
256 s = os.stat(f)
257 except OSError:
258 d = os.path.dirname(f)
259 if not os.path.isdir(d):
260 os.makedirs(d)
261 else:
262 if s.st_nlink > 1:
263 file(f + ".tmp", "wb").write(file(f, "rb").read())
264 rename(f+".tmp", f)
265
266 return file(f, mode)
267
268 return o
269
235 def _makelock_file(info, pathname):
270 def _makelock_file(info, pathname):
236 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
271 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
237 os.write(ld, info)
272 os.write(ld, info)
238 os.close(ld)
273 os.close(ld)
239
274
240 def _readlock_file(pathname):
275 def _readlock_file(pathname):
241 return file(pathname).read()
276 return file(pathname).read()
242
277
243 # Platform specific variants
278 # Platform specific variants
244 if os.name == 'nt':
279 if os.name == 'nt':
245 nulldev = 'NUL:'
280 nulldev = 'NUL:'
246
281
247 def is_exec(f, last):
282 def is_exec(f, last):
248 return last
283 return last
249
284
250 def set_exec(f, mode):
285 def set_exec(f, mode):
251 pass
286 pass
252
287
253 def pconvert(path):
288 def pconvert(path):
254 return path.replace("\\", "/")
289 return path.replace("\\", "/")
255
290
256 def localpath(path):
291 def localpath(path):
257 return path.replace('/', '\\')
292 return path.replace('/', '\\')
258
293
259 def normpath(path):
294 def normpath(path):
260 return pconvert(os.path.normpath(path))
295 return pconvert(os.path.normpath(path))
261
296
262 makelock = _makelock_file
297 makelock = _makelock_file
263 readlock = _readlock_file
298 readlock = _readlock_file
264
299
265 def explain_exit(code):
300 def explain_exit(code):
266 return "exited with status %d" % code, code
301 return "exited with status %d" % code, code
267
302
268 else:
303 else:
269 nulldev = '/dev/null'
304 nulldev = '/dev/null'
270
305
271 def is_exec(f, last):
306 def is_exec(f, last):
272 """check whether a file is executable"""
307 """check whether a file is executable"""
273 return (os.stat(f).st_mode & 0100 != 0)
308 return (os.stat(f).st_mode & 0100 != 0)
274
309
275 def set_exec(f, mode):
310 def set_exec(f, mode):
276 s = os.stat(f).st_mode
311 s = os.stat(f).st_mode
277 if (s & 0100 != 0) == mode:
312 if (s & 0100 != 0) == mode:
278 return
313 return
279 if mode:
314 if mode:
280 # Turn on +x for every +r bit when making a file executable
315 # Turn on +x for every +r bit when making a file executable
281 # and obey umask.
316 # and obey umask.
282 umask = os.umask(0)
317 umask = os.umask(0)
283 os.umask(umask)
318 os.umask(umask)
284 os.chmod(f, s | (s & 0444) >> 2 & ~umask)
319 os.chmod(f, s | (s & 0444) >> 2 & ~umask)
285 else:
320 else:
286 os.chmod(f, s & 0666)
321 os.chmod(f, s & 0666)
287
322
288 def pconvert(path):
323 def pconvert(path):
289 return path
324 return path
290
325
291 def localpath(path):
326 def localpath(path):
292 return path
327 return path
293
328
294 normpath = os.path.normpath
329 normpath = os.path.normpath
295
330
296 def makelock(info, pathname):
331 def makelock(info, pathname):
297 try:
332 try:
298 os.symlink(info, pathname)
333 os.symlink(info, pathname)
299 except OSError, why:
334 except OSError, why:
300 if why.errno == errno.EEXIST:
335 if why.errno == errno.EEXIST:
301 raise
336 raise
302 else:
337 else:
303 _makelock_file(info, pathname)
338 _makelock_file(info, pathname)
304
339
305 def readlock(pathname):
340 def readlock(pathname):
306 try:
341 try:
307 return os.readlink(pathname)
342 return os.readlink(pathname)
308 except OSError, why:
343 except OSError, why:
309 if why.errno == errno.EINVAL:
344 if why.errno == errno.EINVAL:
310 return _readlock_file(pathname)
345 return _readlock_file(pathname)
311 else:
346 else:
312 raise
347 raise
313
348
314 def explain_exit(code):
349 def explain_exit(code):
315 """return a 2-tuple (desc, code) describing a process's status"""
350 """return a 2-tuple (desc, code) describing a process's status"""
316 if os.name == 'nt': # os.WIFxx is not supported on windows
351 if os.name == 'nt': # os.WIFxx is not supported on windows
317 return "aborted with error." , -1
352 return "aborted with error." , -1
318 if os.WIFEXITED(code):
353 if os.WIFEXITED(code):
319 val = os.WEXITSTATUS(code)
354 val = os.WEXITSTATUS(code)
320 return "exited with status %d" % val, val
355 return "exited with status %d" % val, val
321 elif os.WIFSIGNALED(code):
356 elif os.WIFSIGNALED(code):
322 val = os.WTERMSIG(code)
357 val = os.WTERMSIG(code)
323 return "killed by signal %d" % val, val
358 return "killed by signal %d" % val, val
324 elif os.WIFSTOPPED(code):
359 elif os.WIFSTOPPED(code):
325 val = os.WSTOPSIG(code)
360 val = os.WSTOPSIG(code)
326 return "stopped by signal %d" % val, val
361 return "stopped by signal %d" % val, val
327 raise ValueError("invalid exit code")
362 raise ValueError("invalid exit code")
General Comments 0
You need to be logged in to leave comments. Login now