Skip to content

Commit 96882f2

Browse files
authored
Merge pull request libgit2#4586 from emilio/mailmap
Add mailmap support.
2 parents 0ecf0e3 + f98131b commit 96882f2

File tree

63 files changed

+1236
-21
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

63 files changed

+1236
-21
lines changed

include/git2.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include "git2/ignore.h"
3030
#include "git2/index.h"
3131
#include "git2/indexer.h"
32+
#include "git2/mailmap.h"
3233
#include "git2/merge.h"
3334
#include "git2/message.h"
3435
#include "git2/net.h"

include/git2/blame.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@ typedef enum {
4343
/** Restrict the search of commits to those reachable following only the
4444
* first parents. */
4545
GIT_BLAME_FIRST_PARENT = (1<<4),
46+
/** Use mailmap file to map author and committer names and email addresses
47+
* to canonical real names and email addresses. The mailmap will be read
48+
* from the working directory, or HEAD in a bare repository. */
49+
GIT_BLAME_USE_MAILMAP = (1<<5),
4650
} git_blame_flag_t;
4751

4852
/**
@@ -108,6 +112,9 @@ GIT_EXTERN(int) git_blame_init_options(
108112
* changed.
109113
* - `final_start_line_number` is the 1-based line number where this hunk
110114
* begins, in the final version of the file
115+
* - `final_signature` is the author of `final_commit_id`. If
116+
* `GIT_BLAME_USE_MAILMAP` has been specified, it will contain the canonical
117+
* real name and email address.
111118
* - `orig_commit_id` is the OID of the commit where this hunk was found. This
112119
* will usually be the same as `final_commit_id`, except when
113120
* `GIT_BLAME_TRACK_COPIES_ANY_COMMIT_COPIES` has been specified.
@@ -116,6 +123,9 @@ GIT_EXTERN(int) git_blame_init_options(
116123
* - `orig_start_line_number` is the 1-based line number where this hunk begins
117124
* in the file named by `orig_path` in the commit specified by
118125
* `orig_commit_id`.
126+
* - `orig_signature` is the author of `orig_commit_id`. If
127+
* `GIT_BLAME_USE_MAILMAP` has been specified, it will contain the canonical
128+
* real name and email address.
119129
* - `boundary` is 1 iff the hunk has been tracked to a boundary commit (the
120130
* root, or the commit specified in git_blame_options.oldest_commit)
121131
*/

include/git2/commit.h

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,34 @@ GIT_EXTERN(const git_signature *) git_commit_committer(const git_commit *commit)
172172
*/
173173
GIT_EXTERN(const git_signature *) git_commit_author(const git_commit *commit);
174174

