Git development
 help / color / mirror / Atom feed
* Re: [IGNORETHIS/PATCH] Choosing the sha1 prefix of your commits
From: Jeff King @ 2011-10-20  4:34 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Ævar Arnfjörð Bjarmason, Git Mailing List
In-Reply-To: <7vvcrk9td7.fsf@alter.siamese.dyndns.org>

On Wed, Oct 19, 2011 at 09:31:16PM -0700, Junio C Hamano wrote:

> Jeff King <peff@peff.net> writes:
> 
> > And nothing shows up in the body, because git truncates at the NUL we
> > added:
> >
> >   $ git show
> >   commit 31337a1093af2d97eb2e6c08b261c2946395fdd3
> >   Author: Jeff King <peff@peff.net>
> >   Date:   Wed Oct 19 15:34:00 2011 -0400
> >
> >       10
> >
> >   diff --git a/file b/file
> 
> But you cannot hide from "cat-file commit" ;-)

Yes. The implementation is a horrible hack, second only in grossness to
the original idea. :)

> With the recent push to more (perceived) security, it may probably make
> sense to teach "log" family commands to quote-show ^@ and what is behind
> in their output by default, perhaps with an option to turn it off.

Agreed. Having hidden cruft makes birthday collision attacks easier (or
it will, if sha1 ever gets broken to that point).  Unfortunately, there
is a _ton_ of code which assumes that commit messages are
NUL-terminated, as they always have been since e871b64 (2005-05-25).

-Peff

^ permalink raw reply

* Re: [IGNORETHIS/PATCH] Choosing the sha1 prefix of your commits
From: Kyle Moffett @ 2011-10-20  4:32 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Jeff King, Ævar Arnfjörð, Git Mailing List
In-Reply-To: <7vzkgw9tjw.fsf@alter.siamese.dyndns.org>

On Thu, Oct 20, 2011 at 00:27, Junio C Hamano <gitster@pobox.com> wrote:
> Kyle Moffett <kyle@moffetthome.net> writes:
>
>> Heh, there's one other practical downside I can think of...
>>
>> If you create a bunch of commits with the same 8-hex-character prefix
>> then suddenly the "git describe" logic for using the first 7 commit ID
>> characters gets a whole lot less useful.
>
> In the sense that you need to cut and paste a lot more characters, you are
> correct that it would make it less useful, but if you are talking about
> uniqueness, you are mistaken.
>
> The rule is not "using the first 7 hexdigits", but is "using as many
> hexdigits to make assure uniqueness, but use at least 7".

Well, yes, but if you generate some 10 commits with the same
7-character prefix, run "git describe", and then generate another
several with the same prefix... Chances are the previously-unique
output of "git describe" is no longer unique.

Also, even if you do use enough characters for it to be reliably
unique, it's not really an abbreviation if your commit description is
"v2.0.3-42-gdeadbeef04a69f", with that many characters you might as
well just paste the whole SHA1 sum.

With all that said, it is a very clever (and ugly) hack :-D.  Kudos!

Cheers,
Kyle Moffett

-- 
Curious about my work on the Debian powerpcspe port?
I'm keeping a blog here: http://pureperl.blogspot.com/

^ permalink raw reply

* Re: [IGNORETHIS/PATCH] Choosing the sha1 prefix of your commits
From: Junio C Hamano @ 2011-10-20  4:31 UTC (permalink / raw)
  To: Jeff King; +Cc: Ævar Arnfjörð Bjarmason, Git Mailing List
In-Reply-To: <20111019193834.GA14168@sigill.intra.peff.net>

Jeff King <peff@peff.net> writes:

> And nothing shows up in the body, because git truncates at the NUL we
> added:
>
>   $ git show
>   commit 31337a1093af2d97eb2e6c08b261c2946395fdd3
>   Author: Jeff King <peff@peff.net>
>   Date:   Wed Oct 19 15:34:00 2011 -0400
>
>       10
>
>   diff --git a/file b/file

But you cannot hide from "cat-file commit" ;-)

With the recent push to more (perceived) security, it may probably make
sense to teach "log" family commands to quote-show ^@ and what is behind
in their output by default, perhaps with an option to turn it off.

^ permalink raw reply

* Re: [IGNORETHIS/PATCH] Choosing the sha1 prefix of your commits
From: Junio C Hamano @ 2011-10-20  4:27 UTC (permalink / raw)
  To: Kyle Moffett; +Cc: Jeff King, Ævar Arnfjörð, Git Mailing List
In-Reply-To: <CAGZ=bqK2oVPxW3mm-WHMd1+KSiPquympJyhRqLWr1F=G74p+BA@mail.gmail.com>

Kyle Moffett <kyle@moffetthome.net> writes:

> Heh, there's one other practical downside I can think of...
>
> If you create a bunch of commits with the same 8-hex-character prefix
> then suddenly the "git describe" logic for using the first 7 commit ID
> characters gets a whole lot less useful.

In the sense that you need to cut and paste a lot more characters, you are
correct that it would make it less useful, but if you are talking about
uniqueness, you are mistaken.

The rule is not "using the first 7 hexdigits", but is "using as many
hexdigits to make assure uniqueness, but use at least 7".

^ permalink raw reply

* Re: [IGNORETHIS/PATCH] Choosing the sha1 prefix of your commits
From: Jeff King @ 2011-10-20  4:25 UTC (permalink / raw)
  To: Kyle Moffett; +Cc: Ævar Arnfjörð, Git Mailing List
In-Reply-To: <CAGZ=bqK2oVPxW3mm-WHMd1+KSiPquympJyhRqLWr1F=G74p+BA@mail.gmail.com>

On Thu, Oct 20, 2011 at 12:15:03AM -0400, Kyle Moffett wrote:

> On Wed, Oct 19, 2011 at 22:51, Jeff King <peff@peff.net> wrote:
> > Keep in mind that each hex character you add increases the search space
> > by a factor of 16. deadbeef took about 70 seconds to find on my machine.
> > I'm tempted to look for "3133700..0031337", but it would probably
> > take about 4 hours.
> 
> Heh, there's one other practical downside I can think of...
> 
> If you create a bunch of commits with the same 8-hex-character prefix
> then suddenly the "git describe" logic for using the first 7 commit ID
> characters gets a whole lot less useful.

Actually, git will generally find a unique abbreviation among all of
your objects when using abbreviated sha1s, so you'll just get longer
abbreviations. Of course, it is only unique at the time of generation,
so new objects may make it ambiguous. Which is why the default minimum
is 7, not 1.  But of course with this trick you've effectively removed
all of the entropy from those initial 7 characters, and you effectively
only have 1 or 2 non-uniform characters.

So yeah, it is worse.  But really...spending millions of CPU cycles to
get a preimage collision with the partial sha1, and hack-ishly embedding
random crap after a NUL in every commit message, and _this_ is your
complaint? ;)

-Peff

^ permalink raw reply

* Re: [IGNORETHIS/PATCH] Choosing the sha1 prefix of your commits
From: Kyle Moffett @ 2011-10-20  4:15 UTC (permalink / raw)
  To: Jeff King; +Cc: Ævar Arnfjörð, Git Mailing List
In-Reply-To: <20111020025149.GA31549@sigill.intra.peff.net>

On Wed, Oct 19, 2011 at 22:51, Jeff King <peff@peff.net> wrote:
> Keep in mind that each hex character you add increases the search space
> by a factor of 16. deadbeef took about 70 seconds to find on my machine.
> I'm tempted to look for "3133700..0031337", but it would probably
> take about 4 hours.

Heh, there's one other practical downside I can think of...

If you create a bunch of commits with the same 8-hex-character prefix
then suddenly the "git describe" logic for using the first 7 commit ID
characters gets a whole lot less useful.

Cheers,
Kyle Moffett

-- 
Curious about my work on the Debian powerpcspe port?
I'm keeping a blog here: http://pureperl.blogspot.com/

^ permalink raw reply

* Re: [IGNORETHIS/PATCH] Choosing the sha1 prefix of your commits
From: Jeff King @ 2011-10-20  2:51 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason; +Cc: Git Mailing List
In-Reply-To: <20111019193834.GA14168@sigill.intra.peff.net>

On Wed, Oct 19, 2011 at 03:38:34PM -0400, Jeff King wrote:

> It also parameterizes the desired sha1, so you could easily find hashes
> ending in 31337, or any other pattern. Or add "git commit
> --collide=31337".

I couldn't resist:

  $ git commit -q -m foo --collide=deadbeef &&
    git rev-list -1 HEAD
  deadbeefdbd6e62a2185606a4fad653e22509b56

You can also do:

  $ SHA1=0000000000000000000000000000000000031337
  $ MASK=00000000000000000000000000000000000fffff
  $ git commit -q -m foo --collide=$SHA1/$MASK &&
    git rev-list -1 HEAD
  ea49af84db92ed0d2bc3ed13810f5990e7c31337

Keep in mind that each hex character you add increases the search space
by a factor of 16. deadbeef took about 70 seconds to find on my machine.
I'm tempted to look for "3133700..0031337", but it would probably
take about 4 hours.

Patch is below.

-Peff

---
diff --git a/builtin/commit.c b/builtin/commit.c
index c46f2d1..734a7ab 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -105,6 +105,10 @@
 static const char *only_include_assumed;
 static struct strbuf message;
 
+static int collide;
+static unsigned char collide_sha1[20];
+static unsigned char collide_mask[20];
+
 static int null_termination;
 static enum {
 	STATUS_FORMAT_LONG,
@@ -125,6 +129,52 @@ static int opt_parse_m(const struct option *opt, const char *arg, int unset)
 	return 0;
 }
 
+static int parse_partial_sha1(const char *s, unsigned char sha1[20])
+{
+	unsigned int i;
+
+	hashclr(sha1);
+
+	for (i = 0; i < 40 && s[i]; i++) {
+		unsigned int v = hexval(s[i]);
+		if (v & ~0xf)
+			break;
+		if (!(i & 1))
+			v <<= 4;
+		sha1[i/2] |= v;
+	}
+	return i;
+}
+
+static void fill_sha1_mask(int n, unsigned char mask[20]) {
+	int i;
+
+	hashclr(mask);
+	for (i = 0; i < n/2; i++)
+		mask[i] = 0xff;
+	if (n & 1)
+		mask[i] = 0xf0;
+}
+
+static int opt_parse_collide(const struct option *opt, const char *arg,
+			     int unset)
+{
+	if (unset)
+		collide = 0;
+	else {
+		int n = parse_partial_sha1(arg, collide_sha1);
+		if (!arg[n])
+			fill_sha1_mask(n, collide_mask);
+		else if (arg[n] == '/')
+			parse_partial_sha1(arg + n + 1, collide_mask);
+		else
+			die("invalid --collide sha1: %s", arg);
+		collide = 1;
+	}
+	return 0;
+}
+
+
 static struct option builtin_commit_options[] = {
 	OPT__QUIET(&quiet, "suppress summary after successful commit"),
 	OPT__VERBOSE(&verbose, "show diff in commit message template"),
@@ -144,6 +194,7 @@ static int opt_parse_m(const struct option *opt, const char *arg, int unset)
 	OPT_BOOLEAN('e', "edit", &edit_flag, "force edit of commit"),
 	OPT_STRING(0, "cleanup", &cleanup_arg, "default", "how to strip spaces and #comments from message"),
 	OPT_BOOLEAN(0, "status", &include_status, "include status in commit message template"),
+	OPT_CALLBACK(0, "collide", NULL, "sha1[/mask]", "choose commit sha1 like <sha1>", opt_parse_collide),
 	/* end commit message options */
 
 	OPT_GROUP("Commit contents options"),
@@ -1483,8 +1534,9 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
 		exit(1);
 	}
 
-	if (commit_tree(sb.buf, active_cache_tree->sha1, parents, sha1,
-			author_ident.buf)) {
+	if (commit_tree_collide(sb.buf, active_cache_tree->sha1, parents, sha1,
+				author_ident.buf,
+				collide ? collide_sha1 : NULL, collide_mask)) {
 		rollback_index_files();
 		die(_("failed to write commit object"));
 	}
diff --git a/commit.c b/commit.c
index 73b7e00..24ddccd 100644
--- a/commit.c
+++ b/commit.c
@@ -840,6 +840,54 @@ struct commit_list *reduce_heads(struct commit_list *heads)
 	return result;
 }
 
+static inline int sha1_match_mask(const unsigned char *sha1,
+				  const unsigned char *want,
+				  const unsigned char *mask)
+{
+	int i;
+	for (i = 0; i < 20; i++)
+		if ((want[i] & mask[i]) != (sha1[i] & mask[i]))
+		    return 0;
+	return 1;
+}
+
+static void collide_commit(struct strbuf *data,
+			   const unsigned char *want,
+			   const unsigned char *mask)
+{
+	static const char terminator[] = { 0 };
+	char header[32];
+	int header_len;
+	unsigned int lulz;
+	SHA_CTX base;
+
+	header_len = snprintf(header, sizeof(header),
+			      "commit %lu",
+			      data->len + 1 + sizeof(lulz)) + 1;
+	SHA1_Init(&base);
+	SHA1_Update(&base, header, header_len);
+	SHA1_Update(&base, data->buf, data->len);
+	SHA1_Update(&base, terminator, sizeof(terminator));
+
+	lulz = 0;
+	do {
+		SHA_CTX guess;
+		unsigned char sha1[20];
+
+		memcpy(&guess, &base, sizeof(guess));
+		SHA1_Update(&guess, &lulz, sizeof(lulz));
+		SHA1_Final(sha1, &guess);
+
+		if (sha1_match_mask(sha1, want, mask)) {
+			strbuf_add(data, terminator, sizeof(terminator));
+			strbuf_add(data, &lulz, sizeof(lulz));
+			return;
+		}
+
+		lulz++;
+	} while (1);
+}
+
 static const char commit_utf8_warn[] =
 "Warning: commit message does not conform to UTF-8.\n"
 "You may want to amend it after fixing the message, or set the config\n"
