mirror of
https://github.com/git/git
synced 2024-08-26 19:19:18 +00:00
Merge branch 'jk/mailmap-from-blob'
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
This commit is contained in:
commit
3a3100a889
|
@ -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].
|
||||
|
|
|
@ -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::
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
1
cache.h
1
cache.h
|
@ -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 *);
|
||||
|
|
2
config.c
2
config.c
|
@ -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;
|
||||
|
|
90
mailmap.c
90
mailmap.c
|
@ -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;
|
||||
|
@ -129,14 +130,9 @@ 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 buffer[1024];
|
||||
FILE *f = (filename == NULL ? NULL : fopen(filename, "r"));
|
||||
|
||||
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:";
|
||||
|
@ -144,7 +140,7 @@ static int read_single_mailmap(struct string_list *map, const char *filename, ch
|
|||
int len = strlen(buffer);
|
||||
|
||||
if (!repo_abbrev)
|
||||
continue;
|
||||
return;
|
||||
|
||||
if (len && buffer[len - 1] == '\n')
|
||||
buffer[--len] = 0;
|
||||
|
@ -159,7 +155,7 @@ static int read_single_mailmap(struct string_list *map, const char *filename, ch
|
|||
; /* nothing */
|
||||
strcpy(*repo_abbrev, cp);
|
||||
}
|
||||
continue;
|
||||
return;
|
||||
}
|
||||
if ((name2 = parse_name_and_email(buffer, &name1, &email1, 0)) != NULL)
|
||||
parse_name_and_email(name2, &name2, &email2, 1);
|
||||
|
@ -167,16 +163,86 @@ static int read_single_mailmap(struct string_list *map, const char *filename, ch
|
|||
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;
|
||||
|
||||
if (!filename)
|
||||
return 0;
|
||||
|
||||
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)
|
||||
|
|
|
@ -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 <bugs@company.xx>
|
||||
EOF
|
||||
cat >both <<-\EOF &&
|
||||
Blob Guy <author@example.com>
|
||||
Blob Guy <bugs@company.xx>
|
||||
EOF
|
||||
git add just-bugs both &&
|
||||
git commit -m "my mailmaps" &&
|
||||
echo "Repo Guy <author@example.com>" >.mailmap &&
|
||||
echo "Internal Guy <author@example.com>" >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 <author@example.com>" &&
|
||||
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 <author@example.com> (1):
|
||||
|
|
Loading…
Reference in a new issue