diff options
40 files changed, 621 insertions, 149 deletions
diff --git a/Documentation/RelNotes/2.37.2.txt b/Documentation/RelNotes/2.37.2.txt index 300fb095c4..d82b29e014 100644 --- a/Documentation/RelNotes/2.37.2.txt +++ b/Documentation/RelNotes/2.37.2.txt @@ -61,4 +61,28 @@ Fixes since v2.37.1 * Workaround for a false positive compiler warning. + * The resolve-undo information in the index was not protected against + GC, which has been corrected. + + * A corner case bug where lazily fetching objects from a promisor + remote resulted in infinite recursion has been corrected. + + * "git p4" working on UTF-16 files on Windows did not implement + CRLF-to-LF conversion correctly, which has been corrected. + + * "git p4" did not handle non-ASCII client name well, which has been + corrected. + + * "rerere-train" script (in contrib/) used to honor commit.gpgSign + while recreating the throw-away merges. + + * "git checkout" miscounted the paths it updated, which has been + corrected. + + * Fix for a bug that makes write-tree to fail to write out a + non-existent index as a tree, introduced in 2.37. + + * There was a bug in the codepath to upgrade generation information + in commit-graph from v1 to v2 format, which has been corrected. + Also contains minor documentation updates and code clean-ups. diff --git a/Documentation/git-reflog.txt b/Documentation/git-reflog.txt index 5ced7ad4f8..db9d46edfa 100644 --- a/Documentation/git-reflog.txt +++ b/Documentation/git-reflog.txt @@ -22,7 +22,7 @@ depending on the subcommand: [--rewrite] [--updateref] [--stale-fix] [--dry-run | -n] [--verbose] [--all [--single-worktree] | <refs>...] 'git reflog delete' [--rewrite] [--updateref] - [--dry-run | -n] [--verbose] <ref>@\{<specifier>\}... + [--dry-run | -n] [--verbose] <ref>@{<specifier>}... 'git reflog exists' <ref> Reference logs, or "reflogs", record when the tips of branches and diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN index 1b2c580ecc..e2d51b505d 100755 --- a/GIT-VERSION-GEN +++ b/GIT-VERSION-GEN @@ -1,7 +1,7 @@ #!/bin/sh GVF=GIT-VERSION-FILE -DEF_VER=v2.37.1 +DEF_VER=v2.37.2 LF=' ' @@ -30,10 +30,9 @@ static inline unsigned char get_bitmask(uint32_t pos) static int load_bloom_filter_from_graph(struct commit_graph *g, struct bloom_filter *filter, - struct commit *c) + uint32_t graph_pos) { uint32_t lex_pos, start_index, end_index; - uint32_t graph_pos = commit_graph_position(c); while (graph_pos < g->num_commits_in_base) g = g->base_graph; @@ -203,9 +202,10 @@ struct bloom_filter *get_or_compute_bloom_filter(struct repository *r, filter = bloom_filter_slab_at(&bloom_filters, c); if (!filter->data) { - load_commit_graph_info(r, c); - if (commit_graph_position(c) != COMMIT_NOT_FROM_GRAPH) - load_bloom_filter_from_graph(r->objects->commit_graph, filter, c); + uint32_t graph_pos; + if (repo_find_commit_pos_in_graph(r, c, &graph_pos)) + load_bloom_filter_from_graph(r->objects->commit_graph, + filter, graph_pos); } if (filter->data && filter->len) diff --git a/builtin/checkout.c b/builtin/checkout.c index 2eefda81d8..df3f1663d7 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -417,7 +417,7 @@ static int checkout_worktree(const struct checkout_opts *opts, mem_pool_discard(&ce_mem_pool, should_validate_cache_entries()); remove_marked_cache_entries(&the_index, 1); remove_scheduled_dirs(); - errs |= finish_delayed_checkout(&state, &nr_checkouts, opts->show_progress); + errs |= finish_delayed_checkout(&state, opts->show_progress); if (opts->count_checkout_paths) { if (nr_unmerged) diff --git a/builtin/fsck.c b/builtin/fsck.c index 9e54892311..6c73092f10 100644 --- a/builtin/fsck.c +++ b/builtin/fsck.c @@ -19,6 +19,7 @@ #include "decorate.h" #include "packfile.h" #include "object-store.h" +#include "resolve-undo.h" #include "run-command.h" #include "worktree.h" @@ -757,6 +758,43 @@ static int fsck_cache_tree(struct cache_tree *it) return err; } +static int fsck_resolve_undo(struct index_state *istate) +{ + struct string_list_item *item; + struct string_list *resolve_undo = istate->resolve_undo; + + if (!resolve_undo) + return 0; + + for_each_string_list_item(item, resolve_undo) { + const char *path = item->string; + struct resolve_undo_info *ru = item->util; + int i; + + if (!ru) + continue; + for (i = 0; i < 3; i++) { + struct object *obj; + + if (!ru->mode[i] || !S_ISREG(ru->mode[i])) + continue; + + obj = parse_object(the_repository, &ru->oid[i]); + if (!obj) { + error(_("%s: invalid sha1 pointer in resolve-undo"), + oid_to_hex(&ru->oid[i])); + errors_found |= ERROR_REFS; + continue; + } + obj->flags |= USED; + fsck_put_object_name(&fsck_walk_options, &ru->oid[i], + ":(%d):%s", i, path); + mark_object_reachable(obj); + } + } + return 0; +} + static void mark_object_for_connectivity(const struct object_id *oid) { struct object *obj = lookup_unknown_object(the_repository, oid); @@ -938,6 +976,7 @@ int cmd_fsck(int argc, const char **argv, const char *prefix) } if (active_cache_tree) fsck_cache_tree(active_cache_tree); + fsck_resolve_undo(&the_index); } check_connectivity(); diff --git a/builtin/remote.c b/builtin/remote.c index d4b69fe778..a3a0c27d7a 100644 --- a/builtin/remote.c +++ b/builtin/remote.c @@ -1222,10 +1222,9 @@ static int get_one_entry(struct remote *remote, void *priv) static int show_all(void) { - struct string_list list = STRING_LIST_INIT_NODUP; + struct string_list list = STRING_LIST_INIT_DUP; int result; - list.strdup_strings = 1; result = for_each_remote(get_one_entry, &list); if (!result) { diff --git a/commit-graph.c b/commit-graph.c index 92d4503336..a487d49c3e 100644 --- a/commit-graph.c +++ b/commit-graph.c @@ -889,6 +889,14 @@ static int find_commit_pos_in_graph(struct commit *item, struct commit_graph *g, } } +int repo_find_commit_pos_in_graph(struct repository *r, struct commit *c, + uint32_t *pos) +{ + if (!prepare_commit_graph(r)) + return 0; + return find_commit_pos_in_graph(c, r->objects->commit_graph, pos); +} + struct commit *lookup_commit_in_graph(struct repository *repo, const struct object_id *id) { struct commit *commit; @@ -898,7 +906,7 @@ struct commit *lookup_commit_in_graph(struct repository *repo, const struct obje return NULL; if (!search_commit_pos_in_graph(id, repo->objects->commit_graph, &pos)) return NULL; - if (!repo_has_object_file(repo, id)) + if (!has_object(repo, id, 0)) return NULL; commit = lookup_commit(repo, id); @@ -946,9 +954,7 @@ int parse_commit_in_graph(struct repository *r, struct commit *item) void load_commit_graph_info(struct repository *r, struct commit *item) { uint32_t pos; - if (!prepare_commit_graph(r)) - return; - if (find_commit_pos_in_graph(item, r->objects->commit_graph, &pos)) + if (repo_find_commit_pos_in_graph(r, item, &pos)) fill_commit_graph_info(item, r->objects->commit_graph, pos); } diff --git a/commit-graph.h b/commit-graph.h index 2e3ac35237..f23b9e9026 100644 --- a/commit-graph.h +++ b/commit-graph.h @@ -41,6 +41,21 @@ int open_commit_graph(const char *graph_file, int *fd, struct stat *st); int parse_commit_in_graph(struct repository *r, struct commit *item); /* + * Fills `*pos` with the graph position of `c`, and returns 1 if `c` is + * found in the commit-graph belonging to `r`, or 0 otherwise. + * Initializes the commit-graph belonging to `r` if it hasn't been + * already. + * + * Note: this is a low-level helper that does not alter any slab data + * associated with `c`. Useful in circumstances where the slab data is + * already being modified (e.g., writing the commit-graph itself). + * + * In most cases, callers should use `parse_commit_in_graph()` instead. + */ +int repo_find_commit_pos_in_graph(struct repository *r, struct commit *c, + uint32_t *pos); + +/* * Look up the given commit ID in the commit-graph. This will only return a * commit if the ID exists both in the graph and in the object database such * that we don't return commits whose object has been pruned. Otherwise, this diff --git a/compat/mingw.c b/compat/mingw.c index b5502997e2..901375d584 100644 --- a/compat/mingw.c +++ b/compat/mingw.c @@ -1,6 +1,7 @@ #include "../git-compat-util.h" #include "win32.h" #include <aclapi.h> +#include <sddl.h> #include <conio.h> #include <wchar.h> #include "../strbuf.h" @@ -768,8 +769,8 @@ static int has_valid_directory_prefix(wchar_t *wfilename) wfilename[n] = L'\0'; attributes = GetFileAttributesW(wfilename); wfilename[n] = c; - if (attributes == FILE_ATTRIBUTE_DIRECTORY || - attributes == FILE_ATTRIBUTE_DEVICE) + if (attributes & + (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_DEVICE)) return 1; if (attributes == INVALID_FILE_ATTRIBUTES) switch (GetLastError()) { @@ -2670,7 +2671,22 @@ static PSID get_current_user_sid(void) return result; } -int is_path_owned_by_current_sid(const char *path) +static int acls_supported(const char *path) +{ + size_t offset = offset_1st_component(path); + WCHAR wroot[MAX_PATH]; + DWORD file_system_flags; + + if (offset && + xutftowcsn(wroot, path, MAX_PATH, offset) > 0 && + GetVolumeInformationW(wroot, NULL, 0, NULL, NULL, + &file_system_flags, NULL, 0)) + return !!(file_system_flags & FILE_PERSISTENT_ACLS); + + return 0; +} + +int is_path_owned_by_current_sid(const char *path, struct strbuf *report) { WCHAR wpath[MAX_PATH]; PSID sid = NULL; @@ -2709,6 +2725,7 @@ int is_path_owned_by_current_sid(const char *path) else if (sid && IsValidSid(sid)) { /* Now, verify that the SID matches the current user's */ static PSID current_user_sid; + BOOL is_member; if (!current_user_sid) current_user_sid = get_current_user_sid(); @@ -2717,6 +2734,46 @@ int is_path_owned_by_current_sid(const char *path) IsValidSid(current_user_sid) && EqualSid(sid, current_user_sid)) result = 1; + else if (IsWellKnownSid(sid, WinBuiltinAdministratorsSid) && + CheckTokenMembership(NULL, sid, &is_member) && + is_member) + /* + * If owned by the Administrators group, and the + * current user is an administrator, we consider that + * okay, too. + */ + result = 1; + else if (report && + IsWellKnownSid(sid, WinWorldSid) && + !acls_supported(path)) { + /* + * On FAT32 volumes, ownership is not actually recorded. + */ + strbuf_addf(report, "'%s' is on a file system that does" + "not record ownership\n", path); + } else if (report) { + LPSTR str1, str2, to_free1 = NULL, to_free2 = NULL; + + if (ConvertSidToStringSidA(sid, &str1)) + to_free1 = str1; + else + str1 = "(inconvertible)"; + + if (!current_user_sid) + str2 = "(none)"; + else if (!IsValidSid(current_user_sid)) + str2 = "(invalid)"; + else if (ConvertSidToStringSidA(current_user_sid, &str2)) + to_free2 = str2; + else + str2 = "(inconvertible)"; + strbuf_addf(report, + "'%s' is owned by:\n" + "\t'%s'\nbut the current user is:\n" + "\t'%s'\n", path, str1, str2); + LocalFree(to_free1); + LocalFree(to_free2); + } } /* diff --git a/compat/mingw.h b/compat/mingw.h index a74da68f31..209cf7ceba 100644 --- a/compat/mingw.h +++ b/compat/mingw.h @@ -463,7 +463,7 @@ char *mingw_query_user_email(void); * Verifies that the specified path is owned by the user running the * current process. */ -int is_path_owned_by_current_sid(const char *path); +int is_path_owned_by_current_sid(const char *path, struct strbuf *report); #define is_path_owned_by_current_user is_path_owned_by_current_sid /** diff --git a/config.mak.dev b/config.mak.dev index 335efd4620..4fa19d361b 100644 --- a/config.mak.dev +++ b/config.mak.dev @@ -59,9 +59,13 @@ endif # uninitialized warnings on gcc 4.9.2 in xdiff/xdiffi.c and config.c # not worth fixing since newer compilers correctly stop complaining +# +# Likewise, gcc older than 4.9 complains about initializing a +# struct-within-a-struct using just "{ 0 }" ifneq ($(filter gcc4,$(COMPILER_FEATURES)),) ifeq ($(filter gcc5,$(COMPILER_FEATURES)),) DEVELOPER_CFLAGS += -Wno-uninitialized +DEVELOPER_CFLAGS += -Wno-missing-braces endif endif diff --git a/config.mak.uname b/config.mak.uname index ce83cad47a..d63629fe80 100644 --- a/config.mak.uname +++ b/config.mak.uname @@ -656,7 +656,6 @@ ifeq ($(uname_S),MINGW) UNRELIABLE_FSTAT = UnfortunatelyYes OBJECT_CREATION_USES_RENAMES = UnfortunatelyNeedsTo NO_REGEX = YesPlease - NO_PYTHON = YesPlease ETAGS_TARGET = ETAGS NO_POSIX_GOODIES = UnfortunatelyYes DEFAULT_HELP_FORMAT = html @@ -686,6 +685,7 @@ ifneq (,$(wildcard ../THIS_IS_MSYSGIT)) INTERNAL_QSORT = YesPlease HAVE_LIBCHARSET_H = YesPlease NO_GETTEXT = YesPlease + NO_PYTHON = YesPlease COMPAT_CFLAGS += -D__USE_MINGW_ACCESS else ifneq ($(shell expr "$(uname_R)" : '1\.'),2) @@ -717,10 +717,8 @@ else INSTALL = /bin/install INTERNAL_QSORT = YesPlease HAVE_LIBCHARSET_H = YesPlease - NO_GETTEXT = USE_GETTEXT_SCHEME = fallthrough USE_LIBPCRE = YesPlease - NO_CURL = USE_NED_ALLOCATOR = YesPlease ifeq (/mingw64,$(subst 32,64,$(prefix))) # Move system config into top-level /etc/ @@ -730,6 +728,7 @@ else else COMPAT_CFLAGS += -D__USE_MINGW_ANSI_STDIO NO_CURL = YesPlease + NO_PYTHON = YesPlease endif endif endif diff --git a/contrib/buildsystems/CMakeLists.txt b/contrib/buildsystems/CMakeLists.txt index 1b23f2440d..2237109b57 100644 --- a/contrib/buildsystems/CMakeLists.txt +++ b/contrib/buildsystems/CMakeLists.txt @@ -77,7 +77,7 @@ if(USE_VCPKG) set(CMAKE_TOOLCHAIN_FILE ${VCPKG_DIR}/scripts/buildsystems/vcpkg.cmake CACHE STRING "Vcpkg toolchain file") endif() -find_program(SH_EXE sh PATHS "C:/Program Files/Git/bin") +find_program(SH_EXE sh PATHS "C:/Program Files/Git/bin" "$ENV{LOCALAPPDATA}/Programs/Git/bin") if(NOT SH_EXE) message(FATAL_ERROR "sh: shell interpreter was not found in your path, please install one." "On Windows, you can get it as part of 'Git for Windows' install at https://gitforwindows.org/") diff --git a/contrib/rerere-train.sh b/contrib/rerere-train.sh index 26b724c8c6..bd01e430ef 100755 --- a/contrib/rerere-train.sh +++ b/contrib/rerere-train.sh @@ -75,7 +75,7 @@ do continue fi git checkout -q "$parent1^0" - if git merge $other_parents >/dev/null 2>&1 + if git merge --no-gpg-sign $other_parents >/dev/null 2>&1 then # Cleanly merges continue @@ -53,7 +53,11 @@ struct delayed_checkout { enum ce_delay_state state; /* List of filter drivers that signaled delayed blobs. */ struct string_list filters; - /* List of delayed blobs identified by their path. */ + /* + * List of delayed blobs identified by their path. The `util` member + * holds a counter pointer which must be incremented when/if the + * associated blob gets checked out. + */ struct string_list paths; }; @@ -157,12 +157,11 @@ static int remove_available_paths(struct string_list_item *item, void *cb_data) available = string_list_lookup(available_paths, item->string); if (available) - available->util = (void *)item->string; + available->util = item->util; return !available; } -int finish_delayed_checkout(struct checkout *state, int *nr_checkouts, - int show_progress) +int finish_delayed_checkout(struct checkout *state, int show_progress) { int errs = 0; unsigned processed_paths = 0; @@ -227,7 +226,7 @@ int finish_delayed_checkout(struct checkout *state, int *nr_checkouts, strlen(path->string), 0); if (ce) { display_progress(progress, ++processed_paths); - errs |= checkout_entry(ce, state, NULL, nr_checkouts); + errs |= checkout_entry(ce, state, NULL, path->util); filtered_bytes += ce->ce_stat_data.sd_size; display_throughput(progress, filtered_bytes); } else @@ -266,7 +265,8 @@ void update_ce_after_write(const struct checkout *state, struct cache_entry *ce, /* Note: ca is used (and required) iff the entry refers to a regular file. */ static int write_entry(struct cache_entry *ce, char *path, struct conv_attrs *ca, - const struct checkout *state, int to_tempfile) + const struct checkout *state, int to_tempfile, + int *nr_checkouts) { unsigned int ce_mode_s_ifmt = ce->ce_mode & S_IFMT; struct delayed_checkout *dco = state->delayed_checkout; @@ -279,6 +279,7 @@ static int write_entry(struct cache_entry *ce, char *path, struct conv_attrs *ca struct stat st; const struct submodule *sub; struct checkout_metadata meta; + static int scratch_nr_checkouts; clone_checkout_metadata(&meta, &state->meta, &ce->oid); @@ -333,9 +334,15 @@ static int write_entry(struct cache_entry *ce, char *path, struct conv_attrs *ca ret = async_convert_to_working_tree_ca(ca, ce->name, new_blob, size, &buf, &meta, dco); - if (ret && string_list_has_string(&dco->paths, ce->name)) { - free(new_blob); - goto delayed; + if (ret) { + struct string_list_item *item = + string_list_lookup(&dco->paths, ce->name); + if (item) { + item->util = nr_checkouts ? nr_checkouts + : &scratch_nr_checkouts; + free(new_blob); + goto delayed; + } } } else { ret = convert_to_working_tree_ca(ca, ce->name, new_blob, @@ -392,6 +399,8 @@ finish: ce->name); update_ce_after_write(state, ce , &st); } + if (nr_checkouts) + (*nr_checkouts)++; delayed: return 0; } @@ -476,7 +485,7 @@ int checkout_entry_ca(struct cache_entry *ce, struct conv_attrs *ca, convert_attrs(state->istate, &ca_buf, ce->name); ca = &ca_buf; } - return write_entry(ce, topath, ca, state, 1); + return write_entry(ce, topath, ca, state, 1, nr_checkouts); } strbuf_reset(&path); @@ -540,18 +549,15 @@ int checkout_entry_ca(struct cache_entry *ce, struct conv_attrs *ca, create_directories(path.buf, path.len, state); - if (nr_checkouts) - (*nr_checkouts)++; - if (S_ISREG(ce->ce_mode) && !ca) { convert_attrs(state->istate, &ca_buf, ce->name); ca = &ca_buf; } - if (!enqueue_checkout(ce, ca)) + if (!enqueue_checkout(ce, ca, nr_checkouts)) return 0; - return write_entry(ce, path.buf, ca, state, 0); + return write_entry(ce, path.buf, ca, state, 0, nr_checkouts); } void unlink_entry(const struct cache_entry *ce) @@ -43,8 +43,7 @@ static inline int checkout_entry(struct cache_entry *ce, } void enable_delayed_checkout(struct checkout *state); -int finish_delayed_checkout(struct checkout *state, int *nr_checkouts, - int show_progress); +int finish_delayed_checkout(struct checkout *state, int show_progress); /* * Unlink the last component and schedule the leading directories for diff --git a/git-compat-util.h b/git-compat-util.h index 58d7708296..36a25ae252 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -23,6 +23,9 @@ #include <crtdbg.h> #endif +struct strbuf; + + #define _FILE_OFFSET_BITS 64 @@ -487,7 +490,7 @@ static inline void extract_id_from_env(const char *env, uid_t *id) } } -static inline int is_path_owned_by_current_uid(const char *path) +static inline int is_path_owned_by_current_uid(const char *path, struct strbuf *report) { struct stat st; uid_t euid; @@ -822,6 +822,42 @@ def isModeExecChanged(src_mode, dst_mode): return isModeExec(src_mode) != isModeExec(dst_mode) +def p4KeysContainingNonUtf8Chars(): + """Returns all keys which may contain non UTF-8 encoded strings + for which a fallback strategy has to be applied. + """ + return ['desc', 'client', 'FullName'] + + +def p4KeysContainingBinaryData(): + """Returns all keys which may contain arbitrary binary data + """ + return ['data'] + + +def p4KeyContainsFilePaths(key): + """Returns True if the key contains file paths. These are handled by decode_path(). + Otherwise False. + """ + return key.startswith('depotFile') or key in ['path', 'clientFile'] + + +def p4KeyWhichCanBeDirectlyDecoded(key): + """Returns True if the key can be directly decoded as UTF-8 string + Otherwise False. + + Keys which can not be encoded directly: + - `data` which may contain arbitrary binary data + - `desc` or `client` or `FullName` which may contain non-UTF8 encoded text + - `depotFile[0-9]*`, `path`, or `clientFile` which may contain non-UTF8 encoded text, handled by decode_path() + """ + if key in p4KeysContainingNonUtf8Chars() or \ + key in p4KeysContainingBinaryData() or \ + p4KeyContainsFilePaths(key): + return False + return True + + def p4CmdList(cmd, stdin=None, stdin_mode='w+b', cb=None, skip_info=False, errors_as_exceptions=False, *k, **kw): @@ -851,15 +887,13 @@ def p4CmdList(cmd, stdin=None, stdin_mode='w+b', cb=None, skip_info=False, try: while True: entry = marshal.load(p4.stdout) + if bytes is not str: - # Decode unmarshalled dict to use str keys and values, except for: - # - `data` which may contain arbitrary binary data - # - `desc` or `FullName` which may contain non-UTF8 encoded text handled below, eagerly converted to bytes - # - `depotFile[0-9]*`, `path`, or `clientFile` which may contain non-UTF8 encoded text, handled by decode_path() + # Decode unmarshalled dict to use str keys and values. Special cases are handled below. decoded_entry = {} for key, value in entry.items(): key = key.decode() - if isinstance(value, bytes) and not (key in ('data', 'desc', 'FullName', 'path', 'clientFile') or key.startswith('depotFile')): + if isinstance(value, bytes) and p4KeyWhichCanBeDirectlyDecoded(key): value = value.decode() decoded_entry[key] = value # Parse out data if it's an error response @@ -869,10 +903,9 @@ def p4CmdList(cmd, stdin=None, stdin_mode='w+b', cb=None, skip_info=False, if skip_info: if 'code' in entry and entry['code'] == 'info': continue - if 'desc' in entry: - entry['desc'] = metadata_stream_to_writable_bytes(entry['desc']) - if 'FullName' in entry: - entry['FullName'] = metadata_stream_to_writable_bytes(entry['FullName']) + for key in p4KeysContainingNonUtf8Chars(): + if key in entry: + entry[key] = metadata_stream_to_writable_bytes(entry[key]) if cb is not None: cb(entry) else: @@ -3148,7 +3181,7 @@ class P4Sync(Command, P4UserMap): raise e else: if p4_version_string().find('/NT') >= 0: - text = text.replace(b'\r\n', b'\n') + text = text.replace(b'\x0d\x00\x0a\x00', b'\x0a\x00') contents = [text] if type_base == "apple": @@ -62,9 +62,6 @@ static int pick_next_hook(struct child_process *cp, strvec_push(&cp->args, hook_path); strvec_pushv(&cp->args, hook_cb->options->args.v); - /* Provide context for errors if necessary */ - *pp_task_cb = (char *)hook_path; - /* * This pick_next_hook() will be called again, we're only * running one hook, so indicate that no more work will be @@ -80,13 +77,9 @@ static int notify_start_failure(struct strbuf *out, void *pp_task_cp) { struct hook_cb_data *hook_cb = pp_cb; - const char *hook_path = pp_task_cp; hook_cb->rc |= 1; - strbuf_addf(out, _("Couldn't start hook '%s'\n"), - hook_path); - return 1; } diff --git a/merge-ort.c b/merge-ort.c index 8c4927f0e1..ebb9a75425 100644 --- a/merge-ort.c +++ b/merge-ort.c @@ -4294,6 +4294,8 @@ void merge_switch_to_result(struct merge_options *opt, if (checkout(opt, head, result->tree)) { /* failure to function */ result->clean = -1; + merge_finalize(opt, result); + trace2_region_leave("merge", "checkout", opt->repo); return; } trace2_region_leave("merge", "checkout", opt->repo); @@ -4304,6 +4306,9 @@ void merge_switch_to_result(struct merge_options *opt, /* failure to function */ opt->priv = NULL; result->clean = -1; + merge_finalize(opt, result); + trace2_region_leave("merge", "record_conflicted", + opt->repo); return; } opt->priv = NULL; diff --git a/mergetools/vimdiff b/mergetools/vimdiff index f770b8fe24..06937acbf5 100644 --- a/mergetools/vimdiff +++ b/mergetools/vimdiff @@ -29,8 +29,8 @@ ################################################################################ debug_print () { - # Send message to stderr if global variable GIT_MERGETOOL_VIMDIFF is set - # to "true" + # Send message to stderr if global variable GIT_MERGETOOL_VIMDIFF_DEBUG + # is set. if test -n "$GIT_MERGETOOL_VIMDIFF_DEBUG" then @@ -66,11 +66,6 @@ gen_cmd_aux () { debug_print "LAYOUT : $LAYOUT" debug_print "CMD : $CMD" - if test -z "$CMD" - then - CMD="echo" # vim "nop" operator - fi - start=0 end=${#LAYOUT} @@ -144,11 +139,10 @@ gen_cmd_aux () { # Step 2: # - # Search for all valid separators ("+", "/" or ",") which are *not* + # Search for all valid separators ("/" or ",") which are *not* # inside parenthesis. Save the index at which each of them makes the # first appearance. - index_new_tab="" index_horizontal_split="" index_vertical_split="" @@ -182,14 +176,7 @@ gen_cmd_aux () { then current=$c - if test "$current" = "+" - then - if test -z "$index_new_tab" - then - index_new_tab=$i - fi - - elif test "$current" = "/" + if test "$current" = "/" then if test -z "$index_horizontal_split" then @@ -219,14 +206,7 @@ gen_cmd_aux () { terminate="false" - if ! test -z "$index_new_tab" - then - before="-tabnew" - after="tabnext" - index=$index_new_tab - terminate="true" - - elif ! test -z "$index_horizontal_split" + if ! test -z "$index_horizontal_split" then before="leftabove split" after="wincmd j" @@ -333,25 +313,31 @@ gen_cmd () { # Obtain the first part of vim "-c" option to obtain the desired layout - CMD=$(gen_cmd_aux "$LAYOUT") - - - # Adjust the just obtained script depending on whether more than one - # windows are visible or not + CMD= + oldIFS=$IFS + IFS=+ + for tab in $LAYOUT + do + if test -z "$CMD" + then + CMD="echo" # vim "nop" operator + else + CMD="$CMD | tabnew" + fi - if echo "$LAYOUT" | grep ",\|/" >/dev/null - then - CMD="$CMD | tabdo windo diffthis" - else - CMD="$CMD | bufdo diffthis" - fi + # If this is a single window diff with all the buffers + if ! echo "$tab" | grep ",\|/" >/dev/null + then + CMD="$CMD | silent execute 'bufdo diffthis'" + fi + CMD=$(gen_cmd_aux "$tab" "$CMD") + done + IFS=$oldIFS - # Add an extra "-c" option to move to the first tab (notice that we - # can't simply append the command to the previous "-c" string as - # explained here: https://github.com/vim/vim/issues/9076 + CMD="$CMD | execute 'tabdo windo diffthis'" - FINAL_CMD="-c \"$CMD\" -c \"tabfirst\"" + FINAL_CMD="-c \"set hidden diffopt-=hiddenoff | $CMD | tabfirst\"" } @@ -555,22 +541,22 @@ run_unit_tests () { TEST_CASE_15=" (( (LOCAL , BASE , REMOTE) / MERGED)) +(BASE) , LOCAL+ BASE , REMOTE+ (((LOCAL / BASE / REMOTE)) , MERGED ) " TEST_CASE_16="LOCAL,BASE,REMOTE / MERGED + BASE,LOCAL + BASE,REMOTE + (LOCAL / BASE / REMOTE),MERGED" - EXPECTED_CMD_01="-c \"echo | leftabove split | leftabove vertical split | 1b | wincmd l | leftabove vertical split | 2b | wincmd l | 3b | wincmd j | 4b | tabdo windo diffthis\" -c \"tabfirst\"" - EXPECTED_CMD_02="-c \"echo | leftabove vertical split | 1b | wincmd l | 3b | tabdo windo diffthis\" -c \"tabfirst\"" - EXPECTED_CMD_03="-c \"echo | leftabove vertical split | 1b | wincmd l | leftabove vertical split | 4b | wincmd l | 3b | tabdo windo diffthis\" -c \"tabfirst\"" - EXPECTED_CMD_04="-c \"echo | 4b | bufdo diffthis\" -c \"tabfirst\"" - EXPECTED_CMD_05="-c \"echo | leftabove split | 1b | wincmd j | leftabove split | 4b | wincmd j | 3b | tabdo windo diffthis\" -c \"tabfirst\"" - EXPECTED_CMD_06="-c \"echo | leftabove vertical split | leftabove split | 1b | wincmd j | 3b | wincmd l | 4b | tabdo windo diffthis\" -c \"tabfirst\"" - EXPECTED_CMD_07="-c \"echo | leftabove vertical split | 4b | wincmd l | leftabove split | 1b | wincmd j | 3b | tabdo windo diffthis\" -c \"tabfirst\"" - EXPECTED_CMD_08="-c \"echo | leftabove split | leftabove vertical split | 1b | wincmd l | 3b | wincmd j | 4b | tabdo windo diffthis\" -c \"tabfirst\"" - EXPECTED_CMD_09="-c \"echo | leftabove split | 4b | wincmd j | leftabove vertical split | 1b | wincmd l | 3b | tabdo windo diffthis\" -c \"tabfirst\"" - EXPECTED_CMD_10="-c \"echo | leftabove vertical split | leftabove split | 1b | wincmd j | leftabove split | 2b | wincmd j | 3b | wincmd l | 4b | tabdo windo diffthis\" -c \"tabfirst\"" - EXPECTED_CMD_11="-c \"echo | -tabnew | leftabove split | leftabove vertical split | 1b | wincmd l | leftabove vertical split | 2b | wincmd l | 3b | wincmd j | 4b | tabnext | -tabnew | leftabove vertical split | 2b | wincmd l | 1b | tabnext | -tabnew | leftabove vertical split | 2b | wincmd l | 3b | tabnext | leftabove vertical split | leftabove split | 1b | wincmd j | leftabove split | 2b | wincmd j | 3b | wincmd l | 4b | tabdo windo diffthis\" -c \"tabfirst\"" - EXPECTED_CMD_12="-c \"echo | leftabove vertical split | leftabove split | leftabove vertical split | 1b | wincmd l | 3b | wincmd j | 2b | wincmd l | 4b | tabdo windo diffthis\" -c \"tabfirst\"" - EXPECTED_CMD_13="-c \"echo | leftabove vertical split | leftabove split | leftabove vertical split | 1b | wincmd l | 3b | wincmd j | 2b | wincmd l | leftabove vertical split | leftabove split | 1b | wincmd j | 3b | wincmd l | 4b | tabdo windo diffthis\" -c \"tabfirst\"" - EXPECTED_CMD_14="-c \"echo | -tabnew | leftabove vertical split | 2b | wincmd l | 3b | tabnext | leftabove vertical split | 2b | wincmd l | 1b | tabdo windo diffthis\" -c \"tabfirst\"" - EXPECTED_CMD_15="-c \"echo | -tabnew | leftabove split | leftabove vertical split | 1b | wincmd l | leftabove vertical split | 2b | wincmd l | 3b | wincmd j | 4b | tabnext | -tabnew | leftabove vertical split | 2b | wincmd l | 1b | tabnext | -tabnew | leftabove vertical split | 2b | wincmd l | 3b | tabnext | leftabove vertical split | leftabove split | 1b | wincmd j | leftabove split | 2b | wincmd j | 3b | wincmd l | 4b | tabdo windo diffthis\" -c \"tabfirst\"" - EXPECTED_CMD_16="-c \"echo | -tabnew | leftabove split | leftabove vertical split | 1b | wincmd l | leftabove vertical split | 2b | wincmd l | 3b | wincmd j | 4b | tabnext | -tabnew | leftabove vertical split | 2b | wincmd l | 1b | tabnext | -tabnew | leftabove vertical split | 2b | wincmd l | 3b | tabnext | leftabove vertical split | leftabove split | 1b | wincmd j | leftabove split | 2b | wincmd j | 3b | wincmd l | 4b | tabdo windo diffthis\" -c \"tabfirst\"" + EXPECTED_CMD_01="-c \"set hidden diffopt-=hiddenoff | echo | leftabove split | leftabove vertical split | 1b | wincmd l | leftabove vertical split | 2b | wincmd l | 3b | wincmd j | 4b | execute 'tabdo windo diffthis' | tabfirst\"" + EXPECTED_CMD_02="-c \"set hidden diffopt-=hiddenoff | echo | leftabove vertical split | 1b | wincmd l | 3b | execute 'tabdo windo diffthis' | tabfirst\"" + EXPECTED_CMD_03="-c \"set hidden diffopt-=hiddenoff | echo | leftabove vertical split | 1b | wincmd l | leftabove vertical split | 4b | wincmd l | 3b | execute 'tabdo windo diffthis' | tabfirst\"" + EXPECTED_CMD_04="-c \"set hidden diffopt-=hiddenoff | echo | silent execute 'bufdo diffthis' | 4b | execute 'tabdo windo diffthis' | tabfirst\"" + EXPECTED_CMD_05="-c \"set hidden diffopt-=hiddenoff | echo | leftabove split | 1b | wincmd j | leftabove split | 4b | wincmd j | 3b | execute 'tabdo windo diffthis' | tabfirst\"" + EXPECTED_CMD_06="-c \"set hidden diffopt-=hiddenoff | echo | leftabove vertical split | leftabove split | 1b | wincmd j | 3b | wincmd l | 4b | execute 'tabdo windo diffthis' | tabfirst\"" + EXPECTED_CMD_07="-c \"set hidden diffopt-=hiddenoff | echo | leftabove vertical split | 4b | wincmd l | leftabove split | 1b | wincmd j | 3b | execute 'tabdo windo diffthis' | tabfirst\"" + EXPECTED_CMD_08="-c \"set hidden diffopt-=hiddenoff | echo | leftabove split | leftabove vertical split | 1b | wincmd l | 3b | wincmd j | 4b | execute 'tabdo windo diffthis' | tabfirst\"" + EXPECTED_CMD_09="-c \"set hidden diffopt-=hiddenoff | echo | leftabove split | 4b | wincmd j | leftabove vertical split | 1b | wincmd l | 3b | execute 'tabdo windo diffthis' | tabfirst\"" + EXPECTED_CMD_10="-c \"set hidden diffopt-=hiddenoff | echo | leftabove vertical split | leftabove split | 1b | wincmd j | leftabove split | 2b | wincmd j | 3b | wincmd l | 4b | execute 'tabdo windo diffthis' | tabfirst\"" + EXPECTED_CMD_11="-c \"set hidden diffopt-=hiddenoff | echo | leftabove split | leftabove vertical split | 1b | wincmd l | leftabove vertical split | 2b | wincmd l | 3b | wincmd j | 4b | tabnew | leftabove vertical split | 2b | wincmd l | 1b | tabnew | leftabove vertical split | 2b | wincmd l | 3b | tabnew | leftabove vertical split | leftabove split | 1b | wincmd j | leftabove split | 2b | wincmd j | 3b | wincmd l | 4b | execute 'tabdo windo diffthis' | tabfirst\"" + EXPECTED_CMD_12="-c \"set hidden diffopt-=hiddenoff | echo | leftabove vertical split | leftabove split | leftabove vertical split | 1b | wincmd l | 3b | wincmd j | 2b | wincmd l | 4b | execute 'tabdo windo diffthis' | tabfirst\"" + EXPECTED_CMD_13="-c \"set hidden diffopt-=hiddenoff | echo | leftabove vertical split | leftabove split | leftabove vertical split | 1b | wincmd l | 3b | wincmd j | 2b | wincmd l | leftabove vertical split | leftabove split | 1b | wincmd j | 3b | wincmd l | 4b | execute 'tabdo windo diffthis' | tabfirst\"" + EXPECTED_CMD_14="-c \"set hidden diffopt-=hiddenoff | echo | leftabove vertical split | 2b | wincmd l | 3b | tabnew | leftabove vertical split | 2b | wincmd l | 1b | execute 'tabdo windo diffthis' | tabfirst\"" + EXPECTED_CMD_15="-c \"set hidden diffopt-=hiddenoff | echo | leftabove split | leftabove vertical split | 1b | wincmd l | leftabove vertical split | 2b | wincmd l | 3b | wincmd j | 4b | tabnew | leftabove vertical split | 2b | wincmd l | 1b | tabnew | leftabove vertical split | 2b | wincmd l | 3b | tabnew | leftabove vertical split | leftabove split | 1b | wincmd j | leftabove split | 2b | wincmd j | 3b | wincmd l | 4b | execute 'tabdo windo diffthis' | tabfirst\"" + EXPECTED_CMD_16="-c \"set hidden diffopt-=hiddenoff | echo | leftabove split | leftabove vertical split | 1b | wincmd l | leftabove vertical split | 2b | wincmd l | 3b | wincmd j | 4b | tabnew | leftabove vertical split | 2b | wincmd l | 1b | tabnew | leftabove vertical split | 2b | wincmd l | 3b | tabnew | leftabove vertical split | leftabove split | 1b | wincmd j | leftabove split | 2b | wincmd j | 3b | wincmd l | 4b | execute 'tabdo windo diffthis' | tabfirst\"" EXPECTED_TARGET_01="MERGED" EXPECTED_TARGET_02="LOCAL" @@ -635,9 +621,7 @@ run_unit_tests () { cat >expect <<-\EOF -f -c - echo | leftabove split | leftabove vertical split | 1b | wincmd l | leftabove vertical split | quit | wincmd l | 2b | wincmd j | 3b | tabdo windo diffthis - -c - tabfirst + set hidden diffopt-=hiddenoff | echo | leftabove split | leftabove vertical split | 1b | wincmd l | leftabove vertical split | quit | wincmd l | 2b | wincmd j | 3b | execute 'tabdo windo diffthis' | tabfirst lo cal ' ' mer ged diff --git a/parallel-checkout.c b/parallel-checkout.c index 31a3d0ee1b..4f6819f240 100644 --- a/parallel-checkout.c +++ b/parallel-checkout.c @@ -143,7 +143,8 @@ static int is_eligible_for_parallel_checkout(const struct cache_entry *ce, } } -int enqueue_checkout(struct cache_entry *ce, struct conv_attrs *ca) +int enqueue_checkout(struct cache_entry *ce, struct conv_attrs *ca, + int *checkout_counter) { struct parallel_checkout_item *pc_item; @@ -159,6 +160,7 @@ int enqueue_checkout(struct cache_entry *ce, struct conv_attrs *ca) memcpy(&pc_item->ca, ca, sizeof(pc_item->ca)); pc_item->status = PC_ITEM_PENDING; pc_item->id = parallel_checkout.nr; + pc_item->checkout_counter = checkout_counter; parallel_checkout.nr++; return 0; @@ -200,7 +202,8 @@ static int handle_results(struct checkout *state) switch(pc_item->status) { case PC_ITEM_WRITTEN: - /* Already handled */ + if (pc_item->checkout_counter) + (*pc_item->checkout_counter)++; break; case PC_ITEM_COLLIDED: /* @@ -225,7 +228,8 @@ static int handle_results(struct checkout *state) * add any extra overhead. */ ret |= checkout_entry_ca(pc_item->ce, &pc_item->ca, - state, NULL, NULL); + state, NULL, + pc_item->checkout_counter); advance_progress_meter(); break; case PC_ITEM_PENDING: diff --git a/parallel-checkout.h b/parallel-checkout.h index 80f539bcb7..c575284005 100644 --- a/parallel-checkout.h +++ b/parallel-checkout.h @@ -31,7 +31,8 @@ void init_parallel_checkout(void); * entry is not eligible for parallel checkout. Otherwise, enqueue the entry * for later write and return 0. */ -int enqueue_checkout(struct cache_entry *ce, struct conv_attrs *ca); +int enqueue_checkout(struct cache_entry *ce, struct conv_attrs *ca, + int *checkout_counter); size_t pc_queue_size(void); /* @@ -68,6 +69,7 @@ struct parallel_checkout_item { struct cache_entry *ce; struct conv_attrs ca; size_t id; /* position in parallel_checkout.items[] of main process */ + int *checkout_counter; /* Output fields, sent from workers. */ enum pc_item_status status; diff --git a/pkt-line.h b/pkt-line.h index 6d2a63db23..1f623de60a 100644 --- a/pkt-line.h +++ b/pkt-line.h @@ -49,14 +49,6 @@ void packet_fflush(FILE *f); * Read a packetized line into the buffer, which must be at least size bytes * long. The return value specifies the number of bytes read into the buffer. * - * If src_buffer and *src_buffer are not NULL, it should point to a buffer - * containing the packet data to parse, of at least *src_len bytes. After the - * function returns, src_buf will be incremented and src_len decremented by the - * number of bytes consumed. - * - * If src_buffer (or *src_buffer) is NULL, then data is read from the - * descriptor "fd". - * * If options does not contain PACKET_READ_GENTLE_ON_EOF, we will die under any * of the following conditions: * @@ -104,6 +96,14 @@ int packet_length(const char lenbuf_hex[4]); * returns an 'enum packet_read_status' which indicates the status of the read. * The number of bytes read will be assigned to *pktlen if the status of the * read was 'PACKET_READ_NORMAL'. + * + * If src_buffer and *src_buffer are not NULL, it should point to a buffer + * containing the packet data to parse, of at least *src_len bytes. After the + * function returns, src_buf will be incremented and src_len decremented by the + * number of bytes consumed. + * + * If src_buffer (or *src_buffer) is NULL, then data is read from the + * descriptor "fd". */ enum packet_read_status { PACKET_READ_EOF, diff --git a/read-cache.c b/read-cache.c index 76f372ff91..4de207752d 100644 --- a/read-cache.c +++ b/read-cache.c @@ -2294,6 +2294,8 @@ int do_read_index(struct index_state *istate, const char *path, int must_exist) fd = open(path, O_RDONLY); if (fd < 0) { if (!must_exist && errno == ENOENT) { + if (!istate->repo) + istate->repo = the_repository; set_new_index_sparsity(istate); return 0; } diff --git a/revision.c b/revision.c index 211352795c..0c6e26cd9c 100644 --- a/revision.c +++ b/revision.c @@ -33,6 +33,7 @@ #include "bloom.h" #include "json-writer.h" #include "list-objects-filter-options.h" +#include "resolve-undo.h" volatile show_early_output_fn_t show_early_output; @@ -1696,6 +1697,39 @@ static void add_cache_tree(struct cache_tree *it, struct rev_info *revs, } +static void add_resolve_undo_to_pending(struct index_state *istate, struct rev_info *revs) +{ + struct string_list_item *item; + struct string_list *resolve_undo = istate->resolve_undo; + + if (!resolve_undo) + return; + + for_each_string_list_item(item, resolve_undo) { + const char *path = item->string; + struct resolve_undo_info *ru = item->util; + int i; + + if (!ru) + continue; + for (i = 0; i < 3; i++) { + struct blob *blob; + + if (!ru->mode[i] || !S_ISREG(ru->mode[i])) + continue; + + blob = lookup_blob(revs->repo, &ru->oid[i]); + if (!blob) { + warning(_("resolve-undo records `%s` which is missing"), + oid_to_hex(&ru->oid[i])); + continue; + } + add_pending_object_with_path(revs, &blob->object, "", + ru->mode[i], path); + } + } +} + static void do_add_index_objects_to_pending(struct rev_info *revs, struct index_state *istate, unsigned int flags) @@ -1724,6 +1758,8 @@ static void do_add_index_objects_to_pending(struct rev_info *revs, add_cache_tree(istate->cache_tree, revs, &path, flags); strbuf_release(&path); } + + add_resolve_undo_to_pending(istate, revs); } void add_index_objects_to_pending(struct rev_info *revs, unsigned int flags) @@ -1138,16 +1138,17 @@ static int safe_directory_cb(const char *key, const char *value, void *d) * added, for bare ones their git directory. */ static int ensure_valid_ownership(const char *gitfile, - const char *worktree, const char *gitdir) + const char *worktree, const char *gitdir, + struct strbuf *report) { struct safe_directory_data data = { .path = worktree ? worktree : gitdir }; if (!git_env_bool("GIT_TEST_ASSUME_DIFFERENT_OWNER", 0) && - (!gitfile || is_path_owned_by_current_user(gitfile)) && - (!worktree || is_path_owned_by_current_user(worktree)) && - (!gitdir || is_path_owned_by_current_user(gitdir))) + (!gitfile || is_path_owned_by_current_user(gitfile, report)) && + (!worktree || is_path_owned_by_current_user(worktree, report)) && + (!gitdir || is_path_owned_by_current_user(gitdir, report))) return 1; /* @@ -1187,6 +1188,7 @@ enum discovery_result { */ static enum discovery_result setup_git_directory_gently_1(struct strbuf *dir, struct strbuf *gitdir, + struct strbuf *report, int die_on_error) { const char *env_ceiling_dirs = getenv(CEILING_DIRECTORIES_ENVIRONMENT); @@ -1271,10 +1273,11 @@ static enum discovery_result setup_git_directory_gently_1(struct strbuf *dir, strbuf_setlen(dir, offset); if (gitdirenv) { enum discovery_result ret; + const char *gitdir_candidate = + gitdir_path ? gitdir_path : gitdirenv; - if (ensure_valid_ownership(gitfile, - dir->buf, - (gitdir_path ? gitdir_path : gitdirenv))) { + if (ensure_valid_ownership(gitfile, dir->buf, + gitdir_candidate, report)) { strbuf_addstr(gitdir, gitdirenv); ret = GIT_DIR_DISCOVERED; } else @@ -1297,7 +1300,7 @@ static enum discovery_result setup_git_directory_gently_1(struct strbuf *dir, } if (is_git_directory(dir->buf)) { - if (!ensure_valid_ownership(NULL, NULL, dir->buf)) + if (!ensure_valid_ownership(NULL, NULL, dir->buf, report)) return GIT_DIR_INVALID_OWNERSHIP; strbuf_addstr(gitdir, "."); return GIT_DIR_BARE; @@ -1330,7 +1333,7 @@ int discover_git_directory(struct strbuf *commondir, return -1; cwd_len = dir.len; - if (setup_git_directory_gently_1(&dir, gitdir, 0) <= 0) { + if (setup_git_directory_gently_1(&dir, gitdir, NULL, 0) <= 0) { strbuf_release(&dir); return -1; } @@ -1377,7 +1380,7 @@ int discover_git_directory(struct strbuf *commondir, const char *setup_git_directory_gently(int *nongit_ok) { static struct strbuf cwd = STRBUF_INIT; - struct strbuf dir = STRBUF_INIT, gitdir = STRBUF_INIT; + struct strbuf dir = STRBUF_INIT, gitdir = STRBUF_INIT, report = STRBUF_INIT; const char *prefix = NULL; struct repository_format repo_fmt = REPOSITORY_FORMAT_INIT; @@ -1402,7 +1405,7 @@ const char *setup_git_directory_gently(int *nongit_ok) die_errno(_("Unable to read current working directory")); strbuf_addbuf(&dir, &cwd); - switch (setup_git_directory_gently_1(&dir, &gitdir, 1)) { + switch (setup_git_directory_gently_1(&dir, &gitdir, &report, 1)) { case GIT_DIR_EXPLICIT: prefix = setup_explicit_git_dir(gitdir.buf, &cwd, &repo_fmt, nongit_ok); break; @@ -1434,12 +1437,14 @@ const char *setup_git_directory_gently(int *nongit_ok) if (!nongit_ok) { struct strbuf quoted = STRBUF_INIT; + strbuf_complete(&report, '\n'); sq_quote_buf_pretty("ed, dir.buf); die(_("detected dubious ownership in repository at '%s'\n" + "%s" "To add an exception for this directory, call:\n" "\n" "\tgit config --global --add safe.directory %s"), - dir.buf, quoted.buf); + dir.buf, report.buf, quoted.buf); } *nongit_ok = 1; break; @@ -1518,6 +1523,7 @@ const char *setup_git_directory_gently(int *nongit_ok) strbuf_release(&dir); strbuf_release(&gitdir); + strbuf_release(&report); clear_repository_format(&repo_fmt); return prefix; diff --git a/t/lib-parallel-checkout.sh b/t/lib-parallel-checkout.sh index 83b279a846..acaee9cbb6 100644 --- a/t/lib-parallel-checkout.sh +++ b/t/lib-parallel-checkout.sh @@ -25,7 +25,11 @@ test_checkout_workers () { local trace_file=trace-test-checkout-workers && rm -f "$trace_file" && - GIT_TRACE2="$(pwd)/$trace_file" "$@" 2>&8 && + ( + GIT_TRACE2="$(pwd)/$trace_file" && + export GIT_TRACE2 && + "$@" 2>&8 + ) && local workers="$(grep "child_start\[..*\] git checkout--worker" "$trace_file" | wc -l)" && test $workers -eq $expected_workers && diff --git a/t/t0021-conversion.sh b/t/t0021-conversion.sh index bad37abad2..1c840348bd 100755 --- a/t/t0021-conversion.sh +++ b/t/t0021-conversion.sh @@ -1132,4 +1132,26 @@ do ' done +test_expect_success PERL 'delayed checkout correctly reports the number of updated entries' ' + rm -rf repo && + git init repo && + ( + cd repo && + git config filter.delay.process "../rot13-filter.pl delayed.log clean smudge delay" && + git config filter.delay.required true && + + echo "*.a filter=delay" >.gitattributes && + echo a >test-delay10.a && + echo a >test-delay11.a && + git add . && + git commit -m files && + + rm *.a && + git checkout . 2>err && + grep "IN: smudge test-delay10.a .* \\[DELAYED\\]" delayed.log && + grep "IN: smudge test-delay11.a .* \\[DELAYED\\]" delayed.log && + grep "Updated 2 paths from the index" err + ) +' + test_done diff --git a/t/t1800-hook.sh b/t/t1800-hook.sh index 210f429887..64096adac7 100755 --- a/t/t1800-hook.sh +++ b/t/t1800-hook.sh @@ -151,4 +151,30 @@ test_expect_success TTY 'git commit: stdout and stderr are connected to a TTY' ' test_hook_tty commit -m"B.new" ' +test_expect_success 'git hook run a hook with a bad shebang' ' + test_when_finished "rm -rf bad-hooks" && + mkdir bad-hooks && + write_script bad-hooks/test-hook "/bad/path/no/spaces" </dev/null && + + # TODO: We should emit the same (or at least a more similar) + # error on Windows and !Windows. See the OS-specific code in + # start_command() + if test_have_prereq !WINDOWS + then + cat >expect <<-\EOF + fatal: cannot run bad-hooks/test-hook: ... + EOF + else + cat >expect <<-\EOF + error: cannot spawn bad-hooks/test-hook: ... + EOF + fi && + test_expect_code 1 git \ + -c core.hooksPath=bad-hooks \ + hook run test-hook >out 2>err && + test_must_be_empty out && + sed -e "s/test-hook: .*/test-hook: .../" <err >actual && + test_cmp expect actual +' + test_done diff --git a/t/t2030-unresolve-info.sh b/t/t2030-unresolve-info.sh index f691e6d903..2d8c70b03a 100755 --- a/t/t2030-unresolve-info.sh +++ b/t/t2030-unresolve-info.sh @@ -194,4 +194,75 @@ test_expect_success 'rerere forget (add-add conflict)' ' test_i18ngrep "no remembered" actual ' +test_expect_success 'resolve-undo keeps blobs from gc' ' + git checkout -f main && + + # First make sure we do not have any cruft left in the object store + git repack -a -d && + git prune --expire=now && + git prune-packed && + git gc --prune=now && + git fsck --unreachable >cruft && + test_must_be_empty cruft && + + # Now add three otherwise unreferenced blob objects to the index + git reset --hard && + B1=$(echo "resolve undo test data 1" | git hash-object -w --stdin) && + B2=$(echo "resolve undo test data 2" | git hash-object -w --stdin) && + B3=$(echo "resolve undo test data 3" | git hash-object -w --stdin) && + git update-index --add --index-info <<-EOF && + 100644 $B1 1 frotz + 100644 $B2 2 frotz + 100644 $B3 3 frotz + EOF + + # These three blob objects are reachable (only) from the index + git fsck --unreachable >cruft && + test_must_be_empty cruft && + # and they should be protected from GC + git gc --prune=now && + git cat-file -e $B1 && + git cat-file -e $B2 && + git cat-file -e $B3 && + + # Now resolve the conflicted path + B0=$(echo "resolve undo test data 0" | git hash-object -w --stdin) && + git update-index --add --cacheinfo 100644,$B0,frotz && + + # These three blob objects are now reachable only from the resolve-undo + git fsck --unreachable >cruft && + test_must_be_empty cruft && + + # and they should survive GC + git gc --prune=now && + git cat-file -e $B0 && + git cat-file -e $B1 && + git cat-file -e $B2 && + git cat-file -e $B3 && + + # Now we switch away, which nukes resolve-undo, and + # blobs B0..B3 would become dangling. fsck should + # notice that they are now unreachable. + git checkout -f side && + git fsck --unreachable >cruft && + sort cruft >actual && + sort <<-EOF >expect && + unreachable blob $B0 + unreachable blob $B1 + unreachable blob $B2 + unreachable blob $B3 + EOF + test_cmp expect actual && + + # And they should go away when gc runs. + git gc --prune=now && + git fsck --unreachable >cruft && + test_must_be_empty cruft && + + test_must_fail git cat-file -e $B0 && + test_must_fail git cat-file -e $B1 && + test_must_fail git cat-file -e $B2 && + test_must_fail git cat-file -e $B3 +' + test_done diff --git a/t/t2080-parallel-checkout-basics.sh b/t/t2080-parallel-checkout-basics.sh index 3e0f8c675f..c683e60007 100755 --- a/t/t2080-parallel-checkout-basics.sh +++ b/t/t2080-parallel-checkout-basics.sh @@ -226,4 +226,52 @@ test_expect_success SYMLINKS 'parallel checkout checks for symlinks in leading d ) ' +# This test is here (and not in e.g. t2022-checkout-paths.sh), because we +# check the final report including sequential, parallel, and delayed entries +# all at the same time. So we must have finer control of the parallel checkout +# variables. +test_expect_success PERL '"git checkout ." report should not include failed entries' ' + write_script rot13-filter.pl "$PERL_PATH" \ + <"$TEST_DIRECTORY"/t0021/rot13-filter.pl && + + test_config_global filter.delay.process \ + "\"$(pwd)/rot13-filter.pl\" --always-delay delayed.log clean smudge delay" && + test_config_global filter.delay.required true && + test_config_global filter.cat.clean cat && + test_config_global filter.cat.smudge cat && + test_config_global filter.cat.required true && + + set_checkout_config 2 0 && + git init failed_entries && + ( + cd failed_entries && + cat >.gitattributes <<-EOF && + *delay* filter=delay + parallel-ineligible* filter=cat + EOF + echo a >missing-delay.a && + echo a >parallel-ineligible.a && + echo a >parallel-eligible.a && + echo b >success-delay.b && + echo b >parallel-ineligible.b && + echo b >parallel-eligible.b && + git add -A && + git commit -m files && + + a_blob="$(git rev-parse :parallel-ineligible.a)" && + rm .git/objects/$(test_oid_to_path $a_blob) && + rm *.a *.b && + + test_checkout_workers 2 test_must_fail git checkout . 2>err && + + # All *.b entries should succeed and all *.a entries should fail: + # - missing-delay.a: the delay filter will drop this path + # - parallel-*.a: the blob will be missing + # + grep "Updated 3 paths from the index" err && + test_stdout_line_count = 3 ls *.b && + ! ls *.a + ) +' + test_done diff --git a/t/t4200-rerere.sh b/t/t4200-rerere.sh index 9f8c76dffb..7025cfdae5 100755 --- a/t/t4200-rerere.sh +++ b/t/t4200-rerere.sh @@ -365,9 +365,6 @@ test_expect_success 'set up an unresolved merge' ' test_might_fail git config --unset rerere.autoupdate && git reset --hard && git checkout version2 && - fifth=$(git rev-parse fifth) && - echo "$fifth branch fifth of ." | - git fmt-merge-msg >msg && ancestor=$(git merge-base version2 fifth) && test_must_fail git merge-recursive "$ancestor" -- HEAD fifth && diff --git a/t/t5318-commit-graph.sh b/t/t5318-commit-graph.sh index fbf0d64578..db89542dfb 100755 --- a/t/t5318-commit-graph.sh +++ b/t/t5318-commit-graph.sh @@ -811,4 +811,31 @@ test_expect_success 'set up and verify repo with generation data overflow chunk' graph_git_behavior 'generation data overflow chunk repo' repo left right +test_expect_success 'overflow during generation version upgrade' ' + git init overflow-v2-upgrade && + ( + cd overflow-v2-upgrade && + + # This commit will have a date at two seconds past the Epoch, + # and a (v1) generation number of 1, since it is a root commit. + # + # The offset will then be computed as 1-2, which will underflow + # to 2^31, which is greater than the v2 offset small limit of + # 2^31-1. + # + # This is sufficient to need a large offset table for the v2 + # generation numbers. + test_commit --date "@2 +0000" base && + git repack -d && + + # Test that upgrading from generation v1 to v2 correctly + # produces the overflow table. + git -c commitGraph.generationVersion=1 commit-graph write && + git -c commitGraph.generationVersion=2 commit-graph write \ + --changed-paths && + + git rev-list --all + ) +' + test_done diff --git a/t/t5330-no-lazy-fetch-with-commit-graph.sh b/t/t5330-no-lazy-fetch-with-commit-graph.sh new file mode 100755 index 0000000000..2cc7fd7a47 --- /dev/null +++ b/t/t5330-no-lazy-fetch-with-commit-graph.sh @@ -0,0 +1,47 @@ +#!/bin/sh + +test_description='test for no lazy fetch with the commit-graph' + +. ./test-lib.sh + +test_expect_success 'setup: prepare a repository with a commit' ' + git init with-commit && + test_commit -C with-commit the-commit && + oid=$(git -C with-commit rev-parse HEAD) +' + +test_expect_success 'setup: prepare a repository with commit-graph contains the commit' ' + git init with-commit-graph && + echo "$(pwd)/with-commit/.git/objects" \ + >with-commit-graph/.git/objects/info/alternates && + # create a ref that points to the commit in alternates + git -C with-commit-graph update-ref refs/ref_to_the_commit "$oid" && + # prepare some other objects to commit-graph + test_commit -C with-commit-graph something && + git -c gc.writeCommitGraph=true -C with-commit-graph gc && + test_path_is_file with-commit-graph/.git/objects/info/commit-graph +' + +test_expect_success 'setup: change the alternates to what without the commit' ' + git init --bare without-commit && + git -C with-commit-graph cat-file -e $oid && + echo "$(pwd)/without-commit/objects" \ + >with-commit-graph/.git/objects/info/alternates && + test_must_fail git -C with-commit-graph cat-file -e $oid +' + +test_expect_success 'fetch any commit from promisor with the usage of the commit graph' ' + # setup promisor and prepare any commit to fetch + git -C with-commit-graph remote add origin "$(pwd)/with-commit" && + git -C with-commit-graph config remote.origin.promisor true && + git -C with-commit-graph config remote.origin.partialclonefilter blob:none && + test_commit -C with-commit any-commit && + anycommit=$(git -C with-commit rev-parse HEAD) && + GIT_TRACE="$(pwd)/trace.txt" \ + git -C with-commit-graph fetch origin $anycommit 2>err && + ! grep "fatal: promisor-remote: unable to fork off fetch subprocess" err && + grep "git fetch origin" trace.txt >actual && + test_line_count = 1 actual +' + +test_done diff --git a/t/t7063-status-untracked-cache.sh b/t/t7063-status-untracked-cache.sh index 9936cc329e..c1f0d95036 100755 --- a/t/t7063-status-untracked-cache.sh +++ b/t/t7063-status-untracked-cache.sh @@ -985,4 +985,9 @@ test_expect_success '"status" after file replacement should be clean with UC=fal status_is_clean ' +test_expect_success 'empty repo (no index) and core.untrackedCache' ' + git init emptyrepo && + git -C emptyrepo -c core.untrackedCache=true write-tree +' + test_done diff --git a/t/test-lib.sh b/t/test-lib.sh index 8cabb4d10f..120f11812c 100644 --- a/t/test-lib.sh +++ b/t/test-lib.sh @@ -557,14 +557,19 @@ then : nothing } else + _USE_GLIBC_TUNABLES= + if _GLIBC_VERSION=$(getconf GNU_LIBC_VERSION 2>/dev/null) && + _GLIBC_VERSION=${_GLIBC_VERSION#"glibc "} && + expr 2.34 \<= "$_GLIBC_VERSION" >/dev/null + then + _USE_GLIBC_TUNABLES=YesPlease + fi setup_malloc_check () { local g local t MALLOC_CHECK_=3 MALLOC_PERTURB_=165 export MALLOC_CHECK_ MALLOC_PERTURB_ - if _GLIBC_VERSION=$(getconf GNU_LIBC_VERSION 2>/dev/null) && - _GLIBC_VERSION=${_GLIBC_VERSION#"glibc "} && - expr 2.34 \<= "$_GLIBC_VERSION" >/dev/null + if test -n "$_USE_GLIBC_TUNABLES" then g= LD_PRELOAD="libc_malloc_debug.so.0" diff --git a/unpack-trees.c b/unpack-trees.c index d561ca01ed..8a454e03bf 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -487,7 +487,7 @@ static int check_updates(struct unpack_trees_options *o, errs |= run_parallel_checkout(&state, pc_workers, pc_threshold, progress, &cnt); stop_progress(&progress); - errs |= finish_delayed_checkout(&state, NULL, o->verbose_update); + errs |= finish_delayed_checkout(&state, o->verbose_update); git_attr_set_direction(GIT_ATTR_CHECKIN); if (o->clone) |