175+
/**
176+
* Get the committer of a commit, using the mailmap to map names and email
177+
* addresses to canonical real names and email addresses.
178+
*
179+
* Call `git_signature_free` to free the signature.
180+
*
181+
* @param out a pointer to store the resolved signature.
182+
* @param commit a previously loaded commit.
183+
* @param mailmap the mailmap to resolve with. (may be NULL)
184+
* @return 0 or an error code
185+
*/
186+
GIT_EXTERN(int) git_commit_committer_with_mailmap(
187+
git_signature **out, const git_commit *commit, const git_mailmap *mailmap);
188+
189+
/**
190+
* Get the author of a commit, using the mailmap to map names and email
191+
* addresses to canonical real names and email addresses.
192+
*
193+
* Call `git_signature_free` to free the signature.
194+
*
195+
* @param out a pointer to store the resolved signature.
196+
* @param commit a previously loaded commit.
197+
* @param mailmap the mailmap to resolve with. (may be NULL)
198+
* @return 0 or an error code
199+
*/
200+
GIT_EXTERN(int) git_commit_author_with_mailmap(
201+
git_signature **out, const git_commit *commit, const git_mailmap *mailmap);
202+
175203
/**
176204
* Get the full raw text of the commit header.
177205
*

include/git2/mailmap.h

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
/*
2+
* Copyright (C) the libgit2 contributors. All rights reserved.
3+
*
4+
* This file is part of libgit2, distributed under the GNU GPL v2 with
5+
* a Linking Exception. For full terms see the included COPYING file.
6+
*/
7+
#ifndef INCLUDE_git_mailmap_h__
8+
#define INCLUDE_git_mailmap_h__
9+
10+
#include "common.h"
11+
#include "types.h"
12+
#include "buffer.h"
13+
14+
/**
15+
* @file git2/mailmap.h
16+
* @brief Mailmap parsing routines
17+
* @defgroup git_mailmap Git mailmap routines
18+
* @ingroup Git
19+
* @{
20+
*/
21+
GIT_BEGIN_DECL
22+
23+
/**
24+
* Allocate a new mailmap object.
25+
*
26+
* This object is empty, so you'll have to add a mailmap file before you can do
27+
* anything with it. The mailmap must be freed with 'git_mailmap_free'.
28+
*
29+
* @param out pointer to store the new mailmap
30+
* @return 0 on success, or an error code
31+
*/
32+
GIT_EXTERN(int) git_mailmap_new(git_mailmap **out);
33+
34+
/**
35+
* Free the mailmap and its associated memory.
36+
*
37+
* @param mm the mailmap to free
38+
*/
39+
GIT_EXTERN(void) git_mailmap_free(git_mailmap *mm);
40+
41+
/**
42+
* Add a single entry to the given mailmap object. If the entry already exists,
43+
* it will be replaced with the new entry.
44+
*
45+
* @param mm mailmap to add the entry to
46+
* @param real_name the real name to use, or NULL
47+
* @param real_email the real email to use, or NULL
48+
* @param replace_name the name to replace, or NULL
49+
* @param replace_email the email to replace
50+
* @return 0 on success, or an error code
51+
*/
52+
GIT_EXTERN(int) git_mailmap_add_entry(
53+
git_mailmap *mm, const char *real_name, const char *real_email,
54+
const char *replace_name, const char *replace_email);
55+
56+
/**
57+
* Create a new mailmap instance containing a single mailmap file
58+
*
59+
* @param out pointer to store the new mailmap
60+
* @param buf buffer to parse the mailmap from
61+
* @param len the length of the input buffer
62+
* @return 0 on success, or an error code
63+
*/
64+
GIT_EXTERN(int) git_mailmap_from_buffer(
65+
git_mailmap **out, const char *buf, size_t len);
66+
67+
/**
68+
* Create a new mailmap instance from a repository, loading mailmap files based
69+
* on the repository's configuration.
70+
*
71+
* Mailmaps are loaded in the following order:
72+
* 1. '.mailmap' in the root of the repository's working directory, if present.
73+
* 2. The blob object identified by the 'mailmap.blob' config entry, if set.
74+
* [NOTE: 'mailmap.blob' defaults to 'HEAD:.mailmap' in bare repositories]
75+
* 3. The path in the 'mailmap.file' config entry, if set.
76+
*
77+
* @param out pointer to store the new mailmap
78+
* @param repo repository to load mailmap information from
79+
* @return 0 on success, or an error code
80+
*/
81+
GIT_EXTERN(int) git_mailmap_from_repository(
82+
git_mailmap **out, git_repository *repo);
83+
84+
/**
85+
* Resolve a name and email to the corresponding real name and email.
86+
*
87+
* The lifetime of the strings are tied to `mm`, `name`, and `email` parameters.
88+
*
89+
* @param real_name pointer to store the real name
90+
* @param real_email pointer to store the real email
91+
* @param mm the mailmap to perform a lookup with (may be NULL)
92+
* @param name the name to look up
93+
* @param email the email to look up
94+
* @return 0 on success, or an error code
95+
*/
96+
GIT_EXTERN(int) git_mailmap_resolve(
97+
const char **real_name, const char **real_email,
98+
const git_mailmap *mm, const char *name, const char *email);
99+
100+
/**
101+
* Resolve a signature to use real names and emails with a mailmap.
102+
*
103+
* Call `git_signature_free()` to free the data.
104+
*
105+
* @param out new signature
106+
* @param mm mailmap to resolve with
107+
* @param sig signature to resolve
108+
* @return 0 or an error code
109+
*/
110+
GIT_EXTERN(int) git_mailmap_resolve_signature(
111+
git_signature **out, const git_mailmap *mm, const git_signature *sig);
112+
113+
/** @} */
114+
GIT_END_DECL
115+
#endif