@@ -849,6 +897,15 @@ int commit_tree(const char *msg, unsigned char *tree,
 		struct commit_list *parents, unsigned char *ret,
 		const char *author)
 {
+	return commit_tree_collide(msg, tree, parents, ret, author,
+				   NULL, NULL);
+}
+
+int commit_tree_collide(const char *msg, unsigned char *tree,
+			struct commit_list *parents, unsigned char *ret,
+			const char *author, const unsigned char *want,
+			const unsigned char *mask)
+{
 	int result;
 	int encoding_is_utf8;
 	struct strbuf buffer;
@@ -890,6 +947,9 @@ int commit_tree(const char *msg, unsigned char *tree,
 	if (encoding_is_utf8 && !is_utf8(buffer.buf))
 		fprintf(stderr, commit_utf8_warn);
 
+	if (want && mask)
+		collide_commit(&buffer, want, mask);
+
 	result = write_sha1_file(buffer.buf, buffer.len, commit_type, ret);
 	strbuf_release(&buffer);
 	return result;
diff --git a/commit.h b/commit.h
index 009b113..337dcbd 100644
--- a/commit.h
+++ b/commit.h
@@ -184,5 +184,9 @@ static inline int single_parent(struct commit *commit)
 extern int commit_tree(const char *msg, unsigned char *tree,
 		struct commit_list *parents, unsigned char *ret,
 		const char *author);
+extern int commit_tree_collide(const char *msg, unsigned char *tree,
+			       struct commit_list *parents, unsigned char *ret,
+			       const char *author, const unsigned char *sha1,
+			       const unsigned char *mask);
 
 #endif /* COMMIT_H */

^ permalink raw reply related

* Re: [PATCH 12/12] is_refname_available(): reimplement using do_for_each_ref_in_array()
From: Junio C Hamano @ 2011-10-20  1:40 UTC (permalink / raw)
  To: mhagger
  Cc: git, Jeff King, Drew Northup, Jakub Narebski, Heiko Voigt,
	Johan Herland, Julian Phillips
In-Reply-To: <1319060692-27216-13-git-send-email-mhagger@alum.mit.edu>

Hmm, why is this patch and only this one in the series full of whitespace
violations? Did you use a different settings or something?

^ permalink raw reply

* Re: [PATCH 3/6] revert: fix buffer overflow in insn sheet parser
From: Junio C Hamano @ 2011-10-20  1:30 UTC (permalink / raw)
  To: Ramkumar Ramachandra; +Cc: Git List, Jonathan Nieder, Christian Couder
In-Reply-To: <1319058208-17923-4-git-send-email-artagnon@gmail.com>

Ramkumar Ramachandra <artagnon@gmail.com> writes:

> Check that the commit name argument to a "pick" or "revert" action in
> '.git/sequencer/todo' is not too long, to avoid overflowing an
> on-stack buffer.  This fixes a regression introduced by 5a5d80f4
> (revert: Introduce --continue to continue the operation, 2011-08-04).

Given that this function is going to be fixed properly so that it does not
even need to use the "on-stack buffer", is this really necessary?

^ permalink raw reply

* Re: git-p4.skipSubmitEdit
From: Pete Wyckoff @ 2011-10-20  1:16 UTC (permalink / raw)
  To: Luke Diamand; +Cc: L. A. Linden Levy, git
In-Reply-To: <4E9DBD0B.7020505@diamand.org>

luke@diamand.org wrote on Tue, 18 Oct 2011 18:53 +0100:
> Looks good, one minor nit (see below) and a comment.
[..]
> >+        # invoke the editor
> >+        if os.environ.has_key("P4EDITOR"):
> >+            editor = os.environ.get("P4EDITOR")
> >+        else:
> >+            editor = read_pipe("git var GIT_EDITOR").strip()
> >+        system(editor + " " + template_file)
> 
> This is where we should really check the return code. However, doing
> so seems to break lots of the existing tests so it's not as easy as
> it looks.

Indeed.  I'll not fix that now, but agree it should be.

> >+
> >+        # If the file was not saved, prompt to see if this patch should
> >+        # be skipped.  But skip this verification step if configured so.
> >+        if gitConfig("git-p4.skipSubmitEditCheck") == "true":
> >+            print "return true for skipSubmitEditCheck"
> 
> You print a helpful/annoying(?) message here, but not further up at
> skipSubmitEdit?

Aargh.  Leaked debug code.  Thanks for noticing.  I got rid of
it.

		-- Pete

^ permalink raw reply

* [PATCH v4 2/5] commit: teach --gpg-sign option
From: Junio C Hamano @ 2011-10-20  0:37 UTC (permalink / raw)
  To: git
In-Reply-To: <1319071023-31919-1-git-send-email-gitster@pobox.com>

And this uses the gpg-interface.[ch] to allow signing the commit, i.e.

    $ git commit --gpg-sign -m foo
    You need a passphrase to unlock the secret key for
    user: "Junio C Hamano <gitster@pobox.com>"
    4096-bit RSA key, ID 96AFE6CB, created 2011-10-03 (main key ID 713660A7)

    [master 8457d13] foo
     1 files changed, 1 insertions(+), 0 deletions(-)

The lines of GPG detached signature are placed in new header lines, after
the standard tree/parent/author/committer headers, instead of tucking the
signature block at the end of the commit log message text (similar to how
signed tag is done), for multiple reasons:

 - The signature won't clutter output from "git log" and friends if it is
   in the extra header. If we place it at the end of the log message, we
   would need to teach "git log" and friends to strip the signature block
   with an option.

 - Teaching new versions of "git log" and "gitk" to optionally verify and
   show signatures is cleaner if we structurally know where the signature
   block is (instead of scanning in the commit log message).

 - The signature needs to be stripped upon various commit rewriting
   operations, e.g. rebase, filter-branch, etc. They all already ignore
   unknown headers, but if we place signature in the log message, all of
   these tools (and third-party tools) also need to learn how a signature
   block would look like.

 - When we added the optional encoding header, all the tools (both in tree
   and third-party) that acts on the raw commit object should have been
   fixed to ignore headers they do not understand, so it is not like that
   new header would be more likely to break than extra text in the commit.

A commit made with the above sample sequence would look like this:

    $ git cat-file commit HEAD
    tree 3cd71d90e3db4136e5260ab54599791c4f883b9d
    parent b87755351a47b09cb27d6913e6e0e17e6254a4d4
    author Junio C Hamano <gitster@pobox.com> 1317862251 -0700
    committer Junio C Hamano <gitster@pobox.com> 1317862251 -0700
    gpgsig -----BEGIN PGP SIGNATURE-----
    gpgsig Version: GnuPG v1.4.10 (GNU/Linux)
    gpgsig
    gpgsig iQIcBAABAgAGBQJOjPtrAAoJELC16IaWr+bL4TMP/RSe2Y/jYnCkds9unO5JEnfG
    gpgsig ...
    gpgsig =dt98
    gpgsig -----END PGP SIGNATURE-----

    foo

but "git log" (unless you ask for it with --pretty=raw) output is not
cluttered with the signature information.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin/commit-tree.c |   24 +++++++++++++++++++++---
 builtin/commit.c      |   12 ++++++++++--
 builtin/merge.c       |   16 ++++++++++++++--
 commit.c              |   40 +++++++++++++++++++++++++++++++++++++++-
 commit.h              |    2 +-
 notes-cache.c         |    2 +-
 notes-merge.c         |    2 +-
 7 files changed, 87 insertions(+), 11 deletions(-)

diff --git a/builtin/commit-tree.c b/builtin/commit-tree.c
index d083795..a17811f 100644
--- a/builtin/commit-tree.c
+++ b/builtin/commit-tree.c
@@ -8,8 +8,9 @@
 #include "tree.h"
 #include "builtin.h"
 #include "utf8.h"
+#include "gpg-interface.h"
 
-static const char commit_tree_usage[] = "git commit-tree <sha1> [(-p <sha1>)...] < changelog";
+static const char commit_tree_usage[] = "git commit-tree [-S<signer>] <sha1> [(-p <sha1>)...] < changelog";
 
 static void new_parent(struct commit *parent, struct commit_list **parents_p)
 {
@@ -25,6 +26,14 @@ static void new_parent(struct commit *parent, struct commit_list **parents_p)
 	commit_list_insert(parent, parents_p);
 }
 
+static int commit_tree_config(const char *var, const char *value, void *cb)
+{
+	int status = git_gpg_config(var, value, NULL);
+	if (status)
+		return status;
+	return git_default_config(var, value, cb);
+}
+
 int cmd_commit_tree(int argc, const char **argv, const char *prefix)
 {
 	int i;
@@ -32,11 +41,19 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix)
 	unsigned char tree_sha1[20];
 	unsigned char commit_sha1[20];
 	struct strbuf buffer = STRBUF_INIT;
+	const char *sign_commit = NULL;
 
-	git_config(git_default_config, NULL);
+	git_config(commit_tree_config, NULL);
 
 	if (argc < 2 || !strcmp(argv[1], "-h"))
 		usage(commit_tree_usage);
+
+	if (!memcmp(argv[1], "-S", 2)) {
+		sign_commit = argv[1] + 2;
+		argv++;
+		argc--;
+	}
+
 	if (get_sha1(argv[1], tree_sha1))
 		die("Not a valid object name %s", argv[1]);
 
@@ -56,7 +73,8 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix)
 	if (strbuf_read(&buffer, 0, 0) < 0)
 		die_errno("git commit-tree: failed to read");
 
-	if (commit_tree(buffer.buf, tree_sha1, parents, commit_sha1, NULL)) {
+	if (commit_tree(buffer.buf, tree_sha1, parents, commit_sha1,
+			NULL, sign_commit)) {
 		strbuf_release(&buffer);
 		return 1;
 	}
diff --git a/builtin/commit.c b/builtin/commit.c
index cbc9613..90cf7e8 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -26,6 +26,7 @@
 #include "unpack-trees.h"
 #include "quote.h"
 #include "submodule.h"
+#include "gpg-interface.h"
 
 static const char * const builtin_commit_usage[] = {
 	"git commit [options] [--] <filepattern>...",
@@ -85,6 +86,8 @@ static int all, edit_flag, also, interactive, patch_interactive, only, amend, si
 static int quiet, verbose, no_verify, allow_empty, dry_run, renew_authorship;
 static int no_post_rewrite, allow_empty_message;
 static char *untracked_files_arg, *force_date, *ignore_submodule_arg;
+static char *sign_commit;
+
 /*
  * The default commit message cleanup mode will remove the lines
  * beginning with # (shell comments) and leading and trailing
@@ -144,6 +147,8 @@ static struct option builtin_commit_options[] = {
 	OPT_BOOLEAN('e', "edit", &edit_flag, "force edit of commit"),
 	OPT_STRING(0, "cleanup", &cleanup_arg, "default", "how to strip spaces and #comments from message"),
 	OPT_BOOLEAN(0, "status", &include_status, "include status in commit message template"),
+	{ OPTION_STRING, 'S', "gpg-sign", &sign_commit, "key id",
+	  "GPG sign commit", PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
 	/* end commit message options */
 
 	OPT_GROUP("Commit contents options"),
@@ -1323,6 +1328,7 @@ static void print_summary(const char *prefix, const unsigned char *sha1,
 static int git_commit_config(const char *k, const char *v, void *cb)
 {
 	struct wt_status *s = cb;
+	int status;
 
 	if (!strcmp(k, "commit.template"))
 		return git_config_pathname(&template_file, k, v);
@@ -1330,7 +1336,9 @@ static int git_commit_config(const char *k, const char *v, void *cb)
 		include_status = git_config_bool(k, v);
 		return 0;
 	}
-
+	status = git_gpg_config(k, v, NULL);
+	if (status)
+		return status;
 	return git_status_config(k, v, s);
 }
 
@@ -1481,7 +1489,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
 	}
 
 	if (commit_tree(sb.buf, active_cache_tree->sha1, parents, sha1,
-			author_ident.buf)) {
+			author_ident.buf, sign_commit)) {
 		rollback_index_files();
 		die(_("failed to write commit object"));
 	}
diff --git a/builtin/merge.c b/builtin/merge.c
index ab4077f..53cff02 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -26,6 +26,7 @@
 #include "merge-recursive.h"
 #include "resolve-undo.h"
 #include "remote.h"
+#include "gpg-interface.h"
 
 #define DEFAULT_TWOHEAD (1<<0)
 #define DEFAULT_OCTOPUS (1<<1)
@@ -63,6 +64,7 @@ static int allow_rerere_auto;
 static int abort_current_merge;
 static int show_progress = -1;
 static int default_to_upstream;
+static const char *sign_commit;
 
 static struct strategy all_strategy[] = {
 	{ "recursive",  DEFAULT_TWOHEAD | NO_TRIVIAL },
@@ -206,6 +208,8 @@ static struct option builtin_merge_options[] = {
 	OPT_BOOLEAN(0, "abort", &abort_current_merge,
 		"abort the current in-progress merge"),
 	OPT_SET_INT(0, "progress", &show_progress, "force progress reporting", 1),
+	{ OPTION_STRING, 'S', "gpg-sign", &sign_commit, "key id",
+	  "GPG sign commit", PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
 	OPT_END()
 };
 
@@ -525,6 +529,8 @@ static void parse_branch_merge_options(char *bmo)
 
 static int git_merge_config(const char *k, const char *v, void *cb)
 {
+	int status;
+
 	if (branch && !prefixcmp(k, "branch.") &&
 		!prefixcmp(k + 7, branch) &&
 		!strcmp(k + 7 + strlen(branch), ".mergeoptions")) {
@@ -562,6 +568,10 @@ static int git_merge_config(const char *k, const char *v, void *cb)
 		default_to_upstream = git_config_bool(k, v);
 		return 0;
 	}
+
+	status = git_gpg_config(k, v, NULL);
+	if (status)
+		return status;
 	return git_diff_ui_config(k, v, cb);
 }
 
@@ -870,7 +880,8 @@ static int merge_trivial(void)
 	parent->next->item = remoteheads->item;
 	parent->next->next = NULL;
 	run_prepare_commit_msg();
-	commit_tree(merge_msg.buf, result_tree, parent, result_commit, NULL);
+	commit_tree(merge_msg.buf, result_tree, parent, result_commit, NULL,
+		    sign_commit);
 	finish(result_commit, "In-index merge");
 	drop_save();
 	return 0;
@@ -900,7 +911,8 @@ static int finish_automerge(struct commit_list *common,
 	free_commit_list(remoteheads);
 	strbuf_addch(&merge_msg, '\n');
 	run_prepare_commit_msg();
-	commit_tree(merge_msg.buf, result_tree, parents, result_commit, NULL);
+	commit_tree(merge_msg.buf, result_tree, parents, result_commit,
+		    NULL, sign_commit);
 	strbuf_addf(&buf, "Merge made by the '%s' strategy.", wt_strategy);
 	finish(result_commit, buf.buf);
 	strbuf_release(&buf);
diff --git a/commit.c b/commit.c
index 97b4327..4bff3cd 100644
--- a/commit.c
+++ b/commit.c
@@ -6,6 +6,7 @@
 #include "diff.h"
 #include "revision.h"
 #include "notes.h"
+#include "gpg-interface.h"
 
 int save_commit_buffer = 1;
 
@@ -814,6 +815,40 @@ struct commit_list *reduce_heads(struct commit_list *heads)
 	return result;
 }
 
+static const char gpg_sig_header[] = "gpgsig ";
+static const int gpg_sig_header_len = sizeof(gpg_sig_header) - 1;
+
+static int do_sign_commit(struct strbuf *buf, const char *keyid)
+{
+	struct strbuf sig = STRBUF_INIT;
+	int inspos, copypos;
+
+	/* find the end of the header */
+	inspos = strstr(buf->buf, "\n\n") - buf->buf + 1;
+
+	if (!keyid || !*keyid)
+		keyid = get_signing_key();
+	if (sign_buffer(buf, &sig, keyid)) {
+		strbuf_release(&sig);
+		return -1;
+	}
+
+	for (copypos = 0; sig.buf[copypos]; ) {
+		const char *bol = sig.buf + copypos;
+		const char *eol = strchrnul(bol, '\n');
+		int len = (eol - bol) + !!*eol;
+
+		strbuf_insert(buf, inspos, gpg_sig_header, gpg_sig_header_len);
+		inspos += gpg_sig_header_len;
+		strbuf_insert(buf, inspos, bol, len);
+		inspos += len;
+		copypos += len;
+	}
+	strbuf_release(&sig);
+	return 0;
+}
+
+
 static const char commit_utf8_warn[] =
 "Warning: commit message does not conform to UTF-8.\n"
 "You may want to amend it after fixing the message, or set the config\n"
@@ -821,7 +856,7 @@ static const char commit_utf8_warn[] =
 
 int commit_tree(const char *msg, unsigned char *tree,
 		struct commit_list *parents, unsigned char *ret,
-		const char *author)
+		const char *author, const char *sign_commit)
 {
 	int result;
 	int encoding_is_utf8;
@@ -864,6 +899,9 @@ int commit_tree(const char *msg, unsigned char *tree,
 	if (encoding_is_utf8 && !is_utf8(buffer.buf))
 		fprintf(stderr, commit_utf8_warn);
 
+	if (sign_commit && do_sign_commit(&buffer, sign_commit))
+		return -1;
+
 	result = write_sha1_file(buffer.buf, buffer.len, commit_type, ret);
 	strbuf_release(&buffer);
 	return result;
diff --git a/commit.h b/commit.h
index 12d100b8..8c2419b 100644
--- a/commit.h
+++ b/commit.h
@@ -175,6 +175,6 @@ struct commit_list *reduce_heads(struct commit_list *heads);
 
 extern int commit_tree(const char *msg, unsigned char *tree,
 		struct commit_list *parents, unsigned char *ret,
-		const char *author);
+		       const char *author, const char *sign_commit);
 
 #endif /* COMMIT_H */
diff --git a/notes-cache.c b/notes-cache.c
index 4c8984e..c36a960 100644
--- a/notes-cache.c
+++ b/notes-cache.c
@@ -56,7 +56,7 @@ int notes_cache_write(struct notes_cache *c)
 
 	if (write_notes_tree(&c->tree, tree_sha1))
 		return -1;
-	if (commit_tree(c->validity, tree_sha1, NULL, commit_sha1, NULL) < 0)
+	if (commit_tree(c->validity, tree_sha1, NULL, commit_sha1, NULL, NULL) < 0)
 		return -1;
 	if (update_ref("update notes cache", c->tree.ref, commit_sha1, NULL,
 		       0, QUIET_ON_ERR) < 0)
diff --git a/notes-merge.c b/notes-merge.c
index e1aaf43..c29c434 100644
--- a/notes-merge.c
+++ b/notes-merge.c
@@ -546,7 +546,7 @@ void create_notes_commit(struct notes_tree *t, struct commit_list *parents,
 		/* else: t->ref points to nothing, assume root/orphan commit */
 	}
 
-	if (commit_tree(msg, tree_sha1, parents, result_sha1, NULL))
+	if (commit_tree(msg, tree_sha1, parents, result_sha1, NULL, NULL))
 		die("Failed to commit notes tree to database");
 }
 
-- 
1.7.7.498.g3f2e50

^ permalink raw reply related

* [PATCH v4 3/5] log: --show-signature
From: Junio C Hamano @ 2011-10-20  0:37 UTC (permalink / raw)
  To: git
In-Reply-To: <1319071023-31919-1-git-send-email-gitster@pobox.com>

This teaches the "log" family of commands to pass the GPG signature in the
commit objects to "gpg --verify" via the verify_signed_buffer() interface
used to verify signed tag objects. E.g.

    $ git show --show-signature -s HEAD

shows GPG output in the header part of the output.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 commit.c   |   34 ++++++++++++++++++++++++++++++++++
 commit.h   |    3 +++
 log-tree.c |   39 +++++++++++++++++++++++++++++++++++++++
 revision.c |    2 ++
 revision.h |    1 +
 5 files changed, 79 insertions(+), 0 deletions(-)

diff --git a/commit.c b/commit.c
index 4bff3cd..93045a2 100644
--- a/commit.c
+++ b/commit.c
@@ -848,6 +848,40 @@ static int do_sign_commit(struct strbuf *buf, const char *keyid)
 	return 0;
 }
 
+int parse_signed_commit(const unsigned char *sha1,
+			struct strbuf *payload, struct strbuf *signature)
+{
+	unsigned long size;
+	enum object_type type;
+	char *buffer = read_sha1_file(sha1, &type, &size);
+	int in_header, saw_signature = -1;
+	char *line;
+
+	if (!buffer || type != OBJ_COMMIT)
+		goto cleanup;
+
+	line = buffer;
+	in_header = 1;
+	saw_signature = 0;
+	while (*line) {
+		char *next = strchrnul(line, '\n');
+		if (*next)
+			next++;
+		if (in_header && !prefixcmp(line, gpg_sig_header)) {
+			const char *sig = line + gpg_sig_header_len;
+			strbuf_add(signature, sig, next - sig);
+			saw_signature = 1;
+		} else {
+			strbuf_add(payload, line, next - line);
+		}
+		if (*line == '\n')
+			in_header = 0;
+		line = next;
+	}
+ cleanup:
+	free(buffer);
+	return saw_signature;
+}
 
 static const char commit_utf8_warn[] =
 "Warning: commit message does not conform to UTF-8.\n"
diff --git a/commit.h b/commit.h
index 8c2419b..1885471 100644
--- a/commit.h
+++ b/commit.h
@@ -177,4 +177,7 @@ extern int commit_tree(const char *msg, unsigned char *tree,
 		struct commit_list *parents, unsigned char *ret,
 		       const char *author, const char *sign_commit);
 
+extern int parse_signed_commit(const unsigned char *sha1,
+			       struct strbuf *message, struct strbuf *signature);
+
 #endif /* COMMIT_H */
diff --git a/log-tree.c b/log-tree.c
index 24c295e..f7b6976 100644
--- a/log-tree.c
+++ b/log-tree.c
@@ -8,6 +8,7 @@
 #include "refs.h"
 #include "string-list.h"
 #include "color.h"
+#include "gpg-interface.h"
 
 struct decoration name_decoration = { "object names" };
 
@@ -395,6 +396,41 @@ void log_write_email_headers(struct rev_info *opt, struct commit *commit,
 	*extra_headers_p = extra_headers;
 }
 
+static void show_signature(struct rev_info *opt, struct commit *commit)
+{
+	struct strbuf payload = STRBUF_INIT;
+	struct strbuf signature = STRBUF_INIT;
+	struct strbuf gpg_output = STRBUF_INIT;
+	int status;
+	const char *color, *reset, *bol, *eol;
+
+	if (parse_signed_commit(commit->object.sha1, &payload, &signature) <= 0)
+		goto out;
+
+	status = verify_signed_buffer(payload.buf, payload.len,
+				      signature.buf, signature.len,
+				      &gpg_output);
+	if (status && !gpg_output.len)
+		strbuf_addstr(&gpg_output, "No signature\n");
+
+	color = diff_get_color_opt(&opt->diffopt,
+				   status ? DIFF_WHITESPACE : DIFF_FRAGINFO);
+	reset = diff_get_color_opt(&opt->diffopt, DIFF_RESET);
+
+	bol = gpg_output.buf;
+	while (*bol) {
+		eol = strchrnul(bol, '\n');
+		printf("%s%.*s%s%s", color, (int)(eol - bol), bol, reset,
+		       *eol ? "\n" : "");
+		bol = (*eol) ? (eol + 1) : eol;
+	}
+
+ out:
+	strbuf_release(&gpg_output);
+	strbuf_release(&payload);
+	strbuf_release(&signature);
+}
+
 void show_log(struct rev_info *opt)
 {
 	struct strbuf msgbuf = STRBUF_INIT;
@@ -502,6 +538,9 @@ void show_log(struct rev_info *opt)
 		}
 	}
 
