##// END OF EJS Templates
merge with crew.
Vadim Gelfer -
r1920:b7cc0f32 merge default
parent child Browse files
Show More
@@ -0,0 +1,92 b''
1 import sys, textwrap
2 # import from the live mercurial repo
3 sys.path.insert(0, "..")
4 from mercurial.commands import table, globalopts
5 from mercurial.i18n import gettext as _
6
7 def get_desc(docstr):
8 if not docstr:
9 return "", ""
10 # sanitize
11 docstr = docstr.strip("\n")
12 docstr = docstr.rstrip()
13 shortdesc = docstr.splitlines()[0].strip()
14
15 i = docstr.find("\n")
16 if i != -1:
17 desc = docstr[i+2:]
18 else:
19 desc = " %s" % shortdesc
20 return (shortdesc, desc)
21
22 def get_opts(opts):
23 for shortopt, longopt, default, desc in opts:
24 allopts = []
25 if shortopt:
26 allopts.append("-%s" % shortopt)
27 if longopt:
28 allopts.append("--%s" % longopt)
29 desc += default and _(" (default: %s)") % default or ""
30 yield(", ".join(allopts), desc)
31
32 def get_cmd(cmd):
33 d = {}
34 attr = table[cmd]
35 cmds = cmd.lstrip("^").split("|")
36
37 d['synopsis'] = attr[2]
38 d['cmd'] = cmds[0]
39 d['aliases'] = cmd.split("|")[1:]
40 d['desc'] = get_desc(attr[0].__doc__)
41 d['opts'] = list(get_opts(attr[1]))
42 return d
43
44
45 def show_doc(ui):
46 def bold(s, text=""):
47 ui.write("%s\n%s\n%s\n" % (s, "="*len(s), text))
48 def underlined(s, text=""):
49 ui.write("%s\n%s\n%s\n" % (s, "-"*len(s), text))
50
51 # print options
52 underlined(_("OPTIONS"))
53 for optstr, desc in get_opts(globalopts):
54 ui.write("%s::\n %s\n\n" % (optstr, desc))
55
56 # print cmds
57 underlined(_("COMMANDS"))
58 h = {}
59 for c, attr in table.items():
60 f = c.split("|")[0]
61 f = f.lstrip("^")
62 h[f] = c
63 cmds = h.keys()
64 cmds.sort()
65
66 for f in cmds:
67 if f.startswith("debug"): continue
68 d = get_cmd(h[f])
69 # synopsis
70 ui.write("%s::\n" % d['synopsis'].replace("hg ","", 1))
71 # description
72 ui.write("%s\n\n" % d['desc'][1])
73 # options
74 opt_output = list(d['opts'])
75 if opt_output:
76 opts_len = max([len(line[0]) for line in opt_output])
77 ui.write(_(" options:\n"))
78 for optstr, desc in opt_output:
79 if desc:
80 s = "%-*s %s" % (opts_len, optstr, desc)
81 else:
82 s = optstr
83 s = textwrap.fill(s, initial_indent=4 * " ",
84 subsequent_indent=(6 + opts_len) * " ")
85 ui.write("%s\n" % s)
86 ui.write("\n")
87 # aliases
88 if d['aliases']:
89 ui.write(_(" aliases: %s\n\n") % " ".join(d['aliases']))
90
91 if __name__ == "__main__":
92 show_doc(sys.stdout)
This diff has been collapsed as it changes many lines, (1306 lines changed) Show them Hide them
@@ -0,0 +1,1306 b''
1 # queue.py - patch queues for mercurial
2 #
3 # Copyright 2005 Chris Mason <mason@suse.com>
4 #
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
7
8 from mercurial.demandload import *
9 demandload(globals(), "os sys re struct traceback errno bz2")
10 from mercurial.i18n import gettext as _
11 from mercurial import ui, hg, revlog, commands, util
12
13 versionstr = "0.45"
14
15 repomap = {}
16
17 class queue:
18 def __init__(self, ui, path, patchdir=None):
19 self.basepath = path
20 if patchdir:
21 self.path = patchdir
22 else:
23 self.path = os.path.join(path, "patches")
24 self.opener = util.opener(self.path)
25 self.ui = ui
26 self.applied = []
27 self.full_series = []
28 self.applied_dirty = 0
29 self.series_dirty = 0
30 self.series_path = "series"
31 self.status_path = "status"
32
33 if os.path.exists(os.path.join(self.path, self.series_path)):
34 self.full_series = self.opener(self.series_path).read().splitlines()
35 self.read_series(self.full_series)
36
37 if os.path.exists(os.path.join(self.path, self.status_path)):
38 self.applied = self.opener(self.status_path).read().splitlines()
39
40 def find_series(self, patch):
41 pre = re.compile("(\s*)([^#]+)")
42 index = 0
43 for l in self.full_series:
44 m = pre.match(l)
45 if m:
46 s = m.group(2)
47 s = s.rstrip()
48 if s == patch:
49 return index
50 index += 1
51 return None
52
53 def read_series(self, list):
54 def matcher(list):
55 pre = re.compile("(\s*)([^#]+)")
56 for l in list:
57 m = pre.match(l)
58 if m:
59 s = m.group(2)
60 s = s.rstrip()
61 if len(s) > 0:
62 yield s
63 self.series = []
64 self.series = [ x for x in matcher(list) ]
65
66 def save_dirty(self):
67 if self.applied_dirty:
68 if len(self.applied) > 0:
69 nl = "\n"
70 else:
71 nl = ""
72 f = self.opener(self.status_path, "w")
73 f.write("\n".join(self.applied) + nl)
74 if self.series_dirty:
75 if len(self.full_series) > 0:
76 nl = "\n"
77 else:
78 nl = ""
79 f = self.opener(self.series_path, "w")
80 f.write("\n".join(self.full_series) + nl)
81
82 def readheaders(self, patch):
83 def eatdiff(lines):
84 while lines:
85 l = lines[-1]
86 if (l.startswith("diff -") or
87 l.startswith("Index:") or
88 l.startswith("===========")):
89 del lines[-1]
90 else:
91 break
92 def eatempty(lines):
93 while lines:
94 l = lines[-1]
95 if re.match('\s*$', l):
96 del lines[-1]
97 else:
98 break
99
100 pf = os.path.join(self.path, patch)
101 message = []
102 comments = []
103 user = None
104 format = None
105 subject = None
106 diffstart = 0
107
108 for line in file(pf):
109 line = line.rstrip()
110 if diffstart:
111 if line.startswith('+++ '):
112 diffstart = 2
113 break
114 if line.startswith("--- "):
115 diffstart = 1
116 continue
117 elif format == "hgpatch":
118 # parse values when importing the result of an hg export
119 if line.startswith("# User "):
120 user = line[7:]
121 elif not line.startswith("# ") and line:
122 message.append(line)
123 format = None
124 elif line == '# HG changeset patch':
125 format = "hgpatch"
126 elif (format != "tagdone" and (line.startswith("Subject: ") or
127 line.startswith("subject: "))):
128 subject = line[9:]
129 format = "tag"
130 elif (format != "tagdone" and (line.startswith("From: ") or
131 line.startswith("from: "))):
132 user = line[6:]
133 format = "tag"
134 elif format == "tag" and line == "":
135 # when looking for tags (subject: from: etc) they
136 # end once you find a blank line in the source
137 format = "tagdone"
138 else:
139 message.append(line)
140 comments.append(line)
141
142 eatdiff(message)
143 eatdiff(comments)
144 eatempty(message)
145 eatempty(comments)
146
147 # make sure message isn't empty
148 if format and format.startswith("tag") and subject:
149 message.insert(0, "")
150 message.insert(0, subject)
151 return (message, comments, user, diffstart > 1)
152
153 def mergeone(self, repo, mergeq, head, patch, rev, wlock):
154 # first try just applying the patch
155 (err, n) = self.apply(repo, [ patch ], update_status=False,
156 strict=True, merge=rev, wlock=wlock)
157
158 if err == 0:
159 return (err, n)
160
161 if n is None:
162 self.ui.warn("apply failed for patch %s\n" % patch)
163 sys.exit(1)
164
165 self.ui.warn("patch didn't work out, merging %s\n" % patch)
166
167 # apply failed, strip away that rev and merge.
168 repo.update(head, allow=False, force=True, wlock=wlock)
169 self.strip(repo, n, update=False, backup='strip', wlock=wlock)
170
171 c = repo.changelog.read(rev)
172 ret = repo.update(rev, allow=True, wlock=wlock)
173 if ret:
174 self.ui.warn("update returned %d\n" % ret)
175 sys.exit(1)
176 n = repo.commit(None, c[4], c[1], force=1, wlock=wlock)
177 if n == None:
178 self.ui.warn("repo commit failed\n")
179 sys.exit(1)
180 try:
181 message, comments, user, patchfound = mergeq.readheaders(patch)
182 except:
183 self.ui.warn("Unable to read %s\n" % patch)
184 sys.exit(1)
185
186 patchf = self.opener(patch, "w")
187 if comments:
188 comments = "\n".join(comments) + '\n\n'
189 patchf.write(comments)
190 commands.dodiff(patchf, self.ui, repo, head, n)
191 patchf.close()
192 return (0, n)
193
194 def qparents(self, repo, rev=None):
195 if rev is None:
196 (p1, p2) = repo.dirstate.parents()
197 if p2 == revlog.nullid:
198 return p1
199 if len(self.applied) == 0:
200 return None
201 (top, patch) = self.applied[-1].split(':')
202 top = revlog.bin(top)
203 return top
204 pp = repo.changelog.parents(rev)
205 if pp[1] != revlog.nullid:
206 arevs = [ x.split(':')[0] for x in self.applied ]
207 p0 = revlog.hex(pp[0])
208 p1 = revlog.hex(pp[1])
209 if p0 in arevs:
210 return pp[0]
211 if p1 in arevs:
212 return pp[1]
213 return None
214 return pp[0]
215
216 def mergepatch(self, repo, mergeq, series, wlock):
217 if len(self.applied) == 0:
218 # each of the patches merged in will have two parents. This
219 # can confuse the qrefresh, qdiff, and strip code because it
220 # needs to know which parent is actually in the patch queue.
221 # so, we insert a merge marker with only one parent. This way
222 # the first patch in the queue is never a merge patch
223 #
224 pname = ".hg.patches.merge.marker"
225 n = repo.commit(None, '[mq]: merge marker', user=None, force=1,
226 wlock=wlock)
227 self.applied.append(revlog.hex(n) + ":" + pname)
228 self.applied_dirty = 1
229
230 head = self.qparents(repo)
231
232 for patch in series:
233 patch = mergeq.lookup(patch)
234 if not patch:
235 self.ui.warn("patch %s does not exist\n" % patch)
236 return (1, None)
237
238 info = mergeq.isapplied(patch)
239 if not info:
240 self.ui.warn("patch %s is not applied\n" % patch)
241 return (1, None)
242 rev = revlog.bin(info[1])
243 (err, head) = self.mergeone(repo, mergeq, head, patch, rev, wlock)
244 if head:
245 self.applied.append(revlog.hex(head) + ":" + patch)
246 self.applied_dirty = 1
247 if err:
248 return (err, head)
249 return (0, head)
250
251 def apply(self, repo, series, list=False, update_status=True,
252 strict=False, patchdir=None, merge=None, wlock=None):
253 # TODO unify with commands.py
254 if not patchdir:
255 patchdir = self.path
256 pwd = os.getcwd()
257 os.chdir(repo.root)
258 err = 0
259 if not wlock:
260 wlock = repo.wlock()
261 lock = repo.lock()
262 tr = repo.transaction()
263 n = None
264 for patch in series:
265 self.ui.warn("applying %s\n" % patch)
266 pf = os.path.join(patchdir, patch)
267
268 try:
269 message, comments, user, patchfound = self.readheaders(patch)
270 except:
271 self.ui.warn("Unable to read %s\n" % pf)
272 err = 1
273 break
274
275 if not message:
276 message = "imported patch %s\n" % patch
277 else:
278 if list:
279 message.append("\nimported patch %s" % patch)
280 message = '\n'.join(message)
281
282 try:
283 f = os.popen("patch -p1 --no-backup-if-mismatch < '%s'" % (pf))
284 except:
285 self.ui.warn("patch failed, unable to continue (try -v)\n")
286 err = 1
287 break
288 files = []
289 fuzz = False
290 for l in f:
291 l = l.rstrip('\r\n');
292 if self.ui.verbose:
293 self.ui.warn(l + "\n")
294 if l[:14] == 'patching file ':
295 pf = os.path.normpath(l[14:])
296 # when patch finds a space in the file name, it puts
297 # single quotes around the filename. strip them off
298 if pf[0] == "'" and pf[-1] == "'":
299 pf = pf[1:-1]
300 if pf not in files:
301 files.append(pf)
302 printed_file = False
303 file_str = l
304 elif l.find('with fuzz') >= 0:
305 if not printed_file:
306 self.ui.warn(file_str + '\n')
307 printed_file = True
308 self.ui.warn(l + '\n')
309 fuzz = True
310 elif l.find('saving rejects to file') >= 0:
311 self.ui.warn(l + '\n')
312 elif l.find('FAILED') >= 0:
313 if not printed_file:
314 self.ui.warn(file_str + '\n')
315 printed_file = True
316 self.ui.warn(l + '\n')
317 patcherr = f.close()
318
319 if merge and len(files) > 0:
320 # Mark as merged and update dirstate parent info
321 repo.dirstate.update(repo.dirstate.filterfiles(files), 'm')
322 p1, p2 = repo.dirstate.parents()
323 repo.dirstate.setparents(p1, merge)
324 if len(files) > 0:
325 commands.addremove_lock(self.ui, repo, files,
326 opts={}, wlock=wlock)
327 n = repo.commit(files, message, user, force=1, lock=lock,
328 wlock=wlock)
329
330 if n == None:
331 self.ui.warn("repo commit failed\n")
332 sys.exit(1)
333
334 if update_status:
335 self.applied.append(revlog.hex(n) + ":" + patch)
336
337 if patcherr:
338 if not patchfound:
339 self.ui.warn("patch %s is empty\n" % patch)
340 err = 0
341 else:
342 self.ui.warn("patch failed, rejects left in working dir\n")
343 err = 1
344 break
345
346 if fuzz and strict:
347 self.ui.warn("fuzz found when applying patch, stopping\n")
348 err = 1
349 break
350 tr.close()
351 os.chdir(pwd)
352 return (err, n)
353
354 def delete(self, repo, patch):
355 patch = self.lookup(patch)
356 info = self.isapplied(patch)
357 if info:
358 self.ui.warn("cannot delete applied patch %s\n" % patch)
359 sys.exit(1)
360 if patch not in self.series:
361 self.ui.warn("patch %s not in series file\n" % patch)
362 sys.exit(1)
363 i = self.find_series(patch)
364 del self.full_series[i]
365 self.read_series(self.full_series)
366 self.series_dirty = 1
367
368 def check_toppatch(self, repo):
369 if len(self.applied) > 0:
370 (top, patch) = self.applied[-1].split(':')
371 top = revlog.bin(top)
372 pp = repo.dirstate.parents()
373 if top not in pp:
374 self.ui.warn("queue top not at dirstate parents. top %s dirstate %s %s\n" %( revlog.short(top), revlog.short(pp[0]), revlog.short(pp[1])))
375 sys.exit(1)
376 return top
377 return None
378 def check_localchanges(self, repo):
379 (c, a, r, d, u) = repo.changes(None, None)
380 if c or a or d or r:
381 self.ui.write("Local changes found, refresh first\n")
382 sys.exit(1)
383 def new(self, repo, patch, msg=None, force=None):
384 if not force:
385 self.check_localchanges(repo)
386 self.check_toppatch(repo)
387 wlock = repo.wlock()
388 insert = self.series_end()
389 if msg:
390 n = repo.commit([], "[mq]: %s" % msg, force=True, wlock=wlock)
391 else:
392 n = repo.commit([],
393 "New patch: %s" % patch, force=True, wlock=wlock)
394 if n == None:
395 self.ui.warn("repo commit failed\n")
396 sys.exit(1)
397 self.full_series[insert:insert] = [patch]
398 self.applied.append(revlog.hex(n) + ":" + patch)
399 self.read_series(self.full_series)
400 self.series_dirty = 1
401 self.applied_dirty = 1
402 p = self.opener(patch, "w")
403 if msg:
404 msg = msg + "\n"
405 p.write(msg)
406 p.close()
407 wlock = None
408 r = self.qrepo()
409 if r: r.add([patch])
410
411 def strip(self, repo, rev, update=True, backup="all", wlock=None):
412 def limitheads(chlog, stop):
413 """return the list of all nodes that have no children"""
414 p = {}
415 h = []
416 stoprev = 0
417 if stop in chlog.nodemap:
418 stoprev = chlog.rev(stop)
419
420 for r in range(chlog.count() - 1, -1, -1):
421 n = chlog.node(r)
422 if n not in p:
423 h.append(n)
424 if n == stop:
425 break
426 if r < stoprev:
427 break
428 for pn in chlog.parents(n):
429 p[pn] = 1
430 return h
431
432 def bundle(cg):
433 backupdir = repo.join("strip-backup")
434 if not os.path.isdir(backupdir):
435 os.mkdir(backupdir)
436 name = os.path.join(backupdir, "%s" % revlog.short(rev))
437 name = savename(name)
438 self.ui.warn("saving bundle to %s\n" % name)
439 # TODO, exclusive open
440 f = open(name, "wb")
441 try:
442 f.write("HG10")
443 z = bz2.BZ2Compressor(9)
444 while 1:
445 chunk = cg.read(4096)
446 if not chunk:
447 break
448 f.write(z.compress(chunk))
449 f.write(z.flush())
450 except:
451 os.unlink(name)
452 raise
453 f.close()
454 return name
455
456 def stripall(rev, revnum):
457 cl = repo.changelog
458 c = cl.read(rev)
459 mm = repo.manifest.read(c[0])
460 seen = {}
461
462 for x in xrange(revnum, cl.count()):
463 c = cl.read(cl.node(x))
464 for f in c[3]:
465 if f in seen:
466 continue
467 seen[f] = 1
468 if f in mm:
469 filerev = mm[f]
470 else:
471 filerev = 0
472 seen[f] = filerev
473 # we go in two steps here so the strip loop happens in a
474 # sensible order. When stripping many files, this helps keep
475 # our disk access patterns under control.
476 list = seen.keys()
477 list.sort()
478 for f in list:
479 ff = repo.file(f)
480 filerev = seen[f]
481 if filerev != 0:
482 if filerev in ff.nodemap:
483 filerev = ff.rev(filerev)
484 else:
485 filerev = 0
486 ff.strip(filerev, revnum)
487
488 if not wlock:
489 wlock = repo.wlock()
490 lock = repo.lock()
491 chlog = repo.changelog
492 # TODO delete the undo files, and handle undo of merge sets
493 pp = chlog.parents(rev)
494 revnum = chlog.rev(rev)
495
496 if update:
497 urev = self.qparents(repo, rev)
498 repo.update(urev, allow=False, force=True, wlock=wlock)
499 repo.dirstate.write()
500
501 # save is a list of all the branches we are truncating away
502 # that we actually want to keep. changegroup will be used
503 # to preserve them and add them back after the truncate
504 saveheads = []
505 savebases = {}
506
507 tip = chlog.tip()
508 heads = limitheads(chlog, rev)
509 seen = {}
510
511 # search through all the heads, finding those where the revision
512 # we want to strip away is an ancestor. Also look for merges
513 # that might be turned into new heads by the strip.
514 while heads:
515 h = heads.pop()
516 n = h
517 while True:
518 seen[n] = 1
519 pp = chlog.parents(n)
520 if pp[1] != revlog.nullid and chlog.rev(pp[1]) > revnum:
521 if pp[1] not in seen:
522 heads.append(pp[1])
523 if pp[0] == revlog.nullid:
524 break
525 if chlog.rev(pp[0]) < revnum:
526 break
527 n = pp[0]
528 if n == rev:
529 break
530 r = chlog.reachable(h, rev)
531 if rev not in r:
532 saveheads.append(h)
533 for x in r:
534 if chlog.rev(x) > revnum:
535 savebases[x] = 1
536
537 # create a changegroup for all the branches we need to keep
538 if backup is "all":
539 backupch = repo.changegroupsubset([rev], chlog.heads(), 'strip')
540 bundle(backupch)
541 if saveheads:
542 backupch = repo.changegroupsubset(savebases.keys(), saveheads, 'strip')
543 chgrpfile = bundle(backupch)
544
545 stripall(rev, revnum)
546
547 change = chlog.read(rev)
548 repo.manifest.strip(repo.manifest.rev(change[0]), revnum)
549 chlog.strip(revnum, revnum)
550 if saveheads:
551 self.ui.status("adding branch\n")
552 commands.unbundle(self.ui, repo, chgrpfile, update=False)
553 if backup is not "strip":
554 os.unlink(chgrpfile)
555
556 def isapplied(self, patch):
557 """returns (index, rev, patch)"""
558 for i in xrange(len(self.applied)):
559 p = self.applied[i]
560 a = p.split(':')
561 if a[1] == patch:
562 return (i, a[0], a[1])
563 return None
564
565 def lookup(self, patch):
566 if patch == None:
567 return None
568 if patch in self.series:
569 return patch
570 if not os.path.isfile(os.path.join(self.path, patch)):
571 try:
572 sno = int(patch)
573 except(ValueError, OverflowError):
574 self.ui.warn("patch %s not in series\n" % patch)
575 sys.exit(1)
576 if sno >= len(self.series):
577 self.ui.warn("patch number %d is out of range\n" % sno)
578 sys.exit(1)
579 patch = self.series[sno]
580 else:
581 self.ui.warn("patch %s not in series\n" % patch)
582 sys.exit(1)
583 return patch
584
585 def push(self, repo, patch=None, force=False, list=False,
586 mergeq=None, wlock=None):
587 if not wlock:
588 wlock = repo.wlock()
589 patch = self.lookup(patch)
590 if patch and self.isapplied(patch):
591 self.ui.warn("patch %s is already applied\n" % patch)
592 sys.exit(1)
593 if self.series_end() == len(self.series):
594 self.ui.warn("File series fully applied\n")
595 sys.exit(1)
596 if not force:
597 self.check_localchanges(repo)
598
599 self.applied_dirty = 1;
600 start = self.series_end()
601 if start > 0:
602 self.check_toppatch(repo)
603 if not patch:
604 patch = self.series[start]
605 end = start + 1
606 else:
607 end = self.series.index(patch, start) + 1
608 s = self.series[start:end]
609 if mergeq:
610 ret = self.mergepatch(repo, mergeq, s, wlock)
611 else:
612 ret = self.apply(repo, s, list, wlock=wlock)
613 top = self.applied[-1].split(':')[1]
614 if ret[0]:
615 self.ui.write("Errors during apply, please fix and refresh %s\n" %
616 top)
617 else:
618 self.ui.write("Now at: %s\n" % top)
619 return ret[0]
620
621 def pop(self, repo, patch=None, force=False, update=True, wlock=None):
622 def getfile(f, rev):
623 t = repo.file(f).read(rev)
624 try:
625 repo.wfile(f, "w").write(t)
626 except IOError:
627 os.makedirs(os.path.dirname(repo.wjoin(f)))
628 repo.wfile(f, "w").write(t)
629
630 if not wlock:
631 wlock = repo.wlock()
632 if patch:
633 # index, rev, patch
634 info = self.isapplied(patch)
635 if not info:
636 patch = self.lookup(patch)
637 info = self.isapplied(patch)
638 if not info:
639 self.ui.warn("patch %s is not applied\n" % patch)
640 sys.exit(1)
641 if len(self.applied) == 0:
642 self.ui.warn("No patches applied\n")
643 sys.exit(1)
644
645 if not update:
646 parents = repo.dirstate.parents()
647 rr = [ revlog.bin(x.split(':')[0]) for x in self.applied ]
648 for p in parents:
649 if p in rr:
650 self.ui.warn("qpop: forcing dirstate update\n")
651 update = True
652
653 if not force and update:
654 self.check_localchanges(repo)
655
656 self.applied_dirty = 1;
657 end = len(self.applied)
658 if not patch:
659 info = [len(self.applied) - 1] + self.applied[-1].split(':')
660 start = info[0]
661 rev = revlog.bin(info[1])
662
663 # we know there are no local changes, so we can make a simplified
664 # form of hg.update.
665 if update:
666 top = self.check_toppatch(repo)
667 qp = self.qparents(repo, rev)
668 changes = repo.changelog.read(qp)
669 mf1 = repo.manifest.readflags(changes[0])
670 mmap = repo.manifest.read(changes[0])
671 (c, a, r, d, u) = repo.changes(qp, top)
672 if d:
673 raise util.Abort("deletions found between repo revs")
674 for f in c:
675 getfile(f, mmap[f])
676 for f in r:
677 getfile(f, mmap[f])
678 util.set_exec(repo.wjoin(f), mf1[f])
679 repo.dirstate.update(c + r, 'n')
680 for f in a:
681 try: os.unlink(repo.wjoin(f))
682 except: raise
683 try: os.removedirs(os.path.dirname(repo.wjoin(f)))
684 except: pass
685 if a:
686 repo.dirstate.forget(a)
687 repo.dirstate.setparents(qp, revlog.nullid)
688 self.strip(repo, rev, update=False, backup='strip', wlock=wlock)
689 del self.applied[start:end]
690 if len(self.applied):
691 self.ui.write("Now at: %s\n" % self.applied[-1].split(':')[1])
692 else:
693 self.ui.write("Patch queue now empty\n")
694
695 def diff(self, repo, files):
696 top = self.check_toppatch(repo)
697 if not top:
698 self.ui.write("No patches applied\n")
699 return
700 qp = self.qparents(repo, top)
701 commands.dodiff(sys.stdout, self.ui, repo, qp, None, files)
702
703 def refresh(self, repo, short=False):
704 if len(self.applied) == 0:
705 self.ui.write("No patches applied\n")
706 return
707 wlock = repo.wlock()
708 self.check_toppatch(repo)
709 qp = self.qparents(repo)
710 (top, patch) = self.applied[-1].split(':')
711 top = revlog.bin(top)
712 cparents = repo.changelog.parents(top)
713 patchparent = self.qparents(repo, top)
714 message, comments, user, patchfound = self.readheaders(patch)
715
716 patchf = self.opener(patch, "w")
717 if comments:
718 comments = "\n".join(comments) + '\n\n'
719 patchf.write(comments)
720
721 tip = repo.changelog.tip()
722 if top == tip:
723 # if the top of our patch queue is also the tip, there is an
724 # optimization here. We update the dirstate in place and strip
725 # off the tip commit. Then just commit the current directory
726 # tree. We can also send repo.commit the list of files
727 # changed to speed up the diff
728 #
729 # in short mode, we only diff the files included in the
730 # patch already
731 #
732 # this should really read:
733 #(cc, dd, aa, aa2, uu) = repo.changes(tip, patchparent)
734 # but we do it backwards to take advantage of manifest/chlog
735 # caching against the next repo.changes call
736 #
737 (cc, aa, dd, aa2, uu) = repo.changes(patchparent, tip)
738 if short:
739 filelist = cc + aa + dd
740 else:
741 filelist = None
742 (c, a, r, d, u) = repo.changes(None, None, filelist)
743
744 # we might end up with files that were added between tip and
745 # the dirstate parent, but then changed in the local dirstate.
746 # in this case, we want them to only show up in the added section
747 for x in c:
748 if x not in aa:
749 cc.append(x)
750 # we might end up with files added by the local dirstate that
751 # were deleted by the patch. In this case, they should only
752 # show up in the changed section.
753 for x in a:
754 if x in dd:
755 del dd[dd.index(x)]
756 cc.append(x)
757 else:
758 aa.append(x)
759 # make sure any files deleted in the local dirstate
760 # are not in the add or change column of the patch
761 forget = []
762 for x in d + r:
763 if x in aa:
764 del aa[aa.index(x)]
765 forget.append(x)
766 continue
767 elif x in cc:
768 del cc[cc.index(x)]
769 dd.append(x)
770
771 c = list(util.unique(cc))
772 r = list(util.unique(dd))
773 a = list(util.unique(aa))
774 filelist = list(util.unique(c + r + a ))
775 commands.dodiff(patchf, self.ui, repo, patchparent, None,
776 filelist, changes=(c, a, r, [], u))
777 patchf.close()
778
779 changes = repo.changelog.read(tip)
780 repo.dirstate.setparents(*cparents)
781 repo.dirstate.update(a, 'a')
782 repo.dirstate.update(r, 'r')
783 repo.dirstate.update(c, 'n')
784 repo.dirstate.forget(forget)
785
786 if not message:
787 message = "patch queue: %s\n" % patch
788 else:
789 message = "\n".join(message)
790 self.strip(repo, top, update=False, backup='strip', wlock=wlock)
791 n = repo.commit(filelist, message, changes[1], force=1, wlock=wlock)
792 self.applied[-1] = revlog.hex(n) + ':' + patch
793 self.applied_dirty = 1
794 else:
795 commands.dodiff(patchf, self.ui, repo, patchparent, None)
796 patchf.close()
797 self.pop(repo, force=True, wlock=wlock)
798 self.push(repo, force=True, wlock=wlock)
799
800 def init(self, repo, create=False):
801 if os.path.isdir(self.path):
802 raise util.Abort("patch queue directory already exists")
803 os.mkdir(self.path)
804 if create:
805 return self.qrepo(create=True)
806
807 def unapplied(self, repo, patch=None):
808 if patch and patch not in self.series:
809 self.ui.warn("%s not in the series file\n" % patch)
810 sys.exit(1)
811 if not patch:
812 start = self.series_end()
813 else:
814 start = self.series.index(patch) + 1
815 for p in self.series[start:]:
816 self.ui.write("%s\n" % p)
817
818 def qseries(self, repo, missing=None):
819 start = self.series_end()
820 if not missing:
821 for p in self.series[:start]:
822 if self.ui.verbose:
823 self.ui.write("%d A " % self.series.index(p))
824 self.ui.write("%s\n" % p)
825 for p in self.series[start:]:
826 if self.ui.verbose:
827 self.ui.write("%d U " % self.series.index(p))
828 self.ui.write("%s\n" % p)
829 else:
830 list = []
831 for root, dirs, files in os.walk(self.path):
832 d = root[len(self.path) + 1:]
833 for f in files:
834 fl = os.path.join(d, f)
835 if (fl not in self.series and
836 fl not in (self.status_path, self.series_path)
837 and not fl.startswith('.')):
838 list.append(fl)
839 list.sort()
840 if list:
841 for x in list:
842 if self.ui.verbose:
843 self.ui.write("D ")
844 self.ui.write("%s\n" % x)
845
846 def issaveline(self, l):
847 name = l.split(':')[1]
848 if name == '.hg.patches.save.line':
849 return True
850
851 def qrepo(self, create=False):
852 if create or os.path.isdir(os.path.join(self.path, ".hg")):
853 return hg.repository(self.ui, path=self.path, create=create)
854
855 def restore(self, repo, rev, delete=None, qupdate=None):
856 c = repo.changelog.read(rev)
857 desc = c[4].strip()
858 lines = desc.splitlines()
859 i = 0
860 datastart = None
861 series = []
862 applied = []
863 qpp = None
864 for i in xrange(0, len(lines)):
865 if lines[i] == 'Patch Data:':
866 datastart = i + 1
867 elif lines[i].startswith('Dirstate:'):
868 l = lines[i].rstrip()
869 l = l[10:].split(' ')
870 qpp = [ hg.bin(x) for x in l ]
871 elif datastart != None:
872 l = lines[i].rstrip()
873 index = l.index(':')
874 id = l[:index]
875 file = l[index + 1:]
876 if id:
877 applied.append(l)
878 series.append(file)
879 if datastart == None:
880 self.ui.warn("No saved patch data found\n")
881 return 1
882 self.ui.warn("restoring status: %s\n" % lines[0])
883 self.full_series = series
884 self.applied = applied
885 self.read_series(self.full_series)
886 self.series_dirty = 1
887 self.applied_dirty = 1
888 heads = repo.changelog.heads()
889 if delete:
890 if rev not in heads:
891 self.ui.warn("save entry has children, leaving it alone\n")
892 else:
893 self.ui.warn("removing save entry %s\n" % hg.short(rev))
894 pp = repo.dirstate.parents()
895 if rev in pp:
896 update = True
897 else:
898 update = False
899 self.strip(repo, rev, update=update, backup='strip')
900 if qpp:
901 self.ui.warn("saved queue repository parents: %s %s\n" %
902 (hg.short(qpp[0]), hg.short(qpp[1])))
903 if qupdate:
904 print "queue directory updating"
905 r = self.qrepo()
906 if not r:
907 self.ui.warn("Unable to load queue repository\n")
908 return 1
909 r.update(qpp[0], allow=False, force=True)
910
911 def save(self, repo, msg=None):
912 if len(self.applied) == 0:
913 self.ui.warn("save: no patches applied, exiting\n")
914 return 1
915 if self.issaveline(self.applied[-1]):
916 self.ui.warn("status is already saved\n")
917 return 1
918
919 ar = [ ':' + x for x in self.full_series ]
920 if not msg:
921 msg = "hg patches saved state"
922 else:
923 msg = "hg patches: " + msg.rstrip('\r\n')
924 r = self.qrepo()
925 if r:
926 pp = r.dirstate.parents()
927 msg += "\nDirstate: %s %s" % (hg.hex(pp[0]), hg.hex(pp[1]))
928 msg += "\n\nPatch Data:\n"
929 text = msg + "\n".join(self.applied) + '\n' + (ar and "\n".join(ar)
930 + '\n' or "")
931 n = repo.commit(None, text, user=None, force=1)
932 if not n:
933 self.ui.warn("repo commit failed\n")
934 return 1
935 self.applied.append(revlog.hex(n) + ":" + '.hg.patches.save.line')
936 self.applied_dirty = 1
937
938 def series_end(self):
939 end = 0
940 if len(self.applied) > 0:
941 (top, p) = self.applied[-1].split(':')
942 try:
943 end = self.series.index(p)
944 except ValueError:
945 return 0
946 return end + 1
947 return end
948
949 def qapplied(self, repo, patch=None):
950 if patch and patch not in self.series:
951 self.ui.warn("%s not in the series file\n" % patch)
952 sys.exit(1)
953 if not patch:
954 end = len(self.applied)
955 else:
956 end = self.series.index(patch) + 1
957 for x in xrange(end):
958 p = self.appliedname(x)
959 self.ui.write("%s\n" % p)
960
961 def appliedname(self, index):
962 p = self.applied[index]
963 if not self.ui.verbose:
964 p = p.split(':')[1]
965 return p
966
967 def top(self, repo):
968 if len(self.applied):
969 p = self.appliedname(-1)
970 self.ui.write(p + '\n')
971 else:
972 self.ui.write("No patches applied\n")
973
974 def next(self, repo):
975 end = self.series_end()
976 if end == len(self.series):
977 self.ui.write("All patches applied\n")
978 else:
979 self.ui.write(self.series[end] + '\n')
980
981 def prev(self, repo):
982 if len(self.applied) > 1:
983 p = self.appliedname(-2)
984 self.ui.write(p + '\n')
985 elif len(self.applied) == 1:
986 self.ui.write("Only one patch applied\n")
987 else:
988 self.ui.write("No patches applied\n")
989
990 def qimport(self, repo, files, patch=None, existing=None, force=None):
991 if len(files) > 1 and patch:
992 self.ui.warn("-n option not valid when importing multiple files\n")
993 sys.exit(1)
994 i = 0
995 for filename in files:
996 if existing:
997 if not patch:
998 patch = filename
999 if not os.path.isfile(os.path.join(self.path, patch)):
1000 self.ui.warn("patch %s does not exist\n" % patch)
1001 sys.exit(1)
1002 else:
1003 try:
1004 text = file(filename).read()
1005 except IOError:
1006 self.ui.warn("Unable to read %s\n" % patch)
1007 sys.exit(1)
1008 if not patch:
1009 patch = os.path.split(filename)[1]
1010 if not force and os.path.isfile(os.path.join(self.path, patch)):
1011 self.ui.warn("patch %s already exists\n" % patch)
1012 sys.exit(1)
1013 patchf = self.opener(patch, "w")
1014 patchf.write(text)
1015 if patch in self.series:
1016 self.ui.warn("patch %s is already in the series file\n" % patch)
1017 sys.exit(1)
1018 index = self.series_end() + i
1019 self.full_series[index:index] = [patch]
1020 self.read_series(self.full_series)
1021 self.ui.warn("adding %s to series file\n" % patch)
1022 i += 1
1023 patch = None
1024 self.series_dirty = 1
1025
1026 def delete(ui, repo, patch, **opts):
1027 """remove a patch from the series file"""
1028 q = repomap[repo]
1029 q.delete(repo, patch)
1030 q.save_dirty()
1031 return 0
1032
1033 def applied(ui, repo, patch=None, **opts):
1034 """print the patches already applied"""
1035 repomap[repo].qapplied(repo, patch)
1036 return 0
1037
1038 def unapplied(ui, repo, patch=None, **opts):
1039 """print the patches not yet applied"""
1040 repomap[repo].unapplied(repo, patch)
1041 return 0
1042
1043 def qimport(ui, repo, *filename, **opts):
1044 """import a patch"""
1045 q = repomap[repo]
1046 q.qimport(repo, filename, patch=opts['name'],
1047 existing=opts['existing'], force=opts['force'])
1048 q.save_dirty()
1049 return 0
1050
1051 def init(ui, repo, **opts):
1052 """init a new queue repository"""
1053 q = repomap[repo]
1054 r = q.init(repo, create=opts['create_repo'])
1055 q.save_dirty()
1056 if r:
1057 fp = r.wopener('.hgignore', 'w')
1058 print >> fp, 'syntax: glob'
1059 print >> fp, 'status'
1060 fp.close()
1061 r.wopener('series', 'w').close()
1062 r.add(['.hgignore', 'series'])
1063 return 0
1064
1065 def commit(ui, repo, *pats, **opts):
1066 q = repomap[repo]
1067 r = q.qrepo()
1068 if not r: raise util.Abort('no queue repository')
1069 commands.commit(r.ui, r, *pats, **opts)
1070
1071 def series(ui, repo, **opts):
1072 """print the entire series file"""
1073 repomap[repo].qseries(repo, missing=opts['missing'])
1074 return 0
1075
1076 def top(ui, repo, **opts):
1077 """print the name of the current patch"""
1078 repomap[repo].top(repo)
1079 return 0
1080
1081 def next(ui, repo, **opts):
1082 """print the name of the next patch"""
1083 repomap[repo].next(repo)
1084 return 0
1085
1086 def prev(ui, repo, **opts):
1087 """print the name of the previous patch"""
1088 repomap[repo].prev(repo)
1089 return 0
1090
1091 def new(ui, repo, patch, **opts):
1092 """create a new patch"""
1093 q = repomap[repo]
1094 q.new(repo, patch, msg=opts['message'], force=opts['force'])
1095 q.save_dirty()
1096 return 0
1097
1098 def refresh(ui, repo, **opts):
1099 """update the current patch"""
1100 q = repomap[repo]
1101 q.refresh(repo, short=opts['short'])
1102 q.save_dirty()
1103 return 0
1104
1105 def diff(ui, repo, *files, **opts):
1106 """diff of the current patch"""
1107 repomap[repo].diff(repo, files)
1108 return 0
1109
1110 def lastsavename(path):
1111 (dir, base) = os.path.split(path)
1112 names = os.listdir(dir)
1113 namere = re.compile("%s.([0-9]+)" % base)
1114 max = None
1115 maxname = None
1116 for f in names:
1117 m = namere.match(f)
1118 if m:
1119 index = int(m.group(1))
1120 if max == None or index > max:
1121 max = index
1122 maxname = f
1123 if maxname:
1124 return (os.path.join(dir, maxname), max)
1125 return (None, None)
1126
1127 def savename(path):
1128 (last, index) = lastsavename(path)
1129 if last is None:
1130 index = 0
1131 newpath = path + ".%d" % (index + 1)
1132 return newpath
1133
1134 def push(ui, repo, patch=None, **opts):
1135 """push the next patch onto the stack"""
1136 q = repomap[repo]
1137 mergeq = None
1138
1139 if opts['all']:
1140 patch = q.series[-1]
1141 if opts['merge']:
1142 if opts['name']:
1143 newpath = opts['name']
1144 else:
1145 newpath, i = lastsavename(q.path)
1146 if not newpath:
1147 ui.warn("no saved queues found, please use -n\n")
1148 return 1
1149 mergeq = queue(ui, repo.join(""), newpath)
1150 ui.warn("merging with queue at: %s\n" % mergeq.path)
1151 ret = q.push(repo, patch, force=opts['force'], list=opts['list'],
1152 mergeq=mergeq)
1153 q.save_dirty()
1154 return ret
1155
1156 def pop(ui, repo, patch=None, **opts):
1157 """pop the current patch off the stack"""
1158 localupdate = True
1159 if opts['name']:
1160 q = queue(ui, repo.join(""), repo.join(opts['name']))
1161 ui.warn('using patch queue: %s\n' % q.path)
1162 localupdate = False
1163 else:
1164 q = repomap[repo]
1165 if opts['all'] and len(q.applied) > 0:
1166 patch = q.applied[0].split(':')[1]
1167 q.pop(repo, patch, force=opts['force'], update=localupdate)
1168 q.save_dirty()
1169 return 0
1170
1171 def restore(ui, repo, rev, **opts):
1172 """restore the queue state saved by a rev"""
1173 rev = repo.lookup(rev)
1174 q = repomap[repo]
1175 q.restore(repo, rev, delete=opts['delete'],
1176 qupdate=opts['update'])
1177 q.save_dirty()
1178 return 0
1179
1180 def save(ui, repo, **opts):
1181 """save current queue state"""
1182 q = repomap[repo]
1183 ret = q.save(repo, msg=opts['message'])
1184 if ret:
1185 return ret
1186 q.save_dirty()
1187 if opts['copy']:
1188 path = q.path
1189 if opts['name']:
1190 newpath = os.path.join(q.basepath, opts['name'])
1191 if os.path.exists(newpath):
1192 if not os.path.isdir(newpath):
1193 ui.warn("destination %s exists and is not a directory\n" %
1194 newpath)
1195 sys.exit(1)
1196 if not opts['force']:
1197 ui.warn("destination %s exists, use -f to force\n" %
1198 newpath)
1199 sys.exit(1)
1200 else:
1201 newpath = savename(path)
1202 ui.warn("copy %s to %s\n" % (path, newpath))
1203 util.copyfiles(path, newpath)
1204 if opts['empty']:
1205 try:
1206 os.unlink(os.path.join(q.path, q.status_path))
1207 except:
1208 pass
1209 return 0
1210
1211 def strip(ui, repo, rev, **opts):
1212 """strip a revision and all later revs on the same branch"""
1213 rev = repo.lookup(rev)
1214 backup = 'all'
1215 if opts['backup']:
1216 backup = 'strip'
1217 elif opts['nobackup']:
1218 backup = 'none'
1219 repomap[repo].strip(repo, rev, backup=backup)
1220 return 0
1221
1222 def version(ui, q=None):
1223 """print the version number"""
1224 ui.write("mq version %s\n" % versionstr)
1225 return 0
1226
1227 def reposetup(ui, repo):
1228 repomap[repo] = queue(ui, repo.join(""))
1229
1230 cmdtable = {
1231 "qapplied": (applied, [], 'hg qapplied [patch]'),
1232 "qcommit|qci":
1233 (commit,
1234 [('A', 'addremove', None, _('run addremove during commit')),
1235 ('I', 'include', [], _('include names matching the given patterns')),
1236 ('X', 'exclude', [], _('exclude names matching the given patterns')),
1237 ('m', 'message', '', _('use <text> as commit message')),
1238 ('l', 'logfile', '', _('read the commit message from <file>')),
1239 ('d', 'date', '', _('record datecode as commit date')),
1240 ('u', 'user', '', _('record user as commiter'))],
1241 'hg qcommit [options] [files]'),
1242 "^qdiff": (diff, [], 'hg qdiff [files]'),
1243 "qdelete": (delete, [], 'hg qdelete [patch]'),
1244 "^qimport":
1245 (qimport,
1246 [('e', 'existing', None, 'import file in patch dir'),
1247 ('n', 'name', '', 'patch file name'),
1248 ('f', 'force', None, 'overwrite existing files')],
1249 'hg qimport'),
1250 "^qinit":
1251 (init,
1252 [('c', 'create-repo', None, 'create patch repository')],
1253 'hg [-c] qinit'),
1254 "qnew":
1255 (new,
1256 [('m', 'message', '', 'commit message'),
1257 ('f', 'force', None, 'force')],
1258 'hg qnew [-m message ] patch'),
1259 "qnext": (next, [], 'hg qnext'),
1260 "qprev": (prev, [], 'hg qprev'),
1261 "^qpop":
1262 (pop,
1263 [('a', 'all', None, 'pop all patches'),
1264 ('n', 'name', '', 'queue name to pop'),
1265 ('f', 'force', None, 'forget any local changes')],
1266 'hg qpop [options] [patch/index]'),
1267 "^qpush":
1268 (push,
1269 [('f', 'force', None, 'apply if the patch has rejects'),
1270 ('l', 'list', None, 'list patch name in commit text'),
1271 ('a', 'all', None, 'apply all patches'),
1272 ('m', 'merge', None, 'merge from another queue'),
1273 ('n', 'name', '', 'merge queue name')],
1274 'hg qpush [options] [patch/index]'),
1275 "^qrefresh":
1276 (refresh,
1277 [('s', 'short', None, 'short refresh')],
1278 'hg qrefresh'),
1279 "qrestore":
1280 (restore,
1281 [('d', 'delete', None, 'delete save entry'),
1282 ('u', 'update', None, 'update queue working dir')],
1283 'hg qrestore rev'),
1284 "qsave":
1285 (save,
1286 [('m', 'message', '', 'commit message'),
1287 ('c', 'copy', None, 'copy patch directory'),
1288 ('n', 'name', '', 'copy directory name'),
1289 ('e', 'empty', None, 'clear queue status file'),
1290 ('f', 'force', None, 'force copy')],
1291 'hg qsave'),
1292 "qseries":
1293 (series,
1294 [('m', 'missing', None, 'print patches not in series')],
1295 'hg qseries'),
1296 "^strip":
1297 (strip,
1298 [('f', 'force', None, 'force multi-head removal'),
1299 ('b', 'backup', None, 'bundle unrelated changesets'),
1300 ('n', 'nobackup', None, 'no backups')],
1301 'hg strip rev'),
1302 "qtop": (top, [], 'hg qtop'),
1303 "qunapplied": (unapplied, [], 'hg qunapplied [patch]'),
1304 "qversion": (version, [], 'hg qversion')
1305 }
1306
@@ -0,0 +1,13 b''
1 #!/bin/sh
2 hg init 1
3 echo '[ui]' >> 1/.hg/hgrc
4 echo 'timeout = 10' >> 1/.hg/hgrc
5 echo foo > 1/foo
6 hg --cwd 1 ci -A -m foo
7 hg clone 1 2
8 hg clone 2 3
9 echo '[hooks]' >> 2/.hg/hgrc
10 echo 'changegroup.push = hg push -qf ../1' >> 2/.hg/hgrc
11 echo bar >> 3/foo
12 hg --cwd 3 ci -m bar
13 hg --cwd 3 push ../2
@@ -0,0 +1,7 b''
1 adding foo
2 pushing to ../2
3 searching for changes
4 adding changesets
5 adding manifests
6 adding file changes
7 added 1 changesets with 1 changes to 1 files
@@ -12,6 +12,7 b' tests/*.err'
12 build
12 build
13 dist
13 dist
14 doc/*.[0-9]
14 doc/*.[0-9]
15 doc/*.[0-9].gendoc.txt
15 doc/*.[0-9].{x,ht}ml
16 doc/*.[0-9].{x,ht}ml
16 MANIFEST
17 MANIFEST
17 patches
18 patches
@@ -1,27 +1,5 b''
1 shopt -s extglob
1 shopt -s extglob
2
2
3 _hg_command_list()
4 {
5 "$hg" --debug help 2>/dev/null | \
6 awk 'function command_line(line) {
7 gsub(/,/, "", line)
8 gsub(/:.*/, "", line)
9 split(line, aliases)
10 command = aliases[1]
11 delete aliases[1]
12 print command
13 for (i in aliases)
14 if (index(command, aliases[i]) != 1)
15 print aliases[i]
16 }
17 /^list of commands:/ {commands=1}
18 commands && /^ debug/ {a[i++] = $0; next;}
19 commands && /^ [^ ]/ {command_line($0)}
20 /^global options:/ {exit 0}
21 END {for (i in a) command_line(a[i])}'
22
23 }
24
25 _hg_option_list()
3 _hg_option_list()
26 {
4 {
27 "$hg" -v help $1 2>/dev/null | \
5 "$hg" -v help $1 2>/dev/null | \
@@ -37,21 +15,9 b' shopt -s extglob'
37
15
38 _hg_commands()
16 _hg_commands()
39 {
17 {
40 local all commands result
18 local commands
41
19 commands="$("$hg" debugcomplete "$cur" 2>/dev/null)" || commands=""
42 all=$(_hg_command_list)
20 COMPREPLY=(${COMPREPLY[@]:-} $(compgen -W '$commands' -- "$cur"))
43 commands=${all%%$'\n'debug*}
44 result=$(compgen -W '$commands' -- "$cur")
45
46 # hide debug commands from users, but complete them if
47 # there is no other possible command
48 if [ "$result" = "" ]; then
49 local debug
50 debug=debug${all#*$'\n'debug}
51 result=$(compgen -W '$debug' -- "$cur")
52 fi
53
54 COMPREPLY=(${COMPREPLY[@]:-} $result)
55 }
21 }
56
22
57 _hg_paths()
23 _hg_paths()
@@ -1,7 +1,7 b''
1 Summary: Mercurial -- a distributed SCM
1 Summary: Mercurial -- a distributed SCM
2 Name: mercurial
2 Name: mercurial
3 Version: 0.7
3 Version: 0.8
4 Release: 1
4 Release: 0
5 License: GPL
5 License: GPL
6 Group: Development/Tools
6 Group: Development/Tools
7 Source: http://www.selenic.com/mercurial/release/%{name}-%{version}.tar.gz
7 Source: http://www.selenic.com/mercurial/release/%{name}-%{version}.tar.gz
@@ -10,6 +10,7 b' BuildRoot: /tmp/build.%{name}-%{version}'
10
10
11 %define pythonver %(python -c 'import sys;print ".".join(map(str, sys.version_info[:2]))')
11 %define pythonver %(python -c 'import sys;print ".".join(map(str, sys.version_info[:2]))')
12 %define pythonlib %{_libdir}/python%{pythonver}/site-packages/%{name}
12 %define pythonlib %{_libdir}/python%{pythonver}/site-packages/%{name}
13 %define hgext %{_libdir}/python%{pythonver}/site-packages/hgext
13
14
14 %description
15 %description
15 Mercurial is a fast, lightweight source control management system designed
16 Mercurial is a fast, lightweight source control management system designed
@@ -30,10 +31,12 b' rm -rf $RPM_BUILD_ROOT'
30
31
31 %files
32 %files
32 %defattr(-,root,root,-)
33 %defattr(-,root,root,-)
33 %doc doc/* contrib/patchbomb *.cgi
34 %doc doc/* *.cgi
34 %dir %{pythonlib}
35 %dir %{pythonlib}
36 %dir %{hgext}
35 %{_bindir}/hgmerge
37 %{_bindir}/hgmerge
36 %{_bindir}/hg
38 %{_bindir}/hg
37 %{pythonlib}/templates
39 %{pythonlib}/templates
38 %{pythonlib}/*.py*
40 %{pythonlib}/*.py*
39 %{pythonlib}/*.so
41 %{pythonlib}/*.so
42 %{hgext}/*.py*
@@ -28,23 +28,22 b' AllowNoIcons=true'
28 DefaultGroupName=Mercurial
28 DefaultGroupName=Mercurial
29
29
30 [Files]
30 [Files]
31 Source: templates\*.*; DestDir: {app}\Templates; Flags: recursesubdirs createallsubdirs
31 Source: ..\..\msys\1.0\bin\patch.exe; DestDir: {app}
32 Source: contrib\mercurial.el; DestDir: {app}/Contrib
32 Source: contrib\mercurial.el; DestDir: {app}/Contrib
33 Source: contrib\patchbomb; DestDir: {app}/Contrib
33 Source: contrib\win32\ReadMe.html; DestDir: {app}; Flags: isreadme
34 Source: dist\w9xpopen.exe; DestDir: {app}
34 Source: contrib\win32\mercurial.ini; DestDir: {app}; DestName: Mercurial.ini; Flags: confirmoverwrite
35 Source: contrib\win32\postinstall.txt; DestDir: {app}; DestName: ReleaseNotes.txt
35 Source: dist\hg.exe; DestDir: {app}
36 Source: dist\hg.exe; DestDir: {app}
37 Source: dist\library.zip; DestDir: {app}
38 Source: dist\mfc71.dll; DestDir: {sys}; Flags: sharedfile uninsnosharedfileprompt
36 Source: dist\msvcr71.dll; DestDir: {sys}; Flags: sharedfile uninsnosharedfileprompt
39 Source: dist\msvcr71.dll; DestDir: {sys}; Flags: sharedfile uninsnosharedfileprompt
37 Source: dist\library.zip; DestDir: {app}
40 Source: dist\w9xpopen.exe; DestDir: {app}
38 Source: doc\*.txt; DestDir: {app}\Docs
41 Source: doc\*.txt; DestDir: {app}\Docs
39 Source: dist\mfc71.dll; DestDir: {sys}; Flags: sharedfile uninsnosharedfileprompt
42 Source: templates\*.*; DestDir: {app}\Templates; Flags: recursesubdirs createallsubdirs
43 Source: CONTRIBUTORS; DestDir: {app}; DestName: Contributors.txt
40 Source: COPYING; DestDir: {app}; DestName: Copying.txt
44 Source: COPYING; DestDir: {app}; DestName: Copying.txt
41 Source: comparison.txt; DestDir: {app}\Docs; DestName: Comparison.txt
45 Source: comparison.txt; DestDir: {app}\Docs; DestName: Comparison.txt
42 Source: notes.txt; DestDir: {app}\Docs; DestName: DesignNotes.txt
46 Source: notes.txt; DestDir: {app}\Docs; DestName: DesignNotes.txt
43 Source: CONTRIBUTORS; DestDir: {app}; DestName: Contributors.txt
44 Source: contrib\win32\ReadMe.html; DestDir: {app}; Flags: isreadme
45 Source: ..\..\msys\1.0\bin\patch.exe; DestDir: {app}
46 Source: contrib\win32\mercurial.ini; DestDir: {app}; DestName: Mercurial.ini; Flags: confirmoverwrite
47 Source: contrib\win32\postinstall.txt; DestDir: {app}; DestName: ReleaseNotes.txt
48
47
49 [INI]
48 [INI]
50 Filename: {app}\Mercurial.url; Section: InternetShortcut; Key: URL; String: http://www.selenic.com/mercurial/
49 Filename: {app}\Mercurial.url; Section: InternetShortcut; Key: URL; String: http://www.selenic.com/mercurial/
@@ -8,6 +8,12 b' man: $(MAN)'
8
8
9 html: $(HTML)
9 html: $(HTML)
10
10
11 hg.1.txt: hg.1.gendoc.txt
12 touch hg.1.txt
13
14 hg.1.gendoc.txt: ../mercurial/commands.py
15 python gendoc.py > $@
16
11 %: %.xml
17 %: %.xml
12 xmlto man $*.xml
18 xmlto man $*.xml
13
19
@@ -18,4 +24,4 b' html: $(HTML)'
18 asciidoc -b html4 $*.txt || asciidoc -b html $*.txt
24 asciidoc -b html4 $*.txt || asciidoc -b html $*.txt
19
25
20 clean:
26 clean:
21 $(RM) $(MAN) $(MAN:%=%.xml) $(MAN:%=%.html)
27 $(RM) $(MAN) $(MAN:%=%.xml) $(MAN:%=%.html) *.[0-9].gendoc.txt
This diff has been collapsed as it changes many lines, (647 lines changed) Show them Hide them
@@ -14,42 +14,6 b' DESCRIPTION'
14 -----------
14 -----------
15 The hg(1) command provides a command line interface to the Mercurial system.
15 The hg(1) command provides a command line interface to the Mercurial system.
16
16
17 OPTIONS
18 -------
19
20 -R, --repository::
21 repository root directory
22
23 --cwd::
24 change working directory
25
26 -y, --noninteractive::
27 do not prompt, assume 'yes' for any required answers
28
29 -q, --quiet::
30 suppress output
31
32 -v, --verbose::
33 enable additional output
34
35 --debug::
36 enable debugging output
37
38 --traceback::
39 print traceback on exception
40
41 --time::
42 time how long the command takes
43
44 --profile::
45 print command execution profile
46
47 --version::
48 output version information and exit
49
50 -h, --help::
51 display help and exit
52
53 COMMAND ELEMENTS
17 COMMAND ELEMENTS
54 ----------------
18 ----------------
55
19
@@ -70,617 +34,8 b' repository path::'
70 fast and the old-http:// protocol which is much slower but does not
34 fast and the old-http:// protocol which is much slower but does not
71 require a special server on the web host.
35 require a special server on the web host.
72
36
73 COMMANDS
74 --------
75
37
76 add [options] [files ...]::
38 include::hg.1.gendoc.txt[]
77 Schedule files to be version controlled and added to the repository.
78
79 The files will be added to the repository at the next commit.
80
81 If no names are given, add all files in the current directory and
82 its subdirectories.
83
84 addremove [options] [files ...]::
85 Add all new files and remove all missing files from the repository.
86
87 New files are ignored if they match any of the patterns in .hgignore. As
88 with add, these changes take effect at the next commit.
89
90 annotate [-r <rev> -u -n -c -d] [files ...]::
91 List changes in files, showing the revision id responsible for each line
92
93 This command is useful to discover who did a change or when a change took
94 place.
95
96 Without the -a option, annotate will avoid processing files it
97 detects as binary. With -a, annotate will generate an annotation
98 anyway, probably with undesirable results.
99
100 options:
101 -a, --text treat all files as text
102 -I, --include <pat> include names matching the given patterns
103 -X, --exclude <pat> exclude names matching the given patterns
104 -r, --revision <rev> annotate the specified revision
105 -u, --user list the author
106 -d, --date list the commit date
107 -c, --changeset list the changeset
108 -n, --number list the revision number (default)
109
110 bundle <file> <other>::
111 (EXPERIMENTAL)
112
113 Generate a compressed changegroup file collecting all changesets
114 not found in the other repository.
115
116 This file can then be transferred using conventional means and
117 applied to another repository with the unbundle command. This is
118 useful when native push and pull are not available or when
119 exporting an entire repository is undesirable. The standard file
120 extension is ".hg".
121
122 Unlike import/export, this exactly preserves all changeset
123 contents including permissions, rename data, and revision history.
124
125 cat [options] <file ...>::
126 Print the specified files as they were at the given revision.
127 If no revision is given then the tip is used.
128
129 Output may be to a file, in which case the name of the file is
130 given using a format string. The formatting rules are the same as
131 for the export command, with the following additions:
132
133 %s basename of file being printed
134 %d dirname of file being printed, or '.' if in repo root
135 %p root-relative path name of file being printed
136
137 options:
138 -I, --include <pat> include names matching the given patterns
139 -X, --exclude <pat> exclude names matching the given patterns
140 -o, --output <filespec> print output to file with formatted name
141 -r, --rev <rev> print the given revision
142
143 clone [options] <source> [dest]::
144 Create a copy of an existing repository in a new directory.
145
146 If no destination directory name is specified, it defaults to the
147 basename of the source.
148
149 The location of the source is added to the new repository's
150 .hg/hgrc file, as the default to be used for future pulls.
151
152 For efficiency, hardlinks are used for cloning whenever the source
153 and destination are on the same filesystem. Some filesystems,
154 such as AFS, implement hardlinking incorrectly, but do not report
155 errors. In these cases, use the --pull option to avoid
156 hardlinking.
157
158 See pull for valid source format details.
159
160 options:
161 -U, --noupdate do not update the new working directory
162 --pull use pull protocol to copy metadata
163 -e, --ssh specify ssh command to use
164 --remotecmd specify hg command to run on the remote side
165
166 commit [options] [files...]::
167 Commit changes to the given files into the repository.
168
169 If a list of files is omitted, all changes reported by "hg status"
170 from the root of the repository will be commited.
171
172 The HGEDITOR or EDITOR environment variables are used to start an
173 editor to add a commit comment.
174
175 Options:
176
177 -A, --addremove run addremove during commit
178 -I, --include <pat> include names matching the given patterns
179 -X, --exclude <pat> exclude names matching the given patterns
180 -m, --message <text> use <text> as commit message
181 -l, --logfile <file> read the commit message from <file>
182 -d, --date <datecode> record datecode as commit date
183 -u, --user <user> record user as commiter
184
185 aliases: ci
186
187 copy <source ...> <dest>::
188 Mark dest as having copies of source files. If dest is a
189 directory, copies are put in that directory. If dest is a file,
190 there can only be one source.
191
192 By default, this command copies the contents of files as they
193 stand in the working directory. If invoked with --after, the
194 operation is recorded, but no copying is performed.
195
196 This command takes effect in the next commit.
197
198 NOTE: This command should be treated as experimental. While it
199 should properly record copied files, this information is not yet
200 fully used by merge, nor fully reported by log.
201
202 Options:
203 -A, --after record a copy that has already occurred
204 -I, --include <pat> include names matching the given patterns
205 -X, --exclude <pat> exclude names matching the given patterns
206 -f, --force forcibly copy over an existing managed file
207
208 aliases: cp
209
210 diff [-a] [-r revision] [-r revision] [files ...]::
211 Show differences between revisions for the specified files.
212
213 Differences between files are shown using the unified diff format.
214
215 When two revision arguments are given, then changes are shown
216 between those revisions. If only one revision is specified then
217 that revision is compared to the working directory, and, when no
218 revisions are specified, the working directory files are compared
219 to its parent.
220
221 Without the -a option, diff will avoid generating diffs of files
222 it detects as binary. With -a, diff will generate a diff anyway,
223 probably with undesirable results.
224
225 options:
226 -a, --text treat all files as text
227 -I, --include <pat> include names matching the given patterns
228 -p, --show-function show which function each change is in
229 -X, --exclude <pat> exclude names matching the given patterns
230 -w, --ignore-all-space ignore white space when comparing lines
231
232 export [-o filespec] [revision] ...::
233 Print the changeset header and diffs for one or more revisions.
234
235 The information shown in the changeset header is: author,
236 changeset hash, parent and commit comment.
237
238 Output may be to a file, in which case the name of the file is
239 given using a format string. The formatting rules are as follows:
240
241 %% literal "%" character
242 %H changeset hash (40 bytes of hexadecimal)
243 %N number of patches being generated
244 %R changeset revision number
245 %b basename of the exporting repository
246 %h short-form changeset hash (12 bytes of hexadecimal)
247 %n zero-padded sequence number, starting at 1
248 %r zero-padded changeset revision number
249
250 Without the -a option, export will avoid generating diffs of files
251 it detects as binary. With -a, export will generate a diff anyway,
252 probably with undesirable results.
253
254 options:
255 -a, --text treat all files as text
256 -o, --output <filespec> print output to file with formatted name
257
258 forget [options] [files]::
259 Undo an 'hg add' scheduled for the next commit.
260
261 options:
262 -I, --include <pat> include names matching the given patterns
263 -X, --exclude <pat> exclude names matching the given patterns
264
265 grep [options] pattern [files]::
266 Search revisions of files for a regular expression.
267
268 This command behaves differently than Unix grep. It only accepts
269 Python/Perl regexps. It searches repository history, not the
270 working directory. It always prints the revision number in which
271 a match appears.
272
273 By default, grep only prints output for the first revision of a
274 file in which it finds a match. To get it to print every revision
275 that contains a change in match status ("-" for a match that
276 becomes a non-match, or "+" for a non-match that becomes a match),
277 use the --all flag.
278
279 options:
280 -0, --print0 end fields with NUL
281 -I, --include <pat> include names matching the given patterns
282 -X, --exclude <pat> exclude names matching the given patterns
283 --all print all revisions that match
284 -i, --ignore-case ignore case when matching
285 -l, --files-with-matches print only filenames and revs that match
286 -n, --line-number print matching line numbers
287 -r <rev>, --rev <rev> search in given revision range
288 -u, --user print user who committed change
289
290 heads::
291 Show all repository head changesets.
292
293 Repository "heads" are changesets that don't have children
294 changesets. They are where development generally takes place and
295 are the usual targets for update and merge operations.
296
297 options:
298 -b, --branches show branches
299 -r, --rev <rev> show only heads which are descendants of rev
300 --style <style> display using style map file
301 --template <tpl> display using template
302
303 identify::
304 Print a short summary of the current state of the repo.
305
306 This summary identifies the repository state using one or two parent
307 hash identifiers, followed by a "+" if there are uncommitted changes
308 in the working directory, followed by a list of tags for this revision.
309
310 aliases: id
311
312 import [-p <n> -b <base> -f] <patches>::
313 Import a list of patches and commit them individually.
314
315 If there are outstanding changes in the working directory, import
316 will abort unless given the -f flag.
317
318 If a patch looks like a mail message (its first line starts with
319 "From " or looks like an RFC822 header), it will not be applied
320 unless the -f option is used. The importer neither parses nor
321 discards mail headers, so use -f only to override the "mailness"
322 safety check, not to import a real mail message.
323
324 options:
325 -p, --strip <n> directory strip option for patch. This has the same
326 meaning as the corresponding patch option
327 -b <path> base directory to read patches from
328 -f, --force skip check for outstanding uncommitted changes
329
330 aliases: patch
331
332 incoming [-p] [source]::
333 Show new changesets found in the specified repo or the default
334 pull repo. These are the changesets that would be pulled if a pull
335 was requested.
336
337 Currently only local repositories are supported.
338
339 options:
340 -M, --no-merges do not show merges
341 -n, --newest-first show newest records first
342 -p, --patch show patch
343 --style <style> display using style map file
344 --template <tpl> display using template
345
346 aliases: in
347
348 init [dest]::
349 Initialize a new repository in the given directory. If the given
350 directory does not exist, it is created.
351
352 If no directory is given, the current directory is used.
353
354 locate [options] [files]::
355 Print all files under Mercurial control whose names match the
356 given patterns.
357
358 This command searches the current directory and its
359 subdirectories. To search an entire repository, move to the root
360 of the repository.
361
362 If no patterns are given to match, this command prints all file
363 names.
364
365 If you want to feed the output of this command into the "xargs"
366 command, use the "-0" option to both this command and "xargs".
367 This will avoid the problem of "xargs" treating single filenames
368 that contain white space as multiple filenames.
369
370 options:
371
372 -0, --print0 end filenames with NUL, for use with xargs
373 -f, --fullpath print complete paths from the filesystem root
374 -I, --include <pat> include names matching the given patterns
375 -r, --rev <rev> search the repository as it stood at rev
376 -X, --exclude <pat> exclude names matching the given patterns
377
378 log [-r revision ...] [-p] [files]::
379 Print the revision history of the specified files or the entire project.
380
381 By default this command outputs: changeset id and hash, tags,
382 parents, user, date and time, and a summary for each commit. The
383 -v switch adds some more detail, such as changed files, manifest
384 hashes or message signatures.
385
386 options:
387 -I, --include <pat> include names matching the given patterns
388 -X, --exclude <pat> exclude names matching the given patterns
389 -b, --branch show branches
390 -k, --keyword <str> search for keywords
391 -l, --limit <num> print no more than this many changes
392 -M, --no-merges do not show merges
393 -m, --only-merges only show merges
394 -r, --rev <A> show the specified revision or range
395 -p, --patch show patch
396 --style <style> display using style map file
397 --template <tpl> display using template
398
399 aliases: history
400
401 manifest [revision]::
402 Print a list of version controlled files for the given revision.
403
404 The manifest is the list of files being version controlled. If no revision
405 is given then the tip is used.
406
407 outgoing [-p] [dest]::
408 Show changesets not found in the specified destination repo or the
409 default push repo. These are the changesets that would be pushed
410 if a push was requested.
411
412 See pull for valid source format details.
413
414 options:
415 -M, --no-merges do not show merges
416 -p, --patch show patch
417 -n, --newest-first show newest records first
418 --style <style> display using style map file
419 --template <tpl> display using template
420
421 aliases: out
422
423 parents::
424 Print the working directory's parent revisions.
425
426 options:
427 -b, --branches show branches
428 --style <style> display using style map file
429 --template <tpl> display using template
430
431 paths [NAME]::
432 Show definition of symbolic path name NAME. If no name is given, show
433 definition of available names.
434
435 Path names are defined in the [paths] section of /etc/mercurial/hgrc
436 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
437
438 pull <repository path>::
439 Pull changes from a remote repository to a local one.
440
441 This finds all changes from the repository at the specified path
442 or URL and adds them to the local repository. By default, this
443 does not update the copy of the project in the working directory.
444
445 Valid URLs are of the form:
446
447 local/filesystem/path
448 http://[user@]host[:port][/path]
449 https://[user@]host[:port][/path]
450 ssh://[user@]host[:port][/path]
451
452 SSH requires an accessible shell account on the destination machine
453 and a copy of hg in the remote path. With SSH, paths are relative
454 to the remote user's home directory by default; use two slashes at
455 the start of a path to specify it as relative to the filesystem root.
456
457 options:
458 -u, --update update the working directory to tip after pull
459 -e, --ssh specify ssh command to use
460 --remotecmd specify hg command to run on the remote side
461
462 push <destination>::
463 Push changes from the local repository to the given destination.
464
465 This is the symmetrical operation for pull. It helps to move
466 changes from the current repository to a different one. If the
467 destination is local this is identical to a pull in that directory
468 from the current one.
469
470 By default, push will refuse to run if it detects the result would
471 increase the number of remote heads. This generally indicates the
472 the client has forgotten to sync and merge before pushing.
473
474 Valid URLs are of the form:
475
476 local/filesystem/path
477 ssh://[user@]host[:port][/path]
478
479 SSH requires an accessible shell account on the destination
480 machine and a copy of hg in the remote path.
481
482 options:
483
484 -f, --force force update
485 -e, --ssh specify ssh command to use
486 --remotecmd specify hg command to run on the remote side
487
488 rawcommit [-p -d -u -F -m -l]::
489 Lowlevel commit, for use in helper scripts. (DEPRECATED)
490
491 This command is not intended to be used by normal users, as it is
492 primarily useful for importing from other SCMs.
493
494 This command is now deprecated and will be removed in a future
495 release, please use debugsetparents and commit instead.
496
497 recover::
498 Recover from an interrupted commit or pull.
499
500 This command tries to fix the repository status after an interrupted
501 operation. It should only be necessary when Mercurial suggests it.
502
503 remove [options] [files ...]::
504 Schedule the indicated files for removal from the repository.
505
506 This command schedules the files to be removed at the next commit.
507 This only removes files from the current branch, not from the
508 entire project history. If the files still exist in the working
509 directory, they will be deleted from it.
510
511 aliases: rm
512
513 rename <source ...> <dest>::
514 Mark dest as copies of sources; mark sources for deletion. If
515 dest is a directory, copies are put in that directory. If dest is
516 a file, there can only be one source.
517
518 By default, this command copies the contents of files as they
519 stand in the working directory. If invoked with --after, the
520 operation is recorded, but no copying is performed.
521
522 This command takes effect in the next commit.
523
524 NOTE: This command should be treated as experimental. While it
525 should properly record rename files, this information is not yet
526 fully used by merge, nor fully reported by log.
527
528 Options:
529 -A, --after record a rename that has already occurred
530 -f, --force forcibly copy over an existing managed file
531
532 aliases: mv
533
534 revert [names ...]::
535 The revert command has two modes of operation.
536
537 In its default mode, it reverts any uncommitted modifications made
538 to the named files or directories. This restores the contents of
539 the affected files to an unmodified state.
540
541 Using the -r option, it reverts the given files or directories to
542 their state as of an earlier revision. This can be helpful to "roll
543 back" some or all of a change that should not have been committed.
544
545 Revert modifies the working directory. It does not commit any
546 changes, or change the parent of the current working directory.
547
548 If a file has been deleted, it is recreated. If the executable
549 mode of a file was changed, it is reset.
550
551 If a directory is given, all files in that directory and its
552 subdirectories are reverted.
553
554 If no arguments are given, all files in the current directory and
555 its subdirectories are reverted.
556
557 options:
558 -r, --rev <rev> revision to revert to
559 -n, --nonrecursive do not recurse into subdirectories
560
561 root::
562 Print the root directory of the current repository.
563
564 serve [options]::
565 Start a local HTTP repository browser and pull server.
566
567 By default, the server logs accesses to stdout and errors to
568 stderr. Use the "-A" and "-E" options to log to files.
569
570 options:
571 -A, --accesslog <file> name of access log file to write to
572 -d, --daemon run server in background, as a daemon
573 -E, --errorlog <file> name of error log file to write to
574 -a, --address <addr> address to use
575 -p, --port <n> port to use (default: 8000)
576 -n, --name <name> name to show in web pages (default: working dir)
577 --pid-file <file> write server process ID to given file
578 -t, --templatedir <path> web templates to use
579 -6, --ipv6 use IPv6 in addition to IPv4
580
581 status [options] [files]::
582 Show changed files in the working directory. If no names are
583 given, all files are shown. Otherwise, only files matching the
584 given names are shown.
585
586 The codes used to show the status of files are:
587
588 M = changed
589 A = added
590 R = removed
591 ? = not tracked
592
593 options:
594
595 -m, --modified show only modified files
596 -a, --added show only added files
597 -r, --removed show only removed files
598 -u, --unknown show only unknown (not tracked) files
599 -n, --no-status hide status prefix
600 -0, --print0 end filenames with NUL, for use with xargs
601 -I, --include <pat> include names matching the given patterns
602 -X, --exclude <pat> exclude names matching the given patterns
603
604 tag [-l -m <text> -d <datecode> -u <user>] <name> [revision]::
605 Name a particular revision using <name>.
606
607 Tags are used to name particular revisions of the repository and are
608 very useful to compare different revision, to go back to significant
609 earlier versions or to mark branch points as releases, etc.
610
611 If no revision is given, the tip is used.
612
613 To facilitate version control, distribution, and merging of tags,
614 they are stored as a file named ".hgtags" which is managed
615 similarly to other project files and can be hand-edited if
616 necessary.
617
618 options:
619 -l, --local make the tag local
620 -m, --message <text> message for tag commit log entry
621 -d, --date <datecode> datecode for commit
622 -u, --user <user> user for commit
623
624 Note: Local tags are not version-controlled or distributed and are
625 stored in the .hg/localtags file. If there exists a local tag and
626 a public tag with the same name, local tag is used.
627
628 tags::
629 List the repository tags.
630
631 This lists both regular and local tags.
632
633 tip [-p]::
634 Show the tip revision.
635
636 options:
637 -b, --branches show branches
638 -p, --patch show patch
639 --style <style> display using style map file
640 --template <tpl> display using template
641
642 unbundle <file>::
643 (EXPERIMENTAL)
644
645 Apply a compressed changegroup file generated by the bundle
646 command.
647
648 undo::
649 Undo the last commit or pull transaction.
650
651 Roll back the last pull or commit transaction on the
652 repository, restoring the project to its earlier state.
653
654 This command should be used with care. There is only one level of
655 undo and there is no redo.
656
657 This command is not intended for use on public repositories. Once
658 a change is visible for pull by other users, undoing it locally is
659 ineffective.
660
661 update [-m -C] [revision]::
662 Update the working directory to the specified revision.
663
664 By default, update will refuse to run if doing so would require
665 merging or discarding local changes.
666
667 With the -m option, a merge will be performed.
668
669 With the -C option, local changes will be lost.
670
671 options:
672 -m, --merge allow merging of branches
673 -C, --clean overwrite locally modified files
674
675 aliases: up checkout co
676
677 verify::
678 Verify the integrity of the current repository.
679
680 This will perform an extensive check of the repository's
681 integrity, validating the hashes and checksums of each entry in
682 the changelog, manifest, and tracked files, as well as the
683 integrity of their crosslinks and indices.
684
39
685 FILE NAME PATTERNS
40 FILE NAME PATTERNS
686 ------------------
41 ------------------
@@ -1,11 +1,13 b''
1 #!/usr/bin/env python
1 # bisect extension for mercurial
2 #
3 # Copyright 2005, 2006 Benoit Boissinot <benoit.boissinot@ens-lyon.org>
4 # Inspired by git bisect, extension skeleton taken from mq.py.
2 #
5 #
3 # This software may be used and distributed according to the terms
6 # This software may be used and distributed according to the terms
4 # of the GNU General Public License, incorporated herein by reference.
7 # of the GNU General Public License, incorporated herein by reference.
5
8
6 from mercurial.demandload import demandload
9 from mercurial.demandload import demandload
7 demandload(globals(), "os sys sets")
10 demandload(globals(), "os sys sets mercurial:hg,util")
8 from mercurial import hg
9
11
10 versionstr = "0.0.3"
12 versionstr = "0.0.3"
11
13
@@ -30,33 +32,32 b' class bisect(object):'
30 """dichotomic search in the DAG of changesets"""
32 """dichotomic search in the DAG of changesets"""
31 def __init__(self, ui, repo):
33 def __init__(self, ui, repo):
32 self.repo = repo
34 self.repo = repo
33 self.path = os.path.join(repo.join(""), "bisect")
35 self.path = repo.join("bisect")
36 self.opener = util.opener(self.path)
34 self.ui = ui
37 self.ui = ui
35 self.goodrevs = []
38 self.goodrevs = []
36 self.badrev = None
39 self.badrev = None
37 self.good_dirty = 0
40 self.good_dirty = 0
38 self.bad_dirty = 0
41 self.bad_dirty = 0
39 self.good_path = os.path.join(self.path, "good")
42 self.good_path = "good"
40 self.bad_path = os.path.join(self.path, "bad")
43 self.bad_path = "bad"
41
44
42 s = self.good_path
45 if os.path.exists(os.path.join(self.path, self.good_path)):
43 if os.path.exists(s):
46 self.goodrevs = self.opener(self.good_path).read().splitlines()
44 self.goodrevs = self.repo.opener(s).read().splitlines()
45 self.goodrevs = [hg.bin(x) for x in self.goodrevs]
47 self.goodrevs = [hg.bin(x) for x in self.goodrevs]
46 s = self.bad_path
48 if os.path.exists(os.path.join(self.path, self.bad_path)):
47 if os.path.exists(s):
49 r = self.opener(self.bad_path).read().splitlines()
48 r = self.repo.opener(s).read().splitlines()
49 if r:
50 if r:
50 self.badrev = hg.bin(r.pop(0))
51 self.badrev = hg.bin(r.pop(0))
51
52
52 def __del__(self):
53 def __del__(self):
53 if not os.path.isdir(self.path):
54 if not os.path.isdir(self.path):
54 return
55 return
55 f = self.repo.opener(self.good_path, "w")
56 f = self.opener(self.good_path, "w")
56 f.write("\n".join([hg.hex(r) for r in self.goodrevs]))
57 f.write("\n".join([hg.hex(r) for r in self.goodrevs]))
57 if len(self.goodrevs) > 0:
58 if len(self.goodrevs) > 0:
58 f.write("\n")
59 f.write("\n")
59 f = self.repo.opener(self.bad_path, "w")
60 f = self.opener(self.bad_path, "w")
60 if self.badrev:
61 if self.badrev:
61 f.write(hg.hex(self.badrev) + "\n")
62 f.write(hg.hex(self.badrev) + "\n")
62
63
@@ -72,7 +73,8 b' class bisect(object):'
72 def reset(self):
73 def reset(self):
73 """finish a bisection"""
74 """finish a bisection"""
74 if os.path.isdir(self.path):
75 if os.path.isdir(self.path):
75 sl = [self.bad_path, self.good_path]
76 sl = [os.path.join(self.path, p)
77 for p in [self.bad_path, self.good_path]]
76 for s in sl:
78 for s in sl:
77 if os.path.exists(s):
79 if os.path.exists(s):
78 os.unlink(s)
80 os.unlink(s)
@@ -114,7 +116,8 b' class bisect(object):'
114 cl = self.repo.changelog
116 cl = self.repo.changelog
115 if not stop:
117 if not stop:
116 stop = sets.Set([])
118 stop = sets.Set([])
117 for g in reversed(self.goodrevs):
119 for i in xrange(len(self.goodrevs)-1, -1, -1):
120 g = self.goodrevs[i]
118 if g in stop:
121 if g in stop:
119 continue
122 continue
120 stop.update(cl.reachable(g))
123 stop.update(cl.reachable(g))
@@ -162,7 +165,8 b' class bisect(object):'
162 if not self.goodrevs:
165 if not self.goodrevs:
163 self.ui.warn("No good revision given\n")
166 self.ui.warn("No good revision given\n")
164 self.ui.warn("Assuming the first revision is good\n")
167 self.ui.warn("Assuming the first revision is good\n")
165 ancestors, num_ancestors = self.__ancestors_and_nb_ancestors(self.badrev)
168 ancestors, num_ancestors = self.__ancestors_and_nb_ancestors(
169 self.badrev)
166 tot = len(ancestors)
170 tot = len(ancestors)
167 if tot == 1:
171 if tot == 1:
168 if ancestors.pop() != self.badrev:
172 if ancestors.pop() != self.badrev:
@@ -281,7 +285,6 b' for subcommands see "hg bisect help\\"'
281 return bisectcmdtable[cmd][0](*args)
285 return bisectcmdtable[cmd][0](*args)
282
286
283 cmdtable = {
287 cmdtable = {
284 "bisect": (bisect_run, [],
288 "bisect": (bisect_run, [], "hg bisect [help|init|reset|next|good|bad]"),
285 "hg bisect [help|init|reset|next|good|bad]"),
286 #"bisect-test": (test, [], "hg bisect-test rev"),
289 #"bisect-test": (test, [], "hg bisect-test rev"),
287 }
290 }
@@ -49,20 +49,11 b''
49 # to = recipient1, recipient2, ...
49 # to = recipient1, recipient2, ...
50 # cc = cc1, cc2, ...
50 # cc = cc1, cc2, ...
51
51
52 from email.MIMEMultipart import MIMEMultipart
52 from mercurial.demandload import *
53 from email.MIMEText import MIMEText
53 demandload(globals(), '''email.MIMEMultipart email.MIMEText email.Utils
54 from email.Utils import parseaddr
54 mercurial:commands,hg,ui
55 from mercurial import commands
55 os errno popen2 smtplib socket sys tempfile time''')
56 from mercurial import hg
57 from mercurial import ui
58 from mercurial.i18n import gettext as _
56 from mercurial.i18n import gettext as _
59 import os
60 import popen2
61 import smtplib
62 import socket
63 import sys
64 import tempfile
65 import time
66
57
67 try:
58 try:
68 # readline gives raw_input editing capabilities, but is not
59 # readline gives raw_input editing capabilities, but is not
@@ -149,7 +140,10 b' def patchbomb(ui, repo, *revs, **opts):'
149 if opts['diffstat']:
140 if opts['diffstat']:
150 body += cdiffstat('\n'.join(desc), patch) + '\n\n'
141 body += cdiffstat('\n'.join(desc), patch) + '\n\n'
151 body += '\n'.join(patch)
142 body += '\n'.join(patch)
152 msg = MIMEText(body)
143 msg = email.MIMEText.MIMEText(body)
144 if total == 1:
145 subj = '[PATCH] ' + desc[0].strip()
146 else:
153 subj = '[PATCH %d of %d] %s' % (idx, total, desc[0].strip())
147 subj = '[PATCH %d of %d] %s' % (idx, total, desc[0].strip())
154 if subj.endswith('.'): subj = subj[:-1]
148 if subj.endswith('.'): subj = subj[:-1]
155 msg['Subject'] = subj
149 msg['Subject'] = subj
@@ -189,17 +183,9 b' def patchbomb(ui, repo, *revs, **opts):'
189 jumbo.extend(p)
183 jumbo.extend(p)
190 msgs.append(makepatch(p, i + 1, len(patches)))
184 msgs.append(makepatch(p, i + 1, len(patches)))
191
185
192 ui.write(_('\nWrite the introductory message for the patch series.\n\n'))
193
194 sender = (opts['from'] or ui.config('patchbomb', 'from') or
186 sender = (opts['from'] or ui.config('patchbomb', 'from') or
195 prompt('From', ui.username()))
187 prompt('From', ui.username()))
196
188
197 msg = MIMEMultipart()
198 msg['Subject'] = '[PATCH 0 of %d] %s' % (
199 len(patches),
200 opts['subject'] or
201 prompt('Subject:', rest = ' [PATCH 0 of %d] ' % len(patches)))
202
203 def getaddrs(opt, prpt, default = None):
189 def getaddrs(opt, prpt, default = None):
204 addrs = opts[opt] or (ui.config('patchbomb', opt) or
190 addrs = opts[opt] or (ui.config('patchbomb', opt) or
205 prompt(prpt, default = default)).split(',')
191 prompt(prpt, default = default)).split(',')
@@ -207,6 +193,15 b' def patchbomb(ui, repo, *revs, **opts):'
207 to = getaddrs('to', 'To')
193 to = getaddrs('to', 'To')
208 cc = getaddrs('cc', 'Cc', '')
194 cc = getaddrs('cc', 'Cc', '')
209
195
196 if len(patches) > 1:
197 ui.write(_('\nWrite the introductory message for the patch series.\n\n'))
198
199 msg = email.MIMEMultipart.MIMEMultipart()
200 msg['Subject'] = '[PATCH 0 of %d] %s' % (
201 len(patches),
202 opts['subject'] or
203 prompt('Subject:', rest = ' [PATCH 0 of %d] ' % len(patches)))
204
210 ui.write(_('Finish with ^D or a dot on a line by itself.\n\n'))
205 ui.write(_('Finish with ^D or a dot on a line by itself.\n\n'))
211
206
212 body = []
207 body = []
@@ -217,16 +212,16 b' def patchbomb(ui, repo, *revs, **opts):'
217 if l == '.': break
212 if l == '.': break
218 body.append(l)
213 body.append(l)
219
214
220 msg.attach(MIMEText('\n'.join(body) + '\n'))
215 msg.attach(email.MIMEText.MIMEText('\n'.join(body) + '\n'))
221
222 ui.write('\n')
223
216
224 if opts['diffstat']:
217 if opts['diffstat']:
225 d = cdiffstat(_('Final summary:\n'), jumbo)
218 d = cdiffstat(_('Final summary:\n'), jumbo)
226 if d: msg.attach(MIMEText(d))
219 if d: msg.attach(email.MIMEText.MIMEText(d))
227
220
228 msgs.insert(0, msg)
221 msgs.insert(0, msg)
229
222
223 ui.write('\n')
224
230 if not opts['test'] and not opts['mbox']:
225 if not opts['test'] and not opts['mbox']:
231 s = smtplib.SMTP()
226 s = smtplib.SMTP()
232 s.connect(host = ui.config('smtp', 'host', 'mail'),
227 s.connect(host = ui.config('smtp', 'host', 'mail'),
@@ -241,7 +236,7 b' def patchbomb(ui, repo, *revs, **opts):'
241 s.login(username, password)
236 s.login(username, password)
242 parent = None
237 parent = None
243 tz = time.strftime('%z')
238 tz = time.strftime('%z')
244 sender_addr = parseaddr(sender)[1]
239 sender_addr = email.Utils.parseaddr(sender)[1]
245 for m in msgs:
240 for m in msgs:
246 try:
241 try:
247 m['Message-Id'] = genmsgid(m['X-Mercurial-Node'])
242 m['Message-Id'] = genmsgid(m['X-Mercurial-Node'])
@@ -259,8 +254,12 b' def patchbomb(ui, repo, *revs, **opts):'
259 if opts['test']:
254 if opts['test']:
260 ui.status('Displaying ', m['Subject'], ' ...\n')
255 ui.status('Displaying ', m['Subject'], ' ...\n')
261 fp = os.popen(os.getenv('PAGER', 'more'), 'w')
256 fp = os.popen(os.getenv('PAGER', 'more'), 'w')
257 try:
262 fp.write(m.as_string(0))
258 fp.write(m.as_string(0))
263 fp.write('\n')
259 fp.write('\n')
260 except IOError, inst:
261 if inst.errno != errno.EPIPE:
262 raise
264 fp.close()
263 fp.close()
265 elif opts['mbox']:
264 elif opts['mbox']:
266 ui.status('Writing ', m['Subject'], ' ...\n')
265 ui.status('Writing ', m['Subject'], ' ...\n')
@@ -3,7 +3,13 b''
3 # hgmerge - default merge helper for Mercurial
3 # hgmerge - default merge helper for Mercurial
4 #
4 #
5 # This tries to find a way to do three-way merge on the current system.
5 # This tries to find a way to do three-way merge on the current system.
6 # The result ought to end up in $1.
6 # The result ought to end up in $1. Script is run in root directory of
7 # repository.
8 #
9 # Environment variables set by Mercurial:
10 # HG_FILE name of file within repo
11 # HG_MY_NODE revision being merged
12 # HG_OTHER_NODE revision being merged
7
13
8 set -e # bail out quickly on failure
14 set -e # bail out quickly on failure
9
15
@@ -8,10 +8,21 b' cgitb.enable()'
8 # sys.path.insert(0, "/path/to/python/lib") # if not a system-wide install
8 # sys.path.insert(0, "/path/to/python/lib") # if not a system-wide install
9 from mercurial import hgweb
9 from mercurial import hgweb
10
10
11 # The config file looks like this:
11 # The config file looks like this. You can have paths to individual
12 # repos, collections of repos in a directory tree, or both.
13 #
12 # [paths]
14 # [paths]
13 # virtual/path = /real/path
15 # virtual/path = /real/path
14 # virtual/path = /real/path
16 # virtual/path = /real/path
17 #
18 # [collections]
19 # /prefix/to/strip/off = /root/of/tree/full/of/repos
20 #
21 # collections example: say directory tree /foo contains repos /foo/bar,
22 # /foo/quux/baz. Give this config section:
23 # [collections]
24 # /foo = /foo
25 # Then repos will list as bar and quux/baz.
15
26
16 # Alternatively you can pass a list of ('virtual/path', '/real/path') tuples
27 # Alternatively you can pass a list of ('virtual/path', '/real/path') tuples
17 # or use a dictionary with entries like 'virtual/path': '/real/path'
28 # or use a dictionary with entries like 'virtual/path': '/real/path'
@@ -276,6 +276,14 b' def make_file(repo, r, pat, node=None,'
276
276
277 def dodiff(fp, ui, repo, node1, node2, files=None, match=util.always,
277 def dodiff(fp, ui, repo, node1, node2, files=None, match=util.always,
278 changes=None, text=False, opts={}):
278 changes=None, text=False, opts={}):
279 if not node1:
280 node1 = repo.dirstate.parents()[0]
281 # reading the data for node1 early allows it to play nicely
282 # with repo.changes and the revlog cache.
283 change = repo.changelog.read(node1)
284 mmap = repo.manifest.read(change[0])
285 date1 = util.datestr(change[2])
286
279 if not changes:
287 if not changes:
280 changes = repo.changes(node1, node2, files, match=match)
288 changes = repo.changes(node1, node2, files, match=match)
281 modified, added, removed, deleted, unknown = changes
289 modified, added, removed, deleted, unknown = changes
@@ -294,8 +302,6 b' def dodiff(fp, ui, repo, node1, node2, f'
294 return repo.file(f).read(mmap2[f])
302 return repo.file(f).read(mmap2[f])
295 else:
303 else:
296 date2 = util.datestr()
304 date2 = util.datestr()
297 if not node1:
298 node1 = repo.dirstate.parents()[0]
299 def read(f):
305 def read(f):
300 return repo.wread(f)
306 return repo.wread(f)
301
307
@@ -305,10 +311,6 b' def dodiff(fp, ui, repo, node1, node2, f'
305 hexfunc = ui.verbose and hex or short
311 hexfunc = ui.verbose and hex or short
306 r = [hexfunc(node) for node in [node1, node2] if node]
312 r = [hexfunc(node) for node in [node1, node2] if node]
307
313
308 change = repo.changelog.read(node1)
309 mmap = repo.manifest.read(change[0])
310 date1 = util.datestr(change[2])
311
312 diffopts = ui.diffopts()
314 diffopts = ui.diffopts()
313 showfunc = opts.get('show_function') or diffopts['showfunc']
315 showfunc = opts.get('show_function') or diffopts['showfunc']
314 ignorews = opts.get('ignore_all_space') or diffopts['ignorews']
316 ignorews = opts.get('ignore_all_space') or diffopts['ignorews']
@@ -793,12 +795,7 b' def annotate(ui, repo, *pats, **opts):'
793 change = repo.changelog.read(node)
795 change = repo.changelog.read(node)
794 mmap = repo.manifest.read(change[0])
796 mmap = repo.manifest.read(change[0])
795
797
796 for src, abs, rel, exact in walk(repo, pats, opts):
798 for src, abs, rel, exact in walk(repo, pats, opts, node=node):
797 if abs not in mmap:
798 ui.warn(_("warning: %s is not in the repository!\n") %
799 ((pats and rel) or abs))
800 continue
801
802 f = repo.file(abs)
799 f = repo.file(abs)
803 if not opts['text'] and util.binary(f.read(mmap[abs])):
800 if not opts['text'] and util.binary(f.read(mmap[abs])):
804 ui.write(_("%s: binary file\n") % ((pats and rel) or abs))
801 ui.write(_("%s: binary file\n") % ((pats and rel) or abs))
@@ -834,7 +831,7 b' def bundle(ui, repo, fname, dest="defaul'
834 contents including permissions, rename data, and revision history.
831 contents including permissions, rename data, and revision history.
835 """
832 """
836 f = open(fname, "wb")
833 f = open(fname, "wb")
837 dest = ui.expandpath(dest, repo.root)
834 dest = ui.expandpath(dest)
838 other = hg.repository(ui, dest)
835 other = hg.repository(ui, dest)
839 o = repo.findoutgoing(other)
836 o = repo.findoutgoing(other)
840 cg = repo.changegroup(o, 'bundle')
837 cg = repo.changegroup(o, 'bundle')
@@ -896,6 +893,8 b' def clone(ui, source, dest=None, **opts)'
896 such as AFS, implement hardlinking incorrectly, but do not report
893 such as AFS, implement hardlinking incorrectly, but do not report
897 errors. In these cases, use the --pull option to avoid
894 errors. In these cases, use the --pull option to avoid
898 hardlinking.
895 hardlinking.
896
897 See pull for valid source format details.
899 """
898 """
900 if dest is None:
899 if dest is None:
901 dest = os.path.basename(os.path.normpath(source))
900 dest = os.path.basename(os.path.normpath(source))
@@ -921,7 +920,6 b' def clone(ui, source, dest=None, **opts)'
921 if opts['remotecmd']:
920 if opts['remotecmd']:
922 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
921 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
923
922
924 if not os.path.exists(source):
925 source = ui.expandpath(source)
923 source = ui.expandpath(source)
926
924
927 d = Dircleanup(dest)
925 d = Dircleanup(dest)
@@ -978,7 +976,7 b' def clone(ui, source, dest=None, **opts)'
978 f.close()
976 f.close()
979
977
980 if not opts['noupdate']:
978 if not opts['noupdate']:
981 update(ui, repo)
979 update(repo.ui, repo)
982
980
983 d.close()
981 d.close()
984
982
@@ -1023,7 +1021,8 b' def commit(ui, repo, *pats, **opts):'
1023 except ValueError, inst:
1021 except ValueError, inst:
1024 raise util.Abort(str(inst))
1022 raise util.Abort(str(inst))
1025
1023
1026 def docopy(ui, repo, pats, opts):
1024 def docopy(ui, repo, pats, opts, wlock):
1025 # called with the repo lock held
1027 cwd = repo.getcwd()
1026 cwd = repo.getcwd()
1028 errors = 0
1027 errors = 0
1029 copied = []
1028 copied = []
@@ -1069,8 +1068,16 b' def docopy(ui, repo, pats, opts):'
1069 if not os.path.isdir(targetdir):
1068 if not os.path.isdir(targetdir):
1070 os.makedirs(targetdir)
1069 os.makedirs(targetdir)
1071 try:
1070 try:
1071 restore = repo.dirstate.state(abstarget) == 'r'
1072 if restore:
1073 repo.undelete([abstarget], wlock)
1074 try:
1072 shutil.copyfile(relsrc, reltarget)
1075 shutil.copyfile(relsrc, reltarget)
1073 shutil.copymode(relsrc, reltarget)
1076 shutil.copymode(relsrc, reltarget)
1077 restore = False
1078 finally:
1079 if restore:
1080 repo.remove([abstarget], wlock)
1074 except shutil.Error, inst:
1081 except shutil.Error, inst:
1075 raise util.Abort(str(inst))
1082 raise util.Abort(str(inst))
1076 except IOError, inst:
1083 except IOError, inst:
@@ -1084,7 +1091,8 b' def docopy(ui, repo, pats, opts):'
1084 if ui.verbose or not exact:
1091 if ui.verbose or not exact:
1085 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
1092 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
1086 targets[abstarget] = abssrc
1093 targets[abstarget] = abssrc
1087 repo.copy(origsrc, abstarget)
1094 if abstarget != origsrc:
1095 repo.copy(origsrc, abstarget, wlock)
1088 copied.append((abssrc, relsrc, exact))
1096 copied.append((abssrc, relsrc, exact))
1089
1097
1090 def targetpathfn(pat, dest, srcs):
1098 def targetpathfn(pat, dest, srcs):
@@ -1192,15 +1200,26 b' def copy(ui, repo, *pats, **opts):'
1192 should properly record copied files, this information is not yet
1200 should properly record copied files, this information is not yet
1193 fully used by merge, nor fully reported by log.
1201 fully used by merge, nor fully reported by log.
1194 """
1202 """
1195 errs, copied = docopy(ui, repo, pats, opts)
1203 try:
1204 wlock = repo.wlock(0)
1205 errs, copied = docopy(ui, repo, pats, opts, wlock)
1206 except lock.LockHeld, inst:
1207 ui.warn(_("repository lock held by %s\n") % inst.args[0])
1208 errs = 1
1196 return errs
1209 return errs
1197
1210
1198 def debugancestor(ui, index, rev1, rev2):
1211 def debugancestor(ui, index, rev1, rev2):
1199 """find the ancestor revision of two revisions in a given index"""
1212 """find the ancestor revision of two revisions in a given index"""
1200 r = revlog.revlog(util.opener(os.getcwd()), index, "")
1213 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index, "")
1201 a = r.ancestor(r.lookup(rev1), r.lookup(rev2))
1214 a = r.ancestor(r.lookup(rev1), r.lookup(rev2))
1202 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
1215 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
1203
1216
1217 def debugcomplete(ui, cmd):
1218 """returns the completion list associated with the given command"""
1219 clist = findpossible(cmd).keys()
1220 clist.sort()
1221 ui.write("%s\n" % " ".join(clist))
1222
1204 def debugrebuildstate(ui, repo, rev=None):
1223 def debugrebuildstate(ui, repo, rev=None):
1205 """rebuild the dirstate as it would look like for the given revision"""
1224 """rebuild the dirstate as it would look like for the given revision"""
1206 if not rev:
1225 if not rev:
@@ -1246,12 +1265,8 b' def debugcheckstate(ui, repo):'
1246 error = _(".hg/dirstate inconsistent with current parent's manifest")
1265 error = _(".hg/dirstate inconsistent with current parent's manifest")
1247 raise util.Abort(error)
1266 raise util.Abort(error)
1248
1267
1249 def debugconfig(ui):
1268 def debugconfig(ui, repo):
1250 """show combined config settings from all hgrc files"""
1269 """show combined config settings from all hgrc files"""
1251 try:
1252 repo = hg.repository(ui)
1253 except hg.RepoError:
1254 pass
1255 for section, name, value in ui.walkconfig():
1270 for section, name, value in ui.walkconfig():
1256 ui.write('%s.%s=%s\n' % (section, name, value))
1271 ui.write('%s.%s=%s\n' % (section, name, value))
1257
1272
@@ -1283,7 +1298,8 b' def debugstate(ui, repo):'
1283
1298
1284 def debugdata(ui, file_, rev):
1299 def debugdata(ui, file_, rev):
1285 """dump the contents of an data file revision"""
1300 """dump the contents of an data file revision"""
1286 r = revlog.revlog(util.opener(os.getcwd()), file_[:-2] + ".i", file_)
1301 r = revlog.revlog(util.opener(os.getcwd(), audit=False),
1302 file_[:-2] + ".i", file_)
1287 try:
1303 try:
1288 ui.write(r.revision(r.lookup(rev)))
1304 ui.write(r.revision(r.lookup(rev)))
1289 except KeyError:
1305 except KeyError:
@@ -1291,7 +1307,7 b' def debugdata(ui, file_, rev):'
1291
1307
1292 def debugindex(ui, file_):
1308 def debugindex(ui, file_):
1293 """dump the contents of an index file"""
1309 """dump the contents of an index file"""
1294 r = revlog.revlog(util.opener(os.getcwd()), file_, "")
1310 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "")
1295 ui.write(" rev offset length base linkrev" +
1311 ui.write(" rev offset length base linkrev" +
1296 " nodeid p1 p2\n")
1312 " nodeid p1 p2\n")
1297 for i in range(r.count()):
1313 for i in range(r.count()):
@@ -1302,7 +1318,7 b' def debugindex(ui, file_):'
1302
1318
1303 def debugindexdot(ui, file_):
1319 def debugindexdot(ui, file_):
1304 """dump an index DAG as a .dot file"""
1320 """dump an index DAG as a .dot file"""
1305 r = revlog.revlog(util.opener(os.getcwd()), file_, "")
1321 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "")
1306 ui.write("digraph G {\n")
1322 ui.write("digraph G {\n")
1307 for i in range(r.count()):
1323 for i in range(r.count()):
1308 e = r.index[i]
1324 e = r.index[i]
@@ -1730,7 +1746,7 b' def incoming(ui, repo, source="default",'
1730
1746
1731 Currently only local repositories are supported.
1747 Currently only local repositories are supported.
1732 """
1748 """
1733 source = ui.expandpath(source, repo.root)
1749 source = ui.expandpath(source)
1734 other = hg.repository(ui, source)
1750 other = hg.repository(ui, source)
1735 if not other.local():
1751 if not other.local():
1736 raise util.Abort(_("incoming doesn't work for remote repositories yet"))
1752 raise util.Abort(_("incoming doesn't work for remote repositories yet"))
@@ -1917,8 +1933,10 b' def outgoing(ui, repo, dest="default-pus'
1917 Show changesets not found in the specified destination repo or the
1933 Show changesets not found in the specified destination repo or the
1918 default push repo. These are the changesets that would be pushed
1934 default push repo. These are the changesets that would be pushed
1919 if a push was requested.
1935 if a push was requested.
1936
1937 See pull for valid source format details.
1920 """
1938 """
1921 dest = ui.expandpath(dest, repo.root)
1939 dest = ui.expandpath(dest)
1922 other = hg.repository(ui, dest)
1940 other = hg.repository(ui, dest)
1923 o = repo.findoutgoing(other)
1941 o = repo.findoutgoing(other)
1924 o = repo.changelog.nodesbetween(o)[0]
1942 o = repo.changelog.nodesbetween(o)[0]
@@ -1953,7 +1971,7 b' def parents(ui, repo, rev=None, branches'
1953 if n != nullid:
1971 if n != nullid:
1954 displayer.show(changenode=n, brinfo=br)
1972 displayer.show(changenode=n, brinfo=br)
1955
1973
1956 def paths(ui, search=None):
1974 def paths(ui, repo, search=None):
1957 """show definition of symbolic path names
1975 """show definition of symbolic path names
1958
1976
1959 Show definition of symbolic path name NAME. If no name is given, show
1977 Show definition of symbolic path name NAME. If no name is given, show
@@ -1962,11 +1980,6 b' def paths(ui, search=None):'
1962 Path names are defined in the [paths] section of /etc/mercurial/hgrc
1980 Path names are defined in the [paths] section of /etc/mercurial/hgrc
1963 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
1981 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
1964 """
1982 """
1965 try:
1966 repo = hg.repository(ui=ui)
1967 except hg.RepoError:
1968 pass
1969
1970 if search:
1983 if search:
1971 for name, path in ui.configitems("paths"):
1984 for name, path in ui.configitems("paths"):
1972 if name == search:
1985 if name == search:
@@ -1999,7 +2012,7 b' def pull(ui, repo, source="default", **o'
1999 to the remote user's home directory by default; use two slashes at
2012 to the remote user's home directory by default; use two slashes at
2000 the start of a path to specify it as relative to the filesystem root.
2013 the start of a path to specify it as relative to the filesystem root.
2001 """
2014 """
2002 source = ui.expandpath(source, repo.root)
2015 source = ui.expandpath(source)
2003 ui.status(_('pulling from %s\n') % (source))
2016 ui.status(_('pulling from %s\n') % (source))
2004
2017
2005 if opts['ssh']:
2018 if opts['ssh']:
@@ -2044,7 +2057,7 b' def push(ui, repo, dest="default-push", '
2044 SSH requires an accessible shell account on the destination
2057 SSH requires an accessible shell account on the destination
2045 machine and a copy of hg in the remote path.
2058 machine and a copy of hg in the remote path.
2046 """
2059 """
2047 dest = ui.expandpath(dest, repo.root)
2060 dest = ui.expandpath(dest)
2048 ui.status('pushing to %s\n' % (dest))
2061 ui.status('pushing to %s\n' % (dest))
2049
2062
2050 if opts['ssh']:
2063 if opts['ssh']:
@@ -2062,6 +2075,7 b' def push(ui, repo, dest="default-push", '
2062 def rawcommit(ui, repo, *flist, **rc):
2075 def rawcommit(ui, repo, *flist, **rc):
2063 """raw commit interface (DEPRECATED)
2076 """raw commit interface (DEPRECATED)
2064
2077
2078 (DEPRECATED)
2065 Lowlevel commit, for use in helper scripts.
2079 Lowlevel commit, for use in helper scripts.
2066
2080
2067 This command is not intended to be used by normal users, as it is
2081 This command is not intended to be used by normal users, as it is
@@ -2119,7 +2133,7 b' def remove(ui, repo, pat, *pats, **opts)'
2119 def okaytoremove(abs, rel, exact):
2133 def okaytoremove(abs, rel, exact):
2120 modified, added, removed, deleted, unknown = repo.changes(files=[abs])
2134 modified, added, removed, deleted, unknown = repo.changes(files=[abs])
2121 reason = None
2135 reason = None
2122 if modified:
2136 if modified and not opts['force']:
2123 reason = _('is modified')
2137 reason = _('is modified')
2124 elif added:
2138 elif added:
2125 reason = _('has been marked for add')
2139 reason = _('has been marked for add')
@@ -2154,21 +2168,33 b' def rename(ui, repo, *pats, **opts):'
2154 should properly record rename files, this information is not yet
2168 should properly record rename files, this information is not yet
2155 fully used by merge, nor fully reported by log.
2169 fully used by merge, nor fully reported by log.
2156 """
2170 """
2157 errs, copied = docopy(ui, repo, pats, opts)
2171 try:
2172 wlock = repo.wlock(0)
2173 errs, copied = docopy(ui, repo, pats, opts, wlock)
2158 names = []
2174 names = []
2159 for abs, rel, exact in copied:
2175 for abs, rel, exact in copied:
2160 if ui.verbose or not exact:
2176 if ui.verbose or not exact:
2161 ui.status(_('removing %s\n') % rel)
2177 ui.status(_('removing %s\n') % rel)
2162 names.append(abs)
2178 names.append(abs)
2163 repo.remove(names, unlink=True)
2179 repo.remove(names, True, wlock)
2180 except lock.LockHeld, inst:
2181 ui.warn(_("repository lock held by %s\n") % inst.args[0])
2182 errs = 1
2164 return errs
2183 return errs
2165
2184
2166 def revert(ui, repo, *pats, **opts):
2185 def revert(ui, repo, *pats, **opts):
2167 """revert modified files or dirs back to their unmodified states
2186 """revert modified files or dirs back to their unmodified states
2168
2187
2169 Revert any uncommitted modifications made to the named files or
2188 In its default mode, it reverts any uncommitted modifications made
2170 directories. This restores the contents of the affected files to
2189 to the named files or directories. This restores the contents of
2171 an unmodified state.
2190 the affected files to an unmodified state.
2191
2192 Using the -r option, it reverts the given files or directories to
2193 their state as of an earlier revision. This can be helpful to "roll
2194 back" some or all of a change that should not have been committed.
2195
2196 Revert modifies the working directory. It does not commit any
2197 changes, or change the parent of the current working directory.
2172
2198
2173 If a file has been deleted, it is recreated. If the executable
2199 If a file has been deleted, it is recreated. If the executable
2174 mode of a file was changed, it is reset.
2200 mode of a file was changed, it is reset.
@@ -2183,7 +2209,7 b' def revert(ui, repo, *pats, **opts):'
2183 files, choose, anypats = matchpats(repo, pats, opts)
2209 files, choose, anypats = matchpats(repo, pats, opts)
2184 modified, added, removed, deleted, unknown = repo.changes(match=choose)
2210 modified, added, removed, deleted, unknown = repo.changes(match=choose)
2185 repo.forget(added)
2211 repo.forget(added)
2186 repo.undelete(removed + deleted)
2212 repo.undelete(removed)
2187
2213
2188 return repo.update(node, False, True, choose, False)
2214 return repo.update(node, False, True, choose, False)
2189
2215
@@ -2573,50 +2599,51 b' table = {'
2573 ('c', 'changeset', None, _('list the changeset')),
2599 ('c', 'changeset', None, _('list the changeset')),
2574 ('I', 'include', [], _('include names matching the given patterns')),
2600 ('I', 'include', [], _('include names matching the given patterns')),
2575 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2601 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2576 _('hg annotate [OPTION]... FILE...')),
2602 _('hg annotate [-r REV] [-a] [-u] [-d] [-n] [-c] FILE...')),
2577 "bundle":
2603 "bundle":
2578 (bundle,
2604 (bundle,
2579 [],
2605 [],
2580 _('hg bundle FILE DEST')),
2606 _('hg bundle FILE DEST')),
2581 "cat":
2607 "cat":
2582 (cat,
2608 (cat,
2583 [('I', 'include', [], _('include names matching the given patterns')),
2609 [('o', 'output', '', _('print output to file with formatted name')),
2584 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2610 ('r', 'rev', '', _('print the given revision')),
2585 ('o', 'output', '', _('print output to file with formatted name')),
2611 ('I', 'include', [], _('include names matching the given patterns')),
2586 ('r', 'rev', '', _('print the given revision'))],
2612 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2587 _('hg cat [OPTION]... FILE...')),
2613 _('hg cat [OPTION]... FILE...')),
2588 "^clone":
2614 "^clone":
2589 (clone,
2615 (clone,
2590 [('U', 'noupdate', None, _('do not update the new working directory')),
2616 [('U', 'noupdate', None, _('do not update the new working directory')),
2591 ('e', 'ssh', '', _('specify ssh command to use')),
2592 ('', 'pull', None, _('use pull protocol to copy metadata')),
2593 ('r', 'rev', [],
2617 ('r', 'rev', [],
2594 _('a changeset you would like to have after cloning')),
2618 _('a changeset you would like to have after cloning')),
2619 ('', 'pull', None, _('use pull protocol to copy metadata')),
2620 ('e', 'ssh', '', _('specify ssh command to use')),
2595 ('', 'remotecmd', '',
2621 ('', 'remotecmd', '',
2596 _('specify hg command to run on the remote side'))],
2622 _('specify hg command to run on the remote side'))],
2597 _('hg clone [OPTION]... SOURCE [DEST]')),
2623 _('hg clone [OPTION]... SOURCE [DEST]')),
2598 "^commit|ci":
2624 "^commit|ci":
2599 (commit,
2625 (commit,
2600 [('A', 'addremove', None, _('run addremove during commit')),
2626 [('A', 'addremove', None, _('run addremove during commit')),
2601 ('I', 'include', [], _('include names matching the given patterns')),
2602 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2603 ('m', 'message', '', _('use <text> as commit message')),
2627 ('m', 'message', '', _('use <text> as commit message')),
2604 ('l', 'logfile', '', _('read the commit message from <file>')),
2628 ('l', 'logfile', '', _('read the commit message from <file>')),
2605 ('d', 'date', '', _('record datecode as commit date')),
2629 ('d', 'date', '', _('record datecode as commit date')),
2606 ('u', 'user', '', _('record user as commiter'))],
2630 ('u', 'user', '', _('record user as commiter')),
2631 ('I', 'include', [], _('include names matching the given patterns')),
2632 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2607 _('hg commit [OPTION]... [FILE]...')),
2633 _('hg commit [OPTION]... [FILE]...')),
2608 "copy|cp":
2634 "copy|cp":
2609 (copy,
2635 (copy,
2610 [('I', 'include', [], _('include names matching the given patterns')),
2636 [('A', 'after', None, _('record a copy that has already occurred')),
2611 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2612 ('A', 'after', None, _('record a copy that has already occurred')),
2613 ('f', 'force', None,
2637 ('f', 'force', None,
2614 _('forcibly copy over an existing managed file'))],
2638 _('forcibly copy over an existing managed file')),
2639 ('I', 'include', [], _('include names matching the given patterns')),
2640 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2615 _('hg copy [OPTION]... [SOURCE]... DEST')),
2641 _('hg copy [OPTION]... [SOURCE]... DEST')),
2616 "debugancestor": (debugancestor, [], _('debugancestor INDEX REV1 REV2')),
2642 "debugancestor": (debugancestor, [], _('debugancestor INDEX REV1 REV2')),
2643 "debugcomplete": (debugcomplete, [], _('debugcomplete CMD')),
2617 "debugrebuildstate":
2644 "debugrebuildstate":
2618 (debugrebuildstate,
2645 (debugrebuildstate,
2619 [('r', 'rev', "", _("revision to rebuild to"))],
2646 [('r', 'rev', '', _('revision to rebuild to'))],
2620 _('debugrebuildstate [-r REV] [REV]')),
2647 _('debugrebuildstate [-r REV] [REV]')),
2621 "debugcheckstate": (debugcheckstate, [], _('debugcheckstate')),
2648 "debugcheckstate": (debugcheckstate, [], _('debugcheckstate')),
2622 "debugconfig": (debugconfig, [], _('debugconfig')),
2649 "debugconfig": (debugconfig, [], _('debugconfig')),
@@ -2635,20 +2662,19 b' table = {'
2635 (diff,
2662 (diff,
2636 [('r', 'rev', [], _('revision')),
2663 [('r', 'rev', [], _('revision')),
2637 ('a', 'text', None, _('treat all files as text')),
2664 ('a', 'text', None, _('treat all files as text')),
2638 ('I', 'include', [], _('include names matching the given patterns')),
2639 ('p', 'show-function', None,
2665 ('p', 'show-function', None,
2640 _('show which function each change is in')),
2666 _('show which function each change is in')),
2641 ('w', 'ignore-all-space', None,
2667 ('w', 'ignore-all-space', None,
2642 _('ignore white space when comparing lines')),
2668 _('ignore white space when comparing lines')),
2643 ('X', 'exclude', [],
2669 ('I', 'include', [], _('include names matching the given patterns')),
2644 _('exclude names matching the given patterns'))],
2670 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2645 _('hg diff [-a] [-I] [-X] [-r REV1 [-r REV2]] [FILE]...')),
2671 _('hg diff [-a] [-I] [-X] [-r REV1 [-r REV2]] [FILE]...')),
2646 "^export":
2672 "^export":
2647 (export,
2673 (export,
2648 [('o', 'output', '', _('print output to file with formatted name')),
2674 [('o', 'output', '', _('print output to file with formatted name')),
2649 ('a', 'text', None, _('treat all files as text')),
2675 ('a', 'text', None, _('treat all files as text')),
2650 ('', 'switch-parent', None, _('diff against the second parent'))],
2676 ('', 'switch-parent', None, _('diff against the second parent'))],
2651 _('hg export [-a] [-o OUTFILE] REV...')),
2677 _('hg export [-a] [-o OUTFILESPEC] REV...')),
2652 "forget":
2678 "forget":
2653 (forget,
2679 (forget,
2654 [('I', 'include', [], _('include names matching the given patterns')),
2680 [('I', 'include', [], _('include names matching the given patterns')),
@@ -2657,15 +2683,15 b' table = {'
2657 "grep":
2683 "grep":
2658 (grep,
2684 (grep,
2659 [('0', 'print0', None, _('end fields with NUL')),
2685 [('0', 'print0', None, _('end fields with NUL')),
2660 ('I', 'include', [], _('include names matching the given patterns')),
2661 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2662 ('', 'all', None, _('print all revisions that match')),
2686 ('', 'all', None, _('print all revisions that match')),
2663 ('i', 'ignore-case', None, _('ignore case when matching')),
2687 ('i', 'ignore-case', None, _('ignore case when matching')),
2664 ('l', 'files-with-matches', None,
2688 ('l', 'files-with-matches', None,
2665 _('print only filenames and revs that match')),
2689 _('print only filenames and revs that match')),
2666 ('n', 'line-number', None, _('print matching line numbers')),
2690 ('n', 'line-number', None, _('print matching line numbers')),
2667 ('r', 'rev', [], _('search in given revision range')),
2691 ('r', 'rev', [], _('search in given revision range')),
2668 ('u', 'user', None, _('print user who committed change'))],
2692 ('u', 'user', None, _('print user who committed change')),
2693 ('I', 'include', [], _('include names matching the given patterns')),
2694 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2669 _('hg grep [OPTION]... PATTERN [FILE]...')),
2695 _('hg grep [OPTION]... PATTERN [FILE]...')),
2670 "heads":
2696 "heads":
2671 (heads,
2697 (heads,
@@ -2681,10 +2707,10 b' table = {'
2681 [('p', 'strip', 1,
2707 [('p', 'strip', 1,
2682 _('directory strip option for patch. This has the same\n') +
2708 _('directory strip option for patch. This has the same\n') +
2683 _('meaning as the corresponding patch option')),
2709 _('meaning as the corresponding patch option')),
2710 ('b', 'base', '', _('base path')),
2684 ('f', 'force', None,
2711 ('f', 'force', None,
2685 _('skip check for outstanding uncommitted changes')),
2712 _('skip check for outstanding uncommitted changes'))],
2686 ('b', 'base', '', _('base path'))],
2713 _('hg import [-p NUM] [-b BASE] [-f] PATCH...')),
2687 _('hg import [-f] [-p NUM] [-b BASE] PATCH...')),
2688 "incoming|in": (incoming,
2714 "incoming|in": (incoming,
2689 [('M', 'no-merges', None, _('do not show merges')),
2715 [('M', 'no-merges', None, _('do not show merges')),
2690 ('', 'style', '', _('display using template map file')),
2716 ('', 'style', '', _('display using template map file')),
@@ -2705,9 +2731,7 b' table = {'
2705 _('hg locate [OPTION]... [PATTERN]...')),
2731 _('hg locate [OPTION]... [PATTERN]...')),
2706 "^log|history":
2732 "^log|history":
2707 (log,
2733 (log,
2708 [('I', 'include', [], _('include names matching the given patterns')),
2734 [('b', 'branches', None, _('show branches')),
2709 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2710 ('b', 'branches', None, _('show branches')),
2711 ('k', 'keyword', [], _('search for a keyword')),
2735 ('k', 'keyword', [], _('search for a keyword')),
2712 ('l', 'limit', '', _('limit number of changes displayed')),
2736 ('l', 'limit', '', _('limit number of changes displayed')),
2713 ('r', 'rev', [], _('show the specified revision or range')),
2737 ('r', 'rev', [], _('show the specified revision or range')),
@@ -2715,8 +2739,10 b' table = {'
2715 ('', 'style', '', _('display using template map file')),
2739 ('', 'style', '', _('display using template map file')),
2716 ('m', 'only-merges', None, _('show only merges')),
2740 ('m', 'only-merges', None, _('show only merges')),
2717 ('p', 'patch', None, _('show patch')),
2741 ('p', 'patch', None, _('show patch')),
2718 ('', 'template', '', _('display with template'))],
2742 ('', 'template', '', _('display with template')),
2719 _('hg log [-I] [-X] [-r REV]... [-p] [FILE]')),
2743 ('I', 'include', [], _('include names matching the given patterns')),
2744 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2745 _('hg log [OPTION]... [FILE]')),
2720 "manifest": (manifest, [], _('hg manifest [REV]')),
2746 "manifest": (manifest, [], _('hg manifest [REV]')),
2721 "outgoing|out": (outgoing,
2747 "outgoing|out": (outgoing,
2722 [('M', 'no-merges', None, _('do not show merges')),
2748 [('M', 'no-merges', None, _('do not show merges')),
@@ -2724,7 +2750,7 b' table = {'
2724 ('', 'style', '', _('display using template map file')),
2750 ('', 'style', '', _('display using template map file')),
2725 ('n', 'newest-first', None, _('show newest record first')),
2751 ('n', 'newest-first', None, _('show newest record first')),
2726 ('', 'template', '', _('display with template'))],
2752 ('', 'template', '', _('display with template'))],
2727 _('hg outgoing [-p] [-n] [-M] [DEST]')),
2753 _('hg outgoing [-M] [-p] [-n] [DEST]')),
2728 "^parents":
2754 "^parents":
2729 (parents,
2755 (parents,
2730 [('b', 'branches', None, _('show branches')),
2756 [('b', 'branches', None, _('show branches')),
@@ -2740,7 +2766,7 b' table = {'
2740 ('r', 'rev', [], _('a specific revision you would like to pull')),
2766 ('r', 'rev', [], _('a specific revision you would like to pull')),
2741 ('', 'remotecmd', '',
2767 ('', 'remotecmd', '',
2742 _('specify hg command to run on the remote side'))],
2768 _('specify hg command to run on the remote side'))],
2743 _('hg pull [-u] [-e FILE] [-r rev]... [--remotecmd FILE] [SOURCE]')),
2769 _('hg pull [-u] [-e FILE] [-r REV]... [--remotecmd FILE] [SOURCE]')),
2744 "^push":
2770 "^push":
2745 (push,
2771 (push,
2746 [('f', 'force', None, _('force push')),
2772 [('f', 'force', None, _('force push')),
@@ -2748,8 +2774,8 b' table = {'
2748 ('r', 'rev', [], _('a specific revision you would like to push')),
2774 ('r', 'rev', [], _('a specific revision you would like to push')),
2749 ('', 'remotecmd', '',
2775 ('', 'remotecmd', '',
2750 _('specify hg command to run on the remote side'))],
2776 _('specify hg command to run on the remote side'))],
2751 _('hg push [-f] [-e FILE] [-r rev]... [--remotecmd FILE] [DEST]')),
2777 _('hg push [-f] [-e FILE] [-r REV]... [--remotecmd FILE] [DEST]')),
2752 "rawcommit":
2778 "debugrawcommit|rawcommit":
2753 (rawcommit,
2779 (rawcommit,
2754 [('p', 'parent', [], _('parent')),
2780 [('p', 'parent', [], _('parent')),
2755 ('d', 'date', '', _('date code')),
2781 ('d', 'date', '', _('date code')),
@@ -2757,27 +2783,28 b' table = {'
2757 ('F', 'files', '', _('file list')),
2783 ('F', 'files', '', _('file list')),
2758 ('m', 'message', '', _('commit message')),
2784 ('m', 'message', '', _('commit message')),
2759 ('l', 'logfile', '', _('commit message file'))],
2785 ('l', 'logfile', '', _('commit message file'))],
2760 _('hg rawcommit [OPTION]... [FILE]...')),
2786 _('hg debugrawcommit [OPTION]... [FILE]...')),
2761 "recover": (recover, [], _('hg recover')),
2787 "recover": (recover, [], _('hg recover')),
2762 "^remove|rm":
2788 "^remove|rm":
2763 (remove,
2789 (remove,
2764 [('I', 'include', [], _('include names matching the given patterns')),
2790 [('f', 'force', None, _('remove file even if modified')),
2791 ('I', 'include', [], _('include names matching the given patterns')),
2765 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2792 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2766 _('hg remove [OPTION]... FILE...')),
2793 _('hg remove [OPTION]... FILE...')),
2767 "rename|mv":
2794 "rename|mv":
2768 (rename,
2795 (rename,
2769 [('I', 'include', [], _('include names matching the given patterns')),
2796 [('A', 'after', None, _('record a rename that has already occurred')),
2770 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2771 ('A', 'after', None, _('record a rename that has already occurred')),
2772 ('f', 'force', None,
2797 ('f', 'force', None,
2773 _('forcibly copy over an existing managed file'))],
2798 _('forcibly copy over an existing managed file')),
2774 _('hg rename [OPTION]... [SOURCE]... DEST')),
2799 ('I', 'include', [], _('include names matching the given patterns')),
2800 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2801 _('hg rename [OPTION]... SOURCE... DEST')),
2775 "^revert":
2802 "^revert":
2776 (revert,
2803 (revert,
2777 [('I', 'include', [], _('include names matching the given patterns')),
2804 [('r', 'rev', '', _('revision to revert to')),
2778 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2805 ('I', 'include', [], _('include names matching the given patterns')),
2779 ('r', 'rev', '', _('revision to revert to'))],
2806 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2780 _('hg revert [-n] [-r REV] [NAME]...')),
2807 _('hg revert [-r REV] [NAME]...')),
2781 "root": (root, [], _('hg root')),
2808 "root": (root, [], _('hg root')),
2782 "^serve":
2809 "^serve":
2783 (serve,
2810 (serve,
@@ -2791,7 +2818,7 b' table = {'
2791 _('name to show in web pages (default: working dir)')),
2818 _('name to show in web pages (default: working dir)')),
2792 ('', 'pid-file', '', _('name of file to write process ID to')),
2819 ('', 'pid-file', '', _('name of file to write process ID to')),
2793 ('', 'stdio', None, _('for remote clients')),
2820 ('', 'stdio', None, _('for remote clients')),
2794 ('', 'templates', '', _('web templates to use')),
2821 ('t', 'templates', '', _('web templates to use')),
2795 ('', 'style', '', _('template style to use')),
2822 ('', 'style', '', _('template style to use')),
2796 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4'))],
2823 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4'))],
2797 _('hg serve [OPTION]...')),
2824 _('hg serve [OPTION]...')),
@@ -2815,7 +2842,7 b' table = {'
2815 ('d', 'date', '', _('record datecode as commit date')),
2842 ('d', 'date', '', _('record datecode as commit date')),
2816 ('u', 'user', '', _('record user as commiter')),
2843 ('u', 'user', '', _('record user as commiter')),
2817 ('r', 'rev', '', _('revision to tag'))],
2844 ('r', 'rev', '', _('revision to tag'))],
2818 _('hg tag [-r REV] [OPTION]... NAME')),
2845 _('hg tag [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME')),
2819 "tags": (tags, [], _('hg tags')),
2846 "tags": (tags, [], _('hg tags')),
2820 "tip":
2847 "tip":
2821 (tip,
2848 (tip,
@@ -2823,7 +2850,7 b' table = {'
2823 ('', 'style', '', _('display using template map file')),
2850 ('', 'style', '', _('display using template map file')),
2824 ('p', 'patch', None, _('show patch')),
2851 ('p', 'patch', None, _('show patch')),
2825 ('', 'template', '', _('display with template'))],
2852 ('', 'template', '', _('display with template'))],
2826 _('hg [-b] [-p] tip')),
2853 _('hg tip [-b] [-p]')),
2827 "unbundle":
2854 "unbundle":
2828 (unbundle,
2855 (unbundle,
2829 [('u', 'update', None,
2856 [('u', 'update', None,
@@ -2844,7 +2871,8 b' table = {'
2844 }
2871 }
2845
2872
2846 globalopts = [
2873 globalopts = [
2847 ('R', 'repository', '', _('repository root directory')),
2874 ('R', 'repository', '',
2875 _('repository root directory or symbolic path name')),
2848 ('', 'cwd', '', _('change working directory')),
2876 ('', 'cwd', '', _('change working directory')),
2849 ('y', 'noninteractive', None,
2877 ('y', 'noninteractive', None,
2850 _('do not prompt, assume \'yes\' for any required answers')),
2878 _('do not prompt, assume \'yes\' for any required answers')),
@@ -2859,28 +2887,49 b' globalopts = ['
2859 ('h', 'help', None, _('display help and exit')),
2887 ('h', 'help', None, _('display help and exit')),
2860 ]
2888 ]
2861
2889
2862 norepo = ("clone init version help debugancestor debugconfig debugdata"
2890 norepo = ("clone init version help debugancestor debugcomplete debugdata"
2863 " debugindex debugindexdot paths")
2891 " debugindex debugindexdot")
2864
2892 optionalrepo = ("paths debugconfig")
2865 def find(cmd):
2893
2866 """Return (aliases, command table entry) for command string."""
2894 def findpossible(cmd):
2867 choice = None
2895 """
2868 count = 0
2896 Return cmd -> (aliases, command table entry)
2897 for each matching command
2898 """
2899 choice = {}
2900 debugchoice = {}
2869 for e in table.keys():
2901 for e in table.keys():
2870 aliases = e.lstrip("^").split("|")
2902 aliases = e.lstrip("^").split("|")
2871 if cmd in aliases:
2903 if cmd in aliases:
2872 return aliases, table[e]
2904 choice[cmd] = (aliases, table[e])
2905 continue
2873 for a in aliases:
2906 for a in aliases:
2874 if a.startswith(cmd):
2907 if a.startswith(cmd):
2875 count += 1
2908 if aliases[0].startswith("debug"):
2876 choice = aliases, table[e]
2909 debugchoice[a] = (aliases, table[e])
2910 else:
2911 choice[a] = (aliases, table[e])
2877 break
2912 break
2878
2913
2879 if count > 1:
2914 if not choice and debugchoice:
2880 raise AmbiguousCommand(cmd)
2915 choice = debugchoice
2916
2917 return choice
2918
2919 def find(cmd):
2920 """Return (aliases, command table entry) for command string."""
2921 choice = findpossible(cmd)
2922
2923 if choice.has_key(cmd):
2924 return choice[cmd]
2925
2926 if len(choice) > 1:
2927 clist = choice.keys()
2928 clist.sort()
2929 raise AmbiguousCommand(cmd, clist)
2881
2930
2882 if choice:
2931 if choice:
2883 return choice
2932 return choice.values()[0]
2884
2933
2885 raise UnknownCommand(cmd)
2934 raise UnknownCommand(cmd)
2886
2935
@@ -2968,6 +3017,9 b' def dispatch(args):'
2968 mod = getattr(mod, comp)
3017 mod = getattr(mod, comp)
2969 return mod
3018 return mod
2970 try:
3019 try:
3020 try:
3021 mod = importh("hgext." + x[0])
3022 except ImportError:
2971 mod = importh(x[0])
3023 mod = importh(x[0])
2972 except Exception, inst:
3024 except Exception, inst:
2973 on_exception(Exception, inst)
3025 on_exception(Exception, inst)
@@ -2983,22 +3035,6 b' def dispatch(args):'
2983
3035
2984 try:
3036 try:
2985 cmd, func, args, options, cmdoptions = parse(u, args)
3037 cmd, func, args, options, cmdoptions = parse(u, args)
2986 except ParseError, inst:
2987 if inst.args[0]:
2988 u.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
2989 help_(u, inst.args[0])
2990 else:
2991 u.warn(_("hg: %s\n") % inst.args[1])
2992 help_(u, 'shortlist')
2993 sys.exit(-1)
2994 except AmbiguousCommand, inst:
2995 u.warn(_("hg: command '%s' is ambiguous.\n") % inst.args[0])
2996 sys.exit(1)
2997 except UnknownCommand, inst:
2998 u.warn(_("hg: unknown command '%s'\n") % inst.args[0])
2999 help_(u, 'shortlist')
3000 sys.exit(1)
3001
3002 if options["time"]:
3038 if options["time"]:
3003 def get_times():
3039 def get_times():
3004 t = os.times()
3040 t = os.times()
@@ -3020,7 +3056,16 b' def dispatch(args):'
3020 pdb.set_trace()
3056 pdb.set_trace()
3021
3057
3022 try:
3058 try:
3059 if options['cwd']:
3023 try:
3060 try:
3061 os.chdir(options['cwd'])
3062 except OSError, inst:
3063 raise util.Abort('%s: %s' %
3064 (options['cwd'], inst.strerror))
3065
3066 path = u.expandpath(options["repository"]) or ""
3067 repo = path and hg.repository(u, path=path) or None
3068
3024 if options['help']:
3069 if options['help']:
3025 help_(u, cmd, options['version'])
3070 help_(u, cmd, options['version'])
3026 sys.exit(0)
3071 sys.exit(0)
@@ -3031,35 +3076,45 b' def dispatch(args):'
3031 help_(u, 'shortlist')
3076 help_(u, 'shortlist')
3032 sys.exit(0)
3077 sys.exit(0)
3033
3078
3034 if options['cwd']:
3079 if cmd not in norepo.split():
3035 try:
3080 try:
3036 os.chdir(options['cwd'])
3081 if not repo:
3037 except OSError, inst:
3082 repo = hg.repository(u, path=path)
3038 raise util.Abort('%s: %s' %
3083 u = repo.ui
3039 (options['cwd'], inst.strerror))
3040
3041 if cmd not in norepo.split():
3042 path = options["repository"] or ""
3043 repo = hg.repository(ui=u, path=path)
3044 for x in external:
3084 for x in external:
3045 if hasattr(x, 'reposetup'):
3085 if hasattr(x, 'reposetup'):
3046 x.reposetup(u, repo)
3086 x.reposetup(u, repo)
3087 except hg.RepoError:
3088 if cmd not in optionalrepo.split():
3089 raise
3047 d = lambda: func(u, repo, *args, **cmdoptions)
3090 d = lambda: func(u, repo, *args, **cmdoptions)
3048 else:
3091 else:
3049 d = lambda: func(u, *args, **cmdoptions)
3092 d = lambda: func(u, *args, **cmdoptions)
3050
3093
3094 try:
3051 if options['profile']:
3095 if options['profile']:
3052 import hotshot, hotshot.stats
3096 import hotshot, hotshot.stats
3053 prof = hotshot.Profile("hg.prof")
3097 prof = hotshot.Profile("hg.prof")
3054 r = prof.runcall(d)
3098 try:
3099 try:
3100 return prof.runcall(d)
3101 except:
3102 try:
3103 u.warn(_('exception raised - generating '
3104 'profile anyway\n'))
3105 except:
3106 pass
3107 raise
3108 finally:
3055 prof.close()
3109 prof.close()
3056 stats = hotshot.stats.load("hg.prof")
3110 stats = hotshot.stats.load("hg.prof")
3057 stats.strip_dirs()
3111 stats.strip_dirs()
3058 stats.sort_stats('time', 'calls')
3112 stats.sort_stats('time', 'calls')
3059 stats.print_stats(40)
3113 stats.print_stats(40)
3060 return r
3061 else:
3114 else:
3062 return d()
3115 return d()
3116 finally:
3117 u.flush()
3063 except:
3118 except:
3064 # enter the debugger when we hit an exception
3119 # enter the debugger when we hit an exception
3065 if options['debugger']:
3120 if options['debugger']:
@@ -3067,6 +3122,22 b' def dispatch(args):'
3067 if options['traceback']:
3122 if options['traceback']:
3068 traceback.print_exc()
3123 traceback.print_exc()
3069 raise
3124 raise
3125 except ParseError, inst:
3126 if inst.args[0]:
3127 u.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
3128 help_(u, inst.args[0])
3129 else:
3130 u.warn(_("hg: %s\n") % inst.args[1])
3131 help_(u, 'shortlist')
3132 sys.exit(-1)
3133 except AmbiguousCommand, inst:
3134 u.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
3135 (inst.args[0], " ".join(inst.args[1])))
3136 sys.exit(1)
3137 except UnknownCommand, inst:
3138 u.warn(_("hg: unknown command '%s'\n") % inst.args[0])
3139 help_(u, 'shortlist')
3140 sys.exit(1)
3070 except hg.RepoError, inst:
3141 except hg.RepoError, inst:
3071 u.warn(_("abort: "), inst, "!\n")
3142 u.warn(_("abort: "), inst, "!\n")
3072 except revlog.RevlogError, inst:
3143 except revlog.RevlogError, inst:
@@ -3113,12 +3184,6 b' def dispatch(args):'
3113 u.debug(inst, "\n")
3184 u.debug(inst, "\n")
3114 u.warn(_("%s: invalid arguments\n") % cmd)
3185 u.warn(_("%s: invalid arguments\n") % cmd)
3115 help_(u, cmd)
3186 help_(u, cmd)
3116 except AmbiguousCommand, inst:
3117 u.warn(_("hg: command '%s' is ambiguous.\n") % inst.args[0])
3118 help_(u, 'shortlist')
3119 except UnknownCommand, inst:
3120 u.warn(_("hg: unknown command '%s'\n") % inst.args[0])
3121 help_(u, 'shortlist')
3122 except SystemExit:
3187 except SystemExit:
3123 # don't catch this in the catch-all below
3188 # don't catch this in the catch-all below
3124 raise
3189 raise
@@ -1,15 +1,125 b''
1 def demandload(scope, modules):
1 '''Demand load modules when used, not when imported.'''
2 class d:
2
3 def __getattr__(self, name):
3 __author__ = '''Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>.
4 mod = self.__dict__["mod"]
4 This software may be used and distributed according to the terms
5 scope = self.__dict__["scope"]
5 of the GNU General Public License, incorporated herein by reference.'''
6 scope[mod] = __import__(mod, scope, scope, [])
6
7 return getattr(scope[mod], name)
7 # this is based on matt's original demandload module. it is a
8 # complete rewrite. some time, we may need to support syntax of
9 # "import foo as bar".
10
11 class _importer(object):
12 '''import a module. it is not imported until needed, and is
13 imported at most once per scope.'''
14
15 def __init__(self, scope, modname, fromlist):
16 '''scope is context (globals() or locals()) in which import
17 should be made. modname is name of module to import.
18 fromlist is list of modules for "from foo import ..."
19 emulation.'''
20
21 self.scope = scope
22 self.modname = modname
23 self.fromlist = fromlist
24 self.mod = None
25
26 def module(self):
27 '''import the module if needed, and return.'''
28 if self.mod is None:
29 self.mod = __import__(self.modname, self.scope, self.scope,
30 self.fromlist)
31 del self.modname, self.fromlist
32 return self.mod
33
34 class _replacer(object):
35 '''placeholder for a demand loaded module. demandload puts this in
36 a target scope. when an attribute of this object is looked up,
37 this object is replaced in the target scope with the actual
38 module.
39
40 we use __getattribute__ to avoid namespace clashes between
41 placeholder object and real module.'''
42
43 def __init__(self, importer, target):
44 self.importer = importer
45 self.target = target
46 # consider case where we do this:
47 # demandload(globals(), 'foo.bar foo.quux')
48 # foo will already exist in target scope when we get to
49 # foo.quux. so we remember that we will need to demandload
50 # quux into foo's scope when we really load it.
51 self.later = []
52
53 def module(self):
54 return object.__getattribute__(self, 'importer').module()
55
56 def __getattribute__(self, key):
57 '''look up an attribute in a module and return it. replace the
58 name of the module in the caller\'s dict with the actual
59 module.'''
8
60
9 for m in modules.split():
61 module = object.__getattribute__(self, 'module')()
10 dl = d()
62 target = object.__getattribute__(self, 'target')
11 dl.mod = m
63 importer = object.__getattribute__(self, 'importer')
12 dl.scope = scope
64 later = object.__getattribute__(self, 'later')
13 scope[m] = dl
65
66 if later:
67 demandload(module.__dict__, ' '.join(later))
68
69 importer.scope[target] = module
70
71 return getattr(module, key)
72
73 class _replacer_from(_replacer):
74 '''placeholder for a demand loaded module. used for "from foo
75 import ..." emulation. semantics of this are different than
76 regular import, so different implementation needed.'''
77
78 def module(self):
79 importer = object.__getattribute__(self, 'importer')
80 target = object.__getattribute__(self, 'target')
81
82 return getattr(importer.module(), target)
83
84 def demandload(scope, modules):
85 '''import modules into scope when each is first used.
86
87 scope should be the value of globals() in the module calling this
88 function, or locals() in the calling function.
89
90 modules is a string listing module names, separated by white
91 space. names are handled like this:
14
92
93 foo import foo
94 foo bar import foo, bar
95 foo.bar import foo.bar
96 foo:bar from foo import bar
97 foo:bar,quux from foo import bar, quux
98 foo.bar:quux from foo.bar import quux'''
15
99
100 for mod in modules.split():
101 col = mod.find(':')
102 if col >= 0:
103 fromlist = mod[col+1:].split(',')
104 mod = mod[:col]
105 else:
106 fromlist = []
107 importer = _importer(scope, mod, fromlist)
108 if fromlist:
109 for name in fromlist:
110 scope[name] = _replacer_from(importer, name)
111 else:
112 dot = mod.find('.')
113 if dot >= 0:
114 basemod = mod[:dot]
115 val = scope.get(basemod)
116 # if base module has already been demandload()ed,
117 # remember to load this submodule into its namespace
118 # when needed.
119 if isinstance(val, _replacer):
120 later = object.__getattribute__(val, 'later')
121 later.append(mod[dot+1:])
122 continue
123 else:
124 basemod = mod
125 scope[basemod] = _replacer(importer, basemod)
@@ -34,30 +34,27 b' def get_mtime(repo_path):'
34 return os.stat(hg_path).st_mtime
34 return os.stat(hg_path).st_mtime
35
35
36 def staticfile(directory, fname):
36 def staticfile(directory, fname):
37 fname = os.path.realpath(os.path.join(directory, fname))
37 """return a file inside directory with guessed content-type header
38
39 fname always uses '/' as directory separator and isn't allowed to
40 contain unusual path components.
41 Content-type is guessed using the mimetypes module.
42 Return an empty string if fname is illegal or file not found.
38
43
44 """
45 parts = fname.split('/')
46 path = directory
47 for part in parts:
48 if (part in ('', os.curdir, os.pardir) or
49 os.sep in part or os.altsep is not None and os.altsep in part):
50 return ""
51 path = os.path.join(path, part)
39 try:
52 try:
40 # the static dir should be a substring in the real
53 os.stat(path)
41 # file path, if it is not, we have something strange
54 ct = mimetypes.guess_type(path)[0] or "text/plain"
42 # going on => security breach attempt?
55 return "Content-type: %s\n\n%s" % (ct, file(path).read())
43 #
56 except (TypeError, OSError):
44 # This will either:
57 # illegal fname or unreadable file
45 # 1) find the `static' path at index 0 = success
46 # 2) find the `static' path at other index = error
47 # 3) not find the `static' path = ValueError generated
48 if fname.index(directory) != 0:
49 # generate ValueError manually
50 raise ValueError()
51
52 os.stat(fname)
53
54 ct = mimetypes.guess_type(fname)[0] or "text/plain"
55 return "Content-type: %s\n\n%s" % (ct, file(fname).read())
56 except ValueError:
57 # security breach attempt
58 return ""
59 except OSError, e:
60 if e.errno == errno.ENOENT:
61 return ""
58 return ""
62
59
63 class hgrequest(object):
60 class hgrequest(object):
@@ -739,7 +736,7 b' class hgweb(object):'
739
736
740 def run(self, req=hgrequest()):
737 def run(self, req=hgrequest()):
741 def clean(path):
738 def clean(path):
742 p = os.path.normpath(path)
739 p = util.normpath(path)
743 if p[:2] == "..":
740 if p[:2] == "..":
744 raise "suspicious path"
741 raise "suspicious path"
745 return p
742 return p
@@ -1001,17 +998,27 b' def create_server(repo):'
1001 class hgwebdir(object):
998 class hgwebdir(object):
1002 def __init__(self, config):
999 def __init__(self, config):
1003 def cleannames(items):
1000 def cleannames(items):
1004 return [(name.strip('/'), path) for name, path in items]
1001 return [(name.strip(os.sep), path) for name, path in items]
1005
1002
1006 if type(config) == type([]):
1003 if isinstance(config, (list, tuple)):
1007 self.repos = cleannames(config)
1004 self.repos = cleannames(config)
1008 elif type(config) == type({}):
1005 elif isinstance(config, dict):
1009 self.repos = cleannames(config.items())
1006 self.repos = cleannames(config.items())
1010 self.repos.sort()
1007 self.repos.sort()
1011 else:
1008 else:
1012 cp = ConfigParser.SafeConfigParser()
1009 cp = ConfigParser.SafeConfigParser()
1013 cp.read(config)
1010 cp.read(config)
1014 self.repos = cleannames(cp.items("paths"))
1011 self.repos = []
1012 if cp.has_section('paths'):
1013 self.repos.extend(cleannames(cp.items('paths')))
1014 if cp.has_section('collections'):
1015 for prefix, root in cp.items('collections'):
1016 for path in util.walkrepos(root):
1017 repo = os.path.normpath(path)
1018 name = repo
1019 if name.startswith(prefix):
1020 name = name[len(prefix):]
1021 self.repos.append((name.lstrip(os.sep), repo))
1015 self.repos.sort()
1022 self.repos.sort()
1016
1023
1017 def run(self, req=hgrequest()):
1024 def run(self, req=hgrequest()):
@@ -67,6 +67,9 b' class httprepository(remoterepository):'
67 def dev(self):
67 def dev(self):
68 return -1
68 return -1
69
69
70 def lock(self):
71 raise util.Abort(_('operation not supported over http'))
72
70 def do_cmd(self, cmd, **args):
73 def do_cmd(self, cmd, **args):
71 self.ui.debug(_("sending %s command\n") % cmd)
74 self.ui.debug(_("sending %s command\n") % cmd)
72 q = {"cmd": cmd}
75 q = {"cmd": cmd}
@@ -10,10 +10,12 b' import filelog, manifest, changelog, dir'
10 from node import *
10 from node import *
11 from i18n import gettext as _
11 from i18n import gettext as _
12 from demandload import *
12 from demandload import *
13 demandload(globals(), "re lock transaction tempfile stat mdiff errno")
13 demandload(globals(), "re lock transaction tempfile stat mdiff errno ui")
14
14
15 class localrepository(object):
15 class localrepository(object):
16 def __init__(self, ui, path=None, create=0):
16 def __del__(self):
17 self.transhandle = None
18 def __init__(self, parentui, path=None, create=0):
17 if not path:
19 if not path:
18 p = os.getcwd()
20 p = os.getcwd()
19 while not os.path.isdir(os.path.join(p, ".hg")):
21 while not os.path.isdir(os.path.join(p, ".hg")):
@@ -28,7 +30,7 b' class localrepository(object):'
28 raise repo.RepoError(_("repository %s not found") % path)
30 raise repo.RepoError(_("repository %s not found") % path)
29
31
30 self.root = os.path.abspath(path)
32 self.root = os.path.abspath(path)
31 self.ui = ui
33 self.ui = ui.ui(parentui=parentui)
32 self.opener = util.opener(self.path)
34 self.opener = util.opener(self.path)
33 self.wopener = util.opener(self.root)
35 self.wopener = util.opener(self.root)
34 self.manifest = manifest.manifest(self.opener)
36 self.manifest = manifest.manifest(self.opener)
@@ -37,42 +39,23 b' class localrepository(object):'
37 self.nodetagscache = None
39 self.nodetagscache = None
38 self.encodepats = None
40 self.encodepats = None
39 self.decodepats = None
41 self.decodepats = None
42 self.transhandle = None
40
43
41 if create:
44 if create:
42 os.mkdir(self.path)
45 os.mkdir(self.path)
43 os.mkdir(self.join("data"))
46 os.mkdir(self.join("data"))
44
47
45 self.dirstate = dirstate.dirstate(self.opener, ui, self.root)
48 self.dirstate = dirstate.dirstate(self.opener, self.ui, self.root)
46 try:
49 try:
47 self.ui.readconfig(self.join("hgrc"))
50 self.ui.readconfig(self.join("hgrc"), self.root)
48 except IOError:
51 except IOError:
49 pass
52 pass
50
53
51 def hook(self, name, throw=False, **args):
54 def hook(self, name, throw=False, **args):
52 def runhook(name, cmd):
55 def runhook(name, cmd):
53 self.ui.note(_("running hook %s: %s\n") % (name, cmd))
56 self.ui.note(_("running hook %s: %s\n") % (name, cmd))
54 old = {}
57 env = dict([('HG_' + k.upper(), v) for k, v in args.iteritems()])
55 for k, v in args.items():
58 r = util.system(cmd, environ=env, cwd=self.root)
56 k = k.upper()
57 old['HG_' + k] = os.environ.get(k, None)
58 old[k] = os.environ.get(k, None)
59 os.environ['HG_' + k] = str(v)
60 os.environ[k] = str(v)
61
62 try:
63 # Hooks run in the repository root
64 olddir = os.getcwd()
65 os.chdir(self.root)
66 r = os.system(cmd)
67 finally:
68 for k, v in old.items():
69 if v is not None:
70 os.environ[k] = v
71 else:
72 del os.environ[k]
73
74 os.chdir(olddir)
75
76 if r:
59 if r:
77 desc, r = util.explain_exit(r)
60 desc, r = util.explain_exit(r)
78 if throw:
61 if throw:
@@ -82,9 +65,10 b' class localrepository(object):'
82 return True
65 return True
83
66
84 r = True
67 r = True
85 for hname, cmd in self.ui.configitems("hooks"):
68 hooks = [(hname, cmd) for hname, cmd in self.ui.configitems("hooks")
86 s = hname.split(".")
69 if hname.split(".", 1)[0] == name and cmd]
87 if s[0] == name and cmd:
70 hooks.sort()
71 for hname, cmd in hooks:
88 r = runhook(hname, cmd) and r
72 r = runhook(hname, cmd) and r
89 return r
73 return r
90
74
@@ -215,6 +199,10 b' class localrepository(object):'
215 return self.wopener(filename, 'w').write(data)
199 return self.wopener(filename, 'w').write(data)
216
200
217 def transaction(self):
201 def transaction(self):
202 tr = self.transhandle
203 if tr != None and tr.running():
204 return tr.nest()
205
218 # save dirstate for undo
206 # save dirstate for undo
219 try:
207 try:
220 ds = self.opener("dirstate").read()
208 ds = self.opener("dirstate").read()
@@ -222,13 +210,11 b' class localrepository(object):'
222 ds = ""
210 ds = ""
223 self.opener("journal.dirstate", "w").write(ds)
211 self.opener("journal.dirstate", "w").write(ds)
224
212
225 def after():
213 tr = transaction.transaction(self.ui.warn, self.opener,
226 util.rename(self.join("journal"), self.join("undo"))
214 self.join("journal"),
227 util.rename(self.join("journal.dirstate"),
215 aftertrans(self.path))
228 self.join("undo.dirstate"))
216 self.transhandle = tr
229
217 return tr
230 return transaction.transaction(self.ui.warn, self.opener,
231 self.join("journal"), after)
232
218
233 def recover(self):
219 def recover(self):
234 l = self.lock()
220 l = self.lock()
@@ -366,7 +352,7 b' class localrepository(object):'
366 self.dirstate.setparents(n, nullid)
352 self.dirstate.setparents(n, nullid)
367
353
368 def commit(self, files=None, text="", user=None, date=None,
354 def commit(self, files=None, text="", user=None, date=None,
369 match=util.always, force=False, wlock=None):
355 match=util.always, force=False, lock=None, wlock=None):
370 commit = []
356 commit = []
371 remove = []
357 remove = []
372 changed = []
358 changed = []
@@ -404,7 +390,8 b' class localrepository(object):'
404
390
405 if not wlock:
391 if not wlock:
406 wlock = self.wlock()
392 wlock = self.wlock()
407 l = self.lock()
393 if not lock:
394 lock = self.lock()
408 tr = self.transaction()
395 tr = self.transaction()
409
396
410 # check in files
397 # check in files
@@ -519,6 +506,12 b' class localrepository(object):'
519 del mf[fn]
506 del mf[fn]
520 return mf
507 return mf
521
508
509 if node1:
510 # read the manifest from node1 before the manifest from node2,
511 # so that we'll hit the manifest cache if we're going through
512 # all the revisions in parent->child order.
513 mf1 = mfmatches(node1)
514
522 # are we comparing the working directory?
515 # are we comparing the working directory?
523 if not node2:
516 if not node2:
524 if not wlock:
517 if not wlock:
@@ -557,8 +550,6 b' class localrepository(object):'
557 # flush lists from dirstate before comparing manifests
550 # flush lists from dirstate before comparing manifests
558 modified, added = [], []
551 modified, added = [], []
559
552
560 mf1 = mfmatches(node1)
561
562 for fn in mf2:
553 for fn in mf2:
563 if mf1.has_key(fn):
554 if mf1.has_key(fn):
564 if mf1[fn] != mf2[fn] and (mf2[fn] != "" or fcmp(fn, mf1)):
555 if mf1[fn] != mf2[fn] and (mf2[fn] != "" or fcmp(fn, mf1)):
@@ -818,7 +809,7 b' class localrepository(object):'
818 base[h] = 1
809 base[h] = 1
819
810
820 if not unknown:
811 if not unknown:
821 return None
812 return []
822
813
823 rep = {}
814 rep = {}
824 reqcnt = 0
815 reqcnt = 0
@@ -1645,10 +1636,12 b' class localrepository(object):'
1645 # merge the tricky bits
1636 # merge the tricky bits
1646 files = merge.keys()
1637 files = merge.keys()
1647 files.sort()
1638 files.sort()
1639 xp1 = hex(p1)
1640 xp2 = hex(p2)
1648 for f in files:
1641 for f in files:
1649 self.ui.status(_("merging %s\n") % f)
1642 self.ui.status(_("merging %s\n") % f)
1650 my, other, flag = merge[f]
1643 my, other, flag = merge[f]
1651 ret = self.merge3(f, my, other)
1644 ret = self.merge3(f, my, other, xp1, xp2)
1652 if ret:
1645 if ret:
1653 err = True
1646 err = True
1654 util.set_exec(self.wjoin(f), flag)
1647 util.set_exec(self.wjoin(f), flag)
@@ -1669,6 +1662,7 b' class localrepository(object):'
1669 remove.sort()
1662 remove.sort()
1670 for f in remove:
1663 for f in remove:
1671 self.ui.note(_("removing %s\n") % f)
1664 self.ui.note(_("removing %s\n") % f)
1665 util.audit_path(f)
1672 try:
1666 try:
1673 util.unlink(self.wjoin(f))
1667 util.unlink(self.wjoin(f))
1674 except OSError, inst:
1668 except OSError, inst:
@@ -1685,7 +1679,7 b' class localrepository(object):'
1685 self.dirstate.setparents(p1, p2)
1679 self.dirstate.setparents(p1, p2)
1686 return err
1680 return err
1687
1681
1688 def merge3(self, fn, my, other):
1682 def merge3(self, fn, my, other, p1, p2):
1689 """perform a 3-way merge in the working directory"""
1683 """perform a 3-way merge in the working directory"""
1690
1684
1691 def temp(prefix, node):
1685 def temp(prefix, node):
@@ -1708,7 +1702,13 b' class localrepository(object):'
1708
1702
1709 cmd = (os.environ.get("HGMERGE") or self.ui.config("ui", "merge")
1703 cmd = (os.environ.get("HGMERGE") or self.ui.config("ui", "merge")
1710 or "hgmerge")
1704 or "hgmerge")
1711 r = os.system('%s "%s" "%s" "%s"' % (cmd, a, b, c))
1705 r = util.system('%s "%s" "%s" "%s"' % (cmd, a, b, c), cwd=self.root,
1706 environ={'HG_FILE': fn,
1707 'HG_MY_NODE': p1,
1708 'HG_OTHER_NODE': p2,
1709 'HG_FILE_MY_NODE': hex(my),
1710 'HG_FILE_OTHER_NODE': hex(other),
1711 'HG_FILE_BASE_NODE': hex(base)})
1712 if r:
1712 if r:
1713 self.ui.warn(_("merging %s failed!\n") % fn)
1713 self.ui.warn(_("merging %s failed!\n") % fn)
1714
1714
@@ -1759,6 +1759,7 b' class localrepository(object):'
1759 raise
1759 raise
1760 except Exception, inst:
1760 except Exception, inst:
1761 err(_("unpacking changeset %s: %s") % (short(n), inst))
1761 err(_("unpacking changeset %s: %s") % (short(n), inst))
1762 continue
1762
1763
1763 neededmanifests[changes[0]] = n
1764 neededmanifests[changes[0]] = n
1764
1765
@@ -1796,10 +1797,14 b' class localrepository(object):'
1796 raise
1797 raise
1797 except Exception, inst:
1798 except Exception, inst:
1798 err(_("unpacking manifest %s: %s") % (short(n), inst))
1799 err(_("unpacking manifest %s: %s") % (short(n), inst))
1800 continue
1799
1801
1802 try:
1800 ff = [ l.split('\0') for l in delta.splitlines() ]
1803 ff = [ l.split('\0') for l in delta.splitlines() ]
1801 for f, fn in ff:
1804 for f, fn in ff:
1802 filenodes.setdefault(f, {})[bin(fn[:40])] = 1
1805 filenodes.setdefault(f, {})[bin(fn[:40])] = 1
1806 except (ValueError, TypeError), inst:
1807 err(_("broken delta in manifest %s: %s") % (short(n), inst))
1803
1808
1804 self.ui.status(_("crosschecking files in changesets and manifests\n"))
1809 self.ui.status(_("crosschecking files in changesets and manifests\n"))
1805
1810
@@ -1823,6 +1828,9 b' class localrepository(object):'
1823 if f == "/dev/null":
1828 if f == "/dev/null":
1824 continue
1829 continue
1825 files += 1
1830 files += 1
1831 if not f:
1832 err(_("file without name in manifest %s") % short(n))
1833 continue
1826 fl = self.file(f)
1834 fl = self.file(f)
1827 checksize(fl, f)
1835 checksize(fl, f)
1828
1836
@@ -1840,7 +1848,7 b' class localrepository(object):'
1840 del filenodes[f][n]
1848 del filenodes[f][n]
1841
1849
1842 flr = fl.linkrev(n)
1850 flr = fl.linkrev(n)
1843 if flr not in filelinkrevs[f]:
1851 if flr not in filelinkrevs.get(f, []):
1844 err(_("%s:%s points to unexpected changeset %d")
1852 err(_("%s:%s points to unexpected changeset %d")
1845 % (f, short(n), flr))
1853 % (f, short(n), flr))
1846 else:
1854 else:
@@ -1875,3 +1883,13 b' class localrepository(object):'
1875 if errors[0]:
1883 if errors[0]:
1876 self.ui.warn(_("%d integrity errors encountered!\n") % errors[0])
1884 self.ui.warn(_("%d integrity errors encountered!\n") % errors[0])
1877 return 1
1885 return 1
1886
1887 # used to avoid circular references so destructors work
1888 def aftertrans(base):
1889 p = base
1890 def a():
1891 util.rename(os.path.join(p, "journal"), os.path.join(p, "undo"))
1892 util.rename(os.path.join(p, "journal.dirstate"),
1893 os.path.join(p, "undo.dirstate"))
1894 return a
1895
@@ -5,8 +5,8 b''
5 # This software may be used and distributed according to the terms
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 import errno, os, time
8 from demandload import *
9 import util
9 demandload(globals(), 'errno os socket time util')
10
10
11 class LockException(Exception):
11 class LockException(Exception):
12 pass
12 pass
@@ -16,11 +16,22 b' class LockUnavailable(LockException):'
16 pass
16 pass
17
17
18 class lock(object):
18 class lock(object):
19 # lock is symlink on platforms that support it, file on others.
20
21 # symlink is used because create of directory entry and contents
22 # are atomic even over nfs.
23
24 # old-style lock: symlink to pid
25 # new-style lock: symlink to hostname:pid
26
19 def __init__(self, file, timeout=-1, releasefn=None):
27 def __init__(self, file, timeout=-1, releasefn=None):
20 self.f = file
28 self.f = file
21 self.held = 0
29 self.held = 0
22 self.timeout = timeout
30 self.timeout = timeout
23 self.releasefn = releasefn
31 self.releasefn = releasefn
32 self.id = None
33 self.host = None
34 self.pid = None
24 self.lock()
35 self.lock()
25
36
26 def __del__(self):
37 def __del__(self):
@@ -41,16 +52,51 b' class lock(object):'
41 raise inst
52 raise inst
42
53
43 def trylock(self):
54 def trylock(self):
44 pid = os.getpid()
55 if self.id is None:
56 self.host = socket.gethostname()
57 self.pid = os.getpid()
58 self.id = '%s:%s' % (self.host, self.pid)
59 while not self.held:
45 try:
60 try:
46 util.makelock(str(pid), self.f)
61 util.makelock(self.id, self.f)
47 self.held = 1
62 self.held = 1
48 except (OSError, IOError), why:
63 except (OSError, IOError), why:
49 if why.errno == errno.EEXIST:
64 if why.errno == errno.EEXIST:
50 raise LockHeld(util.readlock(self.f))
65 locker = self.testlock()
66 if locker:
67 raise LockHeld(locker)
51 else:
68 else:
52 raise LockUnavailable(why)
69 raise LockUnavailable(why)
53
70
71 def testlock(self):
72 '''return id of locker if lock is valid, else None.'''
73 # if old-style lock, we cannot tell what machine locker is on.
74 # with new-style lock, if locker is on this machine, we can
75 # see if locker is alive. if locker is on this machine but
76 # not alive, we can safely break lock.
77 locker = util.readlock(self.f)
78 c = locker.find(':')
79 if c == -1:
80 return locker
81 host = locker[:c]
82 if host != self.host:
83 return locker
84 try:
85 pid = int(locker[c+1:])
86 except:
87 return locker
88 if util.testpid(pid):
89 return locker
90 # if locker dead, break lock. must do this with another lock
91 # held, or can race and break valid lock.
92 try:
93 l = lock(self.f + '.break')
94 l.trylock()
95 os.unlink(self.f)
96 l.release()
97 except (LockHeld, LockUnavailable):
98 return locker
99
54 def release(self):
100 def release(self):
55 if self.held:
101 if self.held:
56 self.held = 0
102 self.held = 0
@@ -16,8 +16,14 b' def demandload(scope, modules):'
16 """ fake demandload function that collects the required modules """
16 """ fake demandload function that collects the required modules """
17 for m in modules.split():
17 for m in modules.split():
18 mod = None
18 mod = None
19 mod = __import__(m,scope,scope)
19 try:
20 scope[m] = mod
20 module, submodules = m.split(':')
21 submodules = submodules.split(',')
22 except:
23 module = m
24 submodules = []
25 mod = __import__(module, scope, scope, submodules)
26 scope[module] = mod
21 requiredmodules[mod.__name__] = 1
27 requiredmodules[mod.__name__] = 1
22
28
23 def getmodules(libpath,packagename):
29 def getmodules(libpath,packagename):
@@ -48,7 +48,7 b' def decompress(bin):'
48 if t == '\0': return bin
48 if t == '\0': return bin
49 if t == 'x': return zlib.decompress(bin)
49 if t == 'x': return zlib.decompress(bin)
50 if t == 'u': return bin[1:]
50 if t == 'u': return bin[1:]
51 raise RevlogError(_("unknown compression type %s") % t)
51 raise RevlogError(_("unknown compression type %r") % t)
52
52
53 indexformat = ">4l20s20s20s"
53 indexformat = ">4l20s20s20s"
54
54
@@ -15,8 +15,10 b' class rangereader(httprangereader.httpra'
15 def read(self, size=None):
15 def read(self, size=None):
16 try:
16 try:
17 return httprangereader.httprangereader.read(self, size)
17 return httprangereader.httprangereader.read(self, size)
18 except urllib2.HTTPError, inst:
19 raise IOError(None, inst)
18 except urllib2.URLError, inst:
20 except urllib2.URLError, inst:
19 raise IOError(None, str(inst))
21 raise IOError(None, inst.reason[1])
20
22
21 def opener(base):
23 def opener(base):
22 """return a function that opens files over http"""
24 """return a function that opens files over http"""
@@ -22,6 +22,7 b' class transaction(object):'
22 if os.path.exists(journal):
22 if os.path.exists(journal):
23 raise AssertionError(_("journal already exists - run hg recover"))
23 raise AssertionError(_("journal already exists - run hg recover"))
24
24
25 self.count = 1
25 self.report = report
26 self.report = report
26 self.opener = opener
27 self.opener = opener
27 self.after = after
28 self.after = after
@@ -46,7 +47,17 b' class transaction(object):'
46 self.file.write("%s\0%d\n" % (file, offset))
47 self.file.write("%s\0%d\n" % (file, offset))
47 self.file.flush()
48 self.file.flush()
48
49
50 def nest(self):
51 self.count += 1
52 return self
53
54 def running(self):
55 return self.count > 0
56
49 def close(self):
57 def close(self):
58 self.count -= 1
59 if self.count != 0:
60 return
50 self.file.close()
61 self.file.close()
51 self.entries = []
62 self.entries = []
52 if self.after:
63 if self.after:
@@ -5,15 +5,18 b''
5 # This software may be used and distributed according to the terms
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 import os, ConfigParser
8 import ConfigParser
9 from i18n import gettext as _
9 from i18n import gettext as _
10 from demandload import *
10 from demandload import *
11 demandload(globals(), "re socket sys util")
11 demandload(globals(), "os re socket sys util")
12
12
13 class ui(object):
13 class ui(object):
14 def __init__(self, verbose=False, debug=False, quiet=False,
14 def __init__(self, verbose=False, debug=False, quiet=False,
15 interactive=True):
15 interactive=True, parentui=None):
16 self.overlay = {}
16 self.overlay = {}
17 if parentui is None:
18 # this is the parent of all ui children
19 self.parentui = None
17 self.cdata = ConfigParser.SafeConfigParser()
20 self.cdata = ConfigParser.SafeConfigParser()
18 self.readconfig(util.rcpath)
21 self.readconfig(util.rcpath)
19
22
@@ -24,6 +27,19 b' class ui(object):'
24
27
25 self.updateopts(verbose, debug, quiet, interactive)
28 self.updateopts(verbose, debug, quiet, interactive)
26 self.diffcache = None
29 self.diffcache = None
30 else:
31 # parentui may point to an ui object which is already a child
32 self.parentui = parentui.parentui or parentui
33 parent_cdata = self.parentui.cdata
34 self.cdata = ConfigParser.SafeConfigParser(parent_cdata.defaults())
35 # make interpolation work
36 for section in parent_cdata.sections():
37 self.cdata.add_section(section)
38 for name, value in parent_cdata.items(section, raw=True):
39 self.cdata.set(section, name, value)
40
41 def __getattr__(self, key):
42 return getattr(self.parentui, key)
27
43
28 def updateopts(self, verbose=False, debug=False, quiet=False,
44 def updateopts(self, verbose=False, debug=False, quiet=False,
29 interactive=True):
45 interactive=True):
@@ -32,7 +48,7 b' class ui(object):'
32 self.debugflag = (self.debugflag or debug)
48 self.debugflag = (self.debugflag or debug)
33 self.interactive = (self.interactive and interactive)
49 self.interactive = (self.interactive and interactive)
34
50
35 def readconfig(self, fn):
51 def readconfig(self, fn, root=None):
36 if isinstance(fn, basestring):
52 if isinstance(fn, basestring):
37 fn = [fn]
53 fn = [fn]
38 for f in fn:
54 for f in fn:
@@ -40,6 +56,12 b' class ui(object):'
40 self.cdata.read(f)
56 self.cdata.read(f)
41 except ConfigParser.ParsingError, inst:
57 except ConfigParser.ParsingError, inst:
42 raise util.Abort(_("Failed to parse %s\n%s") % (f, inst))
58 raise util.Abort(_("Failed to parse %s\n%s") % (f, inst))
59 # translate paths relative to root (or home) into absolute paths
60 if root is None:
61 root = os.path.expanduser('~')
62 for name, path in self.configitems("paths"):
63 if path.find("://") == -1 and not os.path.isabs(path):
64 self.cdata.set("paths", name, os.path.join(root, path))
43
65
44 def setconfig(self, section, name, val):
66 def setconfig(self, section, name, val):
45 self.overlay[(section, name)] = val
67 self.overlay[(section, name)] = val
@@ -48,22 +70,43 b' class ui(object):'
48 if self.overlay.has_key((section, name)):
70 if self.overlay.has_key((section, name)):
49 return self.overlay[(section, name)]
71 return self.overlay[(section, name)]
50 if self.cdata.has_option(section, name):
72 if self.cdata.has_option(section, name):
73 try:
51 return self.cdata.get(section, name)
74 return self.cdata.get(section, name)
75 except ConfigParser.InterpolationError, inst:
76 raise util.Abort(_("Error in configuration:\n%s") % inst)
77 if self.parentui is None:
52 return default
78 return default
79 else:
80 return self.parentui.config(section, name, default)
53
81
54 def configbool(self, section, name, default=False):
82 def configbool(self, section, name, default=False):
55 if self.overlay.has_key((section, name)):
83 if self.overlay.has_key((section, name)):
56 return self.overlay[(section, name)]
84 return self.overlay[(section, name)]
57 if self.cdata.has_option(section, name):
85 if self.cdata.has_option(section, name):
86 try:
58 return self.cdata.getboolean(section, name)
87 return self.cdata.getboolean(section, name)
88 except ConfigParser.InterpolationError, inst:
89 raise util.Abort(_("Error in configuration:\n%s") % inst)
90 if self.parentui is None:
59 return default
91 return default
92 else:
93 return self.parentui.configbool(section, name, default)
60
94
61 def configitems(self, section):
95 def configitems(self, section):
96 items = {}
97 if self.parentui is not None:
98 items = dict(self.parentui.configitems(section))
62 if self.cdata.has_section(section):
99 if self.cdata.has_section(section):
63 return self.cdata.items(section)
100 try:
64 return []
101 items.update(dict(self.cdata.items(section)))
102 except ConfigParser.InterpolationError, inst:
103 raise util.Abort(_("Error in configuration:\n%s") % inst)
104 x = items.items()
105 x.sort()
106 return x
65
107
66 def walkconfig(self):
108 def walkconfig(self, seen=None):
109 if seen is None:
67 seen = {}
110 seen = {}
68 for (section, name), value in self.overlay.iteritems():
111 for (section, name), value in self.overlay.iteritems():
69 yield section, name, value
112 yield section, name, value
@@ -73,6 +116,9 b' class ui(object):'
73 if (section, name) in seen: continue
116 if (section, name) in seen: continue
74 yield section, name, value.replace('\n', '\\n')
117 yield section, name, value.replace('\n', '\\n')
75 seen[section, name] = 1
118 seen[section, name] = 1
119 if self.parentui is not None:
120 for parent in self.parentui.walkconfig(seen):
121 yield parent
76
122
77 def extensions(self):
123 def extensions(self):
78 return self.configitems("extensions")
124 return self.configitems("extensions")
@@ -107,15 +153,12 b' class ui(object):'
107 if not self.verbose: user = util.shortuser(user)
153 if not self.verbose: user = util.shortuser(user)
108 return user
154 return user
109
155
110 def expandpath(self, loc, root=""):
156 def expandpath(self, loc):
111 paths = {}
157 """Return repository location relative to cwd or from [paths]"""
112 for name, path in self.configitems("paths"):
158 if loc.find("://") != -1 or os.path.exists(loc):
113 m = path.find("://")
159 return loc
114 if m == -1:
115 path = os.path.join(root, path)
116 paths[name] = path
117
160
118 return paths.get(loc, loc)
161 return self.config("paths", loc, loc)
119
162
120 def write(self, *args):
163 def write(self, *args):
121 for a in args:
164 for a in args:
@@ -126,6 +169,12 b' class ui(object):'
126 for a in args:
169 for a in args:
127 sys.stderr.write(str(a))
170 sys.stderr.write(str(a))
128
171
172 def flush(self):
173 try:
174 sys.stdout.flush()
175 finally:
176 sys.stderr.flush()
177
129 def readline(self):
178 def readline(self):
130 return sys.stdin.readline()[:-1]
179 return sys.stdin.readline()[:-1]
131 def prompt(self, msg, pat, default="y"):
180 def prompt(self, msg, pat, default="y"):
@@ -157,7 +206,9 b' class ui(object):'
157 os.environ.get("EDITOR", "vi"))
206 os.environ.get("EDITOR", "vi"))
158
207
159 os.environ["HGUSER"] = self.username()
208 os.environ["HGUSER"] = self.username()
160 util.system("%s \"%s\"" % (editor, name), errprefix=_("edit failed"))
209 util.system("%s \"%s\"" % (editor, name),
210 environ={'HGUSER': self.username()},
211 onerr=util.Abort, errprefix=_("edit failed"))
161
212
162 t = open(name).read()
213 t = open(name).read()
163 t = re.sub("(?m)^HG:.*\n", "", t)
214 t = re.sub("(?m)^HG:.*\n", "", t)
@@ -315,15 +315,42 b' def _matcher(canonroot, cwd, names, inc,'
315 (files and filematch(fn)))),
315 (files and filematch(fn)))),
316 (inc or exc or (pats and pats != [('glob', '**')])) and True)
316 (inc or exc or (pats and pats != [('glob', '**')])) and True)
317
317
318 def system(cmd, errprefix=None):
318 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None):
319 """execute a shell command that must succeed"""
319 '''enhanced shell command execution.
320 run with environment maybe modified, maybe in different dir.
321
322 if command fails and onerr is None, return status. if ui object,
323 print error message and return status, else raise onerr object as
324 exception.'''
325 oldenv = {}
326 for k in environ:
327 oldenv[k] = os.environ.get(k)
328 if cwd is not None:
329 oldcwd = os.getcwd()
330 try:
331 for k, v in environ.iteritems():
332 os.environ[k] = str(v)
333 if cwd is not None and oldcwd != cwd:
334 os.chdir(cwd)
320 rc = os.system(cmd)
335 rc = os.system(cmd)
321 if rc:
336 if rc and onerr:
322 errmsg = "%s %s" % (os.path.basename(cmd.split(None, 1)[0]),
337 errmsg = '%s %s' % (os.path.basename(cmd.split(None, 1)[0]),
323 explain_exit(rc)[0])
338 explain_exit(rc)[0])
324 if errprefix:
339 if errprefix:
325 errmsg = "%s: %s" % (errprefix, errmsg)
340 errmsg = '%s: %s' % (errprefix, errmsg)
326 raise Abort(errmsg)
341 try:
342 onerr.warn(errmsg + '\n')
343 except AttributeError:
344 raise onerr(errmsg)
345 return rc
346 finally:
347 for k, v in oldenv.iteritems():
348 if v is None:
349 del os.environ[k]
350 else:
351 os.environ[k] = v
352 if cwd is not None and oldcwd != cwd:
353 os.chdir(oldcwd)
327
354
328 def rename(src, dst):
355 def rename(src, dst):
329 """forcibly rename a file"""
356 """forcibly rename a file"""
@@ -363,7 +390,14 b' def copyfiles(src, dst, hardlink=None):'
363 else:
390 else:
364 shutil.copy(src, dst)
391 shutil.copy(src, dst)
365
392
366 def opener(base):
393 def audit_path(path):
394 """Abort if path contains dangerous components"""
395 parts = os.path.normcase(path).split(os.sep)
396 if (os.path.splitdrive(path)[0] or parts[0] in ('.hg', '')
397 or os.pardir in parts):
398 raise Abort(_("path contains illegal component: %s\n") % path)
399
400 def opener(base, audit=True):
367 """
401 """
368 return a function that opens files relative to base
402 return a function that opens files relative to base
369
403
@@ -371,6 +405,7 b' def opener(base):'
371 remote file access from higher level code.
405 remote file access from higher level code.
372 """
406 """
373 p = base
407 p = base
408 audit_p = audit
374
409
375 def mktempcopy(name):
410 def mktempcopy(name):
376 d, fn = os.path.split(name)
411 d, fn = os.path.split(name)
@@ -401,6 +436,8 b' def opener(base):'
401 self.close()
436 self.close()
402
437
403 def o(path, mode="r", text=False, atomic=False):
438 def o(path, mode="r", text=False, atomic=False):
439 if audit_p:
440 audit_path(path)
404 f = os.path.join(p, path)
441 f = os.path.join(p, path)
405
442
406 if not text:
443 if not text:
@@ -489,7 +526,7 b" if os.name == 'nt':"
489 return pf
526 return pf
490
527
491 try: # ActivePython can create hard links using win32file module
528 try: # ActivePython can create hard links using win32file module
492 import win32file
529 import win32api, win32con, win32file
493
530
494 def os_link(src, dst): # NB will only succeed on NTFS
531 def os_link(src, dst): # NB will only succeed on NTFS
495 win32file.CreateHardLink(dst, src)
532 win32file.CreateHardLink(dst, src)
@@ -506,8 +543,18 b" if os.name == 'nt':"
506 except:
543 except:
507 return os.stat(pathname).st_nlink
544 return os.stat(pathname).st_nlink
508
545
546 def testpid(pid):
547 '''return False if pid is dead, True if running or not known'''
548 try:
549 win32api.OpenProcess(win32con.PROCESS_QUERY_INFORMATION,
550 False, pid)
551 except:
552 return True
553
509 except ImportError:
554 except ImportError:
510 pass
555 def testpid(pid):
556 '''return False if pid dead, True if running or not known'''
557 return True
511
558
512 def is_exec(f, last):
559 def is_exec(f, last):
513 return last
560 return last
@@ -604,6 +651,14 b' else:'
604 else:
651 else:
605 raise
652 raise
606
653
654 def testpid(pid):
655 '''return False if pid dead, True if running or not sure'''
656 try:
657 os.kill(pid, 0)
658 return True
659 except OSError, inst:
660 return inst.errno != errno.ESRCH
661
607 def explain_exit(code):
662 def explain_exit(code):
608 """return a 2-tuple (desc, code) describing a process's status"""
663 """return a 2-tuple (desc, code) describing a process's status"""
609 if os.WIFEXITED(code):
664 if os.WIFEXITED(code):
@@ -700,3 +755,16 b' def shortuser(user):'
700 if f >= 0:
755 if f >= 0:
701 user = user[f+1:]
756 user = user[f+1:]
702 return user
757 return user
758
759 def walkrepos(path):
760 '''yield every hg repository under path, recursively.'''
761 def errhandler(err):
762 if err.filename == path:
763 raise err
764
765 for root, dirs, files in os.walk(path, onerror=errhandler):
766 for d in dirs:
767 if d == '.hg':
768 yield root
769 dirs[:] = []
770 break
@@ -5,8 +5,11 b''
5 # './setup.py install', or
5 # './setup.py install', or
6 # './setup.py --help' for more options
6 # './setup.py --help' for more options
7
7
8 import sys
9 if not hasattr(sys, 'version_info') or sys.version_info < (2, 3):
10 raise SystemExit, "Mercurial requires python 2.3 or later."
11
8 import glob
12 import glob
9 import sys
10 from distutils.core import setup, Extension
13 from distutils.core import setup, Extension
11 from distutils.command.install_data import install_data
14 from distutils.command.install_data import install_data
12
15
@@ -53,7 +53,16 b' else'
53 fi
53 fi
54 cd "$TESTDIR"
54 cd "$TESTDIR"
55
55
56 PATH="$INST/bin:$PATH"; export PATH
56 BINDIR="$INST/bin"
57 PATH="$BINDIR:$PATH"; export PATH
58 if [ -n "$PYTHON" ]; then
59 {
60 echo "#!/bin/sh"
61 echo "exec \"$PYTHON"'" "$@"'
62 } > "$BINDIR/python"
63 chmod 755 "$BINDIR/python"
64 fi
65
57 PYTHONPATH="$PYTHONDIR"; export PYTHONPATH
66 PYTHONPATH="$PYTHONDIR"; export PYTHONPATH
58
67
59 run_one() {
68 run_one() {
@@ -1,4 +1,4 b''
1 #!/bin/bash
1 #!/bin/sh
2
2
3 hg init test
3 hg init test
4 cd test
4 cd test
@@ -1,5 +1,3 b''
1 transaction abort!
2 rollback completed
3 abort: impossible time zone offset: 4444444
1 abort: impossible time zone offset: 4444444
4 transaction abort!
2 transaction abort!
5 rollback completed
3 rollback completed
@@ -13,4 +11,6 b" abort: invalid date: ' 1 4444'"
13 transaction abort!
11 transaction abort!
14 rollback completed
12 rollback completed
15 abort: date exceeds 32 bits: 111111111111
13 abort: date exceeds 32 bits: 111111111111
14 transaction abort!
15 rollback completed
16 abort: No such file or directory: .../test/bar
16 abort: No such file or directory: .../test/bar
@@ -64,7 +64,6 b' list of commands (use "hg help -v" to sh'
64 paths show definition of symbolic path names
64 paths show definition of symbolic path names
65 pull pull changes from the specified source
65 pull pull changes from the specified source
66 push push changes to the specified destination
66 push push changes to the specified destination
67 rawcommit raw commit interface (DEPRECATED)
68 recover roll back an interrupted transaction
67 recover roll back an interrupted transaction
69 remove remove the specified files on the next commit
68 remove remove the specified files on the next commit
70 rename rename files; equivalent of copy + remove
69 rename rename files; equivalent of copy + remove
@@ -106,7 +105,6 b' list of commands (use "hg help -v" to sh'
106 paths show definition of symbolic path names
105 paths show definition of symbolic path names
107 pull pull changes from the specified source
106 pull pull changes from the specified source
108 push push changes to the specified destination
107 push push changes to the specified destination
109 rawcommit raw commit interface (DEPRECATED)
110 recover roll back an interrupted transaction
108 recover roll back an interrupted transaction
111 remove remove the specified files on the next commit
109 remove remove the specified files on the next commit
112 rename rename files; equivalent of copy + remove
110 rename rename files; equivalent of copy + remove
@@ -173,9 +171,9 b' options:'
173
171
174 -r --rev revision
172 -r --rev revision
175 -a --text treat all files as text
173 -a --text treat all files as text
176 -I --include include names matching the given patterns
177 -p --show-function show which function each change is in
174 -p --show-function show which function each change is in
178 -w --ignore-all-space ignore white space when comparing lines
175 -w --ignore-all-space ignore white space when comparing lines
176 -I --include include names matching the given patterns
179 -X --exclude exclude names matching the given patterns
177 -X --exclude exclude names matching the given patterns
180 hg status [OPTION]... [FILE]...
178 hg status [OPTION]... [FILE]...
181
179
@@ -1,23 +1,23 b''
1 precommit hook: p1=0000000000000000000000000000000000000000 p2=
1 precommit hook: p1=0000000000000000000000000000000000000000 p2=
2 pretxncommit hook: n=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b p1=0000000000000000000000000000000000000000 p2=
2 pretxncommit hook: n=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b p1=0000000000000000000000000000000000000000 p2=
3 0:cb9a9f314b8b
3 0:cb9a9f314b8b
4 commit hook: n=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b p1=0000000000000000000000000000000000000000 p2=
4 commit hook b
5 commit hook b
5 commit hook: n=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b p1=0000000000000000000000000000000000000000 p2=
6 precommit hook: p1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b p2=
6 precommit hook: p1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b p2=
7 pretxncommit hook: n=ab228980c14deea8b9555d91c9581127383e40fd p1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b p2=
7 pretxncommit hook: n=ab228980c14deea8b9555d91c9581127383e40fd p1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b p2=
8 1:ab228980c14d
8 1:ab228980c14d
9 commit hook: n=ab228980c14deea8b9555d91c9581127383e40fd p1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b p2=
9 commit hook b
10 commit hook b
10 commit hook: n=ab228980c14deea8b9555d91c9581127383e40fd p1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b p2=
11 precommit hook: p1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b p2=
11 precommit hook: p1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b p2=
12 pretxncommit hook: n=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 p1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b p2=
12 pretxncommit hook: n=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 p1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b p2=
13 2:ee9deb46ab31
13 2:ee9deb46ab31
14 commit hook: n=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 p1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b p2=
14 commit hook b
15 commit hook b
15 commit hook: n=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 p1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b p2=
16 precommit hook: p1=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 p2=ab228980c14deea8b9555d91c9581127383e40fd
16 precommit hook: p1=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 p2=ab228980c14deea8b9555d91c9581127383e40fd
17 pretxncommit hook: n=07f3376c1e655977439df2a814e3cc14b27abac2 p1=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 p2=ab228980c14deea8b9555d91c9581127383e40fd
17 pretxncommit hook: n=07f3376c1e655977439df2a814e3cc14b27abac2 p1=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 p2=ab228980c14deea8b9555d91c9581127383e40fd
18 3:07f3376c1e65
18 3:07f3376c1e65
19 commit hook: n=07f3376c1e655977439df2a814e3cc14b27abac2 p1=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 p2=ab228980c14deea8b9555d91c9581127383e40fd
19 commit hook b
20 commit hook b
20 commit hook: n=07f3376c1e655977439df2a814e3cc14b27abac2 p1=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 p2=ab228980c14deea8b9555d91c9581127383e40fd
21 prechangegroup hook
21 prechangegroup hook
22 changegroup hook: n=ab228980c14deea8b9555d91c9581127383e40fd
22 changegroup hook: n=ab228980c14deea8b9555d91c9581127383e40fd
23 incoming hook: n=ab228980c14deea8b9555d91c9581127383e40fd
23 incoming hook: n=ab228980c14deea8b9555d91c9581127383e40fd
@@ -34,8 +34,8 b' pretag hook: t=a n=07f3376c1e655977439df'
34 precommit hook: p1=07f3376c1e655977439df2a814e3cc14b27abac2 p2=
34 precommit hook: p1=07f3376c1e655977439df2a814e3cc14b27abac2 p2=
35 pretxncommit hook: n=3cd2c6a5a36c5908aad3bc0d717c29873a05dfc2 p1=07f3376c1e655977439df2a814e3cc14b27abac2 p2=
35 pretxncommit hook: n=3cd2c6a5a36c5908aad3bc0d717c29873a05dfc2 p1=07f3376c1e655977439df2a814e3cc14b27abac2 p2=
36 4:3cd2c6a5a36c
36 4:3cd2c6a5a36c
37 commit hook: n=3cd2c6a5a36c5908aad3bc0d717c29873a05dfc2 p1=07f3376c1e655977439df2a814e3cc14b27abac2 p2=
37 commit hook b
38 commit hook b
38 commit hook: n=3cd2c6a5a36c5908aad3bc0d717c29873a05dfc2 p1=07f3376c1e655977439df2a814e3cc14b27abac2 p2=
39 tag hook: t=a n=07f3376c1e655977439df2a814e3cc14b27abac2 l=0
39 tag hook: t=a n=07f3376c1e655977439df2a814e3cc14b27abac2 l=0
40 pretag hook: t=la n=3cd2c6a5a36c5908aad3bc0d717c29873a05dfc2 l=1
40 pretag hook: t=la n=3cd2c6a5a36c5908aad3bc0d717c29873a05dfc2 l=1
41 tag hook: t=la n=3cd2c6a5a36c5908aad3bc0d717c29873a05dfc2 l=1
41 tag hook: t=la n=3cd2c6a5a36c5908aad3bc0d717c29873a05dfc2 l=1
@@ -47,11 +47,14 b' pretag.forbid hook'
47 abort: pretag.forbid hook exited with status 1
47 abort: pretag.forbid hook exited with status 1
48 4:3cd2c6a5a36c
48 4:3cd2c6a5a36c
49 precommit hook: p1=3cd2c6a5a36c5908aad3bc0d717c29873a05dfc2 p2=
49 precommit hook: p1=3cd2c6a5a36c5908aad3bc0d717c29873a05dfc2 p2=
50 pretxncommit hook: n=469a61fe67d64df9a5023e4c2b8a0b85c61e9b69 p1=3cd2c6a5a36c5908aad3bc0d717c29873a05dfc2 p2=
51 5:469a61fe67d6
50 pretxncommit.forbid hook: tip=5:469a61fe67d6
52 pretxncommit.forbid hook: tip=5:469a61fe67d6
51 abort: pretxncommit.forbid hook exited with status 1
53 abort: pretxncommit.forbid hook exited with status 1
52 transaction abort!
54 transaction abort!
53 rollback completed
55 rollback completed
54 4:3cd2c6a5a36c
56 4:3cd2c6a5a36c
57 precommit hook: p1=3cd2c6a5a36c5908aad3bc0d717c29873a05dfc2 p2=
55 precommit.forbid hook
58 precommit.forbid hook
56 abort: precommit.forbid hook exited with status 1
59 abort: precommit.forbid hook exited with status 1
57 4:3cd2c6a5a36c
60 4:3cd2c6a5a36c
@@ -1,4 +1,4 b''
1 #!/bin/bash
1 #!/bin/sh
2
2
3 hg init test
3 hg init test
4 cd test
4 cd test
@@ -171,3 +171,11 b' mv d1/bb d1/bc'
171 hg rename --after d1/bb d1/bc
171 hg rename --after d1/bb d1/bc
172 hg status
172 hg status
173 hg update -C
173 hg update -C
174
175 echo "# idempotent renames (d1/b -> d1/bb followed by d1/bb -> d1/b)"
176 hg rename d1/b d1/bb
177 echo "some stuff added to d1/bb" >> d1/bb
178 hg rename d1/bb d1/b
179 hg status
180 hg debugstate | grep copy
181 hg update -C
@@ -252,3 +252,5 b' R d1/b'
252 # transitive rename --after
252 # transitive rename --after
253 A d1/bc
253 A d1/bc
254 R d1/b
254 R d1/b
255 # idempotent renames (d1/b -> d1/bb followed by d1/bb -> d1/b)
256 M d1/b
@@ -1,4 +1,4 b''
1 abort: <urlopen error (111, 'Connection refused')>
1 abort: Connection refused
2 255
2 255
3 ls: copy: No such file or directory
3 ls: copy: No such file or directory
4 changeset: 0:61c9426e69fe
4 changeset: 0:61c9426e69fe
1 NO CONTENT: file was removed
NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now