##// END OF EJS Templates
help: add/fix docstrings for a bunch of extensions
Dirkjan Ochtman -
r8873:e872ef2e default
parent child Browse files
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 # authorization is against local user name on system where hook is
11 Authorization is against local user name on system where hook is run, not
12 # run, not committer of original changeset (since that is easy to
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 # # if acl.allow not present, all users allowed by default
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 # Copyright 2005, 2006 Benoit Boissinot <benoit.boissinot@ens-lyon.org>
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 # Copyright 2006, 2007 Matt Mackall <mpm@selenic.com>
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 # ** = cleverdecode:
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