Merge branch 'nm/imap-send-with-curl' into next
authorJunio C Hamano <gitster@pobox.com>
Tue, 19 Sep 2017 03:52:28 +0000 (12:52 +0900)
committerJunio C Hamano <gitster@pobox.com>
Tue, 19 Sep 2017 03:52:28 +0000 (12:52 +0900)
"git imap-send" has our own implementation of the protocol and also
can use more recent libCurl with the imap protocol support. Update
the latter so that it can use the credential subsystem, and then
make it the default option to use, so that we can eventually
deprecate and remove the former.

* nm/imap-send-with-curl:
imap-send: use curl by default when possible
imap_send: setup_curl: retreive credentials if not set in config file
imap-send: add wrapper to get server credentials if needed
imap-send: return with error if curl failed

137 files changed:
.travis.yml
Documentation/git-cat-file.txt
Documentation/git-checkout.txt
Documentation/git-for-each-ref.txt
Documentation/git-notes.txt
Documentation/git-update-index.txt
Documentation/gitattributes.txt
Documentation/rev-list-options.txt
Documentation/technical/api-config.txt
Documentation/technical/api-ref-iteration.txt
Makefile
archive.c
attr.c
builtin/add.c
builtin/am.c
builtin/check-ref-format.c
builtin/clean.c
builtin/clone.c
builtin/commit-tree.c
builtin/commit.c
builtin/config.c
builtin/describe.c
builtin/gc.c
builtin/get-tar-commit-id.c
builtin/help.c
builtin/init-db.c
builtin/ls-files.c
builtin/merge.c
builtin/name-rev.c
builtin/notes.c
builtin/pack-objects.c
builtin/pull.c
builtin/receive-pack.c
builtin/remote.c
builtin/rerere.c
builtin/reset.c
builtin/rev-parse.c
builtin/shortlog.c
builtin/unpack-file.c
builtin/update-index.c
builtin/worktree.c
cache-tree.c
cache.h
ci/install-dependencies.sh [new file with mode: 0755]
ci/lib-travisci.sh [new file with mode: 0755]
ci/print-test-failures.sh [new file with mode: 0755]
ci/run-build.sh [new file with mode: 0755]
ci/run-linux32-docker.sh [new file with mode: 0755]
ci/run-static-analysis.sh [new file with mode: 0755]
ci/run-tests.sh [new file with mode: 0755]
ci/run-windows-build.sh
ci/test-documentation.sh
commit.c
config.c
config.h
connect.c
contrib/diff-highlight/Makefile
convert.c
credential-cache--daemon.c
diff.c
entry.c
environment.c
exec_cmd.c
fast-import.c
git-compat-util.h
gpg-interface.c
hash.h
hashmap.c
hashmap.h
http-backend.c
list.h
ll-merge.c
lockfile.c
lockfile.h
mailinfo.c
merge-recursive.c
merge-recursive.h
name-hash.c
notes-merge.c
notes.c
outgoing/packfile.h [deleted file]
packfile.c
pathspec.c
pkt-line.c
reachable.c
read-cache.c
refs.c
refs.h
refs/files-backend.c
refs/packed-backend.c
refs/packed-backend.h
refs/refs-internal.h
repository.c
rerere.c
revision.c
revision.h
send-pack.c
sequencer.c
setup.c
sha1_file.c
sha1dc_git.c
sha1dc_git.h
shallow.c
streaming.c
sub-process.c
submodule.c
t/helper/.gitignore
t/helper/test-delta.c
t/helper/test-hashmap.c
t/t0027-auto-crlf.sh
t/t1404-update-ref-errors.sh
t/t1407-worktree-ref-store.sh
t/t3600-rm.sh
t/t3700-add.sh
t/t5001-archive-attr.sh
t/t5002-archive-attr-pattern.sh
t/t5004-archive-corner-cases.sh
t/t5304-prune.sh
t/t5531-deep-submodule-push.sh
t/t5572-pull-submodule.sh
t/t6002-rev-list-bisect.sh
t/t6120-describe.sh
t/t7004-tag.sh
t/t7504-commit-msg-hook.sh
t/test-lib.sh
tempfile.c
tempfile.h
trailer.c
transport-helper.c
usage.c
userdiff.c
utf8.c
vcs-svn/svndump.c
worktree.c
worktree.h
wrapper.c
wt-status.c
index 278943d14a244b6b748078ff9ee18a17f58e7b45..fead995eddd15460b6be81e6a5f7c8f0648368ca 100644 (file)
@@ -61,23 +61,8 @@ matrix:
       services:
         - docker
       before_install:
-        - docker pull daald/ubuntu32:xenial
       before_script:
-      script:
-        - >
-          docker run
-          --interactive
-          --env DEVELOPER
-          --env DEFAULT_TEST_TARGET
-          --env GIT_PROVE_OPTS
-          --env GIT_TEST_OPTS
-          --env GIT_TEST_CLONE_2GB
-          --volume "${PWD}:/usr/src/git"
-          daald/ubuntu32:xenial
-          /usr/src/git/ci/run-linux32-build.sh $(id -u $USER)
-        # Use the following command to debug the docker build locally:
-        # $ docker run -itv "${PWD}:/usr/src/git" --entrypoint /bin/bash daald/ubuntu32:xenial
-        # root@container:/# /usr/src/git/ci/run-linux32-build.sh
+      script: ci/run-linux32-docker.sh
     - env: Static Analysis
       os: linux
       compiler:
@@ -86,9 +71,8 @@ matrix:
           packages:
           - coccinelle
       before_install:
-      script:
-        # "before_script" that builds Git is inherited from base job
-        - make coccicheck
+      # "before_script" that builds Git is inherited from base job
+      script: ci/run-static-analysis.sh
       after_failure:
     - env: Documentation
       os: linux
@@ -99,70 +83,14 @@ matrix:
           - asciidoc
           - xmlto
       before_install:
-      before_script: gem install asciidoctor
+      before_script:
       script: ci/test-documentation.sh
       after_failure:
 
-before_install:
-  - >
-    case "${TRAVIS_OS_NAME:-linux}" in
-    linux)
-      export GIT_TEST_HTTPD=YesPlease
-
-      mkdir --parents custom/p4
-      pushd custom/p4
-        wget --quiet http://filehost.perforce.com/perforce/r$LINUX_P4_VERSION/bin.linux26x86_64/p4d
-        wget --quiet http://filehost.perforce.com/perforce/r$LINUX_P4_VERSION/bin.linux26x86_64/p4
-        chmod u+x p4d
-        chmod u+x p4
-        export PATH="$(pwd):$PATH"
-      popd
-      mkdir --parents custom/git-lfs
-      pushd custom/git-lfs
-        wget --quiet https://github.com/github/git-lfs/releases/download/v$LINUX_GIT_LFS_VERSION/git-lfs-linux-amd64-$LINUX_GIT_LFS_VERSION.tar.gz
-        tar --extract --gunzip --file "git-lfs-linux-amd64-$LINUX_GIT_LFS_VERSION.tar.gz"
-        cp git-lfs-$LINUX_GIT_LFS_VERSION/git-lfs .
-        export PATH="$(pwd):$PATH"
-      popd
-      ;;
-    osx)
-      brew update --quiet
-      # Uncomment this if you want to run perf tests:
-      # brew install gnu-time
-      brew install git-lfs gettext
-      brew link --force gettext
-      brew install caskroom/cask/perforce
-      ;;
-    esac;
-    echo "$(tput setaf 6)Perforce Server Version$(tput sgr0)";
-    p4d -V | grep Rev.;
-    echo "$(tput setaf 6)Perforce Client Version$(tput sgr0)";
-    p4 -V | grep Rev.;
-    echo "$(tput setaf 6)Git-LFS Version$(tput sgr0)";
-    git-lfs version;
-
-before_script: make --jobs=2
-
-script:
-  - >
-    mkdir -p $HOME/travis-cache;
-    ln -s $HOME/travis-cache/.prove t/.prove;
-    make --quiet test;
-
-after_failure:
-  - >
-    : '<-- Click here to see detailed test output!                                                        ';
-    for TEST_EXIT in t/test-results/*.exit;
-    do
-      if [ "$(cat "$TEST_EXIT")" != "0" ];
-      then
-        TEST_OUT="${TEST_EXIT%exit}out";
-        echo "------------------------------------------------------------------------";
-        echo "$(tput setaf 1)${TEST_OUT}...$(tput sgr0)";
-        echo "------------------------------------------------------------------------";
-        cat "${TEST_OUT}";
-      fi;
-    done;
+before_install: ci/install-dependencies.sh
+before_script: ci/run-build.sh
+script: ci/run-tests.sh
+after_failure: ci/print-test-failures.sh
 
 notifications:
   email: false
index 204541c690ce24bdf5b6e20baa0ddca928920f71..fb09cd69d63fec03ad1987970fdbdffe8d8a4339 100644 (file)
@@ -192,7 +192,7 @@ newline. The available atoms are:
        The 40-hex object name of the object.
 
 `objecttype`::
-       The type of of the object (the same as `cat-file -t` reports).
+       The type of the object (the same as `cat-file -t` reports).
 
 `objectsize`::
        The size, in bytes, of the object (the same as `cat-file -s`
index d6399c0af86bb84cd86b82500ce706cbcd93cd93..bd268a8fccc32a1d218b30d58a688353a0fe236f 100644 (file)
@@ -38,7 +38,7 @@ $ git checkout -b <branch> --track <remote>/<branch>
 ------------
 +
 You could omit <branch>, in which case the command degenerates to
-"check out the current branch", which is a glorified no-op with a
+"check out the current branch", which is a glorified no-op with
 rather expensive side-effects to show only the tracking information,
 if exists, for the current branch.
 
index bb370c9c7b91425ad939383d8b28c5fbbb73cbac..66b4e0a4050655e7fab27a6fe72af03d6c99f3b2 100644 (file)
@@ -10,8 +10,9 @@ SYNOPSIS
 [verse]
 'git for-each-ref' [--count=<count>] [--shell|--perl|--python|--tcl]
                   [(--sort=<key>)...] [--format=<format>] [<pattern>...]
-                  [--points-at <object>] [(--merged | --no-merged) [<object>]]
-                  [--contains [<object>]] [--no-contains [<object>]]
+                  [--points-at=<object>]
+                  (--merged[=<object>] | --no-merged[=<object>])
+                  [--contains[=<object>]] [--no-contains[=<object>]]
 
 DESCRIPTION
 -----------
@@ -25,19 +26,25 @@ host language allowing their direct evaluation in that language.
 
 OPTIONS
 -------
-<count>::
+<pattern>...::
+       If one or more patterns are given, only refs are shown that
+       match against at least one pattern, either using fnmatch(3) or
+       literally, in the latter case matching completely or from the
+       beginning up to a slash.
+
+--count=<count>::
        By default the command shows all refs that match
        `<pattern>`.  This option makes it stop after showing
        that many refs.
 
-<key>::
+--sort=<key>::
        A field name to sort on.  Prefix `-` to sort in
        descending order of the value.  When unspecified,
        `refname` is used.  You may use the --sort=<key> option
        multiple times, in which case the last key becomes the primary
        key.
 
-<format>::
+--format=<format>::
        A string that interpolates `%(fieldname)` from a ref being shown
        and the object it points at.  If `fieldname`
        is prefixed with an asterisk (`*`) and the ref points
@@ -50,12 +57,6 @@ OPTIONS
        `xx`; for example `%00` interpolates to `\0` (NUL),
        `%09` to `\t` (TAB) and `%0a` to `\n` (LF).
 
-<pattern>...::
-       If one or more patterns are given, only refs are shown that
-       match against at least one pattern, either using fnmatch(3) or
-       literally, in the latter case matching completely or from the
-       beginning up to a slash.
-
 --shell::
 --perl::
 --python::
@@ -65,24 +66,24 @@ OPTIONS
        the specified host language.  This is meant to produce
        a scriptlet that can directly be `eval`ed.
 
---points-at <object>::
+--points-at=<object>::
        Only list refs which points at the given object.
 
---merged [<object>]::
+--merged[=<object>]::
        Only list refs whose tips are reachable from the
        specified commit (HEAD if not specified),
        incompatible with `--no-merged`.
 
---no-merged [<object>]::
+--no-merged[=<object>]::
        Only list refs whose tips are not reachable from the
        specified commit (HEAD if not specified),
        incompatible with `--merged`.
 
---contains [<object>]::
+--contains[=<object>]::
        Only list refs which contain the specified commit (HEAD if not
        specified).
 
---no-contains [<object>]::
+--no-contains[=<object>]::
        Only list refs which don't contain the specified commit (HEAD
        if not specified).
 
index be7db3048d4776200c6ed8781b25ffccb68a41eb..43677297f3b85814c18c972a159b81c8076bc445 100644 (file)
@@ -171,7 +171,7 @@ OPTIONS
        object that does not have notes attached to it.
 
 --stdin::
-       Also read the object names to remove notes from from the standard
+       Also read the object names to remove notes from the standard
        input (there is no reason you cannot combine this with object
        names from the command line).
 
index e19eba62cda292cc51407cbcd7e94675560f97f2..75c7dd9dea4d2b6c26b84b99fb21ba75cf1cdcf3 100644 (file)
@@ -153,7 +153,7 @@ you will need to handle the situation manually.
 +
 Version 4 performs a simple pathname compression that reduces index
 size by 30%-50% on large repositories, which results in faster load
-time. Version 4 is relatively young (first released in in 1.8.0 in
+time. Version 4 is relatively young (first released in 1.8.0 in
 October 2012). Other Git implementations such as JGit and libgit2
 may not support it yet.
 
index c4f2be2542d1fcd417c69596dafdf89bc3a93f00..4c68bc19d5108d1ba0c91b05d35acf8060eb2c9d 100644 (file)
@@ -151,7 +151,10 @@ unspecified.
 
 This attribute sets a specific line-ending style to be used in the
 working directory.  It enables end-of-line conversion without any
-content checks, effectively setting the `text` attribute.
+content checks, effectively setting the `text` attribute.  Note that
+setting this attribute on paths which are in the index with CRLF line
+endings may make the paths to be considered dirty.  Adding the path to
+the index again will normalize the line endings in the index.
 
 Set to string value "crlf"::
 
index a6cf9eb380fabedd0e89b84d182e6e2c4798e503..7d860bfca1442e7957c41b7a7a5cd6f82141ed7e 100644 (file)
@@ -184,6 +184,14 @@ explicitly.
        Pretend as if all objects mentioned by reflogs are listed on the
        command line as `<commit>`.
 
+--single-worktree::
+       By default, all working trees will be examined by the
+       following options when there are more than one (see
+       linkgit:git-worktree[1]): `--all`, `--reflog` and
+       `--indexed-objects`.
+       This option forces them to examine the current working tree
+       only.
+
 --ignore-missing::
        Upon seeing an invalid object name in the input, pretend as if
        the bad input was not given.
index 7a83a3a6e2c8fde8754fa9be4e1b218beca6bc89..9a778b0cad02faab3ce6bd7f89c24a17de45b777 100644 (file)
@@ -186,10 +186,6 @@ parsing is successful, the return value is the result.
 Same as `git_config_bool`, except that integers are returned as-is, and
 an `is_bool` flag is unset.
 
-`git_config_maybe_bool`::
-Deprecated. Use `git_parse_maybe_bool` instead. They are exactly the
-same, except this function takes an unused argument `name`.
-
 `git_parse_maybe_bool`::
 Same as `git_config_bool`, except that it returns -1 on error rather
 than dying.
index 37379d83376fc2342d1a81570b3de86b8e7dcd49..46c3d5c3550a1ba7c83b6937fe68538c3c12074e 100644 (file)
@@ -32,11 +32,8 @@ Iteration functions
 
 * `for_each_glob_ref_in()` the previous and `for_each_ref_in()` combined.
 
-* `head_ref_submodule()`, `for_each_ref_submodule()`,
-  `for_each_ref_in_submodule()`, `for_each_tag_ref_submodule()`,
-  `for_each_branch_ref_submodule()`, `for_each_remote_ref_submodule()`
-  do the same as the functions described above but for a specified
-  submodule.
+* Use `refs_` API for accessing submodules. The submodule ref store could
+  be obtained with `get_submodule_ref_store()`.
 
 * `for_each_rawref()` can be used to learn about broken ref and symref.
 
index f2bb7f2f63e458af6261b4a151d87ef9e973222e..ed5960e6b378a982fb770f428324ba8fec0ba8c6 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -162,6 +162,11 @@ all::
 # algorithm. This is slower, but may detect attempted collision attacks.
 # Takes priority over other *_SHA1 knobs.
 #
+# Define DC_SHA1_EXTERNAL in addition to DC_SHA1 if you want to build / link
+# git with the external SHA1 collision-detect library.
+# Without this option, i.e. the default behavior is to build git with its
+# own built-in code (or submodule).
+#
 # Define DC_SHA1_SUBMODULE in addition to DC_SHA1 to use the
 # sha1collisiondetection shipped as a submodule instead of the
 # non-submodule copy in sha1dc/. This is an experimental option used
@@ -1036,6 +1041,9 @@ BASIC_CFLAGS += -fno-omit-frame-pointer
 ifneq ($(filter undefined,$(SANITIZERS)),)
 BASIC_CFLAGS += -DNO_UNALIGNED_LOADS
 endif
+ifneq ($(filter leak,$(SANITIZERS)),)
+BASIC_CFLAGS += -DSUPPRESS_ANNOTATED_LEAKS
+endif
 endif
 
 ifndef sysconfdir
@@ -1475,6 +1483,15 @@ ifdef APPLE_COMMON_CRYPTO
        BASIC_CFLAGS += -DSHA1_APPLE
 else
        DC_SHA1 := YesPlease
+       BASIC_CFLAGS += -DSHA1_DC
+       LIB_OBJS += sha1dc_git.o
+ifdef DC_SHA1_EXTERNAL
+       ifdef DC_SHA1_SUBMODULE
+$(error Only set DC_SHA1_EXTERNAL or DC_SHA1_SUBMODULE, not both)
+       endif
+       BASIC_CFLAGS += -DDC_SHA1_EXTERNAL
+       EXTLIBS += -lsha1detectcoll
+else
 ifdef DC_SHA1_SUBMODULE
        LIB_OBJS += sha1collisiondetection/lib/sha1.o
        LIB_OBJS += sha1collisiondetection/lib/ubc_check.o
@@ -1484,17 +1501,15 @@ else
        LIB_OBJS += sha1dc/ubc_check.o
 endif
        BASIC_CFLAGS += \
-               -DSHA1_DC \
                -DSHA1DC_NO_STANDARD_INCLUDES \
                -DSHA1DC_INIT_SAFE_HASH_DEFAULT=0 \
                -DSHA1DC_CUSTOM_INCLUDE_SHA1_C="\"cache.h\"" \
-               -DSHA1DC_CUSTOM_TRAILING_INCLUDE_SHA1_C="\"sha1dc_git.c\"" \
-               -DSHA1DC_CUSTOM_TRAILING_INCLUDE_SHA1_H="\"sha1dc_git.h\"" \
                -DSHA1DC_CUSTOM_INCLUDE_UBC_CHECK_C="\"git-compat-util.h\""
 endif
 endif
 endif
 endif
+endif
 
 ifdef SHA1_MAX_BLOCK_SIZE
        LIB_OBJS += compat/sha1-chunked.o
index 1ab8d3a1d7cc9c3e55465fbadf899e8a267de689..1e41f4bbeb1d8730da83aad12bc8987abedcad8a 100644 (file)
--- a/archive.c
+++ b/archive.c
@@ -121,11 +121,6 @@ static int check_attr_export_subst(const struct attr_check *check)
        return check && ATTR_TRUE(check->items[1].value);
 }
 
-static int should_queue_directories(const struct archiver_args *args)
-{
-       return args->pathspec.has_wildcard;
-}
-
 static int write_archive_entry(const unsigned char *sha1, const char *base,
                int baselen, const char *filename, unsigned mode, int stage,
                void *context)
@@ -147,7 +142,7 @@ static int write_archive_entry(const unsigned char *sha1, const char *base,
                strbuf_addch(&path, '/');
        path_without_prefix = path.buf + args->baselen;
 
-       if (!S_ISDIR(mode) || !should_queue_directories(args)) {
+       if (!S_ISDIR(mode)) {
                const struct attr_check *check;
                check = get_archive_attrs(path_without_prefix);
                if (check_attr_export_ignore(check))
@@ -169,14 +164,6 @@ static int write_archive_entry(const unsigned char *sha1, const char *base,
        return write_entry(args, sha1, path.buf, path.len, mode);
 }
 
-static int write_archive_entry_buf(const unsigned char *sha1, struct strbuf *base,
-               const char *filename, unsigned mode, int stage,
-               void *context)
-{
-       return write_archive_entry(sha1, base->buf, base->len,
-                                    filename, mode, stage, context);
-}
-
 static void queue_directory(const unsigned char *sha1,
                struct strbuf *base, const char *filename,
                unsigned mode, int stage, struct archiver_context *c)
@@ -290,9 +277,7 @@ int write_archive_entries(struct archiver_args *args,
        }
 
        err = read_tree_recursive(args->tree, "", 0, 0, &args->pathspec,
-                                 should_queue_directories(args) ?
-                                 queue_or_write_archive_entry :
-                                 write_archive_entry_buf,
+                                 queue_or_write_archive_entry,
                                  &context);
        if (err == READ_TREE_RECURSIVE)
                err = 0;
diff --git a/attr.c b/attr.c
index 2f49151736095cf2e2a76ea91d72743df3778a5d..dfc3a558d83737921e1f97c96886539ef5fc3acb 100644 (file)
--- a/attr.c
+++ b/attr.c
@@ -151,10 +151,12 @@ struct all_attrs_item {
 static void all_attrs_init(struct attr_hashmap *map, struct attr_check *check)
 {
        int i;
+       unsigned int size;
 
        hashmap_lock(map);
 
-       if (map->map.size < check->all_attrs_nr)
+       size = hashmap_get_size(&map->map);
+       if (size < check->all_attrs_nr)
                die("BUG: interned attributes shouldn't be deleted");
 
        /*
@@ -163,13 +165,13 @@ static void all_attrs_init(struct attr_hashmap *map, struct attr_check *check)
         * field), reallocate the provided attr_check instance's all_attrs
         * field and fill each entry with its corresponding git_attr.
         */
-       if (map->map.size != check->all_attrs_nr) {
+       if (size != check->all_attrs_nr) {
                struct attr_hash_entry *e;
                struct hashmap_iter iter;
                hashmap_iter_init(&map->map, &iter);
 
-               REALLOC_ARRAY(check->all_attrs, map->map.size);
-               check->all_attrs_nr = map->map.size;
+               REALLOC_ARRAY(check->all_attrs, size);
+               check->all_attrs_nr = size;
 
                while ((e = hashmap_iter_next(&iter))) {
                        const struct git_attr *a = e->value;
@@ -237,10 +239,11 @@ static const struct git_attr *git_attr_internal(const char *name, int namelen)
 
        if (!a) {
                FLEX_ALLOC_MEM(a, name, name, namelen);
-               a->attr_nr = g_attr_hashmap.map.size;
+               a->attr_nr = hashmap_get_size(&g_attr_hashmap.map);
 
                attr_hashmap_add(&g_attr_hashmap, a->name, namelen, a);
-               assert(a->attr_nr == (g_attr_hashmap.map.size - 1));
+               assert(a->attr_nr ==
+                      (hashmap_get_size(&g_attr_hashmap.map) - 1));
        }
 
        hashmap_unlock(&g_attr_hashmap);
index c20548e4f545116c6e3bff75e59e953be603cc2a..a648cf4c56c9f56eb60a97ca50ccf0b191dc50ff 100644 (file)
@@ -119,6 +119,7 @@ int add_files_to_cache(const char *prefix,
        rev.diffopt.flags |= DIFF_OPT_OVERRIDE_SUBMODULE_CONFIG;
        rev.max_count = 0; /* do not compare unmerged paths with stage #2 */
        run_diff_files(&rev, DIFF_RACY_IS_MODIFIED);
+       clear_pathspec(&rev.prune_data);
        return !!data.add_errors;
 }
 
@@ -514,5 +515,7 @@ int cmd_add(int argc, const char **argv, const char *prefix)
                        die(_("Unable to write new index file"));
        }
 
+       UNLEAK(pathspec);
+       UNLEAK(dir);
        return exit_status;
 }
index c369dd1dce2d705206414d0bbf5a4415d9487cab..d7513f53759bc5834aa02bd904432f64e036ccb6 100644 (file)
@@ -671,9 +671,7 @@ static int detect_patch_format(const char **paths)
                goto done;
        }
 
-       strbuf_reset(&l2);
        strbuf_getline(&l2, fp);
-       strbuf_reset(&l3);
        strbuf_getline(&l3, fp);
 
        /*
@@ -696,6 +694,8 @@ static int detect_patch_format(const char **paths)
 done:
        fclose(fp);
        strbuf_release(&l1);
+       strbuf_release(&l2);
+       strbuf_release(&l3);
        return ret;
 }
 
@@ -881,6 +881,7 @@ static int split_mail_stgit_series(struct am_state *state, const char **paths,
 static int hg_patch_to_mail(FILE *out, FILE *in, int keep_cr)
 {
        struct strbuf sb = STRBUF_INIT;
+       int rc = 0;
 
        while (!strbuf_getline_lf(&sb, in)) {
                const char *str;
@@ -894,19 +895,27 @@ static int hg_patch_to_mail(FILE *out, FILE *in, int keep_cr)
 
                        errno = 0;
                        timestamp = parse_timestamp(str, &end, 10);
-                       if (errno)
-                               return error(_("invalid timestamp"));
+                       if (errno) {
+                               rc = error(_("invalid timestamp"));
+                               goto exit;
+                       }
 
-                       if (!skip_prefix(end, " ", &str))
-                               return error(_("invalid Date line"));
+                       if (!skip_prefix(end, " ", &str)) {
+                               rc = error(_("invalid Date line"));
+                               goto exit;
+                       }
 
                        errno = 0;
                        tz = strtol(str, &end, 10);
-                       if (errno)
-                               return error(_("invalid timezone offset"));
+                       if (errno) {
+                               rc = error(_("invalid timezone offset"));
+                               goto exit;
+                       }
 
-                       if (*end)
-                               return error(_("invalid Date line"));
+                       if (*end) {
+                               rc = error(_("invalid Date line"));
+                               goto exit;
+                       }
 
                        /*
                         * mercurial's timezone is in seconds west of UTC,
@@ -931,9 +940,9 @@ static int hg_patch_to_mail(FILE *out, FILE *in, int keep_cr)
                fwrite(sb.buf, 1, sb.len, out);
                strbuf_reset(&sb);
        }
-
+exit:
        strbuf_release(&sb);
-       return 0;
+       return rc;
 }
 
 /**
@@ -2096,6 +2105,7 @@ static int safe_to_abort(const struct am_state *state)
                        die(_("could not parse %s"), am_path(state, "abort-safety"));
        } else
                oidclr(&abort_safety);
+       strbuf_release(&sb);
 
        if (get_oid("HEAD", &head))
                oidclr(&head);
index eac499450f63554387fb8b32ef5780c32ded4a21..6c40ff110bee7387d4eb4e9c7c04a21e8b12ca44 100644 (file)
@@ -45,6 +45,7 @@ static int check_ref_format_branch(const char *arg)
        if (strbuf_check_branch_ref(&sb, arg))
                die("'%s' is not a valid branch name", arg);
        printf("%s\n", sb.buf + 11);
+       strbuf_release(&sb);
        return 0;
 }
 
index 21a7a3299408ac507917ac13f9342e3148ac53ec..733b6d3745ee5780c697323372f1e78e28b8a117 100644 (file)
@@ -167,7 +167,7 @@ static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag,
                }
 
                *dir_gone = 0;
-               return 0;
+               goto out;
        }
 
        dir = opendir(path->buf);
@@ -181,7 +181,8 @@ static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag,
                        warning_errno(_(msg_warn_remove_failed), quoted.buf);
                        *dir_gone = 0;
                }
-               return res;
+               ret = res;
+               goto out;
        }
 
        strbuf_complete(path, '/');
@@ -249,6 +250,8 @@ static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag,
                for (i = 0; i < dels.nr; i++)
                        printf(dry_run ?  _(msg_would_remove) : _(msg_remove), dels.items[i].string);
        }
+out:
+       strbuf_release(&quoted);
        string_list_clear(&dels, 0);
        return ret;
 }
index 8d11b570a1ca43eae3224ade805352b8a447423e..dbddd98f80d6660f2cde190a0e121022f079071d 100644 (file)
@@ -507,8 +507,8 @@ static void remove_junk(void)
        if (junk_work_tree) {
                strbuf_addstr(&sb, junk_work_tree);
                remove_dir_recursively(&sb, 0);
-               strbuf_reset(&sb);
        }
+       strbuf_release(&sb);
 }
 
 static void remove_junk_on_signal(int signo)
index 19e898fa4e46da9271de950ad9add75af2051ed6..2177251e247cc144d6ac8619fa5940262430d50a 100644 (file)
@@ -102,7 +102,6 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix)
                        if (fd && close(fd))
                                die_errno("git commit-tree: failed to close '%s'",
                                          argv[i]);
-                       strbuf_complete_line(&buffer);
                        continue;
                }
 
index b3b04f5dd3a94d1661e877c5019cc56ac46854ef..58f9747c2f2d99d2ab4d78f62f5e9c0928a12f1f 100644 (file)
@@ -1818,6 +1818,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
        if (!quiet)
                print_summary(prefix, &oid, !current_head);
 
-       strbuf_release(&err);
+       UNLEAK(err);
+       UNLEAK(sb);
        return 0;
 }
index 70ff231e9c5bce550e7af20ddfe91b8b68ee32ae..d13daeeb55927758ceec816f39412123d2ce5846 100644 (file)
@@ -518,10 +518,13 @@ int cmd_config(int argc, const char **argv, const char *prefix)
                        die("$HOME not set");
 
                if (access_or_warn(user_config, R_OK, 0) &&
-                   xdg_config && !access_or_warn(xdg_config, R_OK, 0))
+                   xdg_config && !access_or_warn(xdg_config, R_OK, 0)) {
                        given_config_source.file = xdg_config;
-               else
+                       free(user_config);
+               } else {
                        given_config_source.file = user_config;
+                       free(xdg_config);
+               }
        }
        else if (use_system_config)
                given_config_source.file = git_etc_gitconfig();
@@ -628,6 +631,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)
                check_write();
                check_argc(argc, 2, 2);
                value = normalize_value(argv[0], argv[1]);
+               UNLEAK(value);
                ret = git_config_set_in_file_gently(given_config_source.file, argv[0], value);
                if (ret == CONFIG_NOTHING_SET)
                        error(_("cannot overwrite multiple values with a single value\n"
@@ -638,6 +642,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)
                check_write();
                check_argc(argc, 2, 3);
                value = normalize_value(argv[0], argv[1]);
+               UNLEAK(value);
                return git_config_set_multivar_in_file_gently(given_config_source.file,
                                                              argv[0], value, argv[2], 0);
        }
@@ -645,6 +650,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)
                check_write();
                check_argc(argc, 2, 2);
                value = normalize_value(argv[0], argv[1]);
+               UNLEAK(value);
                return git_config_set_multivar_in_file_gently(given_config_source.file,
                                                              argv[0], value,
                                                              CONFIG_REGEX_NONE, 0);
@@ -653,6 +659,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)
                check_write();
                check_argc(argc, 2, 3);
                value = normalize_value(argv[0], argv[1]);
+               UNLEAK(value);
                return git_config_set_multivar_in_file_gently(given_config_source.file,
                                                              argv[0], value, argv[2], 1);
        }
index 9c13c6817bd784fb9c0e3eff781911577ef7ef8c..e77163e909d07ec168d41d8b96e05afa73490848 100644 (file)
@@ -508,7 +508,7 @@ int cmd_describe(int argc, const char **argv, const char *prefix)
 
        hashmap_init(&names, commit_name_cmp, NULL, 0);
        for_each_rawref(get_name, NULL);
-       if (!names.size && !always)
+       if (!hashmap_get_size(&names) && !always)
                die(_("No names found, cannot describe anything."));
 
        if (argc == 0) {
index 3c78fcb9b1a60b92a1a848ebc8aba01ec06feca9..c22787ac723c421b17032ecf0b718ff434203c86 100644 (file)
@@ -47,7 +47,7 @@ static struct argv_array prune = ARGV_ARRAY_INIT;
 static struct argv_array prune_worktrees = ARGV_ARRAY_INIT;
 static struct argv_array rerere = ARGV_ARRAY_INIT;
 
-static struct tempfile pidfile;
+static struct tempfile *pidfile;
 static struct lock_file log_lock;
 
 static struct string_list pack_garbage = STRING_LIST_INIT_DUP;
@@ -78,7 +78,7 @@ static void process_log_file(void)
                 */
                int saved_errno = errno;
                fprintf(stderr, _("Failed to fstat %s: %s"),
-                       get_tempfile_path(&log_lock.tempfile),
+                       get_tempfile_path(log_lock.tempfile),
                        strerror(saved_errno));
                fflush(stderr);
                commit_lock_file(&log_lock);
@@ -242,7 +242,7 @@ static const char *lock_repo_for_gc(int force, pid_t* ret_pid)
        int fd;
        char *pidfile_path;
 
-       if (is_tempfile_active(&pidfile))
+       if (is_tempfile_active(pidfile))
                /* already locked */
                return NULL;
 
@@ -293,7 +293,7 @@ static const char *lock_repo_for_gc(int force, pid_t* ret_pid)
        write_in_full(fd, sb.buf, sb.len);
        strbuf_release(&sb);
        commit_lock_file(&lock);
-       register_tempfile(&pidfile, pidfile_path);
+       pidfile = register_tempfile(pidfile_path);
        free(pidfile_path);
        return NULL;
 }
index e21c5416cd478eab3b0dddcba5ca9eeefad83d34..6d9a79f9b39834d953685b2641354915df3cfcbe 100644 (file)
@@ -33,8 +33,7 @@ int cmd_get_tar_commit_id(int argc, const char **argv, const char *prefix)
        if (!skip_prefix(content, "52 comment=", &comment))
                return 1;
 
-       n = write_in_full(1, comment, 41);
-       if (n < 41)
+       if (write_in_full(1, comment, 41) < 0)
                die_errno("git get-tar-commit-id: write error");
 
        return 0;
index 334a8494abcc4ac52babf468d8a1ef415aa7f297..d3c8fc40820faa10f7c9004560159986b2900e9e 100644 (file)
@@ -131,6 +131,7 @@ static void exec_woman_emacs(const char *path, const char *page)
                strbuf_addf(&man_page, "(woman \"%s\")", page);
                execlp(path, "emacsclient", "-e", man_page.buf, (char *)NULL);
                warning_errno(_("failed to exec '%s'"), path);
+               strbuf_release(&man_page);
        }
 }
 
@@ -152,6 +153,7 @@ static void exec_man_konqueror(const char *path, const char *page)
                strbuf_addf(&man_page, "man:%s(1)", page);
                execlp(path, filename, "newTab", man_page.buf, (char *)NULL);
                warning_errno(_("failed to exec '%s'"), path);
+               strbuf_release(&man_page);
        }
 }
 
