Merge branch 'sb/merge-commit-msg-hook' into next
authorJunio C Hamano <gitster@pobox.com>
Mon, 25 Sep 2017 06:31:53 +0000 (15:31 +0900)
committerJunio C Hamano <gitster@pobox.com>
Mon, 25 Sep 2017 06:31:57 +0000 (15:31 +0900)
Add documentation for a topic that has recently graduated to the
'master' branch.

* sb/merge-commit-msg-hook:
Documentation/githooks: mention merge in commit-msg hook

190 files changed:
.clang-format [new file with mode: 0644]
.travis.yml
.tsan-suppressions [new file with mode: 0644]
Documentation/RelNotes/2.14.2.txt
Documentation/RelNotes/2.15.0.txt
Documentation/git-apply.txt
Documentation/git-cat-file.txt
Documentation/git-checkout.txt
Documentation/git-cvsserver.txt
Documentation/git-describe.txt
Documentation/git-diff-index.txt
Documentation/git-for-each-ref.txt
Documentation/git-merge.txt
Documentation/git-notes.txt
Documentation/git-rebase.txt
Documentation/git-rerere.txt
Documentation/git-rev-parse.txt
Documentation/git-rm.txt
Documentation/git-svn.txt
Documentation/git-update-index.txt
Documentation/gitattributes.txt
Documentation/gitcore-tutorial.txt
Documentation/githooks.txt
Documentation/gitrepository-layout.txt
Documentation/gittutorial.txt
Documentation/merge-options.txt
Documentation/rev-list-options.txt
Documentation/technical/api-config.txt
Documentation/technical/api-ref-iteration.txt
Documentation/technical/pack-protocol.txt
Documentation/technical/trivial-merge.txt
Documentation/user-manual.txt
Makefile
apply.c
archive.c
attr.c
branch.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/fast-export.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
color.c
commit.c
config.c
config.h
connect.c
contrib/diff-highlight/Makefile
contrib/examples/git-merge.sh
contrib/examples/git-resolve.sh
contrib/subtree/t/t7900-subtree.sh
convert.c
credential-cache--daemon.c
diff.c
dir.c
entry.c
environment.c
exec_cmd.c
fast-import.c
git-compat-util.h
git-gui/po/README
git-merge-octopus.sh
git-p4.py
git-rebase.sh
git-send-email.perl
gpg-interface.c
hash.h
hashmap.c
hashmap.h
http-backend.c
imap-send.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
pathspec.h
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
remote.c
repository.c
rerere.c
revision.c
revision.h
send-pack.c
sequencer.c
setup.c
sha1-lookup.h
sha1_file.c
sha1dc_git.c
sha1dc_git.h
shallow.c
strbuf.h
streaming.c
string-list.h
sub-process.c
submodule.c
t/README
t/check-non-portable-shell.pl
t/helper/.gitignore
t/helper/test-delta.c
t/helper/test-hashmap.c
t/t0027-auto-crlf.sh
t/t1400-update-ref.sh
t/t1404-update-ref-errors.sh
t/t1407-worktree-ref-store.sh
t/t1500-rev-parse.sh
t/t1502-rev-parse-parseopt.sh
t/t3200-branch.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/t6040-tracking-info.sh
t/t6120-describe.sh
t/t7001-mv.sh
t/t7004-tag.sh
t/t9001-send-email.sh
t/t9010-svn-fe.sh
t/t9350-fast-export.sh
t/test-lib.sh
tempfile.c
tempfile.h
templates/hooks--pre-rebase.sample
trailer.c
transport-helper.c
unpack-trees.c
usage.c
userdiff.c
utf8.c
vcs-svn/svndump.c
worktree.c
worktree.h
wrapper.c
wt-status.c
diff --git a/.clang-format b/.clang-format
new file mode 100644 (file)
index 0000000..3ede262
--- /dev/null
@@ -0,0 +1,165 @@
+# Defaults
+
+# Use tabs whenever we need to fill whitespace that spans at least from one tab
+# stop to the next one.
+UseTab: Always
+TabWidth: 8
+IndentWidth: 8
+ContinuationIndentWidth: 8
+ColumnLimit: 80
+
+# C Language specifics
+Language: Cpp
+
+# Align parameters on the open bracket
+# someLongFunction(argument1,
+#                  argument2);
+AlignAfterOpenBracket: Align
+
+# Don't align consecutive assignments
+# int aaaa = 12;
+# int b = 14;
+AlignConsecutiveAssignments: false
+
+# Don't align consecutive declarations
+# int aaaa = 12;
+# double b = 3.14;
+AlignConsecutiveDeclarations: false
+
+# Align escaped newlines as far left as possible
+# #define A   \
+#   int aaaa; \
+#   int b;    \
+#   int cccccccc;
+AlignEscapedNewlines: Left
+
+# Align operands of binary and ternary expressions
+# int aaa = bbbbbbbbbbb +
+#           cccccc;
+AlignOperands: true
+
+# Don't align trailing comments
+# int a; // Comment a
+# int b = 2; // Comment b
+AlignTrailingComments: false
+
+# By default don't allow putting parameters onto the next line
+# myFunction(foo, bar, baz);
+AllowAllParametersOfDeclarationOnNextLine: false
+
+# Don't allow short braced statements to be on a single line
+# if (a)           not       if (a) return;
+#   return;
+AllowShortBlocksOnASingleLine: false
+AllowShortCaseLabelsOnASingleLine: false
+AllowShortFunctionsOnASingleLine: false
+AllowShortIfStatementsOnASingleLine: false
+AllowShortLoopsOnASingleLine: false
+
+# By default don't add a line break after the return type of top-level functions
+# int foo();
+AlwaysBreakAfterReturnType: None
+
+# Pack as many parameters or arguments onto the same line as possible
+# int myFunction(int aaaaaaaaaaaa, int bbbbbbbb,
+#                int cccc);
+BinPackArguments: true
+BinPackParameters: true
+
+# Attach braces to surrounding context except break before braces on function
+# definitions.
+# void foo()
+# {
+#    if (true) {
+#    } else {
+#    }
+# };
+BreakBeforeBraces: Linux
+
+# Break after operators
+# int valuve = aaaaaaaaaaaaa +
+#              bbbbbb -
+#              ccccccccccc;
+BreakBeforeBinaryOperators: None
+BreakBeforeTernaryOperators: false
+
+# Don't break string literals
+BreakStringLiterals: false
+
+# Use the same indentation level as for the switch statement.
+# Switch statement body is always indented one level more than case labels.
+IndentCaseLabels: false
+
+# Don't indent a function definition or declaration if it is wrapped after the
+# type
+IndentWrappedFunctionNames: false
+
+# Align pointer to the right
+# int *a;
+PointerAlignment: Right
+
+# Don't insert a space after a cast
+# x = (int32)y;    not    x = (int32) y;
+SpaceAfterCStyleCast: false
+
+# Insert spaces before and after assignment operators
+# int a = 5;    not    int a=5;
+# a += 42;             a+=42;
+SpaceBeforeAssignmentOperators: true
+
+# Put a space before opening parentheses only after control statement keywords.
+# void f() {
+#   if (true) {
+#     f();
+#   }
+# }
+SpaceBeforeParens: ControlStatements
+
+# Don't insert spaces inside empty '()'
+SpaceInEmptyParentheses: false
+
+# The number of spaces before trailing line comments (// - comments).
+# This does not affect trailing block comments (/* - comments).
+SpacesBeforeTrailingComments: 1
+
+# Don't insert spaces in casts
+# x = (int32) y;    not    x = ( int32 ) y;
+SpacesInCStyleCastParentheses: false
+
+# Don't insert spaces inside container literals
+# var arr = [1, 2, 3];    not    var arr = [ 1, 2, 3 ];
+SpacesInContainerLiterals: false
+
+# Don't insert spaces after '(' or before ')'
+# f(arg);    not    f( arg );
+SpacesInParentheses: false
+
+# Don't insert spaces after '[' or before ']'
+# int a[5];    not    int a[ 5 ];
+SpacesInSquareBrackets: false
+
+# Insert a space after '{' and before '}' in struct initializers
+Cpp11BracedListStyle: false
+
+# A list of macros that should be interpreted as foreach loops instead of as
+# function calls.
+ForEachMacros: ['for_each_string_list_item']
+
+# The maximum number of consecutive empty lines to keep.
+MaxEmptyLinesToKeep: 1
+
+# No empty line at the start of a block.
+KeepEmptyLinesAtTheStartOfBlocks: false
+
+# Penalties
+# This decides what order things should be done if a line is too long
+PenaltyBreakAssignment: 100
+PenaltyBreakBeforeFirstCallParameter: 100
+PenaltyBreakComment: 100
+PenaltyBreakFirstLessLess: 0
+PenaltyBreakString: 100
+PenaltyExcessCharacter: 5
+PenaltyReturnTypeOnItsOwnLine: 0
+
+# Don't sort #include's
+SortIncludes: false
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
diff --git a/.tsan-suppressions b/.tsan-suppressions
new file mode 100644 (file)
index 0000000..8c85014
--- /dev/null
@@ -0,0 +1,10 @@
+# Suppressions for ThreadSanitizer (tsan).
+#
+# This file is used by setting the environment variable TSAN_OPTIONS to, e.g.,
+# "suppressions=$(pwd)/.tsan-suppressions". Observe that relative paths such as
+# ".tsan-suppressions" might not work.
+
+# A static variable is written to racily, but we always write the same value, so
+# in practice it (hopefully!) doesn't matter.
+race:^want_color$
+race:^transfer_debug$
index 5517afcf5920b8fa023e2ba0babf6c4a1d92509b..bcfe78f59dd0cfb24897d986499aa7c8ada12834 100644 (file)
@@ -32,4 +32,63 @@ Fixes since v2.14.1
    daemon is torn down were flaky.  This was fixed by reacting to
    ECONNRESET and behaving as if we got an EOF.
 
+ * Some versions of GnuPG fail to kill gpg-agent it auto-spawned
+   and such a left-over agent can interfere with a test.  Work it
+   around by attempting to kill one before starting a new test.
+
+ * "git log --tag=no-such-tag" showed log starting from HEAD, which
+   has been fixed---it now shows nothing.
+
+ * The "tag.pager" configuration variable was useless for those who
+   actually create tag objects, as it interfered with the use of an
+   editor.  A new mechanism has been introduced for commands to enable
+   pager depending on what operation is being carried out to fix this,
+   and then "git tag -l" is made to run pager by default.
+
+ * "git push --recurse-submodules $there HEAD:$target" was not
+   propagated down to the submodules, but now it is.
+
+ * Commands like "git rebase" accepted the --rerere-autoupdate option
+   from the command line, but did not always use it.  This has been
+   fixed.
+
+ * "git clone --recurse-submodules --quiet" did not pass the quiet
+   option down to submodules.
+
+ * "git am -s" has been taught that some input may end with a trailer
+   block that is not Signed-off-by: and it should refrain from adding
+   an extra blank line before adding a new sign-off in such a case.
+
+ * "git svn" used with "--localtime" option did not compute the tz
+   offset for the timestamp in question and instead always used the
+   current time, which has been corrected.
+
+ * Memory leaks in a few error codepaths have been plugged.
+
+ * bash 4.4 or newer gave a warning on NUL byte in command
+   substitution done in "git stash"; this has been squelched.
+
+ * "git grep -L" and "git grep --quiet -L" reported different exit
+   codes; this has been corrected.
+
+ * When handshake with a subprocess filter notices that the process
+   asked for an unknown capability, Git did not report what program
+   the offending subprocess was running.  This has been corrected.
+
+ * "git apply" that is used as a better "patch -p1" failed to apply a
+   taken from a file with CRLF line endings to a file with CRLF line
+   endings.  The root cause was because it misused convert_to_git()
+   that tried to do "safe-crlf" processing by looking at the index
+   entry at the same path, which is a nonsense---in that mode, "apply"
+   is not working on the data in (or derived from) the index at all.
+   This has been fixed.
+
+ * Killing "git merge --edit" before the editor returns control left
+   the repository in a state with MERGE_MSG but without MERGE_HEAD,
+   which incorrectly tells the subsequent "git commit" that there was
+   a squash merge in progress.  This has been fixed.
+
+ * "git archive" did not work well with pathspecs and the
+   export-ignore attribute.
+
 Also contains various documentation updates and code clean-ups.
index 4b3fd229e927aa57c166855df43863d1aef13517..9b5c417b0e958ecceecbdfb94b88d07a4b57a194 100644 (file)
@@ -77,6 +77,9 @@ UI, Workflows & Features
  * "branch --set-upstream" that has been deprecated in Git 1.8 has
    finally been retired.
 
+ * The codepath to call external process filter for smudge/clean
+   operation learned to show the progress meter.
+
 
 Performance, Internal Implementation, Development Support etc.
 
@@ -133,6 +136,38 @@ Performance, Internal Implementation, Development Support etc.
    piece of memory while writing each index entry out.  This has been
    optimized.
 
+ * Platforms that ship with a separate sha1 with collision detection
+   library can link to it instead of using the copy we ship as part of
+   our source tree.
+
+ * Code around "notes" have been cleaned up.
+   (merge 3964281524 mh/notes-cleanup later to maint).
+
+ * The long-standing rule that an in-core lockfile instance, once it
+   is used, must not be freed, has been lifted and the lockfile and
+   tempfile APIs have been updated to reduce the chance of programming
+   errors.
+
+ * Our hashmap implementation in hashmap.[ch] is not thread-safe when
+   adding a new item needs to expand the hashtable by rehashing; add
+   an API to disable the automatic rehashing to work it around.
+
+ * Many of our programs consider that it is OK to release dynamic
+   storage that is used throughout the life of the program by simply
+   exiting, but this makes it harder to leak detection tools to avoid
+   reporting false positives.  Plug many existing leaks and introduce
+   a mechanism for developers to mark that the region of memory
+   pointed by a pointer is not lost/leaking to help these tools.
+
+ * As "git commit" to conclude a conflicted "git merge" honors the
+   commit-msg hook, "git merge" that records a merge commit that
+   cleanly auto-merges should, but it didn't.
+
+ * The codepath for "git merge-recursive" has been cleaned up.
+
+ * Many leaks of strbuf have been fixed.
+
+
 Also contains various documentation updates and code clean-ups.
 
 
