Merge branch 'master' into next
authorJunio C Hamano <gitster@pobox.com>
Wed, 21 Feb 2018 23:41:32 +0000 (15:41 -0800)
committerJunio C Hamano <gitster@pobox.com>
Wed, 21 Feb 2018 23:41:32 +0000 (15:41 -0800)
* master:
Fourth batch for 2.17

56 files changed:
Documentation/CodingGuidelines
Documentation/git-am.txt
Documentation/git-interpret-trailers.txt
Documentation/git-status.txt
Documentation/git-update-index.txt
Documentation/git-worktree.txt
Documentation/technical/api-submodule-config.txt
Makefile
apply.c
builtin/am.c
builtin/archive.c
builtin/blame.c
builtin/check-ignore.c
builtin/describe.c
builtin/worktree.c
config.mak.uname
contrib/completion/git-completion.bash
contrib/subtree/git-subtree.txt
dir.c
dir.h
fetch-pack.c
fsmonitor.c
fsmonitor.h
git-rebase--interactive.sh
git-sh-i18n.sh
git.c
name-hash.c
packfile.c
quote.c
remote-curl.c
send-pack.c
setup.c
sha1-lookup.c
sha1-lookup.h
submodule-config.c
t/README
t/helper/test-hashmap.c
t/t0002-gitfile.sh
t/t0008-ignores.sh
t/t0050-filesystem.sh
t/t0205-gettext-poison.sh
t/t1300-repo-config.sh
t/t1506-rev-parse-diagnosis.sh
t/t2025-worktree-add.sh
t/t3408-rebase-multi-line.sh
t/t4135-apply-weird-filenames.sh
t/t4150-am.sh
t/t5526-fetch-submodules.sh
t/t5545-push-options.sh
t/t6120-describe.sh
t/t7063-status-untracked-cache.sh
t/t7409-submodule-detached-work-tree.sh
t/t7519-status-fsmonitor.sh
t/test-lib-functions.sh
t/test-lib.sh
unpack-trees.c
index c4cb5ff0d477938b8fd49749c3589c5afbb04221..48aa4edfbdd180e1c6d874b6bb61ea5fc8e32ef5 100644 (file)
@@ -386,6 +386,11 @@ For C programs:
  - Use Git's gettext wrappers to make the user interface
    translatable. See "Marking strings for translation" in po/README.
 
+ - Variables and functions local to a given source file should be marked
+   with "static". Variables that are visible to other source files
+   must be declared with "extern" in header files. However, function
+   declarations should not use "extern", as that is already the default.
+
 For Perl programs:
 
  - Most of the C guidelines above apply.
index 12879e4029a7710e2d78bae476f7ad7d9b0fe830..460662e4b92b56c25989c9d1770165ea9a6a16a2 100644 (file)
@@ -16,7 +16,7 @@ SYNOPSIS
         [--exclude=<path>] [--include=<path>] [--reject] [-q | --quiet]
         [--[no-]scissors] [-S[<keyid>]] [--patch-format=<format>]
         [(<mbox> | <Maildir>)...]
-'git am' (--continue | --skip | --abort)
+'git am' (--continue | --skip | --abort | --quit)
 
 DESCRIPTION
 -----------
@@ -167,6 +167,10 @@ default.   You can use `--no-utf8` to override this.
 --abort::
        Restore the original branch and abort the patching operation.
 
+--quit::
+       Abort the patching operation but keep HEAD and the index
+       untouched.
+
 DISCUSSION
 ----------
 
index 9dd19a1dd9f126fbf54f1dc628b30fbe05445cf3..ff446f15f79f270a8979d1a398450a50ba4a4e95 100644 (file)
@@ -51,7 +51,7 @@ with only spaces at the end of the commit message part, one blank line
 will be added before the new trailer.
 
 Existing trailers are extracted from the input message by looking for
-a group of one or more lines that (i) are all trailers, or (ii) contains at
+a group of one or more lines that (i) is all trailers, or (ii) contains at
 least one Git-generated or user-configured trailer and consists of at
 least 25% trailers.
 The group must be preceded by one or more empty (or whitespace-only) lines.
index 72bfb87f6658b209e9b45956e73e492e957971b7..f9c91c721e909291ba42c27c3fd521135b4869f0 100644 (file)
@@ -184,10 +184,10 @@ in which case `XY` are `!!`.
 
     X          Y     Meaning
     -------------------------------------------------
-              [MD]   not updated
+            [AMD]   not updated
     M        [ MD]   updated in index
     A        [ MD]   added to index
-    D         [ M]   deleted from index
+    D                deleted from index
     R        [ MD]   renamed in index
     C        [ MD]   copied in index
     [MARC]           index and work tree matches
index bdb0342593229e32425bf73d17ad6fcdd0dd3732..3897a59ee94bc424c2c66cb5d05bc0193fb6eaf2 100644 (file)
@@ -464,6 +464,32 @@ command reads the index; while when `--[no-|force-]untracked-cache`
 are used, the untracked cache is immediately added to or removed from
 the index.
 
+Before 2.17, the untracked cache had a bug where replacing a directory
+with a symlink to another directory could cause it to incorrectly show
+files tracked by git as untracked. See the "status: add a failing test
+showing a core.untrackedCache bug" commit to git.git. A workaround for
+that is (and this might work for other undiscovered bugs in the
+future):
+
+----------------
+$ git -c core.untrackedCache=false status
+----------------
+
+This bug has also been shown to affect non-symlink cases of replacing
+a directory with a file when it comes to the internal structures of
+the untracked cache, but no case has been reported where this resulted in
+wrong "git status" output.
+
+There are also cases where existing indexes written by git versions
+before 2.17 will reference directories that don't exist anymore,
+potentially causing many "could not open directory" warnings to be
+printed on "git status". These are new warnings for existing issues
+that were previously silently discarded.
+
+As with the bug described above the solution is to one-off do a "git
+status" run with `core.untrackedCache=false` to flush out the leftover
+bad data.
+
 File System Monitor
 -------------------
 
@@ -484,8 +510,8 @@ the `core.fsmonitor` configuration variable (see
 linkgit:git-config[1]) than using the `--fsmonitor` option to
 `git update-index` in each repository, especially if you want to do so
 across all repositories you use, because you can set the configuration
-variable to `true` (or `false`) in your `$HOME/.gitconfig` just once
-and have it affect all repositories you touch.
+variable in your `$HOME/.gitconfig` just once and have it affect all
+repositories you touch.
 
 When the `core.fsmonitor` configuration variable is changed, the
 file system monitor is added to or removed from the index the next time
index 41585f535dba760590f2d67ecdc96cc8dea3a57f..5ac3f68ab5396ade59c25f2083088c21f94f1f93 100644 (file)
@@ -52,10 +52,11 @@ is linked to the current repository, sharing everything except working
 directory specific files such as HEAD, index, etc. `-` may also be
 specified as `<commit-ish>`; it is synonymous with `@{-1}`.
 +