@@ -169,6 +171,7 @@ static void exec_man_cmd(const char *cmd, const char *page)
        strbuf_addf(&shell_cmd, "%s %s", cmd, page);
        execl(SHELL_PATH, SHELL_PATH, "-c", shell_cmd.buf, (char *)NULL);
        warning(_("failed to exec '%s'"), cmd);
+       strbuf_release(&shell_cmd);
 }
 
 static void add_man_viewer(const char *name)
@@ -438,7 +441,7 @@ static const char *check_git_cmd(const char* cmd)
 
        alias = alias_lookup(cmd);
        if (alias) {
-               printf_ln(_("`git %s' is aliased to `%s'"), cmd, alias);
+               printf_ln(_("'%s' is aliased to '%s'"), cmd, alias);
                free(alias);
                exit(0);
        }
index 47823f9aa4452edfa684b042dd5fbaa935df1d39..c9b7946bade9e10f799942137480e71ee3233701 100644 (file)
@@ -579,6 +579,8 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
                        set_git_work_tree(work_tree);
        }
 
+       UNLEAK(real_git_dir);
+
        flags |= INIT_DB_EXIST_OK;
        return init_db(git_dir, real_git_dir, template_dir, flags);
 }
index e1339e6d17d2bf592b800d3e5a686cbc91c12331..8c713c47acccf0a3a50ec8a0b822aafae8d6bada 100644 (file)
@@ -673,5 +673,6 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
                return bad ? 1 : 0;
        }
 
+       UNLEAK(dir);
        return 0;
 }
index 3672e38974f094831c305d509b9582c146f94236..ab5ffe85e8f5d7a359004f2bbb52a5778d0b91b4 100644 (file)
@@ -73,6 +73,7 @@ static int show_progress = -1;
 static int default_to_upstream = 1;
 static int signoff;
 static const char *sign_commit;
+static int verify_msg = 1;
 
 static struct strategy all_strategy[] = {
        { "recursive",  DEFAULT_TWOHEAD | NO_TRIVIAL },
@@ -236,6 +237,7 @@ static struct option builtin_merge_options[] = {
          N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
        OPT_BOOL(0, "overwrite-ignore", &overwrite_ignore, N_("update ignored files (default)")),
        OPT_BOOL(0, "signoff", &signoff, N_("add Signed-off-by:")),
+       OPT_BOOL(0, "verify", &verify_msg, N_("verify commit-msg hook")),
        OPT_END()
 };
 
@@ -253,6 +255,7 @@ static int save_state(struct object_id *stash)
        struct child_process cp = CHILD_PROCESS_INIT;
        struct strbuf buffer = STRBUF_INIT;
        const char *argv[] = {"stash", "create", NULL};
+       int rc = -1;
 
        cp.argv = argv;
        cp.out = -1;
@@ -266,11 +269,14 @@ static int save_state(struct object_id *stash)
        if (finish_command(&cp) || len < 0)
                die(_("stash failed"));
        else if (!len)          /* no changes */
-               return -1;
+               goto out;
        strbuf_setlen(&buffer, buffer.len-1);
        if (get_oid(buffer.buf, stash))
                die(_("not a valid object: %s"), buffer.buf);
-       return 0;
+       rc = 0;
+out:
+       strbuf_release(&buffer);
+       return rc;
 }
 
 static void read_empty(unsigned const char *sha1, int verbose)
@@ -780,6 +786,12 @@ static void prepare_to_commit(struct commit_list *remoteheads)
                if (launch_editor(git_path_merge_msg(), NULL, NULL))
                        abort_commit(remoteheads, NULL);
        }
+
+       if (verify_msg && run_commit_hook(0 < option_edit, get_index_file(),
+                                         "commit-msg",
+                                         git_path_merge_msg(), NULL))
+               abort_commit(remoteheads, NULL);
+
        read_merge_msg(&msg);
        strbuf_stripspace(&msg, 0 < option_edit);
        if (!msg.len)
@@ -934,6 +946,7 @@ static void write_merge_heads(struct commit_list *remoteheads)
        if (fast_forward == FF_NO)
                strbuf_addstr(&buf, "no-ff");
        write_file_buf(git_path_merge_mode(), buf.buf, buf.len);
+       strbuf_release(&buf);
 }
 
 static void write_merge_state(struct commit_list *remoteheads)
index c41ea7c2a62ba868061884e511256c24f4d020d9..598da6c8bc75e9de50f85eac78d3051f27f96a6c 100644 (file)
@@ -253,7 +253,7 @@ static int name_ref(const char *path, const struct object_id *oid, int flags, vo
                struct commit *commit = (struct commit *)o;
                int from_tag = starts_with(path, "refs/tags/");
 
-               if (taggerdate == ULONG_MAX)
+               if (taggerdate == TIME_MAX)
                        taggerdate = ((struct commit *)o)->date;
                path = name_ref_abbrev(path, can_abbreviate_output);
                name_rev(commit, xstrdup(path), taggerdate, 0, 0,
index 4303848e044c9f9c6a12236d887a521316b4fc05..8e54f2d14648bcf41369e71c2f7f586322c702ef 100644 (file)
@@ -328,6 +328,7 @@ static int notes_copy_from_stdin(int force, const char *rewrite_cmd)
        } else {
                finish_copy_notes_for_rewrite(c, msg);
        }
+       strbuf_release(&buf);
        return ret;
 }
 
index a57b4f058dff3ceec80355560bf6a856f130b8c0..f721137eaf88143aa2ae3f8c67b97fbceccbb6cf 100644 (file)
@@ -1012,7 +1012,7 @@ static int want_object_in_pack(const unsigned char *sha1,
                        return want;
        }
 
-       for (entry = packed_git_mru->head; entry; entry = entry->next) {
+       for (entry = packed_git_mru.head; entry; entry = entry->next) {
                struct packed_git *p = entry->item;
                off_t offset;
 
@@ -1030,7 +1030,7 @@ static int want_object_in_pack(const unsigned char *sha1,
                        }
                        want = want_found_object(exclude, p);
                        if (!exclude && want > 0)
-                               mru_mark(packed_git_mru, entry);
+                               mru_mark(&packed_git_mru, entry);
                        if (want != -1)
                                return want;
                }
index 7fe281414eceaae64926caca2b6194c9fcfef299..6f772e8a224c18238cf9a75436e8ea192765e7fe 100644 (file)
@@ -325,6 +325,10 @@ static int git_pull_config(const char *var, const char *value, void *cb)
        if (!strcmp(var, "rebase.autostash")) {
                config_autostash = git_config_bool(var, value);
                return 0;
+       } else if (!strcmp(var, "submodule.recurse")) {
+               recurse_submodules = git_config_bool(var, value) ?
+                       RECURSE_SUBMODULES_ON : RECURSE_SUBMODULES_OFF;
+               return 0;
        }
        return git_default_config(var, value, cb);
 }
@@ -815,6 +819,8 @@ int cmd_pull(int argc, const char **argv, const char *prefix)
        if (!getenv("GIT_REFLOG_ACTION"))
                set_reflog_message(argc, argv);
 
+       git_config(git_pull_config, NULL);
+
        argc = parse_options(argc, argv, prefix, pull_options, pull_usage, 0);
 
        parse_repo_refspecs(argc, argv, &repo, &refspecs);
@@ -825,8 +831,6 @@ int cmd_pull(int argc, const char **argv, const char *prefix)
        if (opt_rebase < 0)
                opt_rebase = config_get_rebase();
 
-       git_config(git_pull_config, NULL);
-
        if (read_cache_unmerged())
                die_resolve_conflict("pull");
 
index 52c63ebfdc737688a7d99f6be34c43cf80b1d011..dd06b3fb4f9ee3e4da46d807b183541ec14c5481 100644 (file)
@@ -743,7 +743,7 @@ static int run_and_feed_hook(const char *hook_name, feed_fn feed,
                size_t n;
                if (feed(feed_state, &buf, &n))
                        break;
-               if (write_in_full(proc.in, buf, n) != n)
+               if (write_in_full(proc.in, buf, n) < 0)
                        break;
        }
        close(proc.in);
index a995ea86c17474be248a974469f7d535c942fc70..33ba73933271b51986112107b97b4846e4a913b0 100644 (file)
@@ -571,6 +571,7 @@ static int read_remote_branches(const char *refname,
                else
                        item->util = NULL;
        }
+       strbuf_release(&buf);
 
        return 0;
 }
@@ -595,6 +596,7 @@ static int migrate_file(struct remote *remote)
                unlink_or_warn(git_path("remotes/%s", remote->name));
        else if (remote->origin == REMOTE_BRANCHES)
                unlink_or_warn(git_path("branches/%s", remote->name));
+       strbuf_release(&buf);
 
        return 0;
 }
@@ -1563,9 +1565,7 @@ static int set_url(int argc, const char **argv)
                                                       "^$", 0);
                else
                        git_config_set(name_buf.buf, newurl);
-               strbuf_release(&name_buf);
-
-               return 0;
+               goto out;
        }
 
        /* Old URL specified. Demand that one matches. */
@@ -1588,6 +1588,8 @@ static int set_url(int argc, const char **argv)
                git_config_set_multivar(name_buf.buf, newurl, oldurl, 0);
        else
                git_config_set_multivar(name_buf.buf, NULL, oldurl, 1);
+out:
+       strbuf_release(&name_buf);
        return 0;
 }
 
index ffb66e29073c07c43197ae34f2c31d3b9b610903..0bc40298c2417a6d1f25aafb4b3577243e985c57 100644 (file)
@@ -18,7 +18,7 @@ static int outf(void *dummy, mmbuffer_t *ptr, int nbuf)
 {
        int i;
        for (i = 0; i < nbuf; i++)
-               if (write_in_full(1, ptr[i].ptr, ptr[i].size) != ptr[i].size)
+               if (write_in_full(1, ptr[i].ptr, ptr[i].size) < 0)
                        return -1;
        return 0;
 }
index d72c7d1c96b7a7da5c1aaee80d36c5b4acdb2200..9cd89b23059f768c4651725f2c26c5b08c35f650 100644 (file)
@@ -44,10 +44,11 @@ static inline int is_merge(void)
 
 static int reset_index(const struct object_id *oid, int reset_type, int quiet)
 {
-       int nr = 1;
+       int i, nr = 0;
        struct tree_desc desc[2];
        struct tree *tree;
        struct unpack_trees_options opts;
+       int ret = -1;
 
        memset(&opts, 0, sizeof(opts));
        opts.head_idx = 1;
@@ -75,23 +76,32 @@ static int reset_index(const struct object_id *oid, int reset_type, int quiet)
                struct object_id head_oid;
                if (get_oid("HEAD", &head_oid))
                        return error(_("You do not have a valid HEAD."));
-               if (!fill_tree_descriptor(desc, &head_oid))
+               if (!fill_tree_descriptor(desc + nr, &head_oid))
                        return error(_("Failed to find tree of HEAD."));
                nr++;
                opts.fn = twoway_merge;
        }
 
-       if (!fill_tree_descriptor(desc + nr - 1, oid))
-               return error(_("Failed to find tree of %s."), oid_to_hex(oid));
+       if (!fill_tree_descriptor(desc + nr, oid)) {
+               error(_("Failed to find tree of %s."), oid_to_hex(oid));
+               goto out;
+       }
+       nr++;
+
        if (unpack_trees(nr, desc, &opts))
-               return -1;
+               goto out;
 
        if (reset_type == MIXED || reset_type == HARD) {
                tree = parse_tree_indirect(oid);
                prime_cache_tree(&the_index, tree);
        }
 
-       return 0;
+       ret = 0;
+
+out:
+       for (i = 0; i < nr; i++)
+               free((void *)desc[i].buffer);
+       return ret;
 }
 
 static void print_new_head_line(struct commit *commit)
@@ -367,8 +377,8 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
                die_if_unmerged_cache(reset_type);
 
        if (reset_type != SOFT) {
-               struct lock_file *lock = xcalloc(1, sizeof(*lock));
-               hold_locked_index(lock, LOCK_DIE_ON_ERROR);
+               struct lock_file lock = LOCK_INIT;
+               hold_locked_index(&lock, LOCK_DIE_ON_ERROR);
                if (reset_type == MIXED) {
                        int flags = quiet ? REFRESH_QUIET : REFRESH_IN_PORCELAIN;
                        if (read_from_tree(&pathspec, &oid, intent_to_add))
@@ -384,7 +394,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
                                die(_("Could not reset index file to revision '%s'."), rev);
                }
 
-               if (write_locked_index(&the_index, lock, COMMIT_LOCK))
+               if (write_locked_index(&the_index, &lock, COMMIT_LOCK))
                        die(_("Could not write new index file."));
        }
 
index 2bd28d3c085c9f7f86072bb7e1aae443aee745e6..9f24004c0a310d22808c86ab6ee65e6b296fe70c 100644 (file)
@@ -757,8 +757,8 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                                continue;
                        }
                        if (!strcmp(arg, "--bisect")) {
-                               for_each_ref_in("refs/bisect/bad", show_reference, NULL);
-                               for_each_ref_in("refs/bisect/good", anti_reference, NULL);
+                               for_each_fullref_in("refs/bisect/bad", show_reference, NULL, 0);
+                               for_each_fullref_in("refs/bisect/good", anti_reference, NULL, 0);
                                continue;
                        }
                        if (opt_with_value(arg, "--branches", &arg)) {
index 43c4799ea9a9d963f9a44be5cd0ee5bc511e73df..e29875b84389b25237e39e0112eafa8ac34599ee 100644 (file)
@@ -52,26 +52,8 @@ static void insert_one_record(struct shortlog *log,
                              const char *oneline)
 {
        struct string_list_item *item;
-       const char *mailbuf, *namebuf;
-       size_t namelen, maillen;
-       struct strbuf namemailbuf = STRBUF_INIT;
-       struct ident_split ident;
 
-       if (split_ident_line(&ident, author, strlen(author)))
-               return;
-
-       namebuf = ident.name_begin;
-       mailbuf = ident.mail_begin;
-       namelen = ident.name_end - ident.name_begin;
-       maillen = ident.mail_end - ident.mail_begin;
-
-       map_user(&log->mailmap, &mailbuf, &maillen, &namebuf, &namelen);
-       strbuf_add(&namemailbuf, namebuf, namelen);
-
-       if (log->email)
-               strbuf_addf(&namemailbuf, " <%.*s>", (int)maillen, mailbuf);
-
-       item = string_list_insert(&log->list, namemailbuf.buf);
+       item = string_list_insert(&log->list, author);
 
        if (log->summary)
                item->util = (void *)(UTIL_TO_INT(item) + 1);
@@ -114,9 +96,33 @@ static void insert_one_record(struct shortlog *log,
        }
 }
 
+static int parse_stdin_author(struct shortlog *log,
+                              struct strbuf *out, const char *in)
+{
+       const char *mailbuf, *namebuf;
+       size_t namelen, maillen;
+       struct ident_split ident;
+
+       if (split_ident_line(&ident, in, strlen(in)))
+               return -1;
+
+       namebuf = ident.name_begin;
+       mailbuf = ident.mail_begin;
+       namelen = ident.name_end - ident.name_begin;
+       maillen = ident.mail_end - ident.mail_begin;
+
+       map_user(&log->mailmap, &mailbuf, &maillen, &namebuf, &namelen);
+       strbuf_add(out, namebuf, namelen);
+       if (log->email)
+               strbuf_addf(out, " <%.*s>", (int)maillen, mailbuf);
+
+       return 0;
+}
+
 static void read_from_stdin(struct shortlog *log)
 {
        struct strbuf author = STRBUF_INIT;
+       struct strbuf mapped_author = STRBUF_INIT;
        struct strbuf oneline = STRBUF_INIT;
        static const char *author_match[2] = { "Author: ", "author " };
        static const char *committer_match[2] = { "Commit: ", "committer " };
@@ -134,9 +140,15 @@ static void read_from_stdin(struct shortlog *log)
                while (strbuf_getline_lf(&oneline, stdin) != EOF &&
                       !oneline.len)
                        ; /* discard blanks */
-               insert_one_record(log, v, oneline.buf);
+
+               strbuf_reset(&mapped_author);
+               if (parse_stdin_author(log, &mapped_author, v) < 0)
+                       continue;
+
+               insert_one_record(log, mapped_author.buf, oneline.buf);
        }
        strbuf_release(&author);
+       strbuf_release(&mapped_author);
        strbuf_release(&oneline);
 }
 
@@ -153,7 +165,9 @@ void shortlog_add_commit(struct shortlog *log, struct commit *commit)
        ctx.date_mode.type = DATE_NORMAL;
        ctx.output_encoding = get_log_output_encoding();
 
-       fmt = log->committer ? "%cn <%ce>" : "%an <%ae>";
+       fmt = log->committer ?
+               (log->email ? "%cN <%cE>" : "%cN") :
+               (log->email ? "%aN <%aE>" : "%aN");
 
        format_commit_message(commit, fmt, &author, &ctx);
        if (!log->summary) {
index 281ca1db6ce13ddb4cc227ebb7ea454176e8e9e1..32e01555774c838e489fd33c675488e754c3e8e2 100644 (file)
@@ -15,7 +15,7 @@ static char *create_temp_file(struct object_id *oid)
 
        xsnprintf(path, sizeof(path), ".merge_file_XXXXXX");
        fd = xmkstemp(path);
-       if (write_in_full(fd, buf, size) != size)
+       if (write_in_full(fd, buf, size) < 0)
                die_errno("unable to write temp-file");
        close(fd);
        return path;
index d562f2ec69c28f9cecf7da07e71374ae9aecad93..bf7420b808fd1e0314d981bf65073df62f7578a4 100644 (file)
@@ -287,8 +287,10 @@ static int add_one_path(const struct cache_entry *old, const char *path, int len
        }
        option = allow_add ? ADD_CACHE_OK_TO_ADD : 0;
        option |= allow_replace ? ADD_CACHE_OK_TO_REPLACE : 0;
-       if (add_cache_entry(ce, option))
+       if (add_cache_entry(ce, option)) {
+               free(ce);
                return error("%s: cannot add to the index - missing --add option?", path);
+       }
        return 0;
 }
 
@@ -915,7 +917,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
        struct refresh_params refresh_args = {0, &has_errors};
        int lock_error = 0;
        int split_index = -1;
-       struct lock_file *lock_file;
+       struct lock_file lock_file = LOCK_INIT;
        struct parse_opt_ctx_t ctx;
        strbuf_getline_fn getline_fn;
        int parseopt_state = PARSE_OPT_UNKNOWN;
@@ -1014,11 +1016,8 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
 
        git_config(git_default_config, NULL);
 
-       /* We can't free this memory, it becomes part of a linked list parsed atexit() */
-       lock_file = xcalloc(1, sizeof(struct lock_file));
-
        /* we will diagnose later if it turns out that we need to update it */
-       newfd = hold_locked_index(lock_file, 0);
+       newfd = hold_locked_index(&lock_file, 0);
        if (newfd < 0)
                lock_error = errno;
 
@@ -1153,11 +1152,11 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
                                exit(128);
                        unable_to_lock_die(get_index_file(), lock_error);
                }
-               if (write_locked_index(&the_index, lock_file, COMMIT_LOCK))
+               if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
                        die("Unable to write new index file");
        }
 
-       rollback_lock_file(lock_file);
+       rollback_lock_file(&lock_file);
 
        return has_errors ? 1 : 0;
 }
index c98e2ce5f57c1f41ccaf90041a1a22233ae75551..de26849f5551a12aac16e4121c7024ebb40502d2 100644 (file)
@@ -381,6 +381,8 @@ static int add(int ac, const char **av, const char *prefix)
                branch = opts.new_branch;
        }
 
+       UNLEAK(path);
+       UNLEAK(opts);
        return add_worktree(path, branch, &opts);
 }
 