@@ -161,7 +196,6 @@ Fixes since v2.14
  * Some versions of GnuPG fails to kill gpg-agent it auto-spawned
    and such a left-over agent can interfere with a test.  Work it
    around by attempting to kill one before starting a new test.
-   (merge 29ff1f8f74 st/lib-gpg-kill-stray-agent later to maint).
 
  * A recently added test for the "credential-cache" helper revealed
    that EOF detection done around the time the connection to the cache
@@ -170,49 +204,36 @@ Fixes since v2.14
 
  * "git log --tag=no-such-tag" showed log starting from HEAD, which
    has been fixed---it now shows nothing.
-   (merge 5d34d1ac06 jk/rev-list-empty-input later to maint).
 
  * The "tag.pager" configuration variable was useless for those who
    actually create tag objects, as it interfered with the use of an
    editor.  A new mechanism has been introduced for commands to enable
    pager depending on what operation is being carried out to fix this,
    and then "git tag -l" is made to run pager by default.
-   (merge 595d59e2b5 ma/pager-per-subcommand-action later to maint).
 
  * "git push --recurse-submodules $there HEAD:$target" was not
    propagated down to the submodules, but now it is.
-   (merge c7be7201a7 bw/push-options-recursively-to-submodules later to maint).
 
  * Commands like "git rebase" accepted the --rerere-autoupdate option
    from the command line, but did not always use it.  This has been
    fixed.
-   (merge f826fb799e pw/sequence-rerere-autoupdate later to maint).
 
  * "git clone --recurse-submodules --quiet" did not pass the quiet
    option down to submodules.
-   (merge 03c004c581 bw/clone-recursive-quiet later to maint).
 
  * Test portability fix for OBSD.
-   (merge bed67874e2 rs/obsd-getcwd-workaround later to maint).
-   (merge 4c7fda8fc1 rs/t4062-obsd later to maint).
 
  * Portability fix for OBSD.
-   (merge 29c2eda80b rs/in-obsd-basename-dirname-take-const later to maint).
 
  * "git am -s" has been taught that some input may end with a trailer
    block that is not Signed-off-by: and it should refrain from adding
    an extra blank line before adding a new sign-off in such a case.
-   (merge 735285b403 pw/am-signoff later to maint).
 
  * "git svn" used with "--localtime" option did not compute the tz
    offset for the timestamp in question and instead always used the
    current time, which has been corrected.
-   (merge 1adc4b9a58 ur/svn-local-zone later to maint).
 
  * Memory leak in an error codepath has been plugged.
-   (merge 83cd6f9017 rs/fsck-obj-leakfix later to maint).
-   (merge 896dca3ab7 rs/unpack-entry-leakfix later to maint).
-   (merge 149d8cbb2e rs/win32-syslog-leakfix later to maint).
 
  * "git stash -u" used the contents of the committed version of the
    ".gitignore" file to decide which paths are ignored, even when the
@@ -221,16 +242,13 @@ Fixes since v2.14
 
  * bash 4.4 or newer gave a warning on NUL byte in command
    substitution done in "git stash"; this has been squelched.
-   (merge 5fc92f8828 kd/stash-with-bash-4.4 later to maint).
 
  * "git grep -L" and "git grep --quiet -L" reported different exit
    codes; this has been corrected.
-   (merge e1f68c66d5 as/grep-quiet-no-match-exit-code-fix later to maint).
 
  * When handshake with a subprocess filter notices that the process
    asked for an unknown capability, Git did not report what program
    the offending subprocess was running.  This has been corrected.
-   (merge d3ba566342 cc/subprocess-handshake-missing-capabilities later to maint).
 
  * "git apply" that is used as a better "patch -p1" failed to apply a
    taken from a file with CRLF line endings to a file with CRLF line
@@ -239,38 +257,55 @@ Fixes since v2.14
    entry at the same path, which is a nonsense---in that mode, "apply"
    is not working on the data in (or derived from) the index at all.
    This has been fixed.
-   (merge c24f3abace tb/apply-with-crlf later to maint).
 
  * Killing "git merge --edit" before the editor returns control left
    the repository in a state with MERGE_MSG but without MERGE_HEAD,
    which incorrectly tells the subsequent "git commit" that there was
    a squash merge in progress.  This has been fixed.
-   (merge 9d89b35526 mg/killed-merge later to maint).
 
  * "git archive" did not work well with pathspecs and the
    export-ignore attribute.
-   (merge 5ff247ac0c rs/archive-excluded-directory later to maint).
+
+ * In addition to "cc: <a@dd.re.ss> # cruft", "cc: a@dd.re.ss # cruft"
+   was taught to "git send-email" as a valid way to tell it that it
+   needs to also send a carbon copy to <a@dd.re.ss> in the trailer
+   section.
+   (merge cc90750677 mm/send-email-cc-cruft later to maint).
+
+ * "git branch -M a b" while on a branch that is completely unrelated
+   to either branch a or branch b misbehaved when multiple worktree
+   was in use.  This has been fixed.
+   (merge 31824d180d nd/worktree-kill-parse-ref later to maint).
+
+ * "git gc" and friends when multiple worktrees are used off of a
+   single repository did not consider the index and per-worktree refs
+   of other worktrees as the root for reachability traversal, making
+   objects that are in use only in other worktrees to be subject to
+   garbage collection.
+
+ * A regression to "gitk --bisect" by a recent update has been fixed.
+   (merge 1d0538e486 mh/packed-ref-store-prep later to maint).
+
+ * "git -c submodule.recurse=yes pull" did not work as if the
+   "--recurse-submodules" option was given from the command line.
+   This has been corrected.
+
+ * Unlike "git commit-tree < file", "git commit-tree -F file" did not
+   pass the contents of the file verbatim and instead completed an
+   incomplete line at the end, if exists.  The latter has been updated
+   to match the behaviour of the former.
+   (merge c818e74332 rk/commit-tree-make-F-verbatim later to maint).
 
  * Other minor doc, test and build updates and code cleanups.
-   (merge dff2813391 ab/ref-filter-no-contains later to maint).
    (merge f094b89a4d ma/parse-maybe-bool later to maint).
-   (merge 974ce8078c mf/no-dashed-subcommands later to maint).
-   (merge f81935cc4d jc/perl-git-comment-typofix later to maint).
-   (merge 57ea241ef0 rs/t3700-clean-leftover later to maint).
    (merge 39b00fa4d4 jk/drop-sha1-entry-pos later to maint).
-   (merge 0b006014c8 jk/hashcmp-memcmp later to maint).
-   (merge 1e22a9917b rj/add-chmod-error-message later to maint).
-   (merge 881529c846 rs/apply-lose-prefix-length later to maint).
-   (merge 6355a76802 rs/find-pack-entry-bisection later to maint).
-   (merge de3ce210ed rs/merge-microcleanup later to maint).
-   (merge 7f0a02be2f ah/doc-empty-string-is-false later to maint).
-   (merge 70ec6bd63b rs/t1002-do-not-use-sum later to maint).
-   (merge 2456990dfd sb/sha1-file-cleanup later to maint).
-   (merge 2aac933c62 hv/t5526-andand-chain-fix later to maint).
-   (merge c8d0c4fe9b sb/submodule-parallel-update later to maint).
-   (merge 794b7e1674 mg/format-ref-doc-fix later to maint).
-   (merge 24da8a26a9 rs/commit-h-single-parent-cleanup later to maint).
-   (merge 4e36907fa3 jk/doc-the-this later to maint).
-   (merge 15d1d0951e bc/vcs-svn-cleanup later to maint).
-   (merge b8f43b120b jn/vcs-svn-cleanup later to maint).
-   (merge 4a4becfb23 jt/doc-pack-objects-fix later to maint).
+   (merge 6cdf8a7929 ma/ts-cleanups later to maint).
+   (merge 7560f547e6 ma/up-to-date later to maint).
+   (merge 0db3dc75f3 rs/apply-epoch later to maint).
+   (merge 74f1bd912b dw/diff-highlight-makefile-fix later to maint).
+   (merge f991761eb8 jk/config-lockfile-leak-fix later to maint).
+   (merge 150efef1e7 ma/pkt-line-leakfix later to maint).
+   (merge 5554451de6 mg/timestamp-t-fix later to maint).
+   (merge 276d0e35c0 ma/split-symref-update-fix later to maint).
+   (merge 3bc4b8f7c7 bb/doc-eol-dirty later to maint).
+   (merge c1bb33c99c jk/system-path-cleanup later to maint).
index 631cbd840a08eb29add1e900ac9272992669a75c..4ebc3d32719dfefa988d34b41871f0e9fb969471 100644 (file)
@@ -66,7 +66,7 @@ OPTIONS
        disables it is in effect), make sure the patch is
        applicable to what the current index file records.  If
        the file to be patched in the working tree is not
-       up-to-date, it is flagged as an error.  This flag also
+       up to date, it is flagged as an error.  This flag also
        causes the index file to be updated.
 
 --cached::
@@ -259,7 +259,7 @@ treats these changes as follows.
 If `--index` is specified (explicitly or implicitly), then the submodule
 commits must match the index exactly for the patch to apply.  If any
 of the submodules are checked-out, then these check-outs are completely
-ignored, i.e., they are not required to be up-to-date or clean and they
+ignored, i.e., they are not required to be up to date or clean and they
 are not updated.
 
 If `--index` is not specified, then the submodule commits in the patch
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 a336ae5f6fd5587aa8f778d2378783d0fe2e6dc4..ba90066f108cf525eb7781c1607d65358c617b0e 100644 (file)
@@ -223,7 +223,7 @@ access method and requested operation.
 That means that even if you offer only read access (e.g. by using
 the pserver method), 'git-cvsserver' should have write access to
 the database to work reliably (otherwise you need to make sure
-that the database is up-to-date any time 'git-cvsserver' is executed).
+that the database is up to date any time 'git-cvsserver' is executed).
 
 By default it uses SQLite databases in the Git directory, named
 `gitcvs.<module_name>.sqlite`. Note that the SQLite backend creates
index 26f19d3b072aa358043fbf79f3057a11aee2fdb2..c924c945ba8b79e578ddbb7598aa29fabcdf5887 100644 (file)
@@ -87,19 +87,23 @@ OPTIONS
 
 --match <pattern>::
        Only consider tags matching the given `glob(7)` pattern,
-       excluding the "refs/tags/" prefix.  This can be used to avoid
-       leaking private tags from the repository. If given multiple times, a
-       list of patterns will be accumulated, and tags matching any of the
-       patterns will be considered. Use `--no-match` to clear and reset the
-       list of patterns.
+       excluding the "refs/tags/" prefix. If used with `--all`, it also
+       considers local branches and remote-tracking references matching the
+       pattern, excluding respectively "refs/heads/" and "refs/remotes/"
+       prefix; references of other types are never considered. If given
+       multiple times, a list of patterns will be accumulated, and tags
+       matching any of the patterns will be considered.  Use `--no-match` to
+       clear and reset the list of patterns.
 
 --exclude <pattern>::
        Do not consider tags matching the given `glob(7)` pattern, excluding
-       the "refs/tags/" prefix. This can be used to narrow the tag space and
-       find only tags matching some meaningful criteria. If given multiple
-       times, a list of patterns will be accumulated and tags matching any
-       of the patterns will be excluded. When combined with --match a tag will
-       be considered when it matches at least one --match pattern and does not
+       the "refs/tags/" prefix. If used with `--all`, it also does not consider
+       local branches and remote-tracking references matching the pattern,
+       excluding respectively "refs/heads/" and "refs/remotes/" prefix;
+       references of other types are never considered. If given multiple times,
+       a list of patterns will be accumulated and tags matching any of the
+       patterns will be excluded. When combined with --match a tag will be
+       considered when it matches at least one --match pattern and does not
        match any of the --exclude patterns. Use `--no-exclude` to clear and
        reset the list of patterns.
 
index a1715069529eba9d143d527c058e962381efd743..b380677718ae129b4643814d7c1a16351ce4d779 100644 (file)
@@ -85,7 +85,7 @@ a 'git write-tree' + 'git diff-tree'. Thus that's the default mode.
 The non-cached version asks the question:
 
   show me the differences between HEAD and the currently checked out
-  tree - index contents _and_ files that aren't up-to-date
+  tree - index contents _and_ files that aren't up to date
 
 which is obviously a very useful question too, since that tells you what
 you *could* commit. Again, the output matches the 'git diff-tree -r'
@@ -100,8 +100,8 @@ have not actually done a 'git update-index' on it yet - there is no
   torvalds@ppc970:~/v2.6/linux> git diff-index --abbrev HEAD
   :100644 100664 7476bb... 000000...      kernel/sched.c
 
