Skip to content

Commit 39ab4d0

Browse files
williamhgitster
authored andcommitted
config: allow giving separate author and committer idents
The author.email, author.name, committer.email and committer.name settings are analogous to the GIT_AUTHOR_* and GIT_COMMITTER_* environment variables, but for the git config system. This allows them to be set separately for each repository. Git supports setting different authorship and committer information with environment variables. However, environment variables are set in the shell, so if different authorship and committer information is needed for different repositories an external tool is required. This adds support to git config for author.email, author.name, committer.email and committer.name settings so this information can be set per repository. Also, it generalizes the fmt_ident function so it can handle author vs committer identification. Signed-off-by: William Hubbs <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 16a465b commit 39ab4d0

File tree

10 files changed

+197
-24
lines changed

10 files changed

+197
-24
lines changed

Documentation/config/user.txt

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,19 @@
1-
user.email::
2-
Your email address to be recorded in any newly created commits.
3-
Can be overridden by the `GIT_AUTHOR_EMAIL`, `GIT_COMMITTER_EMAIL`, and
4-
`EMAIL` environment variables. See linkgit:git-commit-tree[1].
5-
61
user.name::
7-
Your full name to be recorded in any newly created commits.
8-
Can be overridden by the `GIT_AUTHOR_NAME` and `GIT_COMMITTER_NAME`
9-
environment variables. See linkgit:git-commit-tree[1].
2+
user.email::
3+
author.name::
4+
author.email::
5+
committer.name::
6+
committer.email::
7+
The `user.name` and `user.email` variables determine what ends
8+
up in the `author` and `committer` field of commit
9+
objects.
10+
If you need the `author` or `committer` to be different, the
11+
`author.name`, `author.email`, `committer.name` or
12+
`committer.email` variables can be set.
13+
Also, all of these can be overridden by the `GIT_AUTHOR_NAME`,
14+
`GIT_AUTHOR_EMAIL`, `GIT_COMMITTER_NAME`,
15+
`GIT_COMMITTER_EMAIL` and `EMAIL` environment variables.
16+
See linkgit:git-commit-tree[1] for more information.
1017

1118
user.useConfigOnly::
1219
Instruct Git to avoid trying to guess defaults for `user.email`

blame.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,8 @@ static struct commit *fake_working_tree_commit(struct repository *r,
204204

205205
origin = make_origin(commit, path);
206206

207-
ident = fmt_ident("Not Committed Yet", "not.committed.yet", NULL, 0);
207+
ident = fmt_ident("Not Committed Yet", "not.committed.yet",
208+
WANT_BLANK_IDENT, NULL, 0);
208209
strbuf_addstr(&msg, "tree 0000000000000000000000000000000000000000\n");
209210
for (parent = commit->parents; parent; parent = parent->next)
210211
strbuf_addf(&msg, "parent %s\n",

builtin/am.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1594,6 +1594,7 @@ static void do_commit(const struct am_state *state)
15941594
}
15951595

15961596
author = fmt_ident(state->author_name, state->author_email,
1597+
WANT_AUTHOR_IDENT,
15971598
state->ignore_date ? NULL : state->author_date,
15981599
IDENT_STRICT);
15991600

builtin/commit.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -607,7 +607,8 @@ static void determine_author_info(struct strbuf *author_ident)
607607
set_ident_var(&date, strbuf_detach(&date_buf, NULL));
608608
}
609609

610-
strbuf_addstr(author_ident, fmt_ident(name, email, date, IDENT_STRICT));
610+
strbuf_addstr(author_ident, fmt_ident(name, email, WANT_AUTHOR_IDENT, date,
611+
IDENT_STRICT));
611612
assert_split_ident(&author, author_ident);
612613
export_one("GIT_AUTHOR_NAME", author.name_begin, author.name_end, 0);
613614
export_one("GIT_AUTHOR_EMAIL", author.mail_begin, author.mail_end, 0);

cache.h

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1479,10 +1479,19 @@ int date_overflows(timestamp_t date);
14791479
#define IDENT_STRICT 1
14801480
#define IDENT_NO_DATE 2
14811481
#define IDENT_NO_NAME 4
1482+
1483+
enum want_ident {
1484+
WANT_BLANK_IDENT,
1485+
WANT_AUTHOR_IDENT,
1486+
WANT_COMMITTER_IDENT
1487+
};
1488+
14821489
extern const char *git_author_info(int);
14831490
extern const char *git_committer_info(int);
1484-
extern const char *fmt_ident(const char *name, const char *email, const char *date_str, int);
1485-
extern const char *fmt_name(const char *name, const char *email);
1491+
extern const char *fmt_ident(const char *name, const char *email,
1492+
enum want_ident whose_ident,
1493+
const char *date_str, int);
1494+
extern const char *fmt_name(enum want_ident);
14861495
extern const char *ident_default_name(void);
14871496
extern const char *ident_default_email(void);
14881497
extern const char *git_editor(void);

