diff --git a/examples/commit.c b/examples/commit.c index 1ba4739f0..c6e0a8dc4 100644 --- a/examples/commit.c +++ b/examples/commit.c @@ -63,10 +63,8 @@ int lg2_commit(git_repository *repo, int argc, char **argv) check_lg2(git_tree_lookup(&tree, repo, &tree_oid), "Error looking up tree", NULL); - check_lg2(git_signature_default_author(&author_signature, repo), - "Error creating author signature", NULL); - check_lg2(git_signature_default_committer(&committer_signature, repo), - "Error creating committer signature", NULL); + check_lg2(git_signature_default_from_env(&author_signature, &committer_signature, repo), + "Error creating signature", NULL); check_lg2(git_commit_create_v( &commit_oid, diff --git a/examples/init.c b/examples/init.c index f0f0105be..036c156ab 100644 --- a/examples/init.c +++ b/examples/init.c @@ -130,8 +130,7 @@ static void create_initial_commit(git_repository *repo) /** First use the config to initialize a commit signature for the user. */ - if ((git_signature_default_author(&author_sig, repo) < 0) || - (git_signature_default_committer(&committer_sig, repo) < 0)) + if ((git_signature_default_from_env(&author_sig, &committer_sig, repo) < 0)) fatal("Unable to create a commit signature.", "Perhaps 'user.name' and 'user.email' are not set"); diff --git a/examples/stash.c b/examples/stash.c index c330cbce1..197724364 100644 --- a/examples/stash.c +++ b/examples/stash.c @@ -108,7 +108,7 @@ static int cmd_push(git_repository *repo, struct opts *opts) if (opts->argc) usage("push does not accept any parameters"); - check_lg2(git_signature_default_author(&signature, repo), + check_lg2(git_signature_default_from_env(&signature, NULL, repo), "Unable to get signature", NULL); check_lg2(git_stash_save(&stashid, repo, signature, NULL, GIT_STASH_DEFAULT), "Unable to save stash", NULL); diff --git a/examples/tag.c b/examples/tag.c index 9bebcd1e6..ebe1a9d7b 100644 --- a/examples/tag.c +++ b/examples/tag.c @@ -226,7 +226,7 @@ static void action_create_tag(tag_state *state) check_lg2(git_revparse_single(&target, repo, opts->target), "Unable to resolve spec", opts->target); - check_lg2(git_signature_default_author(&tagger, repo), + check_lg2(git_signature_default_from_env(&tagger, NULL, repo), "Unable to create signature", NULL); check_lg2(git_tag_create(&oid, repo, opts->tag_name, diff --git a/include/git2/signature.h b/include/git2/signature.h index 84c36a33d..a027d25dc 100644 --- a/include/git2/signature.h +++ b/include/git2/signature.h @@ -49,65 +49,53 @@ GIT_EXTERN(int) git_signature_new(git_signature **out, const char *name, const c GIT_EXTERN(int) git_signature_now(git_signature **out, const char *name, const char *email); /** - * Create a new author action signature with default information based on the - * configuration and environment variables. + * Create a new author and/or committer signatures with default + * information based on the configuration and environment variables. * - * If GIT_AUTHOR_NAME environment variable is set it uses that over the - * user.name value from the configuration. + * If `author_out` is set, it will be populated with the author + * information. The `GIT_AUTHOR_NAME` and `GIT_AUTHOR_EMAIL` + * environment variables will be honored, and `user.name` and + * `user.email` configuration options will be honored if the + * environment variables are unset. For timestamps, `GIT_AUTHOR_DATE` + * will be used, otherwise the current time will be used. * - * If GIT_AUTHOR_EMAIL environment variable is set it uses that over the - * user.email value from the configuration. The EMAIL environment variable is - * the fallback email address in case the user.email configuration value isn't - * set. + * If `committer_out` is set, it will be populated with the + * committer information. The `GIT_COMMITTER_NAME` and + * `GIT_COMMITTER_EMAIL` environment variables will be honored, + * and `user.name` and `user.email` configuration options will + * be honored if the environment variables are unset. For timestamps, + * `GIT_COMMITTER_DATE` will be used, otherwise the current time will + * be used. * - * If GIT_AUTHOR_DATE is set it uses that, otherwise it uses the current time - * as the timestamp. + * If neither `GIT_AUTHOR_DATE` nor `GIT_COMMITTER_DATE` are set, + * both timestamps will be set to the same time. * - * It will return GIT_ENOTFOUND if either the user.name or user.email are not - * set and there is no fallback from an environment variable. + * It will return `GIT_ENOTFOUND` if either the `user.name` or + * `user.email` are not set and there is no fallback from an environment + * variable. One of `author_out` or `committer_out` must be set. * - * @param out new signature + * @param author_out pointer to set the author signature, or NULL + * @param committer_out pointer to set the committer signature, or NULL * @param repo repository pointer * @return 0 on success, GIT_ENOTFOUND if config is missing, or error code */ -GIT_EXTERN(int) git_signature_default_author(git_signature **out, git_repository *repo); - -/** - * Create a new committer action signature with default information based on - * the configuration and environment variables. - * - * If GIT_COMMITTER_NAME environment variable is set it uses that over the - * user.name value from the configuration. - * - * If GIT_COMMITTER_EMAIL environment variable is set it uses that over the - * user.email value from the configuration. The EMAIL environment variable is - * the fallback email address in case the user.email configuration value isn't - * set. - * - * If GIT_COMMITTER_DATE is set it uses that, otherwise it uses the current - * time as the timestamp. - * - * It will return GIT_ENOTFOUND if either the user.name or user.email are not - * set and there is no fallback from an environment variable. - * - * @param out new signature - * @param repo repository pointer - * @return 0 on success, GIT_ENOTFOUND if config is missing, or error code - */ -GIT_EXTERN(int) git_signature_default_committer(git_signature **out, git_repository *repo); +GIT_EXTERN(int) git_signature_default_from_env( + git_signature **author_out, + git_signature **committer_out, + git_repository *repo); /** * Create a new action signature with default user and now timestamp. * - * Warning: This function may be deprecated in the future. Use one of - * git_signature_default_author or git_signature_default_committer instead. - * These are more compliant with how git constructs default signatures. - * * This looks up the user.name and user.email from the configuration and * uses the current time as the timestamp, and creates a new signature * based on that information. It will return GIT_ENOTFOUND if either the * user.name or user.email are not set. * + * Note that these do not examine environment variables, only the + * configuration files. Use `git_signature_default_from_env` to + * consider the environment variables. + * * @param out new signature * @param repo repository pointer * @return 0 on success, GIT_ENOTFOUND if config is missing, or error code diff --git a/src/libgit2/signature.c b/src/libgit2/signature.c index e6b1ac662..71da41623 100644 --- a/src/libgit2/signature.c +++ b/src/libgit2/signature.c @@ -153,15 +153,10 @@ int git_signature__pdup(git_signature **dest, const git_signature *source, git_p return 0; } -int git_signature_now(git_signature **sig_out, const char *name, const char *email) +static void current_time(time_t *now_out, int *offset_out) { - time_t now; time_t offset; - struct tm *utc_tm; - git_signature *sig; - struct tm _utc; - - *sig_out = NULL; + struct tm _utc, *utc_tm; /* * Get the current time as seconds since the epoch and @@ -171,21 +166,28 @@ int git_signature_now(git_signature **sig_out, const char *name, const char *ema * us that time as seconds since the epoch. The difference * between its return value and 'now' is our offset to UTC. */ - time(&now); - utc_tm = p_gmtime_r(&now, &_utc); + time(now_out); + utc_tm = p_gmtime_r(now_out, &_utc); utc_tm->tm_isdst = -1; - offset = (time_t)difftime(now, mktime(utc_tm)); + offset = (time_t)difftime(*now_out, mktime(utc_tm)); offset /= 60; - if (git_signature_new(&sig, name, email, now, (int)offset) < 0) - return -1; - - *sig_out = sig; - - return 0; + *offset_out = (int)offset; +} + +int git_signature_now( + git_signature **sig_out, + const char *name, + const char *email) +{ + time_t now; + int offset; + + current_time(&now, &offset); + + return git_signature_new(sig_out, name, email, now, offset); } -#ifndef GIT_DEPRECATE_HARD int git_signature_default(git_signature **out, git_repository *repo) { int error; @@ -202,10 +204,15 @@ int git_signature_default(git_signature **out, git_repository *repo) git_config_free(cfg); return error; } -#endif -static int git_signature__default_from_env(const char *name_env_var, const char *email_env_var, - const char *date_env_var, git_signature **out, git_repository *repo) +static int user_from_env( + git_signature **out, + git_repository *repo, + const char *name_env_var, + const char *email_env_var, + const char *date_env_var, + time_t default_time, + int default_offset) { int error; git_config *cfg; @@ -215,46 +222,53 @@ static int git_signature__default_from_env(const char *name_env_var, const char git_str name_env = GIT_STR_INIT; git_str email_env = GIT_STR_INIT; git_str date_env = GIT_STR_INIT; - int have_email_env = -1; if ((error = git_repository_config_snapshot(&cfg, repo)) < 0) return error; /* Check if the environment variable for the name is set */ - if (!(git__getenv(&name_env, name_env_var))) + if (!(git__getenv(&name_env, name_env_var))) { name = git_str_cstr(&name_env); - else + } else { /* or else read the configuration value. */ if ((error = git_config_get_string(&name, cfg, "user.name")) < 0) goto done; + } /* Check if the environment variable for the email is set. */ - if (!(git__getenv(&email_env, email_env_var))) + if (!(git__getenv(&email_env, email_env_var))) { email = git_str_cstr(&email_env); - else { - /* Check if the fallback EMAIL environment variable is set - * before we check the configuration so that we preserve the - * error message if the configuration value is missing. */ - git_str_dispose(&email_env); - have_email_env = !git__getenv(&email_env, "EMAIL"); - if ((error = git_config_get_string(&email, cfg, "user.email")) < 0) { - if (have_email_env) { - email = git_str_cstr(&email_env); - error = 0; - } else + } else { + if ((error = git_config_get_string(&email, cfg, "user.email")) == GIT_ENOTFOUND) { + git_error *last_error; + + git_error_save(&last_error); + + if ((error = git__getenv(&email_env, "EMAIL")) < 0) { + git_error_restore(last_error); + error = GIT_ENOTFOUND; goto done; + } + + email = git_str_cstr(&email_env); + git_error_free(last_error); + } else if (error < 0) { + goto done; } } /* Check if the environment variable for the timestamp is set */ if (!(git__getenv(&date_env, date_env_var))) { date = git_str_cstr(&date_env); + if ((error = git_date_offset_parse(×tamp, &offset, date)) < 0) goto done; - error = git_signature_new(out, name, email, timestamp, offset); - } else - /* or else default to the current timestamp. */ - error = git_signature_now(out, name, email); + } else { + timestamp = default_time; + offset = default_offset; + } + + error = git_signature_new(out, name, email, timestamp, offset); done: git_config_free(cfg); @@ -264,16 +278,45 @@ done: return error; } -int git_signature_default_author(git_signature **out, git_repository *repo) +int git_signature_default_from_env( + git_signature **author_out, + git_signature **committer_out, + git_repository *repo) { - return git_signature__default_from_env("GIT_AUTHOR_NAME", "GIT_AUTHOR_EMAIL", - "GIT_AUTHOR_DATE", out, repo); -} + git_signature *author = NULL, *committer = NULL; + time_t now; + int offset; + int error; -int git_signature_default_committer(git_signature **out, git_repository *repo) -{ - return git_signature__default_from_env("GIT_COMMITTER_NAME", "GIT_COMMITTER_EMAIL", - "GIT_COMMITTER_DATE", out, repo); + GIT_ASSERT_ARG(author_out || committer_out); + GIT_ASSERT_ARG(repo); + + current_time(&now, &offset); + + if (author_out && + (error = user_from_env(&author, repo, "GIT_AUTHOR_NAME", + "GIT_AUTHOR_EMAIL", "GIT_AUTHOR_DATE", + now, offset)) < 0) + goto on_error; + + if (committer_out && + (error = user_from_env(&committer, repo, "GIT_COMMITTER_NAME", + "GIT_COMMITTER_EMAIL", "GIT_COMMITTER_DATE", + now, offset)) < 0) + goto on_error; + + if (author_out) + *author_out = author; + + if (committer_out) + *committer_out = committer; + + return 0; + +on_error: + git__free(author); + git__free(committer); + return error; } int git_signature__parse(git_signature *sig, const char **buffer_out, diff --git a/tests/libgit2/commit/signature.c b/tests/libgit2/commit/signature.c index 2ad91f3f3..3fa6646cf 100644 --- a/tests/libgit2/commit/signature.c +++ b/tests/libgit2/commit/signature.c @@ -167,7 +167,7 @@ void test_commit_signature__cleanup(void) g_repo = NULL; } -void test_commit_signature__signature_default(void) +void test_commit_signature__from_env(void) { git_signature *author_sign, *committer_sign; git_config *cfg, *local; @@ -179,14 +179,12 @@ void test_commit_signature__signature_default(void) cl_setenv("GIT_AUTHOR_EMAIL", NULL); cl_setenv("GIT_COMMITTER_NAME", NULL); cl_setenv("GIT_COMMITTER_EMAIL", NULL); - cl_git_fail(git_signature_default_author(&author_sign, g_repo)); - cl_git_fail(git_signature_default_committer(&committer_sign, g_repo)); + cl_git_fail(git_signature_default_from_env(&author_sign, &committer_sign, g_repo)); /* Name is read from configuration and email is read from fallback EMAIL * environment variable */ cl_git_pass(git_config_set_string(local, "user.name", "Name (config)")); cl_setenv("EMAIL", "email-envvar@example.com"); - cl_git_pass(git_signature_default_author(&author_sign, g_repo)); - cl_git_pass(git_signature_default_committer(&committer_sign, g_repo)); + cl_git_pass(git_signature_default_from_env(&author_sign, &committer_sign, g_repo)); cl_assert_equal_s("Name (config)", author_sign->name); cl_assert_equal_s("email-envvar@example.com", author_sign->email); cl_assert_equal_s("Name (config)", committer_sign->name); @@ -200,8 +198,7 @@ void test_commit_signature__signature_default(void) cl_setenv("GIT_AUTHOR_EMAIL", "author-envvar@example.com"); cl_setenv("GIT_COMMITTER_NAME", "Committer (envvar)"); cl_setenv("GIT_COMMITTER_EMAIL", "committer-envvar@example.com"); - cl_git_pass(git_signature_default_author(&author_sign, g_repo)); - cl_git_pass(git_signature_default_committer(&committer_sign, g_repo)); + cl_git_pass(git_signature_default_from_env(&author_sign, &committer_sign, g_repo)); cl_assert_equal_s("Author (envvar)", author_sign->name); cl_assert_equal_s("author-envvar@example.com", author_sign->email); cl_assert_equal_s("Committer (envvar)", committer_sign->name); @@ -214,8 +211,7 @@ void test_commit_signature__signature_default(void) cl_setenv("GIT_AUTHOR_EMAIL", NULL); cl_setenv("GIT_COMMITTER_NAME", NULL); cl_setenv("GIT_COMMITTER_EMAIL", NULL); - cl_git_pass(git_signature_default_author(&author_sign, g_repo)); - cl_git_pass(git_signature_default_committer(&committer_sign, g_repo)); + cl_git_pass(git_signature_default_from_env(&author_sign, &committer_sign, g_repo)); cl_assert_equal_s("Name (config)", author_sign->name); cl_assert_equal_s("config@example.com", author_sign->email); cl_assert_equal_s("Name (config)", committer_sign->name); @@ -225,8 +221,7 @@ void test_commit_signature__signature_default(void) /* We can also override the timestamp with an environment variable */ cl_setenv("GIT_AUTHOR_DATE", "1971-02-03 04:05:06+01"); cl_setenv("GIT_COMMITTER_DATE", "1988-09-10 11:12:13-01"); - cl_git_pass(git_signature_default_author(&author_sign, g_repo)); - cl_git_pass(git_signature_default_committer(&committer_sign, g_repo)); + cl_git_pass(git_signature_default_from_env(&author_sign, &committer_sign, g_repo)); cl_assert_equal_i(34398306, author_sign->when.time); /* 1971-02-03 03:05:06 UTC */ cl_assert_equal_i(60, author_sign->when.offset); cl_assert_equal_i(589896733, committer_sign->when.time); /* 1988-09-10 12:12:13 UTC */