List for cgit developers and users
 help / color / mirror / Atom feed
From: john at keeping.me.uk (John Keeping)
Subject: [PATCH] Add support for git's mailmap.
Date: Wed, 24 Aug 2016 20:23:29 +0100	[thread overview]
Message-ID: <20160824192329.wbiroty26davnidh@john.keeping.me.uk> (raw)
In-Reply-To: <f9aa9389-9bfa-ef3a-ac8b-2f8e127e47e4@bnl.gov>

On Wed, Aug 24, 2016 at 02:28:16PM -0400, Jason A. Smith wrote:
> 
> If a mailmap file is present in the repo, it will be used to coalesce
> commits by the same person, just like git does. When no mailmap file is
> found then it functions as before.

Missing sign-off, see [1] for what this means.

[1] http://developercertificate.org/

> ---
>   cgit.h      |  3 +++
>   parsing.c   | 43 +++++++++++++++++++++++++++++++++++++++++++
>   ui-atom.c   |  9 +++++++--
>   ui-commit.c |  4 ++++
>   ui-log.c    |  4 ++++
>   ui-stats.c  |  8 ++++++--
>   6 files changed, 67 insertions(+), 4 deletions(-)
> 
> diff --git a/cgit.h b/cgit.h
> index 325432b..737ab92 100644
> --- a/cgit.h
> +++ b/cgit.h
> @@ -24,6 +24,7 @@
>  #include <utf8.h>
>  #include <notes.h>
>  #include <graph.h>
> +#include <mailmap.h>
>  
>  /* Add isgraph(x) to Git's sane ctype support (see git-compat-util.h) */
>  #undef isgraph
> @@ -370,6 +371,8 @@ extern char *fmtalloc(const char *format,...);
>  extern struct commitinfo *cgit_parse_commit(struct commit *commit);
>  extern struct taginfo *cgit_parse_tag(struct tag *tag);
>  extern void cgit_parse_url(const char *url);
> +extern int cgit_read_mailmap(struct string_list *map);
> +extern int cgit_map_user(struct string_list *map, char **email, char **name);
>  
>  extern const char *cgit_repobasename(const char *reponame);
>  
> diff --git a/parsing.c b/parsing.c
> index 9dacb16..7e7edd1 100644
> --- a/parsing.c
> +++ b/parsing.c
> @@ -88,6 +88,49 @@ static void parse_user(const char *t, char **name, char **email, unsigned long *
>  	}
>  }
>  
> +/* Cgit surrounds the email address with angle brackets while git does not.
> +   Copy the email address, remove the angle brackets, perform the mailmap
> +   user lookup and finally replace the name and email address with the new
> +   values, and put the angle brackets back around the email. */

/*
 * None of the other multiline comments in this code base
 * look like the one above :-)
 */

> +int cgit_read_mailmap(struct string_list *map)
> +{
> +	int err = 0;
> +
> +	memset(map, 0, sizeof(*map));
> +	err = read_mailmap(map, NULL);
> +	return err;
> +}

Missing blank line here, but I'm not sure why the function above exists.
Other than the memset(), it doesn't do anything except call
read_mailmap() and the memset is definitely wrong, it leaks memory if
string_list is non-empty and if you're worried that map might be
uninitialized then this should be calling string_list_init(), but I'd
argue that it's up to the call site and since you always allocate on the
stack you can just assign STRING_LIST_INIT_NODUP on declaration.

> +int cgit_map_user(struct string_list *map, char **email, char **name)
> +{
> +	char *map_email, *map_name;
> +	size_t emaillen, namelen;
> +	int ret;
> +
> +	emaillen = strlen(*email);
> +	namelen = strlen(*name);
> +	map_email = xmalloc(emaillen + 1);
> +	map_name = xmalloc(namelen + 1);
> +	strncpy(map_email, *email, emaillen + 1);
> +	strncpy(map_name, *name, namelen + 1);
> +	memmove(map_email, map_email+1, emaillen-2);
> +	map_email[emaillen-2] = '\0';
> +	emaillen = emaillen - 2;
> +	ret = map_user(map, (const char**)&map_email, &emaillen,
> +			    (const char**)&map_name, &namelen);
> +	if (ret) {
> +		*email = xmalloc(strlen("<") + emaillen + strlen(">") + 1);
> +		sprintf(*email, "<%.*s>", (int)emaillen, map_email);
> +		*name = xmalloc(namelen + 1);
> +		strncpy(*name, map_name, namelen + 1);
> +	} else {
> +		/* When a mapping is found they are pointing to the
> +		   actual mailmap list entry and cannot be freed. */
> +
> +		free(map_email);
> +		free(map_name);
> +	}
> +
> +	return ret;
> +}

