Show More
@@ -0,0 +1,127 b'' | |||
|
1 | # repair.py - functions for repository repair for mercurial | |
|
2 | # | |
|
3 | # Copyright 2005, 2006 Chris Mason <mason@suse.com> | |
|
4 | # Copyright 2007 Matt Mackall | |
|
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 | import changegroup, revlog, os, commands | |
|
10 | ||
|
11 | def strip(ui, repo, rev, backup="all"): | |
|
12 | def limitheads(chlog, stop): | |
|
13 | """return the list of all nodes that have no children""" | |
|
14 | p = {} | |
|
15 | h = [] | |
|
16 | stoprev = 0 | |
|
17 | if stop in chlog.nodemap: | |
|
18 | stoprev = chlog.rev(stop) | |
|
19 | ||
|
20 | for r in xrange(chlog.count() - 1, -1, -1): | |
|
21 | n = chlog.node(r) | |
|
22 | if n not in p: | |
|
23 | h.append(n) | |
|
24 | if n == stop: | |
|
25 | break | |
|
26 | if r < stoprev: | |
|
27 | break | |
|
28 | for pn in chlog.parents(n): | |
|
29 | p[pn] = 1 | |
|
30 | return h | |
|
31 | ||
|
32 | def bundle(repo, bases, heads, rev, suffix): | |
|
33 | cg = repo.changegroupsubset(bases, heads, 'strip') | |
|
34 | backupdir = repo.join("strip-backup") | |
|
35 | if not os.path.isdir(backupdir): | |
|
36 | os.mkdir(backupdir) | |
|
37 | name = os.path.join(backupdir, "%s-%s" % (revlog.short(rev), suffix)) | |
|
38 | ui.warn("saving bundle to %s\n" % name) | |
|
39 | return changegroup.writebundle(cg, name, "HG10BZ") | |
|
40 | ||
|
41 | def stripall(revnum): | |
|
42 | mm = repo.changectx(rev).manifest() | |
|
43 | seen = {} | |
|
44 | ||
|
45 | for x in xrange(revnum, repo.changelog.count()): | |
|
46 | for f in repo.changectx(x).files(): | |
|
47 | if f in seen: | |
|
48 | continue | |
|
49 | seen[f] = 1 | |
|
50 | if f in mm: | |
|
51 | filerev = mm[f] | |
|
52 | else: | |
|
53 | filerev = 0 | |
|
54 | seen[f] = filerev | |
|
55 | # we go in two steps here so the strip loop happens in a | |
|
56 | # sensible order. When stripping many files, this helps keep | |
|
57 | # our disk access patterns under control. | |
|
58 | seen_list = seen.keys() | |
|
59 | seen_list.sort() | |
|
60 | for f in seen_list: | |
|
61 | ff = repo.file(f) | |
|
62 | filerev = seen[f] | |
|
63 | if filerev != 0: | |
|
64 | if filerev in ff.nodemap: | |
|
65 | filerev = ff.rev(filerev) | |
|
66 | else: | |
|
67 | filerev = 0 | |
|
68 | ff.strip(filerev, revnum) | |
|
69 | ||
|
70 | chlog = repo.changelog | |
|
71 | # TODO delete the undo files, and handle undo of merge sets | |
|
72 | pp = chlog.parents(rev) | |
|
73 | revnum = chlog.rev(rev) | |
|
74 | ||
|
75 | # save is a list of all the branches we are truncating away | |
|
76 | # that we actually want to keep. changegroup will be used | |
|
77 | # to preserve them and add them back after the truncate | |
|
78 | saveheads = [] | |
|
79 | savebases = {} | |
|
80 | ||
|
81 | heads = limitheads(chlog, rev) | |
|
82 | seen = {} | |
|
83 | ||
|
84 | # search through all the heads, finding those where the revision | |
|
85 | # we want to strip away is an ancestor. Also look for merges | |
|
86 | # that might be turned into new heads by the strip. | |
|
87 | while heads: | |
|
88 | h = heads.pop() | |
|
89 | n = h | |
|
90 | while True: | |
|
91 | seen[n] = 1 | |
|
92 | pp = chlog.parents(n) | |
|
93 | if pp[1] != revlog.nullid: | |
|
94 | for p in pp: | |
|
95 | if chlog.rev(p) > revnum and p not in seen: | |
|
96 | heads.append(p) | |
|
97 | if pp[0] == revlog.nullid: | |
|
98 | break | |
|
99 | if chlog.rev(pp[0]) < revnum: | |
|
100 | break | |
|
101 | n = pp[0] | |
|
102 | if n == rev: | |
|
103 | break | |
|
104 | r = chlog.reachable(h, rev) | |
|
105 | if rev not in r: | |
|
106 | saveheads.append(h) | |
|
107 | for x in r: | |
|
108 | if chlog.rev(x) > revnum: | |
|
109 | savebases[x] = 1 | |
|
110 | ||
|
111 | # create a changegroup for all the branches we need to keep | |
|
112 | if backup == "all": | |
|
113 | bundle(repo, [rev], chlog.heads(), rev, 'backup') | |
|
114 | if saveheads: | |
|
115 | chgrpfile = bundle(repo, savebases.keys(), saveheads, rev, 'temp') | |
|
116 | ||
|
117 | stripall(revnum) | |
|
118 | ||
|
119 | change = chlog.read(rev) | |
|
120 | chlog.strip(revnum, revnum) | |
|
121 | repo.manifest.strip(repo.manifest.rev(change[0]), revnum) | |
|
122 | if saveheads: | |
|
123 | ui.status("adding branch\n") | |
|
124 | commands.unbundle(ui, repo, "file:%s" % chgrpfile, update=False) | |
|
125 | if backup != "strip": | |
|
126 | os.unlink(chgrpfile) | |
|
127 |
@@ -7,7 +7,7 b' help:' | |||
|
7 | 7 | @echo ' all - build program and documentation' |
|
8 | 8 | @echo ' install - install program and man pages to PREFIX ($(PREFIX))' |
|
9 | 9 | @echo ' install-home - install with setup.py install --home=HOME ($(HOME))' |
|
10 |
@echo ' local - build |
|
|
10 | @echo ' local - build for inplace usage' | |
|
11 | 11 | @echo ' tests - run all tests in the automatic test suite' |
|
12 | 12 | @echo ' test-foo - run only specified tests (e.g. test-merge1)' |
|
13 | 13 | @echo ' dist - run all tests and create a source tarball in dist/' |
@@ -24,6 +24,8 b' all: build doc' | |||
|
24 | 24 | |
|
25 | 25 | local: |
|
26 | 26 | $(PYTHON) setup.py build_ext -i |
|
27 | $(PYTHON) setup.py build_py -c -d . | |
|
28 | $(PYTHON) hg version | |
|
27 | 29 | |
|
28 | 30 | build: |
|
29 | 31 | $(PYTHON) setup.py build |
@@ -33,7 +35,7 b' doc:' | |||
|
33 | 35 | |
|
34 | 36 | clean: |
|
35 | 37 | -$(PYTHON) setup.py clean --all # ignore errors of this command |
|
36 | find . -name '*.py[co]' -exec rm -f '{}' ';' | |
|
38 | find . -name '*.py[cdo]' -exec rm -f '{}' ';' | |
|
37 | 39 | rm -f MANIFEST mercurial/__version__.py mercurial/*.so tests/*.err |
|
38 | 40 | $(MAKE) -C doc clean |
|
39 | 41 |
@@ -525,6 +525,8 b' web::' | |||
|
525 | 525 | Default is "unknown". |
|
526 | 526 | errorlog;; |
|
527 | 527 | Where to output the error log. Default is stderr. |
|
528 | hidden;; | |
|
529 | Whether to hide the repository in the hgwebdir index. Default is false. | |
|
528 | 530 | ipv6;; |
|
529 | 531 | Whether to use IPv6. Default is false. |
|
530 | 532 | name;; |
@@ -30,7 +30,8 b' refresh contents of top applied patch ' | |||
|
30 | 30 | ''' |
|
31 | 31 | |
|
32 | 32 | from mercurial.i18n import _ |
|
33 |
from mercurial import commands, cmdutil, hg, patch, revlog, util |
|
|
33 | from mercurial import commands, cmdutil, hg, patch, revlog, util | |
|
34 | from mercurial import repair | |
|
34 | 35 | import os, sys, re, errno |
|
35 | 36 | |
|
36 | 37 | commands.norepo += " qclone qversion" |
@@ -629,71 +630,9 b' class queue:' | |||
|
629 | 630 | self.removeundo(repo) |
|
630 | 631 | |
|
631 | 632 | def strip(self, repo, rev, update=True, backup="all", wlock=None): |
|
632 | def limitheads(chlog, stop): | |
|
633 | """return the list of all nodes that have no children""" | |
|
634 | p = {} | |
|
635 | h = [] | |
|
636 | stoprev = 0 | |
|
637 | if stop in chlog.nodemap: | |
|
638 | stoprev = chlog.rev(stop) | |
|
639 | ||
|
640 | for r in xrange(chlog.count() - 1, -1, -1): | |
|
641 | n = chlog.node(r) | |
|
642 | if n not in p: | |
|
643 | h.append(n) | |
|
644 | if n == stop: | |
|
645 | break | |
|
646 | if r < stoprev: | |
|
647 | break | |
|
648 | for pn in chlog.parents(n): | |
|
649 | p[pn] = 1 | |
|
650 | return h | |
|
651 | ||
|
652 | def bundle(cg): | |
|
653 | backupdir = repo.join("strip-backup") | |
|
654 | if not os.path.isdir(backupdir): | |
|
655 | os.mkdir(backupdir) | |
|
656 | name = os.path.join(backupdir, "%s" % revlog.short(rev)) | |
|
657 | name = savename(name) | |
|
658 | self.ui.warn("saving bundle to %s\n" % name) | |
|
659 | return changegroup.writebundle(cg, name, "HG10BZ") | |
|
660 | ||
|
661 | def stripall(revnum): | |
|
662 | mm = repo.changectx(rev).manifest() | |
|
663 | seen = {} | |
|
664 | ||
|
665 | for x in xrange(revnum, repo.changelog.count()): | |
|
666 | for f in repo.changectx(x).files(): | |
|
667 | if f in seen: | |
|
668 | continue | |
|
669 | seen[f] = 1 | |
|
670 | if f in mm: | |
|
671 | filerev = mm[f] | |
|
672 | else: | |
|
673 | filerev = 0 | |
|
674 | seen[f] = filerev | |
|
675 | # we go in two steps here so the strip loop happens in a | |
|
676 | # sensible order. When stripping many files, this helps keep | |
|
677 | # our disk access patterns under control. | |
|
678 | seen_list = seen.keys() | |
|
679 | seen_list.sort() | |
|
680 | for f in seen_list: | |
|
681 | ff = repo.file(f) | |
|
682 | filerev = seen[f] | |
|
683 | if filerev != 0: | |
|
684 | if filerev in ff.nodemap: | |
|
685 | filerev = ff.rev(filerev) | |
|
686 | else: | |
|
687 | filerev = 0 | |
|
688 | ff.strip(filerev, revnum) | |
|
689 | ||
|
690 | 633 | if not wlock: |
|
691 | 634 | wlock = repo.wlock() |
|
692 | 635 | lock = repo.lock() |
|
693 | chlog = repo.changelog | |
|
694 | # TODO delete the undo files, and handle undo of merge sets | |
|
695 | pp = chlog.parents(rev) | |
|
696 | revnum = chlog.rev(rev) | |
|
697 | 636 | |
|
698 | 637 | if update: |
|
699 | 638 | self.check_localchanges(repo, refresh=False) |
@@ -701,62 +640,8 b' class queue:' | |||
|
701 | 640 | hg.clean(repo, urev, wlock=wlock) |
|
702 | 641 | repo.dirstate.write() |
|
703 | 642 | |
|
704 | # save is a list of all the branches we are truncating away | |
|
705 | # that we actually want to keep. changegroup will be used | |
|
706 | # to preserve them and add them back after the truncate | |
|
707 | saveheads = [] | |
|
708 | savebases = {} | |
|
709 | ||
|
710 | heads = limitheads(chlog, rev) | |
|
711 | seen = {} | |
|
712 | ||
|
713 | # search through all the heads, finding those where the revision | |
|
714 | # we want to strip away is an ancestor. Also look for merges | |
|
715 | # that might be turned into new heads by the strip. | |
|
716 | while heads: | |
|
717 | h = heads.pop() | |
|
718 | n = h | |
|
719 | while True: | |
|
720 | seen[n] = 1 | |
|
721 | pp = chlog.parents(n) | |
|
722 | if pp[1] != revlog.nullid: | |
|
723 | for p in pp: | |
|
724 | if chlog.rev(p) > revnum and p not in seen: | |
|
725 | heads.append(p) | |
|
726 | if pp[0] == revlog.nullid: | |
|
727 | break | |
|
728 | if chlog.rev(pp[0]) < revnum: | |
|
729 | break | |
|
730 | n = pp[0] | |
|
731 | if n == rev: | |
|
732 | break | |
|
733 | r = chlog.reachable(h, rev) | |
|
734 | if rev not in r: | |
|
735 | saveheads.append(h) | |
|
736 | for x in r: | |
|
737 | if chlog.rev(x) > revnum: | |
|
738 | savebases[x] = 1 | |
|
739 | ||
|
740 | # create a changegroup for all the branches we need to keep | |
|
741 | if backup == "all": | |
|
742 | backupch = repo.changegroupsubset([rev], chlog.heads(), 'strip') | |
|
743 | bundle(backupch) | |
|
744 | if saveheads: | |
|
745 | backupch = repo.changegroupsubset(savebases.keys(), saveheads, 'strip') | |
|
746 | chgrpfile = bundle(backupch) | |
|
747 | ||
|
748 | stripall(revnum) | |
|
749 | ||
|
750 | change = chlog.read(rev) | |
|
751 | chlog.strip(revnum, revnum) | |
|
752 | repo.manifest.strip(repo.manifest.rev(change[0]), revnum) | |
|
753 | 643 | self.removeundo(repo) |
|
754 | if saveheads: | |
|
755 | self.ui.status("adding branch\n") | |
|
756 | commands.unbundle(self.ui, repo, "file:%s" % chgrpfile, | |
|
757 | update=False) | |
|
758 | if backup != "strip": | |
|
759 | os.unlink(chgrpfile) | |
|
644 | repair.strip(self.ui, repo, rev, backup) | |
|
760 | 645 | |
|
761 | 646 | def isapplied(self, patch): |
|
762 | 647 | """returns (index, rev, patch)""" |
@@ -142,6 +142,9 b' class hgwebdir(object):' | |||
|
142 | 142 | def get(section, name, default=None): |
|
143 | 143 | return u.config(section, name, default, untrusted=True) |
|
144 | 144 | |
|
145 | if u.configbool("web", "hidden", untrusted=True): | |
|
146 | continue | |
|
147 | ||
|
145 | 148 | url = ('/'.join([req.env["REQUEST_URI"].split('?')[0], name]) |
|
146 | 149 | .replace("//", "/")) + '/' |
|
147 | 150 |
@@ -105,6 +105,7 b' defaultdateformats = (' | |||
|
105 | 105 | '%m/%d/%Y', |
|
106 | 106 | '%a %b %d %H:%M:%S %Y', |
|
107 | 107 | '%a %b %d %I:%M:%S%p %Y', |
|
108 | '%a, %d %b %Y %H:%M:%S', # GNU coreutils "/bin/date --rfc-2822" | |
|
108 | 109 | '%b %d %H:%M:%S %Y', |
|
109 | 110 | '%b %d %I:%M:%S%p %Y', |
|
110 | 111 | '%b %d %H:%M:%S', |
@@ -48,7 +48,7 b' filelogchild = \'<tr><td align="right">ch' | |||
|
48 | 48 | shortlog = shortlog.tmpl |
|
49 | 49 | tagtag = '<span class="tagtag" title="{name}">{name}</span> ' |
|
50 | 50 | branchtag = '<span class="branchtag" title="{name}">{name}</span> ' |
|
51 | shortlogentry = '<tr class="parity#parity#"><td class="age"><i>#date|age# ago</i></td><td><i>#author#</i></td><td><a class="list" href="{url}rev/#node|short#{sessionvars%urlparameter}"><b>#desc|strip|firstline|escape#</b> <span class="logtags">{branches%branchtag}{tags%tagtag}</span></a></td><td class="link" nowrap><a href="{url}rev/#node|short#{sessionvars%urlparameter}">changeset</a> | <a href="{url}file/#node|short#{sessionvars%urlparameter}">manifest</a></td></tr>' | |
|
51 | shortlogentry = '<tr class="parity#parity#"><td class="age"><i>#date|age# ago</i></td><td><i>#author|person#</i></td><td><a class="list" href="{url}rev/#node|short#{sessionvars%urlparameter}"><b>#desc|strip|firstline|escape#</b> <span class="logtags">{branches%branchtag}{tags%tagtag}</span></a></td><td class="link" nowrap><a href="{url}rev/#node|short#{sessionvars%urlparameter}">changeset</a> | <a href="{url}file/#node|short#{sessionvars%urlparameter}">manifest</a></td></tr>' | |
|
52 | 52 | filelogentry = '<tr class="parity#parity#"><td class="age"><i>#date|age# ago</i></td><td><a class="list" href="{url}rev/#node|short#{sessionvars%urlparameter}"><b>#desc|strip|firstline|escape#</b></a></td><td class="link"><a href="{url}file/#node|short#/#file|urlescape#{sessionvars%urlparameter}">file</a> | <a href="{url}diff/#node|short#/#file|urlescape#{sessionvars%urlparameter}">diff</a> | <a href="{url}annotate/#node|short#/#file|urlescape#{sessionvars%urlparameter}">annotate</a> #rename%filelogrename#</td></tr>' |
|
53 | 53 | archiveentry = ' | <a href="{url}archive/{node|short}{extension}">#type|escape#</a> ' |
|
54 | 54 | indexentry = '<tr class="parity#parity#"><td><a class="list" href="#url#{sessionvars%urlparameter}"><b>#name|escape#</b></a></td><td>#description#</td><td>#contact|obfuscate#</td><td class="age">#lastchange|age# ago</td><td class="indexlinks"><a class="rss_logo" href="#url#rss-log">RSS</a> #archives%archiveentry#</td></tr>' |
@@ -32,10 +32,10 b' summary |' | |||
|
32 | 32 | <tr><td>last change</td><td>#lastchange|rfc822date#</td></tr> |
|
33 | 33 | </table> |
|
34 | 34 | |
|
35 | <div><a class="title" href="{url}log{sessionvars%urlparameter}">changes</a></div> | |
|
35 | <div><a class="title" href="{url}shortlog{sessionvars%urlparameter}">changes</a></div> | |
|
36 | 36 | <table cellspacing="0"> |
|
37 | 37 | #shortlog# |
|
38 | <tr class="light"><td colspan="4"><a class="list" href="{url}log{sessionvars%urlparameter}">...</a></td></tr> | |
|
38 | <tr class="light"><td colspan="4"><a class="list" href="{url}shortlog{sessionvars%urlparameter}">...</a></td></tr> | |
|
39 | 39 | </table> |
|
40 | 40 | |
|
41 | 41 | <div><a class="title" href="{url}tags{sessionvars%urlparameter}">tags</a></div> |
@@ -1,4 +1,4 b'' | |||
|
1 |
default = ' |
|
|
1 | default = 'shortlog' | |
|
2 | 2 | header = header.tmpl |
|
3 | 3 | footer = footer.tmpl |
|
4 | 4 | search = search.tmpl |
@@ -1,7 +1,7 b'' | |||
|
1 | 1 | <table class="slogEntry parity#parity#"> |
|
2 | 2 | <tr> |
|
3 | 3 | <td class="age">#date|age#</td> |
|
4 |
<td class="author">#author| |
|
|
4 | <td class="author">#author|person#</td> | |
|
5 | 5 | <td class="node"><a href="#url#rev/#node|short#{sessionvars%urlparameter}">#desc|strip|firstline|escape#</a></td> |
|
6 | 6 | </tr> |
|
7 | 7 | </table> |
@@ -58,10 +58,10 b' pre { margin: 0; }' | |||
|
58 | 58 | .logEntry th.firstline { text-align: left; width: inherit; } |
|
59 | 59 | |
|
60 | 60 | /* Shortlog entries */ |
|
61 |
.slogEntry { width: 100%; |
|
|
62 |
.slogEntry .age { width: |
|
|
61 | .slogEntry { width: 100%; } | |
|
62 | .slogEntry .age { width: 8em; } | |
|
63 | 63 | .slogEntry td { font-weight: normal; text-align: left; vertical-align: top; } |
|
64 |
.slogEntry td.author { width: |
|
|
64 | .slogEntry td.author { width: 15em; } | |
|
65 | 65 | |
|
66 | 66 | /* Tag entries */ |
|
67 | 67 | #tagEntries { list-style: none; margin: 0; padding: 0; } |
General Comments 0
You need to be logged in to leave comments.
Login now