diff --git a/Documentation/RelNotes/2.32.0.txt b/Documentation/RelNotes/2.32.0.txt index 6f69a521accd84..f39eede0011738 100644 --- a/Documentation/RelNotes/2.32.0.txt +++ b/Documentation/RelNotes/2.32.0.txt @@ -40,6 +40,13 @@ UI, Workflows & Features tweak both the message and the contents, and only the message, respectively. + * When accessing a server with a URL like https://user:pass@site/, we + did not to fall back to the basic authentication with the + credential material embedded in the URL after the "Negotiate" + authentication failed. Now we do. + + * "git send-email" learned to honor the core.hooksPath configuration. + Performance, Internal Implementation, Development Support etc. @@ -66,29 +73,22 @@ Fixes since v2.31 * The fsmonitor interface read from its input without making sure there is something to read from. This bug is new in 2.31 timeframe. - (merge 097ea2c848 jh/fsmonitor-prework later to maint). * The data structure used by fsmonitor interface was not properly duplicated during an in-core merge, leading to use-after-free etc. - (merge 4abc57848d js/fsmonitor-unpack-fix later to maint). * "git bisect" reimplemented more in C during 2.30 timeframe did not take an annotated tag as a good/bad endpoint well. This regression has been corrected. - (merge 7730f85594 jk/bisect-peel-tag-fix later to maint). * Fix macros that can silently inject unintended null-statements. - (merge 116affac3f rs/avoid-null-statement-after-macro-call later to maint). * CALLOC_ARRAY() macro replaces many uses of xcalloc(). - (merge 1c57cc70ec rs/calloc-array later to maint). * Update insn in Makefile comments to run fuzz-all target. - (merge 68b5c3aa48 ah/make-fuzz-all-doc-update later to maint). * Fix a corner case bug in "git mv" on case insensitive systems, which was introduced in 2.29 timeframe. - (merge 93c3d297b5 tb/git-mv-icase-fix later to maint). * We had a code to diagnose and die cleanly when a required clean/smudge filter is missing, but an assert before that @@ -115,15 +115,20 @@ Fixes since v2.31 which has been corrected. (merge 75555676ad bc/clone-bare-with-conflicting-config later to maint). + * When "git checkout" removes a path that does not exist in the + commit it is checking out, it wasn't careful enough not to follow + symbolic links, which has been corrected. + (merge fab78a0c3d mt/checkout-remove-nofollow later to maint). + * Other code cleanup, docfix, build fix, etc. - (merge 486f4bd183 jc/calloc-fix later to maint). - (merge 5f70859c15 jt/clone-unborn-head later to maint). - (merge cfd409ed09 km/config-doc-typofix later to maint). - (merge 8588aa8657 jk/slimmed-down later to maint). - (merge 241b5d3ebe rs/xcalloc-takes-nelem-first later to maint). (merge f451960708 dl/cat-file-doc-cleanup later to maint). (merge 12604a8d0c sv/t9801-test-path-is-file-cleanup later to maint). (merge ea7e63921c jr/doc-ignore-typofix later to maint). (merge 23c781f173 ps/update-ref-trans-hook-doc later to maint). (merge 42efa1231a jk/filter-branch-sha256 later to maint). (merge 4c8e3dca6e tb/push-simple-uses-branch-merge-config later to maint). + (merge 6534d436a2 bs/asciidoctor-installation-hints later to maint). + (merge 47957485b3 ab/read-tree later to maint). + (merge 2be927f3d1 ab/diff-no-index-tests later to maint). + (merge 76593c09bb ab/detox-gettext-tests later to maint). + (merge 28e29ee38b jc/doc-format-patch-clarify later to maint). diff --git a/Documentation/config/rebase.txt b/Documentation/config/rebase.txt index 214f31b451f17b..8c979cb20f2a57 100644 --- a/Documentation/config/rebase.txt +++ b/Documentation/config/rebase.txt @@ -1,10 +1,3 @@ -rebase.useBuiltin:: - Unused configuration variable. Used in Git versions 2.20 and - 2.21 as an escape hatch to enable the legacy shellscript - implementation of rebase. Now the built-in rewrite of it in C - is always used. Setting this will emit a warning, to alert any - remaining users that setting this now does nothing. - rebase.backend:: Default backend to use for rebasing. Possible choices are 'apply' or 'merge'. In the future, if the merge backend gains diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt index 3e49bf221087c0..5cd8578b6f387b 100644 --- a/Documentation/git-format-patch.txt +++ b/Documentation/git-format-patch.txt @@ -36,11 +36,28 @@ SYNOPSIS DESCRIPTION ----------- -Prepare each commit with its patch in -one file per commit, formatted to resemble UNIX mailbox format. +Prepare each commit with its "patch" in +one "message" per commit, formatted to resemble a UNIX mailbox. The output of this command is convenient for e-mail submission or for use with 'git am'. +A "message" generated by the command consists of three parts: + +* A brief metadata header that begins with `From ` + with a fixed `Mon Sep 17 00:00:00 2001` datestamp to help programs + like "file(1)" to recognize that the file is an output from this + command, fields that record the author identity, the author date, + and the title of the change (taken from the first paragraph of the + commit log message). + +* The second and subsequent paragraphs of the commit log message. + +* The "patch", which is the "diff -p --stat" output (see + linkgit:git-diff[1]) between the commit and its parent. + +The log message and the patch is separated by a line with a +three-dash line. + There are two ways to specify which commits to operate on. 1. A single commit, , specifies that the commits leading diff --git a/INSTALL b/INSTALL index 8474ad01bf9db8..66389ce05915d7 100644 --- a/INSTALL +++ b/INSTALL @@ -197,7 +197,9 @@ Issues of note: Building and installing the pdf file additionally requires dblatex. Version >= 0.2.7 is known to work. - All formats require at least asciidoc 8.4.1. + All formats require at least asciidoc 8.4.1. Alternatively, you can + use Asciidoctor (requires Ruby) by passing USE_ASCIIDOCTOR=YesPlease + to make. You need at least Asciidoctor version 1.5. There are also "make quick-install-doc", "make quick-install-man" and "make quick-install-html" which install preformatted man pages diff --git a/archive.c b/archive.c index 2dd2236ae0cef9..295615580d7106 100644 --- a/archive.c +++ b/archive.c @@ -104,7 +104,6 @@ struct directory { struct object_id oid; int baselen, len; unsigned mode; - int stage; char path[FLEX_ARRAY]; }; @@ -135,7 +134,7 @@ static int check_attr_export_subst(const struct attr_check *check) } static int write_archive_entry(const struct object_id *oid, const char *base, - int baselen, const char *filename, unsigned mode, int stage, + int baselen, const char *filename, unsigned mode, void *context) { static struct strbuf path = STRBUF_INIT; @@ -194,7 +193,7 @@ static int write_archive_entry(const struct object_id *oid, const char *base, static void queue_directory(const unsigned char *sha1, struct strbuf *base, const char *filename, - unsigned mode, int stage, struct archiver_context *c) + unsigned mode, struct archiver_context *c) { struct directory *d; size_t len = st_add4(base->len, 1, strlen(filename), 1); @@ -202,7 +201,6 @@ static void queue_directory(const unsigned char *sha1, d->up = c->bottom; d->baselen = base->len; d->mode = mode; - d->stage = stage; c->bottom = d; d->len = xsnprintf(d->path, len, "%.*s%s/", (int)base->len, base->buf, filename); hashcpy(d->oid.hash, sha1); @@ -221,14 +219,14 @@ static int write_directory(struct archiver_context *c) write_directory(c) || write_archive_entry(&d->oid, d->path, d->baselen, d->path + d->baselen, d->mode, - d->stage, c) != READ_TREE_RECURSIVE; + c) != READ_TREE_RECURSIVE; free(d); return ret ? -1 : 0; } static int queue_or_write_archive_entry(const struct object_id *oid, struct strbuf *base, const char *filename, - unsigned mode, int stage, void *context) + unsigned mode, void *context) { struct archiver_context *c = context; @@ -253,14 +251,14 @@ static int queue_or_write_archive_entry(const struct object_id *oid, if (check_attr_export_ignore(check)) return 0; queue_directory(oid->hash, base, filename, - mode, stage, c); + mode, c); return READ_TREE_RECURSIVE; } if (write_directory(c)) return -1; return write_archive_entry(oid, base->buf, base->len, filename, mode, - stage, context); + context); } struct extra_file_info { @@ -313,10 +311,10 @@ int write_archive_entries(struct archiver_args *args, git_attr_set_direction(GIT_ATTR_INDEX); } - err = read_tree_recursive(args->repo, args->tree, "", - 0, 0, &args->pathspec, - queue_or_write_archive_entry, - &context); + err = read_tree(args->repo, args->tree, + &args->pathspec, + queue_or_write_archive_entry, + &context); if (err == READ_TREE_RECURSIVE) err = 0; while (context.bottom) { @@ -375,7 +373,7 @@ struct path_exists_context { static int reject_entry(const struct object_id *oid, struct strbuf *base, const char *filename, unsigned mode, - int stage, void *context) + void *context) { int ret = -1; struct path_exists_context *ctx = context; @@ -402,9 +400,9 @@ static int path_exists(struct archiver_args *args, const char *path) ctx.args = args; parse_pathspec(&ctx.pathspec, 0, 0, "", paths); ctx.pathspec.recursive = 1; - ret = read_tree_recursive(args->repo, args->tree, "", - 0, 0, &ctx.pathspec, - reject_entry, &ctx); + ret = read_tree(args->repo, args->tree, + &ctx.pathspec, + reject_entry, &ctx); clear_pathspec(&ctx.pathspec); return ret != 0; } diff --git a/builtin/checkout.c b/builtin/checkout.c index 2d6550bc3c8638..0e663905200155 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -114,7 +114,7 @@ static int post_checkout_hook(struct commit *old_commit, struct commit *new_comm } static int update_some(const struct object_id *oid, struct strbuf *base, - const char *pathname, unsigned mode, int stage, void *context) + const char *pathname, unsigned mode, void *context) { int len; struct cache_entry *ce; @@ -155,8 +155,8 @@ static int update_some(const struct object_id *oid, struct strbuf *base, static int read_tree_some(struct tree *tree, const struct pathspec *pathspec) { - read_tree_recursive(the_repository, tree, "", 0, 0, - pathspec, update_some, NULL); + read_tree(the_repository, tree, + pathspec, update_some, NULL); /* update the index with the given tree's info * for all args, expanding wildcards, and exit @@ -322,7 +322,7 @@ static void mark_ce_for_checkout_overlay(struct cache_entry *ce, * If it comes from the tree-ish, we already know it * matches the pathspec and could just stamp * CE_MATCHED to it from update_some(). But we still - * need ps_matched and read_tree_recursive (and + * need ps_matched and read_tree (and * eventually tree_entry_interesting) cannot fill * ps_matched yet. Once it can, we can avoid calling * match_pathspec() for _all_ entries when diff --git a/builtin/log.c b/builtin/log.c index f67b67d80ed1e8..980de590638374 100644 --- a/builtin/log.c +++ b/builtin/log.c @@ -599,7 +599,7 @@ static int show_tag_object(const struct object_id *oid, struct rev_info *rev) static int show_tree_object(const struct object_id *oid, struct strbuf *base, - const char *pathname, unsigned mode, int stage, void *context) + const char *pathname, unsigned mode, void *context) { FILE *file = context; fprintf(file, "%s%s\n", pathname, S_ISDIR(mode) ? "/" : ""); @@ -681,9 +681,9 @@ int cmd_show(int argc, const char **argv, const char *prefix) diff_get_color_opt(&rev.diffopt, DIFF_COMMIT), name, diff_get_color_opt(&rev.diffopt, DIFF_RESET)); - read_tree_recursive(the_repository, (struct tree *)o, "", - 0, 0, &match_all, show_tree_object, - rev.diffopt.file); + read_tree(the_repository, (struct tree *)o, + &match_all, show_tree_object, + rev.diffopt.file); rev.shown_one = 1; break; case OBJ_COMMIT: diff --git a/builtin/ls-files.c b/builtin/ls-files.c index f6f9e483b27e18..60a2913a01e9d0 100644 --- a/builtin/ls-files.c +++ b/builtin/ls-files.c @@ -12,6 +12,7 @@ #include "dir.h" #include "builtin.h" #include "tree.h" +#include "cache-tree.h" #include "parse-options.h" #include "resolve-undo.h" #include "string-list.h" @@ -420,6 +421,53 @@ static int get_common_prefix_len(const char *common_prefix) return common_prefix_len; } +static int read_one_entry_opt(struct index_state *istate, + const struct object_id *oid, + struct strbuf *base, + const char *pathname, + unsigned mode, int opt) +{ + int len; + struct cache_entry *ce; + + if (S_ISDIR(mode)) + return READ_TREE_RECURSIVE; + + len = strlen(pathname); + ce = make_empty_cache_entry(istate, base->len + len); + + ce->ce_mode = create_ce_mode(mode); + ce->ce_flags = create_ce_flags(1); + ce->ce_namelen = base->len + len; + memcpy(ce->name, base->buf, base->len); + memcpy(ce->name + base->len, pathname, len+1); + oidcpy(&ce->oid, oid); + return add_index_entry(istate, ce, opt); +} + +static int read_one_entry(const struct object_id *oid, struct strbuf *base, + const char *pathname, unsigned mode, + void *context) +{ + struct index_state *istate = context; + return read_one_entry_opt(istate, oid, base, pathname, + mode, + ADD_CACHE_OK_TO_ADD|ADD_CACHE_SKIP_DFCHECK); +} + +/* + * This is used when the caller knows there is no existing entries at + * the stage that will conflict with the entry being added. + */ +static int read_one_entry_quick(const struct object_id *oid, struct strbuf *base, + const char *pathname, unsigned mode, + void *context) +{ + struct index_state *istate = context; + return read_one_entry_opt(istate, oid, base, pathname, + mode, ADD_CACHE_JUST_APPEND); +} + /* * Read the tree specified with --with-tree option * (typically, HEAD) into stage #1 and then @@ -436,6 +484,8 @@ void overlay_tree_on_index(struct index_state *istate, struct pathspec pathspec; struct cache_entry *last_stage0 = NULL; int i; + read_tree_fn_t fn = NULL; + int err; if (get_oid(tree_name, &oid)) die("tree-ish %s not found.", tree_name); @@ -458,9 +508,32 @@ void overlay_tree_on_index(struct index_state *istate, PATHSPEC_PREFER_CWD, prefix, matchbuf); } else memset(&pathspec, 0, sizeof(pathspec)); - if (read_tree(the_repository, tree, 1, &pathspec, istate)) + + /* + * See if we have cache entry at the stage. If so, + * do it the original slow way, otherwise, append and then + * sort at the end. + */ + for (i = 0; !fn && i < istate->cache_nr; i++) { + const struct cache_entry *ce = istate->cache[i]; + if (ce_stage(ce) == 1) + fn = read_one_entry; + } + + if (!fn) + fn = read_one_entry_quick; + err = read_tree(the_repository, tree, &pathspec, fn, istate); + if (err) die("unable to read tree entries %s", tree_name); + /* + * Sort the cache entry -- we need to nuke the cache tree, though. + */ + if (fn == read_one_entry_quick) { + cache_tree_free(&istate->cache_tree); + QSORT(istate->cache, istate->cache_nr, cmp_cache_name_compare); + } + for (i = 0; i < istate->cache_nr; i++) { struct cache_entry *ce = istate->cache[i]; switch (ce_stage(ce)) { diff --git a/builtin/ls-tree.c b/builtin/ls-tree.c index 7cad3f24ebd084..3a442631c71a07 100644 --- a/builtin/ls-tree.c +++ b/builtin/ls-tree.c @@ -62,7 +62,7 @@ static int show_recursive(const char *base, int baselen, const char *pathname) } static int show_tree(const struct object_id *oid, struct strbuf *base, - const char *pathname, unsigned mode, int stage, void *context) + const char *pathname, unsigned mode, void *context) { int retval = 0; int baselen; @@ -185,6 +185,6 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix) tree = parse_tree_indirect(&oid); if (!tree) die("not a tree object"); - return !!read_tree_recursive(the_repository, tree, "", 0, 0, - &pathspec, show_tree, NULL); + return !!read_tree(the_repository, tree, + &pathspec, show_tree, NULL); } diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c index 8319831514fdc7..525c2d85529f41 100644 --- a/builtin/pack-objects.c +++ b/builtin/pack-objects.c @@ -815,8 +815,8 @@ static struct reused_chunk { /* The offset of the first object of this chunk in the original * packfile. */ off_t original; - /* The offset of the first object of this chunk in the generated - * packfile minus "original". */ + /* The difference for "original" minus the offset of the first object of + * this chunk in the generated packfile. */ off_t difference; } *reused_chunks; static int reused_chunks_nr; diff --git a/builtin/rebase.c b/builtin/rebase.c index de400f9a197339..783b526f6e758a 100644 --- a/builtin/rebase.c +++ b/builtin/rebase.c @@ -100,7 +100,6 @@ struct rebase_options { char *strategy, *strategy_opts; struct strbuf git_format_patch_opt; int reschedule_failed_exec; - int use_legacy_rebase; int reapply_cherry_picks; int fork_point; }; @@ -1102,11 +1101,6 @@ static int rebase_config(const char *var, const char *value, void *data) return 0; } - if (!strcmp(var, "rebase.usebuiltin")) { - opts->use_legacy_rebase = !git_config_bool(var, value); - return 0; - } - if (!strcmp(var, "rebase.backend")) { return git_config_string(&opts->default_backend, var, value); } @@ -1441,11 +1435,6 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) gpg_sign = options.gpg_sign_opt ? "" : NULL; FREE_AND_NULL(options.gpg_sign_opt); - if (options.use_legacy_rebase || - !git_env_bool("GIT_TEST_REBASE_USE_BUILTIN", -1)) - warning(_("the rebase.useBuiltin support has been removed!\n" - "See its entry in 'git help config' for details.")); - strbuf_reset(&buf); strbuf_addf(&buf, "%s/applying", apply_dir()); if(file_exists(buf.buf)) diff --git a/cache.h b/cache.h index 6fda8091f11de1..57f2285bba9f0e 100644 --- a/cache.h +++ b/cache.h @@ -803,7 +803,7 @@ static inline int index_pos_to_insert_pos(uintmax_t pos) #define ADD_CACHE_OK_TO_ADD 1 /* Ok to add */ #define ADD_CACHE_OK_TO_REPLACE 2 /* Ok to replace file/directory */ #define ADD_CACHE_SKIP_DFCHECK 4 /* Ok to skip DF conflict checks */ -#define ADD_CACHE_JUST_APPEND 8 /* Append only; tree.c::read_tree() */ +#define ADD_CACHE_JUST_APPEND 8 /* Append only */ #define ADD_CACHE_NEW_ONLY 16 /* Do not replace existing ones */ #define ADD_CACHE_KEEP_CACHE_TREE 32 /* Do not invalidate cache-tree */ #define ADD_CACHE_RENORMALIZE 64 /* Pass along HASH_RENORMALIZE */ @@ -1659,7 +1659,7 @@ static inline void cache_def_clear(struct cache_def *cache) int has_symlink_leading_path(const char *name, int len); int threaded_has_symlink_leading_path(struct cache_def *, const char *, int); -int check_leading_path(const char *name, int len); +int check_leading_path(const char *name, int len, int warn_on_lstat_err); int has_dirs_only_path(const char *name, int len, int prefix_len); void invalidate_lstat_cache(void); void schedule_dir_for_removal(const char *name, int len); diff --git a/entry.c b/entry.c index 7b9f43716f76e6..33ce80ca91709b 100644 --- a/entry.c +++ b/entry.c @@ -530,7 +530,7 @@ void unlink_entry(const struct cache_entry *ce) submodule_move_head(ce->name, "HEAD", NULL, SUBMODULE_MOVE_HEAD_FORCE); } - if (!check_leading_path(ce->name, ce_namelen(ce))) + if (check_leading_path(ce->name, ce_namelen(ce), 1) >= 0) return; if (remove_or_warn(ce->ce_mode, ce->name)) return; diff --git a/git-send-email.perl b/git-send-email.perl index 1f425c08091d40..f5bbf1647e3bda 100755 --- a/git-send-email.perl +++ b/git-send-email.perl @@ -1942,7 +1942,7 @@ sub validate_patch { my ($fn, $xfer_encoding) = @_; if ($repo) { - my $validate_hook = catfile(catdir($repo->repo_path(), 'hooks'), + my $validate_hook = catfile($repo->hooks_path(), 'sendemail-validate'); my $hook_error; if (-x $validate_hook) { diff --git a/http.c b/http.c index 70b0f15aefb2de..406410f884e2e5 100644 --- a/http.c +++ b/http.c @@ -1650,17 +1650,18 @@ static int handle_curl_result(struct slot_results *results) } else if (missing_target(results)) return HTTP_MISSING_TARGET; else if (results->http_code == 401) { +#ifdef LIBCURL_CAN_HANDLE_AUTH_ANY + http_auth_methods &= ~CURLAUTH_GSSNEGOTIATE; + if (results->auth_avail) { + http_auth_methods &= results->auth_avail; + http_auth_methods_restricted = 1; + return HTTP_REAUTH; + } +#endif if (http_auth.username && http_auth.password) { credential_reject(&http_auth); return HTTP_NOAUTH; } else { -#ifdef LIBCURL_CAN_HANDLE_AUTH_ANY - http_auth_methods &= ~CURLAUTH_GSSNEGOTIATE; - if (results->auth_avail) { - http_auth_methods &= results->auth_avail; - http_auth_methods_restricted = 1; - } -#endif return HTTP_REAUTH; } } else { diff --git a/merge-recursive.c b/merge-recursive.c index b69e694d986ac8..ed31f9496cbcb2 100644 --- a/merge-recursive.c +++ b/merge-recursive.c @@ -453,7 +453,7 @@ static void unpack_trees_finish(struct merge_options *opt) static int save_files_dirs(const struct object_id *oid, struct strbuf *base, const char *path, - unsigned int mode, int stage, void *context) + unsigned int mode, void *context) { struct path_hashmap_entry *entry; int baselen = base->len; @@ -473,8 +473,8 @@ static void get_files_dirs(struct merge_options *opt, struct tree *tree) { struct pathspec match_all; memset(&match_all, 0, sizeof(match_all)); - read_tree_recursive(opt->repo, tree, "", 0, 0, - &match_all, save_files_dirs, opt); + read_tree(opt->repo, tree, + &match_all, save_files_dirs, opt); } static int get_tree_entry_if_blob(struct repository *r, diff --git a/perl/Git.pm b/perl/Git.pm index 02eacef0c2a4a4..73ebbf80cc6f42 100644 --- a/perl/Git.pm +++ b/perl/Git.pm @@ -619,6 +619,19 @@ Return path to the git repository. Must be called on a repository instance. sub repo_path { $_[0]->{opts}->{Repository} } +=item hooks_path () + +Return path to the hooks directory. Must be called on a repository instance. + +=cut + +sub hooks_path { + my ($self) = @_; + + my $dir = $self->command_oneline('rev-parse', '--git-path', 'hooks'); + my $abs = abs_path($dir); + return $abs; +} =item wc_path () diff --git a/symlinks.c b/symlinks.c index 7dbb6b23d9cf35..5232d02020c725 100644 --- a/symlinks.c +++ b/symlinks.c @@ -1,6 +1,7 @@ #include "cache.h" -static int threaded_check_leading_path(struct cache_def *cache, const char *name, int len); +static int threaded_check_leading_path(struct cache_def *cache, const char *name, + int len, int warn_on_lstat_err); static int threaded_has_dirs_only_path(struct cache_def *cache, const char *name, int len, int prefix_len); /* @@ -72,7 +73,7 @@ static int lstat_cache_matchlen(struct cache_def *cache, int prefix_len_stat_func) { int match_len, last_slash, last_slash_dir, previous_slash; - int save_flags, ret; + int save_flags, ret, saved_errno = 0; struct stat st; if (cache->track_flags != track_flags || @@ -139,6 +140,7 @@ static int lstat_cache_matchlen(struct cache_def *cache, if (ret) { *ret_flags = FL_LSTATERR; + saved_errno = errno; if (errno == ENOENT) *ret_flags |= FL_NOENT; } else if (S_ISDIR(st.st_mode)) { @@ -180,6 +182,8 @@ static int lstat_cache_matchlen(struct cache_def *cache, } else { reset_lstat_cache(cache); } + if (saved_errno) + errno = saved_errno; return match_len; } @@ -202,57 +206,47 @@ int threaded_has_symlink_leading_path(struct cache_def *cache, const char *name, return lstat_cache(cache, name, len, FL_SYMLINK|FL_DIR, USE_ONLY_LSTAT) & FL_SYMLINK; } -/* - * Return non-zero if path 'name' has a leading symlink component - */ int has_symlink_leading_path(const char *name, int len) { return threaded_has_symlink_leading_path(&default_cache, name, len); } -/* - * Return zero if path 'name' has a leading symlink component or - * if some leading path component does not exists. - * - * Return -1 if leading path exists and is a directory. - * - * Return path length if leading path exists and is neither a - * directory nor a symlink. - */ -int check_leading_path(const char *name, int len) +int check_leading_path(const char *name, int len, int warn_on_lstat_err) { - return threaded_check_leading_path(&default_cache, name, len); + return threaded_check_leading_path(&default_cache, name, len, + warn_on_lstat_err); } /* - * Return zero if path 'name' has a leading symlink component or - * if some leading path component does not exists. + * Return zero if some leading path component of 'name' does not exist. * * Return -1 if leading path exists and is a directory. * - * Return path length if leading path exists and is neither a - * directory nor a symlink. + * Return the length of a leading component if it either exists but it's not a + * directory, or if we were unable to lstat() it. If warn_on_lstat_err is true, + * also emit a warning for this error. */ -static int threaded_check_leading_path(struct cache_def *cache, const char *name, int len) +static int threaded_check_leading_path(struct cache_def *cache, const char *name, + int len, int warn_on_lstat_err) { int flags; int match_len = lstat_cache_matchlen(cache, name, len, &flags, FL_SYMLINK|FL_NOENT|FL_DIR, USE_ONLY_LSTAT); + int saved_errno = errno; + if (flags & FL_NOENT) return 0; else if (flags & FL_DIR) return -1; - else - return match_len; + if (warn_on_lstat_err && (flags & FL_LSTATERR)) { + char *path = xmemdupz(name, match_len); + errno = saved_errno; + warning_errno(_("failed to lstat '%s'"), path); + free(path); + } + return match_len; } -/* - * Return non-zero if all path components of 'name' exists as a - * directory. If prefix_len > 0, we will test with the stat() - * function instead of the lstat() function for a prefix length of - * 'prefix_len', thus we then allow for symlinks in the prefix part as - * long as those points to real existing directories. - */ int has_dirs_only_path(const char *name, int len, int prefix_len) { return threaded_has_dirs_only_path(&default_cache, name, len, prefix_len); diff --git a/t/t2021-checkout-overwrite.sh b/t/t2021-checkout-overwrite.sh index c2ada7de37312b..70d69263e68215 100755 --- a/t/t2021-checkout-overwrite.sh +++ b/t/t2021-checkout-overwrite.sh @@ -51,4 +51,16 @@ test_expect_success SYMLINKS 'the symlink remained' ' test -h a/b ' +test_expect_success SYMLINKS 'checkout -f must not follow symlinks when removing entries' ' + git checkout -f start && + mkdir dir && + >dir/f && + git add dir/f && + git commit -m "add dir/f" && + mv dir untracked && + ln -s untracked dir && + git checkout -f HEAD~ && + test_path_is_file untracked/f +' + test_done diff --git a/t/t3060-ls-files-with-tree.sh b/t/t3060-ls-files-with-tree.sh index 52ed665fcd2dd7..b257c792a46d86 100755 --- a/t/t3060-ls-files-with-tree.sh +++ b/t/t3060-ls-files-with-tree.sh @@ -47,6 +47,12 @@ test_expect_success setup ' git add . ' +test_expect_success 'usage' ' + test_expect_code 128 git ls-files --with-tree=HEAD -u && + test_expect_code 128 git ls-files --with-tree=HEAD -s && + test_expect_code 128 git ls-files --recurse-submodules --with-tree=HEAD +' + test_expect_success 'git ls-files --with-tree should succeed from subdir' ' # We have to run from a sub-directory to trigger prune_path # Then we finally get to run our --with-tree test @@ -60,4 +66,39 @@ test_expect_success \ 'git ls-files --with-tree should add entries from named tree.' \ 'test_cmp expected output' +test_expect_success 'no duplicates in --with-tree output' ' + git ls-files --with-tree=HEAD >actual && + sort -u actual >expected && + test_cmp expected actual +' + +test_expect_success 'setup: output in a conflict' ' + test_create_repo conflict && + test_commit -C conflict BASE file && + test_commit -C conflict A file foo && + git -C conflict reset --hard BASE && + test_commit -C conflict B file bar +' + +test_expect_success 'output in a conflict' ' + test_must_fail git -C conflict merge A B && + cat >expected <<-\EOF && + file + file + file + file + EOF + git -C conflict ls-files --with-tree=HEAD >actual && + test_cmp expected actual +' + +test_expect_success 'output with removed .git/index' ' + cat >expected <<-\EOF && + file + EOF + rm conflict/.git/index && + git -C conflict ls-files --with-tree=HEAD >actual && + test_cmp expected actual +' + test_done diff --git a/t/t3400-rebase.sh b/t/t3400-rebase.sh index 587b408063a8f7..0bb88aa982beea 100755 --- a/t/t3400-rebase.sh +++ b/t/t3400-rebase.sh @@ -388,22 +388,6 @@ test_expect_success 'rebase--merge.sh and --show-current-patch' ' ) ' -test_expect_success 'rebase -c rebase.useBuiltin=false warning' ' - expected="rebase.useBuiltin support has been removed" && - - # Only warn when the legacy rebase is requested... - test_must_fail git -c rebase.useBuiltin=false rebase 2>err && - test_i18ngrep "$expected" err && - test_must_fail env GIT_TEST_REBASE_USE_BUILTIN=false git rebase 2>err && - test_i18ngrep "$expected" err && - - # ...not when we would have used the built-in anyway - test_must_fail git -c rebase.useBuiltin=true rebase 2>err && - test_must_be_empty err && - test_must_fail env GIT_TEST_REBASE_USE_BUILTIN=true git rebase 2>err && - test_must_be_empty err -' - test_expect_success 'switch to branch checked out here' ' git checkout main && git rebase main main diff --git a/t/t3800-mktag.sh b/t/t3800-mktag.sh index 60a666da595cf3..6275c98523f731 100755 --- a/t/t3800-mktag.sh +++ b/t/t3800-mktag.sh @@ -17,7 +17,7 @@ check_verify_failure () { grep '$2' message && if test '$3' != '--no-strict' then - test_must_fail git mktag --no-strict message.no-strict &&xb + test_must_fail git mktag --no-strict message.no-strict && grep '$2' message.no-strict fi " diff --git a/t/t4053-diff-no-index.sh b/t/t4053-diff-no-index.sh index 0168946b639409..3feadf0e3595b2 100755 --- a/t/t4053-diff-no-index.sh +++ b/t/t4053-diff-no-index.sh @@ -16,6 +16,11 @@ test_expect_success 'setup' ' echo 1 >non/git/b ' +test_expect_success 'git diff --no-index --exit-code' ' + git diff --no-index --exit-code a/1 non/git/a && + test_expect_code 1 git diff --no-index --exit-code a/1 a/2 +' + test_expect_success 'git diff --no-index directories' ' test_expect_code 1 git diff --no-index a b >cnt && test_line_count = 14 cnt @@ -144,4 +149,59 @@ test_expect_success 'diff --no-index allows external diff' ' test_cmp expect actual ' +test_expect_success 'diff --no-index normalizes mode: no changes' ' + echo foo >x && + cp x y && + git diff --no-index x y >out && + test_must_be_empty out +' + +test_expect_success POSIXPERM 'diff --no-index normalizes mode: chmod +x' ' + chmod +x y && + cat >expected <<-\EOF && + diff --git a/x b/y + old mode 100644 + new mode 100755 + EOF + test_expect_code 1 git diff --no-index x y >actual && + test_cmp expected actual +' + +test_expect_success POSIXPERM 'diff --no-index normalizes: mode not like git mode' ' + chmod 666 x && + chmod 777 y && + cat >expected <<-\EOF && + diff --git a/x b/y + old mode 100644 + new mode 100755 + EOF + test_expect_code 1 git diff --no-index x y >actual && + test_cmp expected actual +' + +test_expect_success POSIXPERM,SYMLINKS 'diff --no-index normalizes: mode not like git mode (symlink)' ' + ln -s y z && + X_OID=$(git hash-object --stdin expected <<-EOF && + diff --git a/x b/x + deleted file mode 100644 + index $X_OID..$ZERO_OID + --- a/x + +++ /dev/null + @@ -1 +0,0 @@ + -foo + diff --git a/z b/z + new file mode 120000 + index $ZERO_OID..$Z_OID + --- /dev/null + +++ b/z + @@ -0,0 +1 @@ + +y + \ No newline at end of file + EOF + test_expect_code 1 git -c core.abbrev=no diff --no-index x z >actual && + test_cmp expected actual +' + test_done diff --git a/t/t7007-show.sh b/t/t7007-show.sh index 42d3db62468699..d6cc69e0f2cbd5 100755 --- a/t/t7007-show.sh +++ b/t/t7007-show.sh @@ -38,6 +38,45 @@ test_expect_success 'showing two commits' ' test_cmp expect actual.filtered ' +test_expect_success 'showing a tree' ' + cat >expected <<-EOF && + tree main1: + + main1.t + EOF + git show main1: >actual && + test_cmp expected actual +' + +test_expect_success 'showing two trees' ' + cat >expected <<-EOF && + tree main1^{tree} + + main1.t + + tree main2^{tree} + + main1.t + main2.t + EOF + git show main1^{tree} main2^{tree} >actual && + test_cmp expected actual +' + +test_expect_success 'showing a trees is not recursive' ' + git worktree add not-recursive main1 && + mkdir not-recursive/a && + test_commit -C not-recursive a/file && + cat >expected <<-EOF && + tree HEAD^{tree} + + a/ + main1.t + EOF + git -C not-recursive show HEAD^{tree} >actual && + test_cmp expected actual +' + test_expect_success 'showing a range walks (linear)' ' cat >expect <<-EOF && commit $(git rev-parse main3) diff --git a/t/t9001-send-email.sh b/t/t9001-send-email.sh index 4eee9c3dcb5383..1a1caf8f2ed49c 100755 --- a/t/t9001-send-email.sh +++ b/t/t9001-send-email.sh @@ -513,6 +513,38 @@ do done +test_expect_success $PREREQ "--validate respects relative core.hooksPath path" ' + clean_fake_sendmail && + mkdir my-hooks && + test_when_finished "rm my-hooks.ran" && + write_script my-hooks/sendemail-validate <<-\EOF && + >my-hooks.ran + exit 1 + EOF + test_config core.hooksPath "my-hooks" && + test_must_fail git send-email \ + --from="Example " \ + --to=nobody@example.com \ + --smtp-server="$(pwd)/fake.sendmail" \ + --validate \ + longline.patch 2>err && + test_path_is_file my-hooks.ran && + grep "rejected by sendemail-validate" err +' + +test_expect_success $PREREQ "--validate respects absolute core.hooksPath path" ' + test_config core.hooksPath "$(pwd)/my-hooks" && + test_when_finished "rm my-hooks.ran" && + test_must_fail git send-email \ + --from="Example " \ + --to=nobody@example.com \ + --smtp-server="$(pwd)/fake.sendmail" \ + --validate \ + longline.patch 2>err && + test_path_is_file my-hooks.ran && + grep "rejected by sendemail-validate" err +' + for enc in 7bit 8bit quoted-printable base64 do test_expect_success $PREREQ "--transfer-encoding=$enc produces correct header" ' diff --git a/tree.c b/tree.c index a52479812ce0c4..410e3b477e557f 100644 --- a/tree.c +++ b/tree.c @@ -11,58 +11,10 @@ const char *tree_type = "tree"; -static int read_one_entry_opt(struct index_state *istate, - const struct object_id *oid, - const char *base, int baselen, - const char *pathname, - unsigned mode, int stage, int opt) -{ - int len; - struct cache_entry *ce; - - if (S_ISDIR(mode)) - return READ_TREE_RECURSIVE; - - len = strlen(pathname); - ce = make_empty_cache_entry(istate, baselen + len); - - ce->ce_mode = create_ce_mode(mode); - ce->ce_flags = create_ce_flags(stage); - ce->ce_namelen = baselen + len; - memcpy(ce->name, base, baselen); - memcpy(ce->name + baselen, pathname, len+1); - oidcpy(&ce->oid, oid); - return add_index_entry(istate, ce, opt); -} - -static int read_one_entry(const struct object_id *oid, struct strbuf *base, - const char *pathname, unsigned mode, int stage, - void *context) -{ - struct index_state *istate = context; - return read_one_entry_opt(istate, oid, base->buf, base->len, pathname, - mode, stage, - ADD_CACHE_OK_TO_ADD|ADD_CACHE_SKIP_DFCHECK); -} - -/* - * This is used when the caller knows there is no existing entries at - * the stage that will conflict with the entry being added. - */ -static int read_one_entry_quick(const struct object_id *oid, struct strbuf *base, - const char *pathname, unsigned mode, int stage, - void *context) -{ - struct index_state *istate = context; - return read_one_entry_opt(istate, oid, base->buf, base->len, pathname, - mode, stage, - ADD_CACHE_JUST_APPEND); -} - -static int read_tree_1(struct repository *r, - struct tree *tree, struct strbuf *base, - int stage, const struct pathspec *pathspec, - read_tree_fn_t fn, void *context) +int read_tree_at(struct repository *r, + struct tree *tree, struct strbuf *base, + const struct pathspec *pathspec, + read_tree_fn_t fn, void *context) { struct tree_desc desc; struct name_entry entry; @@ -86,7 +38,7 @@ static int read_tree_1(struct repository *r, } switch (fn(&entry.oid, base, - entry.path, entry.mode, stage, context)) { + entry.path, entry.mode, context)) { case 0: continue; case READ_TREE_RECURSIVE: @@ -119,9 +71,9 @@ static int read_tree_1(struct repository *r, len = tree_entry_len(&entry); strbuf_add(base, entry.path, len); strbuf_addch(base, '/'); - retval = read_tree_1(r, lookup_tree(r, &oid), - base, stage, pathspec, - fn, context); + retval = read_tree_at(r, lookup_tree(r, &oid), + base, pathspec, + fn, context); strbuf_setlen(base, oldlen); if (retval) return -1; @@ -129,17 +81,13 @@ static int read_tree_1(struct repository *r, return 0; } -int read_tree_recursive(struct repository *r, - struct tree *tree, - const char *base, int baselen, - int stage, const struct pathspec *pathspec, - read_tree_fn_t fn, void *context) +int read_tree(struct repository *r, + struct tree *tree, + const struct pathspec *pathspec, + read_tree_fn_t fn, void *context) { struct strbuf sb = STRBUF_INIT; - int ret; - - strbuf_add(&sb, base, baselen); - ret = read_tree_1(r, tree, &sb, stage, pathspec, fn, context); + int ret = read_tree_at(r, tree, &sb, pathspec, fn, context); strbuf_release(&sb); return ret; } @@ -154,47 +102,6 @@ int cmp_cache_name_compare(const void *a_, const void *b_) ce2->name, ce2->ce_namelen, ce_stage(ce2)); } -int read_tree(struct repository *r, struct tree *tree, int stage, - struct pathspec *match, struct index_state *istate) -{ - read_tree_fn_t fn = NULL; - int i, err; - - /* - * Currently the only existing callers of this function all - * call it with stage=1 and after making sure there is nothing - * at that stage; we could always use read_one_entry_quick(). - * - * But when we decide to straighten out git-read-tree not to - * use unpack_trees() in some cases, this will probably start - * to matter. - */ - - /* - * See if we have cache entry at the stage. If so, - * do it the original slow way, otherwise, append and then - * sort at the end. - */ - for (i = 0; !fn && i < istate->cache_nr; i++) { - const struct cache_entry *ce = istate->cache[i]; - if (ce_stage(ce) == stage) - fn = read_one_entry; - } - - if (!fn) - fn = read_one_entry_quick; - err = read_tree_recursive(r, tree, "", 0, stage, match, fn, istate); - if (fn == read_one_entry || err) - return err; - - /* - * Sort the cache entry -- we need to nuke the cache tree, though. - */ - cache_tree_free(&istate->cache_tree); - QSORT(istate->cache, istate->cache_nr, cmp_cache_name_compare); - return 0; -} - struct tree *lookup_tree(struct repository *r, const struct object_id *oid) { struct object *obj = lookup_object(r, oid); diff --git a/tree.h b/tree.h index 3eb0484cbf2a75..6efff003e2120e 100644 --- a/tree.h +++ b/tree.h @@ -31,16 +31,16 @@ struct tree *parse_tree_indirect(const struct object_id *oid); int cmp_cache_name_compare(const void *a_, const void *b_); #define READ_TREE_RECURSIVE 1 -typedef int (*read_tree_fn_t)(const struct object_id *, struct strbuf *, const char *, unsigned int, int, void *); +typedef int (*read_tree_fn_t)(const struct object_id *, struct strbuf *, const char *, unsigned int, void *); -int read_tree_recursive(struct repository *r, - struct tree *tree, - const char *base, int baselen, - int stage, const struct pathspec *pathspec, - read_tree_fn_t fn, void *context); +int read_tree_at(struct repository *r, + struct tree *tree, struct strbuf *base, + const struct pathspec *pathspec, + read_tree_fn_t fn, void *context); -int read_tree(struct repository *r, struct tree *tree, - int stage, struct pathspec *pathspec, - struct index_state *istate); +int read_tree(struct repository *r, + struct tree *tree, + const struct pathspec *pathspec, + read_tree_fn_t fn, void *context); #endif /* TREE_H */ diff --git a/unpack-trees.c b/unpack-trees.c index 9af8e796b338cb..29029f34ed6864 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -2097,7 +2097,7 @@ static int verify_absent_1(const struct cache_entry *ce, if (o->index_only || o->reset || !o->update) return 0; - len = check_leading_path(ce->name, ce_namelen(ce)); + len = check_leading_path(ce->name, ce_namelen(ce), 0); if (!len) return 0; else if (len > 0) {