I think we can do a log better than this if we step back and think about
the call sites below.  There's no need to modify the author and
committer fields in struct commitinfo if we first refactor to introduce
local variables:

	const char *author = info->author;
	const char *author_email = info->author_email;

Then we can almost use map_user() directly if not for the pesky angle
brackets, but looking at the use sites, ui-atom.c is already prepared
for the email address not to have them and there are a few HTML sites
that are trivial to change.  The only difficult case is the email filter
and we could create a new cgit_print_ident() function to wrap all of
that up nicely.

It's a bigger change and will turn this single patch into a series, but
the end result will be much cleaner.

>  #ifdef NO_ICONV
>  #define reencode(a, b, c)
>  #else
> diff --git a/ui-atom.c b/ui-atom.c
> index 41838d3..e0d1144 100644
> --- a/ui-atom.c
> +++ b/ui-atom.c
> @@ -11,7 +11,8 @@
>  #include "html.h"
>  #include "ui-shared.h"
>  
> -static void add_entry(struct commit *commit, const char *host)
> +static void add_entry(struct commit *commit, const char *host,
> +		      struct string_list *mailmap)
>  {
>  	char delim = '&';
>  	char *hex;
> @@ -19,6 +20,7 @@ static void add_entry(struct commit *commit, const char *host)
>  	struct commitinfo *info;
>  
>  	info = cgit_parse_commit(commit);
> +	cgit_map_user(mailmap, &(info->author_email), &(info->author));
>  	hex = oid_to_hex(&commit->object.oid);
>  	html("<entry>\n");
>  	html("<title>");
> @@ -89,6 +91,7 @@ void cgit_print_atom(char *tip, char *path, int max_count)
>  	const char *argv[] = {NULL, tip, NULL, NULL, NULL};
>  	struct commit *commit;
>  	struct rev_info rev;
> +	struct string_list mailmap;
>  	int argc = 2;
>  
>  	if (ctx.qry.show_all)
> @@ -108,6 +111,8 @@ void cgit_print_atom(char *tip, char *path, int max_count)
>  	rev.show_root_diff = 0;
>  	rev.max_count = max_count;
>  	setup_revisions(argc, argv, &rev, NULL);
> +	cgit_read_mailmap(&mailmap);
> +	rev.mailmap = &mailmap;
>  	prepare_revision_walk(&rev);
>  
>  	host = cgit_hosturl();
> @@ -139,7 +144,7 @@ void cgit_print_atom(char *tip, char *path, int max_count)
>  		free(repourl);
>  	}
>  	while ((commit = get_revision(&rev)) != NULL) {
> -		add_entry(commit, host);
> +		add_entry(commit, host, &mailmap);
>  		free_commit_buffer(commit);
>  		free_commit_list(commit->parents);
>  		commit->parents = NULL;
> diff --git a/ui-commit.c b/ui-commit.c
> index 099d294..27d5931 100644
> --- a/ui-commit.c
> +++ b/ui-commit.c
> @@ -18,6 +18,7 @@ void cgit_print_commit(char *hex, const char *prefix)
>  	struct commit *commit, *parent;
>  	struct commitinfo *info, *parent_info;
>  	struct commit_list *p;
> +	struct string_list mailmap;
>  	struct strbuf notes = STRBUF_INIT;
>  	unsigned char sha1[20];
>  	char *tmp, *tmp2;
> @@ -38,6 +39,9 @@ void cgit_print_commit(char *hex, const char *prefix)
>  		return;
>  	}
>  	info = cgit_parse_commit(commit);
> +	cgit_read_mailmap(&mailmap);
> +	cgit_map_user(&mailmap, &(info->author_email), &(info->author));
> +	cgit_map_user(&mailmap, &(info->committer_email), &(info->committer));
>  
>  	format_display_notes(sha1, &notes, PAGE_ENCODING, 0);
>  
> diff --git a/ui-log.c b/ui-log.c
> index c97b8e0..90fe5f8 100644
> --- a/ui-log.c
> +++ b/ui-log.c
> @@ -238,6 +238,7 @@ static void print_commit(struct commit *commit, struct rev_info *revs)
>  			 oid_to_hex(&commit->object.oid), ctx.qry.vpath);
>  	show_commit_decorations(commit);
>  	html("</td><td>");
> +	cgit_map_user(revs->mailmap, &(info->author_email), &(info->author));
>  	cgit_open_filter(ctx.repo->email_filter, info->author_email, "log");
>  	html_txt(info->author);
>  	cgit_close_filter(ctx.repo->email_filter);
> @@ -360,6 +361,7 @@ static char *next_token(char **src)
>  void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *pattern,
>  		    char *path, int pager, int commit_graph, int commit_sort)
>  {
> +	struct string_list mailmap;
>  	struct rev_info rev;
>  	struct commit *commit;
>  	struct argv_array rev_argv = ARGV_ARRAY_INIT;
> @@ -443,6 +445,8 @@ void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *pattern
>  		DIFF_XDL_SET(&rev.diffopt, IGNORE_WHITESPACE);
>  
>  	compile_grep_patterns(&rev.grep_filter);
> +	cgit_read_mailmap(&mailmap);
> +	rev.mailmap = &mailmap;
>  	prepare_revision_walk(&rev);
>  
>  	if (pager) {
> diff --git a/ui-stats.c b/ui-stats.c
> index 7acd358..9ca55f6 100644
> --- a/ui-stats.c
> +++ b/ui-stats.c
> @@ -159,7 +159,7 @@ const char *cgit_find_stats_periodname(int idx)
>  }
>  
>  static void add_commit(struct string_list *authors, struct commit *commit,
> -	const struct cgit_period *period)
> +	const struct cgit_period *period, struct string_list *mailmap)
>  {
>  	struct commitinfo *info;
>  	struct string_list_item *author, *item;
> @@ -171,6 +171,7 @@ static void add_commit(struct string_list *authors, struct commit *commit,
>  	uintptr_t *counter;
>  
>  	info = cgit_parse_commit(commit);
> +	cgit_map_user(mailmap, &(info->author_email), &(info->author));
>  	tmp = xstrdup(info->author);
>  	author = string_list_insert(authors, tmp);
>  	if (!author->util)
> @@ -209,6 +210,7 @@ static int cmp_total_commits(const void *a1, const void *a2)
>  static struct string_list collect_stats(const struct cgit_period *period)
>  {
>  	struct string_list authors;
> +	struct string_list mailmap;
>  	struct rev_info rev;
>  	struct commit *commit;
>  	const char *argv[] = {NULL, ctx.qry.head, NULL, NULL, NULL, NULL};
> @@ -237,10 +239,12 @@ static struct string_list collect_stats(const struct cgit_period *period)
>  	rev.verbose_header = 1;
>  	rev.show_root_diff = 0;
>  	setup_revisions(argc, argv, &rev, NULL);
> +	cgit_read_mailmap(&mailmap);
> +	rev.mailmap = &mailmap;
>  	prepare_revision_walk(&rev);
>  	memset(&authors, 0, sizeof(authors));
>  	while ((commit = get_revision(&rev)) != NULL) {
> -		add_commit(&authors, commit, period);
> +		add_commit(&authors, commit, period, &mailmap);
>  		free_commit_buffer(commit);
>  		free_commit_list(commit->parents);
>  		commit->parents = NULL;
> 

> _______________________________________________
> CGit mailing list
> CGit at lists.zx2c4.com
> http://lists.zx2c4.com/mailman/listinfo/cgit



  reply	other threads:[~2016-08-24 19:23 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-08-24 18:28 smithj4
2016-08-24 19:23 ` john [this message]
2016-08-24 21:27   ` smithj4
2016-08-24 21:38     ` smithj4
2016-08-24 22:30     ` john
2016-08-25 19:22       ` smithj4
2016-08-25 20:12         ` john
2016-08-25 20:13           ` [PATCH 1/2] filter: introduce cgit_open_email_filter() wrapper john
2016-08-25 20:13             ` [PATCH 2/2] Remove angle brackets from {author,committer}_email john
2016-08-26 20:11           ` [PATCH] Add support for git's mailmap smithj4

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20160824192329.wbiroty26davnidh@john.keeping.me.uk \
    --to=cgit@lists.zx2c4.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).