##// END OF EJS Templates
merge with crew
Peter van Dijk -
r1930:70be7489 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, (1308 lines changed) Show them Hide them
@@ -0,0 +1,1308 b''
1 #!/usr/bin/env python
2 # queue.py - patch queues for mercurial
3 #
4 # Copyright 2005 Chris Mason <mason@suse.com>
5 #
6 # This software may be used and distributed according to the terms
7 # of the GNU General Public License, incorporated herein by reference.
8
9 from mercurial.demandload import *
10 demandload(globals(), "os sys re struct traceback errno bz2")
11 from mercurial.i18n import gettext as _
12 from mercurial import ui, hg, revlog, commands, util
13
14 versionstr = "0.45"
15
16 repomap = {}
17
18 class queue:
19 def __init__(self, ui, path, patchdir=None):
20 self.opener = util.opener(path)
21 self.basepath = path
22 if patchdir:
23 self.path = patchdir
24 else:
25 self.path = os.path.join(path, "patches")
26 self.ui = ui
27 self.applied = []
28 self.full_series = []
29 self.applied_dirty = 0
30 self.series_dirty = 0
31 self.series_path = os.path.join(self.path, "series")
32 self.status_path = os.path.join(self.path, "status")
33
34 s = self.series_path
35 if os.path.exists(s):
36 self.full_series = self.opener(s).read().splitlines()
37 self.read_series(self.full_series)
38
39 s = self.status_path
40 if os.path.exists(s):
41 self.applied = self.opener(s).read().splitlines()
42
43 def find_series(self, patch):
44 pre = re.compile("(\s*)([^#]+)")
45 index = 0
46 for l in self.full_series:
47 m = pre.match(l)
48 if m:
49 s = m.group(2)
50 s = s.rstrip()
51 if s == patch:
52 return index
53 index += 1
54 return None
55
56 def read_series(self, list):
57 def matcher(list):
58 pre = re.compile("(\s*)([^#]+)")
59 for l in list:
60 m = pre.match(l)
61 if m:
62 s = m.group(2)
63 s = s.rstrip()
64 if len(s) > 0:
65 yield s
66 self.series = []
67 self.series = [ x for x in matcher(list) ]
68
69 def save_dirty(self):
70 if self.applied_dirty:
71 if len(self.applied) > 0:
72 nl = "\n"
73 else:
74 nl = ""
75 f = self.opener(self.status_path, "w")
76 f.write("\n".join(self.applied) + nl)
77 if self.series_dirty:
78 if len(self.full_series) > 0:
79 nl = "\n"
80 else:
81 nl = ""
82 f = self.opener(self.series_path, "w")
83 f.write("\n".join(self.full_series) + nl)
84
85 def readheaders(self, patch):
86 def eatdiff(lines):
87 while lines:
88 l = lines[-1]
89 if (l.startswith("diff -") or
90 l.startswith("Index:") or
91 l.startswith("===========")):
92 del lines[-1]
93 else:
94 break
95 def eatempty(lines):
96 while lines:
97 l = lines[-1]
98 if re.match('\s*$', l):
99 del lines[-1]
100 else:
101 break
102
103 pf = os.path.join(self.path, patch)
104 message = []
105 comments = []
106 user = None
107 format = None
108 subject = None
109 diffstart = 0
110
111 for line in file(pf):
112 line = line.rstrip()
113 if diffstart:
114 if line.startswith('+++ '):
115 diffstart = 2
116 break
117 if line.startswith("--- "):
118 diffstart = 1
119 continue
120 elif format == "hgpatch":
121 # parse values when importing the result of an hg export
122 if line.startswith("# User "):
123 user = line[7:]
124 elif not line.startswith("# ") and line:
125 message.append(line)
126 format = None
127 elif line == '# HG changeset patch':
128 format = "hgpatch"
129 elif (format != "tagdone" and (line.startswith("Subject: ") or
130 line.startswith("subject: "))):
131 subject = line[9:]
132 format = "tag"
133 elif (format != "tagdone" and (line.startswith("From: ") or
134 line.startswith("from: "))):
135 user = line[6:]
136 format = "tag"
137 elif format == "tag" and line == "":
138 # when looking for tags (subject: from: etc) they
139 # end once you find a blank line in the source
140 format = "tagdone"
141 else:
142 message.append(line)
143 comments.append(line)
144
145 eatdiff(message)
146 eatdiff(comments)
147 eatempty(message)
148 eatempty(comments)
149
150 # make sure message isn't empty
151 if format and format.startswith("tag") and subject:
152 message.insert(0, "")
153 message.insert(0, subject)
154 return (message, comments, user, diffstart > 1)
155
156 def mergeone(self, repo, mergeq, head, patch, rev, wlock):
157 # first try just applying the patch
158 (err, n) = self.apply(repo, [ patch ], update_status=False,
159 strict=True, merge=rev, wlock=wlock)
160
161 if err == 0:
162 return (err, n)
163
164 if n is None:
165 self.ui.warn("apply failed for patch %s\n" % patch)
166 sys.exit(1)
167
168 self.ui.warn("patch didn't work out, merging %s\n" % patch)
169
170 # apply failed, strip away that rev and merge.
171 repo.update(head, allow=False, force=True, wlock=wlock)
172 self.strip(repo, n, update=False, backup='strip', wlock=wlock)
173
174 c = repo.changelog.read(rev)
175 ret = repo.update(rev, allow=True, wlock=wlock)
176 if ret:
177 self.ui.warn("update returned %d\n" % ret)
178 sys.exit(1)
179 n = repo.commit(None, c[4], c[1], force=1, wlock=wlock)
180 if n == None:
181 self.ui.warn("repo commit failed\n")
182 sys.exit(1)
183 try:
184 message, comments, user, patchfound = mergeq.readheaders(patch)
185 except:
186 self.ui.warn("Unable to read %s\n" % patch)
187 sys.exit(1)
188
189 patchf = self.opener(os.path.join(self.path, patch), "w")
190 if comments:
191 comments = "\n".join(comments) + '\n\n'
192 patchf.write(comments)
193 commands.dodiff(patchf, self.ui, repo, head, n)
194 patchf.close()
195 return (0, n)
196
197 def qparents(self, repo, rev=None):
198 if rev is None:
199 (p1, p2) = repo.dirstate.parents()
200 if p2 == revlog.nullid:
201 return p1
202 if len(self.applied) == 0:
203 return None
204 (top, patch) = self.applied[-1].split(':')
205 top = revlog.bin(top)
206 return top
207 pp = repo.changelog.parents(rev)
208 if pp[1] != revlog.nullid:
209 arevs = [ x.split(':')[0] for x in self.applied ]
210 p0 = revlog.hex(pp[0])
211 p1 = revlog.hex(pp[1])
212 if p0 in arevs:
213 return pp[0]
214 if p1 in arevs:
215 return pp[1]
216 return None
217 return pp[0]
218
219 def mergepatch(self, repo, mergeq, series, wlock):
220 if len(self.applied) == 0:
221 # each of the patches merged in will have two parents. This
222 # can confuse the qrefresh, qdiff, and strip code because it
223 # needs to know which parent is actually in the patch queue.
224 # so, we insert a merge marker with only one parent. This way
225 # the first patch in the queue is never a merge patch
226 #
227 pname = ".hg.patches.merge.marker"
228 n = repo.commit(None, '[mq]: merge marker', user=None, force=1,
229 wlock=wlock)
230 self.applied.append(revlog.hex(n) + ":" + pname)
231 self.applied_dirty = 1
232
233 head = self.qparents(repo)
234
235 for patch in series:
236 patch = mergeq.lookup(patch)
237 if not patch:
238 self.ui.warn("patch %s does not exist\n" % patch)
239 return (1, None)
240
241 info = mergeq.isapplied(patch)
242 if not info:
243 self.ui.warn("patch %s is not applied\n" % patch)
244 return (1, None)
245 rev = revlog.bin(info[1])
246 (err, head) = self.mergeone(repo, mergeq, head, patch, rev, wlock)
247 if head:
248 self.applied.append(revlog.hex(head) + ":" + patch)
249 self.applied_dirty = 1
250 if err:
251 return (err, head)
252 return (0, head)
253
254 def apply(self, repo, series, list=False, update_status=True,
255 strict=False, patchdir=None, merge=None, wlock=None):
256 # TODO unify with commands.py
257 if not patchdir:
258 patchdir = self.path
259 pwd = os.getcwd()
260 os.chdir(repo.root)
261 err = 0
262 if not wlock:
263 wlock = repo.wlock()
264 lock = repo.lock()
265 tr = repo.transaction()
266 n = None
267 for patch in series:
268 self.ui.warn("applying %s\n" % patch)
269 pf = os.path.join(patchdir, patch)
270
271 try:
272 message, comments, user, patchfound = self.readheaders(patch)
273 except:
274 self.ui.warn("Unable to read %s\n" % pf)
275 err = 1
276 break
277
278 if not message:
279 message = "imported patch %s\n" % patch
280 else:
281 if list:
282 message.append("\nimported patch %s" % patch)
283 message = '\n'.join(message)
284
285 try:
286 f = os.popen("patch -p1 --no-backup-if-mismatch < '%s'" % (pf))
287 except:
288 self.ui.warn("patch failed, unable to continue (try -v)\n")
289 err = 1
290 break
291 files = []
292 fuzz = False
293 for l in f:
294 l = l.rstrip('\r\n');
295 if self.ui.verbose:
296 self.ui.warn(l + "\n")
297 if l[:14] == 'patching file ':
298 pf = os.path.normpath(l[14:])
299 # when patch finds a space in the file name, it puts
300 # single quotes around the filename. strip them off
301 if pf[0] == "'" and pf[-1] == "'":
302 pf = pf[1:-1]
303 if pf not in files:
304 files.append(pf)
305 printed_file = False
306 file_str = l
307 elif l.find('with fuzz') >= 0:
308 if not printed_file:
309 self.ui.warn(file_str + '\n')
310 printed_file = True
311 self.ui.warn(l + '\n')
312 fuzz = True
313 elif l.find('saving rejects to file') >= 0:
314 self.ui.warn(l + '\n')
315 elif l.find('FAILED') >= 0:
316 if not printed_file:
317 self.ui.warn(file_str + '\n')
318 printed_file = True
319 self.ui.warn(l + '\n')
320 patcherr = f.close()
321
322 if merge and len(files) > 0:
323 # Mark as merged and update dirstate parent info
324 repo.dirstate.update(repo.dirstate.filterfiles(files), 'm')
325 p1, p2 = repo.dirstate.parents()
326 repo.dirstate.setparents(p1, merge)
327 if len(files) > 0:
328 commands.addremove_lock(self.ui, repo, files,
329 opts={}, wlock=wlock)
330 n = repo.commit(files, message, user, force=1, lock=lock,
331 wlock=wlock)
332
333 if n == None:
334 self.ui.warn("repo commit failed\n")
335 sys.exit(1)
336
337 if update_status:
338 self.applied.append(revlog.hex(n) + ":" + patch)
339
340 if patcherr:
341 if not patchfound:
342 self.ui.warn("patch %s is empty\n" % patch)
343 err = 0
344 else:
345 self.ui.warn("patch failed, rejects left in working dir\n")
346 err = 1
347 break
348
349 if fuzz and strict:
350 self.ui.warn("fuzz found when applying patch, stopping\n")
351 err = 1
352 break
353 tr.close()
354 os.chdir(pwd)
355 return (err, n)
356
357 def delete(self, repo, patch):
358 patch = self.lookup(patch)
359 info = self.isapplied(patch)
360 if info:
361 self.ui.warn("cannot delete applied patch %s\n" % patch)
362 sys.exit(1)
363 if patch not in self.series:
364 self.ui.warn("patch %s not in series file\n" % patch)
365 sys.exit(1)
366 i = self.find_series(patch)
367 del self.full_series[i]
368 self.read_series(self.full_series)
369 self.series_dirty = 1
370
371 def check_toppatch(self, repo):
372 if len(self.applied) > 0:
373 (top, patch) = self.applied[-1].split(':')
374 top = revlog.bin(top)
375 pp = repo.dirstate.parents()
376 if top not in pp:
377 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])))
378 sys.exit(1)
379 return top
380 return None
381 def check_localchanges(self, repo):
382 (c, a, r, d, u) = repo.changes(None, None)
383 if c or a or d or r:
384 self.ui.write("Local changes found, refresh first\n")
385 sys.exit(1)
386 def new(self, repo, patch, msg=None, force=None):
387 if not force:
388 self.check_localchanges(repo)
389 self.check_toppatch(repo)
390 wlock = repo.wlock()
391 insert = self.series_end()
392 if msg:
393 n = repo.commit([], "[mq]: %s" % msg, force=True, wlock=wlock)
394 else:
395 n = repo.commit([],
396 "New patch: %s" % patch, force=True, wlock=wlock)
397 if n == None:
398 self.ui.warn("repo commit failed\n")
399 sys.exit(1)
400 self.full_series[insert:insert] = [patch]
401 self.applied.append(revlog.hex(n) + ":" + patch)
402 self.read_series(self.full_series)
403 self.series_dirty = 1
404 self.applied_dirty = 1
405 p = self.opener(os.path.join(self.path, patch), "w")
406 if msg:
407 msg = msg + "\n"
408 p.write(msg)
409 p.close()
410 wlock = None
411 r = self.qrepo()
412 if r: r.add([patch])
413
414 def strip(self, repo, rev, update=True, backup="all", wlock=None):
415 def limitheads(chlog, stop):
416 """return the list of all nodes that have no children"""
417 p = {}
418 h = []
419 stoprev = 0
420 if stop in chlog.nodemap:
421 stoprev = chlog.rev(stop)
422
423 for r in range(chlog.count() - 1, -1, -1):
424 n = chlog.node(r)
425 if n not in p:
426 h.append(n)
427 if n == stop:
428 break
429 if r < stoprev:
430 break
431 for pn in chlog.parents(n):
432 p[pn] = 1
433 return h
434
435 def bundle(cg):
436 backupdir = repo.join("strip-backup")
437 if not os.path.isdir(backupdir):
438 os.mkdir(backupdir)
439 name = os.path.join(backupdir, "%s" % revlog.short(rev))
440 name = savename(name)
441 self.ui.warn("saving bundle to %s\n" % name)
442 # TODO, exclusive open
443 f = open(name, "wb")
444 try:
445 f.write("HG10")
446 z = bz2.BZ2Compressor(9)
447 while 1:
448 chunk = cg.read(4096)
449 if not chunk:
450 break
451 f.write(z.compress(chunk))
452 f.write(z.flush())
453 except:
454 os.unlink(name)
455 raise
456 f.close()
457 return name
458
459 def stripall(rev, revnum):
460 cl = repo.changelog
461 c = cl.read(rev)
462 mm = repo.manifest.read(c[0])
463 seen = {}
464
465 for x in xrange(revnum, cl.count()):
466 c = cl.read(cl.node(x))
467 for f in c[3]:
468 if f in seen:
469 continue
470 seen[f] = 1
471 if f in mm:
472 filerev = mm[f]
473 else:
474 filerev = 0
475 seen[f] = filerev
476 # we go in two steps here so the strip loop happens in a
477 # sensible order. When stripping many files, this helps keep
478 # our disk access patterns under control.
479 list = seen.keys()
480 list.sort()
481 for f in list:
482 ff = repo.file(f)
483 filerev = seen[f]
484 if filerev != 0:
485 if filerev in ff.nodemap:
486 filerev = ff.rev(filerev)
487 else:
488 filerev = 0
489 ff.strip(filerev, revnum)
490
491 if not wlock:
492 wlock = repo.wlock()
493 lock = repo.lock()
494 chlog = repo.changelog
495 # TODO delete the undo files, and handle undo of merge sets
496 pp = chlog.parents(rev)
497 revnum = chlog.rev(rev)
498
499 if update:
500 urev = self.qparents(repo, rev)
501 repo.update(urev, allow=False, force=True, wlock=wlock)
502 repo.dirstate.write()
503
504 # save is a list of all the branches we are truncating away
505 # that we actually want to keep. changegroup will be used
506 # to preserve them and add them back after the truncate
507 saveheads = []
508 savebases = {}
509
510 tip = chlog.tip()
511 heads = limitheads(chlog, rev)
512 seen = {}
513
514 # search through all the heads, finding those where the revision
515 # we want to strip away is an ancestor. Also look for merges
516 # that might be turned into new heads by the strip.
517 while heads:
518 h = heads.pop()
519 n = h
520 while True:
521 seen[n] = 1
522 pp = chlog.parents(n)
523 if pp[1] != revlog.nullid and chlog.rev(pp[1]) > revnum:
524 if pp[1] not in seen:
525 heads.append(pp[1])
526 if pp[0] == revlog.nullid:
527 break
528 if chlog.rev(pp[0]) < revnum:
529 break
530 n = pp[0]
531 if n == rev:
532 break
533 r = chlog.reachable(h, rev)
534 if rev not in r:
535 saveheads.append(h)
536 for x in r:
537 if chlog.rev(x) > revnum:
538 savebases[x] = 1
539
540 # create a changegroup for all the branches we need to keep
541 if backup is "all":
542 backupch = repo.changegroupsubset([rev], chlog.heads(), 'strip')
543 bundle(backupch)
544 if saveheads:
545 backupch = repo.changegroupsubset(savebases.keys(), saveheads, 'strip')
546 chgrpfile = bundle(backupch)
547
548 stripall(rev, revnum)
549
550 change = chlog.read(rev)
551 repo.manifest.strip(repo.manifest.rev(change[0]), revnum)
552 chlog.strip(revnum, revnum)
553 if saveheads:
554 self.ui.status("adding branch\n")
555 commands.unbundle(self.ui, repo, chgrpfile, update=False)
556 if backup is not "strip":
557 os.unlink(chgrpfile)
558
559 def isapplied(self, patch):
560 """returns (index, rev, patch)"""
561 for i in xrange(len(self.applied)):
562 p = self.applied[i]
563 a = p.split(':')
564 if a[1] == patch:
565 return (i, a[0], a[1])
566 return None
567
568 def lookup(self, patch):
569 if patch == None:
570 return None
571 if patch in self.series:
572 return patch
573 if not os.path.isfile(os.path.join(self.path, patch)):
574 try:
575 sno = int(patch)
576 except(ValueError, OverflowError):
577 self.ui.warn("patch %s not in series\n" % patch)
578 sys.exit(1)
579 if sno >= len(self.series):
580 self.ui.warn("patch number %d is out of range\n" % sno)
581 sys.exit(1)
582 patch = self.series[sno]
583 else:
584 self.ui.warn("patch %s not in series\n" % patch)
585 sys.exit(1)
586 return patch
587
588 def push(self, repo, patch=None, force=False, list=False,
589 mergeq=None, wlock=None):
590 if not wlock:
591 wlock = repo.wlock()
592 patch = self.lookup(patch)
593 if patch and self.isapplied(patch):
594 self.ui.warn("patch %s is already applied\n" % patch)
595 sys.exit(1)
596 if self.series_end() == len(self.series):
597 self.ui.warn("File series fully applied\n")
598 sys.exit(1)
599 if not force:
600 self.check_localchanges(repo)
601
602 self.applied_dirty = 1;
603 start = self.series_end()
604 if start > 0:
605 self.check_toppatch(repo)
606 if not patch:
607 patch = self.series[start]
608 end = start + 1
609 else:
610 end = self.series.index(patch, start) + 1
611 s = self.series[start:end]
612 if mergeq:
613 ret = self.mergepatch(repo, mergeq, s, wlock)
614 else:
615 ret = self.apply(repo, s, list, wlock=wlock)
616 top = self.applied[-1].split(':')[1]
617 if ret[0]:
618 self.ui.write("Errors during apply, please fix and refresh %s\n" %
619 top)
620 else:
621 self.ui.write("Now at: %s\n" % top)
622 return ret[0]
623
624 def pop(self, repo, patch=None, force=False, update=True, wlock=None):
625 def getfile(f, rev):
626 t = repo.file(f).read(rev)
627 try:
628 repo.wfile(f, "w").write(t)
629 except IOError:
630 os.makedirs(os.path.dirname(repo.wjoin(f)))
631 repo.wfile(f, "w").write(t)
632
633 if not wlock:
634 wlock = repo.wlock()
635 if patch:
636 # index, rev, patch
637 info = self.isapplied(patch)
638 if not info:
639 patch = self.lookup(patch)
640 info = self.isapplied(patch)
641 if not info:
642 self.ui.warn("patch %s is not applied\n" % patch)
643 sys.exit(1)
644 if len(self.applied) == 0:
645 self.ui.warn("No patches applied\n")
646 sys.exit(1)
647
648 if not update:
649 parents = repo.dirstate.parents()
650 rr = [ revlog.bin(x.split(':')[0]) for x in self.applied ]
651 for p in parents:
652 if p in rr:
653 self.ui.warn("qpop: forcing dirstate update\n")
654 update = True
655
656 if not force and update:
657 self.check_localchanges(repo)
658
659 self.applied_dirty = 1;
660 end = len(self.applied)
661 if not patch:
662 info = [len(self.applied) - 1] + self.applied[-1].split(':')
663 start = info[0]
664 rev = revlog.bin(info[1])
665
666 # we know there are no local changes, so we can make a simplified
667 # form of hg.update.
668 if update:
669 top = self.check_toppatch(repo)
670 qp = self.qparents(repo, rev)
671 changes = repo.changelog.read(qp)
672 mf1 = repo.manifest.readflags(changes[0])
673 mmap = repo.manifest.read(changes[0])
674 (c, a, r, d, u) = repo.changes(qp, top)
675 if d:
676 raise util.Abort("deletions found between repo revs")
677 for f in c:
678 getfile(f, mmap[f])
679 for f in r:
680 getfile(f, mmap[f])
681 util.set_exec(repo.wjoin(f), mf1[f])
682 repo.dirstate.update(c + r, 'n')
683 for f in a:
684 try: os.unlink(repo.wjoin(f))
685 except: raise
686 try: os.removedirs(os.path.dirname(repo.wjoin(f)))
687 except: pass
688 if a:
689 repo.dirstate.forget(a)
690 repo.dirstate.setparents(qp, revlog.nullid)
691 self.strip(repo, rev, update=False, backup='strip', wlock=wlock)
692 del self.applied[start:end]
693 if len(self.applied):
694 self.ui.write("Now at: %s\n" % self.applied[-1].split(':')[1])
695 else:
696 self.ui.write("Patch queue now empty\n")
697
698 def diff(self, repo, files):
699 top = self.check_toppatch(repo)
700 if not top:
701 self.ui.write("No patches applied\n")
702 return
703 qp = self.qparents(repo, top)
704 commands.dodiff(sys.stdout, self.ui, repo, qp, None, files)
705
706 def refresh(self, repo, short=False):
707 if len(self.applied) == 0:
708 self.ui.write("No patches applied\n")
709 return
710 wlock = repo.wlock()
711 self.check_toppatch(repo)
712 qp = self.qparents(repo)
713 (top, patch) = self.applied[-1].split(':')
714 top = revlog.bin(top)
715 cparents = repo.changelog.parents(top)
716 patchparent = self.qparents(repo, top)
717 message, comments, user, patchfound = self.readheaders(patch)
718
719 patchf = self.opener(os.path.join(self.path, patch), "w")
720 if comments:
721 comments = "\n".join(comments) + '\n\n'
722 patchf.write(comments)
723
724 tip = repo.changelog.tip()
725 if top == tip:
726 # if the top of our patch queue is also the tip, there is an
727 # optimization here. We update the dirstate in place and strip
728 # off the tip commit. Then just commit the current directory
729 # tree. We can also send repo.commit the list of files
730 # changed to speed up the diff
731 #
732 # in short mode, we only diff the files included in the
733 # patch already
734 #
735 # this should really read:
736 #(cc, dd, aa, aa2, uu) = repo.changes(tip, patchparent)
737 # but we do it backwards to take advantage of manifest/chlog
738 # caching against the next repo.changes call
739 #
740 (cc, aa, dd, aa2, uu) = repo.changes(patchparent, tip)
741 if short:
742 filelist = cc + aa + dd
743 else:
744 filelist = None
745 (c, a, r, d, u) = repo.changes(None, None, filelist)
746
747 # we might end up with files that were added between tip and
748 # the dirstate parent, but then changed in the local dirstate.
749 # in this case, we want them to only show up in the added section
750 for x in c:
751 if x not in aa:
752 cc.append(x)
753 # we might end up with files added by the local dirstate that
754 # were deleted by the patch. In this case, they should only
755 # show up in the changed section.
756 for x in a:
757 if x in dd:
758 del dd[dd.index(x)]
759 cc.append(x)
760 else:
761 aa.append(x)
762 # make sure any files deleted in the local dirstate
763 # are not in the add or change column of the patch
764 forget = []
765 for x in d + r:
766 if x in aa:
767 del aa[aa.index(x)]
768 forget.append(x)
769 continue
770 elif x in cc:
771 del cc[cc.index(x)]
772 dd.append(x)
773
774 c = list(util.unique(cc))
775 r = list(util.unique(dd))
776 a = list(util.unique(aa))
777 filelist = list(util.unique(c + r + a ))
778 commands.dodiff(patchf, self.ui, repo, patchparent, None,
779 filelist, changes=(c, a, r, [], u))
780 patchf.close()
781
782 changes = repo.changelog.read(tip)
783 repo.dirstate.setparents(*cparents)
784 repo.dirstate.update(a, 'a')
785 repo.dirstate.update(r, 'r')
786 repo.dirstate.update(c, 'n')
787 repo.dirstate.forget(forget)
788
789 if not message:
790 message = "patch queue: %s\n" % patch
791 else:
792 message = "\n".join(message)
793 self.strip(repo, top, update=False, backup='strip', wlock=wlock)
794 n = repo.commit(filelist, message, changes[1], force=1, wlock=wlock)
795 self.applied[-1] = revlog.hex(n) + ':' + patch
796 self.applied_dirty = 1
797 else:
798 commands.dodiff(patchf, self.ui, repo, patchparent, None)
799 patchf.close()
800 self.pop(repo, force=True, wlock=wlock)
801 self.push(repo, force=True, wlock=wlock)
802
803 def init(self, repo, create=False):
804 if os.path.isdir(self.path):
805 raise util.Abort("patch queue directory already exists")
806 os.mkdir(self.path)
807 if create:
808 return self.qrepo(create=True)
809
810 def unapplied(self, repo, patch=None):
811 if patch and patch not in self.series:
812 self.ui.warn("%s not in the series file\n" % patch)
813 sys.exit(1)
814 if not patch:
815 start = self.series_end()
816 else:
817 start = self.series.index(patch) + 1
818 for p in self.series[start:]:
819 self.ui.write("%s\n" % p)
820
821 def qseries(self, repo, missing=None):
822 start = self.series_end()
823 if not missing:
824 for p in self.series[:start]:
825 if self.ui.verbose:
826 self.ui.write("%d A " % self.series.index(p))
827 self.ui.write("%s\n" % p)
828 for p in self.series[start:]:
829 if self.ui.verbose:
830 self.ui.write("%d U " % self.series.index(p))
831 self.ui.write("%s\n" % p)
832 else:
833 list = []
834 for root, dirs, files in os.walk(self.path):
835 d = root[len(self.path) + 1:]
836 for f in files:
837 fl = os.path.join(d, f)
838 if (fl not in self.series and fl != "status" and
839 fl != "series" and not fl.startswith('.')):
840 list.append(fl)
841 list.sort()
842 if list:
843 for x in list:
844 if self.ui.verbose:
845 self.ui.write("D ")
846 self.ui.write("%s\n" % x)
847
848 def issaveline(self, l):
849 name = l.split(':')[1]
850 if name == '.hg.patches.save.line':
851 return True
852
853 def qrepo(self, create=False):
854 if create or os.path.isdir(os.path.join(self.path, ".hg")):
855 return hg.repository(ui=self.ui, path=self.path, create=create)
856
857 def restore(self, repo, rev, delete=None, qupdate=None):
858 c = repo.changelog.read(rev)
859 desc = c[4].strip()
860 lines = desc.splitlines()
861 i = 0
862 datastart = None
863 series = []
864 applied = []
865 qpp = None
866 for i in xrange(0, len(lines)):
867 if lines[i] == 'Patch Data:':
868 datastart = i + 1
869 elif lines[i].startswith('Dirstate:'):
870 l = lines[i].rstrip()
871 l = l[10:].split(' ')
872 qpp = [ hg.bin(x) for x in l ]
873 elif datastart != None:
874 l = lines[i].rstrip()
875 index = l.index(':')
876 id = l[:index]
877 file = l[index + 1:]
878 if id:
879 applied.append(l)
880 series.append(file)
881 if datastart == None:
882 self.ui.warn("No saved patch data found\n")
883 return 1
884 self.ui.warn("restoring status: %s\n" % lines[0])
885 self.full_series = series
886 self.applied = applied
887 self.read_series(self.full_series)
888 self.series_dirty = 1
889 self.applied_dirty = 1
890 heads = repo.changelog.heads()
891 if delete:
892 if rev not in heads:
893 self.ui.warn("save entry has children, leaving it alone\n")
894 else:
895 self.ui.warn("removing save entry %s\n" % hg.short(rev))
896 pp = repo.dirstate.parents()
897 if rev in pp:
898 update = True
899 else:
900 update = False
901 self.strip(repo, rev, update=update, backup='strip')
902 if qpp:
903 self.ui.warn("saved queue repository parents: %s %s\n" %
904 (hg.short(qpp[0]), hg.short(qpp[1])))
905 if qupdate:
906 print "queue directory updating"
907 r = self.qrepo()
908 if not r:
909 self.ui.warn("Unable to load queue repository\n")
910 return 1
911 r.update(qpp[0], allow=False, force=True)
912
913 def save(self, repo, msg=None):
914 if len(self.applied) == 0:
915 self.ui.warn("save: no patches applied, exiting\n")
916 return 1
917 if self.issaveline(self.applied[-1]):
918 self.ui.warn("status is already saved\n")
919 return 1
920
921 ar = [ ':' + x for x in self.full_series ]
922 if not msg:
923 msg = "hg patches saved state"
924 else:
925 msg = "hg patches: " + msg.rstrip('\r\n')
926 r = self.qrepo()
927 if r:
928 pp = r.dirstate.parents()
929 msg += "\nDirstate: %s %s" % (hg.hex(pp[0]), hg.hex(pp[1]))
930 msg += "\n\nPatch Data:\n"
931 text = msg + "\n".join(self.applied) + '\n' + (ar and "\n".join(ar)
932 + '\n' or "")
933 n = repo.commit(None, text, user=None, force=1)
934 if not n:
935 self.ui.warn("repo commit failed\n")
936 return 1
937 self.applied.append(revlog.hex(n) + ":" + '.hg.patches.save.line')
938 self.applied_dirty = 1
939
940 def series_end(self):
941 end = 0
942 if len(self.applied) > 0:
943 (top, p) = self.applied[-1].split(':')
944 try:
945 end = self.series.index(p)
946 except ValueError:
947 return 0
948 return end + 1
949 return end
950
951 def qapplied(self, repo, patch=None):
952 if patch and patch not in self.series:
953 self.ui.warn("%s not in the series file\n" % patch)
954 sys.exit(1)
955 if not patch:
956 end = len(self.applied)
957 else:
958 end = self.series.index(patch) + 1
959 for x in xrange(end):
960 p = self.appliedname(x)
961 self.ui.write("%s\n" % p)
962
963 def appliedname(self, index):
964 p = self.applied[index]
965 if not self.ui.verbose:
966 p = p.split(':')[1]
967 return p
968
969 def top(self, repo):
970 if len(self.applied):
971 p = self.appliedname(-1)
972 self.ui.write(p + '\n')
973 else:
974 self.ui.write("No patches applied\n")
975
976 def next(self, repo):
977 end = self.series_end()
978 if end == len(self.series):
979 self.ui.write("All patches applied\n")
980 else:
981 self.ui.write(self.series[end] + '\n')
982
983 def prev(self, repo):
984 if len(self.applied) > 1:
985 p = self.appliedname(-2)
986 self.ui.write(p + '\n')
987 elif len(self.applied) == 1:
988 self.ui.write("Only one patch applied\n")
989 else:
990 self.ui.write("No patches applied\n")
991
992 def qimport(self, repo, files, patch=None, existing=None, force=None):
993 if len(files) > 1 and patch:
994 self.ui.warn("-n option not valid when importing multiple files\n")
995 sys.exit(1)
996 i = 0
997 for filename in files:
998 if existing:
999 if not patch:
1000 patch = filename
1001 if not os.path.isfile(os.path.join(self.path, patch)):
1002 self.ui.warn("patch %s does not exist\n" % patch)
1003 sys.exit(1)
1004 else:
1005 try:
1006 text = file(filename).read()
1007 except IOError:
1008 self.ui.warn("Unable to read %s\n" % patch)
1009 sys.exit(1)
1010 if not patch:
1011 patch = os.path.split(filename)[1]
1012 if not force and os.path.isfile(os.path.join(self.path, patch)):
1013 self.ui.warn("patch %s already exists\n" % patch)
1014 sys.exit(1)
1015 patchf = self.opener(os.path.join(self.path, patch), "w")
1016 patchf.write(text)
1017 if patch in self.series:
1018 self.ui.warn("patch %s is already in the series file\n" % patch)
1019 sys.exit(1)
1020 index = self.series_end() + i
1021 self.full_series[index:index] = [patch]
1022 self.read_series(self.full_series)
1023 self.ui.warn("adding %s to series file\n" % patch)
1024 i += 1
1025 patch = None
1026 self.series_dirty = 1
1027
1028 def delete(ui, repo, patch, **opts):
1029 """remove a patch from the series file"""
1030 q = repomap[repo]
1031 q.delete(repo, patch)
1032 q.save_dirty()
1033 return 0
1034
1035 def applied(ui, repo, patch=None, **opts):
1036 """print the patches already applied"""
1037 repomap[repo].qapplied(repo, patch)
1038 return 0
1039
1040 def unapplied(ui, repo, patch=None, **opts):
1041 """print the patches not yet applied"""
1042 repomap[repo].unapplied(repo, patch)
1043 return 0
1044
1045 def qimport(ui, repo, *filename, **opts):
1046 """import a patch"""
1047 q = repomap[repo]
1048 q.qimport(repo, filename, patch=opts['name'],
1049 existing=opts['existing'], force=opts['force'])
1050 q.save_dirty()
1051 return 0
1052
1053 def init(ui, repo, **opts):
1054 """init a new queue repository"""
1055 q = repomap[repo]
1056 r = q.init(repo, create=opts['create_repo'])
1057 q.save_dirty()
1058 if r:
1059 fp = r.wopener('.hgignore', 'w')
1060 print >> fp, 'syntax: glob'
1061 print >> fp, 'status'
1062 fp.close()
1063 r.wopener('series', 'w').close()
1064 r.add(['.hgignore', 'series'])
1065 return 0
1066
1067 def commit(ui, repo, *pats, **opts):
1068 q = repomap[repo]
1069 r = q.qrepo()
1070 if not r: raise util.Abort('no queue repository')
1071 commands.commit(r.ui, r, *pats, **opts)
1072
1073 def series(ui, repo, **opts):
1074 """print the entire series file"""
1075 repomap[repo].qseries(repo, missing=opts['missing'])
1076 return 0
1077
1078 def top(ui, repo, **opts):
1079 """print the name of the current patch"""
1080 repomap[repo].top(repo)
1081 return 0
1082
1083 def next(ui, repo, **opts):
1084 """print the name of the next patch"""
1085 repomap[repo].next(repo)
1086 return 0
1087
1088 def prev(ui, repo, **opts):
1089 """print the name of the previous patch"""
1090 repomap[repo].prev(repo)
1091 return 0
1092
1093 def new(ui, repo, patch, **opts):
1094 """create a new patch"""
1095 q = repomap[repo]
1096 q.new(repo, patch, msg=opts['message'], force=opts['force'])
1097 q.save_dirty()
1098 return 0
1099
1100 def refresh(ui, repo, **opts):
1101 """update the current patch"""
1102 q = repomap[repo]
1103 q.refresh(repo, short=opts['short'])
1104 q.save_dirty()
1105 return 0
1106
1107 def diff(ui, repo, *files, **opts):
1108 """diff of the current patch"""
1109 repomap[repo].diff(repo, files)
1110 return 0
1111
1112 def lastsavename(path):
1113 (dir, base) = os.path.split(path)
1114 names = os.listdir(dir)
1115 namere = re.compile("%s.([0-9]+)" % base)
1116 max = None
1117 maxname = None
1118 for f in names:
1119 m = namere.match(f)
1120 if m:
1121 index = int(m.group(1))
1122 if max == None or index > max:
1123 max = index
1124 maxname = f
1125 if maxname:
1126 return (os.path.join(dir, maxname), max)
1127 return (None, None)
1128
1129 def savename(path):
1130 (last, index) = lastsavename(path)
1131 if last is None:
1132 index = 0
1133 newpath = path + ".%d" % (index + 1)
1134 return newpath
1135
1136 def push(ui, repo, patch=None, **opts):
1137 """push the next patch onto the stack"""
1138 q = repomap[repo]
1139 mergeq = None
1140
1141 if opts['all']:
1142 patch = q.series[-1]
1143 if opts['merge']:
1144 if opts['name']:
1145 newpath = opts['name']
1146 else:
1147 newpath, i = lastsavename(q.path)
1148 if not newpath:
1149 ui.warn("no saved queues found, please use -n\n")
1150 return 1
1151 mergeq = queue(ui, repo.join(""), newpath)
1152 ui.warn("merging with queue at: %s\n" % mergeq.path)
1153 ret = q.push(repo, patch, force=opts['force'], list=opts['list'],
1154 mergeq=mergeq)
1155 q.save_dirty()
1156 return ret
1157
1158 def pop(ui, repo, patch=None, **opts):
1159 """pop the current patch off the stack"""
1160 localupdate = True
1161 if opts['name']:
1162 q = queue(ui, repo.join(""), repo.join(opts['name']))
1163 ui.warn('using patch queue: %s\n' % q.path)
1164 localupdate = False
1165 else:
1166 q = repomap[repo]
1167 if opts['all'] and len(q.applied) > 0:
1168 patch = q.applied[0].split(':')[1]
1169 q.pop(repo, patch, force=opts['force'], update=localupdate)
1170 q.save_dirty()
1171 return 0
1172
1173 def restore(ui, repo, rev, **opts):
1174 """restore the queue state saved by a rev"""
1175 rev = repo.lookup(rev)
1176 q = repomap[repo]
1177 q.restore(repo, rev, delete=opts['delete'],
1178 qupdate=opts['update'])
1179 q.save_dirty()
1180 return 0
1181
1182 def save(ui, repo, **opts):
1183 """save current queue state"""
1184 q = repomap[repo]
1185 ret = q.save(repo, msg=opts['message'])
1186 if ret:
1187 return ret
1188 q.save_dirty()
1189 if opts['copy']:
1190 path = q.path
1191 if opts['name']:
1192 newpath = os.path.join(q.basepath, opts['name'])
1193 if os.path.exists(newpath):
1194 if not os.path.isdir(newpath):
1195 ui.warn("destination %s exists and is not a directory\n" %
1196 newpath)
1197 sys.exit(1)
1198 if not opts['force']:
1199 ui.warn("destination %s exists, use -f to force\n" %
1200 newpath)
1201 sys.exit(1)
1202 else:
1203 newpath = savename(path)
1204 ui.warn("copy %s to %s\n" % (path, newpath))
1205 util.copyfiles(path, newpath)
1206 if opts['empty']:
1207 try:
1208 os.unlink(q.status_path)
1209 except:
1210 pass
1211 return 0
1212
1213 def strip(ui, repo, rev, **opts):
1214 """strip a revision and all later revs on the same branch"""
1215 rev = repo.lookup(rev)
1216 backup = 'all'
1217 if opts['backup']:
1218 backup = 'strip'
1219 elif opts['nobackup']:
1220 backup = 'none'
1221 repomap[repo].strip(repo, rev, backup=backup)
1222 return 0
1223
1224 def version(ui, q=None):
1225 """print the version number"""
1226 ui.write("mq version %s\n" % versionstr)
1227 return 0
1228
1229 def reposetup(ui, repo):
1230 repomap[repo] = queue(ui, repo.join(""))
1231
1232 cmdtable = {
1233 "qapplied": (applied, [], 'hg qapplied [patch]'),
1234 "qcommit|qci":
1235 (commit,
1236 [('A', 'addremove', None, _('run addremove during commit')),
1237 ('I', 'include', [], _('include names matching the given patterns')),
1238 ('X', 'exclude', [], _('exclude names matching the given patterns')),
1239 ('m', 'message', '', _('use <text> as commit message')),
1240 ('l', 'logfile', '', _('read the commit message from <file>')),
1241 ('d', 'date', '', _('record datecode as commit date')),
1242 ('u', 'user', '', _('record user as commiter'))],
1243 'hg qcommit [options] [files]'),
1244 "^qdiff": (diff, [], 'hg qdiff [files]'),
1245 "qdelete": (delete, [], 'hg qdelete [patch]'),
1246 "^qimport":
1247 (qimport,
1248 [('e', 'existing', None, 'import file in patch dir'),
1249 ('n', 'name', '', 'patch file name'),
1250 ('f', 'force', None, 'overwrite existing files')],
1251 'hg qimport'),
1252 "^qinit":
1253 (init,
1254 [('c', 'create-repo', None, 'create patch repository')],
1255 'hg [-c] qinit'),
1256 "qnew":
1257 (new,
1258 [('m', 'message', '', 'commit message'),
1259 ('f', 'force', None, 'force')],
1260 'hg qnew [-m message ] patch'),
1261 "qnext": (next, [], 'hg qnext'),
1262 "qprev": (prev, [], 'hg qprev'),
1263 "^qpop":
1264 (pop,
1265 [('a', 'all', None, 'pop all patches'),
1266 ('n', 'name', '', 'queue name to pop'),
1267 ('f', 'force', None, 'forget any local changes')],
1268 'hg qpop [options] [patch/index]'),
1269 "^qpush":
1270 (push,
1271 [('f', 'force', None, 'apply if the patch has rejects'),
1272 ('l', 'list', None, 'list patch name in commit text'),
1273 ('a', 'all', None, 'apply all patches'),
1274 ('m', 'merge', None, 'merge from another queue'),
1275 ('n', 'name', '', 'merge queue name')],
1276 'hg qpush [options] [patch/index]'),
1277 "^qrefresh":
1278 (refresh,
1279 [('s', 'short', None, 'short refresh')],
1280 'hg qrefresh'),
1281 "qrestore":
1282 (restore,
1283 [('d', 'delete', None, 'delete save entry'),
1284 ('u', 'update', None, 'update queue working dir')],
1285 'hg qrestore rev'),
1286 "qsave":
1287 (save,
1288 [('m', 'message', '', 'commit message'),
1289 ('c', 'copy', None, 'copy patch directory'),
1290 ('n', 'name', '', 'copy directory name'),
1291 ('e', 'empty', None, 'clear queue status file'),
1292 ('f', 'force', None, 'force copy')],
1293 'hg qsave'),
1294 "qseries":
1295 (series,
1296 [('m', 'missing', None, 'print patches not in series')],
1297 'hg qseries'),
1298 "^strip":
1299 (strip,
1300 [('f', 'force', None, 'force multi-head removal'),
1301 ('b', 'backup', None, 'bundle unrelated changesets'),
1302 ('n', 'nobackup', None, 'no backups')],
1303 'hg strip rev'),
1304 "qtop": (top, [], 'hg qtop'),
1305 "qunapplied": (unapplied, [], 'hg qunapplied [patch]'),
1306 "qversion": (version, [], 'hg qversion')
1307 }
1308
1 NO CONTENT: new file 100644, binary diff hidden
NO CONTENT: new file 100644, binary diff hidden
@@ -0,0 +1,48 b''
1 body { font-family: sans-serif; font-size: 12px; margin:0px; border:solid #d9d8d1; border-width:1px; margin:10px; }
2 a { color:#0000cc; }
3 a:hover, a:visited, a:active { color:#880000; }
4 div.page_header { height:25px; padding:8px; font-size:18px; font-weight:bold; background-color:#d9d8d1; }
5 div.page_header a:visited { color:#0000cc; }
6 div.page_header a:hover { color:#880000; }
7 div.page_nav { padding:8px; }
8 div.page_nav a:visited { color:#0000cc; }
9 div.page_path { padding:8px; border:solid #d9d8d1; border-width:0px 0px 1px}
10 div.page_footer { height:17px; padding:4px 8px; background-color: #d9d8d1; }
11 div.page_footer_text { float:left; color:#555555; font-style:italic; }
12 div.page_body { padding:8px; }
13 div.title, a.title {
14 display:block; padding:6px 8px;
15 font-weight:bold; background-color:#edece6; text-decoration:none; color:#000000;
16 }
17 a.title:hover { background-color: #d9d8d1; }
18 div.title_text { padding:6px 0px; border: solid #d9d8d1; border-width:0px 0px 1px; }
19 div.log_body { padding:8px 8px 8px 150px; }
20 span.age { position:relative; float:left; width:142px; font-style:italic; }
21 div.log_link {
22 padding:0px 8px;
23 font-size:10px; font-family:sans-serif; font-style:normal;
24 position:relative; float:left; width:136px;
25 }
26 div.list_head { padding:6px 8px 4px; border:solid #d9d8d1; border-width:1px 0px 0px; font-style:italic; }
27 a.list { text-decoration:none; color:#000000; }
28 a.list:hover { text-decoration:underline; color:#880000; }
29 table { padding:8px 4px; }
30 th { padding:2px 5px; font-size:12px; text-align:left; }
31 tr.light:hover, .parity0:hover { background-color:#edece6; }
32 tr.dark, .parity1 { background-color:#f6f6f0; }
33 tr.dark:hover, .parity1:hover { background-color:#edece6; }
34 td { padding:2px 5px; font-size:12px; vertical-align:top; }
35 td.link { padding:2px 5px; font-family:sans-serif; font-size:10px; }
36 div.pre { font-family:monospace; font-size:12px; white-space:pre; }
37 div.diff_info { font-family:monospace; color:#000099; background-color:#edece6; font-style:italic; }
38 div.index_include { border:solid #d9d8d1; border-width:0px 0px 1px; padding:12px 8px; }
39 div.search { margin:4px 8px; position:absolute; top:56px; right:12px }
40 .linenr { color:#999999; text-decoration:none }
41 a.rss_logo {
42 float:right; padding:3px 0px; width:35px; line-height:10px;
43 border:1px solid; border-color:#fcc7a5 #7d3302 #3e1a01 #ff954e;
44 color:#ffffff; background-color:#ff6600;
45 font-weight:bold; font-family:sans-serif; font-size:10px;
46 text-align:center; text-decoration:none;
47 }
48 a.rss_logo:hover { background-color:#ee5500; }
@@ -0,0 +1,70 b''
1 a { text-decoration:none; }
2 .parity0 { background-color: #dddddd; }
3 .parity1 { background-color: #eeeeee; }
4 .lineno { width: 60px; color: #aaaaaa; font-size: smaller;
5 text-align: right; padding-right:1em; }
6 .plusline { color: green; }
7 .minusline { color: red; }
8 .atline { color: purple; }
9 .annotate { font-size: smaller; text-align: right; padding-right: 1em; }
10 .buttons a {
11 background-color: #666666;
12 padding: 2pt;
13 color: white;
14 font-family: sans;
15 font-weight: bold;
16 }
17 .navigate a {
18 background-color: #ccc;
19 padding: 2pt;
20 font-family: sans;
21 color: black;
22 }
23
24 .metatag {
25 background-color: #888888;
26 color: white;
27 text-align: right;
28 }
29
30 /* Common */
31 pre { margin: 0; }
32
33 .logo {
34 background-color: #333;
35 padding: 4pt;
36 margin: 8pt 0 8pt 8pt;
37 font-family: sans;
38 font-size: 60%;
39 color: white;
40 float: right;
41 clear: right;
42 text-align: left;
43 }
44
45 .logo a {
46 font-weight: bold;
47 font-size: 150%;
48 color: #999;
49 }
50
51 /* Changelog entries */
52 .changelogEntry { width: 100%; }
53 .changelogEntry th { font-weight: normal; text-align: right; vertical-align: top; }
54 .changelogEntry th.age, .changelogEntry th.firstline { font-weight: bold; }
55 .changelogEntry th.firstline { text-align: left; width: inherit; }
56
57 /* Tag entries */
58 #tagEntries { list-style: none; margin: 0; padding: 0; }
59 #tagEntries .tagEntry { list-style: none; margin: 0; padding: 0; }
60 #tagEntries .tagEntry span.node { font-family: monospace; }
61
62 /* Changeset entry */
63 #changesetEntry { }
64 #changesetEntry th { font-weight: normal; background-color: #888; color: #fff; text-align: right; }
65 #changesetEntry th.files, #changesetEntry th.description { vertical-align: top; }
66
67 /* File diff view */
68 #filediffEntry { }
69 #filediffEntry th { font-weight: normal; background-color: #888; color: #fff; text-align: right; }
70
@@ -0,0 +1,6 b''
1 <item>
2 <title>#tag|escape#</title>
3 <link>#url#?cs=#node|short#</link>
4 <description><![CDATA[#tag|strip|escape|addbreaks#]]></description>
5 <pubDate>#date|rfc822date#</pubDate>
6 </item>
@@ -0,0 +1,6 b''
1 #header#
2 <title>#repo|escape#: tags </title>
3 <description>#repo|escape# tag history</description>
4 #entriesnotip%tagentry#
5 </channel>
6 </rss>
@@ -0,0 +1,32 b''
1 #!/bin/sh
2 #
3 # Corrupt an hg repo with a pull started during an aborted commit
4 #
5
6 # Create two repos, so that one of them can pull from the other one.
7 hg init source
8 cd source
9 touch foo
10 hg add foo
11 hg ci -m 'add foo'
12 hg clone . ../corrupted
13 echo >> foo
14 hg ci -m 'change foo'
15
16 # Add a hook to wait 5 seconds and then abort the commit
17 cd ../corrupted
18 echo '[hooks]' >> .hg/hgrc
19 echo 'pretxncommit = sleep 5; exit 1' >> .hg/hgrc
20
21 # start a commit...
22 touch bar
23 hg add bar
24 hg ci -m 'add bar' &
25
26 # ... and start a pull while the commit is still running
27 sleep 1
28 hg pull ../source 2>/dev/null
29
30 # see what happened
31 wait
32 hg verify
@@ -0,0 +1,15 b''
1 pulling from ../source
2 abort: pretxncommit hook exited with status 1
3 transaction abort!
4 rollback completed
5 searching for changes
6 adding changesets
7 adding manifests
8 adding file changes
9 added 1 changesets with 1 changes to 1 files
10 (run 'hg update' to get a working copy)
11 checking changesets
12 checking manifests
13 crosschecking files in changesets and manifests
14 checking files
15 1 files, 2 changesets, 2 total revisions
@@ -0,0 +1,41 b''
1 #!/bin/sh
2 #
3 # Corrupt an hg repo with two pulls.
4 #
5
6 # create one repo with a long history
7 hg init source1
8 cd source1
9 touch foo
10 hg add foo
11 for i in 1 2 3 4 5 6 7 8 9 10; do
12 echo $i >> foo
13 hg ci -m $i
14 done
15 cd ..
16
17 # create one repo with a shorter history
18 hg clone -r 0 source1 source2
19 cd source2
20 echo a >> foo
21 hg ci -m a
22 cd ..
23
24 # create a third repo to pull both other repos into it
25 hg init corrupted
26 cd corrupted
27 # use a hook to make the second pull start while the first one is still running
28 echo '[hooks]' >> .hg/hgrc
29 echo 'prechangegroup = sleep 5' >> .hg/hgrc
30
31 # start a pull...
32 hg pull ../source1 &
33
34 # ... and start another pull before the first one has finished
35 sleep 1
36 hg pull ../source2 2>/dev/null
37
38 # see the result
39 wait
40 hg verify
41
@@ -0,0 +1,24 b''
1 requesting all changes
2 adding changesets
3 adding manifests
4 adding file changes
5 added 1 changesets with 1 changes to 1 files
6 pulling from ../source2
7 pulling from ../source1
8 requesting all changes
9 adding changesets
10 adding manifests
11 adding file changes
12 added 10 changesets with 10 changes to 1 files
13 (run 'hg update' to get a working copy)
14 searching for changes
15 adding changesets
16 adding manifests
17 adding file changes
18 added 1 changesets with 1 changes to 1 files (+1 heads)
19 (run 'hg update' to get a working copy)
20 checking changesets
21 checking manifests
22 crosschecking files in changesets and manifests
23 checking files
24 1 files, 11 changesets, 11 total revisions
@@ -0,0 +1,61 b''
1 #!/bin/bash
2
3 hg init test
4 cd test
5 cat >>afile <<EOF
6 0
7 EOF
8 hg add afile
9 hg commit -m "0.0"
10 cat >>afile <<EOF
11 1
12 EOF
13 hg commit -m "0.1"
14 cat >>afile <<EOF
15 2
16 EOF
17 hg commit -m "0.2"
18 cat >>afile <<EOF
19 3
20 EOF
21 hg commit -m "0.3"
22 hg update -C 0
23 cat >>afile <<EOF
24 1
25 EOF
26 hg commit -m "1.1"
27 cat >>afile <<EOF
28 2
29 EOF
30 hg commit -m "1.2"
31 cat >fred <<EOF
32 a line
33 EOF
34 cat >>afile <<EOF
35 3
36 EOF
37 hg add fred
38 hg commit -m "1.3"
39 hg mv afile adifferentfile
40 hg commit -m "1.3m"
41 hg update -C 3
42 hg mv afile anotherfile
43 hg commit -m "0.3m"
44 hg debugindex .hg/data/afile.i
45 hg debugindex .hg/data/adifferentfile.i
46 hg debugindex .hg/data/anotherfile.i
47 hg debugindex .hg/data/fred.i
48 hg debugindex .hg/00manifest.i
49 hg verify
50 cd ..
51 for i in 0 1 2 3 4 5 6 7 8; do
52 mkdir test-"$i"
53 hg --cwd test-"$i" init
54 hg -R test push -r "$i" test-"$i"
55 cd test-"$i"
56 hg verify
57 cd ..
58 done
59 cd test-8
60 hg pull ../test-7
61 hg verify
@@ -0,0 +1,135 b''
1 rev offset length base linkrev nodeid p1 p2
2 0 0 3 0 0 362fef284ce2 000000000000 000000000000
3 1 3 5 1 1 125144f7e028 362fef284ce2 000000000000
4 2 8 7 2 2 4c982badb186 125144f7e028 000000000000
5 3 15 9 3 3 19b1fc555737 4c982badb186 000000000000
6 rev offset length base linkrev nodeid p1 p2
7 0 0 75 0 7 905359268f77 000000000000 000000000000
8 rev offset length base linkrev nodeid p1 p2
9 0 0 75 0 8 905359268f77 000000000000 000000000000
10 rev offset length base linkrev nodeid p1 p2
11 0 0 8 0 6 12ab3bcc5ea4 000000000000 000000000000
12 rev offset length base linkrev nodeid p1 p2
13 0 0 48 0 0 43eadb1d2d06 000000000000 000000000000
14 1 48 48 1 1 8b89697eba2c 43eadb1d2d06 000000000000
15 2 96 48 2 2 626a32663c2f 8b89697eba2c 000000000000
16 3 144 48 3 3 f54c32f13478 626a32663c2f 000000000000
17 4 192 58 3 6 de68e904d169 626a32663c2f 000000000000
18 5 250 68 3 7 3b45cc2ab868 de68e904d169 000000000000
19 6 318 54 6 8 24d86153a002 f54c32f13478 000000000000
20 checking changesets
21 checking manifests
22 crosschecking files in changesets and manifests
23 checking files
24 4 files, 9 changesets, 7 total revisions
25 pushing to test-0
26 searching for changes
27 adding changesets
28 adding manifests
29 adding file changes
30 added 1 changesets with 1 changes to 1 files
31 checking changesets
32 checking manifests
33 crosschecking files in changesets and manifests
34 checking files
35 1 files, 1 changesets, 1 total revisions
36 pushing to test-1
37 searching for changes
38 adding changesets
39 adding manifests
40 adding file changes
41 added 2 changesets with 2 changes to 1 files
42 checking changesets
43 checking manifests
44 crosschecking files in changesets and manifests
45 checking files
46 1 files, 2 changesets, 2 total revisions
47 pushing to test-2
48 searching for changes
49 adding changesets
50 adding manifests
51 adding file changes
52 added 3 changesets with 3 changes to 1 files
53 checking changesets
54 checking manifests
55 crosschecking files in changesets and manifests
56 checking files
57 1 files, 3 changesets, 3 total revisions
58 pushing to test-3
59 searching for changes
60 adding changesets
61 adding manifests
62 adding file changes
63 added 4 changesets with 4 changes to 1 files
64 checking changesets
65 checking manifests
66 crosschecking files in changesets and manifests
67 checking files
68 1 files, 4 changesets, 4 total revisions
69 pushing to test-4
70 searching for changes
71 adding changesets
72 adding manifests
73 adding file changes
74 added 2 changesets with 2 changes to 1 files
75 checking changesets
76 checking manifests
77 crosschecking files in changesets and manifests
78 checking files
79 1 files, 2 changesets, 2 total revisions
80 pushing to test-5
81 searching for changes
82 adding changesets
83 adding manifests
84 adding file changes
85 added 3 changesets with 3 changes to 1 files
86 checking changesets
87 checking manifests
88 crosschecking files in changesets and manifests
89 checking files
90 1 files, 3 changesets, 3 total revisions
91 pushing to test-6
92 searching for changes
93 adding changesets
94 adding manifests
95 adding file changes
96 added 4 changesets with 5 changes to 2 files
97 checking changesets
98 checking manifests
99 crosschecking files in changesets and manifests
100 checking files
101 2 files, 4 changesets, 5 total revisions
102 pushing to test-7
103 searching for changes
104 adding changesets
105 adding manifests
106 adding file changes
107 added 5 changesets with 6 changes to 3 files
108 checking changesets
109 checking manifests
110 crosschecking files in changesets and manifests
111 checking files
112 3 files, 5 changesets, 6 total revisions
113 pushing to test-8
114 searching for changes
115 adding changesets
116 adding manifests
117 adding file changes
118 added 5 changesets with 5 changes to 2 files
119 checking changesets
120 checking manifests
121 crosschecking files in changesets and manifests
122 checking files
123 2 files, 5 changesets, 5 total revisions
124 pulling from ../test-7
125 searching for changes
126 adding changesets
127 adding manifests
128 adding file changes
129 added 4 changesets with 2 changes to 3 files (+1 heads)
130 (run 'hg update' to get a working copy)
131 checking changesets
132 checking manifests
133 crosschecking files in changesets and manifests
134 checking files
135 4 files, 9 changesets, 7 total revisions
@@ -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
@@ -3,23 +3,26 b' shopt -s extglob'
3 _hg_command_list()
3 _hg_command_list()
4 {
4 {
5 "$hg" --debug help 2>/dev/null | \
5 "$hg" --debug help 2>/dev/null | \
6 awk 'function command_line(line) {
6 awk -F', ' '/^list of commands:/ {commands=1}
7 gsub(/,/, "", line)
7 commands==1 && /^ [^ ]/ {
8 gsub(/:.*/, "", line)
8 line = substr($0, 2)
9 split(line, aliases)
9 colon = index(line, ":")
10 if (colon > 0)
11 line = substr(line, 1, colon-1)
12 n = split(line, aliases)
10 command = aliases[1]
13 command = aliases[1]
11 delete aliases[1]
14 if (index(command, "debug") == 1) {
15 for (i=1; i<=n; i++)
16 debug[j++] = aliases[i]
17 next
18 }
12 print command
19 print command
13 for (i in aliases)
20 for (i=2; i<=n; i++)
14 if (index(command, aliases[i]) != 1)
21 if (index(command, aliases[i]) != 1)
15 print aliases[i]
22 print aliases[i]
16 }
23 }
17 /^list of commands:/ {commands=1}
18 commands && /^ debug/ {a[i++] = $0; next;}
19 commands && /^ [^ ]/ {command_line($0)}
20 /^global options:/ {exit 0}
24 /^global options:/ {exit 0}
21 END {for (i in a) command_line(a[i])}'
25 END {for (i in debug) print debug[i]}'
22
23 }
26 }
24
27
25 _hg_option_list()
28 _hg_option_list()
@@ -187,7 +187,7 b' class bisect(object):'
187 check_clean(self.ui, self.repo)
187 check_clean(self.ui, self.repo)
188 rev = self.next()
188 rev = self.next()
189 self.ui.write("Now testing %s\n" % hg.hex(rev))
189 self.ui.write("Now testing %s\n" % hg.hex(rev))
190 return self.repo.update(rev, allow=True, force=True)
190 return self.repo.update(rev, force=True)
191
191
192 def good(self, rev):
192 def good(self, rev):
193 self.goodrevs.append(rev)
193 self.goodrevs.append(rev)
@@ -232,7 +232,7 b' def test(ui, repo, rev):'
232 b.good(new_rev)
232 b.good(new_rev)
233 ui.write("it is good\n")
233 ui.write("it is good\n")
234 anc = b.ancestors()
234 anc = b.ancestors()
235 repo.update(new_rev, allow=True, force=True)
235 repo.update(new_rev, force=True)
236 for v in anc:
236 for v in anc:
237 if v != rev:
237 if v != rev:
238 ui.warn("fail to found cset! :(\n")
238 ui.warn("fail to found cset! :(\n")
@@ -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
This diff has been collapsed as it changes many lines, (616 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,586 +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 identify::
298 Print a short summary of the current state of the repo.
299
300 This summary identifies the repository state using one or two parent
301 hash identifiers, followed by a "+" if there are uncommitted changes
302 in the working directory, followed by a list of tags for this revision.
303
304 aliases: id
305
306 import [-p <n> -b <base> -f] <patches>::
307 Import a list of patches and commit them individually.
308
309 If there are outstanding changes in the working directory, import
310 will abort unless given the -f flag.
311
312 If a patch looks like a mail message (its first line starts with
313 "From " or looks like an RFC822 header), it will not be applied
314 unless the -f option is used. The importer neither parses nor
315 discards mail headers, so use -f only to override the "mailness"
316 safety check, not to import a real mail message.
317
318 options:
319 -p, --strip <n> directory strip option for patch. This has the same
320 meaning as the corresponding patch option
321 -b <path> base directory to read patches from
322 -f, --force skip check for outstanding uncommitted changes
323
324 aliases: patch
325
326 incoming [-p] [source]::
327 Show new changesets found in the specified repo or the default
328 pull repo. These are the changesets that would be pulled if a pull
329 was requested.
330
331 Currently only local repositories are supported.
332
333 options:
334 -p, --patch show patch
335
336 aliases: in
337
338 init [dest]::
339 Initialize a new repository in the given directory. If the given
340 directory does not exist, it is created.
341
342 If no directory is given, the current directory is used.
343
344 locate [options] [files]::
345 Print all files under Mercurial control whose names match the
346 given patterns.
347
348 This command searches the current directory and its
349 subdirectories. To search an entire repository, move to the root
350 of the repository.
351
352 If no patterns are given to match, this command prints all file
353 names.
354
355 If you want to feed the output of this command into the "xargs"
356 command, use the "-0" option to both this command and "xargs".
357 This will avoid the problem of "xargs" treating single filenames
358 that contain white space as multiple filenames.
359
360 options:
361
362 -0, --print0 end filenames with NUL, for use with xargs
363 -f, --fullpath print complete paths from the filesystem root
364 -I, --include <pat> include names matching the given patterns
365 -r, --rev <rev> search the repository as it stood at rev
366 -X, --exclude <pat> exclude names matching the given patterns
367
368 log [-r revision ...] [-p] [files]::
369 Print the revision history of the specified files or the entire project.
370
371 By default this command outputs: changeset id and hash, tags,
372 parents, user, date and time, and a summary for each commit. The
373 -v switch adds some more detail, such as changed files, manifest
374 hashes or message signatures.
375
376 options:
377 -I, --include <pat> include names matching the given patterns
378 -X, --exclude <pat> exclude names matching the given patterns
379 -r, --rev <A> show the specified revision or range
380 -p, --patch show patch
381
382 aliases: history
383
384 manifest [revision]::
385 Print a list of version controlled files for the given revision.
386
387 The manifest is the list of files being version controlled. If no revision
388 is given then the tip is used.
389
390 outgoing [-p] [dest]::
391 Show changesets not found in the specified destination repo or the
392 default push repo. These are the changesets that would be pushed
393 if a push was requested.
394
395 See pull for valid source format details.
396
397 options:
398 -p, --patch show patch
399
400 aliases: out
401
402 parents::
403 Print the working directory's parent revisions.
404
405 paths [NAME]::
406 Show definition of symbolic path name NAME. If no name is given, show
407 definition of available names.
408
409 Path names are defined in the [paths] section of /etc/mercurial/hgrc
410 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
411
412 pull <repository path>::
413 Pull changes from a remote repository to a local one.
414
415 This finds all changes from the repository at the specified path
416 or URL and adds them to the local repository. By default, this
417 does not update the copy of the project in the working directory.
418
419 Valid URLs are of the form:
420
421 local/filesystem/path
422 http://[user@]host[:port][/path]
423 https://[user@]host[:port][/path]
424 ssh://[user@]host[:port][/path]
425
426 SSH requires an accessible shell account on the destination machine
427 and a copy of hg in the remote path. With SSH, paths are relative
428 to the remote user's home directory by default; use two slashes at
429 the start of a path to specify it as relative to the filesystem root.
430
431 options:
432 -u, --update update the working directory to tip after pull
433 -e, --ssh specify ssh command to use
434 --remotecmd specify hg command to run on the remote side
435
436 push <destination>::
437 Push changes from the local repository to the given destination.
438
439 This is the symmetrical operation for pull. It helps to move
440 changes from the current repository to a different one. If the
441 destination is local this is identical to a pull in that directory
442 from the current one.
443
444 By default, push will refuse to run if it detects the result would
445 increase the number of remote heads. This generally indicates the
446 the client has forgotten to sync and merge before pushing.
447
448 Valid URLs are of the form:
449
450 local/filesystem/path
451 ssh://[user@]host[:port][/path]
452
453 SSH requires an accessible shell account on the destination
454 machine and a copy of hg in the remote path.
455
456 options:
457
458 -f, --force force update
459 -e, --ssh specify ssh command to use
460 --remotecmd specify hg command to run on the remote side
461
462 rawcommit [-p -d -u -F -m -l]::
463 Lowlevel commit, for use in helper scripts. (DEPRECATED)
464
465 This command is not intended to be used by normal users, as it is
466 primarily useful for importing from other SCMs.
467
468 This command is now deprecated and will be removed in a future
469 release, please use debugsetparents and commit instead.
470
471 recover::
472 Recover from an interrupted commit or pull.
473
474 This command tries to fix the repository status after an interrupted
475 operation. It should only be necessary when Mercurial suggests it.
476
477 remove [options] [files ...]::
478 Schedule the indicated files for removal from the repository.
479
480 This command schedules the files to be removed at the next commit.
481 This only removes files from the current branch, not from the
482 entire project history. If the files still exist in the working
483 directory, they will be deleted from it.
484
485 aliases: rm
486
487 rename <source ...> <dest>::
488 Mark dest as copies of sources; mark sources for deletion. If
489 dest is a directory, copies are put in that directory. If dest is
490 a file, there can only be one source.
491
492 By default, this command copies the contents of files as they
493 stand in the working directory. If invoked with --after, the
494 operation is recorded, but no copying is performed.
495
496 This command takes effect in the next commit.
497
498 NOTE: This command should be treated as experimental. While it
499 should properly record rename files, this information is not yet
500 fully used by merge, nor fully reported by log.
501
502 Options:
503 -A, --after record a rename that has already occurred
504 -f, --force forcibly copy over an existing managed file
505
506 aliases: mv
507
508 revert [names ...]::
509 The revert command has two modes of operation.
510
511 In its default mode, it reverts any uncommitted modifications made
512 to the named files or directories. This restores the contents of
513 the affected files to an unmodified state.
514
515 Using the -r option, it reverts the given files or directories to
516 their state as of an earlier revision. This can be helpful to "roll
517 back" some or all of a change that should not have been committed.
518
519 Revert modifies the working directory. It does not commit any
520 changes, or change the parent of the current working directory.
521
522 If a file has been deleted, it is recreated. If the executable
523 mode of a file was changed, it is reset.
524
525 If a directory is given, all files in that directory and its
526 subdirectories are reverted.
527
528 If no arguments are given, all files in the current directory and
529 its subdirectories are reverted.
530
531 options:
532 -r, --rev <rev> revision to revert to
533 -n, --nonrecursive do not recurse into subdirectories
534
535 root::
536 Print the root directory of the current repository.
537
538 serve [options]::
539 Start a local HTTP repository browser and pull server.
540
541 By default, the server logs accesses to stdout and errors to
542 stderr. Use the "-A" and "-E" options to log to files.
543
544 options:
545 -A, --accesslog <file> name of access log file to write to
546 -E, --errorlog <file> name of error log file to write to
547 -a, --address <addr> address to use
548 -p, --port <n> port to use (default: 8000)
549 -n, --name <name> name to show in web pages (default: working dir)
550 -t, --templatedir <path> web templates to use
551 -6, --ipv6 use IPv6 in addition to IPv4
552
553 status [options] [files]::
554 Show changed files in the working directory. If no names are
555 given, all files are shown. Otherwise, only files matching the
556 given names are shown.
557
558 The codes used to show the status of files are:
559
560 M = changed
561 A = added
562 R = removed
563 ? = not tracked
564
565 options:
566
567 -m, --modified show only modified files
568 -a, --added show only added files
569 -r, --removed show only removed files
570 -u, --unknown show only unknown (not tracked) files
571 -n, --no-status hide status prefix
572 -0, --print0 end filenames with NUL, for use with xargs
573 -I, --include <pat> include names matching the given patterns
574 -X, --exclude <pat> exclude names matching the given patterns
575
576 tag [-l -m <text> -d <datecode> -u <user>] <name> [revision]::
577 Name a particular revision using <name>.
578
579 Tags are used to name particular revisions of the repository and are
580 very useful to compare different revision, to go back to significant
581 earlier versions or to mark branch points as releases, etc.
582
583 If no revision is given, the tip is used.
584
585 To facilitate version control, distribution, and merging of tags,
586 they are stored as a file named ".hgtags" which is managed
587 similarly to other project files and can be hand-edited if
588 necessary.
589
590 options:
591 -l, --local make the tag local
592 -m, --message <text> message for tag commit log entry
593 -d, --date <datecode> datecode for commit
594 -u, --user <user> user for commit
595
596 Note: Local tags are not version-controlled or distributed and are
597 stored in the .hg/localtags file. If there exists a local tag and
598 a public tag with the same name, local tag is used.
599
600 tags::
601 List the repository tags.
602
603 This lists both regular and local tags.
604
605 tip [-p]::
606 Show the tip revision.
607
608 options:
609 -p, --patch show patch
610
611 unbundle <file>::
612 (EXPERIMENTAL)
613
614 Apply a compressed changegroup file generated by the bundle
615 command.
616
617 undo::
618 Undo the last commit or pull transaction.
619
620 Roll back the last pull or commit transaction on the
621 repository, restoring the project to its earlier state.
622
623 This command should be used with care. There is only one level of
624 undo and there is no redo.
625
626 This command is not intended for use on public repositories. Once
627 a change is visible for pull by other users, undoing it locally is
628 ineffective.
629
630 update [-m -C] [revision]::
631 Update the working directory to the specified revision.
632
633 By default, update will refuse to run if doing so would require
634 merging or discarding local changes.
635
636 With the -m option, a merge will be performed.
637
638 With the -C option, local changes will be lost.
639
640 options:
641 -m, --merge allow merging of branches
642 -C, --clean overwrite locally modified files
643
644 aliases: up checkout co
645
646 verify::
647 Verify the integrity of the current repository.
648
649 This will perform an extensive check of the repository's
650 integrity, validating the hashes and checksums of each entry in
651 the changelog, manifest, and tracked files, as well as the
652 integrity of their crosslinks and indices.
653
39
654 FILE NAME PATTERNS
40 FILE NAME PATTERNS
655 ------------------
41 ------------------
@@ -247,6 +247,9 b' ui::'
247 remote command to use for clone/push/pull operations. Default is 'hg'.
247 remote command to use for clone/push/pull operations. Default is 'hg'.
248 ssh;;
248 ssh;;
249 command to use for SSH connections. Default is 'ssh'.
249 command to use for SSH connections. Default is 'ssh'.
250 timeout;;
251 The timeout used when a lock is held (in seconds), a negative value
252 means no timeout. Default is 600.
250 username;;
253 username;;
251 The committer of a changeset created when running "commit".
254 The committer of a changeset created when running "commit".
252 Typically a person's name and email address, e.g. "Fred Widget
255 Typically a person's name and email address, e.g. "Fred Widget
@@ -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 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,7 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)
153 subj = '[PATCH %d of %d] %s' % (idx, total, desc[0].strip())
144 subj = '[PATCH %d of %d] %s' % (idx, total, desc[0].strip())
154 if subj.endswith('.'): subj = subj[:-1]
145 if subj.endswith('.'): subj = subj[:-1]
155 msg['Subject'] = subj
146 msg['Subject'] = subj
@@ -194,7 +185,7 b' def patchbomb(ui, repo, *revs, **opts):'
194 sender = (opts['from'] or ui.config('patchbomb', 'from') or
185 sender = (opts['from'] or ui.config('patchbomb', 'from') or
195 prompt('From', ui.username()))
186 prompt('From', ui.username()))
196
187
197 msg = MIMEMultipart()
188 msg = email.MIMEMultipart.MIMEMultipart()
198 msg['Subject'] = '[PATCH 0 of %d] %s' % (
189 msg['Subject'] = '[PATCH 0 of %d] %s' % (
199 len(patches),
190 len(patches),
200 opts['subject'] or
191 opts['subject'] or
@@ -217,13 +208,13 b' def patchbomb(ui, repo, *revs, **opts):'
217 if l == '.': break
208 if l == '.': break
218 body.append(l)
209 body.append(l)
219
210
220 msg.attach(MIMEText('\n'.join(body) + '\n'))
211 msg.attach(email.MIMEText.MIMEText('\n'.join(body) + '\n'))
221
212
222 ui.write('\n')
213 ui.write('\n')
223
214
224 if opts['diffstat']:
215 if opts['diffstat']:
225 d = cdiffstat(_('Final summary:\n'), jumbo)
216 d = cdiffstat(_('Final summary:\n'), jumbo)
226 if d: msg.attach(MIMEText(d))
217 if d: msg.attach(email.MIMEText.MIMEText(d))
227
218
228 msgs.insert(0, msg)
219 msgs.insert(0, msg)
229
220
@@ -241,7 +232,7 b' def patchbomb(ui, repo, *revs, **opts):'
241 s.login(username, password)
232 s.login(username, password)
242 parent = None
233 parent = None
243 tz = time.strftime('%z')
234 tz = time.strftime('%z')
244 sender_addr = parseaddr(sender)[1]
235 sender_addr = email.Utils.parseaddr(sender)[1]
245 for m in msgs:
236 for m in msgs:
246 try:
237 try:
247 m['Message-Id'] = genmsgid(m['X-Mercurial-Node'])
238 m['Message-Id'] = genmsgid(m['X-Mercurial-Node'])
@@ -17,28 +17,32 b' fi'
17
17
18 # find decent versions of our utilities, insisting on the GNU versions where we
18 # find decent versions of our utilities, insisting on the GNU versions where we
19 # need to
19 # need to
20 MERGE=merge
20 MERGE="merge"
21 DIFF3=gdiff3
21 DIFF3="gdiff3"
22 DIFF=gdiff
22 DIFF="gdiff"
23 PATCH=gpatch
23 PATCH="gpatch"
24
24
25 type $MERGE >/dev/null 2>&1 || MERGE=
25 type "$MERGE" >/dev/null 2>&1 || MERGE=
26 type $DIFF3 >/dev/null 2>&1 || DIFF3=diff3
26 type "$DIFF3" >/dev/null 2>&1 || DIFF3="diff3"
27 type $DIFF >/dev/null 2>&1 || DIFF=diff
28 type $PATCH >/dev/null 2>&1 || PATCH=patch
29 $DIFF3 --version >/dev/null 2>&1 || DIFF3=
27 $DIFF3 --version >/dev/null 2>&1 || DIFF3=
28 type "$DIFF" >/dev/null 2>&1 || DIFF="diff"
29 type "$DIFF" >/dev/null 2>&1 || DIFF=
30 type "$PATCH" >/dev/null 2>&1 || PATCH="patch"
31 type "$PATCH" >/dev/null 2>&1 || PATCH=
30
32
31 # find optional visual utilities
33 # find optional visual utilities
32 FILEMERGE='/Developer/Applications/Utilities/FileMerge.app/Contents/MacOS/FileMerge'
34 FILEMERGE="/Developer/Applications/Utilities/FileMerge.app/Contents/MacOS/FileMerge"
33 KDIFF3=kdiff3
35 KDIFF3="kdiff3"
34 TKDIFF=tkdiff
36 TKDIFF="tkdiff"
37 MELD="meld"
35
38
36 type $FILEMERGE >/dev/null 2>&1 || FILEMERGE=
39 type "$FILEMERGE" >/dev/null 2>&1 || FILEMERGE=
37 type $KDIFF3 >/dev/null 2>&1 || KDIFF3=
40 type "$KDIFF3" >/dev/null 2>&1 || KDIFF3=
38 type $TKDIFF >/dev/null 2>&1 || TKDIFF=
41 type "$TKDIFF" >/dev/null 2>&1 || TKDIFF=
42 type "$MELD" >/dev/null 2>&1 || MELD=
39
43
40 # random part of names
44 # random part of names
41 RAND="$RANDOM.$RANDOM.$RANDOM.$$"
45 RAND="$RANDOM$RANDOM"
42
46
43 # temporary directory for diff+patch merge
47 # temporary directory for diff+patch merge
44 HGTMP="${TMPDIR-/tmp}/hgmerge.$RAND"
48 HGTMP="${TMPDIR-/tmp}/hgmerge.$RAND"
@@ -68,6 +72,19 b' failure() {'
68 exit 1
72 exit 1
69 }
73 }
70
74
75 # Ask if the merge was successful
76 ask_if_merged() {
77 while true; do
78 echo "$LOCAL seems unchanged."
79 echo "Was the merge successful? [y/n]"
80 read answer
81 case "$answer" in
82 y*|Y*) success;;
83 n*|N*) failure;;
84 esac
85 done
86 }
87
71 # Clean up when interrupted
88 # Clean up when interrupted
72 trap "failure" 1 2 3 6 15 # HUP INT QUIT ABRT TERM
89 trap "failure" 1 2 3 6 15 # HUP INT QUIT ABRT TERM
73
90
@@ -76,18 +93,16 b' mv "$LOCAL" "$BACKUP"'
76 cp "$BACKUP" "$LOCAL"
93 cp "$BACKUP" "$LOCAL"
77
94
78 # Attempt to do a non-interactive merge
95 # Attempt to do a non-interactive merge
79 if [ -n "$MERGE" ]; then
96 if [ -n "$MERGE" -o -n "$DIFF3" ]; then
80 $MERGE "$LOCAL" "$BASE" "$OTHER" 2> /dev/null && success
97 if [ -n "$MERGE" ]; then
81 cp "$BACKUP" "$LOCAL"
98 $MERGE "$LOCAL" "$BASE" "$OTHER" 2> /dev/null && success
82 elif [ -n "$DIFF3" ]; then
99 elif [ -n "$DIFF3" ]; then
83 echo $DIFF3 -m "$BACKUP" "$BASE" "$OTHER"
100 $DIFF3 -m "$BACKUP" "$BASE" "$OTHER" > "$LOCAL" && success
84 $DIFF3 -m "$BACKUP" "$BASE" "$OTHER" > "$LOCAL" && success
101 fi
85 if [ $? -eq 2 ]; then
102 if [ $? -gt 1 ]; then
86 echo "$DIFF3 failed! Exiting." 1>&2
103 echo "automatic merge failed! Exiting." 1>&2
87 cp "$BACKUP" "$LOCAL"
88 failure
104 failure
89 fi
105 fi
90 cp "$BACKUP" "$LOCAL"
91 fi
106 fi
92
107
93 # on MacOS X try FileMerge.app, shipped with Apple's developer tools
108 # on MacOS X try FileMerge.app, shipped with Apple's developer tools
@@ -97,71 +112,66 b' if [ -n "$FILEMERGE" ]; then'
97 # filemerge prefers the right by default
112 # filemerge prefers the right by default
98 $FILEMERGE -left "$OTHER" -right "$LOCAL" -ancestor "$BASE" -merge "$LOCAL"
113 $FILEMERGE -left "$OTHER" -right "$LOCAL" -ancestor "$BASE" -merge "$LOCAL"
99 [ $? -ne 0 ] && echo "FileMerge failed to launch" && failure
114 [ $? -ne 0 ] && echo "FileMerge failed to launch" && failure
100 if test "$LOCAL" -nt "$CHGTEST"
115 test "$LOCAL" -nt "$CHGTEST" && success || ask_if_merged
101 then
102 success
103 else
104 echo "$LOCAL seems unchanged. Was the merge successful?"
105 select answer in yes no
106 do
107 test "$answer" == "yes" && success || failure
108 done
109 fi
110 failure
111 fi
116 fi
112
117
113 if [ -n "$DISPLAY" ]; then
118 if [ -n "$DISPLAY" ]; then
114 # try using kdiff3, which is fairly nice
119 # try using kdiff3, which is fairly nice
115 if [ -n "$KDIFF3" ]; then
120 if [ -n "$KDIFF3" ]; then
116 $KDIFF3 --auto "$BASE" "$LOCAL" "$OTHER" -o "$LOCAL" || failure
121 $KDIFF3 --auto "$BASE" "$BACKUP" "$OTHER" -o "$LOCAL" || failure
117 success
122 success
118 fi
123 fi
119
124
120 # try using tkdiff, which is a bit less sophisticated
125 # try using tkdiff, which is a bit less sophisticated
121 if [ -n "$TKDIFF" ]; then
126 if [ -n "$TKDIFF" ]; then
122 $TKDIFF "$LOCAL" "$OTHER" -a "$BASE" -o "$LOCAL" || failure
127 $TKDIFF "$BACKUP" "$OTHER" -a "$BASE" -o "$LOCAL" || failure
123 success
128 success
129 fi
130
131 if [ -n "$MELD" ]; then
132 cp "$BACKUP" "$CHGTEST"
133 # protect our feet - meld allows us to save to the left file
134 cp "$BACKUP" "$LOCAL.tmp.$RAND"
135 # Meld doesn't have automatic merging, so to reduce intervention
136 # use the file with conflicts
137 $MELD "$LOCAL.tmp.$RAND" "$LOCAL" "$OTHER" || failure
138 # Also it doesn't return good error code
139 test "$LOCAL" -nt "$CHGTEST" && success || ask_if_merged
124 fi
140 fi
125 fi
141 fi
126
142
127 # Attempt to do a merge with $EDITOR
143 # Attempt to do a merge with $EDITOR
128 if [ -n "$MERGE" ]; then
144 if [ -n "$MERGE" -o -n "$DIFF3" ]; then
129 echo "conflicts detected in $LOCAL"
130 $MERGE "$LOCAL" "$BASE" "$OTHER" 2>/dev/null || $EDITOR "$LOCAL"
131 success
132 fi
133
134 if [ -n "$DIFF3" ]; then
135 echo "conflicts detected in $LOCAL"
145 echo "conflicts detected in $LOCAL"
136 $DIFF3 -m "$BACKUP" "$BASE" "$OTHER" > "$LOCAL" || {
146 cp "$BACKUP" "$CHGTEST"
137 case $? in
147 $EDITOR "$LOCAL" || failure
138 1)
148 # Some editors do not return meaningful error codes
139 $EDITOR "$LOCAL" ;;
149 # Do not take any chances
140 2) echo "$DIFF3 failed! Exiting." 1>&2
150 test "$LOCAL" -nt "$CHGTEST" && success || ask_if_merged
141 cp "$BACKUP" "$LOCAL"
142 failure ;;
143 esac
144 success
145 }
146 fi
151 fi
147
152
148 # attempt to manually merge with diff and patch
153 # attempt to manually merge with diff and patch
149 if [ -n "$DIFF" -a -n "$PATCH" ]; then
154 if [ -n "$DIFF" -a -n "$PATCH" ]; then
150
155
151 (umask 077 && mkdir "$HGTMP") || {
156 (umask 077 && mkdir "$HGTMP") || {
152 echo "Could not create temporary directory $HGTMP" 1>&2
157 echo "Could not create temporary directory $HGTMP" 1>&2
153 failure
158 failure
154 }
159 }
155
160
156 $DIFF -u "$BASE" "$OTHER" > "$HGTMP/diff" || :
161 $DIFF -u "$BASE" "$OTHER" > "$HGTMP/diff" || :
157 if $PATCH "$LOCAL" < "$HGTMP/diff"; then
162 if $PATCH "$LOCAL" < "$HGTMP/diff"; then
158 success
163 success
159 else
164 else
160 # If rejects are empty after using the editor, merge was ok
165 # If rejects are empty after using the editor, merge was ok
161 $EDITOR "$LOCAL" "$LOCAL.rej" && test -s "$LOCAL.rej" || success
166 $EDITOR "$LOCAL" "$LOCAL.rej" || failure
167 test -s "$LOCAL.rej" || success
162 fi
168 fi
163 failure
169 failure
164 fi
170 fi
165
171
166 echo "hgmerge: unable to find merge, tkdiff, kdiff3, or diff+patch!"
172 echo
173 echo "hgmerge: unable to find any merge utility!"
174 echo "supported programs:"
175 echo "merge, FileMerge, tkdiff, kdiff3, meld, diff+patch"
176 echo
167 failure
177 failure
@@ -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'
@@ -17,6 +17,10 b''
17 #define inline
17 #define inline
18 #endif
18 #endif
19
19
20 #ifdef __SUNPRO_C
21 # define inline
22 #endif
23
20 #ifdef _WIN32
24 #ifdef _WIN32
21 #ifdef _MSC_VER
25 #ifdef _MSC_VER
22 #define inline __inline
26 #define inline __inline
@@ -82,6 +82,21 b' def walkchangerevs(ui, repo, pats, opts)'
82 "iter", rev, None: in-order traversal of the revs earlier iterated
82 "iter", rev, None: in-order traversal of the revs earlier iterated
83 over with "add" - use to display data'''
83 over with "add" - use to display data'''
84
84
85 def increasing_windows(start, end, windowsize=8, sizelimit=512):
86 if start < end:
87 while start < end:
88 yield start, min(windowsize, end-start)
89 start += windowsize
90 if windowsize < sizelimit:
91 windowsize *= 2
92 else:
93 while start > end:
94 yield start, min(windowsize, start-end-1)
95 start -= windowsize
96 if windowsize < sizelimit:
97 windowsize *= 2
98
99
85 files, matchfn, anypats = matchpats(repo, pats, opts)
100 files, matchfn, anypats = matchpats(repo, pats, opts)
86
101
87 if repo.changelog.count() == 0:
102 if repo.changelog.count() == 0:
@@ -90,7 +105,6 b' def walkchangerevs(ui, repo, pats, opts)'
90 revs = map(int, revrange(ui, repo, opts['rev'] or ['tip:0']))
105 revs = map(int, revrange(ui, repo, opts['rev'] or ['tip:0']))
91 wanted = {}
106 wanted = {}
92 slowpath = anypats
107 slowpath = anypats
93 window = 300
94 fncache = {}
108 fncache = {}
95
109
96 chcache = {}
110 chcache = {}
@@ -106,17 +120,17 b' def walkchangerevs(ui, repo, pats, opts)'
106 if not slowpath:
120 if not slowpath:
107 # Only files, no patterns. Check the history of each file.
121 # Only files, no patterns. Check the history of each file.
108 def filerevgen(filelog):
122 def filerevgen(filelog):
109 for i in xrange(filelog.count() - 1, -1, -window):
123 for i, window in increasing_windows(filelog.count()-1, -1):
110 revs = []
124 revs = []
111 for j in xrange(max(0, i - window), i + 1):
125 for j in xrange(i - window, i + 1):
112 revs.append(filelog.linkrev(filelog.node(j)))
126 revs.append(filelog.linkrev(filelog.node(j)))
113 revs.reverse()
127 revs.reverse()
114 for rev in revs:
128 for rev in revs:
115 yield rev
129 yield rev
116
130
117 minrev, maxrev = min(revs), max(revs)
131 minrev, maxrev = min(revs), max(revs)
118 for file in files:
132 for file_ in files:
119 filelog = repo.file(file)
133 filelog = repo.file(file_)
120 # A zero count may be a directory or deleted file, so
134 # A zero count may be a directory or deleted file, so
121 # try to find matching entries on the slow path.
135 # try to find matching entries on the slow path.
122 if filelog.count() == 0:
136 if filelog.count() == 0:
@@ -127,13 +141,13 b' def walkchangerevs(ui, repo, pats, opts)'
127 if rev < minrev:
141 if rev < minrev:
128 break
142 break
129 fncache.setdefault(rev, [])
143 fncache.setdefault(rev, [])
130 fncache[rev].append(file)
144 fncache[rev].append(file_)
131 wanted[rev] = 1
145 wanted[rev] = 1
132 if slowpath:
146 if slowpath:
133 # The slow path checks files modified in every changeset.
147 # The slow path checks files modified in every changeset.
134 def changerevgen():
148 def changerevgen():
135 for i in xrange(repo.changelog.count() - 1, -1, -window):
149 for i, window in increasing_windows(repo.changelog.count()-1, -1):
136 for j in xrange(max(0, i - window), i + 1):
150 for j in xrange(i - window, i + 1):
137 yield j, getchange(j)[3]
151 yield j, getchange(j)[3]
138
152
139 for rev, changefiles in changerevgen():
153 for rev, changefiles in changerevgen():
@@ -143,9 +157,9 b' def walkchangerevs(ui, repo, pats, opts)'
143 wanted[rev] = 1
157 wanted[rev] = 1
144
158
145 def iterate():
159 def iterate():
146 for i in xrange(0, len(revs), window):
160 for i, window in increasing_windows(0, len(revs)):
147 yield 'window', revs[0] < revs[-1], revs[-1]
161 yield 'window', revs[0] < revs[-1], revs[-1]
148 nrevs = [rev for rev in revs[i:min(i+window, len(revs))]
162 nrevs = [rev for rev in revs[i:i+window]
149 if rev in wanted]
163 if rev in wanted]
150 srevs = list(nrevs)
164 srevs = list(nrevs)
151 srevs.sort()
165 srevs.sort()
@@ -262,6 +276,14 b' def make_file(repo, r, pat, node=None,'
262
276
263 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,
264 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
265 if not changes:
287 if not changes:
266 changes = repo.changes(node1, node2, files, match=match)
288 changes = repo.changes(node1, node2, files, match=match)
267 modified, added, removed, deleted, unknown = changes
289 modified, added, removed, deleted, unknown = changes
@@ -280,8 +302,6 b' def dodiff(fp, ui, repo, node1, node2, f'
280 return repo.file(f).read(mmap2[f])
302 return repo.file(f).read(mmap2[f])
281 else:
303 else:
282 date2 = util.datestr()
304 date2 = util.datestr()
283 if not node1:
284 node1 = repo.dirstate.parents()[0]
285 def read(f):
305 def read(f):
286 return repo.wread(f)
306 return repo.wread(f)
287
307
@@ -291,10 +311,6 b' def dodiff(fp, ui, repo, node1, node2, f'
291 hexfunc = ui.verbose and hex or short
311 hexfunc = ui.verbose and hex or short
292 r = [hexfunc(node) for node in [node1, node2] if node]
312 r = [hexfunc(node) for node in [node1, node2] if node]
293
313
294 change = repo.changelog.read(node1)
295 mmap = repo.manifest.read(change[0])
296 date1 = util.datestr(change[2])
297
298 diffopts = ui.diffopts()
314 diffopts = ui.diffopts()
299 showfunc = opts.get('show_function') or diffopts['showfunc']
315 showfunc = opts.get('show_function') or diffopts['showfunc']
300 ignorews = opts.get('ignore_all_space') or diffopts['ignorews']
316 ignorews = opts.get('ignore_all_space') or diffopts['ignorews']
@@ -447,7 +463,6 b' def help_(ui, cmd=None, with_version=Fal'
447 f = f.lstrip("^")
463 f = f.lstrip("^")
448 if not ui.debugflag and f.startswith("debug"):
464 if not ui.debugflag and f.startswith("debug"):
449 continue
465 continue
450 d = ""
451 doc = e[0].__doc__
466 doc = e[0].__doc__
452 if not doc:
467 if not doc:
453 doc = _("(No help text available)")
468 doc = _("(No help text available)")
@@ -681,6 +696,8 b' def clone(ui, source, dest=None, **opts)'
681 such as AFS, implement hardlinking incorrectly, but do not report
696 such as AFS, implement hardlinking incorrectly, but do not report
682 errors. In these cases, use the --pull option to avoid
697 errors. In these cases, use the --pull option to avoid
683 hardlinking.
698 hardlinking.
699
700 See pull for valid source format details.
684 """
701 """
685 if dest is None:
702 if dest is None:
686 dest = os.path.basename(os.path.normpath(source))
703 dest = os.path.basename(os.path.normpath(source))
@@ -725,8 +742,8 b' def clone(ui, source, dest=None, **opts)'
725 # can end up with extra data in the cloned revlogs that's
742 # can end up with extra data in the cloned revlogs that's
726 # not pointed to by changesets, thus causing verify to
743 # not pointed to by changesets, thus causing verify to
727 # fail
744 # fail
728 l1 = lock.lock(os.path.join(source, ".hg", "lock"))
745 l1 = other.lock()
729 except OSError:
746 except lock.LockException:
730 copy = False
747 copy = False
731
748
732 if copy:
749 if copy:
@@ -808,7 +825,8 b' def commit(ui, repo, *pats, **opts):'
808 except ValueError, inst:
825 except ValueError, inst:
809 raise util.Abort(str(inst))
826 raise util.Abort(str(inst))
810
827
811 def docopy(ui, repo, pats, opts):
828 def docopy(ui, repo, pats, opts, wlock):
829 # called with the repo lock held
812 cwd = repo.getcwd()
830 cwd = repo.getcwd()
813 errors = 0
831 errors = 0
814 copied = []
832 copied = []
@@ -818,14 +836,19 b' def docopy(ui, repo, pats, opts):'
818 reasons = {'?': _('is not managed'),
836 reasons = {'?': _('is not managed'),
819 'a': _('has been marked for add'),
837 'a': _('has been marked for add'),
820 'r': _('has been marked for remove')}
838 'r': _('has been marked for remove')}
821 reason = reasons.get(repo.dirstate.state(abs))
839 state = repo.dirstate.state(abs)
840 reason = reasons.get(state)
822 if reason:
841 if reason:
842 if state == 'a':
843 origsrc = repo.dirstate.copied(abs)
844 if origsrc is not None:
845 return origsrc
823 if exact:
846 if exact:
824 ui.warn(_('%s: not copying - file %s\n') % (rel, reason))
847 ui.warn(_('%s: not copying - file %s\n') % (rel, reason))
825 else:
848 else:
826 return True
849 return abs
827
850
828 def copy(abssrc, relsrc, target, exact):
851 def copy(origsrc, abssrc, relsrc, target, exact):
829 abstarget = util.canonpath(repo.root, cwd, target)
852 abstarget = util.canonpath(repo.root, cwd, target)
830 reltarget = util.pathto(cwd, abstarget)
853 reltarget = util.pathto(cwd, abstarget)
831 prevsrc = targets.get(abstarget)
854 prevsrc = targets.get(abstarget)
@@ -849,8 +872,16 b' def docopy(ui, repo, pats, opts):'
849 if not os.path.isdir(targetdir):
872 if not os.path.isdir(targetdir):
850 os.makedirs(targetdir)
873 os.makedirs(targetdir)
851 try:
874 try:
852 shutil.copyfile(relsrc, reltarget)
875 restore = repo.dirstate.state(abstarget) == 'r'
853 shutil.copymode(relsrc, reltarget)
876 if restore:
877 repo.undelete([abstarget], wlock)
878 try:
879 shutil.copyfile(relsrc, reltarget)
880 shutil.copymode(relsrc, reltarget)
881 restore = False
882 finally:
883 if restore:
884 repo.remove([abstarget], wlock)
854 except shutil.Error, inst:
885 except shutil.Error, inst:
855 raise util.Abort(str(inst))
886 raise util.Abort(str(inst))
856 except IOError, inst:
887 except IOError, inst:
@@ -864,7 +895,8 b' def docopy(ui, repo, pats, opts):'
864 if ui.verbose or not exact:
895 if ui.verbose or not exact:
865 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
896 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
866 targets[abstarget] = abssrc
897 targets[abstarget] = abssrc
867 repo.copy(abssrc, abstarget)
898 if abstarget != origsrc:
899 repo.copy(origsrc, abstarget, wlock)
868 copied.append((abssrc, relsrc, exact))
900 copied.append((abssrc, relsrc, exact))
869
901
870 def targetpathfn(pat, dest, srcs):
902 def targetpathfn(pat, dest, srcs):
@@ -938,8 +970,9 b' def docopy(ui, repo, pats, opts):'
938 for pat in pats:
970 for pat in pats:
939 srcs = []
971 srcs = []
940 for tag, abssrc, relsrc, exact in walk(repo, [pat], opts):
972 for tag, abssrc, relsrc, exact in walk(repo, [pat], opts):
941 if okaytocopy(abssrc, relsrc, exact):
973 origsrc = okaytocopy(abssrc, relsrc, exact)
942 srcs.append((abssrc, relsrc, exact))
974 if origsrc:
975 srcs.append((origsrc, abssrc, relsrc, exact))
943 if not srcs:
976 if not srcs:
944 continue
977 continue
945 copylist.append((tfn(pat, dest, srcs), srcs))
978 copylist.append((tfn(pat, dest, srcs), srcs))
@@ -947,8 +980,8 b' def docopy(ui, repo, pats, opts):'
947 raise util.Abort(_('no files to copy'))
980 raise util.Abort(_('no files to copy'))
948
981
949 for targetpath, srcs in copylist:
982 for targetpath, srcs in copylist:
950 for abssrc, relsrc, exact in srcs:
983 for origsrc, abssrc, relsrc, exact in srcs:
951 copy(abssrc, relsrc, targetpath(abssrc), exact)
984 copy(origsrc, abssrc, relsrc, targetpath(abssrc), exact)
952
985
953 if errors:
986 if errors:
954 ui.warn(_('(consider using --after)\n'))
987 ui.warn(_('(consider using --after)\n'))
@@ -971,15 +1004,32 b' def copy(ui, repo, *pats, **opts):'
971 should properly record copied files, this information is not yet
1004 should properly record copied files, this information is not yet
972 fully used by merge, nor fully reported by log.
1005 fully used by merge, nor fully reported by log.
973 """
1006 """
974 errs, copied = docopy(ui, repo, pats, opts)
1007 try:
1008 wlock = repo.wlock(0)
1009 errs, copied = docopy(ui, repo, pats, opts, wlock)
1010 except lock.LockHeld, inst:
1011 ui.warn(_("repository lock held by %s\n") % inst.args[0])
1012 errs = 1
975 return errs
1013 return errs
976
1014
977 def debugancestor(ui, index, rev1, rev2):
1015 def debugancestor(ui, index, rev1, rev2):
978 """find the ancestor revision of two revisions in a given index"""
1016 """find the ancestor revision of two revisions in a given index"""
979 r = revlog.revlog(util.opener(os.getcwd()), index, "")
1017 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index, "")
980 a = r.ancestor(r.lookup(rev1), r.lookup(rev2))
1018 a = r.ancestor(r.lookup(rev1), r.lookup(rev2))
981 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
1019 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
982
1020
1021 def debugrebuildstate(ui, repo, rev=None):
1022 """rebuild the dirstate as it would look like for the given revision"""
1023 if not rev:
1024 rev = repo.changelog.tip()
1025 else:
1026 rev = repo.lookup(rev)
1027 change = repo.changelog.read(rev)
1028 n = change[0]
1029 files = repo.manifest.readflags(n)
1030 wlock = repo.wlock()
1031 repo.dirstate.rebuild(rev, files.iteritems())
1032
983 def debugcheckstate(ui, repo):
1033 def debugcheckstate(ui, repo):
984 """validate the correctness of the current dirstate"""
1034 """validate the correctness of the current dirstate"""
985 parent1, parent2 = repo.dirstate.parents()
1035 parent1, parent2 = repo.dirstate.parents()
@@ -1050,7 +1100,8 b' def debugstate(ui, repo):'
1050
1100
1051 def debugdata(ui, file_, rev):
1101 def debugdata(ui, file_, rev):
1052 """dump the contents of an data file revision"""
1102 """dump the contents of an data file revision"""
1053 r = revlog.revlog(util.opener(os.getcwd()), file_[:-2] + ".i", file_)
1103 r = revlog.revlog(util.opener(os.getcwd(), audit=False),
1104 file_[:-2] + ".i", file_)
1054 try:
1105 try:
1055 ui.write(r.revision(r.lookup(rev)))
1106 ui.write(r.revision(r.lookup(rev)))
1056 except KeyError:
1107 except KeyError:
@@ -1058,7 +1109,7 b' def debugdata(ui, file_, rev):'
1058
1109
1059 def debugindex(ui, file_):
1110 def debugindex(ui, file_):
1060 """dump the contents of an index file"""
1111 """dump the contents of an index file"""
1061 r = revlog.revlog(util.opener(os.getcwd()), file_, "")
1112 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "")
1062 ui.write(" rev offset length base linkrev" +
1113 ui.write(" rev offset length base linkrev" +
1063 " nodeid p1 p2\n")
1114 " nodeid p1 p2\n")
1064 for i in range(r.count()):
1115 for i in range(r.count()):
@@ -1069,7 +1120,7 b' def debugindex(ui, file_):'
1069
1120
1070 def debugindexdot(ui, file_):
1121 def debugindexdot(ui, file_):
1071 """dump an index DAG as a .dot file"""
1122 """dump an index DAG as a .dot file"""
1072 r = revlog.revlog(util.opener(os.getcwd()), file_, "")
1123 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "")
1073 ui.write("digraph G {\n")
1124 ui.write("digraph G {\n")
1074 for i in range(r.count()):
1125 for i in range(r.count()):
1075 e = r.index[i]
1126 e = r.index[i]
@@ -1284,6 +1335,7 b' def grep(ui, repo, pattern, *pats, **opt'
1284 s = linestate(line, lnum, cstart, cend)
1335 s = linestate(line, lnum, cstart, cend)
1285 m[s] = s
1336 m[s] = s
1286
1337
1338 # FIXME: prev isn't used, why ?
1287 prev = {}
1339 prev = {}
1288 ucache = {}
1340 ucache = {}
1289 def display(fn, rev, states, prevstates):
1341 def display(fn, rev, states, prevstates):
@@ -1593,7 +1645,19 b' def log(ui, repo, *pats, **opts):'
1593 self.write(*args)
1645 self.write(*args)
1594 def __getattr__(self, key):
1646 def __getattr__(self, key):
1595 return getattr(self.ui, key)
1647 return getattr(self.ui, key)
1648
1596 changeiter, getchange, matchfn = walkchangerevs(ui, repo, pats, opts)
1649 changeiter, getchange, matchfn = walkchangerevs(ui, repo, pats, opts)
1650
1651 if opts['limit']:
1652 try:
1653 limit = int(opts['limit'])
1654 except ValueError:
1655 raise util.Abort(_('limit must be a positive integer'))
1656 if limit <= 0: raise util.Abort(_('limit must be positive'))
1657 else:
1658 limit = sys.maxint
1659 count = 0
1660
1597 for st, rev, fns in changeiter:
1661 for st, rev, fns in changeiter:
1598 if st == 'window':
1662 if st == 'window':
1599 du = dui(ui)
1663 du = dui(ui)
@@ -1607,7 +1671,6 b' def log(ui, repo, *pats, **opts):'
1607 if opts['only_merges'] and len(parents) != 2:
1671 if opts['only_merges'] and len(parents) != 2:
1608 continue
1672 continue
1609
1673
1610 br = None
1611 if opts['keyword']:
1674 if opts['keyword']:
1612 changes = getchange(rev)
1675 changes = getchange(rev)
1613 miss = 0
1676 miss = 0
@@ -1620,7 +1683,8 b' def log(ui, repo, *pats, **opts):'
1620 if miss:
1683 if miss:
1621 continue
1684 continue
1622
1685
1623 if opts['branch']:
1686 br = None
1687 if opts['branches']:
1624 br = repo.branchlookup([repo.changelog.node(rev)])
1688 br = repo.branchlookup([repo.changelog.node(rev)])
1625
1689
1626 show_changeset(du, repo, rev, brinfo=br)
1690 show_changeset(du, repo, rev, brinfo=br)
@@ -1629,8 +1693,11 b' def log(ui, repo, *pats, **opts):'
1629 dodiff(du, du, repo, prev, changenode, match=matchfn)
1693 dodiff(du, du, repo, prev, changenode, match=matchfn)
1630 du.write("\n\n")
1694 du.write("\n\n")
1631 elif st == 'iter':
1695 elif st == 'iter':
1632 for args in du.hunk[rev]:
1696 if count == limit: break
1633 ui.write(*args)
1697 if du.hunk[rev]:
1698 count += 1
1699 for args in du.hunk[rev]:
1700 ui.write(*args)
1634
1701
1635 def manifest(ui, repo, rev=None):
1702 def manifest(ui, repo, rev=None):
1636 """output the latest or given revision of the project manifest
1703 """output the latest or given revision of the project manifest
@@ -1664,6 +1731,8 b' def outgoing(ui, repo, dest="default-pus'
1664 Show changesets not found in the specified destination repo or the
1731 Show changesets not found in the specified destination repo or the
1665 default push repo. These are the changesets that would be pushed
1732 default push repo. These are the changesets that would be pushed
1666 if a push was requested.
1733 if a push was requested.
1734
1735 See pull for valid source format details.
1667 """
1736 """
1668 dest = ui.expandpath(dest, repo.root)
1737 dest = ui.expandpath(dest, repo.root)
1669 other = hg.repository(ui, dest)
1738 other = hg.repository(ui, dest)
@@ -1681,7 +1750,7 b' def outgoing(ui, repo, dest="default-pus'
1681 dodiff(ui, ui, repo, prev, n)
1750 dodiff(ui, ui, repo, prev, n)
1682 ui.write("\n")
1751 ui.write("\n")
1683
1752
1684 def parents(ui, repo, rev=None, branch=None):
1753 def parents(ui, repo, rev=None, branches=None):
1685 """show the parents of the working dir or revision
1754 """show the parents of the working dir or revision
1686
1755
1687 Print the working directory's parent revisions.
1756 Print the working directory's parent revisions.
@@ -1692,7 +1761,7 b' def parents(ui, repo, rev=None, branch=N'
1692 p = repo.dirstate.parents()
1761 p = repo.dirstate.parents()
1693
1762
1694 br = None
1763 br = None
1695 if branch is not None:
1764 if branches is not None:
1696 br = repo.branchlookup(p)
1765 br = repo.branchlookup(p)
1697 for n in p:
1766 for n in p:
1698 if n != nullid:
1767 if n != nullid:
@@ -1767,7 +1836,7 b' def pull(ui, repo, source="default", **o'
1767
1836
1768 return r
1837 return r
1769
1838
1770 def push(ui, repo, dest="default-push", force=False, ssh=None, remotecmd=None):
1839 def push(ui, repo, dest="default-push", **opts):
1771 """push changes to the specified destination
1840 """push changes to the specified destination
1772
1841
1773 Push changes from the local repository to the given destination.
1842 Push changes from the local repository to the given destination.
@@ -1792,18 +1861,22 b' def push(ui, repo, dest="default-push", '
1792 dest = ui.expandpath(dest, repo.root)
1861 dest = ui.expandpath(dest, repo.root)
1793 ui.status('pushing to %s\n' % (dest))
1862 ui.status('pushing to %s\n' % (dest))
1794
1863
1795 if ssh:
1864 if opts['ssh']:
1796 ui.setconfig("ui", "ssh", ssh)
1865 ui.setconfig("ui", "ssh", opts['ssh'])
1797 if remotecmd:
1866 if opts['remotecmd']:
1798 ui.setconfig("ui", "remotecmd", remotecmd)
1867 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
1799
1868
1800 other = hg.repository(ui, dest)
1869 other = hg.repository(ui, dest)
1801 r = repo.push(other, force)
1870 revs = None
1871 if opts['rev']:
1872 revs = [repo.lookup(rev) for rev in opts['rev']]
1873 r = repo.push(other, opts['force'], revs=revs)
1802 return r
1874 return r
1803
1875
1804 def rawcommit(ui, repo, *flist, **rc):
1876 def rawcommit(ui, repo, *flist, **rc):
1805 """raw commit interface (DEPRECATED)
1877 """raw commit interface (DEPRECATED)
1806
1878
1879 (DEPRECATED)
1807 Lowlevel commit, for use in helper scripts.
1880 Lowlevel commit, for use in helper scripts.
1808
1881
1809 This command is not intended to be used by normal users, as it is
1882 This command is not intended to be used by normal users, as it is
@@ -1896,21 +1969,33 b' def rename(ui, repo, *pats, **opts):'
1896 should properly record rename files, this information is not yet
1969 should properly record rename files, this information is not yet
1897 fully used by merge, nor fully reported by log.
1970 fully used by merge, nor fully reported by log.
1898 """
1971 """
1899 errs, copied = docopy(ui, repo, pats, opts)
1972 try:
1900 names = []
1973 wlock = repo.wlock(0)
1901 for abs, rel, exact in copied:
1974 errs, copied = docopy(ui, repo, pats, opts, wlock)
1902 if ui.verbose or not exact:
1975 names = []
1903 ui.status(_('removing %s\n') % rel)
1976 for abs, rel, exact in copied:
1904 names.append(abs)
1977 if ui.verbose or not exact:
1905 repo.remove(names, unlink=True)
1978 ui.status(_('removing %s\n') % rel)
1979 names.append(abs)
1980 repo.remove(names, True, wlock)
1981 except lock.LockHeld, inst:
1982 ui.warn(_("repository lock held by %s\n") % inst.args[0])
1983 errs = 1
1906 return errs
1984 return errs
1907
1985
1908 def revert(ui, repo, *pats, **opts):
1986 def revert(ui, repo, *pats, **opts):
1909 """revert modified files or dirs back to their unmodified states
1987 """revert modified files or dirs back to their unmodified states
1910
1988
1911 Revert any uncommitted modifications made to the named files or
1989 In its default mode, it reverts any uncommitted modifications made
1912 directories. This restores the contents of the affected files to
1990 to the named files or directories. This restores the contents of
1913 an unmodified state.
1991 the affected files to an unmodified state.
1992
1993 Using the -r option, it reverts the given files or directories to
1994 their state as of an earlier revision. This can be helpful to "roll
1995 back" some or all of a change that should not have been committed.
1996
1997 Revert modifies the working directory. It does not commit any
1998 changes, or change the parent of the current working directory.
1914
1999
1915 If a file has been deleted, it is recreated. If the executable
2000 If a file has been deleted, it is recreated. If the executable
1916 mode of a file was changed, it is reset.
2001 mode of a file was changed, it is reset.
@@ -1925,7 +2010,7 b' def revert(ui, repo, *pats, **opts):'
1925 files, choose, anypats = matchpats(repo, pats, opts)
2010 files, choose, anypats = matchpats(repo, pats, opts)
1926 modified, added, removed, deleted, unknown = repo.changes(match=choose)
2011 modified, added, removed, deleted, unknown = repo.changes(match=choose)
1927 repo.forget(added)
2012 repo.forget(added)
1928 repo.undelete(removed + deleted)
2013 repo.undelete(removed)
1929
2014
1930 return repo.update(node, False, True, choose, False)
2015 return repo.update(node, False, True, choose, False)
1931
2016
@@ -2022,6 +2107,16 b' def serve(ui, repo, **opts):'
2022 if opts[o]:
2107 if opts[o]:
2023 ui.setconfig("web", o, opts[o])
2108 ui.setconfig("web", o, opts[o])
2024
2109
2110 if opts['daemon'] and not opts['daemon_pipefds']:
2111 rfd, wfd = os.pipe()
2112 args = sys.argv[:]
2113 args.append('--daemon-pipefds=%d,%d' % (rfd, wfd))
2114 pid = os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
2115 args[0], args)
2116 os.close(wfd)
2117 os.read(rfd, 1)
2118 os._exit(0)
2119
2025 try:
2120 try:
2026 httpd = hgweb.create_server(repo)
2121 httpd = hgweb.create_server(repo)
2027 except socket.error, inst:
2122 except socket.error, inst:
@@ -2040,6 +2135,25 b' def serve(ui, repo, **opts):'
2040 ui.status(_('listening at http://%s:%d/\n') % (addr, port))
2135 ui.status(_('listening at http://%s:%d/\n') % (addr, port))
2041 else:
2136 else:
2042 ui.status(_('listening at http://%s/\n') % addr)
2137 ui.status(_('listening at http://%s/\n') % addr)
2138
2139 if opts['pid_file']:
2140 fp = open(opts['pid_file'], 'w')
2141 fp.write(str(os.getpid()))
2142 fp.close()
2143
2144 if opts['daemon_pipefds']:
2145 rfd, wfd = [int(x) for x in opts['daemon_pipefds'].split(',')]
2146 os.close(rfd)
2147 os.write(wfd, 'y')
2148 os.close(wfd)
2149 sys.stdout.flush()
2150 sys.stderr.flush()
2151 fd = os.open(util.nulldev, os.O_RDWR)
2152 if fd != 0: os.dup2(fd, 0)
2153 if fd != 1: os.dup2(fd, 1)
2154 if fd != 2: os.dup2(fd, 2)
2155 if fd not in (0, 1, 2): os.close(fd)
2156
2043 httpd.serve_forever()
2157 httpd.serve_forever()
2044
2158
2045 def status(ui, repo, *pats, **opts):
2159 def status(ui, repo, *pats, **opts):
@@ -2164,7 +2278,10 b' def tip(ui, repo, **opts):'
2164 Show the tip revision.
2278 Show the tip revision.
2165 """
2279 """
2166 n = repo.changelog.tip()
2280 n = repo.changelog.tip()
2167 show_changeset(ui, repo, changenode=n)
2281 br = None
2282 if opts['branches']:
2283 br = repo.branchlookup([n])
2284 show_changeset(ui, repo, changenode=n, brinfo=br)
2168 if opts['patch']:
2285 if opts['patch']:
2169 dodiff(ui, ui, repo, repo.changelog.parents(n)[0], n)
2286 dodiff(ui, ui, repo, repo.changelog.parents(n)[0], n)
2170
2287
@@ -2283,47 +2400,51 b' table = {'
2283 ('c', 'changeset', None, _('list the changeset')),
2400 ('c', 'changeset', None, _('list the changeset')),
2284 ('I', 'include', [], _('include names matching the given patterns')),
2401 ('I', 'include', [], _('include names matching the given patterns')),
2285 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2402 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2286 _('hg annotate [OPTION]... FILE...')),
2403 _('hg annotate [-r REV] [-a] [-u] [-d] [-n] [-c] FILE...')),
2287 "bundle":
2404 "bundle":
2288 (bundle,
2405 (bundle,
2289 [],
2406 [],
2290 _('hg bundle FILE DEST')),
2407 _('hg bundle FILE DEST')),
2291 "cat":
2408 "cat":
2292 (cat,
2409 (cat,
2293 [('I', 'include', [], _('include names matching the given patterns')),
2410 [('o', 'output', '', _('print output to file with formatted name')),
2294 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2411 ('r', 'rev', '', _('print the given revision')),
2295 ('o', 'output', '', _('print output to file with formatted name')),
2412 ('I', 'include', [], _('include names matching the given patterns')),
2296 ('r', 'rev', '', _('print the given revision'))],
2413 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2297 _('hg cat [OPTION]... FILE...')),
2414 _('hg cat [OPTION]... FILE...')),
2298 "^clone":
2415 "^clone":
2299 (clone,
2416 (clone,
2300 [('U', 'noupdate', None, _('do not update the new working directory')),
2417 [('U', 'noupdate', None, _('do not update the new working directory')),
2301 ('e', 'ssh', '', _('specify ssh command to use')),
2302 ('', 'pull', None, _('use pull protocol to copy metadata')),
2303 ('r', 'rev', [],
2418 ('r', 'rev', [],
2304 _('a changeset you would like to have after cloning')),
2419 _('a changeset you would like to have after cloning')),
2420 ('', 'pull', None, _('use pull protocol to copy metadata')),
2421 ('e', 'ssh', '', _('specify ssh command to use')),
2305 ('', 'remotecmd', '',
2422 ('', 'remotecmd', '',
2306 _('specify hg command to run on the remote side'))],
2423 _('specify hg command to run on the remote side'))],
2307 _('hg clone [OPTION]... SOURCE [DEST]')),
2424 _('hg clone [OPTION]... SOURCE [DEST]')),
2308 "^commit|ci":
2425 "^commit|ci":
2309 (commit,
2426 (commit,
2310 [('A', 'addremove', None, _('run addremove during commit')),
2427 [('A', 'addremove', None, _('run addremove during commit')),
2311 ('I', 'include', [], _('include names matching the given patterns')),
2312 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2313 ('m', 'message', '', _('use <text> as commit message')),
2428 ('m', 'message', '', _('use <text> as commit message')),
2314 ('l', 'logfile', '', _('read the commit message from <file>')),
2429 ('l', 'logfile', '', _('read the commit message from <file>')),
2315 ('d', 'date', '', _('record datecode as commit date')),
2430 ('d', 'date', '', _('record datecode as commit date')),
2316 ('u', 'user', '', _('record user as commiter'))],
2431 ('u', 'user', '', _('record user as commiter')),
2432 ('I', 'include', [], _('include names matching the given patterns')),
2433 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2317 _('hg commit [OPTION]... [FILE]...')),
2434 _('hg commit [OPTION]... [FILE]...')),
2318 "copy|cp":
2435 "copy|cp":
2319 (copy,
2436 (copy,
2320 [('I', 'include', [], _('include names matching the given patterns')),
2437 [('A', 'after', None, _('record a copy that has already occurred')),
2321 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2322 ('A', 'after', None, _('record a copy that has already occurred')),
2323 ('f', 'force', None,
2438 ('f', 'force', None,
2324 _('forcibly copy over an existing managed file'))],
2439 _('forcibly copy over an existing managed file')),
2440 ('I', 'include', [], _('include names matching the given patterns')),
2441 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2325 _('hg copy [OPTION]... [SOURCE]... DEST')),
2442 _('hg copy [OPTION]... [SOURCE]... DEST')),
2326 "debugancestor": (debugancestor, [], _('debugancestor INDEX REV1 REV2')),
2443 "debugancestor": (debugancestor, [], _('debugancestor INDEX REV1 REV2')),
2444 "debugrebuildstate":
2445 (debugrebuildstate,
2446 [('r', 'rev', '', _('revision to rebuild to'))],
2447 _('debugrebuildstate [-r REV] [REV]')),
2327 "debugcheckstate": (debugcheckstate, [], _('debugcheckstate')),
2448 "debugcheckstate": (debugcheckstate, [], _('debugcheckstate')),
2328 "debugconfig": (debugconfig, [], _('debugconfig')),
2449 "debugconfig": (debugconfig, [], _('debugconfig')),
2329 "debugsetparents": (debugsetparents, [], _('debugsetparents REV1 [REV2]')),
2450 "debugsetparents": (debugsetparents, [], _('debugsetparents REV1 [REV2]')),
@@ -2341,20 +2462,19 b' table = {'
2341 (diff,
2462 (diff,
2342 [('r', 'rev', [], _('revision')),
2463 [('r', 'rev', [], _('revision')),
2343 ('a', 'text', None, _('treat all files as text')),
2464 ('a', 'text', None, _('treat all files as text')),
2344 ('I', 'include', [], _('include names matching the given patterns')),
2345 ('p', 'show-function', None,
2465 ('p', 'show-function', None,
2346 _('show which function each change is in')),
2466 _('show which function each change is in')),
2347 ('w', 'ignore-all-space', None,
2467 ('w', 'ignore-all-space', None,
2348 _('ignore white space when comparing lines')),
2468 _('ignore white space when comparing lines')),
2349 ('X', 'exclude', [],
2469 ('I', 'include', [], _('include names matching the given patterns')),
2350 _('exclude names matching the given patterns'))],
2470 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2351 _('hg diff [-a] [-I] [-X] [-r REV1 [-r REV2]] [FILE]...')),
2471 _('hg diff [-a] [-I] [-X] [-r REV1 [-r REV2]] [FILE]...')),
2352 "^export":
2472 "^export":
2353 (export,
2473 (export,
2354 [('o', 'output', '', _('print output to file with formatted name')),
2474 [('o', 'output', '', _('print output to file with formatted name')),
2355 ('a', 'text', None, _('treat all files as text')),
2475 ('a', 'text', None, _('treat all files as text')),
2356 ('', 'switch-parent', None, _('diff against the second parent'))],
2476 ('', 'switch-parent', None, _('diff against the second parent'))],
2357 _('hg export [-a] [-o OUTFILE] REV...')),
2477 _('hg export [-a] [-o OUTFILESPEC] REV...')),
2358 "forget":
2478 "forget":
2359 (forget,
2479 (forget,
2360 [('I', 'include', [], _('include names matching the given patterns')),
2480 [('I', 'include', [], _('include names matching the given patterns')),
@@ -2363,19 +2483,19 b' table = {'
2363 "grep":
2483 "grep":
2364 (grep,
2484 (grep,
2365 [('0', 'print0', None, _('end fields with NUL')),
2485 [('0', 'print0', None, _('end fields with NUL')),
2366 ('I', 'include', [], _('include names matching the given patterns')),
2367 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2368 ('', 'all', None, _('print all revisions that match')),
2486 ('', 'all', None, _('print all revisions that match')),
2369 ('i', 'ignore-case', None, _('ignore case when matching')),
2487 ('i', 'ignore-case', None, _('ignore case when matching')),
2370 ('l', 'files-with-matches', None,
2488 ('l', 'files-with-matches', None,
2371 _('print only filenames and revs that match')),
2489 _('print only filenames and revs that match')),
2372 ('n', 'line-number', None, _('print matching line numbers')),
2490 ('n', 'line-number', None, _('print matching line numbers')),
2373 ('r', 'rev', [], _('search in given revision range')),
2491 ('r', 'rev', [], _('search in given revision range')),
2374 ('u', 'user', None, _('print user who committed change'))],
2492 ('u', 'user', None, _('print user who committed change')),
2493 ('I', 'include', [], _('include names matching the given patterns')),
2494 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2375 _('hg grep [OPTION]... PATTERN [FILE]...')),
2495 _('hg grep [OPTION]... PATTERN [FILE]...')),
2376 "heads":
2496 "heads":
2377 (heads,
2497 (heads,
2378 [('b', 'branches', None, _('find branch info')),
2498 [('b', 'branches', None, _('show branches')),
2379 ('r', 'rev', '', _('show only heads which are descendants of rev'))],
2499 ('r', 'rev', '', _('show only heads which are descendants of rev'))],
2380 _('hg heads [-b] [-r <rev>]')),
2500 _('hg heads [-b] [-r <rev>]')),
2381 "help": (help_, [], _('hg help [COMMAND]')),
2501 "help": (help_, [], _('hg help [COMMAND]')),
@@ -2385,10 +2505,10 b' table = {'
2385 [('p', 'strip', 1,
2505 [('p', 'strip', 1,
2386 _('directory strip option for patch. This has the same\n') +
2506 _('directory strip option for patch. This has the same\n') +
2387 _('meaning as the corresponding patch option')),
2507 _('meaning as the corresponding patch option')),
2508 ('b', 'base', '', _('base path')),
2388 ('f', 'force', None,
2509 ('f', 'force', None,
2389 _('skip check for outstanding uncommitted changes')),
2510 _('skip check for outstanding uncommitted changes'))],
2390 ('b', 'base', '', _('base path'))],
2511 _('hg import [-p NUM] [-b BASE] [-f] PATCH...')),
2391 _('hg import [-f] [-p NUM] [-b BASE] PATCH...')),
2392 "incoming|in": (incoming,
2512 "incoming|in": (incoming,
2393 [('M', 'no-merges', None, _('do not show merges')),
2513 [('M', 'no-merges', None, _('do not show merges')),
2394 ('p', 'patch', None, _('show patch')),
2514 ('p', 'patch', None, _('show patch')),
@@ -2407,24 +2527,25 b' table = {'
2407 _('hg locate [OPTION]... [PATTERN]...')),
2527 _('hg locate [OPTION]... [PATTERN]...')),
2408 "^log|history":
2528 "^log|history":
2409 (log,
2529 (log,
2410 [('I', 'include', [], _('include names matching the given patterns')),
2530 [('b', 'branches', None, _('show branches')),
2411 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2412 ('b', 'branch', None, _('show branches')),
2413 ('k', 'keyword', [], _('search for a keyword')),
2531 ('k', 'keyword', [], _('search for a keyword')),
2532 ('l', 'limit', '', _('limit number of changes displayed')),
2414 ('r', 'rev', [], _('show the specified revision or range')),
2533 ('r', 'rev', [], _('show the specified revision or range')),
2415 ('M', 'no-merges', None, _('do not show merges')),
2534 ('M', 'no-merges', None, _('do not show merges')),
2416 ('m', 'only-merges', None, _('show only merges')),
2535 ('m', 'only-merges', None, _('show only merges')),
2417 ('p', 'patch', None, _('show patch'))],
2536 ('p', 'patch', None, _('show patch')),
2418 _('hg log [-I] [-X] [-r REV]... [-p] [FILE]')),
2537 ('I', 'include', [], _('include names matching the given patterns')),
2538 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2539 _('hg log [OPTION]... [FILE]')),
2419 "manifest": (manifest, [], _('hg manifest [REV]')),
2540 "manifest": (manifest, [], _('hg manifest [REV]')),
2420 "outgoing|out": (outgoing,
2541 "outgoing|out": (outgoing,
2421 [('M', 'no-merges', None, _('do not show merges')),
2542 [('M', 'no-merges', None, _('do not show merges')),
2422 ('p', 'patch', None, _('show patch')),
2543 ('p', 'patch', None, _('show patch')),
2423 ('n', 'newest-first', None, _('show newest record first'))],
2544 ('n', 'newest-first', None, _('show newest record first'))],
2424 _('hg outgoing [-p] [-n] [-M] [DEST]')),
2545 _('hg outgoing [-M] [-p] [-n] [DEST]')),
2425 "^parents":
2546 "^parents":
2426 (parents,
2547 (parents,
2427 [('b', 'branch', None, _('show branches'))],
2548 [('b', 'branches', None, _('show branches'))],
2428 _('hg parents [-b] [REV]')),
2549 _('hg parents [-b] [REV]')),
2429 "paths": (paths, [], _('hg paths [NAME]')),
2550 "paths": (paths, [], _('hg paths [NAME]')),
2430 "^pull":
2551 "^pull":
@@ -2435,15 +2556,16 b' table = {'
2435 ('r', 'rev', [], _('a specific revision you would like to pull')),
2556 ('r', 'rev', [], _('a specific revision you would like to pull')),
2436 ('', 'remotecmd', '',
2557 ('', 'remotecmd', '',
2437 _('specify hg command to run on the remote side'))],
2558 _('specify hg command to run on the remote side'))],
2438 _('hg pull [-u] [-e FILE] [-r rev] [--remotecmd FILE] [SOURCE]')),
2559 _('hg pull [-u] [-e FILE] [-r REV]... [--remotecmd FILE] [SOURCE]')),
2439 "^push":
2560 "^push":
2440 (push,
2561 (push,
2441 [('f', 'force', None, _('force push')),
2562 [('f', 'force', None, _('force push')),
2442 ('e', 'ssh', '', _('specify ssh command to use')),
2563 ('e', 'ssh', '', _('specify ssh command to use')),
2564 ('r', 'rev', [], _('a specific revision you would like to push')),
2443 ('', 'remotecmd', '',
2565 ('', 'remotecmd', '',
2444 _('specify hg command to run on the remote side'))],
2566 _('specify hg command to run on the remote side'))],
2445 _('hg push [-f] [-e FILE] [--remotecmd FILE] [DEST]')),
2567 _('hg push [-f] [-e FILE] [-r REV]... [--remotecmd FILE] [DEST]')),
2446 "rawcommit":
2568 "debugrawcommit|rawcommit":
2447 (rawcommit,
2569 (rawcommit,
2448 [('p', 'parent', [], _('parent')),
2570 [('p', 'parent', [], _('parent')),
2449 ('d', 'date', '', _('date code')),
2571 ('d', 'date', '', _('date code')),
@@ -2451,7 +2573,7 b' table = {'
2451 ('F', 'files', '', _('file list')),
2573 ('F', 'files', '', _('file list')),
2452 ('m', 'message', '', _('commit message')),
2574 ('m', 'message', '', _('commit message')),
2453 ('l', 'logfile', '', _('commit message file'))],
2575 ('l', 'logfile', '', _('commit message file'))],
2454 _('hg rawcommit [OPTION]... [FILE]...')),
2576 _('hg debugrawcommit [OPTION]... [FILE]...')),
2455 "recover": (recover, [], _('hg recover')),
2577 "recover": (recover, [], _('hg recover')),
2456 "^remove|rm":
2578 "^remove|rm":
2457 (remove,
2579 (remove,
@@ -2460,27 +2582,30 b' table = {'
2460 _('hg remove [OPTION]... FILE...')),
2582 _('hg remove [OPTION]... FILE...')),
2461 "rename|mv":
2583 "rename|mv":
2462 (rename,
2584 (rename,
2463 [('I', 'include', [], _('include names matching the given patterns')),
2585 [('A', 'after', None, _('record a rename that has already occurred')),
2464 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2465 ('A', 'after', None, _('record a rename that has already occurred')),
2466 ('f', 'force', None,
2586 ('f', 'force', None,
2467 _('forcibly copy over an existing managed file'))],
2587 _('forcibly copy over an existing managed file')),
2588 ('I', 'include', [], _('include names matching the given patterns')),
2589 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2468 _('hg rename [OPTION]... [SOURCE]... DEST')),
2590 _('hg rename [OPTION]... [SOURCE]... DEST')),
2469 "^revert":
2591 "^revert":
2470 (revert,
2592 (revert,
2471 [('I', 'include', [], _('include names matching the given patterns')),
2593 [('r', 'rev', '', _('revision to revert to')),
2472 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2594 ('I', 'include', [], _('include names matching the given patterns')),
2473 ('r', 'rev', '', _('revision to revert to'))],
2595 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2474 _('hg revert [-n] [-r REV] [NAME]...')),
2596 _('hg revert [-r REV] [NAME]...')),
2475 "root": (root, [], _('hg root')),
2597 "root": (root, [], _('hg root')),
2476 "^serve":
2598 "^serve":
2477 (serve,
2599 (serve,
2478 [('A', 'accesslog', '', _('name of access log file to write to')),
2600 [('A', 'accesslog', '', _('name of access log file to write to')),
2601 ('d', 'daemon', None, _('run server in background')),
2602 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
2479 ('E', 'errorlog', '', _('name of error log file to write to')),
2603 ('E', 'errorlog', '', _('name of error log file to write to')),
2480 ('p', 'port', 0, _('port to use (default: 8000)')),
2604 ('p', 'port', 0, _('port to use (default: 8000)')),
2481 ('a', 'address', '', _('address to use')),
2605 ('a', 'address', '', _('address to use')),
2482 ('n', 'name', '',
2606 ('n', 'name', '',
2483 _('name to show in web pages (default: working dir)')),
2607 _('name to show in web pages (default: working dir)')),
2608 ('', 'pid-file', '', _('name of file to write process ID to')),
2484 ('', 'stdio', None, _('for remote clients')),
2609 ('', 'stdio', None, _('for remote clients')),
2485 ('t', 'templates', '', _('web templates to use')),
2610 ('t', 'templates', '', _('web templates to use')),
2486 ('', 'style', '', _('template style to use')),
2611 ('', 'style', '', _('template style to use')),
@@ -2506,9 +2631,13 b' table = {'
2506 ('d', 'date', '', _('record datecode as commit date')),
2631 ('d', 'date', '', _('record datecode as commit date')),
2507 ('u', 'user', '', _('record user as commiter')),
2632 ('u', 'user', '', _('record user as commiter')),
2508 ('r', 'rev', '', _('revision to tag'))],
2633 ('r', 'rev', '', _('revision to tag'))],
2509 _('hg tag [-r REV] [OPTION]... NAME')),
2634 _('hg tag [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME')),
2510 "tags": (tags, [], _('hg tags')),
2635 "tags": (tags, [], _('hg tags')),
2511 "tip": (tip, [('p', 'patch', None, _('show patch'))], _('hg tip')),
2636 "tip":
2637 (tip,
2638 [('b', 'branches', None, _('show branches')),
2639 ('p', 'patch', None, _('show patch'))],
2640 _('hg tip [-b] [-p]')),
2512 "unbundle":
2641 "unbundle":
2513 (unbundle,
2642 (unbundle,
2514 [('u', 'update', None,
2643 [('u', 'update', None,
@@ -2734,13 +2863,22 b' def dispatch(args):'
2734 if options['profile']:
2863 if options['profile']:
2735 import hotshot, hotshot.stats
2864 import hotshot, hotshot.stats
2736 prof = hotshot.Profile("hg.prof")
2865 prof = hotshot.Profile("hg.prof")
2737 r = prof.runcall(d)
2866 try:
2738 prof.close()
2867 try:
2739 stats = hotshot.stats.load("hg.prof")
2868 return prof.runcall(d)
2740 stats.strip_dirs()
2869 except:
2741 stats.sort_stats('time', 'calls')
2870 try:
2742 stats.print_stats(40)
2871 u.warn(_('exception raised - generating profile '
2743 return r
2872 'anyway\n'))
2873 except:
2874 pass
2875 raise
2876 finally:
2877 prof.close()
2878 stats = hotshot.stats.load("hg.prof")
2879 stats.strip_dirs()
2880 stats.sort_stats('time', 'calls')
2881 stats.print_stats(40)
2744 else:
2882 else:
2745 return d()
2883 return d()
2746 except:
2884 except:
@@ -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)
@@ -197,9 +197,24 b' class dirstate(object):'
197
197
198 def clear(self):
198 def clear(self):
199 self.map = {}
199 self.map = {}
200 self.copies = {}
201 self.markdirty()
202
203 def rebuild(self, parent, files):
204 self.clear()
205 umask = os.umask(0)
206 os.umask(umask)
207 for f, mode in files:
208 if mode:
209 self.map[f] = ('n', ~umask, -1, 0)
210 else:
211 self.map[f] = ('n', ~umask & 0666, -1, 0)
212 self.pl = (parent, nullid)
200 self.markdirty()
213 self.markdirty()
201
214
202 def write(self):
215 def write(self):
216 if not self.dirty:
217 return
203 st = self.opener("dirstate", "w", atomic=True)
218 st = self.opener("dirstate", "w", atomic=True)
204 st.write("".join(self.pl))
219 st.write("".join(self.pl))
205 for f, e in self.map.items():
220 for f, e in self.map.items():
@@ -270,11 +285,11 b' class dirstate(object):'
270 elif not dc:
285 elif not dc:
271 dc = self.filterfiles(files)
286 dc = self.filterfiles(files)
272
287
273 def statmatch(file, stat):
288 def statmatch(file_, stat):
274 file = util.pconvert(file)
289 file_ = util.pconvert(file_)
275 if file not in dc and self.ignore(file):
290 if file_ not in dc and self.ignore(file_):
276 return False
291 return False
277 return match(file)
292 return match(file_)
278
293
279 return self.walkhelper(files=files, statmatch=statmatch, dc=dc)
294 return self.walkhelper(files=files, statmatch=statmatch, dc=dc)
280
295
@@ -350,9 +365,9 b' class dirstate(object):'
350 continue
365 continue
351 if stat.S_ISDIR(st.st_mode):
366 if stat.S_ISDIR(st.st_mode):
352 cmp1 = (lambda x, y: cmp(x[1], y[1]))
367 cmp1 = (lambda x, y: cmp(x[1], y[1]))
353 sorted = [ x for x in findfiles(f) ]
368 sorted_ = [ x for x in findfiles(f) ]
354 sorted.sort(cmp1)
369 sorted_.sort(cmp1)
355 for e in sorted:
370 for e in sorted_:
356 yield e
371 yield e
357 else:
372 else:
358 ff = util.normpath(ff)
373 ff = util.normpath(ff)
@@ -380,7 +395,7 b' class dirstate(object):'
380
395
381 for src, fn, st in self.statwalk(files, match):
396 for src, fn, st in self.statwalk(files, match):
382 try:
397 try:
383 type, mode, size, time = self[fn]
398 type_, mode, size, time = self[fn]
384 except KeyError:
399 except KeyError:
385 unknown.append(fn)
400 unknown.append(fn)
386 continue
401 continue
@@ -399,22 +414,23 b' class dirstate(object):'
399 nonexistent = False
414 nonexistent = False
400 # XXX: what to do with file no longer present in the fs
415 # XXX: what to do with file no longer present in the fs
401 # who are not removed in the dirstate ?
416 # who are not removed in the dirstate ?
402 if nonexistent and type in "nm":
417 if nonexistent and type_ in "nm":
403 deleted.append(fn)
418 deleted.append(fn)
404 continue
419 continue
405 # check the common case first
420 # check the common case first
406 if type == 'n':
421 if type_ == 'n':
407 if not st:
422 if not st:
408 st = os.stat(fn)
423 st = os.stat(fn)
409 if size != st.st_size or (mode ^ st.st_mode) & 0100:
424 if size >= 0 and (size != st.st_size
425 or (mode ^ st.st_mode) & 0100):
410 modified.append(fn)
426 modified.append(fn)
411 elif time != st.st_mtime:
427 elif time != st.st_mtime:
412 lookup.append(fn)
428 lookup.append(fn)
413 elif type == 'm':
429 elif type_ == 'm':
414 modified.append(fn)
430 modified.append(fn)
415 elif type == 'a':
431 elif type_ == 'a':
416 added.append(fn)
432 added.append(fn)
417 elif type == 'r':
433 elif type_ == 'r':
418 removed.append(fn)
434 removed.append(fn)
419
435
420 return (lookup, modified, added, removed, deleted, unknown)
436 return (lookup, modified, added, removed, deleted, unknown)
@@ -7,6 +7,7 b''
7 # of the GNU General Public License, incorporated herein by reference.
7 # of the GNU General Public License, incorporated herein by reference.
8
8
9 import os, cgi, sys, urllib
9 import os, cgi, sys, urllib
10 import mimetypes
10 from demandload import demandload
11 from demandload import demandload
11 demandload(globals(), "mdiff time re socket zlib errno ui hg ConfigParser")
12 demandload(globals(), "mdiff time re socket zlib errno ui hg ConfigParser")
12 demandload(globals(), "zipfile tempfile StringIO tarfile BaseHTTPServer util")
13 demandload(globals(), "zipfile tempfile StringIO tarfile BaseHTTPServer util")
@@ -18,7 +19,11 b' def templatepath():'
18 for f in "templates", "../templates":
19 for f in "templates", "../templates":
19 p = os.path.join(os.path.dirname(__file__), f)
20 p = os.path.join(os.path.dirname(__file__), f)
20 if os.path.isdir(p):
21 if os.path.isdir(p):
21 return p
22 return os.path.normpath(p)
23 else:
24 # executable version (py2exe) doesn't support __file__
25 if hasattr(sys, 'frozen'):
26 return os.path.join(sys.prefix, "templates")
22
27
23 def age(x):
28 def age(x):
24 def plural(t, c):
29 def plural(t, c):
@@ -71,6 +76,30 b' def get_mtime(repo_path):'
71 else:
76 else:
72 return os.stat(hg_path).st_mtime
77 return os.stat(hg_path).st_mtime
73
78
79 def staticfile(directory, fname):
80 """return a file inside directory with guessed content-type header
81
82 fname always uses '/' as directory separator and isn't allowed to
83 contain unusual path components.
84 Content-type is guessed using the mimetypes module.
85 Return an empty string if fname is illegal or file not found.
86
87 """
88 parts = fname.split('/')
89 path = directory
90 for part in parts:
91 if (part in ('', os.curdir, os.pardir) or
92 os.sep in part or os.altsep is not None and os.altsep in part):
93 return ""
94 path = os.path.join(path, part)
95 try:
96 os.stat(path)
97 ct = mimetypes.guess_type(path)[0] or "text/plain"
98 return "Content-type: %s\n\n%s" % (ct, file(path).read())
99 except (TypeError, OSError):
100 # illegal fname or unreadable file
101 return ""
102
74 class hgrequest(object):
103 class hgrequest(object):
75 def __init__(self, inp=None, out=None, env=None):
104 def __init__(self, inp=None, out=None, env=None):
76 self.inp = inp or sys.stdin
105 self.inp = inp or sys.stdin
@@ -660,9 +689,10 b' class hgweb(object):'
660 i = self.repo.tagslist()
689 i = self.repo.tagslist()
661 i.reverse()
690 i.reverse()
662
691
663 def entries(**map):
692 def entries(notip=False, **map):
664 parity = 0
693 parity = 0
665 for k,n in i:
694 for k,n in i:
695 if notip and k == "tip": continue
666 yield {"parity": parity,
696 yield {"parity": parity,
667 "tag": k,
697 "tag": k,
668 "tagmanifest": hex(cl.read(n)[0]),
698 "tagmanifest": hex(cl.read(n)[0]),
@@ -672,7 +702,8 b' class hgweb(object):'
672
702
673 yield self.t("tags",
703 yield self.t("tags",
674 manifest=hex(mf),
704 manifest=hex(mf),
675 entries=entries)
705 entries=lambda **x: entries(False, **x),
706 entriesnotip=lambda **x: entries(True, **x))
676
707
677 def summary(self):
708 def summary(self):
678 cl = self.repo.changelog
709 cl = self.repo.changelog
@@ -843,6 +874,7 b' class hgweb(object):'
843 'ca': [('cmd', ['archive']), ('node', None)],
874 'ca': [('cmd', ['archive']), ('node', None)],
844 'tags': [('cmd', ['tags'])],
875 'tags': [('cmd', ['tags'])],
845 'tip': [('cmd', ['changeset']), ('node', ['tip'])],
876 'tip': [('cmd', ['changeset']), ('node', ['tip'])],
877 'static': [('cmd', ['static']), ('file', None)]
846 }
878 }
847
879
848 for k in shortcuts.iterkeys():
880 for k in shortcuts.iterkeys():
@@ -858,6 +890,7 b' class hgweb(object):'
858 expand_form(req.form)
890 expand_form(req.form)
859
891
860 t = self.repo.ui.config("web", "templates", templatepath())
892 t = self.repo.ui.config("web", "templates", templatepath())
893 static = self.repo.ui.config("web", "static", os.path.join(t,"static"))
861 m = os.path.join(t, "map")
894 m = os.path.join(t, "map")
862 style = self.repo.ui.config("web", "style", "")
895 style = self.repo.ui.config("web", "style", "")
863 if req.form.has_key('style'):
896 if req.form.has_key('style'):
@@ -981,6 +1014,11 b' class hgweb(object):'
981
1014
982 req.write(self.t("error"))
1015 req.write(self.t("error"))
983
1016
1017 elif req.form['cmd'][0] == 'static':
1018 fname = req.form['file'][0]
1019 req.write(staticfile(static, fname)
1020 or self.t("error", error="%r not found" % fname))
1021
984 else:
1022 else:
985 req.write(self.t("error"))
1023 req.write(self.t("error"))
986
1024
@@ -1075,17 +1113,27 b' def create_server(repo):'
1075 class hgwebdir(object):
1113 class hgwebdir(object):
1076 def __init__(self, config):
1114 def __init__(self, config):
1077 def cleannames(items):
1115 def cleannames(items):
1078 return [(name.strip('/'), path) for name, path in items]
1116 return [(name.strip(os.sep), path) for name, path in items]
1079
1117
1080 if type(config) == type([]):
1118 if isinstance(config, (list, tuple)):
1081 self.repos = cleannames(config)
1119 self.repos = cleannames(config)
1082 elif type(config) == type({}):
1120 elif isinstance(config, dict):
1083 self.repos = cleannames(config.items())
1121 self.repos = cleannames(config.items())
1084 self.repos.sort()
1122 self.repos.sort()
1085 else:
1123 else:
1086 cp = ConfigParser.SafeConfigParser()
1124 cp = ConfigParser.SafeConfigParser()
1087 cp.read(config)
1125 cp.read(config)
1088 self.repos = cleannames(cp.items("paths"))
1126 self.repos = []
1127 if cp.has_section('paths'):
1128 self.repos.extend(cleannames(cp.items('paths')))
1129 if cp.has_section('collections'):
1130 for prefix, root in cp.items('collections'):
1131 for path in util.walkrepos(root):
1132 repo = os.path.normpath(path)
1133 name = repo
1134 if name.startswith(prefix):
1135 name = name[len(prefix):]
1136 self.repos.append((name.lstrip(os.sep), repo))
1089 self.repos.sort()
1137 self.repos.sort()
1090
1138
1091 def run(self, req=hgrequest()):
1139 def run(self, req=hgrequest()):
@@ -1142,4 +1190,10 b' class hgwebdir(object):'
1142 else:
1190 else:
1143 req.write(tmpl("notfound", repo=virtual))
1191 req.write(tmpl("notfound", repo=virtual))
1144 else:
1192 else:
1145 req.write(tmpl("index", entries=entries))
1193 if req.form.has_key('static'):
1194 static = os.path.join(templatepath(), "static")
1195 fname = req.form['static'][0]
1196 req.write(staticfile(static, fname)
1197 or tmpl("error", error="%r not found" % fname))
1198 else:
1199 req.write(tmpl("index", entries=entries))
@@ -13,6 +13,8 b' from demandload import *'
13 demandload(globals(), "re lock transaction tempfile stat mdiff errno")
13 demandload(globals(), "re lock transaction tempfile stat mdiff errno")
14
14
15 class localrepository(object):
15 class localrepository(object):
16 def __del__(self):
17 self.transhandle = None
16 def __init__(self, ui, path=None, create=0):
18 def __init__(self, ui, path=None, create=0):
17 if not path:
19 if not path:
18 p = os.getcwd()
20 p = os.getcwd()
@@ -37,6 +39,7 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)
@@ -215,6 +218,10 b' class localrepository(object):'
215 return self.wopener(filename, 'w').write(data)
218 return self.wopener(filename, 'w').write(data)
216
219
217 def transaction(self):
220 def transaction(self):
221 tr = self.transhandle
222 if tr != None and tr.running():
223 return tr.nest()
224
218 # save dirstate for undo
225 # save dirstate for undo
219 try:
226 try:
220 ds = self.opener("dirstate").read()
227 ds = self.opener("dirstate").read()
@@ -222,21 +229,18 b' class localrepository(object):'
222 ds = ""
229 ds = ""
223 self.opener("journal.dirstate", "w").write(ds)
230 self.opener("journal.dirstate", "w").write(ds)
224
231
225 def after():
232 tr = transaction.transaction(self.ui.warn, self.opener,
226 util.rename(self.join("journal"), self.join("undo"))
233 self.join("journal"),
227 util.rename(self.join("journal.dirstate"),
234 aftertrans(self.path))
228 self.join("undo.dirstate"))
235 self.transhandle = tr
229
236 return tr
230 return transaction.transaction(self.ui.warn, self.opener,
231 self.join("journal"), after)
232
237
233 def recover(self):
238 def recover(self):
234 lock = self.lock()
239 l = self.lock()
235 if os.path.exists(self.join("journal")):
240 if os.path.exists(self.join("journal")):
236 self.ui.status(_("rolling back interrupted transaction\n"))
241 self.ui.status(_("rolling back interrupted transaction\n"))
237 transaction.rollback(self.opener, self.join("journal"))
242 transaction.rollback(self.opener, self.join("journal"))
238 self.manifest = manifest.manifest(self.opener)
243 self.reload()
239 self.changelog = changelog.changelog(self.opener)
240 return True
244 return True
241 else:
245 else:
242 self.ui.warn(_("no interrupted transaction available\n"))
246 self.ui.warn(_("no interrupted transaction available\n"))
@@ -245,34 +249,51 b' class localrepository(object):'
245 def undo(self, wlock=None):
249 def undo(self, wlock=None):
246 if not wlock:
250 if not wlock:
247 wlock = self.wlock()
251 wlock = self.wlock()
248 lock = self.lock()
252 l = self.lock()
249 if os.path.exists(self.join("undo")):
253 if os.path.exists(self.join("undo")):
250 self.ui.status(_("rolling back last transaction\n"))
254 self.ui.status(_("rolling back last transaction\n"))
251 transaction.rollback(self.opener, self.join("undo"))
255 transaction.rollback(self.opener, self.join("undo"))
252 util.rename(self.join("undo.dirstate"), self.join("dirstate"))
256 util.rename(self.join("undo.dirstate"), self.join("dirstate"))
253 self.dirstate.read()
257 self.reload()
258 self.wreload()
254 else:
259 else:
255 self.ui.warn(_("no undo information available\n"))
260 self.ui.warn(_("no undo information available\n"))
256
261
257 def lock(self, wait=1):
262 def wreload(self):
263 self.dirstate.read()
264
265 def reload(self):
266 self.changelog.load()
267 self.manifest.load()
268 self.tagscache = None
269 self.nodetagscache = None
270
271 def do_lock(self, lockname, wait, releasefn=None, acquirefn=None):
258 try:
272 try:
259 return lock.lock(self.join("lock"), 0)
273 l = lock.lock(self.join(lockname), 0, releasefn)
260 except lock.LockHeld, inst:
261 if wait:
262 self.ui.warn(_("waiting for lock held by %s\n") % inst.args[0])
263 return lock.lock(self.join("lock"), wait)
264 raise inst
265
266 def wlock(self, wait=1):
267 try:
268 wlock = lock.lock(self.join("wlock"), 0, self.dirstate.write)
269 except lock.LockHeld, inst:
274 except lock.LockHeld, inst:
270 if not wait:
275 if not wait:
271 raise inst
276 raise inst
272 self.ui.warn(_("waiting for lock held by %s\n") % inst.args[0])
277 self.ui.warn(_("waiting for lock held by %s\n") % inst.args[0])
273 wlock = lock.lock(self.join("wlock"), wait, self.dirstate.write)
278 try:
274 self.dirstate.read()
279 # default to 600 seconds timeout
275 return wlock
280 l = lock.lock(self.join(lockname),
281 int(self.ui.config("ui", "timeout") or 600),
282 releasefn)
283 except lock.LockHeld, inst:
284 raise util.Abort(_("timeout while waiting for "
285 "lock held by %s") % inst.args[0])
286 if acquirefn:
287 acquirefn()
288 return l
289
290 def lock(self, wait=1):
291 return self.do_lock("lock", wait, acquirefn=self.reload)
292
293 def wlock(self, wait=1):
294 return self.do_lock("wlock", wait,
295 self.dirstate.write,
296 self.wreload)
276
297
277 def checkfilemerge(self, filename, text, filelog, manifest1, manifest2):
298 def checkfilemerge(self, filename, text, filelog, manifest1, manifest2):
278 "determine whether a new filenode is needed"
299 "determine whether a new filenode is needed"
@@ -311,7 +332,7 b' class localrepository(object):'
311
332
312 if not wlock:
333 if not wlock:
313 wlock = self.wlock()
334 wlock = self.wlock()
314 lock = self.lock()
335 l = self.lock()
315 tr = self.transaction()
336 tr = self.transaction()
316 mm = m1.copy()
337 mm = m1.copy()
317 mfm = mf1.copy()
338 mfm = mf1.copy()
@@ -350,7 +371,7 b' class localrepository(object):'
350 self.dirstate.setparents(n, nullid)
371 self.dirstate.setparents(n, nullid)
351
372
352 def commit(self, files=None, text="", user=None, date=None,
373 def commit(self, files=None, text="", user=None, date=None,
353 match=util.always, force=False, wlock=None):
374 match=util.always, force=False, lock=None, wlock=None):
354 commit = []
375 commit = []
355 remove = []
376 remove = []
356 changed = []
377 changed = []
@@ -388,7 +409,8 b' class localrepository(object):'
388
409
389 if not wlock:
410 if not wlock:
390 wlock = self.wlock()
411 wlock = self.wlock()
391 lock = self.lock()
412 if not lock:
413 lock = self.lock()
392 tr = self.transaction()
414 tr = self.transaction()
393
415
394 # check in files
416 # check in files
@@ -503,12 +525,18 b' class localrepository(object):'
503 del mf[fn]
525 del mf[fn]
504 return mf
526 return mf
505
527
528 if node1:
529 # read the manifest from node1 before the manifest from node2,
530 # so that we'll hit the manifest cache if we're going through
531 # all the revisions in parent->child order.
532 mf1 = mfmatches(node1)
533
506 # are we comparing the working directory?
534 # are we comparing the working directory?
507 if not node2:
535 if not node2:
508 if not wlock:
536 if not wlock:
509 try:
537 try:
510 wlock = self.wlock(wait=0)
538 wlock = self.wlock(wait=0)
511 except lock.LockHeld:
539 except lock.LockException:
512 wlock = None
540 wlock = None
513 lookup, modified, added, removed, deleted, unknown = (
541 lookup, modified, added, removed, deleted, unknown = (
514 self.dirstate.changes(files, match))
542 self.dirstate.changes(files, match))
@@ -541,8 +569,6 b' class localrepository(object):'
541 # flush lists from dirstate before comparing manifests
569 # flush lists from dirstate before comparing manifests
542 modified, added = [], []
570 modified, added = [], []
543
571
544 mf1 = mfmatches(node1)
545
546 for fn in mf2:
572 for fn in mf2:
547 if mf1.has_key(fn):
573 if mf1.has_key(fn):
548 if mf1[fn] != mf2[fn] and (mf2[fn] != "" or fcmp(fn, mf1)):
574 if mf1[fn] != mf2[fn] and (mf2[fn] != "" or fcmp(fn, mf1)):
@@ -597,7 +623,6 b' class localrepository(object):'
597 if os.path.exists(p):
623 if os.path.exists(p):
598 self.ui.warn(_("%s still exists!\n") % f)
624 self.ui.warn(_("%s still exists!\n") % f)
599 elif self.dirstate.state(f) == 'a':
625 elif self.dirstate.state(f) == 'a':
600 self.ui.warn(_("%s never committed!\n") % f)
601 self.dirstate.forget([f])
626 self.dirstate.forget([f])
602 elif f not in self.dirstate:
627 elif f not in self.dirstate:
603 self.ui.warn(_("%s not tracked!\n") % f)
628 self.ui.warn(_("%s not tracked!\n") % f)
@@ -932,7 +957,7 b' class localrepository(object):'
932 return subset
957 return subset
933
958
934 def pull(self, remote, heads=None):
959 def pull(self, remote, heads=None):
935 lock = self.lock()
960 l = self.lock()
936
961
937 # if we have an empty repo, fetch everything
962 # if we have an empty repo, fetch everything
938 if self.changelog.tip() == nullid:
963 if self.changelog.tip() == nullid:
@@ -951,7 +976,7 b' class localrepository(object):'
951 cg = remote.changegroupsubset(fetch, heads, 'pull')
976 cg = remote.changegroupsubset(fetch, heads, 'pull')
952 return self.addchangegroup(cg)
977 return self.addchangegroup(cg)
953
978
954 def push(self, remote, force=False):
979 def push(self, remote, force=False, revs=None):
955 lock = remote.lock()
980 lock = remote.lock()
956
981
957 base = {}
982 base = {}
@@ -963,17 +988,25 b' class localrepository(object):'
963 return 1
988 return 1
964
989
965 update = self.findoutgoing(remote, base)
990 update = self.findoutgoing(remote, base)
966 if not update:
991 if revs is not None:
992 msng_cl, bases, heads = self.changelog.nodesbetween(update, revs)
993 else:
994 bases, heads = update, self.changelog.heads()
995
996 if not bases:
967 self.ui.status(_("no changes found\n"))
997 self.ui.status(_("no changes found\n"))
968 return 1
998 return 1
969 elif not force:
999 elif not force:
970 if len(heads) < len(self.changelog.heads()):
1000 if len(bases) < len(heads):
971 self.ui.warn(_("abort: push creates new remote branches!\n"))
1001 self.ui.warn(_("abort: push creates new remote branches!\n"))
972 self.ui.status(_("(did you forget to merge?"
1002 self.ui.status(_("(did you forget to merge?"
973 " use push -f to force)\n"))
1003 " use push -f to force)\n"))
974 return 1
1004 return 1
975
1005
976 cg = self.changegroup(update, 'push')
1006 if revs is None:
1007 cg = self.changegroup(update, 'push')
1008 else:
1009 cg = self.changegroupsubset(update, revs, 'push')
977 return remote.addchangegroup(cg)
1010 return remote.addchangegroup(cg)
978
1011
979 def changegroupsubset(self, bases, heads, source):
1012 def changegroupsubset(self, bases, heads, source):
@@ -1646,6 +1679,7 b' class localrepository(object):'
1646 remove.sort()
1679 remove.sort()
1647 for f in remove:
1680 for f in remove:
1648 self.ui.note(_("removing %s\n") % f)
1681 self.ui.note(_("removing %s\n") % f)
1682 util.audit_path(f)
1649 try:
1683 try:
1650 util.unlink(self.wjoin(f))
1684 util.unlink(self.wjoin(f))
1651 except OSError, inst:
1685 except OSError, inst:
@@ -1852,3 +1886,13 b' class localrepository(object):'
1852 if errors[0]:
1886 if errors[0]:
1853 self.ui.warn(_("%d integrity errors encountered!\n") % errors[0])
1887 self.ui.warn(_("%d integrity errors encountered!\n") % errors[0])
1854 return 1
1888 return 1
1889
1890 # used to avoid circular references so destructors work
1891 def aftertrans(base):
1892 p = base
1893 def a():
1894 util.rename(os.path.join(p, "journal"), os.path.join(p, "undo"))
1895 util.rename(os.path.join(p, "journal.dirstate"),
1896 os.path.join(p, "undo.dirstate"))
1897 return a
1898
@@ -5,17 +5,21 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, time
8 from demandload import *
9 import util
9 demandload(globals(), 'errno os time util')
10
10
11 class LockHeld(Exception):
11 class LockException(Exception):
12 pass
13 class LockHeld(LockException):
14 pass
15 class LockUnavailable(LockException):
12 pass
16 pass
13
17
14 class lock(object):
18 class lock(object):
15 def __init__(self, file, wait=1, releasefn=None):
19 def __init__(self, file, timeout=-1, releasefn=None):
16 self.f = file
20 self.f = file
17 self.held = 0
21 self.held = 0
18 self.wait = wait
22 self.timeout = timeout
19 self.releasefn = releasefn
23 self.releasefn = releasefn
20 self.lock()
24 self.lock()
21
25
@@ -23,13 +27,16 b' class lock(object):'
23 self.release()
27 self.release()
24
28
25 def lock(self):
29 def lock(self):
30 timeout = self.timeout
26 while 1:
31 while 1:
27 try:
32 try:
28 self.trylock()
33 self.trylock()
29 return 1
34 return 1
30 except LockHeld, inst:
35 except LockHeld, inst:
31 if self.wait:
36 if timeout != 0:
32 time.sleep(1)
37 time.sleep(1)
38 if timeout > 0:
39 timeout -= 1
33 continue
40 continue
34 raise inst
41 raise inst
35
42
@@ -38,8 +45,11 b' class lock(object):'
38 try:
45 try:
39 util.makelock(str(pid), self.f)
46 util.makelock(str(pid), self.f)
40 self.held = 1
47 self.held = 1
41 except (OSError, IOError):
48 except (OSError, IOError), why:
42 raise LockHeld(util.readlock(self.f))
49 if why.errno == errno.EEXIST:
50 raise LockHeld(util.readlock(self.f))
51 else:
52 raise LockUnavailable(why)
43
53
44 def release(self):
54 def release(self):
45 if self.held:
55 if self.held:
@@ -66,7 +66,7 b' static struct flist *lalloc(int size)'
66 a = NULL;
66 a = NULL;
67 } else
67 } else
68 a->head = a->tail = a->base;
68 a->head = a->tail = a->base;
69 return a;
69 return a;
70 }
70 }
71 if (!PyErr_Occurred())
71 if (!PyErr_Occurred())
72 PyErr_NoMemory();
72 PyErr_NoMemory();
@@ -13,7 +13,7 b' of the GNU General Public License, incor'
13 from node import *
13 from node import *
14 from i18n import gettext as _
14 from i18n import gettext as _
15 from demandload import demandload
15 from demandload import demandload
16 demandload(globals(), "binascii errno heapq mdiff sha struct zlib")
16 demandload(globals(), "binascii errno heapq mdiff os sha struct zlib")
17
17
18 def hash(text, p1, p2):
18 def hash(text, p1, p2):
19 """generate a hash from the given text and its parent hashes
19 """generate a hash from the given text and its parent hashes
@@ -187,15 +187,33 b' class revlog(object):'
187 self.indexfile = indexfile
187 self.indexfile = indexfile
188 self.datafile = datafile
188 self.datafile = datafile
189 self.opener = opener
189 self.opener = opener
190
191 self.indexstat = None
190 self.cache = None
192 self.cache = None
191 self.chunkcache = None
193 self.chunkcache = None
194 self.load()
192
195
196 def load(self):
193 try:
197 try:
194 i = self.opener(self.indexfile).read()
198 f = self.opener(self.indexfile)
195 except IOError, inst:
199 except IOError, inst:
196 if inst.errno != errno.ENOENT:
200 if inst.errno != errno.ENOENT:
197 raise
201 raise
198 i = ""
202 i = ""
203 else:
204 try:
205 st = os.fstat(f.fileno())
206 except AttributeError, inst:
207 st = None
208 else:
209 oldst = self.indexstat
210 if (oldst and st.st_dev == oldst.st_dev
211 and st.st_ino == oldst.st_ino
212 and st.st_mtime == oldst.st_mtime
213 and st.st_ctime == oldst.st_ctime):
214 return
215 self.indexstat = st
216 i = f.read()
199
217
200 if i and i[:4] != "\0\0\0\0":
218 if i and i[:4] != "\0\0\0\0":
201 raise RevlogError(_("incompatible revlog signature on %s") %
219 raise RevlogError(_("incompatible revlog signature on %s") %
@@ -624,12 +642,10 b' class revlog(object):'
624 # we store negative distances because heap returns smallest member
642 # we store negative distances because heap returns smallest member
625 h = [(-dist[node], node)]
643 h = [(-dist[node], node)]
626 seen = {}
644 seen = {}
627 earliest = self.count()
628 while h:
645 while h:
629 d, n = heapq.heappop(h)
646 d, n = heapq.heappop(h)
630 if n not in seen:
647 if n not in seen:
631 seen[n] = 1
648 seen[n] = 1
632 r = self.rev(n)
633 yield (-d, n)
649 yield (-d, n)
634 for p in self.parents(n):
650 for p in self.parents(n):
635 heapq.heappush(h, (-dist[p], p))
651 heapq.heappush(h, (-dist[p], p))
@@ -690,11 +706,6 b' class revlog(object):'
690 p = self.parents(self.node(revs[0]))[0]
706 p = self.parents(self.node(revs[0]))[0]
691 revs.insert(0, self.rev(p))
707 revs.insert(0, self.rev(p))
692
708
693 # helper to reconstruct intermediate versions
694 def construct(text, base, rev):
695 bins = [self.chunk(r) for r in xrange(base + 1, rev + 1)]
696 return mdiff.patches(text, bins)
697
698 # build deltas
709 # build deltas
699 for d in xrange(0, len(revs) - 1):
710 for d in xrange(0, len(revs) - 1):
700 a, b = revs[d], revs[d + 1]
711 a, b = revs[d], revs[d + 1]
@@ -738,10 +749,10 b' class revlog(object):'
738 base = prev = -1
749 base = prev = -1
739 start = end = measure = 0
750 start = end = measure = 0
740 if r:
751 if r:
741 start = self.start(self.base(t))
752 base = self.base(t)
753 start = self.start(base)
742 end = self.end(t)
754 end = self.end(t)
743 measure = self.length(self.base(t))
755 measure = self.length(base)
744 base = self.base(t)
745 prev = self.tip()
756 prev = self.tip()
746
757
747 transaction.add(self.datafile, end)
758 transaction.add(self.datafile, end)
@@ -793,14 +804,15 b' class revlog(object):'
793 raise RevlogError(_("consistency error adding group"))
804 raise RevlogError(_("consistency error adding group"))
794 measure = len(text)
805 measure = len(text)
795 else:
806 else:
796 e = (end, len(cdelta), self.base(t), link, p1, p2, node)
807 e = (end, len(cdelta), base, link, p1, p2, node)
797 self.index.append(e)
808 self.index.append(e)
798 self.nodemap[node] = r
809 self.nodemap[node] = r
799 dfh.write(cdelta)
810 dfh.write(cdelta)
800 ifh.write(struct.pack(indexformat, *e))
811 ifh.write(struct.pack(indexformat, *e))
801
812
802 t, r, chain, prev = r, r + 1, node, node
813 t, r, chain, prev = r, r + 1, node, node
803 start = self.start(self.base(t))
814 base = self.base(t)
815 start = self.start(base)
804 end = self.end(t)
816 end = self.end(t)
805
817
806 dfh.close()
818 dfh.close()
@@ -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:
@@ -179,7 +179,7 b' def canonpath(root, cwd, myname):'
179 if root == os.sep:
179 if root == os.sep:
180 rootsep = os.sep
180 rootsep = os.sep
181 else:
181 else:
182 rootsep = root + os.sep
182 rootsep = root + os.sep
183 name = myname
183 name = myname
184 if not name.startswith(os.sep):
184 if not name.startswith(os.sep):
185 name = os.path.join(root, cwd, name)
185 name = os.path.join(root, cwd, name)
@@ -363,7 +363,14 b' def copyfiles(src, dst, hardlink=None):'
363 else:
363 else:
364 shutil.copy(src, dst)
364 shutil.copy(src, dst)
365
365
366 def opener(base):
366 def audit_path(path):
367 """Abort if path contains dangerous components"""
368 parts = os.path.normcase(path).split(os.sep)
369 if (os.path.splitdrive(path)[0] or parts[0] in ('.hg', '')
370 or os.pardir in parts):
371 raise Abort(_("path contains illegal component: %s\n") % path)
372
373 def opener(base, audit=True):
367 """
374 """
368 return a function that opens files relative to base
375 return a function that opens files relative to base
369
376
@@ -371,6 +378,7 b' def opener(base):'
371 remote file access from higher level code.
378 remote file access from higher level code.
372 """
379 """
373 p = base
380 p = base
381 audit_p = audit
374
382
375 def mktempcopy(name):
383 def mktempcopy(name):
376 d, fn = os.path.split(name)
384 d, fn = os.path.split(name)
@@ -401,6 +409,8 b' def opener(base):'
401 self.close()
409 self.close()
402
410
403 def o(path, mode="r", text=False, atomic=False):
411 def o(path, mode="r", text=False, atomic=False):
412 if audit_p:
413 audit_path(path)
404 f = os.path.join(p, path)
414 f = os.path.join(p, path)
405
415
406 if not text:
416 if not text:
@@ -690,3 +700,16 b" def datestr(date=None, format='%c'):"
690 (time.strftime(format, time.gmtime(float(t) - tz)),
700 (time.strftime(format, time.gmtime(float(t) - tz)),
691 -tz / 3600,
701 -tz / 3600,
692 ((-tz % 3600) / 60)))
702 ((-tz % 3600) / 60)))
703
704 def walkrepos(path):
705 '''yield every hg repository under path, recursively.'''
706 def errhandler(err):
707 if err.filename == path:
708 raise err
709
710 for root, dirs, files in os.walk(path, onerror=errhandler):
711 for d in dirs:
712 if d == '.hg':
713 yield root
714 dirs[:] = []
715 break
@@ -89,7 +89,9 b' try:'
89 data_files=[('mercurial/templates',
89 data_files=[('mercurial/templates',
90 ['templates/map'] +
90 ['templates/map'] +
91 glob.glob('templates/map-*') +
91 glob.glob('templates/map-*') +
92 glob.glob('templates/*.tmpl'))],
92 glob.glob('templates/*.tmpl')),
93 ('mercurial/templates/static',
94 glob.glob('templates/static/*'))],
93 cmdclass=cmdclass,
95 cmdclass=cmdclass,
94 scripts=['hg', 'hgmerge'],
96 scripts=['hg', 'hgmerge'],
95 options=dict(bdist_mpkg=dict(zipdist=True,
97 options=dict(bdist_mpkg=dict(zipdist=True,
@@ -1,11 +1,21 b''
1 #header#
1 #header#
2 <title>#repo|escape#: Error</title>
3 <link rel="alternate" type="application/rss+xml"
4 href="?cmd=changelog;style=rss" title="RSS feed for #repo|escape#">
5 </head>
6 <body>
7
8 <div class="page_header">
9 <a href="http://www.selenic.com/mercurial/" title="Mercurial"><div style="float:right;">Mercurial</div></a><a href="?cmd=summary;style=gitweb">#repo|escape#</a> / error
10 </div>
11
2 <div class="page_nav">
12 <div class="page_nav">
3 <a href="?cmd=summary;style=gitweb">summary</a> | <a href="?cmd=changelog;style=gitweb">log</a> | <a href="?cmd=tags;style=gitweb">tags</a> | <a href="?cmd=manifest;manifest=#manifest#;path=/;style=gitweb">manifest</a><br/>
13 <a href="?cmd=summary;style=gitweb">summary</a> | <a href="?cmd=changelog;style=gitweb">changelog</a> | <a href="?cmd=tags;style=gitweb">tags</a> | <a href="?cmd=manifest;manifest=#manifest#;path=/;style=gitweb">manifest</a><br/>
4 </div>
14 </div>
5
15
6 <div>
16 <div>
7 <br/>
17 <br/>
8 <i>Error parsing query string</i><br/>
18 <i>An error occured while processing your request</i><br/>
9 <br/>
19 <br/>
10 </div>
20 </div>
11
21
@@ -4,56 +4,8 b' Content-type: text/html'
4 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
4 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
5 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US" lang="en-US">
5 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US" lang="en-US">
6 <head>
6 <head>
7 <link rel="icon" href="?static=hgicon.png" type="image/png">
7 <meta http-equiv="content-type" content="text/html; charset=utf-8"/>
8 <meta http-equiv="content-type" content="text/html; charset=utf-8"/>
8 <meta name="robots" content="index, nofollow"/>
9 <meta name="robots" content="index, nofollow"/>
9 <style type="text/css">
10 <style type="text/css">/*<![CDATA[*/ @import "?static=style-gitweb.css"; /*]]>*/</style>
10 body { font-family: sans-serif; font-size: 12px; margin:0px; border:solid #d9d8d1; border-width:1px; margin:10px; }
11 a { color:#0000cc; }
12 a:hover, a:visited, a:active { color:#880000; }
13 div.page_header { height:25px; padding:8px; font-size:18px; font-weight:bold; background-color:#d9d8d1; }
14 div.page_header a:visited { color:#0000cc; }
15 div.page_header a:hover { color:#880000; }
16 div.page_nav { padding:8px; }
17 div.page_nav a:visited { color:#0000cc; }
18 div.page_path { padding:8px; border:solid #d9d8d1; border-width:0px 0px 1px}
19 div.page_footer { height:17px; padding:4px 8px; background-color: #d9d8d1; }
20 div.page_footer_text { float:left; color:#555555; font-style:italic; }
21 div.page_body { padding:8px; }
22 div.title, a.title {
23 display:block; padding:6px 8px;
24 font-weight:bold; background-color:#edece6; text-decoration:none; color:#000000;
25 }
26 a.title:hover { background-color: #d9d8d1; }
27 div.title_text { padding:6px 0px; border: solid #d9d8d1; border-width:0px 0px 1px; }
28 div.log_body { padding:8px 8px 8px 150px; }
29 span.age { position:relative; float:left; width:142px; font-style:italic; }
30 div.log_link {
31 padding:0px 8px;
32 font-size:10px; font-family:sans-serif; font-style:normal;
33 position:relative; float:left; width:136px;
34 }
35 div.list_head { padding:6px 8px 4px; border:solid #d9d8d1; border-width:1px 0px 0px; font-style:italic; }
36 a.list { text-decoration:none; color:#000000; }
37 a.list:hover { text-decoration:underline; color:#880000; }
38 table { padding:8px 4px; }
39 th { padding:2px 5px; font-size:12px; text-align:left; }
40 tr.light:hover, .parity0:hover { background-color:#edece6; }
41 tr.dark, .parity1 { background-color:#f6f6f0; }
42 tr.dark:hover, .parity1:hover { background-color:#edece6; }
43 td { padding:2px 5px; font-size:12px; vertical-align:top; }
44 td.link { padding:2px 5px; font-family:sans-serif; font-size:10px; }
45 div.pre { font-family:monospace; font-size:12px; white-space:pre; }
46 div.diff_info { font-family:monospace; color:#000099; background-color:#edece6; font-style:italic; }
47 div.index_include { border:solid #d9d8d1; border-width:0px 0px 1px; padding:12px 8px; }
48 div.search { margin:4px 8px; position:absolute; top:56px; right:12px }
49 .linenr { color:#999999; text-decoration:none }
50 a.rss_logo {
51 float:right; padding:3px 0px; width:35px; line-height:10px;
52 border:1px solid; border-color:#fcc7a5 #7d3302 #3e1a01 #ff954e;
53 color:#ffffff; background-color:#ff6600;
54 font-weight:bold; font-family:sans-serif; font-size:10px;
55 text-align:center; text-decoration:none;
56 }
57 a.rss_logo:hover { background-color:#ee5500; }
58 </style>
59
11
@@ -3,78 +3,6 b' Content-type: text/html'
3 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
3 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
4 <html>
4 <html>
5 <head>
5 <head>
6 <link rel="icon" href="?static=hgicon.png" type="image/png">
6 <meta name="robots" content="index, nofollow" />
7 <meta name="robots" content="index, nofollow" />
7 <style type="text/css">
8 <style type="text/css">/*<![CDATA[*/ @import "?static=style.css"; /*]]>*/</style>
8 <!--
9 a { text-decoration:none; }
10 .parity0 { background-color: #dddddd; }
11 .parity1 { background-color: #eeeeee; }
12 .lineno { width: 60px; color: #aaaaaa; font-size: smaller;
13 text-align: right; padding-right:1em; }
14 .plusline { color: green; }
15 .minusline { color: red; }
16 .atline { color: purple; }
17 .annotate { font-size: smaller; text-align: right; padding-right: 1em; }
18 .buttons a {
19 background-color: #666666;
20 padding: 2pt;
21 color: white;
22 font-family: sans;
23 font-weight: bold;
24 }
25 .navigate a {
26 background-color: #ccc;
27 padding: 2pt;
28 font-family: sans;
29 color: black;
30 }
31
32 .metatag {
33 background-color: #888888;
34 color: white;
35 text-align: right;
36 }
37
38 /* Common */
39 pre { margin: 0; }
40
41 .logo {
42 background-color: #333;
43 padding: 4pt;
44 margin: 8pt 0 8pt 8pt;
45 font-family: sans;
46 font-size: 60%;
47 color: white;
48 float: right;
49 clear: right;
50 text-align: left;
51 }
52
53 .logo a {
54 font-weight: bold;
55 font-size: 150%;
56 color: #999;
57 }
58
59 /* Changelog entries */
60 .changelogEntry { width: 100%; }
61 .changelogEntry th { font-weight: normal; text-align: right; vertical-align: top; }
62 .changelogEntry th.age, .changelogEntry th.firstline { font-weight: bold; }
63 .changelogEntry th.firstline { text-align: left; width: inherit; }
64
65 /* Tag entries */
66 #tagEntries { list-style: none; margin: 0; padding: 0; }
67 #tagEntries .tagEntry { list-style: none; margin: 0; padding: 0; }
68 #tagEntries .tagEntry span.node { font-family: monospace; }
69
70 /* Changeset entry */
71 #changesetEntry { }
72 #changesetEntry th { font-weight: normal; background-color: #888; color: #fff; text-align: right; }
73 #changesetEntry th.files, #changesetEntry th.description { vertical-align: top; }
74
75 /* File diff view */
76 #filediffEntry { }
77 #filediffEntry th { font-weight: normal; background-color: #888; color: #fff; text-align: right; }
78
79 -->
80 </style>
@@ -4,3 +4,5 b' changelog = changelog-rss.tmpl'
4 changelogentry = changelogentry-rss.tmpl
4 changelogentry = changelogentry-rss.tmpl
5 filelog = filelog-rss.tmpl
5 filelog = filelog-rss.tmpl
6 filelogentry = filelogentry-rss.tmpl
6 filelogentry = filelogentry-rss.tmpl
7 tags = tags-rss.tmpl
8 tagentry = tagentry-rss.tmpl
@@ -1,11 +1,14 b''
1 #header#
1 #header#
2 <title>#repo|escape#: tags</title>
2 <title>#repo|escape#: tags</title>
3 <link rel="alternate" type="application/rss+xml"
4 href="?cmd=tags;style=rss" title="RSS feed for #repo|escape#: tags">
3 </head>
5 </head>
4 <body>
6 <body>
5
7
6 <div class="buttons">
8 <div class="buttons">
7 <a href="?cl=tip">changelog</a>
9 <a href="?cl=tip">changelog</a>
8 <a href="?mf=#manifest|short#;path=/">manifest</a>
10 <a href="?mf=#manifest|short#;path=/">manifest</a>
11 <a type="application/rss+xml" href="?cmd=tags;style=rss">rss</a>
9 </div>
12 </div>
10
13
11 <h2>tags:</h2>
14 <h2>tags:</h2>
@@ -18,8 +18,7 b' echo "name = test-archive" >> .hg/hgrc'
18 echo "allowzip = true" >> .hg/hgrc
18 echo "allowzip = true" >> .hg/hgrc
19 echo "allowgz = true" >> .hg/hgrc
19 echo "allowgz = true" >> .hg/hgrc
20 echo "allowbz2 = true" >> .hg/hgrc
20 echo "allowbz2 = true" >> .hg/hgrc
21 hg serve -p 20059 > /dev/null &
21 hg serve -p 20059 -d --pid-file=hg.pid
22 sleep 1 # wait for server to be started
23
22
24 TIP=`hg id -v | cut -f1 -d' '`
23 TIP=`hg id -v | cut -f1 -d' '`
25 QTIP=`hg id -q`
24 QTIP=`hg id -q`
@@ -35,5 +34,5 b' http_proxy= python getarchive.py "$TIP" '
35 http_proxy= python getarchive.py "$TIP" zip > archive.zip
34 http_proxy= python getarchive.py "$TIP" zip > archive.zip
36 unzip -t archive.zip | sed "s/$QTIP/TIP/"
35 unzip -t archive.zip | sed "s/$QTIP/TIP/"
37
36
38 kill $!
37 kill `cat hg.pid`
39 sleep 1 # wait for server to scream and die
38 sleep 1 # wait for server to scream and die
@@ -12,4 +12,3 b' Archive: archive.zip'
12 testing: test-archive-TIP/baz/bletch OK
12 testing: test-archive-TIP/baz/bletch OK
13 testing: test-archive-TIP/foo OK
13 testing: test-archive-TIP/foo OK
14 No errors detected in compressed data of archive.zip.
14 No errors detected in compressed data of archive.zip.
15 killed!
@@ -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,3 +1,2 b''
1 removing b
1 removing b
2 b never committed!
3 nothing changed
2 nothing changed
@@ -7,8 +7,7 b' hg init'
7 hg addremove
7 hg addremove
8 hg commit -m 1
8 hg commit -m 1
9 hg verify
9 hg verify
10 hg serve -p 20059 > /dev/null &
10 hg serve -p 20059 -d --pid-file=hg.pid
11 sleep 1 # wait for server to be started
12 cd ..
11 cd ..
13
12
14 http_proxy= hg clone http://localhost:20059/ copy
13 http_proxy= hg clone http://localhost:20059/ copy
@@ -19,4 +18,4 b' cat foo'
19 hg manifest
18 hg manifest
20 hg pull
19 hg pull
21
20
22 kill $!
21 kill `cat ../test/hg.pid`
@@ -12,9 +12,8 b' chmod -w .hg'
12 cd ..
12 cd ..
13
13
14 hg clone a b
14 hg clone a b
15
16 chmod +w a/.hg # let test clean up
17
15 cd b
18 cd b
16 hg verify
19 hg verify
17
18 cd ..
19
20 chmod +w a/.hg # let test clean up
@@ -19,4 +19,3 b' 2ed2a3912a0b24502043eae84ee4b279c18b90dd'
19 pulling from http://localhost:20059/
19 pulling from http://localhost:20059/
20 searching for changes
20 searching for changes
21 no changes found
21 no changes found
22 killed!
@@ -158,3 +158,24 b' hg remove d1/b'
158 hg rename d1 d3
158 hg rename d1 d3
159 hg status
159 hg status
160 hg update -C
160 hg update -C
161
162 echo "# transitive rename"
163 hg rename d1/b d1/bb
164 hg rename d1/bb d1/bc
165 hg status
166 hg update -C
167
168 echo "# transitive rename --after"
169 hg rename d1/b d1/bb
170 mv d1/bb d1/bc
171 hg rename --after d1/bb d1/bc
172 hg status
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
@@ -246,3 +246,11 b' R d1/a'
246 R d1/b
246 R d1/b
247 R d1/ba
247 R d1/ba
248 R d1/d11/a1
248 R d1/d11/a1
249 # transitive rename
250 A d1/bc
251 R d1/b
252 # transitive rename --after
253 A d1/bc
254 R d1/b
255 # idempotent renames (d1/b -> d1/bb followed by d1/bb -> d1/b)
256 M d1/b
@@ -1,5 +1,5 b''
1 255
1 255
2 abort: <urlopen error (, 'Connection refused')>
2 abort: Connection refused
3 ls: copy: No such file or directory
3 ls: copy: No such file or directory
4 changeset: 0:61c9426e69fe
4 changeset: 0:61c9426e69fe
5 tag: tip
5 tag: tip
General Comments 0
You need to be logged in to leave comments. Login now