-i.e., it shows that the tree has changed, and that `kernel/sched.c` has is
-not up-to-date and may contain new stuff. The all-zero sha1 means that to
+i.e., it shows that the tree has changed, and that `kernel/sched.c` is
+not up to date and may contain new stuff. The all-zero sha1 means that to
 get the real diff, you need to look at the object in the working directory
 directly rather than do an object-to-object diff.
 
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 615e6bacde7355e3125268bc969d0fee2a06392e..4df6431c341a466f2cb9fef094607751b58c008d 100644 (file)
@@ -141,7 +141,7 @@ exception is when the changed index entries are in the state that
 would result from the merge already.)
 
 If all named commits are already ancestors of `HEAD`, 'git merge'
-will exit early with the message "Already up-to-date."
+will exit early with the message "Already up to date."
 
 FAST-FORWARD MERGE
 ------------------
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 4f6bed61a92b74e4540b71e0e0cac7cba60302ca..6805a74aec20cacd7b9320136b8648ad5b40ef5b 100644 (file)
@@ -334,7 +334,7 @@ which makes little sense.
 
 -f::
 --force-rebase::
-       Force a rebase even if the current branch is up-to-date and
+       Force a rebase even if the current branch is up to date and
        the command without `--force` would return without doing anything.
 +
 You may find this (or --no-ff with an interactive rebase) helpful after
index 9ee083c415a27dc499b75031d49df9e3f3856ec0..031f31fa471154f474b35448564aff6c8c2b3f51 100644 (file)
@@ -205,7 +205,7 @@ development on the topic branch:
 ------------
 
 you could run `git rebase master topic`, to bring yourself
-up-to-date before your topic is ready to be sent upstream.
+up to date before your topic is ready to be sent upstream.
 This would result in falling back to a three-way merge, and it
 would conflict the same way as the test merge you resolved earlier.
 'git rerere' will be run by 'git rebase' to help you resolve this
index b1293f24bb46f71238f3dee4700340a79b90fca8..0917b8207b9d6e6c88dc611cf40a81ab17481a2e 100644 (file)
@@ -235,6 +235,9 @@ print a message to stderr and exit with nonzero status.
 --is-bare-repository::
        When the repository is bare print "true", otherwise "false".
 
+--is-shallow-repository::
+       When the repository is shallow print "true", otherwise "false".
+
 --resolve-git-dir <path>::
        Check if <path> is a valid repository or a gitfile that
        points at a valid repository, and print the location of the
index 683e59133003536c776c476304e369bdd80e27a2..b5c46223c44b1b17673ae4782f74528b3d151bed 100644 (file)
@@ -146,7 +146,7 @@ the submodule's history. If it exists the submodule.<name> section
 in the linkgit:gitmodules[5] file will also be removed and that file
 will be staged (unless --cached or -n are used).
 
-A submodule is considered up-to-date when the HEAD is the same as
+A submodule is considered up to date when the HEAD is the same as
 recorded in the index, no tracked files are modified and no untracked
 files that aren't ignored are present in the submodules work tree.
 Ignored files are deemed expendable and won't stop a submodule's work
index aa2aeabb60209fabd772907a91c2812b2a305d7d..636e09048e8846b813166737ef58eef96eb3a3c4 100644 (file)
@@ -424,7 +424,7 @@ Any other arguments are passed directly to 'git log'
 'set-tree'::
        You should consider using 'dcommit' instead of this command.
        Commit specified commit or tree objects to SVN.  This relies on
-       your imported fetch data being up-to-date.  This makes
+       your imported fetch data being up to date.  This makes
        absolutely no attempts to do patching when committing to SVN, it
        simply overwrites files with those specified in the tree or
        commit.  All merging is assumed to have taken place
index 1579abf3c3b45f4e63f8f17746dde31a06a3a564..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.
 
@@ -214,7 +214,7 @@ will remove the intended effect of the option.
 Using --refresh
 ---------------
 `--refresh` does not calculate a new sha1 file or bring the index
-up-to-date for mode/content changes. But what it *does* do is to
+up to date for mode/content changes. But what it *does* do is to
 "re-match" the stat information of a file with the index, so that you
 can refresh the index for a file that hasn't been changed but where
 the stat entry is out of date.
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 7577f27ec20c90eabea2652a438ca09961559f6a..e29a9effccbcf439fec8a8fe9bfd1927f58439d7 100644 (file)
@@ -631,7 +631,7 @@ So after you do a `cp -a` to create a new copy, you'll want to do
 $ git update-index --refresh
 ----------------
 +
-in the new repository to make sure that the index file is up-to-date.
+in the new repository to make sure that the index file is up to date.
 
 Note that the second point is true even across machines. You can
 duplicate a remote Git repository with *any* regular copy mechanism, be it
@@ -701,7 +701,7 @@ $ git checkout-index -u -a
 ----------------
 
 where the `-u` flag means that you want the checkout to keep the index
-up-to-date (so that you don't have to refresh it afterward), and the
+up to date (so that you don't have to refresh it afterward), and the
 `-a` flag means "check out all files" (if you have a stale copy or an
 older version of a checked out tree you may also need to add the `-f`
 flag first, to tell 'git checkout-index' to *force* overwriting of any old
@@ -1283,7 +1283,7 @@ run a single command, 'git-receive-pack'.
 
 First, you need to create an empty repository on the remote
 machine that will house your public repository. This empty
-repository will be populated and be kept up-to-date by pushing
+repository will be populated and be kept up to date by pushing
 into it later. Obviously, this repository creation needs to be
 done only once.
 
@@ -1450,7 +1450,7 @@ transport protocols (HTTP), you need to keep this repository
 would contain a call to 'git update-server-info'
 but you need to manually enable the hook with
 `mv post-update.sample post-update`.  This makes sure
-'git update-server-info' keeps the necessary files up-to-date.
+'git update-server-info' keeps the necessary files up to date.
 
 3. Push into the public repository from your primary
    repository.
index f4e75b9c90e20bc7d7595f127af4bbdb83036bcb..5d3f45560ea9c29ec382c8772522f06ab5f81f73 100644 (file)
@@ -368,7 +368,7 @@ them.
 
 When enabled, the default 'post-update' hook runs
 'git update-server-info' to keep the information used by dumb
-transports (e.g., HTTP) up-to-date.  If you are publishing
+transports (e.g., HTTP) up to date.  If you are publishing
 a Git repository that is accessible via HTTP, you should
 probably enable this hook.
 
index f51ed4e37cd3fad80bf855e6217906f094f894fb..adf9554ad2f84cb69a6b0a940e4d3e0e336df9ff 100644 (file)
@@ -71,7 +71,7 @@ objects/info/packs::
        This file is to help dumb transports discover what packs
        are available in this object store.  Whenever a pack is
        added or removed, `git update-server-info` should be run
-       to keep this file up-to-date if the repository is
+       to keep this file up to date if the repository is
        published for dumb transports.  'git repack' does this
        by default.
 
index 794b83393ea21a8d569c34791ee4af1dfb99a57a..242de31cb6ccc6600b94a68a9efd215df2cb71f7 100644 (file)
@@ -109,7 +109,7 @@ summary of the situation with 'git status':
 $ git status
 On branch master
 Changes to be committed:
-Your branch is up-to-date with 'origin/master'.
+Your branch is up to date with 'origin/master'.
   (use "git reset HEAD <file>..." to unstage)
 
        modified:   file1
index 5b4a62e93624bc8289c835631aab5d5f13dff708..4e323043017deee83d547e52e7797dd61a02cd5d 100644 (file)
@@ -39,7 +39,7 @@ set to `no` at the beginning of them.
 
 --ff-only::
        Refuse to merge and exit with a non-zero status unless the
-       current `HEAD` is already up-to-date or the merge can be
+       current `HEAD` is already up to date or the merge can be
        resolved as a fast-forward.
 
 --log[=<n>]::
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 a34917153fd3dc10fefe71a1ba05e5dacb9af7f9..ed1eae8b83a651d34f9ce2488f58d02a763fbfd4 100644 (file)
@@ -199,7 +199,7 @@ After reference and capabilities discovery, the client can decide to
 terminate the connection by sending a flush-pkt, telling the server it can
 now gracefully terminate, and disconnect, when it does not need any pack
 data. This can happen with the ls-remote command, and also can happen when
-the client already is up-to-date.
+the client already is up to date.
 
 Otherwise, it enters the negotiation phase, where the client and
 server determine what the minimal packfile necessary for transport is,
index c79d4a7c476da2c57c117529150de77646ed116e..1f1c33d0da30dbdf90c37bd808eb52059b598c84 100644 (file)
@@ -32,7 +32,7 @@ or the result.
 If multiple cases apply, the one used is listed first.
 
 A result which changes the index is an error if the index is not empty
-and not up-to-date.
+and not up to date.
 
 Entries marked '+' have stat information. Spaces marked '*' don't
 affect the result.
@@ -65,7 +65,7 @@ empty, no entry is left for that stage). Otherwise, the given entry is
 left in stage 0, and there are no other entries.
 
 A result of "no merge" is an error if the index is not empty and not
-up-to-date.
+up to date.
 
 *empty* means that the tree must not have a directory-file conflict
  with the entry.
index bc29298678a6d2d9f6b2927f97c40ba8978bd93b..b4d88af133e8eafd92f86443bd74de063bad4835 100644 (file)
@@ -2044,10 +2044,12 @@ If a push would not result in a <<fast-forwards,fast-forward>> of the
 remote branch, then it will fail with an error like:
 
 -------------------------------------------------
-error: remote 'refs/heads/master' is not an ancestor of
- local  'refs/heads/master'.
- Maybe you are not up-to-date and need to pull first?
-error: failed to push to 'ssh://yourserver.com/~you/proj.git'
+ ! [rejected]        master -> master (non-fast-forward)
+error: failed to push some refs to '...'
+hint: Updates were rejected because the tip of your current branch is behind
+hint: its remote counterpart. Integrate the remote changes (e.g.
+hint: 'git pull ...') before pushing again.
+hint: See the 'Note about fast-forwards' in 'git push --help' for details.
 -------------------------------------------------
 
 This can happen, for example, if you:
@@ -2193,7 +2195,7 @@ $ cd work
 Linus's tree will be stored in the remote-tracking branch named origin/master,
 and can be updated using linkgit:git-fetch[1]; you can track other
 public trees using linkgit:git-remote[1] to set up a "remote" and
-linkgit:git-fetch[1] to keep them up-to-date; see
+linkgit:git-fetch[1] to keep them up to date; see
 <<repositories-and-branches>>.
 
 Now create the branches in which you are going to work; these start out
index f2bb7f2f63e458af6261b4a151d87ef9e973222e..ed4ca438bd9c6ddab51f78cb4b620f02a7e12eed 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
@@ -2442,6 +2457,10 @@ $(SP_OBJ): %.sp: %.c GIT-CFLAGS FORCE
 .PHONY: sparse $(SP_OBJ)
 sparse: $(SP_OBJ)
 
+.PHONY: style
+style:
+       git clang-format --style file --diff --extensions c,h
+
 check: common-cmds.h
        @if sparse; \
        then \
diff --git a/apply.c b/apply.c
index 86666217d4df0c8162c8d93d859ab5112c10f4b9..71cbbd141c73bd72778d71e1e2400413c11f492f 100644 (file)
--- a/apply.c
+++ b/apply.c
@@ -812,16 +812,13 @@ static int has_epoch_timestamp(const char *nameline)
         * 1970-01-01, and the seconds part must be "00".
         */
        const char stamp_regexp[] =
-               "^(1969-12-31|1970-01-01)"
-               " "
-               "[0-2][0-9]:[0-5][0-9]:00(\\.0+)?"
+               "^[0-2][0-9]:([0-5][0-9]):00(\\.0+)?"
                " "
                "([-+][0-2][0-9]:?[0-5][0-9])\n";
        const char *timestamp = NULL, *cp, *colon;
        static regex_t *stamp;
        regmatch_t m[10];
-       int zoneoffset;
-       int hourminute;
+       int zoneoffset, epoch_hour, hour, minute;
        int status;
 
        for (cp = nameline; *cp != '\n'; cp++) {
@@ -830,6 +827,18 @@ static int has_epoch_timestamp(const char *nameline)
        }
        if (!timestamp)
                return 0;
+
+       /*
+        * YYYY-MM-DD hh:mm:ss must be from either 1969-12-31
+        * (west of GMT) or 1970-01-01 (east of GMT)
+        */
+       if (skip_prefix(timestamp, "1969-12-31 ", &timestamp))
+               epoch_hour = 24;
+       else if (skip_prefix(timestamp, "1970-01-01 ", &timestamp))
+               epoch_hour = 0;
+       else
+               return 0;
+
        if (!stamp) {
                stamp = xmalloc(sizeof(*stamp));
                if (regcomp(stamp, stamp_regexp, REG_EXTENDED)) {
@@ -847,6 +856,9 @@ static int has_epoch_timestamp(const char *nameline)
                return 0;
        }
 
+       hour = strtol(timestamp, NULL, 10);
+       minute = strtol(timestamp + m[1].rm_so, NULL, 10);
+
        zoneoffset = strtol(timestamp + m[3].rm_so + 1, (char **) &colon, 10);
        if (*colon == ':')
                zoneoffset = zoneoffset * 60 + strtol(colon + 1, NULL, 10);
@@ -855,20 +867,7 @@ static int has_epoch_timestamp(const char *nameline)
        if (timestamp[m[3].rm_so] == '-')
                zoneoffset = -zoneoffset;
 
-       /*
-        * YYYY-MM-DD hh:mm:ss must be from either 1969-12-31
-        * (west of GMT) or 1970-01-01 (east of GMT)
-        */
-       if ((zoneoffset < 0 && memcmp(timestamp, "1969-12-31", 10)) ||
-           (0 <= zoneoffset && memcmp(timestamp, "1970-01-01", 10)))
-               return 0;
-
-       hourminute = (strtol(timestamp + 11, NULL, 10) * 60 +
-                     strtol(timestamp + 14, NULL, 10) -
-                     zoneoffset);
-
-       return ((zoneoffset < 0 && hourminute == 1440) ||
-               (0 <= zoneoffset && !hourminute));
+       return hour * 60 + minute - zoneoffset == epoch_hour * 60;
 }
 
 /*
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 ce1f8bb58ebd4709e2b13e85232d7138d5cde0d6..703ded69ca2697f34bc1e225ccecebf33e5ad0b1 100644 (file)
--- a/branch.c
+++ b/branch.c
@@ -357,8 +357,9 @@ int replace_each_worktree_head_symref(const char *oldref, const char *newref,
 
                if (worktrees[i]->is_detached)
                        continue;
-               if (worktrees[i]->head_ref &&
-                   strcmp(oldref, worktrees[i]->head_ref))
+               if (!worktrees[i]->head_ref)
+                       continue;
+               if (strcmp(oldref, worktrees[i]->head_ref))
                        continue;
 
                refs = get_worktree_ref_store(worktrees[i]);
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..29075dbd0f884513420b646cdb06d7c0b3d33643 100644 (file)
@@ -129,13 +129,24 @@ static void add_to_known_names(const char *path,
 
 static int get_name(const char *path, const struct object_id *oid, int flag, void *cb_data)
 {
-       int is_tag = starts_with(path, "refs/tags/");
+       int is_tag = 0;
        struct object_id peeled;
        int is_annotated, prio;
-
-       /* Reject anything outside refs/tags/ unless --all */
-       if (!all && !is_tag)
+       const char *path_to_match = NULL;
+
+       if (skip_prefix(path, "refs/tags/", &path_to_match)) {
+               is_tag = 1;
+       } else if (all) {
+               if ((exclude_patterns.nr || patterns.nr) &&
+                   !skip_prefix(path, "refs/heads/", &path_to_match) &&
+                   !skip_prefix(path, "refs/remotes/", &path_to_match)) {
+                       /* Only accept reference of known type if there are match/exclude patterns */
+                       return 0;
+               }
+       } else {
+               /* Reject anything outside refs/tags/ unless --all */
                return 0;
+       }
 
        /*
         * If we're given exclude patterns, first exclude any tag which match
@@ -144,11 +155,8 @@ static int get_name(const char *path, const struct object_id *oid, int flag, voi
        if (exclude_patterns.nr) {
                struct string_list_item *item;
 
-               if (!is_tag)
-                       return 0;
-
                for_each_string_list_item(item, &exclude_patterns) {
-                       if (!wildmatch(item->string, path + 10, 0))
+                       if (!wildmatch(item->string, path_to_match, 0))
                                return 0;
                }
        }
@@ -158,18 +166,18 @@ static int get_name(const char *path, const struct object_id *oid, int flag, voi
         * pattern.
         */
        if (patterns.nr) {
+               int found = 0;
                struct string_list_item *item;
 
-               if (!is_tag)
-                       return 0;
-
                for_each_string_list_item(item, &patterns) {
-                       if (!wildmatch(item->string, path + 10, 0))
+                       if (!wildmatch(item->string, path_to_match, 0)) {
+                               found = 1;
                                break;
+                       }
+               }
 
-                       /* If we get here, no pattern matched. */
+               if (!found)
                        return 0;
-               }
        }
 
        /* Is it annotated? */
@@ -508,7 +516,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 d412c0a8f354659acade8ea6271f5c60826a0b3f..da42ee5e604b72a8b3ac1edf38d8f75bd6805419 100644 (file)
@@ -344,6 +344,7 @@ static void show_filemodify(struct diff_queue_struct *q,
                            struct diff_options *options, void *data)
 {
        int i;
+       struct string_list *changed = data;
 
        /*
         * Handle files below a directory first, in case they are all deleted
@@ -359,20 +360,31 @@ static void show_filemodify(struct diff_queue_struct *q,
                case DIFF_STATUS_DELETED:
                        printf("D ");
                        print_path(spec->path);
+                       string_list_insert(changed, spec->path);
                        putchar('\n');
                        break;
 
                case DIFF_STATUS_COPIED:
                case DIFF_STATUS_RENAMED:
-                       printf("%c ", q->queue[i]->status);
-                       print_path(ospec->path);
-                       putchar(' ');
-                       print_path(spec->path);
-                       putchar('\n');
-
-                       if (!oidcmp(&ospec->oid, &spec->oid) &&
-                           ospec->mode == spec->mode)
-                               break;
+                       /*
+                        * If a change in the file corresponding to ospec->path
+                        * has been observed, we cannot trust its contents
+                        * because the diff is calculated based on the prior
+                        * contents, not the current contents.  So, declare a
+                        * copy or rename only if there was no change observed.
+                        */
+                       if (!string_list_has_string(changed, ospec->path)) {
+                               printf("%c ", q->queue[i]->status);
+                               print_path(ospec->path);
+                               putchar(' ');
+                               print_path(spec->path);
+                               string_list_insert(changed, spec->path);
+                               putchar('\n');
+
+                               if (!oidcmp(&ospec->oid, &spec->oid) &&
+                                   ospec->mode == spec->mode)
+                                       break;
+                       }
                        /* fallthrough */
 
                case DIFF_STATUS_TYPE_CHANGED:
@@ -393,6 +405,7 @@ static void show_filemodify(struct diff_queue_struct *q,
                                       get_object_mark(object));
                        }
                        print_path(spec->path);
+                       string_list_insert(changed, spec->path);
                        putchar('\n');
                        break;
 
@@ -528,7 +541,8 @@ static void anonymize_ident_line(const char **beg, const char **end)
        *end = out->buf + out->len;
 }
 
