From c352cfdda7dbd3f9185d4c0b1b4472c69cd64399 Mon Sep 17 00:00:00 2001
From: Gisle Aas <gisle@aas.no>
Date: Mon, 5 Jul 2010 17:45:30 +0200
Subject: [PATCH] Set ident environment variables for the commit hooks to observe

The GIT_{AUTHOR,COMMITTER}_{NAME,EMAIL} environment variables are set to
reflect the values about to be committed.
---
 Documentation/githooks.txt |    4 ++-
 builtin/commit.c           |    9 ++++---
 cache.h                    |    2 +
 ident.c                    |   46 ++++++++++++++++++++++++++++++-------------
 t/t7503-pre-commit-hook.sh |   26 ++++++++++++++++++++++++
 5 files changed, 68 insertions(+), 19 deletions(-)

diff --git a/Documentation/githooks.txt b/Documentation/githooks.txt
index 7183aa9..a330a67 100644
--- a/Documentation/githooks.txt
+++ b/Documentation/githooks.txt
@@ -84,7 +84,9 @@ such a line is found.
 
 All the 'git commit' hooks are invoked with the environment
 variable `GIT_EDITOR=:` if the command will not bring up an editor
-to modify the commit message.
+to modify the commit message.  The 'GIT_AUTHOR_NAME', 'GIT_AUTHOR_EMAIL',
+'GIT_COMMITTER_NAME', 'GIT_COMMITTER_EMAIL' environment variables
+will also be set.
 
 prepare-commit-msg
 ~~~~~~~~~~~~~~~~~~
diff --git a/builtin/commit.c b/builtin/commit.c
index c101f00..06e9342 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -557,6 +557,11 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
 	const char *hook_arg2 = NULL;
 	int ident_shown = 0;
 
+	determine_author_info();
+	git_committer_info(0); /* This checks if committer ident is explicitly given */
+	git_setenv_ident(author_name, author_email,
+			 getenv("GIT_COMMITTER_NAME"), getenv("GIT_COMMITTER_EMAIL"));
+
 	if (!no_verify && run_hook(index_file, "pre-commit", NULL))
 		return 0;
 
@@ -632,10 +637,6 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
 
 	strbuf_release(&sb);
 
-	determine_author_info();
-
-	/* This checks if committer ident is explicitly given */
-	git_committer_info(0);
 	if (use_editor && include_status) {
 		char *author_ident;
 		const char *committer_ident;
diff --git a/cache.h b/cache.h
index c9fa3df..2e70ff7 100644
--- a/cache.h
+++ b/cache.h
@@ -822,6 +822,8 @@ enum date_mode parse_date_format(const char *format);
 #define IDENT_NO_DATE	       4
 extern const char *git_author_info(int);
 extern const char *git_committer_info(int);
+extern void git_setenv_ident(const char *author_name, const char *author_email,
+			     const char *committer_name, const char *committer_email);
 extern const char *fmt_ident(const char *name, const char *email, const char *date_str, int);
 extern const char *fmt_name(const char *name, const char *email);
 extern const char *git_editor(void);
diff --git a/ident.c b/ident.c
index 9e24388..190e4a0 100644
--- a/ident.c
+++ b/ident.c
@@ -182,39 +182,45 @@ static const char *env_hint =
 "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 void get_ident(const char **name, const char **email, 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();
-	if (!name)
-		name = git_default_name;
-	if (!email)
-		email = git_default_email;
+	if (!*name)
+		*name = git_default_name;
+	if (!*email)
+		*email = git_default_email;
 
-	if (!*name) {
+	if (!**name) {
 		struct passwd *pw;
 
 		if ((warn_on_no_name || error_on_no_name) &&
-		    name == git_default_name && env_hint) {
+		    *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);
+			die("empty ident %s <%s> not allowed", *name, *email);
 		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;
+		*name = git_default_name;
 	}
+}
+
+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 name_addr_only = (flag & IDENT_NO_DATE);
+
+	get_ident(&name, &email, flag);
 
 	strcpy(date, git_default_date);
 	if (!name_addr_only && date_str)
@@ -240,6 +246,18 @@ const char *fmt_name(const char *name, const char *email)
 	return fmt_ident(name, email, NULL, IDENT_ERROR_ON_NO_NAME | IDENT_NO_DATE);
 }
 
+void git_setenv_ident(const char *author_name, const char *author_email,
+		      const char *committer_name, const char *committer_email)
+{
+	get_ident(&author_name, &author_email, IDENT_ERROR_ON_NO_NAME);
+	setenv("GIT_AUTHOR_NAME", author_name, 1);
+	setenv("GIT_AUTHOR_EMAIL", author_email, 1);
+
+	get_ident(&committer_name, &committer_email, IDENT_ERROR_ON_NO_NAME);
+	setenv("GIT_COMMITTER_NAME", committer_name, 1);
+	setenv("GIT_COMMITTER_EMAIL", committer_email, 1);
+}
+
 const char *git_author_info(int flag)
 {
 	return fmt_ident(getenv("GIT_AUTHOR_NAME"),
diff --git a/t/t7503-pre-commit-hook.sh b/t/t7503-pre-commit-hook.sh
index 8528f64..21ac57d 100755
--- a/t/t7503-pre-commit-hook.sh
+++ b/t/t7503-pre-commit-hook.sh
@@ -85,4 +85,30 @@ test_expect_success POSIXPERM '--no-verify with non-executable hook' '
 
 '
 
+# a hook that test author
+cat > "$HOOK" <<EOT
+#!/bin/sh
+case "\$GIT_AUTHOR_NAME" in Superman) exit 0;; esac
+echo "Only patches from Superman is accepted now"
+exit 1
+EOT
+cat "$HOOK"
+chmod +x "$HOOK"
+
+test_expect_success 'with failing hook' '
+
+	echo "another" >> file &&
+	git add file &&
+	test_must_fail git commit -m "another"
+
+'
+
+test_expect_success 'with non-failing hook' '
+
+	echo "another" >> file &&
+	git add file &&
+	git commit -m "another" --author "Superman <root@example.com>"
+
+'
+
 test_done
-- 
1.6.6.rc1.31.g1a56b