index 2440d1dc89175efaf74286da4b2c8f97fc52fbd9..71d092ed514549f03407aef4fd17ab1467191c68 100644 (file)
@@ -603,19 +603,16 @@ static struct cache_tree *cache_tree_find(struct cache_tree *it, const char *pat
 int write_index_as_tree(unsigned char *sha1, struct index_state *index_state, const char *index_path, int flags, const char *prefix)
 {
        int entries, was_valid, newfd;
-       struct lock_file *lock_file;
+       struct lock_file lock_file = LOCK_INIT;
+       int ret = 0;
 
-       /*
-        * We can't free this memory, it becomes part of a linked list
-        * parsed atexit()
-        */
-       lock_file = xcalloc(1, sizeof(struct lock_file));
-
-       newfd = hold_lock_file_for_update(lock_file, index_path, LOCK_DIE_ON_ERROR);
+       newfd = hold_lock_file_for_update(&lock_file, index_path, LOCK_DIE_ON_ERROR);
 
        entries = read_index_from(index_state, index_path);
-       if (entries < 0)
-               return WRITE_TREE_UNREADABLE_INDEX;
+       if (entries < 0) {
+               ret = WRITE_TREE_UNREADABLE_INDEX;
+               goto out;
+       }
        if (flags & WRITE_TREE_IGNORE_CACHE_TREE)
                cache_tree_free(&index_state->cache_tree);
 
@@ -624,10 +621,12 @@ int write_index_as_tree(unsigned char *sha1, struct index_state *index_state, co
 
        was_valid = cache_tree_fully_valid(index_state->cache_tree);
        if (!was_valid) {
-               if (cache_tree_update(index_state, flags) < 0)
-                       return WRITE_TREE_UNMERGED_INDEX;
+               if (cache_tree_update(index_state, flags) < 0) {
+                       ret = WRITE_TREE_UNMERGED_INDEX;
+                       goto out;
+               }
                if (0 <= newfd) {
-                       if (!write_locked_index(index_state, lock_file, COMMIT_LOCK))
+                       if (!write_locked_index(index_state, &lock_file, COMMIT_LOCK))
                                newfd = -1;
                }
                /* Not being able to write is fine -- we are only interested
@@ -641,17 +640,19 @@ int write_index_as_tree(unsigned char *sha1, struct index_state *index_state, co
        if (prefix) {
                struct cache_tree *subtree;
                subtree = cache_tree_find(index_state->cache_tree, prefix);
-               if (!subtree)
-                       return WRITE_TREE_PREFIX_ERROR;
+               if (!subtree) {
+                       ret = WRITE_TREE_PREFIX_ERROR;
+                       goto out;
+               }
                hashcpy(sha1, subtree->oid.hash);
        }
        else
                hashcpy(sha1, index_state->cache_tree->oid.hash);
 
+out:
        if (0 <= newfd)
-               rollback_lock_file(lock_file);
-
-       return 0;
+               rollback_lock_file(&lock_file);
+       return ret;
 }
 
 int write_cache_as_tree(unsigned char *sha1, int flags, const char *prefix)
diff --git a/cache.h b/cache.h
index a916bc79e3c1a77f55764f34f6357478e83b6cb0..49b083ee0a10ea379f271e141ccee0fa852a1954 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -4,6 +4,7 @@
 #include "git-compat-util.h"
 #include "strbuf.h"
 #include "hashmap.h"
+#include "mru.h"
 #include "advice.h"
 #include "gettext.h"
 #include "convert.h"
@@ -1589,8 +1590,7 @@ extern struct packed_git {
  * A most-recently-used ordered version of the packed_git list, which can
  * be iterated instead of packed_git (and marked via mru_mark).
  */
-struct mru;
-extern struct mru *packed_git_mru;
+extern struct mru packed_git_mru;
 
 struct pack_entry {
        off_t offset;
diff --git a/ci/install-dependencies.sh b/ci/install-dependencies.sh
new file mode 100755 (executable)
index 0000000..a29246a
--- /dev/null
@@ -0,0 +1,46 @@
+#!/usr/bin/env bash
+#
+# Install dependencies required to build and test Git on Linux and macOS
+#
+
+. ${0%/*}/lib-travisci.sh
+
+P4WHENCE=http://filehost.perforce.com/perforce/r$LINUX_P4_VERSION
+LFSWHENCE=https://github.com/github/git-lfs/releases/download/v$LINUX_GIT_LFS_VERSION
+
+case "${TRAVIS_OS_NAME:-linux}" in
+linux)
+       export GIT_TEST_HTTPD=YesPlease
+
+       mkdir --parents custom/p4
+       pushd custom/p4
+               wget --quiet "$P4WHENCE/bin.linux26x86_64/p4d"
+               wget --quiet "$P4WHENCE/bin.linux26x86_64/p4"
+               chmod u+x p4d
+               chmod u+x p4
+               export PATH="$(pwd):$PATH"
+       popd
+       mkdir --parents custom/git-lfs
+       pushd custom/git-lfs
+               wget --quiet "$LFSWHENCE/git-lfs-linux-amd64-$LINUX_GIT_LFS_VERSION.tar.gz"
+               tar --extract --gunzip --file "git-lfs-linux-amd64-$LINUX_GIT_LFS_VERSION.tar.gz"
+               cp git-lfs-$LINUX_GIT_LFS_VERSION/git-lfs .
+               export PATH="$(pwd):$PATH"
+       popd
+       ;;
+osx)
+       brew update --quiet
+       # Uncomment this if you want to run perf tests:
+       # brew install gnu-time
+       brew install git-lfs gettext
+       brew link --force gettext
+       brew install caskroom/cask/perforce
+       ;;
+esac
+
+echo "$(tput setaf 6)Perforce Server Version$(tput sgr0)"
+p4d -V | grep Rev.
+echo "$(tput setaf 6)Perforce Client Version$(tput sgr0)"
+p4 -V | grep Rev.
+echo "$(tput setaf 6)Git-LFS Version$(tput sgr0)"
+git-lfs version
diff --git a/ci/lib-travisci.sh b/ci/lib-travisci.sh
new file mode 100755 (executable)
index 0000000..9c4ae9b
--- /dev/null
@@ -0,0 +1,28 @@
+# Library of functions shared by all CI scripts
+
+skip_branch_tip_with_tag () {
+       # Sometimes, a branch is pushed at the same time the tag that points
+       # at the same commit as the tip of the branch is pushed, and building
+       # both at the same time is a waste.
+       #
+       # Travis gives a tagname e.g. v2.14.0 in $TRAVIS_BRANCH when
+       # the build is triggered by a push to a tag.  Let's see if
+       # $TRAVIS_BRANCH is exactly at a tag, and if so, if it is
+       # different from $TRAVIS_BRANCH.  That way, we can tell if
+       # we are building the tip of a branch that is tagged and
+       # we can skip the build because we won't be skipping a build
+       # of a tag.
+
+       if TAG=$(git describe --exact-match "$TRAVIS_BRANCH" 2>/dev/null) &&
+               $TAG != $TRAVIS_BRANCH
+       then
+               echo "Tip of $TRAVIS_BRANCH is exactly at $TAG"
+               exit 0
+       fi
+}
+
+# Set 'exit on error' for all CI scripts to let the caller know that
+# something went wrong
+set -e
+
+skip_branch_tip_with_tag
diff --git a/ci/print-test-failures.sh b/ci/print-test-failures.sh
new file mode 100755 (executable)
index 0000000..8c8973c
--- /dev/null
@@ -0,0 +1,18 @@
+#!/bin/sh
+#
+# Print output of failing tests
+#
+
+. ${0%/*}/lib-travisci.sh
+
+for TEST_EXIT in t/test-results/*.exit
+do
+       if [ "$(cat "$TEST_EXIT")" != "0" ]
+       then
+               TEST_OUT="${TEST_EXIT%exit}out"
+               echo "------------------------------------------------------------------------"
+               echo "$(tput setaf 1)${TEST_OUT}...$(tput sgr0)"
+               echo "------------------------------------------------------------------------"
+               cat "${TEST_OUT}"
+       fi
+done
diff --git a/ci/run-build.sh b/ci/run-build.sh
new file mode 100755 (executable)
index 0000000..4f940d1
--- /dev/null
@@ -0,0 +1,8 @@
+#!/bin/sh
+#
+# Build Git
+#
+
+. ${0%/*}/lib-travisci.sh
+
+make --jobs=2
diff --git a/ci/run-linux32-docker.sh b/ci/run-linux32-docker.sh
new file mode 100755 (executable)
index 0000000..0edf63a
--- /dev/null
@@ -0,0 +1,23 @@
+#!/bin/sh
+#
+# Download and run Docker image to build and test 32-bit Git
+#
+
+. ${0%/*}/lib-travisci.sh
+
+docker pull daald/ubuntu32:xenial
+
+# Use the following command to debug the docker build locally:
+# $ docker run -itv "${PWD}:/usr/src/git" --entrypoint /bin/bash daald/ubuntu32:xenial
+# root@container:/# /usr/src/git/ci/run-linux32-build.sh
+
+docker run \
+       --interactive \
+       --env DEVELOPER \
+       --env DEFAULT_TEST_TARGET \
+       --env GIT_PROVE_OPTS \
+       --env GIT_TEST_OPTS \
+       --env GIT_TEST_CLONE_2GB \
+       --volume "${PWD}:/usr/src/git" \
+       daald/ubuntu32:xenial \
+       /usr/src/git/ci/run-linux32-build.sh $(id -u $USER)
diff --git a/ci/run-static-analysis.sh b/ci/run-static-analysis.sh
new file mode 100755 (executable)
index 0000000..68dd0f0
--- /dev/null
@@ -0,0 +1,8 @@
+#!/bin/sh
+#
+# Perform various static code analysis checks
+#
+
+. ${0%/*}/lib-travisci.sh
+
+make coccicheck
diff --git a/ci/run-tests.sh b/ci/run-tests.sh
new file mode 100755 (executable)
index 0000000..f0c743d
--- /dev/null
@@ -0,0 +1,10 @@
+#!/bin/sh
+#
+# Test Git
+#
+
+. ${0%/*}/lib-travisci.sh
+
+mkdir -p $HOME/travis-cache
+ln -s $HOME/travis-cache/.prove t/.prove
+make --quiet test
index 2d98f6b2f94b12aafbebcd440b3809bf48c2aca6..8757b3a97c5d7598bfdb2ec1f42cbb6851bae448 100755 (executable)
@@ -6,6 +6,8 @@
 # supported) and a commit hash.
 #
 
+. ${0%/*}/lib-travisci.sh
+
 test $# -ne 2 && echo "Unexpected number of parameters" && exit 1
 test -z "$GFW_CI_TOKEN" && echo "GFW_CI_TOKEN not defined" && exit
 
index 6214e6acb4a1acbc3eef764a8c610a5f6e100ee0..7a0a848e83d68152acaa1de50fd7fb5fe9990151 100755 (executable)
@@ -3,7 +3,9 @@
 # Perform sanity checks on documentation and build it.
 #
 
-set -e
+. ${0%/*}/lib-travisci.sh
+
+gem install asciidoctor
 
 make check-builtins
 make check-docs
index 17a93d1e64fd7122ce0d3471a85388cd445ab6b2..906298052d485867599a98af1cf49e4728028b50 100644 (file)
--- a/commit.c
+++ b/commit.c
@@ -1570,10 +1570,13 @@ int commit_tree_extended(const char *msg, size_t msg_len,
        if (encoding_is_utf8 && !verify_utf8(&buffer))
                fprintf(stderr, _(commit_utf8_warn));
 
-       if (sign_commit && do_sign_commit(&buffer, sign_commit))
-               return -1;
+       if (sign_commit && do_sign_commit(&buffer, sign_commit)) {
+               result = -1;
+               goto out;
+       }
 
        result = write_sha1_file(buffer.buf, buffer.len, commit_type, ret);
+out:
        strbuf_release(&buffer);
        return result;
 }
index d0d8ce823ae6d8be4b9cc92e1d01e44ca7569e84..a77cae56ed26212c6bfbbd831c85c8cf64deb8b9 100644 (file)
--- a/config.c
+++ b/config.c
@@ -956,11 +956,6 @@ int git_parse_maybe_bool(const char *value)
        return -1;
 }
 
-int git_config_maybe_bool(const char *name, const char *value)
-{
-       return git_parse_maybe_bool(value);
-}
-
 int git_config_bool_or_int(const char *name, const char *value, int *is_bool)
 {
        int v = git_parse_maybe_bool_text(value);
@@ -2297,10 +2292,11 @@ static int write_error(const char *filename)
        return 4;
 }
 
-static int store_write_section(int fd, const char *key)
+static ssize_t write_section(int fd, const char *key)
 {
        const char *dot;
-       int i, success;
+       int i;
+       ssize_t ret;
        struct strbuf sb = STRBUF_INIT;
 
        dot = memchr(key, '.', store.baselen);
@@ -2316,15 +2312,16 @@ static int store_write_section(int fd, const char *key)
                strbuf_addf(&sb, "[%.*s]\n", store.baselen, key);
        }
 
-       success = write_in_full(fd, sb.buf, sb.len) == sb.len;
+       ret = write_in_full(fd, sb.buf, sb.len);
        strbuf_release(&sb);
 
-       return success;
+       return ret;
 }
 
-static int store_write_pair(int fd, const char *key, const char *value)
+static ssize_t write_pair(int fd, const char *key, const char *value)
 {
-       int i, success;
+       int i;
+       ssize_t ret;
        int length = strlen(key + store.baselen + 1);
        const char *quote = "";
        struct strbuf sb = STRBUF_INIT;
@@ -2364,10 +2361,10 @@ static int store_write_pair(int fd, const char *key, const char *value)
                }
        strbuf_addf(&sb, "%s\n", quote);
 
-       success = write_in_full(fd, sb.buf, sb.len) == sb.len;
+       ret = write_in_full(fd, sb.buf, sb.len);
        strbuf_release(&sb);
 
-       return success;
+       return ret;
 }
 
 static ssize_t find_beginning_of_line(const char *contents, size_t size,
@@ -2450,7 +2447,7 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,
 {
        int fd = -1, in_fd = -1;
        int ret;
-       struct lock_file *lock = NULL;
+       struct lock_file lock = LOCK_INIT;
        char *filename_buf = NULL;
        char *contents = NULL;
        size_t contents_sz;
@@ -2469,8 +2466,7 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,
         * The lock serves a purpose in addition to locking: the new
         * contents of .git/config will be written into it.
         */
-       lock = xcalloc(1, sizeof(struct lock_file));
-       fd = hold_lock_file_for_update(lock, config_filename, 0);
+       fd = hold_lock_file_for_update(&lock, config_filename, 0);
        if (fd < 0) {
                error_errno("could not lock config file %s", config_filename);
                free(store.key);
@@ -2497,8 +2493,8 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,
                }
 
                store.key = (char *)key;
-               if (!store_write_section(fd, key) ||
-                   !store_write_pair(fd, key, value))
+               if (write_section(fd, key) < 0 ||
+                   write_pair(fd, key, value) < 0)
                        goto write_err_out;
        } else {
                struct stat st;
@@ -2583,8 +2579,8 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,
                close(in_fd);
                in_fd = -1;
 
-               if (chmod(get_lock_file_path(lock), st.st_mode & 07777) < 0) {
-                       error_errno("chmod on %s failed", get_lock_file_path(lock));
+               if (chmod(get_lock_file_path(&lock), st.st_mode & 07777) < 0) {
+                       error_errno("chmod on %s failed", get_lock_file_path(&lock));
                        ret = CONFIG_NO_WRITE;
                        goto out_free;
                }
@@ -2608,11 +2604,10 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,
                        /* write the first part of the config */
                        if (copy_end > copy_begin) {
                                if (write_in_full(fd, contents + copy_begin,
-                                                 copy_end - copy_begin) <
-                                   copy_end - copy_begin)
+                                                 copy_end - copy_begin) < 0)
                                        goto write_err_out;
                                if (new_line &&
-                                   write_str_in_full(fd, "\n") != 1)
+                                   write_str_in_full(fd, "\n") < 0)
                                        goto write_err_out;
                        }
                        copy_begin = store.offset[i];
@@ -2621,46 +2616,36 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,
                /* write the pair (value == NULL means unset) */
                if (value != NULL) {
                        if (store.state == START) {
-                               if (!store_write_section(fd, key))
+                               if (write_section(fd, key) < 0)
                                        goto write_err_out;
                        }
-                       if (!store_write_pair(fd, key, value))
+                       if (write_pair(fd, key, value) < 0)
                                goto write_err_out;
                }
 
                /* write the rest of the config */
                if (copy_begin < contents_sz)
                        if (write_in_full(fd, contents + copy_begin,
-                                         contents_sz - copy_begin) <
-                           contents_sz - copy_begin)
+                                         contents_sz - copy_begin) < 0)
                                goto write_err_out;
 
                munmap(contents, contents_sz);
                contents = NULL;
        }
 
-       if (commit_lock_file(lock) < 0) {
+       if (commit_lock_file(&lock) < 0) {
                error_errno("could not write config file %s", config_filename);
                ret = CONFIG_NO_WRITE;
-               lock = NULL;
                goto out_free;
        }
 
-       /*
-        * lock is committed, so don't try to roll it back below.
-        * NOTE: Since lockfile.c keeps a linked list of all created
-        * lock_file structures, it isn't safe to free(lock).  It's
-        * better to just leave it hanging around.
-        */
-       lock = NULL;
        ret = 0;
 
        /* Invalidate the config cache */
        git_config_clear();
 
 out_free:
-       if (lock)
-               rollback_lock_file(lock);
+       rollback_lock_file(&lock);
        free(filename_buf);
        if (contents)
                munmap(contents, contents_sz);
@@ -2669,7 +2654,7 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,
        return ret;
 
 write_err_out:
-       ret = write_error(get_lock_file_path(lock));
+       ret = write_error(get_lock_file_path(&lock));
        goto out_free;
 
 }
@@ -2818,7 +2803,7 @@ int git_config_rename_section_in_file(const char *config_filename,
                                        continue;
                                }
                                store.baselen = strlen(new_name);
-                               if (!store_write_section(out_fd, new_name)) {
+                               if (write_section(out_fd, new_name) < 0) {
                                        ret = write_error(get_lock_file_path(lock));
                                        goto out;
                                }
@@ -2844,7 +2829,7 @@ int git_config_rename_section_in_file(const char *config_filename,
                if (remove)
                        continue;
                length = strlen(output);
-               if (write_in_full(out_fd, output, length) != length) {
+               if (write_in_full(out_fd, output, length) < 0) {
                        ret = write_error(get_lock_file_path(lock));
                        goto out;
                }
index 97471b887320b48f1b4c2d5a2023f6ffd1acf94e..456b3d134d3ed187b80b5941c20690763f3eab35 100644 (file)
--- a/config.h
+++ b/config.h
@@ -56,7 +56,6 @@ extern unsigned long git_config_ulong(const char *, const char *);
 extern ssize_t git_config_ssize_t(const char *, const char *);
 extern int git_config_bool_or_int(const char *, const char *, int *);
 extern int git_config_bool(const char *, const char *);
-extern int git_config_maybe_bool(const char *, const char *);
 extern int git_config_string(const char **, const char *, const char *);
 extern int git_config_pathname(const char **, const char *, const char *);
 extern int git_config_set_in_file_gently(const char *, const char *, const char *);
index 49b28b83be2717027ac33570b1041604db5a062d..df56c0cbff4dfb1263917f6147bd7e1912418dd6 100644 (file)
--- a/connect.c
+++ b/connect.c
@@ -778,7 +778,6 @@ struct child_process *git_connect(int fd[2], const char *url,
        char *hostandport, *path;
        struct child_process *conn = &no_fork;
        enum protocol protocol;
-       struct strbuf cmd = STRBUF_INIT;
 
        /* Without this we cannot rely on waitpid() to tell
         * what happened to our children.
@@ -826,6 +825,8 @@ struct child_process *git_connect(int fd[2], const char *url,
                             target_host, 0);
                free(target_host);
        } else {
+               struct strbuf cmd = STRBUF_INIT;
+
                conn = xmalloc(sizeof(*conn));
                child_process_init(conn);
 
@@ -862,6 +863,7 @@ struct child_process *git_connect(int fd[2], const char *url,
                                free(hostandport);
                                free(path);
                                free(conn);
+                               strbuf_release(&cmd);
                                return NULL;
                        }
 
index fbf5c582496eece56b29a91a2f3085716f4b0e9f..f2be7cc92437191219d16ddf1536883da23541c0 100644 (file)
@@ -17,4 +17,7 @@ shebang.perl: FORCE
 test: all
        $(MAKE) -C t
 
+clean:
+       $(RM) diff-highlight
+
 .PHONY: FORCE
index 02962261c58f71485a27f9799a5db29ec77e0bc7..a09935cb8123f04a8fdb4d48157c2a98e67b3c64 100644 (file)
--- a/convert.c
+++ b/convert.c
@@ -423,8 +423,10 @@ static int filter_buffer_or_fd(int in, int out, void *data)
        child_process.in = -1;
        child_process.out = out;
 
-       if (start_command(&child_process))
+       if (start_command(&child_process)) {
+               strbuf_release(&cmd);
                return error("cannot fork to run external filter '%s'", params->cmd);
+       }
 
        sigchain_push(SIGPIPE, SIG_IGN);
 
index 0d5c6250940633e75e2c9fb4b59b242a52188411..4dfbc8c9f917a600c0995b7e33927df3fa19453d 100644 (file)
@@ -5,8 +5,6 @@
 #include "unix-socket.h"
 #include "parse-options.h"
 
-static struct tempfile socket_file;
-
 struct credential_cache_entry {
        struct credential item;
        timestamp_t expiration;
@@ -260,6 +258,7 @@ static void init_socket_directory(const char *path)
 
 int cmd_main(int argc, const char **argv)
 {
+       struct tempfile *socket_file;
        const char *socket_path;
        int ignore_sighup = 0;
        static const char *usage[] = {
@@ -285,7 +284,7 @@ int cmd_main(int argc, const char **argv)
                die("socket directory must be an absolute path");
 
        init_socket_directory(socket_path);
-       register_tempfile(&socket_file, socket_path);
+       socket_file = register_tempfile(socket_path);
 
        if (ignore_sighup)
                signal(SIGHUP, SIG_IGN);
diff --git a/diff.c b/diff.c
index 3d3e553a98bc4fd83d2f79515056c4116086b5df..3c6a3e0faa58f810ce2fe8b27f62581c175edf64 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -459,7 +459,7 @@ static struct diff_tempfile {
         * If this diff_tempfile instance refers to a temporary file,
         * this tempfile object is used to manage its lifetime.
         */
-       struct tempfile tempfile;
+       struct tempfile *tempfile;
 } diff_temp[2];
 
 struct emit_callback {
@@ -1414,7 +1414,7 @@ static void remove_tempfile(void)
 {
        int i;
        for (i = 0; i < ARRAY_SIZE(diff_temp); i++) {
-               if (is_tempfile_active(&diff_temp[i].tempfile))
+               if (is_tempfile_active(diff_temp[i].tempfile))
                        delete_tempfile(&diff_temp[i].tempfile);
                diff_temp[i].name = NULL;
        }
@@ -2583,6 +2583,7 @@ static void show_stats(struct diffstat_t *data, struct diff_options *options)
        }
 
        print_stat_summary_inserts_deletes(options, total_files, adds, dels);
+       strbuf_release(&out);
 }
 
 static void show_shortstats(struct diffstat_t *data, struct diff_options *options)
@@ -3720,7 +3721,6 @@ static void prep_temp_blob(const char *path, struct diff_tempfile *temp,
                           const struct object_id *oid,
                           int mode)
 {
-       int fd;
        struct strbuf buf = STRBUF_INIT;
        struct strbuf template = STRBUF_INIT;
        char *path_dup = xstrdup(path);
@@ -3730,18 +3730,18 @@ static void prep_temp_blob(const char *path, struct diff_tempfile *temp,
        strbuf_addstr(&template, "XXXXXX_");
        strbuf_addstr(&template, base);
 
-       fd = mks_tempfile_ts(&temp->tempfile, template.buf, strlen(base) + 1);
-       if (fd < 0)
+       temp->tempfile = mks_tempfile_ts(template.buf, strlen(base) + 1);
+       if (!temp->tempfile)
                die_errno("unable to create temp-file");
        if (convert_to_working_tree(path,
                        (const char *)blob, (size_t)size, &buf)) {
                blob = buf.buf;
                size = buf.len;
        }
-       if (write_in_full(fd, blob, size) != size)
+       if (write_in_full(temp->tempfile->fd, blob, size) < 0 ||
+           close_tempfile_gently(temp->tempfile))
                die_errno("unable to write temp-file");
-       close_tempfile(&temp->tempfile);
-       temp->name = get_tempfile_path(&temp->tempfile);
+       temp->name = get_tempfile_path(temp->tempfile);
        oid_to_hex_r(temp->hex, oid);
        xsnprintf(temp->mode, sizeof(temp->mode), "%06o", mode);
        strbuf_release(&buf);
@@ -5289,6 +5289,7 @@ static void show_rename_copy(struct diff_options *opt, const char *renamecopy,
        emit_diff_symbol(opt, DIFF_SYMBOL_SUMMARY,
                                 sb.buf, sb.len, 0);
        show_mode_change(opt, p, 0);
+       strbuf_release(&sb);
 }
 
 static void diff_summary(struct diff_options *opt, struct diff_filepair *p)
@@ -5314,6 +5315,7 @@ static void diff_summary(struct diff_options *opt, struct diff_filepair *p)
                        strbuf_addf(&sb, " (%d%%)\n", similarity_index(p));
                        emit_diff_symbol(opt, DIFF_SYMBOL_SUMMARY,
                                         sb.buf, sb.len, 0);
+                       strbuf_release(&sb);
                }
                show_mode_change(opt, p, !p->score);
                break;
diff --git a/entry.c b/entry.c
index cb291aa88bf148608184edeff68943315b44e6ab..1c7e3c11d5075d4b39eeca76ab2272e781f773c8 100644 (file)
--- a/entry.c
+++ b/entry.c
@@ -257,7 +257,8 @@ static int write_entry(struct cache_entry *ce,
        char *new;
        struct strbuf buf = STRBUF_INIT;
        unsigned long size;
-       size_t wrote, newsize = 0;
+       ssize_t wrote;
+       size_t newsize = 0;
        struct stat st;
        const struct submodule *sub;
 
@@ -332,7 +333,7 @@ static int write_entry(struct cache_entry *ce,
                        fstat_done = fstat_output(fd, state, &st);
                close(fd);
                free(new);
-               if (wrote != size)
+               if (wrote < 0)
                        return error("unable to write file %s", path);
                break;
        case S_IFGITLINK:
index 3fd4b1084590d0118cad5e7ae32fb37e570e7523..f1f934b6fddd101191a17b1bc1883da287793319 100644 (file)
@@ -97,7 +97,7 @@ int ignore_untracked_cache_config;
 /* This is set by setup_git_dir_gently() and/or git_default_config() */
 char *git_work_tree_cfg;
 
-static const char *namespace;
+static char *namespace;
 
 static const char *super_prefix;
 
@@ -152,8 +152,10 @@ void setup_git_env(void)
        if (getenv(NO_REPLACE_OBJECTS_ENVIRONMENT))
                check_replace_refs = 0;
        replace_ref_base = getenv(GIT_REPLACE_REF_BASE_ENVIRONMENT);
+       free(git_replace_ref_base);
        git_replace_ref_base = xstrdup(replace_ref_base ? replace_ref_base
                                                          : "refs/replace/");
+       free(namespace);
        namespace = expand_namespace(getenv(GIT_NAMESPACE_ENVIRONMENT));
        shallow_file = getenv(GIT_SHALLOW_FILE_ENVIRONMENT);
        if (shallow_file)
index fb94aeba9cef2893ea7173e5634640e596659474..ce192a2d64c846e9d7ccffe10c9476f9c5abb8b8 100644 (file)
@@ -5,21 +5,14 @@
 #define MAX_ARGS       32
 
 static const char *argv_exec_path;
+
+#ifdef RUNTIME_PREFIX
 static const char *argv0_path;
 
-char *system_path(const char *path)
+static const char *system_prefix(void)
 {
-#ifdef RUNTIME_PREFIX
        static const char *prefix;
-#else
-       static const char *prefix = PREFIX;
-#endif
-       struct strbuf d = STRBUF_INIT;
-
-       if (is_absolute_path(path))
-               return xstrdup(path);
 
-#ifdef RUNTIME_PREFIX
        assert(argv0_path);
        assert(is_absolute_path(argv0_path));
 
@@ -32,10 +25,7 @@ char *system_path(const char *path)
                                "but prefix computation failed.  "
                                "Using static fallback '%s'.\n", prefix);
        }
-#endif
-
-       strbuf_addf(&d, "%s/%s", prefix, path);
-       return strbuf_detach(&d, NULL);
+       return prefix;
 }
 
 void git_extract_argv0_path(const char *argv0)
@@ -51,6 +41,30 @@ void git_extract_argv0_path(const char *argv0)
                argv0_path = xstrndup(argv0, slash - argv0);
 }
 
+#else
+
+static const char *system_prefix(void)
+{
+       return PREFIX;
+}
+
+void git_extract_argv0_path(const char *argv0)
+{
+}
+
+#endif /* RUNTIME_PREFIX */
+
+char *system_path(const char *path)
+{
+       struct strbuf d = STRBUF_INIT;
+
+       if (is_absolute_path(path))
+               return xstrdup(path);
+
+       strbuf_addf(&d, "%s/%s", system_prefix(), path);
+       return strbuf_detach(&d, NULL);
+}
+
 void git_set_argv_exec_path(const char *exec_path)
 {
        argv_exec_path = exec_path;
index 49516d60e69a26c41d9c47ecc0b60dbadabdad12..35bf671f12c41ce5a2a9f9224babee792bf71209 100644 (file)
@@ -2952,7 +2952,7 @@ static void parse_reset_branch(const char *arg)
 
 static void cat_blob_write(const char *buf, unsigned long size)
 {
-       if (write_in_full(cat_blob_fd, buf, size) != size)
+       if (write_in_full(cat_blob_fd, buf, size) < 0)
                die_errno("Write to frontend failed");
 }
 
index 6678b488cc73851d468094deb5352d311ab310d0..003e444c46edce65f04413d732c22d71d708fd4f 100644 (file)
@@ -1169,4 +1169,24 @@ static inline int is_missing_file_error(int errno_)
 
 extern int cmd_main(int, const char **);
 
+/*
+ * You can mark a stack variable with UNLEAK(var) to avoid it being
+ * reported as a leak by tools like LSAN or valgrind. The argument
+ * should generally be the variable itself (not its address and not what
+ * it points to). It's safe to use this on pointers which may already
+ * have been freed, or on pointers which may still be in use.
+ *
+ * Use this _only_ for a variable that leaks by going out of scope at
+ * program exit (so only from cmd_* functions or their direct helpers).
+ * Normal functions, especially those which may be called multiple
+ * times, should actually free their memory. This is only meant as
+ * an annotation, and does nothing in non-leak-checking builds.
+ */
+#ifdef SUPPRESS_ANNOTATED_LEAKS
+extern void unleak_memory(const void *ptr, size_t len);
+#define UNLEAK(var) unleak_memory(&(var), sizeof(var));
+#else
+#define UNLEAK(var)
+#endif
+
 #endif
index d936f3a32fe5d38c2b41ae0488ac2c02250a1618..4feacf16e5bcd93dcde4a2ea769f6b61ca369593 100644 (file)
@@ -202,26 +202,26 @@ int verify_signed_buffer(const char *payload, size_t payload_size,
                         struct strbuf *gpg_output, struct strbuf *gpg_status)
 {
        struct child_process gpg = CHILD_PROCESS_INIT;
-       static struct tempfile temp;
-       int fd, ret;
+       struct tempfile *temp;
+       int ret;
        struct strbuf buf = STRBUF_INIT;
 
-       fd = mks_tempfile_t(&temp, ".git_vtag_tmpXXXXXX");
-       if (fd < 0)
+       temp = mks_tempfile_t(".git_vtag_tmpXXXXXX");
+       if (!temp)
                return error_errno(_("could not create temporary file"));
-       if (write_in_full(fd, signature, signature_size) < 0) {
+       if (write_in_full(temp->fd, signature, signature_size) < 0 ||
+           close_tempfile_gently(temp) < 0) {
                error_errno(_("failed writing detached signature to '%s'"),
-                           temp.filename.buf);
+                           temp->filename.buf);
                delete_tempfile(&temp);
                return -1;
        }
-       close(fd);
 
        argv_array_pushl(&gpg.args,
                         gpg_program,
                         "--status-fd=1",
                         "--keyid-format=long",
-                        "--verify", temp.filename.buf, "-",
+                        "--verify", temp->filename.buf, "-",
                         NULL);
 
        if (!gpg_status)
diff --git a/hash.h b/hash.h
index bef3e630a093b508e78419c5f4e228e836ecb88c..024d0d3d50b174aac1d948e9ecfa7ae92020355a 100644 (file)
--- a/hash.h
+++ b/hash.h
@@ -8,11 +8,7 @@
 #elif defined(SHA1_OPENSSL)
 #include <openssl/sha.h>
 #elif defined(SHA1_DC)
-#ifdef DC_SHA1_SUBMODULE
-#include "sha1collisiondetection/lib/sha1.h"
-#else
-#include "sha1dc/sha1.h"
-#endif
+#include "sha1dc_git.h"
 #else /* SHA1_BLK */
 #include "block-sha1/sha1.h"
 #endif
index 9b6a12859be67a09685f13310f9769df260d558d..d42f01ff5ae6f3494d5b427c64bc64406c35d576 100644 (file)
--- a/hashmap.c
+++ b/hashmap.c
@@ -116,9 +116,6 @@ static void rehash(struct hashmap *map, unsigned int newsize)
        unsigned int i, oldsize = map->tablesize;
        struct hashmap_entry **oldtable = map->table;
 
-       if (map->disallow_rehash)
-               return;
-
        alloc_table(map, newsize);
        for (i = 0; i < oldsize; i++) {
                struct hashmap_entry *e = oldtable[i];
@@ -166,6 +163,12 @@ void hashmap_init(struct hashmap *map, hashmap_cmp_fn equals_function,
        while (initial_size > size)
                size <<= HASHMAP_RESIZE_BITS;
        alloc_table(map, size);
+
+       /*
+        * Keep track of the number of items in the map and
+        * allow the map to automatically grow as necessary.
+        */
+       map->do_count_items = 1;
 }
 
 void hashmap_free(struct hashmap *map, int free_entries)
@@ -206,9 +209,11 @@ void hashmap_add(struct hashmap *map, void *entry)
        map->table[b] = entry;
 
        /* fix size and rehash if appropriate */
-       map->size++;
-       if (map->size > map->grow_at)
-               rehash(map, map->tablesize << HASHMAP_RESIZE_BITS);
+       if (map->do_count_items) {
+               map->private_size++;
+               if (map->private_size > map->grow_at)
+                       rehash(map, map->tablesize << HASHMAP_RESIZE_BITS);
+       }
 }
 
 void *hashmap_remove(struct hashmap *map, const void *key, const void *keydata)
@@ -224,9 +229,12 @@ void *hashmap_remove(struct hashmap *map, const void *key, const void *keydata)
        old->next = NULL;
 
        /* fix size and rehash if appropriate */
-       map->size--;
-       if (map->size < map->shrink_at)
-               rehash(map, map->tablesize >> HASHMAP_RESIZE_BITS);
+       if (map->do_count_items) {
+               map->private_size--;
+               if (map->private_size < map->shrink_at)
+                       rehash(map, map->tablesize >> HASHMAP_RESIZE_BITS);
+       }
+
        return old;
 }
 
index 7a8fa7fa3da147385969dec392c9406c9dae34ea..7cb29a6aede8e24320555e1845f8e4eb2032ade9 100644 (file)
--- a/hashmap.h
+++ b/hashmap.h
@@ -183,7 +183,7 @@ struct hashmap {
        const void *cmpfn_data;
 
        /* total number of entries (0 means the hashmap is empty) */
-       unsigned int size;
+       unsigned int private_size; /* use hashmap_get_size() */
 
        /*
         * tablesize is the allocated size of the hash table. A non-0 value
@@ -196,8 +196,7 @@ struct hashmap {
        unsigned int grow_at;
        unsigned int shrink_at;
 
-       /* See `hashmap_disallow_rehash`. */
-       unsigned disallow_rehash : 1;
+       unsigned int do_count_items : 1;
 };
 
 /* hashmap functions */
@@ -252,6 +251,18 @@ static inline void hashmap_entry_init(void *entry, unsigned int hash)
        e->next = NULL;
 }
 
+/*
+ * Return the number of items in the map.
+ */
+static inline unsigned int hashmap_get_size(struct hashmap *map)
+{
+       if (map->do_count_items)
+               return map->private_size;
+
+       BUG("hashmap_get_size: size not set");
+       return 0;
+}
+
 /*
  * Returns the hashmap entry for the specified key, or NULL if not found.
  *
@@ -344,24 +355,6 @@ extern void *hashmap_remove(struct hashmap *map, const void *key,
  */
 int hashmap_bucket(const struct hashmap *map, unsigned int hash);
 
-/*
- * Disallow/allow rehashing of the hashmap.
- * This is useful if the caller knows that the hashmap needs multi-threaded
- * access.  The caller is still required to guard/lock searches and inserts
- * in a manner appropriate to their usage.  This simply prevents the table
- * from being unexpectedly re-mapped.
- *
- * It is up to the caller to ensure that the hashmap is initialized to a
- * reasonable size to prevent poor performance.
- *
- * A call to allow rehashing does not force a rehash; that might happen
- * with the next insert or delete.
- */
-static inline void hashmap_disallow_rehash(struct hashmap *map, unsigned value)
-{
-       map->disallow_rehash = value;
-}
-
 /*
  * Used to iterate over all entries of a hashmap. Note that it is
  * not safe to add or remove entries to the hashmap while
@@ -387,6 +380,43 @@ static inline void *hashmap_iter_first(struct hashmap *map,
        return hashmap_iter_next(iter);
 }
 
+/*
+ * Disable item counting and automatic rehashing when adding/removing items.
+ *
+ * Normally, the hashmap keeps track of the number of items in the map
+ * and uses it to dynamically resize it.  This (both the counting and
+ * the resizing) can cause problems when the map is being used by
+ * threaded callers (because the hashmap code does not know about the
+ * locking strategy used by the threaded callers and therefore, does
+ * not know how to protect the "private_size" counter).
+ */
+static inline void hashmap_disable_item_counting(struct hashmap *map)
+{
+       map->do_count_items = 0;
+}
+
+/*
+ * Re-enable item couting when adding/removing items.
+ * If counting is currently disabled, it will force count them.
+ * It WILL NOT automatically rehash them.
+ */
+static inline void hashmap_enable_item_counting(struct hashmap *map)
+{
+       void *item;
+       unsigned int n = 0;
+       struct hashmap_iter iter;
+
+       if (map->do_count_items)
+               return;
+
+       hashmap_iter_init(map, &iter);
+       while ((item = hashmap_iter_next(&iter)))
+               n++;
+
+       map->do_count_items = 1;
+       map->private_size = n;
+}
+
 /* String interning */
 
 /*
index 8076b1d5e5ae01341fa739f55c6bcf6695e6507a..e51c7805c805ac6c1234665f5f90fcdd51d39231 100644 (file)
@@ -358,7 +358,7 @@ static void inflate_request(const char *prog_name, int out, int buffer_input)
                                die("zlib error inflating request, result %d", ret);
 
                        n = stream.total_out - cnt;
-                       if (write_in_full(out, out_buf, n) != n)
+                       if (write_in_full(out, out_buf, n) < 0)
                                die("%s aborted reading request", prog_name);
                        cnt += n;
 
@@ -379,7 +379,7 @@ static void copy_request(const char *prog_name, int out)
        ssize_t n = read_request(0, &buf);
        if (n < 0)
                die_errno("error reading request body");
-       if (write_in_full(out, buf, n) != n)
+       if (write_in_full(out, buf, n) < 0)
                die("%s aborted reading request", prog_name);
        close(out);
        free(buf);
diff --git a/list.h b/list.h
index a226a870dc09d6b58346a92d5f98078d2e55163a..eb601192f4ca9a6af126c82f4c2a24cb9145009d 100644 (file)
--- a/list.h
+++ b/list.h
@@ -163,4 +163,42 @@ static inline void list_replace_init(struct list_head *old,
        INIT_LIST_HEAD(old);
 }
 
+/*
+ * This is exactly the same as a normal list_head, except that it can be
+ * declared volatile (e.g., if you have a list that may be accessed from signal
+ * handlers).
+ */
+struct volatile_list_head {
+       volatile struct volatile_list_head *next, *prev;
+};
+
+#define VOLATILE_LIST_HEAD(name) \
+       volatile struct volatile_list_head name = { &(name), &(name) }
+
+static inline void __volatile_list_del(volatile struct volatile_list_head *prev,
+                                      volatile struct volatile_list_head *next)
+{
+       next->prev = prev;
+       prev->next = next;
+}
+
+static inline void volatile_list_del(volatile struct volatile_list_head *elem)
+{
+       __volatile_list_del(elem->prev, elem->next);
+}
+
+static inline int volatile_list_empty(volatile struct volatile_list_head *head)
+{
+       return head == head->next;
+}
+
+static inline void volatile_list_add(volatile struct volatile_list_head *newp,
+                                    volatile struct volatile_list_head *head)
+{
+       head->next->prev = newp;
+       newp->next = head->next;
+       newp->prev = head;
+       head->next = newp;
+}
+
 #endif /* LIST_H */
index 9fb855a90030e07e28c1ae9d0994006da22cd4ed..a6ad2ec12dc9c1ece81f120196c81fd259e40eb6 100644 (file)
@@ -154,7 +154,7 @@ static void create_temp(mmfile_t *src, char *path, size_t len)
 
        xsnprintf(path, len, ".merge_file_XXXXXX");
        fd = xmkstemp(path);
-       if (write_in_full(fd, src->ptr, src->size) != src->size)
+       if (write_in_full(fd, src->ptr, src->size) < 0)
                die_errno("unable to write temp-file");
        close(fd);
 }
index aa69210d8b3a9063517d2d84153dc3ba738bef30..efcb7d7dfe30a2fbad03244ba4ca9e536a3649e4 100644 (file)
@@ -72,7 +72,6 @@ static void resolve_symlink(struct strbuf *path)
 /* Make sure errno contains a meaningful value on error */
 static int lock_file(struct lock_file *lk, const char *path, int flags)
 {
-       int fd;
        struct strbuf filename = STRBUF_INIT;
 
        strbuf_addstr(&filename, path);
@@ -80,9 +79,9 @@ static int lock_file(struct lock_file *lk, const char *path, int flags)
                resolve_symlink(&filename);
 
        strbuf_addstr(&filename, LOCK_SUFFIX);
-       fd = create_tempfile(&lk->tempfile, filename.buf);
+       lk->tempfile = create_tempfile(filename.buf);
        strbuf_release(&filename);
-       return fd;
+       return lk->tempfile ? lk->tempfile->fd : -1;
 }
 
 /*
@@ -191,7 +190,7 @@ char *get_locked_file_path(struct lock_file *lk)
 {
        struct strbuf ret = STRBUF_INIT;
 
-       strbuf_addstr(&ret, get_tempfile_path(&lk->tempfile));
+       strbuf_addstr(&ret, get_tempfile_path(lk->tempfile));
        if (ret.len <= LOCK_SUFFIX_LEN ||
            strcmp(ret.buf + ret.len - LOCK_SUFFIX_LEN, LOCK_SUFFIX))
                die("BUG: get_locked_file_path() called for malformed lock object");
index 572064939c718281b56abda7757bb21d35f42de3..7c1c484d7cfdfab126fd0ce947ac40f650e5d107 100644 (file)
  *
  * The caller:
  *
- * * Allocates a `struct lock_file` either as a static variable or on
- *   the heap, initialized to zeros. Once you use the structure to
- *   call the `hold_lock_file_for_*()` family of functions, it belongs
- *   to the lockfile subsystem and its storage must remain valid
- *   throughout the life of the program (i.e. you cannot use an
- *   on-stack variable to hold this structure).
+ * * Allocates a `struct lock_file` with whatever storage duration you
+ *   desire. The struct does not have to be initialized before being
+ *   used, but it is good practice to do so using by setting it to
+ *   all-zeros (or using the LOCK_INIT macro). This puts the object in a
+ *   consistent state that allows you to call rollback_lock_file() even
+ *   if the lock was never taken (in which case it is a noop).
  *
  * * Attempts to create a lockfile by calling `hold_lock_file_for_update()`.
  *
  *   `rollback_lock_file()`.
  *
  * * Close the file descriptor without removing or renaming the
- *   lockfile by calling `close_lock_file()`, and later call
+ *   lockfile by calling `close_lock_file_gently()`, and later call
  *   `commit_lock_file()`, `commit_lock_file_to()`,
  *   `rollback_lock_file()`, or `reopen_lock_file()`.
  *
- * Even after the lockfile is committed or rolled back, the
- * `lock_file` object must not be freed or altered by the caller.
- * However, it may be reused; just pass it to another call of
- * `hold_lock_file_for_update()`.
+ * After the lockfile is committed or rolled back, the `lock_file`
+ * object can be discarded or reused.
  *
  * If the program exits before `commit_lock_file()`,
  * `commit_lock_file_to()`, or `rollback_lock_file()` is called, the
@@ -85,7 +83,7 @@
  *
  * If you need to close the file descriptor you obtained from a
  * `hold_lock_file_for_*()` function yourself, do so by calling
- * `close_lock_file()`. See "tempfile.h" for more information.
+ * `close_lock_file_gently()`. See "tempfile.h" for more information.
  *
  *
  * Under the covers, a lockfile is just a tempfile with a few helper
  *
  * Similarly, `commit_lock_file`, `commit_lock_file_to`, and
  * `close_lock_file` return 0 on success. On failure they set `errno`
- * appropriately, do their best to roll back the lockfile, and return
- * -1.
+ * appropriately and return -1. The `commit` variants (but not `close`)
+ * do their best to delete the temporary file before returning.
  */
 
 #include "tempfile.h"
 
 struct lock_file {
-       struct tempfile tempfile;
+       struct tempfile *tempfile;
 };
 
+#define LOCK_INIT { NULL }
+
 /* String appended to a filename to derive the lockfile name: */
 #define LOCK_SUFFIX ".lock"
 #define LOCK_SUFFIX_LEN 5
@@ -180,7 +180,7 @@ static inline int hold_lock_file_for_update(
  */
 static inline int is_lock_file_locked(struct lock_file *lk)
 {
-       return is_tempfile_active(&lk->tempfile);
+       return is_tempfile_active(lk->tempfile);
 }
 
 /*
@@ -202,12 +202,13 @@ extern NORETURN void unable_to_lock_die(const char *path, int err);
 /*
  * Associate a stdio stream with the lockfile (which must still be
  * open). Return `NULL` (*without* rolling back the lockfile) on
- * error. The stream is closed automatically when `close_lock_file()`
- * is called or when the file is committed or rolled back.
+ * error. The stream is closed automatically when
+ * `close_lock_file_gently()` is called or when the file is committed or
+ * rolled back.
  */
 static inline FILE *fdopen_lock_file(struct lock_file *lk, const char *mode)
 {
-       return fdopen_tempfile(&lk->tempfile, mode);
+       return fdopen_tempfile(lk->tempfile, mode);
 }
 
 /*
@@ -216,17 +217,17 @@ static inline FILE *fdopen_lock_file(struct lock_file *lk, const char *mode)
  */
 static inline const char *get_lock_file_path(struct lock_file *lk)
 {
-       return get_tempfile_path(&lk->tempfile);
+       return get_tempfile_path(lk->tempfile);
 }
 
 static inline int get_lock_file_fd(struct lock_file *lk)
 {
-       return get_tempfile_fd(&lk->tempfile);
+       return get_tempfile_fd(lk->tempfile);
 }
 
 static inline FILE *get_lock_file_fp(struct lock_file *lk)
 {
-       return get_tempfile_fp(&lk->tempfile);
+       return get_tempfile_fp(lk->tempfile);
 }
 
 /*
@@ -241,22 +242,21 @@ extern char *get_locked_file_path(struct lock_file *lk);
  * lockfile over the file being locked. Return 0 upon success. On
  * failure to `close(2)`, return a negative value and roll back the
  * lock file. Usually `commit_lock_file()`, `commit_lock_file_to()`,
- * or `rollback_lock_file()` should eventually be called if
- * `close_lock_file()` succeeds.
+ * or `rollback_lock_file()` should eventually be called.
  */
-static inline int close_lock_file(struct lock_file *lk)
+static inline int close_lock_file_gently(struct lock_file *lk)
 {
-       return close_tempfile(&lk->tempfile);
+       return close_tempfile_gently(lk->tempfile);
 }
 
 /*
- * Re-open a lockfile that has been closed using `close_lock_file()`
+ * Re-open a lockfile that has been closed using `close_lock_file_gently()`
  * but not yet committed or rolled back. This can be used to implement
  * a sequence of operations like the following:
  *
  * * Lock file.
  *
- * * Write new contents to lockfile, then `close_lock_file()` to
+ * * Write new contents to lockfile, then `close_lock_file_gently()` to
  *   cause the contents to be written to disk.
  *
  * * Pass the name of the lockfile to another program to allow it (and
@@ -270,7 +270,7 @@ static inline int close_lock_file(struct lock_file *lk)
  */
 static inline int reopen_lock_file(struct lock_file *lk)
 {
-       return reopen_tempfile(&lk->tempfile);
+       return reopen_tempfile(lk->tempfile);
 }
 
 /*
index bd574cb75210334b1a4628f182743b49bd389cd7..f2387a32675591d6da83473ef6235ba823721d1c 100644 (file)
@@ -149,16 +149,14 @@ static void handle_from(struct mailinfo *mi, const struct strbuf *from)
        at = strchr(f.buf, '@');
        if (!at) {
                parse_bogus_from(mi, from);
-               return;
+               goto out;
        }
 
        /*
         * If we already have one email, don't take any confusing lines
         */
-       if (mi->email.len && strchr(at + 1, '@')) {
-               strbuf_release(&f);
-               return;
-       }
+       if (mi->email.len && strchr(at + 1, '@'))
+               goto out;
 
        /* Pick up the string around '@', possibly delimited with <>
         * pair; that is the email part.
@@ -198,6 +196,7 @@ static void handle_from(struct mailinfo *mi, const struct strbuf *from)
        }
 
        get_sane_name(&mi->name, &f, &mi->email);
+out:
        strbuf_release(&f);
 }
 
@@ -929,6 +928,7 @@ static int handle_boundary(struct mailinfo *mi, struct strbuf *line)
                        error("Detected mismatched boundaries, can't recover");
                        mi->input_error = -1;
                        mi->content_top = mi->content;
+                       strbuf_release(&newline);
                        return 0;
                }
                handle_filter(mi, &newline);
index 182626c02781384733b863b025bcfc5a1978e893..1d3f8f0d22157e11c1e0db6f230e39240d2f3e63 100644 (file)
 #include "dir.h"
 #include "submodule.h"
 
+struct path_hashmap_entry {
+       struct hashmap_entry e;
+       char path[FLEX_ARRAY];
+};
+
+static int path_hashmap_cmp(const void *cmp_data,
+                           const void *entry,
+                           const void *entry_or_key,
+                           const void *keydata)
+{
+       const struct path_hashmap_entry *a = entry;
+       const struct path_hashmap_entry *b = entry_or_key;
+       const char *key = keydata;
+
+       if (ignore_case)
+               return strcasecmp(a->path, key ? key : b->path);
+       else
+               return strcmp(a->path, key ? key : b->path);
+}
+
+static unsigned int path_hash(const char *path)
+{
+       return ignore_case ? strihash(path) : strhash(path);
+}
+
 static void flush_output(struct merge_options *o)
 {
        if (o->buffer_output < 2 && o->obuf.len) {
@@ -314,29 +339,25 @@ static int save_files_dirs(const unsigned char *sha1,
                struct strbuf *base, const char *path,
                unsigned int mode, int stage, void *context)
 {
+       struct path_hashmap_entry *entry;
        int baselen = base->len;
        struct merge_options *o = context;
 
        strbuf_addstr(base, path);
 
-       if (S_ISDIR(mode))
-               string_list_insert(&o->current_directory_set, base->buf);
-       else
-               string_list_insert(&o->current_file_set, base->buf);
+       FLEX_ALLOC_MEM(entry, path, base->buf, base->len);
+       hashmap_entry_init(entry, path_hash(entry->path));
+       hashmap_add(&o->current_file_dir_set, entry);
 
        strbuf_setlen(base, baselen);
        return (S_ISDIR(mode) ? READ_TREE_RECURSIVE : 0);
 }
 
-static int get_files_dirs(struct merge_options *o, struct tree *tree)
+static void get_files_dirs(struct merge_options *o, struct tree *tree)
 {
-       int n;
        struct pathspec match_all;
        memset(&match_all, 0, sizeof(match_all));
-       if (read_tree_recursive(tree, "", 0, 0, &match_all, save_files_dirs, o))
-               return 0;
-       n = o->current_file_set.nr + o->current_directory_set.nr;
-       return n;
+       read_tree_recursive(tree, "", 0, 0, &match_all, save_files_dirs, o);
 }
 
 /*
@@ -646,6 +667,7 @@ static void add_flattened_path(struct strbuf *out, const char *s)
 
 static char *unique_path(struct merge_options *o, const char *path, const char *branch)
 {
+       struct path_hashmap_entry *entry;
        struct strbuf newpath = STRBUF_INIT;
        int suffix = 0;
        size_t base_len;
@@ -654,14 +676,16 @@ static char *unique_path(struct merge_options *o, const char *path, const char *
        add_flattened_path(&newpath, branch);
 
        base_len = newpath.len;
-       while (string_list_has_string(&o->current_file_set, newpath.buf) ||
-              string_list_has_string(&o->current_directory_set, newpath.buf) ||
+       while (hashmap_get_from_hash(&o->current_file_dir_set,
+                                    path_hash(newpath.buf), newpath.buf) ||
               (!o->call_depth && file_exists(newpath.buf))) {
                strbuf_setlen(&newpath, base_len);
                strbuf_addf(&newpath, "_%d", suffix++);
        }
 
-       string_list_insert(&o->current_file_set, newpath.buf);
+       FLEX_ALLOC_MEM(entry, path, newpath.buf, newpath.len);
+       hashmap_entry_init(entry, path_hash(entry->path));
+       hashmap_add(&o->current_file_dir_set, entry);
        return strbuf_detach(&newpath, NULL);
 }
 
@@ -1945,8 +1969,14 @@ int merge_trees(struct merge_options *o,
        if (unmerged_cache()) {
                struct string_list *entries, *re_head, *re_merge;
                int i;
-               string_list_clear(&o->current_file_set, 1);
-               string_list_clear(&o->current_directory_set, 1);
+               /*
+                * Only need the hashmap while processing entries, so
+                * initialize it here and free it when we are done running
+                * through the entries. Keeping it in the merge_options as
+                * opposed to decaring a local hashmap is for convenience
+                * so that we don't have to pass it to around.
+                */
+               hashmap_init(&o->current_file_dir_set, path_hashmap_cmp, NULL, 512);
                get_files_dirs(o, head);
                get_files_dirs(o, merge);
 
@@ -1956,7 +1986,7 @@ int merge_trees(struct merge_options *o,
                re_merge = get_renames(o, merge, common, head, merge, entries);
                clean = process_renames(o, re_head, re_merge);
                if (clean < 0)
-                       return clean;
+                       goto cleanup;
                for (i = entries->nr-1; 0 <= i; i--) {
                        const char *path = entries->items[i].string;
                        struct stage_data *e = entries->items[i].util;
@@ -1964,8 +1994,10 @@ int merge_trees(struct merge_options *o,
                                int ret = process_entry(o, path, e);
                                if (!ret)
                                        clean = 0;
-                               else if (ret < 0)
-                                       return ret;
+                               else if (ret < 0) {
+                                       clean = ret;
+                                       goto cleanup;
+                               }
                        }
                }
                for (i = 0; i < entries->nr; i++) {
@@ -1975,13 +2007,19 @@ int merge_trees(struct merge_options *o,
                                    entries->items[i].string);
                }
 
+cleanup:
                string_list_clear(re_merge, 0);
                string_list_clear(re_head, 0);
                string_list_clear(entries, 1);
 
+               hashmap_free(&o->current_file_dir_set, 1);
+
                free(re_merge);
                free(re_head);
                free(entries);
+
+               if (clean < 0)
+                       return clean;
        }
        else
                clean = 1;
@@ -2177,8 +2215,6 @@ void init_merge_options(struct merge_options *o)
        if (o->verbosity >= 5)
                o->buffer_output = 0;
        strbuf_init(&o->obuf, 0);
-       string_list_init(&o->current_file_set, 1);
-       string_list_init(&o->current_directory_set, 1);
        string_list_init(&o->df_conflict_file_set, 1);
 }
 
index 735343b4136d998ee3d2ac3672bdd7b5a5ba017e..80d69d140195cc3ba1054050569e56bfc0277b56 100644 (file)
@@ -25,8 +25,7 @@ struct merge_options {
        int show_rename_progress;
        int call_depth;
        struct strbuf obuf;
-       struct string_list current_file_set;
-       struct string_list current_directory_set;
+       struct hashmap current_file_dir_set;
        struct string_list df_conflict_file_set;
 };
 
index bd8dc7a6a7498d42f2576eb866e97dd55797194d..45c98db0a057e93b5f3f84eba2008da86130010d 100644 (file)
@@ -584,9 +584,15 @@ static void lazy_init_name_hash(struct index_state *istate)
        hashmap_init(&istate->dir_hash, dir_entry_cmp, NULL, istate->cache_nr);
 
        if (lookup_lazy_params(istate)) {
-               hashmap_disallow_rehash(&istate->dir_hash, 1);
+               /*
+                * Disable item counting and automatic rehashing because
+                * we do per-chain (mod n) locking rather than whole hashmap
+                * locking and we need to prevent the table-size from changing
+                * and bucket items from being redistributed.
+                */
+               hashmap_disable_item_counting(&istate->dir_hash);
                threaded_lazy_init_name_hash(istate);
-               hashmap_disallow_rehash(&istate->dir_hash, 0);
+               hashmap_enable_item_counting(&istate->dir_hash);
        } else {
                int nr;
                for (nr = 0; nr < istate->cache_nr; nr++)
index b04d2f213116a6413d564107a6e12e3f7317accb..597d43f65c664c04dc3045c3d913146321f9e97b 100644 (file)
@@ -302,7 +302,7 @@ static void write_buf_to_worktree(const struct object_id *obj,
        fd = xopen(path, O_WRONLY | O_EXCL | O_CREAT, 0666);
 
        while (size > 0) {
-               long ret = write_in_full(fd, buf, size);
+               ssize_t ret = write_in_full(fd, buf, size);
                if (ret < 0) {
                        /* Ignore epipe */
                        if (errno == EPIPE)
diff --git a/notes.c b/notes.c
index f090c88363883e65cea66fd63b2c8dfb3f642b7d..5c62862574399acb486530d3fab5a7f659eb7c2f 100644 (file)
--- a/notes.c
+++ b/notes.c
@@ -64,7 +64,7 @@ struct non_note {
 #define CLR_PTR_TYPE(ptr)       ((void *) ((uintptr_t) (ptr) & ~3))
 #define SET_PTR_TYPE(ptr, type) ((void *) ((uintptr_t) (ptr) | (type)))
 
-#define GET_NIBBLE(n, sha1) (((sha1[(n) >> 1]) >> ((~(n) & 0x01) << 2)) & 0x0f)
+#define GET_NIBBLE(n, sha1) ((((sha1)[(n) >> 1]) >> ((~(n) & 0x01) << 2)) & 0x0f)
 
 #define KEY_INDEX (GIT_SHA1_RAWSZ - 1)
 #define FANOUT_PATH_SEPARATORS ((GIT_SHA1_HEXSZ / 2) - 1)
@@ -335,31 +335,20 @@ static void note_tree_free(struct int_node *tree)
 }
 
 /*
- * Convert a partial SHA1 hex string to the corresponding partial SHA1 value.
- * - hex      - Partial SHA1 segment in ASCII hex format
- * - hex_len  - Length of above segment. Must be multiple of 2 between 0 and 40
- * - sha1     - Partial SHA1 value is written here
- * - sha1_len - Max #bytes to store in sha1, Must be >= hex_len / 2, and < 20
- * Returns -1 on error (invalid arguments or invalid SHA1 (not in hex format)).
- * Otherwise, returns number of bytes written to sha1 (i.e. hex_len / 2).
- * Pads sha1 with NULs up to sha1_len (not included in returned length).
+ * Read `len` pairs of hexadecimal digits from `hex` and write the
+ * values to `binary` as `len` bytes. Return 0 on success, or -1 if
+ * the input does not consist of hex digits).
  */
-static int get_oid_hex_segment(const char *hex, unsigned int hex_len,
-               unsigned char *oid, unsigned int oid_len)
+static int hex_to_bytes(unsigned char *binary, const char *hex, size_t len)
 {
-       unsigned int i, len = hex_len >> 1;
-       if (hex_len % 2 != 0 || len > oid_len)
-               return -1;
-       for (i = 0; i < len; i++) {
+       for (; len; len--, hex += 2) {
                unsigned int val = (hexval(hex[0]) << 4) | hexval(hex[1]);
+
                if (val & ~0xff)
                        return -1;
-               *oid++ = val;
-               hex += 2;
+               *binary++ = val;
        }
-       for (; i < oid_len; i++)
-               *oid++ = 0;
-       return len;
+       return 0;
 }
 
 static int non_note_cmp(const struct non_note *a, const struct non_note *b)
@@ -417,13 +406,10 @@ static void load_subtree(struct notes_tree *t, struct leaf_node *subtree,
                struct int_node *node, unsigned int n)
 {
        struct object_id object_oid;
-       unsigned int prefix_len;
+       size_t prefix_len;
        void *buf;
        struct tree_desc desc;
        struct name_entry entry;
-       int len, path_len;
-       unsigned char type;
-       struct leaf_node *l;
 
        buf = fill_tree_descriptor(&desc, &subtree->val_oid);
        if (!buf)
@@ -431,66 +417,79 @@ static void load_subtree(struct notes_tree *t, struct leaf_node *subtree,
                     oid_to_hex(&subtree->val_oid));
 
        prefix_len = subtree->key_oid.hash[KEY_INDEX];
-       assert(prefix_len * 2 >= n);
+       if (prefix_len >= GIT_SHA1_RAWSZ)
+               BUG("prefix_len (%"PRIuMAX") is out of range", (uintmax_t)prefix_len);
+       if (prefix_len * 2 < n)
+               BUG("prefix_len (%"PRIuMAX") is too small", (uintmax_t)prefix_len);
        memcpy(object_oid.hash, subtree->key_oid.hash, prefix_len);
        while (tree_entry(&desc, &entry)) {
-               path_len = strlen(entry.path);
-               len = get_oid_hex_segment(entry.path, path_len,
-                               object_oid.hash + prefix_len, GIT_SHA1_RAWSZ - prefix_len);
-               if (len < 0)
-                       goto handle_non_note; /* entry.path is not a SHA1 */
-               len += prefix_len;
+               unsigned char type;
+               struct leaf_node *l;
+               size_t path_len = strlen(entry.path);
+
+               if (path_len == 2 * (GIT_SHA1_RAWSZ - prefix_len)) {
+                       /* This is potentially the remainder of the SHA-1 */
+
+                       if (!S_ISREG(entry.mode))
+                               /* notes must be blobs */
+                               goto handle_non_note;
+
+                       if (hex_to_bytes(object_oid.hash + prefix_len, entry.path,
+                                        GIT_SHA1_RAWSZ - prefix_len))
+                               goto handle_non_note; /* entry.path is not a SHA1 */
 
-               /*
-                * If object SHA1 is complete (len == 20), assume note object
-                * If object SHA1 is incomplete (len < 20), and current
-                * component consists of 2 hex chars, assume note subtree
-                */
-               if (len <= GIT_SHA1_RAWSZ) {
                        type = PTR_TYPE_NOTE;
-                       l = (struct leaf_node *)
-                               xcalloc(1, sizeof(struct leaf_node));
-                       oidcpy(&l->key_oid, &object_oid);
-                       oidcpy(&l->val_oid, entry.oid);
-                       if (len < GIT_SHA1_RAWSZ) {
-                               if (!S_ISDIR(entry.mode) || path_len != 2)
-                                       goto handle_non_note; /* not subtree */
-                               l->key_oid.hash[KEY_INDEX] = (unsigned char) len;
-                               type = PTR_TYPE_SUBTREE;
-                       }
-                       if (note_tree_insert(t, node, n, l, type,
-                                            combine_notes_concatenate))
-                               die("Failed to load %s %s into notes tree "
-                                   "from %s",
-                                   type == PTR_TYPE_NOTE ? "note" : "subtree",
-                                   oid_to_hex(&l->key_oid), t->ref);
+               } else if (path_len == 2) {
+                       /* This is potentially an internal node */
+                       size_t len = prefix_len;
+
+                       if (!S_ISDIR(entry.mode))
+                               /* internal nodes must be trees */
+                               goto handle_non_note;
+
+                       if (hex_to_bytes(object_oid.hash + len++, entry.path, 1))
+                               goto handle_non_note; /* entry.path is not a SHA1 */
+
+                       /*
+                        * Pad the rest of the SHA-1 with zeros,
+                        * except for the last byte, where we write
+                        * the length:
+                        */
+                       memset(object_oid.hash + len, 0, GIT_SHA1_RAWSZ - len - 1);
+                       object_oid.hash[KEY_INDEX] = (unsigned char)len;
+
+                       type = PTR_TYPE_SUBTREE;
+               } else {
+                       /* This can't be part of a note */
+                       goto handle_non_note;
                }
+
+               l = xcalloc(1, sizeof(*l));
+               oidcpy(&l->key_oid, &object_oid);
+               oidcpy(&l->val_oid, entry.oid);
+               if (note_tree_insert(t, node, n, l, type,
+                                    combine_notes_concatenate))
+                       die("Failed to load %s %s into notes tree "
+                           "from %s",
+                           type == PTR_TYPE_NOTE ? "note" : "subtree",
+                           oid_to_hex(&l->key_oid), t->ref);
+
                continue;
 
 handle_non_note:
                /*
-                * Determine full path for this non-note entry:
-                * The filename is already found in entry.path, but the
-                * directory part of the path must be deduced from the subtree
-                * containing this entry. We assume here that the overall notes
-                * tree follows a strict byte-based progressive fanout
-                * structure (i.e. using 2/38, 2/2/36, etc. fanouts, and not
-                * e.g. 4/36 fanout). This means that if a non-note is found at
-                * path "dead/beef", the following code will register it as
-                * being found on "de/ad/beef".
-                * On the other hand, if you use such non-obvious non-note
-                * paths in the middle of a notes tree, you deserve what's
-                * coming to you ;). Note that for non-notes that are not
-                * SHA1-like at the top level, there will be no problems.
-                *
-                * To conclude, it is strongly advised to make sure non-notes
-                * have at least one non-hex character in the top-level path
-                * component.
+                * Determine full path for this non-note entry. The
+                * filename is already found in entry.path, but the
+                * directory part of the path must be deduced from the
+                * subtree containing this entry based on our
+                * knowledge that the overall notes tree follows a
+                * strict byte-based progressive fanout structure
+                * (i.e. using 2/38, 2/2/36, etc. fanouts).
                 */
                {
                        struct strbuf non_note_path = STRBUF_INIT;
                        const char *q = oid_to_hex(&subtree->key_oid);
-                       int i;
+                       size_t i;
                        for (i = 0; i < prefix_len; i++) {
                                strbuf_addch(&non_note_path, *q++);
                                strbuf_addch(&non_note_path, *q++);
diff --git a/outgoing/packfile.h b/outgoing/packfile.h
deleted file mode 100644 (file)
index e69de29..0000000
index f86fa051c9e67def994b8b487ad34f200967d5e9..f69a5c8d607af191fa8ba04e84da8f02c40823ef 100644 (file)
@@ -40,9 +40,7 @@ static unsigned int pack_max_fds;
 static size_t peak_pack_mapped;
 static size_t pack_mapped;
 struct packed_git *packed_git;
-
-static struct mru packed_git_mru_storage;
-struct mru *packed_git_mru = &packed_git_mru_storage;
+struct mru packed_git_mru;
 
 #define SZ_FMT PRIuMAX
 static inline uintmax_t sz_fmt(size_t s) { return s; }
@@ -861,9 +859,9 @@ static void prepare_packed_git_mru(void)
 {
        struct packed_git *p;
 
-       mru_clear(packed_git_mru);
+       mru_clear(&packed_git_mru);
        for (p = packed_git; p; p = p->next)
-               mru_append(packed_git_mru, p);
+               mru_append(&packed_git_mru, p);
 }
 
 static int prepare_packed_git_run_once = 0;
@@ -1832,9 +1830,9 @@ int find_pack_entry(const unsigned char *sha1, struct pack_entry *e)
        if (!packed_git)
                return 0;
 
-       for (p = packed_git_mru->head; p; p = p->next) {
+       for (p = packed_git_mru.head; p; p = p->next) {
                if (fill_pack_entry(sha1, e, p->item)) {
-                       mru_mark(packed_git_mru, p);
+                       mru_mark(&packed_git_mru, p);
                        return 1;
                }
        }
index e2a23ebc9668b066d337391e96139af49907edfa..1079362450e2f4d8a054ef7cb155a0fbcbc065bc 100644 (file)
@@ -536,7 +536,7 @@ void parse_pathspec(struct pathspec *pathspec,
 {
        struct pathspec_item *item;
        const char *entry = argv ? *argv : NULL;
-       int i, n, prefixlen, warn_empty_string, nr_exclude = 0;
+       int i, n, prefixlen, nr_exclude = 0;
 
        memset(pathspec, 0, sizeof(*pathspec));
 
@@ -569,13 +569,10 @@ void parse_pathspec(struct pathspec *pathspec,
        }
 
        n = 0;
-       warn_empty_string = 1;
        while (argv[n]) {
-               if (*argv[n] == '\0' && warn_empty_string) {
-                       warning(_("empty strings as pathspecs will be made invalid in upcoming releases. "
-                                 "please use . instead if you meant to match all paths"));
-                       warn_empty_string = 0;
-               }
+               if (*argv[n] == '\0')
+                       die("empty string is not a valid pathspec. "
+                                 "please use . instead if you meant to match all paths");
                n++;
        }
 
index 7db9119573abe2c8308bd37c670c027c07b425c8..647bbd3bceda71f15fdf137a37f3fa53e6fa6d86 100644 (file)
@@ -94,9 +94,9 @@ void packet_flush(int fd)
 int packet_flush_gently(int fd)
 {
        packet_trace("0000", 4, 1);
-       if (write_in_full(fd, "0000", 4) == 4)
-               return 0;
-       return error("flush packet write failed");
+       if (write_in_full(fd, "0000", 4) < 0)
+               return error("flush packet write failed");
+       return 0;
 }
 
 void packet_buf_flush(struct strbuf *buf)
@@ -136,19 +136,19 @@ static void format_packet(struct strbuf *out, const char *fmt, va_list args)
 static int packet_write_fmt_1(int fd, int gently,
                              const char *fmt, va_list args)
 {
-       struct strbuf buf = STRBUF_INIT;
-       ssize_t count;
+       static struct strbuf buf = STRBUF_INIT;
 
+       strbuf_reset(&buf);
        format_packet(&buf, fmt, args);
-       count = write_in_full(fd, buf.buf, buf.len);
-       if (count == buf.len)
-               return 0;
-
-       if (!gently) {
-               check_pipe(errno);
-               die_errno("packet write with format failed");
+       if (write_in_full(fd, buf.buf, buf.len) < 0) {
+               if (!gently) {
+                       check_pipe(errno);
+                       die_errno("packet write with format failed");
+               }
+               return error("packet write with format failed");
        }
-       return error("packet write with format failed");
+
+       return 0;
 }
 
 void packet_write_fmt(int fd, const char *fmt, ...)
@@ -183,9 +183,9 @@ static int packet_write_gently(const int fd_out, const char *buf, size_t size)
        packet_size = size + 4;
        set_packet_header(packet_write_buffer, packet_size);
        memcpy(packet_write_buffer + 4, buf, size);
-       if (write_in_full(fd_out, packet_write_buffer, packet_size) == packet_size)
-               return 0;
-       return error("packet write failed");
+       if (write_in_full(fd_out, packet_write_buffer, packet_size) < 0)
+               return error("packet write failed");
+       return 0;
 }
 
 void packet_buf_write(struct strbuf *buf, const char *fmt, ...)
index d1ac5d97efdaff5dc971e38da278dcf1d820ef2c..88d7d679da9c837463f74d9a9277025b008635af 100644 (file)
@@ -10,6 +10,7 @@
 #include "progress.h"
 #include "list-objects.h"
 #include "packfile.h"
+#include "worktree.h"
 
 struct connectivity_progress {
        struct progress *progress;
@@ -177,6 +178,7 @@ void mark_reachable_objects(struct rev_info *revs, int mark_reflog,
 
        /* detached HEAD is not included in the list above */
        head_ref(add_one_ref, revs);
+       other_head_refs(add_one_ref, revs);
 
        /* Add all reflog info */
        if (mark_reflog)
index 40da87ea71f088114c5a40893850cc88efee6953..cdcd11c71efd67130dd8159c9ac4da86e854222a 100644 (file)
@@ -1922,7 +1922,7 @@ static int ce_write_flush(git_SHA_CTX *context, int fd)
        unsigned int buffered = write_buffer_len;
        if (buffered) {
                git_SHA1_Update(context, write_buffer, buffered);
-               if (write_in_full(fd, write_buffer, buffered) != buffered)
+               if (write_in_full(fd, write_buffer, buffered) < 0)
                        return -1;
                write_buffer_len = 0;
        }
@@ -1971,7 +1971,7 @@ static int ce_flush(git_SHA_CTX *context, int fd, unsigned char *sha1)
 
        /* Flush first if not enough space for SHA1 signature */
        if (left + 20 > WRITE_BUFFER_SIZE) {
-               if (write_in_full(fd, write_buffer, left) != left)
+               if (write_in_full(fd, write_buffer, left) < 0)
                        return -1;
                left = 0;
        }
@@ -1980,7 +1980,7 @@ static int ce_flush(git_SHA_CTX *context, int fd, unsigned char *sha1)
        git_SHA1_Final(write_buffer + left, context);
        hashcpy(sha1, write_buffer + left);
        left += 20;
-       return (write_in_full(fd, write_buffer, left) != left) ? -1 : 0;
+       return (write_in_full(fd, write_buffer, left) < 0) ? -1 : 0;
 }
 
 static void ce_smudge_racily_clean_entry(struct cache_entry *ce)
@@ -2103,7 +2103,9 @@ static int ce_write_entry(git_SHA_CTX *c, int fd, struct cache_entry *ce,
                if (!result)
                        result = ce_write(c, fd, to_remove_vi, prefix_size);
                if (!result)
-                       result = ce_write(c, fd, ce->name + common, ce_namelen(ce) - common + 1);
+                       result = ce_write(c, fd, ce->name + common, ce_namelen(ce) - common);
+               if (!result)
+                       result = ce_write(c, fd, padding, 1);
 
                strbuf_splice(previous_name, common, to_remove,
                              ce->name + common, ce_namelen(ce) - common);
@@ -2309,8 +2311,11 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
 
        if (ce_flush(&c, newfd, istate->sha1))
                return -1;
-       if (close_tempfile(tempfile))
-               return error(_("could not close '%s'"), tempfile->filename.buf);
+       if (close_tempfile_gently(tempfile)) {
+               error(_("could not close '%s'"), tempfile->filename.buf);
+               delete_tempfile(&tempfile);
+               return -1;
+       }
        if (stat(tempfile->filename.buf, &st))
                return -1;
        istate->timestamp.sec = (unsigned int)st.st_mtime;
@@ -2334,7 +2339,7 @@ static int commit_locked_index(struct lock_file *lk)
 static int do_write_locked_index(struct index_state *istate, struct lock_file *lock,
                                 unsigned flags)
 {
-       int ret = do_write_index(istate, &lock->tempfile, 0);
+       int ret = do_write_index(istate, lock->tempfile, 0);
        if (ret)
                return ret;
        assert((flags & (COMMIT_LOCK | CLOSE_LOCK)) !=
@@ -2342,7 +2347,7 @@ static int do_write_locked_index(struct index_state *istate, struct lock_file *l
        if (flags & COMMIT_LOCK)
                return commit_locked_index(lock);
        else if (flags & CLOSE_LOCK)
-               return close_lock_file(lock);
+               return close_lock_file_gently(lock);
        else
                return ret;
 }
@@ -2417,34 +2422,33 @@ static int clean_shared_index_files(const char *current_hex)
        return 0;
 }
 
-static struct tempfile temporary_sharedindex;
-
 static int write_shared_index(struct index_state *istate,
                              struct lock_file *lock, unsigned flags)
 {
+       struct tempfile *temp;
        struct split_index *si = istate->split_index;
-       int fd, ret;
+       int ret;
 
-       fd = mks_tempfile(&temporary_sharedindex, git_path("sharedindex_XXXXXX"));
-       if (fd < 0) {
+       temp = mks_tempfile(git_path("sharedindex_XXXXXX"));
+       if (!temp) {
                hashclr(si->base_sha1);
                return do_write_locked_index(istate, lock, flags);
        }
        move_cache_to_base_index(istate);
-       ret = do_write_index(si->base, &temporary_sharedindex, 1);
+       ret = do_write_index(si->base, temp, 1);
        if (ret) {
-               delete_tempfile(&temporary_sharedindex);
+               delete_tempfile(&temp);
                return ret;
        }
-       ret = adjust_shared_perm(get_tempfile_path(&temporary_sharedindex));
+       ret = adjust_shared_perm(get_tempfile_path(temp));
        if (ret) {
                int save_errno = errno;
-               error("cannot fix permission bits on %s", get_tempfile_path(&temporary_sharedindex));
-               delete_tempfile(&temporary_sharedindex);
+               error("cannot fix permission bits on %s", get_tempfile_path(temp));
+               delete_tempfile(&temp);
                errno = save_errno;
                return ret;
        }
-       ret = rename_tempfile(&temporary_sharedindex,
+       ret = rename_tempfile(&temp,
                              git_path("sharedindex.%s", sha1_to_hex(si->base->sha1)));
        if (!ret) {
                hashcpy(si->base_sha1, si->base->sha1);
diff --git a/refs.c b/refs.c
index b0106b8162c7b8e3770e04c212abb05f1afbd0c3..83f0e687857dfaee3b91ce4d47b16f2f16945ce2 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -336,12 +336,6 @@ int for_each_tag_ref(each_ref_fn fn, void *cb_data)
        return refs_for_each_tag_ref(get_main_ref_store(), fn, cb_data);
 }
 
-int for_each_tag_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data)
-{
-       return refs_for_each_tag_ref(get_submodule_ref_store(submodule),
-                                    fn, cb_data);
-}
-
 int refs_for_each_branch_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)
 {
        return refs_for_each_ref_in(refs, "refs/heads/", fn, cb_data);
@@ -352,12 +346,6 @@ int for_each_branch_ref(each_ref_fn fn, void *cb_data)
        return refs_for_each_branch_ref(get_main_ref_store(), fn, cb_data);
 }
 
-int for_each_branch_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data)
-{
-       return refs_for_each_branch_ref(get_submodule_ref_store(submodule),
-                                       fn, cb_data);
-}
-
 int refs_for_each_remote_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)
 {
        return refs_for_each_ref_in(refs, "refs/remotes/", fn, cb_data);
@@ -368,12 +356,6 @@ int for_each_remote_ref(each_ref_fn fn, void *cb_data)
        return refs_for_each_remote_ref(get_main_ref_store(), fn, cb_data);
 }
 
-int for_each_remote_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data)
-{
-       return refs_for_each_remote_ref(get_submodule_ref_store(submodule),
-                                       fn, cb_data);
-}
-
 int head_ref_namespaced(each_ref_fn fn, void *cb_data)
 {
        struct strbuf buf = STRBUF_INIT;
@@ -612,7 +594,7 @@ static int write_pseudoref(const char *pseudoref, const unsigned char *sha1,
        if (fd < 0) {
                strbuf_addf(err, "could not open '%s' for writing: %s",
                            filename, strerror(errno));
-               return -1;
+               goto done;
        }
 
        if (old_sha1) {
@@ -627,7 +609,7 @@ static int write_pseudoref(const char *pseudoref, const unsigned char *sha1,
                }
        }
 
-       if (write_in_full(fd, buf.buf, buf.len) != buf.len) {
+       if (write_in_full(fd, buf.buf, buf.len) < 0) {
                strbuf_addf(err, "could not write to '%s'", filename);
                rollback_lock_file(&lock);
                goto done;
@@ -957,6 +939,8 @@ int ref_transaction_update(struct ref_transaction *transaction,
                return -1;
        }
 
+       flags &= REF_TRANSACTION_UPDATE_ALLOWED_FLAGS;
+
        flags |= (new_sha1 ? REF_HAVE_NEW : 0) | (old_sha1 ? REF_HAVE_OLD : 0);
 
        ref_transaction_add_update(transaction, refname, flags,
@@ -1266,19 +1250,13 @@ int refs_rename_ref_available(struct ref_store *refs,
        return ok;
 }
 
-int head_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data)
+int refs_head_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)
 {
        struct object_id oid;
        int flag;
 
-       if (submodule) {
-               if (resolve_gitlink_ref(submodule, "HEAD", oid.hash) == 0)
-                       return fn("HEAD", &oid, 0, cb_data);
-
-               return 0;
-       }
-
-       if (!read_ref_full("HEAD", RESOLVE_REF_READING, oid.hash, &flag))
+       if (!refs_read_ref_full(refs, "HEAD", RESOLVE_REF_READING,
+                               oid.hash, &flag))
                return fn("HEAD", &oid, flag, cb_data);
 
        return 0;
@@ -1286,7 +1264,7 @@ int head_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data)
 
 int head_ref(each_ref_fn fn, void *cb_data)
 {
-       return head_ref_submodule(NULL, fn, cb_data);
+       return refs_head_ref(get_main_ref_store(), fn, cb_data);
 }
 
 struct ref_iterator *refs_ref_iterator_begin(
@@ -1344,11 +1322,6 @@ int for_each_ref(each_ref_fn fn, void *cb_data)
        return refs_for_each_ref(get_main_ref_store(), fn, cb_data);
 }
 
-int for_each_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data)
-{
-       return refs_for_each_ref(get_submodule_ref_store(submodule), fn, cb_data);
-}
-
 int refs_for_each_ref_in(struct ref_store *refs, const char *prefix,
                         each_ref_fn fn, void *cb_data)
 {
@@ -1370,23 +1343,15 @@ int for_each_fullref_in(const char *prefix, each_ref_fn fn, void *cb_data, unsig
                               prefix, fn, 0, flag, cb_data);
 }
 
-int for_each_ref_in_submodule(const char *submodule, const char *prefix,
-                             each_ref_fn fn, void *cb_data)
-{
-       return refs_for_each_ref_in(get_submodule_ref_store(submodule),
-                                   prefix, fn, cb_data);
-}
-
-int for_each_fullref_in_submodule(const char *submodule, const char *prefix,
-                                 each_ref_fn fn, void *cb_data,
-                                 unsigned int broken)
+int refs_for_each_fullref_in(struct ref_store *refs, const char *prefix,
+                            each_ref_fn fn, void *cb_data,
+                            unsigned int broken)
 {
        unsigned int flag = 0;
 
        if (broken)
                flag = DO_FOR_EACH_INCLUDE_BROKEN;
-       return do_for_each_ref(get_submodule_ref_store(submodule),
-                              prefix, fn, 0, flag, cb_data);
+       return do_for_each_ref(refs, prefix, fn, 0, flag, cb_data);
 }
 
 int for_each_replace_ref(each_ref_fn fn, void *cb_data)
@@ -1394,7 +1359,7 @@ int for_each_replace_ref(each_ref_fn fn, void *cb_data)
        return do_for_each_ref(get_main_ref_store(),
                               git_replace_ref_base, fn,
                               strlen(git_replace_ref_base),
-                              0, cb_data);
+                              DO_FOR_EACH_INCLUDE_BROKEN, cb_data);
 }
 
 int for_each_namespaced_ref(each_ref_fn fn, void *cb_data)
@@ -1521,25 +1486,10 @@ const char *resolve_ref_unsafe(const char *refname, int resolve_flags,
 int resolve_gitlink_ref(const char *submodule, const char *refname,
                        unsigned char *sha1)
 {
-       size_t len = strlen(submodule);
        struct ref_store *refs;
        int flags;
 
-       while (len && submodule[len - 1] == '/')
-               len--;
-
-       if (!len)
-               return -1;
-
-       if (submodule[len]) {
-               /* We need to strip off one or more trailing slashes */
-               char *stripped = xmemdupz(submodule, len);
-
-               refs = get_submodule_ref_store(stripped);
-               free(stripped);
-       } else {
-               refs = get_submodule_ref_store(submodule);
-       }
+       refs = get_submodule_ref_store(submodule);
 
        if (!refs)
                return -1;
@@ -1654,31 +1604,32 @@ struct ref_store *get_submodule_ref_store(const char *submodule)
 {
        struct strbuf submodule_sb = STRBUF_INIT;
        struct ref_store *refs;
-       int ret;
+       char *to_free = NULL;
+       size_t len;
 
-       if (!submodule || !*submodule) {
-               /*
-                * FIXME: This case is ideally not allowed. But that
-                * can't happen until we clean up all the callers.
-                */
-               return get_main_ref_store();
-       }
+       if (!submodule)
+               return NULL;
+
+       len = strlen(submodule);
+       while (len && is_dir_sep(submodule[len - 1]))
+               len--;
+       if (!len)
+               return NULL;
+
+       if (submodule[len])
+               /* We need to strip off one or more trailing slashes */
+               submodule = to_free = xmemdupz(submodule, len);
 
        refs = lookup_ref_store_map(&submodule_ref_stores, submodule);
        if (refs)
-               return refs;
+               goto done;
 
        strbuf_addstr(&submodule_sb, submodule);
-       ret = is_nonbare_repository_dir(&submodule_sb);
-       strbuf_release(&submodule_sb);
-       if (!ret)
-               return NULL;
+       if (!is_nonbare_repository_dir(&submodule_sb))
+               goto done;
 
-       ret = submodule_to_gitdir(&submodule_sb, submodule);
-       if (ret) {
-               strbuf_release(&submodule_sb);
-               return NULL;
-       }
+       if (submodule_to_gitdir(&submodule_sb, submodule))
+               goto done;
 
        /* assume that add_submodule_odb() has been called */
        refs = ref_store_init(submodule_sb.buf,
@@ -1686,7 +1637,10 @@ struct ref_store *get_submodule_ref_store(const char *submodule)
        register_ref_store_map(&submodule_ref_stores, "submodule",
                               refs, submodule);
 
+done:
        strbuf_release(&submodule_sb);
+       free(to_free);
+
        return refs;
 }
 
diff --git a/refs.h b/refs.h
index 6daa78eb50ddc195c6d5bfb568d0b05ab08f7923..ae33a5f74d65dda62c84deb1eaae1774a0d21214 100644 (file)
--- a/refs.h
+++ b/refs.h
@@ -275,6 +275,8 @@ typedef int each_ref_fn(const char *refname,
  * modifies the reference also returns a nonzero value to immediately
  * stop the iteration. Returned references are sorted.
  */
+int refs_head_ref(struct ref_store *refs,
+                 each_ref_fn fn, void *cb_data);
 int refs_for_each_ref(struct ref_store *refs,
                      each_ref_fn fn, void *cb_data);
 int refs_for_each_ref_in(struct ref_store *refs, const char *prefix,
@@ -289,6 +291,9 @@ int refs_for_each_remote_ref(struct ref_store *refs,
 int head_ref(each_ref_fn fn, void *cb_data);
 int for_each_ref(each_ref_fn fn, void *cb_data);
 int for_each_ref_in(const char *prefix, each_ref_fn fn, void *cb_data);
+int refs_for_each_fullref_in(struct ref_store *refs, const char *prefix,
+                            each_ref_fn fn, void *cb_data,
+                            unsigned int broken);
 int for_each_fullref_in(const char *prefix, each_ref_fn fn, void *cb_data,
                        unsigned int broken);
 int for_each_tag_ref(each_ref_fn fn, void *cb_data);
@@ -299,21 +304,6 @@ int for_each_glob_ref(each_ref_fn fn, const char *pattern, void *cb_data);
 int for_each_glob_ref_in(each_ref_fn fn, const char *pattern,
                         const char *prefix, void *cb_data);
 
-int head_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data);
-int for_each_ref_submodule(const char *submodule,
-                          each_ref_fn fn, void *cb_data);
-int for_each_ref_in_submodule(const char *submodule, const char *prefix,
-                             each_ref_fn fn, void *cb_data);
-int for_each_fullref_in_submodule(const char *submodule, const char *prefix,
-                                 each_ref_fn fn, void *cb_data,
-                                 unsigned int broken);
-int for_each_tag_ref_submodule(const char *submodule,
-                              each_ref_fn fn, void *cb_data);
-int for_each_branch_ref_submodule(const char *submodule,
-                                 each_ref_fn fn, void *cb_data);
-int for_each_remote_ref_submodule(const char *submodule,
-                                 each_ref_fn fn, void *cb_data);
-
 int head_ref_namespaced(each_ref_fn fn, void *cb_data);
 int for_each_namespaced_ref(each_ref_fn fn, void *cb_data);
 
@@ -354,6 +344,14 @@ int refs_pack_refs(struct ref_store *refs, unsigned int flags);
 #define REF_NODEREF    0x01
 #define REF_FORCE_CREATE_REFLOG 0x40
 
+/*
+ * Flags that can be passed in to ref_transaction_update
+ */
+#define REF_TRANSACTION_UPDATE_ALLOWED_FLAGS \
+       REF_ISPRUNING |                      \
+       REF_FORCE_CREATE_REFLOG |            \
+       REF_NODEREF
+
 /*
  * Setup reflog before using. Fill in err and return -1 on failure.
  */
index fccbc24ac4ad3631b96a4796f4b1f89913fa0ea3..dac33628b37d4e19ea15e6706f1ab67c9b3a3e6c 100644 (file)
@@ -12,7 +12,7 @@
 
 struct ref_lock {
        char *ref_name;
-       struct lock_file *lk;
+       struct lock_file lk;
        struct object_id old_oid;
 };
 
@@ -106,15 +106,6 @@ static void files_reflog_path(struct files_ref_store *refs,
                              struct strbuf *sb,
                              const char *refname)
 {
-       if (!refname) {
-               /*
-                * FIXME: of course this is wrong in multi worktree
-                * setting. To be fixed real soon.
-                */
-               strbuf_addf(sb, "%s/logs", refs->gitcommondir);
-               return;
-       }
-
        switch (ref_type(refname)) {
        case REF_TYPE_PER_WORKTREE:
        case REF_TYPE_PSEUDOREF:
@@ -418,9 +409,7 @@ static int files_read_raw_ref(struct ref_store *ref_store,
 
 static void unlock_ref(struct ref_lock *lock)
 {
-       /* Do not free lock->lk -- atexit() still looks at them */
-       if (lock->lk)
-               rollback_lock_file(lock->lk);
+       rollback_lock_file(&lock->lk);
        free(lock->ref_name);
        free(lock);
 }
@@ -534,11 +523,8 @@ static int lock_raw_ref(struct files_ref_store *refs,
                goto error_return;
        }
 
-       if (!lock->lk)
-               lock->lk = xcalloc(1, sizeof(struct lock_file));
-
        if (hold_lock_file_for_update_timeout(
-                           lock->lk, ref_file.buf, LOCK_NO_DEREF,
+                           &lock->lk, ref_file.buf, LOCK_NO_DEREF,
                            get_files_ref_lock_timeout_ms()) < 0) {
                if (errno == ENOENT && --attempts_remaining > 0) {
                        /*
@@ -949,11 +935,9 @@ static struct ref_lock *lock_ref_sha1_basic(struct files_ref_store *refs,
                goto error_return;
        }
 
-       lock->lk = xcalloc(1, sizeof(struct lock_file));
-
        lock->ref_name = xstrdup(refname);
 
-       if (raceproof_create_file(ref_file.buf, create_reflock, lock->lk)) {
+       if (raceproof_create_file(ref_file.buf, create_reflock, &lock->lk)) {
                last_errno = errno;
                unable_to_lock_message(ref_file.buf, errno, err);
                goto error_return;
@@ -1057,11 +1041,17 @@ static void prune_ref(struct files_ref_store *refs, struct ref_to_prune *r)
        strbuf_release(&err);
 }
 
-static void prune_refs(struct files_ref_store *refs, struct ref_to_prune *r)
+/*
+ * Prune the loose versions of the references in the linked list
+ * `*refs_to_prune`, freeing the entries in the list as we go.
+ */
+static void prune_refs(struct files_ref_store *refs, struct ref_to_prune **refs_to_prune)
 {
-       while (r) {
+       while (*refs_to_prune) {
+               struct ref_to_prune *r = *refs_to_prune;
+               *refs_to_prune = r->next;
                prune_ref(refs, r);
-               r = r->next;
+               free(r);
        }
 }
 
@@ -1100,6 +1090,11 @@ static int files_pack_refs(struct ref_store *ref_store, unsigned int flags)
        int ok;
        struct ref_to_prune *refs_to_prune = NULL;
        struct strbuf err = STRBUF_INIT;
+       struct ref_transaction *transaction;
+
+       transaction = ref_store_transaction_begin(refs->packed_ref_store, &err);
+       if (!transaction)
+               return -1;
 
        packed_refs_lock(refs->packed_ref_store, LOCK_DIE_ON_ERROR, &err);
 
@@ -1115,12 +1110,14 @@ static int files_pack_refs(struct ref_store *ref_store, unsigned int flags)
                        continue;
 
                /*
-                * Create an entry in the packed-refs cache equivalent
-                * to the one from the loose ref cache, except that
-                * we don't copy the peeled status, because we want it
-                * to be re-peeled.
+                * Add a reference creation for this reference to the
+                * packed-refs transaction:
                 */
-               add_packed_ref(refs->packed_ref_store, iter->refname, iter->oid);
+               if (ref_transaction_update(transaction, iter->refname,
+                                          iter->oid->hash, NULL,
+                                          REF_NODEREF, NULL, &err))
+                       die("failure preparing to create packed reference %s: %s",
+                           iter->refname, err.buf);
 
                /* Schedule the loose reference for pruning if requested. */
                if ((flags & PACK_REFS_PRUNE)) {
@@ -1134,11 +1131,14 @@ static int files_pack_refs(struct ref_store *ref_store, unsigned int flags)
        if (ok != ITER_DONE)
                die("error while iterating over references");
 
-       if (commit_packed_refs(refs->packed_ref_store, &err))
-               die("unable to overwrite old ref-pack file: %s", err.buf);
+       if (ref_transaction_commit(transaction, &err))
+               die("unable to write new packed-refs: %s", err.buf);
+
+       ref_transaction_free(transaction);
+
        packed_refs_unlock(refs->packed_ref_store);
 
-       prune_refs(refs, refs_to_prune);
+       prune_refs(refs, &refs_to_prune);
        strbuf_release(&err);
        return 0;
 }
@@ -1157,7 +1157,7 @@ static int files_delete_refs(struct ref_store *ref_store, const char *msg,
        if (packed_refs_lock(refs->packed_ref_store, 0, &err))
                goto error;
 
-       if (repack_without_refs(refs->packed_ref_store, refnames, &err)) {
+       if (refs_delete_refs(refs->packed_ref_store, msg, refnames, flags)) {
                packed_refs_unlock(refs->packed_ref_store);
                goto error;
        }
@@ -1402,16 +1402,16 @@ static int files_rename_ref(struct ref_store *ref_store,
        return ret;
 }
 
-static int close_ref(struct ref_lock *lock)
+static int close_ref_gently(struct ref_lock *lock)
 {
-       if (close_lock_file(lock->lk))
+       if (close_lock_file_gently(&lock->lk))
                return -1;
        return 0;
 }
 
 static int commit_ref(struct ref_lock *lock)
 {
-       char *path = get_locked_file_path(lock->lk);
+       char *path = get_locked_file_path(&lock->lk);
        struct stat st;
 
        if (!lstat(path, &st) && S_ISDIR(st.st_mode)) {
@@ -1435,7 +1435,7 @@ static int commit_ref(struct ref_lock *lock)
                free(path);
        }
 
-       if (commit_lock_file(lock->lk))
+       if (commit_lock_file(&lock->lk))
                return -1;
        return 0;
 }
@@ -1549,7 +1549,7 @@ static int log_ref_write_fd(int fd, const struct object_id *old_oid,
 
        written = len <= maxlen ? write_in_full(fd, logrec, len) : -1;
        free(logrec);
-       if (written != len)
+       if (written < 0)
                return -1;
 
        return 0;
@@ -1627,12 +1627,12 @@ static int write_ref_to_lockfile(struct ref_lock *lock,
                unlock_ref(lock);
                return -1;
        }
-       fd = get_lock_file_fd(lock->lk);
-       if (write_in_full(fd, oid_to_hex(oid), GIT_SHA1_HEXSZ) != GIT_SHA1_HEXSZ ||
-           write_in_full(fd, &term, 1) != 1 ||
-           close_ref(lock) < 0) {
+       fd = get_lock_file_fd(&lock->lk);
+       if (write_in_full(fd, oid_to_hex(oid), GIT_SHA1_HEXSZ) < 0 ||
+           write_in_full(fd, &term, 1) < 0 ||
+           close_ref_gently(lock) < 0) {
                strbuf_addf(err,
-                           "couldn't write '%s'", get_lock_file_path(lock->lk));
+                           "couldn't write '%s'", get_lock_file_path(&lock->lk));
                unlock_ref(lock);
                return -1;
        }
@@ -1709,7 +1709,7 @@ static int create_ref_symlink(struct ref_lock *lock, const char *target)
 {
        int ret = -1;
 #ifndef NO_SYMLINK_HEAD
-       char *ref_path = get_locked_file_path(lock->lk);
+       char *ref_path = get_locked_file_path(&lock->lk);
        unlink(ref_path);
        ret = symlink(target, ref_path);
        free(ref_path);
@@ -1745,14 +1745,14 @@ static int create_symref_locked(struct files_ref_store *refs,
                return 0;
        }
 
-       if (!fdopen_lock_file(lock->lk, "w"))
+       if (!fdopen_lock_file(&lock->lk, "w"))
                return error("unable to fdopen %s: %s",
-                            lock->lk->tempfile.filename.buf, strerror(errno));
+                            lock->lk.tempfile->filename.buf, strerror(errno));
 
        update_symref_reflog(refs, lock, refname, target, logmsg);
 
        /* no error check; commit_ref will check ferror */
-       fprintf(lock->lk->tempfile.fp, "ref: %s\n", target);
+       fprintf(lock->lk.tempfile->fp, "ref: %s\n", target);
        if (commit_ref(lock) < 0)
                return error("unable to write symref for %s: %s", refname,
                             strerror(errno));
@@ -2059,23 +2059,63 @@ static struct ref_iterator_vtable files_reflog_iterator_vtable = {
        files_reflog_iterator_abort
 };
 
-static struct ref_iterator *files_reflog_iterator_begin(struct ref_store *ref_store)
+static struct ref_iterator *reflog_iterator_begin(struct ref_store *ref_store,
+                                                 const char *gitdir)
 {
-       struct files_ref_store *refs =
-               files_downcast(ref_store, REF_STORE_READ,
-                              "reflog_iterator_begin");
        struct files_reflog_iterator *iter = xcalloc(1, sizeof(*iter));
        struct ref_iterator *ref_iterator = &iter->base;
        struct strbuf sb = STRBUF_INIT;
 
        base_ref_iterator_init(ref_iterator, &files_reflog_iterator_vtable);
-       files_reflog_path(refs, &sb, NULL);
+       strbuf_addf(&sb, "%s/logs", gitdir);
        iter->dir_iterator = dir_iterator_begin(sb.buf);
        iter->ref_store = ref_store;
        strbuf_release(&sb);
+
        return ref_iterator;
 }
 
+static enum iterator_selection reflog_iterator_select(
+       struct ref_iterator *iter_worktree,
+       struct ref_iterator *iter_common,
+       void *cb_data)
+{
+       if (iter_worktree) {
+               /*
+                * We're a bit loose here. We probably should ignore
+                * common refs if they are accidentally added as
+                * per-worktree refs.
+                */
+               return ITER_SELECT_0;
+       } else if (iter_common) {
+               if (ref_type(iter_common->refname) == REF_TYPE_NORMAL)
+                       return ITER_SELECT_1;
+
+               /*
+                * The main ref store may contain main worktree's
+                * per-worktree refs, which should be ignored
+                */
+               return ITER_SKIP_1;
+       } else
+               return ITER_DONE;
+}
+
+static struct ref_iterator *files_reflog_iterator_begin(struct ref_store *ref_store)
+{
+       struct files_ref_store *refs =
+               files_downcast(ref_store, REF_STORE_READ,
+                              "reflog_iterator_begin");
+
+       if (!strcmp(refs->gitdir, refs->gitcommondir)) {
+               return reflog_iterator_begin(ref_store, refs->gitcommondir);
+       } else {
+               return merge_ref_iterator_begin(
+                       reflog_iterator_begin(ref_store, refs->gitdir),
+                       reflog_iterator_begin(ref_store, refs->gitcommondir),
+                       reflog_iterator_select, refs);
+       }
+}
+
 /*
  * If update is a direct update of head_ref (the reference pointed to
  * by HEAD), then add an extra REF_LOG_ONLY update for HEAD.
@@ -2099,11 +2139,10 @@ static int split_head_update(struct ref_update *update,
 
        /*
         * First make sure that HEAD is not already in the
-        * transaction. This insertion is O(N) in the transaction
+        * transaction. This check is O(lg N) in the transaction
         * size, but it happens at most once per transaction.
         */
-       item = string_list_insert(affected_refnames, "HEAD");
-       if (item->util) {
+       if (string_list_has_string(affected_refnames, "HEAD")) {
                /* An entry already existed */
                strbuf_addf(err,
                            "multiple updates for 'HEAD' (including one "
@@ -2118,6 +2157,14 @@ static int split_head_update(struct ref_update *update,
                        update->new_oid.hash, update->old_oid.hash,
                        update->msg);
 
+       /*
+        * Add "HEAD". This insertion is O(N) in the transaction
+        * size, but it happens at most once per transaction.
+        * Add new_update->refname instead of a literal "HEAD".
+        */
+       if (strcmp(new_update->refname, "HEAD"))
+               BUG("%s unexpectedly not 'HEAD'", new_update->refname);
+       item = string_list_insert(affected_refnames, new_update->refname);
        item->util = new_update;
 
        return 0;
@@ -2144,13 +2191,12 @@ static int split_symref_update(struct files_ref_store *refs,
 
        /*
         * First make sure that referent is not already in the
-        * transaction. This insertion is O(N) in the transaction
+        * transaction. This check is O(lg N) in the transaction
         * size, but it happens at most once per symref in a
         * transaction.
         */
-       item = string_list_insert(affected_refnames, referent);
-       if (item->util) {
-               /* An entry already existed */
+       if (string_list_has_string(affected_refnames, referent)) {
+               /* An entry already exists */
                strbuf_addf(err,
                            "multiple updates for '%s' (including one "
                            "via symref '%s') are not allowed",
@@ -2185,6 +2231,17 @@ static int split_symref_update(struct files_ref_store *refs,
        update->flags |= REF_LOG_ONLY | REF_NODEREF;
        update->flags &= ~REF_HAVE_OLD;
 
+       /*
+        * Add the referent. This insertion is O(N) in the transaction
+        * size, but it happens at most once per symref in a
+        * transaction. Make sure to add new_update->refname, which will
+        * be valid as long as affected_refnames is in use, and NOT
+        * referent, which might soon be freed by our caller.
+        */
+       item = string_list_insert(affected_refnames, new_update->refname);
+       if (item->util)
+               BUG("%s unexpectedly found in affected_refnames",
+                   new_update->refname);
        item->util = new_update;
 
        return 0;
@@ -2256,7 +2313,7 @@ static int lock_ref_for_update(struct files_ref_store *refs,
        struct strbuf referent = STRBUF_INIT;
        int mustexist = (update->flags & REF_HAVE_OLD) &&
                !is_null_oid(&update->old_oid);
-       int ret;
+       int ret = 0;
        struct ref_lock *lock;
 
        files_assert_main_repository(refs, "lock_ref_for_update");
@@ -2268,7 +2325,7 @@ static int lock_ref_for_update(struct files_ref_store *refs,
                ret = split_head_update(update, transaction, head_ref,
                                        affected_refnames, err);
                if (ret)
-                       return ret;
+                       goto out;
        }
 
        ret = lock_raw_ref(refs, update->refname, mustexist,
@@ -2282,7 +2339,7 @@ static int lock_ref_for_update(struct files_ref_store *refs,
                strbuf_addf(err, "cannot lock ref '%s': %s",
                            original_update_refname(update), reason);
                free(reason);
-               return ret;
+               goto out;
        }
 
        update->backend_data = lock;
@@ -2301,10 +2358,12 @@ static int lock_ref_for_update(struct files_ref_store *refs,
                                        strbuf_addf(err, "cannot lock ref '%s': "
                                                    "error reading reference",
                                                    original_update_refname(update));
-                                       return -1;
+                                       ret = TRANSACTION_GENERIC_ERROR;
+                                       goto out;
                                }
                        } else if (check_old_oid(update, &lock->old_oid, err)) {
-                               return TRANSACTION_GENERIC_ERROR;
+                               ret = TRANSACTION_GENERIC_ERROR;
+                               goto out;
                        }
                } else {
                        /*
@@ -2318,13 +2377,15 @@ static int lock_ref_for_update(struct files_ref_store *refs,
                                                  referent.buf, transaction,
                                                  affected_refnames, err);
                        if (ret)
-                               return ret;
+                               goto out;
                }
        } else {
                struct ref_update *parent_update;
 
-               if (check_old_oid(update, &lock->old_oid, err))
-                       return TRANSACTION_GENERIC_ERROR;
+               if (check_old_oid(update, &lock->old_oid, err)) {
+                       ret = TRANSACTION_GENERIC_ERROR;
+                       goto out;
+               }
 
                /*
                 * If this update is happening indirectly because of a
@@ -2361,7 +2422,8 @@ static int lock_ref_for_update(struct files_ref_store *refs,
                                    "cannot update ref '%s': %s",
                                    update->refname, write_err);
                        free(write_err);
-                       return TRANSACTION_GENERIC_ERROR;
+                       ret = TRANSACTION_GENERIC_ERROR;
+                       goto out;
                } else {
                        update->flags |= REF_NEEDS_COMMIT;
                }
@@ -2372,22 +2434,35 @@ static int lock_ref_for_update(struct files_ref_store *refs,
                 * the lockfile is still open. Close it to
                 * free up the file descriptor:
                 */
-               if (close_ref(lock)) {
+               if (close_ref_gently(lock)) {
                        strbuf_addf(err, "couldn't close '%s.lock'",
                                    update->refname);
-                       return TRANSACTION_GENERIC_ERROR;
+                       ret = TRANSACTION_GENERIC_ERROR;
+                       goto out;
                }
        }
-       return 0;
+
+out:
+       strbuf_release(&referent);
+       return ret;
 }
 
+struct files_transaction_backend_data {
+       struct ref_transaction *packed_transaction;
+       int packed_refs_locked;
+};
+
 /*
  * Unlock any references in `transaction` that are still locked, and
  * mark the transaction closed.
  */
-static void files_transaction_cleanup(struct ref_transaction *transaction)
+static void files_transaction_cleanup(struct files_ref_store *refs,
+                                     struct ref_transaction *transaction)
 {
        size_t i;
+       struct files_transaction_backend_data *backend_data =
+               transaction->backend_data;
+       struct strbuf err = STRBUF_INIT;
 
        for (i = 0; i < transaction->nr; i++) {
                struct ref_update *update = transaction->updates[i];
@@ -2399,6 +2474,17 @@ static void files_transaction_cleanup(struct ref_transaction *transaction)
                }
        }
 
+       if (backend_data->packed_transaction &&
+           ref_transaction_abort(backend_data->packed_transaction, &err)) {
+               error("error aborting transaction: %s", err.buf);
+               strbuf_release(&err);
+       }
+
+       if (backend_data->packed_refs_locked)
+               packed_refs_unlock(refs->packed_ref_store);
+
+       free(backend_data);
+
        transaction->state = REF_TRANSACTION_CLOSED;
 }
 
@@ -2415,12 +2501,17 @@ static int files_transaction_prepare(struct ref_store *ref_store,
        char *head_ref = NULL;
        int head_type;
        struct object_id head_oid;
+       struct files_transaction_backend_data *backend_data;
+       struct ref_transaction *packed_transaction = NULL;
 
        assert(err);
 
        if (!transaction->nr)
                goto cleanup;
 
+       backend_data = xcalloc(1, sizeof(*backend_data));
+       transaction->backend_data = backend_data;
+
        /*
         * Fail if a refname appears more than once in the
         * transaction. (If we end up splitting up any updates using
@@ -2487,6 +2578,41 @@ static int files_transaction_prepare(struct ref_store *ref_store,
                                          head_ref, &affected_refnames, err);
                if (ret)
                        break;
+
+               if (update->flags & REF_DELETING &&
+                   !(update->flags & REF_LOG_ONLY) &&
+                   !(update->flags & REF_ISPRUNING)) {
+                       /*
+                        * This reference has to be deleted from
+                        * packed-refs if it exists there.
+                        */
+                       if (!packed_transaction) {
+                               packed_transaction = ref_store_transaction_begin(
+                                               refs->packed_ref_store, err);
+                               if (!packed_transaction) {
+                                       ret = TRANSACTION_GENERIC_ERROR;
+                                       goto cleanup;
+                               }
+
+                               backend_data->packed_transaction =
+                                       packed_transaction;
+                       }
+
+                       ref_transaction_add_update(
+                                       packed_transaction, update->refname,
+                                       update->flags & ~REF_HAVE_OLD,
+                                       update->new_oid.hash, update->old_oid.hash,
+                                       NULL);
+               }
+       }
+
+       if (packed_transaction) {
+               if (packed_refs_lock(refs->packed_ref_store, 0, err)) {
+                       ret = TRANSACTION_GENERIC_ERROR;
+                       goto cleanup;
+               }
+               backend_data->packed_refs_locked = 1;
+               ret = ref_transaction_prepare(packed_transaction, err);
        }
 
 cleanup:
@@ -2494,7 +2620,7 @@ static int files_transaction_prepare(struct ref_store *ref_store,
        string_list_clear(&affected_refnames, 0);
 
        if (ret)
-               files_transaction_cleanup(transaction);
+               files_transaction_cleanup(refs, transaction);
        else
                transaction->state = REF_TRANSACTION_PREPARED;
 
@@ -2509,9 +2635,10 @@ static int files_transaction_finish(struct ref_store *ref_store,
                files_downcast(ref_store, 0, "ref_transaction_finish");
        size_t i;
        int ret = 0;
-       struct string_list refs_to_delete = STRING_LIST_INIT_NODUP;
-       struct string_list_item *ref_to_delete;
        struct strbuf sb = STRBUF_INIT;
+       struct files_transaction_backend_data *backend_data;
+       struct ref_transaction *packed_transaction;
+
 
        assert(err);
 
@@ -2520,6 +2647,9 @@ static int files_transaction_finish(struct ref_store *ref_store,
                return 0;
        }
 
+       backend_data = transaction->backend_data;
+       packed_transaction = backend_data->packed_transaction;
+
        /* Perform updates first so live commits remain referenced */
        for (i = 0; i < transaction->nr; i++) {
                struct ref_update *update = transaction->updates[i];
@@ -2555,7 +2685,44 @@ static int files_transaction_finish(struct ref_store *ref_store,
                        }
                }
        }
-       /* Perform deletes now that updates are safely completed */
+
+       /*
+        * Now that updates are safely completed, we can perform
+        * deletes. First delete the reflogs of any references that
+        * will be deleted, since (in the unexpected event of an
+        * error) leaving a reference without a reflog is less bad
+        * than leaving a reflog without a reference (the latter is a
+        * mildly invalid repository state):
+        */
+       for (i = 0; i < transaction->nr; i++) {
+               struct ref_update *update = transaction->updates[i];
+               if (update->flags & REF_DELETING &&
+                   !(update->flags & REF_LOG_ONLY) &&
+                   !(update->flags & REF_ISPRUNING)) {
+                       strbuf_reset(&sb);
+                       files_reflog_path(refs, &sb, update->refname);
+                       if (!unlink_or_warn(sb.buf))
+                               try_remove_empty_parents(refs, update->refname,
+                                                        REMOVE_EMPTY_PARENTS_REFLOG);
+               }
+       }
+
+       /*
+        * Perform deletes now that updates are safely completed.
+        *
+        * First delete any packed versions of the references, while
+        * retaining the packed-refs lock:
+        */
+       if (packed_transaction) {
+               ret = ref_transaction_commit(packed_transaction, err);
+               ref_transaction_free(packed_transaction);
+               packed_transaction = NULL;
+               backend_data->packed_transaction = NULL;
+               if (ret)
+                       goto cleanup;
+       }
+
+       /* Now delete the loose versions of the references: */
        for (i = 0; i < transaction->nr; i++) {
                struct ref_update *update = transaction->updates[i];
                struct ref_lock *lock = update->backend_data;
@@ -2573,39 +2740,13 @@ static int files_transaction_finish(struct ref_store *ref_store,
                                }
                                update->flags |= REF_DELETED_LOOSE;
                        }
-
-                       if (!(update->flags & REF_ISPRUNING))
-                               string_list_append(&refs_to_delete,
-                                                  lock->ref_name);
                }
        }
 
-       if (packed_refs_lock(refs->packed_ref_store, 0, err)) {
-               ret = TRANSACTION_GENERIC_ERROR;
-               goto cleanup;
-       }
-
-       if (repack_without_refs(refs->packed_ref_store, &refs_to_delete, err)) {
-               ret = TRANSACTION_GENERIC_ERROR;
-               packed_refs_unlock(refs->packed_ref_store);
-               goto cleanup;
-       }
-
-       packed_refs_unlock(refs->packed_ref_store);
-
-       /* Delete the reflogs of any references that were deleted: */
-       for_each_string_list_item(ref_to_delete, &refs_to_delete) {
-               strbuf_reset(&sb);
-               files_reflog_path(refs, &sb, ref_to_delete->string);
-               if (!unlink_or_warn(sb.buf))
-                       try_remove_empty_parents(refs, ref_to_delete->string,
-                                                REMOVE_EMPTY_PARENTS_REFLOG);
-       }
-
        clear_loose_ref_cache(refs);
 
 cleanup:
-       files_transaction_cleanup(transaction);
+       files_transaction_cleanup(refs, transaction);
 
        for (i = 0; i < transaction->nr; i++) {
                struct ref_update *update = transaction->updates[i];
@@ -2623,7 +2764,6 @@ static int files_transaction_finish(struct ref_store *ref_store,
        }
 
        strbuf_release(&sb);
-       string_list_clear(&refs_to_delete, 0);
        return ret;
 }
 
@@ -2631,7 +2771,10 @@ static int files_transaction_abort(struct ref_store *ref_store,
                                   struct ref_transaction *transaction,
                                   struct strbuf *err)
 {
-       files_transaction_cleanup(transaction);
+       struct files_ref_store *refs =
+               files_downcast(ref_store, 0, "ref_transaction_abort");
+
+       files_transaction_cleanup(refs, transaction);
        return 0;
 }
 
@@ -2653,6 +2796,7 @@ static int files_initial_transaction_commit(struct ref_store *ref_store,
        size_t i;
        int ret = 0;
        struct string_list affected_refnames = STRING_LIST_INIT_NODUP;
+       struct ref_transaction *packed_transaction = NULL;
 
        assert(err);
 
@@ -2685,6 +2829,12 @@ static int files_initial_transaction_commit(struct ref_store *ref_store,
                                 &affected_refnames))
                die("BUG: initial ref transaction called with existing refs");
 
+       packed_transaction = ref_store_transaction_begin(refs->packed_ref_store, err);
+       if (!packed_transaction) {
+               ret = TRANSACTION_GENERIC_ERROR;
+               goto cleanup;
+       }
+
        for (i = 0; i < transaction->nr; i++) {
                struct ref_update *update = transaction->updates[i];
 
@@ -2697,6 +2847,15 @@ static int files_initial_transaction_commit(struct ref_store *ref_store,
                        ret = TRANSACTION_NAME_CONFLICT;
                        goto cleanup;
                }
+
+               /*
+                * Add a reference creation for this reference to the
+                * packed-refs transaction:
+                */
+               ref_transaction_add_update(packed_transaction, update->refname,
+                                          update->flags & ~REF_HAVE_OLD,
+                                          update->new_oid.hash, update->old_oid.hash,
+                                          NULL);
        }
 
        if (packed_refs_lock(refs->packed_ref_store, 0, err)) {
@@ -2704,21 +2863,14 @@ static int files_initial_transaction_commit(struct ref_store *ref_store,
                goto cleanup;
        }
 
-       for (i = 0; i < transaction->nr; i++) {
-               struct ref_update *update = transaction->updates[i];
-
-               if ((update->flags & REF_HAVE_NEW) &&
-                   !is_null_oid(&update->new_oid))
-                       add_packed_ref(refs->packed_ref_store, update->refname,
-                                      &update->new_oid);
-       }
-
-       if (commit_packed_refs(refs->packed_ref_store, err)) {
+       if (initial_ref_transaction_commit(packed_transaction, err)) {
                ret = TRANSACTION_GENERIC_ERROR;
                goto cleanup;
        }
 
 cleanup:
+       if (packed_transaction)
+               ref_transaction_free(packed_transaction);
        packed_refs_unlock(refs->packed_ref_store);
        transaction->state = REF_TRANSACTION_CLOSED;
        string_list_clear(&affected_refnames, 0);
@@ -2848,16 +3000,17 @@ static int files_reflog_expire(struct ref_store *ref_store,
                        !(type & REF_ISSYMREF) &&
                        !is_null_oid(&cb.last_kept_oid);
 
-               if (close_lock_file(&reflog_lock)) {
+               if (close_lock_file_gently(&reflog_lock)) {
                        status |= error("couldn't write %s: %s", log_file,
                                        strerror(errno));
+                       rollback_lock_file(&reflog_lock);
                } else if (update &&
-                          (write_in_full(get_lock_file_fd(lock->lk),
-                               oid_to_hex(&cb.last_kept_oid), GIT_SHA1_HEXSZ) != GIT_SHA1_HEXSZ ||
-                           write_str_in_full(get_lock_file_fd(lock->lk), "\n") != 1 ||
-                           close_ref(lock) < 0)) {
+                          (write_in_full(get_lock_file_fd(&lock->lk),
+                               oid_to_hex(&cb.last_kept_oid), GIT_SHA1_HEXSZ) < 0 ||
+                           write_str_in_full(get_lock_file_fd(&lock->lk), "\n") < 1 ||
+                           close_ref_gently(lock) < 0)) {
                        status |= error("couldn't write %s",
-                                       get_lock_file_path(lock->lk));
+                                       get_lock_file_path(&lock->lk));
                        rollback_lock_file(&reflog_lock);
                } else if (commit_lock_file(&reflog_lock)) {
                        status |= error("unable to write reflog '%s' (%s)",
index 412c85034fc3b8632f16d13c4d467ad41060ddeb..3bc47ffd5ea4e82f2505a8649b162d18ebf11a21 100644 (file)
@@ -75,7 +75,7 @@ struct packed_ref_store {
         * "packed-refs" file. Note that this (and thus the enclosing
         * `packed_ref_store`) must not be freed.
         */
-       struct tempfile tempfile;
+       struct tempfile *tempfile;
 };
 
 struct ref_store *packed_ref_store_create(const char *path,
@@ -91,19 +91,6 @@ struct ref_store *packed_ref_store_create(const char *path,
        return ref_store;
 }
 
-/*
- * Die if refs is not the main ref store. caller is used in any
- * necessary error messages.
- */
-static void packed_assert_main_repository(struct packed_ref_store *refs,
-                                         const char *caller)
-{
-       if (refs->store_flags & REF_STORE_MAIN)
-               return;
-
-       die("BUG: operation %s only allowed for main ref store", caller);
-}
-
 /*
  * Downcast `ref_store` to `packed_ref_store`. Die if `ref_store` is
  * not a `packed_ref_store`. Also die if `packed_ref_store` doesn't
@@ -321,40 +308,6 @@ static struct ref_dir *get_packed_refs(struct packed_ref_store *refs)
        return get_packed_ref_dir(get_packed_ref_cache(refs));
 }
 
-/*
- * Add or overwrite a reference in the in-memory packed reference
- * cache. This may only be called while the packed-refs file is locked
- * (see packed_refs_lock()). To actually write the packed-refs file,
- * call commit_packed_refs().
- */
-void add_packed_ref(struct ref_store *ref_store,
-                   const char *refname, const struct object_id *oid)
-{
-       struct packed_ref_store *refs =
-               packed_downcast(ref_store, REF_STORE_WRITE,
-                               "add_packed_ref");
-       struct ref_dir *packed_refs;
-       struct ref_entry *packed_entry;
-
-       if (!is_lock_file_locked(&refs->lock))
-               die("BUG: packed refs not locked");
-
-       if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL))
-               die("Reference has invalid format: '%s'", refname);
-
-       packed_refs = get_packed_refs(refs);
-       packed_entry = find_ref_entry(packed_refs, refname);
-       if (packed_entry) {
-               /* Overwrite the existing entry: */
-               oidcpy(&packed_entry->u.value.oid, oid);
-               packed_entry->flag = REF_ISPACKED;
-               oidclr(&packed_entry->u.value.peeled);
-       } else {
-               packed_entry = create_ref_entry(refname, oid, REF_ISPACKED);
-               add_ref_entry(packed_refs, packed_entry);
-       }
-}
-
 /*
  * Return the ref_entry for the given refname from the packed
  * references.  If it does not exist, return NULL.
@@ -525,7 +478,6 @@ int packed_refs_lock(struct ref_store *ref_store, int flags, struct strbuf *err)
                                "packed_refs_lock");
        static int timeout_configured = 0;
        static int timeout_value = 1000;
-       struct packed_ref_cache *packed_ref_cache;
 
        if (!timeout_configured) {
                git_config_get_int("core.packedrefstimeout", &timeout_value);
@@ -545,8 +497,9 @@ int packed_refs_lock(struct ref_store *ref_store, int flags, struct strbuf *err)
                return -1;
        }
 
-       if (close_lock_file(&refs->lock)) {
+       if (close_lock_file_gently(&refs->lock)) {
                strbuf_addf(err, "unable to close %s: %s", refs->path, strerror(errno));
+               rollback_lock_file(&refs->lock);
                return -1;
        }
 
@@ -560,9 +513,11 @@ int packed_refs_lock(struct ref_store *ref_store, int flags, struct strbuf *err)
         */
        validate_packed_ref_cache(refs);
 
-       packed_ref_cache = get_packed_ref_cache(refs);
-       /* Increment the reference count to prevent it from being freed: */
-       acquire_packed_ref_cache(packed_ref_cache);
+       /*
+        * Now make sure that the packed-refs file as it exists in the
+        * locked state is loaded into the cache:
+        */
+       get_packed_ref_cache(refs);
        return 0;
 }
 
@@ -576,7 +531,6 @@ void packed_refs_unlock(struct ref_store *ref_store)
        if (!is_lock_file_locked(&refs->lock))
                die("BUG: packed_refs_unlock() called when not locked");
        rollback_lock_file(&refs->lock);
-       release_packed_ref_cache(refs->cache);
 }
 
 int packed_refs_is_locked(struct ref_store *ref_store)
@@ -596,29 +550,35 @@ int packed_refs_is_locked(struct ref_store *ref_store)
 static const char PACKED_REFS_HEADER[] =
        "# pack-refs with: peeled fully-peeled \n";
 
+static int packed_init_db(struct ref_store *ref_store, struct strbuf *err)
+{
+       /* Nothing to do. */
+       return 0;
+}
+
 /*
- * Write the current version of the packed refs cache from memory to
- * disk. The packed-refs file must already be locked for writing (see
- * packed_refs_lock()). Return zero on success. On errors, rollback
- * the lockfile, write an error message to `err`, and return a nonzero
- * value.
+ * Write the packed-refs from the cache to the packed-refs tempfile,
+ * incorporating any changes from `updates`. `updates` must be a
+ * sorted string list whose keys are the refnames and whose util
+ * values are `struct ref_update *`. On error, rollback the tempfile,
+ * write an error message to `err`, and return a nonzero value.
+ *
+ * The packfile must be locked before calling this function and will
+ * remain locked when it is done.
  */
-int commit_packed_refs(struct ref_store *ref_store, struct strbuf *err)
+static int write_with_updates(struct packed_ref_store *refs,
+                             struct string_list *updates,
+                             struct strbuf *err)
 {
-       struct packed_ref_store *refs =
-               packed_downcast(ref_store, REF_STORE_WRITE | REF_STORE_MAIN,
-                               "commit_packed_refs");
-       struct packed_ref_cache *packed_ref_cache =
-               get_packed_ref_cache(refs);
+       struct ref_iterator *iter = NULL;
+       size_t i;
        int ok;
-       int ret = -1;
-       struct strbuf sb = STRBUF_INIT;
        FILE *out;
-       struct ref_iterator *iter;
+       struct strbuf sb = STRBUF_INIT;
        char *packed_refs_path;
 
        if (!is_lock_file_locked(&refs->lock))
-               die("BUG: commit_packed_refs() called when unlocked");
+               die("BUG: write_with_updates() called while unlocked");
 
        /*
         * If packed-refs is a symlink, we want to overwrite the
@@ -627,146 +587,303 @@ int commit_packed_refs(struct ref_store *ref_store, struct strbuf *err)
         */
        packed_refs_path = get_locked_file_path(&refs->lock);
        strbuf_addf(&sb, "%s.new", packed_refs_path);
-       if (create_tempfile(&refs->tempfile, sb.buf) < 0) {
+       free(packed_refs_path);
+       refs->tempfile = create_tempfile(sb.buf);
+       if (!refs->tempfile) {
                strbuf_addf(err, "unable to create file %s: %s",
                            sb.buf, strerror(errno));
                strbuf_release(&sb);
-               goto out;
+               return -1;
        }
        strbuf_release(&sb);
 
-       out = fdopen_tempfile(&refs->tempfile, "w");
+       out = fdopen_tempfile(refs->tempfile, "w");
        if (!out) {
                strbuf_addf(err, "unable to fdopen packed-refs tempfile: %s",
                            strerror(errno));
                goto error;
        }
 
-       if (fprintf(out, "%s", PACKED_REFS_HEADER) < 0) {
-               strbuf_addf(err, "error writing to %s: %s",
-                           get_tempfile_path(&refs->tempfile), strerror(errno));
-               goto error;
-       }
+       if (fprintf(out, "%s", PACKED_REFS_HEADER) < 0)
+               goto write_error;
+
+       /*
+        * We iterate in parallel through the current list of refs and
+        * the list of updates, processing an entry from at least one
+        * of the lists each time through the loop. When the current
+        * list of refs is exhausted, set iter to NULL. When the list
+        * of updates is exhausted, leave i set to updates->nr.
+        */
+       iter = packed_ref_iterator_begin(&refs->base, "",
+                                        DO_FOR_EACH_INCLUDE_BROKEN);
+       if ((ok = ref_iterator_advance(iter)) != ITER_OK)
+               iter = NULL;
+
+       i = 0;
 
-       iter = cache_ref_iterator_begin(packed_ref_cache->cache, NULL, 0);
-       while ((ok = ref_iterator_advance(iter)) == ITER_OK) {
-               struct object_id peeled;
-               int peel_error = ref_iterator_peel(iter, &peeled);
-
-               if (write_packed_entry(out, iter->refname, iter->oid->hash,
-                                      peel_error ? NULL : peeled.hash)) {
-                       strbuf_addf(err, "error writing to %s: %s",
-                                   get_tempfile_path(&refs->tempfile),
-                                   strerror(errno));
-                       ref_iterator_abort(iter);
-                       goto error;
+       while (iter || i < updates->nr) {
+               struct ref_update *update = NULL;
+               int cmp;
+
+               if (i >= updates->nr) {
+                       cmp = -1;
+               } else {
+                       update = updates->items[i].util;
+
+                       if (!iter)
+                               cmp = +1;
+                       else
+                               cmp = strcmp(iter->refname, update->refname);
+               }
+
+               if (!cmp) {
+                       /*
+                        * There is both an old value and an update
+                        * for this reference. Check the old value if
+                        * necessary:
+                        */
+                       if ((update->flags & REF_HAVE_OLD)) {
+                               if (is_null_oid(&update->old_oid)) {
+                                       strbuf_addf(err, "cannot update ref '%s': "
+                                                   "reference already exists",
+                                                   update->refname);
+                                       goto error;
+                               } else if (oidcmp(&update->old_oid, iter->oid)) {
+                                       strbuf_addf(err, "cannot update ref '%s': "
+                                                   "is at %s but expected %s",
+                                                   update->refname,
+                                                   oid_to_hex(iter->oid),
+                                                   oid_to_hex(&update->old_oid));
+                                       goto error;
+                               }
+                       }
+
+                       /* Now figure out what to use for the new value: */
+                       if ((update->flags & REF_HAVE_NEW)) {
+                               /*
+                                * The update takes precedence. Skip
+                                * the iterator over the unneeded
+                                * value.
+                                */
+                               if ((ok = ref_iterator_advance(iter)) != ITER_OK)
+                                       iter = NULL;
+                               cmp = +1;
+                       } else {
+                               /*
+                                * The update doesn't actually want to
+                                * change anything. We're done with it.
+                                */
+                               i++;
+                               cmp = -1;
+                       }
+               } else if (cmp > 0) {
+                       /*
+                        * There is no old value but there is an
+                        * update for this reference. Make sure that
+                        * the update didn't expect an existing value:
+                        */
+                       if ((update->flags & REF_HAVE_OLD) &&
+                           !is_null_oid(&update->old_oid)) {
+                               strbuf_addf(err, "cannot update ref '%s': "
+                                           "reference is missing but expected %s",
+                                           update->refname,
+                                           oid_to_hex(&update->old_oid));
+                               goto error;
+                       }
+               }
+
+               if (cmp < 0) {
+                       /* Pass the old reference through. */
+
+                       struct object_id peeled;
+                       int peel_error = ref_iterator_peel(iter, &peeled);
+
+                       if (write_packed_entry(out, iter->refname,
+                                              iter->oid->hash,
+                                              peel_error ? NULL : peeled.hash))
+                               goto write_error;
+
+                       if ((ok = ref_iterator_advance(iter)) != ITER_OK)
+                               iter = NULL;
+               } else if (is_null_oid(&update->new_oid)) {
+                       /*
+                        * The update wants to delete the reference,
+                        * and the reference either didn't exist or we
+                        * have already skipped it. So we're done with
+                        * the update (and don't have to write
+                        * anything).
+                        */
+                       i++;
+               } else {
+                       struct object_id peeled;
+                       int peel_error = peel_object(update->new_oid.hash,
+                                                    peeled.hash);
+
+                       if (write_packed_entry(out, update->refname,
+                                              update->new_oid.hash,
+                                              peel_error ? NULL : peeled.hash))
+                               goto write_error;
+
+                       i++;
                }
        }
 
        if (ok != ITER_DONE) {
-               strbuf_addf(err, "unable to rewrite packed-refs file: "
+               strbuf_addf(err, "unable to write packed-refs file: "
                            "error iterating over old contents");
                goto error;
        }
 
-       if (rename_tempfile(&refs->tempfile, packed_refs_path)) {
-               strbuf_addf(err, "error replacing %s: %s",
-                           refs->path, strerror(errno));
-               goto out;
+       if (close_tempfile_gently(refs->tempfile)) {
+               strbuf_addf(err, "error closing file %s: %s",
+                           get_tempfile_path(refs->tempfile),
+                           strerror(errno));
+               strbuf_release(&sb);
+               delete_tempfile(&refs->tempfile);
+               return -1;
        }
 
-       ret = 0;
-       goto out;
+       return 0;
+
+write_error:
+       strbuf_addf(err, "error writing to %s: %s",
+                   get_tempfile_path(refs->tempfile), strerror(errno));
 
 error:
-       delete_tempfile(&refs->tempfile);
+       if (iter)
+               ref_iterator_abort(iter);
 
-out:
-       free(packed_refs_path);
-       return ret;
+       delete_tempfile(&refs->tempfile);
+       return -1;
 }
 
-/*
- * Rewrite the packed-refs file, omitting any refs listed in
- * 'refnames'. On error, leave packed-refs unchanged, write an error
- * message to 'err', and return a nonzero value. The packed refs lock
- * must be held when calling this function; it will still be held when
- * the function returns.
- *
- * The refs in 'refnames' needn't be sorted. `err` must not be NULL.
- */
-int repack_without_refs(struct ref_store *ref_store,
-                       struct string_list *refnames, struct strbuf *err)
+struct packed_transaction_backend_data {
+       /* True iff the transaction owns the packed-refs lock. */
+       int own_lock;
+
+       struct string_list updates;
+};
+
+static void packed_transaction_cleanup(struct packed_ref_store *refs,
+                                      struct ref_transaction *transaction)
 {
-       struct packed_ref_store *refs =
-               packed_downcast(ref_store, REF_STORE_WRITE | REF_STORE_MAIN,
-                               "repack_without_refs");
-       struct ref_dir *packed;
-       struct string_list_item *refname;
-       int needs_repacking = 0, removed = 0;
+       struct packed_transaction_backend_data *data = transaction->backend_data;
 
-       packed_assert_main_repository(refs, "repack_without_refs");
-       assert(err);
+       if (data) {
+               string_list_clear(&data->updates, 0);
 
-       if (!is_lock_file_locked(&refs->lock))
-               die("BUG: repack_without_refs called without holding lock");
+               if (is_tempfile_active(refs->tempfile))
+                       delete_tempfile(&refs->tempfile);
 
-       /* Look for a packed ref */
-       for_each_string_list_item(refname, refnames) {
-               if (get_packed_ref(refs, refname->string)) {
-                       needs_repacking = 1;
-                       break;
+               if (data->own_lock && is_lock_file_locked(&refs->lock)) {
+                       packed_refs_unlock(&refs->base);
+                       data->own_lock = 0;
                }
-       }
 
-       /* Avoid locking if we have nothing to do */
-       if (!needs_repacking)
-               return 0; /* no refname exists in packed refs */
-
-       packed = get_packed_refs(refs);
-
-       /* Remove refnames from the cache */
-       for_each_string_list_item(refname, refnames)
-               if (remove_entry_from_dir(packed, refname->string) != -1)
-                       removed = 1;
-       if (!removed) {
-               /*
-                * All packed entries disappeared while we were
-                * acquiring the lock.
-                */
-               clear_packed_ref_cache(refs);
-               return 0;
+               free(data);
+               transaction->backend_data = NULL;
        }
 
-       /* Write what remains */
-       return commit_packed_refs(&refs->base, err);
-}
-
-static int packed_init_db(struct ref_store *ref_store, struct strbuf *err)
-{
-       /* Nothing to do. */
-       return 0;
+       transaction->state = REF_TRANSACTION_CLOSED;
 }
 
 static int packed_transaction_prepare(struct ref_store *ref_store,
                                      struct ref_transaction *transaction,
                                      struct strbuf *err)
 {
-       die("BUG: not implemented yet");
+       struct packed_ref_store *refs = packed_downcast(
+                       ref_store,
+                       REF_STORE_READ | REF_STORE_WRITE | REF_STORE_ODB,
+                       "ref_transaction_prepare");
+       struct packed_transaction_backend_data *data;
+       size_t i;
+       int ret = TRANSACTION_GENERIC_ERROR;
+
+       /*
+        * Note that we *don't* skip transactions with zero updates,
+        * because such a transaction might be executed for the side
+        * effect of ensuring that all of the references are peeled.
+        * If the caller wants to optimize away empty transactions, it
+        * should do so itself.
+        */
+
+       data = xcalloc(1, sizeof(*data));
+       string_list_init(&data->updates, 0);
+
+       transaction->backend_data = data;
+
+       /*
+        * Stick the updates in a string list by refname so that we
+        * can sort them:
+        */
+       for (i = 0; i < transaction->nr; i++) {
+               struct ref_update *update = transaction->updates[i];
+               struct string_list_item *item =
+                       string_list_append(&data->updates, update->refname);
+
+               /* Store a pointer to update in item->util: */
+               item->util = update;
+       }
+       string_list_sort(&data->updates);
+
+       if (ref_update_reject_duplicates(&data->updates, err))
+               goto failure;
+
+       if (!is_lock_file_locked(&refs->lock)) {
+               if (packed_refs_lock(ref_store, 0, err))
+                       goto failure;
+               data->own_lock = 1;
+       }
+
+       if (write_with_updates(refs, &data->updates, err))
+               goto failure;
+
+       transaction->state = REF_TRANSACTION_PREPARED;
+       return 0;
+
+failure:
+       packed_transaction_cleanup(refs, transaction);
+       return ret;
 }
 
 static int packed_transaction_abort(struct ref_store *ref_store,
                                    struct ref_transaction *transaction,
                                    struct strbuf *err)
 {
-       die("BUG: not implemented yet");
+       struct packed_ref_store *refs = packed_downcast(
+                       ref_store,
+                       REF_STORE_READ | REF_STORE_WRITE | REF_STORE_ODB,
+                       "ref_transaction_abort");
+
+       packed_transaction_cleanup(refs, transaction);
+       return 0;
 }
 
 static int packed_transaction_finish(struct ref_store *ref_store,
                                     struct ref_transaction *transaction,
                                     struct strbuf *err)
 {
-       die("BUG: not implemented yet");
+       struct packed_ref_store *refs = packed_downcast(
+                       ref_store,
+                       REF_STORE_READ | REF_STORE_WRITE | REF_STORE_ODB,
+                       "ref_transaction_finish");
+       int ret = TRANSACTION_GENERIC_ERROR;
+       char *packed_refs_path;
+
+       packed_refs_path = get_locked_file_path(&refs->lock);
+       if (rename_tempfile(&refs->tempfile, packed_refs_path)) {
+               strbuf_addf(err, "error replacing %s: %s",
+                           refs->path, strerror(errno));
+               goto cleanup;
+       }
+
+       clear_packed_ref_cache(refs);
+       ret = 0;
+
+cleanup:
+       free(packed_refs_path);
+       packed_transaction_cleanup(refs, transaction);
+       return ret;
 }
 
 static int packed_initial_transaction_commit(struct ref_store *ref_store,
@@ -779,7 +896,50 @@ static int packed_initial_transaction_commit(struct ref_store *ref_store,
 static int packed_delete_refs(struct ref_store *ref_store, const char *msg,
                             struct string_list *refnames, unsigned int flags)
 {
-       die("BUG: not implemented yet");
+       struct packed_ref_store *refs =
+               packed_downcast(ref_store, REF_STORE_WRITE, "delete_refs");
+       struct strbuf err = STRBUF_INIT;
+       struct ref_transaction *transaction;
+       struct string_list_item *item;
+       int ret;
+
+       (void)refs; /* We need the check above, but don't use the variable */
+
+       if (!refnames->nr)
+               return 0;
+
+       /*
+        * Since we don't check the references' old_oids, the
+        * individual updates can't fail, so we can pack all of the
+        * updates into a single transaction.
+        */
+
+       transaction = ref_store_transaction_begin(ref_store, &err);
+       if (!transaction)
+               return -1;
+
+       for_each_string_list_item(item, refnames) {
+               if (ref_transaction_delete(transaction, item->string, NULL,
+                                          flags, msg, &err)) {
+                       warning(_("could not delete reference %s: %s"),
+                               item->string, err.buf);
+                       strbuf_reset(&err);
+               }
+       }
+
+       ret = ref_transaction_commit(transaction, &err);
+
+       if (ret) {
+               if (refnames->nr == 1)
+                       error(_("could not delete reference %s: %s"),
+                             refnames->items[0].string, err.buf);
+               else
+                       error(_("could not delete references: %s"), err.buf);
+       }
+
+       ref_transaction_free(transaction);
+       strbuf_release(&err);
+       return ret;
 }
 
 static int packed_pack_refs(struct ref_store *ref_store, unsigned int flags)
index 03b7c1de95be9387fa0d2165036a4aa6d276d19c..61687e408ad4b83d111531f9d5b10a058abcf673 100644 (file)
@@ -1,6 +1,15 @@
 #ifndef REFS_PACKED_BACKEND_H
 #define REFS_PACKED_BACKEND_H
 
+/*
+ * Support for storing references in a `packed-refs` file.
+ *
+ * Note that this backend doesn't check for D/F conflicts, because it
+ * doesn't care about them. But usually it should be wrapped in a
+ * `files_ref_store` that prevents D/F conflicts from being created,
+ * even among packed refs.
+ */
+
 struct ref_store *packed_ref_store_create(const char *path,
                                          unsigned int store_flags);
 
@@ -14,12 +23,4 @@ int packed_refs_lock(struct ref_store *ref_store, int flags, struct strbuf *err)
 void packed_refs_unlock(struct ref_store *ref_store);
 int packed_refs_is_locked(struct ref_store *ref_store);
 
-void add_packed_ref(struct ref_store *ref_store,
-                   const char *refname, const struct object_id *oid);
-
-int commit_packed_refs(struct ref_store *ref_store, struct strbuf *err);
-
-int repack_without_refs(struct ref_store *ref_store,
-                       struct string_list *refnames, struct strbuf *err);
-
 #endif /* REFS_PACKED_BACKEND_H */
index b02dc5a7e3500cae1784199a82b37be020004df1..d7d344de73e6f85fed1e8460bce246a894eab6da 100644 (file)
@@ -242,6 +242,7 @@ struct ref_transaction {
        size_t alloc;
        size_t nr;
        enum ref_transaction_state state;
+       void *backend_data;
 };
 
 /*
index f107af7d763e848648ace5da0010be4df2e0d4dc..97c732bd48ca8776203ae0936fe8ee61bb330ad9 100644 (file)
@@ -40,11 +40,15 @@ static void repo_setup_env(struct repository *repo)
 
        repo->different_commondir = find_common_dir(&sb, repo->gitdir,
                                                    !repo->ignore_env);
+       free(repo->commondir);
        repo->commondir = strbuf_detach(&sb, NULL);
+       free(repo->objectdir);
        repo->objectdir = git_path_from_env(DB_ENVIRONMENT, repo->commondir,
                                            "objects", !repo->ignore_env);
+       free(repo->graft_file);
        repo->graft_file = git_path_from_env(GRAFT_ENVIRONMENT, repo->commondir,
                                             "info/grafts", !repo->ignore_env);
+       free(repo->index_file);
        repo->index_file = git_path_from_env(INDEX_ENVIRONMENT, repo->gitdir,
                                             "index", !repo->ignore_env);
 }
@@ -52,16 +56,12 @@ static void repo_setup_env(struct repository *repo)
 void repo_set_gitdir(struct repository *repo, const char *path)
 {
        const char *gitfile = read_gitfile(path);
+       char *old_gitdir = repo->gitdir;
 
-       /*
-        * NEEDSWORK: Eventually we want to be able to free gitdir and the rest
-        * of the environment before reinitializing it again, but we have some
-        * crazy code paths where we try to set gitdir with the current gitdir
-        * and we don't want to free gitdir before copying the passed in value.
-        */
        repo->gitdir = xstrdup(gitfile ? gitfile : path);
-
        repo_setup_env(repo);
+
+       free(old_gitdir);
 }
 
 /*
index d77235645ea88ff9a51346436fcaac69dabfaaeb..1ce440f4bb84d001ff2b0ac1a67772f4bf5926c0 100644 (file)
--- a/rerere.c
+++ b/rerere.c
@@ -258,7 +258,7 @@ static int write_rr(struct string_list *rr, int out_fd)
                                    rerere_id_hex(id),
                                    rr->items[i].string, 0);
 
-               if (write_in_full(out_fd, buf.buf, buf.len) != buf.len)
+               if (write_in_full(out_fd, buf.buf, buf.len) < 0)
                        die("unable to write rerere record");
 
                strbuf_release(&buf);
index 94a5e98525187a2d7f1c6f4e9a22f4cb3bb0cf84..f9a90d71d2008415e80b30c9e5d3b2743af5438f 100644 (file)
@@ -20,6 +20,7 @@
 #include "cache-tree.h"
 #include "bisect.h"
 #include "packfile.h"
+#include "worktree.h"
 
 volatile show_early_output_fn_t show_early_output;
 
@@ -1132,6 +1133,7 @@ struct all_refs_cb {
        int warned_bad_reflog;
        struct rev_info *all_revs;
        const char *name_for_errormsg;
+       struct ref_store *refs;
 };
 
 int ref_excluded(struct string_list *ref_excludes, const char *path)
@@ -1168,6 +1170,7 @@ static void init_all_refs_cb(struct all_refs_cb *cb, struct rev_info *revs,
        cb->all_revs = revs;
        cb->all_flags = flags;
        revs->rev_input_given = 1;
+       cb->refs = NULL;
 }
 
 void clear_ref_exclusion(struct string_list **ref_excludes_p)
@@ -1188,12 +1191,19 @@ void add_ref_exclusion(struct string_list **ref_excludes_p, const char *exclude)
        string_list_append(*ref_excludes_p, exclude);
 }
 
-static void handle_refs(const char *submodule, struct rev_info *revs, unsigned flags,
-               int (*for_each)(const char *, each_ref_fn, void *))
+static void handle_refs(struct ref_store *refs,
+                       struct rev_info *revs, unsigned flags,
+                       int (*for_each)(struct ref_store *, each_ref_fn, void *))
 {
        struct all_refs_cb cb;
+
+       if (!refs) {
+               /* this could happen with uninitialized submodules */
+               return;
+       }
+
        init_all_refs_cb(&cb, revs, flags);
-       for_each(submodule, handle_one_ref, &cb);
+       for_each(refs, handle_one_ref, &cb);
 }
 
 static void handle_one_reflog_commit(struct object_id *oid, void *cb_data)
@@ -1229,17 +1239,41 @@ static int handle_one_reflog(const char *path, const struct object_id *oid,
        struct all_refs_cb *cb = cb_data;
        cb->warned_bad_reflog = 0;
        cb->name_for_errormsg = path;
-       for_each_reflog_ent(path, handle_one_reflog_ent, cb_data);
+       refs_for_each_reflog_ent(cb->refs, path,
+                                handle_one_reflog_ent, cb_data);
        return 0;
 }
 
+static void add_other_reflogs_to_pending(struct all_refs_cb *cb)
+{
+       struct worktree **worktrees, **p;
+
+       worktrees = get_worktrees(0);
+       for (p = worktrees; *p; p++) {
+               struct worktree *wt = *p;
+
+               if (wt->is_current)
+                       continue;
+
+               cb->refs = get_worktree_ref_store(wt);
+               refs_for_each_reflog(cb->refs,
+                                    handle_one_reflog,
+                                    cb);
+       }
+       free_worktrees(worktrees);
+}
+
 void add_reflogs_to_pending(struct rev_info *revs, unsigned flags)
 {
        struct all_refs_cb cb;
 
        cb.all_revs = revs;
        cb.all_flags = flags;
+       cb.refs = get_main_ref_store();
        for_each_reflog(handle_one_reflog, &cb);
+
+       if (!revs->single_worktree)
+               add_other_reflogs_to_pending(&cb);
 }
 
 static void add_cache_tree(struct cache_tree *it, struct rev_info *revs,
@@ -1263,13 +1297,13 @@ static void add_cache_tree(struct cache_tree *it, struct rev_info *revs,
 
 }
 
-void add_index_objects_to_pending(struct rev_info *revs, unsigned flags)
+static void do_add_index_objects_to_pending(struct rev_info *revs,
+                                           struct index_state *istate)
 {
        int i;
 
-       read_cache();
-       for (i = 0; i < active_nr; i++) {
-               struct cache_entry *ce = active_cache[i];
+       for (i = 0; i < istate->cache_nr; i++) {
+               struct cache_entry *ce = istate->cache[i];
                struct blob *blob;
 
                if (S_ISGITLINK(ce->ce_mode))
@@ -1282,13 +1316,39 @@ void add_index_objects_to_pending(struct rev_info *revs, unsigned flags)
                                             ce->ce_mode, ce->name);
        }
 
-       if (active_cache_tree) {
+       if (istate->cache_tree) {
                struct strbuf path = STRBUF_INIT;
-               add_cache_tree(active_cache_tree, revs, &path);
+               add_cache_tree(istate->cache_tree, revs, &path);
                strbuf_release(&path);
        }
 }
 
+void add_index_objects_to_pending(struct rev_info *revs, unsigned int flags)
+{
+       struct worktree **worktrees, **p;
+
+       read_cache();
+       do_add_index_objects_to_pending(revs, &the_index);
+
+       if (revs->single_worktree)
+               return;
+
+       worktrees = get_worktrees(0);
+       for (p = worktrees; *p; p++) {
+               struct worktree *wt = *p;
+               struct index_state istate = { NULL };
+
+               if (wt->is_current)
+                       continue; /* current index already taken care of */
+
+               if (read_index_from(&istate,
+                                   worktree_git_path(wt, "index")) > 0)
+                       do_add_index_objects_to_pending(revs, &istate);
+               discard_index(&istate);
+       }
+       free_worktrees(worktrees);
+}
+
 static int add_parents_only(struct rev_info *revs, const char *arg_, int flags,
                            int exclude_parent)
 {
@@ -2069,23 +2129,25 @@ void parse_revision_opt(struct rev_info *revs, struct parse_opt_ctx_t *ctx,
        ctx->argc -= n;
 }
 
-static int for_each_bisect_ref(const char *submodule, each_ref_fn fn, void *cb_data, const char *term) {
+static int for_each_bisect_ref(struct ref_store *refs, each_ref_fn fn,
+                              void *cb_data, const char *term)
+{
        struct strbuf bisect_refs = STRBUF_INIT;
        int status;
        strbuf_addf(&bisect_refs, "refs/bisect/%s", term);
-       status = for_each_fullref_in_submodule(submodule, bisect_refs.buf, fn, cb_data, 0);
+       status = refs_for_each_fullref_in(refs, bisect_refs.buf, fn, cb_data, 0);
        strbuf_release(&bisect_refs);
        return status;
 }
 
-static int for_each_bad_bisect_ref(const char *submodule, each_ref_fn fn, void *cb_data)
+static int for_each_bad_bisect_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)
 {
-       return for_each_bisect_ref(submodule, fn, cb_data, term_bad);
+       return for_each_bisect_ref(refs, fn, cb_data, term_bad);
 }
 
-static int for_each_good_bisect_ref(const char *submodule, each_ref_fn fn, void *cb_data)
+static int for_each_good_bisect_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)
 {
-       return for_each_bisect_ref(submodule, fn, cb_data, term_good);
+       return for_each_bisect_ref(refs, fn, cb_data, term_good);
 }
 
 static int handle_revision_pseudo_opt(const char *submodule,
@@ -2094,8 +2156,22 @@ static int handle_revision_pseudo_opt(const char *submodule,
 {
        const char *arg = argv[0];
        const char *optarg;
+       struct ref_store *refs;
        int argcount;
 
+       if (submodule) {
+               /*
+                * We need some something like get_submodule_worktrees()
+                * before we can go through all worktrees of a submodule,
+                * .e.g with adding all HEADs from --all, which is not
+                * supported right now, so stick to single worktree.
+                */
+               if (!revs->single_worktree)
+                       die("BUG: --single-worktree cannot be used together with submodule");
+               refs = get_submodule_ref_store(submodule);
+       } else
+               refs = get_main_ref_store();
+
        /*
         * NOTE!
         *
@@ -2107,22 +2183,29 @@ static int handle_revision_pseudo_opt(const char *submodule,
         * register it in the list at the top of handle_revision_opt.
         */
        if (!strcmp(arg, "--all")) {
-               handle_refs(submodule, revs, *flags, for_each_ref_submodule);
-               handle_refs(submodule, revs, *flags, head_ref_submodule);
+               handle_refs(refs, revs, *flags, refs_for_each_ref);
+               handle_refs(refs, revs, *flags, refs_head_ref);
+               if (!revs->single_worktree) {
+                       struct all_refs_cb cb;
+
+                       init_all_refs_cb(&cb, revs, *flags);
+                       other_head_refs(handle_one_ref, &cb);
+               }
                clear_ref_exclusion(&revs->ref_excludes);
        } else if (!strcmp(arg, "--branches")) {
-               handle_refs(submodule, revs, *flags, for_each_branch_ref_submodule);
+               handle_refs(refs, revs, *flags, refs_for_each_branch_ref);
                clear_ref_exclusion(&revs->ref_excludes);
        } else if (!strcmp(arg, "--bisect")) {
                read_bisect_terms(&term_bad, &term_good);
-               handle_refs(submodule, revs, *flags, for_each_bad_bisect_ref);
-               handle_refs(submodule, revs, *flags ^ (UNINTERESTING | BOTTOM), for_each_good_bisect_ref);
+               handle_refs(refs, revs, *flags, for_each_bad_bisect_ref);
+               handle_refs(refs, revs, *flags ^ (UNINTERESTING | BOTTOM),
+                           for_each_good_bisect_ref);
                revs->bisect = 1;
        } else if (!strcmp(arg, "--tags")) {
-               handle_refs(submodule, revs, *flags, for_each_tag_ref_submodule);
+               handle_refs(refs, revs, *flags, refs_for_each_tag_ref);
                clear_ref_exclusion(&revs->ref_excludes);
        } else if (!strcmp(arg, "--remotes")) {
-               handle_refs(submodule, revs, *flags, for_each_remote_ref_submodule);
+               handle_refs(refs, revs, *flags, refs_for_each_remote_ref);
                clear_ref_exclusion(&revs->ref_excludes);
        } else if ((argcount = parse_long_opt("glob", argv, &optarg))) {
                struct all_refs_cb cb;
@@ -2169,6 +2252,8 @@ static int handle_revision_pseudo_opt(const char *submodule,
                        return error("invalid argument to --no-walk");
        } else if (!strcmp(arg, "--do-walk")) {
                revs->no_walk = 0;
+       } else if (!strcmp(arg, "--single-worktree")) {
+               revs->single_worktree = 1;
        } else {
                return 0;
        }
index bc18487d6fff5e86dff60748c084995a77d2b588..3a3d3e2cf824bf0ae404204c1ea81dad813d5f93 100644 (file)
@@ -96,6 +96,7 @@ struct rev_info {
                        topo_order:1,
                        simplify_merges:1,
                        simplify_by_decoration:1,
+                       single_worktree:1,
                        tag_objects:1,
                        tree_objects:1,
                        blob_objects:1,
index 11d6f3d98398e8b7a1add501dd7ee85a736531b6..b865f662e41880d11305ac6a4fe64450ef26f133 100644 (file)
@@ -492,8 +492,11 @@ int send_pack(struct send_pack_args *args,
                         * we were to send it and we're trying to send the refs
                         * atomically, abort the whole operation.
                         */
-                       if (use_atomic)
+                       if (use_atomic) {
+                               strbuf_release(&req_buf);
+                               strbuf_release(&cap_buf);
                                return atomic_push_failure(args, remote_refs, ref);
+                       }
                        /* Fallthrough for non atomic case. */
                default:
                        continue;
index fcceabb80f4261006cdd65bc0ec95ac54ea42e7c..60636ce54b615e19a22dc2396211d56540b4e920 100644 (file)
@@ -1565,6 +1565,7 @@ static int save_head(const char *head)
        static struct lock_file head_lock;
        struct strbuf buf = STRBUF_INIT;
        int fd;
+       ssize_t written;
 
        fd = hold_lock_file_for_update(&head_lock, git_path_head_file(), 0);
        if (fd < 0) {
@@ -1572,7 +1573,9 @@ static int save_head(const char *head)
                return error_errno(_("could not lock HEAD"));
        }
        strbuf_addf(&buf, "%s\n", head);
-       if (write_in_full(fd, buf.buf, buf.len) < 0) {
+       written = write_in_full(fd, buf.buf, buf.len);
+       strbuf_release(&buf);
+       if (written < 0) {
                rollback_lock_file(&head_lock);
                return error_errno(_("could not write to '%s'"),
                                   git_path_head_file());
diff --git a/setup.c b/setup.c
index 23950173fc01268320d2e23c36ef80a1b1231a5e..6d8380acd2b66ee7d8206639d4b03933afb1816e 100644 (file)
--- a/setup.c
+++ b/setup.c
@@ -399,11 +399,6 @@ void setup_work_tree(void)
        if (getenv(GIT_WORK_TREE_ENVIRONMENT))
                setenv(GIT_WORK_TREE_ENVIRONMENT, ".", 1);
 
-       /*
-        * NEEDSWORK: this call can essentially be set_git_dir(get_git_dir())
-        * which can cause some problems when trying to free the old value of
-        * gitdir.
-        */
        set_git_dir(remove_leading_path(git_dir, work_tree));
        initialized = 1;
 }
index 5f71bbac3ea9a11a4369ec0353e2e1258979ba19..dd7cbe52ef73754f30dfc61379549bc2fbd5e86c 100644 (file)
@@ -1820,6 +1820,7 @@ int index_path(struct object_id *oid, const char *path, struct stat *st, unsigne
 {
        int fd;
        struct strbuf sb = STRBUF_INIT;
+       int rc = 0;
 
        switch (st->st_mode & S_IFMT) {
        case S_IFREG:
@@ -1836,8 +1837,7 @@ int index_path(struct object_id *oid, const char *path, struct stat *st, unsigne
                if (!(flags & HASH_WRITE_OBJECT))
                        hash_sha1_file(sb.buf, sb.len, blob_type, oid->hash);
                else if (write_sha1_file(sb.buf, sb.len, blob_type, oid->hash))
-                       return error("%s: failed to insert into database",
-                                    path);
+                       rc = error("%s: failed to insert into database", path);
                strbuf_release(&sb);
                break;
        case S_IFDIR:
@@ -1845,12 +1845,12 @@ int index_path(struct object_id *oid, const char *path, struct stat *st, unsigne
        default:
                return error("%s: unsupported file type", path);
        }
-       return 0;
+       return rc;
 }
 
 int read_pack_header(int fd, struct pack_header *header)
 {
-       if (read_in_full(fd, header, sizeof(*header)) < sizeof(*header))
+       if (read_in_full(fd, header, sizeof(*header)) != sizeof(*header))
                /* "eof before pack header was fully read" */
                return PH_ERROR_EOF;
 
index 4d32b4f77e042f0da2b5181f753d0933ad438567..e0cc9d988c70337977853101c397960132af1981 100644 (file)
@@ -1,8 +1,19 @@
+#include "cache.h"
+
+#ifdef DC_SHA1_EXTERNAL
 /*
- * This code is included at the end of sha1dc/sha1.c with the
- * SHA1DC_CUSTOM_TRAILING_INCLUDE_SHA1_C macro.
+ * Same as SHA1DCInit, but with default save_hash=0
  */
+void git_SHA1DCInit(SHA1_CTX *ctx)
+{
+       SHA1DCInit(ctx);
+       SHA1DCSetSafeHash(ctx, 0);
+}
+#endif
 
+/*
+ * Same as SHA1DCFinal, but convert collision attack case into a verbose die().
+ */
 void git_SHA1DCFinal(unsigned char hash[20], SHA1_CTX *ctx)
 {
        if (!SHA1DCFinal(hash, ctx))
@@ -11,6 +22,9 @@ void git_SHA1DCFinal(unsigned char hash[20], SHA1_CTX *ctx)
            sha1_to_hex(hash));
 }
 
+/*
+ * Same as SHA1DCUpdate, but adjust types to match git's usual interface.
+ */
 void git_SHA1DCUpdate(SHA1_CTX *ctx, const void *vdata, unsigned long len)
 {
        const char *data = vdata;
index a8a5c1da169e42360fa39851f27a8c4863d0e9ad..a8c272927842901190b07098911ef785a458bcf3 100644 (file)
@@ -1,19 +1,23 @@
-/*
- * This code is included at the end of sha1dc/sha1.h with the
- * SHA1DC_CUSTOM_TRAILING_INCLUDE_SHA1_H macro.
- */
+/* Plumbing with collition-detecting SHA1 code */
 
-/*
- * Same as SHA1DCFinal, but convert collision attack case into a verbose die().
- */
-void git_SHA1DCFinal(unsigned char [20], SHA1_CTX *);
+#ifdef DC_SHA1_SUBMODULE
+#include "sha1collisiondetection/lib/sha1.h"
+#elif defined(DC_SHA1_EXTERNAL)
+#include <sha1dc/sha1.h>
+#else
+#include "sha1dc/sha1.h"
+#endif
+
+#ifdef DC_SHA1_EXTERNAL
+void git_SHA1DCInit(SHA1_CTX *);
+#else
+#define git_SHA1DCInit SHA1DCInit
+#endif
 
-/*
- * Same as SHA1DCUpdate, but adjust types to match git's usual interface.
- */
+void git_SHA1DCFinal(unsigned char [20], SHA1_CTX *);
 void git_SHA1DCUpdate(SHA1_CTX *ctx, const void *data, unsigned long len);
 
 #define platform_SHA_CTX SHA1_CTX
-#define platform_SHA1_Init SHA1DCInit
+#define platform_SHA1_Init git_SHA1DCInit
 #define platform_SHA1_Update git_SHA1DCUpdate
 #define platform_SHA1_Final git_SHA1DCFinal
index f5591e56dab67d6113deb8196c4a68c8af3f2ee0..eabb65d3a7c286832f5a93bb732e8c9c94fa772e 100644 (file)
--- a/shallow.c
+++ b/shallow.c
@@ -286,28 +286,26 @@ int write_shallow_commits(struct strbuf *out, int use_pack_protocol,
        return write_shallow_commits_1(out, use_pack_protocol, extra, 0);
 }
 
-static struct tempfile temporary_shallow;
-
 const char *setup_temporary_shallow(const struct oid_array *extra)
 {
+       struct tempfile *temp;
        struct strbuf sb = STRBUF_INIT;
-       int fd;
 
        if (write_shallow_commits(&sb, 0, extra)) {
-               fd = xmks_tempfile(&temporary_shallow, git_path("shallow_XXXXXX"));
+               temp = xmks_tempfile(git_path("shallow_XXXXXX"));
 
-               if (write_in_full(fd, sb.buf, sb.len) != sb.len)
+               if (write_in_full(temp->fd, sb.buf, sb.len) < 0 ||
+                   close_tempfile_gently(temp) < 0)
                        die_errno("failed to write to %s",
-                                 get_tempfile_path(&temporary_shallow));
-               close_tempfile(&temporary_shallow);
+                                 get_tempfile_path(temp));
                strbuf_release(&sb);
-               return get_tempfile_path(&temporary_shallow);
+               return get_tempfile_path(temp);
        }
        /*
         * is_repository_shallow() sees empty string as "no shallow
         * file".
         */
-       return get_tempfile_path(&temporary_shallow);
+       return "";
 }
 
 void setup_alternate_shallow(struct lock_file *shallow_lock,
@@ -321,7 +319,7 @@ void setup_alternate_shallow(struct lock_file *shallow_lock,
                                       LOCK_DIE_ON_ERROR);
        check_shallow_file_for_update();
        if (write_shallow_commits(&sb, 0, extra)) {
-               if (write_in_full(fd, sb.buf, sb.len) != sb.len)
+               if (write_in_full(fd, sb.buf, sb.len) < 0)
                        die_errno("failed to write to %s",
                                  get_lock_file_path(shallow_lock));
                *alternate_shallow_file = get_lock_file_path(shallow_lock);
@@ -368,7 +366,7 @@ void prune_shallow(int show_only)
                                       LOCK_DIE_ON_ERROR);
        check_shallow_file_for_update();
        if (write_shallow_commits_1(&sb, 0, NULL, SEEN_ONLY)) {
-               if (write_in_full(fd, sb.buf, sb.len) != sb.len)
+               if (write_in_full(fd, sb.buf, sb.len) < 0)
                        die_errno("failed to write to %s",
                                  get_lock_file_path(&shallow_lock));
                commit_lock_file(&shallow_lock);
index 6f1c60f12bc5eea13c4b27cf6c80d690ffe49ab8..5892b50bd89c3c66bdb541ca0100f0671834a542 100644 (file)
@@ -540,7 +540,7 @@ int stream_blob_to_fd(int fd, const struct object_id *oid, struct stream_filter
                        kept = 0;
                wrote = write_in_full(fd, buf, readlen);
 
-               if (wrote != readlen)
+               if (wrote < 0)
                        goto close_and_exit;
        }
        if (kept && (lseek(fd, kept - 1, SEEK_CUR) == (off_t) -1 ||
index 6ccfaaba99c05c0e9112b142a2a0653dccda9eda..6dde5062bef388fcdd3db10d9af19f8ffc7f7d6f 100644 (file)
@@ -184,8 +184,8 @@ static int handshake_capabilities(struct child_process *process,
                        if (supported_capabilities)
                                *supported_capabilities |= capabilities[i].flag;
                } else {
-                       warning("subprocess '%s' requested unsupported capability '%s'",
-                               process->argv[0], p);
+                       die("subprocess '%s' requested unsupported capability '%s'",
+                           process->argv[0], p);
                }
        }
 
index 3cea8221e0bc3dbe77b157384adcecdfc077c7dc..b12600fc798f4427c8ad3bff1384c6fb5a6f4bc5 100644 (file)
@@ -69,6 +69,13 @@ int is_staging_gitmodules_ok(const struct index_state *istate)
        return 1;
 }
 
+static int for_each_remote_ref_submodule(const char *submodule,
+                                        each_ref_fn fn, void *cb_data)
+{
+       return refs_for_each_remote_ref(get_submodule_ref_store(submodule),
+                                       fn, cb_data);
+}
+
 /*
  * Try to update the "path" entry in the "submodule.<name>" section of the
  * .gitmodules file. Return 0 only if a .gitmodules file was found, a section
@@ -767,19 +774,36 @@ static int append_oid_to_argv(const struct object_id *oid, void *data)
        return 0;
 }
 
+struct has_commit_data {
+       int result;
+       const char *path;
+};
+
 static int check_has_commit(const struct object_id *oid, void *data)
 {
-       int *has_commit = data;
+       struct has_commit_data *cb = data;
 
-       if (!lookup_commit_reference(oid))
-               *has_commit = 0;
+       enum object_type type = sha1_object_info(oid->hash, NULL);
 
-       return 0;
+       switch (type) {
+       case OBJ_COMMIT:
+               return 0;
+       case OBJ_BAD:
+               /*
+                * Object is missing or invalid. If invalid, an error message
+                * has already been printed.
+                */
+               cb->result = 0;
+               return 0;
+       default:
+               die(_("submodule entry '%s' (%s) is a %s, not a commit"),
+                   cb->path, oid_to_hex(oid), typename(type));
+       }
 }
 
 static int submodule_has_commits(const char *path, struct oid_array *commits)
 {
-       int has_commit = 1;
+       struct has_commit_data has_commit = { 1, path };
 
        /*
         * Perform a cheap, but incorrect check for the existence of 'commits'.
@@ -795,7 +819,7 @@ static int submodule_has_commits(const char *path, struct oid_array *commits)
 
        oid_array_for_each_unique(commits, check_has_commit, &has_commit);
 
-       if (has_commit) {
+       if (has_commit.result) {
                /*
                 * Even if the submodule is checked out and the commit is
                 * present, make sure it exists in the submodule's object store
@@ -814,12 +838,12 @@ static int submodule_has_commits(const char *path, struct oid_array *commits)
                cp.dir = path;
 
                if (capture_command(&cp, &out, GIT_MAX_HEXSZ + 1) || out.len)
-                       has_commit = 0;
+                       has_commit.result = 0;
 
                strbuf_release(&out);
        }
 
-       return has_commit;
+       return has_commit.result;
 }
 
 static int submodule_needs_pushing(const char *path, struct oid_array *commits)
@@ -1627,6 +1651,8 @@ static int find_first_merges(struct object_array *result, const char *path,
                        oid_to_hex(&a->object.oid));
        init_revisions(&revs, NULL);
        rev_opts.submodule = path;
+       /* FIXME: can't handle linked worktrees in submodules yet */
+       revs.single_worktree = path != NULL;
        setup_revisions(ARRAY_SIZE(rev_args)-1, rev_args, &revs, &rev_opts);
 
        /* save all revisions from the above list that contain b */
index 721650256e63eb51c66d5985c04bc3095f183c1c..7c9d28a8348341c148f150c3371d09dd98693b53 100644 (file)
@@ -35,3 +35,4 @@
 /test-svn-fe
 /test-urlmatch-normalization
 /test-wildmatch
+/test-write-cache
index 59937dc1be1c4f0b3d80e3ef3a86e09bff3703b6..591730adc4f3940940fdb4691da0afb81648e353 100644 (file)
@@ -69,7 +69,7 @@ int cmd_main(int argc, const char **argv)
        }
 
        fd = open (argv[4], O_WRONLY|O_CREAT|O_TRUNC, 0666);
-       if (fd < 0 || write_in_full(fd, out_buf, out_size) != out_size) {
+       if (fd < 0 || write_in_full(fd, out_buf, out_size) < 0) {
                perror(argv[4]);
                return 1;
        }
index 6004c81f0bce4d701a341c963f56caca425da067..1145d5167115a761d319b1e205500e765cd090cd 100644 (file)
@@ -235,7 +235,8 @@ int cmd_main(int argc, const char **argv)
                } else if (!strcmp("size", cmd)) {
 
                        /* print table sizes */
-                       printf("%u %u\n", map.tablesize, map.size);
+                       printf("%u %u\n", map.tablesize,
+                              hashmap_get_size(&map));
 
                } else if (!strcmp("intern", cmd) && l1) {
 
index deb3ae7813052d01b6dab92586e2c37d313ef8ff..68108d956a3f65c868b08ecb81bae87e9c1f5e67 100755 (executable)
@@ -315,7 +315,7 @@ test_expect_success 'setup master' '
        echo >.gitattributes &&
        git checkout -b master &&
        git add .gitattributes &&
-       git commit -m "add .gitattributes" "" &&
+       git commit -m "add .gitattributes" . &&
        printf "\$Id: 0000000000000000000000000000000000000000 \$\nLINEONE\nLINETWO\nLINETHREE"     >LF &&
        printf "\$Id: 0000000000000000000000000000000000000000 \$\r\nLINEONE\r\nLINETWO\r\nLINETHREE" >CRLF &&
        printf "\$Id: 0000000000000000000000000000000000000000 \$\nLINEONE\r\nLINETWO\nLINETHREE"   >CRLF_mix_LF &&
index c34ece48f5ad77a6d53b5f0a1dcf7d53d7413837..100d50e3623c2eb4b3498dd87f95402617c6d737 100755 (executable)
@@ -404,4 +404,77 @@ test_expect_success 'broken reference blocks indirect create' '
        test_cmp expected output.err
 '
 
+test_expect_success 'no bogus intermediate values during delete' '
+       prefix=refs/slow-transaction &&
+       # Set up a reference with differing loose and packed versions:
+       git update-ref $prefix/foo $C &&
+       git pack-refs --all &&
+       git update-ref $prefix/foo $D &&
+       git for-each-ref $prefix >unchanged &&
+       # Now try to update the reference, but hold the `packed-refs` lock
+       # for a while to see what happens while the process is blocked:
+       : >.git/packed-refs.lock &&
+       test_when_finished "rm -f .git/packed-refs.lock" &&
+       {
+               # Note: the following command is intentionally run in the
+               # background. We increase the timeout so that `update-ref`
+               # attempts to acquire the `packed-refs` lock for longer than
+               # it takes for us to do the check then delete it:
+               git -c core.packedrefstimeout=3000 update-ref -d $prefix/foo &
+       } &&
+       pid2=$! &&
+       # Give update-ref plenty of time to get to the point where it tries
+       # to lock packed-refs:
+       sleep 1 &&
+       # Make sure that update-ref did not complete despite the lock:
+       kill -0 $pid2 &&
+       # Verify that the reference still has its old value:
+       sha1=$(git rev-parse --verify --quiet $prefix/foo || echo undefined) &&
+       case "$sha1" in
+       $D)
+               # This is what we hope for; it means that nothing
+               # user-visible has changed yet.
+               : ;;
+       undefined)
+               # This is not correct; it means the deletion has happened
+               # already even though update-ref should not have been
+               # able to acquire the lock yet.
+               echo "$prefix/foo deleted prematurely" &&
+               break
+               ;;
+       $C)
+               # This value should never be seen. Probably the loose
+               # reference has been deleted but the packed reference
+               # is still there:
+               echo "$prefix/foo incorrectly observed to be C" &&
+               break
+               ;;
+       *)
+               # WTF?
+               echo "unexpected value observed for $prefix/foo: $sha1" &&
+               break
+               ;;
+       esac >out &&
+       rm -f .git/packed-refs.lock &&
+       wait $pid2 &&
+       test_must_be_empty out &&
+       test_must_fail git rev-parse --verify --quiet $prefix/foo
+'
+
+test_expect_success 'delete fails cleanly if packed-refs file is locked' '
+       prefix=refs/locked-packed-refs &&
+       # Set up a reference with differing loose and packed versions:
+       git update-ref $prefix/foo $C &&
+       git pack-refs --all &&
+       git update-ref $prefix/foo $D &&
+       git for-each-ref $prefix >unchanged &&
+       # Now try to delete it while the `packed-refs` lock is held:
+       : >.git/packed-refs.lock &&
+       test_when_finished "rm -f .git/packed-refs.lock" &&
+       test_must_fail git update-ref -d $prefix/foo >out 2>err &&
+       git for-each-ref $prefix >actual &&
+       test_i18ngrep "Unable to create $Q.*packed-refs.lock$Q: File exists" err &&
+       test_cmp unchanged actual
+'
+
 test_done
index 5df06f355641ad7a38daf34f9fc841e7fe4f8a0f..8842d0329fb7947e811d4ffccff3bba9e8885d5d 100755 (executable)
@@ -49,4 +49,34 @@ test_expect_success 'create_symref(FOO, refs/heads/master)' '
        test_cmp expected actual
 '
 
+test_expect_success 'for_each_reflog()' '
+       echo $_z40 > .git/logs/PSEUDO-MAIN &&
+       mkdir -p     .git/logs/refs/bisect &&
+       echo $_z40 > .git/logs/refs/bisect/random &&
+
+       echo $_z40 > .git/worktrees/wt/logs/PSEUDO-WT &&
+       mkdir -p     .git/worktrees/wt/logs/refs/bisect &&
+       echo $_z40 > .git/worktrees/wt/logs/refs/bisect/wt-random &&
+
+       $RWT for-each-reflog | cut -c 42- | sort >actual &&
+       cat >expected <<-\EOF &&
+       HEAD 0x1
+       PSEUDO-WT 0x0
+       refs/bisect/wt-random 0x0
+       refs/heads/master 0x0
+       refs/heads/wt-master 0x0
+       EOF
+       test_cmp expected actual &&
+
+       $RMAIN for-each-reflog | cut -c 42- | sort >actual &&
+       cat >expected <<-\EOF &&
+       HEAD 0x1
+       PSEUDO-MAIN 0x0
+       refs/bisect/random 0x0
+       refs/heads/master 0x0
+       refs/heads/wt-master 0x0
+       EOF
+       test_cmp expected actual
+'
+
 test_done
index f8568f8841d34d17f3d8fdae4d8d2404a6693c4d..81c6059a2d9fe4d23e3fac11d765ecaf20aef756 100755 (executable)
@@ -858,9 +858,8 @@ test_expect_success 'rm files with two different errors' '
        test_i18ncmp expect actual
 '
 
-test_expect_success 'rm empty string should invoke warning' '
-       git rm -rf "" 2>output &&
-       test_i18ngrep "warning: empty strings" output
+test_expect_success 'rm empty string should fail' '
+       test_must_fail git rm -rf ""
 '
 
 test_done
index 0aae21d6984bf0bd5c7c7de425a021da6122beee..2748805642201d7c514792bab8d8b3940fb4086c 100755 (executable)
@@ -331,9 +331,8 @@ test_expect_success 'git add --dry-run --ignore-missing of non-existing file out
        test_i18ncmp expect.err actual.err
 '
 
-test_expect_success 'git add empty string should invoke warning' '
-       git add "" 2>output &&
-       test_i18ngrep "warning: empty strings" output
+test_expect_success 'git add empty string should fail' '
+       test_must_fail git add ""
 '
 
 test_expect_success 'git add --chmod=[+-]x stages correctly' '
index 897f6f06d55a9790a237b75c12eaab95763d1864..e9aa97117ab41beab708f4ded34628b015021e4e 100755 (executable)
@@ -73,7 +73,7 @@ test_expect_missing   archive-pathspec/ignored-by-tree
 test_expect_missing    archive-pathspec/ignored-by-tree.d
 test_expect_missing    archive-pathspec/ignored-by-tree.d/file
 test_expect_exists     archive-pathspec/ignored-by-worktree
-test_expect_missing    archive-pathspec/excluded-by-pathspec.d failure
+test_expect_missing    archive-pathspec/excluded-by-pathspec.d
 test_expect_missing    archive-pathspec/excluded-by-pathspec.d/file
 
 test_expect_success 'git archive with wildcard pathspec' '
index 6667d159ab0950f10a3271f1cb81a63953214f17..bda6d7d7e9e835213f9b1323407222850db47ad6 100755 (executable)
@@ -76,7 +76,7 @@ test_expect_missing   archive/deep/and/slashless/ &&
 test_expect_missing    archive/deep/and/slashless/foo &&
 test_expect_missing    archive/deep/with/wildcard/ &&
 test_expect_missing    archive/deep/with/wildcard/foo &&
-test_expect_exists     archive/one-level-lower/
+test_expect_missing    archive/one-level-lower/
 test_expect_missing    archive/one-level-lower/two-levels-lower/ignored-only-if-dir/
 test_expect_missing    archive/one-level-lower/two-levels-lower/ignored-ony-if-dir/ignored-by-ignored-dir
 
index f6207f42b5a4ed382b819cb81631cd0acab61463..ced44355cab99fc4f5fd9768daf10f5a2c1b21c7 100755 (executable)
@@ -108,14 +108,14 @@ test_expect_success 'archive empty subtree with no pathspec' '
        git archive --format=tar $root_tree >subtree-all.tar &&
        make_dir extract &&
        "$TAR" xf subtree-all.tar -C extract &&
-       check_dir extract sub
+       check_dir extract
 '
 
 test_expect_success 'archive empty subtree by direct pathspec' '
        git archive --format=tar $root_tree -- sub >subtree-path.tar &&
        make_dir extract &&
        "$TAR" xf subtree-path.tar -C extract &&
-       check_dir extract sub
+       check_dir extract
 '
 
 ZIPINFO=zipinfo
index 133b5842b1aee8e8e00eb0ca7e1b2d32ff4a4c9a..6694c19a1eecf10117b843bbacbc5bb47924c9c8 100755 (executable)
@@ -283,4 +283,41 @@ test_expect_success 'prune: handle alternate object database' '
        git -C B prune
 '
 
+test_expect_success 'prune: handle index in multiple worktrees' '
+       git worktree add second-worktree &&
+       echo "new blob for second-worktree" >second-worktree/blob &&
+       git -C second-worktree add blob &&
+       git prune --expire=now &&
+       git -C second-worktree show :blob >actual &&
+       test_cmp second-worktree/blob actual
+'
+
+test_expect_success 'prune: handle HEAD in multiple worktrees' '
+       git worktree add --detach third-worktree &&
+       echo "new blob for third-worktree" >third-worktree/blob &&
+       git -C third-worktree add blob &&
+       git -C third-worktree commit -m "third" &&
+       rm .git/worktrees/third-worktree/index &&
+       test_must_fail git -C third-worktree show :blob &&
+       git prune --expire=now &&
+       git -C third-worktree show HEAD:blob >actual &&
+       test_cmp third-worktree/blob actual
+'
+
+test_expect_success 'prune: handle HEAD reflog in multiple worktrees' '
+       git config core.logAllRefUpdates true &&
+       echo "lost blob for third-worktree" >expected &&
+       (
+               cd third-worktree &&
+               cat ../expected >blob &&
+               git add blob &&
+               git commit -m "second commit in third" &&
+               git reset --hard HEAD^
+       ) &&
+       git prune --expire=now &&
+       SHA1=`git hash-object expected` &&
+       git -C third-worktree show "$SHA1" >actual &&
+       test_cmp expected actual
+'
+
 test_done
index 0f84a53146f3bac1bded4d2b11212b8ddef1bf2c..39cb2c1c3489c25545ab3cd69706c0a21378c6b7 100755 (executable)
@@ -298,6 +298,16 @@ test_expect_success 'push succeeds if submodule commit disabling recursion from
        )
 '
 
+test_expect_success 'submodule entry pointing at a tag is error' '
+       git -C work/gar/bage tag -a test1 -m "tag" &&
+       tag=$(git -C work/gar/bage rev-parse test1^{tag}) &&
+       git -C work update-index --cacheinfo 160000 "$tag" gar/bage &&
+       git -C work commit -m "bad commit" &&
+       test_when_finished "git -C work reset --hard HEAD^" &&
+       test_must_fail git -C work push --recurse-submodules=on-demand ../pub.git master 2>err &&
+       test_i18ngrep "is a tag, not a commit" err
+'
+
 test_expect_success 'push fails if recurse submodules option passed as yes' '
        (
                cd work/gar/bage &&
index 077eb07e11594838f9850b4879bbbdeafd3b0c8e..321bd37deb3d32027c18f8184a9ad46aec9c96c7 100755 (executable)
@@ -65,6 +65,38 @@ test_expect_success 'recursive pull updates working tree' '
        test_path_is_file super/sub/merge_strategy.t
 '
 
+test_expect_success "submodule.recurse option triggers recursive pull" '
+       test_commit -C child merge_strategy_2 &&
+       git -C parent submodule update --remote &&
+       git -C parent add sub &&
+       git -C parent commit -m "update submodule" &&
+
+       git -C super -c submodule.recurse pull --no-rebase &&
+       test_path_is_file super/sub/merge_strategy_2.t
+'
+
+test_expect_success " --[no-]recurse-submodule and submodule.recurse" '
+       test_commit -C child merge_strategy_3 &&
+       git -C parent submodule update --remote &&
+       git -C parent add sub &&
+       git -C parent commit -m "update submodule" &&
+
+       git -C super -c submodule.recurse pull --no-recurse-submodules --no-rebase &&
+       test_path_is_missing super/sub/merge_strategy_3.t &&
+       git -C super -c submodule.recurse=false pull --recurse-submodules --no-rebase &&
+       test_path_is_file super/sub/merge_strategy_3.t &&
+
+       test_commit -C child merge_strategy_4 &&
+       git -C parent submodule update --remote &&
+       git -C parent add sub &&
+       git -C parent commit -m "update submodule" &&
+
+       git -C super -c submodule.recurse=false pull --no-recurse-submodules --no-rebase &&
+       test_path_is_missing super/sub/merge_strategy_4.t &&
+       git -C super -c submodule.recurse=true pull --recurse-submodules --no-rebase &&
+       test_path_is_file super/sub/merge_strategy_4.t
+'
+
 test_expect_success 'recursive rebasing pull' '
        # change upstream
        test_commit -C child rebase_strategy &&
index 534903bbd2972301965a3d7eed6fa8e380eeeb86..a6614080388353c828fdd3894c093ef03f71511d 100755 (executable)
@@ -236,17 +236,31 @@ test_sequence "--bisect"
 #
 #
 
-test_expect_success '--bisect can default to good/bad refs' '
+test_expect_success 'set up fake --bisect refs' '
        git update-ref refs/bisect/bad c3 &&
        good=$(git rev-parse b1) &&
        git update-ref refs/bisect/good-$good $good &&
        good=$(git rev-parse c1) &&
-       git update-ref refs/bisect/good-$good $good &&
+       git update-ref refs/bisect/good-$good $good
+'
 
+test_expect_success 'rev-list --bisect can default to good/bad refs' '
        # the only thing between c3 and c1 is c2
        git rev-parse c2 >expect &&
        git rev-list --bisect >actual &&
        test_cmp expect actual
 '
 
+test_expect_success 'rev-parse --bisect can default to good/bad refs' '
+       git rev-parse c3 ^b1 ^c1 >expect &&
+       git rev-parse --bisect >actual &&
+
+       # output order depends on the refnames, which in turn depends on
+       # the exact sha1s. We just want to make sure we have the same set
+       # of lines in any order.
+       sort <expect >expect.sorted &&
+       sort <actual >actual.sorted &&
+       test_cmp expect.sorted actual.sorted
+'
+
 test_done
index aa74eb8f0d5dfb4f228b1e8c7b9dbbc0b88adb4d..dd6dd9df9b52647381fb4bb6ff2a1cdc1ade7469 100755 (executable)
@@ -198,6 +198,31 @@ test_expect_success 'name-rev with exact tags' '
        test_cmp expect actual
 '
 
+test_expect_success 'name-rev --all' '
+       >expect.unsorted &&
+       for rev in $(git rev-list --all)
+       do
+               git name-rev $rev >>expect.unsorted
+       done &&
+       sort <expect.unsorted >expect &&
+       git name-rev --all >actual.unsorted &&
+       sort <actual.unsorted >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'name-rev --stdin' '
+       >expect.unsorted &&
+       for rev in $(git rev-list --all)
+       do
+               name=$(git name-rev --name-only $rev) &&
+               echo "$rev ($name)" >>expect.unsorted
+       done &&
+       sort <expect.unsorted >expect &&
+       git rev-list --all | git name-rev --stdin >actual.unsorted &&
+       sort <actual.unsorted >actual &&
+       test_cmp expect actual
+'
+
 test_expect_success 'describe --contains with the exact tags' '
        echo "A^0" >expect &&
        tag_object=$(git rev-parse refs/tags/A) &&
@@ -250,7 +275,39 @@ test_expect_success 'describe chokes on severely broken submodules' '
 '
 test_expect_success 'describe ignoring a borken submodule' '
        git describe --broken >out &&
+       test_when_finished "mv .git/modules/sub_moved .git/modules/sub1" &&
        grep broken out
 '
 
+# we require ulimit, this excludes Windows
+test_expect_failure ULIMIT_STACK_SIZE 'name-rev works in a deep repo' '
+       i=1 &&
+       while test $i -lt 8000
+       do
+               echo "commit refs/heads/master
+committer A U Thor <author@example.com> $((1000000000 + $i * 100)) +0200
+data <<EOF
+commit #$i
+EOF"
+               test $i = 1 && echo "from refs/heads/master^0"
+               i=$(($i + 1))
+       done | git fast-import &&
+       git checkout master &&
+       git tag far-far-away HEAD^ &&
+       echo "HEAD~4000 tags/far-far-away~3999" >expect &&
+       git name-rev HEAD~4000 >actual &&
+       test_cmp expect actual &&
+       run_with_limited_stack git name-rev HEAD~4000 >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success ULIMIT_STACK_SIZE 'describe works in a deep repo' '
+       git tag -f far-far-away HEAD~7999 &&
+       echo "far-far-away" >expect &&
+       git describe --tags --abbrev=0 HEAD~4000 >actual &&
+       test_cmp expect actual &&
+       run_with_limited_stack git describe --tags --abbrev=0 HEAD~4000 >actual &&
+       test_cmp expect actual
+'
+
 test_done
index dbcd6f623c7e233b487d4d0c1dd7d71cb34059b0..5bf5ace56bfde11e9b080db09e45db8df26a539f 100755 (executable)
@@ -1863,12 +1863,6 @@ test_expect_success 'version sort with very long prerelease suffix' '
        git tag -l --sort=version:refname
 '
 
-run_with_limited_stack () {
-       (ulimit -s 128 && "$@")
-}
-
-test_lazy_prereq ULIMIT_STACK_SIZE 'run_with_limited_stack true'
-
 # we require ulimit, this excludes Windows
 test_expect_success ULIMIT_STACK_SIZE '--contains and --no-contains work in a deep repo' '
        >expect &&
index 88d4cda2992496d8ff5643caaaccf53e90dedc3a..302a3a2082a9da593ace637a2c32d8f4cf992cb3 100755 (executable)
@@ -101,6 +101,10 @@ cat > "$HOOK" <<EOF
 exit 1
 EOF
 
+commit_msg_is () {
+       test "$(git log --pretty=format:%s%b -1)" = "$1"
+}
+
 test_expect_success 'with failing hook' '
 
        echo "another" >> file &&
@@ -135,6 +139,32 @@ test_expect_success '--no-verify with failing hook (editor)' '
 
 '
 
+test_expect_success 'merge fails with failing hook' '
+
+       test_when_finished "git branch -D newbranch" &&
+       test_when_finished "git checkout -f master" &&
+       git checkout --orphan newbranch &&
+       : >file2 &&
+       git add file2 &&
+       git commit --no-verify file2 -m in-side-branch &&
+       test_must_fail git merge --allow-unrelated-histories master &&
+       commit_msg_is "in-side-branch" # HEAD before merge
+
+'
+
+test_expect_success 'merge bypasses failing hook with --no-verify' '
+
+       test_when_finished "git branch -D newbranch" &&
+       test_when_finished "git checkout -f master" &&
+       git checkout --orphan newbranch &&
+       : >file2 &&
+       git add file2 &&
+       git commit --no-verify file2 -m in-side-branch &&
+       git merge --no-verify --allow-unrelated-histories master &&
+       commit_msg_is "Merge branch '\''master'\'' into newbranch"
+'
+
+
 chmod -x "$HOOK"
 test_expect_success POSIXPERM 'with non-executable hook' '
 
@@ -178,10 +208,6 @@ exit 0
 EOF
 chmod +x "$HOOK"
 
-commit_msg_is () {
-       test "$(git log --pretty=format:%s%b -1)" = "$1"
-}
-
 test_expect_success 'hook edits commit message' '
 
        echo "additional" >> file &&
@@ -217,7 +243,36 @@ test_expect_success "hook doesn't edit commit message (editor)" '
        echo "more plus" > FAKE_MSG &&
        GIT_EDITOR="\"\$FAKE_EDITOR\"" git commit --no-verify &&
        commit_msg_is "more plus"
+'
 
+test_expect_success 'hook called in git-merge picks up commit message' '
+       test_when_finished "git branch -D newbranch" &&
+       test_when_finished "git checkout -f master" &&
+       git checkout --orphan newbranch &&
+       : >file2 &&
+       git add file2 &&
+       git commit --no-verify file2 -m in-side-branch &&
+       git merge --allow-unrelated-histories master &&
+       commit_msg_is "new message"
+'
+
+test_expect_failure 'merge --continue remembers --no-verify' '
+       test_when_finished "git branch -D newbranch" &&
+       test_when_finished "git checkout -f master" &&
+       git checkout master &&
+       echo a >file2 &&
+       git add file2 &&
+       git commit --no-verify -m "add file2 to master" &&
+       git checkout -b newbranch master^ &&
+       echo b >file2 &&
+       git add file2 &&
+       git commit --no-verify file2 -m in-side-branch &&
+       git merge --no-verify -m not-rewritten-by-hook master &&
+       # resolve conflict:
+       echo c >file2 &&
+       git add file2 &&
+       git merge --continue &&
+       commit_msg_is not-rewritten-by-hook
 '
 
 # set up fake editor to replace `pick` by `reword`
@@ -237,4 +292,5 @@ test_expect_success 'hook is called for reword during `rebase -i`' '
 
 '
 
+
 test_done
index 5fbd8d4a90b3b88cf57ca53c6b1fe99d5a957460..83f5d3dd21bbb7cae8b41bf2404f97bb43979d2c 100644 (file)
@@ -44,6 +44,11 @@ GIT_BUILD_DIR="$TEST_DIRECTORY"/..
 : ${ASAN_OPTIONS=detect_leaks=0:abort_on_error=1}
 export ASAN_OPTIONS
 
+# If LSAN is in effect we _do_ want leak checking, but we still
+# want to abort so that we notice the problems.
+: ${LSAN_OPTIONS=abort_on_error=1}
+export LSAN_OPTIONS
+
 ################################################################
 # It appears that people try to run tests without building...
 "$GIT_BUILD_DIR/git" >/dev/null
@@ -274,7 +279,7 @@ then
        test -z "$verbose" && verbose_only="$valgrind_only"
 elif test -n "$valgrind"
 then
-       verbose=t
+       test -z "$verbose_log" && verbose=t
 fi
 
 if test -n "$color"
@@ -1167,6 +1172,12 @@ run_with_limited_cmdline () {
 
 test_lazy_prereq CMDLINE_LIMIT 'run_with_limited_cmdline true'
 
+run_with_limited_stack () {
+       (ulimit -s 128 && "$@")
+}
+
+test_lazy_prereq ULIMIT_STACK_SIZE 'run_with_limited_stack true'
+
 build_option () {
        git version --build-options |
        sed -ne "s/^$1: //p"
index 68437106703470f39a6079aaad7d3bb407ac3389..5fdafdd2d2d72390ee9fe3c2afd501ad222fac8e 100644 (file)
  *     `fdopen_tempfile()` has been called on the object
  *   - `owner` holds the PID of the process that created the file
  *
- * - Active, file closed (after successful `close_tempfile()`). Same
+ * - Active, file closed (after `close_tempfile_gently()`). Same
  *   as the previous state, except that the temporary file is closed,
  *   `fd` is -1, and `fp` is `NULL`.
  *
- * - Inactive (after `delete_tempfile()`, `rename_tempfile()`, a
- *   failed attempt to create a temporary file, or a failed
- *   `close_tempfile()`). In this state:
+ * - Inactive (after `delete_tempfile()`, `rename_tempfile()`, or a
+ *   failed attempt to create a temporary file). In this state:
  *
  *   - `active` is unset
  *   - `filename` is empty (usually, though there are transitory
  *     states in which this condition doesn't hold). Client code should
  *     *not* rely on the filename being empty in this state.
  *   - `fd` is -1 and `fp` is `NULL`
- *   - the object is left registered in the `tempfile_list`, and
- *     `on_list` is set.
+ *   - the object is removed from `tempfile_list` (but could be used again)
  *
  * A temporary file is owned by the process that created it. The
  * `tempfile` has an `owner` field that records the owner's PID. This
 #include "tempfile.h"
 #include "sigchain.h"
 
-static struct tempfile *volatile tempfile_list;
+static VOLATILE_LIST_HEAD(tempfile_list);
 
-static void remove_tempfiles(int skip_fclose)
+static void remove_tempfiles(int in_signal_handler)
 {
        pid_t me = getpid();
+       volatile struct volatile_list_head *pos;
 
-       while (tempfile_list) {
-               if (tempfile_list->owner == me) {
-                       /* fclose() is not safe to call in a signal handler */
-                       if (skip_fclose)
-                               tempfile_list->fp = NULL;
-                       delete_tempfile(tempfile_list);
-               }
-               tempfile_list = tempfile_list->next;
+       list_for_each(pos, &tempfile_list) {
+               struct tempfile *p = list_entry(pos, struct tempfile, list);
+
+               if (!is_tempfile_active(p) || p->owner != me)
+                       continue;
+
+               if (p->fd >= 0)
+                       close(p->fd);
+
+               if (in_signal_handler)
+                       unlink(p->filename.buf);
+               else
+                       unlink_or_warn(p->filename.buf);
+
+               p->active = 0;
        }
 }
 
@@ -85,39 +91,48 @@ static void remove_tempfiles_on_signal(int signo)
        raise(signo);
 }
 
-/*
- * Initialize *tempfile if necessary and add it to tempfile_list.
- */
-static void prepare_tempfile_object(struct tempfile *tempfile)
+static struct tempfile *new_tempfile(void)
 {
-       if (!tempfile_list) {
-               /* One-time initialization */
+       struct tempfile *tempfile = xmalloc(sizeof(*tempfile));
+       tempfile->fd = -1;
+       tempfile->fp = NULL;
+       tempfile->active = 0;
+       tempfile->owner = 0;
+       INIT_LIST_HEAD(&tempfile->list);
+       strbuf_init(&tempfile->filename, 0);
+       return tempfile;
+}
+
+static void activate_tempfile(struct tempfile *tempfile)
+{
+       static int initialized;
+
+       if (is_tempfile_active(tempfile))
+               BUG("activate_tempfile called for active object");
+
+       if (!initialized) {
                sigchain_push_common(remove_tempfiles_on_signal);
                atexit(remove_tempfiles_on_exit);
+               initialized = 1;
        }
 
-       if (tempfile->active)
-               die("BUG: prepare_tempfile_object called for active object");
-       if (!tempfile->on_list) {
-               /* Initialize *tempfile and add it to tempfile_list: */
-               tempfile->fd = -1;
-               tempfile->fp = NULL;
-               tempfile->active = 0;
-               tempfile->owner = 0;
-               strbuf_init(&tempfile->filename, 0);
-               tempfile->next = tempfile_list;
-               tempfile_list = tempfile;
-               tempfile->on_list = 1;
-       } else if (tempfile->filename.len) {
-               /* This shouldn't happen, but better safe than sorry. */
-               die("BUG: prepare_tempfile_object called for improperly-reset object");
-       }
+       volatile_list_add(&tempfile->list, &tempfile_list);
+       tempfile->owner = getpid();
+       tempfile->active = 1;
+}
+
+static void deactivate_tempfile(struct tempfile *tempfile)
+{
+       tempfile->active = 0;
+       strbuf_release(&tempfile->filename);
+       volatile_list_del(&tempfile->list);
+       free(tempfile);
 }
 
 /* Make sure errno contains a meaningful value on error */
-int create_tempfile(struct tempfile *tempfile, const char *path)
+struct tempfile *create_tempfile(const char *path)
 {
-       prepare_tempfile_object(tempfile);
+       struct tempfile *tempfile = new_tempfile();
 
        strbuf_add_absolute_path(&tempfile->filename, path);
        tempfile->fd = open(tempfile->filename.buf,
@@ -127,52 +142,48 @@ int create_tempfile(struct tempfile *tempfile, const char *path)
                tempfile->fd = open(tempfile->filename.buf,
                                    O_RDWR | O_CREAT | O_EXCL, 0666);
        if (tempfile->fd < 0) {
-               strbuf_reset(&tempfile->filename);
-               return -1;
+               deactivate_tempfile(tempfile);
+               return NULL;
        }
-       tempfile->owner = getpid();
-       tempfile->active = 1;
+       activate_tempfile(tempfile);
        if (adjust_shared_perm(tempfile->filename.buf)) {
                int save_errno = errno;
                error("cannot fix permission bits on %s", tempfile->filename.buf);
-               delete_tempfile(tempfile);
+               delete_tempfile(&tempfile);
                errno = save_errno;
-               return -1;
+               return NULL;
        }
-       return tempfile->fd;
+
+       return tempfile;
 }
 
-void register_tempfile(struct tempfile *tempfile, const char *path)
+struct tempfile *register_tempfile(const char *path)
 {
-       prepare_tempfile_object(tempfile);
+       struct tempfile *tempfile = new_tempfile();
        strbuf_add_absolute_path(&tempfile->filename, path);
-       tempfile->owner = getpid();
-       tempfile->active = 1;
+       activate_tempfile(tempfile);
+       return tempfile;
 }
 
-int mks_tempfile_sm(struct tempfile *tempfile,
-                   const char *template, int suffixlen, int mode)
+struct tempfile *mks_tempfile_sm(const char *template, int suffixlen, int mode)
 {
-       prepare_tempfile_object(tempfile);
+       struct tempfile *tempfile = new_tempfile();
 
        strbuf_add_absolute_path(&tempfile->filename, template);
        tempfile->fd = git_mkstemps_mode(tempfile->filename.buf, suffixlen, mode);
        if (tempfile->fd < 0) {
-               strbuf_reset(&tempfile->filename);
-               return -1;
+               deactivate_tempfile(tempfile);
+               return NULL;
        }
-       tempfile->owner = getpid();
-       tempfile->active = 1;
-       return tempfile->fd;
+       activate_tempfile(tempfile);
+       return tempfile;
 }
 
-int mks_tempfile_tsm(struct tempfile *tempfile,
-                    const char *template, int suffixlen, int mode)
+struct tempfile *mks_tempfile_tsm(const char *template, int suffixlen, int mode)
 {
+       struct tempfile *tempfile = new_tempfile();
        const char *tmpdir;
 
-       prepare_tempfile_object(tempfile);
-
        tmpdir = getenv("TMPDIR");
        if (!tmpdir)
                tmpdir = "/tmp";
@@ -180,35 +191,34 @@ int mks_tempfile_tsm(struct tempfile *tempfile,
        strbuf_addf(&tempfile->filename, "%s/%s", tmpdir, template);
        tempfile->fd = git_mkstemps_mode(tempfile->filename.buf, suffixlen, mode);
        if (tempfile->fd < 0) {
-               strbuf_reset(&tempfile->filename);
-               return -1;
+               deactivate_tempfile(tempfile);
+               return NULL;
        }
-       tempfile->owner = getpid();
-       tempfile->active = 1;
-       return tempfile->fd;
+       activate_tempfile(tempfile);
+       return tempfile;
 }
 
-int xmks_tempfile_m(struct tempfile *tempfile, const char *template, int mode)
+struct tempfile *xmks_tempfile_m(const char *template, int mode)
 {
-       int fd;
+       struct tempfile *tempfile;
        struct strbuf full_template = STRBUF_INIT;
 
        strbuf_add_absolute_path(&full_template, template);
-       fd = mks_tempfile_m(tempfile, full_template.buf, mode);
-       if (fd < 0)
+       tempfile = mks_tempfile_m(full_template.buf, mode);
+       if (!tempfile)
                die_errno("Unable to create temporary file '%s'",
                          full_template.buf);
 
        strbuf_release(&full_template);
-       return fd;
+       return tempfile;
 }
 
 FILE *fdopen_tempfile(struct tempfile *tempfile, const char *mode)
 {
-       if (!tempfile->active)
-               die("BUG: fdopen_tempfile() called for inactive object");
+       if (!is_tempfile_active(tempfile))
+               BUG("fdopen_tempfile() called for inactive object");
        if (tempfile->fp)
-               die("BUG: fdopen_tempfile() called for open object");
+               BUG("fdopen_tempfile() called for open object");
 
        tempfile->fp = fdopen(tempfile->fd, mode);
        return tempfile->fp;
@@ -216,34 +226,36 @@ FILE *fdopen_tempfile(struct tempfile *tempfile, const char *mode)
 
 const char *get_tempfile_path(struct tempfile *tempfile)
 {
-       if (!tempfile->active)
-               die("BUG: get_tempfile_path() called for inactive object");
+       if (!is_tempfile_active(tempfile))
+               BUG("get_tempfile_path() called for inactive object");
        return tempfile->filename.buf;
 }
 
 int get_tempfile_fd(struct tempfile *tempfile)
 {
-       if (!tempfile->active)
-               die("BUG: get_tempfile_fd() called for inactive object");
+       if (!is_tempfile_active(tempfile))
+               BUG("get_tempfile_fd() called for inactive object");
        return tempfile->fd;
 }
 
 FILE *get_tempfile_fp(struct tempfile *tempfile)
 {
-       if (!tempfile->active)
-               die("BUG: get_tempfile_fp() called for inactive object");
+       if (!is_tempfile_active(tempfile))
+               BUG("get_tempfile_fp() called for inactive object");
        return tempfile->fp;
 }
 
-int close_tempfile(struct tempfile *tempfile)
+int close_tempfile_gently(struct tempfile *tempfile)
 {
-       int fd = tempfile->fd;
-       FILE *fp = tempfile->fp;
+       int fd;
+       FILE *fp;
        int err;
 
-       if (fd < 0)
+       if (!is_tempfile_active(tempfile) || tempfile->fd < 0)
                return 0;
 
+       fd = tempfile->fd;
+       fp = tempfile->fp;
        tempfile->fd = -1;
        if (fp) {
                tempfile->fp = NULL;
@@ -258,54 +270,52 @@ int close_tempfile(struct tempfile *tempfile)
                err = close(fd);
        }
 
-       if (err) {
-               int save_errno = errno;
-               delete_tempfile(tempfile);
-               errno = save_errno;
-               return -1;
-       }
-
-       return 0;
+       return err ? -1 : 0;
 }
 
 int reopen_tempfile(struct tempfile *tempfile)
 {
+       if (!is_tempfile_active(tempfile))
+               BUG("reopen_tempfile called for an inactive object");
        if (0 <= tempfile->fd)
-               die("BUG: reopen_tempfile called for an open object");
-       if (!tempfile->active)
-               die("BUG: reopen_tempfile called for an inactive object");
+               BUG("reopen_tempfile called for an open object");
        tempfile->fd = open(tempfile->filename.buf, O_WRONLY);
        return tempfile->fd;
 }
 
-int rename_tempfile(struct tempfile *tempfile, const char *path)
+int rename_tempfile(struct tempfile **tempfile_p, const char *path)
 {
-       if (!tempfile->active)
-               die("BUG: rename_tempfile called for inactive object");
+       struct tempfile *tempfile = *tempfile_p;
+
+       if (!is_tempfile_active(tempfile))
+               BUG("rename_tempfile called for inactive object");
 
-       if (close_tempfile(tempfile))
+       if (close_tempfile_gently(tempfile)) {
+               delete_tempfile(tempfile_p);
                return -1;
+       }
 
        if (rename(tempfile->filename.buf, path)) {
                int save_errno = errno;
-               delete_tempfile(tempfile);
+               delete_tempfile(tempfile_p);
                errno = save_errno;
                return -1;
        }
 
-       tempfile->active = 0;
-       strbuf_reset(&tempfile->filename);
+       deactivate_tempfile(tempfile);
+       *tempfile_p = NULL;
        return 0;
 }
 
-void delete_tempfile(struct tempfile *tempfile)
+void delete_tempfile(struct tempfile **tempfile_p)
 {
-       if (!tempfile->active)
+       struct tempfile *tempfile = *tempfile_p;
+
+       if (!is_tempfile_active(tempfile))
                return;
 
-       if (!close_tempfile(tempfile)) {
-               unlink_or_warn(tempfile->filename.buf);
-               tempfile->active = 0;
-               strbuf_reset(&tempfile->filename);
-       }
+       close_tempfile_gently(tempfile);
+       unlink_or_warn(tempfile->filename.buf);
+       deactivate_tempfile(tempfile);
+       *tempfile_p = NULL;
 }
index 2f0038decd5b6d00b55fa03ec8988a3810d1784f..b8f4b5e145b8b6b1d80cac299dbaedc8253e861b 100644 (file)
@@ -1,6 +1,8 @@
 #ifndef TEMPFILE_H
 #define TEMPFILE_H
 
+#include "list.h"
+
 /*
  * Handle temporary files.
  *
  *
  * The caller:
  *
- * * Allocates a `struct tempfile` either as a static variable or on
- *   the heap, initialized to zeros. Once you use the structure to
- *   call `create_tempfile()`, it belongs to the tempfile subsystem
- *   and its storage must remain valid throughout the life of the
- *   program (i.e. you cannot use an on-stack variable to hold this
- *   structure).
- *
  * * Attempts to create a temporary file by calling
- *   `create_tempfile()`.
+ *   `create_tempfile()`. The resources used for the temporary file are
+ *   managed by the tempfile API.
  *
  * * Writes new content to the file by either:
  *
- *   * writing to the file descriptor returned by `create_tempfile()`
- *     (also available via `tempfile->fd`).
+ *   * writing to the `tempfile->fd` file descriptor
  *
  *   * calling `fdopen_tempfile()` to get a `FILE` pointer for the
  *     open file and writing to the file using stdio.
  *
- *   Note that the file descriptor returned by create_tempfile()
+ *   Note that the file descriptor created by create_tempfile()
  *   is marked O_CLOEXEC, so the new contents must be written by
  *   the current process, not any spawned one.
  *
  *   control of the file.
  *
  * * Close the file descriptor without removing or renaming the
- *   temporary file by calling `close_tempfile()`, and later call
+ *   temporary file by calling `close_tempfile_gently()`, and later call
  *   `delete_tempfile()` or `rename_tempfile()`.
  *
- * Even after the temporary file is renamed or deleted, the `tempfile`
- * object must not be freed or altered by the caller. However, it may
- * be reused; just pass it to another call of `create_tempfile()`.
+ * After the temporary file is renamed or deleted, the `tempfile`
+ * object is no longer valid and should not be reused.
  *
  * If the program exits before `rename_tempfile()` or
  * `delete_tempfile()` is called, an `atexit(3)` handler will close
  * and remove the temporary file.
  *
  * If you need to close the file descriptor yourself, do so by calling
- * `close_tempfile()`. You should never call `close(2)` or `fclose(3)`
+ * `close_tempfile_gently()`. You should never call `close(2)` or `fclose(3)`
  * yourself, otherwise the `struct tempfile` structure would still
  * think that the file descriptor needs to be closed, and a later
  * cleanup would result in duplicate calls to `close(2)`. Worse yet,
  * Error handling
  * --------------
  *
- * `create_tempfile()` returns a file descriptor on success or -1 on
- * failure. On errors, `errno` describes the reason for failure.
+ * `create_tempfile()` returns an allocated tempfile on success or NULL
+ * on failure. On errors, `errno` describes the reason for failure.
  *
- * `delete_tempfile()`, `rename_tempfile()`, and `close_tempfile()`
- * return 0 on success. On failure they set `errno` appropriately, do
- * their best to delete the temporary file, and return -1.
+ * `delete_tempfile()`, `rename_tempfile()`, and `close_tempfile_gently()`
+ * return 0 on success. On failure they set `errno` appropriately and return
+ * -1. `delete` and `rename` (but not `close`) do their best to delete the
+ * temporary file before returning.
  */
 
 struct tempfile {
-       struct tempfile *volatile next;
+       volatile struct volatile_list_head list;
        volatile sig_atomic_t active;
        volatile int fd;
        FILE *volatile fp;
        volatile pid_t owner;
-       char on_list;
        struct strbuf filename;
 };
 
 /*
  * Attempt to create a temporary file at the specified `path`. Return
- * a file descriptor for writing to it, or -1 on error. It is an error
- * if a file already exists at that path.
+ * a tempfile (whose "fd" member can be used for writing to it), or
+ * NULL on error. It is an error if a file already exists at that path.
  */
-extern int create_tempfile(struct tempfile *tempfile, const char *path);
+extern struct tempfile *create_tempfile(const char *path);
 
 /*
  * Register an existing file as a tempfile, meaning that it will be
@@ -102,7 +96,7 @@ extern int create_tempfile(struct tempfile *tempfile, const char *path);
  * but it can be worked with like any other closed tempfile (for
  * example, it can be opened using reopen_tempfile()).
  */
-extern void register_tempfile(struct tempfile *tempfile, const char *path);
+extern struct tempfile *register_tempfile(const char *path);
 
 
 /*
@@ -134,83 +128,78 @@ extern void register_tempfile(struct tempfile *tempfile, const char *path);
  * know the (absolute) path of the file that was created, it can be
  * read from tempfile->filename.
  *
- * On success, the functions return a file descriptor that is open for
- * writing the temporary file. On errors, they return -1 and set errno
- * appropriately (except for the "x" variants, which die() on errors).
+ * On success, the functions return a tempfile whose "fd" member is open
+ * for writing the temporary file. On errors, they return NULL and set
+ * errno appropriately (except for the "x" variants, which die() on
+ * errors).
  */
 
 /* See "mks_tempfile functions" above. */
-extern int mks_tempfile_sm(struct tempfile *tempfile,
-                          const char *template, int suffixlen, int mode);
+extern struct tempfile *mks_tempfile_sm(const char *template,
+                                       int suffixlen, int mode);
 
 /* See "mks_tempfile functions" above. */
-static inline int mks_tempfile_s(struct tempfile *tempfile,
-                                const char *template, int suffixlen)
+static inline struct tempfile *mks_tempfile_s(const char *template,
+                                             int suffixlen)
 {
-       return mks_tempfile_sm(tempfile, template, suffixlen, 0600);
+       return mks_tempfile_sm(template, suffixlen, 0600);
 }
 
 /* See "mks_tempfile functions" above. */
-static inline int mks_tempfile_m(struct tempfile *tempfile,
-                                const char *template, int mode)
+static inline struct tempfile *mks_tempfile_m(const char *template, int mode)
 {
-       return mks_tempfile_sm(tempfile, template, 0, mode);
+       return mks_tempfile_sm(template, 0, mode);
 }
 
 /* See "mks_tempfile functions" above. */
-static inline int mks_tempfile(struct tempfile *tempfile,
-                              const char *template)
+static inline struct tempfile *mks_tempfile(const char *template)
 {
-       return mks_tempfile_sm(tempfile, template, 0, 0600);
+       return mks_tempfile_sm(template, 0, 0600);
 }
 
 /* See "mks_tempfile functions" above. */
-extern int mks_tempfile_tsm(struct tempfile *tempfile,
-                           const char *template, int suffixlen, int mode);
+extern struct tempfile *mks_tempfile_tsm(const char *template,
+                                        int suffixlen, int mode);
 
 /* See "mks_tempfile functions" above. */
-static inline int mks_tempfile_ts(struct tempfile *tempfile,
-                                 const char *template, int suffixlen)
+static inline struct tempfile *mks_tempfile_ts(const char *template,
+                                              int suffixlen)
 {
-       return mks_tempfile_tsm(tempfile, template, suffixlen, 0600);
+       return mks_tempfile_tsm(template, suffixlen, 0600);
 }
 
 /* See "mks_tempfile functions" above. */
-static inline int mks_tempfile_tm(struct tempfile *tempfile,
-                                 const char *template, int mode)
+static inline struct tempfile *mks_tempfile_tm(const char *template, int mode)
 {
-       return mks_tempfile_tsm(tempfile, template, 0, mode);
+       return mks_tempfile_tsm(template, 0, mode);
 }
 
 /* See "mks_tempfile functions" above. */
-static inline int mks_tempfile_t(struct tempfile *tempfile,
-                                const char *template)
+static inline struct tempfile *mks_tempfile_t(const char *template)
 {
-       return mks_tempfile_tsm(tempfile, template, 0, 0600);
+       return mks_tempfile_tsm(template, 0, 0600);
 }
 
 /* See "mks_tempfile functions" above. */
-extern int xmks_tempfile_m(struct tempfile *tempfile,
-                          const char *template, int mode);
+extern struct tempfile *xmks_tempfile_m(const char *template, int mode);
 
 /* See "mks_tempfile functions" above. */
-static inline int xmks_tempfile(struct tempfile *tempfile,
-                               const char *template)
+static inline struct tempfile *xmks_tempfile(const char *template)
 {
-       return xmks_tempfile_m(tempfile, template, 0600);
+       return xmks_tempfile_m(template, 0600);
 }
 
 /*
  * Associate a stdio stream with the temporary file (which must still
  * be open). Return `NULL` (*without* deleting the file) on error. The
- * stream is closed automatically when `close_tempfile()` is called or
+ * stream is closed automatically when `close_tempfile_gently()` is called or
  * when the file is deleted or renamed.
  */
 extern FILE *fdopen_tempfile(struct tempfile *tempfile, const char *mode);
 
 static inline int is_tempfile_active(struct tempfile *tempfile)
 {
-       return tempfile->active;
+       return tempfile && tempfile->active;
 }
 
 /*
@@ -226,20 +215,20 @@ extern FILE *get_tempfile_fp(struct tempfile *tempfile);
  * If the temporary file is still open, close it (and the file pointer
  * too, if it has been opened using `fdopen_tempfile()`) without
  * deleting the file. Return 0 upon success. On failure to `close(2)`,
- * return a negative value and delete the file. Usually
- * `delete_tempfile()` or `rename_tempfile()` should eventually be
- * called if `close_tempfile()` succeeds.
+ * return a negative value. Usually `delete_tempfile()` or `rename_tempfile()`
+ * should eventually be called regardless of whether `close_tempfile_gently()`
+ * succeeds.
  */
-extern int close_tempfile(struct tempfile *tempfile);
+extern int close_tempfile_gently(struct tempfile *tempfile);
 
 /*
  * Re-open a temporary file that has been closed using
- * `close_tempfile()` but not yet deleted or renamed. This can be used
+ * `close_tempfile_gently()` but not yet deleted or renamed. This can be used
  * to implement a sequence of operations like the following:
  *
  * * Create temporary file.
  *
- * * Write new contents to file, then `close_tempfile()` to cause the
+ * * Write new contents to file, then `close_tempfile_gently()` to cause the
  *   contents to be written to disk.
  *
  * * Pass the name of the temporary file to another program to allow
@@ -259,7 +248,7 @@ extern int reopen_tempfile(struct tempfile *tempfile);
  * `delete_tempfile()` for a `tempfile` object that has already been
  * deleted or renamed.
  */
-extern void delete_tempfile(struct tempfile *tempfile);
+extern void delete_tempfile(struct tempfile **tempfile_p);
 
 /*
  * Close the file descriptor and/or file pointer if they are still
@@ -270,6 +259,6 @@ extern void delete_tempfile(struct tempfile *tempfile);
  * `rename(2)`. It is a bug to call `rename_tempfile()` for a
  * `tempfile` object that is not currently active.
  */
-extern int rename_tempfile(struct tempfile *tempfile, const char *path);
+extern int rename_tempfile(struct tempfile **tempfile_p, const char *path);
 
 #endif /* TEMPFILE_H */
index c30e3a0c0415db110bfce8d29514a8258647d7f7..3ba157ed0d6157281f0a9e91f7b7f208652adfcc 100644 (file)
--- a/trailer.c
+++ b/trailer.c
@@ -995,7 +995,7 @@ static void free_all(struct list_head *head)
        }
 }
 
-static struct tempfile trailers_tempfile;
+static struct tempfile *trailers_tempfile;
 
 static FILE *create_in_place_tempfile(const char *file)
 {
@@ -1017,9 +1017,9 @@ static FILE *create_in_place_tempfile(const char *file)
                strbuf_add(&template, file, tail - file + 1);
        strbuf_addstr(&template, "git-interpret-trailers-XXXXXX");
 
-       xmks_tempfile_m(&trailers_tempfile, template.buf, st.st_mode);
+       trailers_tempfile = xmks_tempfile_m(template.buf, st.st_mode);
        strbuf_release(&template);
-       outfile = fdopen_tempfile(&trailers_tempfile, "w");
+       outfile = fdopen_tempfile(trailers_tempfile, "w");
        if (!outfile)
                die_errno(_("could not open temporary file"));
 
index f50b34df2dba4827db6a96cf97d1c4ac7ca811a0..c948d5215c22fbbb3e174e219017935741e9b2d5 100644 (file)
@@ -44,8 +44,7 @@ static void sendline(struct helper_data *helper, struct strbuf *buffer)
 {
        if (debug)
                fprintf(stderr, "Debug: Remote helper: -> %s", buffer->buf);
-       if (write_in_full(helper->helper->in, buffer->buf, buffer->len)
-               != buffer->len)
+       if (write_in_full(helper->helper->in, buffer->buf, buffer->len) < 0)
                die_errno("Full write to remote helper failed");
 }
 
@@ -74,7 +73,7 @@ static void write_constant(int fd, const char *str)
 {
        if (debug)
                fprintf(stderr, "Debug: Remote helper: -> %s", str);
-       if (write_in_full(fd, str, strlen(str)) != strlen(str))
+       if (write_in_full(fd, str, strlen(str)) < 0)
                die_errno("Full write to remote helper failed");
 }
 
@@ -604,6 +603,7 @@ static int process_connect_service(struct transport *transport,
                        cmdbuf.buf);
 
 exit:
+       strbuf_release(&cmdbuf);
        fclose(input);
        return ret;
 }
diff --git a/usage.c b/usage.c
index 1ea7df9a202339972ee59f35a5ba8852502c915f..cdd534c9dfc4bd38ce112da62643ab2dc7b4fbe9 100644 (file)
--- a/usage.c
+++ b/usage.c
@@ -241,3 +241,18 @@ NORETURN void BUG(const char *fmt, ...)
        va_end(ap);
 }
 #endif
+
+#ifdef SUPPRESS_ANNOTATED_LEAKS
+void unleak_memory(const void *ptr, size_t len)
+{
+       static struct suppressed_leak_root {
+               struct suppressed_leak_root *next;
+               char data[FLEX_ARRAY];
+       } *suppressed_leaks;
+       struct suppressed_leak_root *root;
+
+       FLEX_ALLOC_MEM(root, data, ptr, len);
+       root->next = suppressed_leaks;
+       suppressed_leaks = root;
+}
+#endif
index 2c1502f719a17abaef6c5c56439c560994b45d37..6321103ce25dea936eea84fdb2a0d6d636e1cddb 100644 (file)
@@ -293,6 +293,7 @@ struct userdiff_driver *userdiff_get_textconv(struct userdiff_driver *driver)
                strbuf_addf(&name, "textconv/%s", driver->name);
                notes_cache_init(c, name.buf, driver->textconv);
                driver->textconv_cache = c;
+               strbuf_release(&name);
        }
 
        return driver;
diff --git a/utf8.c b/utf8.c
index 0c8e011a58cae3c683851007ec81828ad8284471..47a42047c814fb5c1a933d75fe1b5ecf338dfda1 100644 (file)
--- a/utf8.c
+++ b/utf8.c
@@ -381,7 +381,7 @@ void strbuf_utf8_replace(struct strbuf *sb_src, int pos, int width,
                old = src;
                n = utf8_width((const char**)&src, NULL);
                if (!src)       /* broken utf-8, do nothing */
-                       return;
+                       goto out;
                if (n && w >= pos && w < pos + width) {
                        if (subst) {
                                memcpy(dst, subst, subst_len);
@@ -397,6 +397,7 @@ void strbuf_utf8_replace(struct strbuf *sb_src, int pos, int width,
        }
        strbuf_setlen(&sb_dst, dst - sb_dst.buf);
        strbuf_swap(sb_src, &sb_dst);
+out:
        strbuf_release(&sb_dst);
 }
 
index ec6b350611d367fd75f909c4695d9faf2d68f91e..08d136b8cc5ccdeb858eeb60c4ad4b2e42939aab 100644 (file)
@@ -318,6 +318,7 @@ static void end_revision(const char *note_ref)
                strbuf_addf(&mark, ":%"PRIu32, rev_ctx.revision);
                fast_export_note(mark.buf, "inline");
                fast_export_buf_to_data(&rev_ctx.note);
+               strbuf_release(&mark);
        }
 }
 
index c0c5a2b3735c89fc98d7aa29863eb4a28651d808..8aaeea03771cfefa9edd019d0dc5146fef17a9c7 100644 (file)
@@ -386,3 +386,25 @@ int submodule_uses_worktrees(const char *path)
        closedir(dir);
        return ret;
 }
+
+int other_head_refs(each_ref_fn fn, void *cb_data)
+{
+       struct worktree **worktrees, **p;
+       int ret = 0;
+
+       worktrees = get_worktrees(0);
+       for (p = worktrees; *p; p++) {
+               struct worktree *wt = *p;
+               struct ref_store *refs;
+
+               if (wt->is_current)
+                       continue;
+
+               refs = get_worktree_ref_store(wt);
+               ret = refs_head_ref(refs, fn, cb_data);
+               if (ret)
+                       break;
+       }
+       free_worktrees(worktrees);
+       return ret;
+}
index 5ea5e503fbe491a76d3c8ce279b7e281a5d07d3e..9276c81ae7cb5c42a86fd9ebbdb28720be409e47 100644 (file)
@@ -1,6 +1,8 @@
 #ifndef WORKTREE_H
 #define WORKTREE_H
 
+#include "refs.h"
+
 struct worktree {
        char *path;
        char *id;
@@ -70,6 +72,12 @@ extern void free_worktrees(struct worktree **);
 extern const struct worktree *find_shared_symref(const char *symref,
                                                 const char *target);
 
+/*
+ * Similar to head_ref() for all HEADs _except_ one from the current
+ * worktree, which is covered by head_ref().
+ */
+int other_head_refs(each_ref_fn fn, void *cb_data);
+
 int is_worktree_being_rebased(const struct worktree *wt, const char *target);
 int is_worktree_being_bisected(const struct worktree *wt, const char *target);
 
index 36630e5d1855a41407e217e610aa1f293288abe1..61aba0b5c1bece4aad04bee649accae990a8ce18 100644 (file)
--- a/wrapper.c
+++ b/wrapper.c
@@ -652,7 +652,7 @@ int xsnprintf(char *dst, size_t max, const char *fmt, ...)
 void write_file_buf(const char *path, const char *buf, size_t len)
 {
        int fd = xopen(path, O_WRONLY | O_CREAT | O_TRUNC, 0666);
-       if (write_in_full(fd, buf, len) != len)
+       if (write_in_full(fd, buf, len) < 0)
                die_errno(_("could not write to %s"), path);
        if (close(fd))
                die_errno(_("could not close %s"), path);
index 77c27c51134d2feff93befeee20f4523a37d524a..6f730ee8f22b958c7502656d2e1d01ea1f932396 100644 (file)
@@ -934,7 +934,7 @@ size_t wt_status_locate_end(const char *s, size_t len)
 
 void wt_status_add_cut_line(FILE *fp)
 {
-       const char *explanation = _("Do not touch the line above.\nEverything below will be removed.");
+       const char *explanation = _("Do not modify or remove the line above.\nEverything below it will be ignored.");
        struct strbuf buf = STRBUF_INIT;
 
        fprintf(fp, "%c %s", comment_line_char, cut_line);
@@ -1026,6 +1026,7 @@ static void wt_longstatus_print_tracking(struct wt_status *s)
                                 comment_line_char);
        else
                fputs("\n", s->fp);
+       strbuf_release(&sb);
 }
 
 static int has_unmerged(struct wt_status *s)
@@ -1193,6 +1194,7 @@ static int read_rebase_todolist(const char *fname, struct string_list *lines)
                string_list_append(lines, line.buf);
        }
        fclose(f);
+       strbuf_release(&line);
        return 0;
 }