##// END OF EJS Templates
merge with brendan
Benoit Boissinot -
r3058:11e3396e merge default
parent child Browse files
Show More
@@ -0,0 +1,179 b''
1 # churn.py - create a graph showing who changed the most lines
2 #
3 # Copyright 2006 Josef "Jeff" Sipek <jeffpc@josefsipek.net>
4 #
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
7 #
8 #
9 # Aliases map file format is simple one alias per line in the following
10 # format:
11 #
12 # <alias email> <actual email>
13
14 from mercurial.demandload import *
15 from mercurial.i18n import gettext as _
16 demandload(globals(), 'time sys signal os')
17 demandload(globals(), 'mercurial:hg,mdiff,fancyopts,commands,ui,util,templater,node')
18
19 def __gather(ui, repo, node1, node2):
20 def dirtywork(f, mmap1, mmap2):
21 lines = 0
22
23 to = mmap1 and repo.file(f).read(mmap1[f]) or None
24 tn = mmap2 and repo.file(f).read(mmap2[f]) or None
25
26 diff = mdiff.unidiff(to, "", tn, "", f).split("\n")
27
28 for line in diff:
29 if not line:
30 continue # skip EOF
31 if line.startswith(" "):
32 continue # context line
33 if line.startswith("--- ") or line.startswith("+++ "):
34 continue # begining of diff
35 if line.startswith("@@ "):
36 continue # info line
37
38 # changed lines
39 lines += 1
40
41 return lines
42
43 ##
44
45 lines = 0
46
47 changes = repo.status(node1, node2, None, util.always)[:5]
48
49 modified, added, removed, deleted, unknown = changes
50
51 who = repo.changelog.read(node2)[1]
52 who = templater.email(who) # get the email of the person
53
54 mmap1 = repo.manifest.read(repo.changelog.read(node1)[0])
55 mmap2 = repo.manifest.read(repo.changelog.read(node2)[0])
56 for f in modified:
57 lines += dirtywork(f, mmap1, mmap2)
58
59 for f in added:
60 lines += dirtywork(f, None, mmap2)
61
62 for f in removed:
63 lines += dirtywork(f, mmap1, None)
64
65 for f in deleted:
66 lines += dirtywork(f, mmap1, mmap2)
67
68 for f in unknown:
69 lines += dirtywork(f, mmap1, mmap2)
70
71 return (who, lines)
72
73 def gather_stats(ui, repo, amap, revs=None, progress=False):
74 stats = {}
75
76 cl = repo.changelog
77
78 if not revs:
79 revs = range(0, cl.count())
80
81 nr_revs = len(revs)
82 cur_rev = 0
83
84 for rev in revs:
85 cur_rev += 1 # next revision
86
87 node2 = cl.node(rev)
88 node1 = cl.parents(node2)[0]
89
90 if cl.parents(node2)[1] != node.nullid:
91 ui.note(_('Revision %d is a merge, ignoring...\n') % (rev,))
92 continue
93
94 who, lines = __gather(ui, repo, node1, node2)
95
96 # remap the owner if possible
97 if amap.has_key(who):
98 ui.note("using '%s' alias for '%s'\n" % (amap[who], who))
99 who = amap[who]
100
101 if not stats.has_key(who):
102 stats[who] = 0
103 stats[who] += lines
104
105 ui.note("rev %d: %d lines by %s\n" % (rev, lines, who))
106
107 if progress:
108 if int(100.0*(cur_rev - 1)/nr_revs) < int(100.0*cur_rev/nr_revs):
109 ui.write("%d%%.." % (int(100.0*cur_rev/nr_revs),))
110 sys.stdout.flush()
111
112 if progress:
113 ui.write("done\n")
114 sys.stdout.flush()
115
116 return stats
117
118 def churn(ui, repo, **opts):
119 "Graphs the number of lines changed"
120
121 def pad(s, l):
122 if len(s) < l:
123 return s + " " * (l-len(s))
124 return s[0:l]
125
126 def graph(n, maximum, width, char):
127 n = int(n * width / float(maximum))
128
129 return char * (n)
130
131 def get_aliases(f):
132 aliases = {}
133
134 for l in f.readlines():
135 l = l.strip()
136 alias, actual = l.split(" ")
137 aliases[alias] = actual
138
139 return aliases
140
141 amap = {}
142 aliases = opts.get('aliases')
143 if aliases:
144 try:
145 f = open(aliases,"r")
146 except OSError, e:
147 print "Error: " + e
148 return
149
150 amap = get_aliases(f)
151 f.close()
152
153 revs = [int(r) for r in commands.revrange(ui, repo, opts['rev'])]
154 revs.sort()
155 stats = gather_stats(ui, repo, amap, revs, opts.get('progress'))
156
157 # make a list of tuples (name, lines) and sort it in descending order
158 ordered = stats.items()
159 ordered.sort(cmp=lambda x, y: cmp(y[1], x[1]))
160
161 maximum = ordered[0][1]
162
163 ui.note("Assuming 80 character terminal\n")
164 width = 80 - 1
165
166 for i in ordered:
167 person = i[0]
168 lines = i[1]
169 print "%s %6d %s" % (pad(person, 20), lines,
170 graph(lines, maximum, width - 20 - 1 - 6 - 2 - 2, '*'))
171
172 cmdtable = {
173 "churn":
174 (churn,
175 [('r', 'rev', [], _('limit statistics to the specified revisions')),
176 ('', 'aliases', '', _('file with email aliases')),
177 ('', 'progress', None, _('show progress'))],
178 'hg churn [-r revision range] [-a file] [--progress]'),
179 }
@@ -135,6 +135,21 b' decode/encode::'
135 # them to the working dir
135 # them to the working dir
136 **.txt = tempfile: unix2dos -n INFILE OUTFILE
136 **.txt = tempfile: unix2dos -n INFILE OUTFILE
137
137
138 defaults::
139 Use the [defaults] section to define command defaults, i.e. the
140 default options/arguments to pass to the specified commands.
141
142 The following example makes 'hg log' run in verbose mode, and
143 'hg status' show only the modified files, by default.
144
145 [defaults]
146 log = -v
147 status = -m
148
149 The actual commands, instead of their aliases, must be used when
150 defining command defaults. The command defaults will also be
151 applied to the aliases of the commands defined.
152
138 email::
153 email::
139 Settings for extensions that send email messages.
154 Settings for extensions that send email messages.
140 from;;
155 from;;
@@ -127,11 +127,6 b' def clone(ui, source, dest=None, pull=Fa'
127 if self.dir_:
127 if self.dir_:
128 self.rmtree(self.dir_, True)
128 self.rmtree(self.dir_, True)
129
129
130 dest_repo = None
131 try:
132 dest_repo = repository(ui, dest)
133 raise util.Abort(_("destination '%s' already exists." % dest))
134 except RepoError:
135 dest_repo = repository(ui, dest, create=True)
130 dest_repo = repository(ui, dest, create=True)
136
131
137 dest_path = None
132 dest_path = None
@@ -31,8 +31,16 b' class localrepository(repo.repository):'
31 path = p
31 path = p
32 self.path = os.path.join(path, ".hg")
32 self.path = os.path.join(path, ".hg")
33
33
34 if not create and not os.path.isdir(self.path):
34 if not os.path.isdir(self.path):
35 if create:
36 if not os.path.exists(path):
37 os.mkdir(path)
38 os.mkdir(self.path)
39 os.mkdir(self.join("data"))
40 else:
35 raise repo.RepoError(_("repository %s not found") % path)
41 raise repo.RepoError(_("repository %s not found") % path)
42 elif create:
43 raise repo.RepoError(_("repository %s already exists") % path)
36
44
37 self.root = os.path.abspath(path)
45 self.root = os.path.abspath(path)
38 self.origroot = path
46 self.origroot = path
@@ -75,12 +83,6 b' class localrepository(repo.repository):'
75 self.decodepats = None
83 self.decodepats = None
76 self.transhandle = None
84 self.transhandle = None
77
85
78 if create:
79 if not os.path.exists(path):
80 os.mkdir(path)
81 os.mkdir(self.path)
82 os.mkdir(self.join("data"))
83
84 self.dirstate = dirstate.dirstate(self.opener, self.ui, self.root)
86 self.dirstate = dirstate.dirstate(self.opener, self.ui, self.root)
85
87
86 def url(self):
88 def url(self):
@@ -34,9 +34,10 b' class sshrepository(remoterepository):'
34 if create:
34 if create:
35 try:
35 try:
36 self.validate_repo(ui, sshcmd, args, remotecmd)
36 self.validate_repo(ui, sshcmd, args, remotecmd)
37 return # the repo is good, nothing more to do
38 except hg.RepoError:
37 except hg.RepoError:
39 pass
38 pass
39 else:
40 raise hg.RepoError(_("repository %s already exists") % path)
40
41
41 cmd = '%s %s "%s init %s"'
42 cmd = '%s %s "%s init %s"'
42 cmd = cmd % (sshcmd, args, remotecmd, self.path)
43 cmd = cmd % (sshcmd, args, remotecmd, self.path)
@@ -52,6 +53,9 b' class sshrepository(remoterepository):'
52 return self._url
53 return self._url
53
54
54 def validate_repo(self, ui, sshcmd, args, remotecmd):
55 def validate_repo(self, ui, sshcmd, args, remotecmd):
56 # cleanup up previous run
57 self.cleanup()
58
55 cmd = '%s %s "%s -R %s serve --stdio"'
59 cmd = '%s %s "%s -R %s serve --stdio"'
56 cmd = cmd % (sshcmd, args, remotecmd, self.path)
60 cmd = cmd % (sshcmd, args, remotecmd, self.path)
57
61
@@ -90,7 +94,7 b' class sshrepository(remoterepository):'
90 if not l: break
94 if not l: break
91 self.ui.status(_("remote: "), l)
95 self.ui.status(_("remote: "), l)
92
96
93 def __del__(self):
97 def cleanup(self):
94 try:
98 try:
95 self.pipeo.close()
99 self.pipeo.close()
96 self.pipei.close()
100 self.pipei.close()
@@ -101,6 +105,8 b' class sshrepository(remoterepository):'
101 except:
105 except:
102 pass
106 pass
103
107
108 __del__ = cleanup
109
104 def do_cmd(self, cmd, **args):
110 def do_cmd(self, cmd, **args):
105 self.ui.debug(_("sending %s command\n") % cmd)
111 self.ui.debug(_("sending %s command\n") % cmd)
106 self.pipeo.write("%s\n" % cmd)
112 self.pipeo.write("%s\n" % cmd)
@@ -54,7 +54,7 b' class ui(object):'
54 def updateopts(self, verbose=False, debug=False, quiet=False,
54 def updateopts(self, verbose=False, debug=False, quiet=False,
55 interactive=True, traceback=False, config=[]):
55 interactive=True, traceback=False, config=[]):
56 self.quiet = (self.quiet or quiet) and not verbose and not debug
56 self.quiet = (self.quiet or quiet) and not verbose and not debug
57 self.verbose = (self.verbose or verbose) or debug
57 self.verbose = ((self.verbose or verbose) or debug) and not self.quiet
58 self.debugflag = (self.debugflag or debug)
58 self.debugflag = (self.debugflag or debug)
59 self.interactive = (self.interactive and interactive)
59 self.interactive = (self.interactive and interactive)
60 self.traceback = self.traceback or traceback
60 self.traceback = self.traceback or traceback
@@ -8,7 +8,7 b' error = error-gitweb.tmpl'
8 naventry = '<a href="?cmd=changelog;rev=#rev#;style=gitweb">#label|escape#</a> '
8 naventry = '<a href="?cmd=changelog;rev=#rev#;style=gitweb">#label|escape#</a> '
9 navshortentry = '<a href="?cmd=shortlog;rev=#rev#;style=gitweb">#label|escape#</a> '
9 navshortentry = '<a href="?cmd=shortlog;rev=#rev#;style=gitweb">#label|escape#</a> '
10 filedifflink = '<a href="?cmd=filediff;node=#node#;file=#file|urlescape#;style=gitweb">#file|escape#</a> '
10 filedifflink = '<a href="?cmd=filediff;node=#node#;file=#file|urlescape#;style=gitweb">#file|escape#</a> '
11 filenodelink = '<tr class="light"><td><a class="list" href="">#file|escape#</a></td><td></td><td class="link"><a href="?cmd=file;filenode=#filenode#;file=#file|urlescape#;style=gitweb">file</a> | <!-- FIXME: <a href="?fd=#filenode|short#;file=#file|urlescape#;style=gitweb">diff</a> | --> <a href="?cmd=filelog;filenode=#filenode|short#;file=#file|urlescape#;style=gitweb">revisions</a></td></tr>'
11 filenodelink = '<tr class="light"><td><a class="list" href="">#file|escape#</a></td><td></td><td class="link"><a href="?cmd=file;filenode=#filenode#;file=#file|urlescape#;style=gitweb">file</a> | <a href="?fa=#filenode|short#;file=#file|urlescape#;style=gitweb">annotate</a> | <!-- FIXME: <a href="?fd=#filenode|short#;file=#file|urlescape#;style=gitweb">diff</a> | --> <a href="?cmd=filelog;filenode=#filenode|short#;file=#file|urlescape#;style=gitweb">revisions</a></td></tr>'
12 fileellipses = '...'
12 fileellipses = '...'
13 changelogentry = changelogentry-gitweb.tmpl
13 changelogentry = changelogentry-gitweb.tmpl
14 searchentry = changelogentry-gitweb.tmpl
14 searchentry = changelogentry-gitweb.tmpl
@@ -46,5 +46,5 b' filediffchild = \'<tr><th class="child">c'
46 filelogchild = '<tr><td align="right">child #rev#:&nbsp;</td><td><a href="?cmd=file;file=#file|urlescape#;filenode=#node#;style=gitweb">#node|short#</a></td></tr>'
46 filelogchild = '<tr><td align="right">child #rev#:&nbsp;</td><td><a href="?cmd=file;file=#file|urlescape#;filenode=#node#;style=gitweb">#node|short#</a></td></tr>'
47 shortlog = shortlog-gitweb.tmpl
47 shortlog = shortlog-gitweb.tmpl
48 shortlogentry = '<tr class="parity#parity#"><td class="age"><i>#date|age# ago</i></td><td><i>#author#</i></td><td><a class="list" href="?cmd=changeset;node=#node|short#;style=gitweb"><b>#desc|strip|firstline|escape#</b></a></td><td class="link"><a href="?cmd=changeset;node=#node|short#;style=gitweb">changeset</a> | <a href="?cmd=manifest;manifest=#manifest|short#;path=/;style=gitweb">manifest</a></td></tr>'
48 shortlogentry = '<tr class="parity#parity#"><td class="age"><i>#date|age# ago</i></td><td><i>#author#</i></td><td><a class="list" href="?cmd=changeset;node=#node|short#;style=gitweb"><b>#desc|strip|firstline|escape#</b></a></td><td class="link"><a href="?cmd=changeset;node=#node|short#;style=gitweb">changeset</a> | <a href="?cmd=manifest;manifest=#manifest|short#;path=/;style=gitweb">manifest</a></td></tr>'
49 filelogentry = '<tr class="parity#parity#"><td class="age"><i>#date|age# ago</i></td><td><a class="list" href="?cmd=changeset;node=#node|short#;style=gitweb"><b>#desc|strip|firstline|escape#</b></a></td><td class="link"><!-- FIXME: <a href="?fd=#node|short#;file=#file|urlescape#;style=gitweb">diff</a> | --> <a href="?fa=#filenode|short#;file=#file|urlescape#;style=gitweb">annotate</a> #rename%filelogrename#</td></tr>'
49 filelogentry = '<tr class="parity#parity#"><td class="age"><i>#date|age# ago</i></td><td><a class="list" href="?cmd=changeset;node=#node|short#;style=gitweb"><b>#desc|strip|firstline|escape#</b></a></td><td class="link"><a href="?f=#node|short#;file=#file|urlescape#;style=gitweb">file</a> | <!-- FIXME: <a href="?fd=#node|short#;file=#file|urlescape#;style=gitweb">diff</a> | --> <a href="?fa=#filenode|short#;file=#file|urlescape#;style=gitweb">annotate</a> #rename%filelogrename#</td></tr>'
50 archiveentry = ' | <a href="?ca=#node|short#;type=#type|urlescape#">#type|escape#</a> '
50 archiveentry = ' | <a href="?ca=#node|short#;type=#type|urlescape#">#type|escape#</a> '
@@ -27,6 +27,9 b' hg init local'
27 echo this > local/foo
27 echo this > local/foo
28 hg ci --cwd local -A -m "init" -d "1000000 0"
28 hg ci --cwd local -A -m "init" -d "1000000 0"
29
29
30 echo "#test failure"
31 hg init local
32
30 echo "# init+push to remote2"
33 echo "# init+push to remote2"
31 hg init -e ./dummyssh ssh://user@dummy/remote2
34 hg init -e ./dummyssh ssh://user@dummy/remote2
32 hg incoming -R remote2 local
35 hg incoming -R remote2 local
@@ -35,6 +38,12 b' hg push -R local -e ./dummyssh ssh://use'
35 echo "# clone to remote1"
38 echo "# clone to remote1"
36 hg clone -e ./dummyssh local ssh://user@dummy/remote1
39 hg clone -e ./dummyssh local ssh://user@dummy/remote1
37
40
41 echo "# init to existing repo"
42 hg init -e ./dummyssh ssh://user@dummy/remote1
43
44 echo "# clone to existing repo"
45 hg clone -e ./dummyssh local ssh://user@dummy/remote1
46
38 echo "# output of dummyssh"
47 echo "# output of dummyssh"
39 cat dummylog
48 cat dummylog
40
49
@@ -1,6 +1,9 b''
1 # creating 'local'
1 # creating 'local'
2 adding foo
2 adding foo
3 #test failure
4 abort: repository local already exists!
3 # init+push to remote2
5 # init+push to remote2
6 remote: abort: repository remote2 not found!
4 changeset: 0:c4e059d443be
7 changeset: 0:c4e059d443be
5 tag: tip
8 tag: tip
6 user: test
9 user: test
@@ -14,20 +17,25 b' remote: adding manifests'
14 remote: adding file changes
17 remote: adding file changes
15 remote: added 1 changesets with 1 changes to 1 files
18 remote: added 1 changesets with 1 changes to 1 files
16 # clone to remote1
19 # clone to remote1
20 remote: abort: repository remote1 not found!
17 searching for changes
21 searching for changes
18 remote: abort: repository remote1 not found!
19 remote: adding changesets
22 remote: adding changesets
20 remote: adding manifests
23 remote: adding manifests
21 remote: adding file changes
24 remote: adding file changes
22 remote: added 1 changesets with 1 changes to 1 files
25 remote: added 1 changesets with 1 changes to 1 files
26 # init to existing repo
27 abort: repository ssh://user@dummy/remote1 already exists!
28 # clone to existing repo
29 abort: repository ssh://user@dummy/remote1 already exists!
23 # output of dummyssh
30 # output of dummyssh
24 Got arguments 1:user@dummy 2:hg -R remote2 serve --stdio 3: 4: 5:
31 Got arguments 1:user@dummy 2:hg -R remote2 serve --stdio 3: 4: 5:
25 Got arguments 1:user@dummy 2:hg init remote2 3: 4: 5:
32 Got arguments 1:user@dummy 2:hg init remote2 3: 4: 5:
26 Got arguments 1:user@dummy 2:hg -R remote2 serve --stdio 3: 4: 5:
33 Got arguments 1:user@dummy 2:hg -R remote2 serve --stdio 3: 4: 5:
27 Got arguments 1:user@dummy 2:hg -R remote2 serve --stdio 3: 4: 5:
34 Got arguments 1:user@dummy 2:hg -R remote2 serve --stdio 3: 4: 5:
28 Got arguments 1:user@dummy 2:hg -R remote1 serve --stdio 3: 4: 5:
35 Got arguments 1:user@dummy 2:hg -R remote1 serve --stdio 3: 4: 5:
36 Got arguments 1:user@dummy 2:hg init remote1 3: 4: 5:
29 Got arguments 1:user@dummy 2:hg -R remote1 serve --stdio 3: 4: 5:
37 Got arguments 1:user@dummy 2:hg -R remote1 serve --stdio 3: 4: 5:
30 Got arguments 1:user@dummy 2:hg init remote1 3: 4: 5:
38 Got arguments 1:user@dummy 2:hg -R remote1 serve --stdio 3: 4: 5:
31 Got arguments 1:user@dummy 2:hg -R remote1 serve --stdio 3: 4: 5:
39 Got arguments 1:user@dummy 2:hg -R remote1 serve --stdio 3: 4: 5:
32 # comparing repositories
40 # comparing repositories
33 0:c4e059d443be
41 0:c4e059d443be
General Comments 0
You need to be logged in to leave comments. Login now