-If <commit-ish> is a branch name (call it `<branch>` and is not found,
+If <commit-ish> is a branch name (call it `<branch>`) and is not found,
 and neither `-b` nor `-B` nor `--detach` are used, but there does
 exist a tracking branch in exactly one remote (call it `<remote>`)
-with a matching name, treat as equivalent to
+with a matching name, treat as equivalent to:
++
 ------------
 $ git worktree add --track -b <branch> <path> <remote>/<branch>
 ------------
index 3dce003fda008e61c7a91c85554f9a1cafe6106f..ee907c4a82a9127c0abc67b8a5dd215a3b0535d4 100644 (file)
@@ -4,7 +4,7 @@ submodule config cache API
 The submodule config cache API allows to read submodule
 configurations/information from specified revisions. Internally
 information is lazily read into a cache that is used to avoid
-unnecessary parsing of the same .gitmodule files. Lookups can be done by
+unnecessary parsing of the same .gitmodules files. Lookups can be done by
 submodule path or name.
 
 Usage
index c56fdc14ca154b7d80541569d9abf700ea419499..5ae9616e0a7028d4a54977eda4b213e1e0b03ed3 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -2162,6 +2162,8 @@ gettext.sp gettext.s gettext.o: EXTRA_CPPFLAGS = \
 http-push.sp http.sp http-walker.sp remote-curl.sp imap-send.sp: SPARSE_FLAGS += \
        -DCURL_DISABLE_TYPECHECK
 
+pack-revindex.sp: SPARSE_FLAGS += -Wno-memcpy-max-count
+
 ifdef NO_EXPAT
 http-walker.sp http-walker.s http-walker.o: EXTRA_CPPFLAGS = -DNO_EXPAT
 endif
@@ -2734,7 +2736,7 @@ clean: profile-clean coverage-clean
        $(RM) $(TEST_PROGRAMS) $(NO_INSTALL)
        $(RM) -r bin-wrappers $(dep_dirs)
        $(RM) -r po/build/
-       $(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo common-cmds.h $(ETAGS_TARGET) tags cscope*
+       $(RM) *.pyc *.pyo */*.pyc */*.pyo common-cmds.h $(ETAGS_TARGET) tags cscope*
        $(RM) -r $(GIT_TARNAME) .doc-tmp-dir
        $(RM) $(GIT_TARNAME).tar.gz git-core_$(GIT_VERSION)-*.tar.gz
        $(RM) $(htmldocs).tar.gz $(manpages).tar.gz
diff --git a/apply.c b/apply.c
index 40a368b3153f90ff0061894d0af5db5b401a00d9..7c88d4ee71304825a475fc1e67ed38b48621cb3a 100644 (file)
--- a/apply.c
+++ b/apply.c
@@ -950,7 +950,7 @@ static int gitdiff_verify_name(struct apply_state *state,
                }
                free(another);
        } else {
-               if (!starts_with(line, "/dev/null\n"))
+               if (!is_dev_null(line))
                        return error(_("git apply: bad git-diff - expected /dev/null on line %d"), state->linenr);
        }
 
index 6661edc162b770d40c62f3a03b7c0e59cb47ca51..05a82f4aa5be6ba82d409f002dce7786734780a1 100644 (file)
@@ -2149,7 +2149,8 @@ enum resume_mode {
        RESUME_APPLY,
        RESUME_RESOLVED,
        RESUME_SKIP,
-       RESUME_ABORT
+       RESUME_ABORT,
+       RESUME_QUIT
 };
 
 static int git_am_config(const char *k, const char *v, void *cb)
@@ -2249,6 +2250,9 @@ int cmd_am(int argc, const char **argv, const char *prefix)
                OPT_CMDMODE(0, "abort", &resume,
                        N_("restore the original branch and abort the patching operation."),
                        RESUME_ABORT),
+               OPT_CMDMODE(0, "quit", &resume,
+                       N_("abort the patching operation but keep HEAD where it is."),
+                       RESUME_QUIT),
                OPT_BOOL(0, "committer-date-is-author-date",
                        &state.committer_date_is_author_date,
                        N_("lie about committer date")),
@@ -2317,7 +2321,7 @@ int cmd_am(int argc, const char **argv, const char *prefix)
                 * stray directories.
                 */
                if (file_exists(state.dir) && !state.rebasing) {
-                       if (resume == RESUME_ABORT) {
+                       if (resume == RESUME_ABORT || resume == RESUME_QUIT) {
                                am_destroy(&state);
                                am_state_release(&state);
                                return 0;
@@ -2359,6 +2363,10 @@ int cmd_am(int argc, const char **argv, const char *prefix)
        case RESUME_ABORT:
                am_abort(&state);
                break;
+       case RESUME_QUIT:
+               am_rerere_clear();
+               am_destroy(&state);
+               break;
        default:
                die("BUG: invalid resume value");
        }
index f863465a0fa137f3446071a0ec1653adad536fdc..73971d0dd20e7233d6cb1e6c16986ede25526939 100644 (file)
@@ -55,7 +55,7 @@ static int run_remote_archiver(int argc, const char **argv,
 
        buf = packet_read_line(fd[0], NULL);
        if (!buf)
-               die(_("git archive: expected ACK/NAK, got EOF"));
+               die(_("git archive: expected ACK/NAK, got a flush packet"));
        if (strcmp(buf, "ACK")) {
                if (starts_with(buf, "NACK "))
                        die(_("git archive: NACK %s"), buf + 5);
index 005f55aaa257fc34f81517650223a3c195c03178..9dcb367b90d99fc9ebbbf878f53cd5b4651d7864 100644 (file)
@@ -649,6 +649,15 @@ static int blame_move_callback(const struct option *option, const char *arg, int
        return 0;
 }
 
+static int is_a_rev(const char *name)
+{
+       struct object_id oid;
+
+       if (get_oid(name, &oid))
+               return 0;
+       return OBJ_NONE < sha1_object_info(oid.hash, NULL);
+}
+
 int cmd_blame(int argc, const char **argv, const char *prefix)
 {
        struct rev_info revs;
@@ -845,16 +854,15 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
        } else {
                if (argc < 2)
                        usage_with_options(blame_opt_usage, options);
-               path = add_prefix(prefix, argv[argc - 1]);
-               if (argc == 3 && !file_exists(path)) { /* (2b) */
+               if (argc == 3 && is_a_rev(argv[argc - 1])) { /* (2b) */
                        path = add_prefix(prefix, argv[1]);
                        argv[1] = argv[2];
+               } else {        /* (2a) */
+                       if (argc == 2 && is_a_rev(argv[1]) && !get_git_work_tree())
+                               die("missing <path> to blame");
+                       path = add_prefix(prefix, argv[argc - 1]);
                }
                argv[argc - 1] = "--";
-
-               setup_work_tree();
-               if (!file_exists(path))
-                       die_errno("cannot stat path '%s'", path);
        }
 
        revs.disable_stdin = 1;
index 3e280b9c7aa9c93c8e7572a4fe3f7ef3ac92b3af..ec9a959e08d0e3dfa0d2b08529cacbe8d2e04c6e 100644 (file)
@@ -72,7 +72,7 @@ static int check_ignore(struct dir_struct *dir,
 {
        const char *full_path;
        char *seen;
-       int num_ignored = 0, dtype = DT_UNKNOWN, i;
+       int num_ignored = 0, i;
        struct exclude *exclude;
        struct pathspec pathspec;
 
@@ -104,6 +104,7 @@ static int check_ignore(struct dir_struct *dir,
                full_path = pathspec.items[i].match;
                exclude = NULL;
                if (!seen[i]) {
+                       int dtype = DT_UNKNOWN;
                        exclude = last_exclude_matching(dir, &the_index,
                                                        full_path, &dtype);
                }
index c4289847063ac02ed061f890ffd71cd329d67109..e4869df7b434845544dfcc0c37cae6a77cd42dad 100644 (file)
@@ -502,7 +502,7 @@ static void describe(const char *arg, int last_one)
 
        if (cmit)
                describe_commit(&oid, &sb);
-       else if (lookup_blob(&oid))
+       else if (sha1_object_info(oid.hash, NULL) == OBJ_BLOB)
                describe_blob(oid, &sb);
        else
                die(_("%s is neither a commit nor blob"), arg);
index 9efdc224661b02192d06c6877e49c5585ddbcdca..4e7c98758fcfb93c19e3dbf58fc3167829e998b8 100644 (file)
@@ -345,9 +345,23 @@ static int add_worktree(const char *path, const char *refname,
         * Hook failure does not warrant worktree deletion, so run hook after
         * is_junk is cleared, but do return appropriate code when hook fails.
         */
-       if (!ret && opts->checkout)
-               ret = run_hook_le(NULL, "post-checkout", oid_to_hex(&null_oid),
-                                 oid_to_hex(&commit->object.oid), "1", NULL);
+       if (!ret && opts->checkout) {
+               const char *hook = find_hook("post-checkout");
+               if (hook) {
+                       const char *env[] = { "GIT_DIR", "GIT_WORK_TREE", NULL };
+                       cp.git_cmd = 0;
+                       cp.no_stdin = 1;
+                       cp.stdout_to_stderr = 1;
+                       cp.dir = path;
+                       cp.env = env;
+                       cp.argv = NULL;
+                       argv_array_pushl(&cp.args, absolute_path(hook),
+                                        oid_to_hex(&null_oid),
+                                        oid_to_hex(&commit->object.oid),
+                                        "1", NULL);
+                       ret = run_command(&cp);
+               }
+       }
 
        argv_array_clear(&child_env);
        strbuf_release(&sb);
index 685a80d13843800be3c633539b0a22e88652444a..6a1d0de0cc571f395eeeb8f149d4377a1a5e1602 100644 (file)
@@ -182,7 +182,6 @@ ifeq ($(uname_O),Cygwin)
        NO_ST_BLOCKS_IN_STRUCT_STAT = YesPlease
        X = .exe
        UNRELIABLE_FSTAT = UnfortunatelyYes
-       SPARSE_FLAGS = -isystem /usr/include/w32api -Wno-one-bit-signed-bitfield
        OBJECT_CREATION_USES_RENAMES = UnfortunatelyNeedsTo
        MMAP_PREVENTS_DELETE = UnfortunatelyYes
        COMPAT_OBJS += compat/cygwin.o
index 88813e91244d820b4aa2c0fb5e60a44db03c3e59..c7d5c7af29a8e9595375a535af59549b01da1b49 100644 (file)
@@ -1077,7 +1077,7 @@ _git_am ()
 {
        __git_find_repo_path
        if [ -d "$__git_repo_path"/rebase-apply ]; then
-               __gitcomp "--skip --continue --resolved --abort"
+               __gitcomp "--skip --continue --resolved --abort --quit"
                return
        fi
        case "$cur" in
index 60d76cdddfc75f0c77d5798ac0b1adc6b0e93899..352deda69dcfd009ff025615dff07dd0a5d015d1 100644 (file)
@@ -28,7 +28,7 @@ as a subdirectory of your application.
 
 Subtrees are not to be confused with submodules, which are meant for
 the same task. Unlike submodules, subtrees do not need any special
-constructions (like .gitmodule files or gitlinks) be present in
+constructions (like .gitmodules files or gitlinks) be present in
 your repository, and do not force end-users of your
 repository to do anything special or to understand how subtrees
 work. A subtree is just a subdirectory that can be
diff --git a/dir.c b/dir.c
index 536416ff2df5f7c475deba4739d4edaf340ae898..6dd91be8185d4012e11495a9238636913fedeeef 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -771,7 +771,16 @@ static void invalidate_directory(struct untracked_cache *uc,
                                 struct untracked_cache_dir *dir)
 {
        int i;
-       uc->dir_invalidated++;
+
+       /*
+        * Invalidation increment here is just roughly correct. If
+        * untracked_nr or any of dirs[].recurse is non-zero, we
+        * should increment dir_invalidated too. But that's more
+        * expensive to do.
+        */
+       if (dir->valid)
+               uc->dir_invalidated++;
+
        dir->valid = 0;
        dir->untracked_nr = 0;
        for (i = 0; i < dir->dirs_nr; i++)
@@ -1770,7 +1779,7 @@ static enum path_treatment treat_path(struct dir_struct *dir,
        if (!de)
                return treat_path_fast(dir, untracked, cdir, istate, path,
                                       baselen, pathspec);
-       if (is_dot_or_dotdot(de->d_name) || !strcmp(de->d_name, ".git"))
+       if (is_dot_or_dotdot(de->d_name) || !fspathcmp(de->d_name, ".git"))
                return path_none;
        strbuf_setlen(path, baselen);
        strbuf_addstr(path, de->d_name);
@@ -1806,24 +1815,19 @@ static int valid_cached_dir(struct dir_struct *dir,
         */
        refresh_fsmonitor(istate);
        if (!(dir->untracked->use_fsmonitor && untracked->valid)) {
-               if (stat(path->len ? path->buf : ".", &st)) {
-                       invalidate_directory(dir->untracked, untracked);
+               if (lstat(path->len ? path->buf : ".", &st)) {
                        memset(&untracked->stat_data, 0, sizeof(untracked->stat_data));
                        return 0;
                }
                if (!untracked->valid ||
                        match_stat_data_racy(istate, &untracked->stat_data, &st)) {
-                       if (untracked->valid)
-                               invalidate_directory(dir->untracked, untracked);
                        fill_stat_data(&untracked->stat_data, &st);
                        return 0;
                }
        }
 
-       if (untracked->check_only != !!check_only) {
-               invalidate_directory(dir->untracked, untracked);
+       if (untracked->check_only != !!check_only)
                return 0;
-       }
 
        /*
         * prep_exclude will be called eventually on this directory,
@@ -1850,13 +1854,20 @@ static int open_cached_dir(struct cached_dir *cdir,
                           struct strbuf *path,
                           int check_only)
 {
+       const char *c_path;
+
        memset(cdir, 0, sizeof(*cdir));
        cdir->untracked = untracked;
        if (valid_cached_dir(dir, untracked, istate, path, check_only))
                return 0;
-       cdir->fdir = opendir(path->len ? path->buf : ".");
-       if (dir->untracked)
+       c_path = path->len ? path->buf : ".";
+       cdir->fdir = opendir(c_path);
+       if (!cdir->fdir)
+               warning_errno(_("could not open directory '%s'"), c_path);
+       if (dir->untracked) {
+               invalidate_directory(dir->untracked, untracked);
                dir->untracked->dir_opened++;
+       }
        if (!cdir->fdir)
                return -1;
        return 0;
@@ -2966,10 +2977,12 @@ static int invalidate_one_component(struct untracked_cache *uc,
 }
 
 void untracked_cache_invalidate_path(struct index_state *istate,
-                                    const char *path)
+                                    const char *path, int safe_path)
 {
        if (!istate->untracked || !istate->untracked->root)
                return;
+       if (!safe_path && !verify_path(path))
+               return;
        invalidate_one_component(istate->untracked, istate->untracked->root,
                                 path, strlen(path));
 }
@@ -2977,13 +2990,13 @@ void untracked_cache_invalidate_path(struct index_state *istate,
 void untracked_cache_remove_from_index(struct index_state *istate,
                                       const char *path)
 {
-       untracked_cache_invalidate_path(istate, path);
+       untracked_cache_invalidate_path(istate, path, 1);
 }
 
 void untracked_cache_add_to_index(struct index_state *istate,
                                  const char *path)
 {
-       untracked_cache_invalidate_path(istate, path);
+       untracked_cache_invalidate_path(istate, path, 1);
 }
 
 /* Update gitfile and core.worktree setting to connect work tree and git dir */
diff --git a/dir.h b/dir.h
index e7bb786a33967360750ad21446ebe65b3bea8b80..b0758b82a20017dd3ce29c54454678f026718078 100644 (file)
--- a/dir.h
+++ b/dir.h
@@ -350,7 +350,7 @@ static inline int dir_path_match(const struct dir_entry *ent,
 int cmp_dir_entry(const void *p1, const void *p2);
 int check_dir_entry_contains(const struct dir_entry *out, const struct dir_entry *in);
 
-void untracked_cache_invalidate_path(struct index_state *, const char *);
+void untracked_cache_invalidate_path(struct index_state *, const char *, int safe_path);
 void untracked_cache_remove_from_index(struct index_state *, const char *);
 void untracked_cache_add_to_index(struct index_state *, const char *);
 
index 8253d746e0c40492e05360fe6f27bfa85bb39fbc..d97461296d5692521cd038f9ffa0b5f5c3c3f3dd 100644 (file)
@@ -261,8 +261,8 @@ static enum ack_type get_ack(int fd, struct object_id *result_oid)
        char *line = packet_read_line(fd, &len);
        const char *arg;
 
-       if (!len)
-               die(_("git fetch-pack: expected ACK/NAK, got EOF"));
+       if (!line)
+               die(_("git fetch-pack: expected ACK/NAK, got a flush packet"));
        if (!strcmp(line, "NAK"))
                return NAK;
        if (skip_prefix(line, "ACK ", &arg)) {
index 0af7c4edba37fd9f6a8cee83cd2715c3fa08b488..6d7bcd5d0ed8f2d3f5abdea2f26c6be72909b657 100644 (file)
@@ -130,7 +130,7 @@ static void fsmonitor_refresh_callback(struct index_state *istate, const char *n
         * as it could be a new untracked file.
         */
        trace_printf_key(&trace_fsmonitor, "fsmonitor_refresh_callback '%s'", name);
-       untracked_cache_invalidate_path(istate, name);
+       untracked_cache_invalidate_path(istate, name, 0);
 }
 
 void refresh_fsmonitor(struct index_state *istate)
index cd3cc0ccf228c9d601621685042d51368a71c707..65f37436369cd934f2bdb3b627396c5f72a7cb03 100644 (file)
@@ -65,7 +65,7 @@ static inline void mark_fsmonitor_invalid(struct index_state *istate, struct cac
 {
        if (core_fsmonitor) {
                ce->ce_flags &= ~CE_FSMONITOR_VALID;
-               untracked_cache_invalidate_path(istate, ce->name);
+               untracked_cache_invalidate_path(istate, ce->name, 1);
                trace_printf_key(&trace_fsmonitor, "mark_fsmonitor_invalid '%s'", ce->name);
        }
 }
index 81c5b42875771ab98ca86e718e14a5a393aa1a86..637bb3ecd5bd25222b725c10619cd3a078fd95b4 100644 (file)
@@ -396,7 +396,7 @@ pick_one_preserving_merges () {
                                        --sq-quote "$gpg_sign_opt")} \
                                $allow_rerere_autoupdate "$merge_args" \
                                "$strategy_args" \
-                               -m $(git rev-parse --sq-quote "$msg_content") \
+                               -m "$(git rev-parse --sq-quote "$msg_content")" \
                                "$new_parents"
                        then
                                printf "%s\n" "$msg_content" > "$GIT_DIR"/MERGE_MSG
index 1ef1889dbd806494b8659023a091920b07692035..9d065fb4bf9a308e64fa2d4aa5dff294b116f1a9 100644 (file)
@@ -17,15 +17,15 @@ export TEXTDOMAINDIR
 
 # First decide what scheme to use...
 GIT_INTERNAL_GETTEXT_SH_SCHEME=fallthrough
-if test -n "@@USE_GETTEXT_SCHEME@@"
+if test -n "$GIT_GETTEXT_POISON"
+then
+       GIT_INTERNAL_GETTEXT_SH_SCHEME=poison
+elif test -n "@@USE_GETTEXT_SCHEME@@"
 then
        GIT_INTERNAL_GETTEXT_SH_SCHEME="@@USE_GETTEXT_SCHEME@@"
 elif test -n "$GIT_INTERNAL_GETTEXT_TEST_FALLBACKS"
 then
        : no probing necessary
-elif test -n "$GIT_GETTEXT_POISON"
-then
-       GIT_INTERNAL_GETTEXT_SH_SCHEME=poison
 elif type gettext.sh >/dev/null 2>&1
 then
        # GNU libintl's gettext.sh
diff --git a/git.c b/git.c
index c870b9719c21b2db23ea7ab5d56fdeda483cb02e..96cd734f1282161eebde954f7e638cf8dd8c3516 100644 (file)
--- a/git.c
+++ b/git.c
@@ -5,11 +5,11 @@
 #include "run-command.h"
 
 const char git_usage_string[] =
-       "git [--version] [--help] [-C <path>] [-c name=value]\n"
-       "           [--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]\n"
-       "           [-p | --paginate | --no-pager] [--no-replace-objects] [--bare]\n"
-       "           [--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]\n"
-       "           <command> [<args>]";
+       N_("git [--version] [--help] [-C <path>] [-c <name>=<value>]\n"
+          "           [--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]\n"
+          "           [-p | --paginate | --no-pager] [--no-replace-objects] [--bare]\n"
+          "           [--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]\n"
+          "           <command> [<args>]");
 
 const char git_more_info_string[] =
        N_("'git help -a' and 'git help -g' list available subcommands and some\n"
@@ -92,7 +92,7 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
                                *envchanged = 1;
                } else if (!strcmp(cmd, "--git-dir")) {
                        if (*argc < 2) {
-                               fprintf(stderr, "No directory given for --git-dir.\n" );
+                               fprintf(stderr, _("no directory given for --git-dir\n" ));
                                usage(git_usage_string);
                        }
                        setenv(GIT_DIR_ENVIRONMENT, (*argv)[1], 1);
@@ -106,7 +106,7 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
                                *envchanged = 1;
                } else if (!strcmp(cmd, "--namespace")) {
                        if (*argc < 2) {
-                               fprintf(stderr, "No namespace given for --namespace.\n" );
+                               fprintf(stderr, _("no namespace given for --namespace\n" ));
                                usage(git_usage_string);
                        }
                        setenv(GIT_NAMESPACE_ENVIRONMENT, (*argv)[1], 1);
@@ -120,7 +120,7 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
                                *envchanged = 1;
                } else if (!strcmp(cmd, "--work-tree")) {
                        if (*argc < 2) {
-                               fprintf(stderr, "No directory given for --work-tree.\n" );
+                               fprintf(stderr, _("no directory given for --work-tree\n" ));
                                usage(git_usage_string);
                        }
                        setenv(GIT_WORK_TREE_ENVIRONMENT, (*argv)[1], 1);
@@ -134,7 +134,7 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
                                *envchanged = 1;
                } else if (!strcmp(cmd, "--super-prefix")) {
                        if (*argc < 2) {
-                               fprintf(stderr, "No prefix given for --super-prefix.\n" );
+                               fprintf(stderr, _("no prefix given for --super-prefix\n" ));
                                usage(git_usage_string);
                        }
                        setenv(GIT_SUPER_PREFIX_ENVIRONMENT, (*argv)[1], 1);
@@ -156,7 +156,7 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
                                *envchanged = 1;
                } else if (!strcmp(cmd, "-c")) {
                        if (*argc < 2) {
-                               fprintf(stderr, "-c expects a configuration string\n" );
+                               fprintf(stderr, _("-c expects a configuration string\n" ));
                                usage(git_usage_string);
                        }
                        git_config_push_parameter((*argv)[1]);
@@ -194,12 +194,12 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
                                *envchanged = 1;
                } else if (!strcmp(cmd, "-C")) {
                        if (*argc < 2) {
-                               fprintf(stderr, "No directory given for -C.\n" );
+                               fprintf(stderr, _("no directory given for -C\n" ));
                                usage(git_usage_string);
                        }
                        if ((*argv)[1][0]) {
                                if (chdir((*argv)[1]))
-                                       die_errno("Cannot change to '%s'", (*argv)[1]);
+                                       die_errno("cannot change to '%s'", (*argv)[1]);
                                if (envchanged)
                                        *envchanged = 1;
                        }
@@ -209,7 +209,7 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
                        list_builtins();
                        exit(0);
                } else {
-                       fprintf(stderr, "Unknown option: %s\n", cmd);
+                       fprintf(stderr, _("unknown option: %s\n"), cmd);
                        usage(git_usage_string);
                }
 
@@ -247,7 +247,7 @@ static int handle_alias(int *argcp, const char ***argv)
                        if (ret >= 0)   /* normal exit */
                                exit(ret);
 
-                       die_errno("While expanding alias '%s': '%s'",
+                       die_errno("while expanding alias '%s': '%s'",
                            alias_command, alias_string + 1);
                }
                count = split_cmdline(alias_string, &new_argv);
@@ -256,8 +256,8 @@ static int handle_alias(int *argcp, const char ***argv)
                            split_cmdline_strerror(count));
                option_count = handle_options(&new_argv, &count, &envchanged);
                if (envchanged)
-                       die("alias '%s' changes environment variables\n"
-                                "You can use '!git' in the alias to do this.",
+                       die("alias '%s' changes environment variables.\n"
+                                "You can use '!git' in the alias to do this",
                                 alias_command);
                memmove(new_argv - option_count, new_argv,
                                count * sizeof(char *));
@@ -684,8 +684,8 @@ int cmd_main(int argc, const char **argv)
                if (errno != ENOENT)
                        break;
                if (was_alias) {
-                       fprintf(stderr, "Expansion of alias '%s' failed; "
-                               "'%s' is not a git command\n",
+                       fprintf(stderr, _("expansion of alias '%s' failed; "
+                                         "'%s' is not a git command\n"),
                                cmd, argv[0]);
                        exit(1);
                }
@@ -696,7 +696,7 @@ int cmd_main(int argc, const char **argv)
                        break;
        }
 
-       fprintf(stderr, "Failed to run command '%s': %s\n",
+       fprintf(stderr, _("failed to run command '%s': %s\n"),
                cmd, strerror(errno));
 
        return 1;
index ada66f066a002ac5ecb73f94a582d758c0a3a657..163849831c9f11316ce97c649b77c32cf2eed276 100644 (file)
@@ -699,12 +699,12 @@ void adjust_dirname_case(struct index_state *istate, char *name)
                if (*ptr == '/') {
                        struct dir_entry *dir;
 
-                       ptr++;
-                       dir = find_dir_entry(istate, name, ptr - name + 1);
+                       dir = find_dir_entry(istate, name, ptr - name);
                        if (dir) {
                                memcpy((void *)startPtr, dir->name + (startPtr - name), ptr - startPtr);
-                               startPtr = ptr;
+                               startPtr = ptr + 1;
                        }
+                       ptr++;
                }
        }
 }
index 7dbe8739d17d64fad79eac9e7477d00ea3d80df2..5d07f330c8a4bf0a748e3fc87ed70f5affafd681 100644 (file)
@@ -1722,11 +1722,8 @@ off_t find_pack_entry_one(const unsigned char *sha1,
 {
        const uint32_t *level1_ofs = p->index_data;
        const unsigned char *index = p->index_data;
-       unsigned hi, lo, stride;
-       static int debug_lookup = -1;
-
-       if (debug_lookup < 0)
-               debug_lookup = !!getenv("GIT_DEBUG_LOOKUP");
+       unsigned stride;
+       uint32_t result;
 
        if (!index) {
                if (open_pack_index(p))
@@ -1739,8 +1736,6 @@ off_t find_pack_entry_one(const unsigned char *sha1,
                index += 8;
        }
        index += 4 * 256;
-       hi = ntohl(level1_ofs[*sha1]);
-       lo = ((*sha1 == 0x0) ? 0 : ntohl(level1_ofs[*sha1 - 1]));
        if (p->index_version > 1) {
                stride = 20;
        } else {
@@ -1748,24 +1743,8 @@ off_t find_pack_entry_one(const unsigned char *sha1,
                index += 4;
        }
 
-       if (debug_lookup)
-               printf("%02x%02x%02x... lo %u hi %u nr %"PRIu32"\n",
-                      sha1[0], sha1[1], sha1[2], lo, hi, p->num_objects);
-
-       while (lo < hi) {
-               unsigned mi = lo + (hi - lo) / 2;
-               int cmp = hashcmp(index + mi * stride, sha1);
-
-               if (debug_lookup)
-                       printf("lo %u hi %u rg %u mi %u\n",
-                              lo, hi, hi - lo, mi);
-               if (!cmp)
-                       return nth_packed_object_offset(p, mi);
-               if (cmp > 0)
-                       hi = mi;
-               else
-                       lo = mi+1;
-       }
+       if (bsearch_hash(sha1, level1_ofs, index, stride, &result))
+               return nth_packed_object_offset(p, result);
        return 0;
 }
 
diff --git a/quote.c b/quote.c
index 37d26868656fe8c8c65de41580ab5ae9e89981fc..c95dd2cafbaa85c9c443a229134842bf06ce3200 100644 (file)
--- a/quote.c
+++ b/quote.c
@@ -118,9 +118,15 @@ static char *sq_dequote_step(char *arg, char **next)
                                *next = NULL;
                        return arg;
                case '\\':
-                       c = *++src;
-                       if (need_bs_quote(c) && *++src == '\'') {
-                               *dst++ = c;
+                       /*
+                        * Allow backslashed characters outside of
+                        * single-quotes only if they need escaping,
+                        * and only if we resume the single-quoted part
+                        * afterward.
+                        */
+                       if (need_bs_quote(src[1]) && src[2] == '\'') {
+                               *dst++ = src[1];
+                               src += 2;
                                continue;
                        }
                /* Fallthrough */
index 6ec535243565d4b1a50e22868966b4aec33776f8..a7c4c9b5ff4822e36bfc43a59d113c624537297e 100644 (file)
@@ -13,6 +13,7 @@
 #include "credential.h"
 #include "sha1-array.h"
 #include "send-pack.h"
+#include "quote.h"
 
 static struct remote *remote;
 /* always ends with a trailing slash */
@@ -145,7 +146,15 @@ static int set_option(const char *name, const char *value)
                        return -1;
                return 0;
        } else if (!strcmp(name, "push-option")) {
-               string_list_append(&options.push_options, value);
+               if (*value != '"')
+                       string_list_append(&options.push_options, value);
+               else {
+                       struct strbuf unquoted = STRBUF_INIT;
+                       if (unquote_c_style(&unquoted, value, NULL) < 0)
+                               die("invalid quoting in push-option value");
+                       string_list_append_nodup(&options.push_options,
+                                                strbuf_detach(&unquoted, NULL));
+               }
                return 0;
 
 #if LIBCURL_VERSION_NUM >= 0x070a08
@@ -351,6 +360,8 @@ static struct discovery *discover_refs(const char *service, int for_push)
                 * pkt-line matches our request.
                 */
                line = packet_read_line_buf(&last->buf, &last->len, NULL);
+               if (!line)
+                       die("invalid server response; expected service, got flush packet");
 
                strbuf_reset(&exp);
                strbuf_addf(&exp, "# service=%s", service);
index 2112d3b27ad21e1d457b08a3aa71a811b5e8ee79..8d9190f5e7815c6b2f18afd266643a8c862e526e 100644 (file)
@@ -137,6 +137,8 @@ static int pack_objects(int fd, struct ref *refs, struct oid_array *extra, struc
 static int receive_unpack_status(int in)
 {
        const char *line = packet_read_line(in, NULL);
+       if (!line)
+               return error(_("unexpected flush packet while reading remote unpack status"));
        if (!skip_prefix(line, "unpack ", &line))
                return error(_("unable to parse remote unpack status: %s"), line);
        if (strcmp(line, "ok"))
diff --git a/setup.c b/setup.c
index c5d55dcee45ca811def540ca43c1e1eb57532a81..72877796420b06213665f5d357decb202e71fa91 100644 (file)
--- a/setup.c
+++ b/setup.c
@@ -119,7 +119,7 @@ char *prefix_path(const char *prefix, int len, const char *path)
 {
        char *r = prefix_path_gently(prefix, len, NULL, path);
        if (!r)
-               die("'%s' is outside repository", path);
+               die(_("'%s' is outside repository"), path);
        return r;
 }
 
@@ -160,7 +160,7 @@ int check_filename(const char *prefix, const char *arg)
                free(to_free);
                return 0; /* file does not exist */
        }
-       die_errno("failed to stat '%s'", arg);
+       die_errno(_("failed to stat '%s'"), arg);
 }
 
 static void NORETURN die_verify_filename(const char *prefix,
@@ -230,7 +230,7 @@ void verify_filename(const char *prefix,
                     int diagnose_misspelt_rev)
 {
        if (*arg == '-')
-               die("option '%s' must come before non-option arguments", arg);
+               die(_("option '%s' must come before non-option arguments"), arg);
        if (looks_like_pathspec(arg) || check_filename(prefix, arg))
                return;
        die_verify_filename(prefix, arg, diagnose_misspelt_rev);
@@ -385,14 +385,14 @@ void setup_work_tree(void)
                return;
 
        if (work_tree_config_is_bogus)
-               die("unable to set up work tree using invalid config");
+               die(_("unable to set up work tree using invalid config"));
 
        work_tree = get_git_work_tree();
        git_dir = get_git_dir();
        if (!is_absolute_path(git_dir))
                git_dir = real_path(get_git_dir());
        if (!work_tree || chdir(work_tree))
-               die("This operation must be run in a work tree");
+               die(_("this operation must be run in a work tree"));
 
        /*
         * Make sure subsequent git processes find correct worktree
@@ -530,17 +530,17 @@ void read_gitfile_error_die(int error_code, const char *path, const char *dir)
                /* non-fatal; follow return path */
                break;
        case READ_GITFILE_ERR_OPEN_FAILED:
-               die_errno("Error opening '%s'", path);
+               die_errno(_("error opening '%s'"), path);
        case READ_GITFILE_ERR_TOO_LARGE:
-               die("Too large to be a .git file: '%s'", path);
+               die(_("too large to be a .git file: '%s'"), path);
        case READ_GITFILE_ERR_READ_FAILED:
-               die("Error reading %s", path);
+               die(_("error reading %s"), path);
        case READ_GITFILE_ERR_INVALID_FORMAT:
-               die("Invalid gitfile format: %s", path);
+               die(_("invalid gitfile format: %s"), path);
        case READ_GITFILE_ERR_NO_PATH:
-               die("No path in gitfile: %s", path);
+               die(_("no path in gitfile: %s"), path);
        case READ_GITFILE_ERR_NOT_A_REPO:
-               die("Not a git repository: %s", dir);
+               die(_("not a git repository: %s"), dir);
        default:
                die("BUG: unknown error code");
        }
@@ -639,7 +639,7 @@ static const char *setup_explicit_git_dir(const char *gitdirenv,
        int offset;
 
        if (PATH_MAX - 40 < strlen(gitdirenv))
-               die("'$%s' too big", GIT_DIR_ENVIRONMENT);
+               die(_("'$%s' too big"), GIT_DIR_ENVIRONMENT);
 
        gitfile = (char*)read_gitfile(gitdirenv);
        if (gitfile) {
@@ -653,7 +653,7 @@ static const char *setup_explicit_git_dir(const char *gitdirenv,
                        free(gitfile);
                        return NULL;
                }
-               die("Not a git repository: '%s'", gitdirenv);
+               die(_("not a git repository: '%s'"), gitdirenv);
        }
 
        if (check_repository_format_gently(gitdirenv, repo_fmt, nongit_ok)) {
@@ -682,12 +682,12 @@ static const char *setup_explicit_git_dir(const char *gitdirenv,
                else {
                        char *core_worktree;
                        if (chdir(gitdirenv))
-                               die_errno("Could not chdir to '%s'", gitdirenv);
+                               die_errno(_("cannot chdir to '%s'"), gitdirenv);
                        if (chdir(git_work_tree_cfg))
-                               die_errno("Could not chdir to '%s'", git_work_tree_cfg);
+                               die_errno(_("cannot chdir to '%s'"), git_work_tree_cfg);
                        core_worktree = xgetcwd();
                        if (chdir(cwd->buf))
-                               die_errno("Could not come back to cwd");
+                               die_errno(_("cannot come back to cwd"));
                        set_git_work_tree(core_worktree);
                        free(core_worktree);
                }
@@ -715,7 +715,7 @@ static const char *setup_explicit_git_dir(const char *gitdirenv,
        if (offset >= 0) {      /* cwd inside worktree? */
                set_git_dir(real_path(gitdirenv));
                if (chdir(worktree))
-                       die_errno("Could not chdir to '%s'", worktree);
+                       die_errno(_("cannot chdir to '%s'"), worktree);
                strbuf_addch(cwd, '/');
                free(gitfile);
                return cwd->buf + offset;
@@ -743,7 +743,7 @@ static const char *setup_discovered_git_dir(const char *gitdir,
                if (offset != cwd->len && !is_absolute_path(gitdir))
                        gitdir = to_free = real_pathdup(gitdir, 1);
                if (chdir(cwd->buf))
-                       die_errno("Could not come back to cwd");
+                       die_errno(_("cannot come back to cwd"));
                ret = setup_explicit_git_dir(gitdir, cwd, repo_fmt, nongit_ok);
                free(to_free);
                return ret;
@@ -753,7 +753,7 @@ static const char *setup_discovered_git_dir(const char *gitdir,
        if (is_bare_repository_cfg > 0) {
                set_git_dir(offset == cwd->len ? gitdir : real_path(gitdir));
                if (chdir(cwd->buf))
-                       die_errno("Could not come back to cwd");
+                       die_errno(_("cannot come back to cwd"));
                return NULL;
        }
 
@@ -792,7 +792,7 @@ static const char *setup_bare_git_dir(struct strbuf *cwd, int offset,
 
                gitdir = offset == cwd->len ? "." : xmemdupz(cwd->buf, offset);
                if (chdir(cwd->buf))
-                       die_errno("Could not come back to cwd");
+                       die_errno(_("cannot come back to cwd"));
                return setup_explicit_git_dir(gitdir, cwd, repo_fmt, nongit_ok);
        }
 
@@ -800,7 +800,7 @@ static const char *setup_bare_git_dir(struct strbuf *cwd, int offset,
        inside_work_tree = 0;
        if (offset != cwd->len) {
                if (chdir(cwd->buf))
-                       die_errno("Cannot come back to cwd");
+                       die_errno(_("cannot come back to cwd"));
                root_len = offset_1st_component(cwd->buf);
                strbuf_setlen(cwd, offset > root_len ? offset : root_len);
                set_git_dir(cwd->buf);
@@ -813,9 +813,9 @@ static const char *setup_bare_git_dir(struct strbuf *cwd, int offset,
 static const char *setup_nongit(const char *cwd, int *nongit_ok)
 {
        if (!nongit_ok)
-               die(_("Not a git repository (or any of the parent directories): %s"), DEFAULT_GIT_DIR_ENVIRONMENT);
+               die(_("not a git repository (or any of the parent directories): %s"), DEFAULT_GIT_DIR_ENVIRONMENT);
        if (chdir(cwd))
-               die_errno(_("Cannot come back to cwd"));
+               die_errno(_("cannot come back to cwd"));
        *nongit_ok = 1;
        return NULL;
 }
@@ -824,7 +824,7 @@ static dev_t get_device_or_die(const char *path, const char *prefix, int prefix_
 {
        struct stat buf;
        if (stat(path, &buf)) {
-               die_errno("failed to stat '%*s%s%s'",
+               die_errno(_("failed to stat '%*s%s%s'"),
                                prefix_len,
                                prefix ? prefix : "",
                                prefix ? "/" : "", path);
@@ -1066,13 +1066,13 @@ const char *setup_git_directory_gently(int *nongit_ok)
                break;
        case GIT_DIR_DISCOVERED:
                if (dir.len < cwd.len && chdir(dir.buf))
-                       die(_("Cannot change to '%s'"), dir.buf);
+                       die(_("cannot change to '%s'"), dir.buf);
                prefix = setup_discovered_git_dir(gitdir.buf, &cwd, dir.len,
                                                  &repo_fmt, nongit_ok);
                break;
        case GIT_DIR_BARE:
                if (dir.len < cwd.len && chdir(dir.buf))
-                       die(_("Cannot change to '%s'"), dir.buf);
+                       die(_("cannot change to '%s'"), dir.buf);
                prefix = setup_bare_git_dir(&cwd, dir.len, &repo_fmt, nongit_ok);
                break;
        case GIT_DIR_HIT_CEILING:
@@ -1085,7 +1085,7 @@ const char *setup_git_directory_gently(int *nongit_ok)
                        strbuf_release(&dir);
                        return NULL;
                }
-               die(_("Not a git repository (or any parent up to mount point %s)\n"
+               die(_("not a git repository (or any parent up to mount point %s)\n"
                      "Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set)."),
                    dir.buf);
        default:
@@ -1169,7 +1169,7 @@ int git_config_perm(const char *var, const char *value)
        /* A filemode value was given: 0xxx */
 
        if ((i & 0600) != 0600)
-               die(_("Problem with core.sharedRepository filemode value "
+               die(_("problem with core.sharedRepository filemode value "
                    "(0%.3o).\nThe owner of files must always have "
                    "read and write permissions."), i);
 
@@ -1212,7 +1212,7 @@ void sanitize_stdfds(void)
        while (fd != -1 && fd < 2)
                fd = dup(fd);
        if (fd == -1)
-               die_errno("open /dev/null or dup failed");
+               die_errno(_("open /dev/null or dup failed"));
        if (fd > 2)
                close(fd);
 }
@@ -1227,12 +1227,12 @@ int daemonize(void)
                case 0:
                        break;
                case -1:
-                       die_errno("fork failed");
+                       die_errno(_("fork failed"));
                default:
                        exit(0);
        }
        if (setsid() == -1)
-               die_errno("setsid failed");
+               die_errno(_("setsid failed"));
        close(0);
        close(1);
        close(2);
index 4cf3ebd9212f6d5c9b9829373e58c34b83f0a548..8d0b1db3e27c99b80faa2867565de237581802f4 100644 (file)
@@ -99,3 +99,31 @@ int sha1_pos(const unsigned char *sha1, void *table, size_t nr,
        } while (lo < hi);
        return -lo-1;
 }
+
+int bsearch_hash(const unsigned char *sha1, const uint32_t *fanout_nbo,
+                const unsigned char *table, size_t stride, uint32_t *result)
+{
+       uint32_t hi, lo;
+
+       hi = ntohl(fanout_nbo[*sha1]);
+       lo = ((*sha1 == 0x0) ? 0 : ntohl(fanout_nbo[*sha1 - 1]));
+
+       while (lo < hi) {
+               unsigned mi = lo + (hi - lo) / 2;
+               int cmp = hashcmp(table + mi * stride, sha1);
+
+               if (!cmp) {
+                       if (result)
+                               *result = mi;
+                       return 1;
+               }
+               if (cmp > 0)
+                       hi = mi;
+               else
+                       lo = mi + 1;
+       }
+
+       if (result)
+               *result = lo;
+       return 0;
+}
index cf5314f402ce78f0d5ab2bd72ee7f334b6394e04..7678b23b36c291c7b1b6656e0c42fc14ea12da15 100644 (file)
@@ -7,4 +7,26 @@ extern int sha1_pos(const unsigned char *sha1,
                    void *table,
                    size_t nr,
                    sha1_access_fn fn);
+
+/*
+ * Searches for sha1 in table, using the given fanout table to determine the
+ * interval to search, then using binary search. Returns 1 if found, 0 if not.
+ *
+ * Takes the following parameters:
+ *
+ *  - sha1: the hash to search for
+ *  - fanout_nbo: a 256-element array of NETWORK-order 32-bit integers; the
+ *    integer at position i represents the number of elements in table whose
+ *    first byte is less than or equal to i
+ *  - table: a sorted list of hashes with optional extra information in between
+ *  - stride: distance between two consecutive elements in table (should be
+ *    GIT_MAX_RAWSZ or greater)
+ *  - result: if not NULL, this function stores the element index of the
+ *    position found (if the search is successful) or the index of the least
+ *    element that is greater than sha1 (if the search is not successful)
+ *
+ * This function does not verify the validity of the fanout table.
+ */
+int bsearch_hash(const unsigned char *sha1, const uint32_t *fanout_nbo,
+                const unsigned char *table, size_t stride, uint32_t *result);
 #endif
index 2aa8a1747f8586839aa3036fbbc59f6c716c6128..602ba8ca8b8455df9b34e2990397c838d542569f 100644 (file)
@@ -9,7 +9,7 @@
 /*
  * submodule cache lookup structure
  * There is one shared set of 'struct submodule' entries which can be
- * looked up by their sha1 blob id of the .gitmodule file and either
+ * looked up by their sha1 blob id of the .gitmodules file and either
  * using path or name as key.
  * for_path stores submodule entries with path as key
  * for_name stores submodule entries with name as key
@@ -91,7 +91,7 @@ static void submodule_cache_clear(struct submodule_cache *cache)
        /*
         * We iterate over the name hash here to be symmetric with the
         * allocation of struct submodule entries. Each is allocated by
-        * their .gitmodule blob sha1 and submodule name.
+        * their .gitmodules blob sha1 and submodule name.
         */
        hashmap_iter_init(&cache->for_name, &iter);
        while ((entry = hashmap_iter_next(&iter)))
index b3f7b449c366c2d7d7afd12139a54b7a588f9747..1a1361a8063b8e247b6a92b83b6e8876953f95bd 100644 (file)
--- a/t/README
+++ b/t/README
@@ -655,7 +655,7 @@ library for your script to use.
                test_expect_code 1 git merge "merge msg" B master
        '
 
- - test_must_fail <git-command>
+ - test_must_fail [<options>] <git-command>
 
    Run a git command and ensure it fails in a controlled way.  Use
    this instead of "! <git-command>".  When git-command dies due to a
@@ -663,11 +663,21 @@ library for your script to use.
    treats it as just another expected failure, which would let such a
    bug go unnoticed.
 
- - test_might_fail <git-command>
+   Accepts the following options:
+
+     ok=<signal-name>[,<...>]:
+       Don't treat an exit caused by the given signal as error.
+       Multiple signals can be specified as a comma separated list.
+       Currently recognized signal names are: sigpipe, success.
+       (Don't use 'success', use 'test_might_fail' instead.)
+
+ - test_might_fail [<options>] <git-command>
 
    Similar to test_must_fail, but tolerate success, too.  Use this
    instead of "<git-command> || :" to catch failures due to segv.
 
+   Accepts the same options as test_must_fail.
+
  - test_cmp <expected> <actual>
 
    Check whether the content of the <actual> file matches the
index 1145d5167115a761d319b1e205500e765cd090cd..9ae9281c071254019ccca3486b3a6762d9c0085f 100644 (file)
@@ -1,5 +1,6 @@
 #include "git-compat-util.h"
 #include "hashmap.h"
+#include "strbuf.h"
 
 struct test_entry
 {
@@ -29,11 +30,12 @@ static int test_entry_cmp(const void *cmp_data,
                return strcmp(e1->key, key ? key : e2->key);
 }
 
-static struct test_entry *alloc_test_entry(int hash, char *key, int klen,
-               char *value, int vlen)
+static struct test_entry *alloc_test_entry(unsigned int hash,
+                                          char *key, char *value)
 {
-       struct test_entry *entry = malloc(sizeof(struct test_entry) + klen
-                       + vlen + 2);
+       size_t klen = strlen(key);
+       size_t vlen = strlen(value);
+       struct test_entry *entry = xmalloc(st_add4(sizeof(*entry), klen, vlen, 2));
        hashmap_entry_init(entry, hash);
        memcpy(entry->key, key, klen + 1);
        memcpy(entry->key + klen + 1, value, vlen + 1);
@@ -85,11 +87,11 @@ static void perf_hashmap(unsigned int method, unsigned int rounds)
        unsigned int *hashes;
        unsigned int i, j;
 
-       entries = malloc(TEST_SIZE * sizeof(struct test_entry *));
-       hashes = malloc(TEST_SIZE * sizeof(int));
+       ALLOC_ARRAY(entries, TEST_SIZE);
+       ALLOC_ARRAY(hashes, TEST_SIZE);
        for (i = 0; i < TEST_SIZE; i++) {
-               snprintf(buf, sizeof(buf), "%i", i);
-               entries[i] = alloc_test_entry(0, buf, strlen(buf), "", 0);
+               xsnprintf(buf, sizeof(buf), "%i", i);
+               entries[i] = alloc_test_entry(0, buf, "");
                hashes[i] = hash(method, i, entries[i]->key);
        }
 
@@ -144,7 +146,7 @@ static void perf_hashmap(unsigned int method, unsigned int rounds)
  */
 int cmd_main(int argc, const char **argv)
 {
-       char line[1024];
+       struct strbuf line = STRBUF_INIT;
        struct hashmap map;
        int icase;
 
@@ -153,44 +155,42 @@ int cmd_main(int argc, const char **argv)
        hashmap_init(&map, test_entry_cmp, &icase, 0);
 
        /* process commands from stdin */
-       while (fgets(line, sizeof(line), stdin)) {
+       while (strbuf_getline(&line, stdin) != EOF) {
                char *cmd, *p1 = NULL, *p2 = NULL;
-               int l1 = 0, l2 = 0, hash = 0;
+               unsigned int hash = 0;
                struct test_entry *entry;
 
                /* break line into command and up to two parameters */
-               cmd = strtok(line, DELIM);
+               cmd = strtok(line.buf, DELIM);
                /* ignore empty lines */
                if (!cmd || *cmd == '#')
                        continue;
 
                p1 = strtok(NULL, DELIM);
                if (p1) {
-                       l1 = strlen(p1);
                        hash = icase ? strihash(p1) : strhash(p1);
                        p2 = strtok(NULL, DELIM);
-                       if (p2)
-                               l2 = strlen(p2);
                }
 
-               if (!strcmp("hash", cmd) && l1) {
+               if (!strcmp("hash", cmd) && p1) {
 
                        /* print results of different hash functions */
-                       printf("%u %u %u %u\n", strhash(p1), memhash(p1, l1),
-                                       strihash(p1), memihash(p1, l1));
+                       printf("%u %u %u %u\n",
+                              strhash(p1), memhash(p1, strlen(p1)),
+                              strihash(p1), memihash(p1, strlen(p1)));
 
-               } else if (!strcmp("add", cmd) && l1 && l2) {
+               } else if (!strcmp("add", cmd) && p1 && p2) {
 
                        /* create entry with key = p1, value = p2 */
-                       entry = alloc_test_entry(hash, p1, l1, p2, l2);
+                       entry = alloc_test_entry(hash, p1, p2);
 
                        /* add to hashmap */
                        hashmap_add(&map, entry);
 
-               } else if (!strcmp("put", cmd) && l1 && l2) {
+               } else if (!strcmp("put", cmd) && p1 && p2) {
 
                        /* create entry with key = p1, value = p2 */
-                       entry = alloc_test_entry(hash, p1, l1, p2, l2);
+                       entry = alloc_test_entry(hash, p1, p2);
 
                        /* add / replace entry */
                        entry = hashmap_put(&map, entry);
@@ -199,7 +199,7 @@ int cmd_main(int argc, const char **argv)
                        puts(entry ? get_value(entry) : "NULL");
                        free(entry);
 
-               } else if (!strcmp("get", cmd) && l1) {
+               } else if (!strcmp("get", cmd) && p1) {
 
                        /* lookup entry in hashmap */
                        entry = hashmap_get_from_hash(&map, hash, p1);
@@ -212,7 +212,7 @@ int cmd_main(int argc, const char **argv)
                                entry = hashmap_get_next(&map, entry);
                        }
 
-               } else if (!strcmp("remove", cmd) && l1) {
+               } else if (!strcmp("remove", cmd) && p1) {
 
                        /* setup static key */
                        struct hashmap_entry key;
@@ -238,7 +238,7 @@ int cmd_main(int argc, const char **argv)
                        printf("%u %u\n", map.tablesize,
                               hashmap_get_size(&map));
 
-               } else if (!strcmp("intern", cmd) && l1) {
+               } else if (!strcmp("intern", cmd) && p1) {
 
                        /* test that strintern works */
                        const char *i1 = strintern(p1);
@@ -252,7 +252,7 @@ int cmd_main(int argc, const char **argv)
                        else
                                printf("%s\n", i1);
 
-               } else if (!strcmp("perfhashmap", cmd) && l1 && l2) {
+               } else if (!strcmp("perfhashmap", cmd) && p1 && p2) {
 
                        perf_hashmap(atoi(p1), atoi(p2));
 
@@ -263,6 +263,7 @@ int cmd_main(int argc, const char **argv)
                }
        }
 
+       strbuf_release(&line);
        hashmap_free(&map, 1);
        return 0;
 }
index 9670e8cbe6cb9a9faa3519b0f11dc16713496188..3691023d510a0d97bf1390b781afe1ac9fa270f4 100755 (executable)
@@ -10,15 +10,6 @@ objpath() {
        echo "$1" | sed -e 's|\(..\)|\1/|'
 }
 
-objck() {
-       p=$(objpath "$1")
-       if test ! -f "$REAL/objects/$p"
-       then
-               echo "Object not found: $REAL/objects/$p"
-               false
-       fi
-}
-
 test_expect_success 'initial setup' '
        REAL="$(pwd)/.real" &&
        mv .git "$REAL"
@@ -26,30 +17,14 @@ test_expect_success 'initial setup' '
 
 test_expect_success 'bad setup: invalid .git file format' '
        echo "gitdir $REAL" >.git &&
-       if git rev-parse 2>.err
-       then
-               echo "git rev-parse accepted an invalid .git file"
-               false
-       fi &&
-       if ! grep "Invalid gitfile format" .err
-       then
-               echo "git rev-parse returned wrong error"
-               false
-       fi
+       test_must_fail git rev-parse 2>.err &&
+       test_i18ngrep "invalid gitfile format" .err
 '
 
 test_expect_success 'bad setup: invalid .git file path' '
        echo "gitdir: $REAL.not" >.git &&
-       if git rev-parse 2>.err
-       then
-               echo "git rev-parse accepted an invalid .git file path"
-               false
-       fi &&
-       if ! grep "Not a git repository" .err
-       then
-               echo "git rev-parse returned wrong error"
-               false
-       fi
+       test_must_fail git rev-parse 2>.err &&
+       test_i18ngrep "not a git repository" .err
 '
 
 test_expect_success 'final setup + check rev-parse --git-dir' '
@@ -60,7 +35,7 @@ test_expect_success 'final setup + check rev-parse --git-dir' '
 test_expect_success 'check hash-object' '
        echo "foo" >bar &&
        SHA=$(cat bar | git hash-object -w --stdin) &&
-       objck $SHA
+       test_path_is_file "$REAL/objects/$(objpath $SHA)"
 '
 
 test_expect_success 'check cat-file' '
@@ -69,29 +44,21 @@ test_expect_success 'check cat-file' '
 '
 
 test_expect_success 'check update-index' '
-       if test -f "$REAL/index"
-       then
-               echo "Hmm, $REAL/index exists?"
-               false
-       fi &&
+       test_path_is_missing "$REAL/index" &&
        rm -f "$REAL/objects/$(objpath $SHA)" &&
        git update-index --add bar &&
-       if ! test -f "$REAL/index"
-       then
-               echo "$REAL/index not found"
-               false
-       fi &&
-       objck $SHA
+       test_path_is_file "$REAL/index" &&
+       test_path_is_file "$REAL/objects/$(objpath $SHA)"
 '
 
 test_expect_success 'check write-tree' '
        SHA=$(git write-tree) &&
-       objck $SHA
+       test_path_is_file "$REAL/objects/$(objpath $SHA)"
 '
 
 test_expect_success 'check commit-tree' '
        SHA=$(echo "commit bar" | git commit-tree $SHA) &&
-       objck $SHA
+       test_path_is_file "$REAL/objects/$(objpath $SHA)"
 '
 
 test_expect_success 'check rev-list' '
index d27f438bf410d06f37ec76a6ce040316218a8138..c03f155a357446338fa831e061dc58d8e5f4c657 100755 (executable)
@@ -307,7 +307,7 @@ test_expect_success_multi 'needs work tree' '' '
                cd .git &&
                test_check_ignore "foo" 128
        ) &&
-       stderr_contains "fatal: This operation must be run in a work tree"
+       stderr_contains "fatal: this operation must be run in a work tree"
 '
 
 ############################################################################
@@ -775,6 +775,26 @@ test_expect_success PIPE 'streaming support for --stdin' '
        echo "$response" | grep "^::    two"
 '
 
+test_expect_success 'existing file and directory' '
+       test_when_finished "rm one" &&
+       test_when_finished "rmdir top-level-dir" &&
+       >one &&
+       mkdir top-level-dir &&
+       git check-ignore one top-level-dir >actual &&
+       grep one actual &&
+       grep top-level-dir actual
+'
+
+test_expect_success 'existing directory and file' '
+       test_when_finished "rm one" &&
+       test_when_finished "rmdir top-level-dir" &&
+       >one &&
+       mkdir top-level-dir &&
+       git check-ignore top-level-dir one >actual &&
+       grep one actual &&
+       grep top-level-dir actual
+'
+
 ############################################################################
 #
 # test whitespace handling
index b29d749bb7b33406b2d433d96c35d252e305eed0..192c94eccd13c3b251cfc6910ad6ef175312ea13 100755 (executable)
@@ -80,7 +80,21 @@ test_expect_success 'merge (case change)' '
        git merge topic
 '
 
-
+test_expect_success CASE_INSENSITIVE_FS 'add directory (with different case)' '
+       git reset --hard initial &&
+       mkdir -p dir1/dir2 &&
+       echo >dir1/dir2/a &&
+       echo >dir1/dir2/b &&
+       git add dir1/dir2/a &&
+       git add dir1/DIR2/b &&
+       git ls-files >actual &&
+       cat >expected <<-\EOF &&
+               camelcase
+               dir1/dir2/a
+               dir1/dir2/b
+       EOF
+       test_cmp expected actual
+'
 
 test_expect_failure CASE_INSENSITIVE_FS 'add (with different case)' '
        git reset --hard initial &&
index 2361590d54856a1a284945e46fece2716487f624..438e778d6afa086e8823b2ac3d38c2a26f171dbd 100755 (executable)
@@ -7,10 +7,6 @@ test_description='Gettext Shell poison'
 
 . ./lib-gettext.sh
 
-test_expect_success GETTEXT_POISON "sanity: \$GIT_INTERNAL_GETTEXT_SH_SCHEME is set (to $GIT_INTERNAL_GETTEXT_SH_SCHEME)" '
-    test -n "$GIT_INTERNAL_GETTEXT_SH_SCHEME"
-'
-
 test_expect_success GETTEXT_POISON 'sanity: $GIT_INTERNAL_GETTEXT_SH_SCHEME" is poison' '
     test "$GIT_INTERNAL_GETTEXT_SH_SCHEME" = "poison"
 '
index cbeb9bebeea67c3654c279b2c12fed4825840d8f..4f8e6f5fde3295cc91d2feb1fd85fa007e56016c 100755 (executable)
@@ -1206,6 +1206,29 @@ test_expect_success 'git -c is not confused by empty environment' '
        GIT_CONFIG_PARAMETERS="" git -c x.one=1 config --list
 '
 
+sq="'"
+test_expect_success 'detect bogus GIT_CONFIG_PARAMETERS' '
+       cat >expect <<-\EOF &&
+       env.one one
+       env.two two
+       EOF
+       GIT_CONFIG_PARAMETERS="${sq}env.one=one${sq} ${sq}env.two=two${sq}" \
+               git config --get-regexp "env.*" >actual &&
+       test_cmp expect actual &&
+
+       cat >expect <<-EOF &&
+       env.one one${sq}
+       env.two two
+       EOF
+       GIT_CONFIG_PARAMETERS="${sq}env.one=one${sq}\\$sq$sq$sq ${sq}env.two=two${sq}" \
+               git config --get-regexp "env.*" >actual &&
+       test_cmp expect actual &&
+
+       test_must_fail env \
+               GIT_CONFIG_PARAMETERS="${sq}env.one=one${sq}\\$sq ${sq}env.two=two${sq}" \
+               git config --get-regexp "env.*"
+'
+
 test_expect_success 'git config --edit works' '
        git config -f tmp test.value no &&
        echo test.value=yes >expect &&
index 79a0251efa6e049be4fffa41004e73b9950d2bd7..4ee009da666f22f6ff1ddce93dc1f30924c6cc26 100755 (executable)
@@ -157,7 +157,7 @@ test_expect_success 'relative path not found' '
 test_expect_success 'relative path outside worktree' '
        test_must_fail git rev-parse HEAD:../file.txt >output 2>error &&
        test -z "$(cat output)" &&
-       grep "outside repository" error
+       test_i18ngrep "outside repository" error
 '
 
 test_expect_success 'relative path when cwd is outside worktree' '
index 2b959449730e14dd4e650ce9115a463d71eedca4..d0d2e4f7ec3310ec51da7144fa87151129f393c0 100755 (executable)
@@ -451,32 +451,68 @@ test_expect_success 'git worktree --no-guess-remote option overrides config' '
 '
 
 post_checkout_hook () {
-       test_when_finished "rm -f .git/hooks/post-checkout" &&
-       mkdir -p .git/hooks &&
-       write_script .git/hooks/post-checkout <<-\EOF
-       echo $* >hook.actual
+       gitdir=${1:-.git}
+       test_when_finished "rm -f $gitdir/hooks/post-checkout" &&
+       mkdir -p $gitdir/hooks &&
+       write_script $gitdir/hooks/post-checkout <<-\EOF
+       {
+               echo $*
+               git rev-parse --git-dir --show-toplevel
+       } >hook.actual
        EOF
 }
 
 test_expect_success '"add" invokes post-checkout hook (branch)' '
        post_checkout_hook &&
-       printf "%s %s 1\n" $_z40 $(git rev-parse HEAD) >hook.expect &&
+       {
+               echo $_z40 $(git rev-parse HEAD) 1 &&
+               echo $(pwd)/.git/worktrees/gumby &&
+               echo $(pwd)/gumby
+       } >hook.expect &&
        git worktree add gumby &&
-       test_cmp hook.expect hook.actual
+       test_cmp hook.expect gumby/hook.actual
 '
 
 test_expect_success '"add" invokes post-checkout hook (detached)' '
        post_checkout_hook &&
-       printf "%s %s 1\n" $_z40 $(git rev-parse HEAD) >hook.expect &&
+       {
+               echo $_z40 $(git rev-parse HEAD) 1 &&
+               echo $(pwd)/.git/worktrees/grumpy &&
+               echo $(pwd)/grumpy
+       } >hook.expect &&
        git worktree add --detach grumpy &&
-       test_cmp hook.expect hook.actual
+       test_cmp hook.expect grumpy/hook.actual
 '
 
 test_expect_success '"add --no-checkout" suppresses post-checkout hook' '
        post_checkout_hook &&
        rm -f hook.actual &&
        git worktree add --no-checkout gloopy &&
-       test_path_is_missing hook.actual
+       test_path_is_missing gloopy/hook.actual
+'
+
+test_expect_success '"add" in other worktree invokes post-checkout hook' '
+       post_checkout_hook &&
+       {
+               echo $_z40 $(git rev-parse HEAD) 1 &&
+               echo $(pwd)/.git/worktrees/guppy &&
+               echo $(pwd)/guppy
+       } >hook.expect &&
+       git -C gloopy worktree add --detach ../guppy &&
+       test_cmp hook.expect guppy/hook.actual
+'
+
+test_expect_success '"add" in bare repo invokes post-checkout hook' '
+       rm -rf bare &&
+       git clone --bare . bare &&
+       {
+               echo $_z40 $(git --git-dir=bare rev-parse HEAD) 1 &&
+               echo $(pwd)/bare/worktrees/goozy &&
+               echo $(pwd)/goozy
+       } >hook.expect &&
+       post_checkout_hook bare &&
+       git -C bare worktree add --detach ../goozy &&
+       test_cmp hook.expect goozy/hook.actual
 '
 
 test_done
index 6b84e6042a6fcc9cf850a53ad2a885597fb178fc..e7292f5b9b938018f515f858f2dadc29162261ad 100755 (executable)
@@ -24,8 +24,23 @@ But otherwise with a sane description." &&
        >elif &&
        git add elif &&
        test_tick &&
-       git commit -m second
+       git commit -m second &&
 
+       git checkout -b side2 &&
+       >afile &&
+       git add afile &&
+       test_tick &&
+       git commit -m third &&
+       echo hello >afile &&
+       test_tick &&
+       git commit -a -m fourth &&
+       git checkout -b side-merge &&
+       git reset --hard HEAD^^ &&
+       git merge --no-ff -m "A merge commit log message that has a long
+summary that spills over multiple lines.
+
+But otherwise with a sane description." side2 &&
+       git branch side-merge-original
 '
 
 test_expect_success rebase '
@@ -36,6 +51,15 @@ test_expect_success rebase '
        git cat-file commit side@{1} | sed -e "1,/^\$/d" >expect &&
        test_cmp expect actual
 
+'
+test_expect_success rebasep '
+
+       git checkout side-merge &&
+       git rebase -p side &&
+       git cat-file commit HEAD | sed -e "1,/^\$/d" >actual &&
+       git cat-file commit side-merge-original | sed -e "1,/^\$/d" >expect &&
+       test_cmp expect actual
+
 '
 
 test_done
index 27cb0009fb1ed52b749785c31081b3097fef0022..c7c688fcc4bbdfe97a5c595fddc96e2590e021a2 100755 (executable)
@@ -89,4 +89,21 @@ test_expect_success 'traditional, whitespace-damaged, colon in timezone' '
        test_cmp expected "post image.txt"
 '
 
+cat >diff-from-svn <<\EOF
+Index: Makefile
+===================================================================
+diff --git a/branches/Makefile
+deleted file mode 100644
+--- a/branches/Makefile        (revision 13)
++++ /dev/null  (nonexistent)
+@@ +1 0,0 @@
+-
+EOF
+
+test_expect_success 'apply handles a diff generated by Subversion' '
+       >Makefile &&
+       git apply -p2 diff-from-svn &&
+       test_path_is_missing Makefile
+'
+
 test_done
index 73b67b4280b99e0328e201e6b69c3d88b766ea84..512c754e02a84ee0beb467d3862b0d677c7aae2c 100755 (executable)
@@ -1045,4 +1045,16 @@ test_expect_success 'am works with multi-line in-body headers' '
        git cat-file commit HEAD | grep "^$LONG$"
 '
 
+test_expect_success 'am --quit keeps HEAD where it is' '
+       mkdir .git/rebase-apply &&
+       >.git/rebase-apply/last &&
+       >.git/rebase-apply/next &&
+       git rev-parse HEAD^ >.git/ORIG_HEAD &&
+       git rev-parse HEAD >expected &&
+       git am --quit &&
+       test_path_is_missing .git/rebase-apply &&
+       git rev-parse HEAD >actual &&
+       test_cmp expected actual
+'
+
 test_done
index a552ad4ead899fbb431c68bef558811d25cd8c63..74486c73b0bcc20ee69e49d5c1cc072994e4d7b0 100755 (executable)
@@ -485,7 +485,7 @@ test_expect_success "don't fetch submodule when newly recorded commits are alrea
        )
 '
 
-test_expect_success "'fetch.recurseSubmodules=on-demand' works also without .gitmodule entry" '
+test_expect_success "'fetch.recurseSubmodules=on-demand' works also without .gitmodules entry" '
        (
                cd downstream &&
                git fetch --recurse-submodules
index 463783789c8ccda6a483197f6626afbfca7d8ccf..b47a95871cac3dd8593b3b9262d238be3914ac62 100755 (executable)
@@ -217,17 +217,32 @@ test_expect_success 'invalid push option in config' '
        test_refs master HEAD@{1}
 '
 
+test_expect_success 'push options keep quoted characters intact (direct)' '
+       mk_repo_pair &&
+       git -C upstream config receive.advertisePushOptions true &&
+       test_commit -C workbench one &&
+       git -C workbench push --push-option="\"embedded quotes\"" up master &&
+       echo "\"embedded quotes\"" >expect &&
+       test_cmp expect upstream/.git/hooks/pre-receive.push_options
+'
+
 . "$TEST_DIRECTORY"/lib-httpd.sh
 start_httpd
 
-test_expect_success 'push option denied properly by http server' '
+# set up http repository for fetching/pushing, with push options config
+# bool set to $1
+mk_http_pair () {
        test_when_finished "rm -rf test_http_clone" &&
-       test_when_finished "rm -rf \"$HTTPD_DOCUMENT_ROOT_PATH\"/upstream.git" &&
+       test_when_finished 'rm -rf "$HTTPD_DOCUMENT_ROOT_PATH"/upstream.git' &&
        mk_repo_pair &&
-       git -C upstream config receive.advertisePushOptions false &&
+       git -C upstream config receive.advertisePushOptions "$1" &&
        git -C upstream config http.receivepack true &&
        cp -R upstream/.git "$HTTPD_DOCUMENT_ROOT_PATH"/upstream.git &&
-       git clone "$HTTPD_URL"/smart/upstream test_http_clone &&
+       git clone "$HTTPD_URL"/smart/upstream test_http_clone
+}
+
+test_expect_success 'push option denied properly by http server' '
+       mk_http_pair false &&
        test_commit -C test_http_clone one &&
        test_must_fail git -C test_http_clone push --push-option=asdf origin master 2>actual &&
        test_i18ngrep "the receiving end does not support push options" actual &&
@@ -235,13 +250,7 @@ test_expect_success 'push option denied properly by http server' '
 '
 
 test_expect_success 'push options work properly across http' '
-       test_when_finished "rm -rf test_http_clone" &&
-       test_when_finished "rm -rf \"$HTTPD_DOCUMENT_ROOT_PATH\"/upstream.git" &&
-       mk_repo_pair &&
-       git -C upstream config receive.advertisePushOptions true &&
-       git -C upstream config http.receivepack true &&
-       cp -R upstream/.git "$HTTPD_DOCUMENT_ROOT_PATH"/upstream.git &&
-       git clone "$HTTPD_URL"/smart/upstream test_http_clone &&
+       mk_http_pair true &&
 
        test_commit -C test_http_clone one &&
        git -C test_http_clone push origin master &&
@@ -260,6 +269,15 @@ test_expect_success 'push options work properly across http' '
        test_cmp expect actual
 '
 
+test_expect_success 'push options keep quoted characters intact (http)' '
+       mk_http_pair true &&
+
+       test_commit -C test_http_clone one &&
+       git -C test_http_clone push --push-option="\"embedded quotes\"" origin master &&
+       echo "\"embedded quotes\"" >expect &&
+       test_cmp expect "$HTTPD_DOCUMENT_ROOT_PATH"/upstream.git/hooks/pre-receive.push_options
+'
+
 stop_httpd
 
 test_done
index a5d901502414f25616a474152ee0f5816465bd37..bae78c4e89e2402d5ab37e3b7cd094dde41d69b3 100755 (executable)
@@ -378,4 +378,12 @@ check_describe tags/A --all A
 check_describe tags/c --all c
 check_describe heads/branch_A --all --match='branch_*' branch_A
 
+test_expect_success 'describe complains about tree object' '
+       test_must_fail git describe HEAD^{tree}
+'
+
+test_expect_success 'describe complains about missing object' '
+       test_must_fail git describe $_z40
+'
+
 test_done
index e5fb892f9575fda4baf0b2a0e6b31cf13a0d6c0b..46b947824fd68150cfaa6018355cd086c5a8759e 100755 (executable)
@@ -22,6 +22,12 @@ avoid_racy() {
        sleep 1
 }
 
+status_is_clean() {
+       >../status.expect &&
+       git status --porcelain >../status.actual &&
+       test_cmp ../status.expect ../status.actual
+}
+
 test_lazy_prereq UNTRACKED_CACHE '
        { git update-index --test-untracked-cache; ret=$?; } &&
        test $ret -ne 1
@@ -683,4 +689,85 @@ test_expect_success 'untracked cache survives a commit' '
        test_cmp ../before ../after
 '
 
+test_expect_success 'teardown worktree' '
+       cd ..
+'
+
+test_expect_success SYMLINKS 'setup worktree for symlink test' '
+       git init worktree-symlink &&
+       cd worktree-symlink &&
+       git config core.untrackedCache true &&
+       mkdir one two &&
+       touch one/file two/file &&
+       git add one/file two/file &&
+       git commit -m"first commit" &&
+       git rm -rf one &&
+       ln -s two one &&
+       git add one &&
+       git commit -m"second commit"
+'
+
+test_expect_success SYMLINKS '"status" after symlink replacement should be clean with UC=true' '
+       git checkout HEAD~ &&
+       status_is_clean &&
+       status_is_clean &&
+       git checkout master &&
+       avoid_racy &&
+       status_is_clean &&
+       status_is_clean
+'
+
+test_expect_success SYMLINKS '"status" after symlink replacement should be clean with UC=false' '
+       git config core.untrackedCache false &&
+       git checkout HEAD~ &&
+       status_is_clean &&
+       status_is_clean &&
+       git checkout master &&
+       avoid_racy &&
+       status_is_clean &&
+       status_is_clean
+'
+
+test_expect_success 'setup worktree for non-symlink test' '
+       git init worktree-non-symlink &&
+       cd worktree-non-symlink &&
+       git config core.untrackedCache true &&
+       mkdir one two &&
+       touch one/file two/file &&
+       git add one/file two/file &&
+       git commit -m"first commit" &&
+       git rm -rf one &&
+       cp two/file one &&
+       git add one &&
+       git commit -m"second commit"
+'
+
+test_expect_success '"status" after file replacement should be clean with UC=true' '
+       git checkout HEAD~ &&
+       status_is_clean &&
+       status_is_clean &&
+       git checkout master &&
+       avoid_racy &&
+       status_is_clean &&
+       test-dump-untracked-cache >../actual &&
+       grep -F "recurse valid" ../actual >../actual.grep &&
+       cat >../expect.grep <<EOF &&
+/ 0000000000000000000000000000000000000000 recurse valid
+/two/ 0000000000000000000000000000000000000000 recurse valid
+EOF
+       status_is_clean &&
+       test_cmp ../expect.grep ../actual.grep
+'
+
+test_expect_success '"status" after file replacement should be clean with UC=false' '
+       git config core.untrackedCache false &&
+       git checkout HEAD~ &&
+       status_is_clean &&
+       status_is_clean &&
+       git checkout master &&
+       avoid_racy &&
+       status_is_clean &&
+       status_is_clean
+'
+
 test_done
index c20717181e95fc74738bdf53aa3e0b0797d77d6a..fc018e3638a8bebebf0f1c689567e9867bfbbee8 100755 (executable)
@@ -6,7 +6,7 @@
 test_description='Test submodules on detached working tree
 
 This test verifies that "git submodule" initialization, update and addition works
-on detahced working trees
+on detached working trees
 '
 
 TEST_NO_CREATE_REPO=1
index eb2d13bbcf8abefd7af2be6f9cb3bf97e389ab15..756beb0d8eb466d78b235af363b6a36dde37c79e 100755 (executable)
@@ -314,4 +314,43 @@ test_expect_success 'splitting the index results in the same state' '
        test_cmp expect actual
 '
 
+test_expect_success UNTRACKED_CACHE 'ignore .git changes when invalidating UNTR' '
+       test_create_repo dot-git &&
+       (
+               cd dot-git &&
+               mkdir -p .git/hooks &&
+               : >tracked &&
+               : >modified &&
+               mkdir dir1 &&
+               : >dir1/tracked &&
+               : >dir1/modified &&
+               mkdir dir2 &&
+               : >dir2/tracked &&
+               : >dir2/modified &&
+               write_integration_script &&
+               git config core.fsmonitor .git/hooks/fsmonitor-test &&
+               git update-index --untracked-cache &&
+               git update-index --fsmonitor &&
+               GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace-before" \
+               git status &&
+               test-dump-untracked-cache >../before
+       ) &&
+       cat >>dot-git/.git/hooks/fsmonitor-test <<-\EOF &&
+       printf ".git\0"
+       printf ".git/index\0"
+       printf "dir1/.git\0"
+       printf "dir1/.git/index\0"
+       EOF
+       (
+               cd dot-git &&
+               GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace-after" \
+               git status &&
+               test-dump-untracked-cache >../after
+       ) &&
+       grep "directory invalidation" trace-before >>before &&
+       grep "directory invalidation" trace-after >>after &&
+       # UNTR extension unchanged, dir invalidation count unchanged
+       test_cmp before after
+'
+
 test_done
index 67b5994afb76810b15634e1b3bb6f9717298b029..8a8a9329eeaa322121ecdbb3cd1f5fa6c9ecc94c 100644 (file)
@@ -610,6 +610,14 @@ list_contains () {
 #
 # Writing this as "! git checkout ../outerspace" is wrong, because
 # the failure could be due to a segv.  We want a controlled failure.
+#
+# Accepts the following options:
+#
+#   ok=<signal-name>[,<...>]:
+#     Don't treat an exit caused by the given signal as error.
+#     Multiple signals can be specified as a comma separated list.
+#     Currently recognized signal names are: sigpipe, success.
+#     (Don't use 'success', use 'test_might_fail' instead.)
 
 test_must_fail () {
        case "$1" in
@@ -656,6 +664,8 @@ test_must_fail () {
 #
 # Writing "git config --unset all.configuration || :" would be wrong,
 # because we want to notice if it fails due to segv.
+#
+# Accepts the same options as test_must_fail.
 
 test_might_fail () {
        test_must_fail ok=success "$@"
index 33f6ce26f69d442238c60619a5f70673d451a7fb..9535d2e0a936eb1b7a84686a9b702d3ca9fd680d 100644 (file)
@@ -116,6 +116,7 @@ unset VISUAL EMAIL LANGUAGE COLUMNS $("$PERL_PATH" -e '
        my @vars = grep(/^GIT_/ && !/^GIT_($ok)/o, @env);
        print join("\n", @vars);
 ')
+unset XDG_CACHE_HOME
 unset XDG_CONFIG_HOME
 unset GITPERLLIB
 GIT_AUTHOR_EMAIL=author@example.com
index e6a15bbe44f24555a8edb9461c2af553de70cba0..c9f6e314d5cd830d63236ea88c5ff9c7baf418bb 100644 (file)
@@ -1528,7 +1528,7 @@ static void invalidate_ce_path(const struct cache_entry *ce,
        if (!ce)
                return;
        cache_tree_invalidate_path(o->src_index, ce->name);
-       untracked_cache_invalidate_path(o->src_index, ce->name);
+       untracked_cache_invalidate_path(o->src_index, ce->name, 1);
 }
 
 /*