def fixup_user(user,authors): user=user.strip("\"") if "<<>>" in user: user = user.replace("<<>>", "") if "<<" in user: user = user.replace("<<", "<") if authors!=None: # if we have an authors table, try to get mapping # by defaulting to the current value of 'user' user=authors.get(user,user) name,mail,m='','',user_re.match(user) if m==None: # if we don't have 'Name <mail>' syntax, extract name # and mail from hg helpers. this seems to work pretty well. # if email doesn't contain @, replace it with devnull@localhost name=templatefilters.person(user) mail='<%s>' % util.email(user) if '@' not in mail: mail = '<devnull@localhost>' else: # if we have 'Name <mail>' syntax, everything is fine :) name,mail=m.group(1),m.group(2) # remove any silly quoting from username m2=user_clean_re.match(name) if m2!=None: name=m2.group(1) return '%s %s' % (name,mail)
def breakdown(ui, repo, **opts): """Breaks down commits over the past `d` days by user, module, or churn Things I want to know: * which branch has the most commits and churn * which author has the most commits and churn * which files had the most churn * which commits had no ticket * table for standup of all commits made to any branch, sorted by author """ ui.note("looking %d days back\n" % opts['days']) changes = walk_back_by_days(ui, repo, opts['days']) ui.note("found %d changes in range\n" % len(changes)) counters = { 'commits_by_author': Counter(), 'commits_by_branch': Counter(), 'commits_by_weekday': Counter(), } for c in changes: # update author's commit count counters['commits_by_author'][templatefilters.person(c.user())] += 1 counters['commits_by_branch'][c.branch()] += 1 counters['commits_by_weekday'][day_of_week(c.date())] += 1 #for f in repo[c]: # print f break summarize_counter(ui, counters['commits_by_author'], count=20, msg="Commits by Author") summarize_counter(ui, counters['commits_by_branch'], count=20, msg="Commits by Branch") summarize_counter(ui, counters['commits_by_weekday'], msg="Commits by Weekday")
def fetch_data_authors(ctx): # data is a dictionnary mapping an author name to the data for # this author user = ctx.user() email = util.email(user) name = person(user) yield name
def graph(web, req, tmpl): rev = webutil.changectx(web.repo, req).rev() bg_height = 39 revcount = web.maxshortchanges if "revcount" in req.form: revcount = int(req.form.get("revcount", [revcount])[0]) revcount = max(revcount, 1) tmpl.defaults["sessionvars"]["revcount"] = revcount lessvars = copy.copy(tmpl.defaults["sessionvars"]) lessvars["revcount"] = max(revcount / 2, 1) morevars = copy.copy(tmpl.defaults["sessionvars"]) morevars["revcount"] = revcount * 2 max_rev = len(web.repo) - 1 revcount = min(max_rev, revcount) revnode = web.repo.changelog.node(rev) revnode_hex = hex(revnode) uprev = min(max_rev, rev + revcount) downrev = max(0, rev - revcount) count = len(web.repo) changenav = webutil.revnavgen(rev, revcount, count, web.repo.changectx) startrev = rev # if starting revision is less than 60 set it to uprev if rev < web.maxshortchanges: startrev = uprev dag = graphmod.dagwalker(web.repo, range(startrev, downrev - 1, -1)) tree = list(graphmod.colored(dag, web.repo)) canvasheight = (len(tree) + 1) * bg_height - 27 data = [] for (id, type, ctx, vtx, edges) in tree: if type != graphmod.CHANGESET: continue node = str(ctx) age = templatefilters.age(ctx.date()) desc = templatefilters.firstline(ctx.description()) desc = cgi.escape(templatefilters.nonempty(desc)) user = cgi.escape(templatefilters.person(ctx.user())) branch = ctx.branch() branch = branch, web.repo.branchtags().get(branch) == ctx.node() data.append((node, vtx, edges, desc, user, age, branch, ctx.tags(), ctx.bookmarks())) return tmpl( "graph", rev=rev, revcount=revcount, uprev=uprev, lessvars=lessvars, morevars=morevars, downrev=downrev, canvasheight=canvasheight, jsdata=data, bg_height=bg_height, node=revnode_hex, changenav=changenav, )
def graph(web, req, tmpl): rev = webutil.changectx(web.repo, req).rev() bg_height = 39 revcount = web.maxshortchanges if 'revcount' in req.form: revcount = int(req.form.get('revcount', [revcount])[0]) revcount = max(revcount, 1) tmpl.defaults['sessionvars']['revcount'] = revcount lessvars = copy.copy(tmpl.defaults['sessionvars']) lessvars['revcount'] = max(revcount / 2, 1) morevars = copy.copy(tmpl.defaults['sessionvars']) morevars['revcount'] = revcount * 2 max_rev = len(web.repo) - 1 revcount = min(max_rev, revcount) revnode = web.repo.changelog.node(rev) revnode_hex = hex(revnode) uprev = min(max_rev, rev + revcount) downrev = max(0, rev - revcount) count = len(web.repo) changenav = webutil.revnavgen(rev, revcount, count, web.repo.changectx) startrev = rev # if starting revision is less than 60 set it to uprev if rev < web.maxshortchanges: startrev = uprev dag = graphmod.dagwalker(web.repo, range(startrev, downrev - 1, -1)) tree = list(graphmod.colored(dag, web.repo)) canvasheight = (len(tree) + 1) * bg_height - 27 data = [] for (id, type, ctx, vtx, edges) in tree: if type != graphmod.CHANGESET: continue node = str(ctx) age = templatefilters.age(ctx.date()) desc = templatefilters.firstline(ctx.description()) desc = cgi.escape(templatefilters.nonempty(desc)) user = cgi.escape(templatefilters.person(ctx.user())) branch = ctx.branch() branch = branch, web.repo.branchtags().get(branch) == ctx.node() data.append((node, vtx, edges, desc, user, age, branch, ctx.tags(), ctx.bookmarks())) return tmpl('graph', rev=rev, revcount=revcount, uprev=uprev, lessvars=lessvars, morevars=morevars, downrev=downrev, canvasheight=canvasheight, jsdata=data, bg_height=bg_height, node=revnode_hex, changenav=changenav)
def _update_issue(ui, repo, node, **kwargs): """Update a Roundup issue for corresponding changesets. Return True if updating the Roundup issue fails, else False. """ repourl = ui.config('hgroundup', 'repourl') if not repourl: repourl = posixpath.join(ui.config('web', 'baseurl'), 'rev/') fromaddr = ui.config('hgroundup', 'fromaddr') toaddr = ui.config('hgroundup', 'toaddr') for var in ('repourl', 'fromaddr', 'toaddr'): if not locals()[var]: raise RuntimeError( 'roundup hook not configured properly,\nplease ' 'set the "%s" property in the [hgroundup] section' % var) start = repo[node].rev() issues = {} for rev in xrange(start, len(repo)): ctx = repo[rev] description = fromlocal(ctx.description().strip()) matches = ISSUE_PATTERN.finditer(description) ids = set() for match in matches: data = match.groupdict() ui.debug('match in commit msg: %s\n' % data) # check for duplicated issue numbers in the same commit msg if data['issue_id'] in ids: continue ids.add(data['issue_id']) comment = Template(COMMENT_TEMPLATE).substitute({ 'author': fromlocal(person(ctx.user())), 'branch': ctx.branch(), 'changeset_id': str(ctx), 'changeset_url': posixpath.join(repourl, str(ctx)), 'commit_msg': description.splitlines()[0], }) add_comment(issues, data, comment) if issues: smtp_host = ui.config('smtp', 'host', default='localhost') smtp_port = int(ui.config('smtp', 'port', 25)) s = smtplib.SMTP(smtp_host, smtp_port) username = ui.config('smtp', 'username', '') if username: password = ui.config('smtp', 'password', '') s.login(username, password) try: send_comments(s, fromaddr, toaddr, issues) ui.status("sent email to roundup at " + toaddr + '\n') except Exception, err: # make sure an issue updating roundup does not prevent an # otherwise successful push. ui.warn("sending email to roundup at %s failed: %s\n" % (toaddr, err))
def graphdata(usetuples, **map): data = [] row = 0 for (id, type, ctx, vtx, edges) in tree: if type != graphmod.CHANGESET: continue node = str(ctx) age = templatefilters.age(ctx.date()) desc = templatefilters.firstline(ctx.description()) desc = cgi.escape(templatefilters.nonempty(desc)) user = cgi.escape(templatefilters.person(ctx.user())) branch = cgi.escape(ctx.branch()) try: branchnode = web.repo.branchtip(branch) except error.RepoLookupError: branchnode = None branch = branch, branchnode == ctx.node() if usetuples: data.append((node, vtx, edges, desc, user, age, branch, [cgi.escape(x) for x in ctx.tags() ], [cgi.escape(x) for x in ctx.bookmarks()])) else: edgedata = [ dict(col=edge[0], nextcol=edge[1], color=(edge[2] - 1) % 6 + 1, width=edge[3], bcolor=edge[4]) for edge in edges ] data.append( dict(node=node, col=vtx[0], color=(vtx[1] - 1) % 6 + 1, edges=edgedata, row=row, nextrow=row + 1, desc=desc, user=user, age=age, bookmarks=webutil.nodebookmarksdict( web.repo, ctx.node()), branches=webutil.nodebranchdict(web.repo, ctx), inbranch=webutil.nodeinbranch(web.repo, ctx), tags=webutil.nodetagsdict(web.repo, ctx.node()))) row += 1 return data
def graphdata(usetuples, **map): data = [] row = 0 for (id, type, ctx, vtx, edges) in tree: if type != graphmod.CHANGESET: continue node = str(ctx) age = templatefilters.age(ctx.date()) desc = templatefilters.firstline(ctx.description()) desc = cgi.escape(templatefilters.nonempty(desc)) user = cgi.escape(templatefilters.person(ctx.user())) branch = cgi.escape(ctx.branch()) try: branchnode = web.repo.branchtip(branch) except error.RepoLookupError: branchnode = None branch = branch, branchnode == ctx.node() if usetuples: data.append((node, vtx, edges, desc, user, age, branch, [cgi.escape(x) for x in ctx.tags()], [cgi.escape(x) for x in ctx.bookmarks()])) else: edgedata = [{'col': edge[0], 'nextcol': edge[1], 'color': (edge[2] - 1) % 6 + 1, 'width': edge[3], 'bcolor': edge[4]} for edge in edges] data.append( {'node': node, 'col': vtx[0], 'color': (vtx[1] - 1) % 6 + 1, 'edges': edgedata, 'row': row, 'nextrow': row + 1, 'desc': desc, 'user': user, 'age': age, 'bookmarks': webutil.nodebookmarksdict( web.repo, ctx.node()), 'branches': webutil.nodebranchdict(web.repo, ctx), 'inbranch': webutil.nodeinbranch(web.repo, ctx), 'tags': webutil.nodetagsdict(web.repo, ctx.node())}) row += 1 return data
def graphdata(usetuples, **map): data = [] row = 0 for (id, type, ctx, vtx, edges) in tree: if type != graphmod.CHANGESET: continue node = str(ctx) age = templatefilters.age(ctx.date()) desc = templatefilters.firstline(ctx.description()) desc = cgi.escape(templatefilters.nonempty(desc)) user = cgi.escape(templatefilters.person(ctx.user())) branch = cgi.escape(ctx.branch()) try: branchnode = web.repo.branchtip(branch) except error.RepoLookupError: branchnode = None branch = branch, branchnode == ctx.node() if usetuples: data.append((node, vtx, edges, desc, user, age, branch, [cgi.escape(x) for x in ctx.tags()], [cgi.escape(x) for x in ctx.bookmarks()])) else: edgedata = [dict(col=edge[0], nextcol=edge[1], color=(edge[2] - 1) % 6 + 1, width=edge[3], bcolor=edge[4]) for edge in edges] data.append( dict(node=node, col=vtx[0], color=(vtx[1] - 1) % 6 + 1, edges=edgedata, row=row, nextrow=row + 1, desc=desc, user=user, age=age, bookmarks=webutil.nodebookmarksdict( web.repo, ctx.node()), branches=webutil.nodebranchdict(web.repo, ctx), inbranch=webutil.nodeinbranch(web.repo, ctx), tags=webutil.nodetagsdict(web.repo, ctx.node()))) row += 1 return data
def commit_factory(self, commit_id): "Make a Commit object holding data for a specified commit ID." from mercurial.node import short from mercurial.templatefilters import person node = self.repository.lookup(commit_id) commit = Commit(self, short(node)) # Extract commit-specific values from a "context" object ctx = self.repository.changectx(node) commit.rev = '%d:%s' % (ctx.rev(), commit.commit) commit.branch = ctx.branch() commit.author = person(ctx.user()) commit.logmsg = ctx.description() # Extract changed files from status against first parent st = self.repository.status(ctx.p1().node(), ctx.node()) commit.files = ' '.join(st[0] + st[1] + st[2]) return commit
def generate(env, ctx): n = ctx.node() ns = short(n) d = env.copy() d["branch"] = ctx.branch() d["author"] = person(ctx.user()) d["rev"] = "%d:%s" % (ctx.rev(), ns) logmsg = filter(None, ctx.description().splitlines()) d["logmsg"] = " ".join(logmsg[:4]) if len(logmsg) > 4: d["logmsg"] += "..." if env["baseurl"]: d["url"] = env["baseurl"].rstrip("/") + "/rev/%s" % ns else: d["url"] = "" d["files"] = getfiles(env, ctx) return json.dumps({"to": env["channels"].split(","), "privmsg": d["template"] % d})
def commit_factory(self, commit_id): "Make a Commit object holding data for a specified commit ID." from mercurial.node import short from mercurial.templatefilters import person node = self.repository.lookup(commit_id) commit = Commit(self, short(node)) # Extract commit-specific values from a "context" object ctx = self.repository.changectx(node) commit.rev = '%d:%s' % (ctx.rev(), commit.commit) commit.branch = ctx.branch() commit.author = person(ctx.user()) commit.author_date = \ datetime.datetime.fromtimestamp(ctx.date()[0]).strftime('%Y-%m-%d %H:%M:%S') commit.logmsg = ctx.description() # Extract changed files from status against first parent st = self.repository.status(ctx.p1().node(), ctx.node()) commit.files = ' '.join(st[0] + st[1] + st[2]) return commit
def graph(web, req, tmpl): rev = webutil.changectx(web.repo, req).rev() bg_height = 39 revcount = 25 if 'revcount' in req.form: revcount = int(req.form.get('revcount', [revcount])[0]) tmpl.defaults['sessionvars']['revcount'] = revcount lessvars = copy.copy(tmpl.defaults['sessionvars']) lessvars['revcount'] = revcount / 2 morevars = copy.copy(tmpl.defaults['sessionvars']) morevars['revcount'] = revcount * 2 max_rev = len(web.repo) - 1 revcount = min(max_rev, revcount) revnode = web.repo.changelog.node(rev) revnode_hex = hex(revnode) uprev = min(max_rev, rev + revcount) downrev = max(0, rev - revcount) count = len(web.repo) changenav = webutil.revnavgen(rev, revcount, count, web.repo.changectx) dag = graphmod.revisions(web.repo, rev, downrev) tree = list(graphmod.colored(dag)) canvasheight = (len(tree) + 1) * bg_height - 27; data = [] for (id, type, ctx, vtx, edges) in tree: if type != graphmod.CHANGESET: continue node = short(ctx.node()) age = templatefilters.age(ctx.date()) desc = templatefilters.firstline(ctx.description()) desc = cgi.escape(templatefilters.nonempty(desc)) user = cgi.escape(templatefilters.person(ctx.user())) branch = ctx.branch() branch = branch, web.repo.branchtags().get(branch) == ctx.node() data.append((node, vtx, edges, desc, user, age, branch, ctx.tags())) return tmpl('graph', rev=rev, revcount=revcount, uprev=uprev, lessvars=lessvars, morevars=morevars, downrev=downrev, canvasheight=canvasheight, jsdata=data, bg_height=bg_height, node=revnode_hex, changenav=changenav)
def collect_data(cl,options): if options.split=='none': fetch_data = fetch_data_any else: fetch_data = globals()['fetch_data_' + options.split] stat_maker = nostat if options.uselines: stat_maker = diffstat # starting with mercurial 1.1, this could be simplified by iterating in cl directly data = {} for i in xrange(options.length): node = cl.read(cl.node(i)) # Check whether the number of changed files == 0 if options.skipmerges and len(node[3]) == 0: continue # Skip merges # find out date and filter date = datetime.fromtimestamp(node[2][0]) if options.datemin!=None and date<options.datemin: continue if options.datemax!=None and date>options.datemax: continue # find out who this is who = node[1] email = util.email(who) if email in options.exclude: continue ctx = options.repo.changectx(i) for k, v in fetch_data(ctx, stat_maker, options): if not data.has_key(k): data[k] = {} data[k][date] = sum(v) # post_collect_data titlemap = {} if options.split=='authors': for who in data.keys(): email = util.email(who) titlemap[email] = person(who) options.titlemap = titlemap return data
def collect_data(cl,options): data = {} namemap = {} if not options.split: data["Overall activity"] = {} localactivity = 1 # starting with mercurial 1.1, this could be simplified by iterating in cl directly for i in xrange(options.length): node = cl.read(cl.node(i)) # Check whether the number of changed files == 0 if options.skipmerges and len(node[3]) == 0: continue # Skip merges # find out date and filter date = datetime.datetime.fromtimestamp(node[2][0]) if options.datemin!=None and date<options.datemin: continue if options.datemax!=None and date>options.datemax: continue # find out who this is who = node[1] email = util.email(who) namemap[email] = person(who) if email in options.exclude: continue if options.uselines: localactivity = changedlines(options.repo, i) if options.split: # data is dictionnary mapping an author name to the data for # this author email = options.amap.get(email, email) # alias remap if not data.has_key(email): data[email] = {} data[email][date] = localactivity else: # data only contains one entry for the global graphic data["Overall activity"][date] = localactivity options.namemap = namemap return data
def fixup_user(user, authors): user = user.strip("\"") if authors != None: # if we have an authors table, try to get mapping # by defaulting to the current value of 'user' user = authors.get(user, user) name, mail, m = '', '', user_re.match(user) if m == None: # if we don't have 'Name <mail>' syntax, extract name # and mail from hg helpers. this seems to work pretty well. # if email doesn't contain @, replace it with devnull@localhost name = templatefilters.person(user) mail = '<%s>' % util.email(user) if '@' not in mail: mail = '<devnull@localhost>' else: # if we have 'Name <mail>' syntax, everything is fine :) name, mail = m.group(1), m.group(2) # remove any silly quoting from username m2 = user_clean_re.match(name) if m2 != None: name = m2.group(1) return '%s %s' % (name, mail)
def get_user(self): node_user = self.node.user() email = util.email(node_user) name = person(node_user) return name
def username(user): author = templatefilters.person(user) if not author: author = util.shortuser(user) return author
def display_user(ctx): return templatefilters.person(ctx.user())
def get_user(self): node_user = self.repository.repo[self.get_node()].user() email = util.email(node_user) name = person(node_user) return name, email