config.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1445,7 +1445,9 @@ int git_default_config(const char *var, const char *value, void *cb)
14451445
if (starts_with(var, "core."))
14461446
return git_default_core_config(var, value, cb);
14471447

1448-
if (starts_with(var, "user."))
1448+
if (starts_with(var, "user.") ||
1449+
starts_with(var, "author.") ||
1450+
starts_with(var, "committer."))
14491451
return git_ident_config(var, value, cb);
14501452

14511453
if (starts_with(var, "i18n."))

ident.c

Lines changed: 86 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@
1111
static struct strbuf git_default_name = STRBUF_INIT;
1212
static struct strbuf git_default_email = STRBUF_INIT;
1313
static struct strbuf git_default_date = STRBUF_INIT;
14+
static struct strbuf git_author_name = STRBUF_INIT;
15+
static struct strbuf git_author_email = STRBUF_INIT;
16+
static struct strbuf git_committer_name = STRBUF_INIT;
17+
static struct strbuf git_committer_email = STRBUF_INIT;
1418
static int default_email_is_bogus;
1519
static int default_name_is_bogus;
1620

@@ -355,13 +359,19 @@ N_("\n"
355359
"\n");
356360

357361
const char *fmt_ident(const char *name, const char *email,
358-
const char *date_str, int flag)
362+
enum want_ident whose_ident, const char *date_str, int flag)
359363
{
360364
static struct strbuf ident = STRBUF_INIT;
361365
int strict = (flag & IDENT_STRICT);
362366
int want_date = !(flag & IDENT_NO_DATE);
363367
int want_name = !(flag & IDENT_NO_NAME);
364368

369+
if (!email) {
370+
if (whose_ident == WANT_AUTHOR_IDENT && git_author_email.len)
371+
email = git_author_email.buf;
372+
else if (whose_ident == WANT_COMMITTER_IDENT && git_committer_email.len)
373+
email = git_committer_email.buf;
374+
}
365375
if (!email) {
366376
if (strict && ident_use_config_only
367377
&& !(ident_config_given & IDENT_MAIL_GIVEN)) {
@@ -377,6 +387,13 @@ const char *fmt_ident(const char *name, const char *email,
377387

378388
if (want_name) {
379389
int using_default = 0;
390+
if (!name) {
391+
if (whose_ident == WANT_AUTHOR_IDENT && git_author_name.len)
392+
name = git_author_name.buf;
393+
else if (whose_ident == WANT_COMMITTER_IDENT &&
394+
git_committer_name.len)
395+
name = git_committer_name.buf;
396+
}
380397
if (!name) {
381398
if (strict && ident_use_config_only
382399
&& !(ident_config_given & IDENT_NAME_GIVEN)) {
@@ -425,9 +442,25 @@ const char *fmt_ident(const char *name, const char *email,
425442
return ident.buf;
426443
}
427444

428-
const char *fmt_name(const char *name, const char *email)
445+
const char *fmt_name(enum want_ident whose_ident)
429446
{
430-
return fmt_ident(name, email, NULL, IDENT_STRICT | IDENT_NO_DATE);
447+
char *name = NULL;
448+
char *email = NULL;
449+
450+
switch (whose_ident) {
451+
case WANT_BLANK_IDENT:
452+
break;
453+
case WANT_AUTHOR_IDENT:
454+
name = getenv("GIT_AUTHOR_NAME");
455+
email = getenv("GIT_AUTHOR_EMAIL");
456+
break;
457+
case WANT_COMMITTER_IDENT:
458+
name = getenv("GIT_COMMITTER_NAME");
459+
email = getenv("GIT_COMMITTER_EMAIL");
460+
break;
461+
}
462+
return fmt_ident(name, email, whose_ident, NULL,
463+
IDENT_STRICT | IDENT_NO_DATE);
431464
}
432465

433466
const char *git_author_info(int flag)
@@ -438,6 +471,7 @@ const char *git_author_info(int flag)
438471
author_ident_explicitly_given |= IDENT_MAIL_GIVEN;
439472
return fmt_ident(getenv("GIT_AUTHOR_NAME"),
440473
getenv("GIT_AUTHOR_EMAIL"),
474+
WANT_AUTHOR_IDENT,
441475
getenv("GIT_AUTHOR_DATE"),
442476
flag);
443477
}
@@ -450,6 +484,7 @@ const char *git_committer_info(int flag)
450484
committer_ident_explicitly_given |= IDENT_MAIL_GIVEN;
451485
return fmt_ident(getenv("GIT_COMMITTER_NAME"),
452486
getenv("GIT_COMMITTER_EMAIL"),
487+
WANT_COMMITTER_IDENT,
453488
getenv("GIT_COMMITTER_DATE"),
454489
flag);
455490
}
@@ -473,10 +508,45 @@ int author_ident_sufficiently_given(void)
473508
return ident_is_sufficient(author_ident_explicitly_given);
474509
}
475510

476-
int git_ident_config(const char *var, const char *value, void *data)
511+
static int set_ident(const char *var, const char *value)
477512
{
478-
if (!strcmp(var, "user.useconfigonly")) {
479-
ident_use_config_only = git_config_bool(var, value);
513+
if (!strcmp(var, "author.name")) {
514+
if (!value)
515+
return config_error_nonbool(var);
516+
strbuf_reset(&git_author_name);
517+
strbuf_addstr(&git_author_name, value);
518+
author_ident_explicitly_given |= IDENT_NAME_GIVEN;
519+
ident_config_given |= IDENT_NAME_GIVEN;
520+
return 0;
521+
}
522+
523+
if (!strcmp(var, "author.email")) {
524+
if (!value)
525+
return config_error_nonbool(var);
526+
strbuf_reset(&git_author_email);
527+
strbuf_addstr(&git_author_email, value);
528+
author_ident_explicitly_given |= IDENT_MAIL_GIVEN;
529+
ident_config_given |= IDENT_MAIL_GIVEN;
530+
return 0;
531+
}
532+
533+
if (!strcmp(var, "committer.name")) {
534+
if (!value)
535+
return config_error_nonbool(var);
536+
strbuf_reset(&git_committer_name);
537+
strbuf_addstr(&git_committer_name, value);
538+
committer_ident_explicitly_given |= IDENT_NAME_GIVEN;
539+
ident_config_given |= IDENT_NAME_GIVEN;
540+
return 0;
541+
}
542+
543+
if (!strcmp(var, "committer.email")) {
544+
if (!value)
545+
return config_error_nonbool(var);
546+
strbuf_reset(&git_committer_email);
547+
strbuf_addstr(&git_committer_email, value);
548+
committer_ident_explicitly_given |= IDENT_MAIL_GIVEN;
549+
ident_config_given |= IDENT_MAIL_GIVEN;
480550
return 0;
481551
}
482552

@@ -505,6 +575,16 @@ int git_ident_config(const char *var, const char *value, void *data)
505575
return 0;
506576
}
507577

578+
int git_ident_config(const char *var, const char *value, void *data)
579+
{
580+
if (!strcmp(var, "user.useconfigonly")) {
581+
ident_use_config_only = git_config_bool(var, value);
582+
return 0;
583+
}
584+
585+
return set_ident(var, value);
586+
}
587+
508588
static int buf_cmp(const char *a_begin, const char *a_end,
509589
const char *b_begin, const char *b_end)
510590
{

log-tree.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -687,8 +687,7 @@ void show_log(struct rev_info *opt)
687687
*/
688688
if (ctx.need_8bit_cte >= 0 && opt->add_signoff)
689689
ctx.need_8bit_cte =
690-
has_non_ascii(fmt_name(getenv("GIT_COMMITTER_NAME"),
691-
getenv("GIT_COMMITTER_EMAIL")));
690+
has_non_ascii(fmt_name(WANT_COMMITTER_IDENT));
692691
ctx.date_mode = opt->date_mode;
693692
ctx.date_mode_explicit = opt->date_mode_explicit;
694693
ctx.abbrev = opt->diffopt.abbrev;

sequencer.c

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -836,7 +836,7 @@ static const char *read_author_ident(struct strbuf *buf)
836836
}
837837

838838
strbuf_reset(&out);
839-
strbuf_addstr(&out, fmt_ident(name, email, date, 0));
839+
strbuf_addstr(&out, fmt_ident(name, email, WANT_AUTHOR_IDENT, date, 0));
840840
strbuf_swap(buf, &out);
841841
strbuf_release(&out);
842842
free(name);
@@ -4087,8 +4087,7 @@ void append_signoff(struct strbuf *msgbuf, size_t ignore_footer, unsigned flag)
40874087
int has_footer;
40884088

40894089
strbuf_addstr(&sob, sign_off_header);
4090-
strbuf_addstr(&sob, fmt_name(getenv("GIT_COMMITTER_NAME"),
4091-
getenv("GIT_COMMITTER_EMAIL")));
4090+
strbuf_addstr(&sob, fmt_name(WANT_COMMITTER_IDENT));
40924091
strbuf_addch(&sob, '\n');
40934092

40944093
if (!ignore_footer)

t/t7517-per-repo-email.sh

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,4 +85,78 @@ test_expect_success REBASE_P \
8585
test_must_fail git rebase -p master
8686
'
8787

88+
test_expect_success 'author.name overrides user.name' '
89+
test_config user.name user &&
90+
test_config user.email [email protected] &&
91+
test_config author.name author &&
92+
test_commit author-name-override-user &&
93+
echo author [email protected] > expected-author &&
94+
echo user [email protected] > expected-committer &&
95+
git log --format="%an %ae" -1 > actual-author &&
96+
git log --format="%cn %ce" -1 > actual-committer &&
97+
test_cmp expected-author actual-author &&
98+
test_cmp expected-committer actual-committer
99+
'
100+
101+
test_expect_success 'author.email overrides user.email' '
102+
test_config user.name user &&
103+
test_config user.email [email protected] &&
104+
test_config author.email [email protected] &&
105+
test_commit author-email-override-user &&
106+
echo user [email protected] > expected-author &&
107+
echo user [email protected] > expected-committer &&
108+
git log --format="%an %ae" -1 > actual-author &&
109+
git log --format="%cn %ce" -1 > actual-committer &&
110+
test_cmp expected-author actual-author &&
111+
test_cmp expected-committer actual-committer
112+
'
113+
114+
test_expect_success 'committer.name overrides user.name' '
115+
test_config user.name user &&
116+
test_config user.email [email protected] &&
117+
test_config committer.name committer &&
118+
test_commit committer-name-override-user &&
119+
echo user [email protected] > expected-author &&
120+
echo committer [email protected] > expected-committer &&
121+
git log --format="%an %ae" -1 > actual-author &&
122+
git log --format="%cn %ce" -1 > actual-committer &&
123+
test_cmp expected-author actual-author &&
124+
test_cmp expected-committer actual-committer
125+
'
126+
127+
test_expect_success 'committer.email overrides user.email' '
128+
test_config user.name user &&
129+
test_config user.email [email protected] &&
130+
test_config committer.email [email protected] &&
131+
test_commit committer-email-override-user &&
132+
echo user [email protected] > expected-author &&
133+
echo user [email protected] > expected-committer &&
134+
git log --format="%an %ae" -1 > actual-author &&
135+
git log --format="%cn %ce" -1 > actual-committer &&
136+
test_cmp expected-author actual-author &&
137+
test_cmp expected-committer actual-committer
138+
'
139+
140+
test_expect_success 'author and committer environment variables override config settings' '
141+
test_config user.name user &&
142+
test_config user.email [email protected] &&
143+
test_config author.name author &&
144+
test_config author.email [email protected] &&
145+
test_config committer.name committer &&
146+
test_config committer.email [email protected] &&
147+
GIT_AUTHOR_NAME=env_author && export GIT_AUTHOR_NAME &&
148+
[email protected] && export GIT_AUTHOR_EMAIL &&
149+
GIT_COMMITTER_NAME=env_commit && export GIT_COMMITTER_NAME &&
150+
[email protected] && export GIT_COMMITTER_EMAIL &&
151+
test_commit env-override-conf &&
152+
echo env_author [email protected] > expected-author &&
153+
echo env_commit [email protected] > expected-committer &&
154+
git log --format="%an %ae" -1 > actual-author &&
155+
git log --format="%cn %ce" -1 > actual-committer &&
156+
sane_unset GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL &&
157+
sane_unset GIT_COMMITTER_NAME GIT_COMMITTER_EMAIL &&
158+
test_cmp expected-author actual-author &&
159+
test_cmp expected-committer actual-committer
160+
'
161+
88162
test_done

0 commit comments

Comments
 (0)