include/git2/types.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -434,6 +434,9 @@ struct git_writestream {
434434
void (*free)(git_writestream *stream);
435435
};
436436

437+
/** Representation of .mailmap file state. */
438+
typedef struct git_mailmap git_mailmap;
439+
437440
/** @} */
438441
GIT_END_DECL
439442

src/blame.c

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "git2/diff.h"
1515
#include "git2/blob.h"
1616
#include "git2/signature.h"
17+
#include "git2/mailmap.h"
1718
#include "util.h"
1819
#include "repository.h"
1920
#include "blame_git.h"
@@ -132,6 +133,9 @@ git_blame* git_blame__alloc(
132133
return NULL;
133134
}
134135

136+
if (opts.flags & GIT_BLAME_USE_MAILMAP)
137+
git_mailmap_from_repository(&gbr->mailmap, repo);
138+
135139
return gbr;
136140
}
137141

@@ -150,6 +154,8 @@ void git_blame_free(git_blame *blame)
150154

151155
git_array_clear(blame->line_index);
152156

157+
git_mailmap_free(blame->mailmap);
158+
153159
git__free(blame->path);
154160
git_blob_free(blame->final_blob);
155161
git__free(blame);
@@ -279,7 +285,7 @@ static int index_blob_lines(git_blame *blame)
279285
return blame->num_lines;
280286
}
281287

282-
static git_blame_hunk* hunk_from_entry(git_blame__entry *e)
288+
static git_blame_hunk* hunk_from_entry(git_blame__entry *e, git_blame *blame)
283289
{
284290
git_blame_hunk *h = new_hunk(
285291
e->lno+1, e->num_lines, e->s_lno+1, e->suspect->path);
@@ -289,8 +295,9 @@ static git_blame_hunk* hunk_from_entry(git_blame__entry *e)
289295

290296
git_oid_cpy(&h->final_commit_id, git_commit_id(e->suspect->commit));
291297
git_oid_cpy(&h->orig_commit_id, git_commit_id(e->suspect->commit));
292-
git_signature_dup(&h->final_signature, git_commit_author(e->suspect->commit));
293-
git_signature_dup(&h->orig_signature, git_commit_author(e->suspect->commit));
298+
git_commit_author_with_mailmap(
299+
&h->final_signature, e->suspect->commit, blame->mailmap);
300+
git_signature_dup(&h->orig_signature, h->final_signature);
294301
h->boundary = e->is_boundary ? 1 : 0;
295302
return h;
296303
}
@@ -341,7 +348,7 @@ static int blame_internal(git_blame *blame)
341348
cleanup:
342349
for (ent = blame->ent; ent; ) {
343350
git_blame__entry *e = ent->next;
344-
git_blame_hunk *h = hunk_from_entry(ent);
351+
git_blame_hunk *h = hunk_from_entry(ent, blame);
345352

346353
git_vector_insert(&blame->hunks, h);
347354

src/blame.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ typedef struct git_blame__entry {
6767
struct git_blame {
6868
char *path;
6969
git_repository *repository;
70+
git_mailmap *mailmap;
7071
git_blame_options options;
7172

7273
git_vector hunks;

src/commit.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include "git2/object.h"
1212
#include "git2/repository.h"
1313
#include "git2/signature.h"
14+
#include "git2/mailmap.h"
1415
#include "git2/sys/commit.h"
1516

1617
#include "odb.h"
@@ -889,3 +890,15 @@ int git_commit_create_with_signature(
889890
git_buf_dispose(&commit);
890891
return error;
891892
}
893+
894+
int git_commit_committer_with_mailmap(
895+
git_signature **out, const git_commit *commit, const git_mailmap *mailmap)
896+
{
897+
return git_mailmap_resolve_signature(out, mailmap, commit->committer);
898+
}
899+
900+
int git_commit_author_with_mailmap(
901+
git_signature **out, const git_commit *commit, const git_mailmap *mailmap)
902+
{
903+
return git_mailmap_resolve_signature(out, mailmap, commit->author);
904+
}

0 commit comments

Comments
 (0)