|
|
|
/*
|
|
|
|
* ident.c
|
|
|
|
*
|
|
|
|
* create git identifier lines of the form "name <email> date"
|
|
|
|
*
|
|
|
|
* Copyright (C) 2005 Linus Torvalds
|
|
|
|
*/
|
|
|
|
#include "cache.h"
|
|
|
|
|
|
|
|
static char git_default_date[50];
|
|
|
|
|
|
|
|
#ifdef NO_GECOS_IN_PWENT
|
|
|
|
#define get_gecos(ignored) "&"
|
|
|
|
#else
|
|
|
|
#define get_gecos(struct_passwd) ((struct_passwd)->pw_gecos)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static void copy_gecos(const struct passwd *w, char *name, size_t sz)
|
|
|
|
{
|
|
|
|
char *src, *dst;
|
|
|
|
size_t len, nlen;
|
|
|
|
|
|
|
|
nlen = strlen(w->pw_name);
|
|
|
|
|
|
|
|
/* Traditionally GECOS field had office phone numbers etc, separated
|
|
|
|
* with commas. Also & stands for capitalized form of the login name.
|
|
|
|
*/
|
|
|
|
|
|
|
|
for (len = 0, dst = name, src = get_gecos(w); len < sz; src++) {
|
|
|
|
int ch = *src;
|
|
|
|
if (ch != '&') {
|
|
|
|
*dst++ = ch;
|
|
|
|
if (ch == 0 || ch == ',')
|
|
|
|
break;
|
|
|
|
len++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (len + nlen < sz) {
|
|
|
|
/* Sorry, Mr. McDonald... */
|
|
|
|
*dst++ = toupper(*w->pw_name);
|
|
|
|
memcpy(dst, w->pw_name + 1, nlen - 1);
|
|
|
|
dst += nlen - 1;
|
|
|
|
len += nlen;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (len < sz)
|
|
|
|
name[len] = 0;
|
|
|
|
else
|
|
|
|
die("Your parents must have hated you!");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
ident: check /etc/mailname if email is unknown
Before falling back to gethostname(), check /etc/mailname if
GIT_AUTHOR_EMAIL is not set in the environment or through config
files. Only fall back if /etc/mailname cannot be opened or read.
The /etc/mailname convention comes from Debian policy section 11.6
("mail transport, delivery and user agents"), though maybe it could be
useful sometimes on other machines, too. The lack of this support was
noticed by various people in different ways:
- Ian observed that git was choosing the address
'ian@anarres.relativity.greenend.org.uk' rather than
'ian@davenant.greenend.org.uk' as it should have done.
- Jonathan noticed that operations like "git commit" were needlessly
slow when using a resolver that was slow to handle reverse DNS
lookups.
Alas, after this patch, if /etc/mailname is set up and the [user] name
and email configuration aren't, the committer email will not provide a
charming reminder of which machine commits were made on any more. But
I think it's worth it.
Mechanics: the functionality of reading mailname goes in its own
function, so people who care about other distros can easily add an
implementation to a similar location without making copy_email() too
long and losing clarity. While at it, we split out the fallback
default logic that does gethostname(), too (rearranging it a little
and adding a check for errors from gethostname while at it).
Based on a patch by Gerrit Pape <pape@smarden.org>.
Requested-by: Ian Jackson <ijackson@chiark.greenend.org.uk>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
Improved-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
13 years ago
|
|
|
static int add_mailname_host(char *buf, size_t len)
|
|
|
|
{
|
|
|
|
FILE *mailname;
|
|
|
|
|
|
|
|
mailname = fopen("/etc/mailname", "r");
|
|
|
|
if (!mailname) {
|
|
|
|
if (errno != ENOENT)
|
|
|
|
warning("cannot open /etc/mailname: %s",
|
|
|
|
strerror(errno));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (!fgets(buf, len, mailname)) {
|
|
|
|
if (ferror(mailname))
|
|
|
|
warning("cannot read /etc/mailname: %s",
|
|
|
|
strerror(errno));
|
|
|
|
fclose(mailname);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
/* success! */
|
|
|
|
fclose(mailname);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void add_domainname(char *buf, size_t len)
|
|
|
|
{
|
|
|
|
struct hostent *he;
|
|
|
|
size_t namelen;
|
|
|
|
const char *domainname;
|
|
|
|
|
|
|
|
if (gethostname(buf, len)) {
|
|
|
|
warning("cannot get host name: %s", strerror(errno));
|
|
|
|
strlcpy(buf, "(none)", len);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
namelen = strlen(buf);
|
|
|
|
if (memchr(buf, '.', namelen))
|
|
|
|
return;
|
|
|
|
|
|
|
|
he = gethostbyname(buf);
|
|
|
|
buf[namelen++] = '.';
|
|
|
|
buf += namelen;
|
|
|
|
len -= namelen;
|
|
|
|
if (he && (domainname = strchr(he->h_name, '.')))
|
|
|
|
strlcpy(buf, domainname + 1, len);
|
|
|
|
else
|
|
|
|
strlcpy(buf, "(none)", len);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void copy_email(const struct passwd *pw)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Make up a fake email address
|
|
|
|
* (name + '@' + hostname [+ '.' + domainname])
|
|
|
|
*/
|
|
|
|
size_t len = strlen(pw->pw_name);
|
|
|
|
if (len > sizeof(git_default_email)/2)
|
|
|
|
die("Your sysadmin must hate you!");
|
|
|
|
memcpy(git_default_email, pw->pw_name, len);
|
|
|
|
git_default_email[len++] = '@';
|
ident: check /etc/mailname if email is unknown
Before falling back to gethostname(), check /etc/mailname if
GIT_AUTHOR_EMAIL is not set in the environment or through config
files. Only fall back if /etc/mailname cannot be opened or read.
The /etc/mailname convention comes from Debian policy section 11.6
("mail transport, delivery and user agents"), though maybe it could be
useful sometimes on other machines, too. The lack of this support was
noticed by various people in different ways:
- Ian observed that git was choosing the address
'ian@anarres.relativity.greenend.org.uk' rather than
'ian@davenant.greenend.org.uk' as it should have done.
- Jonathan noticed that operations like "git commit" were needlessly
slow when using a resolver that was slow to handle reverse DNS
lookups.
Alas, after this patch, if /etc/mailname is set up and the [user] name
and email configuration aren't, the committer email will not provide a
charming reminder of which machine commits were made on any more. But
I think it's worth it.
Mechanics: the functionality of reading mailname goes in its own
function, so people who care about other distros can easily add an
implementation to a similar location without making copy_email() too
long and losing clarity. While at it, we split out the fallback
default logic that does gethostname(), too (rearranging it a little
and adding a check for errors from gethostname while at it).
Based on a patch by Gerrit Pape <pape@smarden.org>.
Requested-by: Ian Jackson <ijackson@chiark.greenend.org.uk>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
Improved-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
13 years ago
|
|
|
|
|
|
|
if (!add_mailname_host(git_default_email + len,
|
|
|
|
sizeof(git_default_email) - len))
|
|
|
|
return; /* read from "/etc/mailname" (Debian) */
|
|
|
|
add_domainname(git_default_email + len,
|
|
|
|
sizeof(git_default_email) - len);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void setup_ident(const char **name, const char **emailp)
|
|
|
|
{
|
|
|
|
struct passwd *pw = NULL;
|
|
|
|
|
|
|
|
/* Get the name ("gecos") */
|
|
|
|
if (!*name && !git_default_name[0]) {
|
|
|
|
pw = getpwuid(getuid());
|
|
|
|
if (!pw)
|
|
|
|
die("You don't exist. Go away!");
|
|
|
|
copy_gecos(pw, git_default_name, sizeof(git_default_name));
|
|
|
|
}
|
|
|
|
if (!*name)
|
|
|
|
*name = git_default_name;
|
|
|
|
|
|
|
|
if (!*emailp && !git_default_email[0]) {
|
|
|
|
const char *email = getenv("EMAIL");
|
|
|
|
|
|
|
|
if (email && email[0]) {
|
|
|
|
strlcpy(git_default_email, email,
|
|
|
|
sizeof(git_default_email));
|
|
|
|
user_ident_explicitly_given |= IDENT_MAIL_GIVEN;
|
|
|
|
} else {
|
|
|
|
if (!pw)
|
|
|
|
pw = getpwuid(getuid());
|
|
|
|
if (!pw)
|
|
|
|
die("You don't exist. Go away!");
|
|
|
|
copy_email(pw);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!*emailp)
|
|
|
|
*emailp = git_default_email;
|
|
|
|
|
|
|
|
/* And set the default date */
|
|
|
|
if (!git_default_date[0])
|
|
|
|
datestamp(git_default_date, sizeof(git_default_date));
|
|
|
|
}
|
|
|
|
|
|
|
|
static int add_raw(char *buf, size_t size, int offset, const char *str)
|
|
|
|
{
|
|
|
|
size_t len = strlen(str);
|
|
|
|
if (offset + len > size)
|
|
|
|
return size;
|
|
|
|
memcpy(buf + offset, str, len);
|
|
|
|
return offset + len;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int crud(unsigned char c)
|
|
|
|
{
|
|
|
|
return c <= 32 ||
|
|
|
|
c == '.' ||
|
|
|
|
c == ',' ||
|
|
|
|
c == ':' ||
|
|
|
|
c == ';' ||
|
|
|
|
c == '<' ||
|
|
|
|
c == '>' ||
|
|
|
|
c == '"' ||
|
|
|
|
c == '\\' ||
|
|
|
|
c == '\'';
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Copy over a string to the destination, but avoid special
|
|
|
|
* characters ('\n', '<' and '>') and remove crud at the end
|
|
|
|
*/
|
|
|
|
static int copy(char *buf, size_t size, int offset, const char *src)
|
|
|
|
{
|
|
|
|
size_t i, len;
|
|
|
|
unsigned char c;
|
|
|
|
|
|
|
|
/* Remove crud from the beginning.. */
|
|
|
|
while ((c = *src) != 0) {
|
|
|
|
if (!crud(c))
|
|
|
|
break;
|
|
|
|
src++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Remove crud from the end.. */
|
|
|
|
len = strlen(src);
|
|
|
|
while (len > 0) {
|
|
|
|
c = src[len-1];
|
|
|
|
if (!crud(c))
|
|
|
|
break;
|
|
|
|
--len;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Copy the rest to the buffer, but avoid the special
|
|
|
|
* characters '\n' '<' and '>' that act as delimiters on
|
|
|
|
* an identification line
|
|
|
|
*/
|
|
|
|
for (i = 0; i < len; i++) {
|
|
|
|
c = *src++;
|
|
|
|
switch (c) {
|
|
|
|
case '\n': case '<': case '>':
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (offset >= size)
|
|
|
|
return size;
|
|
|
|
buf[offset++] = c;
|
|
|
|
}
|
|
|
|
return offset;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const char *env_hint =
|
|
|
|
"\n"
|
|
|
|
"*** Please tell me who you are.\n"
|
|
|
|
"\n"
|
|
|
|
"Run\n"
|
|
|
|
"\n"
|
|
|
|
" git config --global user.email \"you@example.com\"\n"
|
|
|
|
" git config --global user.name \"Your Name\"\n"
|
|
|
|
"\n"
|
|
|
|
"to set your account\'s default identity.\n"
|
|
|
|
"Omit --global to set the identity only in this repository.\n"
|
|
|
|
"\n";
|
|
|
|
|
|
|
|
const char *fmt_ident(const char *name, const char *email,
|
|
|
|
const char *date_str, int flag)
|
|
|
|
{
|
|
|
|
static char buffer[1000];
|
|
|
|
char date[50];
|
|
|
|
int i;
|
|
|
|
int error_on_no_name = (flag & IDENT_ERROR_ON_NO_NAME);
|
|
|
|
int warn_on_no_name = (flag & IDENT_WARN_ON_NO_NAME);
|
|
|
|
int name_addr_only = (flag & IDENT_NO_DATE);
|
|
|
|
|
|
|
|
setup_ident(&name, &email);
|
|
|
|
|
|
|
|
if (!*name) {
|
Allow non-developer to clone, checkout and fetch more easily.
The code that uses committer_info() in reflog can barf and die
whenever it is asked to update a ref. And I do not think
calling ignore_missing_committer_name() upfront like recent
receive-pack did in the aplication is a reasonable workaround.
What the patch does.
- git_committer_info() takes one parameter. It used to be "if
this is true, then die() if the name is not available due to
bad GECOS, otherwise issue a warning once but leave the name
empty". The reason was because we wanted to prevent bad
commits from being made by git-commit-tree (and its
callers). The value 0 is only used by "git var -l".
Now it takes -1, 0 or 1. When set to -1, it does not
complain but uses the pw->pw_name when name is not
available. Existing 0 and 1 values mean the same thing as
they used to mean before. 0 means issue warnings and leave
it empty, 1 means barf and die.
- ignore_missing_committer_name() and its existing caller
(receive-pack, to set the reflog) have been removed.
- git-format-patch, to come up with the phoney message ID when
asked to thread, now passes -1 to git_committer_info(). This
codepath uses only the e-mail part, ignoring the name. It
used to barf and die. The other call in the same program
when asked to add signed-off-by line based on committer
identity still passes 1 to make sure it barfs instead of
adding a bogus s-o-b line.
- log_ref_write in refs.c, to come up with the name to record
who initiated the ref update in the reflog, passes -1. It
used to barf and die.
The last change means that git-update-ref, git-branch, and
commit walker backends can now be used in a repository with
reflog by somebody who does not have the user identity required
to make a commit. They all used to barf and die.
I've run tests and all of them seem to pass, and also tried "git
clone" as a user whose GECOS is empty -- git clone works again
now (it was broken when reflog was enabled by default).
But this definitely needs extra sets of eyeballs.
Signed-off-by: Junio C Hamano <junkio@cox.net>
18 years ago
|
|
|
struct passwd *pw;
|
|
|
|
|
|
|
|
if ((warn_on_no_name || error_on_no_name) &&
|
Allow non-developer to clone, checkout and fetch more easily.
The code that uses committer_info() in reflog can barf and die
whenever it is asked to update a ref. And I do not think
calling ignore_missing_committer_name() upfront like recent
receive-pack did in the aplication is a reasonable workaround.
What the patch does.
- git_committer_info() takes one parameter. It used to be "if
this is true, then die() if the name is not available due to
bad GECOS, otherwise issue a warning once but leave the name
empty". The reason was because we wanted to prevent bad
commits from being made by git-commit-tree (and its
callers). The value 0 is only used by "git var -l".
Now it takes -1, 0 or 1. When set to -1, it does not
complain but uses the pw->pw_name when name is not
available. Existing 0 and 1 values mean the same thing as
they used to mean before. 0 means issue warnings and leave
it empty, 1 means barf and die.
- ignore_missing_committer_name() and its existing caller
(receive-pack, to set the reflog) have been removed.
- git-format-patch, to come up with the phoney message ID when
asked to thread, now passes -1 to git_committer_info(). This
codepath uses only the e-mail part, ignoring the name. It
used to barf and die. The other call in the same program
when asked to add signed-off-by line based on committer
identity still passes 1 to make sure it barfs instead of
adding a bogus s-o-b line.
- log_ref_write in refs.c, to come up with the name to record
who initiated the ref update in the reflog, passes -1. It
used to barf and die.
The last change means that git-update-ref, git-branch, and
commit walker backends can now be used in a repository with
reflog by somebody who does not have the user identity required
to make a commit. They all used to barf and die.
I've run tests and all of them seem to pass, and also tried "git
clone" as a user whose GECOS is empty -- git clone works again
now (it was broken when reflog was enabled by default).
But this definitely needs extra sets of eyeballs.
Signed-off-by: Junio C Hamano <junkio@cox.net>
18 years ago
|
|
|
name == git_default_name && env_hint) {
|
|
|
|
fputs(env_hint, stderr);
|
|
|
|
env_hint = NULL; /* warn only once */
|
|
|
|
}
|
|
|
|
if (error_on_no_name)
|
|
|
|
die("empty ident %s <%s> not allowed", name, email);
|
Allow non-developer to clone, checkout and fetch more easily.
The code that uses committer_info() in reflog can barf and die
whenever it is asked to update a ref. And I do not think
calling ignore_missing_committer_name() upfront like recent
receive-pack did in the aplication is a reasonable workaround.
What the patch does.
- git_committer_info() takes one parameter. It used to be "if
this is true, then die() if the name is not available due to
bad GECOS, otherwise issue a warning once but leave the name
empty". The reason was because we wanted to prevent bad
commits from being made by git-commit-tree (and its
callers). The value 0 is only used by "git var -l".
Now it takes -1, 0 or 1. When set to -1, it does not
complain but uses the pw->pw_name when name is not
available. Existing 0 and 1 values mean the same thing as
they used to mean before. 0 means issue warnings and leave
it empty, 1 means barf and die.
- ignore_missing_committer_name() and its existing caller
(receive-pack, to set the reflog) have been removed.
- git-format-patch, to come up with the phoney message ID when
asked to thread, now passes -1 to git_committer_info(). This
codepath uses only the e-mail part, ignoring the name. It
used to barf and die. The other call in the same program
when asked to add signed-off-by line based on committer
identity still passes 1 to make sure it barfs instead of
adding a bogus s-o-b line.
- log_ref_write in refs.c, to come up with the name to record
who initiated the ref update in the reflog, passes -1. It
used to barf and die.
The last change means that git-update-ref, git-branch, and
commit walker backends can now be used in a repository with
reflog by somebody who does not have the user identity required
to make a commit. They all used to barf and die.
I've run tests and all of them seem to pass, and also tried "git
clone" as a user whose GECOS is empty -- git clone works again
now (it was broken when reflog was enabled by default).
But this definitely needs extra sets of eyeballs.
Signed-off-by: Junio C Hamano <junkio@cox.net>
18 years ago
|
|
|
pw = getpwuid(getuid());
|
|
|
|
if (!pw)
|
|
|
|
die("You don't exist. Go away!");
|
|
|
|
strlcpy(git_default_name, pw->pw_name,
|
|
|
|
sizeof(git_default_name));
|
|
|
|
name = git_default_name;
|
|
|
|
}
|
|
|
|
|
|
|
|
strcpy(date, git_default_date);
|
|
|
|
if (!name_addr_only && date_str && date_str[0]) {
|
|
|
|
if (parse_date(date_str, date, sizeof(date)) < 0)
|
|
|
|
die("invalid date format: %s", date_str);
|
|
|
|
}
|
|
|
|
|
|
|
|
i = copy(buffer, sizeof(buffer), 0, name);
|
|
|
|
i = add_raw(buffer, sizeof(buffer), i, " <");
|
|
|
|
i = copy(buffer, sizeof(buffer), i, email);
|
|
|
|
if (!name_addr_only) {
|
|
|
|
i = add_raw(buffer, sizeof(buffer), i, "> ");
|
|
|
|
i = copy(buffer, sizeof(buffer), i, date);
|
|
|
|
} else {
|
|
|
|
i = add_raw(buffer, sizeof(buffer), i, ">");
|
|
|
|
}
|
|
|
|
if (i >= sizeof(buffer))
|
|
|
|
die("Impossibly long personal identifier");
|
|
|
|
buffer[i] = 0;
|
|
|
|
return buffer;
|
|
|
|
}
|
|
|
|
|
|
|
|
const char *fmt_name(const char *name, const char *email)
|
|
|
|
{
|
|
|
|
return fmt_ident(name, email, NULL, IDENT_ERROR_ON_NO_NAME | IDENT_NO_DATE);
|
|
|
|
}
|
|
|
|
|
|
|
|
const char *git_author_info(int flag)
|
|
|
|
{
|
|
|
|
return fmt_ident(getenv("GIT_AUTHOR_NAME"),
|
|
|
|
getenv("GIT_AUTHOR_EMAIL"),
|
|
|
|
getenv("GIT_AUTHOR_DATE"),
|
|
|
|
flag);
|
|
|
|
}
|
|
|
|
|
|
|
|
const char *git_committer_info(int flag)
|
|
|
|
{
|
|
|
|
if (getenv("GIT_COMMITTER_NAME"))
|
|
|
|
user_ident_explicitly_given |= IDENT_NAME_GIVEN;
|
|
|
|
if (getenv("GIT_COMMITTER_EMAIL"))
|
|
|
|
user_ident_explicitly_given |= IDENT_MAIL_GIVEN;
|
|
|
|
return fmt_ident(getenv("GIT_COMMITTER_NAME"),
|
|
|
|
getenv("GIT_COMMITTER_EMAIL"),
|
|
|
|
getenv("GIT_COMMITTER_DATE"),
|
|
|
|
flag);
|
|
|
|
}
|
|
|
|
|
|
|
|
int user_ident_sufficiently_given(void)
|
|
|
|
{
|
|
|
|
#ifndef WINDOWS
|
|
|
|
return (user_ident_explicitly_given & IDENT_MAIL_GIVEN);
|
|
|
|
#else
|
|
|
|
return (user_ident_explicitly_given == IDENT_ALL_GIVEN);
|
|
|
|
#endif
|
|
|
|
}
|