-static void handle_commit(struct commit *commit, struct rev_info *rev)
+static void handle_commit(struct commit *commit, struct rev_info *rev,
+                         struct string_list *paths_of_changed_objects)
 {
        int saved_output_format = rev->diffopt.output_format;
        const char *commit_buffer;
@@ -615,6 +629,7 @@ static void handle_commit(struct commit *commit, struct rev_info *rev)
        if (full_tree)
                printf("deleteall\n");
        log_tree_diff_flush(rev);
+       string_list_clear(paths_of_changed_objects, 0);
        rev->diffopt.output_format = saved_output_format;
 
        printf("\n");
@@ -630,14 +645,15 @@ static void *anonymize_tag(const void *old, size_t *len)
        return strbuf_detach(&out, len);
 }
 
-static void handle_tail(struct object_array *commits, struct rev_info *revs)
+static void handle_tail(struct object_array *commits, struct rev_info *revs,
+                       struct string_list *paths_of_changed_objects)
 {
        struct commit *commit;
        while (commits->nr) {
                commit = (struct commit *)commits->objects[commits->nr - 1].item;
                if (has_unshown_parent(commit))
                        return;
-               handle_commit(commit, revs);
+               handle_commit(commit, revs, paths_of_changed_objects);
                commits->nr--;
        }
 }
@@ -977,6 +993,7 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
        char *export_filename = NULL, *import_filename = NULL;
        uint32_t lastimportid;
        struct string_list refspecs_list = STRING_LIST_INIT_NODUP;
+       struct string_list paths_of_changed_objects = STRING_LIST_INIT_DUP;
        struct option options[] = {
                OPT_INTEGER(0, "progress", &progress,
                            N_("show progress after <n> objects")),
@@ -1049,14 +1066,15 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
        if (prepare_revision_walk(&revs))
                die("revision walk setup failed");
        revs.diffopt.format_callback = show_filemodify;
+       revs.diffopt.format_callback_data = &paths_of_changed_objects;
        DIFF_OPT_SET(&revs.diffopt, RECURSIVE);
        while ((commit = get_revision(&revs))) {
                if (has_unshown_parent(commit)) {
                        add_object_array(&commit->object, NULL, &commits);
                }
                else {
-                       handle_commit(commit, &revs);
-                       handle_tail(&commits, &revs);
+                       handle_commit(commit, &revs, &paths_of_changed_objects);
+                       handle_tail(&commits, &revs, &paths_of_changed_objects);
                }
        }
 
index 3c78fcb9b1a60b92a1a848ebc8aba01ec06feca9..3c5eae0edf12e461e84c71c0e2988fcfa58d3d44 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;
 
@@ -258,7 +258,7 @@ static const char *lock_repo_for_gc(int force, pid_t* ret_pid)
                int should_exit;
 
                if (!scan_fmt)
-                       scan_fmt = xstrfmt("%s %%%dc", "%"SCNuMAX, HOST_NAME_MAX);
+                       scan_fmt = xstrfmt("%s %%%ds", "%"SCNuMAX, HOST_NAME_MAX);
                fp = fopen(pidfile_path, "r");
                memset(locking_host, 0, sizeof(locking_host));
                should_exit =
@@ -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 780435d7a1ffe904151437ba1df31084f4367d83..ab5ffe85e8f5d7a359004f2bbb52a5778d0b91b4 100644 (file)
@@ -255,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;
@@ -268,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)
@@ -942,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)
@@ -1367,7 +1372,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                 * If head can reach all the merge then we are up to date.
                 * but first the most common case of merging one remote.
                 */
-               finish_up_to_date(_("Already up-to-date."));
+               finish_up_to_date(_("Already up to date."));
                goto done;
        } else if (fast_forward != FF_NO && !remoteheads->next &&
                        !common->next &&
@@ -1450,7 +1455,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                        }
                }
                if (up_to_date) {
-                       finish_up_to_date(_("Already up-to-date. Yeeah!"));
+                       finish_up_to_date(_("Already up to date. Yeeah!"));
                        goto done;
                }
        }
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 82ad6e0c813375b480129c22035afc4d6674992a..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;
                }
@@ -2171,7 +2171,10 @@ static void *threaded_find_deltas(void *arg)
 {
        struct thread_params *me = arg;
 
+       progress_lock();
        while (me->remaining) {
+               progress_unlock();
+
                find_deltas(me->list, &me->remaining,
                            me->window, me->depth, me->processed);
 
@@ -2193,7 +2196,10 @@ static void *threaded_find_deltas(void *arg)
                        pthread_cond_wait(&me->cond, &me->mutex);
                me->data_ready = 0;
                pthread_mutex_unlock(&me->mutex);
+
+               progress_lock();
        }
+       progress_unlock();
        /* leave ->working 1 so that this doesn't get more work assigned */
        return NULL;
 }
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..a8d7e6f7aecc2e4179547bde3fd6d07231764b9f 100644 (file)
@@ -387,6 +387,14 @@ static const char *skipspaces(const char *s)
        return s;
 }
 
+static char *findspace(const char *s)
+{
+       for (; *s; s++)
+               if (isspace(*s))
+                       return (char*)s;
+       return NULL;
+}
+
 static int cmd_parseopt(int argc, const char **argv, const char *prefix)
 {
        static int keep_dashdash = 0, stop_at_non_option = 0;
@@ -434,7 +442,7 @@ static int cmd_parseopt(int argc, const char **argv, const char *prefix)
        /* parse: (<short>|<short>,<long>|<long>)[*=?!]*<arghint>? SP+ <help> */
        while (strbuf_getline(&sb, stdin) != EOF) {
                const char *s;
-               const char *help;
+               char *help;
                struct option *o;
 
                if (!sb.len)
@@ -444,15 +452,17 @@ static int cmd_parseopt(int argc, const char **argv, const char *prefix)
                memset(opts + onb, 0, sizeof(opts[onb]));
 
                o = &opts[onb++];
-               help = strchr(sb.buf, ' ');
-               if (!help || *sb.buf == ' ') {
+               help = findspace(sb.buf);
+               if (!help || sb.buf == help) {
                        o->type = OPTION_GROUP;
                        o->help = xstrdup(skipspaces(sb.buf));
                        continue;
                }
 
+               *help = '\0';
+
                o->type = OPTION_CALLBACK;
-               o->help = xstrdup(skipspaces(help));
+               o->help = xstrdup(skipspaces(help+1));
                o->value = &parsed;
                o->flags = PARSE_OPT_NOARG;
                o->callback = &parseopt_dump;
@@ -757,8 +767,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)) {
@@ -868,6 +878,11 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                                                : "false");
                                continue;
                        }
+                       if (!strcmp(arg, "--is-shallow-repository")) {
+                               printf("%s\n", is_repository_shallow() ? "true"
+                                               : "false");
+                               continue;
+                       }
                        if (!strcmp(arg, "--shared-index-path")) {
                                if (read_cache() < 0)
                                        die(_("Could not read the index"));
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..b3ed0a0
--- /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) &&
+               test "$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
diff --git a/color.c b/color.c
index 7aa8b076f045e5c0b74826153030e2923c0fc56e..9ccd954d6ba5cea1dd275cac23bb19966b9c6386 100644 (file)
--- a/color.c
+++ b/color.c
@@ -338,6 +338,13 @@ static int check_auto_color(void)
 
 int want_color(int var)
 {
+       /*
+        * NEEDSWORK: This function is sometimes used from multiple threads, and
+        * we end up using want_auto racily. That "should not matter" since
+        * we always write the same value, but it's still wrong. This function
+        * is listed in .tsan-suppressions for the time being.
+        */
+
        static int want_auto = -1;
 
        if (var < 0)
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 ee99f1a4ee89a9e9f358b6cc89fc84cab554b439..932e78dbfee97750ae38afcf699e95f118f956dd 100755 (executable)
@@ -399,7 +399,7 @@ case "$allow_fast_forward,$#,$common,$no_commit" in
 ?,1,"$1",*)
        # If head can reach all the merge then we are up to date.
        # but first the most common case of merging one remote.
