* [PATCH v2 3/7] push -s: skeleton
From: Junio C Hamano @ 2011-09-08 20:01 UTC (permalink / raw)
To: git
In-Reply-To: <1315512102-19022-1-git-send-email-gitster@pobox.com>
If a tag is GPG-signed, and if you trust the cryptographic robustness of
the SHA-1 hash and GPG, you can sleep well knowing that all the history
leading to the signed commit cannot be tampered with. However, it would be
both cumbersome and cluttering to sign each and every commit. Especially
if you strive to keep your history clean by tweaking, rewriting and
polishing your commits before pushing the resulting history out, many
commits you will create locally end up not mattering at all, and it is a
waste of time to sign them all.
A better alternative could be to sign a "push certificate" (for the lack
of better name) every time you push, asserting that what commits you are
pushing to update which refs.
The basic workflow based on this idea would go like this:
1. You push out your work with "git push -s";
2. "git push", as usual, learns where the remote refs are and which refs
are to be updated with this push. It prepares a text file in core,
that looks like the following:
Push-Certificate-Version: 0
Pusher: Junio C Hamano <gitster@pobox.com> 1315427886 -0700
Update: 3793ac56b4c4f9bf0bddc306a0cec21118683728 refs/heads/master
Update: 12850bec0c24b529c9a9df6a95ad4bdeea39373e refs/heads/next
Each "Update" line shows the new object name at the tip of the ref
this push tries to update.
The user then is asked to sign this push certificate using GPG. The
result is carried to the other side (i.e. receive-pack). In the
protocol exchange, this step comes immediately after the sender tells
what the result of the push should be, before it sends the pack data.
3. The receiving end keeps the signed push certificate in core, receives
the pack data and unpacks (or stores and indexes) it as usual.
4. A new phase to record the push certificate is introduced in the
codepath after the receiving end runs receive_hook(). It is envisioned
that this phase:
a. parses the updated-to object names, and appends the push
certificate (still GPG signed) to a note attached to each of the
objects that will sit at the tip of the refs;
b. verifies that the push certificate is signed with a GPG key that is
authorized to push into this repository; and/or
c. invokes pre-receive-signature hook, feeds the push certificate to it
and asks it to veto the ref updates.
And here is a skeleton to implement this. The patch has the necessary
protocol extensions implemented (although I do not know if we need
separate codepath for stateless RPC mode), but does not have subroutines
to:
- Sign the certificate with GPG key;
- Parse the signed certificate to identify the updated-to objects, and
add the certificate as notes to them;
- Verify the certificate and find out what GPG key was used to sign it;
or
- Invoke and feed the certificate to pre-receive-signature hook.
all of which should be fairly trivial. The places that needs to implement
these are clearly marked with large comments, so I'll leave it up to other
people who are interested in the topic to fill in the blanks ;-)
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
builtin/push.c | 1 +
builtin/receive-pack.c | 54 +++++++++++++++++++++++++++++++++++++++-
builtin/send-pack.c | 64 +++++++++++++++++++++++++++++++++++++++++++++---
send-pack.h | 1 +
transport.c | 4 +++
transport.h | 4 +++
6 files changed, 123 insertions(+), 5 deletions(-)
diff --git a/builtin/push.c b/builtin/push.c
index 35cce53..2238f4e 100644
--- a/builtin/push.c
+++ b/builtin/push.c
@@ -261,6 +261,7 @@ int cmd_push(int argc, const char **argv, const char *prefix)
OPT_BIT('u', "set-upstream", &flags, "set upstream for git pull/status",
TRANSPORT_PUSH_SET_UPSTREAM),
OPT_BOOLEAN(0, "progress", &progress, "force progress reporting"),
+ OPT_BIT('s', "signed", &flags, "GPG sign the push", TRANSPORT_PUSH_SIGNED),
OPT_END()
};
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index ae164da..20b6799 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -30,12 +30,14 @@ static int receive_unpack_limit = -1;
static int transfer_unpack_limit = -1;
static int unpack_limit = 100;
static int report_status;
+static int signed_push;
static int use_sideband;
static int prefer_ofs_delta = 1;
static int auto_update_server_info;
static int auto_gc = 1;
static const char *head_name;
static int sent_capabilities;
+static char *push_certificate;
static enum deny_action parse_deny_action(const char *var, const char *value)
{
@@ -114,7 +116,7 @@ static int show_ref(const char *path, const unsigned char *sha1, int flag, void
else
packet_write(1, "%s %s%c%s%s\n",
sha1_to_hex(sha1), path, 0,
- " report-status delete-refs side-band-64k",
+ " report-status delete-refs side-band-64k signed-push",
prefer_ofs_delta ? " ofs-delta" : "");
sent_capabilities = 1;
return 0;
@@ -579,6 +581,31 @@ static void check_aliased_updates(struct command *commands)
string_list_clear(&ref_list, 0);
}
+static int record_signed_push(char *cert)
+{
+ /*
+ * This is the place for you to parse the signed push
+ * certificate, grab the commit object names the push updates
+ * refs to, and append the certificate to the notes to these
+ * commits.
+ *
+ * You could also feed the signed push certificate to GPG,
+ * verify the signer identity, and all the other fun stuff,
+ * including feeding it to "pre-receive-signature" hook.
+ *
+ * Here we just throw it to stderr to demonstrate that the
+ * codepath is being exercised.
+ */
+ char *cp, *ep;
+ for (cp = cert; *cp; cp = ep) {
+ ep = strchrnul(cp, '\n');
+ if (*ep == '\n')
+ ep++;
+ fprintf(stderr, "RSP: %.*s", (int)(ep - cp), cp);
+ }
+ return 0;
+}
+
static void execute_commands(struct command *commands, const char *unpacker_error)
{
struct command *cmd;
@@ -596,6 +623,12 @@ static void execute_commands(struct command *commands, const char *unpacker_erro
return;
}
+ if (push_certificate && record_signed_push(push_certificate)) {
+ for (cmd = commands; cmd; cmd = cmd->next)
+ cmd->error_string = "n/a (push signature error)";
+ return;
+ }
+
check_aliased_updates(commands);
head_name = resolve_ref("HEAD", sha1, 0, NULL);
@@ -636,6 +669,8 @@ static struct command *read_head_info(void)
report_status = 1;
if (strstr(refname + reflen + 1, "side-band-64k"))
use_sideband = LARGE_PACKET_MAX;
+ if (strstr(refname + reflen + 1, "signed-push"))
+ signed_push = 1;
}
cmd = xcalloc(1, sizeof(struct command) + len - 80);
hashcpy(cmd->old_sha1, old_sha1);
@@ -731,6 +766,21 @@ static const char *unpack(void)
}
}
+static char *receive_push_certificate(void)
+{
+ struct strbuf cert = STRBUF_INIT;
+ for (;;) {
+ char line[1000];
+ int len;
+
+ len = packet_read_line(0, line, sizeof(line));
+ if (!len)
+ break;
+ strbuf_add(&cert, line, len);
+ }
+ return strbuf_detach(&cert, NULL);
+}
+
static void report(struct command *commands, const char *unpack_status)
{
struct command *cmd;
@@ -846,6 +896,8 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
if ((commands = read_head_info()) != NULL) {
const char *unpack_status = NULL;
+ if (signed_push)
+ push_certificate = receive_push_certificate();
if (!delete_only(commands))
unpack_status = unpack();
execute_commands(commands, unpack_status);
diff --git a/builtin/send-pack.c b/builtin/send-pack.c
index 87833f4..7f4778c 100644
--- a/builtin/send-pack.c
+++ b/builtin/send-pack.c
@@ -237,6 +237,27 @@ static int sideband_demux(int in, int out, void *data)
return ret;
}
+static void sign_push_certificate(struct strbuf *cert)
+{
+ /*
+ * Here, take the contents of cert->buf, and have the user GPG
+ * sign it, and read it back in the strbuf.
+ *
+ * You may want to append some extra info to cert before giving
+ * it to GPG, possibly via a hook.
+ *
+ * Here we upcase them just to demonstrate that the codepath
+ * is being exercised.
+ */
+ char *cp;
+ for (cp = cert->buf; *cp; cp++) {
+ int ch = *cp;
+ if ('a' <= ch && ch <= 'z')
+ *cp = toupper(ch);
+ }
+ return;
+}
+
int send_pack(struct send_pack_args *args,
int fd[], struct child_process *conn,
struct ref *remote_refs,
@@ -250,9 +271,11 @@ int send_pack(struct send_pack_args *args,
int allow_deleting_refs = 0;
int status_report = 0;
int use_sideband = 0;
+ int signed_push = 0;
unsigned cmds_sent = 0;
int ret;
struct async demux;
+ struct strbuf push_cert = STRBUF_INIT;
/* Does the other end support the reporting? */
if (server_supports("report-status"))
@@ -270,6 +293,18 @@ int send_pack(struct send_pack_args *args,
return 0;
}
+ if (args->signed_push) {
+ if (server_supports("signed-push"))
+ signed_push = !args->dry_run;
+ else
+ warning("The receiving side does not support signed-push");
+ }
+
+ if (signed_push) {
+ strbuf_addstr(&push_cert, "Push-Certificate-Version: 0\n");
+ strbuf_addf(&push_cert, "Pusher: %s\n", git_committer_info(0));
+ }
+
/*
* Finally, tell the other end!
*/
@@ -301,15 +336,19 @@ int send_pack(struct send_pack_args *args,
char *old_hex = sha1_to_hex(ref->old_sha1);
char *new_hex = sha1_to_hex(ref->new_sha1);
- if (!cmds_sent && (status_report || use_sideband)) {
- packet_buf_write(&req_buf, "%s %s %s%c%s%s",
+ if (!cmds_sent &&
+ (status_report || use_sideband || signed_push))
+ packet_buf_write(&req_buf, "%s %s %s%c%s%s%s",
old_hex, new_hex, ref->name, 0,
status_report ? " report-status" : "",
- use_sideband ? " side-band-64k" : "");
- }
+ use_sideband ? " side-band-64k" : "",
+ signed_push ? " signed-push" : "");
else
packet_buf_write(&req_buf, "%s %s %s",
old_hex, new_hex, ref->name);
+ if (signed_push)
+ strbuf_addf(&push_cert, "Update: %s %s\n",
+ new_hex, ref->name);
ref->status = status_report ?
REF_STATUS_EXPECTING_REPORT :
REF_STATUS_OK;
@@ -326,6 +365,23 @@ int send_pack(struct send_pack_args *args,
safe_write(out, req_buf.buf, req_buf.len);
packet_flush(out);
}
+
+ if (signed_push && cmds_sent) {
+ char *cp, *ep;
+
+ sign_push_certificate(&push_cert);
+ strbuf_reset(&req_buf);
+ for (cp = push_cert.buf; *cp; cp = ep) {
+ ep = strchrnul(cp, '\n');
+ if (*ep == '\n')
+ ep++;
+ packet_buf_write(&req_buf, "%.*s",
+ (int)(ep - cp), cp);
+ }
+ /* Do we need anything funky for stateless rpc? */
+ safe_write(out, req_buf.buf, req_buf.len);
+ packet_flush(out);
+ }
strbuf_release(&req_buf);
if (use_sideband && cmds_sent) {
diff --git a/send-pack.h b/send-pack.h
index 05d7ab1..754943e 100644
--- a/send-pack.h
+++ b/send-pack.h
@@ -11,6 +11,7 @@ struct send_pack_args {
use_thin_pack:1,
use_ofs_delta:1,
dry_run:1,
+ signed_push:1,
stateless_rpc:1;
};
diff --git a/transport.c b/transport.c
index fa279d5..7a7ffe4 100644
--- a/transport.c
+++ b/transport.c
@@ -476,6 +476,9 @@ static int set_git_option(struct git_transport_options *opts,
else
opts->depth = atoi(value);
return 0;
+ } else if (!strcmp(name, TRANS_OPT_SIGNED_PUSH)) {
+ opts->signed_push = !!value;
+ return 0;
}
return 1;
}
@@ -793,6 +796,7 @@ static int git_transport_push(struct transport *transport, struct ref *remote_re
args.progress = transport->progress;
args.dry_run = !!(flags & TRANSPORT_PUSH_DRY_RUN);
args.porcelain = !!(flags & TRANSPORT_PUSH_PORCELAIN);
+ args.signed_push = !!(flags & TRANSPORT_PUSH_SIGNED);
ret = send_pack(&args, data->fd, data->conn, remote_refs,
&data->extra_have);
diff --git a/transport.h b/transport.h
index 059b330..d2fa478 100644
--- a/transport.h
+++ b/transport.h
@@ -8,6 +8,7 @@ struct git_transport_options {
unsigned thin : 1;
unsigned keep : 1;
unsigned followtags : 1;
+ unsigned signed_push : 1;
int depth;
const char *uploadpack;
const char *receivepack;
@@ -102,6 +103,7 @@ struct transport {
#define TRANSPORT_PUSH_PORCELAIN 16
#define TRANSPORT_PUSH_SET_UPSTREAM 32
#define TRANSPORT_RECURSE_SUBMODULES_CHECK 64
+#define TRANSPORT_PUSH_SIGNED 128
#define TRANSPORT_SUMMARY_WIDTH (2 * DEFAULT_ABBREV + 3)
@@ -128,6 +130,8 @@ struct transport *transport_get(struct remote *, const char *);
/* Aggressively fetch annotated tags if possible */
#define TRANS_OPT_FOLLOWTAGS "followtags"
+#define TRANS_OPT_SIGNED_PUSH "signedpush"
+
/**
* Returns 0 if the option was used, non-zero otherwise. Prints a
* message to stderr if the option is not used.
--
1.7.7.rc0.188.g3793ac
^ permalink raw reply related
* [PATCH v2 7/7] push -s: support pre-receive-signature hook
From: Junio C Hamano @ 2011-09-08 20:01 UTC (permalink / raw)
To: git
In-Reply-To: <1315512102-19022-1-git-send-email-gitster@pobox.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
builtin/receive-pack.c | 31 +++++++++++++++++++++++++++----
1 files changed, 27 insertions(+), 4 deletions(-)
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index 939b867..b5a54e7 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -303,6 +303,27 @@ static int run_receive_hook(struct command *commands, const char *hook_name)
return status;
}
+static int feed_signature_hook(void *state_, const char **bufp, size_t *sizep)
+{
+ const char **cert_p = state_;
+
+ if (!*cert_p)
+ return -1; /* EOF */
+ *bufp = *cert_p;
+ *cert_p = NULL; /* just return once */
+ *sizep = strlen(*bufp);
+ return 0;
+}
+
+static int run_receive_signature_hook(const char *cert)
+{
+ static const char hook[] = "hooks/pre-receive-signature";
+
+ if (!cert)
+ return 0;
+ return run_and_feed_hook(hook, feed_signature_hook, &cert);
+}
+
static int run_update_hook(struct command *cmd)
{
static const char update_hook[] = "hooks/update";
@@ -642,10 +663,6 @@ static int record_signed_push(char *cert)
* certificate, grab the commit object names the push updates
* refs to, and append the certificate to the notes to these
* commits.
- *
- * You could also feed the signed push certificate to GPG,
- * verify the signer identity, and all the other fun stuff,
- * including feeding it to "pre-receive-signature" hook.
*/
size_t total, payload;
char *cp, *ep;
@@ -714,6 +731,12 @@ static void execute_commands(struct command *commands, const char *unpacker_erro
return;
}
+ if (run_receive_signature_hook(push_certificate)) {
+ for (cmd = commands; cmd; cmd = cmd->next)
+ cmd->error_string = "n/a (pre-receive-signature hook declined)";
+ return;
+ }
+
if (record_signed_push(push_certificate)) {
for (cmd = commands; cmd; cmd = cmd->next)
cmd->error_string = "n/a (push signature error)";
--
1.7.7.rc0.188.g3793ac
^ permalink raw reply related
* [PATCH v2 4/7] push -s: send signed push certificate
From: Junio C Hamano @ 2011-09-08 20:01 UTC (permalink / raw)
To: git
In-Reply-To: <1315512102-19022-1-git-send-email-gitster@pobox.com>
And this uses the GPG interface to sign the push certificate. The format
of the signed certificate is very similar to a signed tag, in that the
result is a concatenation of the payload, immediately followed by a
detached signature.
This places the same constraint as an annotated tag on the push
certificate payload; it has to be a text file and the final line
must not be an incomplete line.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
builtin/send-pack.c | 29 ++++++++++++-----------------
1 files changed, 12 insertions(+), 17 deletions(-)
diff --git a/builtin/send-pack.c b/builtin/send-pack.c
index 7f4778c..f715324 100644
--- a/builtin/send-pack.c
+++ b/builtin/send-pack.c
@@ -8,6 +8,7 @@
#include "send-pack.h"
#include "quote.h"
#include "transport.h"
+#include "gpg-interface.h"
static const char send_pack_usage[] =
"git send-pack [--all | --mirror] [--dry-run] [--force] [--receive-pack=<git-receive-pack>] [--verbose] [--thin] [<host>:]<directory> [<ref>...]\n"
@@ -237,25 +238,18 @@ static int sideband_demux(int in, int out, void *data)
return ret;
}
-static void sign_push_certificate(struct strbuf *cert)
+/*
+ * Take the contents of cert->buf, and have the user GPG sign it, and
+ * read it back in the strbuf.
+ */
+static int sign_push_certificate(struct strbuf *cert)
{
/*
- * Here, take the contents of cert->buf, and have the user GPG
- * sign it, and read it back in the strbuf.
- *
- * You may want to append some extra info to cert before giving
- * it to GPG, possibly via a hook.
- *
- * Here we upcase them just to demonstrate that the codepath
- * is being exercised.
+ * You may want to append some extra info to cert before
+ * giving it to GPG, possibly via a hook, here.
*/
- char *cp;
- for (cp = cert->buf; *cp; cp++) {
- int ch = *cp;
- if ('a' <= ch && ch <= 'z')
- *cp = toupper(ch);
- }
- return;
+
+ return sign_buffer(cert, git_committer_info(IDENT_NO_DATE));
}
int send_pack(struct send_pack_args *args,
@@ -369,7 +363,8 @@ int send_pack(struct send_pack_args *args,
if (signed_push && cmds_sent) {
char *cp, *ep;
- sign_push_certificate(&push_cert);
+ if (sign_push_certificate(&push_cert))
+ return error(_("failed to sign push certificate"));
strbuf_reset(&req_buf);
for (cp = push_cert.buf; *cp; cp = ep) {
ep = strchrnul(cp, '\n');
--
1.7.7.rc0.188.g3793ac
^ permalink raw reply related
* [PATCH v2 2/7] Split GPG interface into its own helper library
From: Junio C Hamano @ 2011-09-08 20:01 UTC (permalink / raw)
To: git
In-Reply-To: <1315512102-19022-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.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
Makefile | 2 +
builtin/tag.c | 60 ++++----------------------------
builtin/verify-tag.c | 35 ++-----------------
gpg-interface.c | 94 ++++++++++++++++++++++++++++++++++++++++++++++++++
gpg-interface.h | 11 ++++++
5 files changed, 117 insertions(+), 85 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..e9d36fa 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>]",
@@ -208,60 +209,13 @@ 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;
+ const char *key;
- 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;
+ if (*signingkey)
+ key = signingkey;
+ else
+ key = git_committer_info(IDENT_ERROR_ON_NO_NAME|IDENT_NO_DATE);
+ return sign_buffer(buffer, key);
}
static const char tag_template[] =
diff --git a/builtin/verify-tag.c b/builtin/verify-tag.c
index 3134766..8b4f742 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,12 @@ 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);
-
- unlink_or_warn(path);
-
- return ret;
+ return verify_signed_buffer(buf, size, len);
}
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..b83cca1
--- /dev/null
+++ b/gpg-interface.c
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2011, Google Inc.
+ */
+#include "cache.h"
+#include "run-command.h"
+#include "strbuf.h"
+#include "gpg-interface.h"
+#include "sigchain.h"
+
+int sign_buffer(struct strbuf *buffer, const char *signing_key)
+{
+ struct child_process gpg;
+ const char *args[4];
+ ssize_t len;
+ int i, j;
+
+ 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);
+ len = strbuf_read(buffer, 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 = 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;
+}
+
+int verify_signed_buffer(const char *buf, size_t total, size_t payload)
+{
+ 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, buf, total) < 0)
+ return error("failed writing temporary file '%s': %s",
+ path, strerror(errno));
+ close(fd);
+
+ 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, payload);
+ close(gpg.in);
+ 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..7689357
--- /dev/null
+++ b/gpg-interface.h
@@ -0,0 +1,11 @@
+#ifndef GPG_INTERFACE_H
+#define GPG_INTERFACE_H
+
+/*
+ * Copyright (c) 2011, Google Inc.
+ */
+
+extern int sign_buffer(struct strbuf *buffer, const char *signing_key);
+extern int verify_signed_buffer(const char *buffer, size_t total, size_t payload);
+
+#endif
--
1.7.7.rc0.188.g3793ac
^ permalink raw reply related
* [PATCH v2 6/7] refactor run_receive_hook()
From: Junio C Hamano @ 2011-09-08 20:01 UTC (permalink / raw)
To: git
In-Reply-To: <1315512102-19022-1-git-send-email-gitster@pobox.com>
Running a hook has to make complex set-up to establish web of
communication between child process and multiplexer, which is common
regardless of what kind of data is fed to the hook. Refactor the parts
that is specific to the data fed to the particular set of hooks from the
part that runs the hook, so that the code can be reused to drive hooks
that take different kind of data.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
builtin/receive-pack.c | 71 +++++++++++++++++++++++++++++++++++-------------
1 files changed, 52 insertions(+), 19 deletions(-)
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index 344660e..939b867 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -212,21 +212,15 @@ static int copy_to_sideband(int in, int out, void *arg)
return 0;
}
-static int run_receive_hook(struct command *commands, const char *hook_name)
+typedef int (*feed_fn)(void *, const char **, size_t *);
+static int run_and_feed_hook(const char *hook_name, feed_fn feed, void *feed_state)
{
- static char buf[sizeof(commands->old_sha1) * 2 + PATH_MAX + 4];
- struct command *cmd;
struct child_process proc;
struct async muxer;
const char *argv[2];
- int have_input = 0, code;
-
- for (cmd = commands; !have_input && cmd; cmd = cmd->next) {
- if (!cmd->error_string)
- have_input = 1;
- }
+ int code;
- if (!have_input || access(hook_name, X_OK) < 0)
+ if (access(hook_name, X_OK) < 0)
return 0;
argv[0] = hook_name;
@@ -254,15 +248,13 @@ static int run_receive_hook(struct command *commands, const char *hook_name)
return code;
}
- for (cmd = commands; cmd; cmd = cmd->next) {
- if (!cmd->error_string) {
- size_t n = snprintf(buf, sizeof(buf), "%s %s %s\n",
- sha1_to_hex(cmd->old_sha1),
- sha1_to_hex(cmd->new_sha1),
- cmd->ref_name);
- if (write_in_full(proc.in, buf, n) != n)
- break;
- }
+ while (1) {
+ const char *buf;
+ size_t n;
+ if (feed(feed_state, &buf, &n))
+ break;
+ if (write_in_full(proc.in, buf, n) != n)
+ break;
}
close(proc.in);
if (use_sideband)
@@ -270,6 +262,47 @@ static int run_receive_hook(struct command *commands, const char *hook_name)
return finish_command(&proc);
}
+struct receive_hook_feed_state {
+ struct command *cmd;
+ struct strbuf buf;
+};
+
+static int feed_receive_hook(void *state_, const char **bufp, size_t *sizep)
+{
+ struct receive_hook_feed_state *state = state_;
+ struct command *cmd = state->cmd;
+
+ while (cmd && cmd->error_string)
+ cmd = cmd->next;
+ if (!cmd)
+ return -1; /* EOF */
+ strbuf_reset(&state->buf);
+ strbuf_addf(&state->buf, "%s %s %s\n",
+ sha1_to_hex(cmd->old_sha1), sha1_to_hex(cmd->new_sha1),
+ cmd->ref_name);
+ state->cmd = cmd->next;
+ if (bufp) {
+ *bufp = state->buf.buf;
+ *sizep = state->buf.len;
+ }
+ return 0;
+}
+
+static int run_receive_hook(struct command *commands, const char *hook_name)
+{
+ struct receive_hook_feed_state state;
+ int status;
+
+ strbuf_init(&state.buf, 0);
+ state.cmd = commands;
+ if (feed_receive_hook(&state, NULL, NULL))
+ return 0;
+ state.cmd = commands;
+ status = run_and_feed_hook(hook_name, feed_receive_hook, &state);
+ strbuf_release(&state.buf);
+ return status;
+}
+
static int run_update_hook(struct command *cmd)
{
static const char update_hook[] = "hooks/update";
--
1.7.7.rc0.188.g3793ac
^ permalink raw reply related
* [PATCH v2 5/7] push -s: receiving end
From: Junio C Hamano @ 2011-09-08 20:01 UTC (permalink / raw)
To: git
In-Reply-To: <1315512102-19022-1-git-send-email-gitster@pobox.com>
This stores the GPG signed push certificate in the receiving repository
using the notes mechanism. The certificate is appended to a note in the
refs/notes/signed-push tree for each object that appears on the right
hand side of the push certificate, i.e. the object that was pushed to
update the tip of a ref.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
builtin/receive-pack.c | 72 +++++++++++++++++++++++++++++++++++++++++++----
1 files changed, 65 insertions(+), 7 deletions(-)
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index 20b6799..344660e 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -11,6 +11,11 @@
#include "transport.h"
#include "string-list.h"
#include "sha1-array.h"
+#include "gpg-interface.h"
+#include "notes.h"
+#include "notes-merge.h"
+#include "blob.h"
+#include "tag.h"
static const char receive_pack_usage[] = "git receive-pack <git-dir>";
@@ -581,6 +586,22 @@ static void check_aliased_updates(struct command *commands)
string_list_clear(&ref_list, 0);
}
+static void get_note_text(struct strbuf *buf, struct notes_tree *t,
+ const unsigned char *object)
+{
+ const unsigned char *sha1 = get_note(t, object);
+ char *text;
+ unsigned long len;
+ enum object_type type;
+
+ if (!sha1)
+ return;
+ text = read_sha1_file(sha1, &type, &len);
+ if (text && len && type == OBJ_BLOB)
+ strbuf_add(buf, text, len);
+ free(text);
+}
+
static int record_signed_push(char *cert)
{
/*
@@ -592,18 +613,55 @@ static int record_signed_push(char *cert)
* You could also feed the signed push certificate to GPG,
* verify the signer identity, and all the other fun stuff,
* including feeding it to "pre-receive-signature" hook.
- *
- * Here we just throw it to stderr to demonstrate that the
- * codepath is being exercised.
*/
+ size_t total, payload;
char *cp, *ep;
- for (cp = cert; *cp; cp = ep) {
+ int ret = 0;
+ struct notes_tree *t;
+ struct strbuf nbuf = STRBUF_INIT;
+
+ if (!cert)
+ return 0;
+
+ init_notes(NULL, "refs/notes/signed-push", NULL, 0);
+ t = &default_notes_tree;
+
+ total = strlen(cert);
+ payload = parse_signature(cert, total);
+ for (cp = cert; cp < cert + payload; cp = ep) {
+ unsigned char sha1[20], nsha1[20];
+
ep = strchrnul(cp, '\n');
if (*ep == '\n')
ep++;
- fprintf(stderr, "RSP: %.*s", (int)(ep - cp), cp);
+ if (prefixcmp(cp, "Update: "))
+ continue;
+ cp += strlen("Update: ");
+ if (get_sha1_hex(cp, sha1) || cp[40] != ' ')
+ continue;
+
+ get_note_text(&nbuf, t, sha1);
+ if (nbuf.len)
+ strbuf_addch(&nbuf, '\n');
+ strbuf_add(&nbuf, cert, total);
+ if (write_sha1_file(nbuf.buf, nbuf.len, blob_type, nsha1) ||
+ add_note(t, sha1, nsha1, NULL))
+ ret = error(_("unable to write note object"));
+ strbuf_reset(&nbuf);
}
- return 0;
+
+ if (!ret) {
+ unsigned char commit[20];
+ unsigned char parent[20];
+ struct ref_lock *lock;
+
+ resolve_ref(t->ref, parent, 0, NULL);
+ lock = lock_any_ref_for_update(t->ref, parent, 0);
+ create_notes_commit(t, NULL, "push", commit);
+ ret = write_ref_sha1(lock, commit, "push");
+ }
+ free_notes(t);
+ return ret;
}
static void execute_commands(struct command *commands, const char *unpacker_error)
@@ -623,7 +681,7 @@ static void execute_commands(struct command *commands, const char *unpacker_erro
return;
}
- if (push_certificate && record_signed_push(push_certificate)) {
+ if (record_signed_push(push_certificate)) {
for (cmd = commands; cmd; cmd = cmd->next)
cmd->error_string = "n/a (push signature error)";
return;
--
1.7.7.rc0.188.g3793ac
^ permalink raw reply related
* Re: git rebase fails with: Patch does not have a valid e-mail address.
From: Junio C Hamano @ 2011-09-08 21:21 UTC (permalink / raw)
To: James Blackburn; +Cc: git
In-Reply-To: <CACyv8dc28sRWsObYi3vbFNakj=R-2Q9eAoJdFfqNxsqq2+_aPg@mail.gmail.com>
James Blackburn <jamesblackburn@gmail.com> writes:
>> Perhaps you used "filter-branch" for conversion and your "doesn't
>> ordinarily complain about this" refers to it? If so, I have to say that it
>> is filter-branch that needs to be fixed to error out.
>
> I use cvs2git for the conversion (which produces a fast-import
> stream). That tool doesn't enforce an email address, and it's only
> when I try to rebase that I run into this problem... AFAICS there's
> nothing fundamentally wrong with what I'm trying to do, so forcing me
> to re-write the author seems to be the wrong answer, no?
Sorry, I do not follow. Nobody is forcing you to rewrite the author, but
earlier didn't you say _you_ rewrote the author into that invalid empty
string yourself, no?
^ permalink raw reply
* Re: [PATCH 2/2] push -s: skeleton
From: Junio C Hamano @ 2011-09-08 22:19 UTC (permalink / raw)
To: Jeff King; +Cc: git, Shawn O. Pearce
In-Reply-To: <20110908210217.GA32522@sigill.intra.peff.net>
Jeff King <peff@peff.net> writes:
> Yeah, it is a potential problem, but it just seems wrong to put too much
> policy work onto the server.
My take on it is somewhat different. The only thing in the end result we
want to see is that the pushed commits are annotated with GPG signatures
in the notes tree, and there is no reason for us to cast in stone that
there has to be any significance in the commit history of the notes tree.
In a busy hosting site that has many branches being pushed simultaneously,
it is entirely plausible that the server side may just want to store each
received push certificate in a new flat file in a filesystem, and have
asynchronous process sweep the new certificates to update the notes tree,
possibly creating a single notes tree commit that records updates by
multiple pushes, for performance purposes, in its implementation of
record_signed_push() in receive-pack.
If you forced the clients to also prepare notes and push the notes tree to
the server, you are forcing the ordering in the history of the notes, and
closing the door for such a server implementation. I would consider it an
unnecessary and/or premature policy decision.
^ permalink raw reply
* [PATCH v2 1/7] send-pack: typofix error message
From: Junio C Hamano @ 2011-09-08 20:01 UTC (permalink / raw)
To: git
In-Reply-To: <1315512102-19022-1-git-send-email-gitster@pobox.com>
The message identifies the process as receive-pack when it cannot fork the
sideband demultiplexer. We are actually a send-pack.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
builtin/send-pack.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/builtin/send-pack.c b/builtin/send-pack.c
index c1f6ddd..87833f4 100644
--- a/builtin/send-pack.c
+++ b/builtin/send-pack.c
@@ -334,7 +334,7 @@ int send_pack(struct send_pack_args *args,
demux.data = fd;
demux.out = -1;
if (start_async(&demux))
- die("receive-pack: unable to fork off sideband demultiplexer");
+ die("send-pack: unable to fork off sideband demultiplexer");
in = demux.out;
}
--
1.7.7.rc0.188.g3793ac
^ permalink raw reply related
* Re: [PATCH v4] gitk: Allow commit editing
From: Paul Mackerras @ 2011-09-08 20:59 UTC (permalink / raw)
To: Michal Sojka; +Cc: Jeff King, git
In-Reply-To: <87fwknaveh.fsf@steelpick.2x.cz>
On Sat, Aug 27, 2011 at 02:31:02PM +0200, Michal Sojka wrote:
> Here is a new version with the micro-optimization.
>
> Another minor change is that this patch now applies to gitk repo
> (http://git.kernel.org/pub/scm/gitk/gitk.git) instead of the main git
> repo.
>
> -Michal
>
> --8<---------------cut here---------------start------------->8---
> I often use gitk to review patches before pushing them out and I would
> like to have an easy way to edit commits. The current approach with
> copying the commitid, switching to terminal, invoking git rebase -i,
> editing the commit and switching back to gitk is a way too complicated
> just for changing a single letter in the commit message or removing a
> debug printf().
>
> This patch adds "Edit this commit" item to gitk's context menu which
> invokes interactive rebase in a non-interactive way :-). git gui is
> used to actually edit the commit.
>
> Besides editing the commit message, splitting of commits, as described
> in git-rebase(1), is also supported.
>
> The user is warned if the commit to be edited is contained in another
> ref besides the current branch and the stash (e.g. in a remote
> branch). Additionally, error box is displayed if the user attempts to
> edit a commit not contained in the current branch.
I have to say that this patch makes me pretty nervous. I can see the
attractiveness of the feature, but I don't like making gitk
unresponsive for a potentially long time, i.e. until git gui exits.
It may not be clear to users that the reason gitk isn't responding is
because some other git gui window is still running.
Also, if some subsequent commit no longer applies because of the
changes you make to a commit, it's going to abort the rebase
completely and thus lose the changes you made. That could be
annoying.
I usually do this by starting a new branch just before the commit I
want to change and then use a combination of the cherry-pick menu item
and git commit --amend. Maybe something to make that simpler for
users would be good, i.e. automate it a bit but still have it be a
step-by-step process if necessary. Part of the problem of course is
that neither gitk nor git gui are really designed to be an editing
environment. In fact you really want an edit/compile/test environment
so you don't introduce new bugs.
Paul.
^ permalink raw reply
* Re: The imporantance of including http credential caching in 1.7.7
From: John Szakmeister @ 2011-09-08 15:02 UTC (permalink / raw)
To: Michael J Gruber; +Cc: Kyle Neath, git, Jeff King
In-Reply-To: <4E68C04F.9060804@drmicha.warpmail.net>
On Thu, Sep 8, 2011 at 9:17 AM, Michael J Gruber
<git@drmicha.warpmail.net> wrote:
[snip]
> It would be interesting to know what we can rely on in the user group
> you're thinking about (which I called ssh-challenged). Setting up ssh
> keys is too complicated. Can we require a working gpg setup? They do
> want to check sigs, don't they?
I don't think you can require a working gpg setup (at least for not
addressing the ssh-challenged group).
[snip]
> Or that in C, probably using Junio's gpg-lib. That would be secure and
> useful *if* we can rely on people having a convenient gpg setup
> (gpg-agent or such).
>
> So: What credential store/password wallet/etc. can we rely on for this
> group? Is gpg fair game?
I think there probably need to be providers for using Keychain under
the Mac, gnome-keyring and kwallet under Linux, and probably something
using the wincrypt API under Windows. I don't think there's a
one-store-fits-all solution here, unfortunately. :-(
I'm actually tempted try and work on a couple of those myself.
-John
^ permalink raw reply
* Git Bug - diff in commit message.
From: anikey @ 2011-09-08 14:49 UTC (permalink / raw)
To: git
Hi, peops. I'm pretty much sure that's a bug.
What I did was putting git diff (i needed to tell people that for my changes
to start working they needed to aplly message-inline patch to some code
which was not under git) in commit message. Like adding:
diff --git a/app/controllers/settings_controller.rb
b/app/controllers/settings_controller.rb
index 937da74..0e8440d 100644
--- a/app/controllers/settings_controller.rb
+++ b/app/controllers/settings_controller.rb
@@ -42,7 +42,7 @@ class SettingsController < ApplicationController
end
def snmp_mibs
- render layout: 'ext3'
+ render layout: 'ext3_2'
end
def cfg_auth_keys(auth_type=:all)
though the commit itself didn't contain that change. So while `git rebase
some_branch_name` I started getting:
First, rewinding head to replay your work on top of it...
Applying: My cool patch.
fatal: sha1 information is lacking or useless
(app/controllers/settings_controller.rb).
Repository lacks necessary blobs to fall back on 3-way merge.
Cannot fall back to three-way merge.
Patch failed at 0001 My cool patch.
When you have resolved this problem run "git rebase --continue".
If you would prefer to skip this patch, instead run "git rebase --skip".
To restore the original branch and stop rebasing run "git rebase --abort".
I wasn't able to figure out what was wrong for a very long time, when things
came to my mind.
Thanks.
--
View this message in context: http://git.661346.n2.nabble.com/Git-Bug-diff-in-commit-message-tp6772145p6772145.html
Sent from the git mailing list archive at Nabble.com.
^ permalink raw reply related
* Re: Add interactive patch menu help scrolled away if hunk is long
From: Sverre Rabbelier @ 2011-09-08 14:43 UTC (permalink / raw)
To: Sergio Callegari; +Cc: git
In-Reply-To: <loom.20110907T143944-529@post.gmane.org>
Heya,
On Wed, Sep 7, 2011 at 14:43, Sergio Callegari
<sergio.callegari@gmail.com> wrote:
> Probably it would be better not to reprint the hunk when '?' is selected.
Seconded! I was trying to explain git add -i to someone and typed '?'
three times in a row wondering why it wasn't working.
--
Cheers,
Sverre Rabbelier
^ permalink raw reply
* [PATCHv4 5/5] branch: allow pattern arguments
From: Michael J Gruber @ 2011-09-08 14:25 UTC (permalink / raw)
To: git; +Cc: Jeff King, Junio C Hamano
In-Reply-To: <cover.1315491900.git.git@drmicha.warpmail.net>
Allow pattern arguments for the list mode just like for git tag -l.
Signed-off-by: Michael J Gruber <git@drmicha.warpmail.net>
---
Documentation/git-branch.txt | 8 ++++++--
builtin/branch.c | 24 +++++++++++++++++++++---
t/t3203-branch-output.sh | 23 +++++++++++++++++++++++
3 files changed, 50 insertions(+), 5 deletions(-)
diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt
index 26024b6..2e27ee4 100644
--- a/Documentation/git-branch.txt
+++ b/Documentation/git-branch.txt
@@ -10,7 +10,7 @@ SYNOPSIS
[verse]
'git branch' [--color[=<when>] | --no-color] [-r | -a]
[--list] [-v [--abbrev=<length> | --no-abbrev]]
- [(--merged | --no-merged | --contains) [<commit>]]
+ [(--merged | --no-merged | --contains) [<commit>]] [<pattern>...]
'git branch' [--set-upstream | --track | --no-track] [-l] [-f] <branchname> [<start-point>]
'git branch' (-m | -M) [<oldbranch>] <newbranch>
'git branch' (-d | -D) [-r] <branchname>...
@@ -22,6 +22,9 @@ With no arguments, existing branches are listed and the current branch will
be highlighted with an asterisk. Option `-r` causes the remote-tracking
branches to be listed, and option `-a` shows both. This list mode is also
activated by the `--list` option (see below).
+<pattern> restricts the output to matching branches, the pattern is a shell
+wildcard (i.e., matched using fnmatch(3)).
+Multiple patterns may be given; if any of them matches, the tag is shown.
With `--contains`, shows only the branches that contain the named commit
(in other words, the branches whose tip commits are descendants of the
@@ -112,7 +115,8 @@ OPTIONS
List both remote-tracking branches and local branches.
--list::
- Activate the list mode.
+ Activate the list mode. `git branch <pattern>` would try to create a branch,
+ use `git branch --list <pattern>` to list matching branches.
-v::
--verbose::
diff --git a/builtin/branch.c b/builtin/branch.c
index b17ad26..099c75c 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -260,9 +260,22 @@ static char *resolve_symref(const char *src, const char *prefix)
struct append_ref_cb {
struct ref_list *ref_list;
+ const char **pattern;
int ret;
};
+static int match_patterns(const char **pattern, const char *refname)
+{
+ if (!*pattern)
+ return 1; /* no pattern always matches */
+ while (*pattern) {
+ if (!fnmatch(*pattern, refname, 0))
+ return 1;
+ pattern++;
+ }
+ return 0;
+}
+
static int append_ref(const char *refname, const unsigned char *sha1, int flags, void *cb_data)
{
struct append_ref_cb *cb = (struct append_ref_cb *)(cb_data);
@@ -297,6 +310,9 @@ static int append_ref(const char *refname, const unsigned char *sha1, int flags,
if ((kind & ref_list->kinds) == 0)
return 0;
+ if (!match_patterns(cb->pattern, refname))
+ return 0;
+
commit = NULL;
if (ref_list->verbose || ref_list->with_commit || merge_filter != NO_FILTER) {
commit = lookup_commit_reference_gently(sha1, 1);
@@ -492,7 +508,7 @@ static void show_detached(struct ref_list *ref_list)
}
}
-static int print_ref_list(int kinds, int detached, int verbose, int abbrev, struct commit_list *with_commit)
+static int print_ref_list(int kinds, int detached, int verbose, int abbrev, struct commit_list *with_commit, const char **pattern)
{
int i;
struct append_ref_cb cb;
@@ -506,6 +522,7 @@ static int print_ref_list(int kinds, int detached, int verbose, int abbrev, stru
if (merge_filter != NO_FILTER)
init_revisions(&ref_list.revs, NULL);
cb.ref_list = &ref_list;
+ cb.pattern = pattern;
cb.ret = 0;
for_each_rawref(append_ref, &cb);
if (merge_filter != NO_FILTER) {
@@ -523,7 +540,7 @@ static int print_ref_list(int kinds, int detached, int verbose, int abbrev, stru
qsort(ref_list.list, ref_list.index, sizeof(struct ref_item), ref_cmp);
detached = (detached && (kinds & REF_LOCAL_BRANCH));
- if (detached)
+ if (detached && match_patterns(pattern, "HEAD"))
show_detached(&ref_list);
for (i = 0; i < ref_list.index; i++) {
@@ -707,7 +724,8 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
if (delete)
return delete_branches(argc, argv, delete > 1, kinds);
else if (list)
- return print_ref_list(kinds, detached, verbose, abbrev, with_commit);
+ return print_ref_list(kinds, detached, verbose, abbrev,
+ with_commit, argv);
else if (rename && (argc == 1))
rename_branch(head, argv[0], rename > 1);
else if (rename && (argc == 2))
diff --git a/t/t3203-branch-output.sh b/t/t3203-branch-output.sh
index 97d10b1..76fe7e0 100755
--- a/t/t3203-branch-output.sh
+++ b/t/t3203-branch-output.sh
@@ -38,6 +38,15 @@ test_expect_success 'git branch --list shows local branches' '
'
cat >expect <<'EOF'
+ branch-one
+ branch-two
+EOF
+test_expect_success 'git branch --list pattern shows matching local branches' '
+ git branch --list branch* >actual &&
+ test_cmp expect actual
+'
+
+cat >expect <<'EOF'
origin/HEAD -> origin/branch-one
origin/branch-one
origin/branch-two
@@ -72,6 +81,20 @@ test_expect_success 'git branch -v shows branch summaries' '
'
cat >expect <<'EOF'
+two
+one
+EOF
+test_expect_success 'git branch --list -v pattern shows branch summaries' '
+ git branch --list -v branch* >tmp &&
+ awk "{print \$NF}" <tmp >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git branch -v pattern does not show branch summaries' '
+ test_must_fail git branch -v branch*
+'
+
+cat >expect <<'EOF'
* (no branch)
branch-one
branch-two
--
1.7.7.rc0.469.g9eb94
^ permalink raw reply related
* [PATCHv4 4/5] branch: introduce --list option
From: Michael J Gruber @ 2011-09-08 14:25 UTC (permalink / raw)
To: git; +Cc: Jeff King, Junio C Hamano
In-Reply-To: <cover.1315491900.git.git@drmicha.warpmail.net>
Currently, there is no way to invoke the list mode explicitly.
Introduce a --list option which invokes the list mode. This will be
beneficial for invoking list mode with pattern matching, which otherwise
would be interpreted as branch creation.
Along with --list, test also combinations of existing options.
Signed-off-by: Michael J Gruber <git@drmicha.warpmail.net>
---
Documentation/git-branch.txt | 11 ++++++++---
builtin/branch.c | 11 ++++++++---
t/t3200-branch.sh | 32 ++++++++++++++++++++++++++++++++
t/t3203-branch-output.sh | 5 +++++
4 files changed, 53 insertions(+), 6 deletions(-)
diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt
index 4c64ac9..26024b6 100644
--- a/Documentation/git-branch.txt
+++ b/Documentation/git-branch.txt
@@ -9,7 +9,7 @@ SYNOPSIS
--------
[verse]
'git branch' [--color[=<when>] | --no-color] [-r | -a]
- [-v [--abbrev=<length> | --no-abbrev]]
+ [--list] [-v [--abbrev=<length> | --no-abbrev]]
[(--merged | --no-merged | --contains) [<commit>]]
'git branch' [--set-upstream | --track | --no-track] [-l] [-f] <branchname> [<start-point>]
'git branch' (-m | -M) [<oldbranch>] <newbranch>
@@ -20,7 +20,8 @@ DESCRIPTION
With no arguments, existing branches are listed and the current branch will
be highlighted with an asterisk. Option `-r` causes the remote-tracking
-branches to be listed, and option `-a` shows both.
+branches to be listed, and option `-a` shows both. This list mode is also
+activated by the `--list` option (see below).
With `--contains`, shows only the branches that contain the named commit
(in other words, the branches whose tip commits are descendants of the
@@ -110,9 +111,13 @@ OPTIONS
--all::
List both remote-tracking branches and local branches.
+--list::
+ Activate the list mode.
+
-v::
--verbose::
- Show sha1 and commit subject line for each head, along with
+ When in list mode,
+ show sha1 and commit subject line for each head, along with
relationship to upstream branch (if any). If given twice, print
the name of the upstream branch, as well.
diff --git a/builtin/branch.c b/builtin/branch.c
index bd3a315..b17ad26 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -612,7 +612,7 @@ static int opt_parse_merge_filter(const struct option *opt, const char *arg, int
int cmd_branch(int argc, const char **argv, const char *prefix)
{
- int delete = 0, rename = 0, force_create = 0;
+ int delete = 0, rename = 0, force_create = 0, list = 0;
int verbose = 0, abbrev = -1, detached = 0;
int reflog = 0;
enum branch_track track;
@@ -651,6 +651,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
OPT_BIT('D', NULL, &delete, "delete branch (even if not merged)", 2),
OPT_BIT('m', "move", &rename, "move/rename a branch and its reflog", 1),
OPT_BIT('M', NULL, &rename, "move/rename a branch, even if target exists", 2),
+ OPT_BOOLEAN(0, "list", &list, "list branch names"),
OPT_BOOLEAN('l', "create-reflog", &reflog, "create the branch's reflog"),
OPT__FORCE(&force_create, "force creation (when already exists)"),
{
@@ -693,7 +694,11 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
argc = parse_options(argc, argv, prefix, options, builtin_branch_usage,
0);
- if (!!delete + !!rename + !!force_create > 1)
+
+ if (!delete && !rename && !force_create && argc == 0)
+ list = 1;
+
+ if (!!delete + !!rename + !!force_create + !!list > 1)
usage_with_options(builtin_branch_usage, options);
if (abbrev == -1)
@@ -701,7 +706,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
if (delete)
return delete_branches(argc, argv, delete > 1, kinds);
- else if (argc == 0)
+ else if (list)
return print_ref_list(kinds, detached, verbose, abbrev, with_commit);
else if (rename && (argc == 1))
rename_branch(head, argv[0], rename > 1);
diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh
index 9e69c8c..c466b20 100755
--- a/t/t3200-branch.sh
+++ b/t/t3200-branch.sh
@@ -98,6 +98,38 @@ test_expect_success 'git branch -m q r/q should fail when r exists' '
test_must_fail git branch -m q r/q
'
+test_expect_success 'git branch -v -d t should work' '
+ git branch t &&
+ test .git/refs/heads/t &&
+ git branch -v -d t &&
+ test ! -f .git/refs/heads/t
+'
+
+test_expect_success 'git branch -v -m t s should work' '
+ git branch t &&
+ test .git/refs/heads/t &&
+ git branch -v -m t s &&
+ test ! -f .git/refs/heads/t &&
+ test -f .git/refs/heads/s &&
+ git branch -d s
+'
+
+test_expect_success 'git branch -m -d t s should fail' '
+ git branch t &&
+ test .git/refs/heads/t &&
+ test_must_fail git branch -m -d t s &&
+ git branch -d t &&
+ test ! -f .git/refs/heads/t
+'
+
+test_expect_success 'git branch --list -d t should fail' '
+ git branch t &&
+ test .git/refs/heads/t &&
+ test_must_fail git branch --list -d t &&
+ git branch -d t &&
+ test ! -f .git/refs/heads/t
+'
+
mv .git/config .git/config-saved
test_expect_success 'git branch -m q q2 without config should succeed' '
diff --git a/t/t3203-branch-output.sh b/t/t3203-branch-output.sh
index 6b7c118..97d10b1 100755
--- a/t/t3203-branch-output.sh
+++ b/t/t3203-branch-output.sh
@@ -32,6 +32,11 @@ test_expect_success 'git branch shows local branches' '
test_cmp expect actual
'
+test_expect_success 'git branch --list shows local branches' '
+ git branch --list >actual &&
+ test_cmp expect actual
+'
+
cat >expect <<'EOF'
origin/HEAD -> origin/branch-one
origin/branch-one
--
1.7.7.rc0.469.g9eb94
^ permalink raw reply related
* [PATCHv4 0/5] mg/branch-list amendment
From: Michael J Gruber @ 2011-09-08 14:25 UTC (permalink / raw)
To: git; +Cc: Jeff King, Junio C Hamano
In-Reply-To: <4E6889DF.7030404@drmicha.warpmail.net>
So, this is what the top 2 commits in mg/branch-list should be replaced
with if we don't let "-v" activate list mode, so that we can have a verbose
option for branch creation later on, i.e.:
git branch -v foo is the verbose version of git branch
Michael J Gruber (2):
branch: introduce --list option
branch: allow pattern arguments
Documentation/git-branch.txt | 17 +++++++++++++----
builtin/branch.c | 35 +++++++++++++++++++++++++++++------
t/t3200-branch.sh | 32 ++++++++++++++++++++++++++++++++
t/t3203-branch-output.sh | 28 ++++++++++++++++++++++++++++
4 files changed, 102 insertions(+), 10 deletions(-)
--
1.7.7.rc0.469.g9eb94
^ permalink raw reply
* Re: [PATCH] send-mail: Add option to sleep between sending each email.
From: Georgi Chorbadzhiyski @ 2011-09-08 14:07 UTC (permalink / raw)
To: Matthieu Moy; +Cc: Ramkumar Ramachandra, git
In-Reply-To: <4E68CA0C.5080702@unixsol.org>
[-- Attachment #1: Type: text/plain, Size: 1218 bytes --]
Around 09/08/2011 04:58 PM, Georgi Chorbadzhiyski scribbled:
> Around 09/08/2011 02:15 PM, Matthieu Moy scribbled:
>> [1] Actually, I think there's a problem with Georgi's patch. If I read
>> correctly, the sleep is inserted within the confirmation loop, which
>> means the user will have
>>
>> send this email? yes
>> sending email
>> sleeping 10 seconds
>> send this email? yes
>> sending email
>> sleeping 10 seconds
>> ...
>>
>> while it should be
>>
>> send this email? yes
>> ok, I'll send it later
>> send this email? yes
>> ok, I'll send it later
>> sending first email ...
>> sleeping 10 seconds
>> sending second email
>> done.
>>
>> (i.e. don't force the user to wait between confirmations, and don't wait
>> after the last email)
>
> In order for this to work, confirmation should be split from send_message()
> and from a quick look this not seem very easy. Might be easier to just
> disable the sleep if user was asked for confirmation. It'll be good to
> not sleep after last email, but main "foreach my $t (@files) {" loop should
> pass some hint to send_message().
The attached patch (apply on on top of the original) should implement the
idea.
--
Georgi Chorbadzhiyski
http://georgi.unixsol.org/
[-- Attachment #2: send-email-sleep-fix1.diff --]
[-- Type: text/plain, Size: 399 bytes --]
diff --git a/git-send-email.perl b/git-send-email.perl
index 7239fd4..d4559c9 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -1149,7 +1149,7 @@ X-Mailer: git-send-email $gitversion
}
}
- if (!$dry_run && $sleep) {
+ if (!$dry_run && $sleep && $message_num < scalar $#files && $confirm eq 'never') {
print "Sleeping: $sleep second(s).\n" if (!$quiet);
sleep($sleep);
};
^ permalink raw reply related
* Re: [PATCH] send-mail: Add option to sleep between sending each email.
From: Georgi Chorbadzhiyski @ 2011-09-08 13:58 UTC (permalink / raw)
To: Matthieu Moy; +Cc: Ramkumar Ramachandra, git
In-Reply-To: <vpq39g7gua3.fsf@bauges.imag.fr>
Around 09/08/2011 02:15 PM, Matthieu Moy scribbled:
> [1] Actually, I think there's a problem with Georgi's patch. If I read
> correctly, the sleep is inserted within the confirmation loop, which
> means the user will have
>
> send this email? yes
> sending email
> sleeping 10 seconds
> send this email? yes
> sending email
> sleeping 10 seconds
> ...
>
> while it should be
>
> send this email? yes
> ok, I'll send it later
> send this email? yes
> ok, I'll send it later
> sending first email ...
> sleeping 10 seconds
> sending second email
> done.
>
> (i.e. don't force the user to wait between confirmations, and don't wait
> after the last email)
In order for this to work, confirmation should be split from send_message()
and from a quick look this not seem very easy. Might be easier to just
disable the sleep if user was asked for confirmation. It'll be good to
not sleep after last email, but main "foreach my $t (@files) {" loop should
pass some hint to send_message().
--
Georgi Chorbadzhiyski
http://georgi.unixsol.org/
^ permalink raw reply
* Re: The imporantance of including http credential caching in 1.7.7
From: Michael J Gruber @ 2011-09-08 13:17 UTC (permalink / raw)
To: Kyle Neath; +Cc: git, Jeff King
In-Reply-To: <CAFcyEthuf49_kOmoLmoSSbNJN+iOBpicP4-eFAV5wL5_RffwGg@mail.gmail.com>
Kyle Neath venit, vidit, dixit 07.09.2011 22:14:
> Junio C Hamano <gitster@pobox.com> wrote:
>> If this were a new, insignificant, and obscure feature in a piece of
>> software with mere 20k users, it may be OK to release a new version with
>> the feature in an uncooked shape.
>
> For the sake of my paycheck, I should certainly hope not! I'm not at all
> suggesting we merge what we have in. However, I do think this feature is
> important enough to delay the release. I trust in the judgement of the core
> members to know when something is ready for inclusion in master.
>
> Michael J Gruber <git@drmicha.warpmail.net> wrote:
>> So, it's been a year or more that you've been aware of the importance of
>> this issue (from your/github's perspective), and we hear about it now,
>> at the end of the rc phase.
>
> I apologize if it sounds like that. I've been discussing this situation with
> many people (including Jeff King) for a very long time now, and it was my
> understanding that the credential caching was done and simply waiting for a
> new release. This is the first I've heard that it will not be included in
> 1.7.7, so I'm voicing my opinion now. Admittedly, late in the game - and I
> apologize for that.
OK, I've calmed down :)
> I'd be happy to help in any capacity I can. Unfortunately I'm no C hacker, and
> I've accepted that as a character flaw (it's something I'm working on). I'm
> afraid I can't be of much help with the actual code. What I can provide is an
> alternate viewpoint to the core team. A viewpoint of someone who's spent 3
> years trying to make git easier for newcomers.
It would be interesting to know what we can rely on in the user group
you're thinking about (which I called ssh-challenged). Setting up ssh
keys is too complicated. Can we require a working gpg setup? They do
want to check sigs, don't they?
What I have in mind is a very simple, but secure version of Jeff's
credential-store, respectively his example, somewhat like:
---%<---
STORAGE=$HOME/.credentials
for i in "$@"; do
case "$i" in
--unique=*)
unique=${i#--unique=} ;;
esac
done
key=$(git config get credential.gpgkey) # or error out
if ! test -e "$STORAGE/$unique"; then
mkdir -m 0700 "$STORAGE"
git credential-getpass "$@" | gpg -ear $key >"$STORAGE/$unique"
fi
gpg <"$STORAGE/$unique"
---%<---
Or that in C, probably using Junio's gpg-lib. That would be secure and
useful *if* we can rely on people having a convenient gpg setup
(gpg-agent or such).
So: What credential store/password wallet/etc. can we rely on for this
group? Is gpg fair game?
Michael
^ permalink raw reply
* Re: git-rebase skips automatically no more needed commits
From: Martin von Zweigbergk @ 2011-09-08 13:14 UTC (permalink / raw)
To: Francis Moreau; +Cc: Junio C Hamano, Michael J Gruber, git
In-Reply-To: <CAC9WiBjMUg3SUOP9AJw6qCKrGVLs6Qy_O2fQa_SHX30NkjAEdw@mail.gmail.com>
On Thu, 8 Sep 2011, Francis Moreau wrote:
> On Wed, Sep 7, 2011 at 6:29 PM, Junio C Hamano <gitster@pobox.com> wrote:
> > Francis Moreau <francis.moro@gmail.com> writes:
> >
> >> If I start the rebase process with : "git rebase -i -p master foo"
> >> then the filtering is happening. Here 'foo' has been created with
> >> v2.6.39 tag as start point and contains some patches cherry-picked
> >> from the upstream.
> >>
> >> However if I call git-rebase this way: "git rebase -i -p --onto master
> >> v2.6.39 foo", then it seems that no filtering is done.
Patches that are in both sides of v2.6.39...foo will be filtered, but
as Junio said, what is in master is not considered. In your case,
since foo was created from the tag, there should of course be no
commits in foo..v2.6.39.
> I'm using "git rebase --onto A B C" because I want to simplify git's
> work.
What work? Calculating mege-base and patch-ids? But that's exactly
what you said you don't want to avoid, no?
> I know that any commits between A..B are already in A history
Did you mean "B..A" here?
> and I'm not interested in rebasing them anyways.
They wouldn't be rebased by running "git rebase A C" either... Maybe
I'm misunderstanding something.
> Hmm I dont understand why "rebase --onto A B C" wouldn't try to find
> the merge base between A and B. If it doesn't (which is quite uncommon
> I guess) then don't do the filtering as we're currently doing but if
> there's a merge base (common case) then do the filtering.
It could do that. I think that's what Junio meant by "justifiable--- I
dunno". In your case, though, it seems like "git rebase master foo"
would do exactly what you want.
Somewhat related, I think "git rebase --onto A B C" should NOT filter
out patches that also appear both sides of "B...C", because when
"--onto" is used, "B" is only used as a way to calculate the
merge-base. I think it would make more sense to filter out from
appears in both "B..C" what also appears in "C..A". This has been on
my todo list for way too long. Some day I'll send out a patch for it
and we'll see what others think.
Martin
^ permalink raw reply
* Re: Tracking database changes.
From: Victor Engmark @ 2011-09-08 12:12 UTC (permalink / raw)
To: Rodrigo Cortés; +Cc: git
In-Reply-To: <30328581.178675.1315346163453.JavaMail.trustmail@mail1.terreactive.ch>
On Tue, Sep 06, 2011 at 06:55:56PM -0300, Rodrigo Cortés wrote:
> Is there a way to use git to track database changes?
1. Export the data and/or data model with a tool like mysqldump.
2. Remove output which would clutter up your diffs without adding any
useful information. This could include things like date and time of the
export and user name of the exporter.
3. Commit and enjoy!
Optionally create a cron job to export and commit automatically during
off-peak hours.
Cheers,
Victor
--
terreActive AG
Kasinostrasse 30
CH-5001 Aarau
Tel: +41 62 834 00 55
Fax: +41 62 823 93 56
www.terreactive.ch
Wir sichern Ihren Erfolg - seit 15 Jahren
^ permalink raw reply
* Re: Tracking database changes.
From: Rodrigo Cortés @ 2011-09-08 12:16 UTC (permalink / raw)
To: Rodrigo Cortés, git
In-Reply-To: <20110908121225.GC32087@victor.terreactive.ch>
So... there is no "plugin" for git to do that work.
On Thu, Sep 8, 2011 at 9:12 AM, Victor Engmark
<victor.engmark@terreactive.ch> wrote:
> On Tue, Sep 06, 2011 at 06:55:56PM -0300, Rodrigo Cortés wrote:
>> Is there a way to use git to track database changes?
>
> 1. Export the data and/or data model with a tool like mysqldump.
> 2. Remove output which would clutter up your diffs without adding any
> useful information. This could include things like date and time of the
> export and user name of the exporter.
> 3. Commit and enjoy!
>
> Optionally create a cron job to export and commit automatically during
> off-peak hours.
>
> Cheers,
> Victor
>
> --
> terreActive AG
> Kasinostrasse 30
> CH-5001 Aarau
> Tel: +41 62 834 00 55
> Fax: +41 62 823 93 56
> www.terreactive.ch
>
> Wir sichern Ihren Erfolg - seit 15 Jahren
>
--
Rodrigo Cortés Carvajal
Ingeniería Eléctrica
Universidad Tecnológica de Chile
^ permalink raw reply
* git rebase fails with: Patch does not have a valid e-mail address.
From: James Blackburn @ 2011-09-08 11:47 UTC (permalink / raw)
To: git
Hi,
I'm trying to rewrite some history and git's telling me:
-bash:jamesb:lc-cam-025:33079> git rebase
7f58969b933745d4cb9bb128bbd3fa8d441cdb92
First, rewinding head to replay your work on top of it...
Patch does not have a valid e-mail address.
Now it's true there isn't an email address for the author - the author
no longer works for the company, and the email address was removed
during the conversion. Therefore the repo contains "Author <>".
Given git doesn't ordinarily complain about this, should this prevent
rebase from working?
Cheers,
James
^ permalink raw reply
* Re: [PATCH] send-mail: Add option to sleep between sending each email.
From: Matthieu Moy @ 2011-09-08 11:15 UTC (permalink / raw)
To: Ramkumar Ramachandra; +Cc: Georgi Chorbadzhiyski, git
In-Reply-To: <CALkWK0mNBG8EwysjO8uoR+fU5ZM=Pz9es3t_+s6cFgR6NSodGQ@mail.gmail.com>
Ramkumar Ramachandra <artagnon@gmail.com> writes:
> Hi,
>
> I mocked up a small patch to demonstrate the "special cover letter
> handling" idea. Let me know if you think it's worth pursuing.
If it was really a problem to have to wait a few seconds/minutes more,
I'd consider it as an interesting compromise (allow [PATCH 1/2] to come
after [PATCH 2/2] but make sure they both come after the coverletter to
make sure the recipient notice they're part of the same thread).
But quite frankly, I don't think it's worth the trouble.
As you said, the normal workflow is to use "git send-email", and go back
to work after, so you can do something else while the emails are being
sent (see below [1]). I don't think bothering users with --sleep and
--initial-wait is worth the benefit of having both possibilities. People
worried about email order should be fine with --sleep.
[1] Actually, I think there's a problem with Georgi's patch. If I read
correctly, the sleep is inserted within the confirmation loop, which
means the user will have
send this email? yes
sending email
sleeping 10 seconds
send this email? yes
sending email
sleeping 10 seconds
...
while it should be
send this email? yes
ok, I'll send it later
send this email? yes
ok, I'll send it later
sending first email ...
sleeping 10 seconds
sending second email
done.
(i.e. don't force the user to wait between confirmations, and don't wait
after the last email)
--
Matthieu Moy
http://www-verimag.imag.fr/~moy/
^ permalink raw reply
* Re: [PATCH] send-mail: Add option to sleep between sending each email.
From: Georgi Chorbadzhiyski @ 2011-09-08 10:57 UTC (permalink / raw)
To: Ramkumar Ramachandra; +Cc: Matthieu Moy, git
In-Reply-To: <CALkWK0mNBG8EwysjO8uoR+fU5ZM=Pz9es3t_+s6cFgR6NSodGQ@mail.gmail.com>
Around 09/08/2011 01:44 PM, Ramkumar Ramachandra scribbled:
> I mocked up a small patch to demonstrate the "special cover letter
> handling" idea. Let me know if you think it's worth pursuing.
> Warning: Untested.
>
> Signed-off-by: Ramkumar Ramachandra <artagnon@gmail.com>
>
> -- 8< --
> diff --git a/git-send-email.perl b/git-send-email.perl
> index 98ab33a..30b8651 100755
> --- a/git-send-email.perl
> +++ b/git-send-email.perl
> @@ -80,6 +80,7 @@ git send-email [options] <file | directory |
> rev-list options >
> --[no-]suppress-from * Send to self. Default off.
> --[no-]chain-reply-to * Chain In-Reply-To: fields. Default off.
> --[no-]thread * Use In-Reply-To: field. Default on.
> + --[no-]initial-wait <int> * Wait <int> seconds after sending
> first email.
>
> Administering:
> --confirm <str> * Confirm recipients before sending;
> @@ -190,7 +191,7 @@ sub do_edit {
> }
>
> # Variables with corresponding config settings
> -my ($thread, $chain_reply_to, $suppress_from, $signed_off_by_cc);
> +my ($thread, $initial_wait, $chain_reply_to, $suppress_from,
> $signed_off_by_cc);
> my ($to_cmd, $cc_cmd);
> my ($smtp_server, $smtp_server_port, @smtp_server_options);
> my ($smtp_authuser, $smtp_encryption);
> @@ -205,6 +206,7 @@ my $not_set_by_user = "true but not set by the user";
>
> my %config_bool_settings = (
> "thread" => [\$thread, 1],
> + "initialwait" => [\$initial_wait, 0],
> "chainreplyto" => [\$chain_reply_to, $not_set_by_user],
> "suppressfrom" => [\$suppress_from, undef],
> "signedoffbycc" => [\$signed_off_by_cc, undef],
> @@ -1141,6 +1143,11 @@ X-Mailer: git-send-email $gitversion
> } else {
> print "Result: OK\n";
> }
> + if ($initial_wait) {
> + print "Sleeping: $initial_wait seconds.\n" if (!$quiet);
> + sleep($initial_wait);
> + $initial_wait = 0;
> + }
> }
>
> return 1;
I don't see how this would solve the problem that MTA can send
emails after the first one out of order.
--
Georgi Chorbadzhiyski
http://georgi.unixsol.org/
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox