aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/RelNotes/2.37.2.txt24
-rw-r--r--Documentation/git-reflog.txt2
-rwxr-xr-xGIT-VERSION-GEN2
-rw-r--r--bloom.c10
-rw-r--r--builtin/checkout.c2
-rw-r--r--builtin/fsck.c39
-rw-r--r--builtin/remote.c3
-rw-r--r--commit-graph.c14
-rw-r--r--commit-graph.h15
-rw-r--r--compat/mingw.c63
-rw-r--r--compat/mingw.h2
-rw-r--r--config.mak.dev4
-rw-r--r--config.mak.uname5
-rw-r--r--contrib/buildsystems/CMakeLists.txt2
-rwxr-xr-xcontrib/rerere-train.sh2
-rw-r--r--convert.h6
-rw-r--r--entry.c34
-rw-r--r--entry.h3
-rw-r--r--git-compat-util.h5
-rwxr-xr-xgit-p4.py53
-rw-r--r--hook.c7
-rw-r--r--merge-ort.c5
-rw-r--r--mergetools/vimdiff102
-rw-r--r--parallel-checkout.c10
-rw-r--r--parallel-checkout.h4
-rw-r--r--pkt-line.h16
-rw-r--r--read-cache.c2
-rw-r--r--revision.c36
-rw-r--r--setup.c30
-rw-r--r--t/lib-parallel-checkout.sh6
-rwxr-xr-xt/t0021-conversion.sh22
-rwxr-xr-xt/t1800-hook.sh26
-rwxr-xr-xt/t2030-unresolve-info.sh71
-rwxr-xr-xt/t2080-parallel-checkout-basics.sh48
-rwxr-xr-xt/t4200-rerere.sh3
-rwxr-xr-xt/t5318-commit-graph.sh27
-rwxr-xr-xt/t5330-no-lazy-fetch-with-commit-graph.sh47
-rwxr-xr-xt/t7063-status-untracked-cache.sh5
-rw-r--r--t/test-lib.sh11
-rw-r--r--unpack-trees.c2
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='
'
diff --git a/bloom.c b/bloom.c
index 5e297038bb..816f063dca 100644
--- a/bloom.c
+++ b/bloom.c
@@ -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
diff --git a/convert.h b/convert.h
index 5ee1c32205..0a6e4086b8 100644
--- a/convert.h
+++ b/convert.h
@@ -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;
};
diff --git a/entry.c b/entry.c
index 1c9df62b30..616e4f073c 100644
--- a/entry.c
+++ b/entry.c
@@ -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)
diff --git a/entry.h b/entry.h
index 252fd24c2e..9be4659881 100644
--- a/entry.h
+++ b/entry.h
@@ -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;
diff --git a/git-p4.py b/git-p4.py
index 8fbf6eb1fe..77dc19daa6 100755
--- a/git-p4.py
+++ b/git-p4.py
@@ -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":
diff --git a/hook.c b/hook.c
index d113ee7faa..a493939a4f 100644
--- a/hook.c
+++ b/hook.c
@@ -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)
diff --git a/setup.c b/setup.c
index 09b6549ba9..640f6ea4d0 100644
--- a/setup.c
+++ b/setup.c
@@ -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(&quoted, 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)