diff options
| -rw-r--r-- | Documentation/RelNotes/2.42.0.txt | 17 | ||||
| -rw-r--r-- | Documentation/git-for-each-ref.txt | 27 | ||||
| -rw-r--r-- | builtin/commit.c | 7 | ||||
| -rw-r--r-- | builtin/fsck.c | 4 | ||||
| -rw-r--r-- | diff-no-index.c | 120 | ||||
| -rw-r--r-- | imap-send.c | 34 | ||||
| -rw-r--r-- | pkt-line.c | 12 | ||||
| -rw-r--r-- | pkt-line.h | 2 | ||||
| -rw-r--r-- | pretty.c | 12 | ||||
| -rw-r--r-- | read-cache.c | 15 | ||||
| -rw-r--r-- | ref-filter.c | 126 | ||||
| -rw-r--r-- | remote-curl.c | 3 | ||||
| -rw-r--r-- | t/lib-gpg.sh | 21 | ||||
| -rwxr-xr-x | t/t0091-bugreport.sh | 67 | ||||
| -rwxr-xr-x | t/t1450-fsck.sh | 4 | ||||
| -rwxr-xr-x | t/t2200-add-update.sh | 11 | ||||
| -rwxr-xr-x | t/t4053-diff-no-index.sh | 64 | ||||
| -rwxr-xr-x | t/t4205-log-pretty-formats.sh | 17 | ||||
| -rwxr-xr-x | t/t6300-for-each-ref.sh | 191 | ||||
| -rwxr-xr-x | t/t7510-signed-commit.sh | 7 |
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 @@ -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 |
