summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJunio C Hamano <gitster@pobox.com>2022-08-03 20:36:08 (GMT)
committerJunio C Hamano <gitster@pobox.com>2022-08-03 20:36:08 (GMT)
commit87098a047be46ee69da056336109eee2139c1398 (patch)
tree6153173514050f41a47867414dc972894c36747c
parent8e56affcb52e2cf344437a892356a419985ea4ba (diff)
parentec031da9f97a2545601304b5ac1e93fee09425b4 (diff)
downloadgit-87098a047be46ee69da056336109eee2139c1398.zip
git-87098a047be46ee69da056336109eee2139c1398.tar.gz
git-87098a047be46ee69da056336109eee2139c1398.tar.bz2
Merge branch 'sa/cat-file-mailmap'
"git cat-file" learned an option to use the mailmap when showing commit and tag objects. * sa/cat-file-mailmap: cat-file: add mailmap support ident: rename commit_rewrite_person() to apply_mailmap_to_header() ident: move commit_rewrite_person() to ident.c revision: improve commit_rewrite_person()
-rw-r--r--Documentation/git-cat-file.txt6
-rw-r--r--builtin/cat-file.c43
-rw-r--r--cache.h6
-rw-r--r--ident.c74
-rw-r--r--revision.c50
-rwxr-xr-xt/t4203-mailmap.sh59
6 files changed, 190 insertions, 48 deletions
diff --git a/Documentation/git-cat-file.txt b/Documentation/git-cat-file.txt
index 24a811f..1880e9b 100644
--- a/Documentation/git-cat-file.txt
+++ b/Documentation/git-cat-file.txt
@@ -63,6 +63,12 @@ OPTIONS
or to ask for a "blob" with `<object>` being a tag object that
points at it.
+--[no-]mailmap::
+--[no-]use-mailmap::
+ Use mailmap file to map author, committer and tagger names
+ and email addresses to canonical real names and email addresses.
+ See linkgit:git-shortlog[1].
+
--textconv::
Show the content as transformed by a textconv filter. In this case,
`<object>` has to be of the form `<tree-ish>:<path>`, or `:<path>` in
diff --git a/builtin/cat-file.c b/builtin/cat-file.c
index f42782e..cbccb55 100644
--- a/builtin/cat-file.c
+++ b/builtin/cat-file.c
@@ -16,6 +16,7 @@
#include "packfile.h"
#include "object-store.h"
#include "promisor-remote.h"
+#include "mailmap.h"
enum batch_mode {
BATCH_MODE_CONTENTS,
@@ -36,6 +37,22 @@ struct batch_options {
static const char *force_path;
+static struct string_list mailmap = STRING_LIST_INIT_NODUP;
+static int use_mailmap;
+
+static char *replace_idents_using_mailmap(char *, size_t *);
+
+static char *replace_idents_using_mailmap(char *object_buf, size_t *size)
+{
+ struct strbuf sb = STRBUF_INIT;
+ const char *headers[] = { "author ", "committer ", "tagger ", NULL };
+
+ strbuf_attach(&sb, object_buf, *size, *size + 1);
+ apply_mailmap_to_header(&sb, headers, &mailmap);
+ *size = sb.len;
+ return strbuf_detach(&sb, NULL);
+}
+
static int filter_object(const char *path, unsigned mode,
const struct object_id *oid,
char **buf, unsigned long *size)
@@ -160,6 +177,12 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
if (!buf)
die("Cannot read object %s", obj_name);
+ if (use_mailmap) {
+ size_t s = size;
+ buf = replace_idents_using_mailmap(buf, &s);
+ size = cast_size_t_to_ulong(s);
+ }
+
/* otherwise just spit out the data */
break;
@@ -193,6 +216,12 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
}
buf = read_object_with_reference(the_repository, &oid,
exp_type_id, &size, NULL);
+
+ if (use_mailmap) {
+ size_t s = size;
+ buf = replace_idents_using_mailmap(buf, &s);
+ size = cast_size_t_to_ulong(s);
+ }
break;
}
default:
@@ -360,11 +389,18 @@ static void print_object_or_die(struct batch_options *opt, struct expand_data *d
void *contents;
contents = read_object_file(oid, &type, &size);
+
+ if (use_mailmap) {
+ size_t s = size;
+ contents = replace_idents_using_mailmap(contents, &s);
+ size = cast_size_t_to_ulong(s);
+ }
+
if (!contents)
die("object %s disappeared", oid_to_hex(oid));
if (type != data->type)
die("object %s changed type!?", oid_to_hex(oid));
- if (data->info.sizep && size != data->size)
+ if (data->info.sizep && size != data->size && !use_mailmap)
die("object %s changed size!?", oid_to_hex(oid));
batch_write(opt, contents, size);
@@ -856,6 +892,8 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix)
OPT_CMDMODE('s', NULL, &opt, N_("show object size"), 's'),
OPT_BOOL(0, "allow-unknown-type", &unknown_type,
N_("allow -s and -t to work with broken/corrupt objects")),
+ OPT_BOOL(0, "use-mailmap", &use_mailmap, N_("use mail map file")),
+ OPT_ALIAS(0, "mailmap", "use-mailmap"),
/* Batch mode */
OPT_GROUP(N_("Batch objects requested on stdin (or --batch-all-objects)")),
OPT_CALLBACK_F(0, "batch", &batch, N_("format"),
@@ -898,6 +936,9 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix)
opt_cw = (opt == 'c' || opt == 'w');
opt_epts = (opt == 'e' || opt == 'p' || opt == 't' || opt == 's');
+ if (use_mailmap)
+ read_mailmap(&mailmap);
+
/* --batch-all-objects? */
if (opt == 'b')
batch.all_objects = 1;
diff --git a/cache.h b/cache.h
index ac5ab4e..4aa1bd0 100644
--- a/cache.h
+++ b/cache.h
@@ -1689,6 +1689,12 @@ struct ident_split {
int split_ident_line(struct ident_split *, const char *, int);
/*
+ * Given a commit or tag object buffer and the commit or tag headers, replaces
+ * the idents in the headers with their canonical versions using the mailmap mechanism.
+ */
+void apply_mailmap_to_header(struct strbuf *, const char **, struct string_list *);
+
+/*
* Compare split idents for equality or strict ordering. Note that we
* compare only the ident part of the line, ignoring any timestamp.
*
diff --git a/ident.c b/ident.c
index 89ca5b4..7f66bed 100644
--- a/ident.c
+++ b/ident.c
@@ -8,6 +8,7 @@
#include "cache.h"
#include "config.h"
#include "date.h"
+#include "mailmap.h"
static struct strbuf git_default_name = STRBUF_INIT;
static struct strbuf git_default_email = STRBUF_INIT;
@@ -346,6 +347,79 @@ person_only:
return 0;
}
+/*
+ * Returns the difference between the new and old length of the ident line.
+ */
+static ssize_t rewrite_ident_line(const char *person, size_t len,
+ struct strbuf *buf,
+ struct string_list *mailmap)
+{
+ size_t namelen, maillen;
+ const char *name;
+ const char *mail;
+ struct ident_split ident;
+
+ if (split_ident_line(&ident, person, len))
+ return 0;
+
+ mail = ident.mail_begin;
+ maillen = ident.mail_end - ident.mail_begin;
+ name = ident.name_begin;
+ namelen = ident.name_end - ident.name_begin;
+
+ if (map_user(mailmap, &mail, &maillen, &name, &namelen)) {
+ struct strbuf namemail = STRBUF_INIT;
+ size_t newlen;
+
+ strbuf_addf(&namemail, "%.*s <%.*s>",
+ (int)namelen, name, (int)maillen, mail);
+
+ strbuf_splice(buf, ident.name_begin - buf->buf,
+ ident.mail_end - ident.name_begin + 1,
+ namemail.buf, namemail.len);
+ newlen = namemail.len;
+
+ strbuf_release(&namemail);
+
+ return newlen - (ident.mail_end - ident.name_begin);
+ }
+
+ return 0;
+}
+
+void apply_mailmap_to_header(struct strbuf *buf, const char **header,
+ struct string_list *mailmap)
+{
+ size_t buf_offset = 0;
+
+ if (!mailmap)
+ return;
+
+ for (;;) {
+ const char *person, *line;
+ size_t i;
+ int found_header = 0;
+
+ line = buf->buf + buf_offset;
+ if (!*line || *line == '\n')
+ return; /* End of headers */
+
+ for (i = 0; header[i]; i++)
+ if (skip_prefix(line, header[i], &person)) {
+ const char *endp = strchrnul(person, '\n');
+ found_header = 1;
+ buf_offset += endp - line;
+ buf_offset += rewrite_ident_line(person, endp - person, buf, mailmap);
+ break;
+ }
+
+ if (!found_header) {
+ buf_offset = strchrnul(line, '\n') - buf->buf;
+ if (buf->buf[buf_offset] == '\n')
+ buf_offset++;
+ }
+ }
+}
static void ident_env_hint(enum want_ident whose_ident)
{
diff --git a/revision.c b/revision.c
index 0c6e26c..87f1c11 100644
--- a/revision.c
+++ b/revision.c
@@ -3791,51 +3791,6 @@ int rewrite_parents(struct rev_info *revs, struct commit *commit,
return 0;
}
-static int commit_rewrite_person(struct strbuf *buf, const char *what, struct string_list *mailmap)
-{
- char *person, *endp;
- size_t len, namelen, maillen;
- const char *name;
- const char *mail;
- struct ident_split ident;
-
- person = strstr(buf->buf, what);
- if (!person)
- return 0;
-
- person += strlen(what);
- endp = strchr(person, '\n');
- if (!endp)
- return 0;
-
- len = endp - person;
-
- if (split_ident_line(&ident, person, len))
- return 0;
-
- mail = ident.mail_begin;
- maillen = ident.mail_end - ident.mail_begin;
- name = ident.name_begin;
- namelen = ident.name_end - ident.name_begin;
-
- if (map_user(mailmap, &mail, &maillen, &name, &namelen)) {
- struct strbuf namemail = STRBUF_INIT;
-
- strbuf_addf(&namemail, "%.*s <%.*s>",
- (int)namelen, name, (int)maillen, mail);
-
- strbuf_splice(buf, ident.name_begin - buf->buf,
- ident.mail_end - ident.name_begin + 1,
- namemail.buf, namemail.len);
-
- strbuf_release(&namemail);
-
- return 1;
- }
-
- return 0;
-}
-
static int commit_match(struct commit *commit, struct rev_info *opt)
{
int retval;
@@ -3868,11 +3823,12 @@ static int commit_match(struct commit *commit, struct rev_info *opt)
strbuf_addstr(&buf, message);
if (opt->grep_filter.header_list && opt->mailmap) {
+ const char *commit_headers[] = { "author ", "committer ", NULL };
+
if (!buf.len)
strbuf_addstr(&buf, message);
- commit_rewrite_person(&buf, "\nauthor ", opt->mailmap);
- commit_rewrite_person(&buf, "\ncommitter ", opt->mailmap);
+ apply_mailmap_to_header(&buf, commit_headers, opt->mailmap);
}
/* Append "fake" message parts as needed */
diff --git a/t/t4203-mailmap.sh b/t/t4203-mailmap.sh
index 0b2d21e..cd1cab3 100755
--- a/t/t4203-mailmap.sh
+++ b/t/t4203-mailmap.sh
@@ -963,4 +963,63 @@ test_expect_success SYMLINKS 'symlinks not respected in-tree' '
test_cmp expect actual
'
+test_expect_success 'prepare for cat-file --mailmap' '
+ rm -f .mailmap &&
+ git commit --allow-empty -m foo --author="Orig <orig@example.com>"
+'
+
+test_expect_success '--no-use-mailmap disables mailmap in cat-file' '
+ test_when_finished "rm .mailmap" &&
+ cat >.mailmap <<-EOF &&
+ A U Thor <author@example.com> Orig <orig@example.com>
+ EOF
+ cat >expect <<-EOF &&
+ author Orig <orig@example.com>
+ EOF
+ git cat-file --no-use-mailmap commit HEAD >log &&
+ sed -n "/^author /s/\([^>]*>\).*/\1/p" log >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '--use-mailmap enables mailmap in cat-file' '
+ test_when_finished "rm .mailmap" &&
+ cat >.mailmap <<-EOF &&
+ A U Thor <author@example.com> Orig <orig@example.com>
+ EOF
+ cat >expect <<-EOF &&
+ author A U Thor <author@example.com>
+ EOF
+ git cat-file --use-mailmap commit HEAD >log &&
+ sed -n "/^author /s/\([^>]*>\).*/\1/p" log >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '--no-mailmap disables mailmap in cat-file for annotated tag objects' '
+ test_when_finished "rm .mailmap" &&
+ cat >.mailmap <<-EOF &&
+ Orig <orig@example.com> C O Mitter <committer@example.com>
+ EOF
+ cat >expect <<-EOF &&
+ tagger C O Mitter <committer@example.com>
+ EOF
+ git tag -a -m "annotated tag" v1 &&
+ git cat-file --no-mailmap -p v1 >log &&
+ sed -n "/^tagger /s/\([^>]*>\).*/\1/p" log >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '--mailmap enables mailmap in cat-file for annotated tag objects' '
+ test_when_finished "rm .mailmap" &&
+ cat >.mailmap <<-EOF &&
+ Orig <orig@example.com> C O Mitter <committer@example.com>
+ EOF
+ cat >expect <<-EOF &&
+ tagger Orig <orig@example.com>
+ EOF
+ git tag -a -m "annotated tag" v2 &&
+ git cat-file --mailmap -p v2 >log &&
+ sed -n "/^tagger /s/\([^>]*>\).*/\1/p" log >actual &&
+ test_cmp expect actual
+'
+
test_done