aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/RelNotes/2.37.3.txt46
-rw-r--r--Documentation/git-add.txt4
-rw-r--r--Documentation/git-reflog.txt2
-rwxr-xr-xGIT-VERSION-GEN2
-rw-r--r--Makefile1
l---------RelNotes2
-rw-r--r--builtin/checkout.c1
-rw-r--r--cache.h9
-rw-r--r--compat/mingw.c63
-rw-r--r--compat/mingw.h2
-rw-r--r--compat/nonblock.c50
-rw-r--r--compat/nonblock.h9
-rw-r--r--config.mak.dev4
-rw-r--r--config.mak.uname5
-rw-r--r--contrib/buildsystems/CMakeLists.txt2
-rw-r--r--diff-lib.c5
-rw-r--r--fsck.c4
-rw-r--r--fsck.h2
-rw-r--r--git-compat-util.h27
-rw-r--r--hook.c7
-rw-r--r--merge-ort.c5
-rw-r--r--mergetools/vimdiff102
-rw-r--r--packfile.c17
-rw-r--r--preload-index.c6
-rw-r--r--promisor-remote.c2
-rw-r--r--read-cache.c5
-rw-r--r--run-command.c33
-rw-r--r--setup.c30
-rwxr-xr-xt/t1092-sparse-checkout-compatibility.sh25
-rwxr-xr-xt/t1450-fsck.sh14
-rwxr-xr-xt/t1800-hook.sh26
-rwxr-xr-xt/t3701-add-interactive.sh13
-rwxr-xr-xt/t5504-fetch-receive-strict.sh17
-rwxr-xr-xt/t5616-partial-clone.sh7
-rw-r--r--t/test-lib.sh11
-rw-r--r--tree-walk.c14
-rw-r--r--tree-walk.h8
-rw-r--r--unpack-trees.c106
-rw-r--r--wrapper.c22
39 files changed, 566 insertions, 144 deletions
diff --git a/Documentation/RelNotes/2.37.3.txt b/Documentation/RelNotes/2.37.3.txt
new file mode 100644
index 0000000000..d66689e598
--- /dev/null
+++ b/Documentation/RelNotes/2.37.3.txt
@@ -0,0 +1,46 @@
+Git 2.37.3 Release Notes
+========================
+
+This primarily is to backport various fixes accumulated on the 'master'
+front since 2.37.2.
+
+Fixes since v2.37.2
+-------------------
+
+ * The build procedure for Windows that uses CMake has been updated to
+ pick up the shell interpreter from local installation location.
+
+ * Conditionally allow building Python interpreter on Windows
+
+ * Fix to lstat() emulation on Windows.
+
+ * Older gcc with -Wall complains about the universal zero initializer
+ "struct s = { 0 };" idiom, which makes developers' lives
+ inconvenient (as -Werror is enabled by DEVELOPER=YesPlease). The
+ build procedure has been tweaked to help these compilers.
+
+ * Plug memory leaks in the failure code path in the "merge-ort" merge
+ strategy backend.
+
+ * Avoid repeatedly running getconf to ask libc version in the test
+ suite, and instead just as it once per script.
+
+ * Platform-specific code that determines if a directory is OK to use
+ as a repository has been taught to report more details, especially
+ on Windows.
+
+ * "vimdiff3" regression has been corrected.
+
+ * "git fsck" reads mode from tree objects but canonicalizes the mode
+ before passing it to the logic to check object sanity, which has
+ hid broken tree objects from the checking logic. This has been
+ corrected, but to help exiting projects with broken tree objects
+ that they cannot fix retroactively, the severity of anomalies this
+ code detects has been demoted to "info" for now.
+
+ * Fixes to sparse index compatibility work for "reset" and "checkout"
+ commands.
+
+ * Documentation for "git add --renormalize" has been improved.
+
+Also contains other minor documentation updates and code clean-ups.
diff --git a/Documentation/git-add.txt b/Documentation/git-add.txt
index 11eb70f16c..9b37f35654 100644
--- a/Documentation/git-add.txt
+++ b/Documentation/git-add.txt
@@ -188,7 +188,9 @@ for "git add --no-all <pathspec>...", i.e. ignored removed files.
forcibly add them again to the index. This is useful after
changing `core.autocrlf` configuration or the `text` attribute
in order to correct files added with wrong CRLF/LF line endings.
- This option implies `-u`.
+ This option implies `-u`. Lone CR characters are untouched, thus
+ while a CRLF cleans to LF, a CRCRLF sequence is only partially
+ cleaned to CRLF.
--chmod=(+|-)x::
Override the executable bit of the added files. The executable
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 e2d51b505d..fb16249620 100755
--- a/GIT-VERSION-GEN
+++ b/GIT-VERSION-GEN
@@ -1,7 +1,7 @@
#!/bin/sh
GVF=GIT-VERSION-FILE
-DEF_VER=v2.37.2
+DEF_VER=v2.37.3
LF='
'
diff --git a/Makefile b/Makefile
index 04d0fd1fe6..17fdb16dd8 100644
--- a/Makefile
+++ b/Makefile
@@ -910,6 +910,7 @@ LIB_OBJS += combine-diff.o
LIB_OBJS += commit-graph.o
LIB_OBJS += commit-reach.o
LIB_OBJS += commit.o
+LIB_OBJS += compat/nonblock.o
LIB_OBJS += compat/obstack.o
LIB_OBJS += compat/terminal.o
LIB_OBJS += compat/zlib-uncompress2.o
diff --git a/RelNotes b/RelNotes
index 952f80e09a..6d88c2c8e0 120000
--- a/RelNotes
+++ b/RelNotes
@@ -1 +1 @@
-Documentation/RelNotes/2.37.2.txt \ No newline at end of file
+Documentation/RelNotes/2.37.3.txt \ No newline at end of file
diff --git a/builtin/checkout.c b/builtin/checkout.c
index df3f1663d7..cdd96cd9c6 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -626,6 +626,7 @@ static void show_local_changes(struct object *head,
repo_init_revisions(the_repository, &rev, NULL);
rev.diffopt.flags = opts->flags;
rev.diffopt.output_format |= DIFF_FORMAT_NAME_STATUS;
+ rev.diffopt.flags.recursive = 1;
diff_setup_done(&rev.diffopt);
add_pending_object(&rev, head, NULL);
run_diff_index(&rev, 0);
diff --git a/cache.h b/cache.h
index ac5ab4ef9d..33fc8337c6 100644
--- a/cache.h
+++ b/cache.h
@@ -831,6 +831,15 @@ struct cache_entry *index_file_exists(struct index_state *istate, const char *na
int index_name_pos(struct index_state *, const char *name, int namelen);
/*
+ * Like index_name_pos, returns the position of an entry of the given name in
+ * the index if one exists, otherwise returns a negative value where the negated
+ * value minus 1 is the position where the index entry would be inserted. Unlike
+ * index_name_pos, however, a sparse index is not expanded to find an entry
+ * inside a sparse directory.
+ */
+int index_name_pos_sparse(struct index_state *, const char *name, int namelen);
+
+/*
* Determines whether an entry with the given name exists within the
* given index. The return value is 1 if an exact match is found, otherwise
* it is 0. Note that, unlike index_name_pos, this function does not expand
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/compat/nonblock.c b/compat/nonblock.c
new file mode 100644
index 0000000000..9694ebdb1d
--- /dev/null
+++ b/compat/nonblock.c
@@ -0,0 +1,50 @@
+#include "git-compat-util.h"
+#include "nonblock.h"
+
+#ifdef O_NONBLOCK
+
+int enable_pipe_nonblock(int fd)
+{
+ int flags = fcntl(fd, F_GETFL);
+ if (flags < 0)
+ return -1;
+ flags |= O_NONBLOCK;
+ return fcntl(fd, F_SETFL, flags);
+}
+
+#elif defined(GIT_WINDOWS_NATIVE)
+
+#include "win32.h"
+
+int enable_pipe_nonblock(int fd)
+{
+ HANDLE h = (HANDLE)_get_osfhandle(fd);
+ DWORD mode;
+ DWORD type = GetFileType(h);
+ if (type == FILE_TYPE_UNKNOWN && GetLastError() != NO_ERROR) {
+ errno = EBADF;
+ return -1;
+ }
+ if (type != FILE_TYPE_PIPE)
+ BUG("unsupported file type: %lu", type);
+ if (!GetNamedPipeHandleState(h, &mode, NULL, NULL, NULL, NULL, 0)) {
+ errno = err_win_to_posix(GetLastError());
+ return -1;
+ }
+ mode |= PIPE_NOWAIT;
+ if (!SetNamedPipeHandleState(h, &mode, NULL, NULL)) {
+ errno = err_win_to_posix(GetLastError());
+ return -1;
+ }
+ return 0;
+}
+
+#else
+
+int enable_pipe_nonblock(int fd)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+#endif
diff --git a/compat/nonblock.h b/compat/nonblock.h
new file mode 100644
index 0000000000..af1a331301
--- /dev/null
+++ b/compat/nonblock.h
@@ -0,0 +1,9 @@
+#ifndef COMPAT_NONBLOCK_H
+#define COMPAT_NONBLOCK_H
+
+/*
+ * Enable non-blocking I/O for the pipe specified by the passed-in descriptor.
+ */
+int enable_pipe_nonblock(int fd);
+
+#endif
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/diff-lib.c b/diff-lib.c
index 7eb66a417a..2edea41a23 100644
--- a/diff-lib.c
+++ b/diff-lib.c
@@ -466,6 +466,11 @@ static void do_oneway_diff(struct unpack_trees_options *o,
* Something removed from the tree?
*/
if (!idx) {
+ if (S_ISSPARSEDIR(tree->ce_mode)) {
+ diff_tree_oid(&tree->oid, NULL, tree->name, &revs->diffopt);
+ return;
+ }
+
diff_index_show_file(revs, "-", tree, &tree->oid, 1,
tree->ce_mode, 0);
return;
diff --git a/fsck.c b/fsck.c
index dd4822ba1b..b3da1d68c0 100644
--- a/fsck.c
+++ b/fsck.c
@@ -308,7 +308,7 @@ static int fsck_walk_tree(struct tree *tree, void *data, struct fsck_options *op
return -1;
name = fsck_get_object_name(options, &tree->object.oid);
- if (init_tree_desc_gently(&desc, tree->buffer, tree->size))
+ if (init_tree_desc_gently(&desc, tree->buffer, tree->size, 0))
return -1;
while (tree_entry_gently(&desc, &entry)) {
struct object *obj;
@@ -578,7 +578,7 @@ static int fsck_tree(const struct object_id *tree_oid,
const char *o_name;
struct name_stack df_dup_candidates = { NULL };
- if (init_tree_desc_gently(&desc, buffer, size)) {
+ if (init_tree_desc_gently(&desc, buffer, size, TREE_DESC_RAW_MODES)) {
retval += report(options, tree_oid, OBJ_TREE,
FSCK_MSG_BAD_TREE,
"cannot be parsed as a tree");
diff --git a/fsck.h b/fsck.h
index d07f7a2459..6f801e53b1 100644
--- a/fsck.h
+++ b/fsck.h
@@ -56,7 +56,6 @@ enum fsck_msg_type {
FUNC(GITMODULES_PATH, ERROR) \
FUNC(GITMODULES_UPDATE, ERROR) \
/* warnings */ \
- FUNC(BAD_FILEMODE, WARN) \
FUNC(EMPTY_NAME, WARN) \
FUNC(FULL_PATHNAME, WARN) \
FUNC(HAS_DOT, WARN) \
@@ -66,6 +65,7 @@ enum fsck_msg_type {
FUNC(ZERO_PADDED_FILEMODE, WARN) \
FUNC(NUL_IN_COMMIT, WARN) \
/* infos (reported as warnings, but ignored by default) */ \
+ FUNC(BAD_FILEMODE, INFO) \
FUNC(GITMODULES_PARSE, INFO) \
FUNC(GITIGNORE_SYMLINK, INFO) \
FUNC(GITATTRIBUTES_SYMLINK, INFO) \
diff --git a/git-compat-util.h b/git-compat-util.h
index 58d7708296..6aee4d92e7 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;
@@ -995,6 +998,28 @@ static inline unsigned long cast_size_t_to_ulong(size_t a)
return (unsigned long)a;
}
+/*
+ * Limit size of IO chunks, because huge chunks only cause pain. OS X
+ * 64-bit is buggy, returning EINVAL if len >= INT_MAX; and even in
+ * the absence of bugs, large chunks can result in bad latencies when
+ * you decide to kill the process.
+ *
+ * We pick 8 MiB as our default, but if the platform defines SSIZE_MAX
+ * that is smaller than that, clip it to SSIZE_MAX, as a call to
+ * read(2) or write(2) larger than that is allowed to fail. As the last
+ * resort, we allow a port to pass via CFLAGS e.g. "-DMAX_IO_SIZE=value"
+ * to override this, if the definition of SSIZE_MAX given by the platform
+ * is broken.
+ */
+#ifndef MAX_IO_SIZE
+# define MAX_IO_SIZE_DEFAULT (8*1024*1024)
+# if defined(SSIZE_MAX) && (SSIZE_MAX < MAX_IO_SIZE_DEFAULT)
+# define MAX_IO_SIZE SSIZE_MAX
+# else
+# define MAX_IO_SIZE MAX_IO_SIZE_DEFAULT
+# endif
+#endif
+
#ifdef HAVE_ALLOCA_H
# include <alloca.h>
# define xalloca(size) (alloca(size))
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/packfile.c b/packfile.c
index 8e812a84a3..75c254e0e0 100644
--- a/packfile.c
+++ b/packfile.c
@@ -2229,7 +2229,17 @@ static int add_promisor_object(const struct object_id *oid,
void *set_)
{
struct oidset *set = set_;
- struct object *obj = parse_object(the_repository, oid);
+ struct object *obj;
+ int we_parsed_object;
+
+ obj = lookup_object(the_repository, oid);
+ if (obj && obj->parsed) {
+ we_parsed_object = 0;
+ } else {
+ we_parsed_object = 1;
+ obj = parse_object(the_repository, oid);
+ }
+
if (!obj)
return 1;
@@ -2243,7 +2253,7 @@ static int add_promisor_object(const struct object_id *oid,
struct tree *tree = (struct tree *)obj;
struct tree_desc desc;
struct name_entry entry;
- if (init_tree_desc_gently(&desc, tree->buffer, tree->size))
+ if (init_tree_desc_gently(&desc, tree->buffer, tree->size, 0))
/*
* Error messages are given when packs are
* verified, so do not print any here.
@@ -2251,7 +2261,8 @@ static int add_promisor_object(const struct object_id *oid,
return 0;
while (tree_entry_gently(&desc, &entry))
oidset_insert(set, &entry.oid);
- free_tree_buffer(tree);
+ if (we_parsed_object)
+ free_tree_buffer(tree);
} else if (obj->type == OBJ_COMMIT) {
struct commit *commit = (struct commit *) obj;
struct commit_list *parents = commit->parents;
diff --git a/preload-index.c b/preload-index.c
index e5529a5863..100f7a374d 100644
--- a/preload-index.c
+++ b/preload-index.c
@@ -151,6 +151,12 @@ void preload_index(struct index_state *index,
}
stop_progress(&pd.progress);
+ if (pathspec) {
+ /* earlier we made deep copies for each thread to work with */
+ for (i = 0; i < threads; i++)
+ clear_pathspec(&data[i].pathspec);
+ }
+
trace_performance_leave("preload index");
trace2_data_intmax("index", NULL, "preload/sum_lstat", t2_sum_lstat);
diff --git a/promisor-remote.c b/promisor-remote.c
index 5b33f88bca..68f46f5ec7 100644
--- a/promisor-remote.c
+++ b/promisor-remote.c
@@ -146,7 +146,7 @@ static void promisor_remote_init(struct repository *r)
if (r->promisor_remote_config)
return;
config = r->promisor_remote_config =
- xcalloc(sizeof(*r->promisor_remote_config), 1);
+ xcalloc(1, sizeof(*r->promisor_remote_config));
config->promisors_tail = &config->promisors;
repo_config(r, promisor_remote_config, config);
diff --git a/read-cache.c b/read-cache.c
index 4de207752d..b09128b188 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -620,6 +620,11 @@ int index_name_pos(struct index_state *istate, const char *name, int namelen)
return index_name_stage_pos(istate, name, namelen, 0, EXPAND_SPARSE);
}
+int index_name_pos_sparse(struct index_state *istate, const char *name, int namelen)
+{
+ return index_name_stage_pos(istate, name, namelen, 0, NO_EXPAND_SPARSE);
+}
+
int index_entry_exists(struct index_state *istate, const char *name, int namelen)
{
return index_name_stage_pos(istate, name, namelen, 0, NO_EXPAND_SPARSE) >= 0;
diff --git a/run-command.c b/run-command.c
index 14f17830f5..5ec3a46dcc 100644
--- a/run-command.c
+++ b/run-command.c
@@ -10,6 +10,7 @@
#include "config.h"
#include "packfile.h"
#include "hook.h"
+#include "compat/nonblock.h"
void child_process_init(struct child_process *child)
{
@@ -1364,12 +1365,25 @@ static int pump_io_round(struct io_pump *slots, int nr, struct pollfd *pfd)
continue;
if (io->type == POLLOUT) {
- ssize_t len = xwrite(io->fd,
- io->u.out.buf, io->u.out.len);
+ ssize_t len;
+
+ /*
+ * Don't use xwrite() here. It loops forever on EAGAIN,
+ * and we're in our own poll() loop here.
+ *
+ * Note that we lose xwrite()'s handling of MAX_IO_SIZE
+ * and EINTR, so we have to implement those ourselves.
+ */
+ len = write(io->fd, io->u.out.buf,
+ io->u.out.len <= MAX_IO_SIZE ?
+ io->u.out.len : MAX_IO_SIZE);
if (len < 0) {
- io->error = errno;
- close(io->fd);
- io->fd = -1;
+ if (errno != EINTR && errno != EAGAIN &&
+ errno != ENOSPC) {
+ io->error = errno;
+ close(io->fd);
+ io->fd = -1;
+ }
} else {
io->u.out.buf += len;
io->u.out.len -= len;
@@ -1438,6 +1452,15 @@ int pipe_command(struct child_process *cmd,
return -1;
if (in) {
+ if (enable_pipe_nonblock(cmd->in) < 0) {
+ error_errno("unable to make pipe non-blocking");
+ close(cmd->in);
+ if (out)
+ close(cmd->out);
+ if (err)
+ close(cmd->err);
+ return -1;
+ }
io[nr].fd = cmd->in;
io[nr].type = POLLOUT;
io[nr].u.out.buf = in;
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/t1092-sparse-checkout-compatibility.sh b/t/t1092-sparse-checkout-compatibility.sh
index f9f8c988bb..5dced39889 100755
--- a/t/t1092-sparse-checkout-compatibility.sh
+++ b/t/t1092-sparse-checkout-compatibility.sh
@@ -372,6 +372,14 @@ test_expect_success 'deep changes during checkout' '
test_all_match git checkout base
'
+test_expect_success 'checkout with modified sparse directory' '
+ init_repos &&
+
+ test_all_match git checkout rename-in-to-out -- . &&
+ test_sparse_match git sparse-checkout reapply &&
+ test_all_match git checkout base
+'
+
test_expect_success 'add outside sparse cone' '
init_repos &&
@@ -687,6 +695,23 @@ test_expect_success 'reset with wildcard pathspec' '
test_all_match git ls-files -s -- folder1
'
+test_expect_success 'reset hard with removed sparse dir' '
+ init_repos &&
+
+ run_on_all git rm -r --sparse folder1 &&
+ test_all_match git status --porcelain=v2 &&
+
+ test_all_match git reset --hard &&
+ test_all_match git status --porcelain=v2 &&
+
+ cat >expect <<-\EOF &&
+ folder1/
+ EOF
+
+ git -C sparse-index ls-files --sparse folder1 >out &&
+ test_cmp expect out
+'
+
test_expect_success 'update-index modify outside sparse definition' '
init_repos &&
diff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh
index ab7f31f1dc..53c2aa10b7 100755
--- a/t/t1450-fsck.sh
+++ b/t/t1450-fsck.sh
@@ -364,6 +364,20 @@ test_expect_success 'tree entry with type mismatch' '
test_i18ngrep ! "dangling blob" out
'
+test_expect_success 'tree entry with bogus mode' '
+ test_when_finished "remove_object \$blob" &&
+ test_when_finished "remove_object \$tree" &&
+ blob=$(echo blob | git hash-object -w --stdin) &&
+ blob_oct=$(echo $blob | hex2oct) &&
+ tree=$(printf "100000 foo\0${blob_oct}" |
+ git hash-object -t tree --stdin -w --literally) &&
+ git fsck 2>err &&
+ cat >expect <<-EOF &&
+ warning in tree $tree: badFilemode: contains bad file modes
+ EOF
+ test_cmp expect err
+'
+
test_expect_success 'tag pointing to nonexistent' '
badoid=$(test_oid deadbeef) &&
cat >invalid-tag <<-EOF &&
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/t3701-add-interactive.sh b/t/t3701-add-interactive.sh
index b354fb39de..3b7df9bed5 100755
--- a/t/t3701-add-interactive.sh
+++ b/t/t3701-add-interactive.sh
@@ -766,6 +766,19 @@ test_expect_success 'detect bogus diffFilter output' '
force_color test_must_fail git add -p <y
'
+test_expect_success 'handle very large filtered diff' '
+ git reset --hard &&
+ # The specific number here is not important, but it must
+ # be large enough that the output of "git diff --color"
+ # fills up the pipe buffer. 10,000 results in ~200k of
+ # colored output.
+ test_seq 10000 >test &&
+ test_config interactive.diffFilter cat &&
+ printf y >y &&
+ force_color git add -p >output 2>&1 <y &&
+ git diff-files --exit-code -- test
+'
+
test_expect_success 'diff.algorithm is passed to `git diff-files`' '
git reset --hard &&
diff --git a/t/t5504-fetch-receive-strict.sh b/t/t5504-fetch-receive-strict.sh
index b0b795aca9..ac4099ca89 100755
--- a/t/t5504-fetch-receive-strict.sh
+++ b/t/t5504-fetch-receive-strict.sh
@@ -352,4 +352,21 @@ test_expect_success \
grep "Cannot demote unterminatedheader" act
'
+test_expect_success 'badFilemode is not a strict error' '
+ git init --bare badmode.git &&
+ tree=$(
+ cd badmode.git &&
+ blob=$(echo blob | git hash-object -w --stdin | hex2oct) &&
+ printf "123456 foo\0${blob}" |
+ git hash-object -t tree --stdin -w --literally
+ ) &&
+
+ rm -rf dst.git &&
+ git init --bare dst.git &&
+ git -C dst.git config transfer.fsckObjects true &&
+
+ git -C badmode.git push ../dst.git $tree:refs/tags/tree 2>err &&
+ grep "$tree: badFilemode" err
+'
+
test_done
diff --git a/t/t5616-partial-clone.sh b/t/t5616-partial-clone.sh
index 4a3778d04a..9aeacc2f6a 100755
--- a/t/t5616-partial-clone.sh
+++ b/t/t5616-partial-clone.sh
@@ -49,6 +49,13 @@ test_expect_success 'do partial clone 1' '
test "$(git -C pc1 config --local remote.origin.partialclonefilter)" = "blob:none"
'
+test_expect_success 'rev-list --missing=allow-promisor on partial clone' '
+ git -C pc1 rev-list --objects --missing=allow-promisor HEAD >actual &&
+ git -C pc1 rev-list --objects --missing=print HEAD >expect.raw &&
+ grep -v "^?" expect.raw >expect &&
+ test_cmp expect actual
+'
+
test_expect_success 'verify that .promisor file contains refs fetched' '
ls pc1/.git/objects/pack/pack-*.promisor >promisorlist &&
test_line_count = 1 promisorlist &&
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/tree-walk.c b/tree-walk.c
index 506234b4b8..74f4d710e8 100644
--- a/tree-walk.c
+++ b/tree-walk.c
@@ -47,17 +47,20 @@ static int decode_tree_entry(struct tree_desc *desc, const char *buf, unsigned l
/* Initialize the descriptor entry */
desc->entry.path = path;
- desc->entry.mode = canon_mode(mode);
+ desc->entry.mode = (desc->flags & TREE_DESC_RAW_MODES) ? mode : canon_mode(mode);
desc->entry.pathlen = len - 1;
oidread(&desc->entry.oid, (const unsigned char *)path + len);
return 0;
}
-static int init_tree_desc_internal(struct tree_desc *desc, const void *buffer, unsigned long size, struct strbuf *err)
+static int init_tree_desc_internal(struct tree_desc *desc, const void *buffer,
+ unsigned long size, struct strbuf *err,
+ enum tree_desc_flags flags)
{
desc->buffer = buffer;
desc->size = size;
+ desc->flags = flags;
if (size)
return decode_tree_entry(desc, buffer, size, err);
return 0;
@@ -66,15 +69,16 @@ static int init_tree_desc_internal(struct tree_desc *desc, const void *buffer, u
void init_tree_desc(struct tree_desc *desc, const void *buffer, unsigned long size)
{
struct strbuf err = STRBUF_INIT;
- if (init_tree_desc_internal(desc, buffer, size, &err))
+ if (init_tree_desc_internal(desc, buffer, size, &err, 0))
die("%s", err.buf);
strbuf_release(&err);
}
-int init_tree_desc_gently(struct tree_desc *desc, const void *buffer, unsigned long size)
+int init_tree_desc_gently(struct tree_desc *desc, const void *buffer, unsigned long size,
+ enum tree_desc_flags flags)
{
struct strbuf err = STRBUF_INIT;
- int result = init_tree_desc_internal(desc, buffer, size, &err);
+ int result = init_tree_desc_internal(desc, buffer, size, &err, flags);
if (result)
error("%s", err.buf);
strbuf_release(&err);
diff --git a/tree-walk.h b/tree-walk.h
index a5058469e9..6305d53150 100644
--- a/tree-walk.h
+++ b/tree-walk.h
@@ -34,6 +34,11 @@ struct tree_desc {
/* counts the number of bytes left in the `buffer`. */
unsigned int size;
+
+ /* option flags passed via init_tree_desc_gently() */
+ enum tree_desc_flags {
+ TREE_DESC_RAW_MODES = (1 << 0),
+ } flags;
};
/**
@@ -79,7 +84,8 @@ int update_tree_entry_gently(struct tree_desc *);
*/
void init_tree_desc(struct tree_desc *desc, const void *buf, unsigned long size);
-int init_tree_desc_gently(struct tree_desc *desc, const void *buf, unsigned long size);
+int init_tree_desc_gently(struct tree_desc *desc, const void *buf, unsigned long size,
+ enum tree_desc_flags flags);
/*
* Visit the next entry in a tree. Returns 1 when there are more entries
diff --git a/unpack-trees.c b/unpack-trees.c
index 8a454e03bf..90b92114be 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -1070,6 +1070,67 @@ static struct cache_entry *create_ce_entry(const struct traverse_info *info,
}
/*
+ * Determine whether the path specified by 'p' should be unpacked as a new
+ * sparse directory in a sparse index. A new sparse directory 'A/':
+ * - must be outside the sparse cone.
+ * - must not already be in the index (i.e., no index entry with name 'A/'
+ * exists).
+ * - must not have any child entries in the index (i.e., no index entry
+ * 'A/<something>' exists).
+ * If 'p' meets the above requirements, return 1; otherwise, return 0.
+ */
+static int entry_is_new_sparse_dir(const struct traverse_info *info,
+ const struct name_entry *p)
+{
+ int res, pos;
+ struct strbuf dirpath = STRBUF_INIT;
+ struct unpack_trees_options *o = info->data;
+
+ if (!S_ISDIR(p->mode))
+ return 0;
+
+ /*
+ * If the path is inside the sparse cone, it can't be a sparse directory.
+ */
+ strbuf_add(&dirpath, info->traverse_path, info->pathlen);
+ strbuf_add(&dirpath, p->path, p->pathlen);
+ strbuf_addch(&dirpath, '/');
+ if (path_in_cone_mode_sparse_checkout(dirpath.buf, o->src_index)) {
+ res = 0;
+ goto cleanup;
+ }
+
+ pos = index_name_pos_sparse(o->src_index, dirpath.buf, dirpath.len);
+ if (pos >= 0) {
+ /* Path is already in the index, not a new sparse dir */
+ res = 0;
+ goto cleanup;
+ }
+
+ /* Where would this sparse dir be inserted into the index? */
+ pos = -pos - 1;
+ if (pos >= o->src_index->cache_nr) {
+ /*
+ * Sparse dir would be inserted at the end of the index, so we
+ * know it has no child entries.
+ */
+ res = 1;
+ goto cleanup;
+ }
+
+ /*
+ * If the dir has child entries in the index, the first would be at the
+ * position the sparse directory would be inserted. If the entry at this
+ * position is inside the dir, not a new sparse dir.
+ */
+ res = strncmp(o->src_index->cache[pos]->name, dirpath.buf, dirpath.len);
+
+cleanup:
+ strbuf_release(&dirpath);
+ return res;
+}
+
+/*
* Note that traverse_by_cache_tree() duplicates some logic in this function
* without actually calling it. If you change the logic here you may need to
* check and change there as well.
@@ -1078,21 +1139,44 @@ static int unpack_single_entry(int n, unsigned long mask,
unsigned long dirmask,
struct cache_entry **src,
const struct name_entry *names,
- const struct traverse_info *info)
+ const struct traverse_info *info,
+ int *is_new_sparse_dir)
{
int i;
struct unpack_trees_options *o = info->data;
unsigned long conflicts = info->df_conflicts | dirmask;
+ const struct name_entry *p = names;
- if (mask == dirmask && !src[0])
- return 0;
+ *is_new_sparse_dir = 0;
+ if (mask == dirmask && !src[0]) {
+ /*
+ * If we're not in a sparse index, we can't unpack a directory
+ * without recursing into it, so we return.
+ */
+ if (!o->src_index->sparse_index)
+ return 0;
+
+ /* Find first entry with a real name (we could use "mask" too) */
+ while (!p->mode)
+ p++;
+
+ /*
+ * If the directory is completely missing from the index but
+ * would otherwise be a sparse directory, we should unpack it.
+ * If not, we'll return and continue recursively traversing the
+ * tree.
+ */
+ *is_new_sparse_dir = entry_is_new_sparse_dir(info, p);
+ if (!*is_new_sparse_dir)
+ return 0;
+ }
/*
- * When we have a sparse directory entry for src[0],
- * then this isn't necessarily a directory-file conflict.
+ * When we are unpacking a sparse directory, then this isn't necessarily
+ * a directory-file conflict.
*/
- if (mask == dirmask && src[0] &&
- S_ISSPARSEDIR(src[0]->ce_mode))
+ if (mask == dirmask &&
+ (*is_new_sparse_dir || (src[0] && S_ISSPARSEDIR(src[0]->ce_mode))))
conflicts = 0;
/*
@@ -1352,7 +1436,7 @@ static int unpack_sparse_callback(int n, unsigned long mask, unsigned long dirma
{
struct cache_entry *src[MAX_UNPACK_TREES + 1] = { NULL, };
struct unpack_trees_options *o = info->data;
- int ret;
+ int ret, is_new_sparse_dir;
assert(o->merge);
@@ -1376,7 +1460,7 @@ static int unpack_sparse_callback(int n, unsigned long mask, unsigned long dirma
* "index" tree (i.e., names[0]) and adjust 'names', 'n', 'mask', and
* 'dirmask' accordingly.
*/
- ret = unpack_single_entry(n - 1, mask >> 1, dirmask >> 1, src, names + 1, info);
+ ret = unpack_single_entry(n - 1, mask >> 1, dirmask >> 1, src, names + 1, info, &is_new_sparse_dir);
if (src[0])
discard_cache_entry(src[0]);
@@ -1394,6 +1478,7 @@ static int unpack_callback(int n, unsigned long mask, unsigned long dirmask, str
struct cache_entry *src[MAX_UNPACK_TREES + 1] = { NULL, };
struct unpack_trees_options *o = info->data;
const struct name_entry *p = names;
+ int is_new_sparse_dir;
/* Find first entry with a real name (we could use "mask" too) */
while (!p->mode)
@@ -1440,7 +1525,7 @@ static int unpack_callback(int n, unsigned long mask, unsigned long dirmask, str
}
}
- if (unpack_single_entry(n, mask, dirmask, src, names, info) < 0)
+ if (unpack_single_entry(n, mask, dirmask, src, names, info, &is_new_sparse_dir))
return -1;
if (o->merge && src[0]) {
@@ -1478,6 +1563,7 @@ static int unpack_callback(int n, unsigned long mask, unsigned long dirmask, str
}
if (!is_sparse_directory_entry(src[0], names, info) &&
+ !is_new_sparse_dir &&
traverse_trees_recursive(n, dirmask, mask & ~dirmask,
names, info) < 0) {
return -1;
diff --git a/wrapper.c b/wrapper.c
index 1c3c970080..f52f3e21cc 100644
--- a/wrapper.c
+++ b/wrapper.c
@@ -161,28 +161,6 @@ void xsetenv(const char *name, const char *value, int overwrite)
die_errno(_("could not setenv '%s'"), name ? name : "(null)");
}
-/*
- * Limit size of IO chunks, because huge chunks only cause pain. OS X
- * 64-bit is buggy, returning EINVAL if len >= INT_MAX; and even in
- * the absence of bugs, large chunks can result in bad latencies when
- * you decide to kill the process.
- *
- * We pick 8 MiB as our default, but if the platform defines SSIZE_MAX
- * that is smaller than that, clip it to SSIZE_MAX, as a call to
- * read(2) or write(2) larger than that is allowed to fail. As the last
- * resort, we allow a port to pass via CFLAGS e.g. "-DMAX_IO_SIZE=value"
- * to override this, if the definition of SSIZE_MAX given by the platform
- * is broken.
- */
-#ifndef MAX_IO_SIZE
-# define MAX_IO_SIZE_DEFAULT (8*1024*1024)
-# if defined(SSIZE_MAX) && (SSIZE_MAX < MAX_IO_SIZE_DEFAULT)
-# define MAX_IO_SIZE SSIZE_MAX
-# else
-# define MAX_IO_SIZE MAX_IO_SIZE_DEFAULT
-# endif
-#endif
-
/**
* xopen() is the same as open(), but it die()s if the open() fails.
*/