Skip to content

Commit

Permalink
Merge branch 'jk/mailmap-from-blob'
Browse files Browse the repository at this point in the history
Allow us to read, and default to read, mailmap files from the tip
of the history in bare repositories.  This will help running tools
like shortlog in server settings.

* jk/mailmap-from-blob:
  mailmap: default mailmap.blob in bare repositories
  mailmap: fix some documentation loose-ends for mailmap.blob
  mailmap: clean up read_mailmap error handling
  mailmap: support reading mailmap from blobs
  mailmap: refactor mailmap parsing for non-file sources
  • Loading branch information
gitster committed Jan 6, 2013
2 parents 245d6d0 + 8c473ce commit 3a3100a
Show file tree
Hide file tree
Showing 7 changed files with 214 additions and 38 deletions.
8 changes: 8 additions & 0 deletions Documentation/config.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1521,6 +1521,14 @@ mailmap.file::
subdirectory, or somewhere outside of the repository itself.
See linkgit:git-shortlog[1] and linkgit:git-blame[1].

mailmap.blob::
Like `mailmap.file`, but consider the value as a reference to a
blob in the repository. If both `mailmap.file` and
`mailmap.blob` are given, both are parsed, with entries from
`mailmap.file` taking precedence. In a bare repository, this
defaults to `HEAD:.mailmap`. In a non-bare repository, it
defaults to empty.

man.viewer::
Specify the programs that may be used to display help in the
'man' format. See linkgit:git-help[1].
Expand Down
2 changes: 1 addition & 1 deletion Documentation/git-log.txt
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ log.showroot::
`git log -p` output would be shown without a diff attached.
The default is `true`.

mailmap.file::
mailmap.*::
See linkgit:git-shortlog[1].

notes.displayRef::
Expand Down
3 changes: 2 additions & 1 deletion Documentation/mailmap.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
If the file `.mailmap` exists at the toplevel of the repository, or at
the location pointed to by the mailmap.file configuration option, it
the location pointed to by the mailmap.file or mailmap.blob
configuration options, it
is used to map author and committer names and email addresses to
canonical real names and email addresses.

Expand Down
1 change: 1 addition & 0 deletions cache.h
Original file line number Diff line number Diff line change
Expand Up @@ -1164,6 +1164,7 @@ extern int author_ident_sufficiently_given(void);
extern const char *git_commit_encoding;
extern const char *git_log_output_encoding;
extern const char *git_mailmap_file;
extern const char *git_mailmap_blob;

