diff options
156 files changed, 1625 insertions, 663 deletions
diff --git a/.clang-format b/.clang-format index 16fd12253e..0b82f3c776 100644 --- a/.clang-format +++ b/.clang-format @@ -100,11 +100,13 @@ BreakStringLiterals: false # Switch statement body is always indented one level more than case labels. IndentCaseLabels: false -# Indents directives before the hash. +# Indents directives before the hash. Each level uses a single space for +# indentation. # #if FOO -# # include <foo> +# # include <foo> # #endif IndentPPDirectives: AfterHash +PPIndentWidth: 1 # Don't indent a function definition or declaration if it is wrapped after the # type diff --git a/Documentation/CodingGuidelines b/Documentation/CodingGuidelines index 52afb2725f..e4bd0abdcd 100644 --- a/Documentation/CodingGuidelines +++ b/Documentation/CodingGuidelines @@ -185,8 +185,8 @@ For shell scripts specifically (not exhaustive): - Even though "local" is not part of POSIX, we make heavy use of it in our test suite. We do not use it in scripted Porcelains, and - hopefully nobody starts using "local" before they are reimplemented - in C ;-) + hopefully nobody starts using "local" before all shells that matter + support it (notably, ksh from AT&T Research does not support it yet). - Some versions of shell do not understand "export variable=value", so we write "variable=value" and then "export variable" on two @@ -241,6 +241,16 @@ For C programs: - We use tabs to indent, and interpret tabs as taking up to 8 spaces. + - Nested C preprocessor directives are indented after the hash by one + space per nesting level. + + #if FOO + # include <foo.h> + # if BAR + # include <bar.h> + # endif + #endif + - We try to keep to at most 80 characters per line. - As a Git developer we assume you have a reasonably modern compiler @@ -261,7 +271,7 @@ For C programs: . since around 2007 with 2b6854c863a, we have been using initializer elements which are not computable at load time. E.g.: - const char *args[] = {"constant", variable, NULL}; + const char *args[] = { "constant", variable, NULL }; . since early 2012 with e1327023ea, we have been using an enum definition whose last element is followed by a comma. This, like @@ -558,6 +568,42 @@ For C programs: use your own debugger and arguments. Example: `GIT_DEBUGGER="ddd --gdb" ./bin-wrappers/git log` (See `wrap-for-bin.sh`.) + - The primary data structure that a subsystem 'S' deals with is called + `struct S`. Functions that operate on `struct S` are named + `S_<verb>()` and should generally receive a pointer to `struct S` as + first parameter. E.g. + + struct strbuf; + + void strbuf_add(struct strbuf *buf, ...); + + void strbuf_reset(struct strbuf *buf); + + is preferred over: + + struct strbuf; + + void add_string(struct strbuf *buf, ...); + + void reset_strbuf(struct strbuf *buf); + + - There are several common idiomatic names for functions performing + specific tasks on a structure `S`: + + - `S_init()` initializes a structure without allocating the + structure itself. + + - `S_release()` releases a structure's contents without freeing the + structure. + + - `S_clear()` is equivalent to `S_release()` followed by `S_init()` + such that the structure is directly usable after clearing it. When + `S_clear()` is provided, `S_init()` shall not allocate resources + that need to be released again. + + - `S_free()` releases a structure's contents and frees the + structure. + For Perl programs: - Most of the C guidelines above apply. diff --git a/Documentation/RelNotes/2.47.0.txt b/Documentation/RelNotes/2.47.0.txt index fc713b4696..b948fa4db4 100644 --- a/Documentation/RelNotes/2.47.0.txt +++ b/Documentation/RelNotes/2.47.0.txt @@ -9,6 +9,18 @@ UI, Workflows & Features * A few descriptions in "git show-ref -h" have been clarified. + * A 'P' command to "git add -p" that passes the patch hunk to the + pager has been added. + + * "git grep -W" omits blank lines that follow the found function at + the end of the file, just like it omits blank lines before the next + function. + + * The value of http.proxy can have "path" at the end for a socks + proxy that listens to a unix-domain socket, but we started to + discard it when we taught proxy auth code path to use the + credential helpers, which has been corrected. + Performance, Internal Implementation, Development Support etc. -------------------------------------------------------------- @@ -22,6 +34,23 @@ Performance, Internal Implementation, Development Support etc. * The reviewing guidelines document now explicitly encourages people to give positive reviews and how. + * Test script linter has been updated to catch an attempt to use + one-shot export construct "VAR=VAL func" for shell functions (which + does not work for some shells) better. + + * Some project conventions have been added to CodingGuidelines. + + * In the refs subsystem, implicit reliance of the_repository has been + eliminated; the repository associated with the ref store object is + used instead. + + * Various tests in reftable library have been rewritten using the unit test + framework. + + * A test that fails on an unusually slow machine was found, and made + less likely to cause trouble by lengthening the expiry value it + uses. + Fixes since v2.46 ----------------- @@ -46,7 +75,59 @@ Fixes since v2.46 incorrectly and called it "fuzz", which has been corrected. (merge 70058db385 jc/doc-rebase-fuzz-vs-offset-fix later to maint). + * "git notes add -m '' --allow-empty" and friends that take prepared + data to create notes should not invoke an editor, but it started + doing so since Git 2.42, which has been corrected. + (merge 8b426c84f3 dd/notes-empty-no-edit-by-default later to maint). + + * An expensive operation to prepare tracing was done in re-encoding + code path even when the tracing was not requested, which has been + corrected. + (merge 63ad8dbf16 dh/encoding-trace-optim later to maint). + + * More leakfixes. + (merge f30bfafcd4 ps/leakfixes-part-3 later to maint). + + * The credential helper to talk to OSX keychain sometimes sent + garbage bytes after the username, which has been corrected. + (merge b201316835 jk/osxkeychain-username-is-nul-terminated later to maint). + + * A recent update broke "git ls-remote" used outside a repository, + which has been corrected. + (merge 9e89dcb66a ps/ls-remote-out-of-repo-fix later to maint). + + * The patch parser in 'git apply' has been a bit more lenient against + unexpected mode bits, like 100664, recorded on extended header lines. + (merge e95d515141 jk/apply-patch-mode-check-fix later to maint). + + * "git config --value=foo --fixed-value section.key newvalue" barfed + when the existing value in the configuration file used the + valueless true syntax, which has been corrected. + (merge 615d2de3b4 tb/config-fixed-value-with-valueless-true later to maint). + + * The patch parser in "git patch-id" has been tightened to avoid + getting confused by lines that look like a patch header in the log + message. + (merge a6e9429f72 jc/patch-id later to maint). + + * "git reflog expire" failed to honor annotated tags when computing + reachable commits. + (merge 5133ead528 jc/reflog-expire-lookup-commit-fix later to maint). + + * A flakey test and incorrect calls to strtoX() functions have been + fixed. + (merge ec60bb9fc4 kl/test-fixes later to maint). + * Other code cleanup, docfix, build fix, etc. (merge 8db8786fc2 jt/doc-post-receive-hook-update later to maint). (merge 1c473dd6af tn/doc-commit-fix later to maint). (merge bb0498b1bb jc/how-to-maintain-updates later to maint). + (merge 6e71d6ac7c ks/unit-test-comment-typofix later to maint). + (merge 63ee933383 ps/p4-tests-updates later to maint). + (merge 7c7516b8db jc/jl-git-no-advice-fix later to maint). + (merge c3d034df16 jc/leakfix-hashfile later to maint). + (merge d98d9c77e5 jc/leakfix-mailmap later to maint). + (merge c199707496 jr/ls-files-expand-literal-doc later to maint). + (merge e2e373ba82 ss/packed-ref-store-leakfix later to maint). + (merge 0c4d5aa22d rs/use-decimal-width later to maint). + (merge 67be8c4de5 jc/document-use-of-local later to maint). diff --git a/Documentation/config/http.txt b/Documentation/config/http.txt index 162b33fc52..a14371b5c9 100644 --- a/Documentation/config/http.txt +++ b/Documentation/config/http.txt @@ -5,8 +5,8 @@ http.proxy:: proxy string with a user name but no password, in which case git will attempt to acquire one in the same way it does for other credentials. See linkgit:gitcredentials[7] for more information. The syntax thus is - '[protocol://][user[:password]@]proxyhost[:port]'. This can be overridden - on a per-remote basis; see remote.<name>.proxy + '[protocol://][user[:password]@]proxyhost[:port][/path]'. This can be + overridden on a per-remote basis; see remote.<name>.proxy + Any proxy, however configured, must be completely transparent and must not modify, transform, or buffer the request or response in any way. Proxies which diff --git a/Documentation/git-ls-files.txt b/Documentation/git-ls-files.txt index d08c7da8f4..58c529afbe 100644 --- a/Documentation/git-ls-files.txt +++ b/Documentation/git-ls-files.txt @@ -219,9 +219,9 @@ followed by the ("attr/<eolattr>"). --format=<format>:: A string that interpolates `%(fieldname)` from the result being shown. - It also interpolates `%%` to `%`, and `%xx` where `xx` are hex digits - interpolates to character with hex code `xx`; for example `%00` - interpolates to `\0` (NUL), `%09` to `\t` (TAB) and %0a to `\n` (LF). + It also interpolates `%%` to `%`, and `%xXX` where `XX` are hex digits + interpolates to character with hex code `XX`; for example `%x00` + interpolates to `\0` (NUL), `%x09` to `\t` (TAB) and %x0a to `\n` (LF). --format cannot be combined with `-s`, `-o`, `-k`, `-t`, `--resolve-undo` and `--eol`. \--:: @@ -1341,7 +1341,9 @@ UNIT_TEST_PROGRAMS += t-oidtree UNIT_TEST_PROGRAMS += t-prio-queue UNIT_TEST_PROGRAMS += t-reftable-basics UNIT_TEST_PROGRAMS += t-reftable-merged +UNIT_TEST_PROGRAMS += t-reftable-pq UNIT_TEST_PROGRAMS += t-reftable-record +UNIT_TEST_PROGRAMS += t-reftable-tree UNIT_TEST_PROGRAMS += t-strbuf UNIT_TEST_PROGRAMS += t-strcmp-offset UNIT_TEST_PROGRAMS += t-strvec @@ -2681,11 +2683,9 @@ REFTABLE_OBJS += reftable/writer.o REFTABLE_TEST_OBJS += reftable/block_test.o REFTABLE_TEST_OBJS += reftable/dump.o -REFTABLE_TEST_OBJS += reftable/pq_test.o REFTABLE_TEST_OBJS += reftable/readwrite_test.o REFTABLE_TEST_OBJS += reftable/stack_test.o REFTABLE_TEST_OBJS += reftable/test_framework.o -REFTABLE_TEST_OBJS += reftable/tree_test.o TEST_OBJS := $(patsubst %$X,%.o,$(TEST_PROGRAMS)) $(patsubst %,t/helper/%,$(TEST_BUILTINS_OBJS)) diff --git a/add-patch.c b/add-patch.c index 46f6bddfe5..1cec4ab67b 100644 --- a/add-patch.c +++ b/add-patch.c @@ -7,9 +7,11 @@ #include "environment.h" #include "gettext.h" #include "object-name.h" +#include "pager.h" #include "read-cache-ll.h" #include "repository.h" #include "strbuf.h" +#include "sigchain.h" #include "run-command.h" #include "strvec.h" #include "pathspec.h" @@ -1398,7 +1400,7 @@ N_("j - leave this hunk undecided, see next undecided hunk\n" "/ - search for a hunk matching the given regex\n" "s - split the current hunk into smaller hunks\n" "e - manually edit the current hunk\n" - "p - print the current hunk\n" + "p - print the current hunk, 'P' to use the pager\n" "? - print help\n"); static int patch_update_file(struct add_p_state *s, @@ -1409,7 +1411,7 @@ static int patch_update_file(struct add_p_state *s, struct hunk *hunk; char ch; struct child_process cp = CHILD_PROCESS_INIT; - int colored = !!s->colored.len, quit = 0; + int colored = !!s->colored.len, quit = 0, use_pager = 0; enum prompt_mode_type prompt_mode_type; enum { ALLOW_GOTO_PREVIOUS_HUNK = 1 << 0, @@ -1459,9 +1461,18 @@ static int patch_update_file(struct add_p_state *s, strbuf_reset(&s->buf); if (file_diff->hunk_nr) { if (rendered_hunk_index != hunk_index) { + if (use_pager) { + setup_pager(); + sigchain_push(SIGPIPE, SIG_IGN); + } render_hunk(s, hunk, 0, colored, &s->buf); fputs(s->buf.buf, stdout); rendered_hunk_index = hunk_index; + if (use_pager) { + sigchain_pop(SIGPIPE); + wait_for_pager(); + use_pager = 0; + } } strbuf_reset(&s->buf); @@ -1682,8 +1693,9 @@ soft_increment: hunk->use = USE_HUNK; goto soft_increment; } - } else if (s->answer.buf[0] == 'p') { + } else if (ch == 'p') { rendered_hunk_index = -1; + use_pager = (s->answer.buf[0] == 'P') ? 1 : 0; } else if (s->answer.buf[0] == '?') { const char *p = _(help_patch_remainder), *eol = p; @@ -995,6 +995,7 @@ static int parse_mode_line(const char *line, int linenr, unsigned int *mode) *mode = strtoul(line, &end, 8); if (end == line || !isspace(*end)) return error(_("invalid mode on line %d: %s"), linenr, line); + *mode = canon_mode(*mode); return 0; } diff --git a/builtin/credential-cache.c b/builtin/credential-cache.c index 3db8df70a9..aaf2f8438b 100644 --- a/builtin/credential-cache.c +++ b/builtin/credential-cache.c @@ -88,6 +88,8 @@ static void spawn_daemon(const char *socket) die_errno("unable to read result code from cache daemon"); if (r != 3 || memcmp(buf, "ok\n", 3)) die("cache daemon did not start: %.*s", r, buf); + + child_process_clear(&daemon); close(daemon.out); } @@ -137,7 +139,8 @@ static void announce_capabilities(void) int cmd_credential_cache(int argc, const char **argv, const char *prefix) { - char *socket_path = NULL; + const char *socket_path_arg = NULL; + char *socket_path; int timeout = 900; const char *op; const char * const usage[] = { @@ -147,7 +150,7 @@ int cmd_credential_cache(int argc, const char **argv, const char *prefix) struct option options[] = { OPT_INTEGER(0, "timeout", &timeout, "number of seconds to cache credentials"), - OPT_STRING(0, "socket", &socket_path, "path", + OPT_STRING(0, "socket", &socket_path_arg, "path", "path of cache-daemon socket"), OPT_END() }; @@ -160,6 +163,7 @@ int cmd_credential_cache(int argc, const char **argv, const char *prefix) if (!have_unix_sockets()) die(_("credential-cache unavailable; no unix socket support")); + socket_path = xstrdup_or_null(socket_path_arg); if (!socket_path) socket_path = get_socket_path(); if (!socket_path) @@ -176,6 +180,7 @@ int cmd_credential_cache(int argc, const char **argv, const char *prefix) else ; /* ignore unknown operation */ + free(socket_path); return 0; } diff --git a/builtin/credential-store.c b/builtin/credential-store.c index 494c809332..97968bfa1c 100644 --- a/builtin/credential-store.c +++ b/builtin/credential-store.c @@ -218,5 +218,6 @@ int cmd_credential_store(int argc, const char **argv, const char *prefix) ; /* Ignore unknown operation. */ string_list_clear(&fns, 0); + credential_clear(&c); return 0; } diff --git a/builtin/describe.c b/builtin/describe.c index cf8edc4222..954929c514 100644 --- a/builtin/describe.c +++ b/builtin/describe.c @@ -529,6 +529,7 @@ static void describe_blob(struct object_id oid, struct strbuf *dst) traverse_commit_list(&revs, process_commit, process_object, &pcd); reset_revision_walk(); release_revisions(&revs); + strvec_clear(&args); } static void describe(const char *arg, int last_one) @@ -619,6 +620,8 @@ int cmd_describe(int argc, const char **argv, const char *prefix) if (contains) { struct string_list_item *item; struct strvec args; + const char **argv_copy; + int ret; strvec_init(&args); strvec_pushl(&args, "name-rev", @@ -637,7 +640,21 @@ int cmd_describe(int argc, const char **argv, const char *prefix) strvec_pushv(&args, argv); else strvec_push(&args, "HEAD"); - return cmd_name_rev(args.nr, args.v, prefix); + + /* + * `cmd_name_rev()` modifies the array, so we'd leak its + * contained strings if we didn't do a copy here. + */ + ALLOC_ARRAY(argv_copy, args.nr + 1); + for (size_t i = 0; i < args.nr; i++) + argv_copy[i] = args.v[i]; + argv_copy[args.nr] = NULL; + + ret = cmd_name_rev(args.nr, argv_copy, prefix); + + strvec_clear(&args); + free(argv_copy); + return ret; } hashmap_init(&names, commit_name_neq, NULL, 0); @@ -679,7 +696,6 @@ int cmd_describe(int argc, const char **argv, const char *prefix) } else if (dirty) { struct lock_file index_lock = LOCK_INIT; struct rev_info revs; - struct strvec args = STRVEC_INIT; int fd; setup_work_tree(); @@ -694,8 +710,9 @@ int cmd_describe(int argc, const char **argv, const char *prefix) repo_update_index_if_able(the_repository, &index_lock); repo_init_revisions(the_repository, &revs, prefix); - strvec_pushv(&args, diff_index_args); - if (setup_revisions(args.nr, args.v, &revs, NULL) != 1) + + if (setup_revisions(ARRAY_SIZE(diff_index_args) - 1, + diff_index_args, &revs, NULL) != 1) BUG("malformed internal diff-index command line"); run_diff_index(&revs, 0); diff --git a/builtin/get-tar-commit-id.c b/builtin/get-tar-commit-id.c index 66a7389f9f..7195a072ed 100644 --- a/builtin/get-tar-commit-id.c +++ b/builtin/get-tar-commit-id.c @@ -35,6 +35,7 @@ int cmd_get_tar_commit_id(int argc, const char **argv UNUSED, const char *prefix if (header->typeflag[0] != TYPEFLAG_GLOBAL_HEADER) return 1; + errno = 0; len = strtol(content, &end, 10); if (errno == ERANGE || end == content || len < 0) return 1; diff --git a/builtin/log.c b/builtin/log.c index 4d4b60caa7..a73a767606 100644 --- a/builtin/log.c +++ b/builtin/log.c @@ -1434,6 +1434,7 @@ static void make_cover_letter(struct rev_info *rev, int use_separate_file, int need_8bit_cte = 0; struct pretty_print_context pp = {0}; struct commit *head = list[0]; + char *to_free = NULL; if (!cmit_fmt_is_mail(rev->commit_format)) die(_("cover letter needs email format")); @@ -1455,7 +1456,7 @@ static void make_cover_letter(struct rev_info *rev, int use_separate_file, } if (!branch_name) - branch_name = find_branch_name(rev); + branch_name = to_free = find_branch_name(rev); pp.fmt = CMIT_FMT_EMAIL; pp.date_mode.type = DATE_RFC2822; @@ -1466,6 +1467,7 @@ static void make_cover_letter(struct rev_info *rev, int use_separate_file, encoding, need_8bit_cte, cfg); fprintf(rev->diffopt.file, "%s\n", sb.buf); + free(to_free); free(pp.after_subject); strbuf_release(&sb); diff --git a/builtin/ls-remote.c b/builtin/ls-remote.c index debf2d4f88..0a491595ca 100644 --- a/builtin/ls-remote.c +++ b/builtin/ls-remote.c @@ -19,17 +19,16 @@ static const char * const ls_remote_usage[] = { * Is there one among the list of patterns that match the tail part * of the path? */ -static int tail_match(const char **pattern, const char *path) +static int tail_match(const struct strvec *pattern, const char *path) { - const char *p; char *pathbuf; - if (!pattern) + if (!pattern->nr) return 1; /* no restriction */ pathbuf = xstrfmt("/%s", path); - while ((p = *(pattern++)) != NULL) { - if (!wildmatch(p, pathbuf, 0)) { + for (size_t i = 0; i < pattern->nr; i++) { + if (!wildmatch(pattern->v[i], pathbuf, 0)) { free(pathbuf); return 1; } @@ -47,7 +46,7 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix) int status = 0; int show_symref_target = 0; const char *uploadpack = NULL; - const char **pattern = NULL; + struct strvec pattern = STRVEC_INIT; struct transport_ls_refs_options transport_options = TRANSPORT_LS_REFS_OPTIONS_INIT; int i; @@ -91,15 +90,25 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix) PARSE_OPT_STOP_AT_NON_OPTION); dest = argv[0]; + /* + * TODO: This is buggy, but required for transport helpers. When a + * transport helper advertises a "refspec", then we'd add that to a + * list of refspecs via `refspec_append()`, which transitively depends + * on `the_hash_algo`. Thus, when the hash algorithm isn't properly set + * up, this would lead to a segfault. + * + * We really should fix this in the transport helper logic such that we + * lazily parse refspec capabilities _after_ we have learned about the + * remote's object format. Otherwise, we may end up misparsing refspecs + * depending on what object hash the remote uses. + */ + if (!the_repository->hash_algo) + repo_set_hash_algo(the_repository, GIT_HASH_SHA1); + packet_trace_identity("ls-remote"); - if (argc > 1) { - int i; - CALLOC_ARRAY(pattern, argc); - for (i = 1; i < argc; i++) { - pattern[i - 1] = xstrfmt("*/%s", argv[i]); - } - } + for (int i = 1; i < argc; i++) + strvec_pushf(&pattern, "*/%s", argv[i]); if (flags & REF_TAGS) strvec_push(&transport_options.ref_prefixes, "refs/tags/"); @@ -136,7 +145,7 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix) struct ref_array_item *item; if (!check_ref_type(ref, flags)) continue; - if (!tail_match(pattern, ref->name)) + if (!tail_match(&pattern, ref->name)) continue; item = ref_array_push(&ref_array, ref->name, &ref->old_oid); item->symref = xstrdup_or_null(ref->symref); @@ -158,5 +167,7 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix) if (transport_disconnect(transport)) status = 1; transport_ls_refs_options_release(&transport_options); + + strvec_clear(&pattern); return status; } diff --git a/builtin/name-rev.c b/builtin/name-rev.c index 70e9ec4e47..f62c0a36cb 100644 --- a/builtin/name-rev.c +++ b/builtin/name-rev.c @@ -677,7 +677,9 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix) always, allow_undefined, data.name_only); } - UNLEAK(string_pool); - UNLEAK(revs); + string_list_clear(&data.ref_filters, 0); + string_list_clear(&data.exclude_filters, 0); + mem_pool_discard(&string_pool, 0); + object_array_clear(&revs); return 0; } diff --git a/builtin/notes.c b/builtin/notes.c index d9c356e354..4cc5bfedc3 100644 --- a/builtin/notes.c +++ b/builtin/notes.c @@ -114,7 +114,6 @@ struct note_msg { }; struct note_data { - int given; int use_editor; int stripspace; char *edit_path; @@ -193,7 +192,7 @@ static void write_commented_object(int fd, const struct object_id *object) static void prepare_note_data(const struct object_id *object, struct note_data *d, const struct object_id *old_note) { - if (d->use_editor || !d->given) { + if (d->use_editor || !d->msg_nr) { int fd; struct strbuf buf = STRBUF_INIT; @@ -201,7 +200,7 @@ static void prepare_note_data(const struct object_id *object, struct note_data * d->edit_path = git_pathdup("NOTES_EDITMSG"); fd = xopen(d->edit_path, O_CREAT | O_TRUNC | O_WRONLY, 0600); - if (d->given) + if (d->msg_nr) write_or_die(fd, d->buf.buf, d->buf.len); else if (old_note) copy_obj_to_fd(fd, old_note); @@ -515,7 +514,6 @@ static int add(int argc, const char **argv, const char *prefix) if (d.msg_nr) concat_messages(&d); - d.given = !!d.buf.len; object_ref = argc > 1 ? argv[1] : "HEAD"; @@ -528,7 +526,7 @@ static int add(int argc, const char **argv, const char *prefix) if (note) { if (!force) { free_notes(t); - if (d.given) { + if (d.msg_nr) { free_note_data(&d); return error(_("Cannot add notes. " "Found existing notes for object %s. " @@ -690,14 +688,14 @@ static int append_edit(int argc, const char **argv, const char *prefix) usage_with_options(usage, options); } - if (d.msg_nr) + if (d.msg_nr) { concat_messages(&d); - d.given = !!d.buf.len; - - if (d.given && edit) - fprintf(stderr, _("The -m/-F/-c/-C options have been deprecated " - "for the 'edit' subcommand.\n" - "Please use 'git notes add -f -m/-F/-c/-C' instead.\n")); + if (edit) + fprintf(stderr, _("The -m/-F/-c/-C options have been " + "deprecated for the 'edit' subcommand.\n" + "Please use 'git notes add -f -m/-F/-c/-C' " + "instead.\n")); + } object_ref = 1 < argc ? argv[1] : "HEAD"; diff --git a/builtin/patch-id.c b/builtin/patch-id.c index d790ae6354..35c1179f7e 100644 --- a/builtin/patch-id.c +++ b/builtin/patch-id.c @@ -7,10 +7,9 @@ #include "parse-options.h" #include "setup.h" -static void flush_current_id(int patchlen, struct object_id *id, struct object_id *result) +static void flush_current_id(struct object_id *id, struct object_id *result) { - if (patchlen) - printf("%s %s\n", oid_to_hex(result), oid_to_hex(id)); + printf("%s %s\n", oid_to_hex(result), oid_to_hex(id)); } static int remove_space(char *line) @@ -60,9 +59,27 @@ static int scan_hunk_header(const char *p, int *p_before, int *p_after) return 1; } +/* + * flag bits to control get_one_patchid()'s behaviour. + * + * STABLE/VERBATIM are given from the command line option as + * --stable/--verbatim. FIND_HEADER conveys the internal state + * maintained by the caller to allow the function to avoid mistaking + * lines of log message before seeing the "diff" part as the beginning + * of the next patch. + */ +enum { + GOPID_STABLE = (1<<0), /* --stable */ + GOPID_VERBATIM = (1<<1), /* --verbatim */ + GOPID_FIND_HEADER = (1<<2), /* stop at the beginning of patch message */ +}; + static int get_one_patchid(struct object_id *next_oid, struct object_id *result, - struct strbuf *line_buf, int stable, int verbatim) + struct strbuf *line_buf, unsigned flags) { + int stable = flags & GOPID_STABLE; + int verbatim = flags & GOPID_VERBATIM; + int find_header = flags & GOPID_FIND_HEADER; int patchlen = 0, found_next = 0; int before = -1, after = -1; int diff_is_binary = 0; @@ -77,24 +94,40 @@ static int get_one_patchid(struct object_id *next_oid, struct object_id *result, const char *p = line; int len; - /* Possibly skip over the prefix added by "log" or "format-patch" */ - if (!skip_prefix(line, "commit ", &p) && - !skip_prefix(line, "From ", &p) && - starts_with(line, "\\ ") && 12 < strlen(line)) { - if (verbatim) - the_hash_algo->update_fn(&ctx, line, strlen(line)); - continue; - } - - if (!get_oid_hex(p, next_oid)) { - found_next = 1; - break; + /* + * The caller hasn't seen us find a patch header and + * return to it, or we have started processing patch + * and may encounter the beginning of the next patch. + */ + if (find_header) { + /* + * If we see a line that begins with "<object name>", + * "commit <object name>" or "From <object name>", it is + * the beginning of a patch. Return to the caller, as + * we are done with the one we have been processing. + */ + if (skip_prefix(line, "commit ", &p)) + ; + else if (skip_prefix(line, "From ", &p)) + ; + if (!get_oid_hex(p, next_oid)) { + if (verbatim) + the_hash_algo->update_fn(&ctx, line, strlen(line)); + found_next = 1; + break; + } } /* Ignore commit comments */ if (!patchlen && !starts_with(line, "diff ")) continue; + /* + * We are past the commit log message. Prepare to + * stop at the beginning of the next patch header. + */ + find_header = 1; + /* Parsing diff header? */ if (before == -1) { if (starts_with(line, "GIT binary patch") || @@ -127,6 +160,16 @@ static int get_one_patchid(struct object_id *next_oid, struct object_id *result, break; } + /* + * A hunk about an incomplete line may have this + * marker at the end, which should just be ignored. + */ + if (starts_with(line, "\\ ") && 12 < strlen(line)) { + if (verbatim) + the_hash_algo->update_fn(&ctx, line, strlen(line)); + continue; + } + if (diff_is_binary) { if (starts_with(line, "diff ")) { diff_is_binary = 0; @@ -173,17 +216,20 @@ static int get_one_patchid(struct object_id *next_oid, struct object_id *result, return patchlen; } -static void generate_id_list(int stable, int verbatim) +static void generate_id_list(unsigned flags) { struct object_id oid, n, result; int patchlen; struct strbuf line_buf = STRBUF_INIT; oidclr(&oid, the_repository->hash_algo); + flags |= GOPID_FIND_HEADER; while (!feof(stdin)) { - patchlen = get_one_patchid(&n, &result, &line_buf, stable, verbatim); - flush_current_id(patchlen, &oid, &result); + patchlen = get_one_patchid(&n, &result, &line_buf, flags); + if (patchlen) + flush_current_id(&oid, &result); oidcpy(&oid, &n); + flags &= ~GOPID_FIND_HEADER; } strbuf_release(&line_buf); } @@ -219,6 +265,7 @@ int cmd_patch_id(int argc, const char **argv, const char *prefix) /* if nothing is set, default to unstable */ struct patch_id_opts config = {0, 0}; int opts = 0; + unsigned flags = 0; struct option builtin_patch_id_options[] = { OPT_CMDMODE(0, "unstable", &opts, N_("use the unstable patch-id algorithm"), 1), @@ -250,7 +297,11 @@ int cmd_patch_id(int argc, const char **argv, const char *prefix) if (!the_hash_algo) repo_set_hash_algo(the_repository, GIT_HASH_SHA1); - generate_id_list(opts ? opts > 1 : config.stable, - opts ? opts == 3 : config.verbatim); + if (opts ? opts > 1 : config.stable) + flags |= GOPID_STABLE; + if (opts ? opts == 3 : config.verbatim) + flags |= GOPID_VERBATIM; + generate_id_list(flags); + return 0; } diff --git a/builtin/remote.c b/builtin/remote.c index 08292498bd..9d54fddf8c 100644 --- a/builtin/remote.c +++ b/builtin/remote.c @@ -258,7 +258,7 @@ struct branch_info { char *push_remote_name; }; -static struct string_list branch_list = STRING_LIST_INIT_NODUP; +static struct string_list branch_list = STRING_LIST_INIT_DUP; static const char *abbrev_ref(const char *name, const char *prefix) { @@ -292,8 +292,8 @@ static int config_read_branches(const char *key, const char *value, type = PUSH_REMOTE; else return 0; - name = xmemdupz(key, key_len); + name = xmemdupz(key, key_len); item = string_list_insert(&branch_list, name); if (!item->util) @@ -337,6 +337,7 @@ static int config_read_branches(const char *key, const char *value, BUG("unexpected type=%d", type); } + free(name); return 0; } @@ -554,13 +555,16 @@ static int add_branch_for_removal(const char *refname, refspec.dst = (char *)refname; if (remote_find_tracking(branches->remote, &refspec)) return 0; + free(refspec.src); /* don't delete a branch if another remote also uses it */ for (kr = branches->keep->list; kr; kr = kr->next) { memset(&refspec, 0, sizeof(refspec)); refspec.dst = (char *)refname; - if (!remote_find_tracking(kr->remote, &refspec)) + if (!remote_find_tracking(kr->remote, &refspec)) { + free(refspec.src); return 0; + } } /* don't delete non-remote-tracking refs */ @@ -667,7 +671,11 @@ static int config_read_push_default(const char *key, const char *value, static void handle_push_default(const char* old_name, const char* new_name) { struct push_default_info push_default = { - old_name, CONFIG_SCOPE_UNKNOWN, STRBUF_INIT, -1 }; + .old_name = old_name, + .scope = CONFIG_SCOPE_UNKNOWN, + .origin = STRBUF_INIT, + .linenr = -1, + }; git_config(config_read_push_default, &push_default); if (push_default.scope >= CONFIG_SCOPE_COMMAND) ; /* pass */ @@ -687,6 +695,8 @@ static void handle_push_default(const char* old_name, const char* new_name) push_default.origin.buf, push_default.linenr, old_name); } + + strbuf_release(&push_default.origin); } @@ -784,7 +794,7 @@ static int mv(int argc, const char **argv, const char *prefix) } if (!refspec_updated) - return 0; + goto out; /* * First remove symrefs, then rename the rest, finally create @@ -850,10 +860,15 @@ static int mv(int argc, const char **argv, const char *prefix) display_progress(progress, ++refs_renamed_nr); } stop_progress(&progress); - string_list_clear(&remote_branches, 1); handle_push_default(rename.old_name, rename.new_name); +out: + string_list_clear(&remote_branches, 1); + strbuf_release(&old_remote_context); + strbuf_release(&buf); + strbuf_release(&buf2); + strbuf_release(&buf3); return 0; } @@ -944,12 +959,21 @@ static int rm(int argc, const char **argv, const char *prefix) if (!result) { strbuf_addf(&buf, "remote.%s", remote->name); - if (git_config_rename_section(buf.buf, NULL) < 1) - return error(_("Could not remove config section '%s'"), buf.buf); + if (git_config_rename_section(buf.buf, NULL) < 1) { + result = error(_("Could not remove config section '%s'"), buf.buf); + goto out; + } handle_push_default(remote->name, NULL); } +out: + for (struct known_remote *r = known_remotes.list; r;) { + struct known_remote *next = r->next; + free(r); + r = next; + } + strbuf_release(&buf); return result; } @@ -982,8 +1006,10 @@ static int append_ref_to_tracked_list(const char *refname, memset(&refspec, 0, sizeof(refspec)); refspec.dst = (char *)refname; - if (!remote_find_tracking(states->remote, &refspec)) + if (!remote_find_tracking(states->remote, &refspec)) { string_list_append(&states->tracked, abbrev_branch(refspec.src)); + free(refspec.src); + } return 0; } diff --git a/builtin/replay.c b/builtin/replay.c index 9acf51c32b..138198ce9c 100644 --- a/builtin/replay.c +++ b/builtin/replay.c @@ -151,7 +151,7 @@ static void get_ref_information(struct rev_cmdline_info *cmd_info, static void determine_replay_mode(struct rev_cmdline_info *cmd_info, const char *onto_name, - const char **advance_name, + char **advance_name, struct commit **onto, struct strset **update_refs) { @@ -174,6 +174,7 @@ static void determine_replay_mode(struct rev_cmdline_info *cmd_info, *onto = peel_committish(*advance_name); if (repo_dwim_ref(the_repository, *advance_name, strlen(*advance_name), &oid, &fullname, 0) == 1) { + free(*advance_name); *advance_name = fullname; } else { die(_("argument to --advance must be a reference")); @@ -197,6 +198,7 @@ static void determine_replay_mode(struct rev_cmdline_info *cmd_info, if (negative_refs_complete) { struct hashmap_iter iter; struct strmap_entry *entry; + const char *last_key = NULL; if (rinfo.negative_refexprs == 0) die(_("all positive revisions given must be references")); @@ -208,8 +210,11 @@ static void determine_replay_mode(struct rev_cmdline_info *cmd_info, /* Only one entry, but we have to loop to get it */ strset_for_each_entry(&rinfo.negative_refs, &iter, entry) { - *advance_name = entry->key; + last_key = entry->key; } + + free(*advance_name); + *advance_name = xstrdup_or_null(last_key); } else { /* positive_refs_complete */ if (rinfo.negative_refexprs > 1) die(_("cannot implicitly determine correct base for --onto")); @@ -271,7 +276,8 @@ static struct commit *pick_regular_commit(struct commit *pickme, int cmd_replay(int argc, const char **argv, const char *prefix) { - const char *advance_name = NULL; + const char *advance_name_opt = NULL; + char *advance_name = NULL; struct commit *onto = NULL; const char *onto_name = NULL; int contained = 0; @@ -292,7 +298,7 @@ int cmd_replay(int argc, const char **argv, const char *prefix) NULL }; struct option replay_options[] = { - OPT_STRING(0, "advance", &advance_name, + OPT_STRING(0, "advance", &advance_name_opt, N_("branch"), N_("make replay advance given branch")), OPT_STRING(0, "onto", &onto_name, @@ -306,14 +312,15 @@ int cmd_replay(int argc, const char **argv, const char *prefix) argc = parse_options(argc, argv, prefix, replay_options, replay_usage, PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN_OPT); - if (!onto_name && !advance_name) { + if (!onto_name && !advance_name_opt) { error(_("option --onto or --advance is mandatory")); usage_with_options(replay_usage, replay_options); } - if (advance_name && contained) + if (advance_name_opt && contained) die(_("options '%s' and '%s' cannot be used together"), "--advance", "--contained"); + advance_name = xstrdup_or_null(advance_name_opt); repo_init_revisions(the_repository, &revs, prefix); @@ -441,6 +448,7 @@ int cmd_replay(int argc, const char **argv, const char *prefix) cleanup: release_revisions(&revs); + free(advance_name); /* Return */ if (ret < 0) diff --git a/builtin/rerere.c b/builtin/rerere.c index b2efc6f640..81b65ffa39 100644 --- a/builtin/rerere.c +++ b/builtin/rerere.c @@ -73,11 +73,17 @@ int cmd_rerere(int argc, const char **argv, const char *prefix) if (!strcmp(argv[0], "forget")) { struct pathspec pathspec; + int ret; + if (argc < 2) warning(_("'git rerere forget' without paths is deprecated")); parse_pathspec(&pathspec, 0, PATHSPEC_PREFER_CWD, prefix, argv + 1); - return rerere_forget(the_repository, &pathspec); + + ret = rerere_forget(the_repository, &pathspec); + + clear_pathspec(&pathspec); + return ret; } if (!strcmp(argv[0], "clear")) { diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c index 2e64f5bda7..5845d3f59b 100644 --- a/builtin/rev-parse.c +++ b/builtin/rev-parse.c @@ -553,7 +553,10 @@ static int cmd_parseopt(int argc, const char **argv, const char *prefix) strbuf_release(&sb); strvec_clear(&longnames); strvec_clear(&usage); - free((char *) opts->help); + for (size_t i = 0; i < opts_nr; i++) { + free((char *) opts[i].help); + free((char *) opts[i].argh); + } free(opts); return 0; } diff --git a/builtin/shortlog.c b/builtin/shortlog.c index 5bde7c68c2..b529608c92 100644 --- a/builtin/shortlog.c +++ b/builtin/shortlog.c @@ -514,4 +514,5 @@ void shortlog_output(struct shortlog *log) string_list_clear(&log->list, 1); clear_mailmap(&log->mailmap); string_list_clear(&log->format, 0); + string_list_clear(&log->trailers, 0); } diff --git a/builtin/show-branch.c b/builtin/show-branch.c index d72f4cb98d..7d797a880c 100644 --- a/builtin/show-branch.c +++ b/builtin/show-branch.c @@ -502,14 +502,14 @@ static int rev_is_head(const char *head, const char *name) return !strcmp(head, name); } -static int show_merge_base(struct commit_list *seen, int num_rev) +static int show_merge_base(const struct commit_list *seen, int num_rev) { int all_mask = ((1u << (REV_SHIFT + num_rev)) - 1); int all_revs = all_mask & ~((1u << REV_SHIFT) - 1); int exit_status = 1; - while (seen) { - struct commit *commit = pop_commit(&seen); + for (const struct commit_list *s = seen; s; s = s->next) { + struct commit *commit = s->item; int flags = commit->object.flags & all_mask; if (!(flags & UNINTERESTING) && ((flags & all_revs) == all_revs)) { @@ -635,7 +635,7 @@ static int parse_reflog_param(const struct option *opt, const char *arg, int cmd_show_branch(int ac, const char **av, const char *prefix) { struct commit *rev[MAX_REVS], *commit; - char *reflog_msg[MAX_REVS]; + char *reflog_msg[MAX_REVS] = {0}; struct commit_list *list = NULL, *seen = NULL; unsigned int rev_mask[MAX_REVS]; int num_rev, i, extra = 0; @@ -692,6 +692,8 @@ int cmd_show_branch(int ac, const char **av, const char *prefix) parse_reflog_param), OPT_END() }; + const char **args_copy = NULL; + int ret; init_commit_name_slab(&name_slab); @@ -699,8 +701,9 @@ int cmd_show_branch(int ac, const char **av, const char *prefix) /* If nothing is specified, try the default first */ if (ac == 1 && default_args.nr) { + DUP_ARRAY(args_copy, default_args.v, default_args.nr); ac = default_args.nr; - av = default_args.v; + av = args_copy; } ac = parse_options(ac, av, prefix, builtin_show_branch_options, @@ -780,7 +783,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix) } for (i = 0; i < reflog; i++) { - char *logmsg; + char *logmsg = NULL; char *nth_desc; const char *msg; char *end; @@ -790,6 +793,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix) if (read_ref_at(get_main_ref_store(the_repository), ref, flags, 0, base + i, &oid, &logmsg, ×tamp, &tz, NULL)) { + free(logmsg); reflog = i; break; } @@ -842,7 +846,8 @@ int cmd_show_branch(int ac, const char **av, const char *prefix) if (!ref_name_cnt) { fprintf(stderr, "No revs to be shown.\n"); - exit(0); + ret = 0; + goto out; } for (num_rev = 0; ref_name[num_rev]; num_rev++) { @@ -879,11 +884,15 @@ int cmd_show_branch(int ac, const char **av, const char *prefix) commit_list_sort_by_date(&seen); - if (merge_base) - return show_merge_base(seen, num_rev); + if (merge_base) { + ret = show_merge_base(seen, num_rev); + goto out; + } - if (independent) - return show_independent(rev, num_rev, rev_mask); + if (independent) { + ret = show_independent(rev, num_rev, rev_mask); + goto out; + } /* Show list; --more=-1 means list-only */ if (1 < num_rev || extra < 0) { @@ -919,8 +928,10 @@ int cmd_show_branch(int ac, const char **av, const char *prefix) putchar('\n'); } } - if (extra < 0) - exit(0); + if (extra < 0) { + ret = 0; + goto out; + } /* Sort topologically */ sort_in_topological_order(&seen, sort_order); @@ -932,8 +943,8 @@ int cmd_show_branch(int ac, const char **av, const char *prefix) all_mask = ((1u << (REV_SHIFT + num_rev)) - 1); all_revs = all_mask & ~((1u << REV_SHIFT) - 1); - while (seen) { - struct commit *commit = pop_commit(&seen); + for (struct commit_list *l = seen; l; l = l->next) { + struct commit *commit = l->item; int this_flag = commit->object.flags; int is_merge_point = ((this_flag & all_revs) == all_revs); @@ -973,6 +984,15 @@ int cmd_show_branch(int ac, const char **av, const char *prefix) if (shown_merge_point && --extra < 0) break; } + + ret = 0; + +out: + for (size_t i = 0; i < ARRAY_SIZE(reflog_msg); i++) + free(reflog_msg[i]); + free_commit_list(seen); + free_commit_list(list); + free(args_copy); free(head); - return 0; + return ret; } diff --git a/builtin/stash.c b/builtin/stash.c index fb75ca219e..d90e072ddc 100644 --- a/builtin/stash.c +++ b/builtin/stash.c @@ -1521,6 +1521,7 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q struct strbuf patch = STRBUF_INIT; struct strbuf stash_msg_buf = STRBUF_INIT; struct strbuf untracked_files = STRBUF_INIT; + struct strbuf out = STRBUF_INIT; if (patch_mode && keep_index == -1) keep_index = 1; @@ -1626,7 +1627,6 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q struct child_process cp_add = CHILD_PROCESS_INIT; struct child_process cp_diff = CHILD_PROCESS_INIT; struct child_process cp_apply = CHILD_PROCESS_INIT; - struct strbuf out = STRBUF_INIT; cp_add.git_cmd = 1; strvec_push(&cp_add.args, "add"); @@ -1718,6 +1718,7 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q done: strbuf_release(&patch); + strbuf_release(&out); free_stash_info(&info); strbuf_release(&stash_msg_buf); strbuf_release(&untracked_files); @@ -1869,6 +1870,8 @@ int cmd_stash(int argc, const char **argv, const char *prefix) OPT_SUBCOMMAND_F("save", &fn, save_stash, PARSE_OPT_NOCOMPLETE), OPT_END() }; + const char **args_copy; + int ret; git_config(git_stash_config, NULL); @@ -1892,5 +1895,16 @@ int cmd_stash(int argc, const char **argv, const char *prefix) /* Assume 'stash push' */ strvec_push(&args, "push"); strvec_pushv(&args, argv); - return !!push_stash(args.nr, args.v, prefix, 1); + + /* + * `push_stash()` ends up modifying the array, which causes memory + * leaks if we didn't copy the array here. + */ + DUP_ARRAY(args_copy, args.v, args.nr); + + ret = !!push_stash(args.nr, args_copy, prefix, 1); + + strvec_clear(&args); + free(args_copy); + return ret; } diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index f1218a1995..673810d2c0 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -1530,7 +1530,7 @@ struct module_clone_data { const char *path; const char *name; const char *url; - const char *depth; + int depth; struct list_objects_filter_options *filter_options; unsigned int quiet: 1; unsigned int progress: 1; @@ -1729,8 +1729,8 @@ static int clone_submodule(const struct module_clone_data *clone_data, strvec_push(&cp.args, "--quiet"); if (clone_data->progress) strvec_push(&cp.args, "--progress"); - if (clone_data->depth && *(clone_data->depth)) - strvec_pushl(&cp.args, "--depth", clone_data->depth, NULL); + if (clone_data->depth > 0) + strvec_pushf(&cp.args, "--depth=%d", clone_data->depth); if (reference->nr) { struct string_list_item *item; @@ -1851,8 +1851,7 @@ static int module_clone(int argc, const char **argv, const char *prefix) N_("reference repository")), OPT_BOOL(0, "dissociate", &dissociate, N_("use --reference only while cloning")), - OPT_STRING(0, "depth", &clone_data.depth, - N_("string"), + OPT_INTEGER(0, "depth", &clone_data.depth, N_("depth for shallow clones")), OPT__QUIET(&quiet, "suppress output for cloning a submodule"), OPT_BOOL(0, "progress", &progress, @@ -2269,6 +2268,7 @@ static int is_tip_reachable(const char *path, const struct object_id *oid) struct child_process cp = CHILD_PROCESS_INIT; struct strbuf rev = STRBUF_INIT; char *hex = oid_to_hex(oid); + int reachable; cp.git_cmd = 1; cp.dir = path; @@ -2278,9 +2278,12 @@ static int is_tip_reachable(const char *path, const struct object_id *oid) prepare_submodule_repo_env(&cp.env); if (capture_command(&cp, &rev, GIT_MAX_HEXSZ + 1) || rev.len) - return 0; + reachable = 0; + else + reachable = 1; - return 1; + strbuf_release(&rev); + return reachable; } static int fetch_in_submodule(const char *module_path, int depth, int quiet, @@ -3200,7 +3203,7 @@ static int add_submodule(const struct add_data *add_data) } clone_data.dissociate = add_data->dissociate; if (add_data->depth >= 0) - clone_data.depth = xstrfmt("%d", add_data->depth); + clone_data.depth = add_data->depth; if (clone_submodule(&clone_data, &reference)) goto cleanup; @@ -3223,6 +3226,7 @@ static int add_submodule(const struct add_data *add_data) die(_("unable to checkout submodule '%s'"), add_data->sm_path); } ret = 0; + cleanup: string_list_clear(&reference, 1); return ret; diff --git a/builtin/worktree.c b/builtin/worktree.c index 1d51e54fcd..cec3ada6b0 100644 --- a/builtin/worktree.c +++ b/builtin/worktree.c @@ -769,7 +769,7 @@ static int add(int ac, const char **av, const char *prefix) char *branch_to_free = NULL; char *new_branch_to_free = NULL; const char *new_branch = NULL; - const char *opt_track = NULL; + char *opt_track = NULL; const char *lock_reason = NULL; int keep_locked = 0; int used_new_branch_options; @@ -846,7 +846,7 @@ static int add(int ac, const char **av, const char *prefix) if (opts.orphan && !new_branch) { int n; const char *s = worktree_basename(path, &n); - new_branch = xstrndup(s, n); + new_branch = new_branch_to_free = xstrndup(s, n); } else if (opts.orphan) { ; /* no-op */ } else if (opts.detach) { @@ -875,7 +875,7 @@ static int add(int ac, const char **av, const char *prefix) remote = unique_tracking_name(branch, &oid, NULL); if (remote) { new_branch = branch; - branch = remote; + branch = new_branch_to_free = remote; } } @@ -923,6 +923,7 @@ static int add(int ac, const char **av, const char *prefix) ret = add_worktree(path, branch, &opts); free(path); + free(opt_track); free(branch_to_free); free(new_branch_to_free); return ret; diff --git a/ci/install-dependencies.sh b/ci/install-dependencies.sh index fb34ced8f0..4781cd20bb 100755 --- a/ci/install-dependencies.sh +++ b/ci/install-dependencies.sh @@ -7,7 +7,7 @@ begin_group "Install dependencies" -P4WHENCE=https://cdist2.perforce.com/perforce/r21.2 +P4WHENCE=https://cdist2.perforce.com/perforce/r23.2 LFSWHENCE=https://github.com/github/git-lfs/releases/download/v$LINUX_GIT_LFS_VERSION JGITWHENCE=https://repo.eclipse.org/content/groups/releases//org/eclipse/jgit/org.eclipse.jgit.pgm/6.8.0.202311291450-r/org.eclipse.jgit.pgm-6.8.0.202311291450-r.sh diff --git a/commit-reach.c b/commit-reach.c index dabc2972e4..02f8218b8e 100644 --- a/commit-reach.c +++ b/commit-reach.c @@ -1227,4 +1227,5 @@ void tips_reachable_from_bases(struct repository *r, done: free(commits); repo_clear_commit_marks(r, SEEN); + free_commit_list(stack); } @@ -2914,7 +2914,7 @@ static int matches(const char *key, const char *value, { if (strcmp(key, store->key)) return 0; /* not ours */ - if (store->fixed_value) + if (store->fixed_value && value) return !strcmp(store->fixed_value, value); if (!store->value_pattern) return 1; /* always matches */ diff --git a/contrib/credential/osxkeychain/git-credential-osxkeychain.c b/contrib/credential/osxkeychain/git-credential-osxkeychain.c index 6ce22a28ed..1c8310d7fe 100644 --- a/contrib/credential/osxkeychain/git-credential-osxkeychain.c +++ b/contrib/credential/osxkeychain/git-credential-osxkeychain.c @@ -141,7 +141,7 @@ static void find_username_in_item(CFDictionaryRef item) username_buf, buffer_len, ENCODING)) { - write_item("username", username_buf, buffer_len - 1); + write_item("username", username_buf, strlen(username_buf)); } free(username_buf); } @@ -324,6 +324,9 @@ static void trace_encoding(const char *context, const char *path, struct strbuf trace = STRBUF_INIT; int i; + if (!trace_want(&coe)) + return; + strbuf_addf(&trace, "%s (%s, considered %s):\n", context, path, encoding); for (i = 0; i < len && buf; ++i) { strbuf_addf( @@ -960,7 +963,7 @@ int async_query_available_blobs(const char *cmd, struct string_list *available_p while ((line = packet_read_line(process->out, NULL))) { const char *path; if (skip_prefix(line, "pathname=", &path)) - string_list_insert(available_paths, xstrdup(path)); + string_list_insert(available_paths, path); else ; /* ignore unknown keys */ } @@ -1050,14 +1053,20 @@ static int read_convert_config(const char *var, const char *value, * The command-line will not be interpolated in any way. */ - if (!strcmp("smudge", key)) + if (!strcmp("smudge", key)) { + FREE_AND_NULL(drv->smudge); return git_config_string(&drv->smudge, var, value); + } - if (!strcmp("clean", key)) + if (!strcmp("clean", key)) { + FREE_AND_NULL(drv->clean); return git_config_string(&drv->clean, var, value); + } - if (!strcmp("process", key)) + if (!strcmp("process", key)) { + FREE_AND_NULL(drv->process); return git_config_string(&drv->process, var, value); + } if (!strcmp("required", key)) { drv->required = git_config_bool(var, value); diff --git a/csum-file.c b/csum-file.c index 8abbf01325..2131ee6b12 100644 --- a/csum-file.c +++ b/csum-file.c @@ -102,6 +102,15 @@ int finalize_hashfile(struct hashfile *f, unsigned char *result, return fd; } +void discard_hashfile(struct hashfile *f) +{ + if (0 <= f->check_fd) + close(f->check_fd); + if (0 <= f->fd) + close(f->fd); + free_hashfile(f); +} + void hashwrite(struct hashfile *f, const void *buf, unsigned int count) { while (count) { diff --git a/csum-file.h b/csum-file.h index 566e05cbd2..36c7c5585f 100644 --- a/csum-file.h +++ b/csum-file.h @@ -47,6 +47,7 @@ struct hashfile *hashfd(int fd, const char *name); struct hashfile *hashfd_check(const char *name); struct hashfile *hashfd_throughput(int fd, const char *name, struct progress *tp); int finalize_hashfile(struct hashfile *, unsigned char *, enum fsync_component, unsigned int); +void discard_hashfile(struct hashfile *); void hashwrite(struct hashfile *, const void *, unsigned int); void hashflush(struct hashfile *f); void crc32_begin(struct hashfile *); @@ -191,7 +191,7 @@ int finish_delayed_checkout(struct checkout *state, int show_progress) progress = start_delayed_progress(_("Filtering content"), dco->paths.nr); while (dco->filters.nr > 0) { for_each_string_list_item(filter, &dco->filters) { - struct string_list available_paths = STRING_LIST_INIT_NODUP; + struct string_list available_paths = STRING_LIST_INIT_DUP; if (!async_query_available_blobs(filter->string, &available_paths)) { /* Filter reported an error */ @@ -245,6 +245,8 @@ int finish_delayed_checkout(struct checkout *state, int show_progress) } else errs = 1; } + + string_list_clear(&available_paths, 0); } filter_string_list(&dco->filters, 0, string_is_not_null, NULL); @@ -1735,7 +1735,8 @@ static int grep_source_1(struct grep_opt *opt, struct grep_source *gs, int colle peek_eol = end_of_line(peek_bol, &peek_left); } - if (match_funcname(opt, gs, peek_bol, peek_eol)) + if (peek_bol >= gs->buf + gs->size || + match_funcname(opt, gs, peek_bol, peek_eol)) show_function = 0; } if (show_function || @@ -1227,6 +1227,8 @@ static CURL *get_curl_handle(void) */ curl_easy_setopt(result, CURLOPT_PROXY, ""); } else if (curl_http_proxy) { + struct strbuf proxy = STRBUF_INIT; + if (starts_with(curl_http_proxy, "socks5h")) curl_easy_setopt(result, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5_HOSTNAME); @@ -1265,7 +1267,27 @@ static CURL *get_curl_handle(void) if (!proxy_auth.host) die("Invalid proxy URL '%s'", curl_http_proxy); - curl_easy_setopt(result, CURLOPT_PROXY, proxy_auth.host); + strbuf_addstr(&proxy, proxy_auth.host); + if (proxy_auth.path) { + curl_version_info_data *ver = curl_version_info(CURLVERSION_NOW); + + if (ver->version_num < 0x075400) + die("libcurl 7.84 or later is required to support paths in proxy URLs"); + + if (!starts_with(proxy_auth.protocol, "socks")) + die("Invalid proxy URL '%s': only SOCKS proxies support paths", + curl_http_proxy); + + if (strcasecmp(proxy_auth.host, "localhost")) + die("Invalid proxy URL '%s': host must be localhost if a path is present", + curl_http_proxy); + + strbuf_addch(&proxy, '/'); + strbuf_add_percentencode(&proxy, proxy_auth.path, 0); + } + curl_easy_setopt(result, CURLOPT_PROXY, proxy.buf); + strbuf_release(&proxy); + var_override(&curl_no_proxy, getenv("NO_PROXY")); var_override(&curl_no_proxy, getenv("no_proxy")); curl_easy_setopt(result, CURLOPT_NOPROXY, curl_no_proxy); diff --git a/log-tree.c b/log-tree.c index 576ef30d90..fdb24cbef2 100644 --- a/log-tree.c +++ b/log-tree.c @@ -31,6 +31,7 @@ #include "tree.h" #include "wildmatch.h" #include "write-or-die.h" +#include "pager.h" static struct decoration name_decoration = { "object names" }; static int decoration_loaded; @@ -411,16 +412,6 @@ void show_decorations(struct rev_info *opt, struct commit *commit) strbuf_release(&sb); } -static unsigned int digits_in_number(unsigned int number) -{ - unsigned int i = 10, result = 1; - while (i <= number) { - i *= 10; - result++; - } - return result; -} - void fmt_output_subject(struct strbuf *filename, const char *subject, struct rev_info *info) @@ -464,7 +455,7 @@ void fmt_output_email_subject(struct strbuf *sb, struct rev_info *opt) strbuf_addf(sb, "Subject: [%s%s%0*d/%d] ", opt->subject_prefix, *opt->subject_prefix ? " " : "", - digits_in_number(opt->total), + decimal_width(opt->total), opt->nr, opt->total); } else if (opt->total == 0 && opt->subject_prefix && *opt->subject_prefix) { strbuf_addf(sb, "Subject: [%s] ", @@ -201,8 +201,10 @@ static int read_mailmap_blob(struct string_list *map, const char *name) buf = repo_read_object_file(the_repository, &oid, &type, &size); if (!buf) return error("unable to read mailmap object at %s", name); - if (type != OBJ_BLOB) + if (type != OBJ_BLOB) { + free(buf); return error("mailmap is not a blob: %s", name); + } read_mailmap_string(map, buf); diff --git a/object-name.c b/object-name.c index 527b853ac4..240a93e7ce 100644 --- a/object-name.c +++ b/object-name.c @@ -27,7 +27,8 @@ #include "date.h" #include "object-file-convert.h" -static int get_oid_oneline(struct repository *r, const char *, struct object_id *, struct commit_list *); +static int get_oid_oneline(struct repository *r, const char *, struct object_id *, + const struct commit_list *); typedef int (*disambiguate_hint_fn)(struct repository *, const struct object_id *, void *); @@ -1254,6 +1255,8 @@ static int peel_onion(struct repository *r, const char *name, int len, prefix = xstrndup(sp + 1, name + len - 1 - (sp + 1)); commit_list_insert((struct commit *)o, &list); ret = get_oid_oneline(r, prefix, oid, list); + + free_commit_list(list); free(prefix); return ret; } @@ -1388,9 +1391,10 @@ static int handle_one_ref(const char *path, const struct object_id *oid, static int get_oid_oneline(struct repository *r, const char *prefix, struct object_id *oid, - struct commit_list *list) + const struct commit_list *list) { - struct commit_list *backup = NULL, *l; + struct commit_list *copy = NULL; + const struct commit_list *l; int found = 0; int negative = 0; regex_t regex; @@ -1411,14 +1415,14 @@ static int get_oid_oneline(struct repository *r, for (l = list; l; l = l->next) { l->item->object.flags |= ONELINE_SEEN; - commit_list_insert(l->item, &backup); + commit_list_insert(l->item, ©); } - while (list) { + while (copy) { const char *p, *buf; struct commit *commit; int matches; - commit = pop_most_recent_commit(&list, ONELINE_SEEN); + commit = pop_most_recent_commit(©, ONELINE_SEEN); if (!parse_object(r, &commit->object.oid)) continue; buf = repo_get_commit_buffer(r, commit, NULL); @@ -1433,10 +1437,9 @@ static int get_oid_oneline(struct repository *r, } } regfree(®ex); - free_commit_list(list); - for (l = backup; l; l = l->next) + for (l = list; l; l = l->next) clear_commit_marks(l->item, ONELINE_SEEN); - free_commit_list(backup); + free_commit_list(copy); return found ? 0 : -1; } @@ -2024,7 +2027,10 @@ static enum get_oid_result get_oid_with_context_1(struct repository *repo, refs_for_each_ref(get_main_ref_store(repo), handle_one_ref, &cb); refs_head_ref(get_main_ref_store(repo), handle_one_ref, &cb); commit_list_sort_by_date(&list); - return get_oid_oneline(repo, name + 2, oid, list); + ret = get_oid_oneline(repo, name + 2, oid, list); + + free_commit_list(list); + return ret; } if (namelen < 3 || name[2] != ':' || @@ -14,6 +14,7 @@ int pager_use_color = 1; static struct child_process pager_process; static char *pager_program; +static int old_fd1 = -1, old_fd2 = -1; /* Is the value coming back from term_columns() just a guess? */ static int term_columns_guessed; @@ -23,10 +24,11 @@ static void close_pager_fds(void) { /* signal EOF to pager */ close(1); - close(2); + if (old_fd2 != -1) + close(2); } -static void wait_for_pager_atexit(void) +static void finish_pager(void) { fflush(stdout); fflush(stderr); @@ -34,8 +36,37 @@ static void wait_for_pager_atexit(void) finish_command(&pager_process); } +static void wait_for_pager_atexit(void) +{ + if (old_fd1 == -1) + return; + + finish_pager(); +} + +void wait_for_pager(void) +{ + if (old_fd1 == -1) + return; + + finish_pager(); + sigchain_pop_common(); + unsetenv("GIT_PAGER_IN_USE"); + dup2(old_fd1, 1); + close(old_fd1); + old_fd1 = -1; + if (old_fd2 != -1) { + dup2(old_fd2, 2); + close(old_fd2); + old_fd2 = -1; + } +} + static void wait_for_pager_signal(int signo) { + if (old_fd1 == -1) + return; + close_pager_fds(); finish_command_in_signal(&pager_process); sigchain_pop(signo); @@ -111,6 +142,7 @@ void prepare_pager_args(struct child_process *pager_process, const char *pager) void setup_pager(void) { + static int once = 0; const char *pager = git_pager(isatty(1)); if (!pager) @@ -140,14 +172,20 @@ void setup_pager(void) die("unable to execute pager '%s'", pager); /* original process continues, but writes to the pipe */ + old_fd1 = dup(1); dup2(pager_process.in, 1); - if (isatty(2)) + if (isatty(2)) { + old_fd2 = dup(2); dup2(pager_process.in, 2); + } close(pager_process.in); - /* this makes sure that the parent terminates after the pager */ sigchain_push_common(wait_for_pager_signal); - atexit(wait_for_pager_atexit); + + if (!once) { + once++; + atexit(wait_for_pager_atexit); + } } int pager_in_use(void) @@ -5,6 +5,7 @@ struct child_process; const char *git_pager(int stdout_is_tty); void setup_pager(void); +void wait_for_pager(void); int pager_in_use(void); int term_columns(void); void term_clear_line(void); diff --git a/read-cache.c b/read-cache.c index 48bf24f87c..1f67bb755b 100644 --- a/read-cache.c +++ b/read-cache.c @@ -2963,7 +2963,7 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile, if (err) { free(ieot); - return err; + goto cleanup; } offset = hashfile_total(f); @@ -2992,8 +2992,14 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile, hashwrite(f, sb.buf, sb.len); strbuf_release(&sb); free(ieot); - if (err) - return -1; + /* + * NEEDSWORK: write_index_ext_header() never returns a failure, + * and this part may want to be simplified. + */ + if (err) { + err = -1; + goto cleanup; + } } if (write_extensions & WRITE_SPLIT_INDEX_EXTENSION && @@ -3008,8 +3014,14 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile, sb.len) < 0; hashwrite(f, sb.buf, sb.len); strbuf_release(&sb); - if (err) - return -1; + /* + * NEEDSWORK: write_link_extension() never returns a failure, + * and this part may want to be simplified. + */ + if (err) { + err = -1; + goto cleanup; + } } if (write_extensions & WRITE_CACHE_TREE_EXTENSION && !drop_cache_tree && istate->cache_tree) { @@ -3019,8 +3031,14 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile, err = write_index_ext_header(f, eoie_c, CACHE_EXT_TREE, sb.len) < 0; hashwrite(f, sb.buf, sb.len); strbuf_release(&sb); - if (err) - return -1; + /* + * NEEDSWORK: write_index_ext_header() never returns a failure, + * and this part may want to be simplified. + */ + if (err) { + err = -1; + goto cleanup; + } } if (write_extensions & WRITE_RESOLVE_UNDO_EXTENSION && istate->resolve_undo) { @@ -3031,8 +3049,14 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile, sb.len) < 0; hashwrite(f, sb.buf, sb.len); strbuf_release(&sb); - if (err) - return -1; + /* + * NEEDSWORK: write_index_ext_header() never returns a failure, + * and this part may want to be simplified. + */ + if (err) { + err = -1; + goto cleanup; + } } if (write_extensions & WRITE_UNTRACKED_CACHE_EXTENSION && istate->untracked) { @@ -3043,8 +3067,14 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile, sb.len) < 0; hashwrite(f, sb.buf, sb.len); strbuf_release(&sb); - if (err) - return -1; + /* + * NEEDSWORK: write_index_ext_header() never returns a failure, + * and this part may want to be simplified. + */ + if (err) { + err = -1; + goto cleanup; + } } if (write_extensions & WRITE_FSMONITOR_EXTENSION && istate->fsmonitor_last_update) { @@ -3054,12 +3084,25 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile, err = write_index_ext_header(f, eoie_c, CACHE_EXT_FSMONITOR, sb.len) < 0; hashwrite(f, sb.buf, sb.len); strbuf_release(&sb); - if (err) - return -1; + /* + * NEEDSWORK: write_index_ext_header() never returns a failure, + * and this part may want to be simplified. + */ + if (err) { + err = -1; + goto cleanup; + } } if (istate->sparse_index) { - if (write_index_ext_header(f, eoie_c, CACHE_EXT_SPARSE_DIRECTORIES, 0) < 0) - return -1; + err = write_index_ext_header(f, eoie_c, CACHE_EXT_SPARSE_DIRECTORIES, 0); + /* + * NEEDSWORK: write_index_ext_header() never returns a failure, + * and this part may want to be simplified. + */ + if (err) { + err = -1; + goto cleanup; + } } /* @@ -3075,8 +3118,14 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile, err = write_index_ext_header(f, NULL, CACHE_EXT_ENDOFINDEXENTRIES, sb.len) < 0; hashwrite(f, sb.buf, sb.len); strbuf_release(&sb); - if (err) - return -1; + /* + * NEEDSWORK: write_index_ext_header() never returns a failure, + * and this part may want to be simplified. + */ + if (err) { + err = -1; + goto cleanup; + } } csum_fsync_flag = 0; @@ -3085,13 +3134,16 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile, finalize_hashfile(f, istate->oid.hash, FSYNC_COMPONENT_INDEX, CSUM_HASH_IN_STREAM | csum_fsync_flag); + f = NULL; if (close_tempfile_gently(tempfile)) { - error(_("could not close '%s'"), get_tempfile_path(tempfile)); - return -1; + err = error(_("could not close '%s'"), get_tempfile_path(tempfile)); + goto cleanup; + } + if (stat(get_tempfile_path(tempfile), &st)) { + err = error_errno(_("could not stat '%s'"), get_tempfile_path(tempfile)); + goto cleanup; } - if (stat(get_tempfile_path(tempfile), &st)) - return -1; istate->timestamp.sec = (unsigned int)st.st_mtime; istate->timestamp.nsec = ST_MTIME_NSEC(st); trace_performance_since(start, "write index, changed mask = %x", istate->cache_changed); @@ -3106,6 +3158,11 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile, istate->cache_nr); return 0; + +cleanup: + if (f) + discard_hashfile(f); + return err; } void set_alternate_index_output(const char *name) diff --git a/ref-filter.c b/ref-filter.c index 8c5e673fc0..54880a2497 100644 --- a/ref-filter.c +++ b/ref-filter.c @@ -1628,6 +1628,7 @@ static void grab_date(const char *buf, struct atom_value *v, const char *atomnam timestamp = parse_timestamp(eoemail + 2, &zone, 10); if (timestamp == TIME_MAX) goto bad; + errno = 0; tz = strtol(zone, NULL, 10); if ((tz == LONG_MIN || tz == LONG_MAX) && errno == ERANGE) goto bad; @@ -332,7 +332,8 @@ void reflog_expiry_prepare(const char *refname, if (!cb->cmd.expire_unreachable || is_head(refname)) { cb->unreachable_expire_kind = UE_HEAD; } else { - commit = lookup_commit(the_repository, oid); + commit = lookup_commit_reference_gently(the_repository, + oid, 1); if (commit && is_null_oid(&commit->object.oid)) commit = NULL; cb->unreachable_expire_kind = commit ? UE_NORMAL : UE_ALWAYS; @@ -2,8 +2,6 @@ * The backend-independent part of the reference module. */ -#define USE_THE_REPOSITORY_VARIABLE - #include "git-compat-util.h" #include "advice.h" #include "config.h" @@ -1754,8 +1752,8 @@ static int refs_read_special_head(struct ref_store *ref_store, goto done; } - result = parse_loose_ref_contents(content.buf, oid, referent, type, - failure_errno); + result = parse_loose_ref_contents(ref_store->repo->hash_algo, content.buf, + oid, referent, type, failure_errno); done: strbuf_release(&full_path); @@ -1838,7 +1836,7 @@ const char *refs_resolve_ref_unsafe(struct ref_store *refs, failure_errno != ENOTDIR) return NULL; - oidclr(oid, the_repository->hash_algo); + oidclr(oid, refs->repo->hash_algo); if (*flags & REF_BAD_NAME) *flags |= REF_ISBROKEN; return refname; @@ -1848,7 +1846,7 @@ const char *refs_resolve_ref_unsafe(struct ref_store *refs, if (!(read_flags & REF_ISSYMREF)) { if (*flags & REF_BAD_NAME) { - oidclr(oid, the_repository->hash_algo); + oidclr(oid, refs->repo->hash_algo); *flags |= REF_ISBROKEN; } return refname; @@ -1856,7 +1854,7 @@ const char *refs_resolve_ref_unsafe(struct ref_store *refs, refname = sb_refname.buf; if (resolve_flags & RESOLVE_REF_NO_RECURSE) { - oidclr(oid, the_repository->hash_algo); + oidclr(oid, refs->repo->hash_algo); return refname; } if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) { @@ -2011,7 +2009,7 @@ struct ref_store *repo_get_submodule_ref_store(struct repository *repo, free(subrepo); goto done; } - refs = ref_store_init(subrepo, the_repository->ref_storage_format, + refs = ref_store_init(subrepo, repo->ref_storage_format, submodule_sb.buf, REF_STORE_READ | REF_STORE_ODB); register_ref_store_map(&repo->submodule_ref_stores, "submodule", @@ -2045,7 +2043,7 @@ struct ref_store *get_worktree_ref_store(const struct worktree *wt) common_path.buf, REF_STORE_ALL_CAPS); strbuf_release(&common_path); } else { - refs = ref_store_init(wt->repo, the_repository->ref_storage_format, + refs = ref_store_init(wt->repo, wt->repo->ref_storage_format, wt->repo->commondir, REF_STORE_ALL_CAPS); } @@ -1086,211 +1086,4 @@ int repo_migrate_ref_storage_format(struct repository *repo, unsigned int flags, struct strbuf *err); -/* - * The following functions have been removed in Git v2.46 in favor of functions - * that receive a `ref_store` as parameter. The intent of this section is - * merely to help patch authors of in-flight series to have a reference what - * they should be migrating to. The section will be removed in Git v2.47. - */ -#if 0 -static char *resolve_refdup(const char *refname, int resolve_flags, - struct object_id *oid, int *flags) -{ - return refs_resolve_refdup(get_main_ref_store(the_repository), - refname, resolve_flags, - oid, flags); -} - -static int read_ref_full(const char *refname, int resolve_flags, - struct object_id *oid, int *flags) -{ - return refs_read_ref_full(get_main_ref_store(the_repository), refname, - resolve_flags, oid, flags); -} - -static int read_ref(const char *refname, struct object_id *oid) -{ - return refs_read_ref(get_main_ref_store(the_repository), refname, oid); -} - -static int ref_exists(const char *refname) -{ - return refs_ref_exists(get_main_ref_store(the_repository), refname); -} - -static int for_each_tag_ref(each_ref_fn fn, void *cb_data) -{ - return refs_for_each_tag_ref(get_main_ref_store(the_repository), fn, cb_data); -} - -static int for_each_branch_ref(each_ref_fn fn, void *cb_data) -{ - return refs_for_each_branch_ref(get_main_ref_store(the_repository), fn, cb_data); -} - -static int for_each_remote_ref(each_ref_fn fn, void *cb_data) -{ - return refs_for_each_remote_ref(get_main_ref_store(the_repository), fn, cb_data); -} - -static int head_ref_namespaced(each_ref_fn fn, void *cb_data) -{ - return refs_head_ref_namespaced(get_main_ref_store(the_repository), - fn, cb_data); -} - -static int for_each_glob_ref_in(each_ref_fn fn, const char *pattern, - const char *prefix, void *cb_data) -{ - return refs_for_each_glob_ref_in(get_main_ref_store(the_repository), - fn, pattern, prefix, cb_data); -} - -static int for_each_glob_ref(each_ref_fn fn, const char *pattern, void *cb_data) -{ - return refs_for_each_glob_ref(get_main_ref_store(the_repository), - fn, pattern, cb_data); -} - -static int delete_ref(const char *msg, const char *refname, - const struct object_id *old_oid, unsigned int flags) -{ - return refs_delete_ref(get_main_ref_store(the_repository), msg, refname, - old_oid, flags); -} - -static struct ref_transaction *ref_transaction_begin(struct strbuf *err) -{ - return ref_store_transaction_begin(get_main_ref_store(the_repository), err); -} - -static int update_ref(const char *msg, const char *refname, - const struct object_id *new_oid, - const struct object_id *old_oid, - unsigned int flags, enum action_on_err onerr) -{ - return refs_update_ref(get_main_ref_store(the_repository), msg, refname, new_oid, - old_oid, flags, onerr); -} - -static char *shorten_unambiguous_ref(const char *refname, int strict) -{ - return refs_shorten_unambiguous_ref(get_main_ref_store(the_repository), - refname, strict); -} - -static int head_ref(each_ref_fn fn, void *cb_data) -{ - return refs_head_ref(get_main_ref_store(the_repository), fn, cb_data); -} - -static int for_each_ref(each_ref_fn fn, void *cb_data) -{ - return refs_for_each_ref(get_main_ref_store(the_repository), fn, cb_data); -} - -static int for_each_ref_in(const char *prefix, each_ref_fn fn, void *cb_data) -{ - return refs_for_each_ref_in(get_main_ref_store(the_repository), prefix, fn, cb_data); -} - -static int for_each_fullref_in(const char *prefix, - const char **exclude_patterns, - each_ref_fn fn, void *cb_data) -{ - return refs_for_each_fullref_in(get_main_ref_store(the_repository), - prefix, exclude_patterns, fn, cb_data); -} - -static int for_each_namespaced_ref(const char **exclude_patterns, - each_ref_fn fn, void *cb_data) -{ - return refs_for_each_namespaced_ref(get_main_ref_store(the_repository), - exclude_patterns, fn, cb_data); -} - -static int for_each_rawref(each_ref_fn fn, void *cb_data) -{ - return refs_for_each_rawref(get_main_ref_store(the_repository), fn, cb_data); -} - -static const char *resolve_ref_unsafe(const char *refname, int resolve_flags, - struct object_id *oid, int *flags) -{ - return refs_resolve_ref_unsafe(get_main_ref_store(the_repository), refname, - resolve_flags, oid, flags); -} - -static int create_symref(const char *ref_target, const char *refs_heads_master, - const char *logmsg) -{ - return refs_create_symref(get_main_ref_store(the_repository), ref_target, - refs_heads_master, logmsg); -} - -static int for_each_reflog(each_reflog_fn fn, void *cb_data) -{ - return refs_for_each_reflog(get_main_ref_store(the_repository), fn, cb_data); -} - -static int for_each_reflog_ent_reverse(const char *refname, each_reflog_ent_fn fn, - void *cb_data) -{ - return refs_for_each_reflog_ent_reverse(get_main_ref_store(the_repository), - refname, fn, cb_data); -} - -static int for_each_reflog_ent(const char *refname, each_reflog_ent_fn fn, - void *cb_data) -{ - return refs_for_each_reflog_ent(get_main_ref_store(the_repository), refname, - fn, cb_data); -} - -static int reflog_exists(const char *refname) -{ - return refs_reflog_exists(get_main_ref_store(the_repository), refname); -} - -static int safe_create_reflog(const char *refname, struct strbuf *err) -{ - return refs_create_reflog(get_main_ref_store(the_repository), refname, - err); -} - -static int delete_reflog(const char *refname) -{ - return refs_delete_reflog(get_main_ref_store(the_repository), refname); -} - -static int reflog_expire(const char *refname, - unsigned int flags, - reflog_expiry_prepare_fn prepare_fn, - reflog_expiry_should_prune_fn should_prune_fn, - reflog_expiry_cleanup_fn cleanup_fn, - void *policy_cb_data) -{ - return refs_reflog_expire(get_main_ref_store(the_repository), - refname, flags, - prepare_fn, should_prune_fn, - cleanup_fn, policy_cb_data); -} - -static int delete_refs(const char *msg, struct string_list *refnames, - unsigned int flags) -{ - return refs_delete_refs(get_main_ref_store(the_repository), msg, refnames, flags); -} - -static int rename_ref(const char *oldref, const char *newref, const char *logmsg) -{ - return refs_rename_ref(get_main_ref_store(the_repository), oldref, newref, logmsg); -} - -static int copy_existing_ref(const char *oldref, const char *newref, const char *logmsg) -{ - return refs_copy_existing_ref(get_main_ref_store(the_repository), oldref, newref, logmsg); -} -#endif - #endif /* REFS_H */ diff --git a/refs/files-backend.c b/refs/files-backend.c index aa52d9be7c..6380dff443 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@ -1,5 +1,3 @@ -#define USE_THE_REPOSITORY_VARIABLE - #include "../git-compat-util.h" #include "../copy.h" #include "../environment.h" @@ -157,6 +155,7 @@ static void files_ref_store_release(struct ref_store *ref_store) free_ref_cache(refs->loose); free(refs->gitcommondir); ref_store_release(refs->packed_ref_store); + free(refs->packed_ref_store); } static void files_reflog_path(struct files_ref_store *refs, @@ -248,7 +247,7 @@ static void loose_fill_ref_dir_regular_file(struct files_ref_store *refs, if (!refs_resolve_ref_unsafe(&refs->base, refname, RESOLVE_REF_READING, &oid, &flag)) { - oidclr(&oid, the_repository->hash_algo); + oidclr(&oid, refs->base.repo->hash_algo); flag |= REF_ISBROKEN; } else if (is_null_oid(&oid)) { /* @@ -265,7 +264,7 @@ static void loose_fill_ref_dir_regular_file(struct files_ref_store *refs, if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) { if (!refname_is_safe(refname)) die("loose refname is dangerous: %s", refname); - oidclr(&oid, the_repository->hash_algo); + oidclr(&oid, refs->base.repo->hash_algo); flag |= REF_BAD_NAME | REF_ISBROKEN; } add_entry_to_dir(dir, create_ref_entry(refname, &oid, flag)); @@ -552,7 +551,8 @@ stat_ref: strbuf_rtrim(&sb_contents); buf = sb_contents.buf; - ret = parse_loose_ref_contents(buf, oid, referent, type, &myerr); + ret = parse_loose_ref_contents(ref_store->repo->hash_algo, buf, + oid, referent, type, &myerr); out: if (ret && !myerr) @@ -586,7 +586,8 @@ static int files_read_symbolic_ref(struct ref_store *ref_store, const char *refn return !(type & REF_ISSYMREF); } -int parse_loose_ref_contents(const char *buf, struct object_id *oid, +int parse_loose_ref_contents(const struct git_hash_algo *algop, + const char *buf, struct object_id *oid, struct strbuf *referent, unsigned int *type, int *failure_errno) { @@ -604,7 +605,7 @@ int parse_loose_ref_contents(const char *buf, struct object_id *oid, /* * FETCH_HEAD has additional data after the sha. */ - if (parse_oid_hex(buf, oid, &p) || + if (parse_oid_hex_algop(buf, oid, &p, algop) || (*p != '\0' && !isspace(*p))) { *type |= REF_ISBROKEN; *failure_errno = EINVAL; @@ -1152,7 +1153,7 @@ static struct ref_lock *lock_ref_oid_basic(struct files_ref_store *refs, if (!refs_resolve_ref_unsafe(&refs->base, lock->ref_name, 0, &lock->old_oid, NULL)) - oidclr(&lock->old_oid, the_repository->hash_algo); + oidclr(&lock->old_oid, refs->base.repo->hash_algo); goto out; error_return: @@ -1998,7 +1999,8 @@ static int files_delete_reflog(struct ref_store *ref_store, return ret; } -static int show_one_reflog_ent(struct strbuf *sb, each_reflog_ent_fn fn, void *cb_data) +static int show_one_reflog_ent(struct files_ref_store *refs, struct strbuf *sb, + each_reflog_ent_fn fn, void *cb_data) { struct object_id ooid, noid; char *email_end, *message; @@ -2008,8 +2010,8 @@ static int show_one_reflog_ent(struct strbuf *sb, each_reflog_ent_fn fn, void *c /* old SP new SP name <email> SP time TAB msg LF */ if (!sb->len || sb->buf[sb->len - 1] != '\n' || - parse_oid_hex(p, &ooid, &p) || *p++ != ' ' || - parse_oid_hex(p, &noid, &p) || *p++ != ' ' || + parse_oid_hex_algop(p, &ooid, &p, refs->base.repo->hash_algo) || *p++ != ' ' || + parse_oid_hex_algop(p, &noid, &p, refs->base.repo->hash_algo) || *p++ != ' ' || !(email_end = strchr(p, '>')) || email_end[1] != ' ' || !(timestamp = parse_timestamp(email_end + 2, &message, 10)) || @@ -2108,7 +2110,7 @@ static int files_for_each_reflog_ent_reverse(struct ref_store *ref_store, strbuf_splice(&sb, 0, 0, bp + 1, endp - (bp + 1)); scanp = bp; endp = bp + 1; - ret = show_one_reflog_ent(&sb, fn, cb_data); + ret = show_one_reflog_ent(refs, &sb, fn, cb_data); strbuf_reset(&sb); if (ret) break; @@ -2120,7 +2122,7 @@ static int files_for_each_reflog_ent_reverse(struct ref_store *ref_store, * Process it, and we can end the loop. */ strbuf_splice(&sb, 0, 0, buf, endp - buf); - ret = show_one_reflog_ent(&sb, fn, cb_data); + ret = show_one_reflog_ent(refs, &sb, fn, cb_data); strbuf_reset(&sb); break; } @@ -2170,7 +2172,7 @@ static int files_for_each_reflog_ent(struct ref_store *ref_store, return -1; while (!ret && !strbuf_getwholeline(&sb, logfp, '\n')) - ret = show_one_reflog_ent(&sb, fn, cb_data); + ret = show_one_reflog_ent(refs, &sb, fn, cb_data); fclose(logfp); strbuf_release(&sb); return ret; diff --git a/refs/packed-backend.c b/refs/packed-backend.c index a0666407cd..89976aa359 100644 --- a/refs/packed-backend.c +++ b/refs/packed-backend.c @@ -1,5 +1,3 @@ -#define USE_THE_REPOSITORY_VARIABLE - #include "../git-compat-util.h" #include "../config.h" #include "../dir.h" @@ -794,7 +792,7 @@ static int packed_read_raw_ref(struct ref_store *ref_store, const char *refname, return -1; } - if (get_oid_hex(rec, oid)) + if (get_oid_hex_algop(rec, oid, ref_store->repo->hash_algo)) die_invalid_line(refs->path, rec, snapshot->eof - rec); *type = REF_ISPACKED; @@ -879,7 +877,7 @@ static int next_record(struct packed_ref_iterator *iter) p = iter->pos; if (iter->eof - p < snapshot_hexsz(iter->snapshot) + 2 || - parse_oid_hex(p, &iter->oid, &p) || + parse_oid_hex_algop(p, &iter->oid, &p, iter->repo->hash_algo) || !isspace(*p++)) die_invalid_line(iter->snapshot->refs->path, iter->pos, iter->eof - iter->pos); @@ -896,7 +894,7 @@ static int next_record(struct packed_ref_iterator *iter) if (!refname_is_safe(iter->base.refname)) die("packed refname is dangerous: %s", iter->base.refname); - oidclr(&iter->oid, the_repository->hash_algo); + oidclr(&iter->oid, iter->repo->hash_algo); iter->base.flags |= REF_BAD_NAME | REF_ISBROKEN; } if (iter->snapshot->peeled == PEELED_FULLY || @@ -909,7 +907,7 @@ static int next_record(struct packed_ref_iterator *iter) if (iter->pos < iter->eof && *iter->pos == '^') { p = iter->pos + 1; if (iter->eof - p < snapshot_hexsz(iter->snapshot) + 1 || - parse_oid_hex(p, &iter->peeled, &p) || + parse_oid_hex_algop(p, &iter->peeled, &p, iter->repo->hash_algo) || *p++ != '\n') die_invalid_line(iter->snapshot->refs->path, iter->pos, iter->eof - iter->pos); @@ -921,13 +919,13 @@ static int next_record(struct packed_ref_iterator *iter) * we suppress it if the reference is broken: */ if ((iter->base.flags & REF_ISBROKEN)) { - oidclr(&iter->peeled, the_repository->hash_algo); + oidclr(&iter->peeled, iter->repo->hash_algo); iter->base.flags &= ~REF_KNOWS_PEELED; } else { iter->base.flags |= REF_KNOWS_PEELED; } } else { - oidclr(&iter->peeled, the_repository->hash_algo); + oidclr(&iter->peeled, iter->repo->hash_algo); } return ITER_OK; diff --git a/refs/refs-internal.h b/refs/refs-internal.h index fa975d69aa..309b382284 100644 --- a/refs/refs-internal.h +++ b/refs/refs-internal.h @@ -705,7 +705,8 @@ struct ref_store { * Parse contents of a loose ref file. *failure_errno maybe be set to EINVAL for * invalid contents. */ -int parse_loose_ref_contents(const char *buf, struct object_id *oid, +int parse_loose_ref_contents(const struct git_hash_algo *algop, + const char *buf, struct object_id *oid, struct strbuf *referent, unsigned int *type, int *failure_errno); diff --git a/refs/reftable-backend.c b/refs/reftable-backend.c index fbe74c239d..bf4446afd3 100644 --- a/refs/reftable-backend.c +++ b/refs/reftable-backend.c @@ -1,5 +1,3 @@ -#define USE_THE_REPOSITORY_VARIABLE - #include "../git-compat-util.h" #include "../abspath.h" #include "../chdir-notify.h" @@ -201,7 +199,8 @@ static void fill_reftable_log_record(struct reftable_log_record *log, const stru log->value.update.tz_offset = sign * atoi(tz_begin); } -static int read_ref_without_reload(struct reftable_stack *stack, +static int read_ref_without_reload(struct reftable_ref_store *refs, + struct reftable_stack *stack, const char *refname, struct object_id *oid, struct strbuf *referent, @@ -220,7 +219,7 @@ static int read_ref_without_reload(struct reftable_stack *stack, *type |= REF_ISSYMREF; } else if (reftable_ref_record_val1(&ref)) { oidread(oid, reftable_ref_record_val1(&ref), - the_repository->hash_algo); + refs->base.repo->hash_algo); } else { /* We got a tombstone, which should not happen. */ BUG("unhandled reference value type %d", ref.value_type); @@ -487,16 +486,16 @@ static int reftable_ref_iterator_advance(struct ref_iterator *ref_iterator) switch (iter->ref.value_type) { case REFTABLE_REF_VAL1: oidread(&iter->oid, iter->ref.value.val1, - the_repository->hash_algo); + refs->base.repo->hash_algo); break; case REFTABLE_REF_VAL2: oidread(&iter->oid, iter->ref.value.val2.value, - the_repository->hash_algo); + refs->base.repo->hash_algo); break; case REFTABLE_REF_SYMREF: if (!refs_resolve_ref_unsafe(&iter->refs->base, iter->ref.refname, RESOLVE_REF_READING, &iter->oid, &flags)) - oidclr(&iter->oid, the_repository->hash_algo); + oidclr(&iter->oid, refs->base.repo->hash_algo); break; default: BUG("unhandled reference value type %d", iter->ref.value_type); @@ -508,7 +507,7 @@ static int reftable_ref_iterator_advance(struct ref_iterator *ref_iterator) if (check_refname_format(iter->ref.refname, REFNAME_ALLOW_ONELEVEL)) { if (!refname_is_safe(iter->ref.refname)) die(_("refname is dangerous: %s"), iter->ref.refname); - oidclr(&iter->oid, the_repository->hash_algo); + oidclr(&iter->oid, refs->base.repo->hash_algo); flags |= REF_BAD_NAME | REF_ISBROKEN; } @@ -551,7 +550,7 @@ static int reftable_ref_iterator_peel(struct ref_iterator *ref_iterator, if (iter->ref.value_type == REFTABLE_REF_VAL2) { oidread(peeled, iter->ref.value.val2.target_value, - the_repository->hash_algo); + iter->refs->base.repo->hash_algo); return 0; } @@ -659,7 +658,7 @@ static int reftable_be_read_raw_ref(struct ref_store *ref_store, if (ret) return ret; - ret = read_ref_without_reload(stack, refname, oid, referent, type); + ret = read_ref_without_reload(refs, stack, refname, oid, referent, type); if (ret < 0) return ret; if (ret > 0) { @@ -868,8 +867,8 @@ static int reftable_be_transaction_prepare(struct ref_store *ref_store, goto done; } - ret = read_ref_without_reload(stack_for(refs, "HEAD", NULL), "HEAD", &head_oid, - &head_referent, &head_type); + ret = read_ref_without_reload(refs, stack_for(refs, "HEAD", NULL), "HEAD", + &head_oid, &head_referent, &head_type); if (ret < 0) goto done; ret = 0; @@ -936,7 +935,7 @@ static int reftable_be_transaction_prepare(struct ref_store *ref_store, string_list_insert(&affected_refnames, new_update->refname); } - ret = read_ref_without_reload(stack, rewritten_ref, + ret = read_ref_without_reload(refs, stack, rewritten_ref, ¤t_oid, &referent, &u->type); if (ret < 0) goto done; @@ -1500,7 +1499,8 @@ static int write_copy_table(struct reftable_writer *writer, void *cb_data) memcpy(logs[logs_nr].value.update.old_hash, old_ref.value.val1, GIT_MAX_RAWSZ); logs_nr++; - ret = read_ref_without_reload(arg->stack, "HEAD", &head_oid, &head_referent, &head_type); + ret = read_ref_without_reload(arg->refs, arg->stack, "HEAD", &head_oid, + &head_referent, &head_type); if (ret < 0) goto done; append_head_reflog = (head_type & REF_ISSYMREF) && !strcmp(head_referent.buf, arg->oldname); @@ -1790,15 +1790,16 @@ static struct ref_iterator *reftable_be_reflog_iterator_begin(struct ref_store * ref_iterator_select, NULL); } -static int yield_log_record(struct reftable_log_record *log, +static int yield_log_record(struct reftable_ref_store *refs, + struct reftable_log_record *log, each_reflog_ent_fn fn, void *cb_data) { struct object_id old_oid, new_oid; const char *full_committer; - oidread(&old_oid, log->value.update.old_hash, the_repository->hash_algo); - oidread(&new_oid, log->value.update.new_hash, the_repository->hash_algo); + oidread(&old_oid, log->value.update.old_hash, refs->base.repo->hash_algo); + oidread(&new_oid, log->value.update.new_hash, refs->base.repo->hash_algo); /* * When both the old object ID and the new object ID are null @@ -1841,7 +1842,7 @@ static int reftable_be_for_each_reflog_ent_reverse(struct ref_store *ref_store, break; } - ret = yield_log_record(&log, fn, cb_data); + ret = yield_log_record(refs, &log, fn, cb_data); if (ret) break; } @@ -1886,7 +1887,7 @@ static int reftable_be_for_each_reflog_ent(struct ref_store *ref_store, } for (i = logs_nr; i--;) { - ret = yield_log_record(&logs[i], fn, cb_data); + ret = yield_log_record(refs, &logs[i], fn, cb_data); if (ret) goto done; } @@ -2200,7 +2201,7 @@ static int reftable_be_reflog_expire(struct ref_store *ref_store, goto done; if (reftable_ref_record_val1(&ref_record)) oidread(&oid, reftable_ref_record_val1(&ref_record), - the_repository->hash_algo); + ref_store->repo->hash_algo); prepare_fn(refname, &oid, policy_cb_data); while (1) { @@ -2216,9 +2217,9 @@ static int reftable_be_reflog_expire(struct ref_store *ref_store, } oidread(&old_oid, log.value.update.old_hash, - the_repository->hash_algo); + ref_store->repo->hash_algo); oidread(&new_oid, log.value.update.new_hash, - the_repository->hash_algo); + ref_store->repo->hash_algo); /* * Skip over the reflog existence marker. We will add it back @@ -2250,9 +2251,9 @@ static int reftable_be_reflog_expire(struct ref_store *ref_store, *dest = logs[i]; oidread(&old_oid, logs[i].value.update.old_hash, - the_repository->hash_algo); + ref_store->repo->hash_algo); oidread(&new_oid, logs[i].value.update.new_hash, - the_repository->hash_algo); + ref_store->repo->hash_algo); if (should_prune_fn(&old_oid, &new_oid, logs[i].value.update.email, (timestamp_t)logs[i].value.update.time, @@ -2269,7 +2270,7 @@ static int reftable_be_reflog_expire(struct ref_store *ref_store, if (flags & EXPIRE_REFLOGS_UPDATE_REF && last_hash && reftable_ref_record_val1(&ref_record)) - oidread(&arg.update_oid, last_hash, the_repository->hash_algo); + oidread(&arg.update_oid, last_hash, ref_store->repo->hash_algo); arg.refs = refs; arg.records = rewritten; diff --git a/reftable/pq.c b/reftable/pq.c index 7fb45d8c60..2b5b7d1c0e 100644 --- a/reftable/pq.c +++ b/reftable/pq.c @@ -22,27 +22,21 @@ int pq_less(struct pq_entry *a, struct pq_entry *b) struct pq_entry merged_iter_pqueue_remove(struct merged_iter_pqueue *pq) { - int i = 0; + size_t i = 0; struct pq_entry e = pq->heap[0]; pq->heap[0] = pq->heap[pq->len - 1]; pq->len--; - i = 0; while (i < pq->len) { - int min = i; - int j = 2 * i + 1; - int k = 2 * i + 2; - if (j < pq->len && pq_less(&pq->heap[j], &pq->heap[i])) { + size_t min = i; + size_t j = 2 * i + 1; + size_t k = 2 * i + 2; + if (j < pq->len && pq_less(&pq->heap[j], &pq->heap[i])) min = j; - } - if (k < pq->len && pq_less(&pq->heap[k], &pq->heap[min])) { + if (k < pq->len && pq_less(&pq->heap[k], &pq->heap[min])) min = k; - } - - if (min == i) { + if (min == i) break; - } - SWAP(pq->heap[i], pq->heap[min]); i = min; } @@ -52,20 +46,17 @@ struct pq_entry merged_iter_pqueue_remove(struct merged_iter_pqueue *pq) void merged_iter_pqueue_add(struct merged_iter_pqueue *pq, const struct pq_entry *e) { - int i = 0; + size_t i = 0; REFTABLE_ALLOC_GROW(pq->heap, pq->len + 1, pq->cap); pq->heap[pq->len++] = *e; i = pq->len - 1; while (i > 0) { - int j = (i - 1) / 2; - if (pq_less(&pq->heap[j], &pq->heap[i])) { + size_t j = (i - 1) / 2; + if (pq_less(&pq->heap[j], &pq->heap[i])) break; - } - SWAP(pq->heap[j], pq->heap[i]); - i = j; } } diff --git a/reftable/pq.h b/reftable/pq.h index f796c23179..707bd26767 100644 --- a/reftable/pq.h +++ b/reftable/pq.h @@ -22,7 +22,6 @@ struct merged_iter_pqueue { size_t cap; }; -void merged_iter_pqueue_check(struct merged_iter_pqueue pq); struct pq_entry merged_iter_pqueue_remove(struct merged_iter_pqueue *pq); void merged_iter_pqueue_add(struct merged_iter_pqueue *pq, const struct pq_entry *e); void merged_iter_pqueue_release(struct merged_iter_pqueue *pq); diff --git a/reftable/pq_test.c b/reftable/pq_test.c deleted file mode 100644 index b7d3c80cc7..0000000000 --- a/reftable/pq_test.c +++ /dev/null @@ -1,74 +0,0 @@ -/* -Copyright 2020 Google LLC - -Use of this source code is governed by a BSD-style -license that can be found in the LICENSE file or at -https://developers.google.com/open-source/licenses/bsd -*/ - -#include "system.h" - -#include "basics.h" -#include "constants.h" -#include "pq.h" -#include "record.h" -#include "reftable-tests.h" -#include "test_framework.h" - -void merged_iter_pqueue_check(struct merged_iter_pqueue pq) -{ - int i; - for (i = 1; i < pq.len; i++) { - int parent = (i - 1) / 2; - - EXPECT(pq_less(&pq.heap[parent], &pq.heap[i])); - } -} - -static void test_pq(void) -{ - struct merged_iter_pqueue pq = { NULL }; - struct reftable_record recs[54]; - int N = ARRAY_SIZE(recs) - 1, i; - char *last = NULL; - - for (i = 0; i < N; i++) { - struct strbuf refname = STRBUF_INIT; - strbuf_addf(&refname, "%02d", i); - - reftable_record_init(&recs[i], BLOCK_TYPE_REF); - recs[i].u.ref.refname = strbuf_detach(&refname, NULL); - } - - i = 1; - do { - struct pq_entry e = { - .rec = &recs[i], - }; - - merged_iter_pqueue_add(&pq, &e); - merged_iter_pqueue_check(pq); - - i = (i * 7) % N; - } while (i != 1); - - while (!merged_iter_pqueue_is_empty(pq)) { - struct pq_entry e = merged_iter_pqueue_remove(&pq); - merged_iter_pqueue_check(pq); - - EXPECT(reftable_record_type(e.rec) == BLOCK_TYPE_REF); - if (last) - EXPECT(strcmp(last, e.rec->u.ref.refname) < 0); - last = e.rec->u.ref.refname; - } - - for (i = 0; i < N; i++) - reftable_record_release(&recs[i]); - merged_iter_pqueue_release(&pq); -} - -int pq_test_main(int argc, const char *argv[]) -{ - RUN_TEST(test_pq); - return 0; -} diff --git a/reftable/reftable-tests.h b/reftable/reftable-tests.h index d5e03dcc1b..4b666810af 100644 --- a/reftable/reftable-tests.h +++ b/reftable/reftable-tests.h @@ -11,11 +11,9 @@ https://developers.google.com/open-source/licenses/bsd int basics_test_main(int argc, const char **argv); int block_test_main(int argc, const char **argv); -int pq_test_main(int argc, const char **argv); int record_test_main(int argc, const char **argv); int readwrite_test_main(int argc, const char **argv); int stack_test_main(int argc, const char **argv); -int tree_test_main(int argc, const char **argv); int reftable_dump_main(int argc, char *const *argv); #endif diff --git a/reftable/tree.c b/reftable/tree.c index 528f33ae38..5ffb2e0d69 100644 --- a/reftable/tree.c +++ b/reftable/tree.c @@ -39,25 +39,20 @@ struct tree_node *tree_search(void *key, struct tree_node **rootp, void infix_walk(struct tree_node *t, void (*action)(void *arg, void *key), void *arg) { - if (t->left) { + if (t->left) infix_walk(t->left, action, arg); - } action(arg, t->key); - if (t->right) { + if (t->right) infix_walk(t->right, action, arg); - } } void tree_free(struct tree_node *t) { - if (!t) { + if (!t) return; - } - if (t->left) { + if (t->left) tree_free(t->left); - } - if (t->right) { + if (t->right) tree_free(t->right); - } reftable_free(t); } diff --git a/reftable/tree_test.c b/reftable/tree_test.c deleted file mode 100644 index 6961a657ad..0000000000 --- a/reftable/tree_test.c +++ /dev/null @@ -1,60 +0,0 @@ -/* -Copyright 2020 Google LLC - -Use of this source code is governed by a BSD-style -license that can be found in the LICENSE file or at -https://developers.google.com/open-source/licenses/bsd -*/ - -#include "system.h" -#include "tree.h" - -#include "test_framework.h" -#include "reftable-tests.h" - -static int test_compare(const void *a, const void *b) -{ - return (char *)a - (char *)b; -} - -struct curry { - void *last; -}; - -static void check_increasing(void *arg, void *key) -{ - struct curry *c = arg; - if (c->last) { - EXPECT(test_compare(c->last, key) < 0); - } - c->last = key; -} - -static void test_tree(void) -{ - struct tree_node *root = NULL; - - void *values[11] = { NULL }; - struct tree_node *nodes[11] = { NULL }; - int i = 1; - struct curry c = { NULL }; - do { - nodes[i] = tree_search(values + i, &root, &test_compare, 1); - i = (i * 7) % 11; - } while (i != 1); - - for (i = 1; i < ARRAY_SIZE(nodes); i++) { - EXPECT(values + i == nodes[i]->key); - EXPECT(nodes[i] == - tree_search(values + i, &root, &test_compare, 0)); - } - - infix_walk(root, check_increasing, &c); - tree_free(root); -} - -int tree_test_main(int argc, const char *argv[]) -{ - RUN_TEST(test_tree); - return 0; -} @@ -1107,7 +1107,7 @@ fail_exit: int rerere_forget(struct repository *r, struct pathspec *pathspec) { - int i, fd; + int i, fd, ret; struct string_list conflict = STRING_LIST_INIT_DUP; struct string_list merge_rr = STRING_LIST_INIT_DUP; @@ -1132,7 +1132,12 @@ int rerere_forget(struct repository *r, struct pathspec *pathspec) continue; rerere_forget_one_path(r->index, it->string, &merge_rr); } - return write_rr(&merge_rr, fd); + + ret = write_rr(&merge_rr, fd); + + string_list_clear(&conflict, 0); + string_list_clear(&merge_rr, 1); + return ret; } /* @@ -1215,7 +1215,7 @@ static int canonicalize_ceiling_entry(struct string_list_item *item, } struct safe_directory_data { - const char *path; + char *path; int is_safe; }; @@ -1235,17 +1235,45 @@ static int safe_directory_cb(const char *key, const char *value, char *allowed = NULL; if (!git_config_pathname(&allowed, key, value)) { - const char *check = allowed ? allowed : value; - if (ends_with(check, "/*")) { - size_t len = strlen(check); - if (!fspathncmp(check, data->path, len - 1)) + char *normalized = NULL; + + /* + * Setting safe.directory to a non-absolute path + * makes little sense---it won't be relative to + * the configuration file the item is defined in. + * Except for ".", which means "if we are at the top + * level of a repository, then it is OK", which is + * slightly tighter than "*" that allows discovery. + */ + if (!is_absolute_path(allowed) && strcmp(allowed, ".")) { + warning(_("safe.directory '%s' not absolute"), + allowed); + goto next; + } + + /* + * A .gitconfig in $HOME may be shared across + * different machines and safe.directory entries + * may or may not exist as paths on all of these + * machines. In other words, it is not a warning + * worthy event when there is no such path on this + * machine---the entry may be useful elsewhere. + */ + normalized = real_pathdup(allowed, 0); + if (!normalized) + goto next; + + if (ends_with(normalized, "/*")) { + size_t len = strlen(normalized); + if (!fspathncmp(normalized, data->path, len - 1)) data->is_safe = 1; - } else if (!fspathcmp(data->path, check)) { + } else if (!fspathcmp(data->path, normalized)) { data->is_safe = 1; } - } - if (allowed != value) + next: + free(normalized); free(allowed); + } } return 0; @@ -1263,9 +1291,7 @@ static int ensure_valid_ownership(const char *gitfile, const char *worktree, const char *gitdir, struct strbuf *report) { - struct safe_directory_data data = { - .path = worktree ? worktree : gitdir - }; + struct safe_directory_data data = { 0 }; if (!git_env_bool("GIT_TEST_ASSUME_DIFFERENT_OWNER", 0) && (!gitfile || is_path_owned_by_current_user(gitfile, report)) && @@ -1274,12 +1300,22 @@ static int ensure_valid_ownership(const char *gitfile, return 1; /* + * normalize the data.path for comparison with normalized paths + * that come from the configuration file. The path is unsafe + * if it cannot be normalized. + */ + data.path = real_pathdup(worktree ? worktree : gitdir, 0); + if (!data.path) + return 0; + + /* * data.path is the "path" that identifies the repository and it is * constant regardless of what failed above. data.is_safe should be * initialized to false, and might be changed by the callback. */ git_protected_config(safe_directory_cb, &data); + free(data.path); return data.is_safe; } diff --git a/t/check-non-portable-shell.pl b/t/check-non-portable-shell.pl index b2b28c2ced..6ee7700eb4 100755 --- a/t/check-non-portable-shell.pl +++ b/t/check-non-portable-shell.pl @@ -49,8 +49,8 @@ while (<>) { /\bexport\s+[A-Za-z0-9_]*=/ and err '"export FOO=bar" is not portable (use FOO=bar && export FOO)'; /\blocal\s+[A-Za-z0-9_]*=\$([A-Za-z0-9_{]|[(][^(])/ and err q(quote "$val" in 'local var=$val'); - /^\s*([A-Z0-9_]+=(\w*|(["']).*?\3)\s+)+(\w+)/ and exists($func{$4}) and - err '"FOO=bar shell_func" assignment extends beyond "shell_func"'; + /\b([A-Z0-9_]+=(\w*|(["']).*?\3)\s+)+(\w+)/ and !/test_env.+=/ and exists($func{$4}) and + err '"FOO=bar shell_func" is not portable (use test_env FOO=bar shell_func)'; $line = ''; # this resets our $. for each file close ARGV if eof; diff --git a/t/helper/test-json-writer.c b/t/helper/test-json-writer.c index ed52eb76bf..a288069b04 100644 --- a/t/helper/test-json-writer.c +++ b/t/helper/test-json-writer.c @@ -415,6 +415,7 @@ static void get_i(struct line *line, intmax_t *s_in) get_s(line, &s); + errno = 0; *s_in = strtol(s, &endptr, 10); if (*endptr || errno == ERANGE) die("line[%d]: invalid integer value", line->nr); @@ -427,6 +428,7 @@ static void get_d(struct line *line, double *s_in) get_s(line, &s); + errno = 0; *s_in = strtod(s, &endptr); if (*endptr || errno == ERANGE) die("line[%d]: invalid float value", line->nr); diff --git a/t/helper/test-reftable.c b/t/helper/test-reftable.c index 9d378427da..623cf3f0f5 100644 --- a/t/helper/test-reftable.c +++ b/t/helper/test-reftable.c @@ -6,8 +6,6 @@ int cmd__reftable(int argc, const char **argv) { /* test from simple to complex. */ block_test_main(argc, argv); - tree_test_main(argc, argv); - pq_test_main(argc, argv); readwrite_test_main(argc, argv); stack_test_main(argc, argv); return 0; diff --git a/t/helper/test-repository.c b/t/helper/test-repository.c index c6a074df3d..63c37de33d 100644 --- a/t/helper/test-repository.c +++ b/t/helper/test-repository.c @@ -19,7 +19,7 @@ static void test_parse_commit_in_graph(const char *gitdir, const char *worktree, setup_git_env(gitdir); - memset(the_repository, 0, sizeof(*the_repository)); + repo_clear(the_repository); if (repo_init(&r, gitdir, worktree)) die("Couldn't init repo"); @@ -49,7 +49,7 @@ static void test_get_commit_tree_in_graph(const char *gitdir, setup_git_env(gitdir); - memset(the_repository, 0, sizeof(*the_repository)); + repo_clear(the_repository); if (repo_init(&r, gitdir, worktree)) die("Couldn't init repo"); diff --git a/t/helper/test-trace2.c b/t/helper/test-trace2.c index cd955ec63e..c588c273ce 100644 --- a/t/helper/test-trace2.c +++ b/t/helper/test-trace2.c @@ -26,6 +26,7 @@ static int get_i(int *p_value, const char *data) if (!data || !*data) return MyError; + errno = 0; *p_value = strtol(data, &endptr, 10); if (*endptr || errno == ERANGE) return MyError; diff --git a/t/socks4-proxy.pl b/t/socks4-proxy.pl new file mode 100644 index 0000000000..4c3a35c008 --- /dev/null +++ b/t/socks4-proxy.pl @@ -0,0 +1,48 @@ +use strict; +use IO::Select; +use IO::Socket::UNIX; +use IO::Socket::INET; + +my $path = shift; + +unlink($path); +my $server = IO::Socket::UNIX->new(Listen => 1, Local => $path) + or die "unable to listen on $path: $!"; + +$| = 1; +print "ready\n"; + +while (my $client = $server->accept()) { + sysread $client, my $buf, 8; + my ($version, $cmd, $port, $ip) = unpack 'CCnN', $buf; + next unless $version == 4; # socks4 + next unless $cmd == 1; # TCP stream connection + + # skip NUL-terminated id + while (sysread $client, my $char, 1) { + last unless ord($char); + } + + # version(0), reply(5a == granted), port (ignored), ip (ignored) + syswrite $client, "\x00\x5a\x00\x00\x00\x00\x00\x00"; + + my $remote = IO::Socket::INET->new(PeerHost => $ip, PeerPort => $port) + or die "unable to connect to $ip/$port: $!"; + + my $io = IO::Select->new($client, $remote); + while ($io->count) { + for my $fh ($io->can_read(0)) { + for my $pair ([$client, $remote], [$remote, $client]) { + my ($from, $to) = @$pair; + next unless $fh == $from; + + my $r = sysread $from, my $buf, 1024; + if (!defined $r || $r <= 0) { + $io->remove($from); + next; + } + syswrite $to, $buf; + } + } + } +} diff --git a/t/t0018-advice.sh b/t/t0018-advice.sh index 29306b367c..fac52322a7 100755 --- a/t/t0018-advice.sh +++ b/t/t0018-advice.sh @@ -96,7 +96,6 @@ test_expect_success 'advice should be printed when GIT_ADVICE is set to true' ' >README && GIT_ADVICE=true git status ) >actual && - cat actual > /tmp/actual && test_cmp expect actual ' diff --git a/t/t0021-conversion.sh b/t/t0021-conversion.sh index 0b4997022b..eeb2714d9d 100755 --- a/t/t0021-conversion.sh +++ b/t/t0021-conversion.sh @@ -5,6 +5,7 @@ test_description='blob conversion via gitattributes' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh . "$TEST_DIRECTORY"/lib-terminal.sh diff --git a/t/t0033-safe-directory.sh b/t/t0033-safe-directory.sh index 5fe61f1291..e97a84764f 100755 --- a/t/t0033-safe-directory.sh +++ b/t/t0033-safe-directory.sh @@ -119,4 +119,182 @@ test_expect_success 'local clone of unowned repo accepted in safe directory' ' test_path_is_dir target ' +test_expect_success SYMLINKS 'checked paths are normalized' ' + test_when_finished "rm -rf repository; rm -f repo" && + ( + sane_unset GIT_TEST_ASSUME_DIFFERENT_OWNER && + git config --global --unset-all safe.directory + ) && + git init repository && + ln -s repository repo && + ( + cd repository && + sane_unset GIT_TEST_ASSUME_DIFFERENT_OWNER && + test_commit sample + ) && + + ( + sane_unset GIT_TEST_ASSUME_DIFFERENT_OWNER && + git config --global safe.directory "$(pwd)/repository" + ) && + git -C repository for-each-ref && + git -C repository/ for-each-ref && + git -C repo for-each-ref && + git -C repo/ for-each-ref && + test_must_fail git -C repository/.git for-each-ref && + test_must_fail git -C repository/.git/ for-each-ref && + test_must_fail git -C repo/.git for-each-ref && + test_must_fail git -C repo/.git/ for-each-ref +' + +test_expect_success SYMLINKS 'checked leading paths are normalized' ' + test_when_finished "rm -rf repository; rm -f repo" && + ( + sane_unset GIT_TEST_ASSUME_DIFFERENT_OWNER && + git config --global --unset-all safe.directory + ) && + mkdir -p repository && + git init repository/s && + ln -s repository repo && + ( + cd repository/s && + sane_unset GIT_TEST_ASSUME_DIFFERENT_OWNER && + test_commit sample + ) && + + ( + sane_unset GIT_TEST_ASSUME_DIFFERENT_OWNER && + git config --global safe.directory "$(pwd)/repository/*" + ) && + git -C repository/s for-each-ref && + git -C repository/s/ for-each-ref && + git -C repo/s for-each-ref && + git -C repo/s/ for-each-ref && + git -C repository/s/.git for-each-ref && + git -C repository/s/.git/ for-each-ref && + git -C repo/s/.git for-each-ref && + git -C repo/s/.git/ for-each-ref +' + +test_expect_success SYMLINKS 'configured paths are normalized' ' + test_when_finished "rm -rf repository; rm -f repo" && + ( + sane_unset GIT_TEST_ASSUME_DIFFERENT_OWNER && + git config --global --unset-all safe.directory + ) && + git init repository && + ln -s repository repo && + ( + cd repository && + sane_unset GIT_TEST_ASSUME_DIFFERENT_OWNER && + test_commit sample + ) && + + ( + sane_unset GIT_TEST_ASSUME_DIFFERENT_OWNER && + git config --global safe.directory "$(pwd)/repo" + ) && + git -C repository for-each-ref && + git -C repository/ for-each-ref && + git -C repo for-each-ref && + git -C repo/ for-each-ref && + test_must_fail git -C repository/.git for-each-ref && + test_must_fail git -C repository/.git/ for-each-ref && + test_must_fail git -C repo/.git for-each-ref && + test_must_fail git -C repo/.git/ for-each-ref +' + +test_expect_success SYMLINKS 'configured leading paths are normalized' ' + test_when_finished "rm -rf repository; rm -f repo" && + ( + sane_unset GIT_TEST_ASSUME_DIFFERENT_OWNER && + git config --global --unset-all safe.directory + ) && + mkdir -p repository && + git init repository/s && + ln -s repository repo && + ( + cd repository/s && + sane_unset GIT_TEST_ASSUME_DIFFERENT_OWNER && + test_commit sample + ) && + + ( + sane_unset GIT_TEST_ASSUME_DIFFERENT_OWNER && + git config --global safe.directory "$(pwd)/repo/*" + ) && + git -C repository/s for-each-ref && + git -C repository/s/ for-each-ref && + git -C repository/s/.git for-each-ref && + git -C repository/s/.git/ for-each-ref && + git -C repo/s for-each-ref && + git -C repo/s/ for-each-ref && + git -C repo/s/.git for-each-ref && + git -C repo/s/.git/ for-each-ref +' + +test_expect_success 'safe.directory set to a dot' ' + test_when_finished "rm -rf repository" && + ( + sane_unset GIT_TEST_ASSUME_DIFFERENT_OWNER && + git config --global --unset-all safe.directory + ) && + mkdir -p repository/subdir && + git init repository && + ( + cd repository && + sane_unset GIT_TEST_ASSUME_DIFFERENT_OWNER && + test_commit sample + ) && + + ( + sane_unset GIT_TEST_ASSUME_DIFFERENT_OWNER && + git config --global safe.directory "." + ) && + git -C repository for-each-ref && + git -C repository/ for-each-ref && + git -C repository/.git for-each-ref && + git -C repository/.git/ for-each-ref && + + # What is allowed is repository/subdir but the repository + # path is repository. + test_must_fail git -C repository/subdir for-each-ref && + + # Likewise, repository .git/refs is allowed with "." but + # repository/.git that is accessed is not allowed. + test_must_fail git -C repository/.git/refs for-each-ref +' + +test_expect_success 'safe.directory set to asterisk' ' + test_when_finished "rm -rf repository" && + ( + sane_unset GIT_TEST_ASSUME_DIFFERENT_OWNER && + git config --global --unset-all safe.directory + ) && + mkdir -p repository/subdir && + git init repository && + ( + cd repository && + sane_unset GIT_TEST_ASSUME_DIFFERENT_OWNER && + test_commit sample + ) && + + ( + sane_unset GIT_TEST_ASSUME_DIFFERENT_OWNER && + git config --global safe.directory "*" + ) && + # these are trivial + git -C repository for-each-ref && + git -C repository/ for-each-ref && + git -C repository/.git for-each-ref && + git -C repository/.git/ for-each-ref && + + # With "*", everything is allowed, and the repository is + # discovered, which is different behaviour from "." above. + git -C repository/subdir for-each-ref && + + # Likewise. + git -C repository/.git/refs for-each-ref +' + test_done diff --git a/t/t0301-credential-cache.sh b/t/t0301-credential-cache.sh index c10e35905e..5d5b64205f 100755 --- a/t/t0301-credential-cache.sh +++ b/t/t0301-credential-cache.sh @@ -1,6 +1,8 @@ #!/bin/sh test_description='credential-cache tests' + +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh . "$TEST_DIRECTORY"/lib-credential.sh diff --git a/t/t0302-credential-store.sh b/t/t0302-credential-store.sh index 716bf1af9f..f83db659e2 100755 --- a/t/t0302-credential-store.sh +++ b/t/t0302-credential-store.sh @@ -1,6 +1,8 @@ #!/bin/sh test_description='credential-store tests' + +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh . "$TEST_DIRECTORY"/lib-credential.sh diff --git a/t/t0303-credential-external.sh b/t/t0303-credential-external.sh index 72ae405c3e..8aadbe86c4 100755 --- a/t/t0303-credential-external.sh +++ b/t/t0303-credential-external.sh @@ -29,6 +29,7 @@ you can set GIT_TEST_CREDENTIAL_HELPER_SETUP to a sequence of shell commands. ' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh . "$TEST_DIRECTORY"/lib-credential.sh diff --git a/t/t1300-config.sh b/t/t1300-config.sh index 9de2d95f06..f13277c8f3 100755 --- a/t/t1300-config.sh +++ b/t/t1300-config.sh @@ -2704,6 +2704,15 @@ test_expect_success '--get and --get-all with --fixed-value' ' test_must_fail git config --file=config --get-regexp --fixed-value fixed+ non-existent ' +test_expect_success '--fixed-value with value-less configuration' ' + test_when_finished rm -f config && + cat >config <<-\EOF && + [section] + key + EOF + git config --file=config --fixed-value section.key value pattern +' + test_expect_success 'includeIf.hasconfig:remote.*.url' ' git init hasremoteurlTest && test_when_finished "rm -rf hasremoteurlTest" && diff --git a/t/t1410-reflog.sh b/t/t1410-reflog.sh index 5bf883f1e3..246a3f46ab 100755 --- a/t/t1410-reflog.sh +++ b/t/t1410-reflog.sh @@ -146,6 +146,14 @@ test_expect_success rewind ' test_line_count = 5 output ' +test_expect_success 'reflog expire should not barf on an annotated tag' ' + test_when_finished "git tag -d v0.tag || :" && + git -c core.logAllRefUpdates=always \ + tag -a -m "tag name" v0.tag main && + git reflog expire --dry-run refs/tags/v0.tag 2>err && + test_grep ! "error: [Oo]bject .* not a commit" err +' + test_expect_success 'corrupt and check' ' corrupt $F && diff --git a/t/t1502-rev-parse-parseopt.sh b/t/t1502-rev-parse-parseopt.sh index b754b9fd74..5eaa6428c4 100755 --- a/t/t1502-rev-parse-parseopt.sh +++ b/t/t1502-rev-parse-parseopt.sh @@ -1,6 +1,8 @@ #!/bin/sh test_description='test git rev-parse --parseopt' + +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh check_invalid_long_option () { diff --git a/t/t1511-rev-parse-caret.sh b/t/t1511-rev-parse-caret.sh index 6ecfed86bc..e7e78a4028 100755 --- a/t/t1511-rev-parse-caret.sh +++ b/t/t1511-rev-parse-caret.sh @@ -5,6 +5,7 @@ test_description='tests for ref^{stuff}' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success 'setup' ' diff --git a/t/t2030-unresolve-info.sh b/t/t2030-unresolve-info.sh index be3fcdde07..b3f6bc97b5 100755 --- a/t/t2030-unresolve-info.sh +++ b/t/t2030-unresolve-info.sh @@ -5,6 +5,7 @@ test_description='undoing resolution' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh check_resolve_undo () { diff --git a/t/t2080-parallel-checkout-basics.sh b/t/t2080-parallel-checkout-basics.sh index 5ffe1a41e2..59e5570cb2 100755 --- a/t/t2080-parallel-checkout-basics.sh +++ b/t/t2080-parallel-checkout-basics.sh @@ -8,6 +8,7 @@ working tree. ' TEST_NO_CREATE_REPO=1 +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh . "$TEST_DIRECTORY/lib-parallel-checkout.sh" diff --git a/t/t2082-parallel-checkout-attributes.sh b/t/t2082-parallel-checkout-attributes.sh index f3511cd43a..aec55496eb 100755 --- a/t/t2082-parallel-checkout-attributes.sh +++ b/t/t2082-parallel-checkout-attributes.sh @@ -10,6 +10,7 @@ properly (without access to the index or attribute stack). ' TEST_NO_CREATE_REPO=1 +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh . "$TEST_DIRECTORY/lib-parallel-checkout.sh" . "$TEST_DIRECTORY/lib-encoding.sh" diff --git a/t/t2107-update-index-basic.sh b/t/t2107-update-index-basic.sh index cc72ead79f..f0eab13f96 100755 --- a/t/t2107-update-index-basic.sh +++ b/t/t2107-update-index-basic.sh @@ -5,6 +5,7 @@ test_description='basic update-index tests Tests for command-line parsing and basic operation. ' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success 'update-index --nonsense fails' ' diff --git a/t/t2400-worktree-add.sh b/t/t2400-worktree-add.sh index ba320dc417..cfc4aeb179 100755 --- a/t/t2400-worktree-add.sh +++ b/t/t2400-worktree-add.sh @@ -6,6 +6,7 @@ GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME TEST_CREATE_REPO_NO_TEMPLATE=1 +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh . "$TEST_DIRECTORY"/lib-rebase.sh diff --git a/t/t2501-cwd-empty.sh b/t/t2501-cwd-empty.sh index f6d8d7d03d..8af4e8cfe3 100755 --- a/t/t2501-cwd-empty.sh +++ b/t/t2501-cwd-empty.sh @@ -2,6 +2,7 @@ test_description='Test handling of the current working directory becoming empty' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success setup ' diff --git a/t/t3201-branch-contains.sh b/t/t3201-branch-contains.sh index 800fc33165..6e587d27d7 100755 --- a/t/t3201-branch-contains.sh +++ b/t/t3201-branch-contains.sh @@ -2,6 +2,7 @@ test_description='branch --contains <commit>, --no-contains <commit> --merged, and --no-merged' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success setup ' diff --git a/t/t3202-show-branch.sh b/t/t3202-show-branch.sh index a1139f79e2..3b6dad0c46 100755 --- a/t/t3202-show-branch.sh +++ b/t/t3202-show-branch.sh @@ -2,6 +2,7 @@ test_description='test show-branch' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success 'error descriptions on empty repository' ' diff --git a/t/t3206-range-diff.sh b/t/t3206-range-diff.sh index d2ca43d6aa..86010931ab 100755 --- a/t/t3206-range-diff.sh +++ b/t/t3206-range-diff.sh @@ -5,6 +5,7 @@ test_description='range-diff tests' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh # Note that because of the range-diff's heuristics, test_commit does more diff --git a/t/t3301-notes.sh b/t/t3301-notes.sh index 536bd11ff4..99137fb235 100755 --- a/t/t3301-notes.sh +++ b/t/t3301-notes.sh @@ -1557,4 +1557,14 @@ test_expect_success 'empty notes are displayed by git log' ' test_cmp expect actual ' +test_expect_success 'empty notes do not invoke the editor' ' + test_commit 18th && + GIT_EDITOR="false" git notes add -C "$empty_blob" --allow-empty && + git notes remove HEAD && + GIT_EDITOR="false" git notes add -m "" --allow-empty && + git notes remove HEAD && + GIT_EDITOR="false" git notes add -F /dev/null --allow-empty && + git notes remove HEAD +' + test_done diff --git a/t/t3430-rebase-merges.sh b/t/t3430-rebase-merges.sh index 36ca126bcd..2aa8593f77 100755 --- a/t/t3430-rebase-merges.sh +++ b/t/t3430-rebase-merges.sh @@ -392,8 +392,7 @@ test_expect_success 'refuse to merge ancestors of HEAD' ' test_expect_success 'root commits' ' git checkout --orphan unrelated && - (GIT_AUTHOR_NAME="Parsnip" GIT_AUTHOR_EMAIL="root@example.com" \ - test_commit second-root) && + test_commit --author "Parsnip <root@example.com>" second-root && test_commit third-root && cat >script-from-scratch <<-\EOF && pick third-root diff --git a/t/t3650-replay-basics.sh b/t/t3650-replay-basics.sh index 389670262e..12bd3db4cb 100755 --- a/t/t3650-replay-basics.sh +++ b/t/t3650-replay-basics.sh @@ -5,6 +5,7 @@ test_description='basic git replay tests' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh GIT_AUTHOR_NAME=author@name diff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh index 9a48933cec..718438ffc7 100755 --- a/t/t3701-add-interactive.sh +++ b/t/t3701-add-interactive.sh @@ -575,6 +575,54 @@ test_expect_success 'navigate to hunk via regex / pattern' ' test_cmp expect actual.trimmed ' +test_expect_success 'print again the hunk' ' + test_when_finished "git reset" && + tr _ " " >expect <<-EOF && + +15 + 20 + (1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]? @@ -1,2 +1,3 @@ + 10 + +15 + 20 + (1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]?_ + EOF + test_write_lines s y g 1 p | git add -p >actual && + tail -n 7 <actual >actual.trimmed && + test_cmp expect actual.trimmed +' + +test_expect_success TTY 'print again the hunk (PAGER)' ' + test_when_finished "git reset" && + cat >expect <<-EOF && + <GREEN>+<RESET><GREEN>15<RESET> + 20<RESET> + <BOLD;BLUE>(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]? <RESET>PAGER <CYAN>@@ -1,2 +1,3 @@<RESET> + PAGER 10<RESET> + PAGER <GREEN>+<RESET><GREEN>15<RESET> + PAGER 20<RESET> + <BOLD;BLUE>(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]? <RESET> + EOF + test_write_lines s y g 1 P | + ( + GIT_PAGER="sed s/^/PAGER\ /" && + export GIT_PAGER && + test_terminal git add -p >actual + ) && + tail -n 7 <actual | test_decode_color >actual.trimmed && + test_cmp expect actual.trimmed +' + +test_expect_success TTY 'P handles SIGPIPE when writing to pager' ' + test_when_finished "rm -f huge_file; git reset" && + printf "\n%2500000s" Y >huge_file && + git add -N huge_file && + test_write_lines P q | ( + GIT_PAGER="head -n 1" && + export GIT_PAGER && + test_terminal git add -p + ) +' + test_expect_success 'split hunk "add -p (edit)"' ' # Split, say Edit and do nothing. Then: # diff --git a/t/t3903-stash.sh b/t/t3903-stash.sh index a7f71f8126..e4c0937f61 100755 --- a/t/t3903-stash.sh +++ b/t/t3903-stash.sh @@ -8,6 +8,7 @@ test_description='Test git stash' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh . "$TEST_DIRECTORY"/lib-unique-files.sh diff --git a/t/t3904-stash-patch.sh b/t/t3904-stash-patch.sh index 368fc2a6cc..aa5019fd6c 100755 --- a/t/t3904-stash-patch.sh +++ b/t/t3904-stash-patch.sh @@ -1,6 +1,8 @@ #!/bin/sh test_description='stash -p' + +TEST_PASSES_SANITIZE_LEAK=true . ./lib-patch-mode.sh test_expect_success 'setup' ' diff --git a/t/t3905-stash-include-untracked.sh b/t/t3905-stash-include-untracked.sh index 1289ae3e07..a1733f45c3 100755 --- a/t/t3905-stash-include-untracked.sh +++ b/t/t3905-stash-include-untracked.sh @@ -5,6 +5,7 @@ test_description='Test git stash --include-untracked' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success 'stash save --include-untracked some dirty working directory' ' diff --git a/t/t4034-diff-words.sh b/t/t4034-diff-words.sh index 74586f3813..4dcd7e9925 100755 --- a/t/t4034-diff-words.sh +++ b/t/t4034-diff-words.sh @@ -70,7 +70,7 @@ test_language_driver () { word_diff --color-words ' test_expect_success "diff driver '$lang' in Islandic" ' - LANG=is_IS.UTF-8 LANGUAGE=is LC_ALL="$is_IS_locale" \ + test_env LANG=is_IS.UTF-8 LANGUAGE=is LC_ALL="$is_IS_locale" \ word_diff --color-words ' } diff --git a/t/t4129-apply-samemode.sh b/t/t4129-apply-samemode.sh index 4eb8444029..d9a1084b5e 100755 --- a/t/t4129-apply-samemode.sh +++ b/t/t4129-apply-samemode.sh @@ -130,4 +130,66 @@ test_expect_success 'git apply respects core.fileMode' ' test_grep ! "has type 100644, expected 100755" err ' +test_expect_success POSIXPERM 'patch mode for new file is canonicalized' ' + cat >patch <<-\EOF && + diff --git a/non-canon b/non-canon + new file mode 100660 + --- /dev/null + +++ b/non-canon + +content + EOF + test_when_finished "git reset --hard" && + ( + umask 0 && + git apply --index patch 2>err + ) && + test_must_be_empty err && + git ls-files -s -- non-canon >staged && + test_grep "^100644" staged && + ls -l non-canon >worktree && + test_grep "^-rw-rw-rw" worktree +' + +test_expect_success POSIXPERM 'patch mode for deleted file is canonicalized' ' + test_when_finished "git reset --hard" && + echo content >non-canon && + git add non-canon && + chmod 666 non-canon && + + cat >patch <<-\EOF && + diff --git a/non-canon b/non-canon + deleted file mode 100660 + --- a/non-canon + +++ /dev/null + @@ -1 +0,0 @@ + -content + EOF + git apply --index patch 2>err && + test_must_be_empty err && + git ls-files -- non-canon >staged && + test_must_be_empty staged && + test_path_is_missing non-canon +' + +test_expect_success POSIXPERM 'patch mode for mode change is canonicalized' ' + test_when_finished "git reset --hard" && + echo content >non-canon && + git add non-canon && + + cat >patch <<-\EOF && + diff --git a/non-canon b/non-canon + old mode 100660 + new mode 100770 + EOF + ( + umask 0 && + git apply --index patch 2>err + ) && + test_must_be_empty err && + git ls-files -s -- non-canon >staged && + test_grep "^100755" staged && + ls -l non-canon >worktree && + test_grep "^-rwxrwxrwx" worktree +' + test_done diff --git a/t/t4200-rerere.sh b/t/t4200-rerere.sh index b0a3e84984..213b36fb96 100755 --- a/t/t4200-rerere.sh +++ b/t/t4200-rerere.sh @@ -25,6 +25,7 @@ test_description='git rerere GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success 'setup' ' diff --git a/t/t4201-shortlog.sh b/t/t4201-shortlog.sh index f698d0c9ad..c20c885724 100755 --- a/t/t4201-shortlog.sh +++ b/t/t4201-shortlog.sh @@ -9,6 +9,7 @@ test_description='git shortlog GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success 'setup' ' diff --git a/t/t4203-mailmap.sh b/t/t4203-mailmap.sh index 8a88dd7900..79e5f42760 100755 --- a/t/t4203-mailmap.sh +++ b/t/t4203-mailmap.sh @@ -5,6 +5,7 @@ test_description='.mailmap configurations' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success 'setup commits and contacts file' ' diff --git a/t/t4204-patch-id.sh b/t/t4204-patch-id.sh index 605faea0c7..dc8ddb10af 100755 --- a/t/t4204-patch-id.sh +++ b/t/t4204-patch-id.sh @@ -114,6 +114,46 @@ test_expect_success 'patch-id supports git-format-patch output' ' test "$2" = $(git rev-parse HEAD) ' +test_expect_success 'patch-id computes the same for various formats' ' + # This test happens to consider "git log -p -1" output + # the canonical input format, so use it as the norm. + git log -1 -p same >log-p.output && + git patch-id <log-p.output >expect && + + # format-patch begins with "From <commit object name>" + git format-patch -1 --stdout same >format-patch.output && + git patch-id <format-patch.output >actual && + test_cmp actual expect && + + # "diff-tree --stdin -p" begins with "<commit object name>" + same=$(git rev-parse same) && + echo $same | git diff-tree --stdin -p >diff-tree.output && + git patch-id <diff-tree.output >actual && + test_cmp actual expect && + + # "diff-tree --stdin -v -p" begins with "commit <commit object name>" + echo $same | git diff-tree --stdin -p -v >diff-tree-v.output && + git patch-id <diff-tree-v.output >actual && + test_cmp actual expect +' + +hash=$(git rev-parse same:) +for cruft in "$hash" "commit $hash is bad" "From $hash status" +do + test_expect_success "patch-id with <$cruft> in log message" ' + git format-patch -1 --stdout same >patch-0 && + git patch-id <patch-0 >expect && + + { + sed -e "/^$/q" patch-0 && + printf "random message\n%s\n\n" "$cruft" && + sed -e "1,/^$/d" patch-0 + } >patch-cruft && + git patch-id <patch-cruft >actual && + test_cmp actual expect + ' +done + test_expect_success 'whitespace is irrelevant in footer' ' get_patch_id main && git checkout same && diff --git a/t/t5318-commit-graph.sh b/t/t5318-commit-graph.sh index a2b4442660..2916c07e3c 100755 --- a/t/t5318-commit-graph.sh +++ b/t/t5318-commit-graph.sh @@ -1,6 +1,8 @@ #!/bin/sh test_description='commit graph' + +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh . "$TEST_DIRECTORY"/lib-chunk.sh diff --git a/t/t5512-ls-remote.sh b/t/t5512-ls-remote.sh index 42e77eb5a9..d64b40e408 100755 --- a/t/t5512-ls-remote.sh +++ b/t/t5512-ls-remote.sh @@ -5,6 +5,7 @@ test_description='git ls-remote' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh generate_references () { @@ -402,4 +403,17 @@ test_expect_success 'v0 clients can handle multiple symrefs' ' test_cmp expect actual ' +test_expect_success 'helper with refspec capability fails gracefully' ' + mkdir test-bin && + write_script test-bin/git-remote-foo <<-EOF && + echo import + echo refspec ${SQ}*:*${SQ} + EOF + ( + PATH="$PWD/test-bin:$PATH" && + export PATH && + test_must_fail nongit git ls-remote foo::bar + ) +' + test_done diff --git a/t/t5514-fetch-multiple.sh b/t/t5514-fetch-multiple.sh index 25772c85c5..579872c258 100755 --- a/t/t5514-fetch-multiple.sh +++ b/t/t5514-fetch-multiple.sh @@ -5,6 +5,7 @@ test_description='fetch --all works correctly' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh setup_repository () { diff --git a/t/t5520-pull.sh b/t/t5520-pull.sh index 47534f1062..1098cbd0a1 100755 --- a/t/t5520-pull.sh +++ b/t/t5520-pull.sh @@ -5,6 +5,7 @@ test_description='pulling into void' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh modify () { diff --git a/t/t5528-push-default.sh b/t/t5528-push-default.sh index 14f7eced9a..bc2bada34c 100755 --- a/t/t5528-push-default.sh +++ b/t/t5528-push-default.sh @@ -4,6 +4,7 @@ test_description='check various push.default settings' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success 'setup bare remotes' ' diff --git a/t/t5535-fetch-push-symref.sh b/t/t5535-fetch-push-symref.sh index e8f6d233ff..7122af7fdb 100755 --- a/t/t5535-fetch-push-symref.sh +++ b/t/t5535-fetch-push-symref.sh @@ -2,6 +2,7 @@ test_description='avoiding conflicting update through symref aliasing' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success 'setup' ' diff --git a/t/t5543-atomic-push.sh b/t/t5543-atomic-push.sh index 04b47ad84a..479d103469 100755 --- a/t/t5543-atomic-push.sh +++ b/t/t5543-atomic-push.sh @@ -5,6 +5,7 @@ test_description='pushing to a repository using the atomic push option' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh mk_repo_pair () { diff --git a/t/t5564-http-proxy.sh b/t/t5564-http-proxy.sh index bb35b87071..4aef99bc28 100755 --- a/t/t5564-http-proxy.sh +++ b/t/t5564-http-proxy.sh @@ -39,4 +39,59 @@ test_expect_success 'clone can prompt for proxy password' ' expect_askpass pass proxuser ' +start_socks() { + mkfifo socks_output && + { + "$PERL_PATH" "$TEST_DIRECTORY/socks4-proxy.pl" "$1" >socks_output & + echo $! > "$TRASH_DIRECTORY/socks.pid" + } && + read line <socks_output && + test "$line" = ready +} + +# The %30 tests that the correct amount of percent-encoding is applied to the +# proxy string passed to curl. +test_lazy_prereq SOCKS_PROXY ' + test_have_prereq PERL && + start_socks "$TRASH_DIRECTORY/%30.sock" +' + +test_atexit ' + test ! -e "$TRASH_DIRECTORY/socks.pid" || + kill "$(cat "$TRASH_DIRECTORY/socks.pid")" +' + +# The below tests morally ought to be gated on a prerequisite that Git is +# linked with a libcurl that supports Unix socket paths for proxies (7.84 or +# later), but this is not easy to test right now. Instead, we || the tests with +# this function. +old_libcurl_error() { + grep -Fx "fatal: libcurl 7.84 or later is required to support paths in proxy URLs" "$1" +} + +test_expect_success SOCKS_PROXY 'clone via Unix socket' ' + test_when_finished "rm -rf clone" && + test_config_global http.proxy "socks4://localhost$PWD/%2530.sock" && { + { + GIT_TRACE_CURL=$PWD/trace git clone "$HTTPD_URL/smart/repo.git" clone 2>err && + grep -i "SOCKS4 request granted" trace + } || + old_libcurl_error err + } +' + +test_expect_success 'Unix socket requires socks*:' - <<\EOT + ! git clone -c http.proxy=localhost/path https://example.com/repo.git 2>err && { + grep -Fx "fatal: Invalid proxy URL 'localhost/path': only SOCKS proxies support paths" err || + old_libcurl_error err + } +EOT + +test_expect_success 'Unix socket requires localhost' - <<\EOT + ! git clone -c http.proxy=socks4://127.0.0.1/path https://example.com/repo.git 2>err && { + grep -Fx "fatal: Invalid proxy URL 'socks4://127.0.0.1/path': host must be localhost if a path is present" err || + old_libcurl_error err + } +EOT + test_done diff --git a/t/t5570-git-daemon.sh b/t/t5570-git-daemon.sh index f9a9bf9503..c5f08b6799 100755 --- a/t/t5570-git-daemon.sh +++ b/t/t5570-git-daemon.sh @@ -4,6 +4,7 @@ test_description='test fetching over git protocol' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh . "$TEST_DIRECTORY"/lib-git-daemon.sh diff --git a/t/t6007-rev-list-cherry-pick-file.sh b/t/t6007-rev-list-cherry-pick-file.sh index 6f3e543977..2d337d7287 100755 --- a/t/t6007-rev-list-cherry-pick-file.sh +++ b/t/t6007-rev-list-cherry-pick-file.sh @@ -5,6 +5,7 @@ test_description='test git rev-list --cherry-pick -- file' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh # A---B---D---F diff --git a/t/t6010-merge-base.sh b/t/t6010-merge-base.sh index 44c726ea39..f96ea82e78 100755 --- a/t/t6010-merge-base.sh +++ b/t/t6010-merge-base.sh @@ -6,6 +6,7 @@ test_description='Merge base and parent list computation. ' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh M=1130000000 diff --git a/t/t6120-describe.sh b/t/t6120-describe.sh index 79e0f19deb..05ed2510d9 100755 --- a/t/t6120-describe.sh +++ b/t/t6120-describe.sh @@ -14,6 +14,7 @@ test_description='test describe' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh check_describe () { diff --git a/t/t6133-pathspec-rev-dwim.sh b/t/t6133-pathspec-rev-dwim.sh index a290ffca0d..6dd4bbbf9f 100755 --- a/t/t6133-pathspec-rev-dwim.sh +++ b/t/t6133-pathspec-rev-dwim.sh @@ -1,6 +1,8 @@ #!/bin/sh test_description='test dwim of revs versus pathspecs in revision parser' + +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success 'setup' ' diff --git a/t/t6421-merge-partial-clone.sh b/t/t6421-merge-partial-clone.sh index 711b709e75..b99f29ef9b 100755 --- a/t/t6421-merge-partial-clone.sh +++ b/t/t6421-merge-partial-clone.sh @@ -230,8 +230,9 @@ test_expect_merge_algorithm failure success 'Objects downloaded for single relev grep fetch_count trace.output | cut -d "|" -f 9 | tr -d " ." >actual && test_cmp expect actual && - # Check the number of fetch commands exec-ed - grep d0.*fetch.negotiationAlgorithm trace.output >fetches && + # Check the number of fetch commands exec-ed by filtering trace to + # child_start events by the top-level program (2nd field == d0) + grep " d0 .* child_start .*fetch.negotiationAlgorithm" trace.output >fetches && test_line_count = 2 fetches && git rev-list --objects --all --missing=print | @@ -318,8 +319,9 @@ test_expect_merge_algorithm failure success 'Objects downloaded when a directory grep fetch_count trace.output | cut -d "|" -f 9 | tr -d " ." >actual && test_cmp expect actual && - # Check the number of fetch commands exec-ed - grep d0.*fetch.negotiationAlgorithm trace.output >fetches && + # Check the number of fetch commands exec-ed by filtering trace to + # child_start events by the top-level program (2nd field == d0) + grep " d0 .* child_start .*fetch.negotiationAlgorithm" trace.output >fetches && test_line_count = 1 fetches && git rev-list --objects --all --missing=print | @@ -422,8 +424,9 @@ test_expect_merge_algorithm failure success 'Objects downloaded with lots of ren grep fetch_count trace.output | cut -d "|" -f 9 | tr -d " ." >actual && test_cmp expect actual && - # Check the number of fetch commands exec-ed - grep d0.*fetch.negotiationAlgorithm trace.output >fetches && + # Check the number of fetch commands exec-ed by filtering trace to + # child_start events by the top-level program (2nd field == d0) + grep " d0 .* child_start .*fetch.negotiationAlgorithm" trace.output >fetches && test_line_count = 4 fetches && git rev-list --objects --all --missing=print | diff --git a/t/t7064-wtstatus-pv2.sh b/t/t7064-wtstatus-pv2.sh index 11884d2fc3..06c1301222 100755 --- a/t/t7064-wtstatus-pv2.sh +++ b/t/t7064-wtstatus-pv2.sh @@ -4,6 +4,7 @@ test_description='git status --porcelain=v2 This test exercises porcelain V2 output for git status.' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh diff --git a/t/t7400-submodule-basic.sh b/t/t7400-submodule-basic.sh index 981488885f..098d8833b6 100755 --- a/t/t7400-submodule-basic.sh +++ b/t/t7400-submodule-basic.sh @@ -12,6 +12,7 @@ subcommands of git submodule. GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success 'setup - enable local submodules' ' diff --git a/t/t7704-repack-cruft.sh b/t/t7704-repack-cruft.sh index 71e1ef3a10..959e6e2648 100755 --- a/t/t7704-repack-cruft.sh +++ b/t/t7704-repack-cruft.sh @@ -330,7 +330,7 @@ test_expect_success '--max-cruft-size with pruning' ' # repack (and prune) with a --max-cruft-size to ensure # that we appropriately split the resulting set of packs git repack -d --cruft --max-cruft-size=1M \ - --cruft-expiration=10.seconds.ago && + --cruft-expiration=1000.seconds.ago && ls $packdir/pack-*.mtimes | sort >cruft.after && for cruft in $(cat cruft.after) diff --git a/t/t7810-grep.sh b/t/t7810-grep.sh index 875dcfd98f..af2cf2f78a 100755 --- a/t/t7810-grep.sh +++ b/t/t7810-grep.sh @@ -31,6 +31,7 @@ int main(int argc, const char **argv) return 0; /* char ?? */ } + EOF test_expect_success setup ' diff --git a/t/t9800-git-p4-basic.sh b/t/t9800-git-p4-basic.sh index 53af8e34ac..3e6dfce248 100755 --- a/t/t9800-git-p4-basic.sh +++ b/t/t9800-git-p4-basic.sh @@ -5,6 +5,7 @@ test_description='git p4 tests' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh test_expect_success 'start p4d' ' @@ -297,8 +298,20 @@ test_expect_success 'exit when p4 fails to produce marshaled output' ' # p4 changes, files, or describe; just in p4 print. If P4CLIENT is unset, the # message will include "Librarian checkout". test_expect_success 'exit gracefully for p4 server errors' ' - test_when_finished "mv \"$db\"/depot/file1,v,hidden \"$db\"/depot/file1,v" && - mv "$db"/depot/file1,v "$db"/depot/file1,v,hidden && + # Note that newer Perforce versions started to store files + # compressed in directories. The case statement handles both + # old and new layout. + case "$(echo "$db"/depot/file1*)" in + *,v) + test_when_finished "mv \"$db\"/depot/file1,v,hidden \"$db\"/depot/file1,v" && + mv "$db"/depot/file1,v "$db"/depot/file1,v,hidden;; + *,d) + path="$(echo "$db"/depot/file1,d/*.gz)" && + test_when_finished "mv \"$path\",hidden \"$path\"" && + mv "$path" "$path",hidden;; + *) + BUG "unhandled p4d layout";; + esac && test_when_finished cleanup_git && test_expect_code 1 git p4 clone --dest="$git" //depot@1 >out 2>err && test_grep "Error from p4 print" err diff --git a/t/t9801-git-p4-branch.sh b/t/t9801-git-p4-branch.sh index c598011635..cdbfacc727 100755 --- a/t/t9801-git-p4-branch.sh +++ b/t/t9801-git-p4-branch.sh @@ -5,6 +5,7 @@ test_description='git p4 tests for p4 branches' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh test_expect_success 'start p4d' ' diff --git a/t/t9802-git-p4-filetype.sh b/t/t9802-git-p4-filetype.sh index bb236cd2b5..1bc48305b0 100755 --- a/t/t9802-git-p4-filetype.sh +++ b/t/t9802-git-p4-filetype.sh @@ -2,6 +2,7 @@ test_description='git p4 filetype tests' +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh test_expect_success 'start p4d' ' @@ -300,10 +301,22 @@ test_expect_success SYMLINKS 'empty symlink target' ' # text # @@ # + # Note that newer Perforce versions started to store files + # compressed in directories. The case statement handles both + # old and new layout. cd "$db/depot" && - sed "/@target1/{; s/target1/@/; n; d; }" \ - empty-symlink,v >empty-symlink,v.tmp && - mv empty-symlink,v.tmp empty-symlink,v + case "$(echo empty-symlink*)" in + empty-symlink,v) + sed "/@target1/{; s/target1/@/; n; d; }" \ + empty-symlink,v >empty-symlink,v.tmp && + mv empty-symlink,v.tmp empty-symlink,v;; + empty-symlink,d) + path="empty-symlink,d/$(ls empty-symlink,d/ | tail -n1)" && + rm "$path" && + gzip </dev/null >"$path";; + *) + BUG "unhandled p4d layout";; + esac ) && ( # Make sure symlink really is empty. Asking diff --git a/t/t9803-git-p4-shell-metachars.sh b/t/t9803-git-p4-shell-metachars.sh index 2913277013..ab7fe16266 100755 --- a/t/t9803-git-p4-shell-metachars.sh +++ b/t/t9803-git-p4-shell-metachars.sh @@ -2,6 +2,7 @@ test_description='git p4 transparency to shell metachars in filenames' +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh test_expect_success 'start p4d' ' diff --git a/t/t9804-git-p4-label.sh b/t/t9804-git-p4-label.sh index 3236457106..c8963fd398 100755 --- a/t/t9804-git-p4-label.sh +++ b/t/t9804-git-p4-label.sh @@ -2,6 +2,7 @@ test_description='git p4 label tests' +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh test_expect_success 'start p4d' ' diff --git a/t/t9805-git-p4-skip-submit-edit.sh b/t/t9805-git-p4-skip-submit-edit.sh index 90ef647db7..72dce3d2b4 100755 --- a/t/t9805-git-p4-skip-submit-edit.sh +++ b/t/t9805-git-p4-skip-submit-edit.sh @@ -2,6 +2,7 @@ test_description='git p4 skipSubmitEdit config variables' +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh test_expect_success 'start p4d' ' diff --git a/t/t9806-git-p4-options.sh b/t/t9806-git-p4-options.sh index c26d297433..e4ce44ebf3 100755 --- a/t/t9806-git-p4-options.sh +++ b/t/t9806-git-p4-options.sh @@ -5,6 +5,7 @@ test_description='git p4 options' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh test_expect_success 'start p4d' ' diff --git a/t/t9808-git-p4-chdir.sh b/t/t9808-git-p4-chdir.sh index 58a9b3b71e..342f7f3d4a 100755 --- a/t/t9808-git-p4-chdir.sh +++ b/t/t9808-git-p4-chdir.sh @@ -2,6 +2,7 @@ test_description='git p4 relative chdir' +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh test_expect_success 'start p4d' ' diff --git a/t/t9809-git-p4-client-view.sh b/t/t9809-git-p4-client-view.sh index 9c9710d8c7..f33fdea889 100755 --- a/t/t9809-git-p4-client-view.sh +++ b/t/t9809-git-p4-client-view.sh @@ -2,6 +2,7 @@ test_description='git p4 client view' +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh test_expect_success 'start p4d' ' diff --git a/t/t9810-git-p4-rcs.sh b/t/t9810-git-p4-rcs.sh index 5fe83315ec..15e32c9f35 100755 --- a/t/t9810-git-p4-rcs.sh +++ b/t/t9810-git-p4-rcs.sh @@ -2,6 +2,7 @@ test_description='git p4 rcs keywords' +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh CP1252="\223\224" diff --git a/t/t9811-git-p4-label-import.sh b/t/t9811-git-p4-label-import.sh index 5ac5383fb7..52a4b0af81 100755 --- a/t/t9811-git-p4-label-import.sh +++ b/t/t9811-git-p4-label-import.sh @@ -5,6 +5,7 @@ test_description='git p4 label tests' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh test_expect_success 'start p4d' ' diff --git a/t/t9812-git-p4-wildcards.sh b/t/t9812-git-p4-wildcards.sh index 254a7c2446..46aa5fd56c 100755 --- a/t/t9812-git-p4-wildcards.sh +++ b/t/t9812-git-p4-wildcards.sh @@ -2,6 +2,7 @@ test_description='git p4 wildcards' +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh test_expect_success 'start p4d' ' diff --git a/t/t9813-git-p4-preserve-users.sh b/t/t9813-git-p4-preserve-users.sh index fd018c87a8..0efea28da2 100755 --- a/t/t9813-git-p4-preserve-users.sh +++ b/t/t9813-git-p4-preserve-users.sh @@ -2,6 +2,7 @@ test_description='git p4 preserve users' +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh test_expect_success 'start p4d' ' diff --git a/t/t9814-git-p4-rename.sh b/t/t9814-git-p4-rename.sh index 2a9838f37f..00df6ebd3b 100755 --- a/t/t9814-git-p4-rename.sh +++ b/t/t9814-git-p4-rename.sh @@ -2,6 +2,7 @@ test_description='git p4 rename' +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh test_expect_success 'start p4d' ' diff --git a/t/t9815-git-p4-submit-fail.sh b/t/t9815-git-p4-submit-fail.sh index c766fd159f..92ef9d8c24 100755 --- a/t/t9815-git-p4-submit-fail.sh +++ b/t/t9815-git-p4-submit-fail.sh @@ -2,6 +2,7 @@ test_description='git p4 submit failure handling' +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh test_expect_success 'start p4d' ' diff --git a/t/t9816-git-p4-locked.sh b/t/t9816-git-p4-locked.sh index 5e904ac80d..e687fbc25f 100755 --- a/t/t9816-git-p4-locked.sh +++ b/t/t9816-git-p4-locked.sh @@ -2,6 +2,7 @@ test_description='git p4 locked file behavior' +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh test_expect_success 'start p4d' ' diff --git a/t/t9817-git-p4-exclude.sh b/t/t9817-git-p4-exclude.sh index ec3d937c6a..3deb334fed 100755 --- a/t/t9817-git-p4-exclude.sh +++ b/t/t9817-git-p4-exclude.sh @@ -2,6 +2,7 @@ test_description='git p4 tests for excluded paths during clone and sync' +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh test_expect_success 'start p4d' ' diff --git a/t/t9818-git-p4-block.sh b/t/t9818-git-p4-block.sh index de591d875c..091bb72bdb 100755 --- a/t/t9818-git-p4-block.sh +++ b/t/t9818-git-p4-block.sh @@ -2,6 +2,7 @@ test_description='git p4 fetching changes in multiple blocks' +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh test_expect_success 'start p4d' ' diff --git a/t/t9819-git-p4-case-folding.sh b/t/t9819-git-p4-case-folding.sh index b4d93f0c17..985be20357 100755 --- a/t/t9819-git-p4-case-folding.sh +++ b/t/t9819-git-p4-case-folding.sh @@ -2,6 +2,7 @@ test_description='interaction with P4 case-folding' +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh if test_have_prereq CASE_INSENSITIVE_FS diff --git a/t/t9820-git-p4-editor-handling.sh b/t/t9820-git-p4-editor-handling.sh index fa1bba1dd9..48e4dfb95c 100755 --- a/t/t9820-git-p4-editor-handling.sh +++ b/t/t9820-git-p4-editor-handling.sh @@ -2,6 +2,7 @@ test_description='git p4 handling of EDITOR' +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh test_expect_success 'start p4d' ' diff --git a/t/t9821-git-p4-path-variations.sh b/t/t9821-git-p4-path-variations.sh index ef80f1690b..49691c53da 100755 --- a/t/t9821-git-p4-path-variations.sh +++ b/t/t9821-git-p4-path-variations.sh @@ -2,6 +2,7 @@ test_description='Clone repositories with path case variations' +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh test_expect_success 'start p4d with case folding enabled' ' diff --git a/t/t9822-git-p4-path-encoding.sh b/t/t9822-git-p4-path-encoding.sh index 572d395498..e62ed49f51 100755 --- a/t/t9822-git-p4-path-encoding.sh +++ b/t/t9822-git-p4-path-encoding.sh @@ -2,6 +2,7 @@ test_description='Clone repositories with non ASCII paths' +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh UTF8_ESCAPED="a-\303\244_o-\303\266_u-\303\274.txt" diff --git a/t/t9823-git-p4-mock-lfs.sh b/t/t9823-git-p4-mock-lfs.sh index 88b76dc4d6..98a40d8af3 100755 --- a/t/t9823-git-p4-mock-lfs.sh +++ b/t/t9823-git-p4-mock-lfs.sh @@ -2,6 +2,7 @@ test_description='Clone repositories and store files in Mock LFS' +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh test_file_is_not_in_mock_lfs () { diff --git a/t/t9825-git-p4-handle-utf16-without-bom.sh b/t/t9825-git-p4-handle-utf16-without-bom.sh index f049ff8229..d0b86537dd 100755 --- a/t/t9825-git-p4-handle-utf16-without-bom.sh +++ b/t/t9825-git-p4-handle-utf16-without-bom.sh @@ -2,6 +2,7 @@ test_description='git p4 handling of UTF-16 files without BOM' +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh UTF16="\227\000\227\000" @@ -22,9 +23,25 @@ test_expect_success 'init depot with UTF-16 encoded file and artificially remove cd db && p4d -jc && # P4D automatically adds a BOM. Remove it here to make the file invalid. - sed -e "\$d" depot/file1,v >depot/file1,v.new && - mv depot/file1,v.new depot/file1,v && - printf "@$UTF16@" >>depot/file1,v && + # + # Note that newer Perforce versions started to store files + # compressed in directories. The case statement handles both + # old and new layout. + case "$(echo depot/file1*)" in + depot/file1,v) + sed -e "\$d" depot/file1,v >depot/file1,v.new && + mv depot/file1,v.new depot/file1,v && + printf "@$UTF16@" >>depot/file1,v;; + depot/file1,d) + path="$(echo depot/file1,d/*.gz)" && + gunzip -c "$path" >"$path.unzipped" && + sed -e "\$d" "$path.unzipped" >"$path.new" && + printf "$UTF16" >>"$path.new" && + gzip -c "$path.new" >"$path" && + rm "$path.unzipped" "$path.new";; + *) + BUG "unhandled p4d layout";; + esac && p4d -jrF checkpoint.1 ) ' diff --git a/t/t9826-git-p4-keep-empty-commits.sh b/t/t9826-git-p4-keep-empty-commits.sh index fd64afe064..54083f842e 100755 --- a/t/t9826-git-p4-keep-empty-commits.sh +++ b/t/t9826-git-p4-keep-empty-commits.sh @@ -2,6 +2,7 @@ test_description='Clone repositories and keep empty commits' +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh test_expect_success 'start p4d' ' diff --git a/t/t9827-git-p4-change-filetype.sh b/t/t9827-git-p4-change-filetype.sh index d3670bd7a2..3476ea2fd3 100755 --- a/t/t9827-git-p4-change-filetype.sh +++ b/t/t9827-git-p4-change-filetype.sh @@ -2,6 +2,7 @@ test_description='git p4 support for file type change' +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh test_expect_success 'start p4d' ' diff --git a/t/t9828-git-p4-map-user.sh b/t/t9828-git-p4-map-user.sh index ca6c2942bd..7c8f9e3930 100755 --- a/t/t9828-git-p4-map-user.sh +++ b/t/t9828-git-p4-map-user.sh @@ -2,6 +2,7 @@ test_description='Clone repositories and map users' +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh test_expect_success 'start p4d' ' diff --git a/t/t9829-git-p4-jobs.sh b/t/t9829-git-p4-jobs.sh index 88cfb1fcd3..3fc0948d9c 100755 --- a/t/t9829-git-p4-jobs.sh +++ b/t/t9829-git-p4-jobs.sh @@ -2,6 +2,7 @@ test_description='git p4 retrieve job info' +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh test_expect_success 'start p4d' ' diff --git a/t/t9830-git-p4-symlink-dir.sh b/t/t9830-git-p4-symlink-dir.sh index 3fb6960c18..02561a7f0e 100755 --- a/t/t9830-git-p4-symlink-dir.sh +++ b/t/t9830-git-p4-symlink-dir.sh @@ -2,6 +2,7 @@ test_description='git p4 symlinked directories' +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh test_expect_success 'start p4d' ' diff --git a/t/t9831-git-p4-triggers.sh b/t/t9831-git-p4-triggers.sh index ff6c0352e6..f287f41e37 100755 --- a/t/t9831-git-p4-triggers.sh +++ b/t/t9831-git-p4-triggers.sh @@ -2,6 +2,7 @@ test_description='git p4 with server triggers' +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh test_expect_success 'start p4d' ' diff --git a/t/t9832-unshelve.sh b/t/t9832-unshelve.sh index 6b3cb0414a..a266775408 100755 --- a/t/t9832-unshelve.sh +++ b/t/t9832-unshelve.sh @@ -6,6 +6,7 @@ last_shelved_change () { test_description='git p4 unshelve' +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh test_expect_success 'start p4d' ' diff --git a/t/t9833-errors.sh b/t/t9833-errors.sh index e22369ccdf..da1d30c142 100755 --- a/t/t9833-errors.sh +++ b/t/t9833-errors.sh @@ -2,6 +2,7 @@ test_description='git p4 errors' +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh test_expect_success 'start p4d' ' diff --git a/t/t9834-git-p4-file-dir-bug.sh b/t/t9834-git-p4-file-dir-bug.sh index dac67e89d7..565870fc74 100755 --- a/t/t9834-git-p4-file-dir-bug.sh +++ b/t/t9834-git-p4-file-dir-bug.sh @@ -6,6 +6,7 @@ This test creates files and directories with the same name in perforce and checks that git-p4 recovers from the error at the same time as the perforce repository.' +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh test_expect_success 'start p4d' ' diff --git a/t/t9835-git-p4-metadata-encoding-python2.sh b/t/t9835-git-p4-metadata-encoding-python2.sh index 036bf79c66..ad20ffdede 100755 --- a/t/t9835-git-p4-metadata-encoding-python2.sh +++ b/t/t9835-git-p4-metadata-encoding-python2.sh @@ -6,6 +6,7 @@ This test checks that the import process handles inconsistent text encoding in p4 metadata (author names, commit messages, etc) without failing, and produces maximally sane output in git.' +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh python_target_version='2' diff --git a/t/t9836-git-p4-metadata-encoding-python3.sh b/t/t9836-git-p4-metadata-encoding-python3.sh index 63350dc4b5..71ae763399 100755 --- a/t/t9836-git-p4-metadata-encoding-python3.sh +++ b/t/t9836-git-p4-metadata-encoding-python3.sh @@ -6,6 +6,7 @@ This test checks that the import process handles inconsistent text encoding in p4 metadata (author names, commit messages, etc) without failing, and produces maximally sane output in git.' +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh python_target_version='3' diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh index 932d5ad759..cc6aa9f0cd 100755 --- a/t/t9902-completion.sh +++ b/t/t9902-completion.sh @@ -16,6 +16,7 @@ test_untraceable=UnfortunatelyYes GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=master export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./lib-bash.sh complete () diff --git a/t/t9903-bash-prompt.sh b/t/t9903-bash-prompt.sh index d667dda654..95e9955bca 100755 --- a/t/t9903-bash-prompt.sh +++ b/t/t9903-bash-prompt.sh @@ -8,6 +8,7 @@ test_description='test git-specific bash prompt functions' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./lib-bash.sh . "$GIT_BUILD_DIR/contrib/completion/git-prompt.sh" diff --git a/t/unit-tests/t-example-decorate.c b/t/unit-tests/t-example-decorate.c index a4a75db735..8bf0709c41 100644 --- a/t/unit-tests/t-example-decorate.c +++ b/t/unit-tests/t-example-decorate.c @@ -15,36 +15,29 @@ static void t_add(struct test_vars *vars) { void *ret = add_decoration(&vars->n, vars->one, &vars->decoration_a); - if (!check(ret == NULL)) - test_msg("when adding a brand-new object, NULL should be returned"); + check(ret == NULL); ret = add_decoration(&vars->n, vars->two, NULL); - if (!check(ret == NULL)) - test_msg("when adding a brand-new object, NULL should be returned"); + check(ret == NULL); } static void t_readd(struct test_vars *vars) { void *ret = add_decoration(&vars->n, vars->one, NULL); - if (!check(ret == &vars->decoration_a)) - test_msg("when readding an already existing object, existing decoration should be returned"); + check(ret == &vars->decoration_a); ret = add_decoration(&vars->n, vars->two, &vars->decoration_b); - if (!check(ret == NULL)) - test_msg("when readding an already existing object, existing decoration should be returned"); + check(ret == NULL); } static void t_lookup(struct test_vars *vars) { void *ret = lookup_decoration(&vars->n, vars->one); - if (!check(ret == NULL)) - test_msg("lookup should return added declaration"); + check(ret == NULL); ret = lookup_decoration(&vars->n, vars->two); - if (!check(ret == &vars->decoration_b)) - test_msg("lookup should return added declaration"); + check(ret == &vars->decoration_b); ret = lookup_decoration(&vars->n, vars->three); - if (!check(ret == NULL)) - test_msg("lookup for unknown object should return NULL"); + check(ret == NULL); } static void t_loop(struct test_vars *vars) @@ -55,8 +48,7 @@ static void t_loop(struct test_vars *vars) if (vars->n.entries[i].base) objects_noticed++; } - if (!check_int(objects_noticed, ==, 2)) - test_msg("should have 2 objects"); + check_int(objects_noticed, ==, 2); } int cmd_main(int argc UNUSED, const char **argv UNUSED) diff --git a/t/unit-tests/t-reftable-pq.c b/t/unit-tests/t-reftable-pq.c new file mode 100644 index 0000000000..039bd0f1f9 --- /dev/null +++ b/t/unit-tests/t-reftable-pq.c @@ -0,0 +1,152 @@ +/* +Copyright 2020 Google LLC + +Use of this source code is governed by a BSD-style +license that can be found in the LICENSE file or at +https://developers.google.com/open-source/licenses/bsd +*/ + +#include "test-lib.h" +#include "reftable/constants.h" +#include "reftable/pq.h" + +static void merged_iter_pqueue_check(const struct merged_iter_pqueue *pq) +{ + for (size_t i = 1; i < pq->len; i++) { + size_t parent = (i - 1) / 2; + check(pq_less(&pq->heap[parent], &pq->heap[i])); + } +} + +static int pq_entry_equal(struct pq_entry *a, struct pq_entry *b) +{ + return !reftable_record_cmp(a->rec, b->rec) && (a->index == b->index); +} + +static void t_pq_record(void) +{ + struct merged_iter_pqueue pq = { 0 }; + struct reftable_record recs[54]; + size_t N = ARRAY_SIZE(recs) - 1, i; + char *last = NULL; + + for (i = 0; i < N; i++) { + reftable_record_init(&recs[i], BLOCK_TYPE_REF); + recs[i].u.ref.refname = xstrfmt("%02"PRIuMAX, (uintmax_t)i); + } + + i = 1; + do { + struct pq_entry e = { + .rec = &recs[i], + }; + + merged_iter_pqueue_add(&pq, &e); + merged_iter_pqueue_check(&pq); + i = (i * 7) % N; + } while (i != 1); + + while (!merged_iter_pqueue_is_empty(pq)) { + struct pq_entry top = merged_iter_pqueue_top(pq); + struct pq_entry e = merged_iter_pqueue_remove(&pq); + merged_iter_pqueue_check(&pq); + + check(pq_entry_equal(&top, &e)); + check(reftable_record_type(e.rec) == BLOCK_TYPE_REF); + if (last) + check_int(strcmp(last, e.rec->u.ref.refname), <, 0); + last = e.rec->u.ref.refname; + } + + for (i = 0; i < N; i++) + reftable_record_release(&recs[i]); + merged_iter_pqueue_release(&pq); +} + +static void t_pq_index(void) +{ + struct merged_iter_pqueue pq = { 0 }; + struct reftable_record recs[13]; + char *last = NULL; + size_t N = ARRAY_SIZE(recs), i; + + for (i = 0; i < N; i++) { + reftable_record_init(&recs[i], BLOCK_TYPE_REF); + recs[i].u.ref.refname = (char *) "refs/heads/master"; + } + + i = 1; + do { + struct pq_entry e = { + .rec = &recs[i], + .index = i, + }; + + merged_iter_pqueue_add(&pq, &e); + merged_iter_pqueue_check(&pq); + i = (i * 7) % N; + } while (i != 1); + + for (i = N - 1; i > 0; i--) { + struct pq_entry top = merged_iter_pqueue_top(pq); + struct pq_entry e = merged_iter_pqueue_remove(&pq); + merged_iter_pqueue_check(&pq); + + check(pq_entry_equal(&top, &e)); + check(reftable_record_type(e.rec) == BLOCK_TYPE_REF); + check_int(e.index, ==, i); + if (last) + check_str(last, e.rec->u.ref.refname); + last = e.rec->u.ref.refname; + } + + merged_iter_pqueue_release(&pq); +} + +static void t_merged_iter_pqueue_top(void) +{ + struct merged_iter_pqueue pq = { 0 }; + struct reftable_record recs[13]; + size_t N = ARRAY_SIZE(recs), i; + + for (i = 0; i < N; i++) { + reftable_record_init(&recs[i], BLOCK_TYPE_REF); + recs[i].u.ref.refname = (char *) "refs/heads/master"; + } + + i = 1; + do { + struct pq_entry e = { + .rec = &recs[i], + .index = i, + }; + + merged_iter_pqueue_add(&pq, &e); + merged_iter_pqueue_check(&pq); + i = (i * 7) % N; + } while (i != 1); + + for (i = N - 1; i > 0; i--) { + struct pq_entry top = merged_iter_pqueue_top(pq); + struct pq_entry e = merged_iter_pqueue_remove(&pq); + + merged_iter_pqueue_check(&pq); + check(pq_entry_equal(&top, &e)); + check(reftable_record_equal(top.rec, &recs[i], GIT_SHA1_RAWSZ)); + for (size_t j = 0; i < pq.len; j++) { + check(pq_less(&top, &pq.heap[j])); + check_int(top.index, >, j); + } + } + + merged_iter_pqueue_release(&pq); +} + +int cmd_main(int argc, const char *argv[]) +{ + TEST(t_pq_record(), "pq works with record-based comparison"); + TEST(t_pq_index(), "pq works with index-based comparison"); + TEST(t_merged_iter_pqueue_top(), "merged_iter_pqueue_top works"); + + return test_done(); +} diff --git a/t/unit-tests/t-reftable-tree.c b/t/unit-tests/t-reftable-tree.c new file mode 100644 index 0000000000..e7d774d774 --- /dev/null +++ b/t/unit-tests/t-reftable-tree.c @@ -0,0 +1,84 @@ +/* +Copyright 2020 Google LLC + +Use of this source code is governed by a BSD-style +license that can be found in the LICENSE file or at +https://developers.google.com/open-source/licenses/bsd +*/ + +#include "test-lib.h" +#include "reftable/tree.h" + +static int t_compare(const void *a, const void *b) +{ + return (char *)a - (char *)b; +} + +struct curry { + void **arr; + size_t len; +}; + +static void store(void *arg, void *key) +{ + struct curry *c = arg; + c->arr[c->len++] = key; +} + +static void t_tree_search(void) +{ + struct tree_node *root = NULL; + void *values[11] = { 0 }; + struct tree_node *nodes[11] = { 0 }; + size_t i = 1; + + /* + * Pseudo-randomly insert the pointers for elements between + * values[1] and values[10] (inclusive) in the tree. + */ + do { + nodes[i] = tree_search(&values[i], &root, &t_compare, 1); + i = (i * 7) % 11; + } while (i != 1); + + for (i = 1; i < ARRAY_SIZE(nodes); i++) { + check_pointer_eq(&values[i], nodes[i]->key); + check_pointer_eq(nodes[i], tree_search(&values[i], &root, &t_compare, 0)); + } + + check(!tree_search(values, &root, t_compare, 0)); + tree_free(root); +} + +static void t_infix_walk(void) +{ + struct tree_node *root = NULL; + void *values[11] = { 0 }; + void *out[11] = { 0 }; + struct curry c = { + .arr = (void **) &out, + }; + size_t i = 1; + size_t count = 0; + + do { + tree_search(&values[i], &root, t_compare, 1); + i = (i * 7) % 11; + count++; + } while (i != 1); + + infix_walk(root, &store, &c); + for (i = 1; i < ARRAY_SIZE(values); i++) + check_pointer_eq(&values[i], out[i - 1]); + check(!out[i - 1]); + check_int(c.len, ==, count); + tree_free(root); +} + +int cmd_main(int argc, const char *argv[]) +{ + TEST(t_tree_search(), "tree_search works"); + TEST(t_infix_walk(), "infix_walk works"); + + return test_done(); +} diff --git a/t/unit-tests/test-lib.h b/t/unit-tests/test-lib.h index 2de6d715d5..c59f646fd9 100644 --- a/t/unit-tests/test-lib.h +++ b/t/unit-tests/test-lib.h @@ -76,8 +76,9 @@ int test_assert(const char *location, const char *check, int ok); int check_bool_loc(const char *loc, const char *check, int ok); /* - * Compare two integers. Prints a message with the two values if the - * comparison fails. NB this is not thread safe. + * Compare the equality of two pointers of same type. Prints a message + * with the two values if the equality fails. NB this is not thread + * safe. */ #define check_pointer_eq(a, b) \ (test__tmp[0].p = (a), test__tmp[1].p = (b), \ diff --git a/transport.c b/transport.c index 12cc5b4d96..7c4af9f56f 100644 --- a/transport.c +++ b/transport.c @@ -1115,6 +1115,7 @@ static struct transport_vtable builtin_smart_vtable = { struct transport *transport_get(struct remote *remote, const char *url) { const char *helper; + char *helper_to_free = NULL; const char *p; struct transport *ret = xcalloc(1, sizeof(*ret)); @@ -1139,10 +1140,11 @@ struct transport *transport_get(struct remote *remote, const char *url) while (is_urlschemechar(p == url, *p)) p++; if (starts_with(p, "::")) - helper = xstrndup(url, p - url); + helper = helper_to_free = xstrndup(url, p - url); if (helper) { transport_helper_init(ret, helper); + free(helper_to_free); } else if (starts_with(url, "rsync:")) { die(_("git-over-rsync is no longer supported")); } else if (url_is_local_not_ssh(url) && is_file(url) && is_bundle(url, 1)) { |
