Show More
@@ -1,130 +1,131 b'' | |||||
1 | # perf.py - performance test routines |
|
1 | # perf.py - performance test routines | |
|
2 | '''helper extension to measure performance''' | |||
2 |
|
3 | |||
3 | from mercurial import cmdutil, match, commands |
|
4 | from mercurial import cmdutil, match, commands | |
4 | import time, os, sys |
|
5 | import time, os, sys | |
5 |
|
6 | |||
6 | def timer(func): |
|
7 | def timer(func): | |
7 | results = [] |
|
8 | results = [] | |
8 | begin = time.time() |
|
9 | begin = time.time() | |
9 | count = 0 |
|
10 | count = 0 | |
10 | while 1: |
|
11 | while 1: | |
11 | ostart = os.times() |
|
12 | ostart = os.times() | |
12 | cstart = time.time() |
|
13 | cstart = time.time() | |
13 | r = func() |
|
14 | r = func() | |
14 | cstop = time.time() |
|
15 | cstop = time.time() | |
15 | ostop = os.times() |
|
16 | ostop = os.times() | |
16 | count += 1 |
|
17 | count += 1 | |
17 | a, b = ostart, ostop |
|
18 | a, b = ostart, ostop | |
18 | results.append((cstop - cstart, b[0] - a[0], b[1]-a[1])) |
|
19 | results.append((cstop - cstart, b[0] - a[0], b[1]-a[1])) | |
19 | if cstop - begin > 3 and count >= 100: |
|
20 | if cstop - begin > 3 and count >= 100: | |
20 | break |
|
21 | break | |
21 | if cstop - begin > 10 and count >= 3: |
|
22 | if cstop - begin > 10 and count >= 3: | |
22 | break |
|
23 | break | |
23 | if r: |
|
24 | if r: | |
24 | sys.stderr.write("! result: %s\n" % r) |
|
25 | sys.stderr.write("! result: %s\n" % r) | |
25 | m = min(results) |
|
26 | m = min(results) | |
26 | sys.stderr.write("! wall %f comb %f user %f sys %f (best of %d)\n" |
|
27 | sys.stderr.write("! wall %f comb %f user %f sys %f (best of %d)\n" | |
27 | % (m[0], m[1] + m[2], m[1], m[2], count)) |
|
28 | % (m[0], m[1] + m[2], m[1], m[2], count)) | |
28 |
|
29 | |||
29 | def perfwalk(ui, repo, *pats): |
|
30 | def perfwalk(ui, repo, *pats): | |
30 | try: |
|
31 | try: | |
31 | m = cmdutil.match(repo, pats, {}) |
|
32 | m = cmdutil.match(repo, pats, {}) | |
32 | timer(lambda: len(list(repo.dirstate.walk(m, True, False)))) |
|
33 | timer(lambda: len(list(repo.dirstate.walk(m, True, False)))) | |
33 | except: |
|
34 | except: | |
34 | try: |
|
35 | try: | |
35 | m = cmdutil.match(repo, pats, {}) |
|
36 | m = cmdutil.match(repo, pats, {}) | |
36 | timer(lambda: len([b for a,b,c in repo.dirstate.statwalk([], m)])) |
|
37 | timer(lambda: len([b for a,b,c in repo.dirstate.statwalk([], m)])) | |
37 | except: |
|
38 | except: | |
38 | timer(lambda: len(list(cmdutil.walk(repo, pats, {})))) |
|
39 | timer(lambda: len(list(cmdutil.walk(repo, pats, {})))) | |
39 |
|
40 | |||
40 | def perfstatus(ui, repo, *pats): |
|
41 | def perfstatus(ui, repo, *pats): | |
41 | #m = match.always(repo.root, repo.getcwd()) |
|
42 | #m = match.always(repo.root, repo.getcwd()) | |
42 | #timer(lambda: sum(map(len, repo.dirstate.status(m, False, False, False)))) |
|
43 | #timer(lambda: sum(map(len, repo.dirstate.status(m, False, False, False)))) | |
43 | timer(lambda: sum(map(len, repo.status()))) |
|
44 | timer(lambda: sum(map(len, repo.status()))) | |
44 |
|
45 | |||
45 | def perfheads(ui, repo): |
|
46 | def perfheads(ui, repo): | |
46 | timer(lambda: len(repo.changelog.heads())) |
|
47 | timer(lambda: len(repo.changelog.heads())) | |
47 |
|
48 | |||
48 | def perftags(ui, repo): |
|
49 | def perftags(ui, repo): | |
49 | import mercurial.changelog, mercurial.manifest |
|
50 | import mercurial.changelog, mercurial.manifest | |
50 | def t(): |
|
51 | def t(): | |
51 | repo.changelog = mercurial.changelog.changelog(repo.sopener) |
|
52 | repo.changelog = mercurial.changelog.changelog(repo.sopener) | |
52 | repo.manifest = mercurial.manifest.manifest(repo.sopener) |
|
53 | repo.manifest = mercurial.manifest.manifest(repo.sopener) | |
53 | repo.tagscache = None |
|
54 | repo.tagscache = None | |
54 | return len(repo.tags()) |
|
55 | return len(repo.tags()) | |
55 | timer(t) |
|
56 | timer(t) | |
56 |
|
57 | |||
57 | def perfdirstate(ui, repo): |
|
58 | def perfdirstate(ui, repo): | |
58 | "a" in repo.dirstate |
|
59 | "a" in repo.dirstate | |
59 | def d(): |
|
60 | def d(): | |
60 | repo.dirstate.invalidate() |
|
61 | repo.dirstate.invalidate() | |
61 | "a" in repo.dirstate |
|
62 | "a" in repo.dirstate | |
62 | timer(d) |
|
63 | timer(d) | |
63 |
|
64 | |||
64 | def perfdirstatedirs(ui, repo): |
|
65 | def perfdirstatedirs(ui, repo): | |
65 | "a" in repo.dirstate |
|
66 | "a" in repo.dirstate | |
66 | def d(): |
|
67 | def d(): | |
67 | "a" in repo.dirstate._dirs |
|
68 | "a" in repo.dirstate._dirs | |
68 | del repo.dirstate._dirs |
|
69 | del repo.dirstate._dirs | |
69 | timer(d) |
|
70 | timer(d) | |
70 |
|
71 | |||
71 | def perfmanifest(ui, repo): |
|
72 | def perfmanifest(ui, repo): | |
72 | def d(): |
|
73 | def d(): | |
73 | t = repo.manifest.tip() |
|
74 | t = repo.manifest.tip() | |
74 | m = repo.manifest.read(t) |
|
75 | m = repo.manifest.read(t) | |
75 | repo.manifest.mapcache = None |
|
76 | repo.manifest.mapcache = None | |
76 | repo.manifest._cache = None |
|
77 | repo.manifest._cache = None | |
77 | timer(d) |
|
78 | timer(d) | |
78 |
|
79 | |||
79 | def perfindex(ui, repo): |
|
80 | def perfindex(ui, repo): | |
80 | import mercurial.changelog |
|
81 | import mercurial.changelog | |
81 | def d(): |
|
82 | def d(): | |
82 | t = repo.changelog.tip() |
|
83 | t = repo.changelog.tip() | |
83 | repo.changelog = mercurial.changelog.changelog(repo.sopener) |
|
84 | repo.changelog = mercurial.changelog.changelog(repo.sopener) | |
84 | repo.changelog._loadindexmap() |
|
85 | repo.changelog._loadindexmap() | |
85 | timer(d) |
|
86 | timer(d) | |
86 |
|
87 | |||
87 | def perfstartup(ui, repo): |
|
88 | def perfstartup(ui, repo): | |
88 | cmd = sys.argv[0] |
|
89 | cmd = sys.argv[0] | |
89 | def d(): |
|
90 | def d(): | |
90 | os.system("HGRCPATH= %s version -q > /dev/null" % cmd) |
|
91 | os.system("HGRCPATH= %s version -q > /dev/null" % cmd) | |
91 | timer(d) |
|
92 | timer(d) | |
92 |
|
93 | |||
93 | def perfparents(ui, repo): |
|
94 | def perfparents(ui, repo): | |
94 | nl = [repo.changelog.node(i) for i in xrange(1000)] |
|
95 | nl = [repo.changelog.node(i) for i in xrange(1000)] | |
95 | def d(): |
|
96 | def d(): | |
96 | for n in nl: |
|
97 | for n in nl: | |
97 | repo.changelog.parents(n) |
|
98 | repo.changelog.parents(n) | |
98 | timer(d) |
|
99 | timer(d) | |
99 |
|
100 | |||
100 | def perflookup(ui, repo, rev): |
|
101 | def perflookup(ui, repo, rev): | |
101 | timer(lambda: len(repo.lookup(rev))) |
|
102 | timer(lambda: len(repo.lookup(rev))) | |
102 |
|
103 | |||
103 | def perflog(ui, repo): |
|
104 | def perflog(ui, repo): | |
104 | ui.pushbuffer() |
|
105 | ui.pushbuffer() | |
105 | timer(lambda: commands.log(ui, repo, rev=[], date='', user='')) |
|
106 | timer(lambda: commands.log(ui, repo, rev=[], date='', user='')) | |
106 | ui.popbuffer() |
|
107 | ui.popbuffer() | |
107 |
|
108 | |||
108 | def perftemplating(ui, repo): |
|
109 | def perftemplating(ui, repo): | |
109 | ui.pushbuffer() |
|
110 | ui.pushbuffer() | |
110 | timer(lambda: commands.log(ui, repo, rev=[], date='', user='', |
|
111 | timer(lambda: commands.log(ui, repo, rev=[], date='', user='', | |
111 | template='{date|shortdate} [{rev}:{node|short}]' |
|
112 | template='{date|shortdate} [{rev}:{node|short}]' | |
112 | ' {author|person}: {desc|firstline}\n')) |
|
113 | ' {author|person}: {desc|firstline}\n')) | |
113 | ui.popbuffer() |
|
114 | ui.popbuffer() | |
114 |
|
115 | |||
115 | cmdtable = { |
|
116 | cmdtable = { | |
116 | 'perflookup': (perflookup, []), |
|
117 | 'perflookup': (perflookup, []), | |
117 | 'perfparents': (perfparents, []), |
|
118 | 'perfparents': (perfparents, []), | |
118 | 'perfstartup': (perfstartup, []), |
|
119 | 'perfstartup': (perfstartup, []), | |
119 | 'perfstatus': (perfstatus, []), |
|
120 | 'perfstatus': (perfstatus, []), | |
120 | 'perfwalk': (perfwalk, []), |
|
121 | 'perfwalk': (perfwalk, []), | |
121 | 'perfmanifest': (perfmanifest, []), |
|
122 | 'perfmanifest': (perfmanifest, []), | |
122 | 'perfindex': (perfindex, []), |
|
123 | 'perfindex': (perfindex, []), | |
123 | 'perfheads': (perfheads, []), |
|
124 | 'perfheads': (perfheads, []), | |
124 | 'perftags': (perftags, []), |
|
125 | 'perftags': (perftags, []), | |
125 | 'perfdirstate': (perfdirstate, []), |
|
126 | 'perfdirstate': (perfdirstate, []), | |
126 | 'perfdirstatedirs': (perfdirstate, []), |
|
127 | 'perfdirstatedirs': (perfdirstate, []), | |
127 | 'perflog': (perflog, []), |
|
128 | 'perflog': (perflog, []), | |
128 | 'perftemplating': (perftemplating, []), |
|
129 | 'perftemplating': (perftemplating, []), | |
129 | } |
|
130 | } | |
130 |
|
131 |
@@ -1,99 +1,99 b'' | |||||
1 | # acl.py - changeset access control for mercurial |
|
1 | # acl.py - changeset access control for mercurial | |
2 | # |
|
2 | # | |
3 | # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com> |
|
3 | # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com> | |
4 | # |
|
4 | # | |
5 | # This software may be used and distributed according to the terms of the |
|
5 | # This software may be used and distributed according to the terms of the | |
6 | # GNU General Public License version 2, incorporated herein by reference. |
|
6 | # GNU General Public License version 2, incorporated herein by reference. | |
7 | # |
|
7 | # | |
8 | # this hook allows to allow or deny access to parts of a repo when |
|
8 | ||
9 | # taking incoming changesets. |
|
9 | '''provide simple hooks for access control | |
10 | # |
|
10 | ||
11 |
|
|
11 | Authorization is against local user name on system where hook is run, not | |
12 |
|
|
12 | committer of original changeset (since that is easy to spoof). | |
13 | # spoof). |
|
13 | ||
14 | # |
|
14 | The acl hook is best to use if you use hgsh to set up restricted shells for | |
15 | # acl hook is best to use if you use hgsh to set up restricted shells |
|
15 | authenticated users to only push to / pull from. It's not safe if user has | |
16 | # for authenticated users to only push to / pull from. not safe if |
|
16 | interactive shell access, because they can disable the hook. It's also not | |
17 | # user has interactive shell access, because they can disable hook. |
|
17 | safe if remote users share one local account, because then there's no way to | |
18 | # also not safe if remote users share one local account, because then |
|
18 | tell remote users apart. | |
19 | # no way to tell remote users apart. |
|
19 | ||
20 | # |
|
20 | To use, configure the acl extension in hgrc like this: | |
21 | # to use, configure acl extension in hgrc like this: |
|
21 | ||
22 | # |
|
22 | [extensions] | |
23 | # [extensions] |
|
23 | hgext.acl = | |
24 | # hgext.acl = |
|
24 | ||
25 | # |
|
25 | [hooks] | |
26 | # [hooks] |
|
26 | pretxnchangegroup.acl = python:hgext.acl.hook | |
27 | # pretxnchangegroup.acl = python:hgext.acl.hook |
|
27 | ||
28 | # |
|
28 | [acl] | |
29 | # [acl] |
|
29 | sources = serve # check if source of incoming changes in this list | |
30 | # sources = serve # check if source of incoming changes in this list |
|
30 | # ("serve" == ssh or http, "push", "pull", "bundle") | |
31 | # # ("serve" == ssh or http, "push", "pull", "bundle") |
|
31 | ||
32 | # |
|
32 | Allow and deny lists have a subtree pattern (default syntax is glob) on the | |
33 | # allow and deny lists have subtree pattern (default syntax is glob) |
|
33 | left and user names on right. The deny list is checked before the allow list. | |
34 | # on left, user names on right. deny list checked before allow list. |
|
34 | ||
35 | # |
|
35 | [acl.allow] | |
36 | # [acl.allow] |
|
36 | # if acl.allow not present, all users allowed by default | |
37 |
|
|
37 | # empty acl.allow = no users allowed | |
38 | # # empty acl.allow = no users allowed |
|
38 | docs/** = doc_writer | |
39 | # docs/** = doc_writer |
|
39 | .hgtags = release_engineer | |
40 | # .hgtags = release_engineer |
|
40 | ||
41 | # |
|
41 | [acl.deny] | |
42 | # [acl.deny] |
|
42 | # if acl.deny not present, no users denied by default | |
43 | # # if acl.deny not present, no users denied by default |
|
43 | # empty acl.deny = all users allowed | |
44 | # # empty acl.deny = all users allowed |
|
44 | glob pattern = user4, user5 | |
45 | # glob pattern = user4, user5 |
|
45 | ** = user6 | |
46 | # ** = user6 |
|
46 | ''' | |
47 |
|
47 | |||
48 | from mercurial.i18n import _ |
|
48 | from mercurial.i18n import _ | |
49 | from mercurial import util, match |
|
49 | from mercurial import util, match | |
50 | import getpass, urllib |
|
50 | import getpass, urllib | |
51 |
|
51 | |||
52 | def buildmatch(ui, repo, user, key): |
|
52 | def buildmatch(ui, repo, user, key): | |
53 | '''return tuple of (match function, list enabled).''' |
|
53 | '''return tuple of (match function, list enabled).''' | |
54 | if not ui.has_section(key): |
|
54 | if not ui.has_section(key): | |
55 | ui.debug(_('acl: %s not enabled\n') % key) |
|
55 | ui.debug(_('acl: %s not enabled\n') % key) | |
56 | return None |
|
56 | return None | |
57 |
|
57 | |||
58 | pats = [pat for pat, users in ui.configitems(key) |
|
58 | pats = [pat for pat, users in ui.configitems(key) | |
59 | if user in users.replace(',', ' ').split()] |
|
59 | if user in users.replace(',', ' ').split()] | |
60 | ui.debug(_('acl: %s enabled, %d entries for user %s\n') % |
|
60 | ui.debug(_('acl: %s enabled, %d entries for user %s\n') % | |
61 | (key, len(pats), user)) |
|
61 | (key, len(pats), user)) | |
62 | if pats: |
|
62 | if pats: | |
63 | return match.match(repo.root, '', pats) |
|
63 | return match.match(repo.root, '', pats) | |
64 | return match.exact(repo.root, '', []) |
|
64 | return match.exact(repo.root, '', []) | |
65 |
|
65 | |||
66 |
|
66 | |||
67 | def hook(ui, repo, hooktype, node=None, source=None, **kwargs): |
|
67 | def hook(ui, repo, hooktype, node=None, source=None, **kwargs): | |
68 | if hooktype != 'pretxnchangegroup': |
|
68 | if hooktype != 'pretxnchangegroup': | |
69 | raise util.Abort(_('config error - hook type "%s" cannot stop ' |
|
69 | raise util.Abort(_('config error - hook type "%s" cannot stop ' | |
70 | 'incoming changesets') % hooktype) |
|
70 | 'incoming changesets') % hooktype) | |
71 | if source not in ui.config('acl', 'sources', 'serve').split(): |
|
71 | if source not in ui.config('acl', 'sources', 'serve').split(): | |
72 | ui.debug(_('acl: changes have source "%s" - skipping\n') % source) |
|
72 | ui.debug(_('acl: changes have source "%s" - skipping\n') % source) | |
73 | return |
|
73 | return | |
74 |
|
74 | |||
75 | user = None |
|
75 | user = None | |
76 | if source == 'serve' and 'url' in kwargs: |
|
76 | if source == 'serve' and 'url' in kwargs: | |
77 | url = kwargs['url'].split(':') |
|
77 | url = kwargs['url'].split(':') | |
78 | if url[0] == 'remote' and url[1].startswith('http'): |
|
78 | if url[0] == 'remote' and url[1].startswith('http'): | |
79 | user = urllib.unquote(url[2]) |
|
79 | user = urllib.unquote(url[2]) | |
80 |
|
80 | |||
81 | if user is None: |
|
81 | if user is None: | |
82 | user = getpass.getuser() |
|
82 | user = getpass.getuser() | |
83 |
|
83 | |||
84 | cfg = ui.config('acl', 'config') |
|
84 | cfg = ui.config('acl', 'config') | |
85 | if cfg: |
|
85 | if cfg: | |
86 | ui.readconfig(cfg, sections = ['acl.allow', 'acl.deny']) |
|
86 | ui.readconfig(cfg, sections = ['acl.allow', 'acl.deny']) | |
87 | allow = buildmatch(ui, repo, user, 'acl.allow') |
|
87 | allow = buildmatch(ui, repo, user, 'acl.allow') | |
88 | deny = buildmatch(ui, repo, user, 'acl.deny') |
|
88 | deny = buildmatch(ui, repo, user, 'acl.deny') | |
89 |
|
89 | |||
90 | for rev in xrange(repo[node], len(repo)): |
|
90 | for rev in xrange(repo[node], len(repo)): | |
91 | ctx = repo[rev] |
|
91 | ctx = repo[rev] | |
92 | for f in ctx.files(): |
|
92 | for f in ctx.files(): | |
93 | if deny and deny(f): |
|
93 | if deny and deny(f): | |
94 | ui.debug(_('acl: user %s denied on %s\n') % (user, f)) |
|
94 | ui.debug(_('acl: user %s denied on %s\n') % (user, f)) | |
95 | raise util.Abort(_('acl: access denied for changeset %s') % ctx) |
|
95 | raise util.Abort(_('acl: access denied for changeset %s') % ctx) | |
96 | if allow and not allow(f): |
|
96 | if allow and not allow(f): | |
97 | ui.debug(_('acl: user %s not allowed on %s\n') % (user, f)) |
|
97 | ui.debug(_('acl: user %s not allowed on %s\n') % (user, f)) | |
98 | raise util.Abort(_('acl: access denied for changeset %s') % ctx) |
|
98 | raise util.Abort(_('acl: access denied for changeset %s') % ctx) | |
99 | ui.debug(_('acl: allowing changeset %s\n') % ctx) |
|
99 | ui.debug(_('acl: allowing changeset %s\n') % ctx) |
@@ -1,42 +1,44 b'' | |||||
1 | # Mercurial extension to provide the 'hg children' command |
|
1 | # Mercurial extension to provide the 'hg children' command | |
2 | # |
|
2 | # | |
3 | # Copyright 2007 by Intevation GmbH <intevation@intevation.de> |
|
3 | # Copyright 2007 by Intevation GmbH <intevation@intevation.de> | |
4 | # |
|
4 | # | |
5 | # Author(s): |
|
5 | # Author(s): | |
6 | # Thomas Arendsen Hein <thomas@intevation.de> |
|
6 | # Thomas Arendsen Hein <thomas@intevation.de> | |
7 | # |
|
7 | # | |
8 | # This software may be used and distributed according to the terms of the |
|
8 | # This software may be used and distributed according to the terms of the | |
9 | # GNU General Public License version 2, incorporated herein by reference. |
|
9 | # GNU General Public License version 2, incorporated herein by reference. | |
10 |
|
10 | |||
|
11 | '''provides children command to show children changesets''' | |||
|
12 | ||||
11 | from mercurial import cmdutil |
|
13 | from mercurial import cmdutil | |
12 | from mercurial.commands import templateopts |
|
14 | from mercurial.commands import templateopts | |
13 | from mercurial.i18n import _ |
|
15 | from mercurial.i18n import _ | |
14 |
|
16 | |||
15 |
|
17 | |||
16 | def children(ui, repo, file_=None, **opts): |
|
18 | def children(ui, repo, file_=None, **opts): | |
17 | """show the children of the given or working directory revision |
|
19 | """show the children of the given or working directory revision | |
18 |
|
20 | |||
19 | Print the children of the working directory's revisions. If a |
|
21 | Print the children of the working directory's revisions. If a | |
20 | revision is given via -r/--rev, the children of that revision will |
|
22 | revision is given via -r/--rev, the children of that revision will | |
21 | be printed. If a file argument is given, revision in which the |
|
23 | be printed. If a file argument is given, revision in which the | |
22 | file was last changed (after the working directory revision or the |
|
24 | file was last changed (after the working directory revision or the | |
23 | argument to --rev if given) is printed. |
|
25 | argument to --rev if given) is printed. | |
24 | """ |
|
26 | """ | |
25 | rev = opts.get('rev') |
|
27 | rev = opts.get('rev') | |
26 | if file_: |
|
28 | if file_: | |
27 | ctx = repo.filectx(file_, changeid=rev) |
|
29 | ctx = repo.filectx(file_, changeid=rev) | |
28 | else: |
|
30 | else: | |
29 | ctx = repo[rev] |
|
31 | ctx = repo[rev] | |
30 |
|
32 | |||
31 | displayer = cmdutil.show_changeset(ui, repo, opts) |
|
33 | displayer = cmdutil.show_changeset(ui, repo, opts) | |
32 | for cctx in ctx.children(): |
|
34 | for cctx in ctx.children(): | |
33 | displayer.show(cctx) |
|
35 | displayer.show(cctx) | |
34 |
|
36 | |||
35 |
|
37 | |||
36 | cmdtable = { |
|
38 | cmdtable = { | |
37 | "children": |
|
39 | "children": | |
38 | (children, |
|
40 | (children, | |
39 | [('r', 'rev', '', _('show children of the specified revision')), |
|
41 | [('r', 'rev', '', _('show children of the specified revision')), | |
40 | ] + templateopts, |
|
42 | ] + templateopts, | |
41 | _('hg children [-r REV] [FILE]')), |
|
43 | _('hg children [-r REV] [FILE]')), | |
42 | } |
|
44 | } |
@@ -1,228 +1,229 b'' | |||||
1 | # extdiff.py - external diff program support for mercurial |
|
1 | # extdiff.py - external diff program support for mercurial | |
2 | # |
|
2 | # | |
3 | # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com> |
|
3 | # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com> | |
4 | # |
|
4 | # | |
5 | # This software may be used and distributed according to the terms of the |
|
5 | # This software may be used and distributed according to the terms of the | |
6 | # GNU General Public License version 2, incorporated herein by reference. |
|
6 | # GNU General Public License version 2, incorporated herein by reference. | |
7 |
|
7 | |||
8 | ''' |
|
8 | '''allow external programs to compare revisions | |
|
9 | ||||
9 | The `extdiff' Mercurial extension allows you to use external programs |
|
10 | The `extdiff' Mercurial extension allows you to use external programs | |
10 | to compare revisions, or revision with working directory. The external diff |
|
11 | to compare revisions, or revision with working directory. The external diff | |
11 | programs are called with a configurable set of options and two |
|
12 | programs are called with a configurable set of options and two | |
12 | non-option arguments: paths to directories containing snapshots of |
|
13 | non-option arguments: paths to directories containing snapshots of | |
13 | files to compare. |
|
14 | files to compare. | |
14 |
|
15 | |||
15 | The `extdiff' extension also allows to configure new diff commands, so |
|
16 | The `extdiff' extension also allows to configure new diff commands, so | |
16 | you do not need to type "hg extdiff -p kdiff3" always. |
|
17 | you do not need to type "hg extdiff -p kdiff3" always. | |
17 |
|
18 | |||
18 | [extdiff] |
|
19 | [extdiff] | |
19 | # add new command that runs GNU diff(1) in 'context diff' mode |
|
20 | # add new command that runs GNU diff(1) in 'context diff' mode | |
20 | cdiff = gdiff -Nprc5 |
|
21 | cdiff = gdiff -Nprc5 | |
21 | ## or the old way: |
|
22 | ## or the old way: | |
22 | #cmd.cdiff = gdiff |
|
23 | #cmd.cdiff = gdiff | |
23 | #opts.cdiff = -Nprc5 |
|
24 | #opts.cdiff = -Nprc5 | |
24 |
|
25 | |||
25 | # add new command called vdiff, runs kdiff3 |
|
26 | # add new command called vdiff, runs kdiff3 | |
26 | vdiff = kdiff3 |
|
27 | vdiff = kdiff3 | |
27 |
|
28 | |||
28 | # add new command called meld, runs meld (no need to name twice) |
|
29 | # add new command called meld, runs meld (no need to name twice) | |
29 | meld = |
|
30 | meld = | |
30 |
|
31 | |||
31 | # add new command called vimdiff, runs gvimdiff with DirDiff plugin |
|
32 | # add new command called vimdiff, runs gvimdiff with DirDiff plugin | |
32 | # (see http://www.vim.org/scripts/script.php?script_id=102) |
|
33 | # (see http://www.vim.org/scripts/script.php?script_id=102) | |
33 | # Non English user, be sure to put "let g:DirDiffDynamicDiffText = 1" in |
|
34 | # Non English user, be sure to put "let g:DirDiffDynamicDiffText = 1" in | |
34 | # your .vimrc |
|
35 | # your .vimrc | |
35 | vimdiff = gvim -f '+next' '+execute "DirDiff" argv(0) argv(1)' |
|
36 | vimdiff = gvim -f '+next' '+execute "DirDiff" argv(0) argv(1)' | |
36 |
|
37 | |||
37 | You can use -I/-X and list of file or directory names like normal "hg |
|
38 | You can use -I/-X and list of file or directory names like normal "hg | |
38 | diff" command. The `extdiff' extension makes snapshots of only needed |
|
39 | diff" command. The `extdiff' extension makes snapshots of only needed | |
39 | files, so running the external diff program will actually be pretty |
|
40 | files, so running the external diff program will actually be pretty | |
40 | fast (at least faster than having to compare the entire tree). |
|
41 | fast (at least faster than having to compare the entire tree). | |
41 | ''' |
|
42 | ''' | |
42 |
|
43 | |||
43 | from mercurial.i18n import _ |
|
44 | from mercurial.i18n import _ | |
44 | from mercurial.node import short |
|
45 | from mercurial.node import short | |
45 | from mercurial import cmdutil, util, commands |
|
46 | from mercurial import cmdutil, util, commands | |
46 | import os, shlex, shutil, tempfile |
|
47 | import os, shlex, shutil, tempfile | |
47 |
|
48 | |||
48 | def snapshot(ui, repo, files, node, tmproot): |
|
49 | def snapshot(ui, repo, files, node, tmproot): | |
49 | '''snapshot files as of some revision |
|
50 | '''snapshot files as of some revision | |
50 | if not using snapshot, -I/-X does not work and recursive diff |
|
51 | if not using snapshot, -I/-X does not work and recursive diff | |
51 | in tools like kdiff3 and meld displays too many files.''' |
|
52 | in tools like kdiff3 and meld displays too many files.''' | |
52 | dirname = os.path.basename(repo.root) |
|
53 | dirname = os.path.basename(repo.root) | |
53 | if dirname == "": |
|
54 | if dirname == "": | |
54 | dirname = "root" |
|
55 | dirname = "root" | |
55 | if node is not None: |
|
56 | if node is not None: | |
56 | dirname = '%s.%s' % (dirname, short(node)) |
|
57 | dirname = '%s.%s' % (dirname, short(node)) | |
57 | base = os.path.join(tmproot, dirname) |
|
58 | base = os.path.join(tmproot, dirname) | |
58 | os.mkdir(base) |
|
59 | os.mkdir(base) | |
59 | if node is not None: |
|
60 | if node is not None: | |
60 | ui.note(_('making snapshot of %d files from rev %s\n') % |
|
61 | ui.note(_('making snapshot of %d files from rev %s\n') % | |
61 | (len(files), short(node))) |
|
62 | (len(files), short(node))) | |
62 | else: |
|
63 | else: | |
63 | ui.note(_('making snapshot of %d files from working directory\n') % |
|
64 | ui.note(_('making snapshot of %d files from working directory\n') % | |
64 | (len(files))) |
|
65 | (len(files))) | |
65 | wopener = util.opener(base) |
|
66 | wopener = util.opener(base) | |
66 | fns_and_mtime = [] |
|
67 | fns_and_mtime = [] | |
67 | ctx = repo[node] |
|
68 | ctx = repo[node] | |
68 | for fn in files: |
|
69 | for fn in files: | |
69 | wfn = util.pconvert(fn) |
|
70 | wfn = util.pconvert(fn) | |
70 | if not wfn in ctx: |
|
71 | if not wfn in ctx: | |
71 | # skipping new file after a merge ? |
|
72 | # skipping new file after a merge ? | |
72 | continue |
|
73 | continue | |
73 | ui.note(' %s\n' % wfn) |
|
74 | ui.note(' %s\n' % wfn) | |
74 | dest = os.path.join(base, wfn) |
|
75 | dest = os.path.join(base, wfn) | |
75 | fctx = ctx[wfn] |
|
76 | fctx = ctx[wfn] | |
76 | data = repo.wwritedata(wfn, fctx.data()) |
|
77 | data = repo.wwritedata(wfn, fctx.data()) | |
77 | if 'l' in fctx.flags(): |
|
78 | if 'l' in fctx.flags(): | |
78 | wopener.symlink(data, wfn) |
|
79 | wopener.symlink(data, wfn) | |
79 | else: |
|
80 | else: | |
80 | wopener(wfn, 'w').write(data) |
|
81 | wopener(wfn, 'w').write(data) | |
81 | if 'x' in fctx.flags(): |
|
82 | if 'x' in fctx.flags(): | |
82 | util.set_flags(dest, False, True) |
|
83 | util.set_flags(dest, False, True) | |
83 | if node is None: |
|
84 | if node is None: | |
84 | fns_and_mtime.append((dest, repo.wjoin(fn), os.path.getmtime(dest))) |
|
85 | fns_and_mtime.append((dest, repo.wjoin(fn), os.path.getmtime(dest))) | |
85 | return dirname, fns_and_mtime |
|
86 | return dirname, fns_and_mtime | |
86 |
|
87 | |||
87 | def dodiff(ui, repo, diffcmd, diffopts, pats, opts): |
|
88 | def dodiff(ui, repo, diffcmd, diffopts, pats, opts): | |
88 | '''Do the actuall diff: |
|
89 | '''Do the actuall diff: | |
89 |
|
90 | |||
90 | - copy to a temp structure if diffing 2 internal revisions |
|
91 | - copy to a temp structure if diffing 2 internal revisions | |
91 | - copy to a temp structure if diffing working revision with |
|
92 | - copy to a temp structure if diffing working revision with | |
92 | another one and more than 1 file is changed |
|
93 | another one and more than 1 file is changed | |
93 | - just invoke the diff for a single file in the working dir |
|
94 | - just invoke the diff for a single file in the working dir | |
94 | ''' |
|
95 | ''' | |
95 |
|
96 | |||
96 | revs = opts.get('rev') |
|
97 | revs = opts.get('rev') | |
97 | change = opts.get('change') |
|
98 | change = opts.get('change') | |
98 |
|
99 | |||
99 | if revs and change: |
|
100 | if revs and change: | |
100 | msg = _('cannot specify --rev and --change at the same time') |
|
101 | msg = _('cannot specify --rev and --change at the same time') | |
101 | raise util.Abort(msg) |
|
102 | raise util.Abort(msg) | |
102 | elif change: |
|
103 | elif change: | |
103 | node2 = repo.lookup(change) |
|
104 | node2 = repo.lookup(change) | |
104 | node1 = repo[node2].parents()[0].node() |
|
105 | node1 = repo[node2].parents()[0].node() | |
105 | else: |
|
106 | else: | |
106 | node1, node2 = cmdutil.revpair(repo, revs) |
|
107 | node1, node2 = cmdutil.revpair(repo, revs) | |
107 |
|
108 | |||
108 | matcher = cmdutil.match(repo, pats, opts) |
|
109 | matcher = cmdutil.match(repo, pats, opts) | |
109 | modified, added, removed = repo.status(node1, node2, matcher)[:3] |
|
110 | modified, added, removed = repo.status(node1, node2, matcher)[:3] | |
110 | if not (modified or added or removed): |
|
111 | if not (modified or added or removed): | |
111 | return 0 |
|
112 | return 0 | |
112 |
|
113 | |||
113 | tmproot = tempfile.mkdtemp(prefix='extdiff.') |
|
114 | tmproot = tempfile.mkdtemp(prefix='extdiff.') | |
114 | dir2root = '' |
|
115 | dir2root = '' | |
115 | try: |
|
116 | try: | |
116 | # Always make a copy of node1 |
|
117 | # Always make a copy of node1 | |
117 | dir1 = snapshot(ui, repo, modified + removed, node1, tmproot)[0] |
|
118 | dir1 = snapshot(ui, repo, modified + removed, node1, tmproot)[0] | |
118 | changes = len(modified) + len(removed) + len(added) |
|
119 | changes = len(modified) + len(removed) + len(added) | |
119 |
|
120 | |||
120 | # If node2 in not the wc or there is >1 change, copy it |
|
121 | # If node2 in not the wc or there is >1 change, copy it | |
121 | if node2 or changes > 1: |
|
122 | if node2 or changes > 1: | |
122 | dir2, fns_and_mtime = snapshot(ui, repo, modified + added, node2, tmproot) |
|
123 | dir2, fns_and_mtime = snapshot(ui, repo, modified + added, node2, tmproot) | |
123 | else: |
|
124 | else: | |
124 | # This lets the diff tool open the changed file directly |
|
125 | # This lets the diff tool open the changed file directly | |
125 | dir2 = '' |
|
126 | dir2 = '' | |
126 | dir2root = repo.root |
|
127 | dir2root = repo.root | |
127 | fns_and_mtime = [] |
|
128 | fns_and_mtime = [] | |
128 |
|
129 | |||
129 | # If only one change, diff the files instead of the directories |
|
130 | # If only one change, diff the files instead of the directories | |
130 | if changes == 1 : |
|
131 | if changes == 1 : | |
131 | if len(modified): |
|
132 | if len(modified): | |
132 | dir1 = os.path.join(dir1, util.localpath(modified[0])) |
|
133 | dir1 = os.path.join(dir1, util.localpath(modified[0])) | |
133 | dir2 = os.path.join(dir2root, dir2, util.localpath(modified[0])) |
|
134 | dir2 = os.path.join(dir2root, dir2, util.localpath(modified[0])) | |
134 | elif len(removed) : |
|
135 | elif len(removed) : | |
135 | dir1 = os.path.join(dir1, util.localpath(removed[0])) |
|
136 | dir1 = os.path.join(dir1, util.localpath(removed[0])) | |
136 | dir2 = os.devnull |
|
137 | dir2 = os.devnull | |
137 | else: |
|
138 | else: | |
138 | dir1 = os.devnull |
|
139 | dir1 = os.devnull | |
139 | dir2 = os.path.join(dir2root, dir2, util.localpath(added[0])) |
|
140 | dir2 = os.path.join(dir2root, dir2, util.localpath(added[0])) | |
140 |
|
141 | |||
141 | cmdline = ('%s %s %s %s' % |
|
142 | cmdline = ('%s %s %s %s' % | |
142 | (util.shellquote(diffcmd), ' '.join(diffopts), |
|
143 | (util.shellquote(diffcmd), ' '.join(diffopts), | |
143 | util.shellquote(dir1), util.shellquote(dir2))) |
|
144 | util.shellquote(dir1), util.shellquote(dir2))) | |
144 | ui.debug(_('running %r in %s\n') % (cmdline, tmproot)) |
|
145 | ui.debug(_('running %r in %s\n') % (cmdline, tmproot)) | |
145 | util.system(cmdline, cwd=tmproot) |
|
146 | util.system(cmdline, cwd=tmproot) | |
146 |
|
147 | |||
147 | for copy_fn, working_fn, mtime in fns_and_mtime: |
|
148 | for copy_fn, working_fn, mtime in fns_and_mtime: | |
148 | if os.path.getmtime(copy_fn) != mtime: |
|
149 | if os.path.getmtime(copy_fn) != mtime: | |
149 | ui.debug(_('file changed while diffing. ' |
|
150 | ui.debug(_('file changed while diffing. ' | |
150 | 'Overwriting: %s (src: %s)\n') % (working_fn, copy_fn)) |
|
151 | 'Overwriting: %s (src: %s)\n') % (working_fn, copy_fn)) | |
151 | util.copyfile(copy_fn, working_fn) |
|
152 | util.copyfile(copy_fn, working_fn) | |
152 |
|
153 | |||
153 | return 1 |
|
154 | return 1 | |
154 | finally: |
|
155 | finally: | |
155 | ui.note(_('cleaning up temp directory\n')) |
|
156 | ui.note(_('cleaning up temp directory\n')) | |
156 | shutil.rmtree(tmproot) |
|
157 | shutil.rmtree(tmproot) | |
157 |
|
158 | |||
158 | def extdiff(ui, repo, *pats, **opts): |
|
159 | def extdiff(ui, repo, *pats, **opts): | |
159 | '''use external program to diff repository (or selected files) |
|
160 | '''use external program to diff repository (or selected files) | |
160 |
|
161 | |||
161 | Show differences between revisions for the specified files, using |
|
162 | Show differences between revisions for the specified files, using | |
162 | an external program. The default program used is diff, with |
|
163 | an external program. The default program used is diff, with | |
163 | default options "-Npru". |
|
164 | default options "-Npru". | |
164 |
|
165 | |||
165 | To select a different program, use the -p/--program option. The |
|
166 | To select a different program, use the -p/--program option. The | |
166 | program will be passed the names of two directories to compare. To |
|
167 | program will be passed the names of two directories to compare. To | |
167 | pass additional options to the program, use -o/--option. These |
|
168 | pass additional options to the program, use -o/--option. These | |
168 | will be passed before the names of the directories to compare. |
|
169 | will be passed before the names of the directories to compare. | |
169 |
|
170 | |||
170 | When two revision arguments are given, then changes are shown |
|
171 | When two revision arguments are given, then changes are shown | |
171 | between those revisions. If only one revision is specified then |
|
172 | between those revisions. If only one revision is specified then | |
172 | that revision is compared to the working directory, and, when no |
|
173 | that revision is compared to the working directory, and, when no | |
173 | revisions are specified, the working directory files are compared |
|
174 | revisions are specified, the working directory files are compared | |
174 | to its parent.''' |
|
175 | to its parent.''' | |
175 | program = opts['program'] or 'diff' |
|
176 | program = opts['program'] or 'diff' | |
176 | if opts['program']: |
|
177 | if opts['program']: | |
177 | option = opts['option'] |
|
178 | option = opts['option'] | |
178 | else: |
|
179 | else: | |
179 | option = opts['option'] or ['-Npru'] |
|
180 | option = opts['option'] or ['-Npru'] | |
180 | return dodiff(ui, repo, program, option, pats, opts) |
|
181 | return dodiff(ui, repo, program, option, pats, opts) | |
181 |
|
182 | |||
182 | cmdtable = { |
|
183 | cmdtable = { | |
183 | "extdiff": |
|
184 | "extdiff": | |
184 | (extdiff, |
|
185 | (extdiff, | |
185 | [('p', 'program', '', _('comparison program to run')), |
|
186 | [('p', 'program', '', _('comparison program to run')), | |
186 | ('o', 'option', [], _('pass option to comparison program')), |
|
187 | ('o', 'option', [], _('pass option to comparison program')), | |
187 | ('r', 'rev', [], _('revision')), |
|
188 | ('r', 'rev', [], _('revision')), | |
188 | ('c', 'change', '', _('change made by revision')), |
|
189 | ('c', 'change', '', _('change made by revision')), | |
189 | ] + commands.walkopts, |
|
190 | ] + commands.walkopts, | |
190 | _('hg extdiff [OPT]... [FILE]...')), |
|
191 | _('hg extdiff [OPT]... [FILE]...')), | |
191 | } |
|
192 | } | |
192 |
|
193 | |||
193 | def uisetup(ui): |
|
194 | def uisetup(ui): | |
194 | for cmd, path in ui.configitems('extdiff'): |
|
195 | for cmd, path in ui.configitems('extdiff'): | |
195 | if cmd.startswith('cmd.'): |
|
196 | if cmd.startswith('cmd.'): | |
196 | cmd = cmd[4:] |
|
197 | cmd = cmd[4:] | |
197 | if not path: path = cmd |
|
198 | if not path: path = cmd | |
198 | diffopts = ui.config('extdiff', 'opts.' + cmd, '') |
|
199 | diffopts = ui.config('extdiff', 'opts.' + cmd, '') | |
199 | diffopts = diffopts and [diffopts] or [] |
|
200 | diffopts = diffopts and [diffopts] or [] | |
200 | elif cmd.startswith('opts.'): |
|
201 | elif cmd.startswith('opts.'): | |
201 | continue |
|
202 | continue | |
202 | else: |
|
203 | else: | |
203 | # command = path opts |
|
204 | # command = path opts | |
204 | if path: |
|
205 | if path: | |
205 | diffopts = shlex.split(path) |
|
206 | diffopts = shlex.split(path) | |
206 | path = diffopts.pop(0) |
|
207 | path = diffopts.pop(0) | |
207 | else: |
|
208 | else: | |
208 | path, diffopts = cmd, [] |
|
209 | path, diffopts = cmd, [] | |
209 | def save(cmd, path, diffopts): |
|
210 | def save(cmd, path, diffopts): | |
210 | '''use closure to save diff command to use''' |
|
211 | '''use closure to save diff command to use''' | |
211 | def mydiff(ui, repo, *pats, **opts): |
|
212 | def mydiff(ui, repo, *pats, **opts): | |
212 | return dodiff(ui, repo, path, diffopts, pats, opts) |
|
213 | return dodiff(ui, repo, path, diffopts, pats, opts) | |
213 | mydiff.__doc__ = '''use %(path)s to diff repository (or selected files) |
|
214 | mydiff.__doc__ = '''use %(path)s to diff repository (or selected files) | |
214 |
|
215 | |||
215 | Show differences between revisions for the specified |
|
216 | Show differences between revisions for the specified | |
216 | files, using the %(path)s program. |
|
217 | files, using the %(path)s program. | |
217 |
|
218 | |||
218 | When two revision arguments are given, then changes are |
|
219 | When two revision arguments are given, then changes are | |
219 | shown between those revisions. If only one revision is |
|
220 | shown between those revisions. If only one revision is | |
220 | specified then that revision is compared to the working |
|
221 | specified then that revision is compared to the working | |
221 | directory, and, when no revisions are specified, the |
|
222 | directory, and, when no revisions are specified, the | |
222 | working directory files are compared to its parent.''' % { |
|
223 | working directory files are compared to its parent.''' % { | |
223 | 'path': util.uirepr(path), |
|
224 | 'path': util.uirepr(path), | |
224 | } |
|
225 | } | |
225 | return mydiff |
|
226 | return mydiff | |
226 | cmdtable[cmd] = (save(cmd, path, diffopts), |
|
227 | cmdtable[cmd] = (save(cmd, path, diffopts), | |
227 | cmdtable['extdiff'][1][1:], |
|
228 | cmdtable['extdiff'][1][1:], | |
228 | _('hg %s [OPTION]... [FILE]...') % cmd) |
|
229 | _('hg %s [OPTION]... [FILE]...') % cmd) |
@@ -1,283 +1,283 b'' | |||||
1 | # GnuPG signing extension for Mercurial |
|
|||
2 | # |
|
|||
3 |
|
|
1 | # Copyright 2005, 2006 Benoit Boissinot <benoit.boissinot@ens-lyon.org> | |
4 | # |
|
2 | # | |
5 | # This software may be used and distributed according to the terms of the |
|
3 | # This software may be used and distributed according to the terms of the | |
6 | # GNU General Public License version 2, incorporated herein by reference. |
|
4 | # GNU General Public License version 2, incorporated herein by reference. | |
7 |
|
5 | |||
|
6 | '''GnuPG signing extension for Mercurial''' | |||
|
7 | ||||
8 | import os, tempfile, binascii |
|
8 | import os, tempfile, binascii | |
9 | from mercurial import util, commands, match |
|
9 | from mercurial import util, commands, match | |
10 | from mercurial import node as hgnode |
|
10 | from mercurial import node as hgnode | |
11 | from mercurial.i18n import _ |
|
11 | from mercurial.i18n import _ | |
12 |
|
12 | |||
13 | class gpg(object): |
|
13 | class gpg(object): | |
14 | def __init__(self, path, key=None): |
|
14 | def __init__(self, path, key=None): | |
15 | self.path = path |
|
15 | self.path = path | |
16 | self.key = (key and " --local-user \"%s\"" % key) or "" |
|
16 | self.key = (key and " --local-user \"%s\"" % key) or "" | |
17 |
|
17 | |||
18 | def sign(self, data): |
|
18 | def sign(self, data): | |
19 | gpgcmd = "%s --sign --detach-sign%s" % (self.path, self.key) |
|
19 | gpgcmd = "%s --sign --detach-sign%s" % (self.path, self.key) | |
20 | return util.filter(data, gpgcmd) |
|
20 | return util.filter(data, gpgcmd) | |
21 |
|
21 | |||
22 | def verify(self, data, sig): |
|
22 | def verify(self, data, sig): | |
23 | """ returns of the good and bad signatures""" |
|
23 | """ returns of the good and bad signatures""" | |
24 | sigfile = datafile = None |
|
24 | sigfile = datafile = None | |
25 | try: |
|
25 | try: | |
26 | # create temporary files |
|
26 | # create temporary files | |
27 | fd, sigfile = tempfile.mkstemp(prefix="hg-gpg-", suffix=".sig") |
|
27 | fd, sigfile = tempfile.mkstemp(prefix="hg-gpg-", suffix=".sig") | |
28 | fp = os.fdopen(fd, 'wb') |
|
28 | fp = os.fdopen(fd, 'wb') | |
29 | fp.write(sig) |
|
29 | fp.write(sig) | |
30 | fp.close() |
|
30 | fp.close() | |
31 | fd, datafile = tempfile.mkstemp(prefix="hg-gpg-", suffix=".txt") |
|
31 | fd, datafile = tempfile.mkstemp(prefix="hg-gpg-", suffix=".txt") | |
32 | fp = os.fdopen(fd, 'wb') |
|
32 | fp = os.fdopen(fd, 'wb') | |
33 | fp.write(data) |
|
33 | fp.write(data) | |
34 | fp.close() |
|
34 | fp.close() | |
35 | gpgcmd = ("%s --logger-fd 1 --status-fd 1 --verify " |
|
35 | gpgcmd = ("%s --logger-fd 1 --status-fd 1 --verify " | |
36 | "\"%s\" \"%s\"" % (self.path, sigfile, datafile)) |
|
36 | "\"%s\" \"%s\"" % (self.path, sigfile, datafile)) | |
37 | ret = util.filter("", gpgcmd) |
|
37 | ret = util.filter("", gpgcmd) | |
38 | finally: |
|
38 | finally: | |
39 | for f in (sigfile, datafile): |
|
39 | for f in (sigfile, datafile): | |
40 | try: |
|
40 | try: | |
41 | if f: os.unlink(f) |
|
41 | if f: os.unlink(f) | |
42 | except: pass |
|
42 | except: pass | |
43 | keys = [] |
|
43 | keys = [] | |
44 | key, fingerprint = None, None |
|
44 | key, fingerprint = None, None | |
45 | err = "" |
|
45 | err = "" | |
46 | for l in ret.splitlines(): |
|
46 | for l in ret.splitlines(): | |
47 | # see DETAILS in the gnupg documentation |
|
47 | # see DETAILS in the gnupg documentation | |
48 | # filter the logger output |
|
48 | # filter the logger output | |
49 | if not l.startswith("[GNUPG:]"): |
|
49 | if not l.startswith("[GNUPG:]"): | |
50 | continue |
|
50 | continue | |
51 | l = l[9:] |
|
51 | l = l[9:] | |
52 | if l.startswith("ERRSIG"): |
|
52 | if l.startswith("ERRSIG"): | |
53 | err = _("error while verifying signature") |
|
53 | err = _("error while verifying signature") | |
54 | break |
|
54 | break | |
55 | elif l.startswith("VALIDSIG"): |
|
55 | elif l.startswith("VALIDSIG"): | |
56 | # fingerprint of the primary key |
|
56 | # fingerprint of the primary key | |
57 | fingerprint = l.split()[10] |
|
57 | fingerprint = l.split()[10] | |
58 | elif (l.startswith("GOODSIG") or |
|
58 | elif (l.startswith("GOODSIG") or | |
59 | l.startswith("EXPSIG") or |
|
59 | l.startswith("EXPSIG") or | |
60 | l.startswith("EXPKEYSIG") or |
|
60 | l.startswith("EXPKEYSIG") or | |
61 | l.startswith("BADSIG")): |
|
61 | l.startswith("BADSIG")): | |
62 | if key is not None: |
|
62 | if key is not None: | |
63 | keys.append(key + [fingerprint]) |
|
63 | keys.append(key + [fingerprint]) | |
64 | key = l.split(" ", 2) |
|
64 | key = l.split(" ", 2) | |
65 | fingerprint = None |
|
65 | fingerprint = None | |
66 | if err: |
|
66 | if err: | |
67 | return err, [] |
|
67 | return err, [] | |
68 | if key is not None: |
|
68 | if key is not None: | |
69 | keys.append(key + [fingerprint]) |
|
69 | keys.append(key + [fingerprint]) | |
70 | return err, keys |
|
70 | return err, keys | |
71 |
|
71 | |||
72 | def newgpg(ui, **opts): |
|
72 | def newgpg(ui, **opts): | |
73 | """create a new gpg instance""" |
|
73 | """create a new gpg instance""" | |
74 | gpgpath = ui.config("gpg", "cmd", "gpg") |
|
74 | gpgpath = ui.config("gpg", "cmd", "gpg") | |
75 | gpgkey = opts.get('key') |
|
75 | gpgkey = opts.get('key') | |
76 | if not gpgkey: |
|
76 | if not gpgkey: | |
77 | gpgkey = ui.config("gpg", "key", None) |
|
77 | gpgkey = ui.config("gpg", "key", None) | |
78 | return gpg(gpgpath, gpgkey) |
|
78 | return gpg(gpgpath, gpgkey) | |
79 |
|
79 | |||
80 | def sigwalk(repo): |
|
80 | def sigwalk(repo): | |
81 | """ |
|
81 | """ | |
82 | walk over every sigs, yields a couple |
|
82 | walk over every sigs, yields a couple | |
83 | ((node, version, sig), (filename, linenumber)) |
|
83 | ((node, version, sig), (filename, linenumber)) | |
84 | """ |
|
84 | """ | |
85 | def parsefile(fileiter, context): |
|
85 | def parsefile(fileiter, context): | |
86 | ln = 1 |
|
86 | ln = 1 | |
87 | for l in fileiter: |
|
87 | for l in fileiter: | |
88 | if not l: |
|
88 | if not l: | |
89 | continue |
|
89 | continue | |
90 | yield (l.split(" ", 2), (context, ln)) |
|
90 | yield (l.split(" ", 2), (context, ln)) | |
91 | ln +=1 |
|
91 | ln +=1 | |
92 |
|
92 | |||
93 | # read the heads |
|
93 | # read the heads | |
94 | fl = repo.file(".hgsigs") |
|
94 | fl = repo.file(".hgsigs") | |
95 | for r in reversed(fl.heads()): |
|
95 | for r in reversed(fl.heads()): | |
96 | fn = ".hgsigs|%s" % hgnode.short(r) |
|
96 | fn = ".hgsigs|%s" % hgnode.short(r) | |
97 | for item in parsefile(fl.read(r).splitlines(), fn): |
|
97 | for item in parsefile(fl.read(r).splitlines(), fn): | |
98 | yield item |
|
98 | yield item | |
99 | try: |
|
99 | try: | |
100 | # read local signatures |
|
100 | # read local signatures | |
101 | fn = "localsigs" |
|
101 | fn = "localsigs" | |
102 | for item in parsefile(repo.opener(fn), fn): |
|
102 | for item in parsefile(repo.opener(fn), fn): | |
103 | yield item |
|
103 | yield item | |
104 | except IOError: |
|
104 | except IOError: | |
105 | pass |
|
105 | pass | |
106 |
|
106 | |||
107 | def getkeys(ui, repo, mygpg, sigdata, context): |
|
107 | def getkeys(ui, repo, mygpg, sigdata, context): | |
108 | """get the keys who signed a data""" |
|
108 | """get the keys who signed a data""" | |
109 | fn, ln = context |
|
109 | fn, ln = context | |
110 | node, version, sig = sigdata |
|
110 | node, version, sig = sigdata | |
111 | prefix = "%s:%d" % (fn, ln) |
|
111 | prefix = "%s:%d" % (fn, ln) | |
112 | node = hgnode.bin(node) |
|
112 | node = hgnode.bin(node) | |
113 |
|
113 | |||
114 | data = node2txt(repo, node, version) |
|
114 | data = node2txt(repo, node, version) | |
115 | sig = binascii.a2b_base64(sig) |
|
115 | sig = binascii.a2b_base64(sig) | |
116 | err, keys = mygpg.verify(data, sig) |
|
116 | err, keys = mygpg.verify(data, sig) | |
117 | if err: |
|
117 | if err: | |
118 | ui.warn("%s:%d %s\n" % (fn, ln , err)) |
|
118 | ui.warn("%s:%d %s\n" % (fn, ln , err)) | |
119 | return None |
|
119 | return None | |
120 |
|
120 | |||
121 | validkeys = [] |
|
121 | validkeys = [] | |
122 | # warn for expired key and/or sigs |
|
122 | # warn for expired key and/or sigs | |
123 | for key in keys: |
|
123 | for key in keys: | |
124 | if key[0] == "BADSIG": |
|
124 | if key[0] == "BADSIG": | |
125 | ui.write(_("%s Bad signature from \"%s\"\n") % (prefix, key[2])) |
|
125 | ui.write(_("%s Bad signature from \"%s\"\n") % (prefix, key[2])) | |
126 | continue |
|
126 | continue | |
127 | if key[0] == "EXPSIG": |
|
127 | if key[0] == "EXPSIG": | |
128 | ui.write(_("%s Note: Signature has expired" |
|
128 | ui.write(_("%s Note: Signature has expired" | |
129 | " (signed by: \"%s\")\n") % (prefix, key[2])) |
|
129 | " (signed by: \"%s\")\n") % (prefix, key[2])) | |
130 | elif key[0] == "EXPKEYSIG": |
|
130 | elif key[0] == "EXPKEYSIG": | |
131 | ui.write(_("%s Note: This key has expired" |
|
131 | ui.write(_("%s Note: This key has expired" | |
132 | " (signed by: \"%s\")\n") % (prefix, key[2])) |
|
132 | " (signed by: \"%s\")\n") % (prefix, key[2])) | |
133 | validkeys.append((key[1], key[2], key[3])) |
|
133 | validkeys.append((key[1], key[2], key[3])) | |
134 | return validkeys |
|
134 | return validkeys | |
135 |
|
135 | |||
136 | def sigs(ui, repo): |
|
136 | def sigs(ui, repo): | |
137 | """list signed changesets""" |
|
137 | """list signed changesets""" | |
138 | mygpg = newgpg(ui) |
|
138 | mygpg = newgpg(ui) | |
139 | revs = {} |
|
139 | revs = {} | |
140 |
|
140 | |||
141 | for data, context in sigwalk(repo): |
|
141 | for data, context in sigwalk(repo): | |
142 | node, version, sig = data |
|
142 | node, version, sig = data | |
143 | fn, ln = context |
|
143 | fn, ln = context | |
144 | try: |
|
144 | try: | |
145 | n = repo.lookup(node) |
|
145 | n = repo.lookup(node) | |
146 | except KeyError: |
|
146 | except KeyError: | |
147 | ui.warn(_("%s:%d node does not exist\n") % (fn, ln)) |
|
147 | ui.warn(_("%s:%d node does not exist\n") % (fn, ln)) | |
148 | continue |
|
148 | continue | |
149 | r = repo.changelog.rev(n) |
|
149 | r = repo.changelog.rev(n) | |
150 | keys = getkeys(ui, repo, mygpg, data, context) |
|
150 | keys = getkeys(ui, repo, mygpg, data, context) | |
151 | if not keys: |
|
151 | if not keys: | |
152 | continue |
|
152 | continue | |
153 | revs.setdefault(r, []) |
|
153 | revs.setdefault(r, []) | |
154 | revs[r].extend(keys) |
|
154 | revs[r].extend(keys) | |
155 | for rev in sorted(revs, reverse=True): |
|
155 | for rev in sorted(revs, reverse=True): | |
156 | for k in revs[rev]: |
|
156 | for k in revs[rev]: | |
157 | r = "%5d:%s" % (rev, hgnode.hex(repo.changelog.node(rev))) |
|
157 | r = "%5d:%s" % (rev, hgnode.hex(repo.changelog.node(rev))) | |
158 | ui.write("%-30s %s\n" % (keystr(ui, k), r)) |
|
158 | ui.write("%-30s %s\n" % (keystr(ui, k), r)) | |
159 |
|
159 | |||
160 | def check(ui, repo, rev): |
|
160 | def check(ui, repo, rev): | |
161 | """verify all the signatures there may be for a particular revision""" |
|
161 | """verify all the signatures there may be for a particular revision""" | |
162 | mygpg = newgpg(ui) |
|
162 | mygpg = newgpg(ui) | |
163 | rev = repo.lookup(rev) |
|
163 | rev = repo.lookup(rev) | |
164 | hexrev = hgnode.hex(rev) |
|
164 | hexrev = hgnode.hex(rev) | |
165 | keys = [] |
|
165 | keys = [] | |
166 |
|
166 | |||
167 | for data, context in sigwalk(repo): |
|
167 | for data, context in sigwalk(repo): | |
168 | node, version, sig = data |
|
168 | node, version, sig = data | |
169 | if node == hexrev: |
|
169 | if node == hexrev: | |
170 | k = getkeys(ui, repo, mygpg, data, context) |
|
170 | k = getkeys(ui, repo, mygpg, data, context) | |
171 | if k: |
|
171 | if k: | |
172 | keys.extend(k) |
|
172 | keys.extend(k) | |
173 |
|
173 | |||
174 | if not keys: |
|
174 | if not keys: | |
175 | ui.write(_("No valid signature for %s\n") % hgnode.short(rev)) |
|
175 | ui.write(_("No valid signature for %s\n") % hgnode.short(rev)) | |
176 | return |
|
176 | return | |
177 |
|
177 | |||
178 | # print summary |
|
178 | # print summary | |
179 | ui.write("%s is signed by:\n" % hgnode.short(rev)) |
|
179 | ui.write("%s is signed by:\n" % hgnode.short(rev)) | |
180 | for key in keys: |
|
180 | for key in keys: | |
181 | ui.write(" %s\n" % keystr(ui, key)) |
|
181 | ui.write(" %s\n" % keystr(ui, key)) | |
182 |
|
182 | |||
183 | def keystr(ui, key): |
|
183 | def keystr(ui, key): | |
184 | """associate a string to a key (username, comment)""" |
|
184 | """associate a string to a key (username, comment)""" | |
185 | keyid, user, fingerprint = key |
|
185 | keyid, user, fingerprint = key | |
186 | comment = ui.config("gpg", fingerprint, None) |
|
186 | comment = ui.config("gpg", fingerprint, None) | |
187 | if comment: |
|
187 | if comment: | |
188 | return "%s (%s)" % (user, comment) |
|
188 | return "%s (%s)" % (user, comment) | |
189 | else: |
|
189 | else: | |
190 | return user |
|
190 | return user | |
191 |
|
191 | |||
192 | def sign(ui, repo, *revs, **opts): |
|
192 | def sign(ui, repo, *revs, **opts): | |
193 | """add a signature for the current or given revision |
|
193 | """add a signature for the current or given revision | |
194 |
|
194 | |||
195 | If no revision is given, the parent of the working directory is used, |
|
195 | If no revision is given, the parent of the working directory is used, | |
196 | or tip if no revision is checked out. |
|
196 | or tip if no revision is checked out. | |
197 |
|
197 | |||
198 | See 'hg help dates' for a list of formats valid for -d/--date. |
|
198 | See 'hg help dates' for a list of formats valid for -d/--date. | |
199 | """ |
|
199 | """ | |
200 |
|
200 | |||
201 | mygpg = newgpg(ui, **opts) |
|
201 | mygpg = newgpg(ui, **opts) | |
202 | sigver = "0" |
|
202 | sigver = "0" | |
203 | sigmessage = "" |
|
203 | sigmessage = "" | |
204 |
|
204 | |||
205 | date = opts.get('date') |
|
205 | date = opts.get('date') | |
206 | if date: |
|
206 | if date: | |
207 | opts['date'] = util.parsedate(date) |
|
207 | opts['date'] = util.parsedate(date) | |
208 |
|
208 | |||
209 | if revs: |
|
209 | if revs: | |
210 | nodes = [repo.lookup(n) for n in revs] |
|
210 | nodes = [repo.lookup(n) for n in revs] | |
211 | else: |
|
211 | else: | |
212 | nodes = [node for node in repo.dirstate.parents() |
|
212 | nodes = [node for node in repo.dirstate.parents() | |
213 | if node != hgnode.nullid] |
|
213 | if node != hgnode.nullid] | |
214 | if len(nodes) > 1: |
|
214 | if len(nodes) > 1: | |
215 | raise util.Abort(_('uncommitted merge - please provide a ' |
|
215 | raise util.Abort(_('uncommitted merge - please provide a ' | |
216 | 'specific revision')) |
|
216 | 'specific revision')) | |
217 | if not nodes: |
|
217 | if not nodes: | |
218 | nodes = [repo.changelog.tip()] |
|
218 | nodes = [repo.changelog.tip()] | |
219 |
|
219 | |||
220 | for n in nodes: |
|
220 | for n in nodes: | |
221 | hexnode = hgnode.hex(n) |
|
221 | hexnode = hgnode.hex(n) | |
222 | ui.write("Signing %d:%s\n" % (repo.changelog.rev(n), |
|
222 | ui.write("Signing %d:%s\n" % (repo.changelog.rev(n), | |
223 | hgnode.short(n))) |
|
223 | hgnode.short(n))) | |
224 | # build data |
|
224 | # build data | |
225 | data = node2txt(repo, n, sigver) |
|
225 | data = node2txt(repo, n, sigver) | |
226 | sig = mygpg.sign(data) |
|
226 | sig = mygpg.sign(data) | |
227 | if not sig: |
|
227 | if not sig: | |
228 | raise util.Abort(_("Error while signing")) |
|
228 | raise util.Abort(_("Error while signing")) | |
229 | sig = binascii.b2a_base64(sig) |
|
229 | sig = binascii.b2a_base64(sig) | |
230 | sig = sig.replace("\n", "") |
|
230 | sig = sig.replace("\n", "") | |
231 | sigmessage += "%s %s %s\n" % (hexnode, sigver, sig) |
|
231 | sigmessage += "%s %s %s\n" % (hexnode, sigver, sig) | |
232 |
|
232 | |||
233 | # write it |
|
233 | # write it | |
234 | if opts['local']: |
|
234 | if opts['local']: | |
235 | repo.opener("localsigs", "ab").write(sigmessage) |
|
235 | repo.opener("localsigs", "ab").write(sigmessage) | |
236 | return |
|
236 | return | |
237 |
|
237 | |||
238 | for x in repo.status(unknown=True)[:5]: |
|
238 | for x in repo.status(unknown=True)[:5]: | |
239 | if ".hgsigs" in x and not opts["force"]: |
|
239 | if ".hgsigs" in x and not opts["force"]: | |
240 | raise util.Abort(_("working copy of .hgsigs is changed " |
|
240 | raise util.Abort(_("working copy of .hgsigs is changed " | |
241 | "(please commit .hgsigs manually " |
|
241 | "(please commit .hgsigs manually " | |
242 | "or use --force)")) |
|
242 | "or use --force)")) | |
243 |
|
243 | |||
244 | repo.wfile(".hgsigs", "ab").write(sigmessage) |
|
244 | repo.wfile(".hgsigs", "ab").write(sigmessage) | |
245 |
|
245 | |||
246 | if '.hgsigs' not in repo.dirstate: |
|
246 | if '.hgsigs' not in repo.dirstate: | |
247 | repo.add([".hgsigs"]) |
|
247 | repo.add([".hgsigs"]) | |
248 |
|
248 | |||
249 | if opts["no_commit"]: |
|
249 | if opts["no_commit"]: | |
250 | return |
|
250 | return | |
251 |
|
251 | |||
252 | message = opts['message'] |
|
252 | message = opts['message'] | |
253 | if not message: |
|
253 | if not message: | |
254 | message = "\n".join([_("Added signature for changeset %s") |
|
254 | message = "\n".join([_("Added signature for changeset %s") | |
255 | % hgnode.short(n) |
|
255 | % hgnode.short(n) | |
256 | for n in nodes]) |
|
256 | for n in nodes]) | |
257 | try: |
|
257 | try: | |
258 | m = match.exact(repo.root, '', ['.hgsigs']) |
|
258 | m = match.exact(repo.root, '', ['.hgsigs']) | |
259 | repo.commit(message, opts['user'], opts['date'], match=m) |
|
259 | repo.commit(message, opts['user'], opts['date'], match=m) | |
260 | except ValueError, inst: |
|
260 | except ValueError, inst: | |
261 | raise util.Abort(str(inst)) |
|
261 | raise util.Abort(str(inst)) | |
262 |
|
262 | |||
263 | def node2txt(repo, node, ver): |
|
263 | def node2txt(repo, node, ver): | |
264 | """map a manifest into some text""" |
|
264 | """map a manifest into some text""" | |
265 | if ver == "0": |
|
265 | if ver == "0": | |
266 | return "%s\n" % hgnode.hex(node) |
|
266 | return "%s\n" % hgnode.hex(node) | |
267 | else: |
|
267 | else: | |
268 | raise util.Abort(_("unknown signature version")) |
|
268 | raise util.Abort(_("unknown signature version")) | |
269 |
|
269 | |||
270 | cmdtable = { |
|
270 | cmdtable = { | |
271 | "sign": |
|
271 | "sign": | |
272 | (sign, |
|
272 | (sign, | |
273 | [('l', 'local', None, _('make the signature local')), |
|
273 | [('l', 'local', None, _('make the signature local')), | |
274 | ('f', 'force', None, _('sign even if the sigfile is modified')), |
|
274 | ('f', 'force', None, _('sign even if the sigfile is modified')), | |
275 | ('', 'no-commit', None, _('do not commit the sigfile after signing')), |
|
275 | ('', 'no-commit', None, _('do not commit the sigfile after signing')), | |
276 | ('k', 'key', '', _('the key id to sign with')), |
|
276 | ('k', 'key', '', _('the key id to sign with')), | |
277 | ('m', 'message', '', _('commit message')), |
|
277 | ('m', 'message', '', _('commit message')), | |
278 | ] + commands.commitopts2, |
|
278 | ] + commands.commitopts2, | |
279 | _('hg sign [OPTION]... [REVISION]...')), |
|
279 | _('hg sign [OPTION]... [REVISION]...')), | |
280 | "sigcheck": (check, [], _('hg sigcheck REVISION')), |
|
280 | "sigcheck": (check, [], _('hg sigcheck REVISION')), | |
281 | "sigs": (sigs, [], _('hg sigs')), |
|
281 | "sigs": (sigs, [], _('hg sigs')), | |
282 | } |
|
282 | } | |
283 |
|
283 |
@@ -1,97 +1,96 b'' | |||||
1 | # Mercurial extension to make it easy to refer to the parent of a revision |
|
1 | # Mercurial extension to make it easy to refer to the parent of a revision | |
2 | # |
|
2 | # | |
3 | # Copyright (C) 2007 Alexis S. L. Carvalho <alexis@cecm.usp.br> |
|
3 | # Copyright (C) 2007 Alexis S. L. Carvalho <alexis@cecm.usp.br> | |
4 | # |
|
4 | # | |
5 | # This software may be used and distributed according to the terms of the |
|
5 | # This software may be used and distributed according to the terms of the | |
6 | # GNU General Public License version 2, incorporated herein by reference. |
|
6 | # GNU General Public License version 2, incorporated herein by reference. | |
7 |
|
7 | |||
8 | '''\ |
|
8 | '''use suffixes to refer to ancestor revisions | |
9 | use suffixes to refer to ancestor revisions |
|
|||
10 |
|
9 | |||
11 | This extension allows you to use git-style suffixes to refer to the |
|
10 | This extension allows you to use git-style suffixes to refer to the | |
12 | ancestors of a specific revision. |
|
11 | ancestors of a specific revision. | |
13 |
|
12 | |||
14 | For example, if you can refer to a revision as "foo", then: |
|
13 | For example, if you can refer to a revision as "foo", then: | |
15 |
|
14 | |||
16 | - foo^N = Nth parent of foo |
|
15 | - foo^N = Nth parent of foo | |
17 | foo^0 = foo |
|
16 | foo^0 = foo | |
18 | foo^1 = first parent of foo |
|
17 | foo^1 = first parent of foo | |
19 | foo^2 = second parent of foo |
|
18 | foo^2 = second parent of foo | |
20 | foo^ = foo^1 |
|
19 | foo^ = foo^1 | |
21 |
|
20 | |||
22 | - foo~N = Nth first grandparent of foo |
|
21 | - foo~N = Nth first grandparent of foo | |
23 | foo~0 = foo |
|
22 | foo~0 = foo | |
24 | foo~1 = foo^1 = foo^ = first parent of foo |
|
23 | foo~1 = foo^1 = foo^ = first parent of foo | |
25 | foo~2 = foo^1^1 = foo^^ = first parent of first parent of foo |
|
24 | foo~2 = foo^1^1 = foo^^ = first parent of first parent of foo | |
26 | ''' |
|
25 | ''' | |
27 | from mercurial import error |
|
26 | from mercurial import error | |
28 |
|
27 | |||
29 | def reposetup(ui, repo): |
|
28 | def reposetup(ui, repo): | |
30 | if not repo.local(): |
|
29 | if not repo.local(): | |
31 | return |
|
30 | return | |
32 |
|
31 | |||
33 | class parentrevspecrepo(repo.__class__): |
|
32 | class parentrevspecrepo(repo.__class__): | |
34 | def lookup(self, key): |
|
33 | def lookup(self, key): | |
35 | try: |
|
34 | try: | |
36 | _super = super(parentrevspecrepo, self) |
|
35 | _super = super(parentrevspecrepo, self) | |
37 | return _super.lookup(key) |
|
36 | return _super.lookup(key) | |
38 | except error.RepoError: |
|
37 | except error.RepoError: | |
39 | pass |
|
38 | pass | |
40 |
|
39 | |||
41 | circ = key.find('^') |
|
40 | circ = key.find('^') | |
42 | tilde = key.find('~') |
|
41 | tilde = key.find('~') | |
43 | if circ < 0 and tilde < 0: |
|
42 | if circ < 0 and tilde < 0: | |
44 | raise |
|
43 | raise | |
45 | elif circ >= 0 and tilde >= 0: |
|
44 | elif circ >= 0 and tilde >= 0: | |
46 | end = min(circ, tilde) |
|
45 | end = min(circ, tilde) | |
47 | else: |
|
46 | else: | |
48 | end = max(circ, tilde) |
|
47 | end = max(circ, tilde) | |
49 |
|
48 | |||
50 | cl = self.changelog |
|
49 | cl = self.changelog | |
51 | base = key[:end] |
|
50 | base = key[:end] | |
52 | try: |
|
51 | try: | |
53 | node = _super.lookup(base) |
|
52 | node = _super.lookup(base) | |
54 | except error.RepoError: |
|
53 | except error.RepoError: | |
55 | # eek - reraise the first error |
|
54 | # eek - reraise the first error | |
56 | return _super.lookup(key) |
|
55 | return _super.lookup(key) | |
57 |
|
56 | |||
58 | rev = cl.rev(node) |
|
57 | rev = cl.rev(node) | |
59 | suffix = key[end:] |
|
58 | suffix = key[end:] | |
60 | i = 0 |
|
59 | i = 0 | |
61 | while i < len(suffix): |
|
60 | while i < len(suffix): | |
62 | # foo^N => Nth parent of foo |
|
61 | # foo^N => Nth parent of foo | |
63 | # foo^0 == foo |
|
62 | # foo^0 == foo | |
64 | # foo^1 == foo^ == 1st parent of foo |
|
63 | # foo^1 == foo^ == 1st parent of foo | |
65 | # foo^2 == 2nd parent of foo |
|
64 | # foo^2 == 2nd parent of foo | |
66 | if suffix[i] == '^': |
|
65 | if suffix[i] == '^': | |
67 | j = i + 1 |
|
66 | j = i + 1 | |
68 | p = cl.parentrevs(rev) |
|
67 | p = cl.parentrevs(rev) | |
69 | if j < len(suffix) and suffix[j].isdigit(): |
|
68 | if j < len(suffix) and suffix[j].isdigit(): | |
70 | j += 1 |
|
69 | j += 1 | |
71 | n = int(suffix[i+1:j]) |
|
70 | n = int(suffix[i+1:j]) | |
72 | if n > 2 or n == 2 and p[1] == -1: |
|
71 | if n > 2 or n == 2 and p[1] == -1: | |
73 | raise |
|
72 | raise | |
74 | else: |
|
73 | else: | |
75 | n = 1 |
|
74 | n = 1 | |
76 | if n: |
|
75 | if n: | |
77 | rev = p[n - 1] |
|
76 | rev = p[n - 1] | |
78 | i = j |
|
77 | i = j | |
79 | # foo~N => Nth first grandparent of foo |
|
78 | # foo~N => Nth first grandparent of foo | |
80 | # foo~0 = foo |
|
79 | # foo~0 = foo | |
81 | # foo~1 = foo^1 == foo^ == 1st parent of foo |
|
80 | # foo~1 = foo^1 == foo^ == 1st parent of foo | |
82 | # foo~2 = foo^1^1 == foo^^ == 1st parent of 1st parent of foo |
|
81 | # foo~2 = foo^1^1 == foo^^ == 1st parent of 1st parent of foo | |
83 | elif suffix[i] == '~': |
|
82 | elif suffix[i] == '~': | |
84 | j = i + 1 |
|
83 | j = i + 1 | |
85 | while j < len(suffix) and suffix[j].isdigit(): |
|
84 | while j < len(suffix) and suffix[j].isdigit(): | |
86 | j += 1 |
|
85 | j += 1 | |
87 | if j == i + 1: |
|
86 | if j == i + 1: | |
88 | raise |
|
87 | raise | |
89 | n = int(suffix[i+1:j]) |
|
88 | n = int(suffix[i+1:j]) | |
90 | for k in xrange(n): |
|
89 | for k in xrange(n): | |
91 | rev = cl.parentrevs(rev)[0] |
|
90 | rev = cl.parentrevs(rev)[0] | |
92 | i = j |
|
91 | i = j | |
93 | else: |
|
92 | else: | |
94 | raise |
|
93 | raise | |
95 | return cl.node(rev) |
|
94 | return cl.node(rev) | |
96 |
|
95 | |||
97 | repo.__class__ = parentrevspecrepo |
|
96 | repo.__class__ = parentrevspecrepo |
@@ -1,106 +1,108 b'' | |||||
1 | # Copyright (C) 2006 - Marco Barisione <marco@barisione.org> |
|
1 | # Copyright (C) 2006 - Marco Barisione <marco@barisione.org> | |
2 | # |
|
2 | # | |
3 | # This is a small extension for Mercurial (http://www.selenic.com/mercurial) |
|
3 | # This is a small extension for Mercurial (http://www.selenic.com/mercurial) | |
4 | # that removes files not known to mercurial |
|
4 | # that removes files not known to mercurial | |
5 | # |
|
5 | # | |
6 | # This program was inspired by the "cvspurge" script contained in CVS utilities |
|
6 | # This program was inspired by the "cvspurge" script contained in CVS utilities | |
7 | # (http://www.red-bean.com/cvsutils/). |
|
7 | # (http://www.red-bean.com/cvsutils/). | |
8 | # |
|
8 | # | |
9 | # For help on the usage of "hg purge" use: |
|
9 | # For help on the usage of "hg purge" use: | |
10 | # hg help purge |
|
10 | # hg help purge | |
11 | # |
|
11 | # | |
12 | # This program is free software; you can redistribute it and/or modify |
|
12 | # This program is free software; you can redistribute it and/or modify | |
13 | # it under the terms of the GNU General Public License as published by |
|
13 | # it under the terms of the GNU General Public License as published by | |
14 | # the Free Software Foundation; either version 2 of the License, or |
|
14 | # the Free Software Foundation; either version 2 of the License, or | |
15 | # (at your option) any later version. |
|
15 | # (at your option) any later version. | |
16 | # |
|
16 | # | |
17 | # This program is distributed in the hope that it will be useful, |
|
17 | # This program is distributed in the hope that it will be useful, | |
18 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
18 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
19 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
19 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
20 | # GNU General Public License for more details. |
|
20 | # GNU General Public License for more details. | |
21 | # |
|
21 | # | |
22 | # You should have received a copy of the GNU General Public License |
|
22 | # You should have received a copy of the GNU General Public License | |
23 | # along with this program; if not, write to the Free Software |
|
23 | # along with this program; if not, write to the Free Software | |
24 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
|
24 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |
25 |
|
25 | |||
|
26 | '''enable removing untracked files only''' | |||
|
27 | ||||
26 | from mercurial import util, commands, cmdutil |
|
28 | from mercurial import util, commands, cmdutil | |
27 | from mercurial.i18n import _ |
|
29 | from mercurial.i18n import _ | |
28 | import os, stat |
|
30 | import os, stat | |
29 |
|
31 | |||
30 | def purge(ui, repo, *dirs, **opts): |
|
32 | def purge(ui, repo, *dirs, **opts): | |
31 | '''removes files not tracked by Mercurial |
|
33 | '''removes files not tracked by Mercurial | |
32 |
|
34 | |||
33 | Delete files not known to Mercurial. This is useful to test local |
|
35 | Delete files not known to Mercurial. This is useful to test local | |
34 | and uncommitted changes in an otherwise-clean source tree. |
|
36 | and uncommitted changes in an otherwise-clean source tree. | |
35 |
|
37 | |||
36 | This means that purge will delete: |
|
38 | This means that purge will delete: | |
37 | - Unknown files: files marked with "?" by "hg status" |
|
39 | - Unknown files: files marked with "?" by "hg status" | |
38 | - Empty directories: in fact Mercurial ignores directories unless |
|
40 | - Empty directories: in fact Mercurial ignores directories unless | |
39 | they contain files under source control management |
|
41 | they contain files under source control management | |
40 | But it will leave untouched: |
|
42 | But it will leave untouched: | |
41 | - Modified and unmodified tracked files |
|
43 | - Modified and unmodified tracked files | |
42 | - Ignored files (unless --all is specified) |
|
44 | - Ignored files (unless --all is specified) | |
43 | - New files added to the repository (with "hg add") |
|
45 | - New files added to the repository (with "hg add") | |
44 |
|
46 | |||
45 | If directories are given on the command line, only files in these |
|
47 | If directories are given on the command line, only files in these | |
46 | directories are considered. |
|
48 | directories are considered. | |
47 |
|
49 | |||
48 | Be careful with purge, as you could irreversibly delete some files |
|
50 | Be careful with purge, as you could irreversibly delete some files | |
49 | you forgot to add to the repository. If you only want to print the |
|
51 | you forgot to add to the repository. If you only want to print the | |
50 | list of files that this program would delete, use the --print |
|
52 | list of files that this program would delete, use the --print | |
51 | option. |
|
53 | option. | |
52 | ''' |
|
54 | ''' | |
53 | act = not opts['print'] |
|
55 | act = not opts['print'] | |
54 | eol = '\n' |
|
56 | eol = '\n' | |
55 | if opts['print0']: |
|
57 | if opts['print0']: | |
56 | eol = '\0' |
|
58 | eol = '\0' | |
57 | act = False # --print0 implies --print |
|
59 | act = False # --print0 implies --print | |
58 |
|
60 | |||
59 | def remove(remove_func, name): |
|
61 | def remove(remove_func, name): | |
60 | if act: |
|
62 | if act: | |
61 | try: |
|
63 | try: | |
62 | remove_func(repo.wjoin(name)) |
|
64 | remove_func(repo.wjoin(name)) | |
63 | except OSError: |
|
65 | except OSError: | |
64 | m = _('%s cannot be removed') % name |
|
66 | m = _('%s cannot be removed') % name | |
65 | if opts['abort_on_err']: |
|
67 | if opts['abort_on_err']: | |
66 | raise util.Abort(m) |
|
68 | raise util.Abort(m) | |
67 | ui.warn(_('warning: %s\n') % m) |
|
69 | ui.warn(_('warning: %s\n') % m) | |
68 | else: |
|
70 | else: | |
69 | ui.write('%s%s' % (name, eol)) |
|
71 | ui.write('%s%s' % (name, eol)) | |
70 |
|
72 | |||
71 | def removefile(path): |
|
73 | def removefile(path): | |
72 | try: |
|
74 | try: | |
73 | os.remove(path) |
|
75 | os.remove(path) | |
74 | except OSError: |
|
76 | except OSError: | |
75 | # read-only files cannot be unlinked under Windows |
|
77 | # read-only files cannot be unlinked under Windows | |
76 | s = os.stat(path) |
|
78 | s = os.stat(path) | |
77 | if (s.st_mode & stat.S_IWRITE) != 0: |
|
79 | if (s.st_mode & stat.S_IWRITE) != 0: | |
78 | raise |
|
80 | raise | |
79 | os.chmod(path, stat.S_IMODE(s.st_mode) | stat.S_IWRITE) |
|
81 | os.chmod(path, stat.S_IMODE(s.st_mode) | stat.S_IWRITE) | |
80 | os.remove(path) |
|
82 | os.remove(path) | |
81 |
|
83 | |||
82 | directories = [] |
|
84 | directories = [] | |
83 | match = cmdutil.match(repo, dirs, opts) |
|
85 | match = cmdutil.match(repo, dirs, opts) | |
84 | match.dir = directories.append |
|
86 | match.dir = directories.append | |
85 | status = repo.status(match=match, ignored=opts['all'], unknown=True) |
|
87 | status = repo.status(match=match, ignored=opts['all'], unknown=True) | |
86 |
|
88 | |||
87 | for f in sorted(status[4] + status[5]): |
|
89 | for f in sorted(status[4] + status[5]): | |
88 | ui.note(_('Removing file %s\n') % f) |
|
90 | ui.note(_('Removing file %s\n') % f) | |
89 | remove(removefile, f) |
|
91 | remove(removefile, f) | |
90 |
|
92 | |||
91 | for f in sorted(directories, reverse=True): |
|
93 | for f in sorted(directories, reverse=True): | |
92 | if match(f) and not os.listdir(repo.wjoin(f)): |
|
94 | if match(f) and not os.listdir(repo.wjoin(f)): | |
93 | ui.note(_('Removing directory %s\n') % f) |
|
95 | ui.note(_('Removing directory %s\n') % f) | |
94 | remove(os.rmdir, f) |
|
96 | remove(os.rmdir, f) | |
95 |
|
97 | |||
96 | cmdtable = { |
|
98 | cmdtable = { | |
97 | 'purge|clean': |
|
99 | 'purge|clean': | |
98 | (purge, |
|
100 | (purge, | |
99 | [('a', 'abort-on-err', None, _('abort if an error occurs')), |
|
101 | [('a', 'abort-on-err', None, _('abort if an error occurs')), | |
100 | ('', 'all', None, _('purge ignored files too')), |
|
102 | ('', 'all', None, _('purge ignored files too')), | |
101 | ('p', 'print', None, _('print filenames instead of deleting them')), |
|
103 | ('p', 'print', None, _('print filenames instead of deleting them')), | |
102 | ('0', 'print0', None, _('end filenames with NUL, for use with xargs' |
|
104 | ('0', 'print0', None, _('end filenames with NUL, for use with xargs' | |
103 | ' (implies -p/--print)')), |
|
105 | ' (implies -p/--print)')), | |
104 | ] + commands.walkopts, |
|
106 | ] + commands.walkopts, | |
105 | _('hg purge [OPTION]... [DIR]...')) |
|
107 | _('hg purge [OPTION]... [DIR]...')) | |
106 | } |
|
108 | } |
@@ -1,31 +1,31 b'' | |||||
1 | # Mercurial extension to provide the 'hg share' command |
|
|||
2 | # |
|
|||
3 |
|
|
1 | # Copyright 2006, 2007 Matt Mackall <mpm@selenic.com> | |
4 | # |
|
2 | # | |
5 | # This software may be used and distributed according to the terms of the |
|
3 | # This software may be used and distributed according to the terms of the | |
6 | # GNU General Public License version 2, incorporated herein by reference. |
|
4 | # GNU General Public License version 2, incorporated herein by reference. | |
7 |
|
5 | |||
|
6 | '''provides the hg share command''' | |||
|
7 | ||||
8 | import os |
|
8 | import os | |
9 | from mercurial.i18n import _ |
|
9 | from mercurial.i18n import _ | |
10 | from mercurial import hg, commands |
|
10 | from mercurial import hg, commands | |
11 |
|
11 | |||
12 | def share(ui, source, dest=None, noupdate=False): |
|
12 | def share(ui, source, dest=None, noupdate=False): | |
13 | """create a new shared repository (experimental) |
|
13 | """create a new shared repository (experimental) | |
14 |
|
14 | |||
15 | Initialize a new repository and working directory that shares its |
|
15 | Initialize a new repository and working directory that shares its | |
16 | history with another repository. |
|
16 | history with another repository. | |
17 |
|
17 | |||
18 | NOTE: actions that change history such as rollback or moving the |
|
18 | NOTE: actions that change history such as rollback or moving the | |
19 | source may confuse sharers. |
|
19 | source may confuse sharers. | |
20 | """ |
|
20 | """ | |
21 |
|
21 | |||
22 | return hg.share(ui, source, dest, not noupdate) |
|
22 | return hg.share(ui, source, dest, not noupdate) | |
23 |
|
23 | |||
24 | cmdtable = { |
|
24 | cmdtable = { | |
25 | "share": |
|
25 | "share": | |
26 | (share, |
|
26 | (share, | |
27 | [('U', 'noupdate', None, _('do not create a working copy'))], |
|
27 | [('U', 'noupdate', None, _('do not create a working copy'))], | |
28 | _('[-U] SOURCE [DEST]')), |
|
28 | _('[-U] SOURCE [DEST]')), | |
29 | } |
|
29 | } | |
30 |
|
30 | |||
31 | commands.norepo += " share" |
|
31 | commands.norepo += " share" |
@@ -1,155 +1,158 b'' | |||||
1 | # win32text.py - LF <-> CRLF/CR translation utilities for Windows/Mac users |
|
1 | # win32text.py - LF <-> CRLF/CR translation utilities for Windows/Mac users | |
2 | # |
|
2 | # | |
3 | # Copyright 2005, 2007-2009 Matt Mackall <mpm@selenic.com> and others |
|
3 | # Copyright 2005, 2007-2009 Matt Mackall <mpm@selenic.com> and others | |
4 | # |
|
4 | # | |
5 | # This software may be used and distributed according to the terms of the |
|
5 | # This software may be used and distributed according to the terms of the | |
6 | # GNU General Public License version 2, incorporated herein by reference. |
|
6 | # GNU General Public License version 2, incorporated herein by reference. | |
7 | # |
|
7 | ||
8 | # To perform automatic newline conversion, use: |
|
8 | '''LF <-> CRLF/CR translation utilities | |
9 | # |
|
9 | ||
10 | # [extensions] |
|
10 | To perform automatic newline conversion, use: | |
11 | # hgext.win32text = |
|
11 | ||
12 | # [encode] |
|
12 | [extensions] | |
13 | # ** = cleverencode: |
|
13 | hgext.win32text = | |
14 | # # or ** = macencode: |
|
14 | [encode] | |
15 | # [decode] |
|
15 | ** = cleverencode: | |
16 |
# |
|
16 | # or ** = macencode: | |
17 | # # or ** = macdecode: |
|
17 | ||
18 | # |
|
18 | [decode] | |
19 | # If not doing conversion, to make sure you do not commit CRLF/CR by |
|
19 | ** = cleverdecode: | |
20 | # accident: |
|
20 | # or ** = macdecode: | |
21 | # |
|
21 | ||
22 | # [hooks] |
|
22 | If not doing conversion, to make sure you do not commit CRLF/CR by accident: | |
23 | # pretxncommit.crlf = python:hgext.win32text.forbidcrlf |
|
23 | ||
24 | # # or pretxncommit.cr = python:hgext.win32text.forbidcr |
|
24 | [hooks] | |
25 | # |
|
25 | pretxncommit.crlf = python:hgext.win32text.forbidcrlf | |
26 | # To do the same check on a server to prevent CRLF/CR from being |
|
26 | # or pretxncommit.cr = python:hgext.win32text.forbidcr | |
27 | # pushed or pulled: |
|
27 | ||
28 | # |
|
28 | To do the same check on a server to prevent CRLF/CR from being | |
29 | # [hooks] |
|
29 | pushed or pulled: | |
30 | # pretxnchangegroup.crlf = python:hgext.win32text.forbidcrlf |
|
30 | ||
31 | # # or pretxnchangegroup.cr = python:hgext.win32text.forbidcr |
|
31 | [hooks] | |
|
32 | pretxnchangegroup.crlf = python:hgext.win32text.forbidcrlf | |||
|
33 | # or pretxnchangegroup.cr = python:hgext.win32text.forbidcr | |||
|
34 | ''' | |||
32 |
|
35 | |||
33 | from mercurial.i18n import _ |
|
36 | from mercurial.i18n import _ | |
34 | from mercurial.node import short |
|
37 | from mercurial.node import short | |
35 | from mercurial import util |
|
38 | from mercurial import util | |
36 | import re |
|
39 | import re | |
37 |
|
40 | |||
38 | # regexp for single LF without CR preceding. |
|
41 | # regexp for single LF without CR preceding. | |
39 | re_single_lf = re.compile('(^|[^\r])\n', re.MULTILINE) |
|
42 | re_single_lf = re.compile('(^|[^\r])\n', re.MULTILINE) | |
40 |
|
43 | |||
41 | newlinestr = {'\r\n': 'CRLF', '\r': 'CR'} |
|
44 | newlinestr = {'\r\n': 'CRLF', '\r': 'CR'} | |
42 | filterstr = {'\r\n': 'clever', '\r': 'mac'} |
|
45 | filterstr = {'\r\n': 'clever', '\r': 'mac'} | |
43 |
|
46 | |||
44 | def checknewline(s, newline, ui=None, repo=None, filename=None): |
|
47 | def checknewline(s, newline, ui=None, repo=None, filename=None): | |
45 | # warn if already has 'newline' in repository. |
|
48 | # warn if already has 'newline' in repository. | |
46 | # it might cause unexpected eol conversion. |
|
49 | # it might cause unexpected eol conversion. | |
47 | # see issue 302: |
|
50 | # see issue 302: | |
48 | # http://www.selenic.com/mercurial/bts/issue302 |
|
51 | # http://www.selenic.com/mercurial/bts/issue302 | |
49 | if newline in s and ui and filename and repo: |
|
52 | if newline in s and ui and filename and repo: | |
50 | ui.warn(_('WARNING: %s already has %s line endings\n' |
|
53 | ui.warn(_('WARNING: %s already has %s line endings\n' | |
51 | 'and does not need EOL conversion by the win32text plugin.\n' |
|
54 | 'and does not need EOL conversion by the win32text plugin.\n' | |
52 | 'Before your next commit, please reconsider your ' |
|
55 | 'Before your next commit, please reconsider your ' | |
53 | 'encode/decode settings in \nMercurial.ini or %s.\n') % |
|
56 | 'encode/decode settings in \nMercurial.ini or %s.\n') % | |
54 | (filename, newlinestr[newline], repo.join('hgrc'))) |
|
57 | (filename, newlinestr[newline], repo.join('hgrc'))) | |
55 |
|
58 | |||
56 | def dumbdecode(s, cmd, **kwargs): |
|
59 | def dumbdecode(s, cmd, **kwargs): | |
57 | checknewline(s, '\r\n', **kwargs) |
|
60 | checknewline(s, '\r\n', **kwargs) | |
58 | # replace single LF to CRLF |
|
61 | # replace single LF to CRLF | |
59 | return re_single_lf.sub('\\1\r\n', s) |
|
62 | return re_single_lf.sub('\\1\r\n', s) | |
60 |
|
63 | |||
61 | def dumbencode(s, cmd): |
|
64 | def dumbencode(s, cmd): | |
62 | return s.replace('\r\n', '\n') |
|
65 | return s.replace('\r\n', '\n') | |
63 |
|
66 | |||
64 | def macdumbdecode(s, cmd, **kwargs): |
|
67 | def macdumbdecode(s, cmd, **kwargs): | |
65 | checknewline(s, '\r', **kwargs) |
|
68 | checknewline(s, '\r', **kwargs) | |
66 | return s.replace('\n', '\r') |
|
69 | return s.replace('\n', '\r') | |
67 |
|
70 | |||
68 | def macdumbencode(s, cmd): |
|
71 | def macdumbencode(s, cmd): | |
69 | return s.replace('\r', '\n') |
|
72 | return s.replace('\r', '\n') | |
70 |
|
73 | |||
71 | def cleverdecode(s, cmd, **kwargs): |
|
74 | def cleverdecode(s, cmd, **kwargs): | |
72 | if not util.binary(s): |
|
75 | if not util.binary(s): | |
73 | return dumbdecode(s, cmd, **kwargs) |
|
76 | return dumbdecode(s, cmd, **kwargs) | |
74 | return s |
|
77 | return s | |
75 |
|
78 | |||
76 | def cleverencode(s, cmd): |
|
79 | def cleverencode(s, cmd): | |
77 | if not util.binary(s): |
|
80 | if not util.binary(s): | |
78 | return dumbencode(s, cmd) |
|
81 | return dumbencode(s, cmd) | |
79 | return s |
|
82 | return s | |
80 |
|
83 | |||
81 | def macdecode(s, cmd, **kwargs): |
|
84 | def macdecode(s, cmd, **kwargs): | |
82 | if not util.binary(s): |
|
85 | if not util.binary(s): | |
83 | return macdumbdecode(s, cmd, **kwargs) |
|
86 | return macdumbdecode(s, cmd, **kwargs) | |
84 | return s |
|
87 | return s | |
85 |
|
88 | |||
86 | def macencode(s, cmd): |
|
89 | def macencode(s, cmd): | |
87 | if not util.binary(s): |
|
90 | if not util.binary(s): | |
88 | return macdumbencode(s, cmd) |
|
91 | return macdumbencode(s, cmd) | |
89 | return s |
|
92 | return s | |
90 |
|
93 | |||
91 | _filters = { |
|
94 | _filters = { | |
92 | 'dumbdecode:': dumbdecode, |
|
95 | 'dumbdecode:': dumbdecode, | |
93 | 'dumbencode:': dumbencode, |
|
96 | 'dumbencode:': dumbencode, | |
94 | 'cleverdecode:': cleverdecode, |
|
97 | 'cleverdecode:': cleverdecode, | |
95 | 'cleverencode:': cleverencode, |
|
98 | 'cleverencode:': cleverencode, | |
96 | 'macdumbdecode:': macdumbdecode, |
|
99 | 'macdumbdecode:': macdumbdecode, | |
97 | 'macdumbencode:': macdumbencode, |
|
100 | 'macdumbencode:': macdumbencode, | |
98 | 'macdecode:': macdecode, |
|
101 | 'macdecode:': macdecode, | |
99 | 'macencode:': macencode, |
|
102 | 'macencode:': macencode, | |
100 | } |
|
103 | } | |
101 |
|
104 | |||
102 | def forbidnewline(ui, repo, hooktype, node, newline, **kwargs): |
|
105 | def forbidnewline(ui, repo, hooktype, node, newline, **kwargs): | |
103 | halt = False |
|
106 | halt = False | |
104 | seen = set() |
|
107 | seen = set() | |
105 | # we try to walk changesets in reverse order from newest to |
|
108 | # we try to walk changesets in reverse order from newest to | |
106 | # oldest, so that if we see a file multiple times, we take the |
|
109 | # oldest, so that if we see a file multiple times, we take the | |
107 | # newest version as canonical. this prevents us from blocking a |
|
110 | # newest version as canonical. this prevents us from blocking a | |
108 | # changegroup that contains an unacceptable commit followed later |
|
111 | # changegroup that contains an unacceptable commit followed later | |
109 | # by a commit that fixes the problem. |
|
112 | # by a commit that fixes the problem. | |
110 | tip = repo['tip'] |
|
113 | tip = repo['tip'] | |
111 | for rev in xrange(len(repo)-1, repo[node].rev()-1, -1): |
|
114 | for rev in xrange(len(repo)-1, repo[node].rev()-1, -1): | |
112 | c = repo[rev] |
|
115 | c = repo[rev] | |
113 | for f in c.files(): |
|
116 | for f in c.files(): | |
114 | if f in seen or f not in tip or f not in c: |
|
117 | if f in seen or f not in tip or f not in c: | |
115 | continue |
|
118 | continue | |
116 | seen.add(f) |
|
119 | seen.add(f) | |
117 | data = c[f].data() |
|
120 | data = c[f].data() | |
118 | if not util.binary(data) and newline in data: |
|
121 | if not util.binary(data) and newline in data: | |
119 | if not halt: |
|
122 | if not halt: | |
120 | ui.warn(_('Attempt to commit or push text file(s) ' |
|
123 | ui.warn(_('Attempt to commit or push text file(s) ' | |
121 | 'using %s line endings\n') % |
|
124 | 'using %s line endings\n') % | |
122 | newlinestr[newline]) |
|
125 | newlinestr[newline]) | |
123 | ui.warn(_('in %s: %s\n') % (short(c.node()), f)) |
|
126 | ui.warn(_('in %s: %s\n') % (short(c.node()), f)) | |
124 | halt = True |
|
127 | halt = True | |
125 | if halt and hooktype == 'pretxnchangegroup': |
|
128 | if halt and hooktype == 'pretxnchangegroup': | |
126 | crlf = newlinestr[newline].lower() |
|
129 | crlf = newlinestr[newline].lower() | |
127 | filter = filterstr[newline] |
|
130 | filter = filterstr[newline] | |
128 | ui.warn(_('\nTo prevent this mistake in your local repository,\n' |
|
131 | ui.warn(_('\nTo prevent this mistake in your local repository,\n' | |
129 | 'add to Mercurial.ini or .hg/hgrc:\n' |
|
132 | 'add to Mercurial.ini or .hg/hgrc:\n' | |
130 | '\n' |
|
133 | '\n' | |
131 | '[hooks]\n' |
|
134 | '[hooks]\n' | |
132 | 'pretxncommit.%s = python:hgext.win32text.forbid%s\n' |
|
135 | 'pretxncommit.%s = python:hgext.win32text.forbid%s\n' | |
133 | '\n' |
|
136 | '\n' | |
134 | 'and also consider adding:\n' |
|
137 | 'and also consider adding:\n' | |
135 | '\n' |
|
138 | '\n' | |
136 | '[extensions]\n' |
|
139 | '[extensions]\n' | |
137 | 'hgext.win32text =\n' |
|
140 | 'hgext.win32text =\n' | |
138 | '[encode]\n' |
|
141 | '[encode]\n' | |
139 | '** = %sencode:\n' |
|
142 | '** = %sencode:\n' | |
140 | '[decode]\n' |
|
143 | '[decode]\n' | |
141 | '** = %sdecode:\n') % (crlf, crlf, filter, filter)) |
|
144 | '** = %sdecode:\n') % (crlf, crlf, filter, filter)) | |
142 | return halt |
|
145 | return halt | |
143 |
|
146 | |||
144 | def forbidcrlf(ui, repo, hooktype, node, **kwargs): |
|
147 | def forbidcrlf(ui, repo, hooktype, node, **kwargs): | |
145 | return forbidnewline(ui, repo, hooktype, node, '\r\n', **kwargs) |
|
148 | return forbidnewline(ui, repo, hooktype, node, '\r\n', **kwargs) | |
146 |
|
149 | |||
147 | def forbidcr(ui, repo, hooktype, node, **kwargs): |
|
150 | def forbidcr(ui, repo, hooktype, node, **kwargs): | |
148 | return forbidnewline(ui, repo, hooktype, node, '\r', **kwargs) |
|
151 | return forbidnewline(ui, repo, hooktype, node, '\r', **kwargs) | |
149 |
|
152 | |||
150 | def reposetup(ui, repo): |
|
153 | def reposetup(ui, repo): | |
151 | if not repo.local(): |
|
154 | if not repo.local(): | |
152 | return |
|
155 | return | |
153 | for name, fn in _filters.iteritems(): |
|
156 | for name, fn in _filters.iteritems(): | |
154 | repo.adddatafilter(name, fn) |
|
157 | repo.adddatafilter(name, fn) | |
155 |
|
158 |
General Comments 0
You need to be logged in to leave comments.
Login now