/* IO helper functions */
extern void maybe_flush_or_die(FILE *, const char *);
Expand Down
2 changes: 2 additions & 0 deletions config.c
Original file line number Diff line number Diff line change
Expand Up @@ -839,6 +839,8 @@ static int git_default_mailmap_config(const char *var, const char *value)
{
if (!strcmp(var, "mailmap.file"))
return git_config_string(&git_mailmap_file, var, value);
if (!strcmp(var, "mailmap.blob"))
return git_config_string(&git_mailmap_blob, var, value);

/* Add other config variables here and to Documentation/config.txt. */
return 0;
Expand Down
138 changes: 102 additions & 36 deletions mailmap.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ static inline void debug_mm(const char *format, ...) {}
#endif

const char *git_mailmap_file;
const char *git_mailmap_blob;

struct mailmap_info {
char *name;
Expand Down Expand Up @@ -129,54 +130,119 @@ static char *parse_name_and_email(char *buffer, char **name,
return (*right == '\0' ? NULL : right);
}

static int read_single_mailmap(struct string_list *map, const char *filename, char **repo_abbrev)
static void read_mailmap_line(struct string_list *map, char *buffer,
char **repo_abbrev)
{
char *name1 = NULL, *email1 = NULL, *name2 = NULL, *email2 = NULL;
if (buffer[0] == '#') {
static const char abbrev[] = "# repo-abbrev:";
int abblen = sizeof(abbrev) - 1;
int len = strlen(buffer);

if (!repo_abbrev)
return;

if (len && buffer[len - 1] == '\n')
buffer[--len] = 0;
if (!strncmp(buffer, abbrev, abblen)) {
char *cp;

if (repo_abbrev)
free(*repo_abbrev);
*repo_abbrev = xmalloc(len);

for (cp = buffer + abblen; isspace(*cp); cp++)
; /* nothing */
strcpy(*repo_abbrev, cp);
}
return;
}
if ((name2 = parse_name_and_email(buffer, &name1, &email1, 0)) != NULL)
parse_name_and_email(name2, &name2, &email2, 1);

if (email1)
add_mapping(map, name1, email1, name2, email2);
}

static int read_mailmap_file(struct string_list *map, const char *filename,
char **repo_abbrev)
{
char buffer[1024];
FILE *f = (filename == NULL ? NULL : fopen(filename, "r"));
FILE *f;

if (f == NULL)
return 1;
while (fgets(buffer, sizeof(buffer), f) != NULL) {
char *name1 = NULL, *email1 = NULL, *name2 = NULL, *email2 = NULL;
if (buffer[0] == '#') {
static const char abbrev[] = "# repo-abbrev:";
int abblen = sizeof(abbrev) - 1;
int len = strlen(buffer);

if (!repo_abbrev)
continue;

if (len && buffer[len - 1] == '\n')
buffer[--len] = 0;
if (!strncmp(buffer, abbrev, abblen)) {
char *cp;

if (repo_abbrev)
free(*repo_abbrev);
*repo_abbrev = xmalloc(len);

for (cp = buffer + abblen; isspace(*cp); cp++)
; /* nothing */
strcpy(*repo_abbrev, cp);
}
continue;
}
if ((name2 = parse_name_and_email(buffer, &name1, &email1, 0)) != NULL)
parse_name_and_email(name2, &name2, &email2, 1);
if (!filename)
return 0;

if (email1)
add_mapping(map, name1, email1, name2, email2);
f = fopen(filename, "r");
if (!f) {
if (errno == ENOENT)
return 0;
return error("unable to open mailmap at %s: %s",
filename, strerror(errno));
}

while (fgets(buffer, sizeof(buffer), f) != NULL)
read_mailmap_line(map, buffer, repo_abbrev);
fclose(f);
return 0;
}

static void read_mailmap_buf(struct string_list *map,
const char *buf, unsigned long len,
char **repo_abbrev)
{
while (len) {
const char *end = strchrnul(buf, '\n');
unsigned long linelen = end - buf + 1;
char *line = xmemdupz(buf, linelen);

read_mailmap_line(map, line, repo_abbrev);

free(line);
buf += linelen;
len -= linelen;
}
}

static int read_mailmap_blob(struct string_list *map,
const char *name,
char **repo_abbrev)
{
unsigned char sha1[20];
char *buf;
unsigned long size;
enum object_type type;

if (!name)
return 0;
if (get_sha1(name, sha1) < 0)
return 0;

buf = read_sha1_file(sha1, &type, &size);
if (!buf)
return error("unable to read mailmap object at %s", name);
if (type != OBJ_BLOB)
return error("mailmap is not a blob: %s", name);

read_mailmap_buf(map, buf, size, repo_abbrev);

free(buf);
return 0;
}

int read_mailmap(struct string_list *map, char **repo_abbrev)
{
int err = 0;

map->strdup_strings = 1;
/* each failure returns 1, so >1 means both calls failed */
return read_single_mailmap(map, ".mailmap", repo_abbrev) +
read_single_mailmap(map, git_mailmap_file, repo_abbrev) > 1;

if (!git_mailmap_blob && is_bare_repository())
git_mailmap_blob = "HEAD:.mailmap";

err |= read_mailmap_file(map, ".mailmap", repo_abbrev);
err |= read_mailmap_blob(map, git_mailmap_blob, repo_abbrev);
err |= read_mailmap_file(map, git_mailmap_file, repo_abbrev);
return err;
}

void clear_mailmap(struct string_list *map)
Expand Down
98 changes: 98 additions & 0 deletions t/t4203-mailmap.sh
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,104 @@ test_expect_success 'No mailmap files, but configured' '
test_cmp expect actual
'

test_expect_success 'setup mailmap blob tests' '
git checkout -b map &&
test_when_finished "git checkout master" &&
cat >just-bugs <<-\EOF &&
Blob Guy <[email protected]>
EOF
cat >both <<-\EOF &&
Blob Guy <[email protected]>
Blob Guy <[email protected]>
EOF
git add just-bugs both &&
git commit -m "my mailmaps" &&
echo "Repo Guy <[email protected]>" >.mailmap &&
echo "Internal Guy <[email protected]>" >internal.map
'

test_expect_success 'mailmap.blob set' '
cat >expect <<-\EOF &&
Blob Guy (1):
second
Repo Guy (1):
initial
EOF
git -c mailmap.blob=map:just-bugs shortlog HEAD >actual &&
test_cmp expect actual
'

test_expect_success 'mailmap.blob overrides .mailmap' '
cat >expect <<-\EOF &&
Blob Guy (2):
initial
second
EOF
git -c mailmap.blob=map:both shortlog HEAD >actual &&
test_cmp expect actual
'

test_expect_success 'mailmap.file overrides mailmap.blob' '
cat >expect <<-\EOF &&
Blob Guy (1):
second
Internal Guy (1):
initial
EOF
git \
-c mailmap.blob=map:both \
-c mailmap.file=internal.map \
shortlog HEAD >actual &&
test_cmp expect actual
'

test_expect_success 'mailmap.blob can be missing' '
cat >expect <<-\EOF &&
Repo Guy (1):
initial
nick1 (1):
second
EOF
git -c mailmap.blob=map:nonexistent shortlog HEAD >actual &&
test_cmp expect actual
'

test_expect_success 'mailmap.blob defaults to off in non-bare repo' '
git init non-bare &&
(
cd non-bare &&
test_commit one .mailmap "Fake Name <[email protected]>" &&
echo " 1 Fake Name" >expect &&
git shortlog -ns HEAD >actual &&
test_cmp expect actual &&
rm .mailmap &&
echo " 1 A U Thor" >expect &&
git shortlog -ns HEAD >actual &&
test_cmp expect actual
)
'

test_expect_success 'mailmap.blob defaults to HEAD:.mailmap in bare repo' '
git clone --bare non-bare bare &&
(
cd bare &&
echo " 1 Fake Name" >expect &&
git shortlog -ns HEAD >actual &&
test_cmp expect actual
)
'

test_expect_success 'cleanup after mailmap.blob tests' '
rm -f .mailmap
'

# Extended mailmap configurations should give us the following output for shortlog
cat >expect <<\EOF
A U Thor <[email protected]> (1):
Expand Down

0 comments on commit 3a3100a

Please sign in to comment.