new file mode 100644
--- /dev/null
+++ b/contrib/churn.py
@@ -0,0 +1,179 @@
+# churn.py - create a graph showing who changed the most lines
+#
+# Copyright 2006 Josef "Jeff" Sipek <jeffpc@josefsipek.net>
+#
+# This software may be used and distributed according to the terms
+# of the GNU General Public License, incorporated herein by reference.
+#
+#
+# Aliases map file format is simple one alias per line in the following
+# format:
+#
+# <alias email> <actual email>
+
+from mercurial.demandload import *
+from mercurial.i18n import gettext as _
+demandload(globals(), 'time sys signal os')
+demandload(globals(), 'mercurial:hg,mdiff,fancyopts,commands,ui,util,templater,node')
+
+def __gather(ui, repo, node1, node2):
+ def dirtywork(f, mmap1, mmap2):
+ lines = 0
+
+ to = mmap1 and repo.file(f).read(mmap1[f]) or None
+ tn = mmap2 and repo.file(f).read(mmap2[f]) or None
+
+ diff = mdiff.unidiff(to, "", tn, "", f).split("\n")
+
+ for line in diff:
+ if not line:
+ continue # skip EOF
+ if line.startswith(" "):
+ continue # context line
+ if line.startswith("--- ") or line.startswith("+++ "):
+ continue # begining of diff
+ if line.startswith("@@ "):
+ continue # info line
+
+ # changed lines
+ lines += 1
+
+ return lines
+
+ ##
+
+ lines = 0
+
+ changes = repo.status(node1, node2, None, util.always)[:5]
+
+ modified, added, removed, deleted, unknown = changes
+
+ who = repo.changelog.read(node2)[1]
+ who = templater.email(who) # get the email of the person
+
+ mmap1 = repo.manifest.read(repo.changelog.read(node1)[0])
+ mmap2 = repo.manifest.read(repo.changelog.read(node2)[0])
+ for f in modified:
+ lines += dirtywork(f, mmap1, mmap2)
+
+ for f in added:
+ lines += dirtywork(f, None, mmap2)
+
+ for f in removed:
+ lines += dirtywork(f, mmap1, None)
+
+ for f in deleted:
+ lines += dirtywork(f, mmap1, mmap2)
+
+ for f in unknown:
+ lines += dirtywork(f, mmap1, mmap2)
+
+ return (who, lines)
+
+def gather_stats(ui, repo, amap, revs=None, progress=False):
+ stats = {}
+
+ cl = repo.changelog
+
+ if not revs:
+ revs = range(0, cl.count())
+
+ nr_revs = len(revs)
+ cur_rev = 0
+
+ for rev in revs:
+ cur_rev += 1 # next revision
+
+ node2 = cl.node(rev)
+ node1 = cl.parents(node2)[0]
+
+ if cl.parents(node2)[1] != node.nullid:
+ ui.note(_('Revision %d is a merge, ignoring...\n') % (rev,))
+ continue
+
+ who, lines = __gather(ui, repo, node1, node2)
+
+ # remap the owner if possible
+ if amap.has_key(who):
+ ui.note("using '%s' alias for '%s'\n" % (amap[who], who))
+ who = amap[who]
+
+ if not stats.has_key(who):
+ stats[who] = 0
+ stats[who] += lines
+
+ ui.note("rev %d: %d lines by %s\n" % (rev, lines, who))
+
+ if progress:
+ if int(100.0*(cur_rev - 1)/nr_revs) < int(100.0*cur_rev/nr_revs):
+ ui.write("%d%%.." % (int(100.0*cur_rev/nr_revs),))
+ sys.stdout.flush()
+
+ if progress:
+ ui.write("done\n")
+ sys.stdout.flush()
+
+ return stats
+
+def churn(ui, repo, **opts):
+ "Graphs the number of lines changed"
+
+ def pad(s, l):
+ if len(s) < l:
+ return s + " " * (l-len(s))
+ return s[0:l]
+
+ def graph(n, maximum, width, char):
+ n = int(n * width / float(maximum))
+
+ return char * (n)
+
+ def get_aliases(f):
+ aliases = {}
+
+ for l in f.readlines():
+ l = l.strip()
+ alias, actual = l.split(" ")
+ aliases[alias] = actual
+
+ return aliases
+
+ amap = {}
+ aliases = opts.get('aliases')
+ if aliases:
+ try:
+ f = open(aliases,"r")
+ except OSError, e:
+ print "Error: " + e
+ return
+
+ amap = get_aliases(f)
+ f.close()
+
+ revs = [int(r) for r in commands.revrange(ui, repo, opts['rev'])]
+ revs.sort()
+ stats = gather_stats(ui, repo, amap, revs, opts.get('progress'))
+
+ # make a list of tuples (name, lines) and sort it in descending order
+ ordered = stats.items()
+ ordered.sort(cmp=lambda x, y: cmp(y[1], x[1]))
+
+ maximum = ordered[0][1]
+
+ ui.note("Assuming 80 character terminal\n")
+ width = 80 - 1
+
+ for i in ordered:
+ person = i[0]
+ lines = i[1]
+ print "%s %6d %s" % (pad(person, 20), lines,
+ graph(lines, maximum, width - 20 - 1 - 6 - 2 - 2, '*'))
+
+cmdtable = {
+ "churn":
+ (churn,
+ [('r', 'rev', [], _('limit statistics to the specified revisions')),
+ ('', 'aliases', '', _('file with email aliases')),
+ ('', 'progress', None, _('show progress'))],
+ 'hg churn [-r revision range] [-a file] [--progress]'),
+}
--- a/doc/hgrc.5.txt
+++ b/doc/hgrc.5.txt
@@ -135,6 +135,21 @@ decode/encode::
# them to the working dir
**.txt = tempfile: unix2dos -n INFILE OUTFILE
+defaults::
+ Use the [defaults] section to define command defaults, i.e. the
+ default options/arguments to pass to the specified commands.
+
+ The following example makes 'hg log' run in verbose mode, and
+ 'hg status' show only the modified files, by default.
+
+ [defaults]
+ log = -v
+ status = -m
+
+ The actual commands, instead of their aliases, must be used when
+ defining command defaults. The command defaults will also be
+ applied to the aliases of the commands defined.
+
email::
Settings for extensions that send email messages.
from;;
--- a/mercurial/hg.py
+++ b/mercurial/hg.py
@@ -127,12 +127,7 @@ def clone(ui, source, dest=None, pull=Fa
if self.dir_:
self.rmtree(self.dir_, True)
- dest_repo = None
- try:
- dest_repo = repository(ui, dest)
- raise util.Abort(_("destination '%s' already exists." % dest))
- except RepoError:
- dest_repo = repository(ui, dest, create=True)
+ dest_repo = repository(ui, dest, create=True)
dest_path = None
dir_cleanup = None
--- a/mercurial/localrepo.py
+++ b/mercurial/localrepo.py
@@ -31,8 +31,16 @@ class localrepository(repo.repository):
path = p
self.path = os.path.join(path, ".hg")
- if not create and not os.path.isdir(self.path):
- raise repo.RepoError(_("repository %s not found") % path)
+ if not os.path.isdir(self.path):
+ if create:
+ if not os.path.exists(path):
+ os.mkdir(path)
+ os.mkdir(self.path)
+ os.mkdir(self.join("data"))
+ else:
+ raise repo.RepoError(_("repository %s not found") % path)
+ elif create:
+ raise repo.RepoError(_("repository %s already exists") % path)
self.root = os.path.abspath(path)
self.origroot = path
@@ -75,12 +83,6 @@ class localrepository(repo.repository):
self.decodepats = None
self.transhandle = None
- if create:
- if not os.path.exists(path):
- os.mkdir(path)
- os.mkdir(self.path)
- os.mkdir(self.join("data"))
-
self.dirstate = dirstate.dirstate(self.opener, self.ui, self.root)
def url(self):
--- a/mercurial/sshrepo.py
+++ b/mercurial/sshrepo.py
@@ -34,9 +34,10 @@ class sshrepository(remoterepository):
if create:
try:
self.validate_repo(ui, sshcmd, args, remotecmd)
- return # the repo is good, nothing more to do
except hg.RepoError:
pass
+ else:
+ raise hg.RepoError(_("repository %s already exists") % path)
cmd = '%s %s "%s init %s"'
cmd = cmd % (sshcmd, args, remotecmd, self.path)
@@ -52,6 +53,9 @@ class sshrepository(remoterepository):
return self._url
def validate_repo(self, ui, sshcmd, args, remotecmd):
+ # cleanup up previous run
+ self.cleanup()
+
cmd = '%s %s "%s -R %s serve --stdio"'
cmd = cmd % (sshcmd, args, remotecmd, self.path)
@@ -90,7 +94,7 @@ class sshrepository(remoterepository):
if not l: break
self.ui.status(_("remote: "), l)
- def __del__(self):
+ def cleanup(self):
try:
self.pipeo.close()
self.pipei.close()
@@ -101,6 +105,8 @@ class sshrepository(remoterepository):
except:
pass
+ __del__ = cleanup
+
def do_cmd(self, cmd, **args):
self.ui.debug(_("sending %s command\n") % cmd)
self.pipeo.write("%s\n" % cmd)
--- a/mercurial/ui.py
+++ b/mercurial/ui.py
@@ -54,7 +54,7 @@ class ui(object):
def updateopts(self, verbose=False, debug=False, quiet=False,
interactive=True, traceback=False, config=[]):
self.quiet = (self.quiet or quiet) and not verbose and not debug
- self.verbose = (self.verbose or verbose) or debug
+ self.verbose = ((self.verbose or verbose) or debug) and not self.quiet
self.debugflag = (self.debugflag or debug)
self.interactive = (self.interactive and interactive)
self.traceback = self.traceback or traceback
--- a/templates/map-gitweb
+++ b/templates/map-gitweb
@@ -8,7 +8,7 @@ error = error-gitweb.tmpl
naventry = '<a href="?cmd=changelog;rev=#rev#;style=gitweb">#label|escape#</a> '
navshortentry = '<a href="?cmd=shortlog;rev=#rev#;style=gitweb">#label|escape#</a> '
filedifflink = '<a href="?cmd=filediff;node=#node#;file=#file|urlescape#;style=gitweb">#file|escape#</a> '
-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>'
+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>'
fileellipses = '...'
changelogentry = changelogentry-gitweb.tmpl
searchentry = changelogentry-gitweb.tmpl
@@ -46,5 +46,5 @@ filediffchild = '<tr><th class="child">c
filelogchild = '<tr><td align="right">child #rev#: </td><td><a href="?cmd=file;file=#file|urlescape#;filenode=#node#;style=gitweb">#node|short#</a></td></tr>'
shortlog = shortlog-gitweb.tmpl
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>'
-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>'
+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>'
archiveentry = ' | <a href="?ca=#node|short#;type=#type|urlescape#">#type|escape#</a> '
--- a/tests/test-init
+++ b/tests/test-init
@@ -27,6 +27,9 @@ hg init local
echo this > local/foo
hg ci --cwd local -A -m "init" -d "1000000 0"
+echo "#test failure"
+hg init local
+
echo "# init+push to remote2"
hg init -e ./dummyssh ssh://user@dummy/remote2
hg incoming -R remote2 local
@@ -35,6 +38,12 @@ hg push -R local -e ./dummyssh ssh://use
echo "# clone to remote1"
hg clone -e ./dummyssh local ssh://user@dummy/remote1
+echo "# init to existing repo"
+hg init -e ./dummyssh ssh://user@dummy/remote1
+
+echo "# clone to existing repo"
+hg clone -e ./dummyssh local ssh://user@dummy/remote1
+
echo "# output of dummyssh"
cat dummylog
--- a/tests/test-init.out
+++ b/tests/test-init.out
@@ -1,6 +1,9 @@
# creating 'local'
adding foo
+#test failure
+abort: repository local already exists!
# init+push to remote2
+remote: abort: repository remote2 not found!
changeset: 0:c4e059d443be
tag: tip
user: test
@@ -14,20 +17,25 @@ remote: adding manifests
remote: adding file changes
remote: added 1 changesets with 1 changes to 1 files
# clone to remote1
+remote: abort: repository remote1 not found!
searching for changes
-remote: abort: repository remote1 not found!
remote: adding changesets
remote: adding manifests
remote: adding file changes
remote: added 1 changesets with 1 changes to 1 files
+# init to existing repo
+abort: repository ssh://user@dummy/remote1 already exists!
+# clone to existing repo
+abort: repository ssh://user@dummy/remote1 already exists!
# output of dummyssh
Got arguments 1:user@dummy 2:hg -R remote2 serve --stdio 3: 4: 5:
Got arguments 1:user@dummy 2:hg init remote2 3: 4: 5:
Got arguments 1:user@dummy 2:hg -R remote2 serve --stdio 3: 4: 5:
Got arguments 1:user@dummy 2:hg -R remote2 serve --stdio 3: 4: 5:
Got arguments 1:user@dummy 2:hg -R remote1 serve --stdio 3: 4: 5:
+Got arguments 1:user@dummy 2:hg init remote1 3: 4: 5:
Got arguments 1:user@dummy 2:hg -R remote1 serve --stdio 3: 4: 5:
-Got arguments 1:user@dummy 2:hg init remote1 3: 4: 5:
+Got arguments 1:user@dummy 2:hg -R remote1 serve --stdio 3: 4: 5:
Got arguments 1:user@dummy 2:hg -R remote1 serve --stdio 3: 4: 5:
# comparing repositories
0:c4e059d443be