Subject: Re: [PATCH] Trick to force setup of a specific configured E-Mail per repo

From: Dan Aloni <[email-masked]>
Date: Wed, 3 Feb 2016 10:21:12 +0200

(resend - my mailer was misconfigured)

On Tue, Feb 02, 2016 at 10:56:48PM -0500, Jeff King wrote:
> On Tue, Feb 02, 2016 at 09:54:21PM +0200, Dan Aloni wrote:
>[..]
> > +	if (strict && email && !strcmp(email, "(per-repo)")) {
> > +		die("email is '(per-repo)', suggesting to set specific email "
> > +		    "for the current repo");
> > +	}
> 
> I find it disappointing that we go back to looking for magic sequences
> in the string. Could we perhaps do this more cleanly with a new config
> option? Like a "user.guessIdent" which defaults to true, but people can
> set to false. And without that, we do not do any automagic at all; we
> get the values from the GIT_COMMITTER_* environment or the
> user.{name,email} config variables, or we die().
>[..]

Agreed. New patch attached, feel free to amend.

-- 
Dan Aloni

>>From 35d94d4a00af70eeaf6b291ec951f555b0bc99d3 Mon Sep 17 00:00:00 2001
From: Dan Aloni <[email-masked]>
Date: Wed, 3 Feb 2016 10:09:40 +0200
Subject: [PATCH] Add user.explicit boolean for when ident shouldn't be guessed

Previously, before 5498c57cdd63, many people did the following:

   git config --global user.email "(none)"

This was helpful for people with more than one E-Mail address,
targeting different E-Mail addresses for different clones.
as it barred git from creating commit unless the user.email
config was set in the per-repo config to the correct E-Mail
address.

Now, since the original 'bug' was fixed, and practically every
string is acceptable for user.email and user.name, it is best
to reimplement the feature not as an exploit of a bug, but as
an actual feature.

Signed-off-by: Dan Aloni <[email-masked]>
---
 Documentation/config.txt  |  8 ++++++++
 ident.c                   | 51 +++++++++++++++++++++++++++++++++++++++++++++++
 t/t9904-per-repo-email.sh | 36 +++++++++++++++++++++++++++++++++
 3 files changed, 95 insertions(+)
 create mode 100755 t/t9904-per-repo-email.sh

diff --git a/Documentation/config.txt b/Documentation/config.txt
index 877cbc875ec3..4c99f8f1de4f 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -2791,6 +2791,14 @@ user.name::
 	Can be overridden by the 'GIT_AUTHOR_NAME' and 'GIT_COMMITTER_NAME'
 	environment variables.  See linkgit:git-commit-tree[1].
 
+user.explicit::
+	This instruct Git to avoid trying to guess defaults for 'user.email'
+	and 'user.name' other than strictly from environment or config.
+	If you have multiply E-Mail addresses that you would like to set
+	up per repository, you may want to set this to 'true' in the global
+	config, and then Git would prompt you to set user.email separately,
+	in each of the cloned repositories.
+
 user.signingKey::
 	If linkgit:git-tag[1] or linkgit:git-commit[1] is not selecting the
 	key you want it to automatically when creating a signed tag or
diff --git a/ident.c b/ident.c
index 9dd3ae345255..305dc32a8eaf 100644
--- a/ident.c
+++ b/ident.c
@@ -18,6 +18,7 @@ static int default_name_is_bogus;
 #define IDENT_ALL_GIVEN (IDENT_NAME_GIVEN|IDENT_MAIL_GIVEN)
 static int committer_ident_explicitly_given;
 static int author_ident_explicitly_given;
+static int ident_explicit = 0;
 
 #ifdef NO_GECOS_IN_PWENT
 #define get_gecos(ignored) "&"
@@ -373,6 +374,23 @@ const char *fmt_ident(const char *name, const char *email,
 		die("unable to auto-detect email address (got '%s')", email);
 	}
 
