# HG changeset patch # User Benoit Boissinot # Date 1157411746 -7200 # Node ID 11e3396e3a2be0ce114e1ed94ae4a8c848f5655a # Parent d16b93f4a6ca732bfbde7d810166d0d590050ef0# Parent 60094899dfc9dc0b0c64221e3bbd584619a8d684 merge with brendan diff --git a/contrib/churn.py b/contrib/churn.py 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 +# +# 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: +# +# + +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]'), +} diff --git a/doc/hgrc.5.txt b/doc/hgrc.5.txt --- 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;; diff --git a/mercurial/hg.py b/mercurial/hg.py --- 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 diff --git a/mercurial/localrepo.py b/mercurial/localrepo.py --- 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): diff --git a/mercurial/sshrepo.py b/mercurial/sshrepo.py --- 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) diff --git a/mercurial/ui.py b/mercurial/ui.py --- 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 diff --git a/templates/map-gitweb b/templates/map-gitweb --- a/templates/map-gitweb +++ b/templates/map-gitweb @@ -8,7 +8,7 @@ error = error-gitweb.tmpl naventry = '#label|escape# ' navshortentry = '#label|escape# ' filedifflink = '#file|escape# ' -filenodelink = '#file|escape#file | revisions' +filenodelink = '#file|escape#file | annotate | revisions' fileellipses = '...' changelogentry = changelogentry-gitweb.tmpl searchentry = changelogentry-gitweb.tmpl @@ -46,5 +46,5 @@ filediffchild = 'c filelogchild = 'child #rev#: #node|short#' shortlog = shortlog-gitweb.tmpl shortlogentry = '#date|age# ago#author##desc|strip|firstline|escape#changeset | manifest' -filelogentry = '#date|age# ago#desc|strip|firstline|escape# annotate #rename%filelogrename#' +filelogentry = '#date|age# ago#desc|strip|firstline|escape#file | annotate #rename%filelogrename#' archiveentry = ' | #type|escape# ' diff --git a/tests/test-init b/tests/test-init --- 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 diff --git a/tests/test-init.out b/tests/test-init.out --- 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