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