+	if (opt->show_signature)
+		show_signature(opt, commit);
+
 	if (!commit->buffer)
 		return;
 
diff --git a/revision.c b/revision.c
index c46cfaa..860a312 100644
--- a/revision.c
+++ b/revision.c
@@ -1381,6 +1381,8 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
 		revs->show_notes = 1;
 		revs->show_notes_given = 1;
 		revs->notes_opt.use_default_notes = 1;
+	} else if (!strcmp(arg, "--show-signature")) {
+		revs->show_signature = 1;
 	} else if (!prefixcmp(arg, "--show-notes=") ||
 		   !prefixcmp(arg, "--notes=")) {
 		struct strbuf buf = STRBUF_INIT;
diff --git a/revision.h b/revision.h
index 3d64ada..198bb95 100644
--- a/revision.h
+++ b/revision.h
@@ -89,6 +89,7 @@ struct rev_info {
 			show_merge:1,
 			show_notes:1,
 			show_notes_given:1,
+			show_signature:1,
 			pretty_given:1,
 			abbrev_commit:1,
 			abbrev_commit_given:1,
-- 
1.7.7.498.g3f2e50

^ permalink raw reply related

* [PATCH v4 4/5] t7004: extract generic "GPG testing" bits
From: Junio C Hamano @ 2011-10-20  0:37 UTC (permalink / raw)
  To: git
In-Reply-To: <1319071023-31919-1-git-send-email-gitster@pobox.com>

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 t/lib-gpg.sh          |   29 +++++++++++++++++++++++++++++
 t/lib-gpg/pubring.gpg |  Bin 0 -> 1164 bytes
 t/lib-gpg/random_seed |  Bin 0 -> 600 bytes
 t/lib-gpg/secring.gpg |  Bin 0 -> 1237 bytes
 t/lib-gpg/trustdb.gpg |  Bin 0 -> 1280 bytes
 t/t7004-tag.sh        |   29 +----------------------------
 t/t7004/pubring.gpg   |  Bin 1164 -> 0 bytes
 t/t7004/random_seed   |  Bin 600 -> 0 bytes
 t/t7004/secring.gpg   |  Bin 1237 -> 0 bytes
 t/t7004/trustdb.gpg   |  Bin 1280 -> 0 bytes
 10 files changed, 30 insertions(+), 28 deletions(-)
 create mode 100644 t/lib-gpg.sh
 create mode 100644 t/lib-gpg/pubring.gpg
 create mode 100644 t/lib-gpg/random_seed
 create mode 100644 t/lib-gpg/secring.gpg
 create mode 100644 t/lib-gpg/trustdb.gpg
 delete mode 100644 t/t7004/pubring.gpg
 delete mode 100644 t/t7004/random_seed
 delete mode 100644 t/t7004/secring.gpg
 delete mode 100644 t/t7004/trustdb.gpg

diff --git a/t/lib-gpg.sh b/t/lib-gpg.sh
new file mode 100644
index 0000000..eb09027
--- /dev/null
+++ b/t/lib-gpg.sh
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+# Check if gpg is available
+gpg --version >/dev/null 2>/dev/null
+if [ $? -eq 127 ]; then
+	say "# gpg not found - skipping tag signing and verification tests"
+else
+	# As said here: http://www.gnupg.org/documentation/faqs.html#q6.19
+	# the gpg version 1.0.6 didn't parse trust packets correctly, so for
+	# that version, creation of signed tags using the generated key fails.
+	case "$(gpg --version)" in
+	'gpg (GnuPG) 1.0.6'*)
+		say "Skipping signed tag tests, because a bug in 1.0.6 version"
+		;;
+	*)
+		test_set_prereq GPG
+		;;
+	esac
+fi
+
+# key generation info: gpg --homedir t/t7004 --gen-key
+# Type DSA and Elgamal, size 2048 bits, no expiration date.
+# Name and email: C O Mitter <committer@example.com>
+# No password given, to enable non-interactive operation.
+
+cp -R "$TEST_DIRECTORY"/lib-gpg ./gpghome
+chmod 0700 gpghome
+GNUPGHOME="$(pwd)/gpghome"
+export GNUPGHOME
diff --git a/t/lib-gpg/pubring.gpg b/t/lib-gpg/pubring.gpg
new file mode 100644
index 0000000000000000000000000000000000000000..83855fa4e1c6c37afe550c17afa1e7971042ded5
GIT binary patch
literal 1164
zcmV;71ateD0ipy(XUi!O1OT%L_{gO;4KKDfwF;#WWYZ2?*>Ph_j9y>uobJf#jXEbw
zQUOEXs+@;X*)1kyN*7VwW}f-GD3>Vlu2F6j{T^Kt%(kMo(W3C}W^X)NL|0VdGIzc`
z5@TY!;Du~AFGW=#0vYgS@U2j8Q{Qe9F~lQEBVt+(yjM1jQ{YLe0lxsC<HX~BiFSU3
zs3oq~QtxsAzbwuV1N{{&PcudMk6}p><?O}^6vikhMm+>LD}W25r?wdKr$(|R8_;Mn
z&Yh%2Hbu9IX>#(R8S&@jVI!K^K7AI$=A+*-qJ>-J^fdR_m5GZao*%P{nee=O$Eb$s
zes#O%4Z_4OAZW}8Ey8%%w->rA>z!nf)&NPikkr&pkRlB>=W+x68fwevWl9Ys2R3m2
zNPf<ig>x1y^AXyemu>Q}DAp%==Q`5sCrBzLaH^rOm1NLtt-ig*IfY?HMdv66@%u%#
zy0K62;Z+4O3Ol6i*<7GlXeW6FC?0yV!c_#yGozNjrOE#8+Rw@Z8cGAG=eE%uMUW=V
zX(76db;AG@lD)hAv?4<wPasWcbaZ8MAUtDlZEb0EbY*ftWq4t2aBO8RV{dIfh+YH}
z5dr`n1p-EA%P9gI0|pBT2nPcK0~G=T0~P`S0v-VZ7k~f?2@n&u^&ZXMLk)Bt0G~<e
ziCMX)QRqT4mEiivJ-X>{Mxg+pzucC_!d-8(MV+3G?_=VDC^nQ}umS)BxdIIYMrX@T
z5C{Oyxp_ZR>*yfS@@M%BVY6{X4EXUYpbiBs(uw7knRB|b)>)=o+wQ%-v34o&a|fQ;
z&R=xVK8Z-jS8R4m_gj2C$xPq$xv1H553!y9ip8A~vxoyq&fFbw!L$zU@hDh#f$r>>
zAYPcTf%K%xv5<+JivV@Da!&`-v8eIo5ApKIdiK7E`krA1X!I73&)t#yo}GERUSnBN
zlK-+<I<**0!u}jvbPz5b{hhe8_PVcR!i8ObFp-W0bqNWKA+W14zV9Mj$a~(KZ-0uf
z|BVvaf!K}k92MNalP4hQ^Yxqk-*SzY6q{6Cw*+Fr_+LZp=BFzuo3pVX*T?Ic-|hbZ
z1PurPv|=$EKX~Bz_Bz;JW71^>8;AnsY?5ub{hajO)@PHTt`On@Z;}jyeue<@oS$3l
zEu9m5O={<O&85HqUN|R$bZiPXX4YsWsPOn@@M)Lf;hyI_(eM$*#Sf3}T=1jzGvsiy
ziKuCmV^)`UC@8ma=jBiHqr!1L4>w`5P=-vvIiJTGj0-lRpc+EU9(`B#rn1m1WqmXr
z5Z+N|DR*y*k<4fY0I_eP7qVo9Fx58WMwaK^N~I=>1VN29R|Umg^2q$WGlE#9AFC2_
zOYG5M$}OL?FQ)ej0!KGwY-hAFz#wAPr>3&{`s7!HUZ22Y7KY{9ly1wQ8^hqBojPv8
zh)Dz(5dr`S1p-EA%T59t3;+rV5EHib9?jlE4Z-XHpqd%~B34SUmtRi+{;vv2ei?p%
eLjau^#pu5ba=n|GS94<zkje^zT1YFf0ssTLm?$>@

literal 0
HcmV?d00001

diff --git a/t/lib-gpg/random_seed b/t/lib-gpg/random_seed
new file mode 100644
index 0000000000000000000000000000000000000000..8fed1339ed0a744e5663f4a5e6b6ac9bae3d8524
GIT binary patch
literal 600
zcmV-e0;m1=h9nBV>1C6QsKJEiEJaD@Q3F8s5u<$E+<2(By)JAZSxviTsXg(wKC+O%
zzvV{Z>W3*k?r7~pgmmkbw8-x{Am!eeN)z?cwIHcT2jqgiA(SXo<iO=E?cY80`p#w8
z)O-&?SnwsJ=1VJ-?26&*g88Nr8E=g2onRW^(c+2nJlX)?dmK)tPO0EY-!B!vMCv1)
z-AOW(3WuF+7IdSxMnzrDgnMqVU=|+YFxlY|VeR+Fg<%C@0Xupi0<S7QYJyFTR$}FQ
zzoSAbU>CoCKWKX;!3@L_U=aFUm!M<>ILG}$`bfnadAkLQbI-upV7Qwf^OE&N45Pz<
zk~^KlzNC6)d@QGv=K5-At&A8FS&MQSR`LB}@R1?A3K1p(vM>7CK}EfFhmBJd&cH^-
z(3Ih^`VuoVBB|w~p!Q^#DY%V2A2FhXu<Bp*L)lSCUdqRyI5wxMG&E1sL$)E$Zo&pJ
zgy#;fENqHImgN>LL2!7DhfZ}&;BSAyz=T0#S?2+NET5St@16L?YI?5Io%<uD|2}hl
zx0xsuefz1+bM^-ZIgtKs=)&VAI8(MfytvM>t>%~nsXUb~*EkptHiN?W{=DRu_s;2u
ziHh{2&>;CQO7;>{$DN33_Ef}g+;b<2hIF^p(Y>^riLBb*Y2Xw>F8)jp49&oLKJOic
z+V{Lt!_`eKGhyk5Edie{-^#n!TFlsfux*QBRZEh^4SVePPmb{BvF|>sKd2cYg@vKp
mVI8jcB1(k(tlt^Kr<{EMs>|b*d70nyVMQcc%xEnE(#Uq3d^-35

literal 0
HcmV?d00001

diff --git a/t/lib-gpg/secring.gpg b/t/lib-gpg/secring.gpg
new file mode 100644
index 0000000000000000000000000000000000000000..d831cd9eb3eee613d3c0e1a71093ae01ea7347e3
GIT binary patch
literal 1237
zcmV;`1S<QL0lNf7XUi!O1OT%L_{gO;4KKDfwF;#WWYZ2?*>Ph_j9y>uobJf#jXEbw
zQUOEXs+@;X*)1kyN*7VwW}f-GD3>Vlu2F6j{T^Kt%(kMo(W3C}W^X)NL|0VdGIzc`
z5@TY!;Du~AFGW=#0vYgS@U2j8Q{Qe9F~lQEBVt+(yjM1jQ{YLe0lxsC<HX~BiFSU3
zs3oq~QtxsAzbwuV1N{{&PcudMk6}p><?O}^6vikhMm+>LD}W25r?wdKr$(|R8_;Mn
z&Yh%2Hbu9IX>#(R8S&@jVI!K^K7AI$=A+*-qJ>-J^fdR_m5GZao*%P{nee=O$Eb$s
zes#O%4Z_4OAZW}8Ey8%%w->rA>z!nf)&NPikkr&pkRlB>=W+x68fwevWl9Ys2R3m2
zNPf<ig>x1y^AXyemu>Q}DAp%==Q`5sCrBzLaH^rOm1NLtt-ig*IfY?HMdv66@%u%#
zy0K62;Z+4O3Ol6i*<7GlXeW6FC?0yV!c_#yGozNjrOE#8+Rw@Z8cGAG=eE%uMUW=V
zX(76db;AG@lD)hA005tK#LT31ryNoF9o-(`X`Xl5w88@mle8j3AWtAoX>@dDav(fo
zZ*6U9baZ8MKxKGgZE$R5E@N+PK8Rif6A=Oc9t8qMXUi!98v_Ol2?z%R0s|ES0|OQU
z0RkQY0vCV)3JDMsw)Gy(-a`#^9RQ%c+5fOQwB&AM#DD9)s1L@!pm9S0pF>@|C2*qL
z%1j5#NZ6^U*PB>^d9VTi0G$Fo1V(4eP7nwH&bfI%Q|ss;(eh{c3}LfzMGW}yE1(Vq
zEz*hQmYH+9vesFqTifovzOi;G@N);A+0I{d(LRYt$5(82OZQuRJjqPo^tq_na}Tkd
z|BA((5wnN`O3vIJaly0>?(ryCcY*Hgm>^!5uz~cX%CV4%oQnW;wsKDg)3K=W<qz@l
z$a?m^i29yk259sakI&ta{GOe8x?W>hQIh|%Svs{CO~U>hTXYaE9sQlSv-Y~LWWt4A
ze=w1b1$7Atj3KbAF~092T*!Ogn{R)Lum6n_*@4)N@EjG~z>_B+>GSoQ{oitpmlT^+
zUAF{c!T4W8?B=H{DVwvgAlJw1n&0jJ00a#P0JLH;8$Wp9`Sv>4USrZ_1sjM0<!q8|
zxc!{;-PUK5pso<&0dJBFgMNkp@|>Sr>@A%Wd`)WSc+I820A4sJf^=*OHfGjnC8+TD
zW$<a2;o+X=JkjtG#>Eeh?p*Mr_A}&gvx%r_lw($xcPJ>gap&bv^P|FXJr6fwvQUOh
zz&W4C8jK4zqM#Z=%N~7K_NKDXEM<K(9uVG9XDN4YijmA{1^}^dq8GAcg)r4N<3^U}
z-b$q=iUdK8Hdh73UGm8MyfcDWr5~#ja!c&dVahF^v@fRj3IazrWNc@&F~A^V)u*Pi
z`ugNogkGP(V-|+x+mvq0pc}*Bpq)By!2khN4M6SOU21NKpXW5P=UU_LoDzvrTdR;4
zU)W&&o9V6>MQOk{A%?1(Ozs{Qqlifa7!d*h2?YX1XUk3k8w>yn2@n&u^&ZXMLk+>~
z0HDxvfGrJTMY@$ys#k#t?D63Zq=W#R3eUeT(L61`VGHe?zP;`rnta#-umS)8nA%Rq

literal 0
HcmV?d00001

diff --git a/t/lib-gpg/trustdb.gpg b/t/lib-gpg/trustdb.gpg
new file mode 100644
index 0000000000000000000000000000000000000000..abace962b8bf84be688a6f27e4ebd0ee7052f210
GIT binary patch
literal 1280
zcmZQfFGy!*W@Ke#U|?`dKkWykumMIcY@%4iM%7^n6rj+M4;MLzzlOX&pwTnxkD-}P
zc^HbXN0fL!SIq1?>env3?W^3`d(OOU5YNaX{KU(k^<0;M@87ONv)_6ZxD={-=<kYO
M2Ud3=2BC}r0AuhNr2qf`

literal 0
HcmV?d00001

diff --git a/t/t7004-tag.sh b/t/t7004-tag.sh
index 097ce2b..ded5c86 100755
--- a/t/t7004-tag.sh
+++ b/t/t7004-tag.sh
@@ -8,6 +8,7 @@ test_description='git tag
 Tests for operations with tags.'
 
 . ./test-lib.sh
+. "$TEST_DIRECTORY/lib-gpg.sh"
 
 # creating and listing lightweight tags:
 
@@ -585,24 +586,6 @@ test_expect_success \
 	test_cmp expect actual
 '
 
-# subsequent tests require gpg; check if it is available
-gpg --version >/dev/null 2>/dev/null
-if [ $? -eq 127 ]; then
-	say "# gpg not found - skipping tag signing and verification tests"
-else
-	# As said here: http://www.gnupg.org/documentation/faqs.html#q6.19
-	# the gpg version 1.0.6 didn't parse trust packets correctly, so for
-	# that version, creation of signed tags using the generated key fails.
-	case "$(gpg --version)" in
-	'gpg (GnuPG) 1.0.6'*)
-		say "Skipping signed tag tests, because a bug in 1.0.6 version"
-		;;
-	*)
-		test_set_prereq GPG
-		;;
-	esac
-fi
-
 # trying to verify annotated non-signed tags:
 
 test_expect_success GPG \
@@ -625,16 +608,6 @@ test_expect_success GPG \
 
 # creating and verifying signed tags:
 
-# key generation info: gpg --homedir t/t7004 --gen-key
-# Type DSA and Elgamal, size 2048 bits, no expiration date.
-# Name and email: C O Mitter <committer@example.com>
-# No password given, to enable non-interactive operation.
-
-cp -R "$TEST_DIRECTORY"/t7004 ./gpghome
-chmod 0700 gpghome
-GNUPGHOME="$(pwd)/gpghome"
-export GNUPGHOME
-
 get_tag_header signed-tag $commit commit $time >expect
 echo 'A signed tag message' >>expect
 echo '-----BEGIN PGP SIGNATURE-----' >>expect
diff --git a/t/t7004/pubring.gpg b/t/t7004/pubring.gpg
deleted file mode 100644
index 83855fa4e1c6c37afe550c17afa1e7971042ded5..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 1164
zcmV;71ateD0ipy(XUi!O1OT%L_{gO;4KKDfwF;#WWYZ2?*>Ph_j9y>uobJf#jXEbw
zQUOEXs+@;X*)1kyN*7VwW}f-GD3>Vlu2F6j{T^Kt%(kMo(W3C}W^X)NL|0VdGIzc`
z5@TY!;Du~AFGW=#0vYgS@U2j8Q{Qe9F~lQEBVt+(yjM1jQ{YLe0lxsC<HX~BiFSU3
zs3oq~QtxsAzbwuV1N{{&PcudMk6}p><?O}^6vikhMm+>LD}W25r?wdKr$(|R8_;Mn
z&Yh%2Hbu9IX>#(R8S&@jVI!K^K7AI$=A+*-qJ>-J^fdR_m5GZao*%P{nee=O$Eb$s
zes#O%4Z_4OAZW}8Ey8%%w->rA>z!nf)&NPikkr&pkRlB>=W+x68fwevWl9Ys2R3m2
zNPf<ig>x1y^AXyemu>Q}DAp%==Q`5sCrBzLaH^rOm1NLtt-ig*IfY?HMdv66@%u%#
zy0K62;Z+4O3Ol6i*<7GlXeW6FC?0yV!c_#yGozNjrOE#8+Rw@Z8cGAG=eE%uMUW=V
zX(76db;AG@lD)hAv?4<wPasWcbaZ8MAUtDlZEb0EbY*ftWq4t2aBO8RV{dIfh+YH}
z5dr`n1p-EA%P9gI0|pBT2nPcK0~G=T0~P`S0v-VZ7k~f?2@n&u^&ZXMLk)Bt0G~<e
ziCMX)QRqT4mEiivJ-X>{Mxg+pzucC_!d-8(MV+3G?_=VDC^nQ}umS)BxdIIYMrX@T
z5C{Oyxp_ZR>*yfS@@M%BVY6{X4EXUYpbiBs(uw7knRB|b)>)=o+wQ%-v34o&a|fQ;
z&R=xVK8Z-jS8R4m_gj2C$xPq$xv1H553!y9ip8A~vxoyq&fFbw!L$zU@hDh#f$r>>
zAYPcTf%K%xv5<+JivV@Da!&`-v8eIo5ApKIdiK7E`krA1X!I73&)t#yo}GERUSnBN
zlK-+<I<**0!u}jvbPz5b{hhe8_PVcR!i8ObFp-W0bqNWKA+W14zV9Mj$a~(KZ-0uf
z|BVvaf!K}k92MNalP4hQ^Yxqk-*SzY6q{6Cw*+Fr_+LZp=BFzuo3pVX*T?Ic-|hbZ
z1PurPv|=$EKX~Bz_Bz;JW71^>8;AnsY?5ub{hajO)@PHTt`On@Z;}jyeue<@oS$3l
zEu9m5O={<O&85HqUN|R$bZiPXX4YsWsPOn@@M)Lf;hyI_(eM$*#Sf3}T=1jzGvsiy
ziKuCmV^)`UC@8ma=jBiHqr!1L4>w`5P=-vvIiJTGj0-lRpc+EU9(`B#rn1m1WqmXr
z5Z+N|DR*y*k<4fY0I_eP7qVo9Fx58WMwaK^N~I=>1VN29R|Umg^2q$WGlE#9AFC2_
zOYG5M$}OL?FQ)ej0!KGwY-hAFz#wAPr>3&{`s7!HUZ22Y7KY{9ly1wQ8^hqBojPv8
zh)Dz(5dr`S1p-EA%T59t3;+rV5EHib9?jlE4Z-XHpqd%~B34SUmtRi+{;vv2ei?p%
eLjau^#pu5ba=n|GS94<zkje^zT1YFf0ssTLm?$>@

diff --git a/t/t7004/random_seed b/t/t7004/random_seed
deleted file mode 100644
index 8fed1339ed0a744e5663f4a5e6b6ac9bae3d8524..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 600
zcmV-e0;m1=h9nBV>1C6QsKJEiEJaD@Q3F8s5u<$E+<2(By)JAZSxviTsXg(wKC+O%
zzvV{Z>W3*k?r7~pgmmkbw8-x{Am!eeN)z?cwIHcT2jqgiA(SXo<iO=E?cY80`p#w8
z)O-&?SnwsJ=1VJ-?26&*g88Nr8E=g2onRW^(c+2nJlX)?dmK)tPO0EY-!B!vMCv1)
z-AOW(3WuF+7IdSxMnzrDgnMqVU=|+YFxlY|VeR+Fg<%C@0Xupi0<S7QYJyFTR$}FQ
zzoSAbU>CoCKWKX;!3@L_U=aFUm!M<>ILG}$`bfnadAkLQbI-upV7Qwf^OE&N45Pz<
zk~^KlzNC6)d@QGv=K5-At&A8FS&MQSR`LB}@R1?A3K1p(vM>7CK}EfFhmBJd&cH^-
z(3Ih^`VuoVBB|w~p!Q^#DY%V2A2FhXu<Bp*L)lSCUdqRyI5wxMG&E1sL$)E$Zo&pJ
zgy#;fENqHImgN>LL2!7DhfZ}&;BSAyz=T0#S?2+NET5St@16L?YI?5Io%<uD|2}hl
zx0xsuefz1+bM^-ZIgtKs=)&VAI8(MfytvM>t>%~nsXUb~*EkptHiN?W{=DRu_s;2u
ziHh{2&>;CQO7;>{$DN33_Ef}g+;b<2hIF^p(Y>^riLBb*Y2Xw>F8)jp49&oLKJOic
z+V{Lt!_`eKGhyk5Edie{-^#n!TFlsfux*QBRZEh^4SVePPmb{BvF|>sKd2cYg@vKp
mVI8jcB1(k(tlt^Kr<{EMs>|b*d70nyVMQcc%xEnE(#Uq3d^-35

diff --git a/t/t7004/secring.gpg b/t/t7004/secring.gpg
deleted file mode 100644
index d831cd9eb3eee613d3c0e1a71093ae01ea7347e3..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 1237
zcmV;`1S<QL0lNf7XUi!O1OT%L_{gO;4KKDfwF;#WWYZ2?*>Ph_j9y>uobJf#jXEbw
zQUOEXs+@;X*)1kyN*7VwW}f-GD3>Vlu2F6j{T^Kt%(kMo(W3C}W^X)NL|0VdGIzc`
z5@TY!;Du~AFGW=#0vYgS@U2j8Q{Qe9F~lQEBVt+(yjM1jQ{YLe0lxsC<HX~BiFSU3
zs3oq~QtxsAzbwuV1N{{&PcudMk6}p><?O}^6vikhMm+>LD}W25r?wdKr$(|R8_;Mn
z&Yh%2Hbu9IX>#(R8S&@jVI!K^K7AI$=A+*-qJ>-J^fdR_m5GZao*%P{nee=O$Eb$s
zes#O%4Z_4OAZW}8Ey8%%w->rA>z!nf)&NPikkr&pkRlB>=W+x68fwevWl9Ys2R3m2
zNPf<ig>x1y^AXyemu>Q}DAp%==Q`5sCrBzLaH^rOm1NLtt-ig*IfY?HMdv66@%u%#
zy0K62;Z+4O3Ol6i*<7GlXeW6FC?0yV!c_#yGozNjrOE#8+Rw@Z8cGAG=eE%uMUW=V
zX(76db;AG@lD)hA005tK#LT31ryNoF9o-(`X`Xl5w88@mle8j3AWtAoX>@dDav(fo
zZ*6U9baZ8MKxKGgZE$R5E@N+PK8Rif6A=Oc9t8qMXUi!98v_Ol2?z%R0s|ES0|OQU
z0RkQY0vCV)3JDMsw)Gy(-a`#^9RQ%c+5fOQwB&AM#DD9)s1L@!pm9S0pF>@|C2*qL
z%1j5#NZ6^U*PB>^d9VTi0G$Fo1V(4eP7nwH&bfI%Q|ss;(eh{c3}LfzMGW}yE1(Vq
zEz*hQmYH+9vesFqTifovzOi;G@N);A+0I{d(LRYt$5(82OZQuRJjqPo^tq_na}Tkd
z|BA((5wnN`O3vIJaly0>?(ryCcY*Hgm>^!5uz~cX%CV4%oQnW;wsKDg)3K=W<qz@l
z$a?m^i29yk259sakI&ta{GOe8x?W>hQIh|%Svs{CO~U>hTXYaE9sQlSv-Y~LWWt4A
ze=w1b1$7Atj3KbAF~092T*!Ogn{R)Lum6n_*@4)N@EjG~z>_B+>GSoQ{oitpmlT^+
zUAF{c!T4W8?B=H{DVwvgAlJw1n&0jJ00a#P0JLH;8$Wp9`Sv>4USrZ_1sjM0<!q8|
zxc!{;-PUK5pso<&0dJBFgMNkp@|>Sr>@A%Wd`)WSc+I820A4sJf^=*OHfGjnC8+TD
zW$<a2;o+X=JkjtG#>Eeh?p*Mr_A}&gvx%r_lw($xcPJ>gap&bv^P|FXJr6fwvQUOh
zz&W4C8jK4zqM#Z=%N~7K_NKDXEM<K(9uVG9XDN4YijmA{1^}^dq8GAcg)r4N<3^U}
z-b$q=iUdK8Hdh73UGm8MyfcDWr5~#ja!c&dVahF^v@fRj3IazrWNc@&F~A^V)u*Pi
z`ugNogkGP(V-|+x+mvq0pc}*Bpq)By!2khN4M6SOU21NKpXW5P=UU_LoDzvrTdR;4
zU)W&&o9V6>MQOk{A%?1(Ozs{Qqlifa7!d*h2?YX1XUk3k8w>yn2@n&u^&ZXMLk+>~
z0HDxvfGrJTMY@$ys#k#t?D63Zq=W#R3eUeT(L61`VGHe?zP;`rnta#-umS)8nA%Rq

diff --git a/t/t7004/trustdb.gpg b/t/t7004/trustdb.gpg
deleted file mode 100644
index abace962b8bf84be688a6f27e4ebd0ee7052f210..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 1280
zcmZQfFGy!*W@Ke#U|?`dKkWykumMIcY@%4iM%7^n6rj+M4;MLzzlOX&pwTnxkD-}P
zc^HbXN0fL!SIq1?>env3?W^3`d(OOU5YNaX{KU(k^<0;M@87ONv)_6ZxD={-=<kYO
M2Ud3=2BC}r0AuhNr2qf`

-- 
1.7.7.498.g3f2e50

^ permalink raw reply related

* [PATCH v4 5/5] test "commit -S" and "log --show-signature"
From: Junio C Hamano @ 2011-10-20  0:37 UTC (permalink / raw)
  To: git
In-Reply-To: <1319071023-31919-1-git-send-email-gitster@pobox.com>

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 t/t7510-signed-commit.sh |   60 ++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 60 insertions(+), 0 deletions(-)
 create mode 100755 t/t7510-signed-commit.sh

diff --git a/t/t7510-signed-commit.sh b/t/t7510-signed-commit.sh
new file mode 100755
index 0000000..5c7475d
--- /dev/null
+++ b/t/t7510-signed-commit.sh
@@ -0,0 +1,60 @@
+#!/bin/sh
+
+test_description='signed commit tests'
+. ./test-lib.sh
+. "$TEST_DIRECTORY/lib-gpg.sh"
+
+test_expect_success GPG 'create signed commits' '
+	echo 1 >file && git add file &&
+	test_tick && git commit -S -m initial &&
+	git tag initial &&
+	git branch side &&
+
+	echo 2 >file && test_tick && git commit -a -S -m second &&
+	git tag second &&
+
+	git checkout side &&
+	echo 3 >elif && git add elif &&
+	test_tick && git commit -m "third on side" &&
+
+	git checkout master &&
+	test_tick && git merge -S side &&
+	git tag merge &&
+
+	echo 4 >file && test_tick && git commit -a -m "fourth unsigned" &&
+	git tag fourth-unsigned &&
+
+	test_tick && git commit --amend -S -m "fourth signed"
+'
+
+test_expect_success GPG 'show signatures' '
+	(
+		for commit in initial second merge master
+		do
+			git show --pretty=short --show-signature $commit >actual &&
+			grep "Good signature from" actual || exit 1
+			! grep "BAD signature from" actual || exit 1
+			echo $commit OK
+		done
+	) &&
+	(
+		for commit in merge^2 fourth-unsigned
+		do
+			git show --pretty=short --show-signature $commit >actual &&
+			grep "Good signature from" actual && exit 1
+			! grep "BAD signature from" actual || exit 1
+			echo $commit OK
+		done
+	)
+'
+
+test_expect_success GPG 'detect fudged signature' '
+	git cat-file commit master >raw &&
+	sed -e "s/fourth signed/4th forged/" raw >forged &&
+	git hash-object -w -t commit forged >forged.commit &&
+	git show --pretty=short --show-signature $(cat forged.commit) >actual &&
+	grep "BAD signature from" actual &&
+	! grep "Good signature from" actual
+'
+
+test_done
-- 
1.7.7.498.g3f2e50

^ permalink raw reply related

* [PATCH v4 1/5] Split GPG interface into its own helper library
From: Junio C Hamano @ 2011-10-20  0:36 UTC (permalink / raw)
  To: git
In-Reply-To: <1319071023-31919-1-git-send-email-gitster@pobox.com>

This mostly moves existing code from builtin/tag.c (for signing)
and builtin/verify-tag.c (for verifying) to a new gpg-interface.c
file to provide a more generic library interface.

 - sign_buffer() takes a payload strbuf, a signature strbuf, and a signing
   key, runs "gpg" to produce a detached signature for the payload, and
   appends it to the signature strbuf. The contents of a signed tag that
   concatenates the payload and the detached signature can be produced by
   giving the same strbuf as payload and signature strbuf.

 - verify_signed_buffer() takes a payload and a detached signature as
   <ptr, len> pairs, and runs "gpg --verify" to see if the payload matches
   the signature. It can optionally capture the output from GPG to allow
   the callers to pretty-print it in a way more suitable for their
   contexts.

"verify-tag" (aka "tag -v") used to save the whole tag contents as if it
is a detached signature, and fed gpg the payload part of the tag. It
relied on gpg to fail when the given tag is not signed but just is
annotated.  The updated run_gpg_verify() function detects the lack of
detached signature in the input, and errors out without bothering "gpg".

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 Makefile             |    2 +
 builtin/tag.c        |   76 ++-------------------------
 builtin/verify-tag.c |   35 ++-----------
 gpg-interface.c      |  138 ++++++++++++++++++++++++++++++++++++++++++++++++++
 gpg-interface.h      |   10 ++++
 tag.c                |    5 ++
 6 files changed, 166 insertions(+), 100 deletions(-)
 create mode 100644 gpg-interface.c
 create mode 100644 gpg-interface.h

diff --git a/Makefile b/Makefile
index 8d6d451..2183223 100644
--- a/Makefile
+++ b/Makefile
@@ -530,6 +530,7 @@ LIB_H += exec_cmd.h
 LIB_H += fsck.h
 LIB_H += gettext.h
 LIB_H += git-compat-util.h
+LIB_H += gpg-interface.h
 LIB_H += graph.h
 LIB_H += grep.h
 LIB_H += hash.h
@@ -620,6 +621,7 @@ LIB_OBJS += entry.o
 LIB_OBJS += environment.o
 LIB_OBJS += exec_cmd.o
 LIB_OBJS += fsck.o
+LIB_OBJS += gpg-interface.o
 LIB_OBJS += graph.o
 LIB_OBJS += grep.o
 LIB_OBJS += hash.o
diff --git a/builtin/tag.c b/builtin/tag.c
index 667515e..fb0d4a1 100644
--- a/builtin/tag.c
+++ b/builtin/tag.c
@@ -14,6 +14,7 @@
 #include "parse-options.h"
 #include "diff.h"
 #include "revision.h"
+#include "gpg-interface.h"
 
 static const char * const git_tag_usage[] = {
 	"git tag [-a|-s|-u <key-id>] [-f] [-m <msg>|-F <file>] <tagname> [<head>]",
@@ -23,8 +24,6 @@ static const char * const git_tag_usage[] = {
 	NULL
 };
 
-static char signingkey[1000];
-
 struct tag_filter {
 	const char **patterns;
 	int lines;
@@ -208,60 +207,7 @@ static int verify_tag(const char *name, const char *ref,
 
 static int do_sign(struct strbuf *buffer)
 {
-	struct child_process gpg;
-	const char *args[4];
-	char *bracket;
-	int len;
-	int i, j;
-
-	if (!*signingkey) {
-		if (strlcpy(signingkey, git_committer_info(IDENT_ERROR_ON_NO_NAME),
-				sizeof(signingkey)) > sizeof(signingkey) - 1)
-			return error(_("committer info too long."));
-		bracket = strchr(signingkey, '>');
-		if (bracket)
-			bracket[1] = '\0';
-	}
-
-	/* When the username signingkey is bad, program could be terminated
-	 * because gpg exits without reading and then write gets SIGPIPE. */
-	signal(SIGPIPE, SIG_IGN);
-
-	memset(&gpg, 0, sizeof(gpg));
-	gpg.argv = args;
-	gpg.in = -1;
-	gpg.out = -1;
-	args[0] = "gpg";
-	args[1] = "-bsau";
-	args[2] = signingkey;
-	args[3] = NULL;
-
-	if (start_command(&gpg))
-		return error(_("could not run gpg."));
-
-	if (write_in_full(gpg.in, buffer->buf, buffer->len) != buffer->len) {
-		close(gpg.in);
-		close(gpg.out);
-		finish_command(&gpg);
-		return error(_("gpg did not accept the tag data"));
-	}
-	close(gpg.in);
-	len = strbuf_read(buffer, gpg.out, 1024);
-	close(gpg.out);
-
-	if (finish_command(&gpg) || !len || len < 0)
-		return error(_("gpg failed to sign the tag"));
-
-	/* Strip CR from the line endings, in case we are on Windows. */
-	for (i = j = 0; i < buffer->len; i++)
-		if (buffer->buf[i] != '\r') {
-			if (i != j)
-				buffer->buf[j] = buffer->buf[i];
-			j++;
-		}
-	strbuf_setlen(buffer, j);
-
-	return 0;
+	return sign_buffer(buffer, buffer, get_signing_key());
 }
 
 static const char tag_template[] =
@@ -270,21 +216,11 @@ static const char tag_template[] =
 	"# Write a tag message\n"
 	"#\n");
 
-static void set_signingkey(const char *value)
-{
-	if (strlcpy(signingkey, value, sizeof(signingkey)) >= sizeof(signingkey))
-		die(_("signing key value too long (%.10s...)"), value);
-}
-
 static int git_tag_config(const char *var, const char *value, void *cb)
 {
-	if (!strcmp(var, "user.signingkey")) {
-		if (!value)
-			return config_error_nonbool(var);
-		set_signingkey(value);
-		return 0;
-	}
-
+	int status = git_gpg_config(var, value, cb);
+	if (status)
+		return status;
 	return git_default_config(var, value, cb);
 }
 
@@ -463,7 +399,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
 
 	if (keyid) {
 		sign = 1;
-		set_signingkey(keyid);
+		set_signing_key(keyid);
 	}
 	if (sign)
 		annotate = 1;
diff --git a/builtin/verify-tag.c b/builtin/verify-tag.c
index 3134766..28c2174 100644
--- a/builtin/verify-tag.c
+++ b/builtin/verify-tag.c
@@ -11,6 +11,7 @@
 #include "run-command.h"
 #include <signal.h>
 #include "parse-options.h"
+#include "gpg-interface.h"
 
 static const char * const verify_tag_usage[] = {
 		"git verify-tag [-v|--verbose] <tag>...",
@@ -19,42 +20,16 @@ static const char * const verify_tag_usage[] = {
 
 static int run_gpg_verify(const char *buf, unsigned long size, int verbose)
 {
-	struct child_process gpg;
-	const char *args_gpg[] = {"gpg", "--verify", "FILE", "-", NULL};
-	char path[PATH_MAX];
-	size_t len;
-	int fd, ret;
+	int len;
 
-	fd = git_mkstemp(path, PATH_MAX, ".git_vtag_tmpXXXXXX");
-	if (fd < 0)
-		return error("could not create temporary file '%s': %s",
-						path, strerror(errno));
-	if (write_in_full(fd, buf, size) < 0)
-		return error("failed writing temporary file '%s': %s",
-						path, strerror(errno));
-	close(fd);
-
-	/* find the length without signature */
 	len = parse_signature(buf, size);
 	if (verbose)
 		write_in_full(1, buf, len);
 
-	memset(&gpg, 0, sizeof(gpg));
-	gpg.argv = args_gpg;
-	gpg.in = -1;
-	args_gpg[2] = path;
-	if (start_command(&gpg)) {
-		unlink(path);
-		return error("could not run gpg.");
-	}
-
-	write_in_full(gpg.in, buf, len);
-	close(gpg.in);
-	ret = finish_command(&gpg);
+	if (size == len)
+		return error("no signature found");
 
-	unlink_or_warn(path);
-
-	return ret;
+	return verify_signed_buffer(buf, len, buf + len, size - len, NULL);
 }
 
 static int verify_tag(const char *name, int verbose)
diff --git a/gpg-interface.c b/gpg-interface.c
new file mode 100644
index 0000000..ff232c8
--- /dev/null
+++ b/gpg-interface.c
@@ -0,0 +1,138 @@
+#include "cache.h"
+#include "run-command.h"
+#include "strbuf.h"
+#include "gpg-interface.h"
+#include "sigchain.h"
+
+static char *configured_signing_key;
+
+void set_signing_key(const char *key)
+{
+	free(configured_signing_key);
+	configured_signing_key = xstrdup(key);
+}
+
+int git_gpg_config(const char *var, const char *value, void *cb)
+{
+	if (!strcmp(var, "user.signingkey")) {
+		if (!value)
+			return config_error_nonbool(var);
+		set_signing_key(value);
+	}
+	return 0;
+}
+
+const char *get_signing_key(void)
+{
+	if (configured_signing_key)
+		return configured_signing_key;
+	return git_committer_info(IDENT_ERROR_ON_NO_NAME|IDENT_NO_DATE);
+}
+
+/*
+ * Create a detached signature for the contents of "buffer" and append
+ * it after "signature"; "buffer" and "signature" can be the same
+ * strbuf instance, which would cause the detached signature appended
+ * at the end.
+ */
+int sign_buffer(struct strbuf *buffer, struct strbuf *signature, const char *signing_key)
+{
+	struct child_process gpg;
+	const char *args[4];
+	ssize_t len;
+	size_t i, j, bottom;
+
+	memset(&gpg, 0, sizeof(gpg));
+	gpg.argv = args;
+	gpg.in = -1;
+	gpg.out = -1;
+	args[0] = "gpg";
+	args[1] = "-bsau";
+	args[2] = signing_key;
+	args[3] = NULL;
+
+	if (start_command(&gpg))
+		return error(_("could not run gpg."));
+
+	/*
+	 * When the username signingkey is bad, program could be terminated
+	 * because gpg exits without reading and then write gets SIGPIPE.
+	 */
+	sigchain_push(SIGPIPE, SIG_IGN);
+
+	if (write_in_full(gpg.in, buffer->buf, buffer->len) != buffer->len) {
+		close(gpg.in);
+		close(gpg.out);
+		finish_command(&gpg);
+		return error(_("gpg did not accept the data"));
+	}
+	close(gpg.in);
+
+	bottom = signature->len;
+	len = strbuf_read(signature, gpg.out, 1024);
+	close(gpg.out);
+
+	sigchain_pop(SIGPIPE);
+
+	if (finish_command(&gpg) || !len || len < 0)
+		return error(_("gpg failed to sign the data"));
+
+	/* Strip CR from the line endings, in case we are on Windows. */
+	for (i = j = bottom; i < signature->len; i++)
+		if (signature->buf[i] != '\r') {
+			if (i != j)
+				signature->buf[j] = signature->buf[i];
+			j++;
+		}
+	strbuf_setlen(signature, j);
+
+	return 0;
+}
+
+/*
+ * Run "gpg" to see if the payload matches the detached signature.
+ * gpg_output_to tells where the output from "gpg" should go:
+ *   < 0: /dev/null
+ *   = 0: standard error of the calling process
+ *   > 0: the specified file descriptor
+ */
+int verify_signed_buffer(const char *payload, size_t payload_size,
+			 const char *signature, size_t signature_size,
+			 struct strbuf *gpg_output)
+{
+	struct child_process gpg;
+	const char *args_gpg[] = {"gpg", "--verify", "FILE", "-", NULL};
+	char path[PATH_MAX];
+	int fd, ret;
+
+	fd = git_mkstemp(path, PATH_MAX, ".git_vtag_tmpXXXXXX");
+	if (fd < 0)
+		return error("could not create temporary file '%s': %s",
+			     path, strerror(errno));
+	if (write_in_full(fd, signature, signature_size) < 0)
+		return error("failed writing detached signature to '%s': %s",
+			     path, strerror(errno));
+	close(fd);
+
+	memset(&gpg, 0, sizeof(gpg));
+	gpg.argv = args_gpg;
+	gpg.in = -1;
+	if (gpg_output)
+		gpg.err = -1;
+	args_gpg[2] = path;
+	if (start_command(&gpg)) {
+		unlink(path);
+		return error("could not run gpg.");
+	}
+
+	write_in_full(gpg.in, payload, payload_size);
+	close(gpg.in);
+
+	if (gpg_output)
+		strbuf_read(gpg_output, gpg.err, 0);
+	ret = finish_command(&gpg);
+
+	unlink_or_warn(path);
+
+	return ret;
+}
diff --git a/gpg-interface.h b/gpg-interface.h
new file mode 100644
index 0000000..b9c3608
--- /dev/null
+++ b/gpg-interface.h
@@ -0,0 +1,10 @@
+#ifndef GPG_INTERFACE_H
+#define GPG_INTERFACE_H
+
+extern int sign_buffer(struct strbuf *buffer, struct strbuf *signature, const char *signing_key);
+extern int verify_signed_buffer(const char *payload, size_t payload_size, const char *signature, size_t signature_size, struct strbuf *gpg_output);
+extern int git_gpg_config(const char *, const char *, void *);
+extern void set_signing_key(const char *);
+extern const char *get_signing_key(void);
+
+#endif
diff --git a/tag.c b/tag.c
index 7d38cc0..3aa186d 100644
--- a/tag.c
+++ b/tag.c
@@ -139,6 +139,11 @@ int parse_tag(struct tag *item)
 	return ret;
 }
 
+/*
+ * Look at a signed tag object, and return the offset where
+ * the embedded detached signature begins, or the end of the
+ * data when there is no such signature.
+ */
 size_t parse_signature(const char *buf, unsigned long size)
 {
 	char *eol;
-- 
1.7.7.498.g3f2e50

^ permalink raw reply related

* [PATCH v4 0/5] Signed-commit
From: Junio C Hamano @ 2011-10-20  0:36 UTC (permalink / raw)
  To: git
In-Reply-To: <1318983645-18897-1-git-send-email-gitster@pobox.com>

The fourth iteration.

 - The first patch refactors where the current code invokes gpg for
   signing and verification of tags;

 - The second patch introduces signed commit objects (unchanged);

 - The third patch teaches "git log/show" to show the signature (adjusted
   to the updated "gpg output capture" API, with colors);

 - The fourth patch refactors helper bits to test GPG related features
   out of existing "tag -s" && "tag --verify" test script (new); and

 - The fifth patch adds tests for "commit [--amend] -S", "merge -S",
   and "show --show-signature" (new).

I think the internal API to drive "gpg" in this iteration is good to go.
The verification side has been updated to allow the callers to capture
output from GPG in a strbuf, so that they can pretty-print it in a format
more suitable in their context, rather than just spewing it out to the
standard error stream.

Junio C Hamano (5):
  Split GPG interface into its own helper library
  commit: teach --gpg-sign option
  log: --show-signature
  t7004: extract generic "GPG testing" bits
  test "commit -S" and "log --show-signature"

 Makefile                 |    2 +
 builtin/commit-tree.c    |   24 +++++++-
 builtin/commit.c         |   12 +++-
 builtin/merge.c          |   16 +++++-
 builtin/tag.c            |   76 ++-----------------------
 builtin/verify-tag.c     |   35 ++----------
 commit.c                 |   74 ++++++++++++++++++++++++-
 commit.h                 |    5 +-
 gpg-interface.c          |  138 ++++++++++++++++++++++++++++++++++++++++++++++
 gpg-interface.h          |   10 +++
 log-tree.c               |   39 +++++++++++++
 notes-cache.c            |    2 +-
 notes-merge.c            |    2 +-
 revision.c               |    2 +
 revision.h               |    1 +
 t/lib-gpg.sh             |   29 ++++++++++
 t/lib-gpg/pubring.gpg    |  Bin 0 -> 1164 bytes
 t/lib-gpg/random_seed    |  Bin 0 -> 600 bytes
 t/lib-gpg/secring.gpg    |  Bin 0 -> 1237 bytes
 t/lib-gpg/trustdb.gpg    |  Bin 0 -> 1280 bytes
 t/t7004-tag.sh           |   29 +---------
 t/t7004/pubring.gpg      |  Bin 1164 -> 0 bytes
 t/t7004/random_seed      |  Bin 600 -> 0 bytes
 t/t7004/secring.gpg      |  Bin 1237 -> 0 bytes
 t/t7004/trustdb.gpg      |  Bin 1280 -> 0 bytes
 t/t7510-signed-commit.sh |   60 ++++++++++++++++++++
 tag.c                    |    5 ++
 27 files changed, 422 insertions(+), 139 deletions(-)
 create mode 100644 gpg-interface.c
 create mode 100644 gpg-interface.h
 create mode 100644 t/lib-gpg.sh
 create mode 100644 t/lib-gpg/pubring.gpg
 create mode 100644 t/lib-gpg/random_seed
 create mode 100644 t/lib-gpg/secring.gpg
 create mode 100644 t/lib-gpg/trustdb.gpg
 delete mode 100644 t/t7004/pubring.gpg
 delete mode 100644 t/t7004/random_seed
 delete mode 100644 t/t7004/secring.gpg
 delete mode 100644 t/t7004/trustdb.gpg
 create mode 100755 t/t7510-signed-commit.sh

-- 
1.7.7.498.g3f2e50

^ permalink raw reply

* Re: [msysGit] Re: Compiling on Windows
From: Johannes Schindelin @ 2011-10-19 23:17 UTC (permalink / raw)
  To: Philip Oakley; +Cc: Andrew Ardill, Vincent van Ravesteijn, Git MsysGit, git
In-Reply-To: <2015B7F2CEAE4B449EA4EF744F9B8FD9@PhilipOakley>

Hi,

On Wed, 19 Oct 2011, Philip Oakley wrote:

> From: "Vincent van Ravesteijn" <vfr@lyx.org>
> > Op 18-10-2011 6:08, Andrew Ardill schreef:
> > > Hi list, I have been searching for details on what is required to 
> > > compile on Windows, but haven't found anything conclusive. Perhaps 
> > > there is something on the wiki, but unfortunately it is down at the 
> > > moment.

The quickest way to get Git for Windows compiled is to download & run
msysGit-netinstall from http://msysgit.googlecode.com/.

Ciao,
Johannes

^ permalink raw reply

* Re: [PATCH 08/12] do_for_each_ref_in_arrays(): new function
From: Junio C Hamano @ 2011-10-19 22:39 UTC (permalink / raw)
  To: mhagger
  Cc: git, Jeff King, Drew Northup, Jakub Narebski, Heiko Voigt,
	Johan Herland, Julian Phillips
In-Reply-To: <1319060692-27216-9-git-send-email-mhagger@alum.mit.edu>

Is this necessary?  IOW, is the helper function usable in any context
other than merge-iterate loose and packed refs?

^ permalink raw reply

* Re: [IGNORETHIS/PATCH] Choosing the sha1 prefix of your commits
From: Jonathan Nieder @ 2011-10-19 22:09 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason; +Cc: Git Mailing List
In-Reply-To: <CACBZZX5PqYa0uWiGgs952rk2cy+QRCU95kF63qzSi3fKK-YrCQ@mail.gmail.com>

Ævar Arnfjörð Bjarmason wrote:

> Someone asked me if having a "lulz" header wouldn't
> break things, but since we introduced the "encoding" header a while
> back clients have learned to ignore unknown headers, so it doesn't.
>
> Which is why we can discuss e.g. adding GPG headers without worrying
> about breaking everything.

Just for the record (and I think you understood this already): it is
not quite so simple.  Clients are allowed to assume that no "unknown"
header lines will appear before any of the known fields.  A future git
version could even assume that, say, a

	length-of-remainder-of-header

field appears before any lines after encoding and the GPG stuff.

So it would definitely be frowned upon to add new lines to that part
of the commit object without at least discussing it on this list.
Jeff mentioned one way to get your lulz without violating that
constraint. ;-)

http://thread.gmane.org/gmane.comp.version-control.git/138848/focus=138921

^ permalink raw reply

* Re: [RFC 04/13] add_ref(): move the call of check_refname_format() to callers
From: Michael Haggerty @ 2011-10-19 21:59 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: git, Jeff King, cmn, A Large Angry SCM, Daniel Barkalow,
	Sverre Rabbelier
In-Reply-To: <7vsjmobqim.fsf@alter.siamese.dyndns.org>

On 10/19/2011 11:49 PM, Junio C Hamano wrote:
> mhagger@alum.mit.edu writes:
>> I'm still not clear on how extra_refs are used.  Are they generated
>> from local refs or are they generated from remote refs?  If the
>> latter, then it is probably irresponsible not to do *some* sanity
>> checking in add_extra_ref() to prevent any chance of refnames like
>> "../../../etc/passwd".
> 
> No, add_extra_ref() already tells us what their values are, these are
> never used to actually read from filesystem. Their refname field has
> almost no value other than for debugging and we probably shouldn't even
> insist on uniqueness among extra refs or for that matter collision with
> the real refs. [...]

Thanks for the explanation.  I'm inspired to separate them a little bit
more from "real" refs because they are such a special case.  For
example, maybe it would make sense to add a function
for_each_extra_ref() to avoid having to mix them with real refs in the
iteration.  OTOH not important AFAICS.

Michael

-- 
Michael Haggerty
mhagger@alum.mit.edu
http://softwareswirl.blogspot.com/

^ permalink raw reply

* What's cooking in git.git (Oct 2011, #07; Wed, 19)
From: Junio C Hamano @ 2011-10-19 21:57 UTC (permalink / raw)
  To: git

Here are the topics that have been cooking.  Commits prefixed with '-' are
only in 'pu' (proposed updates) while commits prefixed with '+' are in 'next'.

The fifth batch of topics started graduating to the master branch. It is
probably a good point to stop taking new topics and start switching our
focus to fixing bugs in the topics already in 'master'.

Here are the repositories that have my integration branches:

With maint, master, next, pu, todo, html and man:

	git://git.kernel.org/pub/scm/git/git.git
	git://repo.or.cz/alt-git.git
	https://code.google.com/p/git-core/
	https://github.com/git/git

With only maint, master, html and man:

	git://git.sourceforge.jp/gitroot/git-core/git.git
	git://git-core.git.sourceforge.net/gitroot/git-core/git-core

With all the topics and integration branches but not todo, html or man:

	https://github.com/gitster/git

--------------------------------------------------
[New Topics]

* jc/match-refs-clarify (2011-09-12) 2 commits
 - rename "match_refs()" to "match_push_refs()"
 - send-pack: typofix error message

* jn/libperl-git-config (2011-10-18) 1 commit
 - libperl-git: refactor Git::config_*

* ss/inet-ntop (2011-10-18) 1 commit
 - inet_ntop.c: Work around GCC 4.6's detection of uninitialized variables

--------------------------------------------------
[Graduated to "master"]

* bk/submodule-in-recursive-merge (2011-10-13) 2 commits
  (merged to 'next' on 2011-10-15 at e02205c)
 + submodule: Search for merges only at end of recursive merge
 + submodule: Demonstrate known breakage during recursive merge

Brad helped resurrecting good bits earlier tangled in the stalled topic
hv/submodule-merge-search by mistake.

* cb/httpd-test-fix-port (2011-10-17) 1 commit
  (merged to 'next' on 2011-10-17 at 84fb7e6)
 + use test number as port number

* jm/maint-apply-detects-corrupt-patch-header (2011-10-12) 1 commit
  (merged to 'next' on 2011-10-12 at 80d9503)
 + fix "git apply --index ..." not to deref NULL

* jn/gitweb-manpages (2011-10-16) 5 commits
  (merged to 'next' on 2011-10-16 at 6555a07)
 + gitweb: Add gitweb manpages to 'gitweb' package in git.spec
 + Documentation: Add gitweb config variables to git-config(1)
 + Documentation: Link to gitweb(1) and gitweb.conf(5) in other manpages
 + gitweb: Add gitweb(1) manpage for gitweb itself
 + gitweb: Add gitweb.conf(5) manpage for gitweb configuration files

* js/log-show-children (2011-10-04) 1 commit
  (merged to 'next' on 2011-10-06 at de8f6f2)
 + log --children

* js/maint-no-cherry-pick-head-after-punted (2011-10-06) 2 commits
 + cherry-pick: do not give irrelevant advice when cherry-pick punted
 + revert.c: defer writing CHERRY_PICK_HEAD till it is safe to do so
 (this branch is used by js/no-cherry-pick-head-after-punted.)

Will merge to 'maint' later.

* js/merge-edit-option (2011-10-12) 1 commit
  (merged to 'next' on 2011-10-12 at db28da3)
 + Teach merge the '[-e|--edit]' option

* js/no-cherry-pick-head-after-punted (2011-10-06) 1 commit
  (merged to 'next' on 2011-10-10 at acb29ee)
 + Merge branch 'js/maint-no-cherry-pick-head-after-punted' into js/no-cherry-pick-head-after-punted
 (this branch uses js/maint-no-cherry-pick-head-after-punted.)

* md/smtp-tls-hello-again (2011-10-15) 1 commit
  (merged to 'next' on 2011-10-16 at 3e27de4)
 + send-email: Honour SMTP domain when using TLS

* pt/mingw-misc-fixes (2011-10-15) 7 commits
  (merged to 'next' on 2011-10-16 at 2dc0b55)
 + t9901: fix line-ending dependency on windows
 + mingw: ensure sockets are initialized before calling gethostname
 + mergetools: use the correct tool for Beyond Compare 3 on Windows
 + t9300: do not run --cat-blob-fd related tests on MinGW
 + git-svn: On MSYS, escape and quote SVN_SSH also if set by the user
 + t9001: do not fail only due to CR/LF issues
 + t1020: disable the pwd test on MinGW

* rs/diff-whole-function (2011-10-10) 2 commits
  (merged to 'next' on 2011-10-11 at 6196752)
 + diff: add option to show whole functions as context
 + xdiff: factor out get_func_line()

* rs/pickaxe (2011-10-07) 7 commits
  (merged to 'next' on 2011-10-11 at 27d02b2)
 + pickaxe: factor out pickaxe
 + pickaxe: give diff_grep the same signature as has_changes
 + pickaxe: pass diff_options to contains and has_changes
 + pickaxe: factor out has_changes
 + pickaxe: plug regex/kws leak
 + pickaxe: plug regex leak
 + pickaxe: plug diff filespec leak with empty needle

--------------------------------------------------
[Stalled]

* hv/submodule-merge-search (2011-10-13) 4 commits
 - submodule.c: make two functions static
 - allow multiple calls to submodule merge search for the same path
 - push: Don't push a repository with unpushed submodules
 - push: teach --recurse-submodules the on-demand option

What the topic aims to achieve may make sense, but the implementation
looked somewhat suboptimal.

The fix-up at the tip queued on fg/submodule-auto-push topic has been
moved to this topic.

* sr/transport-helper-fix-rfc (2011-07-19) 2 commits
 - t5800: point out that deleting branches does not work
 - t5800: document inability to push new branch with old content

Perhaps 281eee4 (revision: keep track of the end-user input from the
command line, 2011-08-25) would help.

* rr/revert-cherry-pick (2011-10-12) 7 commits
 - revert: further simplify parsing of a line in insn sheet
 - revert: Simplify passing command-line arguments around
 - revert: Allow mixed pick and revert instructions
 - revert: Make commit descriptions in insn sheet optional
 - revert: Fix buffer overflow in insn sheet parser
 - revert: Simplify getting commit subject
 - revert: Free memory after get_message call

Probably needs a little bit more polish, e.g. squashing the tip fixup into
an earlier one in the series.

* jc/lookup-object-hash (2011-08-11) 6 commits
 - object hash: replace linear probing with 4-way cuckoo hashing
 - object hash: we know the table size is a power of two
 - object hash: next_size() helper for readability
 - pack-objects --count-only
 - object.c: remove duplicated code for object hashing
 - object.c: code movement for readability

I do not think there is anything fundamentally wrong with this series, but
the risk of breakage far outweighs observed performance gain in one
particular workload.

--------------------------------------------------
[Cooking]

* jc/check-ref-format-fixup (2011-10-12) 2 commits
  (merged to 'next' on 2011-10-15 at 8e89bc5)
 + Restrict ref-like names immediately below $GIT_DIR
 + refs.c: move dwim_ref()/dwim_log() from sha1_name.c

An attempt to fix-up unfortunate side effect of mh/check-ref-format-3
topic. "git show -s config" is never meant to refer to $GIT_DIR/config
and treat it as a file that records an object name. But tightening the
rule for reading side means we would need to tighten the rule for the
writing side, too, which turned out to be rather nasty fallout that
needs to be sorted out before the next release.

* pw/p4-update (2011-10-17) 6 commits
  (merged to 'next' on 2011-10-17 at f69f6cc)
 + git-p4: handle files with shell metacharacters
 + git-p4: keyword flattening fixes
 + git-p4: stop ignoring apple filetype
 + git-p4: recognize all p4 filetypes
 + git-p4: handle utf16 filetype properly
 + git-p4 tests: refactor and cleanup

Will merge to 'master' in the fifth wave.

* cn/doc-config-bare-subsection (2011-10-16) 1 commit
  (merged to 'next' on 2011-10-17 at a6412d4)
 + Documentation: update [section.subsection] to reflect what git does

Will merge to 'master' in the fifth wave.

* cb/daemon-permission-errors (2011-10-17) 2 commits
 - daemon: report permission denied error to clients
 - daemon: add tests
 (this branch uses jk/daemon-msgs.)

The tip commit might be loosening things a bit too much.
Will keep in 'pu' until hearing a convincing argument for the patch.

* jc/verbose-checkout (2011-10-16) 2 commits
 - checkout -v: give full status output after switching branches
 - checkout: move the local changes report to the end

This is just to leave a record that the reason why we do not do this not
because we are incapable of coding this, but because it is not a good idea
to do this. I suspect people who are new to git that might think they need
it would soon realize the don't.

Will keep in 'pu' as a showcase for a while and then will drop.

* kk/gitweb-side-by-side-diff (2011-10-17) 2 commits
 - gitweb: add a feature to show side-by-side diff
 - gitweb: change format_diff_line() to remove leading SP from $diff_class

Fun.
Will keep in 'pu' until the planned re-roll comes.

* mh/ref-api-2 (2011-10-17) 14 commits
  (merged to 'next' on 2011-10-19 at cc89f0e)
 + resolve_gitlink_ref_recursive(): change to work with struct ref_cache
 + Pass a (ref_cache *) to the resolve_gitlink_*() helper functions
 + resolve_gitlink_ref(): improve docstring
 + get_ref_dir(): change signature
 + refs: change signatures of get_packed_refs() and get_loose_refs()
 + is_dup_ref(): extract function from sort_ref_array()
 + add_ref(): add docstring
 + parse_ref_line(): add docstring
 + is_refname_available(): remove the "quiet" argument
 + clear_ref_array(): rename from free_ref_array()
 + refs: rename parameters result -> sha1
 + refs: rename "refname" variables
 + struct ref_entry: document name member
 + cache.h: add comments for git_path() and git_path_submodule()
 (this branch uses mh/ref-api.)

* po/insn-editor (2011-10-17) 1 commit
  (merged to 'next' on 2011-10-19 at cbf5e0b)
 + "rebase -i": support special-purpose editor to edit insn sheet

* dm/pack-objects-update (2011-10-18) 4 commits
 - pack-objects: don't traverse objects unnecessarily
 - pack-objects: rewrite add_descendants_to_write_order() iteratively
 - pack-objects: use unsigned int for counter and offset values
 - pack-objects: mark add_to_write_order() as inline

* jk/git-tricks (2011-10-18) 3 commits
 - completion: match ctags symbol names in grep patterns
 - contrib: add git-jump script
 - contrib: add diff highlight script

* jc/make-tags (2011-10-18) 1 commit
 - Makefile: ask "ls-files" to list source files if available

* jc/signed-commit (2011-10-18) 3 commits
 - log: --show-signature
 - commit: teach --gpg-sign option
 - Split GPG interface into its own helper library

This is to replace the earlier "signed push" experiments. "verify-tag"
equivalent needs to be written before this can proceed. I suspect that
teaching "verify-tag" to notice and also handle signed commits would be
the easiest, but "git tag --verify $commit" might look slightly funny
from the UI POV. I dunno.

* mh/ref-api (2011-10-16) 7 commits
  (merged to 'next' on 2011-10-17 at 219000f)
 + clear_ref_cache(): inline function
 + write_ref_sha1(): only invalidate the loose ref cache
 + clear_ref_cache(): extract two new functions
 + clear_ref_cache(): rename parameter
 + invalidate_ref_cache(): expose this function in the refs API
 + invalidate_ref_cache(): take the submodule as parameter
 + invalidate_ref_cache(): rename function from invalidate_cached_refs()
 (this branch is used by mh/ref-api-2.)

Will merge to 'master' in the sixth wave.

* sg/complete-refs (2011-10-15) 10 commits
 - completion: remove broken dead code from __git_heads() and __git_tags()
 - completion: fast initial completion for config 'remote.*.fetch' value
 - completion: improve ls-remote output filtering in __git_refs_remotes()
 - completion: query only refs/heads/ in __git_refs_remotes()
 - completion: support full refs from remote repositories
 - completion: improve ls-remote output filtering in __git_refs()
 - completion: make refs completion consistent for local and remote repos
 - completion: optimize refs completion
 - completion: document __gitcomp()
 - Merge branches 'tm/completion-push-set-upstream', 'tm/completion-commit-fixup-squash' and 'sg/completion' into HEAD

Rerolled.

Will keep in 'pu' until an Ack or two from people who have worked on the
completion in the past comes.

* jc/unseekable-bundle (2011-10-13) 2 commits
  (merged to 'next' on 2011-10-19 at 2978ee0)
 + bundle: add parse_bundle_header() helper function
 + bundle: allowing to read from an unseekable fd

I am not entirely happy with the first patch but it is not so bad either.

* jk/daemon-msgs (2011-10-15) 1 commit
  (merged to 'next' on 2011-10-15 at 415cf53)
 + daemon: give friendlier error messages to clients
 (this branch is used by cb/daemon-permission-errors.)

Will merge to 'master' in the fifth wave.

* jk/maint-pack-objects-compete-with-delete (2011-10-14) 2 commits
  (merged to 'next' on 2011-10-15 at 49479e4)
 + downgrade "packfile cannot be accessed" errors to warnings
 + pack-objects: protect against disappearing packs

Will merge to 'master' in the fifth wave.

* cn/fetch-prune (2011-10-15) 5 commits
  (merged to 'next' on 2011-10-16 at 02a449e)
 + fetch: treat --tags like refs/tags/*:refs/tags/* when pruning
 + fetch: honor the user-provided refspecs when pruning refs
 + remote: separate out the remote_find_tracking logic into query_refspecs
 + t5510: add tests for fetch --prune
 + fetch: free all the additional refspecs

Will merge to 'master' in the sixth wave.

* sc/difftool-skip (2011-10-14) 2 commits
  (merged to 'next' on 2011-10-14 at b91c581)
 + t7800: avoid arithmetic expansion notation
  (merged to 'next' on 2011-10-11 at 38d7e84)
 + git-difftool: allow skipping file by typing 'n' at prompt

Will merge to 'master' in the fifth wave.

* jc/maint-remove-renamed-ref (2011-10-12) 1 commit
  (merged to 'next' on 2011-10-12 at 819c3e4)
 + branch -m/-M: remove undocumented RENAMED-REF

Will merge to 'master' in the sixth wave.

* ph/transport-with-gitfile (2011-10-11) 5 commits
  (merged to 'next' on 2011-10-12 at 6d58417)
 + Fix is_gitfile() for files too small or larger than PATH_MAX to be a gitfile
  (merged to 'next' on 2011-10-06 at 891b8b6)
 + Add test showing git-fetch groks gitfiles
 + Teach transport about the gitfile mechanism
 + Learn to handle gitfiles in enter_repo
 + enter_repo: do not modify input

Will merge to 'master' in the fifth wave.

* jc/request-pull-show-head-4 (2011-10-15) 11 commits
  (merged to 'next' on 2011-10-15 at 7e340ff)
 + fmt-merge-msg.c: Fix an "dubious one-bit signed bitfield" sparse error
  (merged to 'next' on 2011-10-10 at 092175e)
 + environment.c: Fix an sparse "symbol not declared" warning
 + builtin/log.c: Fix an "Using plain integer as NULL pointer" warning
  (merged to 'next' on 2011-10-07 at fcaeca0)
 + fmt-merge-msg: use branch.$name.description
  (merged to 'next' on 2011-10-06 at fa5e0fe)
 + request-pull: use the branch description
 + request-pull: state what commit to expect
 + request-pull: modernize style
 + branch: teach --edit-description option
 + format-patch: use branch description in cover letter
 + branch: add read_branch_desc() helper function
 + Merge branch 'bk/ancestry-path' into jc/branch-desc

Will merge to 'master' in the sixth wave.

--------------------------------------------------
[Discarded]

* jk/http-auth-keyring (2011-10-12) 19 commits
 . http_init: accept separate URL parameter
 . credential-cache: don't cache items without context
 . check_expirations: don't copy over same element
 . t0300: add missing EOF terminator for <<
 . credential-store: use a better storage format
 . t0300: make alternate username tests more robust
 . t0300: make askpass tests a little more robust
 . credential-cache: fix expiration calculation corner cases
 . docs: minor tweaks to credentials API
 . credentials: make credential_fill_gently() static
 . credentials: add "getpass" helper
 . credentials: add "store" helper
 . credentials: add "cache" helper
 . docs: end-user documentation for the credential subsystem
 . http: use hostname in credential description
 . allow the user to configure credential helpers
 . look for credentials in config before prompting
 . http: use credential API to get passwords
 . introduce credentials API
 (this branch is used by js/cred-macos-x-keychain-2.)

Discarded without prejudice to allow design level discussions to continue.
Expecting a re-roll based on jk/http-auth

* js/cred-macos-x-keychain-2 (2011-10-12) 1 commit
 . contrib: add a pair of credential helpers for Mac OS X's keychain
 (this branch uses jk/http-auth-keyring.)

Discarded without prejudice to allow design level discussions to continue.
Expecting a re-roll based on jk/http-auth

^ permalink raw reply

* Re: [RFC 09/13] new_branch(): verify that new branch name is a valid full refname
From: Junio C Hamano @ 2011-10-19 21:52 UTC (permalink / raw)
  To: mhagger
  Cc: Junio C Hamano, git, Jeff King, cmn, A Large Angry SCM,
	Daniel Barkalow, Sverre Rabbelier
In-Reply-To: <1319057716-28094-10-git-send-email-mhagger@alum.mit.edu>

mhagger@alum.mit.edu writes:

> From: Michael Haggerty <mhagger@alum.mit.edu>
>
>
> Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
> ---
>
> Is it possible to omit the REFNAME_ALLOW_ONELEVEL option from this
> call?

I _think_ it takes an unadorned branch name, so the most prudent would be
to check the result of prefixing "refs/heads/" to *name with REFNAME_FULL.

>  fast-import.c |    2 +-
>  1 files changed, 1 insertions(+), 1 deletions(-)
>
> diff --git a/fast-import.c b/fast-import.c
> index 8d8ea3c..51cf898 100644
> --- a/fast-import.c
> +++ b/fast-import.c
> @@ -722,7 +722,7 @@ static struct branch *new_branch(const char *name)
>  
>  	if (b)
>  		die("Invalid attempt to create duplicate branch: %s", name);
> -	if (check_refname_format(name, REFNAME_ALLOW_ONELEVEL))
> +	if (check_refname_format(name, REFNAME_FULL|REFNAME_ALLOW_ONELEVEL))
>  		die("Branch name doesn't conform to GIT standards: %s", name);
>  
>  	b = pool_calloc(1, sizeof(struct branch));

^ permalink raw reply

* [PATCH 10/12] names_conflict(): new function, extracted from is_refname_available()
From: mhagger @ 2011-10-19 21:44 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: git, Jeff King, Drew Northup, Jakub Narebski, Heiko Voigt,
	Johan Herland, Julian Phillips, Michael Haggerty
In-Reply-To: <1319060692-27216-1-git-send-email-mhagger@alum.mit.edu>

From: Michael Haggerty <mhagger@alum.mit.edu>


This costs an extra strlen() in the loop, but even that small price
will be clawed back in the next patch.

Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 refs.c |   43 +++++++++++++++++++++++++++++++------------
 1 files changed, 31 insertions(+), 12 deletions(-)

diff --git a/refs.c b/refs.c
index 7c2bcab..ada691b 100644
--- a/refs.c
+++ b/refs.c
@@ -1088,6 +1088,30 @@ static int remove_empty_directories(const char *file)
 }
 
 /*
+ * Return true iff refname1 and refname2 conflict with each other.
+ * Two reference names conflict if one of them exactly matches the
+ * leading components of the other; e.g., "foo/bar" conflicts with
+ * both "foo" and with "foo/bar/baz" but not with "foo/bar" or
+ * "foo/barbados".
+ */
+static int names_conflict(const char *refname1, const char *refname2)
+{
+	int len1 = strlen(refname1);
+	int len2 = strlen(refname2);
+	int cmplen;
+	const char *lead;
+
+	if (len1 < len2) {
+		cmplen = len1;
+		lead = refname2;
+	} else {
+		cmplen = len2;
+		lead = refname1;
+	}
+	return !strncmp(refname1, refname2, cmplen) && lead[cmplen] == '/';
+}
+
+/*
  * Return true iff a reference named refname could be created without
  * conflicting with the name of an existing reference.  If oldrefname
  * is non-NULL, ignore potential conflicts with oldrefname (e.g.,
@@ -1097,20 +1121,15 @@ static int remove_empty_directories(const char *file)
 static int is_refname_available(const char *refname, const char *oldrefname,
 				struct ref_array *array)
 {
-	int i, namlen = strlen(refname); /* e.g. 'foo/bar' */
+	int i;
 	for (i = 0; i < array->nr; i++ ) {
 		struct ref_entry *entry = array->refs[i];
-		/* entry->name could be 'foo' or 'foo/bar/baz' */
-		if (!oldrefname || strcmp(oldrefname, entry->name)) {
-			int len = strlen(entry->name);
-			int cmplen = (namlen < len) ? namlen : len;
-			const char *lead = (namlen < len) ? entry->name : refname;
-			if (!strncmp(refname, entry->name, cmplen) &&
-			    lead[cmplen] == '/') {
-				error("'%s' exists; cannot create '%s'",
-				      entry->name, refname);
-				return 0;
-			}
+		if (oldrefname && !strcmp(oldrefname, entry->name))
+			continue;
+		if (names_conflict(refname, entry->name)) {
+			error("'%s' exists; cannot create '%s'",
+			      entry->name, refname);
+			return 0;
 		}
 	}
 	return 1;
-- 
1.7.7

^ permalink raw reply related

* [PATCH 11/12] names_conflict(): simplify implementation
From: mhagger @ 2011-10-19 21:44 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: git, Jeff King, Drew Northup, Jakub Narebski, Heiko Voigt,
	Johan Herland, Julian Phillips, Michael Haggerty
In-Reply-To: <1319060692-27216-1-git-send-email-mhagger@alum.mit.edu>

From: Michael Haggerty <mhagger@alum.mit.edu>


Save a bunch of lines of code and a couple of strlen() calls.

Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 refs.c |   17 ++++-------------
 1 files changed, 4 insertions(+), 13 deletions(-)

diff --git a/refs.c b/refs.c
index ada691b..4bc6041 100644
--- a/refs.c
+++ b/refs.c
@@ -1096,19 +1096,10 @@ static int remove_empty_directories(const char *file)
  */
 static int names_conflict(const char *refname1, const char *refname2)
 {
-	int len1 = strlen(refname1);
-	int len2 = strlen(refname2);
-	int cmplen;
-	const char *lead;
-
-	if (len1 < len2) {
-		cmplen = len1;
-		lead = refname2;
-	} else {
-		cmplen = len2;
-		lead = refname1;
-	}
-	return !strncmp(refname1, refname2, cmplen) && lead[cmplen] == '/';
+	for (; *refname1 && *refname1 == *refname2; refname1++, refname2++)
+		;
+	return (*refname1 == '\0' && *refname2 == '/')
+		|| (*refname1 == '/' && *refname2 == '\0');
 }
 
 /*
-- 
1.7.7

^ permalink raw reply related

* [PATCH 12/12] is_refname_available(): reimplement using do_for_each_ref_in_array()
From: mhagger @ 2011-10-19 21:44 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: git, Jeff King, Drew Northup, Jakub Narebski, Heiko Voigt,
	Johan Herland, Julian Phillips, Michael Haggerty
In-Reply-To: <1319060692-27216-1-git-send-email-mhagger@alum.mit.edu>

From: Michael Haggerty <mhagger@alum.mit.edu>

This implementation will survive upcoming changes to the ref_array
data structure.

Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 refs.c |   44 ++++++++++++++++++++++++++++++++------------
 1 files changed, 32 insertions(+), 12 deletions(-)

diff --git a/refs.c b/refs.c
index 4bc6041..c41d995 100644
--- a/refs.c
+++ b/refs.c
@@ -1102,6 +1102,25 @@ static int names_conflict(const char *refname1, const char *refname2)
 		|| (*refname1 == '/' && *refname2 == '\0');
 }
 
+struct name_conflict_cb {
+       const char *refname;
+       const char *oldrefname;
+       const char *conflicting_refname;
+};
+
+static int name_conflict_fn(const char *existingrefname, const unsigned char *sha1,
+			    int flags, void *cb_data)
+{
+       struct name_conflict_cb *data = (struct name_conflict_cb *)cb_data;
+       if (data->oldrefname && !strcmp(data->oldrefname, existingrefname))
+	       return 0;
+       if (names_conflict(data->refname, existingrefname)) {
+	       data->conflicting_refname = existingrefname;
+	       return 1;
+       }
+       return 0;
+}
+
 /*
  * Return true iff a reference named refname could be created without
  * conflicting with the name of an existing reference.  If oldrefname
@@ -1112,18 +1131,19 @@ static int names_conflict(const char *refname1, const char *refname2)
 static int is_refname_available(const char *refname, const char *oldrefname,
 				struct ref_array *array)
 {
-	int i;
-	for (i = 0; i < array->nr; i++ ) {
-		struct ref_entry *entry = array->refs[i];
-		if (oldrefname && !strcmp(oldrefname, entry->name))
-			continue;
-		if (names_conflict(refname, entry->name)) {
-			error("'%s' exists; cannot create '%s'",
-			      entry->name, refname);
-			return 0;
-		}
-	}
-	return 1;
+       struct name_conflict_cb data;
+       data.refname = refname;
+       data.oldrefname = oldrefname;
+       data.conflicting_refname = NULL;
+
+       if (do_for_each_ref_in_array(array, 0, "", name_conflict_fn,
+				    0, DO_FOR_EACH_INCLUDE_BROKEN,
+				    &data)) {
+	       error("'%s' exists; cannot create '%s'",
+		     data.conflicting_refname, refname);
+	       return 0;
+       }
+       return 1;
 }
 
 static struct ref_lock *lock_ref_sha1_basic(const char *refname,
-- 
1.7.7

^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox