aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/RelNotes/2.42.0.txt17
-rw-r--r--Documentation/git-for-each-ref.txt27
-rw-r--r--builtin/commit.c7
-rw-r--r--builtin/fsck.c4
-rw-r--r--diff-no-index.c120
-rw-r--r--imap-send.c34
-rw-r--r--pkt-line.c12
-rw-r--r--pkt-line.h2
-rw-r--r--pretty.c12
-rw-r--r--read-cache.c15
-rw-r--r--ref-filter.c126
-rw-r--r--remote-curl.c3
-rw-r--r--t/lib-gpg.sh21
-rwxr-xr-xt/t0091-bugreport.sh67
-rwxr-xr-xt/t1450-fsck.sh4
-rwxr-xr-xt/t2200-add-update.sh11
-rwxr-xr-xt/t4053-diff-no-index.sh64
-rwxr-xr-xt/t4205-log-pretty-formats.sh17
-rwxr-xr-xt/t6300-for-each-ref.sh191
-rwxr-xr-xt/t7510-signed-commit.sh7
20 files changed, 657 insertions, 104 deletions
diff --git a/Documentation/RelNotes/2.42.0.txt b/Documentation/RelNotes/2.42.0.txt
index 4a9b8ff864..9d8e50a5da 100644
--- a/Documentation/RelNotes/2.42.0.txt
+++ b/Documentation/RelNotes/2.42.0.txt
@@ -19,6 +19,9 @@ UI, Workflows & Features
* 'git notes append' was taught '--separator' to specify string to insert
between paragraphs.
+ * The "git for-each-ref" family of commands learned placeholders
+ related to GPG signature verification.
+
Performance, Internal Implementation, Development Support etc.
@@ -47,6 +50,9 @@ Performance, Internal Implementation, Development Support etc.
* Move functions that are not about pure string manipulation out of
strbuf.[ch]
+ * "imap-send" codepaths got cleaned up to get rid of unused
+ parameters.
+
Fixes since v2.41
-----------------
@@ -151,6 +157,15 @@ Fixes since v2.41
been corrected.
(merge a096a889f4 jk/cherry-pick-revert-status later to maint).
+ * A few places failed to differenciate the case where the index is
+ truly empty (nothing added) and we haven't yet read from the
+ on-disk index file, which have been corrected.
+ (merge 2ee045eea1 js/empty-index-fixes later to maint).
+
+ * "git bugreport" tests did not test what it wanted to test, which
+ has been corrected.
+ (merge 1aa92b8500 ma/t0091-fixup later to maint).
+
* Other code cleanup, docfix, build fix, etc.
(merge 51f9d2e563 sa/doc-ls-remote later to maint).
(merge c6d26a9dda jk/format-patch-message-id-unleak later to maint).
@@ -170,3 +185,5 @@ Fixes since v2.41
(merge 80d32e84b5 rj/leakfixes later to maint).
(merge 0a868031ed pb/complete-diff-options later to maint).
(merge d4f28279ad jc/doc-hash-object-types later to maint).
+ (merge 1876a5ae15 ks/t4205-test-describe-with-abbrev-fix later to maint).
+ (merge 6e6a529b57 jk/fsck-indices-in-worktrees later to maint).
diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt
index 1e215d4e73..2e0318770b 100644
--- a/Documentation/git-for-each-ref.txt
+++ b/Documentation/git-for-each-ref.txt
@@ -221,6 +221,33 @@ symref::
`:lstrip` and `:rstrip` options in the same way as `refname`
above.
+signature::
+ The GPG signature of a commit.
+
+signature:grade::
+ Show "G" for a good (valid) signature, "B" for a bad
+ signature, "U" for a good signature with unknown validity, "X"
+ for a good signature that has expired, "Y" for a good
+ signature made by an expired key, "R" for a good signature
+ made by a revoked key, "E" if the signature cannot be
+ checked (e.g. missing key) and "N" for no signature.
+
+signature:signer::
+ The signer of the GPG signature of a commit.
+
+signature:key::
+ The key of the GPG signature of a commit.
+
+signature:fingerprint::
+ The fingerprint of the GPG signature of a commit.
+
+signature:primarykeyfingerprint::
+ The primary key fingerprint of the GPG signature of a commit.
+
+signature:trustlevel::
+ The trust level of the GPG signature of a commit. Possible
+ outputs are `ultimate`, `fully`, `marginal`, `never` and `undefined`.
+
worktreepath::
The absolute path to the worktree in which the ref is checked
out, if it is checked out in any linked worktree. Empty string
diff --git a/builtin/commit.c b/builtin/commit.c
index 0b6752bfe8..7da5f92448 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -1002,11 +1002,8 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
struct object_id oid;
const char *parent = "HEAD";
- if (!the_index.cache_nr) {
- discard_index(&the_index);
- if (repo_read_index(the_repository) < 0)
- die(_("Cannot read index"));
- }
+ if (!the_index.initialized && repo_read_index(the_repository) < 0)
+ die(_("Cannot read index"));
if (amend)
parent = "HEAD^1";
diff --git a/builtin/fsck.c b/builtin/fsck.c
index fa26462337..3aa9c812eb 100644
--- a/builtin/fsck.c
+++ b/builtin/fsck.c
@@ -810,7 +810,7 @@ static int fsck_resolve_undo(struct index_state *istate,
}
static void fsck_index(struct index_state *istate, const char *index_path,
- int is_main_index)
+ int is_current_worktree)
{
unsigned int i;
@@ -832,7 +832,7 @@ static void fsck_index(struct index_state *istate, const char *index_path,
obj->flags |= USED;
fsck_put_object_name(&fsck_walk_options, &obj->oid,
"%s:%s",
- is_main_index ? "" : index_path,
+ is_current_worktree ? "" : index_path,
istate->cache[i]->name);
mark_object_reachable(obj);
}
diff --git a/diff-no-index.c b/diff-no-index.c
index 4296940f90..4771cf02aa 100644
--- a/diff-no-index.c
+++ b/diff-no-index.c
@@ -41,42 +41,81 @@ static int read_directory_contents(const char *path, struct string_list *list)
*/
static const char file_from_standard_input[] = "-";
-static int get_mode(const char *path, int *mode)
+/*
+ * For paths given on the command-line we treat "-" as stdin and named
+ * pipes and symbolic links to named pipes specially.
+ */
+enum special {
+ SPECIAL_NONE,
+ SPECIAL_STDIN,
+ SPECIAL_PIPE,
+};
+
+static int get_mode(const char *path, int *mode, enum special *special)
{
struct stat st;
- if (!path || !strcmp(path, "/dev/null"))
+ if (!path || !strcmp(path, "/dev/null")) {
*mode = 0;
#ifdef GIT_WINDOWS_NATIVE
- else if (!strcasecmp(path, "nul"))
+ } else if (!strcasecmp(path, "nul")) {
*mode = 0;
#endif
- else if (path == file_from_standard_input)
+ } else if (path == file_from_standard_input) {
*mode = create_ce_mode(0666);
- else if (lstat(path, &st))
+ *special = SPECIAL_STDIN;
+ } else if (lstat(path, &st)) {
return error("Could not access '%s'", path);
- else
+ } else {
*mode = st.st_mode;
+ }
+ /*
+ * For paths on the command-line treat named pipes and symbolic
+ * links that resolve to a named pipe specially.
+ */
+ if (special &&
+ (S_ISFIFO(*mode) ||
+ (S_ISLNK(*mode) && !stat(path, &st) && S_ISFIFO(st.st_mode)))) {
+ *mode = create_ce_mode(0666);
+ *special = SPECIAL_PIPE;
+ }
+
return 0;
}
-static int populate_from_stdin(struct diff_filespec *s)
+static void populate_common(struct diff_filespec *s, struct strbuf *buf)
{
- struct strbuf buf = STRBUF_INIT;
size_t size = 0;
- if (strbuf_read(&buf, 0, 0) < 0)
- return error_errno("error while reading from stdin");
-
s->should_munmap = 0;
- s->data = strbuf_detach(&buf, &size);
+ s->data = strbuf_detach(buf, &size);
s->size = size;
s->should_free = 1;
s->is_stdin = 1;
- return 0;
}
-static struct diff_filespec *noindex_filespec(const char *name, int mode)
+static void populate_from_pipe(struct diff_filespec *s)
+{
+ struct strbuf buf = STRBUF_INIT;
+ int fd = xopen(s->path, O_RDONLY);
+
+ if (strbuf_read(&buf, fd, 0) < 0)
+ die_errno("error while reading from '%s'", s->path);
+ close(fd);
+ populate_common(s, &buf);
+}
+
+static void populate_from_stdin(struct diff_filespec *s)
+{
+ struct strbuf buf = STRBUF_INIT;
+
+ if (strbuf_read(&buf, 0, 0) < 0)
+ die_errno("error while reading from stdin");
+ populate_common(s, &buf);
+}
+
+static struct diff_filespec *noindex_filespec(const char *name, int mode,
+ enum special special)
{
struct diff_filespec *s;
@@ -84,17 +123,22 @@ static struct diff_filespec *noindex_filespec(const char *name, int mode)
name = "/dev/null";
s = alloc_filespec(name);
fill_filespec(s, null_oid(), 0, mode);
- if (name == file_from_standard_input)
+ if (special == SPECIAL_STDIN)
populate_from_stdin(s);
+ else if (special == SPECIAL_PIPE)
+ populate_from_pipe(s);
return s;
}
static int queue_diff(struct diff_options *o,
- const char *name1, const char *name2)
+ const char *name1, const char *name2, int recursing)
{
int mode1 = 0, mode2 = 0;
+ enum special special1 = SPECIAL_NONE, special2 = SPECIAL_NONE;
- if (get_mode(name1, &mode1) || get_mode(name2, &mode2))
+ /* Paths can only be special if we're not recursing. */
+ if (get_mode(name1, &mode1, recursing ? NULL : &special1) ||
+ get_mode(name2, &mode2, recursing ? NULL : &special2))
return -1;
if (mode1 && mode2 && S_ISDIR(mode1) != S_ISDIR(mode2)) {
@@ -102,14 +146,14 @@ static int queue_diff(struct diff_options *o,
if (S_ISDIR(mode1)) {
/* 2 is file that is created */
- d1 = noindex_filespec(NULL, 0);
- d2 = noindex_filespec(name2, mode2);
+ d1 = noindex_filespec(NULL, 0, SPECIAL_NONE);
+ d2 = noindex_filespec(name2, mode2, special2);
name2 = NULL;
mode2 = 0;
} else {
/* 1 is file that is deleted */
- d1 = noindex_filespec(name1, mode1);
- d2 = noindex_filespec(NULL, 0);
+ d1 = noindex_filespec(name1, mode1, special1);
+ d2 = noindex_filespec(NULL, 0, SPECIAL_NONE);
name1 = NULL;
mode1 = 0;
}
@@ -174,7 +218,7 @@ static int queue_diff(struct diff_options *o,
n2 = buffer2.buf;
}
- ret = queue_diff(o, n1, n2);
+ ret = queue_diff(o, n1, n2, 1);
}
string_list_clear(&p1, 0);
string_list_clear(&p2, 0);
@@ -190,8 +234,8 @@ static int queue_diff(struct diff_options *o,
SWAP(name1, name2);
}
- d1 = noindex_filespec(name1, mode1);
- d2 = noindex_filespec(name2, mode2);
+ d1 = noindex_filespec(name1, mode1, special1);
+ d2 = noindex_filespec(name2, mode2, special2);
diff_queue(&diff_queued_diff, d1, d2);
return 0;
}
@@ -216,13 +260,27 @@ static void append_basename(struct strbuf *path, const char *dir, const char *fi
*/
static void fixup_paths(const char **path, struct strbuf *replacement)
{
- unsigned int isdir0, isdir1;
+ struct stat st;
+ unsigned int isdir0 = 0, isdir1 = 0;
+ unsigned int ispipe0 = 0, ispipe1 = 0;
+
+ if (path[0] != file_from_standard_input && !stat(path[0], &st)) {
+ isdir0 = S_ISDIR(st.st_mode);
+ ispipe0 = S_ISFIFO(st.st_mode);
+ }
+
+ if (path[1] != file_from_standard_input && !stat(path[1], &st)) {
+ isdir1 = S_ISDIR(st.st_mode);
+ ispipe1 = S_ISFIFO(st.st_mode);
+ }
+
+ if ((path[0] == file_from_standard_input && isdir1) ||
+ (isdir0 && path[1] == file_from_standard_input))
+ die(_("cannot compare stdin to a directory"));
+
+ if ((isdir0 && ispipe1) || (ispipe0 && isdir1))
+ die(_("cannot compare a named pipe to a directory"));
- if (path[0] == file_from_standard_input ||
- path[1] == file_from_standard_input)
- return;
- isdir0 = is_directory(path[0]);
- isdir1 = is_directory(path[1]);
if (isdir0 == isdir1)
return;
if (isdir0) {
@@ -296,7 +354,7 @@ int diff_no_index(struct rev_info *revs,
setup_diff_pager(&revs->diffopt);
revs->diffopt.flags.exit_with_status = 1;
- if (queue_diff(&revs->diffopt, paths[0], paths[1]))
+ if (queue_diff(&revs->diffopt, paths[0], paths[1], 0))
goto out;
diff_set_mnemonic_prefix(&revs->diffopt, "1/", "2/");
diffcore_std(&revs->diffopt);
diff --git a/imap-send.c b/imap-send.c
index 3518a4ace6..c807e36a7e 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -137,12 +137,10 @@ struct imap_store {
};
struct imap_cmd_cb {
- int (*cont)(struct imap_store *ctx, struct imap_cmd *cmd, const char *prompt);
- void (*done)(struct imap_store *ctx, struct imap_cmd *cmd, int response);
+ int (*cont)(struct imap_store *ctx, const char *prompt);
void *ctx;
char *data;
int dlen;
- int uid;
};
struct imap_cmd {
@@ -786,7 +784,7 @@ static int get_cmd_result(struct imap_store *ctx, struct imap_cmd *tcmd)
if (n != (int)cmdp->cb.dlen)
return RESP_BAD;
} else if (cmdp->cb.cont) {
- if (cmdp->cb.cont(ctx, cmdp, cmd))
+ if (cmdp->cb.cont(ctx, cmd))
return RESP_BAD;
} else {
fprintf(stderr, "IMAP error: unexpected command continuation request\n");
@@ -828,8 +826,6 @@ static int get_cmd_result(struct imap_store *ctx, struct imap_cmd *tcmd)
}
if ((resp2 = parse_response_code(ctx, &cmdp->cb, cmd)) > resp)
resp = resp2;
- if (cmdp->cb.done)
- cmdp->cb.done(ctx, cmdp, resp);
free(cmdp->cb.data);
free(cmdp->cmd);
free(cmdp);
@@ -917,7 +913,7 @@ static char *cram(const char *challenge_64, const char *user, const char *pass)
#endif
-static int auth_cram_md5(struct imap_store *ctx, struct imap_cmd *cmd, const char *prompt)
+static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
{
int ret;
char *response;
@@ -1416,16 +1412,16 @@ static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
if (!curl)
die("curl_easy_init failed");
- server_fill_credential(&server, cred);
- curl_easy_setopt(curl, CURLOPT_USERNAME, server.user);
- curl_easy_setopt(curl, CURLOPT_PASSWORD, server.pass);
+ server_fill_credential(srvc, cred);
+ curl_easy_setopt(curl, CURLOPT_USERNAME, srvc->user);
+ curl_easy_setopt(curl, CURLOPT_PASSWORD, srvc->pass);
- strbuf_addstr(&path, server.use_ssl ? "imaps://" : "imap://");
- strbuf_addstr(&path, server.host);
+ strbuf_addstr(&path, srvc->use_ssl ? "imaps://" : "imap://");
+ strbuf_addstr(&path, srvc->host);
if (!path.len || path.buf[path.len - 1] != '/')
strbuf_addch(&path, '/');
- uri_encoded_folder = curl_easy_escape(curl, server.folder, 0);
+ uri_encoded_folder = curl_easy_escape(curl, srvc->folder, 0);
if (!uri_encoded_folder)
die("failed to encode server folder");
strbuf_addstr(&path, uri_encoded_folder);
@@ -1433,25 +1429,25 @@ static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
curl_easy_setopt(curl, CURLOPT_URL, path.buf);
strbuf_release(&path);
- curl_easy_setopt(curl, CURLOPT_PORT, server.port);
+ curl_easy_setopt(curl, CURLOPT_PORT, srvc->port);
- if (server.auth_method) {
+ if (srvc->auth_method) {
#ifndef GIT_CURL_HAVE_CURLOPT_LOGIN_OPTIONS
warning("No LOGIN_OPTIONS support in this cURL version");
#else
struct strbuf auth = STRBUF_INIT;
strbuf_addstr(&auth, "AUTH=");
- strbuf_addstr(&auth, server.auth_method);
+ strbuf_addstr(&auth, srvc->auth_method);
curl_easy_setopt(curl, CURLOPT_LOGIN_OPTIONS, auth.buf);
strbuf_release(&auth);
#endif
}
- if (!server.use_ssl)
+ if (!srvc->use_ssl)
curl_easy_setopt(curl, CURLOPT_USE_SSL, (long)CURLUSESSL_TRY);
- curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, server.ssl_verify);
- curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, server.ssl_verify);
+ curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, srvc->ssl_verify);
+ curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, srvc->ssl_verify);
curl_easy_setopt(curl, CURLOPT_READFUNCTION, fread_buffer);
diff --git a/pkt-line.c b/pkt-line.c
index 62b4208b66..fcfa357ccd 100644
--- a/pkt-line.c
+++ b/pkt-line.c
@@ -373,10 +373,14 @@ static int get_packet_data(int fd, char **src_buf, size_t *src_size,
return ret;
}
-int packet_length(const char lenbuf_hex[4])
+int packet_length(const char lenbuf_hex[4], size_t size)
{
- int val = hex2chr(lenbuf_hex);
- return (val < 0) ? val : (val << 8) | hex2chr(lenbuf_hex + 2);
+ if (size < 4)
+ BUG("buffer too small");
+ return hexval(lenbuf_hex[0]) << 12 |
+ hexval(lenbuf_hex[1]) << 8 |
+ hexval(lenbuf_hex[2]) << 4 |
+ hexval(lenbuf_hex[3]);
}
static char *find_packfile_uri_path(const char *buffer)
@@ -419,7 +423,7 @@ enum packet_read_status packet_read_with_status(int fd, char **src_buffer,
return PACKET_READ_EOF;
}
- len = packet_length(linelen);
+ len = packet_length(linelen, sizeof(linelen));
if (len < 0) {
if (options & PACKET_READ_GENTLE_ON_READ_ERROR)
diff --git a/pkt-line.h b/pkt-line.h
index 7c23a4bfaf..954eec8719 100644
--- a/pkt-line.h
+++ b/pkt-line.h
@@ -94,7 +94,7 @@ int packet_read(int fd, char *buffer, unsigned size, int options);
* If lenbuf_hex contains non-hex characters, return -1. Otherwise, return the
* numeric value of the length header.
*/
-int packet_length(const char lenbuf_hex[4]);
+int packet_length(const char lenbuf_hex[4], size_t size);
/*
* Read a packetized line into a buffer like the 'packet_read()' function but
diff --git a/pretty.c b/pretty.c
index 7862be105d..82f30e27c5 100644
--- a/pretty.c
+++ b/pretty.c
@@ -1855,10 +1855,10 @@ static size_t format_commit_item(struct strbuf *sb, /* in UTF-8 */
}
orig_len = sb->len;
- if ((context)->flush_type != no_flush)
- consumed = format_and_pad_commit(sb, placeholder, context);
- else
+ if (context->flush_type == no_flush)
consumed = format_commit_one(sb, placeholder, context);
+ else
+ consumed = format_and_pad_commit(sb, placeholder, context);
if (magic == NO_MAGIC)
return consumed;
@@ -1876,14 +1876,13 @@ static size_t format_commit_item(struct strbuf *sb, /* in UTF-8 */
void userformat_find_requirements(const char *fmt, struct userformat_want *w)
{
- struct strbuf dummy = STRBUF_INIT;
-
if (!fmt) {
if (!user_format)
return;
fmt = user_format;
}
- while (strbuf_expand_step(&dummy, &fmt)) {
+ while ((fmt = strchr(fmt, '%'))) {
+ fmt++;
if (skip_prefix(fmt, "%", &fmt))
continue;
@@ -1903,7 +1902,6 @@ void userformat_find_requirements(const char *fmt, struct userformat_want *w)
break;
}
}
- strbuf_release(&dummy);
}
void repo_format_commit_message(struct repository *r,
diff --git a/read-cache.c b/read-cache.c
index b9a995e5a1..27703e1446 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -2236,6 +2236,7 @@ int do_read_index(struct index_state *istate, const char *path, int must_exist)
if (fd < 0) {
if (!must_exist && errno == ENOENT) {
set_new_index_sparsity(istate);
+ istate->initialized = 1;
return 0;
}
die_errno(_("%s: index file open failed"), path);
@@ -2405,12 +2406,14 @@ int read_index_from(struct index_state *istate, const char *path,
base_oid_hex = oid_to_hex(&split_index->base_oid);
base_path = xstrfmt("%s/sharedindex.%s", gitdir, base_oid_hex);
- trace2_region_enter_printf("index", "shared/do_read_index",
- the_repository, "%s", base_path);
- ret = do_read_index(split_index->base, base_path, 0);
- trace2_region_leave_printf("index", "shared/do_read_index",
- the_repository, "%s", base_path);
- if (!ret) {
+ if (file_exists(base_path)) {
+ trace2_region_enter_printf("index", "shared/do_read_index",
+ the_repository, "%s", base_path);
+
+ ret = do_read_index(split_index->base, base_path, 0);
+ trace2_region_leave_printf("index", "shared/do_read_index",
+ the_repository, "%s", base_path);
+ } else {
char *path_copy = xstrdup(path);
char *base_path2 = xstrfmt("%s/sharedindex.%s",
dirname(path_copy), base_oid_hex);
diff --git a/ref-filter.c b/ref-filter.c
index e0d03a9f8e..7324f7b5b8 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -150,6 +150,7 @@ enum atom_type {
ATOM_BODY,
ATOM_TRAILERS,
ATOM_CONTENTS,
+ ATOM_SIGNATURE,
ATOM_RAW,
ATOM_UPSTREAM,
ATOM_PUSH,
@@ -215,6 +216,10 @@ static struct used_atom {
struct email_option {
enum { EO_RAW, EO_TRIM, EO_LOCALPART } option;
} email_option;
+ struct {
+ enum { S_BARE, S_GRADE, S_SIGNER, S_KEY,
+ S_FINGERPRINT, S_PRI_KEY_FP, S_TRUST_LEVEL } option;
+ } signature;
struct refname_atom refname;
char *head;
} u;
@@ -407,8 +412,37 @@ static int subject_atom_parser(struct ref_format *format UNUSED,
return 0;
}
-static int trailers_atom_parser(struct ref_format *format UNUSED,
- struct used_atom *atom,
+static int parse_signature_option(const char *arg)
+{
+ if (!arg)
+ return S_BARE;
+ else if (!strcmp(arg, "signer"))
+ return S_SIGNER;
+ else if (!strcmp(arg, "grade"))
+ return S_GRADE;
+ else if (!strcmp(arg, "key"))
+ return S_KEY;
+ else if (!strcmp(arg, "fingerprint"))
+ return S_FINGERPRINT;
+ else if (!strcmp(arg, "primarykeyfingerprint"))
+ return S_PRI_KEY_FP;
+ else if (!strcmp(arg, "trustlevel"))
+ return S_TRUST_LEVEL;
+ return -1;
+}
+
+static int signature_atom_parser(struct ref_format *format UNUSED,
+ struct used_atom *atom,
+ const char *arg, struct strbuf *err)
+{
+ int opt = parse_signature_option(arg);
+ if (opt < 0)
+ return err_bad_arg(err, "signature", arg);
+ atom->u.signature.option = opt;
+ return 0;
+}
+
+static int trailers_atom_parser(struct ref_format *format, struct used_atom *atom,
const char *arg, struct strbuf *err)
{
atom->u.contents.trailer_opts.no_divider = 1;
@@ -668,6 +702,7 @@ static struct {
[ATOM_BODY] = { "body", SOURCE_OBJ, FIELD_STR, body_atom_parser },
[ATOM_TRAILERS] = { "trailers", SOURCE_OBJ, FIELD_STR, trailers_atom_parser },
[ATOM_CONTENTS] = { "contents", SOURCE_OBJ, FIELD_STR, contents_atom_parser },
+ [ATOM_SIGNATURE] = { "signature", SOURCE_OBJ, FIELD_STR, signature_atom_parser },
[ATOM_RAW] = { "raw", SOURCE_OBJ, FIELD_STR, raw_atom_parser },
[ATOM_UPSTREAM] = { "upstream", SOURCE_NONE, FIELD_STR, remote_ref_atom_parser },
[ATOM_PUSH] = { "push", SOURCE_NONE, FIELD_STR, remote_ref_atom_parser },
@@ -1405,6 +1440,92 @@ static void grab_person(const char *who, struct atom_value *val, int deref, void
}
}
+static void grab_signature(struct atom_value *val, int deref, struct object *obj)
+{
+ int i;
+ struct commit *commit = (struct commit *) obj;
+ struct signature_check sigc = { 0 };
+ int signature_checked = 0;
+
+ for (i = 0; i < used_atom_cnt; i++) {
+ struct used_atom *atom = &used_atom[i];
+ const char *name = atom->name;
+ struct atom_value *v = &val[i];
+ int opt;
+
+ if (!!deref != (*name == '*'))
+ continue;
+ if (deref)
+ name++;
+
+ if (!skip_prefix(name, "signature", &name) ||
+ (*name && *name != ':'))
+ continue;
+ if (!*name)
+ name = NULL;
+ else
+ name++;
+
+ opt = parse_signature_option(name);
+ if (opt < 0)
+ continue;
+
+ if (!signature_checked) {
+ check_commit_signature(commit, &sigc);
+ signature_checked = 1;
+ }
+
+ switch (opt) {
+ case S_BARE:
+ v->s = xstrdup(sigc.output ? sigc.output: "");
+ break;
+ case S_SIGNER:
+ v->s = xstrdup(sigc.signer ? sigc.signer : "");
+ break;
+ case S_GRADE:
+ switch (sigc.result) {
+ case 'G':
+ switch (sigc.trust_level) {
+ case TRUST_UNDEFINED:
+ case TRUST_NEVER:
+ v->s = xstrfmt("%c", (char)'U');
+ break;
+ default:
+ v->s = xstrfmt("%c", (char)'G');
+ break;
+ }
+ break;
+ case 'B':
+ case 'E':
+ case 'N':
+ case 'X':
+ case 'Y':
+ case 'R':
+ v->s = xstrfmt("%c", (char)sigc.result);
+ break;
+ }
+ break;
+ case S_KEY:
+ v->s = xstrdup(sigc.key ? sigc.key : "");
+ break;
+ case S_FINGERPRINT:
+ v->s = xstrdup(sigc.fingerprint ?
+ sigc.fingerprint : "");
+ break;
+ case S_PRI_KEY_FP:
+ v->s = xstrdup(sigc.primary_key_fingerprint ?
+ sigc.primary_key_fingerprint : "");
+ break;
+ case S_TRUST_LEVEL:
+ v->s = xstrdup(gpg_trust_level_to_str(sigc.trust_level));
+ break;
+ }
+ }
+
+ if (signature_checked)
+ signature_check_clear(&sigc);
+}
+
static void find_subpos(const char *buf,
const char **sub, size_t *sublen,
const char **body, size_t *bodylen,
@@ -1598,6 +1719,7 @@ static void grab_values(struct atom_value *val, int deref, struct object *obj, s
grab_sub_body_contents(val, deref, data);
grab_person("author", val, deref, buf);
grab_person("committer", val, deref, buf);
+ grab_signature(val, deref, obj);
break;
case OBJ_TREE:
/* grab_tree_values(val, deref, obj, buf, sz); */
diff --git a/remote-curl.c b/remote-curl.c
index acf7b2bb40..143318658e 100644
--- a/remote-curl.c
+++ b/remote-curl.c
@@ -763,7 +763,8 @@ static void check_pktline(struct check_pktline_state *state, const char *ptr, si
size -= digits_remaining;
if (state->len_filled == 4) {
- state->remaining = packet_length(state->len_buf);
+ state->remaining = packet_length(state->len_buf,
+ sizeof(state->len_buf));
if (state->remaining < 0) {
die(_("remote-curl: bad line length character: %.4s"), state->len_buf);
} else if (state->remaining == 2) {
diff --git a/t/lib-gpg.sh b/t/lib-gpg.sh
index 739e6ec35c..4eebd9c2b5 100644
--- a/t/lib-gpg.sh
+++ b/t/lib-gpg.sh
@@ -51,6 +51,27 @@ test_lazy_prereq GPG '
esac
'
+test_lazy_prereq GPG2 '
+ gpg_version=$(gpg --version 2>&1)
+ test $? != 127 || exit 1
+
+ case "$gpg_version" in
+ "gpg (GnuPG) "[01].*)
+ say "This test requires a GPG version >= v2.0.0"
+ exit 1
+ ;;
+ *)
+ (gpgconf --kill all || : ) &&
+ gpg --homedir "${GNUPGHOME}" --import \
+ "$TEST_DIRECTORY"/lib-gpg/keyring.gpg &&
+ gpg --homedir "${GNUPGHOME}" --import-ownertrust \
+ "$TEST_DIRECTORY"/lib-gpg/ownertrust &&
+ gpg --homedir "${GNUPGHOME}" </dev/null >/dev/null \
+ --sign -u committer@example.com
+ ;;
+ esac
+'
+
test_lazy_prereq GPGSM '
test_have_prereq GPG &&
# Available key info:
diff --git a/t/t0091-bugreport.sh b/t/t0091-bugreport.sh
index b6d2f591ac..f6998269be 100755
--- a/t/t0091-bugreport.sh
+++ b/t/t0091-bugreport.sh
@@ -5,29 +5,50 @@ test_description='git bugreport'
TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
-# Headers "[System Info]" will be followed by a non-empty line if we put some
-# information there; we can make sure all our headers were followed by some
-# information to check if the command was successful.
-HEADER_PATTERN="^\[.*\]$"
-
-check_all_headers_populated () {
- while read -r line
- do
- if test "$(grep "$HEADER_PATTERN" "$line")"
- then
- echo "$line"
- read -r nextline
- if test -z "$nextline"; then
- return 1;
- fi
- fi
- done
-}
-
-test_expect_success 'creates a report with content in the right places' '
- test_when_finished rm git-bugreport-check-headers.txt &&
- git bugreport -s check-headers &&
- check_all_headers_populated <git-bugreport-check-headers.txt
+test_expect_success 'create a report' '
+ git bugreport -s format &&
+ test_file_not_empty git-bugreport-format.txt
+'
+
+test_expect_success 'report contains wanted template (before first section)' '
+ sed -ne "/^\[/q;p" git-bugreport-format.txt >actual &&
+ cat >expect <<-\EOF &&
+ Thank you for filling out a Git bug report!
+ Please answer the following questions to help us understand your issue.
+
+ What did you do before the bug happened? (Steps to reproduce your issue)
+
+ What did you expect to happen? (Expected behavior)
+
+ What happened instead? (Actual behavior)
+
+ What'\''s different between what you expected and what actually happened?
+
+ Anything else you want to add:
+
+ Please review the rest of the bug report below.
+ You can delete any lines you don'\''t wish to share.
+
+
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'sanity check "System Info" section' '
+ test_when_finished rm -f git-bugreport-format.txt &&
+
+ sed -ne "/^\[System Info\]$/,/^$/p" <git-bugreport-format.txt >system &&
+
+ # The beginning should match "git version --build-info" verbatim,
+ # but rather than checking bit-for-bit equality, just test some basics.
+ grep "git version [0-9]." system &&
+ grep "shell-path: ." system &&
+
+ # After the version, there should be some more info.
+ # This is bound to differ from environment to environment,
+ # so we just do some rather high-level checks.
+ grep "uname: ." system &&
+ grep "compiler info: ." system
'
test_expect_success 'dies if file with same name as report already exists' '
diff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh
index 8c442adb1a..5805d47eb9 100755
--- a/t/t1450-fsck.sh
+++ b/t/t1450-fsck.sh
@@ -1036,9 +1036,9 @@ test_expect_success 'fsck detects problems in worktree index' '
test_cmp expect actual
'
-test_expect_success 'fsck reports problems in main index without filename' '
+test_expect_success 'fsck reports problems in current worktree index without filename' '
test_when_finished "rm -f .git/index && git read-tree HEAD" &&
- echo "this object will be removed to break the main index" >file &&
+ echo "this object will be removed to break current worktree index" >file &&
git add file &&
blob=$(git rev-parse :file) &&
remove_object $blob &&
diff --git a/t/t2200-add-update.sh b/t/t2200-add-update.sh
index be394f1131..c01492f33f 100755
--- a/t/t2200-add-update.sh
+++ b/t/t2200-add-update.sh
@@ -197,4 +197,15 @@ test_expect_success '"add -u non-existent" should fail' '
! grep "non-existent" actual
'
+test_expect_success '"commit -a" implies "add -u" if index becomes empty' '
+ git rm -rf \* &&
+ git commit -m clean-slate &&
+ test_commit file1 &&
+ rm file1.t &&
+ test_tick &&
+ git commit -a -m remove &&
+ git ls-tree HEAD: >out &&
+ test_must_be_empty out
+'
+
test_done
diff --git a/t/t4053-diff-no-index.sh b/t/t4053-diff-no-index.sh
index 4e9fa0403d..a28b9ff243 100755
--- a/t/t4053-diff-no-index.sh
+++ b/t/t4053-diff-no-index.sh
@@ -205,4 +205,68 @@ test_expect_success POSIXPERM,SYMLINKS 'diff --no-index normalizes: mode not lik
test_cmp expected actual
'
+test_expect_success "diff --no-index treats '-' as stdin" '
+ cat >expect <<-EOF &&
+ diff --git a/- b/a/1
+ index $ZERO_OID..$(git hash-object --stdin <a/1) 100644
+ --- a/-
+ +++ b/a/1
+ @@ -1 +1 @@
+ -x
+ +1
+ EOF
+
+ test_write_lines x | test_expect_code 1 \
+ git -c core.abbrev=no diff --no-index -- - a/1 >actual &&
+ test_cmp expect actual &&
+
+ test_write_lines 1 | git diff --no-index -- a/1 - >actual &&
+ test_must_be_empty actual
+'
+
+test_expect_success 'diff --no-index refuses to diff stdin and a directory' '
+ test_must_fail git diff --no-index -- - a </dev/null 2>err &&
+ grep "fatal: cannot compare stdin to a directory" err
+'
+
+test_expect_success PIPE 'diff --no-index refuses to diff a named pipe and a directory' '
+ test_when_finished "rm -f pipe" &&
+ mkfifo pipe &&
+ {
+ (>pipe) &
+ } &&
+ test_when_finished "kill $!" &&
+ test_must_fail git diff --no-index -- pipe a 2>err &&
+ grep "fatal: cannot compare a named pipe to a directory" err
+'
+
+test_expect_success PIPE,SYMLINKS 'diff --no-index reads from pipes' '
+ test_when_finished "rm -f old new new-link" &&
+ mkfifo old &&
+ mkfifo new &&
+ ln -s new new-link &&
+ {
+ (test_write_lines a b c >old) &
+ } &&
+ test_when_finished "! kill $!" &&
+ {
+ (test_write_lines a x c >new) &
+ } &&
+ test_when_finished "! kill $!" &&
+
+ cat >expect <<-EOF &&
+ diff --git a/old b/new-link
+ --- a/old
+ +++ b/new-link
+ @@ -1,3 +1,3 @@
+ a
+ -b
+ +x
+ c
+ EOF
+
+ test_expect_code 1 git diff --no-index old new-link >actual &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t4205-log-pretty-formats.sh b/t/t4205-log-pretty-formats.sh
index 4cf8a77667..dd9035aa38 100755
--- a/t/t4205-log-pretty-formats.sh
+++ b/t/t4205-log-pretty-formats.sh
@@ -1012,10 +1012,25 @@ test_expect_success '%(describe:tags) vs git describe --tags' '
test_expect_success '%(describe:abbrev=...) vs git describe --abbrev=...' '
test_when_finished "git tag -d tagname" &&
+
+ # Case 1: We have commits between HEAD and the most recent tag
+ # reachable from it
+ test_commit --no-tag file &&
+ git describe --abbrev=15 >expect &&
+ git log -1 --format="%(describe:abbrev=15)" >actual &&
+ test_cmp expect actual &&
+
+ # Make sure the hash used is at least 15 digits long
+ sed -e "s/^.*-g\([0-9a-f]*\)$/\1/" <actual >hexpart &&
+ test 16 -le $(wc -c <hexpart) &&
+
+ # Case 2: We have a tag at HEAD, describe directly gives the
+ # name of the tag
git tag -a -m tagged tagname &&
git describe --abbrev=15 >expect &&
git log -1 --format="%(describe:abbrev=15)" >actual &&
- test_cmp expect actual
+ test_cmp expect actual &&
+ test tagname = $(cat actual)
'
test_expect_success 'log --pretty with space stealing' '
diff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh
index 5c00607608..6e6ec852b5 100755
--- a/t/t6300-for-each-ref.sh
+++ b/t/t6300-for-each-ref.sh
@@ -6,6 +6,7 @@
test_description='for-each-ref test'
. ./test-lib.sh
+GNUPGHOME_NOT_USED=$GNUPGHOME
. "$TEST_DIRECTORY"/lib-gpg.sh
. "$TEST_DIRECTORY"/lib-terminal.sh
@@ -1522,4 +1523,194 @@ test_expect_success 'git for-each-ref with non-existing refs' '
test_must_be_empty actual
'
+GRADE_FORMAT="%(signature:grade)%0a%(signature:key)%0a%(signature:signer)%0a%(signature:fingerprint)%0a%(signature:primarykeyfingerprint)"
+TRUSTLEVEL_FORMAT="%(signature:trustlevel)%0a%(signature:key)%0a%(signature:signer)%0a%(signature:fingerprint)%0a%(signature:primarykeyfingerprint)"
+
+test_expect_success GPG 'setup for signature atom using gpg' '
+ git checkout -b signed &&
+
+ test_when_finished "test_unconfig commit.gpgSign" &&
+
+ echo "1" >file &&
+ git add file &&
+ test_tick &&
+ git commit -S -m "file: 1" &&
+ git tag first-signed &&
+
+ echo "2" >file &&
+ test_tick &&
+ git commit -a -m "file: 2" &&
+ git tag second-unsigned &&
+
+ git config commit.gpgSign 1 &&
+ echo "3" >file &&
+ test_tick &&
+ git commit -a --no-gpg-sign -m "file: 3" &&
+ git tag third-unsigned &&
+
+ test_tick &&
+ git rebase -f HEAD^^ && git tag second-signed HEAD^ &&
+ git tag third-signed &&
+
+ echo "4" >file &&
+ test_tick &&
+ git commit -a -SB7227189 -m "file: 4" &&
+ git tag fourth-signed &&
+
+ echo "5" >file &&
+ test_tick &&
+ git commit -a --no-gpg-sign -m "file: 5" &&
+ git tag fifth-unsigned &&
+
+ echo "6" >file &&
+ test_tick &&
+ git commit -a --no-gpg-sign -m "file: 6" &&
+
+ test_tick &&
+ git rebase -f HEAD^^ &&
+ git tag fifth-signed HEAD^ &&
+ git tag sixth-signed &&
+
+ echo "7" >file &&
+ test_tick &&
+ git commit -a --no-gpg-sign -m "file: 7" &&
+ git tag seventh-unsigned
+'
+
+test_expect_success GPGSSH 'setup for signature atom using ssh' '
+ test_when_finished "test_unconfig gpg.format user.signingkey" &&
+
+ test_config gpg.format ssh &&
+ test_config user.signingkey "${GPGSSH_KEY_PRIMARY}" &&
+ echo "8" >file &&
+ test_tick &&
+ git commit -a -S -m "file: 8" &&
+ git tag eighth-signed-ssh
+'
+
+test_expect_success GPG2 'bare signature atom' '
+ git verify-commit first-signed 2>out.raw &&
+ grep -Ev "checking the trustdb|PGP trust model" out.raw >out &&
+ head -3 out >expect &&
+ tail -1 out >>expect &&
+ echo >>expect &&
+ git for-each-ref refs/tags/first-signed \
+ --format="%(signature)" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success GPG 'show good signature with custom format' '
+ git verify-commit first-signed &&
+ cat >expect <<-\EOF &&
+ G
+ 13B6F51ECDDE430D
+ C O Mitter <committer@example.com>
+ 73D758744BE721698EC54E8713B6F51ECDDE430D
+ 73D758744BE721698EC54E8713B6F51ECDDE430D
+ EOF
+ git for-each-ref refs/tags/first-signed \
+ --format="$GRADE_FORMAT" >actual &&
+ test_cmp expect actual
+'
+test_expect_success GPGSSH 'show good signature with custom format
+ with ssh' '
+ test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+ FINGERPRINT=$(ssh-keygen -lf "${GPGSSH_KEY_PRIMARY}" | awk "{print \$2;}") &&
+ cat >expect.tmpl <<-\EOF &&
+ G
+ FINGERPRINT
+ principal with number 1
+ FINGERPRINT
+
+ EOF
+ sed "s|FINGERPRINT|$FINGERPRINT|g" expect.tmpl >expect &&
+ git for-each-ref refs/tags/eighth-signed-ssh \
+ --format="$GRADE_FORMAT" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success GPG 'signature atom with grade option and bad signature' '
+ git cat-file commit third-signed >raw &&
+ sed -e "s/^file: 3/file: 3 forged/" raw >forged1 &&
+ FORGED1=$(git hash-object -w -t commit forged1) &&
+ git update-ref refs/tags/third-signed "$FORGED1" &&
+ test_must_fail git verify-commit "$FORGED1" &&
+
+ cat >expect <<-\EOF &&
+ B
+ 13B6F51ECDDE430D
+ C O Mitter <committer@example.com>
+
+
+ EOF
+ git for-each-ref refs/tags/third-signed \
+ --format="$GRADE_FORMAT" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success GPG 'show untrusted signature with custom format' '
+ cat >expect <<-\EOF &&
+ U
+ 65A0EEA02E30CAD7
+ Eris Discordia <discord@example.net>
+ F8364A59E07FFE9F4D63005A65A0EEA02E30CAD7
+ D4BE22311AD3131E5EDA29A461092E85B7227189
+ EOF
+ git for-each-ref refs/tags/fourth-signed \
+ --format="$GRADE_FORMAT" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success GPG 'show untrusted signature with undefined trust level' '
+ cat >expect <<-\EOF &&
+ undefined
+ 65A0EEA02E30CAD7
+ Eris Discordia <discord@example.net>
+ F8364A59E07FFE9F4D63005A65A0EEA02E30CAD7
+ D4BE22311AD3131E5EDA29A461092E85B7227189
+ EOF
+ git for-each-ref refs/tags/fourth-signed \
+ --format="$TRUSTLEVEL_FORMAT" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success GPG 'show untrusted signature with ultimate trust level' '
+ cat >expect <<-\EOF &&
+ ultimate
+ 13B6F51ECDDE430D
+ C O Mitter <committer@example.com>
+ 73D758744BE721698EC54E8713B6F51ECDDE430D
+ 73D758744BE721698EC54E8713B6F51ECDDE430D
+ EOF
+ git for-each-ref refs/tags/sixth-signed \
+ --format="$TRUSTLEVEL_FORMAT" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success GPG 'show unknown signature with custom format' '
+ cat >expect <<-\EOF &&
+ E
+ 13B6F51ECDDE430D
+
+
+
+ EOF
+ GNUPGHOME="$GNUPGHOME_NOT_USED" git for-each-ref \
+ refs/tags/sixth-signed --format="$GRADE_FORMAT" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success GPG 'show lack of signature with custom format' '
+ cat >expect <<-\EOF &&
+ N
+
+
+
+
+ EOF
+ git for-each-ref refs/tags/seventh-unsigned \
+ --format="$GRADE_FORMAT" >actual &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t7510-signed-commit.sh b/t/t7510-signed-commit.sh
index ccbc416402..0d2dd29fe6 100755
--- a/t/t7510-signed-commit.sh
+++ b/t/t7510-signed-commit.sh
@@ -218,6 +218,13 @@ test_expect_success GPG 'amending already signed commit' '
! grep "BAD signature from" actual
'
+test_expect_success GPG2 'bare signature' '
+ git verify-commit fifth-signed 2>expect &&
+ echo >>expect &&
+ git log -1 --format="%GG" fifth-signed >actual &&
+ test_cmp expect actual
+'
+
test_expect_success GPG 'show good signature with custom format' '
cat >expect <<-\EOF &&
G