+	if (ident_explicit) {
+		if (name == git_default_name.buf  &&
+		    !(committer_ident_explicitly_given & IDENT_NAME_GIVEN) &&
+		    !(author_ident_explicitly_given & IDENT_NAME_GIVEN)) {
+			die("requested explicitly given ident in config, "
+			    "but user.name is not set, or environment is "
+			    "not set");
+		}
+		if (email == git_default_email.buf  &&
+		    !(committer_ident_explicitly_given & IDENT_MAIL_GIVEN) &&
+		    !(author_ident_explicitly_given & IDENT_MAIL_GIVEN)) {
+			die("requested explicitly given ident in config, "
+			    "but user.email is not set, or environment is "
+			    "not set");
+		}
+	}
+
 	strbuf_reset(&ident);
 	if (want_name) {
 		strbuf_addstr_without_crud(&ident, name);
@@ -405,6 +423,20 @@ const char *git_author_info(int flag)
 		author_ident_explicitly_given |= IDENT_NAME_GIVEN;
 	if (getenv("GIT_AUTHOR_EMAIL"))
 		author_ident_explicitly_given |= IDENT_MAIL_GIVEN;
+
+	if (ident_explicit) {
+		if (!(author_ident_explicitly_given & IDENT_NAME_GIVEN)) {
+			die("requested explicitly given ident in config, "
+			    "but user.name is not set, or GIT_AUTHOR_NAME "
+			    "is not set");
+		}
+		if (!(author_ident_explicitly_given & IDENT_MAIL_GIVEN)) {
+			die("requested explicitly given ident in config, "
+			    "but user.email is not set, or GIT_AUTHOR_EMAIL "
+			    "is not set");
+		}
+	}
+
 	return fmt_ident(getenv("GIT_AUTHOR_NAME"),
 			 getenv("GIT_AUTHOR_EMAIL"),
 			 getenv("GIT_AUTHOR_DATE"),
@@ -417,6 +449,20 @@ const char *git_committer_info(int flag)
 		committer_ident_explicitly_given |= IDENT_NAME_GIVEN;
 	if (getenv("GIT_COMMITTER_EMAIL"))
 		committer_ident_explicitly_given |= IDENT_MAIL_GIVEN;
+
+	if (ident_explicit) {
+		if (!(committer_ident_explicitly_given & IDENT_NAME_GIVEN)) {
+			die("requested explicitly given ident in config, "
+			    "but user.name is not set, or GIT_COMMITTER_NAME "
+			    "is not set");
+		}
+		if (!(committer_ident_explicitly_given & IDENT_MAIL_GIVEN)) {
+			die("requested explicitly given ident in config, "
+			    "but user.email is not set, or GIT_COMMITTER_EMAIL "
+			    "is not set");
+		}
+	}
+
 	return fmt_ident(getenv("GIT_COMMITTER_NAME"),
 			 getenv("GIT_COMMITTER_EMAIL"),
 			 getenv("GIT_COMMITTER_DATE"),
@@ -444,6 +490,11 @@ int author_ident_sufficiently_given(void)
 
 int git_ident_config(const char *var, const char *value, void *data)
 {
+	if (!strcmp(var, "user.explicit")) {
+		ident_explicit = git_config_bool(var, value);
+		return 0;
+	}
+
 	if (!strcmp(var, "user.name")) {
 		if (!value)
 			return config_error_nonbool(var);
diff --git a/t/t9904-per-repo-email.sh b/t/t9904-per-repo-email.sh
new file mode 100755
index 000000000000..5876c9150ca6
--- /dev/null
+++ b/t/t9904-per-repo-email.sh
@@ -0,0 +1,36 @@
+#!/bin/sh
+#
+# Copyright (c) 2016 Dan Aloni
+#
+
+test_description='per-repo forced setting of E-Mail address'
+
+. ./test-lib.sh
+
+test_expect_failure 'fails commiting if clone email is not set' '
+	echo "Initial" >foo &&
+	git add foo &&
+	unset GIT_AUTHOR_NAME &&
+	unset GIT_AUTHOR_EMAIL &&
+	(git config --global --unset user.name || true) &&
+	(git config --global --unset user.email || true) &&
+	(git config --unset user.name || true) &&
+	(git config --unset user.email || true) &&
+	git config --global user.explicit true &&
+	EDITOR=: VISUAL=: git commit -m msg
+
+'
+
+test_expect_success 'succeeds commiting if clone email is set' '
+	echo "Initial" >foo &&
+	git add foo &&
+	unset GIT_AUTHOR_EMAIL &&
+	(git config --global --unset user.name || true) &&
+	(git config --global --unset user.email || true) &&
+	git config --global user.explicit true &&
+	git config user.name "test" &&
+	git config user.email "[email-masked]" &&
+	EDITOR=: VISUAL=: git commit -m msg
+'
+
+test_done
-- 
2.5.0