-       finish_up_to_date "Already up-to-date."
+       finish_up_to_date "Already up to date."
        exit 0
        ;;
 t,1,"$head",*)
@@ -459,7 +459,7 @@ t,1,"$head",*)
        done
        if test "$up_to_date" = t
        then
-               finish_up_to_date "Already up-to-date. Yeeah!"
+               finish_up_to_date "Already up to date. Yeeah!"
                exit 0
        fi
        ;;
index 70fdc27b724f28793c6a4bff713957a22689b78b..3099dc851ac26a9280e7e6d1a73638c05d01cc17 100755 (executable)
@@ -41,7 +41,7 @@ fi
 
 case "$common" in
 "$merge")
-       echo "Already up-to-date. Yeeah!"
+       echo "Already up to date. Yeeah!"
        dropheads
        exit 0
        ;;
index 3c87ebaf57baede9810c3b9b489eed9751189706..d05c613c9718697f64bc2334dc9ae85bf473f13e 100755 (executable)
@@ -253,7 +253,7 @@ test_expect_success 'merge the added subproj again, should do nothing' '
                # this shouldn not actually do anything, since FETCH_HEAD
                # is already a parent
                result=$(git merge -s ours -m "merge -s -ours" FETCH_HEAD) &&
-               check_equal "${result}" "Already up-to-date."
+               check_equal "${result}" "Already up to date."
        )
 '
 
index c5f0b210370c0ee83ef59298fa2260d627332399..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);
 
@@ -1040,7 +1042,6 @@ static void convert_attrs(struct conv_attrs *ca, const char *path)
                ca->crlf_action = git_path_check_crlf(ccheck + 4);
                if (ca->crlf_action == CRLF_UNDEFINED)
                        ca->crlf_action = git_path_check_crlf(ccheck + 0);
-               ca->attr_action = ca->crlf_action;
                ca->ident = git_path_check_ident(ccheck + 1);
                ca->drv = git_path_check_convert(ccheck + 2);
                if (ca->crlf_action != CRLF_BINARY) {
@@ -1054,12 +1055,14 @@ static void convert_attrs(struct conv_attrs *ca, const char *path)
                        else if (eol_attr == EOL_CRLF)
                                ca->crlf_action = CRLF_TEXT_CRLF;
                }
-               ca->attr_action = ca->crlf_action;
        } else {
                ca->drv = NULL;
                ca->crlf_action = CRLF_UNDEFINED;
                ca->ident = 0;
        }
+
+       /* Save attr and make a decision for action */
+       ca->attr_action = ca->crlf_action;
        if (ca->crlf_action == CRLF_TEXT)
                ca->crlf_action = text_eol_is_crlf() ? CRLF_TEXT_CRLF : CRLF_TEXT_INPUT;
        if (ca->crlf_action == CRLF_UNDEFINED && auto_crlf == AUTO_CRLF_FALSE)
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/dir.c b/dir.c
index 1c55dc3e366f8c9e8bcbc62305d7085533059512..1d17b800cf374d179d7cef3c6bbb180a5a1b4aba 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -49,7 +49,7 @@ struct cached_dir {
 static enum path_treatment read_directory_recursive(struct dir_struct *dir,
        struct index_state *istate, const char *path, int len,
        struct untracked_cache_dir *untracked,
-       int check_only, const struct pathspec *pathspec);
+       int check_only, int stop_at_first_file, const struct pathspec *pathspec);
 static int get_dtype(struct dirent *de, struct index_state *istate,
                     const char *path, int len);
 
@@ -1404,8 +1404,13 @@ static enum path_treatment treat_directory(struct dir_struct *dir,
 
        untracked = lookup_untracked(dir->untracked, untracked,
                                     dirname + baselen, len - baselen);
+
+       /*
+        * If this is an excluded directory, then we only need to check if
+        * the directory contains any files.
+        */
        return read_directory_recursive(dir, istate, dirname, len,
-                                       untracked, 1, pathspec);
+                                       untracked, 1, exclude, pathspec);
 }
 
 /*
@@ -1633,7 +1638,7 @@ static enum path_treatment treat_path_fast(struct dir_struct *dir,
                 * with check_only set.
                 */
                return read_directory_recursive(dir, istate, path->buf, path->len,
-                                               cdir->ucd, 1, pathspec);
+                                               cdir->ucd, 1, 0, pathspec);
        /*
         * We get path_recurse in the first run when
         * directory_exists_in_index() returns index_nonexistent. We
@@ -1793,12 +1798,20 @@ static void close_cached_dir(struct cached_dir *cdir)
  * Also, we ignore the name ".git" (even if it is not a directory).
  * That likely will not change.
  *
+ * If 'stop_at_first_file' is specified, 'path_excluded' is returned
+ * to signal that a file was found. This is the least significant value that
+ * indicates that a file was encountered that does not depend on the order of
+ * whether an untracked or exluded path was encountered first.
+ *
  * Returns the most significant path_treatment value encountered in the scan.
+ * If 'stop_at_first_file' is specified, `path_excluded` is the most
+ * significant path_treatment value that will be returned.
  */
+
 static enum path_treatment read_directory_recursive(struct dir_struct *dir,
        struct index_state *istate, const char *base, int baselen,
        struct untracked_cache_dir *untracked, int check_only,
-       const struct pathspec *pathspec)
+       int stop_at_first_file, const struct pathspec *pathspec)
 {
        struct cached_dir cdir;
        enum path_treatment state, subdir_state, dir_state = path_none;
@@ -1832,12 +1845,34 @@ static enum path_treatment read_directory_recursive(struct dir_struct *dir,
                        subdir_state =
                                read_directory_recursive(dir, istate, path.buf,
                                                         path.len, ud,
-                                                        check_only, pathspec);
+                                                        check_only, stop_at_first_file, pathspec);
                        if (subdir_state > dir_state)
                                dir_state = subdir_state;
                }
 
                if (check_only) {
+                       if (stop_at_first_file) {
+                               /*
+                                * If stopping at first file, then
+                                * signal that a file was found by
+                                * returning `path_excluded`. This is
+                                * to return a consistent value
+                                * regardless of whether an ignored or
+                                * excluded file happened to be
+                                * encountered 1st.
+                                *
+                                * In current usage, the
+                                * `stop_at_first_file` is passed when
+                                * an ancestor directory has matched
+                                * an exclude pattern, so any found
+                                * files will be excluded.
+                                */
+                               if (dir_state >= path_excluded) {
+                                       dir_state = path_excluded;
+                                       break;
+                               }
+                       }
+
                        /* abort early if maximum state has been reached */
                        if (dir_state == path_untracked) {
                                if (cdir.fdir)
@@ -2108,7 +2143,7 @@ int read_directory(struct dir_struct *dir, struct index_state *istate,
                 */
                dir->untracked = NULL;
        if (!len || treat_leading_path(dir, istate, path, len, pathspec))
-               read_directory_recursive(dir, istate, path, len, untracked, 0, pathspec);
+               read_directory_recursive(dir, istate, path, len, untracked, 0, 0, pathspec);
        QSORT(dir->entries, dir->nr, cmp_dir_entry);
        QSORT(dir->ignored, dir->ignored_nr, cmp_dir_entry);
 
diff --git a/entry.c b/entry.c
index 65458f07a4453fa29731fe2b9c20848d6b192b20..1c7e3c11d5075d4b39eeca76ab2272e781f773c8 100644 (file)
--- a/entry.c
+++ b/entry.c
@@ -3,6 +3,7 @@
 #include "dir.h"
 #include "streaming.h"
 #include "submodule.h"
+#include "progress.h"
 
 static void create_directories(const char *path, int path_len,
                               const struct checkout *state)
@@ -161,16 +162,22 @@ static int remove_available_paths(struct string_list_item *item, void *cb_data)
 int finish_delayed_checkout(struct checkout *state)
 {
        int errs = 0;
+       unsigned delayed_object_count;
+       off_t filtered_bytes = 0;
        struct string_list_item *filter, *path;
+       struct progress *progress;
        struct delayed_checkout *dco = state->delayed_checkout;
 
        if (!state->delayed_checkout)
                return errs;
 
        dco->state = CE_RETRY;
+       delayed_object_count = dco->paths.nr;
+       progress = start_delayed_progress(_("Filtering content"), delayed_object_count);
        while (dco->filters.nr > 0) {
                for_each_string_list_item(filter, &dco->filters) {
                        struct string_list available_paths = STRING_LIST_INIT_NODUP;
+                       display_progress(progress, delayed_object_count - dco->paths.nr);
 
                        if (!async_query_available_blobs(filter->string, &available_paths)) {
                                /* Filter reported an error */
@@ -216,11 +223,17 @@ int finish_delayed_checkout(struct checkout *state)
                                }
                                ce = index_file_exists(state->istate, path->string,
                                                       strlen(path->string), 0);
-                               errs |= (ce ? checkout_entry(ce, state, NULL) : 1);
+                               if (ce) {
+                                       errs |= checkout_entry(ce, state, NULL);
+                                       filtered_bytes += ce->ce_stat_data.sd_size;
+                                       display_throughput(progress, filtered_bytes);
+                               } else
+                                       errs = 1;
                        }
                }
                string_list_remove_empty_items(&dco->filters, 0);
        }
+       stop_progress(&progress);
        string_list_clear(&dco->filters, 0);
 
        /* At this point we should not have any delayed paths anymore. */
@@ -244,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;
 
@@ -319,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..9bc15b0363d562fc55268419055e9cdbbeee50c3 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) do {} while (0)
+#endif
+
 #endif
index 0f5837d48e7040e9d2b07513666b608353736645..2514bc22abf4aedfaa8d641976c96fbf222bb8a7 100644 (file)
@@ -165,7 +165,7 @@ to contribute an update.  This may be because you would want to improve
 the translation of existing messages, or because the git-gui software
 itself was updated and there are new messages that need translation.
 
-In any case, make sure you are up-to-date before starting your work:
+In any case, make sure you are up to date before starting your work:
 
        $ git checkout master
        $ git pull
index 6c390d6c229d7ef8cca8ae01d33b42b57e29c3b3..7d19d379512b52168d7c604002e872fc3eef9fd7 100755 (executable)
@@ -74,7 +74,7 @@ do
 
        case "$LF$common$LF" in
        *"$LF$SHA1$LF"*)
-               eval_gettextln "Already up-to-date with \$pretty_name"
+               eval_gettextln "Already up to date with \$pretty_name"
                continue
                ;;
        esac
index 2fa581789c5d9d4dd967e1783c513d64660f8a9c..76859b453afc2d5ba6897d15a680245b455f4f38 100755 (executable)
--- a/git-p4.py
+++ b/git-p4.py
@@ -3614,7 +3614,7 @@ def run(self, args):
 
     def rebase(self):
         if os.system("git update-index --refresh") != 0:
-            die("Some files in your working directory are modified and different than what is in your index. You can use git update-index <filename> to bring the index up-to-date or stash away all your changes with git stash.");
+            die("Some files in your working directory are modified and different than what is in your index. You can use git update-index <filename> to bring the index up to date or stash away all your changes with git stash.");
         if len(read_pipe("git diff-index HEAD --")) > 0:
             die("You have uncommitted changes. Please commit them before rebasing or stash them away with git stash.");
 
index ad8415e3cf6907044b0730b59fc2e6ff30ab7174..6344e8d5e38ca237dc5b5b67844f87f48aaa74ca 100755 (executable)
@@ -350,6 +350,9 @@ do
                shift
                break
                ;;
+       *)
+               usage
+               ;;
        esac
        shift
 done
index fa6526986ed40143a12ffcd6563fd52b49d209e7..2208dcc2139be7130749fd3bae462320f17d3b21 100755 (executable)
@@ -155,7 +155,6 @@ sub format_2822_time {
 }
 
 my $have_email_valid = eval { require Email::Valid; 1 };
-my $have_mail_address = eval { require Mail::Address; 1 };
 my $smtp;
 my $auth;
 my $num_sent = 0;
@@ -490,11 +489,7 @@ sub read_config {
 ($repocommitter) = Git::ident_person(@repo, 'committer');
 
 sub parse_address_line {
-       if ($have_mail_address) {
-               return map { $_->format } Mail::Address->parse($_[0]);
-       } else {
-               return Git::parse_mailboxes($_[0]);
-       }
+       return Git::parse_mailboxes($_[0]);
 }
 
 sub split_addrs {
@@ -1089,6 +1084,26 @@ sub sanitize_address {
 
 }
 
+sub strip_garbage_one_address {
+       my ($addr) = @_;
+       chomp $addr;
+       if ($addr =~ /^(("[^"]*"|[^"<]*)? *<[^>]*>).*/) {
+               # "Foo Bar" <foobar@example.com> [possibly garbage here]
+               # Foo Bar <foobar@example.com> [possibly garbage here]
+               return $1;
+       }
+       if ($addr =~ /^(<[^>]*>).*/) {
+               # <foo@example.com> [possibly garbage here]
+               # if garbage contains other addresses, they are ignored.
+               return $1;
+       }
+       if ($addr =~ /^([^"#,\s]*)/) {
+               # address without quoting: remove anything after the address
+               return $1;
+       }
+       return $addr;
+}
+
 sub sanitize_address_list {
        return (map { sanitize_address($_) } @_);
 }
@@ -1590,10 +1605,12 @@ sub send_message {
        # Now parse the message body
        while(<$fh>) {
                $message .=  $_;
-               if (/^(Signed-off-by|Cc): ([^>]*>?)/i) {
+               if (/^(Signed-off-by|Cc): (.*)/i) {
                        chomp;
                        my ($what, $c) = ($1, $2);
-                       chomp $c;
+                       # strip garbage for the address we'll use:
+                       $c = strip_garbage_one_address($c);
+                       # sanitize a bit more to decide whether to suppress the address:
                        my $sc = sanitize_address($c);
                        if ($sc eq $sender) {
                                next if ($suppress_cc{'self'});
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);
index b2d0b849bb20c88532dd2952a4a5ae94204ccad8..8c785f3ca20c52266d7f99ca0d306a324756ede8 100644 (file)
@@ -35,11 +35,11 @@ typedef void *SSL;
 #include "http.h"
 #endif
 
-#if defined(USE_CURL_FOR_IMAP_SEND) && defined(NO_OPENSSL)
-/* only available option */
+#if defined(USE_CURL_FOR_IMAP_SEND)
+/* Always default to curl if it's available. */
 #define USE_CURL_DEFAULT 1
 #else
-/* strictly opt in */
+/* We don't have curl, so continue to use the historical implementation */
 #define USE_CURL_DEFAULT 0
 #endif
 
@@ -926,6 +926,25 @@ static int auth_cram_md5(struct imap_store *ctx, struct imap_cmd *cmd, const cha
        return 0;
 }
 
+static void server_fill_credential(struct imap_server_conf *srvc, struct credential *cred)
+{
+       if (srvc->user && srvc->pass)
+               return;
+
+       cred->protocol = xstrdup(srvc->use_ssl ? "imaps" : "imap");
+       cred->host = xstrdup(srvc->host);
+
+       cred->username = xstrdup_or_null(srvc->user);
+       cred->password = xstrdup_or_null(srvc->pass);
+
+       credential_fill(cred);
+
+       if (!srvc->user)
+               srvc->user = xstrdup(cred->username);
+       if (!srvc->pass)
+               srvc->pass = xstrdup(cred->password);
+}
+
 static struct imap_store *imap_open_store(struct imap_server_conf *srvc, char *folder)
 {
        struct credential cred = CREDENTIAL_INIT;
@@ -1078,20 +1097,7 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, char *f
                }
 #endif
                imap_info("Logging in...\n");
-               if (!srvc->user || !srvc->pass) {
-                       cred.protocol = xstrdup(srvc->use_ssl ? "imaps" : "imap");
-                       cred.host = xstrdup(srvc->host);
-
-                       cred.username = xstrdup_or_null(srvc->user);
-                       cred.password = xstrdup_or_null(srvc->pass);
-
-                       credential_fill(&cred);
-
-                       if (!srvc->user)
-                               srvc->user = xstrdup(cred.username);
-                       if (!srvc->pass)
-                               srvc->pass = xstrdup(cred.password);
-               }
+               server_fill_credential(srvc, &cred);
 
                if (srvc->auth_method) {
                        struct imap_cmd_cb cb;
@@ -1392,7 +1398,7 @@ static int append_msgs_to_imap(struct imap_server_conf *server,
 }
 
 #ifdef USE_CURL_FOR_IMAP_SEND
-static CURL *setup_curl(struct imap_server_conf *srvc)
+static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
 {
        CURL *curl;
        struct strbuf path = STRBUF_INIT;
@@ -1405,6 +1411,7 @@ static CURL *setup_curl(struct imap_server_conf *srvc)
        if (!curl)
                die("curl_easy_init failed");
 
+       server_fill_credential(&server, cred);
        curl_easy_setopt(curl, CURLOPT_USERNAME, server.user);
        curl_easy_setopt(curl, CURLOPT_PASSWORD, server.pass);
 
@@ -1454,8 +1461,9 @@ static int curl_append_msgs_to_imap(struct imap_server_conf *server,
        struct buffer msgbuf = { STRBUF_INIT, 0 };
        CURL *curl;
        CURLcode res = CURLE_OK;
+       struct credential cred = CREDENTIAL_INIT;
 
-       curl = setup_curl(server);
+       curl = setup_curl(server, &cred);
        curl_easy_setopt(curl, CURLOPT_READDATA, &msgbuf);
 
        fprintf(stderr, "sending %d message%s\n", total, (total != 1) ? "s" : "");
@@ -1490,7 +1498,20 @@ static int curl_append_msgs_to_imap(struct imap_server_conf *server,
        curl_easy_cleanup(curl);
        curl_global_cleanup();
 
-       return 0;
+       if (cred.username) {
+               if (res == CURLE_OK)
+                       credential_approve(&cred);
+#if LIBCURL_VERSION_NUM >= 0x070d01
+               else if (res == CURLE_LOGIN_DENIED)
+#else
+               else
+#endif
+                       credential_reject(&cred);
+       }
+
+       credential_clear(&cred);
+
+       return res != CURLE_OK;
 }
 #endif
 
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 1494ffdb828106882053065e3ac8cc383136cf80..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);
 }
 
@@ -1927,7 +1951,7 @@ int merge_trees(struct merge_options *o,
        }
 
        if (oid_eq(&common->object.oid, &merge->object.oid)) {
-               output(o, 0, _("Already up-to-date!"));
+               output(o, 0, _("Already up to date!"));
                *result = head;
                return 1;
        }
@@ -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 744c68557649ff1b1fbc007cd2bd2951fc09ab93..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)
@@ -624,7 +624,7 @@ int notes_merge(struct notes_merge_options *o,
        if (!oidcmp(&remote->object.oid, base_oid)) {
                /* Already merged; result == local commit */
                if (o->verbosity >= 2)
-                       printf("Already up-to-date!\n");
+                       printf("Already up to date!\n");
                oidcpy(result_oid, &local->object.oid);
                goto found_result;
        }
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..82eb39cd679ffbce82abac691c6f4343335b9924 100644 (file)
@@ -526,17 +526,13 @@ static void NORETURN unsupported_magic(const char *pattern,
            pattern, sb.buf);
 }
 
-/*
- * Given command line arguments and a prefix, convert the input to
- * pathspec. die() if any magic in magic_mask is used.
- */
 void parse_pathspec(struct pathspec *pathspec,
                    unsigned magic_mask, unsigned flags,
                    const char *prefix, const char **argv)
 {
        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 +565,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 60e6500401e687bc0d764c38c5d8b4928b7bfde8..6420d1080ae4165ad81b60376c714bd178463b6a 100644 (file)
@@ -70,6 +70,13 @@ struct pathspec {
  */
 #define PATHSPEC_LITERAL_PATH (1<<6)
 
+/*
+ * Given command line arguments and a prefix, convert the input to
+ * pathspec. die() if any magic in magic_mask is used.
+ *
+ * Any arguments used are copied. It is safe for the caller to modify
+ * or free 'prefix' and 'args' after calling this function.
+ */
 extern void parse_pathspec(struct pathspec *pathspec,
                           unsigned magic_mask,
                           unsigned flags,
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 43c317e4e9f6e67aa3e1d0ae245d097bdba664db..41130900698893f46dd18b817a7e6b6485c735a4 100644 (file)
--- a/remote.c
+++ b/remote.c
@@ -2084,7 +2084,7 @@ int format_tracking_info(struct branch *branch, struct strbuf *sb)
                                _("  (use \"git branch --unset-upstream\" to fixup)\n"));
        } else if (!ours && !theirs) {
                strbuf_addf(sb,
-                       _("Your branch is up-to-date with '%s'.\n"),
+                       _("Your branch is up to date with '%s'.\n"),
                        base);
        } else if (!theirs) {
                strbuf_addf(sb,
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..1520f69d93583972a71d5ece7870b598c9060555 100644 (file)
@@ -20,6 +20,8 @@
 #include "cache-tree.h"
 #include "bisect.h"
 #include "packfile.h"
+#include "worktree.h"
+#include "argv-array.h"
 
 volatile show_early_output_fn_t show_early_output;
 
@@ -1132,6 +1134,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 +1171,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 +1192,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 +1240,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 +1298,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 +1317,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)
 {
@@ -1612,31 +1673,15 @@ int handle_revision_arg(const char *arg_, struct rev_info *revs, int flags, unsi
        return 0;
 }
 
-struct cmdline_pathspec {
-       int alloc;
-       int nr;
-       const char **path;
-};
-
-static void append_prune_data(struct cmdline_pathspec *prune, const char **av)
-{
-       while (*av) {
-               ALLOC_GROW(prune->path, prune->nr + 1, prune->alloc);
-               prune->path[prune->nr++] = *(av++);
-       }
-}
-
 static void read_pathspec_from_stdin(struct rev_info *revs, struct strbuf *sb,
-                                    struct cmdline_pathspec *prune)
+                                    struct argv_array *prune)
 {
-       while (strbuf_getline(sb, stdin) != EOF) {
-               ALLOC_GROW(prune->path, prune->nr + 1, prune->alloc);
-               prune->path[prune->nr++] = xstrdup(sb->buf);
-       }
+       while (strbuf_getline(sb, stdin) != EOF)
+               argv_array_push(prune, sb->buf);
 }
 
 static void read_revisions_from_stdin(struct rev_info *revs,
-                                     struct cmdline_pathspec *prune)
+                                     struct argv_array *prune)
 {
        struct strbuf sb;
        int seen_dashdash = 0;
@@ -2069,23 +2114,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 +2141,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 +2168,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 +2237,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;
        }
@@ -2201,10 +2271,9 @@ static void NORETURN diagnose_missing_default(const char *def)
 int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct setup_revision_opt *opt)
 {
        int i, flags, left, seen_dashdash, read_from_stdin, got_rev_arg = 0, revarg_opt;
-       struct cmdline_pathspec prune_data;
+       struct argv_array prune_data = ARGV_ARRAY_INIT;
        const char *submodule = NULL;
 
-       memset(&prune_data, 0, sizeof(prune_data));
        if (opt)
                submodule = opt->submodule;
 
@@ -2220,7 +2289,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
                        argv[i] = NULL;
                        argc = i;
                        if (argv[i + 1])
-                               append_prune_data(&prune_data, argv + i + 1);
+                               argv_array_pushv(&prune_data, argv + i + 1);
                        seen_dashdash = 1;
                        break;
                }
@@ -2281,14 +2350,14 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
                        for (j = i; j < argc; j++)
                                verify_filename(revs->prefix, argv[j], j == i);
 
-                       append_prune_data(&prune_data, argv + i);
+                       argv_array_pushv(&prune_data, argv + i);
                        break;
                }
                else
                        got_rev_arg = 1;
        }
 
-       if (prune_data.nr) {
+       if (prune_data.argc) {
                /*
                 * If we need to introduce the magic "a lone ':' means no
                 * pathspec whatsoever", here is the place to do so.
@@ -2303,11 +2372,10 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
                 *      call init_pathspec() to set revs->prune_data here.
                 * }
                 */
-               ALLOC_GROW(prune_data.path, prune_data.nr + 1, prune_data.alloc);
-               prune_data.path[prune_data.nr++] = NULL;
                parse_pathspec(&revs->prune_data, 0, 0,
-                              revs->prefix, prune_data.path);
+                              revs->prefix, prune_data.argv);
        }
+       argv_array_clear(&prune_data);
 
        if (revs->def == NULL)
                revs->def = opt ? opt->def : NULL;
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 20af2856818ed51b2afb1718a7e317133ee0d7bd..cf5314f402ce78f0d5ab2bd72ee7f334b6394e04 100644 (file)
@@ -7,10 +7,4 @@ extern int sha1_pos(const unsigned char *sha1,
                    void *table,
                    size_t nr,
                    sha1_access_fn fn);
-
-extern int sha1_entry_pos(const void *table,
-                         size_t elem_size,
-                         size_t key_offset,
-                         unsigned lo, unsigned hi, unsigned nr,
-                         const unsigned char *key);
 #endif
index 5f71bbac3ea9a11a4369ec0353e2e1258979ba19..5a2014811fd0a42aa74b36bf3a470e471968be6f 100644 (file)
@@ -398,7 +398,7 @@ static const char *parse_alt_odb_entry(const char *string,
        return end;
 }
 
-static void link_alt_odb_entries(const char *alt, int len, int sep,
+static void link_alt_odb_entries(const char *alt, int sep,
                                 const char *relative_base, int depth)
 {
        struct strbuf objdirbuf = STRBUF_INIT;
@@ -427,28 +427,19 @@ static void link_alt_odb_entries(const char *alt, int len, int sep,
 
 static void read_info_alternates(const char * relative_base, int depth)
 {
-       char *map;
-       size_t mapsz;
-       struct stat st;
        char *path;
-       int fd;
+       struct strbuf buf = STRBUF_INIT;
 
        path = xstrfmt("%s/info/alternates", relative_base);
-       fd = git_open(path);
-       free(path);
-       if (fd < 0)
-               return;
-       if (fstat(fd, &st) || (st.st_size == 0)) {
-               close(fd);
+       if (strbuf_read_file(&buf, path, 1024) < 0) {
+               warn_on_fopen_errors(path);
+               free(path);
                return;
        }
-       mapsz = xsize_t(st.st_size);
-       map = xmmap(NULL, mapsz, PROT_READ, MAP_PRIVATE, fd, 0);
-       close(fd);
 
-       link_alt_odb_entries(map, mapsz, '\n', relative_base, depth);
-
-       munmap(map, mapsz);
+       link_alt_odb_entries(buf.buf, '\n', relative_base, depth);
+       strbuf_release(&buf);
+       free(path);
 }
 
 struct alternate_object_database *alloc_alt_odb(const char *dir)
@@ -503,7 +494,7 @@ void add_to_alternates_file(const char *reference)
                if (commit_lock_file(lock))
                        die_errno("unable to move new alternates file into place");
                if (alt_odb_tail)
-                       link_alt_odb_entries(reference, strlen(reference), '\n', NULL, 0);
+                       link_alt_odb_entries(reference, '\n', NULL, 0);
        }
        free(alts);
 }
@@ -516,7 +507,7 @@ void add_to_alternates_memory(const char *reference)
         */
        prepare_alt_odb();
 
-       link_alt_odb_entries(reference, strlen(reference), '\n', NULL, 0);
+       link_alt_odb_entries(reference, '\n', NULL, 0);
 }
 
 /*
@@ -619,7 +610,7 @@ void prepare_alt_odb(void)
        if (!alt) alt = "";
 
        alt_odb_tail = &alt_odb_list;
-       link_alt_odb_entries(alt, strlen(alt), PATH_SEP, NULL, 0);
+       link_alt_odb_entries(alt, PATH_SEP, NULL, 0);
 
        read_info_alternates(get_object_directory(), 0);
 }
@@ -1820,6 +1811,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 +1828,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 +1836,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 e705b94db55578aabb2063f7364220978cea5a40..7496cb8ec5a1f8baeda90f8653b2fcbbe2527390 100644 (file)
--- a/strbuf.h
+++ b/strbuf.h
@@ -147,7 +147,10 @@ static inline void strbuf_setlen(struct strbuf *sb, size_t len)
        if (len > (sb->alloc ? sb->alloc - 1 : 0))
                die("BUG: strbuf_setlen() beyond buffer");
        sb->len = len;
-       sb->buf[len] = '\0';
+       if (sb->buf != strbuf_slopbuf)
+               sb->buf[len] = '\0';
+       else
+               assert(!strbuf_slopbuf[0]);
 }
 
 /**
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 29bfb7ae45931686e14c91b1a47a703e7b32fe52..79ae567cbc3bfbd7574b8156aa9fcf56581cad83 100644 (file)
@@ -32,8 +32,10 @@ void string_list_clear_func(struct string_list *list, string_list_clear_func_t c
 typedef int (*string_list_each_func_t)(struct string_list_item *, void *);
 int for_each_string_list(struct string_list *list,
                         string_list_each_func_t, void *cb_data);
-#define for_each_string_list_item(item,list) \
-       for (item = (list)->items; item < (list)->items + (list)->nr; ++item)
+#define for_each_string_list_item(item,list)            \
+       for (item = (list)->items;                      \
+            item && item < (list)->items + (list)->nr; \
+            ++item)
 
 /*
  * Apply want to each item in list, retaining only the ones for which
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 2f958603697515f40520a7ff9df31de13eefc2d7..4b079e4494d9324f0b03dfe8f714b3717292c0b6 100644 (file)
--- a/t/README
+++ b/t/README
@@ -265,12 +265,12 @@ or:
 
     $ sh ./t9200-git-cvsexport-commit.sh --run='-3 21'
 
-As noted above, the test set is built going though items left to
-right, so this:
+As noted above, the test set is built by going through the items
+from left to right, so this:
 
     $ sh ./t9200-git-cvsexport-commit.sh --run='1-4 !3'
 
-will run tests 1, 2, and 4.  Items that comes later have higher
+will run tests 1, 2, and 4.  Items that come later have higher
 precedence.  It means that this:
 
     $ sh ./t9200-git-cvsexport-commit.sh --run='!3 1-4'
index b170cbc0452ca1b5a624f5cf330d420c397cb760..03dc9d28525730cdf57c74d13cab22da59dcdfbd 100755 (executable)
@@ -17,7 +17,7 @@ sub err {
 while (<>) {
        chomp;
        /\bsed\s+-i/ and err 'sed -i is not portable';
-       /\becho\s+-n/ and err 'echo -n is not portable (please use printf)';
+       /\becho\s+-[neE]/ and err 'echo with option is not portable (please use printf)';
        /^\s*declare\s+/ and err 'arrays/declare not portable';
        /^\s*[^#]\s*which\s/ and err 'which is not portable (please use type)';
        /\btest\s+[^=]*==/ and err '"test a == b" is not portable (please use =)';
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 dc98b4bc6dc7aab41eca38d9e1086ad2b6701fd3..664a3a4e4e9b81d3e017c22414b0bbe2ef25b83a 100755 (executable)
@@ -1253,7 +1253,10 @@ run_with_limited_open_files () {
        (ulimit -n 32 && "$@")
 }
 
-test_lazy_prereq ULIMIT_FILE_DESCRIPTORS 'run_with_limited_open_files true'
+test_lazy_prereq ULIMIT_FILE_DESCRIPTORS '
+       test_have_prereq !MINGW,!CYGWIN &&
+       run_with_limited_open_files true
+'
 
 test_expect_success ULIMIT_FILE_DESCRIPTORS 'large transaction creating branches does not burst open file limit' '
 (
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 03d3c7f6d65b46bf977adf76a6d68582206768c2..5c715fe2cf6c7afe2c39e6a7197cdd3d33329b8b 100755 (executable)
@@ -116,6 +116,21 @@ test_expect_success 'git-path inside sub-dir' '
        test_cmp expect actual
 '
 
+test_expect_success 'rev-parse --is-shallow-repository in shallow repo' '
+       test_commit test_commit &&
+       echo true >expect &&
+       git clone --depth 1 --no-local . shallow &&
+       test_when_finished "rm -rf shallow" &&
+       git -C shallow rev-parse --is-shallow-repository >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'rev-parse --is-shallow-repository in non-shallow repo' '
+       echo false >expect &&
+       git rev-parse --is-shallow-repository >actual &&
+       test_cmp expect actual
+'
+
 test_expect_success 'showing the superproject correctly' '
        git rev-parse --show-superproject-working-tree >out &&
        test_must_be_empty out &&
index 310f93fd30ea51f94eaa2b4dd2e8d0e398783cba..6e1b45fd1295c3f3c611ac7d431acf7b3365fc5d 100755 (executable)
@@ -28,6 +28,9 @@ test_expect_success 'setup optionspec' '
 |g,fluf?path     short and long option optional argument
 |longest=very-long-argument-hint  a very long argument hint
 |pair=key=value  with an equals sign in the hint
+|aswitch help te=t contains? fl*g characters!`
+|bswitch=hint   hint has trailing tab character
+|cswitch        switch has trailing tab character
 |short-hint=a    with a one symbol hint
 |
 |Extras
@@ -62,6 +65,9 @@ test_expect_success 'test --parseopt help output' '
 |    --longest <very-long-argument-hint>
 |                          a very long argument hint
 |    --pair <key=value>    with an equals sign in the hint
+|    --aswitch             help te=t contains? fl*g characters!`
+|    --bswitch <hint>      hint has trailing tab character
+|    --cswitch             switch has trailing tab character
 |    --short-hint <a>      with a one symbol hint
 |
 |Extras
@@ -75,17 +81,17 @@ END_EXPECT
 
 test_expect_success 'setup expect.1' "
        cat > expect <<EOF
-set -- --foo --bar 'ham' -b -- 'arg'
+set -- --foo --bar 'ham' -b --aswitch -- 'arg'
 EOF
 "
 
 test_expect_success 'test --parseopt' '
-       git rev-parse --parseopt -- --foo --bar=ham --baz arg < optionspec > output &&
+       git rev-parse --parseopt -- --foo --bar=ham --baz --aswitch arg < optionspec > output &&
        test_cmp expect output
 '
 
 test_expect_success 'test --parseopt with mixed options and arguments' '
-       git rev-parse --parseopt -- --foo arg --bar=ham --baz < optionspec > output &&
+       git rev-parse --parseopt -- --foo arg --bar=ham --baz --aswitch < optionspec > output &&
        test_cmp expect output
 '
 
index 51738fb96943bbc475d0f71ba77e4786dd0aea11..d971649979fa2661923d9df797125cd4d997cf97 100755 (executable)
@@ -162,6 +162,19 @@ test_expect_success 'git branch -M baz bam should add entries to .git/logs/HEAD'
        grep "^0\{40\}.*$msg$" .git/logs/HEAD
 '
 
+test_expect_success 'git branch -M should leave orphaned HEAD alone' '
+       git init orphan &&
+       (
+               cd orphan &&
+               test_commit initial &&
+               git checkout --orphan lonely &&
+               grep lonely .git/HEAD &&
+               test_path_is_missing .git/refs/head/lonely &&
+               git branch -M master mistress &&
+               grep lonely .git/HEAD
+       )
+'
+
 test_expect_success 'resulting reflog can be shown by log -g' '
        oid=$(git rev-parse HEAD) &&
        cat >expect <<-EOF &&
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 be78cc4fad205d7f1da7cc864de3c6e04be51882..8f17fd9da8ef6b54ae0a97500f2154b1c9d968b3 100755 (executable)
@@ -100,7 +100,7 @@ test_expect_success 'checkout (up-to-date with upstream)' '
        (
                cd test && git checkout b6
        ) >actual &&
-       test_i18ngrep "Your branch is up-to-date with .origin/master" actual
+       test_i18ngrep "Your branch is up to date with .origin/master" actual
 '
 
 test_expect_success 'status (diverged from upstream)' '
@@ -130,7 +130,7 @@ test_expect_success 'status (up-to-date with upstream)' '
                # reports nothing to commit
                test_must_fail git commit --dry-run
        ) >actual &&
-       test_i18ngrep "Your branch is up-to-date with .origin/master" actual
+       test_i18ngrep "Your branch is up to date with .origin/master" actual
 '
 
 cat >expect <<\EOF
index aa74eb8f0d5dfb4f228b1e8c7b9dbbc0b88adb4d..1c0e8659d97c0792ce5761cf0704aa7f061575e0 100755 (executable)
@@ -182,10 +182,41 @@ check_describe "test2-lightweight-*" --tags --match="test2-*"
 
 check_describe "test2-lightweight-*" --long --tags --match="test2-*" HEAD^
 
-check_describe "test1-lightweight-*" --long --tags --match="test1-*" --match="test2-*" HEAD^
+check_describe "test2-lightweight-*" --long --tags --match="test1-*" --match="test2-*" HEAD^
 
 check_describe "test2-lightweight-*" --long --tags --match="test1-*" --no-match --match="test2-*" HEAD^
 
+check_describe "test1-lightweight-*" --long --tags --match="test1-*" --match="test3-*" HEAD
+
+check_describe "test1-lightweight-*" --long --tags --match="test3-*" --match="test1-*" HEAD
+
+test_expect_success 'set-up branches' '
+       git branch branch_A A &&
+       git branch branch_C c &&
+       git update-ref refs/remotes/origin/remote_branch_A "A^{commit}" &&
+       git update-ref refs/remotes/origin/remote_branch_C "c^{commit}" &&
+       git update-ref refs/original/original_branch_A test-annotated~2
+'
+
+check_describe "heads/branch_A*" --all --match="branch_*" --exclude="branch_C" HEAD
+
+check_describe "remotes/origin/remote_branch_A*" --all --match="origin/remote_branch_*" --exclude="origin/remote_branch_C" HEAD
+
+check_describe "original/original_branch_A*" --all test-annotated~1
+
+test_expect_success '--match does not work for other types' '
+       test_must_fail git describe --all --match="*original_branch_*" test-annotated~1
+'
+
+test_expect_success '--exclude does not work for other types' '
+       R=$(git describe --all --exclude="any_pattern_even_not_matching" test-annotated~1) &&
+       case "$R" in
+       *original_branch_A*) echo "fail: Found unknown reference $R with --exclude"
+               false;;
+       *) echo ok: Found some known type;;
+       esac
+'
+
 test_expect_success 'name-rev with exact tags' '
        echo A >expect &&
        tag_object=$(git rev-parse refs/tags/A) &&
@@ -198,6 +229,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 +306,38 @@ 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
 '
 
+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 e365d1ff77ee30e0f40b8f01c7e70ebc74e97901..cbc5fb37fe531ff9a60c236a175fd6a4f29b1ece 100755 (executable)
@@ -491,4 +491,29 @@ test_expect_success 'moving a submodule in nested directories' '
        test_cmp actual expect
 '
 
+test_expect_failure 'moving nested submodules' '
+       git commit -am "cleanup commit" &&
+       mkdir sub_nested_nested &&
+       (cd sub_nested_nested &&
+               touch nested_level2 &&
+               git init &&
+               git add . &&
+               git commit -m "nested level 2"
+       ) &&
+       mkdir sub_nested &&
+       (cd sub_nested &&
+               touch nested_level1 &&
+               git init &&
+               git add . &&
+               git commit -m "nested level 1"
+               git submodule add ../sub_nested_nested &&
+               git commit -m "add nested level 2"
+       ) &&
+       git submodule add ./sub_nested nested_move &&
+       git commit -m "add nested_move" &&
+       git submodule update --init --recursive &&
+       git mv nested_move sub_nested_moved &&
+       git status
+'
+
 test_done
index dbcd6f623c7e233b487d4d0c1dd7d71cb34059b0..b545c33f83be94608f18182444ffe37eef753e88 100755 (executable)
@@ -1863,13 +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 &&
        i=1 &&
index d1e4e8ad19f3d3e005e177b98e4eee16f4ef1461..f30980895c2f1b93317870e6b471d67bc99b0fd2 100755 (executable)
@@ -148,6 +148,8 @@ cat >expected-cc <<\EOF
 !two@example.com!
 !three@example.com!
 !four@example.com!
+!five@example.com!
+!six@example.com!
 EOF
 "
 
@@ -161,6 +163,8 @@ test_expect_success $PREREQ 'cc trailer with various syntax' '
        Cc: <two@example.com> # trailing comments are ignored
        Cc: <three@example.com>, <not.four@example.com> one address per line
        Cc: "Some # Body" <four@example.com> [ <also.a.comment> ]
+       Cc: five@example.com # not.six@example.com
+       Cc: six@example.com, not.seven@example.com
        EOF
        clean_fake_sendmail &&
        git send-email -1 --to=recipient@example.com \
index 6dafe7e99ab4bd0006ed4f7204062fca31750689..8eaaca6f9906879f16f3658b0e04796086d4c435 100755 (executable)
@@ -4,12 +4,13 @@ test_description='check svn dumpfile importer'
 
 . ./test-lib.sh
 
+if test_have_prereq !PIPE
+then
+       skip_all="svn dumpfile importer testing requires the PIPE prerequisite"
+       test_done
+fi
+
 reinit_git () {
-       if ! test_declared_prereq PIPE
-       then
-               echo >&4 "reinit_git: need to declare PIPE prerequisite"
-               return 127
-       fi
        rm -fr .git &&
        rm -f stream backflow &&
        git init &&
@@ -54,19 +55,19 @@ text_no_props () {
 
 >empty
 
-test_expect_success PIPE 'empty dump' '
+test_expect_success 'empty dump' '
        reinit_git &&
        echo "SVN-fs-dump-format-version: 2" >input &&
        try_dump input
 '
 
-test_expect_success PIPE 'v4 dumps not supported' '
+test_expect_success 'v4 dumps not supported' '
        reinit_git &&
        echo "SVN-fs-dump-format-version: 4" >v4.dump &&
        try_dump v4.dump must_fail
 '
 
-test_expect_failure PIPE 'empty revision' '
+test_expect_failure 'empty revision' '
        reinit_git &&
        printf "rev <nobody, nobody@local>: %s\n" "" "" >expect &&
        cat >emptyrev.dump <<-\EOF &&
@@ -86,7 +87,7 @@ test_expect_failure PIPE 'empty revision' '
        test_cmp expect actual
 '
 
-test_expect_success PIPE 'empty properties' '
+test_expect_success 'empty properties' '
        reinit_git &&
        printf "rev <nobody, nobody@local>: %s\n" "" "" >expect &&
        cat >emptyprop.dump <<-\EOF &&
@@ -109,7 +110,7 @@ test_expect_success PIPE 'empty properties' '
        test_cmp expect actual
 '
 
-test_expect_success PIPE 'author name and commit message' '
+test_expect_success 'author name and commit message' '
        reinit_git &&
        echo "<author@example.com, author@example.com@local>" >expect.author &&
        cat >message <<-\EOF &&
@@ -143,7 +144,7 @@ test_expect_success PIPE 'author name and commit message' '
        test_cmp expect.author actual.author
 '
 
-test_expect_success PIPE 'unsupported properties are ignored' '
+test_expect_success 'unsupported properties are ignored' '
        reinit_git &&
        echo author >expect &&
        cat >extraprop.dump <<-\EOF &&
@@ -168,7 +169,7 @@ test_expect_success PIPE 'unsupported properties are ignored' '
        test_cmp expect actual
 '
 
-test_expect_failure PIPE 'timestamp and empty file' '
+test_expect_failure 'timestamp and empty file' '
        echo author@example.com >expect.author &&
        echo 1999-01-01 >expect.date &&
        echo file >expect.files &&
@@ -210,7 +211,7 @@ test_expect_failure PIPE 'timestamp and empty file' '
        test_cmp empty file
 '
 
-test_expect_success PIPE 'directory with files' '
+test_expect_success 'directory with files' '
        reinit_git &&
        printf "%s\n" directory/file1 directory/file2 >expect.files &&
        echo hi >hi &&
@@ -263,7 +264,7 @@ test_expect_success PIPE 'directory with files' '
        test_cmp hi directory/file2
 '
 
-test_expect_success PIPE 'branch name with backslash' '
+test_expect_success 'branch name with backslash' '
        reinit_git &&
        sort <<-\EOF >expect.branch-files &&
        trunk/file1
@@ -362,7 +363,7 @@ test_expect_success PIPE 'branch name with backslash' '
        test_cmp expect.branch-files actual.branch-files
 '
 
-test_expect_success PIPE 'node without action' '
+test_expect_success 'node without action' '
        reinit_git &&
        cat >inaction.dump <<-\EOF &&
        SVN-fs-dump-format-version: 3
@@ -383,7 +384,7 @@ test_expect_success PIPE 'node without action' '
        try_dump inaction.dump must_fail
 '
 
-test_expect_success PIPE 'action: add node without text' '
+test_expect_success 'action: add node without text' '
        reinit_git &&
        cat >textless.dump <<-\EOF &&
        SVN-fs-dump-format-version: 3
@@ -405,7 +406,7 @@ test_expect_success PIPE 'action: add node without text' '
        try_dump textless.dump must_fail
 '
 
-test_expect_failure PIPE 'change file mode but keep old content' '
+test_expect_failure 'change file mode but keep old content' '
        reinit_git &&
        cat >expect <<-\EOF &&
        OBJID
@@ -481,7 +482,7 @@ test_expect_failure PIPE 'change file mode but keep old content' '
        test_cmp hello actual.target
 '
 
-test_expect_success PIPE 'NUL in property value' '
+test_expect_success 'NUL in property value' '
        reinit_git &&
        echo "commit message" >expect.message &&
        {
@@ -507,7 +508,7 @@ test_expect_success PIPE 'NUL in property value' '
        test_cmp expect.message actual.message
 '
 
-test_expect_success PIPE 'NUL in log message, file content, and property name' '
+test_expect_success 'NUL in log message, file content, and property name' '
        # Caveat: svnadmin 1.6.16 (r1073529) truncates at \0 in the
        # svn:specialQnotreally example.
        reinit_git &&
@@ -587,7 +588,7 @@ test_expect_success PIPE 'NUL in log message, file content, and property name' '
        test_cmp expect.hello2 actual.hello2
 '
 
-test_expect_success PIPE 'change file mode and reiterate content' '
+test_expect_success 'change file mode and reiterate content' '
        reinit_git &&
        cat >expect <<-\EOF &&
        OBJID
@@ -667,7 +668,7 @@ test_expect_success PIPE 'change file mode and reiterate content' '
        test_cmp hello actual.target
 '
 
-test_expect_success PIPE 'deltas supported' '
+test_expect_success 'deltas supported' '
        reinit_git &&
        {
                # (old) h + (inline) ello + (old) \n
@@ -731,7 +732,7 @@ test_expect_success PIPE 'deltas supported' '
        try_dump delta.dump
 '
 
-test_expect_success PIPE 'property deltas supported' '
+test_expect_success 'property deltas supported' '
        reinit_git &&
        cat >expect <<-\EOF &&
        OBJID
@@ -796,7 +797,7 @@ test_expect_success PIPE 'property deltas supported' '
        test_cmp expect actual
 '
 
-test_expect_success PIPE 'properties on /' '
+test_expect_success 'properties on /' '
        reinit_git &&
        cat <<-\EOF >expect &&
        OBJID
@@ -850,7 +851,7 @@ test_expect_success PIPE 'properties on /' '
        test_cmp expect actual
 '
 
-test_expect_success PIPE 'deltas for typechange' '
+test_expect_success 'deltas for typechange' '
        reinit_git &&
        cat >expect <<-\EOF &&
        OBJID
@@ -935,7 +936,7 @@ test_expect_success PIPE 'deltas for typechange' '
        test_cmp expect actual
 '
 
-test_expect_success PIPE 'deltas need not consume the whole preimage' '
+test_expect_success 'deltas need not consume the whole preimage' '
        reinit_git &&
        cat >expect <<-\EOF &&
        OBJID
@@ -1040,7 +1041,7 @@ test_expect_success PIPE 'deltas need not consume the whole preimage' '
        test_cmp expect.3 actual.3
 '
 
-test_expect_success PIPE 'no hang for delta trying to read past end of preimage' '
+test_expect_success 'no hang for delta trying to read past end of preimage' '
        reinit_git &&
        {
                # COPY 1
@@ -1087,7 +1088,7 @@ test_expect_success 'set up svn repo' '
        fi
 '
 
-test_expect_success SVNREPO,PIPE 't9135/svn.dump' '
+test_expect_success SVNREPO 't9135/svn.dump' '
        mkdir -p simple-git &&
        (
                cd simple-git &&
index 8dcb05c4a5711e95bb64d8ba310e3490744fc015..866ddf60581e3fea1afdbe28e71e7f3137da5722 100755 (executable)
@@ -234,7 +234,7 @@ test_expect_success 'fast-export -C -C | fast-import' '
        mkdir new &&
        git --git-dir=new/.git init &&
        git fast-export -C -C --signed-tags=strip --all > output &&
-       grep "^C file6 file7\$" output &&
+       grep "^C file2 file4\$" output &&
        cat output |
        (cd new &&
         git fast-import &&
@@ -522,4 +522,22 @@ test_expect_success 'delete refspec' '
        test_cmp expected actual
 '
 
+test_expect_success 'when using -C, do not declare copy when source of copy is also modified' '
+       test_create_repo src &&
+       echo a_line >src/file.txt &&
+       git -C src add file.txt &&
+       git -C src commit -m 1st_commit &&
+
+       cp src/file.txt src/file2.txt &&
+       echo another_line >>src/file.txt &&
+       git -C src add file.txt file2.txt &&
+       git -C src commit -m 2nd_commit &&
+
+       test_create_repo dst &&
+       git -C src fast-export --all -C | git -C dst fast-import &&
+       git -C src show >expected &&
+       git -C dst show >actual &&
+       test_cmp expected actual
+'
+
 test_done
index 5fbd8d4a90b3b88cf57ca53c6b1fe99d5a957460..9b61f16f7a8807ad96014d79f1d1b968397584bf 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"
@@ -1062,14 +1067,8 @@ test_i18ngrep () {
 
 test_lazy_prereq PIPE '
        # test whether the filesystem supports FIFOs
-       case $(uname -s) in
-       CYGWIN*|MINGW*)
-               false
-               ;;
-       *)
-               rm -f testfifo && mkfifo testfifo
-               ;;
-       esac
+       test_have_prereq !MINGW,!CYGWIN &&
+       rm -f testfifo && mkfifo testfifo
 '
 
 test_lazy_prereq SYMLINKS '
@@ -1165,7 +1164,19 @@ run_with_limited_cmdline () {
        (ulimit -s 128 && "$@")
 }
 
-test_lazy_prereq CMDLINE_LIMIT 'run_with_limited_cmdline true'
+test_lazy_prereq CMDLINE_LIMIT '
+       test_have_prereq !MINGW,!CYGWIN &&
+       run_with_limited_cmdline true
+'
+
+run_with_limited_stack () {
+       (ulimit -s 128 && "$@")
+}
+
+test_lazy_prereq ULIMIT_STACK_SIZE '
+       test_have_prereq !MINGW,!CYGWIN &&
+       run_with_limited_stack true
+'
 
 build_option () {
        git version --build-options |
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 b7f81c198e22e4ce578a6148fa3346becabbfb3b..db5feab8a1ee6433108ade9e165956678a3e260b 100755 (executable)
@@ -58,7 +58,7 @@ then
        not_in_topic=`git rev-list "^$topic" master`
        if test -z "$not_in_topic"
        then
-               echo >&2 "$topic is already up-to-date with master"
+               echo >&2 "$topic is already up to date with master"
                exit 1 ;# we could allow it, but there is no point.
        else
                exit 0
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 8f68d69a86bd919162b62514af0b1d1d55e204a1..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;
 }
@@ -1117,6 +1117,13 @@ int transport_helper_init(struct transport *transport, const char *name)
 __attribute__((format (printf, 1, 2)))
 static void transfer_debug(const char *fmt, ...)
 {
+       /*
+        * NEEDSWORK: This function is sometimes used from multiple threads, and
+        * we end up using debug_enabled racily. That "should not matter" since
+        * we always write the same value, but it's still wrong. This function
+        * is listed in .tsan-suppressions for the time being.
+        */
+
        va_list args;
        char msgbuf[PBUFFERSIZE];
        static int debug_enabled = -1;
index 68d34259c6cf6ecb4de29c32621ddbe967b6dbf9..71b70ccb12756cde1f1c5aeda952be6e2b91f5b8 100644 (file)
@@ -163,7 +163,7 @@ void setup_unpack_trees_porcelain(struct unpack_trees_options *opts,
        msgs[ERROR_BIND_OVERLAP] = _("Entry '%s' overlaps with '%s'.  Cannot bind.");
 
        msgs[ERROR_SPARSE_NOT_UPTODATE_FILE] =
-               _("Cannot update sparse checkout: the following entries are not up-to-date:\n%s");
+               _("Cannot update sparse checkout: the following entries are not up to date:\n%s");
        msgs[ERROR_WOULD_LOSE_ORPHANED_OVERWRITTEN] =
                _("The following working tree files would be overwritten by sparse checkout update:\n%s");
        msgs[ERROR_WOULD_LOSE_ORPHANED_REMOVED] =
@@ -383,8 +383,8 @@ static int check_updates(struct unpack_trees_options *o)
                        }
                }
        }
-       errs |= finish_delayed_checkout(&state);
        stop_progress(&progress);
+       errs |= finish_delayed_checkout(&state);
        if (o->update)
                git_attr_set_direction(GIT_ATTR_CHECKIN, NULL);
        return errs != 0;
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 e28ffbeb096a85d67b2e885c48ab096340b5d295..8aaeea03771cfefa9edd019d0dc5146fef17a9c7 100644 (file)
@@ -30,7 +30,7 @@ static void add_head_info(struct worktree *wt)
 
        target = refs_resolve_ref_unsafe(get_worktree_ref_store(wt),
                                         "HEAD",
-                                        RESOLVE_REF_READING,
+                                        0,
                                         wt->head_sha1, &flags);
        if (!target)
                return;
@@ -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;
 }